Why isn't my game ending in a tie? - javascript

I'm coding a tic-tac-toe game in pure javascript.
My program correctly checks if either Player1 or Player2 have won. For some reason, it's not able to check if it's a tie though.
I have set a counter called moveCounter. After each successful move, the counter is incremented by one. It is not possible for the counter to reach higher than 9.
Below is a function that tests the game's state.
It will check if player1 or player2 has won. It does this fine.
Below you can see I'm attempting to test for a tie. But nothing is happening. The game is just stuck and nothing is being printed to the console aside from the counter which is being incremented in my placePiece() function.
I have tried playing around with it and altering the if condition, along with it's position without the function and I'm having no luck so far.
Here's my gameState() function:
// The game ends when one player has three of their symbols in a row either horizontally, vertically or diagonally.
function gameState() {
var result;
//boxes
var a1 = document.getElementById('a1');
var a2 = document.getElementById('a2');
var a3 = document.getElementById('a3');
var b1 = document.getElementById('b1');
var b2 = document.getElementById('b2');
var b3 = document.getElementById('b3');
var c1 = document.getElementById('c1');
var c2 = document.getElementById('c2');
var c3 = document.getElementById('c3');
//See if Player 1 has won
if((a1.classList.contains("box-filled-1") && a2.classList.contains("box-filled-1") && a3.classList.contains("box-filled-1")) || (b1.classList.contains("box-filled-1") && b2.classList.contains("box-filled-1") && b3.classList.contains("box-filled-1")) || (b1.classList.contains("box-filled-1") && b2.classList.contains("box-filled-1") && b3.classList.contains("box-filled-1")) || (c1.classList.contains("box-filled-1") && c2.classList.contains("box-filled-1") && c3.classList.contains("box-filled-1")) || (a1.classList.contains("box-filled-1") && b1.classList.contains("box-filled-1") && c1.classList.contains("box-filled-1")) || (a2.classList.contains("box-filled-1") && b2.classList.contains("box-filled-1") && c2.classList.contains("box-filled-1")) || (a3.classList.contains("box-filled-1") && b3.classList.contains("box-filled-1") && c3.classList.contains("box-filled-1")) || (a1.classList.contains("box-filled-1") && b2.classList.contains("box-filled-1") && c3.classList.contains("box-filled-1")) || (a3.classList.contains("box-filled-1") && b2.classList.contains("box-filled-1") && c1.classList.contains("box-filled-1"))){
result = "player1"
return gameOver(result);
}
//See if Player 2 has won
if((a1.classList.contains("box-filled-2") && a2.classList.contains("box-filled-2") && a3.classList.contains("box-filled-2")) || (b1.classList.contains("box-filled-2") && b2.classList.contains("box-filled-2") && b3.classList.contains("box-filled-2")) || (b1.classList.contains("box-filled-2") && b2.classList.contains("box-filled-2") && b3.classList.contains("box-filled-2")) || (c1.classList.contains("box-filled-2") && c2.classList.contains("box-filled-2") && c3.classList.contains("box-filled-2")) || (a1.classList.contains("box-filled-2") && b1.classList.contains("box-filled-2") && c1.classList.contains("box-filled-2")) || (a2.classList.contains("box-filled-2") && b2.classList.contains("box-filled-2") && c2.classList.contains("box-filled-2")) || (a3.classList.contains("box-filled-2") && b3.classList.contains("box-filled-2") && c3.classList.contains("box-filled-2")) || (a1.classList.contains("box-filled-2") && b2.classList.contains("box-filled-2") && c3.classList.contains("box-filled-2")) || (a3.classList.contains("box-filled-2") && b2.classList.contains("box-filled-2") && c1.classList.contains("box-filled-2"))){
result = "player2"
return gameOver(result);
}
//If all of the squares are filled and no players have three in a row the game is a tie.
if(moveCounter === 9){
result = "tie";
console.log("tie");
return gameOver(result);
}
};
Here's where it gets called:
// Players can only click on empty squares. When the player clicks on an empty square, attach the class box-filled-1 (for O) or box-filled-2 (for X) to the square.
function placePiece() {
if(!this.classList.contains("box-filled-1") && !this.classList.contains("box-filled-2")) {
if(currentPlayer === player1){
this.classList.add("box-filled-1");
player1.currentPlayer = false;
player2.currentPlayer = true;
currentPlayer = player2;
currentPlayerFlag();
gameState();
moveCounter++;
console.log(moveCounter);
} else {
this.classList.add("box-filled-2");
player2.currentPlayer = false;
player1.currentPlayer = true;
currentPlayer = player1;
currentPlayerFlag();
gameState();
moveCounter++;
console.log(moveCounter);
}
}
};

Assume you init moveCounter = 0, then move gameState() after incrementing moveCounter
moveCounter++;
gameState();

Related

How to remove edge cases for string and number expressions

I'm building a rock, paper, scissors game and working on excluding edge cases.
The function is setPlayerMoves(), which is supposed to set previously undefined global variables for player moves and give them a 'value' between 1-99. how do I excluded edge cases like wrong move type (eg. 'rok') or wrong value (eg. 100).
Here is my incorrect code:
function setPlayerMoves(player, moveOneType, moveOneValue, moveTwoType, moveTwoValue, moveThreeType, moveThreeValue) {
if (player === 'Player One' && (moveOneType && moveTwoType && moveThreeType === 'rock' || 'paper' || 'scissors') && (moveOneValue && moveTwoValue && moveThreeValue === (>= 1 && <= 99))) {
playerOneMoveOneType = 'rock';
playerOneMoveTwoType = 'paper';
playerOneMoveThreeType = 'scissors';
playerOneMoveOneValue = 11;
playerOneMoveTwoValue = 33;
playerOneMoveThreeValue = 55;
} else if (player === 'Player Two') {
playerTwoMoveOneType = 'rock';
playerTwoMoveTwoType = 'paper';
playerTwoMoveThreeType = 'scissors';
playerTwoMoveOneValue = 11;
playerTwoMoveTwoValue = 33;
playerTwoMoveThreeValue = 55;
}
}
Problem
Code for checking edge cases are wrong.
(player === 'Player One' && (moveOneType && moveTwoType && moveThreeType === 'rock' || 'paper' || 'scissors') && (moveOneValue && moveTwoValue && moveThreeValue === (>= 1 && <= 99)))
Also you are checking the edge cases for only 'player one'
Solution
Check the values seperately before assigning values and create a valid variable
let valid = false
let states = ['rock','paper','scissors'];
if((states.indexOf(moveOneType) != -1) && (states.indexOf(moveTwoType) != -1) && (states.indexOf(moveThreeType) != -1)){
// valid state
valid = true;
}else{
valid = false;
}
if((moveOneValue >=1 && moveOneValue <= 99) && (moveTwoValue >=1 && moveTwoValue <= 99) && (moveThreeValue >=1 && moveThreeValue <= 99)){
//valid value
valid = true;
}else{
valid = false
}
Then assign the values.
if ((player === 'Player One') && valid) {
playerOneMoveOneType = moveOneType;
playerOneMoveTwoType = moveTwoType;
playerOneMoveThreeType = moveThreeType;
playerOneMoveOneValue = moveOneValue;
playerOneMoveTwoValue = moveTwoValue;
playerOneMoveThreeValue = moveThreeValue;
} else if ((player === 'Player Two') && valid) {
playerTwoMoveOneType = moveOneType;
playerTwoMoveTwoType = moveTwoType;
playerTwoMoveThreeType = moveThreeType;
playerTwoMoveOneValue = moveOneValue;
playerTwoMoveTwoValue = moveTwoValue;
playerTwoMoveThreeValue = moveThreeValue;
}
Also if possible
Create an object each for player1 and player2 like below and use.
let player1 = {
moveOneType:'rock'
moveTwoType:'paper'
moveThreeType:'scissors'
moveOneValue: 23
moveTwoValue: 33
moveThreeValue: 98
}

JavaScript : Can't stop a function from running [Tic-Tac-Toe Game]

My code is intended to be a Tic-Tac-Toe Game. It's working quite alright: the players can make their moves and the program alerts "Player X won!" when the game is over. But I just can't implement the game draw code nor make the main function called game() stops running once a player wins the game.
I'm a beginner at programming, and I've been struggling with this question: how do I stop a function from running in JavaScript? I have to admit that I'm kinda lost, and I'd appreciate all the help.
The code is the one that follows:
//TIC-TAC-TOE
let tl = $('.tl');
let tm = $('.tm');
let tr = $('.tr');
let ml = $('.ml');
let mm = $('.mm');
let mr = $('.mr');
let bl = $('.bl');
let bm = $('.bm');
let br = $('.br');
var tabuleiro = {
'tl': tl,
'tm': tm,
'tr': tr,
'ml': ml,
'mm': mm,
'mr': mr,
'bl': bl,
'bm': bm,
'br': br
};
function game() {
var turno = true;
for (let casa in tabuleiro) {
tabuleiro[casa].on("click", function() {
if (tabuleiro[casa].is(':empty')) {
if (turno == true) {
tabuleiro[casa].append("<img class='cross' src='cross.svg'></img>");
tabuleiro[casa].addClass("x");
turno = false;
} else {
tabuleiro[casa].append("<img class='circle' src='circle.svg'></img>");
tabuleiro[casa].addClass("o");
turno = true;
}
}
ganhou();
});
}
}
function ganhou() {
if (tl.hasClass('x') && tm.hasClass('x') && tr.hasClass('x') ||
ml.hasClass('x') && mm.hasClass('x') && mr.hasClass('x') ||
bl.hasClass('x') && bm.hasClass('x') && br.hasClass('x') ||
tl.hasClass('x') && ml.hasClass('x') && bl.hasClass('x') ||
tm.hasClass('x') && mm.hasClass('x') && bm.hasClass('x') ||
tr.hasClass('x') && mr.hasClass('x') && br.hasClass('x') ||
tl.hasClass('x') && mm.hasClass('x') && br.hasClass('x') ||
tr.hasClass('x') && mm.hasClass('x') && bl.hasClass('x')) {
alert("Jogador 1 Venceu!");
} else if (tl.hasClass('o') && tm.hasClass('o') && tr.hasClass('o') ||
ml.hasClass('o') && mm.hasClass('o') && mr.hasClass('o') ||
bl.hasClass('o') && bm.hasClass('o') && br.hasClass('o') ||
tl.hasClass('o') && ml.hasClass('o') && bl.hasClass('o') ||
tm.hasClass('o') && mm.hasClass('o') && bm.hasClass('o') ||
tr.hasClass('o') && mr.hasClass('o') && br.hasClass('o') ||
tl.hasClass('o') && mm.hasClass('o') && br.hasClass('o') ||
tr.hasClass('o') && mm.hasClass('o') && bl.hasClass('o')) {
alert("Jogador 2 Venceu!");
}
}
game();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You could use it under the condition that you win, so if you use an if statement, you do If (what you're testing for) { return; }
This will just terminate the function.
There are a few things to consider.
First thing is the case where there are no moves left.
For this, after a space is clicked, perform a check on each space and if all are occupied, then alert the user. This would be a very niave solution, and you can optimize it however you would like (possibly keep track of the number of moves, and if the number of moves is 9, then its a draw.
Also when the game ends, you may want to consider setting a variable playing = false or something like that. Then on click you can check this variable. If it is false then just return. That way when the game ends you can no longer play.
There are many ways of implementing these things, they are just suggestions to solve the problems you are having.

why I'm getting "max call stack size exceeded" error

I'm working on minimax part of a tic tac toe game, and I keep getting "max call stack size exceeded" error. I isolated the bug to line # 110 which is emptyIndexies(board) function. But I don't see anything wrong with the function. I tested it on the console, and the board seems to update when I click on it; however, the computer turn don't work. Here's the code
var board = [0, 1, 2, 3, 4, 5, 6, 7, 8];
var player, sqrId, user, computer, row, col;
const ARR_LENGTH = 9;
$(document).ready(function() {
//1 checkbox event listener
$(".checkBox").click(function() {
if($(this).is(":checked")) {
user = $(this).val();
player = user;
computer = (user == 'X') ? 'O' : 'X';
}
});
//2 square even listener
$(".square").click(function() {
sqrId = $(this).attr("id");
playerMove();
minimax(board);
if(checkWinner(board, turn)) {
alert(turn+" Wins the game!");
resetBoard();
}
if(!checkDraw()) {
alert("It's a draw!");
}
player = (player == user) ? computer : user;
});
//reset board
$(".reset").click(function() {
resetBoard();
})
});
//player move
function playerMove() {
if($("#"+sqrId).text() == "") {
$("#"+sqrId).text(player);
board[sqrId] = player;
console.log(board);
}
else {
alert("Wrong move");
}
}
/* computer AI generate random number between 0 - 8 */
function computerAI() {
var random;
var min = 0, max = 8;
do {
random = Math.floor(Math.random() * (max + min));
}while($("#"+random).text() != "")
$("#"+random).text(computer);
row = getRow();
col = getCol();
board[row][col] = computer;
}
//getting row number
function getRow() {
return Math.floor(sqrId / ARR_LENGTH);
}
//getting col number
function getCol() {
return sqrId % ARR_LENGTH;
}
/* checking for winner */
// winning combinations using the board indexies
function winning(board){
if (
(board[0] == player && board[1] == player && board[2] == player) ||
(board[3] == player && board[4] == player && board[5] == player) ||
(board[6] == player && board[7] == player && board[8] == player) ||
(board[0] == player && board[3] == player && board[6] == player) ||
(board[1] == player && board[4] == player && board[7] == player) ||
(board[2] == player && board[5] == player && board[8] == player) ||
(board[0] == player && board[4] == player && board[8] == player) ||
(board[2] == player && board[4] == player && board[6] == player)
) {
return true;
} else {
return false;
}
}
function resetBoard() {
$(".square").text("");
$(".checkBox").prop("checked", false);
user = "";
turn = "";
computer = "";
for(var i = 0; i < ARR_LENGTH; i++) {
board[i] = "";
}
}
// returns list of the indexes of empty spots on the board
function emptyIndexies(board){
return board.filter(s => s != "O" && s != "X");
}
// the main minimax function
function minimax(newBoard, player){
//available spots
var availSpots = emptyIndexies(newBoard);
// checks for the terminal states such as win, lose, and tie
//and returning a value accordingly
if (winning(newBoard, user)){
return {score:-10};
}
else if (winning(newBoard, computer)){
return {score:10};
}
else if (availSpots.length === 0){
return {score:0};
}
// an array to collect all the objects
var moves = [];
// loop through available spots
for (var i = 0; i < availSpots.length; i++){
//create an object for each and store the index of that spot
var move = {};
move.index = newBoard[availSpots[i]];
// set the empty spot to the current player
newBoard[availSpots[i]] = player;
/*collect the score resulted from calling minimax
on the opponent of the current player*/
if (player == computer){
var result = minimax(newBoard, user);
move.score = result.score;
}
else{
var result = minimax(newBoard, computer);
move.score = result.score;
}
// reset the spot to empty
newBoard[availSpots[i]] = move.index;
// push the object to the array
moves.push(move);
}
// if it is the computer's turn loop over the moves and choose the move with the highest score
var bestMove;
if(player === computer){
var bestScore = -10000;
for(var i = 0; i < moves.length; i++){
if(moves[i].score > bestScore){
bestScore = moves[i].score;
bestMove = i;
}
}
}else{
// else loop over the moves and choose the move with the lowest score
var bestScore = 10000;
for(var i = 0; i < moves.length; i++){
if(moves[i].score < bestScore){
bestScore = moves[i].score;
bestMove = i;
}
}
}
// return the chosen move (object) from the moves array
return moves[bestMove];
}

missing ) after argument list when creating a variable right after a for statement

I am trying to create the Bagel game and keep getting missing ) after argument list when I try to create the guess variable right after the for statement. Can someone tell me how to fix this?
alert('Lets play the bagel game! If you can guess my three digit number (with each digit being unique) within 20 turns, then you win! I will only provide you with three hints. Pico, which means one digit is correct, but in the wrong position. Fermi, which means one digit is correct and in the right position. Bagels, which means no digits are correct');
//computer generates number
function numberRange(){
var number = Math.round(Math.random()*1000);
var num =number.toString();
if (number <= 100 || number == 1000){
numberRange();
}
else if (num[0] == num[1] || num[0] == num[2] || num[1]==num[2]){
numberRange();
}
else if (number == undefined || num == undefined){
numberRange();
}else{
var numSave = JSON.stringify(num);
sessionStorage.setItem('number',numSave);
}}
numberRange();
var numGet = sessionStorage.getItem('number');
var numUse = JSON.parse(numGet);
//game start
for (i=1;i<21;i++){
var validNumber = /\d{3}/;
var guess = prompt('Turn ' + i ': Guess a number!');
while (validNumber.test(guess) == false) {
alert('Put in a three digit number!');
var guess = prompt('Turn ' + i ': Guess a number!');
}
if (validNumber.test(guess)){
var guessNum = guess.toString();
if (guessNum[0] == numUse[0] && guessNum[1] && numUse[1] && guessNum[2] == numUse[2]){
alert('Congratulations! You win!');
break
}
else if ((guessNum[0] == numUse[0] || guessNum[1] == numUse[1] || guessNum[2] == numUse[2]) && (guessNum[0] == numUse[1] || guessNum[0] == numUse[2] || guessNum[1] == numUse[0] || guessNum[1] == numUse[2] || guessNum[2] == numUse[0] || guessNum[2] == numUse[3])){
alert('Pico and Fermi!');
}else if(guessNum[0] == numUse[1] || guessNum[0] == numUse[2] || guessNum[1] == numUse[0] || guessNum[1] == numUse[2] || guessNum[2] == numUse[0] || guessNum[2] == numUse[3]){
alert('Pico!');
}else if (guessNum[0] == numUse[0] || guessNum[1] == numUse[1] || guessNum[2] == numUse[2]){
alert('Fermi!');
}else (guessNum[0] != numUse[0] && guessNum[0] != numUse[1] && guessNum[0] != numUse[2] && guessNum[1] != numUse[0] && guessNum[1] != numUse[1] && guessNum[1] != numUse[2] && guessNum[2] != numUse[0] && guessNum[2] != numUse[1] && guessNum[2] != numUse[2]){
alert('Begels!');
}
}
}
I see several issues.
var guess = prompt('Turn ' + i ': Guess a number!');
is missing + after i '
It should be like this:
var guess = prompt('Turn ' + i + ': Guess a number!');
You're also defining the same variable name twice inside the for scope, just do guess = prompt('Turn ' + i ': Guess a number!'); inside the while loop.
I didn't check the rest of the code, but this should get you started.
you seem to have missed a "+" as a part of string concatenation.
prompt('Turn ' + i+': Guess a number!');
instead of
prompt('Turn ' + i': Guess a number!');
What the other folks said (there are 2 places you need + signs, l#s 25, 28) and your final else needs an if before the test clause (L# 41). After that it should at least run. A few other comments:
only var a variable the first time it is declared.
look into switch statements and use them when you have more than 2 options and a default.
try to break up long lines (as in add line feeds) and keep all the code on the screen. Javascript doesn't care at all about white space so use it. Far easier to debug things when you can see it all.
a good editor will find this stuff for you. There are a lot of free ones (Atom, Komodo) that do a decent job of syntax highlighting and error detection.

wordwrap for textarea using javascript or jquery

I need functionality that textarea should contain max 5 lines and
each line should contain max 15 chars, when user writes 2 words
e.g. 123456 123456789 and if that line char limit exceeds 15, then
it should bring the 2nd word in next line along with \n char in first line
(means 123456 will be in first line along with \n and 123456789 will be in 2nd)
,
I need to maintain \n(replacing <br>) in my db for some reasons.
i wrote this code, which gives fuzzy result in some conditions
<textarea onkeypress="charCountTextarea('txt1',event,'75','14')" id="txt1"></textarea>
var countLines=0;
var newLines;
function charCountTextarea(textAreaId,e,limit,lineLen)
{
newLines = $("#"+textAreaId).val().split("\n").length;
var t = $("#"+textAreaId)[0];
var lineIndex = t.value.substr(0, t.selectionStart).split("\n").length-1;
//console.log("new lines"+lineIndex);
if(newLines >= 5 && $("#"+textAreaId).val().split("\n")[lineIndex].length>lineLen)
{
if( e.keyCode!=8 && e.keyCode!=46 && e.keyCode!=33 && e.keyCode!=34 && e.keyCode!=35 && e.keyCode!=36 && e.keyCode!=37 && e.keyCode!=38 && e.keyCode!=39 && e.keyCode!=40)
{
e.preventDefault();
return false;
}
}
else
if($("#"+textAreaId).val().split("\n")[lineIndex].length>=lineLen) // which will count the total line char condition
{
console.log($("#"+textAreaId).val().split("\n")[lineIndex][lineLen-1]);
if($("#"+textAreaId).val().split("\n")[lineIndex][lineLen-1].indexOf(" ")==-1 && e.keyCode!=8 && e.keyCode!=46 && e.keyCode!=33 && e.keyCode!=34 && e.keyCode!=35 && e.keyCode!=36 && lineIndex != 4 && newLines<5)
{
// to bring the word in next line
var str = $("#"+textAreaId).val(), replacement = '\n';
str = str.replace(/ ([^ ]*)$/,replacement+'$1');
$("#"+textAreaId).val(str);
}
else
if(e.keyCode!=8 && e.keyCode!=46 && e.keyCode!=33 && e.keyCode!=34 && e.keyCode!=35 && e.keyCode!=36 && lineIndex!=4 && newLines<5)
{
// to insert next line
insertTextAtCaret(document.getElementById(textAreaId), "\n");
}
}
if(e.keyCode == 13 && newLines >= 5)
{
//linesUsed.css('color', 'red');
e.preventDefault();
return false;
}
}
function charCountTextarea(textAreaId,e,limit,lineLen)
{
var code = e.charCode || e.keyCode;
newLines = $("#"+textAreaId).val().split("\n").length;
var t = $("#"+textAreaId)[0];
var lineIndex = t.value.substr(0, t.selectionStart).split("\n").length-1;
console.log('val t :'+$("#"+textAreaId).val()+' line index : '+lineIndex+' new lines '+newLines);
if(lineIndex == 10 && $("#"+textAreaId).val().split("\n")[lineIndex].length>(lineLen+1) && code!=8 && code!=46 && code!=33 && code!=34 && code!=35 && code!=36 && code!=37 && code!=38 && code!=39 && code!=40)
{
$("#"+textAreaId).val(($("#"+textAreaId).val()).substring(0, $("#"+textAreaId).val().length - 1));
alert('You are reached to limit');
return false;
}
if(lineIndex<5)
{
$("#"+textAreaId).val($("#"+textAreaId).val().wordWrap(lineLen, "\n", 0));
}
var countLine1 = $("#"+textAreaId).val().split("\n")[0].length;
if($("#"+textAreaId).val().split("\n")[lineIndex].length>lineLen) // which will count the total line char condition
{
console.log("In condition : ");
if(code!=8 && code!=46 && code!=33 && code!=34 && code!=35 && code!=36 && code!=37 && code!=38 && code!=39 && code!=40)
{
// to insert next line
insertTextAtCaret(document.getElementById(textAreaId), "\n");
}
}
}
You can not know if the input will be fed using keyboard. I could just use the mouse to paste a text there.
I would rely on a function that would constantly check the input and take the action you want, which I would execute using the setInterval() function once the textarea is focused, which then gets cleared using clearInterval() once the textarea loses focus.
And this function would use a RegExp to process the input and split it into necessary lines.
EDIT: Here's what I mean.
$('body').on('focus','#txt1',function(e) {
$(this).data('int',setInterval(checkInput,1));
}).on('blur','#txt1',function(e) {
clearInterval($(this).data('int'));
$(this).removeData('int');
});
function checkInput() {
var val = $('#txt1').val();
// process val here
$('#txt1').val(val);
}

Categories