Othello valid move algorithm doesn't work javascript - javascript

I'm trying to code an Othello game, and now I'm trying to code the algorithm that does return the valid positions. I started by retrieving the directions of the empty squares where there's a black or white stone right next to it, but my code doesn't run. I don't know if I did something wrong but it seems that when I launch the program, the code runs slow because the algorithm is overkill.
What can I do for that?
Here's my Code(By the way, I use React.js):
Here's the data structure of the square array:
history: [{
squares: [
Array(8).fill(null),
Array(8).fill(null),
Array(8).fill(null),
[null,null,null,'black','white',null,null,null],
[null,null,null,'white','black',null,null,null],
Array(8).fill(null),
Array(8).fill(null),
Array(8).fill(null)
]
}],
The Algorithm(for now, just returns the squares that have a stone right next to it(up, up & right , down, left, etc.)):
checkMoveValidity(isBlack) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice()
// Get all the empty spaces
const emptySpaces = []
for (var row=1;row<squares.length-1;row++) {
for (var col=1;col<squares[row].length-1;col++) {
if (!squares[row][col]) {
emptySpaces.push([row,col])
}
}
}
const cordAndDirs = []
for (var sp=0;sp<emptySpaces.length;sp++) {
const element = emptySpaces[sp]
const elObject = {
coordinates: element,
directions: []
}
if (element === [0,0])
{
// Check down
if (squares[1][0]) {
const downEls = []
for (var i=1;i<squares.length;i++) {
downEls.push([i,0])
}
elObject.directions.push(downEls)
}
// Check down right (diagonal)
if (squares[1][1]) {
const drEls = []
for (var i=1;i<squares.length;i++) {
drEls.push([i,i])
}
elObject.directions.push(drEls)
}
// Check right
if (squares[0][1]) {
const rightEls = []
for (var i=1;i<squares.length;i++) {
rightEls.push([0,i])
}
elObject.directions.push(rightEls)
}
}
else if (emptySpaces[sp] === [0,7])
{
// Check down
if (squares[1][7]) {
const downEls = []
for (var i=1;i<squares.length;i++) {
downEls.push([i,7])
}
elObject.directions.push(downEls)
}
// Check down left (diagonal)
if (squares[1][6]) {
const drEls = []
for (var i=1;i<squares.length;i++) {
drEls.push([i,7 - i])
}
elObject.directions.push(drEls)
}
// Check left
if (squares[0][6]) {
const leftEls = []
for (var i=1;i<squares.length;i++) {
leftEls.push([0,7 - i])
}
elObject.directions.push(leftEls)
}
}
else if (emptySpaces[sp] === [7,0])
{
// Check up
if (squares[6][0]) {
const upEls = []
for (var i=1;i<squares.length;i++) {
upEls.push([7 - i,0])
}
elObject.directions.push(upEls)
}
// Check up right
if (squares[6][1]) {
const urEls = []
for (var i=1;i<squares.length;i++) {
urEls.push([7 - i,i])
}
elObject.directions.push(urEls)
}
// Check right
if (squares[7][1]) {
const rightEls = []
for (var i=1;i<squares.length;i++) {
rightEls.push([7,i - 1])
}
elObject.directions.push(rightEls)
}
}
else if (emptySpaces[sp] === [7,7])
{
//Check up
if (squares[6][7]) {
const upEls = []
for (var i=1;i<squares.length;i++) {
upEls.push([7 - i,7])
}
elObject.directions.push(upEls)
}
// Check up left
if (squares[6][6]) {
const ulEls = []
for (var i=1;i<squares.length;i++) {
ulEls.push([7 - i,7 - i])
}
elObject.directions.push(ulEls)
}
// Check left
if (squares[7][6]) {
const leftEls = []
for (var i=1;i<squares.length;i++) {
leftEls.push([7,7 - i])
}
elObject.directions.push(leftEls)
}
}
else
{
const crdsY = emptySpaces[sp][0]
const crdsX = emptySpaces[sp][1]
//Check up
if (squares[crdsY - 1][crdsX]) {
const upEls = []
var i = 1
while (crdsY - i >= 0) {
upEls.push([crdsY - i,crdsX])
i++;
}
elObject.directions.push(upEls)
}
// Check up right
if (squares[crdsY - 1][crdsX + 1]) {
const urEls = []
var i = 1
while (crdsY - i >= 0 && crdsX + i <= 7) {
urEls.push([crdsY - i,crdsX + i])
i++;
}
elObject.directions.push(urEls)
}
// Check right
if (squares[crdsY][crdsX + 1]) {
const rightEls = []
var i = 1
while (crdsX + i <= 7) {
rightEls.push([crdsY,crdsX + i])
i++;
}
elObject.directions.push(rightEls)
}
// Check down right
if (squares[crdsY + 1][crdsX + 1]) {
const rightEls = []
var i = 1
while (crdsY + i <= 7 && crdsX + i <= 7) {
rightEls.push([crdsY + i,crdsX + i])
i++;
}
elObject.directions.push(rightEls)
}
// Check down
if (squares[crdsY + 1][crdsX]) {
const rightEls = []
var i = 1
while (crdsY + i <= 7) {
rightEls.push([crdsY + i,crdsX])
i++;
}
elObject.directions.push(rightEls)
}
// Check down left
if (squares[crdsY + 1][crdsX - 1]) {
const rightEls = []
var i = 1
while (crdsY + i <= 7 && crdsX - i >= 0) {
rightEls.push([crdsY + i,crdsX - i])
i++;
}
elObject.directions.push(rightEls)
}
// Check left
if (squares[crdsY][crdsX - 1]) {
const rightEls = []
var i = 1
while (crdsX - i >= 0) {
rightEls.push([crdsY,crdsX - i])
i++;
}
elObject.directions.push(rightEls)
}
// Check up left
if (squares[crdsY - 1][crdsX - 1]) {
const rightEls = []
var i = 1
while (crdsY - i >= 0 && crdsX - i >= 0) {
rightEls.push([crdsY - i,crdsX - i])
}
elObject.directions.push(rightEls)
}
}
if (elObject.directions.length === 0) {
continue
} else {
cordAndDirs.push(elObject)
}
}
return cordAndDirs
}
And this is where I'm trying to implement it:
render() {
const history = this.state.history
const current = history[this.state.stepNumber]
// Return the game scene here
return (
<div className="master-container">
<GameBoard squares={current.squares} onClick={(row,col) => {
const elems = this.checkMoveValidity(this.state.blackIsNext)
console.log(elems)
}}/>
</div>
)
}
By the way, I checked whether the command has reached to the elements inside the GameBoard component. It does not have any problem, the problem occurred when I implemented the algorithm.

I did it. It was quite simple though, I even simplified the code a bit. I forgot to add i++; to the last while loop, I was stuck forever, and then I had to handle the borders so that I don't get an error. For example, I can't //Check up when the upper border squares have no squares beneath them.
This is how I changed the conditional statements:
for (var sp=0;sp<emptySpaces.length;sp++) {
const element = emptySpaces[sp]
const elObject = {
coordinates: element,
directions: []
}
// You apply the same methods for all the other squares here (can't use constant numbers in 'squares' now...)
const crdsY = emptySpaces[sp][0]
const crdsX = emptySpaces[sp][1]
var i;
//Check up
if (crdsY !== 0 && squares[crdsY - 1][crdsX]) {
const upEls = []
i = 1
while (crdsY - i >= 0) {
upEls.push([crdsY - i,crdsX])
i++;
}
elObject.directions.push(upEls)
}
// Check up right
if (crdsX !== 7 && crdsY !== 0 && squares[crdsY - 1][crdsX + 1]) {
const urEls = []
i = 1
while (crdsY - i >= 0 && crdsX + i <= 7) {
urEls.push([crdsY - i,crdsX + i])
i++;
}
elObject.directions.push(urEls)
}
// Check right
if (crdsX !== 7 && squares[crdsY][crdsX + 1]) {
const rightEls = []
i = 1
while (crdsX + i <= 7) {
rightEls.push([crdsY,crdsX + i])
i++;
}
elObject.directions.push(rightEls)
}
// Check down right
if (crdsX !== 7 && crdsY !== 7 && squares[crdsY + 1][crdsX + 1]) {
const rightEls = []
i = 1
while (crdsY + i <= 7 && crdsX + i <= 7) {
rightEls.push([crdsY + i,crdsX + i])
i++;
}
elObject.directions.push(rightEls)
}
// Check down
if (crdsY !== 7 && squares[crdsY + 1][crdsX]) {
const rightEls = []
i = 1
while (crdsY + i <= 7) {
rightEls.push([crdsY + i,crdsX])
i++;
}
elObject.directions.push(rightEls)
}
// Check down left
if (crdsY !== 7 && crdsX !== 0 && squares[crdsY + 1][crdsX - 1]) {
const rightEls = []
i = 1
while (crdsY + i <= 7 && crdsX - i >= 0) {
rightEls.push([crdsY + i,crdsX - i])
i++;
}
elObject.directions.push(rightEls)
}
// Check left
if (crdsX !== 0 && squares[crdsY][crdsX - 1]) {
const rightEls = []
i = 1
while (crdsX - i >= 0) {
rightEls.push([crdsY,crdsX - i])
i++;
}
elObject.directions.push(rightEls)
}
// Check up left
if (crdsX !== 0 && crdsY !== 0 && squares[crdsY - 1][crdsX - 1]) {
const rightEls = []
i = 1
while (crdsY - i >= 0 && crdsX - i >= 0) {
rightEls.push([crdsY - i,crdsX - i])
i++;
}
elObject.directions.push(rightEls)
}
if (elObject.directions.length !== 0) {
cordAndDirs.push(elObject)
}
And also I changed the the game board onClick property a bit like this:
return (
<div className="master-container">
<GameBoard squares={current.squares} onClick={(row,col) => {
const elems = this.checkElementsAround(this.checkMoveValidity(this.state.blackIsNext))
console.log(elems)
for (let el=0;el<elems.length;el++) {
if (row === elems[el].coordinates[0] && col === elems[el].coordinates[1]) {
this.handleMove(row,col);
break
}
}
}}/>
</div>
)

Related

a slide to use on the mobile web

The problem here is that when you reach the last item by doing a touch move,
If you touch move with the previous item, it doesn't work.
function slideCustom () {
const listWrap = document.querySelector('.list-item-wrap');
const listItem = document.querySelector('.list-item-wrap .list-item');
const listItems = document.querySelectorAll('.list-item-wrap .list-item');
const listItemPrev = document.querySelector('.m-slide-list-wrap .list-prev-btn');
const listItemNext = document.querySelector('.m-slide-list-wrap .list-next-btn');
const listItemCount = listItems.length;
const listItemWidth = listItem.offsetWidth;
let currentIndex = 0;
let touchStartX = null;
listWrap.style.width = listItemWidth * listItemCount + 'px';
function moveSlide(num) {
listWrap.style.left = -(num * ( listItemWidth + 10 )) + 'px';
currentIndex = num;
}
listItems.forEach((item, index) => {
item.addEventListener('click', function() {
moveSlide(index);
listItems.forEach(i => i.classList.remove('active'));
this.classList.add('active');
});
});
moveSlide(currentIndex);
listItemPrev.addEventListener('click', () => {
if (currentIndex > 0) {
moveSlide(currentIndex - 1);
listItems[currentIndex + 1].classList.remove("active");
listItems[currentIndex].classList.add("active");
}
});
listItemNext.addEventListener('click', () => {
if (currentIndex < listItemCount - 1) {
moveSlide(currentIndex + 1);
listItems[currentIndex - 1].classList.remove("active");
listItems[currentIndex].classList.add("active");
}
});
if (listItemCount < 2) {
listItemPrev.remove();
listItemNext.remove();
}
listWrap.addEventListener('touchstart', (event) => {
touchStartX = event.touches[0].clientX;
});
listWrap.addEventListener('touchmove', (event) => {
let touchMoveX = event.touches[0].clientX;
let touchDiffX = touchMoveX - touchStartX;
if (touchStartX !== null) {
if (touchDiffX > 0 && currentIndex > 0) {
moveSlide(currentIndex - 1);
listItems[currentIndex + 1].classList.remove("active");
listItems[currentIndex].classList.add("active");
} else if (touchDiffX < 0 && currentIndex < listItemCount - 1) {
moveSlide(currentIndex + 1);
listItems[currentIndex - 1].classList.remove("active");
listItems[currentIndex].classList.add("active");
} else if (touchDiffX < 0 && currentIndex === listItemCount - 1) {
moveSlide(currentIndex);
} else if (touchDiffX > 0 && currentIndex === 0) {
moveSlide(currentIndex);
}
touchStartX = null;
}
});
}
For example, assuming that there are 7 items,
When you touch move from right to left and reach the last 7th item,
I want to touch move the 7th item from left to right and move to the previous 6th item.

TypeError: maze1.display is not a function

I have the mazeGen class that generates a maze using a version of prims algorithm using JS and P5.JS. Within the mazeGen class i have a display funtion that logs the maze to console. When i run this line it says it is not defined as a funtion. THe class will be used in another progect to generate multiple mazes to need it as a class
let maze1
function setup() {
maze1 = new MazeGen(11);
maze1.display()
}
function draw() {
background(220);
}
class MazeGen {
constructor(size) {
console.warn('in funt')
this.size = size
this.maze = new Array(this.size);
for (let i = 0; i < this.maze.length; i++) {
this.maze[i] = new Array(this.size);
for (let j = 0; j < this.maze.length; j++) {
this.maze[i][j] = 0
}
}
this.maze = this.PrimsAlgorithm()
this.maze[1][0] = 3;
this.maze[this.maze.length - 2][this.maze.length - 1] = 2;
console.table(this.maze);
return this.maze;
}
PrimsAlgorithm() {
this.frontAvailable = [0, 0, 0, 0];
this.currentCell = [1, 1];
while (this.Complete(this.maze, this.size) === false) {
//Console.WriteLine("Maze is not ready");
this.maze[this.currentCell[0]][this.currentCell[1]] = 1;
this.frontAvailable = this.Frontier(this.maze, this.currentCell);
//While the list of frontier cells is not empty
while (this.frontAvailable[0] !== 0 || this.frontAvailable[1] !== 0 || this.frontAvailable[2] !== 0 || this.frontAvailable[3] !== 0) {
//pick a random way
this.picked = false;
this.numSelected = 5;
while (this.picked === false) {
this.numSelected = Math.floor(Math.random() * 5);
if (this.frontAvailable[this.numSelected] === 1) {
this.picked = true;
}
}
//'Move to cell'
this.maze = this.MoveSquare();
this.frontAvailable = this.Frontier();
//Maze.PrintWhole(maze);
}
//List of frontier Cells is now empty
//Move to random cell and check if it is a path
this.currentCell = this.NewCurrent();
}
return this.maze;
}
Frontier() {
this.available = [0, 0, 0, 0];
//left check
if (((this.currentCell[1]) - 2) >= 0 && this.maze[this.currentCell[0]][(this.currentCell[1]) - 2] === 0) {
this.available[0] = 1;
} else {
this.available[0] = 0;
}
//up check
if (((this.currentCell[0]) - 2) >= 0 && this.maze[(this.currentCell[0]) - 2][(this.currentCell[1])] === 0) {
this.available[1] = 1;
} else {
this.available[1] = 0;
}
//right check
if (this.currentCell[1] + 2 < this.maze.length) {
if (this.maze[this.currentCell[0]][(this.currentCell[1]) + 2] === 0) {
this.available[2] = 1;
}
} else {
this.available[2] = 0;
}
//down check
if (this.currentCell[0] + 2 < this.maze.length) {
if (this.maze[this.currentCell[0] + 2][this.currentCell[1]] === 0) {
this.available[3] = 1;
}
} else {
this.available[3] = 0;
}
return this.available;
}
NewCurrent() {
this.found = false
this.currentCell = [];
while (this.found === false) {
this.cellX = Math.floor(Math.random() * (this.maze.length - 3) / 2)
this.cellX = this.cellX * 2 + 1
this.cellY = Math.floor(Math.random() * (this.maze.length - 3) / 2)
this.cellY = this.cellY * 2 + 1
if (this.maze[this.cellX][this.cellY] === 1) {
this.currentCell[0] = this.cellX;
this.currentCell[1] = this.cellY;
this.found = true
}
}
return this.currentCell;
}
MoveSquare() {
if (this.numSelected === 0) {
this.maze[this.currentCell[0]][(this.currentCell[1]) - 2] = 1;
this.maze[this.currentCell[0]][(this.currentCell[1]) - 1] = 1;
this.currentCell[1] = this.currentCell[1] - 2;
}
if (this.numSelected === 1) {
this.maze[(this.currentCell[0]) - 2][(this.currentCell[1])] = 1;
this.maze[(this.currentCell[0]) - 1][(this.currentCell[1])] = 1;
this.currentCell[0] = this.currentCell[0] - 2;
}
if (this.numSelected === 2) {
this.maze[this.currentCell[0]][(this.currentCell[1]) + 2] = 1;
this.maze[this.currentCell[0]][(this.currentCell[1]) + 1] = 1;
this.currentCell[1] = this.currentCell[1] + 2;
}
if (this.numSelected === 3) {
this.maze[(this.currentCell[0]) + 2][(this.currentCell[1])] = 1;
this.maze[(this.currentCell[0]) + 1][(this.currentCell[1])] = 1;
this.currentCell[0] = this.currentCell[0] + 2;
}
return this.maze;
}
Complete() {
let counter = 0;
//Console.WriteLine(counter);
for (let i = 0; i < (this.size - 1) / 2; i++) {
for (let j = 0; j < (this.size - 1) / 2; j++) {
let X = (2 * i) + 1;
let Y = (2 * j) + 1;
if (this.maze[X][Y] === 1) {
counter++;
}
}
}
return counter === (this.size - 1) / 2 * (this.size - 1) / 2;
}
display(){
console.table(this.maze);
}
}
If you return this.maze on constructor it will be an Array, Arrays don't have a display funcion
let maze1;
function setup() {
maze1 = new MazeGen(11);
maze1.display();
}
class MazeGen {
constructor(size) {
this.size = size;
this.maze = new Array(this.size).fill(1);
return this;//This is not really need it
}
display() {
console.log("In Display function");
console.log(this.maze);
}
}
setup();
The constructor property returns a reference to the Object constructor function that created the instance object. Note that the value of this property is a reference to the function itself, not a string containing the function's name.
More examples here : What does a JavaScript constructor return?
Are you trying to show the maze, or get what type of display it is on a webpage?
P5.js does not have such a method called .display(). However, there is a method called .show(), which makes the element visible. In normal web JavaScript, if you want to get or change the display type, you need to use the .style.display property.

recursive function returns the same value each time

So I have a checkers game, and I am trying to get it so that you can jump over multiple spaces. It works if you jump the max amount of spaces, but if you don't, say you can jump over two spaces, and you choose to jump over only one, it still removes both pieces even though you only jumped over one of the pieces. I think the problem has is inside the checkForJump() function, and it seems like every time the function is called, it returns the same array, any help would be appreciated.
function checkForJump(buttonSelected, remove, isRoot) {
if(isRoot)
{
clearAvailableMoves();
switchPiece();
}
let adjacentValue = 0;
let jumpNotValid = false;
let RemoveTiles = remove;
for (let i = 0; i < adjacentTileValues.length; i++) {
adjacentValue = adjacentTileValues[i];
if (board.value[adjacentValue + buttonSelected] == enemyPiece && board.value[buttonSelected + (adjacentValue * 2)] == empty) {
if (isSpaceAlreadyInArray(buttonSelected + adjacentValue * 2, i) == false) {
RemoveTiles.push(buttonSelected + adjacentValue);
let tile = new jumpTile(buttonSelected + (adjacentValue * 2));
RemoveTiles.push(buttonSelected + adjacentValue);
console.log("removeTiles" + RemoveTiles);
for (let k = 0; k < RemoveTiles.length; k++) {
tile.tilesToRemove.push(RemoveTiles[k]);
}
console.log("tile.tilesToRemove: " + tile.tilesToRemove);
availableSpaces[i].push(tile);
checkForJump(buttonSelected + (adjacentValue * 2), RemoveTiles,false);
console.log("available spaces = " + availableSpaces);
}
}
}
};
Here is some of the other code, relating to that could also be the source of the problem
function jumpTile(tileID) {
this.tilesToRemove = [];
this.tileID = tileID;
};
let numBlackPieces = 12;
let numWhitePieces = 12;
let adjacentTileValues = [7, 9, -7, -9];
let availableSpaces = [
[],
[],
[],
[]
];
function checkValidSpace(buttonPressed, piece) {
// if button is adjacent to selectedbutton
if (buttonPressed == selectedButton + 7 || buttonPressed == selectedButton + 9 || buttonPressed == selectedButton - 9 || buttonPressed == selectedButton - 7) {
return true;
} else {
let valid = false;
// checks if there is a valid jump
checkForJump(selectedButton,[],true);
// foreach of the possible tiles, if the button pressed is one of them than remove all pieces in that tiles remove list
for (let i = 0; i < availableSpaces.length; i++) {
for (let j = 0; j < availableSpaces[i].length; j++) {
if (buttonPressed == availableSpaces[i][j].tileID) {
valid = true;
console.log("availableSpaces[" + i + j + "] is " + availableSpaces[i][j].tileID + "");
remove(availableSpaces[i][j].tilesToRemove);
return true;
}
}
}
if (valid == false)
return false;
}
}
function checkForJump(buttonSelected, remove, isRoot) {
if(isRoot)
{
clearAvailableMoves();
switchPiece();
}
let adjacentValue = 0;
let jumpNotValid = false;
let RemoveTiles = remove;
for (let i = 0; i < adjacentTileValues.length; i++) {
adjacentValue = adjacentTileValues[i];
if (board.value[adjacentValue + buttonSelected] == enemyPiece && board.value[buttonSelected + (adjacentValue * 2)] == empty) {
if (isSpaceAlreadyInArray(buttonSelected + adjacentValue * 2, i) == false) {
RemoveTiles.push(buttonSelected + adjacentValue);
let tile = new jumpTile(buttonSelected + (adjacentValue * 2));
RemoveTiles.push(buttonSelected + adjacentValue);
console.log("removeTiles" + RemoveTiles);
for (let k = 0; k < RemoveTiles.length; k++) {
tile.tilesToRemove.push(RemoveTiles[k]);
}
console.log("tile.tilesToRemove: " + tile.tilesToRemove);
availableSpaces[i].push(tile);
checkForJump(buttonSelected + (adjacentValue * 2), RemoveTiles,false);
console.log("available spaces = " + availableSpaces);
}
}
}
};
function isSpaceAlreadyInArray(spot, arrayIndex) {
let SpaceAlreadyInArray = false;
for (let j = 0; j < availableSpaces[arrayIndex].length; j++) {
if (availableSpaces[arrayIndex][j].tileID == spot) {
SpaceAlreadyInArray = true;
}
}
return SpaceAlreadyInArray;
}
function clearAvailableMoves() {
for (let i = 0; i < availableSpaces.length; i++) {
availableSpaces[i].pop();
}
}
function remove(removeList, button) {
for (let i = 0; i < removeList.length; i++) {
board.value[removeList[i]] = empty;
board.buttons[removeList[i]].textContent = empty;
if (piece == black) {
numBlackPieces--;
checkForWin(numBlackPieces);
} else if (piece == white) {
numWhitePieces--;
checkForWin(numWhitePieces);
}
}
clearAvailableMoves();
};

How to add the operators * and / in this calculation algorithm

const calculate = (s) => {
let code;
let index;
let startIndex;
let stackSign = [];
let result = [0];
let numberSign = 1;
for (var i = 0; i < s.length; i++) {
if (s[i] === ' ') {
continue;
} else if (s[i] === '(') {
stackSign.push(numberSign);
result.push(0);
numberSign = 1;
} else if (s[i] === ')') {
result[result.length - 2] += result.pop() * stackSign.pop();
} else if (s[i] === '+') {
numberSign = 1;
} else if (s[i] === '-') {
numberSign = -1;
} else if (s[i] === '/') {
numberSign = result / 1;
} else if (s[i] === '*') {
numberSign = result * 1;
} else {
startIndex = i;
while (
(index = i + 1) < s.length &&
(code = s[index].charCodeAt()) > 47 &&
code < 58
) {
i++;
}
result[result.length - 1] +=
+s.substring(startIndex, i + 1) * numberSign;
}
}
return result.pop();
};
Hello guys i made this algorithm to create a calculator without javascript "eval" but im struggling to add the * and / operators, can you guys help me with that? thanks
How i'm using it:
calculate('50 + 50');

Javascript Minimax Function Exceedingly Slow

After initiating the tic-tac-toe AI move (it's currently written only to assess if miniMax can successfully run), it takes about 1 - 1.5 minutes to complete in firefox or chrome. Other tic-tac-toe game's minimax functions run on my computer in a fraction of the time.
Logging output has not led closer to the solution. Have verified that all terminal states return a score. Is there an obvious error here?
Codepen
$(document).ready(function() {
var state = {
status: "inProgress",
turn: "human", //can be human or AI
game: 1,
turnSwitch: function() {
if (this.turn == "human") {
return (this.turn = "AI");
} else if (this.turn == "AI") {
return (this.turn = "human");
}
}
};
//AI will use minimx algorithm to determine best possible move
function player(name, symbol) {
this.name = name;
this.symbol = symbol;
}
var human = new player("human", "X");
var AI = new player("AI", "O");
var board = {
board: [],
createBoard: function() {
for (let i = 0; i < 9; i++) {
this.board.push(i);
}
return this.board;
},
resetBoard: function() {
this.board = [];
createBoard();
},
//checks board for terminal state
terminalTest: function() {
for (let i = 0; i < 7; i += 3) {
if (
this.board[i] == this.board[i + 1] &&
this.board[i] == this.board[i + 2]
) {
if (typeof this.board[i] !== "number") {
return this.board[i];
}
}
}
for (let j = 0; j < 3; j++) {
if (
this.board[j] == this.board[j + 3] &&
this.board[j] == this.board[j + 6]
) {
if (typeof this.board[j] !== "number") {
return this.board[j];
}
}
}
if (
(this.board[0] == this.board[4] && this.board[0] == this.board[8]) ||
(this.board[2] == this.board[4] && this.board[2] == this.board[6])
) {
if (typeof this.board[4] !== "number") {
return this.board[4];
}
}
},
//checks for possible moves
findEmptyIndicies: function() {
return this.board.filter(a => typeof a == "number");
}
};
function miniMax(boardCurrent, player) {
//store all possible moves
var availableMoves = boardCurrent.findEmptyIndicies();
console.log("available moves length " + availableMoves.length);
//if terminal state, return score
var terminalCheck = boardCurrent.terminalTest();
if (terminalCheck === "X") {
//console.log(boardCurrent.board + " X wins score: -10")
return human.symbol == "X" ? { score: -10 } : { score: 10 };
} else if (terminalCheck === "O") {
//console.log(boardCurrent.board + " ) wins score: 10")
return human.symbol == "X" ? { score: 10 } : { score: -10 };
} else if (availableMoves.length === 0) {
console.log("availmoves length 0 " + availableMoves.length);
//console.log(boardCurrent.board + " score: 0")
return { score: 0 };
}
//array to store objects containing index and score through each iteration of available moves
var movesArray = [];
for (var i = 0; i < availableMoves.length; i++) {
//create object to store return score from each index branch
var move = {};
move.index = boardCurrent.board[availableMoves[i]];
boardCurrent.board[availableMoves[i]] = player.symbol;
//returned score from leaf node if conditionials at top of function are met
if (player.name == "AI") {
var miniMaxReturn = miniMax(boardCurrent, human);
move.score = miniMaxReturn.score;
console.log("move score " + move.score);
} else {
var miniMaxReturn = miniMax(boardCurrent, AI);
move.score = miniMaxReturn.score;
console.log("move score " + move.score);
}
boardCurrent.board[availableMoves[i]] = move.index;
movesArray.push(move);
}
var bestMove;
console.log("moves array length " + movesArray.length);
if (player.name === "AI") {
let bestScore = -1000;
for (let i = 0; i < movesArray.length; i++) {
if (movesArray[i].score > bestScore) {
bestScore = movesArray[i].score;
bestMove = i;
}
}
} else if (player.name === "human") {
let bestScore = 1000;
for (let i = 0; i < movesArray.length; i++) {
if (movesArray[i].score < bestScore) {
bestScore = movesArray[i].score;
bestMove = i;
}
}
}
return movesArray[bestMove];
}
/**function AIMove() {
console.log("ai move initiated");
var AIPosition = miniMax(board, AI);
//var index = AIposition.index;
//console.log(AIposition);
//$("#" + index).html(AI.symbol);
}**/
$(".cell").click(displayMove);
function displayMove() {
if (state.turn == "human") $(this).html(human.symbol);
board.board[this.id] = human.symbol;
state.turn = "AI";
miniMax(board, AI);
//AIMove();
console.log("AI move complete");
}
$(".cell").click(displayMove);
//on window load create board
board.createBoard();
});

Categories