Make players take turns playing tic-tac-toe in JavaScript - javascript

I just picked up JavaScript and want to make my tic-tac-toe game object-oriented. Right now I'm having trouble making my players take turns. Below I've made a global variable turn that starts off as true, then alternates between true and false as a player clicks on the board. The HTML is not provided here, but each grid in the 3x3 board is a form.
If turn === true, it should be player_1's turn, and player_2 otherwise, but it isn't working. Any ideas on what I should be doing to get it right? I understand that the "if...else if" statement at the bottom runs only once, which is why it isn't working. Any ideas on what and where my conditional statement should be for it to work?
$(document).ready(function() {
var turn = true;
var Player = function(id,symbol){
this.symbol = symbol;
this.id = id;
function playerMove(player){
$("#tictac").on("click", function(event){
event.preventDefault();
var $button = $(event.target);
$button.val(symbol);
turn = turn ? false : true;
console.log(checkIfWinner());
console.log("turn:"+turn);
})
};
this.playerMove = playerMove;
function checkIfWinner(player) {
var $board = $("#tictac").children();
if ($board.find("#cell0").children().val() == symbol &&
$board.find("#cell1").children().val() == symbol &&
$board.find("#cell2").children().val() == symbol)
return true;
if ($board.find("#cell2").children().val() == symbol &&
$board.find("#cell5").children().val() == symbol &&
$board.find("#cell8").children().val() == symbol)
return true;
if($board.find("#cell0").children().val() == symbol &&
$board.find("#cell3").children().val() == symbol &&
$board.find("#cell6").children().val() == symbol)
return true;
if ($board.find("#cell0").children().val() == symbol &&
$board.find("#cell4").children().val() == symbol &&
$board.find("#cell8").children().val() == symbol)
return true;
if ($board.find("#cell2").children().val() == symbol &&
$board.find("#cell4").children().val() == symbol &&
$board.find("#cell6").children().val() == symbol)
return true;
if ($board.find("#cell3").children().val() == symbol &&
$board.find("#cell4").children().val() == symbol &&
$board.find("#cell5").children().val() == symbol)
return true;
if ($board.find("#cell6").children().val() == symbol &&
$board.find("#cell7").children().val() == symbol &&
$board.find("#cell8").children().val() == symbol)
return true;
if ($board.find("#cell1").children().val() == symbol &&
$board.find("#cell4").children().val() == symbol &&
$board.find("#cell7").children().val() == symbol)
return true;
return false;
}
this.checkIfWinner = checkIfWinner;
};
var startGame = function(player_1,player_2){
this.player_1 = player_1;
this.player_2 = player_2;
setMessage('<p>'+ player_1.symbol + ' starts the game</p>');
function setMessage(msg){
$("#message").html(msg);
};
};
var player_1 = new Player(1,"X");
var player_2 = new Player(2,"O");
var game = new startGame(player_1,player_2);
if (turn === true){
game.player_1.playerMove();
}
else if (turn === false){
game.player_2.playerMove();
}
game.player_1.playerMove();
});

This is not enough:
if (turn === true){
game.player_1.playerMove();
}
else if (turn === false){
game.player_2.playerMove();
}
You have to negate turn. Also, you can simplify the if ... else conditions:
if (turn) {
game.player_1.playerMove();
} else {
game.player_2.playerMove();
}
turn = !turn; // Negate the value to alternate moves.
You can write the player move even more succinctly, if you wish:
game['player_' + (turn ? '1' : '2')].playerMove();
Don't forget to put the player moves inside a loop of some kind:
while (true) {
// Make the player move.
// Check if the game is over.
// Has the player won? Is the board full? Display an appropriate message.
if (gameOver) {
break; // Break out of the loop if the game is over.
}
turn = !turn;
}

Related

What is the best way to deal with multiple else if statements to increase speed in Google Apps Script?

I have a function that is supposed to unhide certain columns, but only if other filters that relate to the columns are not in use. Because there are 4 other filters that it needs to check to see if they are in use (either 'true' or 'false'), there are 16 possibilities that the function needs to run through.
I've used else if statements to accomplish this and it does work, but it is incredibly slow. I was wondering if there is a more appropriate way to deal with all the possible options that is faster.
This is the code I currently have (sorry if I've shown too much, not sure how much I need to include):
function Unfilter(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var numCols = sheet.getRange(1,3).getValue(); //gets the number of columns to loop through
var xRow = sheet.getRange(1,5).getValue() + 16; //gets the target row to run the blank check on
// check filter statuses
var nameShow = sheet.getRange(1,1).getValue();
var statusShow = sheet.getRange(2,1).getValue();
var dateShow = sheet.getRange(3,1).getValue();
var evidenceShow = sheet.getRange(4,1).getValue();
//loop through all target columns and unhide all columns that are not filtered
for (var i=10; i<=numCols; i++) {
if (sheet.getRange(xRow,i).getValue() == "") {
var catType = sheet.getRange(16,i).getValue();
if (nameShow == true && statusShow == true && dateShow == true && evidenceShow == true) {
sheet.showColumns(i)
} else if (nameShow == false && statusShow == true && dateShow == true && evidenceShow == true) {
if(catType !== "Name") {
sheet.showColumns(i);
}
} else if (nameShow == false && statusShow == false && dateShow == true && evidenceShow == true){
if (catType == "Date" || catType == "Evidence") {
sheet.showColumns(i);
}
} else if (nameShow == false && statusShow == true && dateShow == false && evidenceShow == true) {
if (catType == "Status" || catType == "Evidence") {
sheet.showColumns(i);
}
} else if (nameShow == false && statusShow == true && dateShow == true & evidenceShow == false){
if (catType == "Status"|| catType == "Date") {
sheet.showColumns(i);
}
} else if (nameShow == false && statusShow == false && dateShow == false && evidenceShow == true){
if (catType == "Evidence") {
sheet.showColumns(i);
}
} else if (nameShow == false && statusShow == false && dateShow == true && evidenceShow == false){
if (catType == "Date") {
sheet.showColumns(i);
}
}
//...etc for all 9 remaining possibilities
}
}
}
Even if you can't speed up showColumns() function you can significantly accelerate your script if you will use getValue() or getValues() as few as it possible. For example here you invoke these functions more than 7 times on the same sheet with static data. It's far from best practices:
You could use getValues() just once to get all data from the sheet and analyze the array instead. It would be much faster. getValue() / getValues() are quite slow and intentionally limited functions.
For example:
var data = sheet.getRange(1,1,10,10).getValues(); // get the array
var numCols = data[0][2];
var xRow = data[0][4] + 16;
var nameShow = data[0][0];
var statusShow = data[1][0];
var dateShow = data[2][0];
var evidenceShow = data[3][0];
// etc
I think it will be about five seconds faster already.
And it will even more faster if you change getRange(xRow,i).getValue() to data[xRow-1][i-1] in the loop.

How to recreate the same effect of highlighting with the reference attached?

I am working on a typing game and I am trying to imitate the same effect of highlighting of the characters to be typed like in https://www.ratatype.com/typing-test/test/. However, it was harder than what I imagined.
I created code which uses replace function to give style on the character. However, it only replaces the first instance of the character and the other instances, it won't do what it was supposed to do.
const originTextDiv = document.getElementById('origin-text'); //the div that was supposed to be highlighted.
function spellCheck() {
let textEntered = textArea.value;
//let textCharEntered = textEntered.split("");
let originTextMatch = originText.substring(0, textEntered.length);
// console.log(textEntered);
var key = event.keyCode || event.charCode;
originChar = originText.split("");
//console.log(originTextDiv);
char = originTextDiv.textContent;
//console.log(char);
console.log(originChar[index]);
//console.log(textCharEntered);
if (textEntered == originText) {
textWrapper.style.borderColor = 'orange';
$("#myModal").modal();
stop();
} else {
if (textEntered == originTextMatch) {
textWrapper.style.borderColor = 'green';
textArea.style.backgroundColor = 'white';
// if (!(key == 20 || key == 16 || key == 18 || key == 8 || key == 46 || key ==17 )){
originTextDiv.innerHTML = char.replace(originChar[index], '<span style="background-color:green;">' +originChar[index]+ '</span>'); //here is the code where it is highlighting
++index;
// }
// if (key == 8 || key == 46){
// --index;
// }
} else {
textWrapper.style.borderColor = 'red';
textArea.style.backgroundColor='f23a49';
originTextDiv.innerHTML = char.replace(originChar[index], '<span style="background-color:red;">' +originChar[index]+ '</span>');
if (!(key == 8 || key == 46)){
error++;
}
}
}
}
I expected to have it work as intended but I didn't knew replace only replaces the first instance. I tried to look at on replaceAt functions for javascript but when I tried it the replaceAt also replaces the span tag attached. Could someone give me some pointers on how to recreate the same effect as the reference above?

if statements being skipped even when both expressions are true

I have a webpage that populates a table with arrays. It has a doClick function so that when a user clicks on a cell it passes the row and column of the cell to the function. Example cell: onclick="doClick(0,1)"
function doClick(row, col)
{
var top = row -1;
var bottom = row +1;
var left = col -1;
var right = col +1;
var swapped = false;
if ((top != -1) && (cells[top][col].innerHTML = ""))
{
cells[top][col].innerHTML = cells[row][col].innerHTML;
cells[row][col].innerHTML = "";
swapped = true;
}
else if ((right != 4) && (cells[row][right].innerHTML = ""))
{
cells[row][right].innerHTML = cells[row][col].innerHTML ;
cells[row][col].innerHTML = "";
swapped = true;
}
else if ((bottom != 4) && (cells[bottom][col].innerHTML = ""))
{
cells[bottom][col].innerHTML = cells[row][col].innerHTML;
cells[row][col].innerHTML = "";
swapped = true;
}
else if ((left != -1) && (cells[row][left].inn = ""))
{
cells[row][lef].innerHTML = cells[row][col].innerHTML;
cells[row][col].innerHTML = "";
swapped = true;
}
else
{
alert("Illegal Move.");
}
. The problem is, even if both if expressions are true, the if statement is being skipped and it's falling through to the else statement. I've desk checked it and run it through the developer tools and checked values. A statement that was true on both expressions was skipped. Any suggestions?
cells[row][right].innerHTML = ""
is wrong. You are missing the double (triple) =.
The correct way should be...
cells[row][right].innerHTML === ""
It looks like maybe there are a few typos or misconceptions in your code.
A quick note about Conditions in an IF statement
A statement like (cells[top][col].innerHTML = "") as a condition will always return true as this is setting cells[top][col].innerHTML as "" or at least instantiating the variable. So, the proper condition to test absolutely true or false would be (cells[top][col].innerHTML === ""). However, you can get away with not even doing that and simply replace (cells[top][col].innerHTML = "") with cells[top][col].innerHTML. You may run into some other issues though is the variable is not instantiated already, either way. I would wrap the latter logic in an IF statement to check if cells[top][col].innerHTML is even instantiated.
To fix this, check out the following modifications I have made to your code.
function doClick(row, col)
{
var top = row -1;
var bottom = row +1;
var left = col -1;
var right = col +1;
var swapped = false;
if(typeof cells[top][col].innerHTML !== 'undefined' $$ cells[top][col].innerHTML !== null)
{
if ((top != -1) && cells[top][col].innerHTML !== '')
{
cells[top][col].innerHTML = cells[row][col].innerHTML;
cells[row][col].innerHTML = "";
swapped = true;
}
else if ((right != 4) && cells[row][right].innerHTML !== '')
{
cells[row][right].innerHTML = cells[row][col].innerHTML ;
cells[row][col].innerHTML = "";
swapped = true;
}
else if ((bottom != 4) && (cells[bottom][col].innerHTML))
{
cells[bottom][col].innerHTML = cells[row][col].innerHTML;
cells[row][col].innerHTML = "";
swapped = true;
}
else
{
alert("Illegal Move.");
}
}
else if (typeof cells[row][left].inn !== 'undefined' && (left != -1) && cells[row][left].inn !== '')
{
cells[row][lef].innerHTML = cells[row][col].innerHTML;
cells[row][col].innerHTML = "";
swapped = true;
}
else
{
alert("Illegal Move.");
}
}
An example working to demonstrate the above code
var testVar1 = '';
var testVar2 = 'Hello';
// var testVar3; <- Left this un-instantiated to test existance
// Testing if a var is empty but exists
if(typeof testVar1 !== 'undefined' && testVar1 !== null){
if(testVar1 !== ''){
alert('testVar1 has a value!');
}{
alert('testVar1 does not have a value!');
}
}
// Testing if a var is empty but exists
if(typeof testVar2 !== 'undefined' && testVar2 !== null){
if(testVar2 !== ''){
if(testVar2 === 'Hello'){
alert('testVar2 has a value! Value: ' + testVar2);
}{
alert('testVar2 has a value but it is not the one we expected.');
}
}{
alert('testVar2 does not have a value!');
}
}
// Test existance
if(typeof testVar3 !== 'undefined' && testVar3 !== null){
alert('testVar3 exists!');
}else{
alert('testVar3 does not exist!');
}

Double Condition

It will make selection words starting with "p" and ending with "a". Why it didnt work?
function checkWord(word) {
if (word.charAt(0) = 'p' && word.charAt(word.length - 1) = 'a') {
return true;
} else {
return false;
}
= is used for assigning values, not checking them. Use == for checking the values and === for checking value and types. So, your code should be like:
function checkWord(word) {
if (word.charAt(0) === 'p' && word.charAt(word.length - 1) === 'a') {
return true;
} else {
return false;
}
This should do the trick.
You didn't put 2 equals to if you only put 1 equals you are assigning it and if you put 2 equals you're comparing it the. below code should help
/* Check weather the first letter is equals to p and the last letter is equals to a. */
function checkWord(word) {
let firstPAndLastA = false;
if(word != null){
if (word.charAt(0) == 'p' && word.charAt(word.length - 1) == 'a') {
firstPAndLastA = true;
} else {
firstPAndLastA = false;
}
}
return firstPAndLastA;
}
//Calling Function
console.log(checkWord("ppoa"))

Infinite loop found on line 0

I am trying to make a tic-tac-toe game, but in Codepen my Javascript gives me an error saying "Infinite loop found on line 0. The line number is approximated so look carefully." Here is the pen
See the Pen Tic-Tac-Toe by Maris (#spacegeek224) on CodePen.
and here is my JS:
(function() {
var PLAYERS = ['X','O'];
var TURN = PLAYERS[0];
var BOARD = [new Array(3),new Array(3),new Array(3)];
function togglePlayer() {
TURN = (TURN == PLAYERS[0]) ? PLAYERS[1] : PLAYERS[0];
}
$('.square').click(function(e) {
if ($(e.target).attr('data-p')) {
} else {
$(e.target).attr('data-p',TURN).text(TURN);
BOARD[$(e.target).attr('data-y')][$(e.target).attr('data-x')] = TURN;
if (checkWin(BOARD)) {
$('.turn').attr('data-p',checkWin(BOARD)).text(checkWin(BOARD) + " wins!");
$('.square:not([data-p])').attr('data-p',true);
}
else {
togglePlayer();
$('.turn').attr('data-p',TURN).text(TURN+"\'s turn");
}
}
});
function checkWin(board) {
for (var i = 0;i<3;i++) {
if (board [0][i] !== undefined)
if (board[0][i]==board[1][i] && board[1][i] == board[2][i])
return board[0][i];
else if (board [i][0] !== undefined)
if (board[i][0]==board[i][1] && board[i][1] == board[i][2])
return board[i][0];
else if (board[1][1] !== undefined)
if (board[0][0] == board[1][1] && board[1][1] == board[2][2])
return board[1][1];
else if (board[0][2] == board[1][1] && board[1][1] == board[2][0])
return board[1][1];
}
return false;
}
$('.container').on('dblclick',function() {
$('.square').removeAttr('data-p').text('');
BOARD = [new Array(3),new Array(3),new Array(3)];
TURN = PLAYERS[0];
$('.turn').attr('data-p',TURN).text(TURN+"\'s turn");
});
})();
I figured it out, and i cant believe I didn't see this before. In the checkWin function I should have put if instead of if else inside the for loop.

Categories

Resources