<div class='body-frame'>
  <div class='container'>
    <div class='player-options'>
    <div class='player-heading raylan-givens'>
      <span>Player 1</span>
      <span>Player 2</span>
    </div>
    <div class='icon-menu raylan-givens'>
      <select id='p1select' name="icons-p1">
        <option value="X">X</option>
        <option value="&spades;">&spades;</option>
        <option value="&clubs;">&clubs;</option>
        <option value="&#9728;">&#9728;</option>
        <option value="&#9760;">&#9760;</option>
        <option value="&#9792;">&#9792;</option>
        <option value="&#8786;">&#9786;</option>
        <option value="&#9917;">&#9917;</option>
        <option value="&#10049;">&#10049;</option>
        <option value="&#10052;">&#10052;</option>
        <option value="&#9749;">&#9749;</option>
      </select>
      <select id='p2select' name="icons-p2">
        <option value="O">O</option>
        <option value="&hearts;">&hearts;</option>
        <option value="&diams;">&diams;</option>
        <option value="&#9729;">&#9729;</option>
        <option value="&#9764;">&#9764;</option>
        <option value="&#9794;">&#9794;</option>
        <option value="&#9786;">&#9786;</option>
        <option value="&#9918;">&#9918;</option>
        <option value="&#10037;">&#10037;</option>
        <option value="&#10037;">&#10037;</option>
        <option value="&#9889;">&#9889;</option>
      </select>
    </div>
    <div class='radio-bar raylan-givens'>
      <input id='p1c' type="radio" name="ai-player" value="player1">P1 AI</input>
      <input type="radio" name="ai-player" value="none" checked>NONE</input>
      <input id='p2c' type="radio" name="ai-player" value="player2">P2 AI</input>
    </div>
</div>
    <div class='button-bar'>
      <button class='game-button' id='play'>Play</button>
    </div>
    <div class='game-board'>
      <div class='square corner' id='A1'></div>
      <div class='square side' id='A2'></div>
      <div class='square corner' id='A3'></div>
      <div class='square side' id='B1'></div>
      <div class='square center' id='B2'></div>
      <div class='square side' id='B3'></div>
      <div class='square corner' id='C1'></div>
      <div class='square side' id='C2'></div>
      <div class='square corner' id='C3'></div>
    </div>
<div class='message-board'>
  <div class='marquee' id='game-speak'>Select game-token & AI player (None to play with a friend). Press Play to start!</div>
</div>
    <div class='button-bar'>
      <button class='game-button' id='reset'>Reset</button>
    </div>
  </div>
</div>
body{
  background: #000000;
  color: #FFFFFF;
  font-family: 'Bangers', cursive;
  line-height: 1.6em;
  
}
button{
  width: 100%;
  height: 26px;
  margin-left: 3px;
  font-size: 1em;
  font-weight: bold;
  background: #686868;
  border: 2px none #686868;
  box-shadow: -2px -2px 3px 0px #161616 inset, 2px 2px 3px 0px #848484 inset;
  border-radius: 5px;
}
button:active{
  box-shadow: -2px -2px 3px 0px #848484 inset, 2px 2px 3px 0px #161616 inset;
  padding-left: 15px;
  padding-top: 5px;
}
select{
  width: 30%;
  font-size: 1em;
  text-align: center;
}
.body-frame{
  width: 312px;
  margin: auto;
}
.container{
  
}
.right{
  float: right;
}
.left{
  float: left;
}
.player-options{
  padding-left: 8px;
  padding-right: 8px;
}
.raylan-givens{
  width: 100%;
  height: 1.6em;
  margin-bottom: 5px;
  color: #07efe4;
  letter-spacing: 2px;
  font-size: 1.2em;
  line-height: 1.8em;
  display: flex;
  justify-content: space-between;
}
.radio-bar{
  text-indent: -2.2em;
}
.button-bar{
  width: 100%;
  height: 32px;
  display: flex;
  flex-wrap: wrap;
}
.game-board{
  width: 100%;
  height: 300px;
  display: flex;
  flex-wrap: wrap;
}
.square{
  width: 98px;
  height: 96px;
  margin-left: 5px;
  margin-bottom: 5px;
  text-align: center;
  color: #F4D742;
  line-height: 1.5em;
  background: #212121;
  font-family: 'Luckiest Guy', cursive;
  border-radius: 10px;
  box-shadow: -2px -2px 3px 0px #161616 inset, 2px 2px 3px 0px #666666 inset;
  font-size: 5em;
}

.marquee {
  width: 300px;
  height: 2em;
  margin: 0 auto;
  padding-top: 0.2em;
  color: #0bcde2;
  letter-spacing: 2px;
  font-size: 1.2em;
  overflow: hidden;
  white-space: nowrap;
  box-sizing: border-box;
  animation: marquee 15s linear infinite;
}
@keyframes marquee {
  0%   { text-indent: 23em }
  100% { text-indent: -50em }
}

.clearfix{
  
}
/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\             ///////////////////////////////
∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ TIC-TAC-TOKEN: TRY NOT TO LOSE, IN STYLE! ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
///////////////////////////////             \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
*/
// GLOBALS
var TTTGames = [];
var game = null;
var turnCount = 0;
var recOneDrawTwo = [0,0,0];
// LIST OF WIN COMBINATIONS
var winCheckLists = {
    'A1': [['A2','A3'],['B2','C3'],['B1','C1']],
    'A2': [['A1','A3'],['B2','C2']],
    'A3': [['A1','A2'],['B2','C1'],['B3','C3']],
    'B1': [['A1','C1'],['B2','B3']],
    'B2': [['A1','C3'],['A2','C2'],['A3','C1'],['B1','B3']],
    'B3': [['A3','C3'],['B1','B2']],
    'C1': [['A1','B1'],['A3','B2'],['C2','C3']],
    'C2': [['A2','B2'],['C1','C3']],
    'C3': [['A3','B3'],['A1','B2'],['C1','C2']]
};

/*
===============================
= GLOBAL FUNCTIONS            =
===============================
*/
// START NEW SET OF GAMES - CALLED WHEN 'PLAY' BUTTON IS PRESSED
function nuGame(){
  console.log('INITIALZING GAME');
  TTTGames.unshift(new GameBoard());
  game = TTTGames[0];
  if (TTTGames.length > 1){
    TTTGames.pop();
    recOneDrawTwo = [0,0,0];
    turnCount = 0;
    game.squares.forEach(function(mame_square){
      mame_square.removeEventListener('click', turnCycle, false);
    })
  }
  turnCount = 0;
  game.init();
  document.getElementById('game-speak').innerHTML = '';
}

// ORDER OF OPERATIONS FOR EACH TURN (COMPUTER TURN SUBSEQUENT TO PLAYER)
function turnCycle(){
    console.log();
    if (this.innerHTML == ''){
        game.playerTakeTurn(this);
    }
    if ((game.ai1 || game.ai2) && turnCount<9){
        game.aiPlay();
        if (game.ai1 && game.turn){
            game.turn = false;
        } else {
            game.turn = true;
        }
    }
    if (game.win){
        game.winDisplay();
    } else if (turnCount >= 9){
      game.drawDisplay();
    }
}

/*
===============================
= BOARD CLASS                 =
===============================
*/
class GameBoard {
  // INITIAL VALUES FOR GAMEBOARD
    constructor(){
        this.player1comp = document.getElementById('p1c').checked;
        this.player2comp = document.getElementById('p2c').checked;
        this.player1icon = document.getElementById('p1select').value;
        this.player2icon = document.getElementById('p2select').value;
        this.player1own = [];
        this.player2own = [];
        this.player1Turn = true;
        this.squares = [
            document.getElementById('A1'),
            document.getElementById('A2'),
            document.getElementById('A3'),
            document.getElementById('B1'),
            document.getElementById('B2'),
            document.getElementById('B3'),
            document.getElementById('C1'),
            document.getElementById('C2'),
            document.getElementById('C3')];
        this.aiIcon = null;
        this.aiOwn = null;
        this.plOwn = null;
        this.winGame = false;
        this.winSquares = [];
    }
  // MAKE SQUARES CLICKABLE
    turnOnSquares(){
        this.squares.forEach(function(clicked_square){
          clicked_square.addEventListener('click', turnCycle);
        })
    }
  // MAKE SQUARES NON-CLICKABLE
    turnOffSquares(){
        this.squares.forEach(function(mame_square){
          mame_square.removeEventListener('click', turnCycle, false);
        })
    }
  // REMOVE PLAYER MARKERS AND RESET IN-GAME VALUES
    clearBoard(){
        for (var i in this.squares){
            this.squares[i].innerHTML = '';
        }
        this.player1own.splice(0);
        this.player2own.splice(0);
        this.winGame = false;
        this.winSquares = [];
        console.log('CLEARED BOARD');
    }
  // INITIALIZATION OF BOARD FOR EACH GAME
    init(){
        var self = this;
        self.clearBoard.apply(self);
        if (this.player1comp){
            this.aiIcon = this.player1icon;
            this.aiOwn = this.player1own;
            this.plOwn = this.player2own;
        } else if (this.player2comp){
            this.aiIcon = this.player2icon;
            this.aiOwn = this.player2own;
            this.plOwn = this.player1own;
        }
        if (this.player1comp && this.player1Turn){
            self.aiPlay();
            this.player1Turn = false;
        }
        if (this.player2comp && !this.player1Turn){
            self.aiPlay();
            this.player1Turn = true;
        }
        this.turnOnSquares();
        console.log('INITIALIZATION SUCCESSFUL');
    }
  // GET/ SET FUNCTIONS FOR EXTERNAL REFERENCES
    get turn(){
        return this.player1Turn;
    }
    set turn(val){
        this.player1Turn = val;
    }
    get ai1(){
        return this.player1comp;
    }
    get ai2(){
        return this.player2comp;
    }
    get win(){
        return this.winGame;
    }
  // CHECK FOR WIN CONDITION
    winCheck(square, plist){
        var winCombo = null;
        var winList = winCheckLists[square.id];
        for (var h in winList){
          if (plist.indexOf(winList[h][0])> -1 && plist.indexOf(winList[h][1])> -1){
              winCombo = [square.id, winList[h][0], winList[h][1]];
              break;
              }
        }
        return winCombo;
    }
  // FUNCTION CALLED WHEN PLAYER SELECTS A SQUARE
    playerTakeTurn(square){
        if (this.player1Turn){
            square.innerHTML = this.player1icon;
            this.player1own.push(square.id);
            if (this.winCheck(square, this.player1own)){ 
                this.winGame = true; 
                this.winSquares = this.winCheck(square, this.player1own);
            }
            this.player1Turn = false;
        } else {
            square.innerHTML = this.player2icon;
            this.player2own.push(square.id);
            if (this.winCheck(square, this.player2own)){ 
                this.winGame = true; 
                this.winSquares = this.winCheck(square, this.player2own);
            }
            this.player1Turn = true;
        }
        turnCount ++;
        console.log('PLAYER TURN');
    }
  // LOGIC FOR AI SQUARE SELECTION
    aiPlay(){
      console.log('COMPUTER TAKING TURN', this.aiOwn, this.plOwn);
        var aiChoice = null;
        var emptySpaces = this.squares.filter(function(box){
            return box.innerHTML == '';
        });
        var mockTrial = [];
        var potentialBoxes = [];
        if (!aiChoice){
          console.log('aiChoice-1');
            for (var j in emptySpaces){
                if (this.winCheck(emptySpaces[j], this.aiOwn)){
                    aiChoice = emptySpaces[j];
                }
            }
        } 
        if (!aiChoice) {
          console.log('aiChoice-2');
            for (var k in emptySpaces){
                if (this.winCheck(emptySpaces[k], this.plOwn)){
                    aiChoice = emptySpaces[k];
                }
            }
        }
        if (!aiChoice) {
          console.log('aiChoice-3');
            for (var l in emptySpaces){
                potentialBoxes = [];
                mockTrial = this.aiOwn.slice();
                mockTrial.push(emptySpaces[l].id);
                for (var m in emptySpaces){
                    if (this.winCheck(emptySpaces[m], mockTrial)){
                        potentialBoxes.push(emptySpaces[m].id);
                    }
                }
                console.log('AI trees: ', potentialBoxes.length);
                if (potentialBoxes.length > 1){
                    aiChoice = emptySpaces[l];
                    break;
                }
            }
        }
        if (!aiChoice && this.aiOwn.length==1 && this.aiOwn[0]=='B2'){
            if ((this.plOwn.find(function(a){return a=='A1'}) &&
               this.plOwn.find(function(a){return a=='C3'})) ||
               (this.plOwn.find(function(a){return a=='A3'}) &&
               this.plOwn.find(function(a){return a=='C1'}))){
                aiChoice = this.squares[[1,3,5,7][Math.floor(Math.random()*4)]];
            }
        }
        if (!aiChoice){
          console.log('aiChoice-4');
            for (var n in emptySpaces){
                potentialBoxes = [];
                mockTrial = this.plOwn.slice();
                mockTrial.push(emptySpaces[n].id);
                for (var o in emptySpaces){
                    if (this.winCheck(emptySpaces[o], mockTrial)){
                        potentialBoxes.push(emptySpaces[o].id);
                    }
                }
                console.log('Player trees: ', potentialBoxes.length);
                if (potentialBoxes.length > 1){
                    aiChoice = emptySpaces[n];
                    break;
                }
            }
        }
        if (!aiChoice){
          console.log('aiChoice-5');
            if (this.aiOwn.length == 0 && this.plOwn.length ==1){
                if (this.squares[4].innerHTML == '') {
                    aiChoice = this.squares[4];
                } else {
                    aiChoice = this.squares[[0,2,6,8][Math.floor(Math.random()*4)]];
                }
            } else {
              console.log('aiChoice-5b');
                potentialBoxes = [];
                for (var p in emptySpaces){
                    if (emptySpaces[p].className.search('corner')> -1){
                        potentialBoxes.push(emptySpaces[p]);
                        if (emptySpaces[p].id == 'A1' &&
                        this.plOwn.find(function(elem){return elem=='C3';})){
                            aiChoice = emptySpaces[p];
                            break;
                        }
                        if (emptySpaces[p].id == 'A3' &&
                        this.plOwn.find(function(elem){return elem=='C1';})){
                            aiChoice = emptySpaces[p];
                            break;
                        }
                        if (emptySpaces[p].id == 'C1' &&
                        this.plOwn.find(function(elem){return elem=='A3';})){
                            aiChoice = emptySpaces[p];
                            break;
                        }
                        if (emptySpaces[p].id == 'C3' &&
                        this.plOwn.find(function(elem){return elem=='A1';})){
                            aiChoice = emptySpaces[p];
                            break;
                        }
                    }
                }
                if (!aiChoice && potentialBoxes.length > 0) {
                  console.log('aiChoice-6');
                    aiChoice = potentialBoxes[0];
                }
            }
        }
        if (!aiChoice && emptySpaces.length > 0) {
          console.log('aiChoice-7');
            aiChoice = emptySpaces[0];
        }
        aiChoice.innerHTML = this.aiIcon;
        this.aiOwn.push(aiChoice.id);
        if (this.winCheck(aiChoice, this.aiOwn)){ 
            this.winGame = true; 
            this.winSquares = this.winCheck(aiChoice, this.aiOwn);
        }
        turnCount ++;
      console.log('COMPUTER TURN COMPLETE');
    }
  // CALLED FOR MARQUEE MESSAGE AT END OF EACH GAME
    scoreString(){
      return ' - P1: ' + recOneDrawTwo[0] +
        ' - DRAW: ' + recOneDrawTwo[1] +
        ' - P2: ' + recOneDrawTwo[2] ;
    }
  // WIN SEQUENCE
    winDisplay(){
      this.turnOffSquares();
      for (var q in this.squares){
        if (this.winSquares.indexOf(this.squares[q].id)< 0){
            this.squares[q].innerHTML = '';
        }
      }
      if (this.player1Turn){
        recOneDrawTwo[2] ++;
        document.getElementById('game-speak').innerHTML = 'Player 2 wins!' +
          this.scoreString() + '.....Press Reset for a rematch!';        
      } else { 
        recOneDrawTwo[0] ++;
        document.getElementById('game-speak').innerHTML = 'Player 1 wins!' +
          this.scoreString() + '.....Press Reset for a rematch!';        
      }
    }
  // DRAW SEQUENCE
    drawDisplay(){
        this.turnOffSquares();
        recOneDrawTwo[1] ++;
        document.getElementById('game-speak').innerHTML = 'TIE! ' +
          this.scoreString() + '.....Press Reset for a rematch!';
    }
}

/*
===============================
= LISTEN FOR GAME START/RESET =
===============================
*/
document.addEventListener('DOMContentLoaded', function(){
  // MAKE 'PLAY' BUTTON CLICKABLE
    document.getElementById('play').addEventListener('click', nuGame);
  // MAKE 'RESET' BUTTON CLICKABLE
    document.getElementById('reset').addEventListener('click', function(res){
        if (game){
          res.preventDefault();
          console.log('RESETING GAME');
          turnCount = 0;
          game.init();
          document.getElementById('game-speak').innerHTML = game.scoreString();
        }
    });
})

Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.