A Pen by belcurv

Thumbnail
This awesome code was written by belcurv, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright belcurv ©

Technologies

  • HTML
  • CSS
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>A Pen by  belcurv</title>
  
  
  
      <link rel="stylesheet" href="css/style.css">

  
</head>

<body>

  <section id="tic-tac-toe">

   <p class="title shadowbox">FCC TIC-TAC-TOE</p>

   <div id="game"></div>

   <div id="logger"></div>

</section>

<footer class="belcurv-footer"></footer>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>

  

    <script  src="js/index.js"></script>




</body>

</html>

/*Downloaded from https://www.codeseek.co/belcurv/a-pen-by-belcurv-aJEgzj */
/* basic reset */

body,
html {
   box-sizing: border-box;
   font-family: Arial, sans-serif;
   margin: 0;
   padding: 0;
}

p {
   margin: 0;
   padding: 0;
}

body {
   background: linear-gradient( #6672c2, #40bc51) center center no-repeat fixed;
   color: white;
   display: flex;
   flex-direction: column;
   height: 100vh;
}

#tic-tac-toe {
   flex: 1;
   width: 100%;
   display: flex;
   flex-direction: column;
}

.title {
   color: #fff;
   font-size: 4em;
   font-weight: bold;
   margin: 0;
   text-align: center;
   text-shadow: 0 0 12px #fff;
   box-shadow: 0 0 10px 4px #333;
}

.shadowbox {
   background: rgba(0, 0, 0, 0.3);
}


/* ================================== game ================================= */

#game {
   flex: 1;
   display: flex;
   align-self: center;
   flex-flow: column nowrap;
   justify-content: center;
   width: 720px;
}

#game .board {
   line-height: 1.5em;
   text-align: center;
   font-size: 10em;
   display: flex;
   height: 720px;
   flex-flow: row wrap;
}

#game .square {
   height: 33.34%;
   border: 1px solid #fff;
   box-sizing: border-box;
   flex: 1 0 30%;
}

#game .square#sq0 {
   border-top: none;
   border-left: none;
}

#game .square#sq1 {
   border-top: none;
}

#game .square#sq2 {
   border-top: none;
   border-right: none;
}

#game .square#sq3 {
   border-left: none;
}

#game .square#sq5 {
   border-right: none;
}

#game .square#sq6 {
   border-bottom: none;
   border-left: none;
}

#game .square#sq7 {
   border-bottom: none;
}

#game .square#sq8 {
   border-bottom: none;
   border-right: none;
}


/* ================================ logger ================================= */

#logger {
   color: rgba(255, 255, 255, .7);
   position: fixed;
   bottom: 70px;
}


/* ================================ modals ================================= */

.modal {
   display: flex;
   flex-flow: column nowrap;
   align-items: center;
   justify-content: center;
   text-transform: uppercase;
   box-sizing: border-box;
   position: fixed;
   background: rgba(0, 0, 0, .75);
   top: 0;
   left: 0;
   height: 100vh;
   width: 100vw;
   font-size: 6em;
   font-weight: bold;
   z-index: 999;
}

.modal button {
   margin: 0 0.5em;
   min-width: 2em;
   display: inline;
   font-size: .6em;
   border: 1px solid $fff;
   border-radius: 5px;
   background: #333;
   color: #fff;
   cursor: pointer;
}


/* ============================== responsive =============================== */

@media all and (max-width: 720px) {
   .title {
      font-size: 3em;
   }
   #game {
      width: 480px;
   }
   #game .board {
      height: 480px;
      line-height: 1em;
   }
   .modal {
      font-size: 3em;
   }
}

@media all and (max-width: 360px) {
   .title {
      font-size: 2em;
   }
   #game {
      width: 320px;
   }
   #game .board {
      line-height: 1.3em;
      font-size: 5em;
      height: 320px;
   }
   .modal {
      font-size: 2em;
   }
}


/* ================================= footer ================================ */

.belcurv-footer {
   padding: 1em;
   background: #404040;
   color: #999;
   text-align: center;
   font-size: 13px;
}

.belcurv-footer .footer-credit {
   vertical-align: middle;
}

.belcurv-footer .github-icon {
   display: inline-block;
   fill: rgb(255, 255, 255);
   vertical-align: middle;
}

.belcurv-footer a {
   color: white;
   text-decoration: none;
}

.belcurv-footer a:hover {
   text-decoration: underline;
}

/*Downloaded from https://www.codeseek.co/belcurv/a-pen-by-belcurv-aJEgzj */
/* jshint esversion:6 */
/* globals jQuery, console, document, setTimeout */

var Game = (function ($) {

    'use strict';

    var DOM = {},        // populated by cacheDom()

        gameState = {},  // populated by reset()
        
        victories = [    // possible victory square combos
            [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8],
            [0, 3, 6],
            [1, 4, 7],
            [2, 5, 8],
            [0, 4, 8],
            [2, 4, 6]
        ],
        
        playerToken,     // populated by setPlayerToken()
        computerToken;


    /* ========================== private methods ========================== */

    // cache DOM elements
    function cacheDom() {
        DOM.$game     = $('#game');
        
        // player token select
        DOM.$pModal   = $(document.createElement('div'));
        DOM.$pButtonX = $(document.createElement('button'));
        DOM.$pButtonO = $(document.createElement('button'));
        
        // victory modal and reset button
        DOM.$vModal   = $(document.createElement('div'));
        DOM.$vButton  = $(document.createElement('button'));
    }


    // bind events
    function bindEvents() {
        DOM.$game.on('click', '.square', squareClickHandler);
        DOM.$game.on('click', '.play-again', resetGame);
        DOM.$game.on('click', '.p-token-btn', setPlayerToken);
    }


    // handle clicks to squares
    function squareClickHandler(e) {
        
        if (gameState.gameOver) { return; }

        // get clicked square's index
        var sqIndex = (e.target.id).slice(2, 3);

        // make sure it's player's turn & space is free
        if (gameState.playerTurn && isAvailable(sqIndex)) {

            // register move in gameState
            gameState.spaces[sqIndex].val = playerToken;
            
            // set board text
            $(e.target).text(playerToken);
            
            // increment turns
            step();

        }
    }
    
    
    // handle player token selection
    function setPlayerToken(e) {
        DOM.$pModal
            .hide();
        
        if (e.target.id === 'playerX') {
            playerToken   = 'X';
            computerToken = 'O';
        } else {
            playerToken   = 'O';
            computerToken = 'X';
        }
    }
    
    
    // check if square avaialble
    function isAvailable(sq) {
        return gameState.spaces[sq].val === null;
    }


    // find remaining available moves
    function availableMoves() {
        return gameState.spaces.filter( s => s.val === null );
    }


    // computer's turn
    function computerTurn(e) {
        
        if (gameState.gameOver) { return; }

        var spaces = availableMoves(),
            pick   = Math.floor(Math.random() * spaces.length),
            square = $('#sq' + spaces[pick].ind);
        
        // set board text
        square.text(computerToken);
        
        // register move in gameState
        gameState.spaces[spaces[pick].ind].val = computerToken;
        
        // increment turns
        step();
    }


    // check for a winner
    function checkIfWinner() {
        
        var winner;
        
        ['X', 'O'].forEach(function (player) {
            var occupiedSpaces = gameState.spaces
                .filter( space => space.val === player )
                .map(    space => space.ind );

            victories.forEach(function (v) {
                var match = v.every( el => occupiedSpaces.indexOf(el) > -1 );
                if (match) {
                    gameState.gameOver = true;
                    showVictoryModal(`Player ${player}`);
                    winner = true;
                }
            });
        });
        
        return winner;
    }
    
    
    // check for tie game
    function checkDraw() {
        
        if (!checkIfWinner() && gameState.numberTurns === 9) {
            gameState.gameOver = true;
            showVictoryModal('No One');
        }
    }
        
    
    // build and show victory modal
    function showVictoryModal(player) {
        
        DOM.$vButton
            .addClass('play-again')
            .attr('type', 'button')
            .text('Play Again?');
        
        DOM.$vModal
            .addClass('modal')
            .appendTo(DOM.$game)
            .html(`<p>${player} wins!</p>`)
            .append(DOM.$vButton)
            .show();
    }


    // build the game board
    function makeBoard() {

        var i,
            $board  = $(document.createElement('div')),
            $square = $(document.createElement('div'));

        $square.addClass('square');
        $board.addClass('board');

        // build board from squares
        for (i = 0; i < 9; i += 1) {
            $square.clone()
                .attr('id', 'sq' + i)
                .appendTo($board);
        }

        // append the board to the DOM
        DOM.$game
            .append($board);

    }
    
    
    // clear each square's text
    function clearBoard() {
        $('.square').each(function (ind, el) {
            $(el).text('');
        }); 
    }

    
    // reset game
    function resetGame() {
        
        // erase sqares' text
        clearBoard();

        // hide victory modal
        DOM.$vModal
            .hide();

        // reset game state
        gameState = {
            spaces: [
                { "val": null, "ind": 0 },
                { "val": null, "ind": 1 },
                { "val": null, "ind": 2 },
                { "val": null, "ind": 3 },
                { "val": null, "ind": 4 },
                { "val": null, "ind": 5 },
                { "val": null, "ind": 6 },
                { "val": null, "ind": 7 },
                { "val": null, "ind": 8 }
            ],
            playerTurn: true,
            gameOver: false,
            gameResult: '',
            numberTurns: 0
        };
    }
    
    
    // advance game one turn at a time
    function step() {
        
        gameState.numberTurns += 1;
        checkIfWinner();
        checkDraw();
        
        // check if it's the computer's turn
        if (gameState.numberTurns % 2 !== 0) {
            
            // computer plays after a slight delay
            setTimeout(function () {
                computerTurn();
            }, 500);
            
        }
        
        // toggle player's turn
        gameState.playerTurn = !gameState.playerTurn;
        
//        console.log(gameState.numberTurns);
        
    }
    
    
    // modal for player token select
    function chooseSides() {
        
        var group = $(document.createElement('div'));
        
        DOM.$pButtonX
            .addClass('p-token-btn')
            .attr('id', 'playerX')
            .attr('type', 'button')
            .text('X')
            .appendTo(group);
        
        DOM.$pButtonO
            .addClass('p-token-btn')
            .attr('id', 'playerO')
            .attr('type', 'button')
            .text('O')
            .appendTo(group);
        
        DOM.$pModal
            .addClass('modal')
            .appendTo(DOM.$game)
            .html(`<p>Choose Sides!</p>`)
            .append(group)
            .show();
    }


    /* ========================== public methods =========================== */

    // autoexec on page load
    function init() {

        cacheDom();
        bindEvents();
        makeBoard();
        resetGame();
        chooseSides();

    }


    /* ====================== export  public methods ======================= */

    return {
        init: init
    };

}(jQuery));

/* js/footer.js */
/* jshint esversion:6 */
/* globals jQuery */

var BelcurvFooter = (function ($) {
    
    'use strict';
    
    var DOM      = {},
        desc     = 'Tic Tac Toe',
        version  = '0.0.1',
        link     = 'https://github.com/belcurv/fcc-tic-tac-toe',
        template = `
            <p class="footer-credit"> ${desc} : v${version} : by 
                <a href="${link}" target="_blank">belcurv</a>
                <a href="${link}" target="_blank">
                    <svg width="20" height="20" class="github-icon" viewBox="0 0 16 16" version="1.1" aria-hidden="true">
                        <path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"> </path>
                    </svg>
                </a>
            </p>

            <p class="footer-credit">Source released under the MIT license. Website and documentation licensed under <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank">CC BY 4.0</a></p>`;
    
    
    /* ========================== private methods ========================== */
    
    // cache DOM elements
    function cacheDom() {
        DOM.$footer = $('footer');
    }
    
    
    // main renderer
    function render() {
        DOM.$footer.html(template);
    }
    
    
    /* ========================== public methods =========================== */
    
    // main init function
    function init() {
        cacheDom();
        render();
    }
    
    
    /*  export public methods  */
    return {
        init: init
    };
    
}(jQuery));

/* jshint esversion:6 */
/* globals $, Game, BelcurvFooter, document */

$(document).ready(function () {

    Game.init();
    BelcurvFooter.init();
    
});

Comments