Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                
<div class="wrapper container-fluid">
    <!--THIS contains the top row - X or 0 -->


    <div id="header">
        <h1 class="text-center">Tic Tac Toe</h1>
    </div>

    <div class="row" id="choices">
        <div class="ultra-xs col-xs-3 col-xs-offset-2 col-sm-3 col-sm-offset-3 col-md-3 col-md-offset-3 col-lg-2 col-lg-offset-4" id="ex">
            <div class="input-holder">
                <!--<input type="text" id="xInp" class="player-choice" placeholder="X">-->
                <button class="waves-effect waves-light btn player-choice valign-wrapper" id="xInp">X
                    <span id="xScore" class="score valign">-</span>
                </button>
                <!--<span id="xScore" class="score">-</span>-->
            </div>



        </div>
        <div class="ultra-xs col-xs-3 col-xs-offset-2 col-sm-offset-1 col-sm-3 col-md-3 col-lg-2 col-lg-offset-1" id="zero">
            <div class="input-holder">
                <!--<input type="text" id="oInp" class="player-choice" placeholder="O">-->
                <button class="waves-effect waves-light btn player-choice" id="oInp">O
                    <span id="oScore" class="score">-</span>
                </button>
                <!--<span id="oScore" class="score">-</span>-->
            </div>


        </div>

    </div>

    <div id="feedback-container">
        <p id="feedback">Start game or select player</p>
    </div>

    <!--<div id="table-div">-->
    <div id="table-div">
        <table class="table table-bordered" id="app">
            <tr>
                <td id="one" class="cell"></td>
                <td id="two" class="cell"></td>
                <td id="three" class="cell"></td>
            </tr>
            <tr>
                <td id="four" class="cell"></td>
                <td id="five" class="cell"></td>
                <td id="six" class="cell"></td>
            </tr>
            <tr>
                <td id="seven" class="cell"></td>
                <td id="eight" class="cell"></td>
                <td id="nine" class="cell"></td>
            </tr>
        </table>
    </div>
    <!--</div>-->

    <div class="button-holder">
        <button type="button" class="btn btn-lg btn-block btn-custom" id="restart">
            <span class="glyphicon glyphicon-repeat" aria-hidden="true"></span>
            Restart</button>
        <button type="button" class="btn btn-lg btn-block btn-custom" id="reset">
            <span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
            Reset</button>

    </div>

    <div class="push">
        <!-- this element for implementing sticky footer-->
    </div>
</div>


<footer class="footer">
    <span>&copy; 2017 Developed by Manish Giri for FreeCodeCamp</span>
</footer>

              
            
!

CSS

              
                @import url('https://fonts.googleapis.com/css?family=Mirza|Ravi+Prakash|Roboto');

html, body {
    height: 100%;
    margin: 0;
}

body {
    background-color: #F8F8F8;
}

#header {
    margin-left: auto;
    margin-right: auto;
    margin-bottom: 10px;
}
#header h1 {
    font-family: 'Ravi Prakash', cursive;
    font-size: 60px;
    color: #14BDAC;

}
#choices {
    margin-top: 10px;
    margin-bottom: 20px;
}


input[type="text"] {
    padding: 20px;
    height: 35px;
    width: 190px;
    font-family: sans-serif;
    font-size: 18px;
    appearance: none;
    border: solid 1px #707070;
    transition: box-shadow 0.3s, border 0.3s;
}

input[type="text"]:focus {
    outline: none;
    border: none;
    border-bottom: solid 3px #14BDAC;
    box-shadow: 0 0 5px 1px #969696;
}

#feedback-container {
    width: 80%;
    margin-left: auto;
    margin-right: auto;
    margin-top: 20px;

    font-family: 'Roboto', sans-serif;
}

#feedback {
    text-align: center;
    font-size: 18px;
    /*text-transform: uppercase;*/
}

table  {
    table-layout: fixed !important;

}

#table-div {
    padding: 20px;
    min-width: 400px;
    max-width: 600px;
    margin: 0px auto;
}



#app {
    background-color: #14BDAC;
    width: 65%;
    margin: 0 auto;
    height: 300px;
    /*table-layout: fixed !important;*/
}

#app tr td {
    height: 85px !important;
    border: 4px solid #0DA192;
    padding: 0 !important;
    padding-top: 20px !important;

}

.cell {
    font-size: 40px;
    text-align: center;
    font-family: 'Roboto', sans-serif;
    line-height: 85px;
    vertical-align: center;
    font-weight: 900;
}
/* make cursor as pointer on hover, focus and active states */
.cell:hover, .cell:focus, .cell:active {
    cursor: pointer;
}

.wrapper {
    padding: 20px;
    min-height: 100% !important;
    margin: 0 auto -50px !important;
    padding-top: 0;
    min-width: 320px;
}

/*
footer {
    position: fixed;
    bottom: 0;
    text-align: center;
    width: 100%;
    margin: 20px auto;
}
*/

.footer,
.push {
    height: 50px;
}

.footer {
    /*background-color: black;*/
    margin-top: 10px;
    position: relative;
}

/*footer p {
    font-family: 'Mirza', cursive;
    text-align: center;
}*/

.footer span {
    position: absolute;
    bottom: 2px;
    right: 0;
    left: 0;
    text-align: center;
    font-family: 'Mirza', cursive;
    color: #0DA192;
    font-weight: bold;
    font-size: 16px;

}

.button-holder {
    min-width: 200px;
    max-width: 370px;
    margin: 10px auto;
}

.btn, .btn-custom {
    color: white;
    background-color: #0DA192;
    font-family: 'Roboto', sans-serif !important;
    font-size: 17px;
}

.clicked {
    background-color: #0DA192;
    outline: 0;
}
.btn-custom:hover {
    color: white;
    background-color: red !important;
    outline: 0;
}

.btn-custom:active, .btn-custom:focus {
    color: white;
    background-color: #0DA192;
    outline: 0;
}
.input-holder {
    display: inline-block;
}

.score {
    position: absolute;
    top: -2px;
    right: 20px;
}


/* styles for second button - below */
.button-holder button {
    display: inline-block;
    width: 45%;
}

#reset {
    margin-top: 0;
    margin-left: 33px;
}

#restart:hover {
    background-color: green !important;
}

button span {
    margin-right: 10px;
    margin-top: 2px;
    position: absolute;
}

/* styles for player choice buttons */
#xInp, #oInp {
    width: 150px;
    position: relative;

}



/*Media Queries for responsiveness*/

/* small sizes-ultraxs */
@media screen and (max-width: 508px) {
    .ultra-xs {
        display: block;
        width: 100%;
        margin: 10px auto !important;
        text-align: center;
    }

    .input-holder {
        margin: 0 auto;
    }


}

/* reduce margin on the left for large sizes 1200px-1400px */
@media screen and (min-width: 1200px) and (max-width: 1300px) {
  #ex {
    margin-left: 350px;
  }

  #zero {
    margin-left: 170px;
  }
}

@media screen and (min-width: 768px) and (max-width: 875px) {
    #ex {
        margin-left: 150px;
    }

    #zero {
        margin-left: 90px;
    }

}

/* fix for bottom two buttons misalignment on <425px */

@media screen and (max-width: 425px){
    .button-holder button {
        display: block;
        margin: 20px auto;
        width: 170px;
    }

    #reset {
        margin: 10px auto;
    }

    #table-div table {
        margin-left: 40px;
        /*margin-right: 0;*/
    }
}

@media screen and (max-width: 370px){

    #table-div table {
        margin-left: 10px;
        margin-right: 0;
    }
}
              
            
!

JS

              
                /**
 * Created by manishgiri on 12/20/16.
 */
/**
 * Created by manishgiri on 12/10/16.
 */
/**
 * Created by manishgiri on 11/24/16.
 */
$(document).ready(function () {

    //------------------------------------
    //global variables

    //reference to board cells
    var $tile1 = $("#one");
    var $tile2 = $("#two");
    var $tile3 = $("#three");
    var $tile4 = $("#four");
    var $tile5 = $("#five");
    var $tile6 = $("#six");
    var $tile7 = $("#seven");
    var $tile8 = $("#eight");
    var $tile9 = $("#nine");


    //2D array -internal representation of board - 0 is empty, 1 is occupied
    var board = [
        [0,0,0],
        [0,0,0],
        [0,0,0]
    ];

    //object to relate cell position and array  number
    var cellToMoves = {
        one: 0,
        two: 1,
        three: 2,
        four: 3,
        five: 4,
        six: 5,
        seven: 6,
        eight: 7,
        nine: 8
    };



    //reference to array which will determine number of moves left
    /* var movesLeft = new Array(9);
     movesLeft.fill(0)*/

    //reference to array of cells
    var cellsLeft = Object.keys(cellToMoves);
    console.log("cells left array - ")
    console.log(cellsLeft);

    //reference to x and o player box
    var $xInp = $("#xInp");
    var $oInp = $("#oInp");

    var $feedback = $("#feedback");


    //current player
    var isXSelected = false;
    var isPlayerSelected = false;
    var playerChoice = '';
    var aiChoice = '';
    var currentTurn = '';
    var playerScore = 0;
    var computerScore = 0;

    //function to determine current player based on input selection

    function selectPlayer() {
        $(".player-choice").click(function () {
            console.log("inside select player");
            if(this.id == 'xInp') {
                $($oInp).prop("disabled", true);
                console.log("Player X selected");

                isXSelected = true;
                isPlayerSelected = true;
                playerChoice = 'X';
                setMessage(`Turn ${playerChoice}`);
            }
            else if(this.id == 'oInp') {
                $($xInp).prop("disabled", true);
                console.log("Player O selected");
                isXSelected = false;
                isPlayerSelected = true;
                playerChoice = 'O';
                setMessage(`Turn ${playerChoice}`);
            }

            currentTurn = playerChoice;

        });

    }


    //function to set message on top
    function setMessage(message) {
        $feedback.html(message);
    }

    //function to detect a player win
    function detectWin(player) {
        console.log(`Inside detect win, current player = ${player}`);
        //check a player's win in all cases
        if(
            ($tile1.html() === player && $tile4.html() === player && $tile7.html() === player) ||
            ($tile1.html() === player && $tile2.html() === player && $tile3.html() === player) ||
            ($tile1.html() === player && $tile5.html() === player && $tile9.html() === player) ||
            ($tile4.html() === player && $tile5.html() === player && $tile6.html() === player) ||
            ($tile7.html() === player && $tile8.html() === player && $tile9.html() === player) ||
            ($tile3.html() === player && $tile5.html() === player && $tile7.html() === player) ||
            ($tile2.html() === player && $tile5.html() === player && $tile8.html() === player) ||
            ($tile3.html() === player && $tile6.html() === player && $tile9.html() === player)
        ) {
            //set message
            setMessage(`Player ${player} wins!`);
            return true;
        }
        else {
            return false;
        }

    }

    //function to remove a cell from the playing board
    function removeCell(cells, cell) {
        //cells is the array, cell is the id of the cell clicled
        let cellPos = cells.indexOf(cell);
        if(cellPos !== -1) {
            cells.splice(cellPos, 1);
        }
    }



    //function to make the moves by the player
    function playerMove() {
        //if player selects X or 0
        selectPlayer();

        //check sequence of playermove execution
        let count = 1;
        console.log(`Player Move started = ${count}`);
        count++;


        //player clicks on a td cell
        $(".cell").click(function () {

            //let $this = $(this);
            //test unbinding of click events
            //$(".cell").off("click");
            $(".cell").css("pointer-events", "none");

            //check if click occured on an occupied cell
            if($(this).html() !== "") {
                //SHOW an alert
                swal({
                    title: "Duh!",
                    text: "That cell is filled.",
                    type: "warning"
                });

                restart();
                return;

            }
            console.log("Callback running");
            let $this = $(this);
            let cellID = this.id;
            console.log(`clicked on cell with id ${cellID}`);
            //if player didn't select anything
            if (!isPlayerSelected) {
                //if a player has not been selected, select X by default
                isXSelected = true;
                isPlayerSelected = true;
                playerChoice = 'X';
            }

            currentTurn = playerChoice;

            let player = playerChoice;
            setMessage(`Turn ${player}`);
            $this.css("color", "rgb(84,84,84)");
            $this.html(player);

            //lookup cell position using id of current cell and remove it from movesLeft array
            let selectedCellPos = cellToMoves[cellID];
            console.log(`position of selected cell on board = ${selectedCellPos}`);


            //remove current cell from cellsLeft array
            removeCell(cellsLeft, cellID);

            //verify selected cell was removed
            console.log("After player click,cells left array - ")
            console.log(cellsLeft);

            //change message on top to indicate turn of ai - test
            let nextTurn = currentTurn === 'X' ? 'O' : 'X';
            setMessage(`Turn ${nextTurn}`);

            //test aiMove() here
            //aiMove();
            //test a delay before AI move
            setTimeout(() => {
                aiMove()
            }, 1000);
            //$(".cell").bind("click");
            //$(".cell").css("pointer-events", "auto");

        });

    }


    //test gameAlert here
    gameAlert();
    playerMove();

    function aiMove() {
        $(".cell").css("pointer-events", "auto");

        console.log("AI Move running");

        //test for player win from previous run - works for player win
        if(currentTurn !== "" && detectWin(currentTurn)) {
            playerScore++;
            console.log(`Player score is ${playerScore}`);

            console.log("Inside aimove - 1");

            //code for custom alerts - sweetalert library
            //alert(`Player ${currentTurn} won!!!`);
            swal("Good job!", `Player ${currentTurn} won!!!`, "success");
            let currentPlayerScore = $("#xScore").html();
            console.log(`Current player score = ${currentPlayerScore}`);

            //bug - always increments player X's score, instead of the current player
            //$("#xScore").html(playerScore);

            //fix - check which player has 'currentTurn', and increment accordingly
            if(currentTurn === "X") {
                //increment player X's score - get the current score first
                //for first run, score is -
                let currentXScore = $("#xScore").html();
                if (currentXScore == '-') {
                    currentXScore = 0;
                }
                else {
                    currentXScore = parseInt(currentXScore);
                }
                currentXScore++;
                $("#xScore").html(currentXScore);
            }
            else {
                //increment player O's score
                //for first run, score is -
                let currentOScore = $("#oScore").html();
                if(currentOScore == '-') {
                    currentOScore = 0;
                }
                else {
                    currentOScore = parseInt(currentOScore);
                }
                currentOScore++;
                $("#oScore").html(currentOScore);
            }

            //call restart
            restart();

            //test draw implementation
            //draw();
            // game resets
            // reset();
            return;
        }


        //detemine if AI is using X or O
        let computerChoice = (playerChoice === 'X') ? 'O' : 'X';
        console.log(`AI Choice = ${computerChoice}`);
        currentTurn = computerChoice;


        //set message on top
        setMessage(`Turn ${computerChoice}`);

        //AI choice is computerChoice


        //pick a random cell from the cellsleft array
        let selectedCellPos = Math.floor(Math.random() * cellsLeft.length);
        let selectedCell = cellsLeft[selectedCellPos];
        console.log(`selected cell id = ${selectedCell}`);

        //place an O in this cell
        let selectedCellID = ('#'+selectedCell);
        $(selectedCellID).css("color", "rgb(242,235,211)");
        $(selectedCellID).html(computerChoice);

        //remove this cell from array of available cells
        removeCell(cellsLeft, selectedCell);

        //verify selected cell was removed
        console.log("After AI click,cells left array - ")
        console.log(cellsLeft);


        //detect AI win here

        //test for player win from previous run - works for player win
        if(currentTurn !== "" && detectWin(currentTurn)) {
            console.log("Inside aimove - 2");


            console.log(selectedCellID);

            //ai move
            $(selectedCellID).css("color", "rgb(242,235,211)");
            $(selectedCellID).html(computerChoice);

            //print contents of winning cell for test
            let winAICell = $(selectedCellID).html();
            console.log(`Contents of ${selectedCellID} = ${winAICell}`);

            //increment ai score
            computerScore++;


            //create a delay so AI move can be drawn on screen
            setTimeout(function () {

                //test out swal alert here
                swal({
                        title: "Uh-Oh.",
                        text: "Yikes, Computer Won!!!",
                        type: "warning",
                        //showCancelButton: true,
                        //confirmButtonColor: "#DD6B55",
                        //confirmButtonText: "Yes, delete it!",
                        //closeOnConfirm: false
                    });


                //alert(`Player ${currentTurn} won!!!`);
                //draw ai score
                //computerScore++;
                console.log(`AI score is ${computerScore}`);
                $("#oScore").html(computerScore);



                //call restart
                restart();

                // resets by default
                // reset();
                return;
            }, 1000);

            $(".cell").css("pointer-events", "auto");


            // alert(`Player ${currentTurn} won!!!`);
            // reset();
            // return;
        }

        //set message on top to indicate turn of next player
        let nextTurn = computerChoice === 'X' ? 'O' : 'X';
        setMessage(`Turn ${nextTurn}`);

        //a draw will happen when all cells on the board are filled and code has not yet "returned"
        if(isGameDrawn()) {
            draw();
        }

    }

    //aiMove();

    function commonReset() {
        //do reset procedures common to both reset and restart

        //reset cellsLeft array
        cellsLeft = Object.keys(cellToMoves);

        //verify cellsLeft was reset
        // console.log("After game restart,cells left array - ")
        console.log(cellsLeft);

        //clear the board
        $(".cell").each(function () {
            $(this).html("");
        });

        //set message on top
        setMessage("Start game or select player");

        //if a player choice button was selected at the start, the other button would be disabled
        //enable both buttons
        $("#xInp").prop("disabled", false);
        $("#oInp").prop("disabled", false);

        //reset global variables
        isXSelected = false;
        isPlayerSelected = false;
        playerChoice = '';
        aiChoice = '';
        currentTurn = '';

        //test if cells are clickable again - works
        $(".cell").css("pointer-events", "auto");

    }

    function reset() {

        console.log("-----GAME RESET-------");
        playerScore = 0;
        computerScore = 0;

        //call common reset function
        commonReset();

        //reset player scores to -
        //test
        $("#xScore").html("-");
        $("#oScore").html("-");

    }


    //function to restart game 
    function restart() {

        console.log("-----GAME RESTART-------");

        //call common reset function
        commonReset();
        
    }

    //function to check if game ended in draw
    function isGameDrawn() {
        //loop through the cells and check if all cells are filled
        //clear the board
        let gameDrawn = true;
        $(".cell").each(function () {
            if($(this).html() === "") {
                gameDrawn = false;

            }

        });
        return gameDrawn;
    }

    //function to show if game ends in draw
    function draw() {
        //create the alert for fraw
        swal({
            title: "Meh!",
            text: "Well, that's a draw.",
            //test image hosted on dropbox
            imageUrl: "https://dl.dropbox.com/s/xsd5cv1fuozvvz8/handshake.png?dl=0"
            //imageUrl: "handshake.png"
        });

        //common reset
        commonReset();
        
    }

    //hook up restart function to restart button
    $("#restart").click(restart);

    //hook up reset function to reset button
    $("#reset").click(reset);


    //an alert at the beginning with instructions?
    function gameAlert() {
        swal({
            title: "Tips and Tricks!",
            text: "<ul><li>Select X or O, or directly begin the game as X.</li>" +
            "<li>Player makes the first move.</li> " +
            "<li>Next, the computer makes it's move after a second.</li>" +
            "<li>Restart or Reset a game at any time.</li>" +
            "<li>NOTE - Restarting or Resetting will clear the current game state.</li></ul>",
            html: true,
            timer: 20000,
            showConfirmButton: true,
            type: "warning",
            confirmButtonColor: "#009933"
        });
    }

})

              
            
!
999px

Console