.board__settings
  i.fa.fa-cog.board__settings-cog
  h1.board__header-settings Choose Player
    .board__row
      .board__settings__choice-cross
      .board__settings__choice-nought
.board__difficulty
  i.fa.fa-cog.board__settings-cog
  h1.board__header-difficulty Select Difficulty
  #easy.board__difficulty__button-easy Easy
  #medium.board__difficulty__button-medium Medium
  #hard.board__difficulty__button-hard Hard
.board
  - for (var j = 0; j < 9; j++)
    if (j == 0 || j % 3 == 0)
      .board__row
        - for (var i = j; i < (3 + j); i++)
          - if (i >= 9) break;
            input(type='radio').board__slot-hidden
            label(id=i).board__slot
View Compiled
@import "bourbon";

@import url(https://fonts.googleapis.com/css?family=PT+Sans:400,700,300);


$size-sm: 60px;
$size: 110px;
$spacing: 5px;
$thickness: 2px;
$padding: 70px;
$radius: 30px;

$player-color: rgb(255, 255, 255);
$computer-color: rgba(255, 255, 255, .5);
$border-color: #5E5955;
$default-color: #55504C;

$cross-icon:  "\f00d";
$nought-icon:  "\f10c";

body {
  font-family: Arial;
  @include linear-gradient(50deg, #BB4A9F 0%, #E4E5D7 35%, #BEE8D7 65%, #51C8C8 90%);
  background-repeat: no-repeat;
  background-attachment: fixed;
}

.board-default {
  width: ($size-sm * 3 + $padding);
  height: ($size-sm * 3 + $padding);
  display: flex;
  justify-content: center;
  align-content: center;
  flex-direction: column;
  text-align: center;

  border-radius: $radius;
  background-color: rgba(255, 255, 255, .4);

  &:before {
    content: '';
    position: absolute;
    z-index: -1;
    top: -15px;
    left: -15px;
    right: -15px;
    bottom: -15px;
    border-radius: $radius+21;
    border: 15px solid rgba(0, 0, 0, .05);
  }
  @media(min-width: 450px) {
    width: ($size * 3 + $padding);
    height: ($size * 3 + $padding);
  }
}

.board {
  @extend .board-default;
  position: relative;
  margin: 40px auto;
}

.board__wrapper {
  display: inline-flex;
}
.board__row {
  display: block;
}

// Radio input
.board__slot-hidden {
  display: none;
}

// Radio label
.board__slot {
  display: inline-block;
  height: $size-sm;
  width: $size-sm;
  box-sizing: border-box;
  position: relative;
  cursor: pointer;
  border-radius: 15px;
  background-color: rgba(0, 0, 0, .15);
  margin: 5px;
  @media(min-width: 450px) {
    height: $size;
    width: $size;
    border-radius: $radius;
  }

  &:hover {
    background-color: rgba(0, 0, 0, 0.1);
  }

  &:active {
    border: 2px solid rgba(0, 0, 0, .09);
  }
}

// Settings menu
.board__settings {
  @extend .board-default;
  position: absolute;
  margin: auto;
  right: 0;
  left: 0;
  z-index: 3;
  text-align: center;
  visibility: hidden;
  background-color: rgba(223, 244, 239, 1);
}


.board__settings-cog {

  position: absolute;
  top: -15px;
  left: -15px;
  width: 30px;
  font-size: 2em;
  background-color: rgb(224, 244, 239);
  border-radius: 50%;
  border: 10px solid rgb(224, 244, 239);
  cursor: pointer;
  z-index: 9;
  color: rgba(0, 0, 0, .5);

  &:hover {
    box-shadow: 1px 1px 2px 5px rgba(0, 0, 0, .05);
  }

  &:after {
    content: '';
    position: absolute;
    top: -10px;
    left: -10px;
    right: -10px;
    bottom: -10px;
    border-radius: $radius+21;
    border: 3px solid rgba(0, 0, 0, .2);
  }
}

.board__settings__choice {
  display: inline-block;
  height: $size-sm;
  width: $size-sm;
  box-sizing: border-box;
  position: relative;
  cursor: pointer;
  border-radius: 15px;
  background-color: rgba(0, 0, 0, .15);
  margin: 5px;
  z-index: 99;
  padding-top: 8px;

  @media(min-width: 450px) {
    height: $size;
    width: $size;
    border-radius: $radius;
  }
  @at-root #{&}-cross {
    @extend .board__settings__choice;
    font-size: .7em;
  }
  @at-root #{&}-nought {
    @extend .board__settings__choice;
    font-size: .2em;
  }

  &:hover {
    background-color: rgba(0, 0, 0, 0.1);
  }

  &:active {
    border: 2px solid rgba(0, 0, 0, .09);
  }
}

.board__settings__choice-cross {
  font-family: 'FontAwesome';
  font-size: 2.3em;
  cursor: pointer;
  @media(min-width: 450px) {
    font-size: 3em;
    top: 15px;
  }
  &:before {
    color: $player-color;
    content: $cross-icon;
  }
}

.board__settings__choice-nought {
  font-family: 'FontAwesome';
  font-size: 2.3em;
  cursor: pointer;
  @media(min-width: 450px) {
    font-size: 2.8em;
    top: 10px;
  }
  &:before {
    color: $player-color;
    content: $nought-icon;
  }
}

// Difficulty menu
.board__difficulty {
  @extend .board-default;
  position: absolute;
  margin: auto;
  right: 0;
  left: 0;
  z-index: 2;
  text-align: center;
  background-color: rgba(223, 244, 239, .8);
}

.board__header {
  font-family: 'PT Sans', Arial, sans-serif;
  color: rgba(0, 0, 0, .7);
  font-size: 1.5em;
  position: absolute;
  top: 20px;
  left: 0;
  right: 0;
  text-transform: uppercase;
  @media(min-width: 450px) {
    font-size: 2em;
  }
  @at-root #{&}-settings {
    @extend .board__header;
  }
  @at-root #{&}-difficulty {
    @extend .board__header;
  }
}

.board__difficulty__button {
  position: relative;
  display: block;
  margin: 10px auto;
  padding: 5px;
  font-family: 'PT Sans', arial, sans-serif;
  font-size: 1em;
  width: 150px;
  background-color: #fff;
  border: 1px solid #ccc;
  color: #000;
  text-transform: uppercase;

  &:before, &:after {
    content: '';
    position: absolute;
    z-index: -2;
    transition: all 250ms ease-out;
    bottom: 15px;
    width: 50%;
    height: 20%;
    box-shadow: 0 10px 30px rgba(31, 31, 31, 0.5);
  }
  &:before {
    left: 8px;
    transform: rotate(-3deg);
  }
  &:after {
    right: 8px;
    transform: rotate(3deg);
  }
  &:hover {
    border-color: transparent;
    cursor: pointer;
    &:before, &:after {
      box-shadow: 0 15px 12px rgba(31, 31, 31, 0.7);
    }
  }

  @at-root #{&}-easy {
    @extend .board__difficulty__button;
    margin-top: 70px;
  }
  @at-root #{&}-medium {
    @extend .board__difficulty__button;
  }
  @at-root #{&}-hard {
    @extend .board__difficulty__button;
  }

  @media(min-width: 450px) {
    padding: 10px 40px;
    font-size: 2em;
    width: 200px;
  }
}

// nought icons
.nought:before, .cross:before {
  font-family: 'FontAwesome';
  font-size: 3em;
  position: absolute;
  top: 5px;
  bottom: 0;
  left: 0px;
  right: 0px;
  cursor: default;
  @media(min-width: 450px) {
    font-size: 5em;
    top: 15px;
  }
}
.nought:before {
  content: $nought-icon;
}
.cross:before {
  content: $cross-icon;
}

.computer-color {
  color: $computer-color;
}

.player-color {
  color: $player-color;
}

/* Animations */
.slideUp {
  animation: slideUp 1s ease-in-out;
  animation-fill-mode: forwards;
}
.slideDown {
  animation: slideDown 1s ease-in-out;
  animation-fill-mode: forwards;
}
@keyframes slideUp {
  0% {
    transform: translateY(0);
  }
  100% {
    transform: translateY(-500px);
  }
}
@keyframes slideDown {
  0% {
    transform: translateY(-500px);
  }
  100% {
    transform: translateY(0);
  }
}
View Compiled
/* Globals */
var NUM_ROWS = 3,
  	NUM_COLS = 3,
  	NUM_SQUARES = NUM_ROWS * NUM_COLS,
  	GAMEBOARD = new Array(NUM_SQUARES),
    WIN_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]],
  	MAX_DEPTH,
  	AI_MOVE,
    PLAYER_CLASS = 'cross',
    COMPUTER_CLASS = 'nought',
    RUNNING = false;

$(document).ready(function() {
	/* Start a new game */
	new_game();

  /* Settings cog clicked, show the settings menu */
  $(".board__settings-cog").click(function() {
    if ($(".board__settings").css('visibility') == 'hidden') {
      $(".board__settings").css('visibility', 'visible');
    } else {
      $(".board__settings").css('visibility', 'hidden');
    }
  });

  /* Player class has been switched from the settings menu */
  $(".board__settings__choice-cross").click(function() {
    PLAYER_CLASS = 'cross';
    COMPUTER_CLASS = 'nought';
    $(".board__settings").css('visibility', 'hidden');
    console.log('set class to cross');
  });

  $(".board__settings__choice-nought").click(function() {
    PLAYER_CLASS = 'nought';
    COMPUTER_CLASS = 'cross';
    $(".board__settings").css('visibility', 'hidden');
  });

  /* Difficulty selected */
  $("div[class*=board__difficulty__button]").click(function() {
    var difficulty = $(this).attr("id");

    if (difficulty === 'easy') MAX_DEPTH = 1;
    else if (difficulty === 'medium') MAX_DEPTH = 3;
    else MAX_DEPTH = 6;

    $(".board__difficulty").removeClass('slideDown').addClass('slideUp');
    new_game();

  });

	/* Process a square being clicked */
  $(".board__slot").click(function() {
    if (RUNNING) {
  		var pos = Number($(this).attr("id"));

  		/* If the square is empty, process the click */
  		if (GAMEBOARD[pos] == "") {
  			$(this).addClass(PLAYER_CLASS + ' player-color');
  			GAMEBOARD[pos] = "X";

  			if (full(GAMEBOARD)) {
          RUNNING = false;
  				$(".board__header-difficulty").html("It's a tie!");
          $(".board__difficulty").removeClass('slideUp').addClass('slideDown');
  			} else if (wins(GAMEBOARD, "X")) {
          RUNNING = false;
  				$(".board__header-difficulty").html("You win!");
          $(".board__difficulty").removeClass('slideUp').addClass('slideDown');
  			} else {
  				minimax(GAMEBOARD, "O", 0);
  				GAMEBOARD[AI_MOVE] = "O";
  				$(".board__slot[id=" + AI_MOVE + "]").addClass(COMPUTER_CLASS + ' computer-color');

  				if (wins(GAMEBOARD, "O")) {
            RUNNING = false;
  					$(".board__header-difficulty").html("You lost!");
            $(".board__difficulty").removeClass('slideUp').addClass('slideDown');
  				}
  			}
  		}
    }
	});
});

/* Starts a new game */
function new_game() {
	/* Clear the table */
	$(".board__slot").each(function() {
		$(this).removeClass(PLAYER_CLASS + ' player-color computer-color ' + COMPUTER_CLASS);
	});

	/* Clear the gameboard */
	for (var i = 0; i < NUM_SQUARES; i++) {
		GAMEBOARD[i] = "";
	}

  RUNNING = true;
}

/* For a given state of the board, returns all the available moves */
function get_available_moves(state) {
	var all_moves = Array.apply(null, {length: NUM_SQUARES}).map(Number.call, Number);
	return all_moves.filter(function(i) { return state[i] == ""; });
}

/* Given a state of the board, returns true if the board is full */
function full(state) {
	return !get_available_moves(state).length;
}

/* Given a state of the board, returns true if the specified player has won */
function wins(state, player) {
	var win;

	for (var i = 0; i < WIN_COMBOS.length; i++) {
		win = true;
		for (var j = 0; j < WIN_COMBOS[i].length; j++) {
			if (state[WIN_COMBOS[i][j]] != player) {
				win = false;
			}
		}
		if (win) {
			return true;
		}
	}
	return false;
}

/* Given a state of the board, returns true if the board is full or a player has won */
function terminal(state) {
	return full(state) || wins(state, "X") || wins(state, "O");
}

/* Returns the value of a state of the board */
function score(state) {
	if (wins(state, "X")) {
		return 10;
	} else if (wins(state, "O")) {
		return -10;
	} else {
		return 0;
	}
}

/* Finds the optimal decision for the AI */
function minimax(state, player, depth) {
	if (depth >= MAX_DEPTH || terminal(state)) {
		return score(state);
	}

	var max_score,
		min_score,
		scores = [],
		moves = [],
		opponent = (player == "X") ? "O" : "X",
		successors = get_available_moves(state);

	for (var s in successors) {
		var possible_state = state;
		possible_state[successors[s]] = player;
		scores.push(minimax(possible_state, opponent, depth + 1));
		possible_state[successors[s]] = "";
		moves.push(successors[s]);
	}

	if (player == "X") {
		AI_MOVE = moves[0];
		max_score = scores[0];
		for (var s in scores) {
			if (scores[s] > max_score) {
				max_score = scores[s];
				AI_MOVE = moves[s];
			}
		}
		return max_score;
	} else {
		AI_MOVE = moves[0];
		min_score = scores[0];
		for (var s in scores) {
			if (scores[s] < min_score) {
				min_score = scores[s];
				AI_MOVE = moves[s];
			}
		}
		return min_score;
	}
}
Run Pen

External CSS

  1. //maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css

External JavaScript

  1. //cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js