0
votes

I have created a tic tac toe game in Javascript. There is a strange problem in code. It is working fine unless you press the reset button. If you press the reset button in beginning the computer will put multiple 'O's in board. for example: if you press Reset button 6 times there will be 6 'O's in the board as soon as you put your first 'X'.

please help me here is my codepen : https://codepen.io/raj1998211/pen/rJwJWy?editors=1011

(if u have any doubts please ask)

<html>
<head>
    <title>Game</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <script type="text/javascript" src="jquery.js"></script>

    <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="style.css">

</head>
<body>

<h1>Tic Tac Toe</h1>
<div style="text-align: center;">
<!-- <button onclick="reset();">Reset</button> -->
</div>
<table>
    <tr>
        <td class="box" id="1" style="display: table-cell; text-align: center;"></td>
        <td class="box" id="2" style="display: table-cell; text-align: center;"></td>
        <td class="box" id="3" style="display: table-cell; text-align: center;"></td>
    </tr>
    <tr>
        <td class="box" id="4" style="display: table-cell; text-align: center;"></td>
        <td class="box" id="5" style="display: table-cell; text-align: center;"></td>
        <td class="box" id="6" style="display: table-cell; text-align: center;"></td>
    </tr>
    <tr>
        <td class="box" id="7" style="display: table-cell; text-align: center;"></td>
        <td class="box" id="8" style="display: table-cell; text-align: center;"></td>
        <td class="box" id="9" style="display: table-cell; text-align: center;"></td>
    </tr>
</table>

<div id="winner">
  <p id="winnerText">Winner will be here!!</p>
  <button onclick="reset();">Reset</button>
  <button onclick="reset();">Reset all</button>
</div>

<div id="start">
    <p>Select</p>
    <button id="cross" onclick="selector(this);">X</button>
    <button id="circle" onclick="selector(this);">O</button>
</div>

<script type="text/javascript" src="app.js"></script>


  </body>
</html>

js

var player;
function selector(elem){
    console.log(elem.id);
    $("#start").css("display", "none");
    player = elem.id;
    if(player === "cross"){

    }
    else if(player === "circle"){

    }
}

var px = [];
var po = [];
var winArr = [];

$(".box").on("click", newClick);

// function clkfun(){
//  if (turn === 2)
//  {
//      $(this).addClass("fa fa-times fa-3x");
//      $(this).unbind( "click" );
//      turn--;
//      px.push(Number(this.id));
//      if(res() === "x"){
//          alert("X won.");
//          reset();
//          $("#winner").text("X won");
//      }
//      return;
//  }
//  if (turn === 1)
//  {
//      $(this).addClass("fa fa-circle-o fa-3x");
//      $(this).unbind( "click" );
//      turn++;
//      po.push(Number(this.id));
//      if(res() === "o"){
//          alert("O won.");
//          reset();
//          $("#winner").text("O won");
//      }
//  }
// }

function newClick() {
    $(this).addClass("fa fa-times fa-3x");
    $(this).unbind("click");
    px.push(Number(this.id));
    gameover = false;
    if (res() === "x") {
        // alert("X won.");

        $("#winner").css("display", "block");
        $("#winnerText").text("X won");
        $(".box").unbind("click", newClick);
        gameover = true;
        //reset();
    }

    var test;
    if (px.length === 1) {
        test = generateRandom(1, 9);
    }
    else {
        test = position(px);
        if (test == 99) {
            // alert("Draw");
            $("#winner").css("display", "block");
            $("#winnerText").text("Draw !!");
            $(".box").unbind("click", newClick);
            // reset();
        }
    }

    if (gameover === false) {
        var stest = test.toString();
        $("#" + stest).addClass("fa fa-circle-o fa-3x");
        $("#" + stest).unbind("click");
        po.push(test);
        if (res() === "o") {
            // alert("O won.");
            // reset();
            // $("#winner").text("O won");
            $("#winnerText").text("O won");
            $("#winner").css("display", "block");
            $(".box").unbind("click", newClick);
        }
    }

}


function position(px) {
    if ((px.includes(1) && px.includes(2)) && !po.includes(3))
        return 3;
    if ((px.includes(1) && px.includes(3)) && !po.includes(2))
        return 2;
    if ((px.includes(1) && px.includes(4)) && !po.includes(7))
        return 7;
    if ((px.includes(1) && px.includes(7)) && !po.includes(4))
        return 4;
    if ((px.includes(1) && px.includes(5)) && !po.includes(9))
        return 9;
    if ((px.includes(1) && px.includes(9)) && !po.includes(5))
        return 5;

    if ((px.includes(2) && px.includes(3)) && !po.includes(1))
        return 1;
    if ((px.includes(2) && px.includes(5)) && !po.includes(8))
        return 8;
    if ((px.includes(2) && px.includes(8)) && !po.includes(5))
        return 5;

    if ((px.includes(3) && px.includes(2)) && !po.includes(1))
        return 1;
    if ((px.includes(3) && px.includes(6)) && !po.includes(9))
        return 9;
    if ((px.includes(3) && px.includes(9)) && !po.includes(6))
        return 6;
    if ((px.includes(3) && px.includes(5)) && !po.includes(7))
        return 7;
    if ((px.includes(3) && px.includes(7)) && !po.includes(5))
        return 5;

    if ((px.includes(4) && px.includes(7)) && !po.includes(1))
        return 1;
    if ((px.includes(4) && px.includes(5)) && !po.includes(6))
        return 6;
    if ((px.includes(4) && px.includes(6)) && !po.includes(5))
        return 5;

    if ((px.includes(6) && px.includes(5)) && !po.includes(4))
        return 4;
    if ((px.includes(6) && px.includes(9)) && !po.includes(3))
        return 3;

    if ((px.includes(7) && px.includes(8)) && !po.includes(9))
        return 9;
    if ((px.includes(7) && px.includes(9)) && !po.includes(8))
        return 8;
    if ((px.includes(7) && px.includes(5)) && !po.includes(3))
        return 3;

    if ((px.includes(8) && px.includes(9)) && !po.includes(7))
        return 7;
    if ((px.includes(8) && px.includes(5)) && !po.includes(2))
        return 2;

    if ((px.includes(9) && px.includes(5)) && !po.includes(1))
        return 1;


    else if (px.length + po.length != 9)
        return generateRandom(1, 9);
    else
        return 99;
}

function generateRandom(min, max) {
    var num = Math.floor(Math.random() * (max - min + 1)) + min;
    return (px.includes(num) || po.includes(num)) ? generateRandom(min, max) : num;
}

function reset() {
    $(".box").removeClass("fa fa-times");
    $(".box").removeClass("fa fa-circle-o");
    px = [];
    po = [];
    for (i = 0; i < winArr.length; i++) {
        var newId = winArr[i].toString();
        $("#" + newId).css("color", "black");
    }
    winArr = [];
    //$("#winnerText").text("Winner will be here!!");
    $("#winner").css("display", "none");
    $(".box").bind("click", newClick);
}

function line(arr, i, j, k) {
    if (arr.includes(i) && arr.includes(j) && arr.includes(k))
    {
        winArr.push(i,j,k);
        for(i = 0; i < winArr.length; i++){
            var newId = winArr[i].toString();
            $("#" + newId).css("color", "green");
        }
        // console.log(winArr);
        return true;
    }
    else
        return false;
}

function res() {
    if (line(px, 1, 2, 3) || line(px, 4, 5, 6) || line(px, 7, 8, 9) || line(px, 1, 5, 9) || line(px, 3, 5, 7) || line(px, 1, 4, 7) || line(px, 2, 5, 8) || line(px, 3, 6, 9)) {
        return "x";
    }
    else if (line(po, 1, 2, 3) || line(po, 4, 5, 6) || line(po, 7, 8, 9) || line(po, 1, 5, 9) || line(po, 3, 5, 7) || line(po, 1, 4, 7) || line(po, 2, 5, 8) || line(po, 3, 6, 9)) {
        return "o";
    }
    else
        return "raj";
}

css

.box {
    background-color: #ffffff;
    height: 100px;
    width: 100px;
    border: 3px solid black ;
}

table {
    margin: 50px auto;

}

#winner {
    text-align: center;
    margin-bottom: 20px;
}

h1 {
    text-align: center;
}

.fa-times {
    color: black;
}
.fa-circle-o {
    color: black;
}

#winner{
  background-color:grey;
  height: 200px;
  width: 200px;
  margin: auto;
  margin-top: -300px;
  display:none;
}
#winnerText{
  font-size:40px;
  margin:auto;
  font-weight:bold;
}

#start{
  background-color:grey;
  /* position: fixed; */
  height: 200px;
  width: 200px;
  margin: auto;
  margin-top: -300px;
  display:block;
}





/* for table */
table tr:first-child td{
  border-top:0;
}
table tr:last-child td{
  border-bottom:0;
}
table tr td:first-child{
  border-left:0;
}
table tr td:last-child{
  border-right:0;
}
1
If you do something x times, and it results in x number of outputs, when i should only have one, that usually indicates that you are duplicate binding event handlers.Taplar
how can i stop that?Raj
Don't duplicate bind event handlers. Only bind once. One area of concern from looking over your code is you are intermixing inline bindings with dynamic bindings. You could probably change them all to one or the other. I would suggest dynamic bindings so they are all in one place and managable. Also, if you find yourself unbinding and binding event handlers in your logic, I would suggest you look at delegate event bindings which will allow you to manipulate the child selector to match or not match conditionally based on the state of your 'app'.Taplar
Its not working with delegate. is this similar to my problem ?stackoverflow.com/questions/6361465/…Raj

1 Answers

0
votes

The problem occur due to binding of click event multiple times on function call. so i have added a single line in the reset function that will first unbind the click event listener then it will bind on following lines

    function reset() {
       $(".box").off("click", newClick);
    .....
    .....
       $(".box").on("click", newClick);
    }