diff options
Diffstat (limited to 'src/quadrowple')
-rw-r--r-- | src/quadrowple/index.html | 27 | ||||
-rw-r--r-- | src/quadrowple/server.js | 172 | ||||
-rw-r--r-- | src/quadrowple/static/main.js | 107 | ||||
-rw-r--r-- | src/quadrowple/static/styles.css | 72 |
4 files changed, 378 insertions, 0 deletions
diff --git a/src/quadrowple/index.html b/src/quadrowple/index.html new file mode 100644 index 0000000..574ffca --- /dev/null +++ b/src/quadrowple/index.html @@ -0,0 +1,27 @@ +<!doctype html> +<html lang="en"> + +<head> + <title>Quad-row-ple</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <link rel="shortcut icon" href="/favicon.ico"> + <!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> --> + <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> + <script src="/quadrowple/main.js"></script> + <link rel="stylesheet" type="text/css" href="/quadrowple/styles.css"> +</head> + +<body> + <div id="data"> + <h1 v-if="status">{{status}}</h1> + + <canvas id="canvas" width="351" height="301"></canvas> + + <div> + <button v-on:click="newGame">{{this.playing ? 'Forfeit' : 'New Game'}}</button> + </div> + </div> +</body> + +</html>
\ No newline at end of file diff --git a/src/quadrowple/server.js b/src/quadrowple/server.js new file mode 100644 index 0000000..b44c055 --- /dev/null +++ b/src/quadrowple/server.js @@ -0,0 +1,172 @@ +const path = require('path'); +const fs = require('fs'); + +function setUpRoutes(server, models, jwtFunctions, database) { + + server.get('/quadrowple', (req, res) => res.sendFile(__dirname + "/index.html")) + server.get('/quadrowple/main.js', (req, res) => res.sendFile(__dirname + "/static/main.js")) + server.get('/quadrowple/styles.css', (req, res) => res.sendFile(__dirname + "/static/styles.css")) + // server.use('/static', express.static(path.join(__dirname, '/static'))) + + var games = {} + var gameList = [] + var id = 0 + server.get('/quadrowple/status', (req, res) => { + let cookie = req.cookies.session; + if (!cookie) { + cookie = uuidv4(); + res.cookie('session', cookie, { expires: new Date(Date.now() + (1000 * 60 * 60)) }); + } + currentGames = [] + gameList.forEach( game => { + var diff = new Date() - game.time; + // Keep games that are recently updated + if(diff < 1000 * 10) {// 10 seconds with no update + currentGames.push(game) + } else { // otherwise don't save it and get rid of game + game.players.forEach(c => { + delete games[c] + }); + } + }) + gameList = currentGames + if (!games[cookie]) { + var game = gameList.find((el) => el.waiting) + if (game) { // Start game + game.players.push(cookie) + game.waiting = false + game.board = [[undefined, undefined, undefined, undefined, undefined, undefined], + [undefined, undefined, undefined, undefined, undefined, undefined], + [undefined, undefined, undefined, undefined, undefined, undefined], + [undefined, undefined, undefined, undefined, undefined, undefined], + [undefined, undefined, undefined, undefined, undefined, undefined], + [undefined, undefined, undefined, undefined, undefined, undefined], + [undefined, undefined, undefined, undefined, undefined, undefined]] + game.turn = Math.floor(Math.random() * 2) + games[cookie] = game + } else { // Create new game, and wait + game = { + waiting: true, + players: [cookie], + id: id++, + time: new Date(), + } + gameList.push(game) + games[cookie] = game + } + } else { + games[cookie].time = new Date() + } + res.status(200).send(games[cookie]); + }) + + // returns undefined if game in progress, or index of winner + function gameOver(game) { + for (var player = 0; player < 2; player++) { + // 4 in a row in a column + for (var col = 0; col < 7; col++) { + for (var row = 0; row < 2; row++) { + if (game.board[col][row] == player && + game.board[col][row + 1] == player && + game.board[col][row + 2] == player && + game.board[col][row + 3] == player) { + return player + } + } + } + // 4 in a row in a row + for (var col = 0; col < 3; col++) { + for (var row = 0; row < 6; row++) { + if (game.board[col][row] == player && + game.board[col + 1][row] == player && + game.board[col + 2][row] == player && + game.board[col + 3][row] == player) { + return player + } + } + } + // Check up right diagonal + for (var row = 0; row < 2; row++) { + for (var col = 0; col < 3; col++) { + if (game.board[col][row] == player && + game.board[col + 1][row + 1] == player && + game.board[col + 2][row + 2] == player && + game.board[col + 3][row + 3] == player) { + return player + } + } + } + // Check down right diagonal + for (var row = 3; row < 6; row++) { + for (var col = 0; col < 3; col++) { + if (game.board[col][row] == player && + game.board[col + 1][row - 1] == player && + game.board[col + 2][row - 2] == player && + game.board[col + 3][row - 3] == player) { + return player + } + } + } + } + return undefined + } + + function sendGame(res, game, cookie){ + var winner = gameOver(game) + if (winner != undefined) { + res.status(200).send({ game: game, turn: -1, winner: game.players[winner] == cookie }); + } else { + res.status(200).send({ game: game, turn: game.players[game.turn] == cookie }); + } + } + server.get('/quadrowple/game/:col', (req, res, next) => { + let cookie = req.cookies.session; + if (!cookie || !games[cookie]) { + next() + return + } + var game = games[cookie] + const { col } = req.params; + if (game.players[game.turn] == cookie && gameOver(game) == undefined) { + // Add player's token to column + for (var row = 0; row < 6; row++) { + if (game.board[col][row] == undefined) { + game.board[col][row] = (game.players.indexOf(cookie)) + game.turn = (game.turn + 1) % 2 + break + } + } + } + sendGame(res, game, cookie) + }) + server.get('/quadrowple/game', (req, res, next) => { + let cookie = req.cookies.session; + if (!cookie || !games[cookie]) { + res.status(200).send({restart: true}); + return + } + var game = games[cookie] + if(gameOver(game) != undefined){ + delete games[cookie] + } + sendGame(res, game, cookie) + }) + + server.get('/quadrowple/new', (req, res, next) => { + let cookie = req.cookies.session; + if (!cookie || !games[cookie]) { + next() + return + } + games[cookie].players.forEach( el =>{ + delete games[el] + }) + }) + +} + +module.exports = { + setUpRoutes +}; + + diff --git a/src/quadrowple/static/main.js b/src/quadrowple/static/main.js new file mode 100644 index 0000000..008658c --- /dev/null +++ b/src/quadrowple/static/main.js @@ -0,0 +1,107 @@ +window.onload = function () { + var transactionData = new Vue({ + el: '#data', + data: { + status: "Loading...", + playing: false + }, + methods: { + newGame: function () { + if (this.playing) { + fetch(new Request(`/quadrowple/new`)) + } else { + this.startGame() + } + }, + startGame: function () { + console.log("created") + var mouseDown = function (e) { + col = Math.floor(e.x / 50); + if (0 <= col <= 6) { + fetch(new Request(`/quadrowple/game/${col}`)) + } + } + document.addEventListener("click", mouseDown); + var playInterval; + var playGame = function (t) { + fetch(new Request(`/quadrowple/game`)).then(response => response.json()) + .then(response => { + var c = document.getElementById("canvas"); + var ctx = c.getContext("2d"); + ctx.fillStyle = "#eee" + ctx.fillRect(0, 0, 351, 301) + + if (response.restart) { + t.playing = false + t.status = "The game has been forfeited" + clearInterval(playInterval) + return + } else if (response.winner != undefined) { + clearInterval(playInterval) + if (response.winner) { + t.status = "You won!" + } else { + t.status = "You lose!" + } + } else if (response.turn) { + t.status = "It's your turn!" + } else { + t.status = "Waiting for other player..." + } + + ctx.strokeStyle = "black" + for (var i = 0; i <= 7; i++) { + ctx.moveTo(50 * i, 0); + ctx.lineTo(50 * i, 300); + ctx.stroke(); + } + for (var i = 0; i <= 6; i++) { + ctx.moveTo(0, 50 * i); + ctx.lineTo(350, 50 * i); + ctx.stroke(); + } + + for (var col = 0; col < 7; col++) { + for (var row = 0; row < 6; row++) { + if (response.game.board[col][row] == undefined) { + continue + } else if (response.game.board[col][row] == 0) { + ctx.fillStyle = "#0000FF"; + } else { + ctx.fillStyle = "#FF0000"; + } + ctx.beginPath(); + ctx.arc(25 + 50 * col, 50 * (5 - row) + 25, 23, 0, 2 * Math.PI); + ctx.fill(); + } + } + }); + } + var loadInterval = undefined + var loadStatus = function (recurse, t) { + fetch(new Request(`/quadrowple/status`)).then(response => response.json()) + .then(response => { + if (response.waiting) { + t.status = "Waiting..." + if (recurse) { + loadInterval = window.setInterval(loadStatus, 1000, false, t) + } + } else { + t.status = "Playing..." + t.playing = true + clearInterval(loadInterval) + playInterval = window.setInterval(playGame, 1000, t) + } + }); + } + loadStatus(true, this) + } + }, + created() { + this.startGame(); + }, + computed: { + + } + }); +}
\ No newline at end of file diff --git a/src/quadrowple/static/styles.css b/src/quadrowple/static/styles.css new file mode 100644 index 0000000..4eedd0b --- /dev/null +++ b/src/quadrowple/static/styles.css @@ -0,0 +1,72 @@ +td { + border: 1px solid lightgrey; + min-width: 3em; +} + +table { + max-width: 100%; +} + +li { + cursor: pointer; + text-decoration: underline; +} + +tr:nth-child(2n+1) { + background-color: lightgray; +} +tr { + width: 100%; +} + +.bold { + font-weight: bold; +} + +#data { + width: 100%; +} + +.border { + border: 1px solid lightgrey; +} + +textarea { + border-radius: 4px; + width: 60%; + height: 10em; + display: block; +} + +pre { + white-space: pre-line; +} + +.net-negative { + /* color: red; */ + background-color: lightcoral; +} +.net-positive { + /* color: green; */ + background-color: lightgreen +} +.summary-panel { + float:left; + padding-right: 2em; +} +@media only screen and (max-width: 600px) { + .newItem td { + display:block; + } + .table-index { + display: none; + } + button { + font-size: 32px; + } + input { + font-size: 32px; + display: block; + width: 100%; + } +}
\ No newline at end of file |