1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
import type { View } from "./types";
import { cardFace, clear, money, node } from "./dom";
function cardElement(card: string): HTMLElement {
if (card === "?") {
const back = node("div", "card back");
back.append(node("span", "rank", "?"), node("span", "suit", ""));
return back;
}
const face = cardFace(card);
const element = node("div", "card");
if (face.red) element.classList.add("red");
element.append(node("span", "rank", face.rank), node("span", "suit", face.suit));
return element;
}
function handBlock(label: string, cards: string[], total: number): HTMLElement {
const block = node("div", "hand");
block.append(node("div", "label", label));
const row = node("div", "cards");
for (const card of cards) row.append(cardElement(card));
const sum = node("div", "total");
sum.append(document.createTextNode("Total "), node("strong", undefined, String(total)));
block.append(row, sum);
return block;
}
function outcome(view: View, hand: any): string {
const result = String(hand?.result ?? "");
const winnings = Number(hand?.winnings ?? 0);
switch (result) {
case "blackjack":
return `Blackjack — won ${money(winnings)}`;
case "win":
return `You win — ${money(winnings)}`;
case "push":
return `Push — ${money(winnings)} returned`;
case "bust":
return "Bust — the bet is lost";
case "lose":
return "Dealer wins — the bet is lost";
case "forfeit":
return "Hand forfeited — the bet is lost";
case "playing":
return view.status === "player" ? "Your move." : "Dealing…";
default:
return "Ready to deal.";
}
}
export function renderBlackjack(
board: HTMLElement,
side: HTMLElement,
view: View,
send: (command: unknown) => void,
): void {
clear(board);
clear(side);
const hand = view.hand;
const playerPhase = hand?.phase === "player" || view.status === "player";
const done = hand?.phase === "done" || view.status === "done";
const canDeal = !hand || done;
board.append(node("h2", undefined, "Blackjack"));
board.append(
node("p", "note", "Six decks. Dealer stands on all 17. Blackjack pays 3:2."),
);
if (hand) {
board.append(handBlock("Dealer", hand.dealer ?? [], Number(hand.dealer_total ?? 0)));
board.append(handBlock("You", hand.player ?? [], Number(hand.player_total ?? 0)));
} else {
const empty = node("div", "outcome");
empty.textContent = "Press DEAL to start a hand.";
board.append(empty);
}
if (done || (hand && !playerPhase)) {
const result = node("div", "outcome");
result.textContent = outcome(view, hand);
if (String(hand?.result) === "win" || String(hand?.result) === "blackjack" || String(hand?.result) === "push") {
result.classList.add("win-text");
}
board.append(result);
}
if (view.error) board.append(node("p", "error-text", String(view.error)));
// Sidebar
side.append(node("div", "label", "Blackjack"));
const bet = Number(hand?.bet ?? view.bet ?? 0);
const balance = view.balance ?? view.hand?.balance;
side.append(node("p", "note", "Hit, stand or double. Forfeiting loses the reserved bet."));
if (canDeal) {
const betRow = node("div", "row");
const minus = node("button", "btn small", "−10");
const amount = node("strong", "mono", money(bet));
const plus = node("button", "btn small", "+10");
minus.addEventListener("click", () => send({ AdjustBet: -10 }));
plus.addEventListener("click", () => send({ AdjustBet: 10 }));
betRow.append(minus, amount, plus);
side.append(node("div", "label", "Bet"), betRow);
} else {
const stat = node("div", "stat");
stat.append(node("span", "muted", "Bet"), node("strong", undefined, money(bet)));
side.append(stat);
}
if (balance !== undefined) {
const stat = node("div", "stat");
stat.append(node("span", "muted", "Balance"), node("strong", undefined, money(Number(balance))));
side.append(stat);
}
const actions = node("div", "actions");
if (playerPhase) {
const actionsRow = node("div", "row wrap");
for (const action of hand?.actions ?? []) {
const label = action === "hit" ? "HIT" : action === "stand" ? "STAND" : "DOUBLE";
const button =
action === "stand"
? node("button", "btn primary", label)
: node("button", "btn", label);
button.addEventListener("click", () => {
if (action === "hit") send("Hit");
else if (action === "stand") send("Stand");
else send("Double");
});
actionsRow.append(button);
}
actions.append(actionsRow);
const forfeit = node("button", "btn danger wide", "Forfeit hand");
forfeit.addEventListener("click", () => {
if (window.confirm("Forfeit this hand? The reserved bet is lost.")) send("Forfeit");
});
actions.append(forfeit);
} else if (canDeal) {
const deal = node("button", "btn primary wide big", "DEAL");
deal.addEventListener("click", () => send("Deal"));
actions.append(deal);
}
const leave = node("button", "btn ghost wide", playerPhase ? "Leave (forfeits)" : "Leave table");
leave.addEventListener("click", () => {
if (playerPhase && !window.confirm("Leaving forfeits this hand. Continue?")) return;
send("Leave");
});
actions.append(leave);
side.append(actions);
}
|