Skip to content

Instantly share code, notes, and snippets.

@MikeBild
Created July 5, 2013 11:16
Show Gist options
  • Save MikeBild/e10f1c3e90ce4d17022a to your computer and use it in GitHub Desktop.
Save MikeBild/e10f1c3e90ce4d17022a to your computer and use it in GitHub Desktop.
distributed TicTacToe with ES in pure JavaScript - sample for copy & paste game logic from client to server
<!DOCTYPE html>
<html>
<head>
<style>
table {
border: 1px solid black;
}
table tr td{
width: 50px;
height: 50px;
border: 1px solid black;
text-align: center;
vertical-align: center;
}
</style>
</head>
<body>
<div id="board">
<table>
<tr>
<td id="31"></td>
<td id="32"></td>
<td id="33"></td>
</tr>
<tr>
<td id="21"></td>
<td id="22"></td>
<td id="23"></td>
</tr>
<tr>
<td id="11"></td>
<td id="12"></td>
<td id="13"></td>
</tr>
</table>
</div>
<div>
Round:
<div id="round"></div>
Player:
<div id="player"></div>
Situation:
<div id="situation"></div>
<button id="reset">New Game</button>
</div>
<script>
var checkGameState = function() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", 'http://localhost:9001/status', false);
xmlhttp.send(null);
if(xmlhttp.status == 200) {
return JSON.parse(xmlhttp.responseText);
}
};
var game = {
emit: function(event){
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", 'http://localhost:9001/emit', false);
xmlhttp.send(JSON.stringify(event));
}
};
function refresh(){
var gameState = checkGameState();
document.getElementById("situation").innerText = gameState.situation;
document.getElementById("round").innerText = gameState.round;
elements.forEach(function(element){
element.innerText = "";
});
gameState.turnX.forEach(function(item){
document.getElementById(item).innerText = "X";
});
gameState.turnO.forEach(function(item){
document.getElementById(item).innerText = "O";
});
if(gameState.situation !== "WON") document.getElementById("player").innerText = gameState.player;
};
var elements = Array.prototype.slice.call(document.getElementsByTagName("td"));
elements.forEach(function(element){
element.addEventListener('click', function(){
var gameState = checkGameState();
game.emit({ type:'player' + gameState.player, data: { pos: this.id } });
refresh();
});
});
document.getElementById("reset").addEventListener('click', function(){
game.emit({ type:'reset', data: { } });
refresh();
});
refresh();
</script>
</body>
</html>
var http = require("http"),
webserver = http.createServer();
function intersect(a, b){
var ai=0, bi=0;
var result = new Array();
while( ai < a.length && bi < b.length )
{
if (a[ai] < b[bi] ){ ai++; }
else if (a[ai] > b[bi] ){ bi++; }
else /* they're equal */
{
result.push(a[ai]);
ai++;
bi++;
}
}
return result;
};
var Aggregate = function(){
var events = [];
return {
when: function(match){
state = match.$init ? match.$init : {};
for (var i = 0; i < events.length; i++) {
if(match[events[i].type] && events[i].data)
state = match[events[i].type](state, events[i].data);
}
return state;
},
emit: function(event){
events.push(event);
}
}
};
var game = new Aggregate();
var checkGameState = function() {
var checkWinner = function(playerTurns, solutions){
playerTurns.sort();
for(var i in solutions){
var intersection = intersect(playerTurns, solutions[i]);
if(intersection.toString() === solutions[i].toString())
return "WON";
}
if(state.round === 9)
return "DRAW";
return "RUNNING";
};
var rejectTurn = function(state, event){
return state.turnX.concat(state.turnO).indexOf(event.pos) !== -1;
};
return game.when({
$init: {
player: "X",
situation: "START",
turnX: [],
turnO: [],
round: 0,
solutions: [
[11,12,13],
[21,22,23],
[31,32,33],
[11,22,33],
[13,22,31],
[11,21,31],
[12,22,32],
[13,23,33]
]
},
playerX: function(state, event){
if(rejectTurn(state, event))
return state;
state.round++;
state.turnX.push(event.pos);
state.situation = checkWinner(state.turnX, state.solutions);
state.player = state.player === "X" ? "O" : "X";
return state;
},
playerO: function(state, event){
if(rejectTurn(state, event))
return state;
state.round++;
state.turnO.push(event.pos);
state.situation = checkWinner(state.turnO, state.solutions);
state.player = state.player === "X" ? "O" : "X";
return state;
},
reset: function(state, event){
console.log("reset");
state.round = 0;
state.turnO = [];
state.turnX = [];
state.player = "X";
state.situation = "START";
return state;
}
});
};
webserver.on('request', function(req, res){
res.setHeader("access-control-allow-origin","*");
res.setHeader("access-control-allow-methods","GET, POST, PUT, DELETE, OPTIONS");
res.setHeader("access-control-allow-headers","content-type, accept");
if(req.method === 'OPTIONS'){
return res.end();
}
if(req.url === '/status'){
var gameState = JSON.stringify(checkGameState());
return res.end(gameState);
};
if(req.url === '/emit' && req.method === 'POST'){
var body = '';
req.on('data', function(data) {
body += data;
});
req.on('end', function() {
game.emit(JSON.parse(body));
return res.end("OK Emit");
});
};
});
webserver.listen(9001);
@MikeBild
Copy link
Author

MikeBild commented Jul 5, 2013

A) create tictactoe.html file
B) create tictactoe.js file
C) node tictactoe.js
D) open tictactoe.html in browser
E) have fun ;)

@MikeBild
Copy link
Author

MikeBild commented Jul 5, 2013

@MikeBild
Copy link
Author

MikeBild commented Jul 5, 2013

yes really - game logic is copy & paste from client side!!!

@MikeBild
Copy link
Author

MikeBild commented Jul 7, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment