A simple game of Tic Tac Toe built using JavaScript. Try to beat the computer... it will destroy you!
Created
January 26, 2016 03:41
-
-
Save Jean13/ea69fdb18a349b42be03 to your computer and use it in GitHub Desktop.
Jean's Space 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
html | |
h1 Jean's | |
h2 Space<br>Tic Tac Toe | |
body | |
.container | |
.board.clearfix | |
.row | |
#0.square | |
#1.square | |
#2.square | |
.row | |
#3.square | |
#4.square | |
#5.square | |
.row | |
#6.square | |
#7.square | |
#8.square | |
.modal-container | |
.modal.choose-modal | |
h3 Select Your Tool | |
.button-area | |
span.x-marker X | |
span.o-marker O | |
.modal.end-game-modal | |
h3 | |
p Tough Luck! | |
.button-area | |
span Play Again |
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
var cpuIcon = 'X'; | |
var playerIcon = 'O'; | |
var AIMove; | |
var liveBoard = [1, -1, -1, -1, 1, 1, 1, -1, -1]; | |
var winningLines = [ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8], | |
[0, 3, 6], | |
[1, 4, 7], | |
[2, 5, 8], | |
[0, 4, 8], | |
[2, 4, 6] | |
]; | |
function renderBoard(board) { | |
board.forEach(function(el, i) { | |
var squareId = '#' + i.toString(); | |
if (el === -1) { | |
$(squareId).text(playerIcon); | |
} else if (el === 1) { | |
$(squareId).text(cpuIcon); | |
} | |
}); | |
$('.square:contains(X)').addClass('x-marker'); | |
$('.square:contains(O)').addClass('o-marker'); | |
} | |
function animateWinLine() { | |
var idxOfArray = winningLines.map(function(winLines) { | |
return winLines.map(function(winLine) { | |
return liveBoard[winLine]; | |
}).reduce(function(prev, cur) { | |
return prev + cur; | |
}); | |
}); | |
var squaresToAnimate = winningLines[idxOfArray.indexOf(Math.abs(3))]; | |
squaresToAnimate.forEach(function(el) { | |
$('#' + el).fadeIn(200).fadeOut(200).fadeIn(200).fadeOut(200).fadeIn(200).fadeIn(200).fadeOut(200).fadeIn(200).fadeOut(200).fadeIn(200); | |
}); | |
} | |
function chooseMarker() { | |
$('.modal-container').css('display', 'block'); | |
$('.choose-modal').addClass('animated bounceInUp'); | |
$('.button-area span').click(function() { | |
var marker = $(this).text(); | |
playerIcon = (marker === 'X' ? 'X' : 'O'); | |
cpuIcon = (marker === 'X' ? 'O' : 'X'); | |
$('.choose-modal').addClass('animated bounceOutDown'); | |
setTimeout(function() { | |
$('.modal-container').css('display', 'none'); | |
$('.choose-modal').css('display', 'none'); | |
startNewGame(); | |
}, 700); | |
$('.button-area span').off(); | |
}); | |
} | |
function endGameMessage() { | |
var result = checkVictory(liveBoard); | |
$('.end-game-modal h3').text(result === 'win' ? 'You Lost' : "It's a draw"); | |
$('.modal-container').css('display', 'block'); | |
$('.end-game-modal').css('display', 'block').removeClass('animated bounceOutDown').addClass('animated bounceInUp'); | |
$('.button-area span').click(function() { | |
$('.end-game-modal').removeClass('animated bounceInUp').addClass('animated bounceOutDown'); | |
setTimeout(function() { | |
$('.modal-container').css('display', 'none'); | |
startNewGame(); | |
}, 700); | |
$('.button-area span').off(); | |
}); | |
} | |
function startNewGame() { | |
liveBoard = [0, 0, 0, 0, 0, 0, 0, 0, 0]; | |
$('.square').text("").removeClass('o-marker x-marker'); | |
renderBoard(liveBoard); | |
playerTakeTurn(); | |
} | |
function playerTakeTurn() { | |
$('.square:empty').hover(function() { | |
$(this).text(playerIcon).css('cursor', 'pointer'); | |
}, function() { | |
$(this).text(''); | |
}); | |
$('.square:empty').click(function() { | |
$(this).css('cursor', 'default'); | |
liveBoard[parseInt($(this).attr('id'))] = -1; | |
renderBoard(liveBoard); | |
if (checkVictory(liveBoard)) { | |
setTimeout(endGameMessage, (checkVictory(liveBoard) === 'win') ? 700 : 100); | |
} else { | |
setTimeout(aiTakeTurn, 100); | |
} | |
$('.square').off(); | |
}); | |
} | |
function aiTakeTurn() { | |
miniMax(liveBoard, 'aiPlayer'); | |
liveBoard[AIMove] = 1; | |
renderBoard(liveBoard); | |
if (checkVictory(liveBoard)) { | |
animateWinLine(); | |
setTimeout(endGameMessage, checkVictory(liveBoard) === 'win' ? 700 : 100); | |
} else { | |
playerTakeTurn(); | |
} | |
} | |
function checkVictory(board) { | |
var squaresInPlay = board.reduce(function(prev, cur) { | |
return Math.abs(prev) + Math.abs(cur); | |
}); | |
var outcome = winningLines.map(function(winLines) { | |
return winLines.map(function(winLine) { | |
return board[winLine]; | |
}).reduce(function(prev, cur) { | |
return prev + cur; | |
}); | |
}).filter(function(winLineTotal) { | |
return Math.abs(winLineTotal) === 3; | |
}); | |
if (outcome[0] === 3) { | |
return 'win'; | |
} else if (outcome[0] === -3) { | |
return 'lose'; | |
} else if (squaresInPlay === 9) { | |
return 'draw'; | |
} else { | |
return false; | |
} | |
} | |
function availableMoves(board) { | |
return board.map(function(el, i) { | |
if (!el) { | |
return i; | |
} | |
}).filter(function(e) { | |
return (typeof e !== "undefined"); | |
}); | |
} | |
//AI: minimax algorithm | |
function miniMax(state, player) { | |
var rv = checkVictory(state); | |
if (rv === 'win') { | |
return 10; | |
} | |
if (rv === 'lose') { | |
return -10; | |
} | |
if (rv === 'draw') { | |
return 0; | |
} | |
var moves = []; | |
var scores = []; | |
availableMoves(state).forEach(function(square) { | |
state[square] = (player === 'aiPlayer') ? 1 : -1; | |
scores.push(miniMax(state, (player === 'aiPlayer') ? 'opponent' : 'aiPlayer')); | |
moves.push(square); | |
state[square] = 0; | |
}); | |
if (player === 'aiPlayer') { | |
AIMove = moves[scores.indexOf(Math.max.apply(Math, scores))]; | |
return Math.max.apply(Math, scores); | |
} else { | |
AIMove = moves[scores.indexOf(Math.min.apply(Math, scores))]; | |
return Math.min.apply(Math, scores); | |
} | |
} | |
renderBoard(liveBoard); | |
chooseMarker(); |
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="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.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
@import url(https://fonts.googleapis.com/css?family=Cinzel|Philosopher|Kreon|Merriweather|Electrolize|Orbitron|Geo); | |
$someBlue: #6666ff; | |
$brightGreen: #06cd51; | |
$someWhite: #fff; | |
$someRed: #6f0101; | |
$darkBlue: #5e3669; | |
$someYellow: #eeda76; | |
html { | |
//overflow: hidden; | |
} | |
h1 { | |
font-family: 'Cinzel'; | |
font-size: 30px; | |
text-align: center; | |
color: white; | |
padding: 1%; | |
margin-top: -1.5%; | |
} | |
h2 { | |
font-family: 'Philosopher'; | |
font-size: 34px; | |
text-align: center; | |
color: white; | |
font-weight: bold; | |
margin-top: -2%; | |
text-shadow: 1px 1px 8px grey; | |
} | |
body { | |
background-image: url('http://mynoise.net/Data/SPACESHIP/fb.jpg'); | |
} | |
.container { | |
position: absolute; | |
left: 50%; | |
top: 50%; | |
transform: translate(-50%, -50%); | |
margin-top: 3%; | |
} | |
.clearfix:after { | |
content: ""; | |
display: table; | |
clear: both; | |
} | |
p { | |
font-weight: 400; | |
} | |
h3 { | |
font-weight: 700; | |
text-transform: uppercase; | |
margin: 0 0 10px 0; | |
text-align: center; | |
font-family: 'Electrolize'; | |
color: $someRed; | |
text-shadow: 1px 1px 8px red; | |
} | |
.board { | |
position: relative; | |
width: 22em; | |
font-family: 'Electrolize'; | |
font-weight: 700; | |
margin: 0 auto; | |
color: $brightGreen; | |
font-weight: bold; | |
text-shadow: 1px 1px 8px green; | |
box-shadow: 1px 1px 8px grey; | |
} | |
.row { | |
float: left; | |
} | |
.row:first-child .square { | |
border-top: 0; | |
} | |
.square { | |
color: #605B56 ; | |
border-color: #605B56; | |
font-size: 6em; | |
width: 1.2em; | |
height: 1.2em; | |
line-height: 1.2em; | |
float: left; | |
border-style: solid; | |
border-width: 1px 1px 0 0; | |
text-align: center; | |
cursor: default; | |
} | |
.square:last-child { | |
border-right: 0px; | |
} | |
.x-marker { | |
color: $brightGreen; | |
} | |
.o-marker { | |
color: $brightGreen; | |
} | |
.modal-container{ | |
position: absolute; | |
left: 50%; | |
top: 50%; | |
transform: translate(-50%, -50%); | |
width: 70%; | |
display: none; | |
} | |
.modal { | |
font-family: 'Electrolize'; | |
text-align: center; | |
background-color: grey; | |
font-size: 1em; | |
height: 100%; | |
padding: 30px; | |
opacity: 0.8; | |
box-shadow: 1px 1px 8px grey; | |
border: solid; | |
border-color: grey; | |
} | |
.end-game-modal { | |
display: none; | |
} | |
.choose-modal span{ | |
line-height: 1.5em; | |
width: 1.5em; | |
height: 1.5em; | |
font-size: 2.5em; | |
border: solid; | |
border-radius: 5%; | |
} | |
.end-game-modal span { | |
color: $someYellow; | |
text-transform: uppercase; | |
line-height: 1.5em; | |
font-size: 1.3em; | |
padding: 0.2em 0.4em; | |
text-shadow: 1px 1px 8px yellow; | |
} | |
.button-area span { | |
display: inline-block; | |
background-color: hsla(30, 10%, 10%, 0.35); | |
margin: 0.2em 0.2em 0 0.2em; | |
border-radius: 5%; | |
border: solid; | |
} | |
.button-area span:hover { | |
cursor: pointer; | |
background-color: hsla(30, 10%, 10%, 0.95); | |
} | |
@media screen and (max-width: 500px){ | |
.container { | |
top: 40px; | |
-moz-transform: scale(0.6); | |
-ms-transform: scale(0.6); | |
-o-transform: scale(0.6); | |
-webkit-transform: scale(0.6); | |
transform: scale(0.6); | |
-o-transform-origin: 0 0; | |
-ms-transform-origin: 0 0; | |
-webkit-transform-origin: 0 0; | |
-moz-transform-origin: 0 0; | |
transform-origin: 0 0; | |
left: 50%; | |
margin-left: -100px; | |
margin-top: 5%; | |
} | |
} |
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="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.4.0/animate.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment