Created
November 23, 2016 06:31
-
-
Save anonymous/15d7f2ecbace23a4b7de37ef8db0cfcd to your computer and use it in GitHub Desktop.
Tic-Tac-Toe // source https://jsbin.com/qototad
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="viewport" content="width=600, initial-scale=1"> | |
<title>Tic-Tac-Toe</title> | |
<style id="jsbin-css"> | |
html { | |
background: #f5f5f5; | |
} | |
/* The wrapper | |
* wraps the app | |
*/ | |
.wrapper { | |
width: 80.3vw; | |
height: 90.3vw; | |
display: block; | |
margin-left: auto; | |
margin-right: auto; | |
margin-top: 10vw; | |
font-family: sans-serif; | |
} | |
/* The wrapper's intro. | |
* Here goes the title | |
* and the controls. | |
*/ | |
.wrapper #intro { | |
text-align: center; | |
} | |
.wrapper #intro .title { | |
font-size: 12vw; | |
font-weight: bold; | |
display: inline-block; | |
} | |
.wrapper #intro .title .x, | |
.wrapper #intro .title .o { | |
opacity: 0; | |
float: left; | |
} | |
.wrapper #intro .title .x { | |
color: #e88; | |
} | |
.wrapper #intro .title .o { | |
color: #8be; | |
} | |
.wrapper #intro .message { | |
opacity: 0; | |
font-size: 5vw; | |
color: #999; | |
margin-top: -4px; | |
margin-bottom: 4px; | |
} | |
.wrapper #intro #restart { | |
background: none; | |
color: #e88; | |
border: none; | |
font-weight: bold; | |
text-transform: uppercase; | |
font-size: 5vw; | |
display: none; | |
cursor: pointer; | |
text-align: center; | |
width: 100%; | |
margin-top: -5px; | |
} | |
.wrapper #intro #restart.visible { | |
display: block; | |
} | |
/* tic-tac-toe blocks. | |
* People play here. | |
*/ | |
.wrapper #tic-tac-toe div { | |
display: block; | |
float: left; | |
width: 25vw; | |
height: 25vw; | |
margin-left: 1.2vw; | |
margin-top: 1.2vw; | |
font-size: 22vw; | |
font-weight: bold; | |
text-align: center; | |
background: white; | |
color: #888; | |
cursor: pointer; | |
opacity: 0; | |
} | |
.wrapper #tic-tac-toe div.x { | |
color: #e88; | |
cursor: default; | |
} | |
.wrapper #tic-tac-toe div.o { | |
color: #8be; | |
cursor: default; | |
} | |
.wrapper #tic-tac-toe div.yellow { | |
background: #ffb; | |
} | |
.wrapper #tic-tac-toe div.showCursor:before { | |
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAMAAADDpiTIAAADAFBMVEX///8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMvIC5hAAAA/3RSTlMAAf3+AvsD/PkHBQT3+AkIERL69Q4L9iPsBvMMChQPSucQ8hntIQ3o8RYYHOtCF9sT5D8sNFEVKBtF6StM4yBP9DswJ10tNS7qHzk+Iirw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); | |
background-size: 12vw 12vw; | |
display: block; | |
width: 12vw; | |
height: 12vw; | |
content: ''; | |
opacity: 0; | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
animation: click 5s 1s infinite; | |
} | |
@keyframes click { | |
0% { | |
opacity: 0; | |
} | |
3% { | |
opacity: 0.5; | |
} | |
15% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
20% { | |
margin-left: 6vw; | |
margin-top: 5.5vw; | |
} | |
25% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
30% { | |
margin-left: 6vw; | |
margin-top: 5.5vw; | |
} | |
35% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
47% { | |
opacity: 0.5; | |
} | |
50% { | |
opacity: 0; | |
} | |
} | |
/* | |
* Winner highlight | |
*/ | |
.wrapper #winner { | |
display: none; | |
float: left; | |
margin-top: -80vw; | |
margin-bottom: -113px; | |
margin-left: -5vw; | |
width: 90vw; | |
height: 90vw; | |
font-size: 22vw; | |
text-align: center; | |
text-transform: uppercase; | |
padding-top: 17vw; | |
background: rgba(245, 245, 245, .8); | |
} | |
/* The author. | |
* Hi mom! | |
*/ | |
.wrapper .author { | |
font-size: 3vw; | |
color: #999; | |
float: right; | |
margin-right: 5px; | |
margin-top: 2vw; | |
margin-bottom: 5vw; | |
} | |
.wrapper .author .hearts { | |
color: #e88; | |
} | |
.wrapper .author a { | |
text-decoration: none; | |
color: #8be; | |
} | |
.wrapper .author a:hover { | |
text-decoration: underline; | |
} | |
/* | |
* Misc - taken from elsewhere | |
* Sources: | |
* .noselect - https://stackoverflow.com/questions/826782/how-to-disable-text-selection-highlighting-using-css | |
*/ | |
.noselect, .wrapper #tic-tac-toe div { | |
-webkit-touch-callout: none; /* iOS Safari */ | |
-webkit-user-select: none; /* Chrome/Safari/Opera */ | |
-khtml-user-select: none; /* Konqueror */ | |
-moz-user-select: none; /* Firefox */ | |
-ms-user-select: none; /* Internet Explorer/Edge */ | |
user-select: none; /* Non-prefixed version, currently | |
not supported by any browser */ | |
} | |
</style> | |
</head> | |
<body> | |
<div class='wrapper'> | |
<div id='intro'> | |
<div class='title'> | |
<div class='x'>Tic-</div> | |
<div class='o'>Tac-</div> | |
<div class='x'>Toe!</div> | |
</div> | |
<div class='message'>Click in the board</div> | |
<button id='restart'>Restart</button> | |
</div> | |
<div id='tic-tac-toe' class='.noselect'> | |
<div class=''></div> | |
<div class=''></div> | |
<div class=''></div> | |
<div class=''></div> | |
<div class=''></div> | |
<div class=''></div> | |
<div class=''></div> | |
<div class=''></div> | |
<div class=''></div> | |
</div> | |
<div id='winner'> | |
<!-- | |
PLAYER | |
<b>X</b> | |
WINS! | |
--> | |
</div> | |
<div class='author'> | |
Made with | |
<span class='hearts'>♥</span> | |
by | |
<a href='http://twitter.com/sadasant' target='_blank'> | |
@sadasant | |
</a> | |
</div> | |
</div> | |
<script id="jsbin-javascript"> | |
window.onload = function() { | |
// Makes arrays | |
function makeArray(obj) { | |
return Array.prototype.slice.call(obj) | |
} | |
// Vanilla jQuery for the lazy | |
var $ = function() { | |
var args = makeArray(arguments); | |
var r = document.querySelectorAll.apply(document, args); | |
return r.length === 1 ? r[0] : r; | |
} | |
// Vanilla class functions | |
function addClass(el, className) { | |
removeClass(el, className) | |
el.className += ' ' + className | |
} | |
function removeClass(el, className) { | |
el.className = el.className.replace(className, '') | |
} | |
// Our main variables | |
var title = $('.wrapper #intro .title'); | |
var message = $('.wrapper #intro .message'); | |
var tictactoe = $('.wrapper #tic-tac-toe'); | |
var blocks = $('.wrapper #tic-tac-toe div'); | |
var restart = $('#restart'); | |
var winner = $('#winner'); | |
// Animated intro! | |
intro() | |
function intro() { | |
function show(el) { | |
el.style.opacity = 1; | |
} | |
function hide(el) { | |
el.style.opacity = 0; | |
} | |
var delayer = new Delayer(); | |
var titleParts = makeArray(title.children); | |
var blocksArray = makeArray(blocks); | |
// Flushing | |
message.style.display = 'block' | |
winner.style.display = 'none' | |
hide(message) | |
removeClass(restart, 'visible') | |
titleParts.map(hide) | |
blocksArray.map(function(el) { | |
el.className = ''; | |
el.innerText = ''; | |
hide(el) | |
}) | |
delayer.add(titleParts, show, 500) | |
delayer.add(blocksArray, show, 100) | |
delayer.add([blocks[0]], function(first) { | |
addClass(first, 'showCursor') | |
blocksArray.map(function(el) { | |
el.onclick = function() { | |
removeClass(first, 'showCursor') | |
message.style.display = 'none' | |
first.onclick = undefined | |
startGame(el) | |
} | |
}) | |
}, 100) | |
delayer.add([message], show, 500) | |
delayer.run() | |
} | |
// Main game logic | |
var startsX = true; | |
function startGame(el) { | |
var player1 = { | |
str: startsX ? 'x' : 'o' | |
} | |
var player2 = { | |
str: !startsX ? 'x' : 'o', | |
} | |
var goesP1 = true; | |
var turns = 0; | |
addClass(restart, 'visible') | |
restart.onclick = function() { | |
intro() | |
} | |
var blocksArray = makeArray(blocks); | |
blocksArray.map(function(el) { | |
el.onclick = function() { | |
if (el.innerText) return | |
var player = goesP1 ? player1 : player2; | |
el.innerText = player.str.toUpperCase(); | |
addClass(el, player.str); | |
goesP1 = !goesP1; | |
turns++ | |
if (turns === 9) { | |
setTimeout(tie, 125); | |
} else | |
if (turns >= 5) { | |
setTimeout(function() { | |
checkWinner(player, el); | |
}, 125) | |
} | |
} | |
}) | |
function checkWinner(player, el) { | |
var B = blocksArray; | |
var i = B.indexOf(el); | |
var str = player.str.toUpperCase() | |
function eqAt(a, b, c) { | |
return B[a].innerText === B[b].innerText && B[a].innerText === B[c].innerText; | |
} | |
var col = i % 3; | |
var row = i - col; | |
if (eqAt(row, row + 1, row + 2)) return won(player, row, row + 1, row + 2); | |
if (eqAt(col, col + 3, col + 6)) return won(player, col, col + 3, col + 6); | |
if (eqAt(0, 4, 8)) return won(player, 0, 4, 8) | |
if (eqAt(2, 4, 6)) return won(player, 2, 4, 6) | |
} | |
function won(player, a, b, c) { | |
addClass(blocksArray[a], 'yellow') | |
addClass(blocksArray[b], 'yellow') | |
addClass(blocksArray[c], 'yellow') | |
winner.style.display = 'block'; | |
winner.style.color = player.str === 'x' ? '#e88' : '#8be'; | |
winner.innerHTML = 'player <b>' + player.str + '</b> wins!' | |
blocksArray.map(function(el) { | |
el.onclick = undefined | |
}) | |
} | |
function tie() { | |
winner.style.display = 'block'; | |
winner.style.color = '#999'; | |
winner.innerHTML = 'IT WAS<br>A TIE' | |
} | |
el.onclick() | |
} | |
// Delayer object | |
// adds animation *frames* into | |
// a strack, then replays them | |
function Delayer() { | |
var stack = [] | |
this.add = function(arr, func, time) { | |
if (!(arr && arr.length)) { | |
throw 'Delayer.add error: invalid first argument, must be an array' | |
} | |
arr.map(function(el) { | |
stack.push({ | |
el: el, | |
func: func, | |
timeout: time | |
}) | |
}) | |
} | |
var that = this | |
this.run = function() { | |
if (!stack.length) return | |
var current = stack.shift() | |
setTimeout(function() { | |
current.func(current.el) | |
that.run() | |
}, current.timeout) | |
} | |
} | |
} | |
</script> | |
<script id="jsbin-source-css" type="text/css">html { | |
background: #f5f5f5; | |
} | |
/* The wrapper | |
* wraps the app | |
*/ | |
.wrapper { | |
width: 80.3vw; | |
height: 90.3vw; | |
display: block; | |
margin-left: auto; | |
margin-right: auto; | |
margin-top: 10vw; | |
font-family: sans-serif; | |
} | |
/* The wrapper's intro. | |
* Here goes the title | |
* and the controls. | |
*/ | |
.wrapper #intro { | |
text-align: center; | |
} | |
.wrapper #intro .title { | |
font-size: 12vw; | |
font-weight: bold; | |
display: inline-block; | |
} | |
.wrapper #intro .title .x, | |
.wrapper #intro .title .o { | |
opacity: 0; | |
float: left; | |
} | |
.wrapper #intro .title .x { | |
color: #e88; | |
} | |
.wrapper #intro .title .o { | |
color: #8be; | |
} | |
.wrapper #intro .message { | |
opacity: 0; | |
font-size: 5vw; | |
color: #999; | |
margin-top: -4px; | |
margin-bottom: 4px; | |
} | |
.wrapper #intro #restart { | |
background: none; | |
color: #e88; | |
border: none; | |
font-weight: bold; | |
text-transform: uppercase; | |
font-size: 5vw; | |
display: none; | |
cursor: pointer; | |
text-align: center; | |
width: 100%; | |
margin-top: -5px; | |
} | |
.wrapper #intro #restart.visible { | |
display: block; | |
} | |
/* tic-tac-toe blocks. | |
* People play here. | |
*/ | |
.wrapper #tic-tac-toe div { | |
display: block; | |
float: left; | |
width: 25vw; | |
height: 25vw; | |
margin-left: 1.2vw; | |
margin-top: 1.2vw; | |
font-size: 22vw; | |
font-weight: bold; | |
text-align: center; | |
background: white; | |
color: #888; | |
cursor: pointer; | |
opacity: 0; | |
} | |
.wrapper #tic-tac-toe div.x { | |
color: #e88; | |
cursor: default; | |
} | |
.wrapper #tic-tac-toe div.o { | |
color: #8be; | |
cursor: default; | |
} | |
.wrapper #tic-tac-toe div.yellow { | |
background: #ffb; | |
} | |
.wrapper #tic-tac-toe div.showCursor:before { | |
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAMAAADDpiTIAAADAFBMVEX///8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMvIC5hAAAA/3RSTlMAAf3+AvsD/PkHBQT3+AkIERL69Q4L9iPsBvMMChQPSucQ8hntIQ3o8RYYHOtCF9sT5D8sNFEVKBtF6StM4yBP9DswJ10tNS7qHzk+Iirw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); | |
background-size: 12vw 12vw; | |
display: block; | |
width: 12vw; | |
height: 12vw; | |
content: ''; | |
opacity: 0; | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
animation: click 5s 1s infinite; | |
} | |
@keyframes click { | |
0% { | |
opacity: 0; | |
} | |
3% { | |
opacity: 0.5; | |
} | |
15% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
20% { | |
margin-left: 6vw; | |
margin-top: 5.5vw; | |
} | |
25% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
30% { | |
margin-left: 6vw; | |
margin-top: 5.5vw; | |
} | |
35% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
47% { | |
opacity: 0.5; | |
} | |
50% { | |
opacity: 0; | |
} | |
} | |
/* | |
* Winner highlight | |
*/ | |
.wrapper #winner { | |
display: none; | |
float: left; | |
margin-top: -80vw; | |
margin-bottom: -113px; | |
margin-left: -5vw; | |
width: 90vw; | |
height: 90vw; | |
font-size: 22vw; | |
text-align: center; | |
text-transform: uppercase; | |
padding-top: 17vw; | |
background: rgba(245, 245, 245, .8); | |
} | |
/* The author. | |
* Hi mom! | |
*/ | |
.wrapper .author { | |
font-size: 3vw; | |
color: #999; | |
float: right; | |
margin-right: 5px; | |
margin-top: 2vw; | |
margin-bottom: 5vw; | |
} | |
.wrapper .author .hearts { | |
color: #e88; | |
} | |
.wrapper .author a { | |
text-decoration: none; | |
color: #8be; | |
} | |
.wrapper .author a:hover { | |
text-decoration: underline; | |
} | |
/* | |
* Misc - taken from elsewhere | |
* Sources: | |
* .noselect - https://stackoverflow.com/questions/826782/how-to-disable-text-selection-highlighting-using-css | |
*/ | |
.noselect, .wrapper #tic-tac-toe div { | |
-webkit-touch-callout: none; /* iOS Safari */ | |
-webkit-user-select: none; /* Chrome/Safari/Opera */ | |
-khtml-user-select: none; /* Konqueror */ | |
-moz-user-select: none; /* Firefox */ | |
-ms-user-select: none; /* Internet Explorer/Edge */ | |
user-select: none; /* Non-prefixed version, currently | |
not supported by any browser */ | |
}</script> | |
<script id="jsbin-source-javascript" type="text/javascript">window.onload = function() { | |
// Makes arrays | |
function makeArray(obj) { | |
return Array.prototype.slice.call(obj) | |
} | |
// Vanilla jQuery for the lazy | |
var $ = function() { | |
var args = makeArray(arguments); | |
var r = document.querySelectorAll.apply(document, args); | |
return r.length === 1 ? r[0] : r; | |
} | |
// Vanilla class functions | |
function addClass(el, className) { | |
removeClass(el, className) | |
el.className += ' ' + className | |
} | |
function removeClass(el, className) { | |
el.className = el.className.replace(className, '') | |
} | |
// Our main variables | |
var title = $('.wrapper #intro .title'); | |
var message = $('.wrapper #intro .message'); | |
var tictactoe = $('.wrapper #tic-tac-toe'); | |
var blocks = $('.wrapper #tic-tac-toe div'); | |
var restart = $('#restart'); | |
var winner = $('#winner'); | |
// Animated intro! | |
intro() | |
function intro() { | |
function show(el) { | |
el.style.opacity = 1; | |
} | |
function hide(el) { | |
el.style.opacity = 0; | |
} | |
var delayer = new Delayer(); | |
var titleParts = makeArray(title.children); | |
var blocksArray = makeArray(blocks); | |
// Flushing | |
message.style.display = 'block' | |
winner.style.display = 'none' | |
hide(message) | |
removeClass(restart, 'visible') | |
titleParts.map(hide) | |
blocksArray.map(function(el) { | |
el.className = ''; | |
el.innerText = ''; | |
hide(el) | |
}) | |
delayer.add(titleParts, show, 500) | |
delayer.add(blocksArray, show, 100) | |
delayer.add([blocks[0]], function(first) { | |
addClass(first, 'showCursor') | |
blocksArray.map(function(el) { | |
el.onclick = function() { | |
removeClass(first, 'showCursor') | |
message.style.display = 'none' | |
first.onclick = undefined | |
startGame(el) | |
} | |
}) | |
}, 100) | |
delayer.add([message], show, 500) | |
delayer.run() | |
} | |
// Main game logic | |
var startsX = true; | |
function startGame(el) { | |
var player1 = { | |
str: startsX ? 'x' : 'o' | |
} | |
var player2 = { | |
str: !startsX ? 'x' : 'o', | |
} | |
var goesP1 = true; | |
var turns = 0; | |
addClass(restart, 'visible') | |
restart.onclick = function() { | |
intro() | |
} | |
var blocksArray = makeArray(blocks); | |
blocksArray.map(function(el) { | |
el.onclick = function() { | |
if (el.innerText) return | |
var player = goesP1 ? player1 : player2; | |
el.innerText = player.str.toUpperCase(); | |
addClass(el, player.str); | |
goesP1 = !goesP1; | |
turns++ | |
if (turns === 9) { | |
setTimeout(tie, 125); | |
} else | |
if (turns >= 5) { | |
setTimeout(function() { | |
checkWinner(player, el); | |
}, 125) | |
} | |
} | |
}) | |
function checkWinner(player, el) { | |
var B = blocksArray; | |
var i = B.indexOf(el); | |
var str = player.str.toUpperCase() | |
function eqAt(a, b, c) { | |
return B[a].innerText === B[b].innerText && B[a].innerText === B[c].innerText; | |
} | |
var col = i % 3; | |
var row = i - col; | |
if (eqAt(row, row + 1, row + 2)) return won(player, row, row + 1, row + 2); | |
if (eqAt(col, col + 3, col + 6)) return won(player, col, col + 3, col + 6); | |
if (eqAt(0, 4, 8)) return won(player, 0, 4, 8) | |
if (eqAt(2, 4, 6)) return won(player, 2, 4, 6) | |
} | |
function won(player, a, b, c) { | |
addClass(blocksArray[a], 'yellow') | |
addClass(blocksArray[b], 'yellow') | |
addClass(blocksArray[c], 'yellow') | |
winner.style.display = 'block'; | |
winner.style.color = player.str === 'x' ? '#e88' : '#8be'; | |
winner.innerHTML = 'player <b>' + player.str + '</b> wins!' | |
blocksArray.map(function(el) { | |
el.onclick = undefined | |
}) | |
} | |
function tie() { | |
winner.style.display = 'block'; | |
winner.style.color = '#999'; | |
winner.innerHTML = 'IT WAS<br>A TIE' | |
} | |
el.onclick() | |
} | |
// Delayer object | |
// adds animation *frames* into | |
// a strack, then replays them | |
function Delayer() { | |
var stack = [] | |
this.add = function(arr, func, time) { | |
if (!(arr && arr.length)) { | |
throw 'Delayer.add error: invalid first argument, must be an array' | |
} | |
arr.map(function(el) { | |
stack.push({ | |
el: el, | |
func: func, | |
timeout: time | |
}) | |
}) | |
} | |
var that = this | |
this.run = function() { | |
if (!stack.length) return | |
var current = stack.shift() | |
setTimeout(function() { | |
current.func(current.el) | |
that.run() | |
}, current.timeout) | |
} | |
} | |
}</script></body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
html { | |
background: #f5f5f5; | |
} | |
/* The wrapper | |
* wraps the app | |
*/ | |
.wrapper { | |
width: 80.3vw; | |
height: 90.3vw; | |
display: block; | |
margin-left: auto; | |
margin-right: auto; | |
margin-top: 10vw; | |
font-family: sans-serif; | |
} | |
/* The wrapper's intro. | |
* Here goes the title | |
* and the controls. | |
*/ | |
.wrapper #intro { | |
text-align: center; | |
} | |
.wrapper #intro .title { | |
font-size: 12vw; | |
font-weight: bold; | |
display: inline-block; | |
} | |
.wrapper #intro .title .x, | |
.wrapper #intro .title .o { | |
opacity: 0; | |
float: left; | |
} | |
.wrapper #intro .title .x { | |
color: #e88; | |
} | |
.wrapper #intro .title .o { | |
color: #8be; | |
} | |
.wrapper #intro .message { | |
opacity: 0; | |
font-size: 5vw; | |
color: #999; | |
margin-top: -4px; | |
margin-bottom: 4px; | |
} | |
.wrapper #intro #restart { | |
background: none; | |
color: #e88; | |
border: none; | |
font-weight: bold; | |
text-transform: uppercase; | |
font-size: 5vw; | |
display: none; | |
cursor: pointer; | |
text-align: center; | |
width: 100%; | |
margin-top: -5px; | |
} | |
.wrapper #intro #restart.visible { | |
display: block; | |
} | |
/* tic-tac-toe blocks. | |
* People play here. | |
*/ | |
.wrapper #tic-tac-toe div { | |
display: block; | |
float: left; | |
width: 25vw; | |
height: 25vw; | |
margin-left: 1.2vw; | |
margin-top: 1.2vw; | |
font-size: 22vw; | |
font-weight: bold; | |
text-align: center; | |
background: white; | |
color: #888; | |
cursor: pointer; | |
opacity: 0; | |
} | |
.wrapper #tic-tac-toe div.x { | |
color: #e88; | |
cursor: default; | |
} | |
.wrapper #tic-tac-toe div.o { | |
color: #8be; | |
cursor: default; | |
} | |
.wrapper #tic-tac-toe div.yellow { | |
background: #ffb; | |
} | |
.wrapper #tic-tac-toe div.showCursor:before { | |
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAMAAADDpiTIAAADAFBMVEX///8zMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMvIC5hAAAA/3RSTlMAAf3+AvsD/PkHBQT3+AkIERL69Q4L9iPsBvMMChQPSucQ8hntIQ3o8RYYHOtCF9sT5D8sNFEVKBtF6StM4yBP9DswJ10tNS7qHzk+IirwLzPl7lUph0fgRms3JEBUJj3vHiU8Gk5cV8AyOHdyHdhSYzFLpnpBhH7M14XedeJ5YEnWbkR0YlbP4VNNuF7ZcVCCbd1zSMnmWdrfxJrVNlrRaFvSQ6Gog7S8vmeiZp6bcF+UrtzBZLbOWIu6imx/02nQs51vjXi3OpCsjJFll6uPk8uVp5LHfIGYsb3UpGHKo82gsHa5u4ayr3tqfcWOloDGn4mIqq2pnMO1yMKZpb8X8e1wAAAaJElEQVR4Xuzd+XcV1LnG8eeck5zMMwmBMEggJCFAZFZmAgqCBIMIyiQooyCgWOaIIlO1UKQCIjjU4Tos5YK2WpeAggtbxeGiAk51uHVste1Sq666Vp+udX+qLq7s8+ac5Oz3vJ+/4fvTfvbaG3Egt9/Fq6/Z9+h9fziyevyYrgEkEDP1T8cz+QPFi4dVIiGYnit281RCx7b8PgzdzKT/GcCfUP0LzQmYGW8k8zQ2zUqCTibvqSI6WL5DZQJmXS0dvV0DbUyz1+gu84/QxUxcyYjsyYEiZn4+I/R+GdQwKxi5sW2hhHmKEsHfQgXzBwqthQLmKMWWwHtmL+W4LwC/mX4FbIj3MuAzU7icDfO/afCYeYcN9VYJvGUWsuG25cFTpkU1o+Bge/jJbGZUPFYGH5m8sYyOutFQwI4A5Yp7wjsmN5NRU9odvjF/ZhRlXg3PmGWMpuQd8IoZzugKPgQNbAWWmwePmHJG3UXwhmnOGHgpCZ4wwxgLizPgB/NXxsShHHjBbGBs/KsjPGBKUhgjx85E/DOjGTO7ZyDumcsZOyvPR7wzMxlD23sgzpnVjKUOtyO+mfmMqZZdENfMf9FJQT5lsmYinpmL6GRB2yLKJM+CAhYARlVQJnQJvGcBbAIu6EyhaxC3zMfOAWBcNwo9CQUsAAysptCuJHjNXoW5FQDQ6W4K3ZcO/1kAyD1Oocez4T8LAIUPUOj7ZohDZgmdVOP/AGmfUuj91vCfBYDwvRRaXgX/WQAIvE6hCZPgKdsCHsN/2EKh8uFQwAKQT4hjr4ACFgAWBSkz+AbEFbOFTrbihy5MpUxWLyhgAeCWAsqk7EUcMSfpZCV+7PJayoTWQAELAFeNoNBSKGABYGg9hb4IIE6Yv9HJTpzC9FspdDgM/1kAqHyGQi+3gP8sALT7nEI3tUI8MAfoZAJOrdWHFLqtDfxnAaDF8xTaUAn/WQAIP0KhTVeiyZmldLIR/6/AKxTaOBX+swCAAxTqWwMFLAA8G6LMtCFoWuYvdFKPn7Q6hTIFF0MBCwALUymTOgVNyZygkwE4jbMHUyZlHRSwADByGmVCm6GABYAx5RQ6iSZjPqOTbji9yzZS6GgA/rMAULWAQh+E4T8LAL2foNCjaWgS5ho6qYOTNt9R6KNC+M8CQKvnKPT3XPjPAkD61xS6sysan7mWTrbDVdIeClWfB/9ZAMALFBrQB3HKAjgDEThBoRFz0cjMkRgEgP0hylRcCgUsAAxLpkzRdWhUZh6dlCMyg7Iok78eClgAuKclZYIvQgELABPHUmgtGo/5E530RcTOOYNCS6CABYDmOym0LwD/WQA463oKvZeBxmFW0ckISLReRqE30+A/CwDNfkOht0rgPwsA2fdTaFseGoFZSyedIZT+GoUOtof/LAAkvUGhx8oQc+ZXdFIMuS8pVDcaClgAuJZCxT2hgAWAp4OUKe2OuGB/B3dAg9yRTJnM/lDAAsCNmZRJ3gEFLABMLqVM8C7EkNlMJ2PRUOcWU2geFLAA0K+OQhdBAQsAZVsp9FISYsT8N51MQxS0f5tCizPgPwsAee9S6FAO/GcBoORBCs3piFgwz9JJLaIj5xMKHTsT/rMAkPEOhXbPQPSZNXRSgWgJvEqhlefDfxYA8DGFtveAAhYAVlGow+2IMnMznZQimh4OUqZlFyhgAWB2PmWyZkIBCwBtiyiTPAvRZPbTSUtE2agKyoQugQIWAC7oTKHPoIAFgHHdKPQkosZcQieDEX0Dqym0Kwn+swDQ6W4KfZMO/1kAyD1OocezERXmz3RSgJgofIBC3zeD/ywApH1Kofd7IwrM03RShBgJ30uh5VXwnwWAwOsUmjAJ/rMAgC0UKh+OhjJ30UkmYmg+hcZeAQUsACwKUmbwDVDAAsCFqZTJ6oUGMQ/RSRZi65YCyqTshQIWAC6vpUxoDRSwAHDVCAothZx5mE5SEXND6yn0RQD+swAw/VYKHQ4jtiyAfDSCymco9HILyJgX4ygAtPucQje1gv8sALT6kEK3tYGE+S2dJKNxtHieQhsq4T8LAOFHKLTpSvjPAkDgFQrVT0XEzCI6SUHjOUChvjVQwALAsyHK1A6BAhYAVqdQpmA8ImPW0UkQjWphKmVSp0ABCwBnD6ZMyjooYAFg5DTKhDYjAuaPdBJCYxtTTqGTUMACwGUTKHQ0AFdmNd2g8VUtoNAHYfjPAkDvJyj0aBr8ZwGgzXcU+qgQTszeeA4A2c9R6Hgu/GcBIP1rCt3ZFf6zAJC0h0LV5+H0zC/oJoCm8gKFBvSBAhYATlBoxFwoYAFgf4gyFZfiNMwwuklCExqWTJmi66CABYBBWZTJXw8FLADc05IywRfxU8wsugmjaU0cS6G1UMACQI8zKLQEcvr9mm4y0NSa76TQvgD8ZwHgrOsp9F4G/GcBoPUyCr2ZhlMzd9BNOuJAszkUeqsE/rMAkH0/hbblwX8WANJfo9DB9jgFs4NuWiA+JL1BocfK4D8LAPiSQnWjoYAFgCMUKu6JHzO/o5s0xI+ng5Qp7Q4FLADckUyZzP5QwALAjZmUSd6BHzCz6SYHcWVyKWWCd0EBCwDnFlNoHv6DWU832Ygz/eoo9BUUsABQtpVCLyXBfxYA2r9NocUZENDpQrpphfiTt41Ch3LgPwsAJQ9SaE5H+M8CQM4nFDp2JgwATKGbQsSljHcotHsG/GcBIPAqhVY2h/8sAGAFhbb3gMHP6aYEcWsVhTrcDgUsADwcpEzLLnBkAXREHJudT5msmUh0CzUEgLZFlEmeBQUsAIyqoEzoEiS2XnTTDPHtgs4U+gwKWAAY141CT0IBCwADqym0KwmJ65d00wZxr9PdFPomHf6zAJB7nEKPZ8N/FgAKH6DQ982QoAbRTTv4IO1TCr3fG/6zABC+l0LLq+A/CwCB1yk0YRIS0Uy6yYUvtlCofDgUsAAwn0JjRyLx3Eg3efDHoiBlBt8ABSwATEmlTFYvKGABYHwBZVL2IsH0p5sz4ZUhtZQJrYECFgBq+lJoKRSwADC0nkJfBJBArqab1vDN9E0UOhyG/ywAVD5DoZdbwH8WANp9TqGbWiFRtKWb3vBQqw8pdFs7+M8CQIvnKbShEv6zABB+hEKbpiMhXEc3lfBT4BUK1U+F/ywA4ACF+tZAP9xCN13hrWdDlKkdAgUsAKxOoUzBeChgAWBhKmVSp0C78XTTCT47ezBlUtZBAQsAI6dRJrQZClgAGF5OoZNQ7WK6aQ/PXTaBQkcD8J8FgKoFFPogDP9ZAOj9BIUeTYNaZ9PNDPivzXcU+qjQAoAC2c9R6HiuBaBB+jcUurMrdLqBbs6CCkl7KFR9ngWgwgsUGtAHGt1DN1XQ4gSFOs+1AFTYH6JMxaUWgArDkilTdB3U6UI3V0KRQVmUyZ9tAajQpSVlgi9aACpM7EChtdDlZ3QzHbr0OINCKywAFZrvpNC+gAWgwVnXU+i9DOgxmW7Ogzqtl1HozTQLQIOOcyj0VknCBTAQCmXfT6FteVCieyIHgPTXKHSwvQWgQdIbFNpaBhVG0U0ZlPqKQnWjLQAVjlCouKcFoMLTQcqUdof/LqWb86HXHcmUyexvAajQP5MyyTssABUml1ImeBc8dzndNIdq5xZT6IgFoMLoOgp9ZQGoULaVQi8lwWND6GYStGv/NoUWZ1gAGuRto9ChNHjr93RzGfQreZBC/whbABrkvEmhby0AFTLeodBT8NRIupmKhBB4lULzLAAdVlAmNNkC0GEVZXZ2hI+uoJuhSBgPBymyxwJQYnY+RdpaAEq0LaJEXUf4ZyLd9EEiGVVBiWstAC3mdqZA50ILQIs+3SiwCt65nW7GIcEMrGbkyrMtADU63c3I3QzfnEs3o5Fwco8zYv+0ABQp/IiRClVZAIqkfcpIrYFnetJNPySi8L2M0BwLQJXA64xMsNIC0GULI9MLfrmAbnogUc1nRJZaANosSmEEXrYA1JmSSncL4Je5dHMOEtj4AjpLybEA9BlSS2fnWAAK1fSlq5HwylV0MxyJbWg9Hf3MAlBp+iadNwNr6GYMElibqaMu3L+LbhZaADq0Gzpq/c1L9nzy7sYsRuLXFoDHcvt0X79mxbeH3q3PolAveGUM3dRAr7xxk2ev+fjw/cfqU9lwPS0AH/ybvTv9rqq84jj+u0NyM5GEBAIBEuZAMGAQQ4AQBpEhGgTEIApqZBJUUBkUnKkiImhFHBBRBEFGiwPKVBGVotYBRetSC3UeqlbFSh2K/fUFrBYhyr77nufecznP5x/Ii/PNWmedfZ/9+Ir7T9nwbNVDP04tz6KjCm0ALn7qZW3vWlu154n32mfRkDTEl76UuQTxypdX9sB9t/xzz/b1gxJo3nwbgBsEO3Z4YPGF376/feadCYyqTTaAmAkW9r5u8bzNS2fPbBJirFyG+NKbMuPgUsF2fTuPmbdx9UdbJU/dvJ42gCg99ROv37h606JWfrpKs9SjNICBcIGg4KnH2HLEmQ7xFAAW0O3W2gBM6k6X81cizpTF1QdO3810t+WwARi1hu42xAZgVkYXutkEH+JNf8r0gTt8QTebC8NsAMXZdK/6BTYA416le12M+HMaZe6FS1SG6FYLfDaAKPiOLpXQG1FgA7iXLvVnxKNSypwJ11hHV5rW0AYQHV3pRi2GIS71osz5cA3fdLpP1hTYAKLlYrrPGkSNDSC5Cd1mG+LV2fF46HkyXeZWRJMNILMH3cR/BtRsAOlQeJ4ukjQYcWwEZZ6DGd2mdUL4KkJ0jfbDoWYDOK09L4XC63SLpU2hZgMYWET2S1atNnKHVqMR54ZRpqe5u5mugcI7dIGEZ4qhZwOYmEOSXA+F2xlzSfu6Qc8GgA1Z3G8kFJYwttLeOAZHg3qUGQmnrfLzgO+gMIcxlL13TD4iYgO4gP+ToPlfSmnP2EiYtrqkEY4alZQ5B86q4kE+gcL1jLJTp419/esbOuQiYjaA4Ms8WFEGwlenNaOgxfT/LHvx6hkTjx+fC6fYAFJ/5i/NgMJGGhKov3DlpB1/2t14YKcUHO1aUuZkOCfjaR5iJhQ6ZdFBgaItK37+xwXXXHvJqFSYZAMoGOvQK8ZSRsrfZcnHu7Z9OGfICc1TER02gOLXnLpopTdVQq0WbXp34/UnDh3QJhGe140yx8EhoxayBqHxUNhOMX+TrR8t3TxvTOe+hUHEjg2g8grWqAoKbSnWIxMuYAMou5M1q58BhfcoNhk1sSooMxxO6FPf2YOVGyjWKhmxZgMY3oy/ahEUEk+n2OOIMRtA1zTHE3uWYtN8OJx1bvSuQrorgb/lUSjUPZVioxFLNoD7/fxNoQooXE6xH3A4azxl2kY+/j2SjVBoU4ti5yNmbABVgnFrIyj8gWJ7ESM2gOBXFFgFhdIApfzDcCjrGMo8AD2k7qLEQmi8TbGXEQs2gIxPKXMdFEZSLKcjos8G0HAlhT6Fxk8U+xqHsDqJ/zm1at9BKX89KMyiWFE6oswG0HwL5XZAIXgsxT5HdNkAWj5GObZuCIWnKPZYEL9gjaJMZ/Xp37D8EQrpRRQ7C1FkAxhYxPDc7IPCkxS7A79gNafMUOXp33BdC4W8HIqdjGixATTOYdi2Q2MnxZ5GlNgAzsti+AK9oFDPT6nAaTiI1cbYZTgv+anxCDReoNj3iAYbwJXUaZAPhTMpVqsNzLMBXBrlqxf/TbFP8H9WO8rcjnD4HqHabUEo3ESx6oYwywaQupp6vMn0CvG1MMoGkPwBI7HC9ArxG1NxgIVC5z/QNFzByJSZXiF+H8yxAWT+xAi9BY2rKbYextgA2ixhpLIzoZDZgGS4s02rI2UaQ6biWEbuShheIf4RzLABlJbTAacnQqEigWJ9sZ+VR5mJkBjXj464CBqTKLYaBtgAzmlNZ4yFxiUUyzoGjrMB3J5DpwyAxoqwr/m0iinTFUc0OIuOeQYaQyjWLB/OsgFc7Kdz0vJgeIX4h3CUDeBCOmoyNE6k2KBcWABqOzShuZXOKk+FQko5xa6BY2wAvjfotBtgeIX4X31wiA0g8X067kvA8Arxa2EBmQ4s2Ml9lAYMhMZmiq2EI2wAdTfRhKUwvUJ8IBxgA2j6DY1IageN9ym2DBaaUqY7fkW7rTTkMmh0oFioJSJlAzh3Pk1pkgKNJyj2Bqx8yvweNep1Fc1ZDI0pFOtRGxGxAZzQhQbdAZWpFHsTkbAB9DyFRp0PwyvEu2TA6+pQpgSHG5pNsyZBI/Fuiq2Bmg1gVhINyxoFjd9RbIIPSjaA3SHq+Cn2IDTqtqBYd3hcgfZXemsD1GkxchCl+iVD41KKLYCKDeBJKjXpjTcp9jA02tWi2EkInw3At4NKd9cDCmuZPsNzD8Uehbc1pMwsHCTxISpN7xTm9/qR0OgVoJR/BMJkA8h9gUpT8wAA4yj2ClTeNncS0QbQaDaV1tXBfj9QKmE8DK8QT+sIL6sb9obN/AVU+jEdBwymWBVU/kKxWyFnA0DhTCotS8EBSC2nVP10aFxEsfqN4GGNKDMY+42fQKV7grrD/I/D9ArxMyBlAxhxI5U242DFaZRaApX7KXZbImRsAANaOXXQ43uKTYFGej+KDYZ3pYfzM/2TqqkTOAOH6Euxz6ByGcX+BgkbQOce1EkYg8OMpZS/GzTysikgvxzfBlCSRJ1a3XG4EuNHuXdSbDk8K4My5+HhEHUatI3w5vfqujC9Qrw/jsAGMFc9/j0z4m3ST0HlFYo9BK9KpsyX+vEvapaZTanpULmXYknN4VW1KKMf//6atyjWGSrrKFYFrxpEKf34t2Zlxl/SulKsugAetYgGTc1zZqdTYBhMrxC/BR61kmL68W/NRlPseajMsCvEnXxV1o9/axa8jVKtC2B6hfhieNOrNGVZCo7gForNhekV4jPhTXMopx//1iy/AaXm+6DRtAHFOsOTRtCMzQ5/rW0MlR0U2wRvKqIJkyFRGqDUbKicm0CxAfCkpymgGP+KzKZUoBQqP1NsFzzpasrJx79CjSm2E6ZXiCeMhxcNoJh8/Cvlm0+pHvlQ+Zhi2+BJr1FIPv6Vm0uxC6EylGLNmsKLVlFIPv6VK2imuFLU2MfuC+BFdbIpJB//yr1IsdEwvUL8zlx40R4D41+xYQFKvQOYXiG+G150fMDA+FdsOcU6QGUexRb64EWTDIx/Tbyk7YNKwSkUmwgvqkgzMP6V8k2jVE5tqHxLsbHwpCoD41+xz41f9DMqiWJ94EV1uhgY/0rVPYVSNyYaf839FzxpsYHxr9g2is2CSlmAUqFKeNI+A+NfqZZ+Sq0DTK8QfxWelLzewPjXwEafE2B6hXh2MTypstrA+FeorfkjPFMp9gW8qbvfwPhXaIvxlU7n2RXiRzTHb2D8K7OGYn+HSuIVFFsFj7orZGD8K9KoBaXap0JlLsXmB+FRZ2UxfOV9oKD/VrcBxleIl8CruucwXCs6wgEVIUp9A50HqfgTntN/C8MSuDwRjthLseOh0i6NYs/BszK+ohyrb4JDjqPYuzC+QvwzeFjJqZSaWQnHbFVcKWpuhfjZ8LA2O5MocfpLKXDODIo9CZ0PKLYPntZtT4hHctWqFDgpo4hSTXKh0pNiaYXwttJlSfwt5U/lwmGfUGwMdL6h2H/bu7feqKo4jMPvbmemM6VVKtIDULAHhBSxtKAiqQqKBo2pRjFQjFTxAhRTBRRRjFHUSAOxaqUxSDxFqrVpDQpoiDEGDxHUWIOJiUY0jcZEhRtP0cS8XniJNP+9Z9Zhz/yfL7Dv1sUve72rX9+TW727nv+vcffiDHJuVtL4suc5FGucCJWZNrzghNnOo19ekYIRfxh/6SnxdMhBApWYd/vb9w5te25JumXTy8P9HT21MGYRxY6YvwOzOQHL1CZKJedHrBxL8mM/WMdKNsL4hPhxWKYyC0w/KYo5E+SfSMEy9bj5O1x7PN4MUg2nGd/0urKEUgdhm/qHYp2I5jdKXVMKy9QKih1CNJdRrA22qR2UKrkJ0azJj8UYvaH0K6JZ7/HD4qq0j1KNpyCSYBeF3oV16m6KfWL6z4OZsE6dnabUO4impo9CFbBODVJsNqL5gELzYJ26mmKjgNkJ8Wthn/qFUkXTEc0xytwH+9Rq09Ou8gnxh2GfGtdCqfaJ0WLghhKK3AEH1J0U+wmhpW7YSaluOKBqyyh1T4Bwpt62jHKz4YIaothyhFH18SSGcQtcUBdT7C/ItY4WMZzpcELtpVTxKsjUbH+ToY2HE6qbYl9A4tyfz2B45Qk4oVJbKVUvmCeeO1zGKF6BI2otxbZgbMG0AxTwKgSqikpKNQcYw5TnuxjZVXBFPZOThf/51zUyuhkBXFFzKfb9yYtvktn4Fu6oAUoVLxUU3ygWwx11UVaX+Kb2LmPWmuCOSmym1MI6QfEN70O4pHop1isovuHdCpdU3cIoWw412x9lbmyDW+o4xXoExTduB4BaSrGB/4rvn2Uk8+YAUAcodqmg+MbtAFBtFBsUFN+4HQAqaKYr9SvhnnqPrnTDA+r0erpxGF5QI3Ri7zh4Qa0spgN9TfCE2k/7kp3whXqK1pW/BW+ooIuWVXfCI6qDdjWvgk/U5HbatHMO/KI+o0VHMvCMqiqiLdUdAbyjfqQd6WN18JB6iFa8fj38pHbRvO9a4Su1j6at6QngLZV6kCalB9fBa2o9zTmrvwG+U1/RkK/3nQr/qXXFNKB4/3LEg3qJOTfh0/MQF2pWJXOr5f4KxIjqZS59/mwp4kUdZK6UHFqE2FGlA8yJme9fiDhS47uYvTe2nImYUlXV2RffBOJL3Zwu7OKrHrgxi+LbhNhTVS8WdvFVUzZEKb7nI2+o15KFXXxV64zCLr6qbuMkyhzNz+Krag9XFnbxVQ170hxTe54XX3X5R0/yZKqH2jLIe+qSx35I8wQtI3elUCDU5J6R0R1by0mSyQv+/n1k7YoA+eRf5cFlRxX8RB0AAAAASUVORK5CYII=); | |
background-size: 12vw 12vw; | |
display: block; | |
width: 12vw; | |
height: 12vw; | |
content: ''; | |
opacity: 0; | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
animation: click 5s 1s infinite; | |
} | |
@keyframes click { | |
0% { | |
opacity: 0; | |
} | |
3% { | |
opacity: 0.5; | |
} | |
15% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
20% { | |
margin-left: 6vw; | |
margin-top: 5.5vw; | |
} | |
25% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
30% { | |
margin-left: 6vw; | |
margin-top: 5.5vw; | |
} | |
35% { | |
margin-left: 6.5vw; | |
margin-top: 6vw; | |
} | |
47% { | |
opacity: 0.5; | |
} | |
50% { | |
opacity: 0; | |
} | |
} | |
/* | |
* Winner highlight | |
*/ | |
.wrapper #winner { | |
display: none; | |
float: left; | |
margin-top: -80vw; | |
margin-bottom: -113px; | |
margin-left: -5vw; | |
width: 90vw; | |
height: 90vw; | |
font-size: 22vw; | |
text-align: center; | |
text-transform: uppercase; | |
padding-top: 17vw; | |
background: rgba(245, 245, 245, .8); | |
} | |
/* The author. | |
* Hi mom! | |
*/ | |
.wrapper .author { | |
font-size: 3vw; | |
color: #999; | |
float: right; | |
margin-right: 5px; | |
margin-top: 2vw; | |
margin-bottom: 5vw; | |
} | |
.wrapper .author .hearts { | |
color: #e88; | |
} | |
.wrapper .author a { | |
text-decoration: none; | |
color: #8be; | |
} | |
.wrapper .author a:hover { | |
text-decoration: underline; | |
} | |
/* | |
* Misc - taken from elsewhere | |
* Sources: | |
* .noselect - https://stackoverflow.com/questions/826782/how-to-disable-text-selection-highlighting-using-css | |
*/ | |
.noselect, .wrapper #tic-tac-toe div { | |
-webkit-touch-callout: none; /* iOS Safari */ | |
-webkit-user-select: none; /* Chrome/Safari/Opera */ | |
-khtml-user-select: none; /* Konqueror */ | |
-moz-user-select: none; /* Firefox */ | |
-ms-user-select: none; /* Internet Explorer/Edge */ | |
user-select: none; /* Non-prefixed version, currently | |
not supported by any browser */ | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.onload = function() { | |
// Makes arrays | |
function makeArray(obj) { | |
return Array.prototype.slice.call(obj) | |
} | |
// Vanilla jQuery for the lazy | |
var $ = function() { | |
var args = makeArray(arguments); | |
var r = document.querySelectorAll.apply(document, args); | |
return r.length === 1 ? r[0] : r; | |
} | |
// Vanilla class functions | |
function addClass(el, className) { | |
removeClass(el, className) | |
el.className += ' ' + className | |
} | |
function removeClass(el, className) { | |
el.className = el.className.replace(className, '') | |
} | |
// Our main variables | |
var title = $('.wrapper #intro .title'); | |
var message = $('.wrapper #intro .message'); | |
var tictactoe = $('.wrapper #tic-tac-toe'); | |
var blocks = $('.wrapper #tic-tac-toe div'); | |
var restart = $('#restart'); | |
var winner = $('#winner'); | |
// Animated intro! | |
intro() | |
function intro() { | |
function show(el) { | |
el.style.opacity = 1; | |
} | |
function hide(el) { | |
el.style.opacity = 0; | |
} | |
var delayer = new Delayer(); | |
var titleParts = makeArray(title.children); | |
var blocksArray = makeArray(blocks); | |
// Flushing | |
message.style.display = 'block' | |
winner.style.display = 'none' | |
hide(message) | |
removeClass(restart, 'visible') | |
titleParts.map(hide) | |
blocksArray.map(function(el) { | |
el.className = ''; | |
el.innerText = ''; | |
hide(el) | |
}) | |
delayer.add(titleParts, show, 500) | |
delayer.add(blocksArray, show, 100) | |
delayer.add([blocks[0]], function(first) { | |
addClass(first, 'showCursor') | |
blocksArray.map(function(el) { | |
el.onclick = function() { | |
removeClass(first, 'showCursor') | |
message.style.display = 'none' | |
first.onclick = undefined | |
startGame(el) | |
} | |
}) | |
}, 100) | |
delayer.add([message], show, 500) | |
delayer.run() | |
} | |
// Main game logic | |
var startsX = true; | |
function startGame(el) { | |
var player1 = { | |
str: startsX ? 'x' : 'o' | |
} | |
var player2 = { | |
str: !startsX ? 'x' : 'o', | |
} | |
var goesP1 = true; | |
var turns = 0; | |
addClass(restart, 'visible') | |
restart.onclick = function() { | |
intro() | |
} | |
var blocksArray = makeArray(blocks); | |
blocksArray.map(function(el) { | |
el.onclick = function() { | |
if (el.innerText) return | |
var player = goesP1 ? player1 : player2; | |
el.innerText = player.str.toUpperCase(); | |
addClass(el, player.str); | |
goesP1 = !goesP1; | |
turns++ | |
if (turns === 9) { | |
setTimeout(tie, 125); | |
} else | |
if (turns >= 5) { | |
setTimeout(function() { | |
checkWinner(player, el); | |
}, 125) | |
} | |
} | |
}) | |
function checkWinner(player, el) { | |
var B = blocksArray; | |
var i = B.indexOf(el); | |
var str = player.str.toUpperCase() | |
function eqAt(a, b, c) { | |
return B[a].innerText === B[b].innerText && B[a].innerText === B[c].innerText; | |
} | |
var col = i % 3; | |
var row = i - col; | |
if (eqAt(row, row + 1, row + 2)) return won(player, row, row + 1, row + 2); | |
if (eqAt(col, col + 3, col + 6)) return won(player, col, col + 3, col + 6); | |
if (eqAt(0, 4, 8)) return won(player, 0, 4, 8) | |
if (eqAt(2, 4, 6)) return won(player, 2, 4, 6) | |
} | |
function won(player, a, b, c) { | |
addClass(blocksArray[a], 'yellow') | |
addClass(blocksArray[b], 'yellow') | |
addClass(blocksArray[c], 'yellow') | |
winner.style.display = 'block'; | |
winner.style.color = player.str === 'x' ? '#e88' : '#8be'; | |
winner.innerHTML = 'player <b>' + player.str + '</b> wins!' | |
blocksArray.map(function(el) { | |
el.onclick = undefined | |
}) | |
} | |
function tie() { | |
winner.style.display = 'block'; | |
winner.style.color = '#999'; | |
winner.innerHTML = 'IT WAS<br>A TIE' | |
} | |
el.onclick() | |
} | |
// Delayer object | |
// adds animation *frames* into | |
// a strack, then replays them | |
function Delayer() { | |
var stack = [] | |
this.add = function(arr, func, time) { | |
if (!(arr && arr.length)) { | |
throw 'Delayer.add error: invalid first argument, must be an array' | |
} | |
arr.map(function(el) { | |
stack.push({ | |
el: el, | |
func: func, | |
timeout: time | |
}) | |
}) | |
} | |
var that = this | |
this.run = function() { | |
if (!stack.length) return | |
var current = stack.shift() | |
setTimeout(function() { | |
current.func(current.el) | |
that.run() | |
}, current.timeout) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment