Skip to content

Instantly share code, notes, and snippets.

@Deityhub
Created February 13, 2018 17:23
Show Gist options
  • Save Deityhub/4fac5869b5299f158b264197838b3e14 to your computer and use it in GitHub Desktop.
Save Deityhub/4fac5869b5299f158b264197838b3e14 to your computer and use it in GitHub Desktop.
TicTacToe | Unbeatable AI
<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>&copy 2017 | <a href="https://github.com/deityhub" target="_blank">Ojini Chizoba Jude</a></p>
</footer>
</div>
</body>
</html>
$(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];
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
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);
}
<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