Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- function main() {
- let x=2;//default values
- let y=3;
- try {
- const readline = require('readline');
- const rl = readline.createInterface({
- input: process.stdin,
- output: process.stdout
- });
- rl.question('Input Board x length: ', (answer) => {
- x = answer;
- rl.question('Input Board y length: ', (answer) => {
- y = answer;
- rl.close();
- runMainProcess(x,y);
- });
- });
- }
- catch(err) {
- if(err.message === "require is not defined") {//not running on node so just use default values
- runMainProcess(x,y);
- } else {
- console.err(err);
- }
- }
- function runMainProcess(x,y) {
- let board = chessBoardFactory(x,y);
- let possiblePieceArrays = board.generatePieceCounts();
- let total = 0;
- let i = 0;
- for (pieceArray of possiblePieceArrays) {
- board.pieces = pieceArray;
- total+= board.calculateCurrent();
- console.log("checking validity of possiblePieceArrays, so far "+JSON.stringify(total)+" possible states found.");
- console.log((i++/possiblePieceArrays.length*100).toFixed(2)+"% completed");//progress report
- }
- console.log("Winner winner chicken dinner");
- console.log("For board width "+board.x+" height "+board.y+" the board has "+total+" possible legal states!");
- }
- }
- const chessBoardFactory = (x = 4, y = 4) => {
- let generatedValue = (function(x,y){
- var result = [];
- for (let i=0; i < x; i++) {
- result[i] = [];
- for (let j=0; j < y; j++) {
- result[i][j] = 0;
- }
- }
- return result;
- })(x,y);
- return{
- x,
- y,
- boardArea: x*y,
- value: generatedValue,//created on factory call, value is a 2d array not a function
- resetValues: function() {
- for(n in this.value) {
- for (m in this.value[n]) {
- this.value[n][m] = 0;
- }
- }
- },
- flat: function(){
- return this.value.reduce(function(total, current) {
- return total.concat(current);
- },[]);
- },
- unflat: function(flatboard){//a function to flatten
- return flatboard.reduce(function(total, current, index) {
- total = total||[];
- if (index%y === 0) {
- total.push([current]);//newline (y)
- return total;
- } else {
- total[total.length-1].push(current);//add to current line (x)
- return total;
- }
- },[]);
- },
- isValid: function (){
- let board = this;
- let pieces = this.pieces;
- var king = pieces.filter(function( obj ) {
- return obj.name == "king";
- })[0];
- //check current board.value is valid, if so total++
- whiteKingThreat = false;
- blackKingThreat = false;
- for (let n in board.value) {
- for (let m in board.value[0]) {//we loop through every square on the board.value
- n = parseInt(n);m = parseInt(m);
- if (board.value[n][m] !== 0) {
- direction = (board.value[n][m] > 0) ? 1 : -1;
- var currentPiece = pieces.filter(function( obj ) {
- return obj.id == board.value[n][m]*direction; //make sure id from board.value is positive when matching to pieces id
- });
- currentPiece = currentPiece[0];
- if (!currentPiece.locationRestrict(n,m)) {
- return false; //if locationRestrict returns false then we must return false here to say that the current board state is invalid
- }
- var squaresThreatened = currentPiece.threatens(n,m,direction);
- for (let b in squaresThreatened) {
- try {
- 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
- whiteKingThreat = true;
- }
- 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
- blackKingThreat = true;
- }
- } catch (err) {
- if (err.name == "TypeError") {
- console.log("piece: "+JSON.stringify(currentPiece));
- console.log("threatening: "+JSON.stringify(squaresThreatened[b]));
- }
- throw err;
- }
- }
- if (blackKingThreat && whiteKingThreat) {
- break;
- }
- }
- }
- if (blackKingThreat && whiteKingThreat) {
- break;
- }
- }
- if (!blackKingThreat || !whiteKingThreat) {
- // console.log(board.value+" is valid");//report on individual board states
- return true;
- } else {
- return false;
- }
- },
- pieces: [
- chessPieceFactory(
- {
- name: "pawn",
- id: 1,
- 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
- let board = this;
- if (y+direction < 0 || y+direction>boardValue[0].length) {//if at edge of board.value it threatens no squares
- return [];
- }
- let potential = [
- {
- x:x1+1,
- y:y1+direction
- },
- {
- x:x1-1,
- y:y1+direction
- }
- ];
- result = [];
- for (n in potential) {
- if (!(potential[n].x >= boardValue.length || potential[n].x < 0 || potential[n].y >= boardValue[0].length || potential[n].y < 0)) {
- result.push(potential[n]);
- }
- }
- return result;
- },
- max: parseInt(x),
- currentWhite: 0,
- currentBlack: 0,
- locationRestrict: function(x1,y1,boardValue = generatedValue) {//returns false if co-ordinates aren't allowed
- if (y1 === 0 || y1 === boardValue[0].length-1) {
- return false;//blocks first and last row
- }
- return true;
- }
- }
- ),
- chessPieceFactory(
- {
- name: "king",
- id: 2,
- threatens: function(x1,y1,direction,boardValue = generatedValue) {
- var result = [];
- for (let i=-1; i<=1; i++) {
- for (let j=-1; j<=1; j++) {
- if (i == 0 && j == 0) {
- continue; //dont want to mark self as threat
- }
- if (x1+i >= boardValue.length || x1+i < 0 || y1+j >= boardValue[0].length || y1+j < 0) {
- continue;//if x or y falls outside of board.value then skip it
- }
- result.push({x:x1+i,y:y1+j});
- }
- }
- return result;
- }.bind(this),
- max: 1,//maximum allowed for each side
- currentWhite: 1, //current number of this piece on each side
- currentBlack: 1,
- locationRestrict: function(x1,y1) {
- return true;
- }
- }
- ),
- chessPieceFactory(
- {
- name: "queen",
- id: 3,
- threatens: function(x1,y1,direction,boardValue = generatedValue) {
- var result = [];
- result = crosses(x1,y1,result,boardValue);
- result = diagonals(x1,y1,result,boardValue);
- return result;
- },
- max: 1,
- currentWhite: 0,
- currentBlack: 0,
- locationRestrict: function(x1,y1) {
- return true;
- }
- }
- ),
- chessPieceFactory(
- {
- name: "rook",
- id: 4,
- threatens: function(x1,y1,direction,boardValue = generatedValue) {
- var result = [];
- result = crosses(x1,y1,result,boardValue);
- return result;
- },
- max: 2,
- currentWhite: 0,
- currentBlack: 0,
- locationRestrict: function(x1,y1) {
- return true;
- }
- }
- ),
- chessPieceFactory(
- {
- name:"bishop",
- id: 5,
- threatens: function(x1,y1,direction,boardValue = generatedValue) {
- var result = [];
- result = diagonals(x1,y1,result,boardValue);
- return result;
- },
- max: 2,
- currentWhite: 0,
- currentBlack: 0,
- locationRestrict: function(x1,y1) {
- return true;
- }
- }
- ),
- chessPieceFactory(
- {
- name:"knight",
- id: 6,
- threatens: function(x1,y1,direction,boardValue = generatedValue) {
- let potential = [
- {
- x:x1+1,
- y:y1+2
- },
- {
- x:x1-1,
- y:y1+2
- },
- {
- x:x1+1,
- y:y1-2
- },
- {
- x:x1-1,
- y:y1-2
- },
- {
- x:x1+2,
- y:y1+1
- },
- {
- x:x1+2,
- y:y1-1
- },
- {
- x:x1-2,
- y:y1+1
- },
- {
- x:x1-2,
- y:y1-1
- }
- ];
- result = [];
- for (n in potential) {
- if (!(potential[n].x >= boardValue.length || potential[n].x < 0 || potential[n].y >= boardValue[0].length || potential[n].y < 0)) {
- result.push(potential[n]);
- }
- }
- return result;
- },
- max: 2,
- currentWhite: 0,
- currentBlack: 0,
- locationRestrict: function(x1,y1) {
- return true;
- }
- }
- )
- ],
- calculateCurrent: function() {//looks at the current piece counts in the board object and finds all possible positions with those pieces
- let total = 0;
- let pieces = this.pieces;
- // let board = this; //board is this in the context of this function
- var piecesPlaced = (function(){
- for (let n in pieces) {//places all pieces on the this.value (if possible)
- if(pieces[n].currentBlack !== 0) {
- for(i=1;i<=pieces[n].currentBlack;i++){
- var found = false;
- for (let x=this.value.length-1; (x>=0 && !found); x--) {
- for (let y=this.value[0].length-1; (y>=0 && !found); y--) {
- if (this.value[x][y] == 0) {//found empty space, put piece in here
- this.value[x][y] = -1*pieces[n].id;
- found = true;
- }
- }
- }
- if (!found) {
- console.log("failed to place pieces1");
- return false;
- //failed to place piece
- }
- }
- }
- if(pieces[n].currentWhite !== 0){
- for(i=1;i<=pieces[n].currentWhite;i++){
- var found = false;
- for (let x=0; (x<this.value.length && !found); x++) {//loop through this.value till empty space is found
- for (let y=0; (y<this.value[0].length && !found); y++) {
- if (this.value[x][y] == 0) {//found empty space, put piece in here
- this.value[x][y] = pieces[n].id;
- found = true;
- }
- }
- }
- if (!found) {
- console.log("failed to place pieces2");
- return false;
- //failed to place piece
- }
- }
- }
- }
- return true;
- }.bind(this))();//bind board as context
- if (!piecesPlaced) {
- this.resetValues();
- return 0;//could've returned total here too since total = 0
- }
- var boardStates = (function(){
- var flatboard = this.flat();
- var flatPermutations = perms(flatboard);
- return flatPermutations.reduce(function(total,current) {
- total.push(this.unflat(current));
- return total;
- }.bind(this),[]);//bind board as context
- }.bind(this))();//bind board as context
- for (n in boardStates) {
- this.value = boardStates[n];
- if (this.isValid()) {
- total++;
- }
- }
- this.resetValues();
- return total;
- function perms(data) {//perms function kept within scope of calculateCurrent because it isn't used elsewhere
- if (!(data instanceof Array)) {
- throw new TypeError("input data must be an Array");
- }
- var permArr = [], usedChars = [];
- function permute(input) {
- var i, ch;
- for (i = 0; i < input.length; i++) {
- 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
- continue;
- }
- ch = input.splice(i, 1)[0];
- usedChars.push(ch);
- if (input.length == 0) {
- permArr.push(usedChars.slice());
- }
- permute(input);
- input.splice(i, 0, ch);
- usedChars.pop();
- }
- return permArr
- };
- return permute(data);
- }
- },
- generatePieceCounts: function() {
- let boardArea = this.boardArea;
- let pieces = this.pieces;
- pieceCounts = pieces.map(function(obj){
- return {id:obj.id,currentBlack: obj.currentBlack, currentWhite: obj.currentWhite, max: obj.max};
- });
- //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
- let possiblePieceCounts = allPossibleCounts(pieceCounts);
- //remove impossibilities
- possiblePieceCounts = possiblePieceCounts.filter(function(e) {
- let tally = 0;
- e.forEach(function(row) {
- tally+=row.currentWhite;
- tally+=row.currentBlack;
- });
- return tally<=boardArea;
- });
- var result = [];//put possiblePieceCounts into valid format
- for (pieceArray of possiblePieceCounts) {//create valid piece objects from our possiblePieceCounts data
- let current = getCopy(this.pieces);
- for (n in current) {
- if (current[n].id !== pieceArray[n].id) {
- throw "id mismatch error, something went really wrong";
- }
- current[n].currentBlack = pieceArray[n].currentBlack;
- current[n].currentWhite = pieceArray[n].currentWhite;
- }
- result.push(current);
- }
- console.log(result.length+" possible piece count variaties found");
- return result;
- function allPossibleCounts(pieceCounts) {
- var result = [],
- current = getCopy(pieceCounts);
- 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
- var idx = depth >> 1,
- prop = depth % 2 ? 'currentWhite' : 'currentBlack',
- row = pieceCounts[idx];
- 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
- result.push(getCopy(current));
- return;
- }
- for (var value = row[prop]; value <= row.max; value++) {
- current[idx][prop] = value;
- recurse(depth+1);
- }
- }
- recurse(0);
- return result;
- }
- }
- }};
- const chessPieceFactory = ({
- name,
- id,
- threatens = function(x1,y1, direction) {
- throw "Co-ords being threatened aren't defined for this chessPiece";
- },
- max = 2,
- currentWhite = 0,
- currentBlack = 0,
- locationRestrict = function(x1,y1) {
- return true;
- }
- } = {}) => ({
- name,
- id,
- threatens,
- max,
- currentWhite,
- currentBlack,
- locationRestrict
- });
- function diagonals(x1,y1,result,boardValue) {
- for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal up and right
- if (x1+i >= boardValue.length || x1+i < 0 || y1+i >= boardValue[0].length || y1+i < 0) {
- continue;//if x or y falls outside of boardValue then skip it
- }
- result.push({x:x1+i,y:y1+i});
- if (boardValue[x1+i][y1+i] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal up and left
- if (x1-i >= boardValue.length || x1-i < 0 || y1+i >= boardValue[0].length || y1+i < 0) {
- continue;//if x or y falls outside of boardValue then skip it
- }
- result.push({x:x1-i,y:y1+i});
- if (boardValue[x1-i][y1+i] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal down and left
- if (x1-i >= boardValue.length || x1-i < 0 || y1-i >= boardValue[0].length || y1-i < 0) {
- continue;//if x or y falls outside of boardValue then skip it
- }
- result.push({x:x1-i,y:y1-i});
- if (boardValue[x1-i][y1-i] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- for (let i=1; i<((boardValue.length > boardValue[0].length)?boardValue.length:boardValue[0].length); i++) {//diagonal down and right
- if (x1+i >= boardValue.length || x1+i < 0 || y1-i >= boardValue[0].length || y1-i < 0) {
- continue;//if x or y falls outside of boardValue then skip it
- }
- result.push({x:x1+i,y:y1-i});
- if (boardValue[x1+i][y1-i] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- return result;
- }
- function crosses(x1,y1,result,boardValue) {
- for (let i=x1+1; i<boardValue.length; i++) {//to the right
- result.push({x:i,y:y1});
- if (boardValue[i][y1] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- for (let i=x1-1; i>=0; i--) {//to the left
- result.push({x:i,y:y1});
- if (boardValue[i][y1] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- for (let i=y1+1; i<boardValue[0].length; i++) {//up
- result.push({x:x1,y:i});
- if (boardValue[x1][i] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- for (let i=y1-1; i>=0; i--) {//down
- result.push({x:x1,y:i});
- if (boardValue[x1][i] !== 0) {
- break;//found a piece so we can't continue threatening that line
- }
- }
- return result;
- }
- // function removeDuplicates(arr) {//remove duplicates, no longer needed due to efficiency changes
- // console.log("original length: "+arr.length);
- // for (var i=0; i<arr.length; i++) {
- // var listI = arr[i];
- // loopJ: for (var j=0; j<arr.length; j++) {
- // var listJ = arr[j];
- // if (listI === listJ) continue; //Ignore itself
- // for (var k=listJ.length; k>=0; k--) {
- // if (JSON.stringify(listJ[k]) !== JSON.stringify(listI[k])) continue loopJ;
- // }// At this point, their values are equal.
- // arr.splice(j--, 1);
- // }
- // }
- // console.log("length without duplicates: "+arr.length);
- // return arr;
- // }
- function getCopy(arr) {
- return arr.map ( row => Object.assign({}, row) );
- }
- main();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement