Skip to content

Instantly share code, notes, and snippets.

@Fauntleroy
Created April 23, 2013 06:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fauntleroy/5441300 to your computer and use it in GitHub Desktop.
Save Fauntleroy/5441300 to your computer and use it in GitHub Desktop.
Tic Tac Toe
<div id="game">
<table id="board">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<button id="reset">Reset</button>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
~function(){
var PLAYER = 'o',
AI = 'x';
var $board,
$reset,
turn = PLAYER,
gameover = false,
paths = [[[0,0],[1,1],[2,2]],
[[0,2],[1,1],[2,0]],
[[0,0],[0,1],[0,2]],
[[1,0],[1,1],[1,2]],
[[2,0],[2,1],[2,2]],
[[0,0],[1,0],[2,0]],
[[0,1],[1,1],[2,1]],
[[0,2],[1,2],[2,2]]],
board = [[ null, null, null ],
[ null, null, null ],
[ null, null, null ]];
// occupy a cell with the specified player's livery
var setCell = function( position, player ){
board[ position[0] ][ position[1] ] = player;
var $cell = $board.find('tr:eq('+ position[0] +') > td:eq('+ position[1] +')');
$cell.addClass( player );
};
// start the next turn
var nextTurn = function(){
var status = checkStatus();
turn = ( turn === AI )? PLAYER: AI;
if( turn === AI && status ){
setTimeout( aiMove, 750 );
}
};
// do the next step in the game
var checkStatus = function(){
var possible_ai_scores = findScoringPaths( AI );
var possible_player_scores = findScoringPaths( PLAYER );
var ai_scores = findScoringPaths( AI, 3 );
var player_scores = findScoringPaths( PLAYER, 3 );
if( ai_scores.length > 0 ){
endGame( 'victory', AI );
return false;
}
else if( player_scores.length > 0 ){
endGame( 'victory', PLAYER );
return false;
}
else if( possible_ai_scores.length + possible_player_scores.length === 0 ){
endGame('draw');
return false;
}
return true;
};
// display an endgame message and update game status
var endGame = function( status, player ){
if( status === 'victory' ){
alert( player +' is victorious!' );
}
else if( status === 'draw' ){
alert( PLAYER +' and '+ AI +' failed to best one another' );
}
gameover = true;
};
// a simple, brutish AI move
var aiMove = function(){
// find scoring paths, weight
var possible_scores = findScoringPaths( AI );
var imminent_scores = $.grep( possible_scores, function( path, i ){
return path.score === 2;
});
// find enemy scoring paths, weight
var possible_opponent_scores = findScoringPaths( PLAYER );
var imminent_opponent_scores = $.grep( possible_opponent_scores, function( path, i ){
return path.score === 2;
});
if( !board[1][1] ){
setCell( [1,1], AI );
}
else if( imminent_scores.length > 0 ){
var cell = firstEmptyCell( imminent_scores[0].path );
setCell( cell, AI );
}
else if( imminent_opponent_scores.length > 0 ){
var cell = firstEmptyCell( imminent_opponent_scores[0].path );
setCell( cell, AI );
}
else if( possible_opponent_scores.length > 0 ){
var cell = firstEmptyCell( possible_opponent_scores[0].path );
setCell( cell, AI );
}
else if( possible_scores.length > 0 ){
var cell = firstEmptyCell( possible_scores[0].path );
setCell( cell, AI );
}
// initiate the next turn
nextTurn();
};
// find possible scoring paths for specified player
// sort scoring paths by score
// limit scoring paths to a certain score
var findScoringPaths = function( player, minscore ){
minscore = minscore || 0;
var possible_scores = [],
opponent = ( player === PLAYER )? AI: PLAYER;
for( var i in paths ){
var path = paths[i],
score = 0,
blocked = false;
for( var ii in path ){
var cell = board[ path[ii][0] ][ path[ii][1] ];
if( cell === opponent ){
blocked = true;
break;
}
else if( cell === player ){
score++;
}
}
if( !blocked && score >= minscore ){
possible_scores.push({
path: path,
score: score
});
}
}
// sort paths by score chance
possible_scores.sort( function( a, b ){
return b.score - a.score;
});
return possible_scores;
};
// find the first empty cell in a path
var firstEmptyCell = function( path ){
var cell;
for( var i in path ){
if( !board[ path[i][0] ][ path[i][1] ] ){
cell = path[i];
break;
}
}
return cell;
};
// reset the status of the game
var reset = function(){
board = [[ null, null, null ],
[ null, null, null ],
[ null, null, null ]];
turn = PLAYER;
$board.find('td').removeClass('x o');
gameover = false;
};
// simple bindings to make the game work
$(function(){
$board = $('#board');
$reset = $('#reset');
$board.on( 'click', 'td:not(.x,.o)', function( e ){
if( gameover || turn !== PLAYER ) return;
var $cell = $(this);
var $row = $cell.parent();
var position = [ $row.index(), $cell.index() ];
// set square
setCell( position, PLAYER );
// use turn
nextTurn();
});
$reset.on( 'click', reset );
});
}();
body {
font: sans-serif;
}
#board {
margin: 15px;
padding: 0;
border-spacing: 2px;
background: rgb( 220, 220, 220 );
-webkit-perspective: 1000;
}
#board tr td {
width: 100px;
height: 100px;
margin: 0;
padding: 0;
background: rgb( 235, 235, 235 );
font-size: 85px;
font-weight: bold;
text-align: center;
border: rgb( 204, 204, 204 ) 2px solid;
-webkit-transform-style: preserve-3d;
-webkit-transition: 250ms;
box-shadow: inset 0px 0px 15px 5px rgba( 0, 0, 0, 0.025 );
}
#board tr td:hover:not(.x):not(.o) {
background: rgb( 255, 255, 255 );
box-shadow: inset 0px 0px 25px 5px rgba( 0, 0, 0, 0.045 ), 0px 0px 30px 5px rgba( 255, 255, 255, 0.65 ), 0px 5px 15px 0px rgba(0, 0, 0, 0.5);
cursor: pointer;
-webkit-transform: rotateX( 5deg ) scale(1.05) translateZ(10px);
position: relative;
z-index: 99;
}
#board tr td.x,
#board tr td.o {
background: rgb( 250, 250, 250 );
-webkit-transform: rotateX( 180deg );
}
#board tr td.x {
color: #F00;
}
#board tr td.x:after {
content: "\00D7";
}
#board tr td.o {
color: #00F;
}
#board tr td.o:after {
content: "\25CB";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment