Advertisement
le_9k

calculate legal positions for chess V1.2

Sep 18th, 2017
343
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. function main() {
  2.     let x=2;//default values
  3.     let y=3;
  4.    
  5.     try {
  6.         const readline = require('readline');
  7.  
  8.         const rl = readline.createInterface({
  9.           input: process.stdin,
  10.           output: process.stdout
  11.         });
  12.        
  13.         rl.question('Input Board x length: ', (answer) => {
  14.             x = answer;
  15.             rl.question('Input Board y length: ', (answer) => {
  16.                 y = answer;
  17.                 rl.close();
  18.                 runMainProcess(x,y);
  19.             });
  20.         });
  21.     }
  22.     catch(err) {
  23.         if(err.message === "require is not defined") {//not running on node so just use default values
  24.             runMainProcess(x,y);
  25.         } else {
  26.             console.err(err);
  27.         }
  28.     }
  29.    
  30.     function runMainProcess(x,y) {
  31.         let board = chessBoardFactory(x,y);
  32.        
  33.         let possiblePieceArrays = board.generatePieceCounts();
  34.         let total = 0;
  35.         let i = 0;
  36.         for (pieceArray of possiblePieceArrays) {
  37.             board.pieces = pieceArray;
  38.             total+= board.calculateCurrent();
  39.             console.log("checking validity of possiblePieceArrays, so far "+JSON.stringify(total)+" possible states found.");
  40.             console.log((i++/possiblePieceArrays.length*100).toFixed(2)+"% completed");//progress report
  41.         }
  42.         console.log("Winner winner chicken dinner");
  43.         console.log("For board width "+board.x+" height "+board.y+" the board has "+total+" possible legal states!");
  44.     }
  45. }
  46.  
  47. const chessBoardFactory = (x = 4, y = 4) => {
  48.     let generatedValue = (function(x,y){
  49.         var result = [];
  50.         for (let i=0; i < x; i++) {
  51.             result[i] = [];
  52.             for (let j=0; j < y; j++) {
  53.                 result[i][j] = 0;
  54.             }
  55.         }
  56.         return result;
  57.     })(x,y);
  58.    
  59.     return{
  60.     x,
  61.     y,
  62.     boardArea: x*y,
  63.     value: generatedValue,//created on factory call, value is a 2d array not a function
  64.     resetValues: function() {
  65.         for(n in this.value) {
  66.             for (m in this.value[n]) {
  67.                 this.value[n][m] = 0;
  68.             }
  69.         }
  70.     },
  71.     flat: function(){
  72.         return this.value.reduce(function(total, current) {
  73.             return total.concat(current);
  74.         },[]);
  75.     },
  76.    
  77.     unflat: function(flatboard){//a function to flatten
  78.         return flatboard.reduce(function(total, current, index) {
  79.             total = total||[];
  80.             if (index%y === 0) {
  81.                 total.push([current]);//newline (y)
  82.                 return total;
  83.             } else {
  84.                 total[total.length-1].push(current);//add to current line (x)
  85.                 return total;
  86.             }
  87.         },[]);
  88.     },
  89.    
  90.     isValid: function (){
  91.         let board = this;
  92.         let pieces = this.pieces;
  93.         var king = pieces.filter(function( obj ) {
  94.             return obj.name == "king";
  95.         })[0];
  96.         //check current board.value is valid, if so total++
  97.         whiteKingThreat = false;
  98.         blackKingThreat = false;
  99.         for (let n in board.value) {
  100.             for (let m in board.value[0]) {//we loop through every square on the board.value
  101.                 n = parseInt(n);m = parseInt(m);
  102.                 if (board.value[n][m] !== 0) {
  103.                     direction = (board.value[n][m] > 0) ? 1 : -1;
  104.                     var currentPiece = pieces.filter(function( obj ) {
  105.                         return obj.id == board.value[n][m]*direction; //make sure id from board.value is positive when matching to pieces id
  106.                     });
  107.                     currentPiece = currentPiece[0];
  108.                    
  109.                     if (!currentPiece.locationRestrict(n,m)) {
  110.                         return false; //if locationRestrict returns false then we must return false here to say that the current board state is invalid
  111.                     }
  112.                    
  113.                     var squaresThreatened = currentPiece.threatens(n,m,direction);
  114.                     for (let b in squaresThreatened) {
  115.                         try {
  116.                             if (direction!==1 && (board.value[squaresThreatened[b].x][squaresThreatened[b].y] === king.id)) {//use direction to make sure only black can threaten white and vice versa
  117.                                 whiteKingThreat = true;
  118.                             }
  119.                             if (direction===1 && (board.value[squaresThreatened[b].x][squaresThreatened[b].y] === -1*king.id)) {//use direction to make sure only black can threaten white and vice versa
  120.                                 blackKingThreat = true;
  121.                             }
  122.                         } catch (err) {
  123.                             if (err.name == "TypeError") {
  124.                                 console.log("piece: "+JSON.stringify(currentPiece));
  125.                                 console.log("threatening: "+JSON.stringify(squaresThreatened[b]));
  126.                             }
  127.                             throw err;
  128.                         }
  129.                     }
  130.                    
  131.                     if (blackKingThreat && whiteKingThreat) {
  132.                         break;
  133.                     }
  134.                 }
  135.             }
  136.             if (blackKingThreat && whiteKingThreat) {
  137.                 break;
  138.             }
  139.         }
  140.         if (!blackKingThreat || !whiteKingThreat) {
  141.             // console.log(board.value+" is valid");//report on individual board states
  142.             return true;
  143.         } else {
  144.             return false;
  145.         }
  146.     },
  147.    
  148.     pieces: [
  149.         chessPieceFactory(
  150.             {
  151.                 name: "pawn",
  152.                 id: 1,
  153.                 threatens: function(x1,y1,direction,boardValue = generatedValue) {//direction is 1 for up and -1 for down, aka, are they black or white pieces, which way are they facing
  154.                     let board = this;
  155.                     if (y+direction < 0 || y+direction>boardValue[0].length) {//if at edge of board.value it threatens no squares
  156.                         return [];
  157.                     }
  158.                     let potential = [
  159.                         {
  160.                             x:x1+1,
  161.                             y:y1+direction
  162.                         },
  163.                         {
  164.                             x:x1-1,
  165.                             y:y1+direction
  166.                         }
  167.                     ];
  168.                     result = [];
  169.                     for (n in potential) {
  170.                         if (!(potential[n].x >= boardValue.length || potential[n].x < 0 || potential[n].y >= boardValue[0].length || potential[n].y < 0)) {
  171.                             result.push(potential[n]);
  172.                         }
  173.                     }
  174.                     return result;
  175.                 },
  176.                 max: parseInt(x),
  177.                 currentWhite: 0,
  178.                 currentBlack: 0,
  179.                 locationRestrict: function(x1,y1,boardValue = generatedValue) {//returns false if co-ordinates aren't allowed
  180.                     if (y1 === 0 || y1 === boardValue[0].length-1) {
  181.                         return false;//blocks first and last row
  182.                     }
  183.                     return true;
  184.                 }
  185.             }
  186.         ),
  187.         chessPieceFactory(
  188.             {
  189.                 name: "king",
  190.                 id: 2,
  191.                 threatens: function(x1,y1,direction,boardValue = generatedValue) {
  192.                     var result = [];
  193.                     for (let i=-1; i<=1; i++) {
  194.                         for (let j=-1; j<=1; j++) {
  195.                             if (i == 0 && j == 0) {
  196.                                 continue; //dont want to mark self as threat
  197.                             }
  198.                            
  199.                             if (x1+i >= boardValue.length || x1+i < 0 || y1+j >= boardValue[0].length || y1+j < 0) {
  200.                                 continue;//if x or y falls outside of board.value then skip it
  201.                             }
  202.                             result.push({x:x1+i,y:y1+j});
  203.                         }
  204.                     }
  205.                     return result;
  206.                 }.bind(this),
  207.                 max: 1,//maximum allowed for each side
  208.                 currentWhite: 1, //current number of this piece on each side
  209.                 currentBlack: 1,
  210.                 locationRestrict: function(x1,y1) {
  211.                     return true;
  212.                 }
  213.             }
  214.         ),
  215.         chessPieceFactory(
  216.             {
  217.                 name: "queen",
  218.                 id: 3,
  219.                 threatens: function(x1,y1,direction,boardValue = generatedValue) {
  220.                     var result = [];
  221.                     result = crosses(x1,y1,result,boardValue);
  222.                     result = diagonals(x1,y1,result,boardValue);
  223.                     return result;
  224.                 },
  225.                 max: 1,
  226.                 currentWhite: 0,
  227.                 currentBlack: 0,
  228.                 locationRestrict: function(x1,y1) {
  229.                     return true;
  230.                 }
  231.             }
  232.         ),
  233.         chessPieceFactory(
  234.             {
  235.                 name: "rook",
  236.                 id: 4,
  237.                 threatens: function(x1,y1,direction,boardValue = generatedValue) {
  238.                     var result = [];
  239.                     result = crosses(x1,y1,result,boardValue);
  240.                     return result;
  241.                 },
  242.                 max: 2,
  243.                 currentWhite: 0,
  244.                 currentBlack: 0,
  245.                 locationRestrict: function(x1,y1) {
  246.                     return true;
  247.                 }
  248.             }
  249.         ),
  250.         chessPieceFactory(
  251.             {
  252.                 name:"bishop",
  253.                 id: 5,
  254.                 threatens: function(x1,y1,direction,boardValue = generatedValue) {
  255.                     var result = [];
  256.                     result = diagonals(x1,y1,result,boardValue);
  257.                     return result;
  258.                 },
  259.                 max: 2,
  260.                 currentWhite: 0,
  261.                 currentBlack: 0,
  262.                 locationRestrict: function(x1,y1) {
  263.                     return true;
  264.                 }
  265.             }
  266.         ),
  267.         chessPieceFactory(
  268.             {
  269.                 name:"knight",
  270.                 id: 6,
  271.                 threatens: function(x1,y1,direction,boardValue = generatedValue) {
  272.                     let potential = [
  273.                         {
  274.                             x:x1+1,
  275.                             y:y1+2
  276.                         },
  277.                         {
  278.                             x:x1-1,
  279.                             y:y1+2
  280.                         },
  281.                         {
  282.                             x:x1+1,
  283.                             y:y1-2
  284.                         },
  285.                         {
  286.                             x:x1-1,
  287.                             y:y1-2
  288.                         },
  289.                         {
  290.                             x:x1+2,
  291.                             y:y1+1
  292.                         },
  293.                         {
  294.                             x:x1+2,
  295.                             y:y1-1
  296.                         },
  297.                         {
  298.                             x:x1-2,
  299.                             y:y1+1
  300.                         },
  301.                         {
  302.                             x:x1-2,
  303.                             y:y1-1
  304.                         }
  305.                     ];
  306.                     result = [];
  307.                     for (n in potential) {
  308.                         if (!(potential[n].x >= boardValue.length || potential[n].x < 0 || potential[n].y >= boardValue[0].length || potential[n].y < 0)) {
  309.                             result.push(potential[n]);
  310.                         }
  311.                     }
  312.                     return result;
  313.                 },
  314.                 max: 2,
  315.                 currentWhite: 0,
  316.                 currentBlack: 0,
  317.                 locationRestrict: function(x1,y1) {
  318.                     return true;
  319.                 }
  320.             }
  321.         )
  322.     ],
  323.     calculateCurrent: function() {//looks at the current piece counts in the board object and finds all possible positions with those pieces
  324.        
  325.         let total = 0;
  326.         let pieces = this.pieces;
  327.         // let board = this; //board is this in the context of this function
  328.        
  329.         var piecesPlaced = (function(){
  330.             for (let n in pieces) {//places all pieces on the this.value (if possible)
  331.                 if(pieces[n].currentBlack !== 0) {
  332.                     for(i=1;i<=pieces[n].currentBlack;i++){
  333.                         var found = false;
  334.                         for (let x=this.value.length-1; (x>=0 && !found); x--) {
  335.                             for (let y=this.value[0].length-1; (y>=0 && !found); y--) {
  336.                                 if (this.value[x][y] == 0) {//found empty space, put piece in here
  337.                                     this.value[x][y] = -1*pieces[n].id;
  338.                                     found = true;
  339.                                 }
  340.                             }
  341.                         }
  342.                         if (!found) {
  343.                             console.log("failed to place pieces1");
  344.                             return false;
  345.                             //failed to place piece
  346.                         }
  347.                     }
  348.                 }
  349.                 if(pieces[n].currentWhite !== 0){
  350.                     for(i=1;i<=pieces[n].currentWhite;i++){
  351.                         var found = false;
  352.                         for (let x=0; (x<this.value.length && !found); x++) {//loop through this.value till empty space is found
  353.                             for (let y=0; (y<this.value[0].length && !found); y++) {
  354.                                 if (this.value[x][y] == 0) {//found empty space, put piece in here
  355.                                     this.value[x][y] = pieces[n].id;
  356.                                     found = true;
  357.                                 }
  358.                             }
  359.                         }
  360.                         if (!found) {
  361.                             console.log("failed to place pieces2");
  362.                             return false;
  363.                             //failed to place piece
  364.                         }
  365.                     }
  366.                 }
  367.             }
  368.             return true;
  369.         }.bind(this))();//bind board as context
  370.        
  371.         if (!piecesPlaced) {
  372.             this.resetValues();
  373.             return 0;//could've returned total here too since total = 0
  374.         }
  375.        
  376.         var boardStates = (function(){
  377.             var flatboard = this.flat();
  378.             var flatPermutations = perms(flatboard);
  379.             return flatPermutations.reduce(function(total,current) {
  380.                 total.push(this.unflat(current));
  381.                 return total;
  382.             }.bind(this),[]);//bind board as context
  383.         }.bind(this))();//bind board as context
  384.        
  385.        
  386.        
  387.         for (n in boardStates) {
  388.             this.value = boardStates[n];
  389.             if (this.isValid()) {
  390.                 total++;
  391.             }
  392.         }
  393.         this.resetValues();
  394.         return total;
  395.        
  396.        
  397.        
  398.         function perms(data) {//perms function kept within scope of calculateCurrent because it isn't used elsewhere
  399.             if (!(data instanceof Array)) {
  400.                 throw new TypeError("input data must be an Array");
  401.             }
  402.            
  403.             var permArr = [], usedChars = [];
  404.  
  405.             function permute(input) {
  406.                 var i, ch;
  407.                 for (i = 0; i < input.length; i++) {
  408.                     if (input.lastIndexOf(input[i]) != i) {//if multiple occourances of the same value in the array, the skip over all but the last one, this removes repetitions
  409.                         continue;
  410.                     }
  411.                     ch = input.splice(i, 1)[0];
  412.                     usedChars.push(ch);
  413.                     if (input.length == 0) {
  414.                         permArr.push(usedChars.slice());
  415.                     }
  416.                     permute(input);
  417.                     input.splice(i, 0, ch);
  418.                     usedChars.pop();
  419.                 }
  420.                 return permArr
  421.             };
  422.            
  423.             return permute(data);
  424.         }
  425.     },
  426.     generatePieceCounts: function() {
  427.         let boardArea = this.boardArea;
  428.         let pieces = this.pieces;
  429.         pieceCounts = pieces.map(function(obj){
  430.             return {id:obj.id,currentBlack: obj.currentBlack, currentWhite: obj.currentWhite, max: obj.max};
  431.         });    
  432.  
  433.        
  434.         //get an input of the different counts of white and black pieces of different types, get an output of all of the different count possibilities
  435.         let possiblePieceCounts = allPossibleCounts(pieceCounts);
  436.        
  437.         //remove impossibilities
  438.         possiblePieceCounts = possiblePieceCounts.filter(function(e) {
  439.             let tally = 0;
  440.             e.forEach(function(row) {
  441.                 tally+=row.currentWhite;
  442.                 tally+=row.currentBlack;
  443.             });
  444.             return tally<=boardArea;
  445.         });
  446.        
  447.         var result = [];//put possiblePieceCounts into valid format
  448.         for (pieceArray of possiblePieceCounts) {//create valid piece objects from our possiblePieceCounts data
  449.             let current = getCopy(this.pieces);
  450.             for (n in current) {
  451.                 if (current[n].id !== pieceArray[n].id) {
  452.                     throw "id mismatch error, something went really wrong";
  453.                 }
  454.                 current[n].currentBlack = pieceArray[n].currentBlack;
  455.                 current[n].currentWhite = pieceArray[n].currentWhite;
  456.             }
  457.             result.push(current);
  458.         }
  459.         console.log(result.length+" possible piece count variaties found");
  460.         return result;
  461.        
  462.         function allPossibleCounts(pieceCounts) {
  463.             var result = [],
  464.                 current = getCopy(pieceCounts);
  465.            
  466.             function recurse(depth) {//every time this function is run depth will be +1 in value, this will togle between using currentWhite or currentBlack, and it will have idx point at the same row twice before moving onto the next one
  467.                 var idx = depth >> 1,
  468.                     prop = depth % 2 ? 'currentWhite' : 'currentBlack',
  469.                     row = pieceCounts[idx];
  470.                 if (!row) {//if currentValue total is the same as boardArea that means the maximum number of pieces that can be placed at any time has been reached, at which point return
  471.                     result.push(getCopy(current));
  472.                     return;
  473.                 }
  474.                
  475.                
  476.                 for (var value = row[prop]; value <= row.max; value++) {
  477.                     current[idx][prop] = value;
  478.                     recurse(depth+1);
  479.                 }
  480.             }
  481.  
  482.             recurse(0);
  483.             return result;
  484.         }
  485.     }
  486. }};
  487.  
  488. const chessPieceFactory = ({
  489.     name,
  490.     id,
  491.     threatens = function(x1,y1, direction) {
  492.         throw "Co-ords being threatened aren't defined for this chessPiece";
  493.     },
  494.     max = 2,
  495.     currentWhite = 0,
  496.     currentBlack = 0,
  497.     locationRestrict = function(x1,y1) {
  498.         return true;
  499.     }
  500. } = {}) => ({
  501.     name,
  502.     id,
  503.     threatens,
  504.     max,
  505.     currentWhite,
  506.     currentBlack,
  507.     locationRestrict
  508. });
  509.  
  510. function diagonals(x1,y1,result,boardValue) {
  511.     for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal up and right
  512.         if (x1+i >= boardValue.length || x1+i < 0 || y1+i >= boardValue[0].length || y1+i < 0) {
  513.             continue;//if x or y falls outside of boardValue then skip it
  514.         }
  515.         result.push({x:x1+i,y:y1+i});
  516.         if (boardValue[x1+i][y1+i] !== 0) {
  517.             break;//found a piece so we can't continue threatening that line
  518.         }
  519.     }
  520.     for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal up and left
  521.         if (x1-i >= boardValue.length || x1-i < 0 || y1+i >= boardValue[0].length || y1+i < 0) {
  522.             continue;//if x or y falls outside of boardValue then skip it
  523.         }
  524.         result.push({x:x1-i,y:y1+i});
  525.         if (boardValue[x1-i][y1+i] !== 0) {
  526.             break;//found a piece so we can't continue threatening that line
  527.         }
  528.     }
  529.     for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal down and left
  530.         if (x1-i >= boardValue.length || x1-i < 0 || y1-i >= boardValue[0].length || y1-i < 0) {
  531.             continue;//if x or y falls outside of boardValue then skip it
  532.         }
  533.         result.push({x:x1-i,y:y1-i});
  534.         if (boardValue[x1-i][y1-i] !== 0) {
  535.             break;//found a piece so we can't continue threatening that line
  536.         }
  537.     }
  538.     for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal down and right
  539.         if (x1+i >= boardValue.length || x1+i < 0 || y1-i >= boardValue[0].length || y1-i < 0) {
  540.             continue;//if x or y falls outside of boardValue then skip it
  541.         }
  542.         result.push({x:x1+i,y:y1-i});
  543.         if (boardValue[x1+i][y1-i] !== 0) {
  544.             break;//found a piece so we can't continue threatening that line
  545.         }
  546.     }
  547.     return result;
  548. }
  549.  
  550. function crosses(x1,y1,result,boardValue) {
  551.     for (let i=x1+1; i<boardValue.length; i++) {//to the right
  552.         result.push({x:i,y:y1});
  553.         if (boardValue[i][y1] !== 0) {
  554.             break;//found a piece so we can't continue threatening that line
  555.         }
  556.     }
  557.     for (let i=x1-1; i>=0; i--) {//to the left
  558.         result.push({x:i,y:y1});
  559.         if (boardValue[i][y1] !== 0) {
  560.             break;//found a piece so we can't continue threatening that line
  561.         }
  562.     }
  563.     for (let i=y1+1; i<boardValue[0].length; i++) {//up
  564.         result.push({x:x1,y:i});
  565.         if (boardValue[x1][i] !== 0) {
  566.             break;//found a piece so we can't continue threatening that line
  567.         }
  568.     }
  569.     for (let i=y1-1; i>=0; i--) {//down
  570.         result.push({x:x1,y:i});
  571.         if (boardValue[x1][i] !== 0) {
  572.             break;//found a piece so we can't continue threatening that line
  573.         }
  574.     }
  575.     return result;
  576. }
  577.  
  578. // function removeDuplicates(arr) {//remove duplicates, no longer needed due to efficiency changes
  579.     // console.log("original length: "+arr.length);
  580.     // for (var i=0; i<arr.length; i++) {
  581.         // var listI = arr[i];
  582.         // loopJ: for (var j=0; j<arr.length; j++) {
  583.             // var listJ = arr[j];
  584.             // if (listI === listJ) continue; //Ignore itself
  585.             // for (var k=listJ.length; k>=0; k--) {
  586.                 // if (JSON.stringify(listJ[k]) !== JSON.stringify(listI[k])) continue loopJ;
  587.             // }// At this point, their values are equal.
  588.            
  589.             // arr.splice(j--, 1);
  590.         // }
  591.     // }
  592.     // console.log("length without duplicates: "+arr.length);
  593.     // return arr;
  594. // }
  595.  
  596. function getCopy(arr) {
  597.     return arr.map ( row => Object.assign({}, row) );
  598. }
  599.  
  600. main();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement