| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { COLORS, node, tileEl } from "./dom.js"; |
|
|
| function miniTile(color) { |
| const t = tileEl(color, "mini"); |
| return t; |
| } |
|
|
| function playerCard(label, kids) { |
| const card = node("div", "score-card"); |
| card.appendChild(node("h3", "score-card-who", label)); |
| kids.forEach((k) => card.appendChild(k)); |
| return card; |
| } |
|
|
| function line(text) { |
| return node("p", "score-line", text); |
| } |
|
|
| |
| function roundCard(report, seat, label) { |
| const p = report.players[seat]; |
| const kids = []; |
| const delta = node( |
| "p", |
| "score-delta" + (p.delta < 0 ? " down" : p.delta > 0 ? " up" : ""), |
| (p.delta >= 0 ? "+" + p.delta : String(p.delta)) + " → " + p.score_after |
| ); |
| kids.push(delta); |
|
|
| if (p.tiles.length) { |
| const placed = node("div", "score-tiles"); |
| p.tiles.forEach((t) => { |
| const box = node("div", "score-tile"); |
| box.appendChild(miniTile(t.color)); |
| box.appendChild(node("span", "pts", "+" + t.points)); |
| box.appendChild(node("span", "where", "r" + (t.row + 1) + "·c" + (t.col + 1))); |
| box.title = |
| t.color_name + |
| " → row " + |
| (t.row + 1) + |
| ", column " + |
| (t.col + 1) + |
| " (run of " + |
| t.h_run + |
| " across, " + |
| t.v_run + |
| " down)"; |
| placed.appendChild(box); |
| }); |
| kids.push(placed); |
| kids.push( |
| line( |
| "Wall: +" + |
| p.tiling_points + |
| " from " + |
| p.tiles.length + |
| " tile" + |
| (p.tiles.length > 1 ? "s" : "") |
| ) |
| ); |
| } else { |
| kids.push(line("No pattern line was full. Nothing reached the wall.")); |
| } |
|
|
| const floorBits = []; |
| if (p.floor.tiles.length) { |
| floorBits.push(p.floor.tiles.length + " tile" + (p.floor.tiles.length > 1 ? "s" : "")); |
| } |
| if (p.floor.marker) floorBits.push("the marker"); |
| kids.push( |
| line( |
| floorBits.length |
| ? "Floor: " + floorBits.join(" and ") + " → " + p.floor.penalty |
| : "Floor: clean." |
| ) |
| ); |
| if (p.carried_rows.length) { |
| kids.push( |
| line( |
| "Carried over: " + |
| p.carried_rows |
| .map((r) => r.count + " " + COLORS[r.color] + " in row " + (r.row + 1)) |
| .join(", ") |
| ) |
| ); |
| } |
| return playerCard(label, kids); |
| } |
|
|
| |
| |
| |
| |
| export function renderRoundPanel(host, report, sides) { |
| host.innerHTML = ""; |
| host.classList.add("scoring"); |
| host.hidden = false; |
| const head = node("div", "scoring-head"); |
| head.appendChild(node("h2", "scoring-title", "Round " + (report.round + 1) + " scored")); |
| if (!report.game_over) { |
| const yours = sides[0][0] === report.next_first_player; |
| const who = yours ? sides[0][1] + " start" : sides[1][1] + " starts"; |
| head.appendChild(node("span", "scoring-note", who + " the next round")); |
| } |
| host.appendChild(head); |
| const grid = node("div", "scoring-grid"); |
| sides.forEach(([seat, label]) => grid.appendChild(roundCard(report, seat, label))); |
| host.appendChild(grid); |
| } |
|
|
| |
| function bonusCard(final, seat, label) { |
| const b = final.bonuses[seat]; |
| const kids = []; |
| const total = node("p", "score-delta big", String(b.final_score)); |
| if (final.winner === seat) total.classList.add("won"); |
| kids.push(total); |
|
|
| const rows = [ |
| ["On the board", b.score_before_bonus, ""], |
| ["Full rows ×2", "+" + b.row_points, b.rows + " row" + (b.rows === 1 ? "" : "s")], |
| ["Full columns ×7", "+" + b.col_points, b.cols + " column" + (b.cols === 1 ? "" : "s")], |
| [ |
| "All five of a colour ×10", |
| "+" + b.color_points, |
| b.colors + " colour" + (b.colors === 1 ? "" : "s"), |
| ], |
| ["Bonus", "+" + b.total, ""], |
| ]; |
| const table = node("dl", "bonus-list"); |
| rows.forEach(([name, value, note], i) => { |
| const dt = node("dt", null, name); |
| const dd = node("dd", null, String(value)); |
| if (i === rows.length - 1) { |
| dt.classList.add("sum"); |
| dd.classList.add("sum"); |
| } |
| if (note) dd.title = note; |
| table.append(dt, dd); |
| }); |
| kids.push(table); |
| return playerCard(label, kids); |
| } |
|
|
| |
| export function renderFinalPanel(host, final, sides) { |
| host.innerHTML = ""; |
| host.classList.add("scoring", "final"); |
| host.hidden = false; |
| const head = node("div", "scoring-head"); |
| head.appendChild(node("h2", "scoring-title", final.headline)); |
| head.appendChild( |
| node( |
| "span", |
| "scoring-note", |
| "Final score " + |
| final.scores[sides[0][0]] + |
| "–" + |
| final.scores[sides[1][0]] + |
| " after " + |
| final.rounds_played + |
| " rounds" |
| ) |
| ); |
| host.appendChild(head); |
| const grid = node("div", "scoring-grid"); |
| sides.forEach(([seat, label]) => grid.appendChild(bonusCard(final, seat, label))); |
| host.appendChild(grid); |
| if (final.winner === null) { |
| host.appendChild(line("Level on score and on completed rows. The rulebook calls that a draw.")); |
| } |
| if (final.exhausted) { |
| host.appendChild(line("The bag and the lid ran dry, so the game stopped early.")); |
| } |
| } |
|
|
| |
| export function clearScoring(host) { |
| host.innerHTML = ""; |
| host.hidden = true; |
| host.classList.remove("final"); |
| } |
|
|