cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

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. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

Quick-add: + add another resource

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.

Quick-add: + add another resource

Code Indentation

     

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.

            
              <body>
  <header>
    <h1 class="header-page-title">Tic Tac Toe</h1>
    
  </header>
  <section class="game-container">
    <div class="ui internally celled grid">
      <div class="row">
        <div class="three wide column tic-cell" id="a1"></div>
        <div class="three wide column tic-cell" id="a2"></div>
        <div class="three wide column tic-cell" id="a3"></div>
      </div>
      <div class="row">
        <div class="three wide column tic-cell" id="b1"></div>
        <div class="three wide column tic-cell" id="b2"></div>
        <div class="three wide column tic-cell" id="b3"></div>
      </div>
      <div class="row">
        <div class="three wide column tic-cell" id="c1"></div>
        <div class="three wide column tic-cell" id="c2"></div>
        <div class="three wide column tic-cell" id="c3"></div>
      </div>
    </div>
  </section>
  
  <div class="new-game-container">
    <p><a href="#" id="new-game">NEW GAME</a></p>
  </div>
  
  <div class="ui modal">
    <div class="ui header modal-header" id="modal-header">Choose Your Mark</div>
    <div class="modal-text"><a class="mark-choice" id="X" href="#">X</a> or <a class="mark-choice" id="O" href="#">O</a></div>
  </div>
</body>
            
          
!
            
              // globals ========================

html {
  font-size: 16px;
}

body {
  background: #000;
  color: #FFF;
  font-family: helvetica, sans-serif;
}

// Game Board ===============================

header {
  margin-bottom: 2rem;
}

.header-page-title {
  font-size: 4rem;
  text-align: center;
}

.game-container {
  margin: 0 auto;
  width: 540px;
  height: 540px;
}

.new-game-container {
  margin-top: 2rem;
  text-align: center;
}

// remove :after seudo class from rows
.ui.grid:after,
.ui.grid > .row:after {
  content: none;
  padding: 0;
}

// overrides Semantic UI's font-size in each cell
.ui.grid>.column:not(.row), .ui.grid>.row>.column {
  font-size: 5rem;
}

.ui.celled.grid>.column:not(.row), .ui.celled.grid>.row>.column {
  text-align: center;
  padding: 2rem;
}

.tic-cell {
  height: 180px;
  min-width: 180px;
  transition: ease .2s;
}

.clickable {
  cursor: pointer;
}

.winning-cell {
  background: #FFF;
  color: #000;
  font-size: 6em;
}

// Modal ==================================

.ui.modal > .header {
  text-align: center;
  font-size: 2em;
}

.modal-text {
  color: #000;
  text-align: center;
  font-size: 2em;
}

.mark-choice {
  padding: 0 5rem;
  font-size: 6em;
}



            
          
!
            
              /*!
* Tic Tac Toe in JavaScript.
* This is a challenge for Free Code Camp. http://www.FreeCodeCamp.com/
*/

let ticTacToe = (function () {
  // initialize events system
  let EVT = new EventEmitter();
  
  let config, 
      gameState, 
      modalControl,
      gameLogic;
  
  const allCells = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3'],
        cornersAndCenter = ['a1', 'a3', 'b2', 'c1', 'c3'],
        corners = ['a1', 'a3', 'c1', 'c3'],
        sides = ['a2', 'b1', 'b3', 'c2'];
  
  const winningConditions = [
    ['a1','a2','a3'], // rowA
    ['b1','b2','b3'], // rowB
    ['c1','c2','c3'], // rowC
    ['a1','b1','c1'], // col1
    ['a2','b2','c2'], // col2
    ['a3','b3','c3'], // col3
    ['a1','b2','c3'], // diag1
    ['a3','b2','c1']  // diag2
  ];

  function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }

  // Stores values of who is X and who is O based on the user's preference
  config = {
    userMark: null,
    computerMark: null,
  };
  
  gameState = {
    openPositions: ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3'],
    userTurns: [],
    computerTurns: [],
    intervalCounter: 0
  };
  
  modalControl = {
    showModal: function (player) {

      if (player === "user") {
        $("#modal-header").text("You Win!");
      }
      
      if (player === "computer") {
        $("#modal-header").text("You Lose!");
      }
      
      if (!player) {
        $("#modal-header").text("Choose Your Mark");
        console.log("modalControl 63: player is undefined: ", player);
      }
      
      $('.ui.modal').modal('show'); 
      EVT.emit("reset");
    },
    
    hideModal: function () {
      $('.ui.modal').modal('hide');
    },
    
    /**
    * markSelect()
    * Initiates config.userMark as X or O depending on the user's choice,
    * and initiates config.computerMark as the non-selected mark.
    *
    * @param {string} userChoice - the choice of X or O selected by the user in the modal
    */
    markSelect: function (userChoice) {
      config.userMark = userChoice;   
      config.computerMark = userChoice === 'X' ? 'O' : 'X';  
      EVT.emit("hide modal");
      EVT.emit("start game");
    }
  };
  // Modal Event Listeners
  EVT.on("show modal", modalControl.showModal);
  EVT.on("hide modal", modalControl.hideModal);
  EVT.on("mark selected", modalControl.markSelect);
  
  // Modal Click Handler
  // Clicking "X" or "O" on the modal will trigger markSelect()
  $(".mark-choice").on("click", function (evt) {
    evt.preventDefault();
    EVT.emit("mark selected", evt.target.id);
  });
  
  gameLogic = {

    startGame: function () {
      
      let clearBoard = allCells.map( cell => {
        let id = "#" + cell;
        $(id).empty();
      });
      
      if (config.userMark === "X") {
        EVT.emit("user turn");
      }
      if (config.userMark === "O") {
        EVT.emit("computer turn");
      }
      if (!config.userMark) {
        console.error("ERROR: userMark is still null at beginGame()");
      }
    },
    
    clearBoard: function() {
      config.userMark = null;
      config.computerMark = null;
      gameState.openPositions = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'a3', 'b3', 'c3'];
      gameState.userTurns = [];
      gameState.computerTurns = [];
      gameState.intervalCounter = 0;
    },
    
    userTurn: function () {
      let playerId = "user";
      
      // make unclaimed cells clickable for current turn
      gameState.openPositions.map( cell => {
        var cellID = "#" + cell;
        $(cellID).addClass("clickable");
      });
      
      // handle user's move selection
      $(".clickable").on("click", function(evt) {
        evt.preventDefault();
        evt.stopPropagation();

        // if the current click evt.target.id is not in the userTurns array, proceed
        if (gameState.userTurns.indexOf(evt.target.id) === -1) {
          // add current move to userTurns array
          gameState.userTurns.push(evt.target.id);
          // add current move to the game board
          $("#" + evt.target.id).append(config.userMark);
          // remove current move from the openPositions array
          gameState.openPositions = gameState.openPositions.filter( id => {
            return id !== evt.target.id;
          });
          
          // Remove click event listeners from each cell
          allCells.map( cell => {
            var cellID = "#" + cell;
            $(cellID)
            .off("click")
            .removeClass("clickable");
          });
          
          let winCheck = gameLogic.checkForWin(gameState.userTurns);
          
          // if winCheck passes, pass along the winning condition array and playerId
          if (winCheck.length > 2) {
            EVT.emit("user win", winCheck, playerId);
            return;
          } else {
            EVT.emit("computer turn");
            return;
          } 
        }
      });
      return;
    },
    
    computerTurn: function () {
      let playerId = "computer",
          appendComputerMove,
          currentComputerMove;
      
      function displayComputerMove(move) {
        $("#" + move).append(config.computerMark);
      }
      
        var userPreWinCondition = gameLogic.checkForPreWin(gameState.userTurns);
        console.log("userPreWinCondition", userPreWinCondition);

      // First Move ****************************************************************
      // If the computer has the first move, a random number generator
      // will determine a center or corner move.
      if ( config.computerMark === 'X' && gameState.userTurns.length === 0 ) {
        let randomMove = getRandomInt(0, 4);
        let firstComputerMove = cornersAndCenter[randomMove];
        
        gameState.computerTurns.push(firstComputerMove);
        
        displayComputerMove(firstComputerMove);
        
        gameState.openPositions = gameState.openPositions.filter( id => {
          return id !== firstComputerMove;
        });
        
        EVT.emit("user turn");  
        
        return;
      }

      // Winning Move ****************************************************************
      // check if computer can win this turn 
      if (gameState.computerTurns.length > 1) {
        var computerPreWinMove = gameLogic.checkForPreWin(gameState.computerTurns);

        if (computerPreWinMove.length > 0) {
          // changes computerPreWinMove into a string since an array get returned
          var computerPreWinMovePush = computerPreWinMove.join('');
          
          gameState.computerTurns.push(computerPreWinMovePush);
          displayComputerMove(computerPreWinMove);
          
          let winCheck = gameLogic.checkForWin(gameState.computerTurns);

          if (winCheck.length === 3) {
            EVT.emit('computer win', winCheck, playerId);
            return;
          } 
        }
      }
      
      // User Win Block **************************************************************
      // check if the user can win next turn
      if (userPreWinCondition) {
        
        if (userPreWinCondition.length > 0) {
          let winOrLose = getRandomInt(1, 5);
          
          if (winOrLose !== 5) {
            
            gameState.computerTurns.push(userPreWinCondition[0]);
            displayComputerMove(userPreWinCondition[0]);
            
            gameState.openPositions = gameState.openPositions.filter( id => {
              return id !== userPreWinCondition[0];
            });

            EVT.emit("user turn");
            return;
          }
        }
      }
      
      // Center Move *****************************************************************
      // if the center is free, take it
      if (gameState.openPositions.indexOf("b2") !== -1) {
        
        gameState.computerTurns.push("b2");
        displayComputerMove("b2");
        
        gameState.openPositions = gameState.openPositions.filter( id => {
          return id !== "b2";
        });

        EVT.emit("user turn");
        return;
      }
      
      // Corner Move *****************************************************************
      // if a corner is free, take it
      var availableCorners = corners.map(function(cell) { 
        if (gameState.openPositions.indexOf(cell) > -1) {
          return cell;
        } else {
          return;
        }
      })
      .filter(function(corner) {
        return corner !== undefined;
      });
      
      if (availableCorners.length > 0) {
        let randomCorner = getRandomInt(0, availableCorners.length - 1);
        let computerMove = availableCorners[randomCorner];
        
        gameState.computerTurns.push(computerMove);
        displayComputerMove(computerMove);
        
        gameState.openPositions = gameState.openPositions.filter( id => {
          return id !== computerMove;
        });

        EVT.emit("user turn");
        return;
      }
      
      // Side Move ********************************************************************
      // if a side is free, take it    
      var availableSides = sides.map(function(cell) {
        if (gameState.openPositions.indexOf(cell) > -1) {
          return cell;
        } else {
          return;
        }
      })
      .filter(function(side) {
        return side !== undefined;
      });
      
      if (availableSides.length > 0) {
        let randomSide = getRandomInt(0, availableSides.length - 1);
        let computerMove = availableSides[randomSide];
        
        gameState.computerTurns.push(computerMove);
        displayComputerMove(computerMove);
        
        gameState.openPositions = gameState.openPositions.filter( id => {
          return id !== computerMove;
        });

        EVT.emit("user turn");
        return;
      }
      
      // Tie Game ****************************************************************
      if (gameState.openPositions.length === 0) {
        // Let the user hit NEW GAME
        console.log("Tie game!");
      }
    },
    
    checkForPreWin: function (turnsArray) {
      // Check each winning condition if two spots have been filled
      // in the player's turnsArray
      var preWin = winningConditions.map( condition => {
        var firstTwo = condition.slice(0, 2),
            secondTwo = condition.slice(1, 3),
            firstAndThird = [ condition[0], condition[2] ];
        
        var firstTwoCheck = firstTwo.every(function (cell) {
          return turnsArray.indexOf(cell) !== -1;
        });    
        var secondTwoCheck = secondTwo.every(function (cell) {
          return turnsArray.indexOf(cell) !== -1;
        });
        var firstAndThirdCheck = firstAndThird.every(function (cell) {
          return turnsArray.indexOf(cell) !== -1;
        });
        
        // if any preWin check passes, check if the winning move
        // is available. If so, return that move   
        if (firstTwoCheck) {
          if (gameState.openPositions.indexOf(condition[2]) !== -1) {
            console.log("firstTwoCheck passed! : ", condition[2]);
            return condition[2];
          }
        }
        if (secondTwoCheck) {
          if (gameState.openPositions.indexOf(condition[0]) !== -1) {
            console.log("secondTwoCheck passed! : ", condition[0]);
            return condition[0];
          }
        }
        if (firstAndThirdCheck) {
          if (gameState.openPositions.indexOf(condition[1]) !== -1) {
            console.log("firstAndThirdCheck passed! : ", condition[1]);
            return condition[1];
          }
        }
        // if no winning moves are available, return false  
        return false;
      })
      .filter(function (condition) {
        return condition !== false;
      });
      console.log("checkForPreWin 324: preWin returned: ", preWin);
      return preWin;
    },
    /*
    * checkForWin(turnsArray)
    * Checks if a winning condition is present in the 
    * player's turns array. 
    * 
    * @param {array} turnsArray - The current player's turns array from state
    */
    checkForWin: function (turnsArray) {
      var winTest;
      // map through each win condition
      var winOrLose = winningConditions.map( condition => {
        // for the current win condition, 
        // check if every winning cell is present in the player's turn array
        winTest = condition.every(function(cell) {
          return turnsArray.indexOf(cell) !== -1;
        });
        // if winTest passes, that's a win!
        if (winTest) {
          // check if the provided turnsArray belongs to the user
          // if not, it must be the computer's 
          var winner = turnsArray === gameState.userTurns ? "user" : "computer";
          
          // winOrLose returns the winning condition since winTest passed
          // if no winner is found, that's an error.
          if (winner === "user" || winner === "computer") {
            return condition;
          } else {
            console.error("checkForWin 354: ERROR: winTest passed, but no winner found.")
          }
        // if winTest didn't pass, no winner this time.  
        // winOrLose returns false
        } else {
          return false;
        }
      })
      .filter( result => {
        return result !== false;
      });
      
      // flattens any nested arrays inside the winOrLose array
      if (Array.isArray(winOrLose[0])) {
        winOrLose = winOrLose.reduce(function(a,b){
          return a.concat(b);
        }, []);
      }
      // returns the winning condition or false
      return winOrLose;
    },
    
    winResponse: function (winCondition, player) {

      let toggleWinningCells = setInterval(function() {
        $(`#${winCondition[0]},#${winCondition[1]},#${winCondition[2]}`)
          .toggleClass("winning-cell");
        if (gameState.intervalCounter >= 4) {
          clearInterval(toggleWinningCells);
        }
        gameState.intervalCounter++;
      }, 250);
      
      let delayedModal = setTimeout(function() {
        EVT.emit("show modal", player);
        EVT.emit("reset");
        $(`#${winCondition[0]},#${winCondition[1]},#${winCondition[2]}`)
          .toggleClass("winning-cell");
      }, 1500);
    }
  };
  
    // New Game click handler
  $("#new-game").click(function(evt) {
    evt.preventDefault();
    EVT.emit("show modal");
    EVT.emit("reset");
  });
  // Game Logic Event Listeners
  EVT.on("start game", gameLogic.startGame);
  EVT.on("reset", gameLogic.clearBoard);
  EVT.on("user turn", gameLogic.userTurn);
  EVT.on("computer turn", gameLogic.computerTurn);
  EVT.on("user win", gameLogic.winResponse);
  EVT.on("computer win", gameLogic.winResponse);
  
  
  
  function init() {
    EVT.emit("show modal");  
  }

  return {
    init: init
  };
  
})();

$(document).ready(function() {
  ticTacToe.init();       
})
            
          
!
999px
Loading ..................

Console