Tic-Tac-Toe
How do I make an tic-tac-toe?
Tic-Tac-Toe game built for freeCodeCamp Zipline. Uses OOP for game logic, Underscore.js for utility, jQuery, and Bootstrap.. What is a tic-tac-toe? How do you make a tic-tac-toe? This script and codes were developed by Zac Clemans on 14 January 2023, Saturday.
Tic-Tac-Toe - Script Codes HTML Codes
<!DOCTYPE html>
<html >
<head> <meta charset="UTF-8"> <title>Tic-Tac-Toe</title> <link href='https://fonts.googleapis.com/css?family=Play' rel='stylesheet' type='text/css'>
<meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css"> <link rel='stylesheet prefetch' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css'>
<link rel='stylesheet prefetch' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css'> <link rel="stylesheet" href="css/style.css">
</head>
<body> <h1 class="title">Tic-Tac-Toe</h1>
<div class="board-container"> <div class="board-row"> <div id="cell-0" class="column"></div> <div id="cell-1" class="column"></div> <div id="cell-2" class="column"></div> </div> <div class="board-row"> <div id="cell-3" class="column"></div> <div id="cell-4" class="column"></div> <div id="cell-5" class="column"></div> </div> <div class="board-row"> <div id="cell-6" class="column"></div> <div id="cell-7" class="column"></div> <div id="cell-8" class="column"></div> </div>
</div>
<div class="modal fade" id="player-info-modal" tab-index="-1" role="dialog"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h3>Welcome to Tic-Tac-Toe!</h3> </div> <div class="modal-body"> <form class="player-info" name="player-info" role="form"> <div class="form-group"> <label class="control-label" for="token-radio">Choose a token:</label> <label class="radio-inline"><input id="x-radio" class="token-select" type="radio" value="x"><i id="x-token" class="fa fa-times icon-select"></i></label> <label class="radio-inline"><input id="o-radio" class="token-select" type="radio" value="o"><i id="o-token" class="fa fa-circle-o icon-select"></i></label> </div> </form> </div> <div class="modal-footer"> <input class="btn btn-success" id="submit-player-info" type="submit" value="OK" > </div> </div> </div>
</div>
<div class="modal fade" id="repeat-game-modal" tab-index="-1" role="dialog"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-body"> <h3>Play Game Again?</h3> </div> <div class="modal-footer"> <input class="btn btn-success" id="submit-again-confirm" type="submit" value="Yes"> <input class="btn btn-danger" id="submit-again-deny" type="submit" value="No"> </div> </div> </div>
</div> <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js'></script>
<script src='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js'></script> <script src="js/index.js"></script>
</body>
</html>
Tic-Tac-Toe - Script Codes CSS Codes
body { font-family: "Play";
}
h3 { font-weight: bold;
}
.title { text-align: center; margin-top: 50px;
}
.board-container { width: 420px; margin: 100px auto;
}
.board-row { display: flex; justify-content: space-between; align-items: center; height: 130px;
}
.column { display: flex; align-items: center; justify-content: center; width: 140px; height: 130px;
}
#cell-0, #cell-1, #cell-2, #cell-3, #cell-4, #cell-5 { border-bottom: 2px solid black;
}
#cell-0, #cell-1, #cell-3, #cell-4, #cell-6, #cell-7 { border-right: 2px solid black;
}
.icon, .icon-select { font-size: 90px;
}
.icon-select { text-align: center; width: 100px;
}
.control-label { font-size: 24px;
}
input[type="radio"] { display: none;
}
div#player-info-modal, div#repeat-game-modal { margin-top: 100px;
}
Tic-Tac-Toe - Script Codes JS Codes
$("div#player-info-modal").modal();
$("input#submit-player-info").click(function(){ console.log(humanToken); var humanToken = $("input[type=radio]:checked").val(); $("div#player-info-modal").modal('hide'); var board = new Board(); var players = []; players.push(new Player(humanToken, "human")); if (humanToken == 'x'){ players.push(new Player("o", "computer")); } else{ players.push(new Player("x", "computer")); } var game = new Game(board, players); game.playGame();
});
function Game(board, players){ this.board = board; this.players = players; this.humanPlayer = players[0]; this.computerPlayer = players[1];
}
Game.prototype = { constructor: Game, playGame: function(){ console.log("game started") if (this.humanPlayer.getToken() == 'x'){ this.playerMove(); } else{ this.computerMove(); } }, isGameOver: function(){ return (this.board.isWinner() || this.board.isBoardFull()); }, playerMove: function(){ console.log("player move called"); // This refers to the selector in jQuery functions. // If aliased to 'self' beforehand, 'self' can be used to reference this object. var self = this; // Selector for all valid moves left in the game var positions = "#" + this.board.getValidMoves().join(",#") $(positions).on('click', function(){ var move = $(this).attr('id'); // Once a cell has been selected, turn it off $(positions).off('click'); self.board.setPlayerMove(move, self.humanPlayer.getToken()); if (!self.isGameOver()){ self.computerMove(); } else{ $("div#repeat-game-modal").modal(); self.playAgain(); } }); }, computerMove: function(){ var move = this.computerPlayer.getComputerMove(this.board); this.board.setPlayerMove(move, this.computerPlayer.getToken()); // Once a cell has been selected, turn it off $("#" + move).off('click'); if (!this.isGameOver()){ this.playerMove(); } else{ $("div#repeat-game-modal").modal(); this.playAgain(); } }, playAgain: function(){ var self = this; $("input#submit-again-confirm").click(function(){ $("div#repeat-game-modal").modal('hide'); $(".column").empty(); $("div#player-info-modal").modal(); }); $("input#submit-again-deny").click(function(){ $("div#repeat-game-modal").modal('hide'); }); }
}
function Board(){ this.POSITIONS = [ "#cell-0", "#cell-1", "#cell-2", "#cell-3", "#cell-4", "#cell-5", "#cell-6", "#cell-7", "#cell-8" ]; this.currentBoard = [ "", "", "", "", "", "", "", "", "" ]; this.validMoves = [ "cell-0", "cell-1", "cell-2", "cell-3", "cell-4", "cell-5", "cell-6", "cell-7", "cell-8" ]; this.WINNING_MOVES = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ];
}
Board.prototype = { constructor: Board, getPositions: function(){ return this.POSITIONS; }, getCurrentBoard: function(){ return this.currentBoard; }, getValidMoves: function(){ return this.validMoves; }, getWinningMoves: function(){ return this.WINNING_MOVES; }, isValidMove: function(move){ if (this.validMoves.indexOf(move) != -1){ return true; } else{ return false; } }, setPlayerMove: function(move, token){ this.currentBoard[this.POSITIONS.indexOf("#" + move)] = token; if (token == "x"){ $("#" + move).append("<i class='icon fa fa-times'></i>") } else { $("#" + move).append("<i class='icon fa fa-circle-o'></i>") } // Remove the move just made from the validMoves list. if (this.validMoves.indexOf(move) != -1){ this.validMoves = this.validMoves.slice(0, this.validMoves.indexOf(move)).concat( this.validMoves.slice(this.validMoves.indexOf(move) + 1)); } }, isBoardFull: function(){ return this.currentBoard.every(function(cell){ return (cell == "x" || cell == "o"); }); }, isWinner: function(){ var winner = false; var b = this.currentBoard; this.WINNING_MOVES.forEach(function(moveSet){ if (b[moveSet[0]] == b[moveSet[1]] && b[moveSet[1]] == b[moveSet[2]]){ if (b[moveSet[0]] != ""){ var winningPositions = []; for (var i = 0; i < moveSet.length; i++){ winningPositions.push("#cell-" + moveSet[i] + " > i"); } winningPositions = winningPositions.join(","); $(winningPositions).css('color', 'red'); $(winningPositions).fadeIn(200).fadeOut(200).fadeIn(200).fadeOut(200).fadeIn(200).fadeOut(200).fadeIn(200); winner = true; // return true would only end the forEach function, not the isWinner function } } }); return winner; }, resetBoard: function(){ this.currentBoard = [ "", "", "", "", "", "", "", "", "" ]; this.validMoves = [ "cell-0", "cell-1", "cell-2", "cell-3", "cell-4", "cell-5", "cell-6", "cell-7", "cell-8" ]; }
}
function Player(token, playerType){ this.token = token; this.playerType = playerType;
}
Player.prototype = { constructor: Player, getToken: function(){ return this.token; }, getPlayerType: function(){ return this.playerType; }, getComputerMove: function(board){ // Placeholder until AI is implemented var self = this; if (returnWinOrBlockMove(board)){ return returnWinOrBlockMove(board); } else if (returnMakeForkMove(board)){ return returnMakeForkMove(board); } else if (returnBlockForkMove(board)){ return returnBlockForkMove(board); } else if (returnCenterMove(board)){ return returnCenterMove(board); } else if (returnOppositeCornerMove(board)){ return returnOppositeCornerMove(board); } else if (returnEmptyCornerMove(board)){ return returnEmptyCornerMove(board); } else if (returnEmptySideMove(board)){ return returnEmptySideMove(board); } else{ var move = board.getValidMoves()[Math.floor(Math.random()*board.getValidMoves().length)]; return move; } function returnWinOrBlockMove(board){ var winOrBlock = ''; for (var i = 0; i < board.getWinningMoves().length; i++){ var moveSet = board.getWinningMoves()[i]; //board.WINNING_MOVES.forEach(function(moveSet){ var winState = _.countBy(moveSet, function(position){ if (board.getCurrentBoard()[position] == self.token){ return 'compToken'; } else if (board.getCurrentBoard()[position] == ''){ return 'empty'; } else{ return 'humanToken'; } }); if (winState.compToken == 2 && winState.empty == 1){ for (var j = 0; j < moveSet.length; j++){ var position = moveSet[j]; if (board.getCurrentBoard()[position] == ''){ winOrBlock = "cell-" + position; return winOrBlock; } } } else if (winState.humanToken == 2 && winState.empty == 1){ for (var j = 0; j < moveSet.length; j++){ var position = moveSet[j]; if (board.getCurrentBoard()[position] == ''){ winOrBlock = "cell-" + position; } } } } return winOrBlock; } function returnMakeForkMove(board){ var move = ''; var boardCopy = $.extend(true, {}, board); for (var potentialMove = 0; potentialMove < boardCopy.getCurrentBoard().length; potentialMove++){ if (boardCopy.getCurrentBoard()[potentialMove] == ''){ boardCopy.getCurrentBoard()[potentialMove] = self.token; } else{ continue; } if (countWins(boardCopy) >= 2){ move = "cell-" + potentialMove; } boardCopy.getCurrentBoard()[potentialMove] = ''; } return move; } function returnBlockForkMove(board){ var move = ''; var boardCopy = $.extend(true, {}, board); var badMoves = []; for (var potentialMove = 0; potentialMove < boardCopy.getCurrentBoard().length; potentialMove++){ if (boardCopy.getCurrentBoard()[potentialMove] == ''){ if (self.token == 'x'){ boardCopy.getCurrentBoard()[potentialMove] = 'o'; } else{ boardCopy.getCurrentBoard()[potentialMove] = 'x'; } } else{ continue; } if (countLosses(boardCopy) >= 2){ boardCopy.getCurrentBoard()[potentialMove] = self.token; if (returnBlockForkMove(boardCopy)){ badMoves.push(potentialMove); } else{ move = "cell-" + potentialMove; return move; } } boardCopy.currentBoard[potentialMove] = ''; } for (var potentialMove = 0; potentialMove < boardCopy.getCurrentBoard().length; potentialMove++){ if (boardCopy.getCurrentBoard()[potentialMove] == '' && badMoves.indexOf(potentialMove) == -1){ boardCopy.getCurrentBoard()[potentialMove] = self.token; } else{ continue; } if (countWins(boardCopy) >= 1){ move = "cell-" + potentialMove; return move; } boardCopy.getCurrentBoard()[potentialMove] = ''; } return move; } function returnCenterMove(board){ if(board.getCurrentBoard()[4] == ''){ return "cell-4"; } else{ return ''; } } function returnOppositeCornerMove(board){ var corners = [[0,8], [2, 6]]; var move = ''; corners.forEach(function(corner){ if ((board.getCurrentBoard()[corner[0]] != self.token) && (board.getCurrentBoard()[corner[0]] != '')){ move = board.getCurrentBoard()[corner[1]] == '' ? ("cell-" + corner[1]) : ''; } else if ((board.getCurrentBoard()[corner[1]] != self.token) && (board.getCurrentBoard()[corner[1]] != '')){ move = board.getCurrentBoard()[corner[0]] == '' ? ("cell-" + corner[0]) : ''; } }); return move; } function returnEmptyCornerMove(board){ var move = ''; var corners = [0, 2, 6, 8]; var emptyCorners = []; for (var i = 0; i < corners.length; i++){ if (board.getCurrentBoard()[corners[i]] == ''){ emptyCorners.push(corners[i]); } } if (emptyCorners.length > 0){ move = "cell-" + _.sample(emptyCorners); } return move; } function returnEmptySideMove(board){ var move = ''; var sides = [1, 3, 5, 7]; var emptySides = []; for (var i = 0; i < sides.length; i++){ if (board.getCurrentBoard()[sides[i]] == ''){ emptySides.push(sides[i]); } } if (emptySides.length > 0){ move = "cell-" + _.sample(emptySides); } return move; } function countWins(board){ var winsAvailable = 0; for (var i = 0; i < board.getWinningMoves().length; i++){ var moveSet = board.getWinningMoves()[i]; //board.WINNING_MOVES.forEach(function(moveSet){ var winState = _.countBy(moveSet, function(position){ if (board.getCurrentBoard()[position] == self.token){ return 'compToken'; } else if (board.getCurrentBoard()[position] == ''){ return 'empty'; } else{ return 'humanToken'; } }); if (winState.compToken == 2 && winState.empty == 1){ winsAvailable++; } } return winsAvailable; } function countLosses(board){ var lossesAvailable = 0; for (var i = 0; i < board.getWinningMoves().length; i++){ var moveSet = board.getWinningMoves()[i]; var winState = _.countBy(moveSet, function(position){ if (board.getCurrentBoard()[position] == self.token){ return 'compToken'; } else if (board.getCurrentBoard()[position] == ''){ return 'empty'; } else{ return 'humanToken'; } }); if (winState.humanToken == 2 && winState.empty == 1){ lossesAvailable++; } } return lossesAvailable; } }
}
$("input[type=radio]").click(function(){ if ($(this).attr('id') == 'x-radio'){ $("i#o-token").css({'border': 'none'}); $("i#x-token").css({'border': '3px solid red', 'border-radius': '50%'}); $("input#o-radio").prop('checked', false); } else{ $("i#x-token").css({'border': 'none'}); $("i#o-token").css({'border': '3px solid red', 'border-radius': '50%'}); $("input#x-radio").prop('checked', false); }
});
window.onload = main;
Developer | Zac Clemans |
Username | thalpha |
Uploaded | January 14, 2023 |
Rating | 3 |
Size | 5,278 Kb |
Views | 4,048 |
Find the perfect freelance services for your business! Fiverr's mission is to change how the world works together. Fiverr connects businesses with freelancers offering digital services in 500+ categories. Find Developer!
Name | Size |
Personal Portfolio | 3,289 Kb |
Recipe Box | 7,954 Kb |
Local Weather App - Refactor | 5,264 Kb |
FCC Camper Leaderboard Redux Refactor | 9,125 Kb |
Twitch Streamer Status | 3,948 Kb |
FCC Camper Leaderboard | 6,801 Kb |
Hero arrow test | 1,482 Kb |
Random Chuck Norris Joke Generator | 2,586 Kb |
Simon Says | 3,674 Kb |
Camper News | 3,157 Kb |
Jasper is the AI Content Generator that helps you and your team break through creative blocks to create amazing, original content 10X faster. Discover all the ways the Jasper AI Content Platform can help streamline your creative workflows. Start For Free!
Name | Username | Size |
Wikipedia viewer | Chpecson | 2,865 Kb |
Elephants Full screen site | Orrinward | 3,981 Kb |
Pruebas de d3.js | Juanmanuelcarnerero | 2,485 Kb |
Google Chrome Icon using Pure CSS in one DIV | Grssam | 3,627 Kb |
Scoreboard.js basic usage | Tbleckert | 1,733 Kb |
Mobile first social buttons with no iframe | Alistairtweedie | 3,158 Kb |
Opening Reveal Modal On Document Ready | Winghouchan | 1,787 Kb |
Example SVGZ Data URI | Joeyhoer | 2,981 Kb |
Smoke Shader - Frame Buffer | Omarshe7ta | 2,672 Kb |
Loading animation | Hafizfattah | 0 Kb |
Surf anonymously, prevent hackers from acquiring your IP address, send anonymous email, and encrypt your Internet connection. High speed, ultra secure, and easy to use. Instant setup. Hide Your IP Now!