Skip to content

Commit acca4a3

Browse files
author
Bharat Saraswat
committed
created javascript app
1 parent a6463e4 commit acca4a3

File tree

4 files changed

+347
-0
lines changed

4 files changed

+347
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
(function(context) {
2+
3+
var WINNERS = [
4+
[1,1,1,0,0,0,0,0,0],
5+
[0,0,0,1,1,1,0,0,0],
6+
[0,0,0,0,0,0,1,1,1],
7+
[1,0,0,1,0,0,1,0,0],
8+
[0,1,0,0,1,0,0,1,0],
9+
[0,0,1,0,0,1,0,0,1],
10+
[1,0,0,0,1,0,0,0,1],
11+
[0,0,1,0,1,0,1,0,0]
12+
];
13+
14+
var callbacks = {
15+
onTurnChanged: null,
16+
onFinished: null
17+
}
18+
19+
Board.prototype.PLAYER_1=1;
20+
Board.prototype.PLAYER_2=-1;
21+
22+
Board.prototype.STATE_NOT_STARTED=0;
23+
Board.prototype.STATE_PLAYING=1;
24+
Board.prototype.STATE_FINISHED=2;
25+
26+
/**
27+
* Object prototype that encapsulates all the gaming logic.
28+
**/
29+
function Board() {
30+
this.state = this.STATE_NOT_STARTED;
31+
}
32+
33+
// Initialize required variables and notify callbacks that the
34+
// game has started.
35+
Board.prototype.init = function() {
36+
this.board = [0,0,0,0,0,0,0,0,0];
37+
this.winner = 0;
38+
this.winningCells = null;
39+
this.nextPlayer = this.PLAYER_1;
40+
this.state = this.STATE_PLAYING;
41+
callbacks.onTurnChanged && callbacks.onTurnChanged(this);
42+
}
43+
44+
45+
Board.prototype.setCallback = function(type, callback) {
46+
callbacks[type] = callback;
47+
}
48+
49+
// If allowed, execute a movement to the cell specified parameter
50+
// as the current player. Parameter i is the cell number on the board:
51+
// 0 1 2
52+
// 3 4 5
53+
// 6 7 8
54+
// Invalid board state or trying to play on a cell that is not empty
55+
// will be silently ignored. 0 <= i <= board.length is assumed to be true.
56+
Board.prototype.play = function(i) {
57+
if (this.state != this.STATE_PLAYING || this.board[i] != 0) {
58+
return false;
59+
}
60+
this.board[i] = this.nextPlayer;
61+
62+
if (!checkWinners.apply(this) && checkAvailablePlays.apply(this)) {
63+
this.nextPlayer = this.nextPlayer * -1;
64+
callbacks.onTurnChanged && callbacks.onTurnChanged(this);
65+
} else {
66+
callbacks.onFinished && callbacks.onFinished(this);
67+
}
68+
return true;
69+
}
70+
71+
// Return 0 for an empty cell and Board.PLAYER_1 or Board.PLAYER_2 for
72+
// a non-empty cell.
73+
Board.prototype.getCellState = function(cell) {
74+
return this.board[cell];
75+
}
76+
77+
// Return one of the Board.STATE_* constants according to the current
78+
// state of the game.
79+
Board.prototype.getBoardState = function() {
80+
return this.state;
81+
}
82+
83+
// Return 0, Board.PLAYER_1 or Board.PLAYER_2 depending on who is the
84+
// current turn's.
85+
Board.prototype.getNextPlayer = function() {
86+
return this.state == this.STATE_PLAYING ? this.nextPlayer : 0;
87+
}
88+
89+
// Return 0, Board.PLAYER_1 or Board.PLAYER_2 depending on who won the
90+
// game.
91+
Board.prototype.getWinner = function() {
92+
return this.state == this.STATE_FINISHED ? this.winner : 0;
93+
}
94+
95+
// Return a board mask with 1's representing the cells that have the
96+
// winning move and 0's on all the other cells.
97+
Board.prototype.getWinningCells = function() {
98+
return this.state == this.STATE_FINISHED ? this.winningCells : null;
99+
}
100+
101+
// ---- private methods:
102+
103+
// Return true if there is at least one empty cell in the board. Note
104+
// that this method has a side effect of setting the game's state to a
105+
// draw if there is no remaining empty cell.
106+
function checkAvailablePlays() {
107+
for (var c=0; c < this.board.length; c++) {
108+
if (!this.board[c]) {
109+
return true;
110+
}
111+
}
112+
this.state = this.STATE_FINISHED;
113+
this.winner = 0;
114+
this.nextPlayer = 0;
115+
return false;
116+
}
117+
118+
// Return true if the board has a winning combination. Note
119+
// that this method has a side effect of setting the game's state to
120+
// finished if a winning combination is found.
121+
function checkWinners() {
122+
// check possible solutions:
123+
for (var s=0; s < WINNERS.length; s++) {
124+
var maybeWinner = 0;
125+
for (var c=0; c < this.board.length; c++) {
126+
if (WINNERS[s][c] == 1) {
127+
if ( !this.board[c] || maybeWinner && this.board[c] != maybeWinner) {
128+
maybeWinner = 0;
129+
break;
130+
}
131+
if ( !maybeWinner || maybeWinner && this.board[c] == maybeWinner) {
132+
maybeWinner = this.board[c];
133+
}
134+
}
135+
}
136+
137+
if (maybeWinner) {
138+
this.state = this.STATE_FINISHED;
139+
this.winner = maybeWinner;
140+
this.nextPlayer = 0;
141+
this.winningCells = WINNERS[s];
142+
return true;
143+
}
144+
}
145+
return false;
146+
}
147+
148+
window.Board = Board;
149+
150+
})(window);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content=
6+
"width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
7+
<title>TicTacToe</title>
8+
<link href="styles.css" rel="stylesheet">
9+
<script src="board.js"></script>
10+
<script src="main.js"></script>
11+
</head>
12+
<body>
13+
<div id="game">
14+
<div class="board">
15+
<div class="row">
16+
<div class="cell"></div>
17+
<div class="cell"></div>
18+
<div class="cell"></div>
19+
</div>
20+
<div class="row">
21+
<div class="cell"></div>
22+
<div class="cell"></div>
23+
<div class="cell"></div>
24+
</div>
25+
<div class="row">
26+
<div class="cell"></div>
27+
<div class="cell"></div>
28+
<div class="cell"></div>
29+
</div>
30+
</div>
31+
<!-- insert point A -->
32+
<div class="controls">
33+
<div class="message"></div>
34+
<button class="restart">Restart</button>
35+
</div>
36+
</div>
37+
</body>
38+
</html>

tic_tac_toe/javascript_app/main.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
document.addEventListener('DOMContentLoaded', function() {
2+
var gameEl = document.getElementById('game');
3+
var cells = game.querySelectorAll('.board .cell');
4+
5+
window.board = new Board();
6+
7+
window.board.setCallback('onTurnChanged', redrawBoard);
8+
// insert point C
9+
window.board.setCallback('onFinished', handleFinished);
10+
window.board.init()
11+
12+
// set click listeners
13+
Array.prototype.forEach.call(cells, function(e, i) {
14+
e.setAttribute("board_cell", i );
15+
e.addEventListener('click', function(e) {
16+
window.board.play(i);
17+
});
18+
});
19+
20+
function getPlayerLabel(board, player) {
21+
switch (player) {
22+
case board.PLAYER_1: return '1';
23+
case board.PLAYER_2: return '2';
24+
default: return '';
25+
}
26+
}
27+
28+
function redrawBoard(board) {
29+
// redraw cell states:
30+
Array.prototype.forEach.call(cells, function(e, i) {
31+
var state = board.getCellState(i);
32+
if (state) {
33+
e.classList.add(state == board.PLAYER_1 ? 'tic' : 'tac');
34+
}
35+
});
36+
// insert point D
37+
drawStatusMessage(board);
38+
}
39+
40+
// insert point E
41+
function drawStatusMessage(board) {
42+
// redraw "next player" status message:
43+
var message = '';
44+
switch (board.getBoardState()) {
45+
case board.STATE_PLAYING:
46+
message = 'Player '+getPlayerLabel(board, board.getNextPlayer());
47+
break;
48+
case board.STATE_FINISHED:
49+
if (board.getWinner()) {
50+
message = 'Winner: player '+getPlayerLabel(board, board.getWinner());
51+
} else {
52+
message = 'Draw!';
53+
}
54+
break;
55+
}
56+
game.querySelector('.controls .message').innerText = message;
57+
}
58+
59+
game.querySelector('.controls .restart').addEventListener('click', function() {
60+
game.classList.remove('finished');
61+
Array.prototype.forEach.call(cells, function(e, i) {
62+
e.classList.remove('solution');
63+
e.classList.remove('tic');
64+
e.classList.remove('tac');
65+
});
66+
window.board.init();
67+
drawStatusMessage(board);
68+
});
69+
70+
71+
function handleFinished(board) {
72+
redrawBoard(board);
73+
game.classList.add('finished');
74+
if (board.getWinningCells()) {
75+
for (var c=0; c < board.getWinningCells().length ; c++) {
76+
if (board.getWinningCells()[c]) {
77+
cells.item(c).classList.add('solution');
78+
} else {
79+
cells.item(c).classList.remove('solution');
80+
}
81+
}
82+
}
83+
}
84+
});
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
body, html{
2+
height:100%;
3+
}
4+
body{
5+
font-family:"Open-Sans",sans-serif;
6+
font-size: 14px;
7+
margin:0;
8+
}
9+
#game{
10+
background-color:#E7E7E7;
11+
display:flex;
12+
flex-direction:column;
13+
height:100%;
14+
}
15+
.board{
16+
background-color:white;
17+
display:flex;
18+
flex-direction:column;
19+
height:calc(100% - 30px);
20+
width:calc(100% - 30px);
21+
margin:15px;
22+
box-shadow:0 0 10px #666;
23+
}
24+
.row{
25+
flex:1 1;
26+
display:flex;
27+
}
28+
.cell{
29+
position:relative;
30+
display:flex;
31+
flex:1 1;
32+
border: 1px solid #ddd;
33+
cursor:pointer;
34+
}
35+
.cell.tic, .cell.tac {
36+
overflow: hidden;
37+
}
38+
.cell.tic:before {
39+
content: "x";
40+
color: rgb(42,110,255);
41+
}
42+
.cell.tac:before {
43+
content: "o";
44+
color: rgb(255,42,42);
45+
}
46+
/** insert point B **/
47+
.cell:before {
48+
font: bolder 60px sans-serif;
49+
position: absolute;
50+
top: 50%;
51+
width: 100%;
52+
margin-top: -40px;
53+
text-align: center;
54+
}
55+
.controls {
56+
align-self: center;
57+
display: flex;
58+
align-items: center;
59+
width: calc( 100% - 30px );
60+
margin: 15px;
61+
}
62+
.controls .message {
63+
flex: 1 1;
64+
text-align: center;
65+
}
66+
.cell:hover {
67+
background-color: rgba(176, 223, 255, 0.42);
68+
}
69+
.cell.tic:hover, .cell.tac:hover, #game.finished .cell:hover {
70+
background-color: inherit;
71+
cursor: inherit;
72+
}
73+
.cell.solution, #game.finished .cell.solution:hover {
74+
background-color: rgb(248, 255, 173);
75+
}

0 commit comments

Comments
 (0)