fertig-classic-games/src/games/triominoes/TriominoesData.js

98 lines
3.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Tri-Ominoes — static data and triangular-grid geometry. No Phaser, no state.
//
// The board is a triangular grid. Each cell is an equilateral triangle keyed by
// (r, c). A cell "points up" when (r + c) is even and "points down" otherwise.
// Up and down triangles interlock to tile the plane.
//
// Every cell owns three CORNER VERTICES, listed clockwise (screen coords, y
// down). Vertices are shared between neighbouring cells, so a vertex's number is
// shared by every tile that touches it — which is exactly the Tri-Ominoes
// matching rule (touching corners must agree). We identify a vertex by lattice
// coords (vr, vx): horizontal line index vr and half-column vx.
//
// up (r,c): top=(r, c+1) bottom-right=(r+1, c+2) bottom-left=(r+1, c)
// down (r,c): top-left=(r,c) top-right=(r, c+2) bottom=(r+1, c+1)
//
// Both windings are clockwise, so the three placement rotations of a tile are
// just cyclic shifts of its corner triple — reflections (which a physical tile
// can't do) never sneak in.
export const VALUE_MAX = 5; // double-five set: corner pips 0..5
// All 56 distinct triangles (multisets of 3 values 0..5). The stored triple is
// sorted ascending and treated as the tile's CLOCKWISE corner order.
export function buildTileSet() {
const tiles = [];
let id = 0;
for (let a = 0; a <= VALUE_MAX; a++) {
for (let b = a; b <= VALUE_MAX; b++) {
for (let c = b; c <= VALUE_MAX; c++) {
tiles.push({ id: id++, v: [a, b, c] });
}
}
}
return tiles; // 56 tiles
}
export const tileValue = (t) => t.v[0] + t.v[1] + t.v[2];
export const isTripleTile = (t) => t.v[0] === t.v[1] && t.v[1] === t.v[2];
// Standard hand sizes: 2 players draw 9, 34 draw 7.
export function handSizeFor(n) {
return n === 2 ? 9 : 7;
}
// ── Geometry ─────────────────────────────────────────────────────────────────
export function isUp(r, c) {
return (((r + c) % 2) + 2) % 2 === 0;
}
export const cellKey = (r, c) => `${r},${c}`;
export const vertKey = (vr, vx) => `${vr},${vx}`;
// Three corner vertices in clockwise order. Order matters: a placed tile maps
// corner i ← tileTriple[(i + rot) % 3].
export function cellVertices(r, c) {
if (isUp(r, c)) {
return [
[r, c + 1], // top
[r + 1, c + 2], // bottom-right
[r + 1, c], // bottom-left
];
}
return [
[r, c], // top-left
[r, c + 2], // top-right
[r + 1, c + 1], // bottom
];
}
// The three edge-adjacent cells. The slanted-edge neighbours sit in the same
// row (c±1); the flat-edge neighbour is one row away depending on orientation.
export function neighborCells(r, c) {
return isUp(r, c)
? [[r, c - 1], [r, c + 1], [r + 1, c]]
: [[r, c - 1], [r, c + 1], [r - 1, c]];
}
// ── Pixel mapping ─────────────────────────────────────────────────────────────
// A vertex (vr, vx) maps to a board-space pixel. HALF_W is half the triangle
// base; row height keeps the triangles equilateral.
export const HALF_W = 46;
export const ROW_H = Math.round(HALF_W * 2 * 0.8660254); // ≈ 80
export function vertPixel(vr, vx) {
return { x: vx * HALF_W, y: vr * ROW_H };
}
export function cellCentroid(r, c) {
const vs = cellVertices(r, c);
let x = 0, y = 0;
for (const [vr, vx] of vs) { const p = vertPixel(vr, vx); x += p.x; y += p.y; }
return { x: x / 3, y: y / 3 };
}
// ── Players ─────────────────────────────────────────────────────────────────
export const PLAYER_COLORS = [0xc8a84b, 0x4a90d9, 0x49a25a, 0xd0473a];
export const PLAYER_COLOR_HEX = ['#c8a84b', '#4a90d9', '#49a25a', '#d0473a'];