Tic Tac Toe Extra Turns and other wierd problems

Tic Tac Toe Extra Turns and other wierd problems
0

#1

Here is my project. https://codepen.io/zapbampow/pen/NwbOpO

I am nearly done with the Tic Tac Toe project, but I have 3 problems that I haven’t been able to figure out.

First Problem: Computer taking over user turns

On the computer’s first turn during “impossible” mode, it is taking it’s turn, then the user’s next turn, and it’s next turn. So if the user is first player, the user adds an X to the board. Then the computer adds an O, X, and O to the board. I’ve added a pile of console logs to see what things are happening. The program seems to call playerTurn(), which is the function for what to do when the player clicks on the squares. Then it immediately jumps to the end of the aiTurn(), which is basically a random move and uses that for the playerTurn placement. I’m not sure how or why it is doing this.

Second Problem: Player turn displayed name out of order

This problem probably comes down to my using toggle to hide and unhide the div with the turn order information in it, but anyway… If you play through several games and both sides win some, then at some point the displayed player turn gets messed up. When it is the user turn it will say, “Computer’s Turn”. It does this in 2 Player games and against the computer.

When the game ends it asks, “Play again?” If the user clicks “Yes”, then the game resets.
I have tried to add a conditional after the game resets like this to fix it.

if($('.player-2-header').html() === "<h1>Computer's Turn</h1>" || $('.player-2-header').html() === "<h1>2nd Player's Turn</h1>") {
    $('.player-1-header, .player-2-header').toggle('.hidden');
  }

Final Problem, hopefully: End game board issue

When a game ends the board is supposed to be hidden and a new screen is unhidden that declares the winner and asks for a new game. However, in the “impossible” game when the computer wins, sometimes it does the hide/unhide as it is supposed to, then immediately reverses it. The user is stuck at the completed game and must refresh the page. I haven’t been able to figure out where in the code it is jumping to make it toggle both of those divs twice.

That’s it. Feel free to comment on any or all of these issues.

#2

@zapbampow I spent 20 minutes on it, but came up empty. Have you tried stepping through your program with Chrome’s debugger using breakpoints?


#3

The reason the computer keeps playing turns is because in your function gameEndOrNot, after the user clicks on their first square, the else code block inside gameEndOrNot runs which changes the player to the computer (changeCurrentPlayer()) and then your code executes the nextTurn function runs. Inside nextTurn, the else if code block executes the aiTurn function, because it is the computer’s turn. Inside aiTurn function, the applicable activatePlacement call is made which at somepoint runs gameEndOrNot again. Inside gameEndOrNot, the changeCurrentPlayer function is called so now the current player is the user and then when nextTurn runs the else block of code runs which executes playerTurn. I am going to stop here, because this back and forth continues until the computer has the board set to make the last winning move.

You need to resolve the problem I have pointed out first, before I will look at the other problems. Fixing the above issue could resolve one of the other issues.

Honestly, when I look at your aiTurn code, I fear there could be some typos which could inadvertently cause unforeseen logic issues as you code executes. You should strongly consider simplifying this logic without the use of all those if/else statements. If you did not look at this code for a couple of weeks and then came back to it, I guarantee it would take you a while to figure out exactly what the code is comparing.

Another side issue which needs to be fixed is every time the playerTurn function is called, you are adding extra click events to each square of the board. That will probably cause you problems at some point in your code. There should only be one click event for each box, but code is adding multiple events. If a box has let’s say 3 click events due to your playerTurn adding them to all the boxes, then the code in the callback function will attempt to execute 3 times (even on a button that has never been clicked before the current click).


#4

While I haven’t gotten everything fixed by any means, this plus what @RandellDawson talked through are immensely helpful in finding exactly where things are going wrong. I’ve used other Chrome/Firefox developer tools, but never the debugger. This would have been helpful before! Anyway, I’m getting there. Thanks pointing out this feature and pointing me to a short tutorial.


#5

I just want to thank you for looking at my code. As with each other time you’ve helped me, it was key to my moving forward with this project. My code is probably still messy, but it doesn’t loop around infinitely anymore. Your observation that the way I called for the player turns and that I was creating new click events each time I called the function were very helpful. I took that and @JB-Walker’s tip on how to use Chrome’s developer tools and was able to solve pretty much all the other problems. While there are likely still some small bugs, it works as it should now.


#6

Glad you got it all figured out. If you like, I can take another look at your working code and make suggestions for how to reduce lines of code.


#7

That would be awesome. I know I could do things differently than I have, but I’m working with what I know and understand.


#8

To get you started, you could replace both of the following functions for one player games:

function easyOnePlayerGame() {
  console.log("easyOnePlayerGame called.");
  if (currentPlayer.name === 'Computer') {
    setTimeout(easyComputerTurn, 500);
  }
}

/** The Combination of the pieces for playing against the computer on Impossible level, where the computer places randomly
* @easyOnePlayerGame
*/
function impossibleOnePlayerGame() {
  console.log("impossibleOnePlayerGame called.");
  if (currentPlayer.name === 'Computer') {
    setTimeout(aiTurn, 1000);
  }
}

with a single function called onePlayerGame:

function onePlayerGame(diffLevel) {
  console.log(diffLevel + " OnePlayerGame called.");
  if (currentPlayer.name === 'Computer') {
    if (diffLevel === "easy") {
      setTimeout(easyComputerTurn, 500);
	}
	else {
   	  setTimeout(aiTurn, 1000);
	} 
  }
}

Making the above changes would allow you to replace the following two click event handler functions for selecting the level of a one player game

//Easy
$('.easy').click(function() {
  difficultyLevel = 'easy';
  $('#difficulty-div').toggle('.hidden');
  $('.gameboard').toggle('.hidden');
  $('.headers').toggle('.hidden');
  easyOnePlayerGame();
});

//Impossible
$('.impossible').click(function() {
  difficultyLevel = 'impossible';
  $('#difficulty-div').toggle('.hidden');
  $('.gameboard').toggle('.hidden');
  $('.headers').toggle('.hidden');
  impossibleOnePlayerGame();
});

with a single click event handler function for divs with a class of “level”:

$('.level').click(function() {
	$('#difficulty-div').toggle('.hidden');
  $('.gameboard').toggle('.hidden');
  $('.headers').toggle('.hidden');	
	difficultyLevel = $(this).hasClass('easy') ? 'easy' : 'impossible';
	onePlayerGame(difficultyLevel);
});

Using a similar logic as I have above, see if you can figure out how to create a single click event function to replace both of the following. There is so much repetitive code in these two functions.

$('.first-player').click(function() {
.
.
});

$('.second-player').click(function() {
.
.
});

#9

Thanks. I’ve implemented both of those. I also saved a few lines making a function that does all the hiding and unhiding of the divs in the gameWon and gameDraw functions and call that instead of repeating the same code 3 times.

For first-player and second-player click event I used this code.

  if($(this).hasClass('first-player')){
    firstPlayer.name = 'You';
    secondPlayer.name = 'Computer';
  }
  else {
    firstPlayer.name = 'Computer';
    secondPlayer.name = 'You';
  }

  $('#order').toggle(".hidden");
  $('#difficulty-div').toggle('.hidden');
  changePlayerHeader();
});

I imagine if can use similar logic to make a single call on all the squares instead of one for each square, although I haven’t had a chance to tackle that yet.

As I looked at what you wrote yesterday, I noticed through some research that you used a ternary operator on the difficultyLevel variable. I haven’t spent much time familiarizing myself with ES2015 yet, but I do understand how what you did works. Any resources you can point me to better understand the syntax for ES2015 and get practice with it?


#10

Actually, ternary operators have been around since the beginning of JavaScript.

As far as replacing all the box click events, I will get you started, but let you figured the details.

Since all the boxes have a class called “box”, you could do this:

$('.box').click(function() {
  .
  .
  ..
	var boxID = $(this)[0].id;
	boardPosition = Number(boxID[boxID.length-1]);  // last character of boxID
  if (liveBoard[boardPosition] === null && currentPlayer.name != "Computer") {
    activatePlacement(boardPosition, currentPlayer);
  }	
});

Also, since you put the number of the box at then end of the id name (i.e. id=“box5”), you will first need to figure out how to reference the id and then how to get the last character of the id.

HINT: You will need to use JQuery’s $(this) inside the click event callback function. $(this) is the DOM element clicked. Using google or the JQuery documentation, you should be able to find a JQuery function which lets you get the id value easily.

Happy Coding!