A simple Tic Tac Toe game with jQuery & Twitter Bootstrap
Created
January 13, 2017 09:09
-
-
Save RemLampa/8e43d898f28af7332c134dace84c0e16 to your computer and use it in GitHub Desktop.
Tic-Tac-Toe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h1 id="title" class="text-primary">Tic Tac Toe</h1> | |
<div id="options" class="modal fade" role="dialog" tabindex="-1"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h4 class="modal-title">Choose Your Side</h4> | |
</div> | |
<div class="modal-body"> | |
<p class="lead text-center">Choose: "X" or "O"</p> | |
<p class="text-center">Default is "X".</p> | |
</div> | |
<div class="modal-footer"> | |
<button id="x-button" type="button" class="btn btn-lg btn-primary" data-dismiss="modal">X</button> | |
<button id="o-button" type="button" class="btn btn-lg btn-danger" data-dismiss="modal">O</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="toss-coin" class="modal fade" role="dialog" tabindex="-1"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h4 class="modal-title">Toss Coin!</h4> | |
</div> | |
<div class="modal-body"> | |
<p class="lead text-center"></p> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-lg btn-primary" data-dismiss="modal">Ok</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="winDialog" class="modal fade" role="dialog" tabindex="-1"> | |
<div class="modal-dialog" role="document"> | |
<div class="modal-content"> | |
<div class="modal-header"> | |
<h4 class="modal-title"></h4> | |
</div> | |
<div class="modal-body"> | |
<p class="lead text-center"></p> | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-lg btn-danger" data-dismiss="modal">Reset</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div class="container-fixed"> | |
<div class="row"> | |
<div id="cell-1" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
<div id="cell-2" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
<div id="cell-3" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
</div> | |
<div class="row"> | |
<div id="cell-4" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
<div id="cell-5" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
<div id="cell-6" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
</div> | |
<div class="row"> | |
<div id="cell-7" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
<div id="cell-8" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
<div id="cell-9" class="col-xs-4"> | |
<div class="middle-align"></div> | |
</div> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(document).ready(function() { | |
var playerCells = [], | |
aiCells = [], | |
remaining = 9, | |
winCells = -1, | |
player = 'X', | |
ai = 'O', | |
firstMove = 'ai'; | |
// AI decision function | |
var aiPick = function(ai) { | |
var choice = -1, | |
choiceCell = ''; | |
// first move with highest chance of winning is middle or corner cells | |
if (aiCells.length === 0 && playerCells.length === 0) { | |
choice = [1,3,5,7,9][Math.floor(Math.random()*5)]; | |
} else if (aiCells.length === 1 && aiCells[0] !== 5 && [1,3,7,9].indexOf(playerCells[0]) !== -1) { | |
// always pick corners for second turn if first turn of both players are corners | |
while (choice === -1 || $('#cell-' + choice + ' div').html() !== '') { | |
choice = [1,3,7,9][Math.floor(Math.random() * 4)]; | |
} | |
console.log('ai\'s second turn. pick a corner!'); | |
} else { | |
choice = findWinCell(aiCells); | |
} | |
if (choice === -1) { | |
choice = findWinCell(playerCells); | |
if (choice === -1) { | |
// find best move | |
var blankCellArray = []; | |
patterns.forEach(function(pattern, patternIndex, patternArray) { | |
var blankCells = []; | |
if (pattern.every(function(cell, cellIndex, cellArray) { | |
return playerCells.indexOf(cell) === -1; | |
})) { | |
pattern.forEach(function(cell, cellIndex, cellArray) { | |
if (aiCells.indexOf(cell) === -1 && $('#cell-' + cell + ' div').html() === '') { | |
blankCells.push(cell); | |
} | |
}); | |
if (blankCells.length === 2) { | |
blankCellArray.push(blankCells); | |
} | |
}; | |
}); | |
if (blankCellArray.length > 0) { | |
choice = blankCellArray[Math.floor(Math.random() * blankCellArray.length)][Math.floor(Math.random() * 2)]; | |
console.log(blankCellArray, choice); | |
} | |
if (choice === -1) { | |
choice = Math.floor((Math.random() * 9) + 1); | |
} | |
} | |
} | |
choiceCell = '#cell-' + choice; | |
if ($(choiceCell).find('div').html() === '') { | |
$(choiceCell).find('div').html(ai); | |
aiCells.push(choice); | |
aiCells.sort(); | |
winCells = checkWin(aiCells); | |
if (winCells === -1) { | |
remaining--; | |
if (remaining === 0) { | |
resetBoard('draw', [1, 2, 3, 4, 5, 6, 7, 8, 9]); | |
return; | |
} | |
$('.col-xs-4').each(function(index) { | |
if ($(this).find('div').html() === '') { | |
$(this).addClass('clickable'); | |
} | |
}); | |
} else { | |
resetBoard('ai', winCells); | |
} | |
} else { | |
aiPick(ai); | |
} | |
}; | |
// find a winning cell | |
var findWinCell = function(cells) { | |
var missingCell = -1; | |
patterns.some(function(pattern, patternIndex, patternArray) { | |
var missing = []; | |
pattern.forEach(function(cell, cellIndex, cellArray) { | |
if (cells.indexOf(cell) === -1) { | |
missing.push(cell); | |
} | |
}); | |
if (missing.length === 1 && $('#cell-' + missing[0] + ' div').html() === '') { | |
missingCell = missing[0]; | |
return true; | |
} else { | |
return false; | |
} | |
}); | |
return missingCell; | |
}; | |
// winning patterns | |
var patterns = [ | |
[1, 2, 3], | |
[4, 5, 6], | |
[7, 8, 9], | |
[1, 4, 7], | |
[2, 5, 8], | |
[3, 6, 9], | |
[1, 5, 9], | |
[3, 5, 7] | |
].sort(); | |
// check for win | |
var checkWin = function(cells) { | |
var winIndex = -1; | |
patterns.some(function(pattern, patternIndex, array) { | |
if (pattern.every(function(cell, index, patternArray) { | |
return cells.indexOf(cell) != -1; | |
})) { | |
winIndex = pattern; | |
return true; | |
} | |
}); | |
return winIndex; | |
}; | |
// reset board | |
var resetBoard = function(side, array) { | |
if (side === 'ai') { | |
$('#winDialog .lead').html('You lose.'); | |
} else if (side === 'player') { | |
$('#winDialog .lead').html('You win.'); | |
} else { | |
$('#winDialog .lead').html('It\'s a draw!'); | |
} | |
array.forEach(function(element, index, array) { | |
$('#cell-' + element + ' div').addClass('blinking'); | |
}); | |
var blinkInterval = setInterval(function() { | |
$('.blinking').fadeOut(250); | |
$('.blinking').fadeIn(250); | |
}, 500); | |
setTimeout(function() { | |
clearInterval(blinkInterval); | |
$('#winDialog').modal('show'); | |
}, 1500); | |
$('#winDialog').on('hidden.bs.modal', function(event) { | |
winCells = -1; | |
remaining = 9; | |
aiCells = []; | |
playerCells = []; | |
$('.col-xs-4 div').html(''); | |
$('.blinking').removeClass('blinking'); | |
$('.clickable').removeClass('clickable'); | |
if (firstMove === 'ai') { | |
aiPick(ai); | |
} | |
setTimeout(function() { | |
$('.col-xs-4').addClass('clickable'); | |
}, 500); | |
}); | |
}; | |
// for implementation? (but first player always wins/draws) | |
var tossCoin = function() { | |
$('#toss-coin').modal('show'); | |
if (Math.floor(Math.random() * 2) === 0) { | |
firstMove = 'ai'; | |
$('#toss-coin p').html('AI goes first!'); | |
$('#toss-coin').on('hidden.bs.modal', function(event) { | |
aiPick(ai); | |
}); | |
} else { | |
firstMove = 'player'; | |
$('#toss-coin p').html('You go first!'); | |
} | |
}; | |
$('#x-button').click(function() { | |
player = 'X'; | |
ai = 'O'; | |
if (firstMove === 'ai') { | |
aiPick(ai); | |
} | |
//tossCoin(); | |
}); | |
$('#o-button').click(function() { | |
player = 'O'; | |
ai = 'X'; | |
if (firstMove === 'ai') { | |
aiPick(ai); | |
} | |
//tossCoin(); | |
}); | |
$('#options').modal('show'); | |
$('.col-xs-4').addClass('clickable'); | |
$('.col-xs-4').click(function(event) { | |
if ($(this).hasClass('clickable')) { | |
$('.col-xs-4').removeClass('clickable'); | |
$(this).find('div').html(player); | |
playerCells.push(parseInt($(this).attr('id').substr(-1))); | |
playerCells.sort(); | |
winCells = checkWin(playerCells); | |
if (winCells === -1) { | |
remaining--; | |
if (remaining > 0) { | |
setTimeout(function() { | |
aiPick(ai); | |
}, 750); | |
} | |
if (remaining === 0) { | |
resetBoard('draw', [1, 2, 3, 4, 5, 6, 7, 8, 9]); | |
return; | |
} | |
} else { | |
resetBoard('player', winCells); | |
} | |
} | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#title { | |
text-align: center; | |
} | |
.modal-body { | |
text-align: center; | |
} | |
.modal-footer { | |
text-align: center; | |
} | |
.container-fixed { | |
margin: 50px auto; | |
width: 750px; | |
} | |
.col-xs-4 { | |
text-align: center; | |
border: 1px black solid; | |
height: 250px; | |
width: 250px; | |
display: table; | |
margin: 0; | |
padding: 0; | |
} | |
.clickable { | |
cursor: pointer; | |
} | |
.middle-align { | |
margin: 0; | |
padding: 0; | |
font-size: 100px; | |
display: table-cell; | |
vertical-align: middle; | |
} | |
.blinking { | |
color: #FF0000; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment