<div class="title">Tic Tac Toe</div>
    <div id="content" class="container"></div>
// Google Fonts
@import url(https://fonts.googleapis.com/css?family=Roboto+Condensed|Roboto+Slab);

/// Font Variables

$primary: 'Roboto Condensed', sans-serif;
$secondary: 'Roboto Slab', serif;


//Color Variables
$white: #FFFFFF;
$black: #000000;
$grey: #E4DBBF;
$red: #e74c3c;
$dark: #383127;
$belizeHole: #2980b9;
$nephiritis: #27ae60;
$emerald: #2ecc71;
$wetAsphalt: #34495e;
$midnightBlue: #2c3e50;

/* Cell variables */
//cell border
$fieldBorder: 1px solid $emerald;
//cell size
$fieldSize: 100px;

body{
  color: $midnightBlue;
  font-family: $primary;
  background-color: $grey;
}

.title{
  padding: 15px;
  margin: 0;
  text-align: center;
  font-family: $secondary;
  font-weight: bold;
  font-size: 32px;
}
  

#content{

  .game{
    display: table; 
    margin: 0 auto;
    
    .board{
      margin-bottom: 10px;
      border: $fieldBorder;

      .field{
        float: left;
        width: $fieldSize;
        height: $fieldSize;
        line-height: $fieldSize;
        background-color: $nephiritis;
        text-align: center;
        border: $fieldBorder;

        &:nth-child(3n+1){
          clear: left;
        }

        @media screen and (max-width: 340px) {
            width: 80px;
          height: 80px;
        }

        .mark{
          font-size: 50px;
          color: $white;
          
        }
      
      }
        
    }

    .settings{

    }

    .resultPanel{

      .result{
        font-size: 26px;
        font-weight: bold;
        text-align: center;
      }

      .startAgain{
        @extend .button;
        display: block;
      }
    }
  }
} 

fieldset {
    border: 2px solid $wetAsphalt;
    background: #ddd;
    border-radius: 5px;
    padding: 10px;

    legend {
      background: $wetAsphalt;
      color: #fff;
      padding: 5px 10px;
      font-size: 16px;
      border-radius: 5px;
      box-shadow: 0 0 0 5px #ddd;
      margin-left: 10px;
  }

  input[type="radio"]{
    margin: 0 10px 0 10px;
  } 
}



          
.button{
  background-color: $belizeHole;
  color: $white;
  font-weight: bold;
  width: 100%;
  margin: 5px 0;
  padding: 10px;
  border: none;
  border-radius: 5px;

  &:hover{
    cursor: pointer;
  }
  &.selectedButton{
    background-color: $black;
    color: $white;
  } 
}
View Compiled
var timer;

var Field = React.createClass({

  shouldComponentUpdate: function(nextProps, nextState) {
    return nextProps.mark !== this.props.mark;
  },

  markField: function(){
    this.props.markField(this.props.index);
  },

  render: function() {
    return (
      <div className="field" onClick={this.markField}>
        <span className="mark">{this.props.mark}</span>
      </div>
    );
  }
});


var Board = React.createClass({

  render: function() {
    return (
      <div className="board">
        {
          this.props.gameBoard.map(function(mark, i){
            return (
              <Field mark={mark} key={i} index={i} markField={this.props.markField}/>
            )
          }, this)
        }
        <div style={{"clear":"both"}}></div>
      </div>
    );
  }
});

var StartAgain = React.createClass({

  shouldComponentUpdate: function() {
    return false;
  },

  render: function(){
    return (
      <button className="startAgain" onClick={this.props.startAgain}>{this.props.children}</button>
    );
  }
});

var ResultPanel = React.createClass({

  shouldComponentUpdate: function(nextProps, nextState) {
    return nextProps.result !== this.props.result;
  },

  render: function() {
    return (
      <div className="resultPanel">
        <p className="result">{this.props.result}</p>
        <StartAgain startAgain={this.props.startAgain.bind(null, true)}>Start Again</StartAgain>
        <StartAgain startAgain={this.props.startAgain.bind(null, false)}>Start Again With New Settings</StartAgain>
      </div>
    );
  }
});

var Settings = React.createClass({

  getInitialState: function() {
    return {
      mark: "x",
      starter: "you",
      mode: "easy"
    };
  },

  onMarkChanged: function(e){
    this.setState({
      mark: e.currentTarget.value
    });
  },

  onStarterChanged: function(e){
    this.setState({
      starter: e.currentTarget.value
    });
  },

  onModeChanged: function(e){
    this.setState({
      mode: e.currentTarget.value
    });
  },

  handleSubmit: function(e){
    e.preventDefault();
    this.props.handleSubmittedSettings(this.state.mark === "x", this.state.starter === "you", this.state.mode === "hard");
  },

  render: function() {
    return (
      <form className="settings" onSubmit={this.handleSubmit}>
        <fieldset>
          <legend>Please select your mark</legend>
          <input type="radio" name="mark" id="x" value="x" checked={this.state.mark === "x"} onChange={this.onMarkChanged}/><label htmlFor="x">X</label> 
          <input type="radio" name="mark" id="o" value="o" checked={this.state.mark === "o"} onChange={this.onMarkChanged}/><label htmlFor="o">O</label>
        </fieldset>
        <fieldset>
          <legend>Who should start first?</legend>
          <input type="radio" name="starter" id="you" value="you" checked={this.state.starter === "you"} onChange={this.onStarterChanged}/><label htmlFor="you">You</label> 
          <input type="radio" name="starter" id="cpu" value="cpu" checked={this.state.starter === "cpu"} onChange={this.onStarterChanged}/><label htmlFor="cpu">CPU</label>
        </fieldset>
        <fieldset>
          <legend>Please choose the level of difficulty</legend>
          <input type="radio" name="mode" id="easy" value="easy" checked={this.state.mode === "easy"} onChange={this.onModeChanged}/><label htmlFor="easy">Easy</label> 
          <input type="radio" name="mode" id="hard" value="hard" checked={this.state.mode === "hard"} onChange={this.onModeChanged}/><label htmlFor="hard">Hard</label>
        </fieldset>
        <button className="button">Start</button>
      </form>
    );
  }
});

var Game = React.createClass({

  getInitialState: function() {
    return {
      playerMarkIsX: false,
      gameIsRunning: false,
      playerTurn: false,
      playerStartsFirst: false,
      gameBoard: ["", "","","","","","","",""],
      result: "",
      hardMode: false
    };
  },

  mark(isPlayer){
    return isPlayer ? (this.state.playerMarkIsX ? "x" : "o") : (this.state.playerMarkIsX ? "o" : "x");
  },

  playerMark: function(){
    return this.mark(true);
  },

  cpuMark: function(){
    return this.mark(false);
  },

  startAgain: function(theSameAsPrevious){
    clearTimeout(timer);
    if(theSameAsPrevious){
      this.setState({
        gameBoard: ["", "","","","","","","",""],
        result: "",
        playerTurn: this.state.playerStartsFirst
      }, function(){
        this.startGame(this.state.playerStartsFirst);
      });
    }
    else{
      this.setState(this.getInitialState());
    }
  },

  winMove: function(field, board){
    var gameBoard = board.slice();
    var hor = 3 * Math.floor(field/3);
    var result = false;

    if(gameBoard[hor] == gameBoard[hor + 1] && gameBoard[hor] == gameBoard[hor + 2]){
      result = true;
    }

    var ver = field % 3;
    if(gameBoard[ver] == gameBoard[ver + 3] && gameBoard[ver] == gameBoard[ver + 6]){
      result = true;
    }

    if(field == 0 || field == 4 || field == 8){
      if(gameBoard[0] == gameBoard[4] && gameBoard[0] == gameBoard[8]){
        result = true;
      }
    }

    if(field == 2 || field == 4 || field == 6){
      if(gameBoard[2] == gameBoard[4] && gameBoard[2] == gameBoard[6]){
        result = true;
      }
    }
    return result;
  },

  isBoardEmpty: function(board){
    var gameBoard = board.slice();
    for(var k=0; k < gameBoard.length; k++){
      if(gameBoard[k] !== ""){
        return false;
      }
    }
    return true;
  },

  fullBoard: function(board){
    var gameBoard = board.slice();
    for(var k=0; k < gameBoard.length; k++){
      if(gameBoard[k] == ""){
        return false;
      }
    }
    return true;
  },

  isMarked: function(field){
    return this.state.gameBoard[field] != "";
  },

  checkChanges(isPlayer, field){
    if(this.winMove(field, this.state.gameBoard)){
      this.setState({
        playerTurn: false,
        result: isPlayer ? "You Won!" : "CPU Won!"
      });
    }
    else if(this.fullBoard(this.state.gameBoard)){
      this.setState({
        playerTurn: false,
        result: "Draw"
      });
    }
    else{
      if(isPlayer){
        timer=setTimeout(this.compChoice, 1000);
      } 
    }
  },

  markField: function(field){
    if(this.state.playerTurn){
      var gameBoard = this.state.gameBoard.slice();
      if(!this.isMarked(field)){
        gameBoard[field] = this.playerMark();
        this.setState({
          gameBoard: gameBoard,
          playerTurn: false
        }, function(){
          this.checkChanges(true, field);
        });
      } 
    }  
  },

  compChoiceEasyMode(){
    console.log("easy choice");
    var freeIndexArray = [];
    var gameBoard = this.state.gameBoard.slice();
    for(var i=0; i < gameBoard.length; i++){
      if(gameBoard[i] == "") freeIndexArray.push(i);
    }
    var random = Math.floor(Math.random() * freeIndexArray.length);
    var k = freeIndexArray[random];
    gameBoard[k] = this.cpuMark();
    this.setState({ 
      gameBoard: gameBoard,
      playerTurn: true
    }, function(){
      this.checkChanges(false, k);
    });
  },

  compChoiceHardMode(){
    console.log("hard choice");
    function minmax(field, maximizingPlayer, self){
      if(self.winMove(field, board)){
        if(maximizingPlayer){
          return 10;
        } 
        return -10;
      }

      if(self.fullBoard(board)){
        return 0;
      }

      if(maximizingPlayer){
        var bestValue = Infinity;

        for(var i=0; i < 9; i++){
          if(board[i] == ""){
            board[i] = self.playerMark();
            var val = minmax(i, false, self);
            bestValue = Math.min(bestValue, val);
            board[i] = "";
          }
        }
        return bestValue;
      }

      else {
        var bestValue = -Infinity;
        for(var i=0; i < 9; i++){
          if(board[i] == ""){
            board[i] = self.cpuMark();
            var val = minmax(i, true, self);
            bestValue = Math.max(bestValue, val);
            board[i] = "";
          }
        }
        return bestValue;
      }

    }
    var board = this.state.gameBoard.slice();
    var indexMax = -1;
    var max = -Infinity;

    if(this.state.playerStartsFirst === false && this.isBoardEmpty(board)){
      var firstChoiceArray = [0, 2, 4, 6, 8];
      var random = Math.floor(Math.random() * firstChoiceArray.length);
      var indexMax = firstChoiceArray[random];
    }

    else{
      var results = [];
      for(var i=0; i < 9; i++){
        if(board[i] == ""){
          board[i] = this.cpuMark();
          var res = minmax(i, true, this);
          results.push(res);
          if(res > max){
            max = res;
            indexMax = i;
          }
          board[i] = "";
        }
      }
    }

    board[indexMax] = this.cpuMark();
    this.setState({ 
      gameBoard: board,
      playerTurn: true
    }, function(){
      this.checkChanges(false, indexMax);
    });

  },

  compChoice: function(){
    this.state.hardMode ? this.compChoiceHardMode() : this.compChoiceEasyMode();
  },

  startGame: function(playerStarts){
    if(!playerStarts){
      timer=setTimeout(this.compChoice, 1000);
    }
  },

  handleSubmittedSettings: function(playerMarkIsX, playerStarts, hardMode){
    this.setState({
      playerMarkIsX: playerMarkIsX,
      playerTurn: playerStarts,
      playerStartsFirst: playerStarts,
      gameIsRunning: true,
      hardMode: hardMode
    }, function(){
      this.startGame(playerStarts);
    });
  },

  render: function() {
    var resultPanelOrSettings = !this.state.gameIsRunning ? <Settings handleSubmittedSettings={this.handleSubmittedSettings} /> : <ResultPanel result={this.state.result} startAgain={this.startAgain}/>
    return (
      <div className="game">
        <Board gameBoard={this.state.gameBoard} markField={this.markField} />
        {resultPanelOrSettings}
      </div>
    );
  }
});

ReactDOM.render(                                                                                 
  <Game />,
  document.getElementById('content')
);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react/15.3.1/react-dom.min.js