1import gleeunit/should2import klotski/walk34/// A unit move changes the distance by exactly -1, 0 or +1, so a finished run5/// decomposes without any interpretation at all. Two identities fall straight6/// out, and they are what makes the numbers on the record screen honest rather7/// than decorative:8///9/// descents = optimal + setbacks10/// steps = optimal + 2*setbacks + idle11pub fn the_arithmetic_of_a_run_closes_test() {12 // Four down, one back, one re-walked, one idle, three down.13 let stats = walk.trace_stats([6, 5, 4, 3, 2, 3, 2, 2, 1, 0])14 stats.steps |> should.equal(9)15 stats.optimal |> should.equal(6)16 stats.setbacks |> should.equal(1)17 stats.idle |> should.equal(1)18 stats.descents |> should.equal(7)19 stats.descents |> should.equal(stats.optimal + stats.setbacks)20 stats.steps21 |> should.equal(stats.optimal + 2 * stats.setbacks + stats.idle)22 // `wasted` is not a residual anybody has to trust.23 stats.wasted |> should.equal(3)24}2526/// A detour is one excursion above the closest you had already been, from27/// leaving that mark to regaining it. It is not the same number as `setbacks`28/// and the difference is the whole reason to count both: two setbacks in a row29/// are one detour two deep, and on the chart they are one bump, not two.30pub fn two_setbacks_in_a_row_are_one_detour_two_deep_test() {31 let stats = walk.trace_stats([5, 4, 5, 6, 5, 4, 3, 2, 1, 0])32 stats.setbacks |> should.equal(2)33 stats.detours |> should.equal(1)34 stats.worst_detour |> should.equal(2)35}3637pub fn separate_stumbles_are_separate_detours_test() {38 let stats = walk.trace_stats([5, 4, 5, 4, 3, 4, 3, 2, 1, 0])39 stats.setbacks |> should.equal(2)40 stats.detours |> should.equal(2)41 stats.worst_detour |> should.equal(1)42}4344/// The mark moves before the depth is measured against it, so a step that sets a45/// new closest is zero deep rather than deep by however far it improved.46pub fn a_new_closest_is_never_a_detour_test() {47 let stats = walk.trace_stats([4, 3, 2, 1, 0])48 stats.detours |> should.equal(0)49 stats.worst_detour |> should.equal(0)50 stats.longest_run |> should.equal(4)51 stats.first_setback |> should.equal(-1)52}5354/// The longest unbroken descent, which a setback resets and an idle move breaks55/// just as surely.56pub fn a_run_of_descents_is_broken_by_anything_else_test() {57 let stats = walk.trace_stats([8, 7, 6, 5, 5, 4, 3, 2, 1, 0])58 stats.longest_run |> should.equal(5)59 stats.idle |> should.equal(1)60 stats.first_setback |> should.equal(-1)61}6263pub fn the_first_setback_is_the_step_it_happened_on_test() {64 walk.trace_stats([3, 2, 3, 2, 1, 0]).first_setback |> should.equal(2)65}6667/// A board opened and never moved is a run of nothing, not a run of one.68pub fn an_untouched_board_has_no_run_test() {69 let stats = walk.trace_stats([12])70 stats.steps |> should.equal(0)71 stats.optimal |> should.equal(12)72 stats.detours |> should.equal(0)7374 walk.trace_stats([]).optimal |> should.equal(0)75}