A Pen by Ojini Chizoba Jude on CodePen.
Created
February 13, 2018 17:23
-
-
Save Deityhub/4fac5869b5299f158b264197838b3e14 to your computer and use it in GitHub Desktop.
TicTacToe | Unbeatable AI
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> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<title>Tic-Tac-Toe Game</title> | |
<link href="https://fonts.googleapis.com/css?family=Atomic+Age|Pacifico" rel="stylesheet"> | |
</head> | |
<body> | |
<div class="board"> | |
<div> | |
<h1>Tic Tac Toe</h1> | |
</div> | |
<table> | |
<tr> | |
<td class="cell" id="0"></td> | |
<td class="cell" id="1"></td> | |
<td class="cell" id="2"></td> | |
</tr> | |
<tr> | |
<td class="cell" id="3"></td> | |
<td class="cell" id="4"></td> | |
<td class="cell" id="5"></td> | |
</tr> | |
<tr> | |
<td class="cell" id="6"></td> | |
<td class="cell" id="7"></td> | |
<td class="cell" id="8"></td> | |
</tr> | |
</table> | |
<footer> | |
<p>© 2017 | <a href="https://github.com/deityhub" target="_blank">Ojini Chizoba Jude</a></p> | |
</footer> | |
</div> | |
</body> | |
</html> |
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(){ | |
// human | |
var huPlayer = "O"; | |
// ai | |
var aiPlayer = "X"; | |
//board | |
var board = [0,1,2,3,4,5,6,7,8]; | |
var round = 0; | |
//human player starts game with a click | |
$('td').click(function(e){ | |
var id = e.target.id | |
move(board, huPlayer, id); | |
}) | |
//keeps track of each move of the player | |
function move(board, player, spotClicked){ | |
if(board[spotClicked] != 'O' && board[spotClicked] != 'X'){ | |
$('#'+spotClicked).text(player); | |
board[spotClicked] = player; | |
round++; | |
if(winning(board, player)){ | |
setTimeout(function(){ | |
alert('Win'); | |
reset(); | |
}, 500) | |
}else if(round > 8){ | |
setTimeout(function(){ | |
alert('Tie'); | |
reset(); | |
}, 500) | |
}else{ | |
round++; | |
var index = minimax(board, aiPlayer).index; | |
board[index] = aiPlayer; | |
$('#'+index).text(aiPlayer); | |
if(winning(board, aiPlayer)){ | |
setTimeout(function(){ | |
alert('You lose'); | |
reset() | |
}, 500) | |
} | |
} | |
} | |
} | |
function reset(){ | |
round = 0; | |
board = [0,1,2,3,4,5,6,7,8]; | |
$('td').text(''); | |
} | |
// returns list of the indexes of empty spots on the board | |
function emptySpot(board){ | |
return board.filter(s => s != "O" && s != "X"); | |
} | |
// winning combinations using the board indexies | |
function winning(board, player){ | |
if ( | |
(board[0] == player && board[1] == player && board[2] == player) || | |
(board[3] == player && board[4] == player && board[5] == player) || | |
(board[6] == player && board[7] == player && board[8] == player) || | |
(board[0] == player && board[3] == player && board[6] == player) || | |
(board[1] == player && board[4] == player && board[7] == player) || | |
(board[2] == player && board[5] == player && board[8] == player) || | |
(board[0] == player && board[4] == player && board[8] == player) || | |
(board[2] == player && board[4] == player && board[6] == player) | |
) { | |
return true; | |
}else { | |
return false; | |
} | |
} | |
//defining the minimax function, for the unbeatable ai | |
function minimax(newBoard, player){ | |
//getting the available spot | |
var availSpots = emptySpot(newBoard); | |
//check for terminal states and return appropriate values | |
if(winning(newBoard, huPlayer)){ | |
return {score: -10}; | |
}else if(winning(newBoard, aiPlayer)){ | |
return {score: 10}; | |
}else if(availSpots.length == 0){ | |
return {score: 0}; | |
} | |
// an array to collect all the objects | |
var moves = []; | |
// loop through available spots | |
for (var i = 0; i < availSpots.length; i++){ | |
//create an object for each and store the index of that spot | |
var move = {}; | |
move.index = newBoard[availSpots[i]]; | |
// set the empty spot to the current player | |
newBoard[availSpots[i]] = player; | |
/*collect the score resulted from calling minimax | |
on the opponent of the current player*/ | |
if (player == aiPlayer){ | |
var result = minimax(newBoard, huPlayer); | |
move.score = result.score; | |
} | |
else{ | |
var result = minimax(newBoard, aiPlayer); | |
move.score = result.score; | |
} | |
// reset the spot to empty | |
newBoard[availSpots[i]] = move.index; | |
// push the object to the array | |
moves.push(move); | |
} | |
// if it is the computer's turn loop over the moves and choose the move with the highest score | |
var bestMove; | |
if(player === aiPlayer){ | |
var bestScore = -10000; | |
for(var i = 0; i < moves.length; i++){ | |
if(moves[i].score > bestScore){ | |
bestScore = moves[i].score; | |
bestMove = i; | |
} | |
} | |
}else{ | |
// else loop over the moves and choose the move with the lowest score | |
var bestScore = 10000; | |
for(var i = 0; i < moves.length; i++){ | |
if(moves[i].score < bestScore){ | |
bestScore = moves[i].score; | |
bestMove = i; | |
} | |
} | |
} | |
// return the chosen move (object) from the moves array | |
return moves[bestMove]; | |
} | |
}) |
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.2.1/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
body{ | |
margin: 0; | |
padding: 0; | |
background-color: #f5f4da; | |
} | |
h1{ | |
text-align: center; | |
padding-top: 20px; | |
padding-bottom: 2px; | |
font-family: 'Atomic Age', cursive; | |
} | |
p{ | |
text-align: center; | |
padding-top: 0; | |
padding-bottom: 5px; | |
font-family: 'Pacifico', cursive; | |
} | |
td{ | |
border: 2px solid black; | |
height: 100px; | |
width: 100px; | |
font-family: 'Atomic Age', cursive; | |
font-size: 70px; | |
cursor: pointer; | |
text-align: center; | |
padding-bottom: 12px; | |
} | |
@media (max-width: 500px) { | |
table tr > td{ | |
height: 65px; | |
width: 65px; | |
font-size: 40px; | |
cursor: pointer; | |
text-align: center; | |
padding-bottom: 10px; | |
} | |
div.board{ | |
margin-top: 20px; | |
width: 290px; | |
margin-right: auto; | |
margin-left: auto; | |
border-radius: 10px; | |
} | |
} | |
table{ | |
border-collapse: collapse; | |
margin: 50px auto; | |
} | |
table tr:nth-child(1) td { | |
border-top: 0; | |
} | |
table tr:last-child td { | |
border-bottom: 0; | |
} | |
table tr td:nth-child(1){ | |
border-left: 0; | |
} | |
table tr td:last-child{ | |
border-right: 0; | |
} | |
.board{ | |
margin-top: 20px; | |
width: 400px; | |
margin-right: auto; | |
margin-left: auto; | |
border-radius: 10px; | |
-webkit-box-shadow: 3px 3px 10px -1px rgba(87,86,87,1); | |
-moz-box-shadow: 3px 3px 10px -1px rgba(87,86,87,1); | |
box-shadow: 3px 3px 10px -1px rgba(87,86,87,1); | |
} |
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/twitter-bootstrap/4.0.0-beta.2/css/bootstrap.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment