let express = require('express');
var cors = require('cors')
let app = express();
let gameState = initializeGameState();
let whoseTurn = 'x';
let winner = null;
app.use(express.json());
app.use(cors());
app.get('/version', function (req, res) {
res.send(JSON.stringify({version: '2.0.3'}));
});
app.get('/state', function (req, res) {
const result = JSON.stringify({state: gameState, turn: whoseTurn, winner});
res.send(result);
});
app.post('/restart', function (req, res) {
gameState = initializeGameState();
whoseTurn = 'x';
winner = null;
});
app.post('/tick', function (req, res) {
let request;
try {
request = req.body;
} catch(e) {
res.status(400).send(JSON.stringify({error: true, errorCode: 6, message: 'Parsing error'}));
return;
}
if (!validateTickRequest(request, res)) {
return;
}
gameState[request.y][request.x] = request.value;
whoseTurn = whoseTurn === 'x' ? 'o' : 'x';
winner = checkWin();
res.send(JSON.stringify({status: 'success', winner}));
});
function validateTickRequest(request, res) {
let { x, y, value } = request;
if (winner !== null) {
res.status(400).send(JSON.stringify({error: true, errorCode: 7, message: 'Game has finished!'}));
}
if (!Number.isInteger(x) || x < 0 || x > 2) {
res.status(400).send(JSON.stringify({error: true, errorCode: 1, message: 'Invalid x: ' + x}));
return false;
}
if (!Number.isInteger(y) || y < 0 || y > 2) {
res.status(400).send(JSON.stringify({error: true, errorCode: 2, message: 'Invalid y'}));
return false;
}
if (value != 'x' && value != 'o') {
res.status(400).send(JSON.stringify({error: true, errorCode: 3, message: 'Invalid value'}));
return false;
}
if (value !== whoseTurn) {
res.status(400).send(JSON.stringify({error: true, errorCode: 4, message: 'Not your turn!'}));
return false;
}
if (gameState[y][x] != null) {
res.status(400).send(JSON.stringify({error: true, errorCode: 5, message: 'Cell is already occupied'}));
return false;
}
return true;
}
function checkWin() {
for (let y = 0; y < 3; y++) {
if (gameState[y][0] !== null && (gameState[y][0] === gameState[y][1] && gameState[y][0] === gameState[y][2])) {
return gameState[y][0];
}
}
for (let x = 0; x < 3; x++) {
if (gameState[0][x] !== null && (gameState[0][x] === gameState[1][x] && gameState[0][x] === gameState[2][x])) {
return gameState[0][x];
}
}
if (gameState[0][0] !== null && (gameState[0][0] === gameState[1][1] && gameState[0][0] === gameState[2][2])) {
return gameState[0][0];
}
if (gameState[2][0] !== null && (gameState[2][0] === gameState[1][1] && gameState[2][0] === gameState[0][2])) {
return gameState[2][0];
}
return null;
}
function initializeGameState() {
let newState = [];
for (let y = 0; y < 3; y++) {
newState[y] = [];
for (let x = 0; x < 3; x++) {
newState[y][x] = null;
}
}
return newState;
}
app.options('*', (req, res) => {
// allowed XHR methods
res.header('Access-Control-Allow-Methods', 'GET, PATCH, PUT, POST, DELETE, OPTIONS');
res.send();
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});