summaryrefslogtreecommitdiff
path: root/core/src/commonTest/kotlin/coffee
diff options
context:
space:
mode:
authorElizabeth Alexander Hunt <me@liz.coffee>2026-11-13 08:37:53 -0800
committerElizabeth Alexander Hunt <me@liz.coffee>2026-11-13 08:37:53 -0800
commitbf3e85b69811930d01573ae9fc2703f674c0c951 (patch)
tree375b3433e9e0a7d312cad7aac998e188a2fddab3 /core/src/commonTest/kotlin/coffee
parentf95457986531730095b00e2e894e9bf4d9df7166 (diff)
downloadrookery-bf3e85b69811930d01573ae9fc2703f674c0c951.tar.gz
rookery-bf3e85b69811930d01573ae9fc2703f674c0c951.zip
Redesign as a liz.coffee document; type-to-steer memo; image search
Look: drop the Win98 chrome for the site's own style: black page, serif text, sage links in [brackets], bordered tables, pink accent, Koch snow, a marquee of colony news, an 88x31 blinkie. A paper theme for daylight. Pixel fonts removed. Timer: drawn seven-segment LCD in a timer body with hands-down/ready lamps, sized to the space it has. Touch starts the timer only on touch devices. Big display mode (F). Pips at 8 and 12 s of inspection. Penguin: follows the pointer as on the site, pecks and sleeps when idle, watches during solves, says a word afterwards. Sprite sizes are now in dp (was raw pixels, hence tiny on hidpi). BLD: - Fix: BLD scrambles end in wide moves; hold the cube by its centres so tracing and checking use the solver's real frame. - Memo trainer: typing a target turns the view to that sticker (eight fixed corner views, animated); arrows step through the memo without showing letters; optional pause for execution; recall pass; results separate tracing slips from memory slips (Tracer.firstMistake). - Password fields show the last typed letter briefly. - Pair cards show where the pair sits on the cube. Sticker drill uses the unfolded cube for "where is it". - Letter pairs live under BLD; BLD setup lives in settings. - Reviews reload when the store changes (other devices' practice). Pictures: Brave image search in the pair editor; chosen images are shrunk to WebP and synced. Key read from the local, unsynced config. Dev: `--shots <dir>` renders every screen offscreen with sample data. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Diffstat (limited to 'core/src/commonTest/kotlin/coffee')
-rw-r--r--core/src/commonTest/kotlin/coffee/liz/rookery/BldTest.kt66
1 files changed, 66 insertions, 0 deletions
diff --git a/core/src/commonTest/kotlin/coffee/liz/rookery/BldTest.kt b/core/src/commonTest/kotlin/coffee/liz/rookery/BldTest.kt
index cae88a1..4c39921 100644
--- a/core/src/commonTest/kotlin/coffee/liz/rookery/BldTest.kt
+++ b/core/src/commonTest/kotlin/coffee/liz/rookery/BldTest.kt
@@ -73,6 +73,58 @@ class BldTest {
assertEquals(listOf("'B' is on the buffer piece"), t.parse("B", PieceType.EDGE).second)
}
+ @Test fun bldScramblesWithWideMovesAreHeldByTheirCentres() {
+ // Official 3BLD scrambles end in random wide moves that move the centres.
+ val rng = Random(11)
+ val setup = BldSetup()
+ val t = Tracer(setup)
+ val faces = listOf("U", "R", "F", "D", "L", "B")
+ val sfx = listOf("", "'", "2")
+ val wide = listOf("", "Rw", "Rw'", "Rw2", "Uw", "Uw'", "Uw2", "Fw", "Fw'")
+ repeat(200) {
+ val scr = (List(20) { faces[rng.nextInt(6)] + sfx[rng.nextInt(3)] } + wide[rng.nextInt(wide.size)] + wide[rng.nextInt(wide.size)])
+ .filter { it.isNotEmpty() }.joinToString(" ")
+ val s = setup.stateOf(scr)
+ assertEquals(setup.solvedColors[4], s[4], "U centre after $scr")
+ assertEquals(setup.solvedColors[22], s[22], "F centre after $scr")
+ val after = t.execute(s, t.trace(s, PieceType.CORNER) + t.trace(s, PieceType.EDGE))
+ assertTrue(t.isSolved(after, PieceType.CORNER) && t.isSolved(after, PieceType.EDGE), scr)
+ }
+ }
+
+ @Test fun firstMistakePinpointsWhereATraceGoesWrong() {
+ val rng = Random(3)
+ val setup = BldSetup()
+ val t = Tracer(setup)
+ val faces = listOf("U", "R", "F", "D", "L", "B"); val sfx = listOf("", "'", "2")
+ repeat(100) {
+ val s = setup.stateOf(List(22) { faces[rng.nextInt(6)] + sfx[rng.nextInt(3)] }.joinToString(" "))
+ for (type in PieceType.entries) {
+ val good = t.trace(s, type)
+ assertEquals(null, t.firstMistake(s, good, type))
+ if (good.size < 2) continue
+ // stopping early is reported at the end
+ assertEquals(good.size - 1, t.firstMistake(s, good.dropLast(1), type)?.index)
+ // (if the buffer piece starts at home, the first target is a free choice: nothing to catch)
+ val buf = setup.buffer(type)
+ if (Cube3.pieceId(type, t.homes(s, type)[buf]) == Cube3.pieceId(type, buf)) continue
+ // swapping the first target for a different piece is caught at index 0, with the right answer
+ val other = good.first { Cube3.pieceId(type, it.pos) != Cube3.pieceId(type, good[0].pos) }
+ val m = t.firstMistake(s, listOf(other) + good.drop(1), type)!!
+ assertEquals(0, m.index)
+ assertEquals(good[0].pos, m.expected)
+ }
+ }
+ }
+
+ @Test fun viewCornerShowsTheWholePiece() {
+ // A is the U sticker of UBL: look from up-back-left. C (UFR): the default front view.
+ assertEquals(Triple(-1, 1, -1), Cube3.viewCorner(Cube3.slotOf(PieceType.CORNER, 0)))
+ assertEquals(Triple(1, 1, 1), Cube3.viewCorner(Cube3.slotOf(PieceType.CORNER, 2)))
+ // UR edge leaves front/back to the current view
+ assertEquals(Triple(1, 1, -1), Cube3.viewCorner(Cube3.slotOf(PieceType.EDGE, 1), Triple(-1, -1, -1)))
+ }
+
@Test fun userFrameHasYellowOnTopAndOrangeInFront() {
val solved = BldSetup().solvedColors
assertEquals(NxnCube.Face.D.ordinal, solved[4]) // U centre shows the yellow (WCA D) colour
@@ -94,3 +146,17 @@ class CubeMappingTest {
}
}
}
+
+class BraveImagesTest {
+ @Test fun parsesResultsAndEncodesQueries() {
+ val body = """{"type":"images","results":[
+ {"title":"a penguin","source":"example.org","thumbnail":{"src":"https://imgs.search.brave.com/t1"},"properties":{"url":"https://example.org/p.jpg"}},
+ {"title":"no props","thumbnail":{"src":"https://imgs.search.brave.com/t2"}},
+ {"title":"insecure","thumbnail":{"src":"http://x/t3"}}]}"""
+ val hits = coffee.liz.rookery.images.BraveImages.parse(body)
+ assertEquals(2, hits.size)
+ assertEquals("https://example.org/p.jpg", hits[0].full)
+ assertEquals(hits[1].thumbnail, hits[1].full)
+ assertTrue(coffee.liz.rookery.images.BraveImages.url("bernie sanders & co").contains("q=bernie%20sanders%20%26%20co"))
+ }
+}