Noughts and Crosses
by alexb
HTML
<div class="t3">
<table>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<canvas id="overlay" width="304" height="304"></canvas>
<p class="winner-message">
Congratulations <span class="winner"></span>, you won!
</p>
<p class="draw-message">
There are no winners, only losers.
</p>
<button class="reset">
Start a new game ▶
</button>
<audio id="tuturu" src="//www.myinstants.com/media/sounds/tuturu_1.mp3"/>
<audio id="sadtrombone" src="//www.myinstants.com/media/sounds/sadtrombone.swf.mp3"/>
</div>
CSS
body {
font-family: Arial;
text-align: center;
}
table {
border-spacing: 0;
margin: 30px auto 0;
}
td {
border: 0 solid #000;
border-width: 0 2px 2px 0;
height: 100px;
font-size: 100px;
line-height: 100px;
padding: 0;
text-align: center;
width: 100px;
}
td:last-child {
border-right-width: 0;
}
tr:last-child td {
border-bottom-width: 0;
}
td[data-player="x"]::before {
color: #FFC900;
content: "×";
}
td[data-player="o"]::before {
color: #40B8E8;
content: "○";
}
#overlay {
display: none;
margin-top: -304px;
vertical-align: top;
}
.t3[data-winner] #overlay {
display: initial;
}
.t3[data-winner="x"] .winner::before {
content: "crosses";
font-weight: bold;
}
.t3[data-winner="o"] .winner::before {
content: "noughts";
font-weight: bold;
}
.winner-message,
.draw-message {
display: none;
font-size: 24px;
}
.t3[data-winner] .winner-message {
display: block;
}
.t3[data-winner="false"] .winner-message {
display: none;
}
.t3[data-winner="false"] .draw-message {
display: block;
}
.reset {
cursor: pointer;
background: white;
border: 2px solid black;
border-radius: 10px;
font-size: 18px;
font-weight: bold;
padding: 0.5em 1em;
display: none;
}
.t3[data-winner] .reset {
display: initial;
}
.t3[data-winner="x"] .winner-message {
color: #FFC900;
}
.t3[data-winner="o"] .winner-message {
color: #40B8E8;
}
JavaScript
var player; // player whose turn it is, 'x' or 'o'
var winner; // undefined (game in-progress), false (draw) or 'x' or 'o'
var winFrom, winTo; // start/end coordinates for winning sequence
var state; // state of board (matrix of undefined, 'x' and 'o')
$('td').click(function(e) {
var x = $(e.target).index();
var y = $(e.target).parent().index();
makeMove(x, y);
checkWinner();
updateBoard();
if (winner) {
document.getElementById('tuturu').play();
}
else if (winner === false) {
document.getElementById('sadtrombone').play();
}
});
$('.reset').click(function() {
reset();
updateBoard();
});
reset(); // need to do an initial reset on load to set everything up
function reset() {
player = 'x'; // x goes first
winner = undefined;
winFrom = undefined;
winTo = undefined;
state = [
new Array(3),
new Array(3),
new Array(3)
];
}
function makeMove(x, y) {
if (winner === undefined && !state[y][x]) {
state[y][x] = player;
if (player === 'x') {
player = 'o'
}
else {
player = 'x';
}
}
}
function updateBoard() {
for (var i = 0; i < state.length; i++) {
for (var j = 0; j < state[i].length; j++) {
var cell = state[i][j];
var td = $('tr').eq(i).find('td').eq(j);
if (cell) {
td.attr('data-player', cell);
}
else {
td.removeAttr('data-player');
}
}
}
var overlay = document.getElementById('overlay');
var ctx = overlay.getContext('2d');
ctx.clearRect(0, 0, overlay.width, overlay.height);
if (winner !== undefined) {
$('.t3').attr('data-winner', winner);
if (winner) {
ctx.beginPath();
ctx.moveTo(
50 + winFrom[1] * 102,
50 + winFrom[0] * 102);
...