Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save linuxenko/90e2962c7ffa8a57638f to your computer and use it in GitHub Desktop.

Select an option

Save linuxenko/90e2962c7ffa8a57638f to your computer and use it in GitHub Desktop.
Build a Tic Tac Toe Game [freeCodeCamp [Advanced Projects]] (Challenge)
<div class="tic-container" ng-app="ticApp">
<div class="tic-badge" ng-controller="boardCtrl">
<div class="board">
<div data-pos="0" class="pol" data-selected="o"></div>
<div data-pos="1" class="pol" data-selected="x"></div>
<div data-pos="2" class="pol" data-selected="o"></div>
<div data-pos="3" class="pol" data-selected="x"></div>
<div data-pos="4" class="pol" data-selected="o"></div>
<div data-pos="5" class="pol" data-selected="x"></div>
<div data-pos="6" class="pol" data-selected="o"></div>
<div data-pos="7" class="pol" data-selected="x"></div>
<div data-pos="8" class="pol" data-selected="o"></div>
</div>
<br />
<div class="row playground">
<div class="col-xs-12">
<div class="text-center" ng-if="state=='showstart'">
<span class="btn btn-primary" ng-click="startGame()">Lets GO !</span>
</div>
<div class="text-center" ng-if="state=='complete'">
<span class="btn btn-danger" ng-click="resetGame()">Restart</span>
</div>
<div class="text-center" ng-if="state=='win'">
You Win !!!
<span class="btn btn-info" ng-click="resetGame()">Play Again</span>
</div>
<div class="text-center" ng-if="state=='wait'">
Choose you will play for: <br />
<label class="radio-inline">
<input type="radio" name="player" value="x" ng-click="set('player', 'x');set('state','showstart')" />
<strong>X</strong>
</label>
<label class="radio-inline">
<input type="radio" name="player" value="o" ng-click="set('player', 'o');set('state','showstart')" />
<strong>O</strong>
</label>
</div>
<br />
</div>
</div>
</div>
<div class="copy"><a href="http://www.linuxenko.pro">&copy; Svetlana Linuxenko</a></div>
</div>
var app = angular.module('ticApp', []);
app.directive('pol', function() {
return {
restrict : 'C',
link : function(scope, element, attrs) {
scope.elements.push({
pos : Number.parseInt(attrs.pos, 10),
update : function(p) {
attrs.$set('selected', p);
}
})
element.on('click', function() {
var pos = Number.parseInt(attrs.pos, 10);
if (scope.state === 'start' && scope.checkBoardState(pos)) {
attrs.$set('selected', scope.player);
scope.updateBoard(pos, scope.player);
}
});
scope.$watch('state', function() {
if (scope.state === 'start') {
attrs.$set('selected', '');
}
});
}
}
});
app.controller('boardCtrl', function($scope) {
$scope.state = 'wait';
$scope.player = 'x';
$scope.elements = [];
var board;
var combos = [
[0,1,2],
[3,4,5],
[6,7,8],
[0,4,8],
[2,4,6],
[0,3,6],
[2,5,8],
[1,4,7]
];
function initBoard() {
board = new Array(8);
for (var i = 0; i <= 8; i++) {
board[i] = 'E';
}
}
initBoard();
function aiTurn() {
var player = $scope.player === 'x' ? 'o' : 'x';
var possibleCombos = combos.filter(checkComboPossible);
var mostCombo = possibleCombos[Math.floor(Math.random() * possibleCombos.length)];
var points = comboTurns(mostCombo);
for (var i = 0 ; i < possibleCombos.length; i++) {
var nextPoints = comboTurns(possibleCombos[i]);
if (nextPoints > 0 && nextPoints < points) {
points = nextPoints;
mostCombo = possibleCombos[i];
}
}
points = 0;
for (var i = 0 ; i < possibleCombos.length; i++) {
var nextPoints = comboTurnsMine(possibleCombos[i], player);
if (nextPoints > 0 && nextPoints > points) {
points = nextPoints;
mostCombo = possibleCombos[i];
}
}
var turn = Math.round(Math.random() * 2);
while (board[mostCombo[turn]] !== 'E') {
turn = Math.round(Math.random() * 2);
}
$scope.updateBoard(mostCombo[turn], player);
}
function comboTurnsMine(combo, player) {
return combo.reduce(function(p,c) {
if (board[c] !== 'E' && board[c] === player) {
return p + 1;
} else {
return 0;
}
}, 0);
}
function comboTurns(combo) {
return combo.reduce(function(p,c) { return board[c] !== 'E' ? p - 1 : p; }, 3);
}
function checkComboPossible(combo) {
return combo.some(function(c) { return board[c] === 'E'; });
}
function checkPlayerCombo(combo, player) {
return combo.every(function(c) { return board[c] === player; });
}
function isPlayerWin(player) {
return combos.some(function(c) { return checkPlayerCombo(c, player); });
}
function isBoardComplete() {
return combos.every(function(c) { return !checkComboPossible(c); });
}
$scope.checkBoardState = function(pos) {
return board[pos] === 'E';
}
$scope.updateBoard = function(pos, player) {
board[pos] = player;
if (player !== $scope.player) {
$scope.elements.forEach(function(c) {
if (c.pos === pos) {
c.update(player);
}
});
}
if (isPlayerWin($scope.player) === true) {
$scope.state = 'win';
$scope.$apply();
} else {
if (isPlayerWin(player) === true || isBoardComplete() === true) {
$scope.state = 'complete';
$scope.$apply();
return;
}
if (player === $scope.player) {
aiTurn();
}
}
}
$scope.startGame = function() {
initBoard();
$scope.state = 'start';
setTimeout(function() {
if ($scope.player === 'o') {
aiTurn();
}
}, 50);
}
$scope.resetGame = function() {
initBoard();
$scope.state = 'wait';
}
$scope.set = function(key, val) {
$scope[key] = val;
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.14/angular.min.js"></script>
@import url(https://fonts.googleapis.com/css?family=Roboto);
body,html,.tic-container {
width: 100%;
height: 100%;
font-family: 'Roboto', sans-serif;
}
.tic-badge {
margin: auto;
}
.tic-container {
display: flex;
flex-direction: column;
.copy {
display: flex;
align-items: flex-end;
justify-content: center;
padding: 10px 0px;
}
}
@borderColor : #924da3;
@import url(https://fonts.googleapis.com/css?family=Coming+Soon);
.playground {
font-family: 'Coming Soon', cursive;
font-size: 18px;
}
.board {
width: 340px;
height: 340px;
.pol {
display: flex;
width: 110px;
height: 110px;
float: left;
margin: 1px;
font-size: 64px;
cursor: default;
&:after {
content: attr(data-selected);
display: flex;
margin: auto;
text-transform: uppercase;
font-family: 'Coming Soon', cursive;
}
&:hover {
background: #fcffff;
}
&[data-selected="x"] {
color: #7a4da3;
}
&[data-selected="o"] {
color: #5f4da3;
}
&:nth-child(2),
&:nth-child(5),
&:nth-child(8) {
border-left: 1px solid @borderColor;
border-right: 1px solid @borderColor;
}
&:nth-child(4),
&:nth-child(5),
&:nth-child(6) {
border-top: 1px solid @borderColor;
border-bottom: 1px solid @borderColor;
}
}
}
<link href="https://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