diff options
| author | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-11-13 08:37:53 -0800 |
|---|---|---|
| committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-11-13 08:37:53 -0800 |
| commit | bf3e85b69811930d01573ae9fc2703f674c0c951 (patch) | |
| tree | 375b3433e9e0a7d312cad7aac998e188a2fddab3 /core/src/commonTest/kotlin/coffee/liz | |
| parent | f95457986531730095b00e2e894e9bf4d9df7166 (diff) | |
| download | rookery-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/liz')
| -rw-r--r-- | core/src/commonTest/kotlin/coffee/liz/rookery/BldTest.kt | 66 |
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")) + } +} |
