diff --git a/src/client/css.css b/src/client/css.css index e69de29..5add65c 100644 --- a/src/client/css.css +++ b/src/client/css.css @@ -0,0 +1,66 @@ +players, player, pot { + display: block; +} + +players { + display: flex; + flex-direction: row; + flex-wrap: wrap; + font-size: 2em; +} + +players > * { + width: 10em; + flex-grow: 1; +} + +player { + background-color: var(--background); + margin: .25em; + padding: .25em; + border-radius: 6px; + display: flex; + flex-direction: column; +} + +player > * { + flex-grow: 1; +} + +player[participating] { +} + +player:not([active]) { + opacity: .5; +} + +player[current] { + color: var(--code); +} + +player[me] { + font-weight: bold; +} + +player[winner] { + font-weight: bold; + font-size: 3em; +} + +pot { + font-size: 3em; + display: block; + width: 100%; + text-align: center; +} + +row { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +card { + text-align: center; + width: 100%; +} diff --git a/src/client/index.html b/src/client/index.html index 3355321..5d25a43 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -10,10 +10,17 @@ +
+ Debug + + + +
-
+ +
diff --git a/src/client/js.js b/src/client/js.js index e533a64..50c4d4a 100644 --- a/src/client/js.js +++ b/src/client/js.js @@ -75,40 +75,61 @@ class Games { forUser(id, uid, cb) { this.get(id, (game) => { + var found = false; game.Players.forEach((player, idx) => { if (player.ID == uid) { cb(game.Players[idx]); + found = true; } }); - games.update(id, game, (game) => {ui.drawGame(game)}); + if (found) + games.update(id, game, (game) => {ui.drawGame(game)}); }); } - get(id, cb) { + get(id, cb, catcher) { + if (!catcher) + catcher = console.log; this.requests.get("/api/games/"+id, (text, status) => { if (status != 200) { - throw new Error("bad status getting game: "+status+": "+text); + catcher("bad status getting game:", text, status); + return; } var game = JSON.parse(text); cb(game); }); } - create(id, cb) { + create(id, cb, catcher) { + if (!catcher) + catcher = console.log; this.requests.post("/api/games/"+id, null, (text, status) => { if (status != 200) { - throw new Error("bad status creating game: "+status+": "+text); + catcher("bad status creating game:", text, status); + return; } var game = JSON.parse(text); cb(game); }); } - update(id, game, cb) { + update(id, game, cb, catcher) { // TODO compute turn + var n = 0; + var m = game.Players.length; + game.Players.forEach((player) => n += player.Participating ? 1 : 0); + game.Turn = game.Turn % (m || 1); + if (n) + while (m > 0 && (!game.Players[game.Turn].Active || !game.Players[game.Turn].Participating)) { + game.Turn = (game.Turn + 1) % game.Players.length; + m -= 1; + } + if (!catcher) + catcher = console.log; this.requests.put("/api/games/"+id, JSON.stringify(game), (text, status) => { if (status != 200) { - throw new Error("bad status updating game: "+status+": "+text); + catcher("bad status updating game: ", text, status); + return; } var game2 = JSON.parse(text); cb(game2); @@ -121,7 +142,8 @@ class UI { this.games = games; this.ts = 0; this.threshold = 2; - setInterval(() => { this.refresh() }, 2000); + this.refresh(); + setInterval(() => { this.refresh() }, 500); } now() { @@ -137,36 +159,92 @@ class UI { }); } + formatCurrency(currency) { + return `${Math.floor(currency/100)}.${currency%100 < 10 ? "0" : ""}${currency%100}` + } + + formatCard(card) { + if (card == -1) + return `
?`; + var suit = card.Suit; + var value = card.Value + 2; + switch (suit) { + case 0 : suit = "hearts"; break; + case 1 : suit = "spades"; break; + case 2 : suit = "diamonds"; break; + case 3 : suit = "clubs"; break; + } + switch (value) { + case 10 : value = "jack"; break; + case 11 : value = "queen"; break; + case 12 : value = "king"; break; + case 13 : value = "ace"; break; + } + return `
${value} of ${suit}`; + } + drawGame(game) { + var playersActive = 0; + var activePlayersChecked = 0; + var winning = -1; + game.Players.forEach((player, idx) => { + if (player.ID != "" && player.Participating && player.Active) { + if (winning == -1 || game.Players[winning].Card.Value <= player.Card.Value) { + winning = idx; + } + playersActive += 1; + if (player.Checked) { + activePlayersChecked += 1; + } + } + }); + var complete = playersActive < 2 || playersActive == activePlayersChecked; this.ts = this.now(); var state = ` - ${game.Pot} + ${this.formatCurrency(game.Pot)} `; + var myseat = -1; game.Players.forEach((player, seatnum) => { if (player.ID != "") { - state += ``; + state += ``; state += ` - ${player.Name} - ${player.Balance} - ${this.me(player) ? "?" : player.Card} + + ${player.Name} + ${this.formatCurrency(player.Balance)} + + + ${this.formatCard(!complete && (this.me(player) || !player.Participating || !player.Active) ? -1 : player.Card)} + `; if (this.me(player)) { - var myturn = seatnum == game.Turn - var enabled = player.Active && myturn; - var eleControlGame = this.eleControlGame(); - var enabledWas = eleControlGame.getAttribute("disabled") == null; - if (enabled != enabledWas) { - if (enabled) { - eleControlGame.removeAttribute("disabled"); - } else { - eleControlGame.setAttribute("disabled", true); - } - } + myseat = seatnum; } state += ``; } }); + var enabled = myseat == game.Turn && game.Players[myseat].Participating && game.Players[myseat].Active; + var eleControlGame = this.eleControlGame(); + var enabledWas = eleControlGame.getAttribute("disabled") == null; + if (enabled != enabledWas) { + var foo = (ele) => { + ele.removeAttribute("disabled"); + }; + if (!enabled) { + foo = (ele) => { + ele.setAttribute("disabled", true); + }; + } + foo(eleControlGame) + Array.from(eleControlGame.children).forEach(foo); + } state += ""; this.ele().innerHTML = state; } @@ -209,29 +287,10 @@ class Deck { var idx = Math.floor(Math.random() * 52); if (!this.cards[idx]) { this.cards[idx] = 1; - return idx; + return {Suit: idx%4, Value: idx%13}; } } } - - card(idx) { - var suit = idx % 4; - switch (suit) { - case 0: suit = "hearts"; break; - case 1: suit = "clubs"; break; - case 2: suit = "diamonds"; break; - case 3: suit = "spades"; break; - } - var num = idx % 13; - num += 2 - switch (num) { - case 10: num = "jack"; break; - case 11: num = "queen"; break; - case 12: num = "king"; break; - case 13: num = "ace"; break; - } - return `${num} of ${suit}`; - } } function init() { @@ -248,16 +307,12 @@ function init() { games.create("id", () => { games.update("id", { - Pot: "$0", + Pot: 0, Players: [ {ID: id, Name: name}, ], }, (game) => ui.drawGame(game)); }); - - games.get("id", (game) => { - ui.drawGame(game); - }); } function start() { @@ -272,6 +327,7 @@ function start() { if (player.Participating) { n += 1; game.Players[idx].Active = true; + game.Players[idx].Checked = false; game.Players[idx].Card = deck.draw(); } }); @@ -289,11 +345,28 @@ function start() { function join() { var db = new DB(); var id = db.get("id"); - games.forUser("id", id, (player) => { - if (player.Participating) - throw new Error("redundant join"); - player.Participating = true; - player.Active = false; + var name = db.get("name"); + games.forGame("id", (game) => { + var redundant = false; + var set = false; + for (var i = 0; i < game.Players.length; i++) { + if (!set && game.Players[i].ID == "") { + game.Players[i] = { + ID: id, + Name: name, + Participating: true, + } + set = true; + } else { + redundant = redundant || game.Players[i].ID == id; + } + } + if (!set) { + throw new Error("did not take a seat"); + } + if (redundant) { + throw new Error("cannot take 2 seats"); + } }); } @@ -301,8 +374,6 @@ function drop() { var db = new DB(); var id = db.get("id"); games.forUser("id", id, (player) => { - if (!player.Participating) - throw new Error("redundant drop"); player.Participating = false; player.Active = false; }); @@ -312,8 +383,6 @@ function fold(ele) { var db = new DB(); var id = db.get("id"); games.forUser("id", id, (player) => { - if (!player.Active) - throw new Error("redundant fold"); player.Active = false; }); } @@ -323,22 +392,63 @@ function check(ele) { var id = db.get("id"); games.forGame("id", (game) => { game.Turn += 1; + game.Players.forEach((player) => { + if (player.ID == id) + player.Checked = true; + }); + }); +} + +function collect(ele) { + var db = new DB(); + var id = db.get("id"); + games.forGame("id", (game) => { + var found = false; + game.Players.forEach((player, idx) => { + if (player.ID == id) { + player.Balance += game.Pot; + found = true; + } + }); + if (!found) { + throw new Error("nil player cannot collect pot"); + } + game.Pot = 0; + }); +} + +function resetGame() { + console.log("resetting game"); + games.forGame("id", (game) => { + game.Players = []; + game.Turn = 0; + game.Pot = 0; + }); +} + +function setWallet(ele) { + var db = new DB(); + var id = db.get("id"); + var balance = parseInt(ele.parentNode.getElementsByTagName("input")[0].value); + games.forUser("id", id, (player) => { + player.Balance = balance; }); } function raise(ele) { var db = new DB(); var id = db.get("id"); - var bump = ele.parentNode.getElementsByTagName("input")[0].value; + var bump = parseInt(ele.parentNode.getElementsByTagName("input")[0].value); games.forGame("id", (game) => { game.Turn += 1; game.Players.forEach((player, idx) => { if (player.ID == id) { - // todo it's supposed to be a string, should render client-side player.Balance -= bump; if (player.Balance < 0) throw new Error("cannot bet more than you have"); game.Pot += bump; + } else { + player.Checked = false; } }); }); diff --git a/src/server/game.go b/src/server/game.go index 0f4fc96..57aa615 100644 --- a/src/server/game.go +++ b/src/server/game.go @@ -11,14 +11,20 @@ type Game struct { type Player struct { ID string Name string - Card int + Card Card Balance Currency Active bool Participating bool + Checked bool } type Currency int +type Card struct { + Suit int + Value int +} + func (game Game) GetPlayers() []Player { players := []Player{} for _, player := range game.Players {