Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Tushar Singh
PLUS
Tushar Singh
Courses Plus Student 8,692 Points

Can't stop the toggling.

http://codepen.io/tushar_13/pen/zqPQOp

Try to play this game, what you will notice is that the icons keep on toggling, why is that?? What am I missing here and how can I fix that?

Andrew Liu
Andrew Liu
2,357 Points

I was able to play a few games without a toggling problem. Can you provide more specific steps to reproduce the problem?

Tushar Singh
Tushar Singh
Courses Plus Student 8,692 Points

Well try clicking on the same icon again and again and watch, second problem you as a user just can't win(not recognizing my wincheck() function), well there are many other problems as well, for starters I am trying to fix these complications.

2 Answers

Steven Parker
Steven Parker
230,274 Points

But you forgot to remove the toggling from symbDisp!

Anyway, here's a modified version. I got a bit carried away and made a few other enhancements, some are style choices so just keep the ones you like:

$(function() {
  var player;
  var comp;
  var pTurn;
  var gameEnd = false;
  var turns = 1;

  $('.icon').on('click', userIcon);
  // game function at the end
  $('.square').on('click', function() { game(this) });
  $('#reset').on('click', restart);

  // get user icon
  function userIcon() {
    if ($(this).attr("id") == "x") {
      player = "X";
      comp = "O";
    } else if ($(this).attr("id") == "o") {
      player = "O";
      comp = "X";
    }
    $('#user').hide(1000);
    reset();
  }

  //show icons
  function symbDisp(ths) {
    if ($(ths).text())
      return false;
    $(ths).text(pTurn);
    return true;
  }
  // full restart
  function restart() {
    $('#user').show(1000);
    player = comp = undefined;
    reset();
  }
  // reset the game
  function reset() {
    $('.square').text('').css('background-color', 'red');
    turns = 1;
    gameEnd = false;
  }
  // win conditions
  function winCheck() {
    var wins = [ "123", "456", "789", "147", "258", "369", "159", "357" ];
    for (var i in wins)
      if (rowCheck('#' + wins[i][0], '#' + wins[i][1], '#' + wins[i][2])) return;
    drawCheck();
  }

  function rowCheck(a, b, c) {
    if ($(a).text() == pTurn && $(b).text() == pTurn && $(c).text() == pTurn) {
      $(a + ',' + b + ',' + c).css('background-color',
                                   pTurn==player ? 'green' : 'black');
      gameEnd = true;
      setTimeout(reset, 1500);
      return true;
    }
    return false;
  }
  // moves 9 and still no one wins--draw
  function drawCheck() {
    if (turns === 9) {
      $(".square").css('background-color', 'orange');
      setTimeout(reset, 1500);
    }
  }

  // comp moves(very simple at this point)
  function AI() {
    for (var i=1; i<10; i++) {
      if (!$('#' + i).text()) {
        $('#' + i).text(pTurn);
        return;
      }
    }
  }
  // every function is here
  function game(ths) {
    pTurn = player;
    if (!symbDisp(ths)) return;
    winCheck();
    turns += 1;
    if (!gameEnd) {
      pTurn = comp;
      AI();
      winCheck();
      turns += 1;
    }
  }
});

Try comparing side-by-side and see you can guess the reason for each change. :mortar_board:

Tushar Singh
Tushar Singh
Courses Plus Student 8,692 Points

Thanks a lot brother, I love you :stuck_out_tongue:. Using "for" loop is a much better way. Your code is short and clean. I told you only you can help me, here most of the people are dumb.

I just have a few questions

1) pTurn==player ? 'green' : 'black');--- It's like if-else right?

2) here(wirtten in the code below, I was using gameEnd=false && turns%2===0--- why don't we need "turns%2==0". ) This part is a little confusing.

function game(ths) {
    pTurn = player;
    if (!symbDisp(ths)) return;
    winCheck();
    turns += 1;
    if (!gameEnd) { //here
      pTurn = comp;
      AI();
      winCheck();
      turns += 1;
    }

3) I need your advice or suggestion because you have seen a lot of my work so you know how I do stuff. I don't use "return true/false". I mean if I read it, I can understand the code but I just don't know how to use this kind of coding and where. Can you help me with that?

Steven Parker
Steven Parker
230,274 Points

Everyone is here to learn, some are just further along. :wink:

  1. Yes! It's like "if...else", but it returns a value. It's called the ternary operator. If what's in front of the "?" is true, then it returns the first thing (between "?" and ":"), and if the first part is false, it returns what is after the ":". Use sparingly, it can be confusing! It was a quick way to have wins marked in a different color, which seemed nicer.

  2. I reduced the test to just !gameEnd ("the game is not over") because it really doesn't matter what the turn number is. Since the player just moved, and the game is not over, the computer should always move. This will really pay off if you ever add an option to let the computer move first.

  3. Returning a true or false can be very handy for checking if a function did a particular thing, or checking if there was some kind of error. In symbDisp I returned true if a mark was made, and false if one is NOT made (because the box is already filled). Then, by testing the value (with if) when it is called, the game function can return without doing anything. Before I added that, when you clicked on a square already marked, the computer took an extra turn.
    :small_orange_diamond: And rowCheck helps keep winCheck "DRY". It's true if there's a win and false if not (and you keep checking).

You have a great enthusiasm for programming. Happy coding!

Tushar Singh
Tushar Singh
Courses Plus Student 8,692 Points

Thanks, I will keep that in mind.Ternary operator is a pretty handy tool, better way to write code I guess(basically short and I like short and clean codes).

Next time surely I will use "return", let's see how things work out.

Steven Parker
Steven Parker
230,274 Points

Looks like you toggle the icon every time a mark is made, so if the player makes the last move, the first mark made in the next game (by the player) will be the computer icon. You set pTurn to player at the end of game, you could also set it to comp right before you call AI and remove the toggling from symbDisp.

Also due to the toggle in symbDisp, winCheck will not detect a player win.

Remember that setTimeout does not pause the program, so until the time elapses you can keep making moves after the game is over.

That AI function is committing "switch abuse" :stuck_out_tongue_winking_eye:. That should probably be an if chain like you have in winCheck.

Tushar Singh
Tushar Singh
Courses Plus Student 8,692 Points

Done if chain in AI(); Done adding "pTurn=comp"---> Not working. When I added pturn=comp, things are getting even more messy, when I choose "x" as my icon, I still have an "o".

Please bro,Do something lol, Probably you know I'm stuck on this for a while now and I can't see a way out of it.