];
}
+function ScoredGrid(props) {
+ const keys = Object.keys(props.grid);
+ if (keys.length === 0) {
+ return <div className="scored-grid-empty">No tiles placed</div>;
+ }
+
+ let minR = Infinity, maxR = -Infinity, minC = Infinity, maxC = -Infinity;
+ for (const key of keys) {
+ const [r, c] = key.split(",").map(Number);
+ if (r < minR) minR = r;
+ if (r > maxR) maxR = r;
+ if (c < minC) minC = c;
+ if (c > maxC) maxC = c;
+ }
+
+ const cells = [];
+ for (let r = minR; r <= maxR; r++) {
+ for (let c = minC; c <= maxC; c++) {
+ const key = r + "," + c;
+ const cell = props.grid[key];
+ if (cell) {
+ let className = "tile";
+ if (cell.isBlank) className += " blank";
+ if (!cell.scored) className += " unscored";
+ cells.push(
+ <div key={key} className="scored-cell">
+ <div className={className}>{cell.letter}</div>
+ </div>
+ );
+ } else {
+ cells.push(<div key={key} className="scored-cell" />);
+ }
+ }
+ }
+
+ const cols = maxC - minC + 1;
+ return (
+ <div className="scored-grid"
+ style={{ gridTemplateColumns: `repeat(${cols}, var(--scored-cell-size))` }}>
+ {cells}
+ </div>
+ );
+}
+
+function ScoreBoard(props) {
+ const sorted = [...props.results].sort((a, b) => b.score - a.score);
+ return (
+ <div className="scoreboard">
+ <h2>Final Scores</h2>
+ {sorted.map((result, i) => (
+ <div key={result.name} className="player-result">
+ <div className="player-score-line">
+ <span className="rank">#{i + 1}</span>
+ <span className="player-name">{result.name}</span>
+ <span className={"player-score" +
+ (result.score < 0 ? " negative" : "")}>
+ {result.score} {result.score === 1 || result.score === -1
+ ? "point" : "points"}
+ </span>
+ </div>
+ <ScoredGrid grid={result.scored_grid} />
+ {result.rack.length > 0 ? (
+ <div className="result-rack">
+ {result.rack.map((letter, j) => {
+ const is_blank = (letter === "_");
+ return (
+ <div key={j}
+ className={"tile unscored" + (is_blank ? " blank" : "")}>
+ {is_blank ? "\u00A0" : letter}
+ </div>
+ );
+ })}
+ </div>
+ ) : null}
+ </div>
+ ))}
+ </div>
+ );
+}
+
function Tile(props) {
const { letter, isBlank, invalid, unconnected, selected,
onDragStart, onDragEnd, onClick, onTouchStart } = props;
stuck_players: [],
bag_remaining: null,
game_over: false,
- winner: null,
+ results: null,
blank_pending: null,
drag_source: null,
drag_over_cell: null,
}
receive_game_over(data) {
- this.setState({ game_over: true, winner: data.winner });
+ this.setState({ game_over: true, results: data.results || null });
}
/*****************************************************
render() {
const state = this.state;
+
+ /* Show scoreboard when results are in. */
+ if (state.results) {
+ return [
+ <GameInfo key="gi" id={state.game_info.id} url={state.game_info.url} />,
+ <ScoreBoard key="sb" results={state.results} />
+ ];
+ }
+
const analysis = analyze_grid(state.grid);
const invalid_cells = get_invalid_cells(state.grid, analysis);
const unconnected_cells = get_unconnected_cells(state.grid, analysis);
state.game_over ? (
<div key="go" className="game-over-banner">
- {state.winner} wins! Game over.
+ Game over! Calculating scores...
</div>
) : null,