I took this code from the internet and want to modify it so that regardless of which symbol I select, the x starts first (the computer in this case).
If I select to play with X, I will start first and the computer will play with O.
If I select to play with O, the computer will receive X and make the first move.
const selectBox = document.querySelector(".select-box"),
selectBtnX = selectBox.querySelector(".options .playerX"),
selectBtnO = selectBox.querySelector(".options .playerO"),
playBoard = document.querySelector(".play-board"),
players = document.querySelector(".players"),
allBox = document.querySelectorAll("section span"),
resultBox = document.querySelector(".result-box"),
wonText = resultBox.querySelector(".won-text"),
replayBtn = resultBox.querySelector("button");
let x = 'x';
let O = 'O';
window.onload = () => {
for (let i = 0; i < allBox.length; i++) {
allBox[i].setAttribute("onclick", "clickedBox(this)");
}
}
selectBtnX.onclick = () => {
selectBox.classList.add("hide");
playBoard.classList.add("show");
}
selectBtnO.onclick = () => {
selectBox.classList.add("hide");
playBoard.classList.add("show");
players.setAttribute("class", "players active player");
}
let playerXIcon = "fas fa-times",
playerOIcon = "far fa-circle",
playerSign = "X",
runBot = true;
function clickedBox(element) {
if (players.classList.contains("player")) {
playerSign = "O";
element.innerHTML = `<i class="${playerOIcon}"></i>`;
players.classList.remove("active");
element.setAttribute("id", playerSign);
} else {
element.innerHTML = `<i class="${playerXIcon}"></i>`;
element.setAttribute("id", playerSign);
players.classList.add("active");
}
selectWinner();
element.style.pointerEvents = "none";
playBoard.style.pointerEvents = "none";
let randomTimeDelay = ((Math.random() * 1000) + 200).toFixed();
setTimeout(() => {
bot(runBot);
}, randomTimeDelay);
}
function bot() {
let array = [];
if (runBot) {
playerSign = "O";
for (let i = 0; i < allBox.length; i++) {
if (allBox[i].childElementCount == 0) {
array.push(i);
}
}
let randomBox = array[Math.floor(Math.random() * array.length)];
if (array.length > 0) {
if (players.classList.contains("player")) {
playerSign = "X";
allBox[randomBox].innerHTML = `<i class="${playerXIcon}"></i>`;
allBox[randomBox].setAttribute("id", playerSign);
players.classList.add("active");
} else {
allBox[randomBox].innerHTML = `<i class="${playerOIcon}"></i>`;
players.classList.remove("active");
allBox[randomBox].setAttribute("id", playerSign);
}
selectWinner();
}
allBox[randomBox].style.pointerEvents = "none";
playBoard.style.pointerEvents = "auto";
playerSign = "X";
}
}
function getIdVal(classname) {
return document.querySelector(".box" + classname).id;
}
function checkIdSign(val1, val2, val3, sign) {
if (getIdVal(val1) == sign && getIdVal(val2) == sign && getIdVal(val3) == sign) {
return true;
}
}
function selectWinner() {
if (checkIdSign(1, 2, 3, playerSign) || checkIdSign(4, 5, 6, playerSign) || checkIdSign(7, 8, 9, playerSign) || checkIdSign(1, 4, 7, playerSign) || checkIdSign(2, 5, 8, playerSign) || checkIdSign(3, 6, 9, playerSign) || checkIdSign(1, 5, 9, playerSign) || checkIdSign(3, 5, 7, playerSign)) {
runBot = false;
bot(runBot);
setTimeout(() => {
resultBox.classList.add("show");
playBoard.classList.remove("show");
}, 700);
wonText.innerHTML = `<p>${playerSign}</p> wins!`;
} else {
if (getIdVal(1) != "" && getIdVal(2) != "" && getIdVal(3) != "" && getIdVal(4) != "" && getIdVal(5) != "" && getIdVal(6) != "" && getIdVal(7) != "" && getIdVal(8) != "" && getIdVal(9) != "") {
runBot = false;
bot(runBot);
setTimeout(() => {
resultBox.classList.add("show");
playBoard.classList.remove("show");
}, 700);
wonText.textContent = "It's a draw!";
}
}
}
replayBtn.onclick = () => {
window.location.reload();
}
I think when you select to play with 0, you should call bot to make a first move.
You can refer
selectBtnO.onclick = () => {
selectBox.classList.add("hide");
playBoard.classList.add("show");
players.setAttribute("class", "players active player");
bot(true); // call bot when you selet to play with O
}
Related
I have 2 for loops, 1 outer for loop has 2000 items and the inner for loop has 800 items,my outer for loop is calling a function which calls another function and then I get the 800 items, basically i want to display 800 items in a table but there are columns where the data is inside the outerloop so that is why I need the nestedloop, when i run my browser get hangs, below is my code. Please help me find a way to achieve this.
function GetRowsAssetMaterialItems(
locationName,
expectDate,
urlAssetMaterial,
resultsAssetMaterialItems
) {
return $.ajax({
url: urlAssetMaterial,
method: "GET",
async: true,
headers: {
Accept: "application/json; odata=verbose",
},
success: (data) => {
debugger;
let arr = data.d.results;
let x = arr.filter((a) => {
var locationGatePass =
a.GatePassId.Title != null ? a.GatePassId.Title.toString() : "";
var expectedDateOfReturn = a.ExpectedDateOfReturn;
if (expectDate != "") {
var expectEndTIme = new Date(expectDate);
expectEndTIme.setUTCHours(23, 59, 59, 0);
var expectStartTIme = new Date(expectDate);
expectStartTIme.setUTCHours(0, 0, 0, 0);
if (
locationGatePass
.toLowerCase()
.includes(locationName.toLowerCase()) &&
expectedDateOfReturn <= expectEndTIme &&
expectedDateOfReturn >= expectStartTIme
) {
return a;
}
} else {
if (
locationGatePass.toLowerCase().includes(locationName.toLowerCase())
) {
return a;
}
}
});
resultsAssetMaterialItems = resultsAssetMaterialItems.concat(x);
if (data.d.__next) {
urlAssetMaterial = data.d.__next;
GetRowsAssetMaterialItems(
locationName,
expectDate,
urlAssetMaterial,
resultsAssetMaterialItems
);
} else {
var result = [];
$.each(resultsAssetMaterialItems, function (i, e) {
var matchingItems = $.grep(result, function (item) {
return item.GatePassId.Title === e.GatePassId.Title;
});
if (matchingItems.length === 0) {
result.push(e);
}
});
for (var i = 0; i < result.length; i++) { 1st for loop where items are 2000
var item = result[i];
var sNo = i + 1;
var Atitle = item.Title;
var AMaterialCode = item.MaterialCode;
var AMaterialDescription = item.MaterialDescription;
gatePassMaster(Atitle,AMaterialCode,AMaterialDescription);
}
setTimeout('$("#retriveData").removeAttr("disabled")', 1000);
loadTable();
}
},
});
}
function gatePassMaster(
Atitle,
AMaterialCode,
AMaterialDescription,
AQuantity,
AUnit,
ABatchNo,
AExpectedDateOfReturn,
AReturnedDate,
AGatePassMasterID,
ARemarks,
sNo
) {
debugger;
//$("#pending_rgp tr").remove();
var startDD = $("#startDate").val();
var endDate = $("#endDate").val();
var dateStartend = new Date(startDD);
dateStartend.setUTCHours(23, 59, 59, 0);
var dateStartfirst = new Date(startDD);
dateStartfirst.setUTCHours(00, 00, 00, 0);
var dateEnd = new Date(endDate);
dateEnd.setUTCHours(23, 59, 59, 0);
var dateEndstart = new Date(endDate);
dateEndstart.setUTCHours(00, 00, 00, 0);
var currentTime = new Date();
var getmonth = currentTime.getMonth();
var getyear = currentTime.getFullYear();
// returns the month (from 0 to 11)
var aprilmonth = 04;
var marchmonth = 03;
// returns the day of the month (from 1 to 31)
var aprilday = 01;
var marchday = 31;
var aprilyear = "";
// returns the year (four digits)
if (getmonth >= 3) {
aprilyear = currentTime.getFullYear();
} else {
aprilyear = currentTime.getFullYear() - 1;
}
var startapril = aprilmonth + "-" + aprilday + "-" + aprilyear;
var aprilstart = new Date(startapril);
aprilstart.setUTCHours(24, 0, 0, 0);
var marchyear = "";
if (getmonth > 2) {
marchyear = currentTime.getFullYear() + 1;
} else {
marchyear = currentTime.getFullYear();
}
var endMarch = marchmonth + "-" + marchday + "-" + marchyear;
var marchend = new Date(endMarch);
marchend.setUTCHours(47, 59, 59, 0);
console.log(marchend, aprilstart);
GetRowsGatePassMasterItems(
Atitle,
AMaterialCode,
AMaterialDescription,
AQuantity,
AUnit,
ABatchNo,
AExpectedDateOfReturn,
AReturnedDate,
AGatePassMasterID,
ARemarks,
sNo,
startDD,
endDate,
dateStartfirst,
dateStartend,
dateEnd,
dateEndstart,
aprilstart,
marchend,
locationName
);
}
function GetRowsGatePassMasterItems(
Atitle,
AMaterialCode,
AMaterialDescription,
AQuantity,
AUnit,
ABatchNo,
AExpectedDateOfReturn,
AReturnedDate,
AGatePassMasterID,
ARemarks,
sNo,
startDD,
endDate,
dateStartfirst,
dateStartend,
dateEnd,
dateEndstart,
aprilstart,
marchend,
locationName
) {
debugger;
if (resultsGatePassMasterAllItems != undefined) {
let arr = resultsGatePassMasterAllItems;
let x = arr.filter((a) => {
var gatePassId = a.ID != null ? a.ID.toString() : "";
var locationGatePass = a.Title != null ? a.Title.toString() : "";
var createdDate = a.Created;
if (startDD != "" && endDate != "") {
if (
gatePassId == AGatePassMasterID &&
locationGatePass.toLowerCase().includes(locationName.toLowerCase()) &&
createdDate >= dateStartfirst.toISOString() &&
createdDate <= dateEnd.toISOString()
) {
return a;
}
} else if (startDD != "" && endDate == "") {
if (
gatePassId == AGatePassMasterID &&
locationGatePass.toLowerCase().includes(locationName.toLowerCase()) &&
createdDate >= dateStartfirst.toISOString() &&
createdDate <= dateStartend.toISOString()
) {
return a;
}
} else if (startDD == "" && endDate != "") {
if (
gatePassId == AGatePassMasterID &&
locationGatePass.toLowerCase().includes(locationName.toLowerCase()) &&
createdDate >= dateEndstart.toISOString() &&
createdDate <= dateEnd.toISOString()
) {
return a;
}
} else {
if (
gatePassId == AGatePassMasterID &&
locationGatePass.toLowerCase().includes(locationName.toLowerCase()) &&
createdDate >= aprilstart.toISOString() &&
createdDate <= marchend.toISOString()
) {
return a;
}
}
});
resultsGatePassMasterItems1 = resultsGatePassMasterItems1.concat(x);
debugger;
var result = [];
$.each(resultsGatePassMasterItems1, function (i, e) {
var matchingItems = $.grep(result, function (item) {
return item.Title === e.Title;
});
if (matchingItems.length === 0) {
result.push(e);
}
console.log(matchingItems.length);
});
for (var i = 0; i < result.length; i++) { // inner loop where items are 800
var item = result[i];
if (item.ID == AGatePassMasterID && item.GatePassType == "Returnable") {
number = number + 1;
var GatePassNumber = item.Title;
var CreatedDate = item.Created;
var CreatedBy = item.Author.Title;
var ApprovedBy = item.ApprovedByName;
var Status = item.Status;
if (Status.toLowerCase() == "pending") {
Status = "Open";
} else if (Status.toLowerCase() == "approved") {
Status = "Close";
} else if (Status.toLowerCase() == "rejected") {
Status = "Rejected";
}
results.push({
AtitleName: Atitle,
AMaterialCode: AMaterialCode,
AMaterialDescription: AMaterialDescription,
GatePassNumber: GatePassNumber,
});
excelData.push({
"Sr.No": number,
"Gate Pass Date": creat,
"Gate Pass Number": GatePassNumber,
Vendor: VendorName,
"Material Code": AMaterialCode,
"Material Description": AMaterialDescription,
});
}
}
loadTable();
console.log("finalresult", results);
}
}
I have a string and need to check with and get whether the following strings overlap with the start and end of my target string:
target string: "click on the Run"
search strings: "the Run button to", "code and click on"
Apparently:
"the Run button to" is overlapped at the end of target "click on the Run"
"code and click on" is overlapped at the start of target "click on the Run"
Both, "the Run" and "click on" will be the desired results.
I have come up with a function to check and get the overlapped results for the cases at the start and at the end separately.
Question:
But my code could not be able to get the expected results only if I know how the search string overlapped with the target string in the very first place. And how can I combine the searched results in one go as well?
function findOverlapAtEnd(a, b) {
if (b.length === 2) {
return "";
}
if (a.indexOf(b) >= 0) {
return b;
}
if (a.endsWith(b)) {
return b;
}
return findOverlapAtEnd(a, b.substring(0, b.length - 1));
}
function findOverlapAtStart(a, b) {
if (b.length === 2) {
return "";
}
if (a.indexOf(b) >= 0) {
return b;
}
if (a.startsWith(b)) {
return b;
}
return findOverlapAtStart(a, b.substring(1));
}
console.log(findOverlapAtEnd("click on the Run", "the Run button to"))
console.log(findOverlapAtStart("click on the Run", "code and click on"))
edited:
case in the middle is also considered, e.g.:
target string: "click on the Run"
search strings: "on the"
Return value: "on the"
You may try this
function findOverlapAtEnd(a, b, min) {
if (b.length <= min) {
return '';
}
if (a.indexOf(b) >= 0) {
return b;
}
if (a.endsWith(b)) {
return b;
}
return findOverlapAtEnd(a, b.substring(0, b.length - 1), min);
}
function findOverlapAtStart(a, b, min) {
if (b.length <= min) {
return '';
}
if (a.indexOf(b) >= 0) {
return b;
}
if (a.startsWith(b)) {
return b;
}
return findOverlapAtStart(a, b.substring(1), min);
}
const GetOverlappingSection = (target, search, min) => {
if (target.length < search.length) {
const tmp = target;
target = search;
search = tmp;
}
let overlap1 = findOverlapAtStart(target, search, min);
if (overlap1.length === 0) {
overlap1 = findOverlapAtEnd(target, search, min);
}
return overlap1;
};
const removeEmptyKeyword = overlap => {
let tmpFinaloverlap = [];
overlap.forEach((key, idx) => {
if (!(key.trim().length === 0)) {
tmpFinaloverlap = [...tmpFinaloverlap, key];
}
});
return tmpFinaloverlap;
};
// let overlap = ['click on','the Run']
const GetOverlappingOfKeyowrd1And2 = (keywordSet1, keywordSet2,min) => {
let resultSetoverlap = [];
let tmpresultSetoverlap = [];
keywordSet1.forEach(key =>
keywordSet2.forEach(k2 => {
tmpresultSetoverlap = [
...tmpresultSetoverlap,
GetOverlappingSection(key, k2, min),
];
})
);
// get the resultSetoverlap
tmpresultSetoverlap.forEach(element => {
if (element.length > 0) {
resultSetoverlap = [...resultSetoverlap, element];
}
});
return resultSetoverlap;
};
const min = 2;
//To handle overlapping issue in overlapping set, that casuing
overlap.forEach((key, idx) => {
if (idx < overlap.length - 1) {
for (let i = idx + 1; i < overlap.length; i++) {
console.log(`key: ${key}`);
console.log(`search: ${overlap[i]}`);
let overlapSection = GetOverlappingSection(key, overlap[i], min);
if (overlapSection.length > 0) {
console.log(`overlapSection: ${overlapSection}`);
overlap[idx] = overlap[idx].replace(overlapSection, '');
}
}
}
});
overlap = removeEmptyKeyword(overlap);
console.log(overlap);
overlap.forEach(key => {
keywordSet2 = keywordSet2.map((k1, idx) => {
console.log(`checking overlap keyword:'${key}' in '${k1}'`);
return k1.replace(key, '');
});
});
overlap.forEach(key => {
keywordSet1 = keywordSet1.map((k1, idx) => {
console.log(`checking overlap keyword:'${key}' in '${k1}'`);
return k1.replace(key, '');
});
});
keywordSet2 = removeEmptyKeyword(keywordSet2);
keywordSet1 = removeEmptyKeyword(keywordSet1);
overlap.forEach(key => {
text = text.replace(key, `$#k1k2$&$`);
});
keywordSet1.forEach(key => {
text = text.replace(key, `$#k1$&$`);
});
keywordSet2.forEach(key => {
text = text.replace(key, `$#k2$&$`);
});
console.log(`ResultSetoverlap after processing:${text}`);
Because I need to decompress and I find these logic puzzles fun, here's my solution to the problem...
https://highdex.net/begin_end_overlap.htm
You can view source of the page to see JavaScript code I used. But just in case I ever take that page down, here's the important function...
function GetOverlappingSection(str1, str2, minOverlapLen = 4) {
var work1 = str1;
var work2 = str2;
var w1Len = work1.length;
var w2Len = work2.length;
var resultStr = "";
var foundResult = false;
var workIndex;
if (minOverlapLen < 1) { minOverlapLen = 1; }
else if (minOverlapLen > (w1Len > w2Len ? w2Len : w1Len)) { minOverlapLen = (w1Len > w2Len ? w2Len : w1Len); }
//debugger;
//we have four loops to go through. We trim each string down from each end and see if it matches either end of the other string.
for (var i1f = 0; i1f < w1Len; i1f++) {
workIndex = work2.indexOf(work1);
if (workIndex == 0 || (workIndex != -1 && workIndex == w2Len - work1.length)) {
//we found a match!
foundResult = true;
resultStr = work1;
break;
}
work1 = work1.substr(1);
if (work1.length < minOverlapLen) { break; }
}
if (!foundResult) {
//debugger;
//reset the work vars...
work1 = str1;
for (var i1b = 0; i1b < w1Len; i1b++) {
workIndex = work2.indexOf(work1);
if (workIndex == 0 || (workIndex != -1 && workIndex == w2Len - work1.length)) {
//we found a match!
foundResult = true;
resultStr = work1;
break;
}
work1 = work1.substr(0, work1.length - 1);
if (work1.length < minOverlapLen) { break; }
}
}
if (!foundResult) {
//debugger;
//reset the work vars...
work1 = str1;
for (var i2f = 0; i2f < w2Len; i2f++) {
workIndex = work1.indexOf(work2);
if (workIndex == 0 || (workIndex != -1 && workIndex == w1Len - work2.length)) {
//we found a match!
foundResult = true;
resultStr = work2;
break;
}
work2 = work2.substr(1);
if (work2.length < minOverlapLen) { break; }
}
}
if (!foundResult) {
//debugger;
//reset the work vars...
work2 = str2;
for (var i2b = 0; i2b < w2Len; i2b++) {
workIndex = work1.indexOf(work2);
if (workIndex == 0 || (workIndex != -1 && workIndex == w1Len - work2.length)) {
//we found a match!
foundResult = true;
resultStr = work2;
break;
}
work2 = work2.substr(0, work2.length - 1);
if (work2.length < minOverlapLen) { break; }
}
}
return resultStr;
}
Hopefully that's helpful.
let _board = [[null, null, null], [null, null, null], [null, null, null]];
let _flag = true;
let _AIrowIndex = null;
let _AIcellIndex = null;
const _wrapper = document.querySelector(".wrapper");
const _changeTurn = function () {
if (_flag == true) {
_flag = false;
return playerOne.getSign();
} else {
_flag = true;
return playerTwo.getSign();
}
};
const _displayTurn = function () {
let turn = document.querySelector(".playerInfo__turn")
if (_flag == true) {
turn.innerHTML = `${playerOne.getName()} is your turn`;
} else {
turn.innerHTML = `${playerTwo.getName()} is your turn`;
}
};
const _evaluation = (winner) => {
if(winner == "X"){
return 1;
}else if(winner == "O"){
return -1;
}
else{
return null;
}
};
const _evaluationFunction = function (board) {
/*CHECK 1 DIAG*/
if (board[0][0] === board[1][1] && board[2][2] === board[0][0]) {
return _evaluation(board[0][0]);
/*CHECK 2 DIAG*/
}
if (board[0][2] === board[1][1] && board[2][0] === board[0][2]) {
return _evaluation(board[0][2]);
/*CHECK PAIR*/
}
for (let col = 0; col < 3; col++) {
if (board[0][col] === board[1][col] && board[1][col] === board[2][col]) {
return _evaluation(board[0][col]);
}
}
for (let row = 0; row < 3; row++) {
if (board[row][0] === board[row][1] && board[row][1] === board[row][2]) {
return _evaluation(board[row][0]);
}
}
return 0;
};
const minimax = (_board, depth, isMaximizer) => {
let result = _evaluationFunction(_board);
console.log(result);
if (result !== null) {
return result;
}
if (isMaximizer) {
let bestScore = -Infinity;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (_board[i][j] == null) {
_board[i][j] = playerOne.getSign();
let score = minimax(_board, depth + 1, false);
_board[i][j] = null;
bestScore = Math.max(score, bestScore);
}
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (_board[i][j] == null) {
_board[i][j] = playerTwo.getSign();
let score = minimax(_board, depth + 1, true);
_board[i][j] = null;
bestScore = Math.min(score, bestScore);
}
}
}
return bestScore;
}
};
const _setAIPlay = () => {
let bestScore = Infinity;
let bestMove;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (_board[i][j] == null) {
_board[i][j] = playerTwo.getSign();
let score = minimax(_board, 0, true);
_board[i][j] = null;
if(score < bestScore){
bestScore = score;
console.log(bestScore);
bestMove = {i, j}
}
}
}
};
_board[bestMove.i][bestMove.j] = playerTwo.getSign();
_AIrowIndex = bestMove.i;
_AIcellIndex = bestMove.j;
_displayAIPlay(_AIrowIndex, _AIcellIndex);
_changeTurn();
_checkWinner();
};
const _displayAIPlay = (rowIndex, cellIndex) => {
let AIcell = document.querySelector(`[data-row="${rowIndex}"][data-cell="${cellIndex}"]`);
AIcell.textContent = playerTwo.getSign();
}
I am trying to solve this tic-tac-toe problem with the minimax algorithm but I don't understand why it continues to place the "O" in the adjacent cell, I tried to console.log() result, and best score inside the minimax function and it looks like the recursion is working, but I don't understand why inside _setAIPlay()
if I console.log(bestScore) in the last if statement it returns me as final value or 0 or 1 and not -1 which in this case I think should be the bestscore as a minimizer.
If needed here you can find the full repo gitHub
Here is a simple implementation of a minimax algorithm for Tic Tac Toe in JavaScript. The search tree deepens until a game-over state is detected. If a win is detected, then the current player cannot play, and so the static value is negative (-10). If a draw is detected the returned value is 0.
In all other cases the search deepens. This implementation uses a minimax algorithm where the value is maximised for both players. Therefore the sign of the value coming back from a deeper evaluation is always flipped ("what is good for my opponent is bad for me, and vice versa").
If a board is found to have a winning position then the moves that lead to the shortest path to a win are prioritised. This is achieved by lowering the absolute value by 1 point every time we backtrack.
If there are several moves with an equal value, then a random one will be picked from those.
Here is the implementation, with some basic HTML:
class TicTacToe {
constructor() {
this.board = Array(9).fill(0); // 0 means "empty"
this.moves = [];
this.isWin = this.isDraw = false;
}
get turn() { // returns 1 or 2
return 1 + this.moves.length % 2;
}
get validMoves() {
return [...this.board.keys()].filter(i => !this.board[i])
}
play(move) { // move is an index in this.board
if (this.board[move] !== 0 || this.isWin) return false; // invalid move
this.board[move] = this.turn; // 1 or 2
this.moves.push(move);
// Use regular expression to detect any 3-in-a-row
this.isWin = /^(?:...)*([12])\1\1|^.?.?([12])..\2..\2|^([12])...\3...\3|^..([12]).\4.\4/.test(this.board.join(""));
this.isDraw = !this.isWin && this.moves.length === this.board.length;
return true;
}
takeBack() {
if (this.moves.length === 0) return false; // cannot undo
this.board[this.moves.pop()] = 0;
this.isWin = this.isDraw = false;
return true;
}
minimax() {
if (this.isWin) return { value: -10 };
if (this.isDraw) return { value: 0 };
let best = { value: -Infinity };
for (let move of this.validMoves) {
this.play(move);
let {value} = this.minimax();
this.takeBack();
// Reduce magnitude of value (so shorter paths to wins are prioritised) and negate it
value = value ? (Math.abs(value) - 1) * Math.sign(-value) : 0;
if (value >= best.value) {
if (value > best.value) best = { value, moves: [] };
best.moves.push(move); // keep track of equally valued moves
}
}
return best;
}
goodMove() {
let {moves} = this.minimax();
// Pick a random move when there are choices:
return moves[Math.floor(Math.random() * moves.length)];
}
}
(function main() {
const table = document.querySelector("#game");
const btnNewGame = document.querySelector("#newgame");
const btnCpuMove = document.querySelector("#cpumove");
const messageArea = document.querySelector("#message");
let game, human;
function display() {
game.board.forEach((cell, i) => table.rows[Math.floor(i / 3)].cells[i % 3].className = " XO"[cell]);
messageArea.textContent = game.isWin ? (game.turn == human ? "CPU won" : "You won")
: game.isDraw ? "It's a draw"
: game.turn == human ? "Your turn"
: "CPU is preparing move...";
table.className = game.isWin || game.isDraw || game.turn !== human ? "inactive" : "";
}
function computerMove() {
if (game.isWin || game.isDraw) return;
human = 3 - game.turn;
display();
setTimeout(() => {
game.play(game.goodMove());
display();
}, 500); // Artificial delay before computer move is calculated and played
}
function humanMove(i) {
if (game.turn !== human || !game.play(i)) return; // ignore click when not human turn, or when invalid move
display();
computerMove();
}
function newGame() {
game = new TicTacToe();
human = 1;
display();
}
table.addEventListener("click", e => humanMove(e.target.cellIndex + 3 * e.target.parentNode.rowIndex));
btnNewGame.addEventListener("click", newGame);
btnCpuMove.addEventListener("click", computerMove);
newGame();
})();
#game { border-collapse: collapse }
#game td { border: 1px solid black; width: 30px; height: 30px; text-align: center; cursor: pointer }
#game td.X, #game td.O { cursor: default }
#game td.X { color: green }
#game td.O { color: red }
#game td.X:after { content: "X" }
#game td.O:after { content: "O" }
#game.inactive { background: silver }
<table id="game">
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</table>
<h4 id="message"></h4>
<button id="newgame">New Game</button>
<button id="cpumove">Let CPU play a move</button>
class TicTacToe {
constructor() {
this.board = Array(9).fill(0); // 0 means "empty"
this.moves = [];
this.isWin = this.isDraw = false;
}
get turn() { // returns 1 or 2
return 1 + this.moves.length % 2;
}
get validMoves() {
return [...this.board.keys()].filter(i => !this.board[i])
}
play(move) { // move is an index in this.board
if (this.board[move] !== 0 || this.isWin) return false; // invalid move
this.board[move] = this.turn; // 1 or 2
this.moves.push(move);
// Use regular expression to detect any 3-in-a-row
this.isWin = /^(?:...)*([12])\1\1|^.?.?([12])..\2..\2|^([12])...\3...\3|^..([12]).\4.\4/.test(this.board.join(""));
this.isDraw = !this.isWin && this.moves.length === this.board.length;
return true;
}
takeBack() {
if (this.moves.length === 0) return false; // cannot undo
this.board[this.moves.pop()] = 0;
this.isWin = this.isDraw = false;
return true;
}
minimax() {
if (this.isWin) return { value: -10 };
if (this.isDraw) return { value: 0 };
let best = { value: -Infinity };
for (let move of this.validMoves) {
this.play(move);
let {value} = this.minimax();
this.takeBack();
// Reduce magnitude of value (so shorter paths to wins are prioritised) and negate it
value = value ? (Math.abs(value) - 1) * Math.sign(-value) : 0;
if (value >= best.value) {
if (value > best.value) best = { value, moves: [] };
best.moves.push(move); // keep track of equally valued moves
}
}
return best;
}
goodMove() {
let {moves} = this.minimax();
// Pick a random move when there are choices:
return moves[Math.floor(Math.random() * moves.length)];
}
}
(function main() {
const table = document.querySelector("#game");
const btnNewGame = document.querySelector("#newgame");
const btnCpuMove = document.querySelector("#cpumove");
const messageArea = document.querySelector("#message");
let game, human;
function display() {
game.board.forEach((cell, i) => table.rows[Math.floor(i / 3)].cells[i % 3].className = " XO"[cell]);
messageArea.textContent = game.isWin ? (game.turn == human ? "CPU won" : "You won")
: game.isDraw ? "It's a draw"
: game.turn == human ? "Your turn"
: "CPU is preparing move...";
table.className = game.isWin || game.isDraw || game.turn !== human ? "inactive" : "";
}
function computerMove() {
if (game.isWin || game.isDraw) return;
human = 3 - game.turn();
display();
setTimeout(() => {
game.play(game.goodMove());
display();
}, 500); // Artificial delay before computer move is calculated and played
}
function humanMove(i) {
if (game.turn !== human || !game.play(i)) return; // ignore click when not human turn, or when invalid move
display();
computerMove();
}
function newGame() {
game = new TicTacToe();
human = 1;
display();
}
table.addEventListener("click", e => humanMove(e.target.cellIndex + 3 * e.target.parentNode.rowIndex));
btnNewGame.addEventListener("click", newGame);
btnCpuMove.addEventListener("click", computerMove);
newGame();
})();
#game { border-collapse: collapse }
#game td { border: 1px solid black; width: 30px; height: 30px; text-align: center; cursor: pointer }
#game td.X, #game td.O { cursor: default }
#game td.X { color: green }
#game td.O { color: red }
#game td.X:after { content: "X" }
#game td.O:after { content: "O" }
#game.inactive { background: silver }
<table id="game">
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</table>
<h4 id="message"></h4>
<button id="newgame">New Game</button>
<button id="cpumove">Let CPU play a move</button>
class TicTacToe {
constructor() {
this.board = Array(9).fill(0); // 0 means "empty"
this.moves = [];
this.isWin = this.isDraw = false;
}
get turn() { // returns 1 or 2
return 1 + this.moves.length % 2;
}
get validMoves() {
return [...this.board.keys()].filter(i => !this.board[i])
}
play(move) { // move is an index in this.board
if (this.board[move] !== 0 || this.isWin) return false; // invalid move
this.board[move] = this.turn; // 1 or 2
this.moves.push(move);
// Use regular expression to detect any 3-in-a-row
this.isWin = /^(?:...)*([12])\1\1|^.?.?([12])..\2..\2|^([12])...\3...\3|^..([12]).\4.\4/.test(this.board.join(""));
this.isDraw = !this.isWin && this.moves.length === this.board.length;
return true;
}
takeBack() {
if (this.moves.length === 0) return false; // cannot undo
this.board[this.moves.pop()] = 0;
this.isWin = this.isDraw = false;
return true;
}
minimax() {
if (this.isWin) return { value: -10 };
if (this.isDraw) return { value: 0 };
let best = { value: -Infinity };
for (let move of this.validMoves) {
this.play(move);
let {value} = this.minimax();
this.takeBack();
// Reduce magnitude of value (so shorter paths to wins are prioritised) and negate it
value = value ? (Math.abs(value) - 1) * Math.sign(-value) : 0;
if (value >= best.value) {
if (value > best.value) best = { value, moves: [] };
best.moves.push(move); // keep track of equally valued moves
}
}
return best;
}
goodMove() {
let {moves} = this.minimax();
// Pick a random move when there are choices:
return moves[Math.floor(Math.random() * moves.length)];
}
}
(function main() {
const table = document.querySelector("#game");
const btnNewGame = document.querySelector("#newgame");
const btnCpuMove = document.querySelector("#cpumove");
const messageArea = document.querySelector("#message");
let game, human;
function display() {
game.board.forEach((cell, i) => table.rows[Math.floor(i / 3)].cells[i % 3].className = " XO"[cell]);
messageArea.textContent = game.isWin ? (game.turn == human ? "CPU won" : "You won")
: game.isDraw ? "It's a draw"
: game.turn == human ? "Your turn"
: "CPU is preparing move...";
table.className = game.isWin || game.isDraw || game.turn !== human ? "inactive" : "";
}
function computerMove() {
if (game.isWin || game.isDraw) return;
human = 3 - game.turn;
display();
setTimeout(() => {
game.play(game.goodMove());
display();
}, 500); // Artificial delay before computer move is calculated and played
}
function humanMove(i) {
if (game.turn !== human || !game.play(i)) return; // ignore click when not human turn, or when invalid move
display();
computerMove();
}
function newGame() {
game = new TicTacToe();
human = 1;
display();
}
table.addEventListener("click", e => humanMove(e.target.cellIndex + 3 * e.target.parentNode.rowIndex));
btnNewGame.addEventListener("click", newGame);
btnCpuMove.addEventListener("click", computerMove);
newGame();
})();
#game { border-collapse: collapse }
#game td { border: 1px solid black; width: 30px; height: 30px; text-align: center; cursor: pointer }
#game td.X, #game td.O { cursor: default }
#game td.X { color: green }
#game td.O { color: red }
#game td.X:after { content: "X" }
#game td.O:after { content: "O" }
#game.inactive { background: silver }
<table id="game">
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</table>
<h4 id="message"></h4>
<button id="newgame">New Game</button>
<button id="cpumove">Let CPU play a move</button>
I have a few filters (checkbox and price filter and select option).
I want these filters to work together. But there is a problem. Filters work individually. But I want these filters to work together.
I have used this method. Group filters to work. But you can use your own method.
function rangeSlider() {
$(".range-slider").ionRangeSlider({
hide_min_max: true,
keyboard: true,
min: 0,
max: 150,
from: 0,
to: 140,
type: 'double',
step: 1,
prefix: "$",
grid: true,
onFinish: function(data) {
var _price = filters.findIndex(item => item.field === 'price');
if (_price != -1) filters[_price]['value'] = [data.from, data.to];
else addOrRemoveFilter('price', [data.from, data.to], true);
customFilter();
// console.log(addOrRemoveFilter('price', [data.from, data.to], true));
}
});
}
function customFilter() {
let filtered_list = [];
FlyList.filter(item => {
filters.forEach(function(el, i) {
let _field = el['field'];
let _value = el['value'];
// console.log(_value);
if (typeof(_value) === 'object' && _value.length) {
if (parseInt(item[_field]) >= (parseInt(_value[0] * 1000)) && parseInt(item[_field]) <= (parseInt(_value[1] * 1000))) {
filtered_list.push(item);
} else {
FlyList = [];
}
} else {
let isMulti = _value.split(',');
//RANGE PRICE SLIDER
if (isMulti.length > 1) {
let time = miliseconds(item[_field].split(':')[0], item[_field].split(':')[1])
let num1 = miliseconds(isMulti[0].split(':')[0], isMulti[0].split(':')[1]);
let num2 = miliseconds(isMulti[1].split(':')[0], isMulti[1].split(':')[1]);
if (time >= num1 && time <= num2) filtered_list.push(item);
} else {
//end RANGE PRICE SLIDER
item[_field] == _value ? filtered_list.push(item) : false;
}
}
})
});
function miliseconds(hrs, min) {
return ((hrs * 60 * 60 + min * 60) * 1000);
}
$('#flights').updateDom(filtered_list.length ? filtered_list : FlyList, {
animate: true,
});
}
let filterCheckboxes = document.querySelectorAll('.filtersAll');
filterCheckboxes.forEach(checkbox => checkbox.addEventListener('change', (e) => {
e.preventDefault();
let filterTypeElement = findFilterTypeElement(e.target);
if (filterTypeElement) {
let field = filterTypeElement.getAttribute('data-field');
let val = e.target.value;
addOrRemoveFilter(field, val, e.target.checked);
customFilter();
}
}));
document.getElementById('optionAll').addEventListener('change', (e) => {
e.preventDefault();
let filterTypeElement = findFilterTypeElement(e.target);
if (filterTypeElement) {
let field = filterTypeElement.getAttribute('data-field');
let val = e.target.value;
addOrRemoveFilter(field, val, true);
for (var index = 0; index < e.target.options.length; index++) {
addOrRemoveFilter(field, e.target.options[index].value, false);
}
addOrRemoveFilter(field, val, true);
customFilter();
}
})
function addOrRemoveFilter(f, v, add) {
if (add) {
filters.push({
field: f.toLowerCase(),
value: v
});
} else {
for (let i = 0; i < filters.length; i++) {
if (filters[i].field === f.toLowerCase() && filters[i].value === v) {
filters.splice(i, 1);
}
}
}
// console.log(filters);
}
function getParents(el, parentSelector /* optional */ ) {
// If no parentSelector defined will bubble up all the way to *document*
if (parentSelector === undefined) {
parentSelector = document;
}
var parents = [];
var p = el.parentNode;
while (p && (p !== parentSelector || p.parentNode)) {
var o = p;
parents.push(o);
p = o.parentNode;
}
parents.push(parentSelector); // Push that parentSelector you wanted to stop at
return parents;
}
function findFilterTypeElement(el) {
var result = null;
var parents = getParents(el);
parents.forEach((item) => {
if (hasClass(item, 'filter_type') && result == null) {
result = item;
}
});
return result;
}
function hasClass(element, className) {
return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
}
I am new to ReactJS that is why my code might have few errors,but I don't want that for now.I am creating a tic-tac-toe,everything is working fine except for that the winner is updating on next onMouseUp event. I searched everywhere but I am not able to find the error.
What I am trying to say that when we play tic-tac-toe the player who wins will be alerted as he wins but in my code its not happening.It comes after I do onMouseUp event on the className="square".
Here is the code.
var Mycheck = React.createClass({
getInitialState: function() {
return {
disppp: "block",
disppp2: "none",
cnt: 0,
active: 1,
turnNum: 0,
player: "",
showWinner: "none"
}
},
handleClick: function(event, key) {
this.setState({
cnt: this.state.cnt + 1
})
var divId = parseInt(event.target.id);
var valvar = [];
if (this.state.active) {
if (this.state.cnt < 9) {
this.setState({
turnNum: this.state.turnNum + 1
})
if (this.state.cnt % 2 == 0) {
valvar[key] = "X";
this.setState(valvar)
this.setState({
disppp: "none",
disppp2: "block"
})
} else {
valvar[key] = "O";
this.setState(valvar)
this.setState({
disppp: "block",
disppp2: "none"
})
}
} else {
alert("GAME DRAW");
}
}
this.Findvalue();
},
Findvalue: function() {
var arr = [];
var winner;
var resultant = {
r1: [1, 2, 3],
r2: [4, 5, 6],
r3: [7, 8, 9],
r4: [1, 4, 7],
r5: [2, 5, 8],
r6: [3, 6, 9],
r7: [1, 5, 9],
r8: [3, 5, 7]
};
for (var i = 1; i < 10; i++) {
arr[i] = document.getElementById(i).innerHTML;
}
if (arr[resultant.r1[0]] == arr[resultant.r1[1]] && arr[resultant.r1[0]] == arr[resultant.r1[2]] && arr[resultant.r1[0]] != "" && arr[resultant.r1[1]] != "" && arr[resultant.r1[2]] != "") {
winner = arr[resultant.r1[0]];
}
if (arr[resultant.r2[0]] == arr[resultant.r2[1]] && arr[resultant.r2[0]] == arr[resultant.r2[2]] && arr[resultant.r2[0]] != "" && arr[resultant.r2[1]] != "" && arr[resultant.r2[2]] != "") {
winner = arr[resultant.r2[0]];
}
if (arr[resultant.r3[0]] == arr[resultant.r3[1]] && arr[resultant.r3[0]] == arr[resultant.r3[2]] && arr[resultant.r3[0]] != "" && arr[resultant.r3[1]] != "" && arr[resultant.r3[2]] != "") {
winner = arr[resultant.r3[0]];
}
if (arr[resultant.r4[0]] == arr[resultant.r4[1]] && arr[resultant.r4[0]] == arr[resultant.r4[2]] && arr[resultant.r4[0]] != "" && arr[resultant.r4[1]] != "" && arr[resultant.r4[2]] != "") {
winner = arr[resultant.r4[0]];
}
if (arr[resultant.r5[0]] == arr[resultant.r5[1]] && arr[resultant.r5[0]] == arr[resultant.r5[2]] && arr[resultant.r5[0]] != "" && arr[resultant.r5[1]] != "" && arr[resultant.r5[2]] != "") {
winner = arr[resultant.r5[0]];
}
if (arr[resultant.r6[0]] == arr[resultant.r6[1]] && arr[resultant.r6[0]] == arr[resultant.r6[2]] && arr[resultant.r6[0]] != "" && arr[resultant.r6[1]] != "" && arr[resultant.r6[2]] != "") {
winner = arr[resultant.r6[0]];
}
if (arr[resultant.r7[0]] == arr[resultant.r7[1]] && arr[resultant.r7[0]] == arr[resultant.r7[2]] && arr[resultant.r7[0]] != "" && arr[resultant.r7[1]] != "" && arr[resultant.r7[2]] != "") {
winner = arr[resultant.r7[0]];
}
if (arr[resultant.r8[0]] == arr[resultant.r8[1]] && arr[resultant.r8[0]] == arr[resultant.r8[2]] && arr[resultant.r8[0]] != "" && arr[resultant.r8[1]] != "" && arr[resultant.r8[2]] != "") {
winner = arr[resultant.r8[0]];
}
if (winner == "X") {
this.setState({
active: 0,
player: "X",
showWinner: "block"
})
alert("WINNER IS PLAYER 1");
}
if (winner == "O") {
this.setState({
active: 0,
player: "O",
showWinner: "block"
})
alert("WINNER IS PLAYER 2");
}
},
render: function() {
var divVal = "";
var i = 0;
return ( < div >
< div className = "turn_num" > TURN NUMBER: {
this.state.turnNum + 1
} < /div>
<button style={{display:this.state.disppp}}>PLAYER 1 FOR X</button >
< button style = {
{
display: this.state.disppp2
}
} > PLAYER 2 FOR O < /button><br/ > < br / >
< button className = "square"
id = "1"
onMouseUp = {
e => this.handleClick(e, "valvar1")
}
disabled = {
this.state.valvar1
} > {
this.state.valvar1
} < /button>
<button className="square" id="2" onMouseUp={e => this.handleClick(e,"valvar2")} disabled={this.state.valvar2}>{this.state.valvar2}</button >
< button className = "square"
id = "3"
onMouseUp = {
e => this.handleClick(e, "valvar3")
}
disabled = {
this.state.valvar3
} > {
this.state.valvar3
} < /button>
<button className="square clearB" id="4" onMouseUp={e => this.handleClick(e,"valvar4")} disabled={this.state.valvar4}>{this.state.valvar4}</button >
< button className = "square"
id = "5"
onMouseUp = {
e => this.handleClick(e, "valvar5")
}
disabled = {
this.state.valvar5
} > {
this.state.valvar5
} < /button>
<button className="square"id="6" onMouseUp={e => this.handleClick(e,"valvar6")} disabled={this.state.valvar6}>{this.state.valvar6}</button >
< button className = "square clearB"
id = "7"
onMouseUp = {
e => this.handleClick(e, "valvar7")
}
disabled = {
this.state.valvar7
} > {
this.state.valvar7
} < /button>
<button className="square" id="8" onMouseUp={e => this.handleClick(e,"valvar8")} disabled={this.state.valvar8}>{this.state.valvar8}</button >
< button className = "square"
id = "9"
onMouseUp = {
e => this.handleClick(e, "valvar9")
}
disabled = {
this.state.valvar9
} > {
this.state.valvar9
} < /button><br/ > < br / > < br / >
< /div>
<div className="winnerDetail" style={{display:this.state.showWinner}}>WINNER IS : {this.state.player}</div >
);
}
});
ReactDOM.render( < Mycheck / > , document.getElementById("container"));
.square {
height: 50px;
width: 50px;
border: 1px solid black;
background: none;
float: left;
padding: 10px;
text-align: center;
font-size: 25px;
font-weight: bold;
color: #146fbe;
}
.clearB {
clear: both;
}
.winnerDetail {
font-size: 30px;
font-weight: bold;
margin-top: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
The problem is the way you use setState function. The setState() actions are asynchronous and are batched for performance gains. This is explained in the docs:
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
The value of this.state.cnt will hold previous value right after this.setState() call:
handleClick: function(event, key) {
// if this.state.cnt == 0
this.setState({
cnt: this.state.cnt + 1
})
console.log(this.state.cnt); // this.state.cnt is still 0
}
You should rewrite your handleClick to perform some logic and then change state in the end:
handleClick: function(event, key) {
var cnt = this.state.cnt;
cnt++;
var turnNum = this.state.turnNum;
var divId = parseInt(event.target.id);
var valvar = {};
if (this.state.active) {
if (cnt < 9) {
turnNum++;
if (cnt % 2 == 0) {
valvar[key] = "X";
disppp = "none";
disppp2 = "block";
} else {
valvar[key] = "O";
disppp = "block";
disppp2 = "none";
}
} else {
alert("GAME DRAW");
}
}
this.setState({
cnt: cnt,
turnNum: turnNum,
disppp: disppp,
disppp2: disppp2
})
this.Findvalue();
}
Also I noticed that the valvar variable is an array. It should be an object instead.