Katan: highlight rolled hexes, one-button Build menu on small screens, clearer harbours
CI / Build and check (pull_request) Failing after 2m39s
CI / Deploy to Cloudflare Pages (pull_request) Skipped

- Hexes that paid out for the turn's roll are lit (not on a 7, not the robber's hex, not before the roll).
- Compact layouts fold Road/Settlement/City into one Build button with a menu.
- Harbour markers are drawn with a resource pictogram (? for 3:1), larger, and sit a full hex radius off the coast so the piers show which vertices they serve.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018njn2eMPAYqAoE49hGRg2X
This commit is contained in:
Claude
2026-10-15 17:40:48 +00:00
parent ef69704e00
commit b90058afda
8 changed files with 226 additions and 11 deletions
@@ -111,7 +111,7 @@ class CatanGeometryTest {
val anchor = layout.portAnchor(e)
val mid = layout.edgeMidpoint(e)
assertTrue("harbour on edge $e should be past the coast", dist(anchor, centre) > dist(mid, centre))
assertTrue("and not floating far from it", abs(dist(anchor, mid) - layout.radius * 0.62f) < 1f)
assertTrue("and not floating far from it", abs(dist(anchor, mid) - layout.radius * PORT_OFFSET) < 1f)
}
}
}
@@ -322,4 +322,27 @@ class CatanInteractionTest {
val short = pending.withHand(1, hand())
assertEquals(false to true, CatanInteraction.tradeResponses(CatanRules.legalMoves(short, 1)))
}
// --- Rolled-hex highlight --------------------------------------------------
@Test
fun `rolled hexes are those carrying the number, minus the robber's`() {
val base = playing(turn = 0)
val number = base.numbers.first { it != 0 && it != 7 }
val state = base.copy(lastDice = if (number <= 7) listOf(number - 1, 1) else listOf(6, number - 6))
val all = (0 until CatanBoard.HEX_COUNT).filter { state.numbers[it] == number }
assertTrue(all.isNotEmpty())
assertEquals(all.filter { it != state.robber }.toSet(), CatanInteraction.rolledHexes(state))
val blocked = state.copy(robber = all.first())
assertFalse(all.first() in CatanInteraction.rolledHexes(blocked))
}
@Test
fun `nothing is highlighted before this turn's roll, on a seven, or in setup`() {
val rolled = playing(turn = 0).copy(lastDice = listOf(3, 3))
assertTrue(CatanInteraction.rolledHexes(rolled.copy(phase = CatanPhase.AWAIT_ROLL)).isEmpty())
assertTrue(CatanInteraction.rolledHexes(rolled.copy(lastDice = listOf(3, 4))).isEmpty())
assertTrue(CatanInteraction.rolledHexes(rolled.copy(phase = CatanPhase.SETUP_FIRST)).isEmpty())
assertTrue(CatanInteraction.rolledHexes(rolled.copy(lastDice = emptyList())).isEmpty())
}
}