clone

an offline sliding-block puzzle game in gleam and lustre, with an endless ladder of puzzles ranked by search

main
klot.ski / test / klotski / theme_test.gleam
theme_test.gleam 5.8K
1import gleam/list2import gleeunit/should3import klotski/families4import klotski/shading5import klotski/theme67/// Every pool name must resolve to something that exists, and a theme naming8/// only unruled shapes would carry a ruling pool that can never be reached —9/// which is a table lying about what it does.10pub fn every_theme_is_well_formed_test() {11 theme.validate() |> should.equal([])12}1314/// A rung always looks the way it looked. That is the whole reason the look is15/// seeded rather than chosen: the same pair of numbers goes in, so the same16/// backdrop, ground, screen and press come out — today, tomorrow, and on someone17/// else's phone.18pub fn a_rung_looks_the_way_it_looked_test() {19 let t = theme.by_id("macintosh")20 theme.combo_for(t, "classic", 7)21 |> should.equal(theme.combo_for(t, "classic", 7))22}2324/// Mixing the theme into the hash means the same rung is a different25/// combination under a different theme rather than the same arrangement26/// recoloured.27pub fn a_theme_changes_the_combination_not_just_the_colours_test() {28 let macintosh = theme.combo_for(theme.by_id("macintosh"), "classic", 3)29 let night = theme.combo_for(theme.by_id("night"), "classic", 3)30 { macintosh.backdrop != night.backdrop || macintosh.shading != night.shading }31 |> should.be_true32}3334/// The pools are what make a couple of hundred looks out of about twenty35/// written-down numbers. If a ladder only ever drew two of them, there would be36/// no reason to have written them as pools.37pub fn the_ladder_walks_through_the_pools_test() {38 let t = theme.by_id("macintosh")39 let looks =40 rungs(60)41 |> list.map(fn(rung) {42 let combo = theme.combo_for(t, "classic", rung)43 shading.pattern_id(combo.shading)44 <> "/"45 <> shading.ruling_id(combo.ruling)46 <> "/"47 <> combo.ground.name48 })49 { list.length(list.unique(looks)) > 30 } |> should.be_true50}5152fn rungs(count: Int) -> List(Int) {53 case count {54 0 -> []55 _ -> [count, ..rungs(count - 1)]56 }57}5859/// The shapes that take no ruling always report FINE rather than a frequency60/// they quietly ignore, so the sprite cache key agrees with what is on screen.61pub fn a_combo_never_names_a_ruling_its_screen_ignores_test() {62 let t = theme.by_id("paper")63 rungs(80)64 |> list.all(fn(rung) {65 let combo = theme.combo_for(t, "mixed", rung)66 combo.ruling == shading.effective_ruling(combo.shading, combo.ruling)67 })68 |> should.be_true69}7071/// A pinned shape beats the roll — that setting is the escape hatch for anyone72/// who wants one texture and means it. The ruling is not pinned, because it is73/// the press this board was printed on rather than a preference.74pub fn pinning_a_shape_leaves_the_press_to_the_board_test() {75 let t = theme.by_id("macintosh")76 let rolled = theme.combo_for(t, "classic", 5)77 let pinned = theme.apply_pin(rolled, Ok(shading.Lines))7879 pinned.shading |> should.equal(shading.Lines)80 pinned.backdrop |> should.equal(rolled.backdrop)81 pinned.ground |> should.equal(rolled.ground)82 pinned.ruling83 |> should.equal(shading.effective_ruling(shading.Lines, rolled.ruling))8485 // Pinning a shape that takes no ruling reports the one it can print at.86 theme.apply_pin(rolled, Ok(shading.Solid)).ruling87 |> should.equal(shading.Fine)88}8990/// Which of these is a dark screen is not a flag anybody has to remember to set:91/// it is what the paper is. Swapping ink for paper is all "night" ever was.92pub fn darkness_is_what_the_paper_is_test() {93 theme.is_dark(theme.by_id("night")) |> should.be_true94 theme.is_dark(theme.by_id("amoled")) |> should.be_true95 theme.is_dark(theme.by_id("macintosh")) |> should.be_false96 theme.is_dark(theme.by_id("paper")) |> should.be_false97}9899/// AUTO is stored as AUTO and resolved live, rather than being written down as100/// whichever theme the system wanted at the moment it was chosen — otherwise a101/// phone that goes dark at sunset finds the app remembered a decision the person102/// never made. It resolves to the house pair, not to the deliberate extremes.103pub fn auto_resolves_to_the_house_pair_test() {104 theme.resolve("auto", False) |> should.equal("macintosh")105 theme.resolve("auto", True) |> should.equal("night")106 theme.resolve("amoled", False) |> should.equal("amoled")107 theme.resolve("nonsense", True) |> should.equal("macintosh")108}109110/// The second plate is the same hue at full strength: driven to the primary the111/// colour is already mostly made of, with the other two channels pulled back. It112/// has to stay recognisably the same piece — the 2x2 stays the red one.113pub fn the_second_plate_keeps_the_hue_test() {114 let plate = theme.plate_two(theme.Rgb(0xD1, 0x4A, 0x42))115 plate.r |> should.equal(255)116 { plate.g < 0x4A && plate.b < 0x42 } |> should.be_true117}118119/// Piece colours are deliberately *not* in a pool. The 2x2 is Cao Cao, and a120/// goal block that changes colour between rungs stops being a thing you121/// recognise and starts being a thing you have to re-read.122pub fn the_goal_block_holds_its_colour_test() {123 let t = theme.by_id("macintosh")124 theme.piece_colour(t, 4)125 |> should.equal(Ok(theme.Rgb(0xD1, 0x4A, 0x42)))126 // The empty class is never reprinted.127 theme.piece_colour(t, 0) |> should.be_error128}129130/// The maker's mark belongs to the day rather than to a board: it is the same131/// mark on every screen it appears on, all day, and it moves on tomorrow.132pub fn the_makers_mark_belongs_to_the_day_test() {133 let t = theme.by_id("macintosh")134 theme.logo_ink(t, 120) |> should.equal(theme.logo_ink(t, 120))135 list.contains(t.pieces, theme.logo_ink(t, 120)) |> should.be_true136}137138/// Every family has to be able to carry a look, which means every id the seed is139/// drawn on has to reach a real pool.140pub fn every_family_can_be_printed_test() {141 families.all()142 |> list.all(fn(family) {143 let combo = theme.combo_for(theme.by_id("amoled"), family.id, 1)144 list.contains(theme.by_id("amoled").grounds, combo.ground)145 })146 |> should.be_true147}