dice (Dice)

test dice_dice: prelude
///- --test static

open testing
open sm'_operators

sixth_power_sequence

inl sixth_power_sequence () =
    stream.iterate_map ((*) 6) (fun x => if x <= 0 then None else Some x) 1
test sixth_power_sequence: passed
///- --test

sixth_power_sequence ()
|> stream.take_while (fun _ i => i <= 7i32)
|> stream.to_list
|> _assert_eq [ 1u64; 6; 36; 216; 1296; 7776; 46656; 279936 ]
__assert_eq { expected = Cons(1, Cons(6, Cons(36, Cons(216, Cons(1296, Cons(7776, Cons(46656, Cons(279936, Nil)))))))) }

accumulate_dice_rolls

inl accumulate_dice_rolls
    (rolls : list u8)
    (power : i8)
    acc
    =
    inl rec body forall a {number} b {number} c {number}.
        (acc : a)
        (power : b)
        (rolls : list c)
        : option (a * list c)
        =
        match rolls with
        | _ when power < 0 =>
            inl result = acc + 1
            trace Debug
                fun () => "dice.accumulate_dice_rolls"
                fun () => { power acc result }
            Some (result, rolls)
        | [] => None
        | roll :: rest when roll > 1 =>
            inl coeff = sixth_power_sequence () |> stream.item power
            inl value = conv (roll - 1) * coeff
            trace Debug
                fun () => "dice.accumulate_dice_rolls"
                fun () => { power acc roll value }
            루프 (acc + value) (power - 1) rest
        | roll :: rest =>
            trace Debug
                fun () => "dice.accumulate_dice_rolls"
                fun () => { power acc roll }
            루프 acc (power - 1) rest
    and inl 루프 forall a {number} b {number} c {number}.
        (acc : a)
        (power : b)
        (rolls : list c)
        : option (a * list c)
        =
        if var_is rolls |> not
        then body acc power rolls
        else
            inl acc = dyn acc
            join body acc power rolls
    루프 acc power rolls
test accumulate_dice_rolls: passed
///- --test

accumulate_dice_rolls [ 6; 5; 4; 3; 2 ] 0 1000i32
|> _assert_eq (Some (1006, [ 5; 4; 3; 2 ]))
02:27:26 1d dice.accumulate_dice_rolls / { power = 0; acc = 1000; roll = 6; value = 5 }
02:27:26 2d dice.accumulate_dice_rolls / { power = -1; acc = 1005; result = 1006 }
__assert_eq { expected = Some(1006, Cons(5, Cons(4, Cons(3, Cons(2, Nil))))) }
test accumulate_dice_rolls_2: passed
///- --test

accumulate_dice_rolls [ 6; 5; 4; 3; 2 ] 1 1000i32
|> _assert_eq (Some (1035, [ 4; 3; 2 ]))
02:27:25 1d dice.accumulate_dice_rolls / { power = 1; acc = 1000; roll = 6; value = 30 }
02:27:25 2d dice.accumulate_dice_rolls / { power = 0; acc = 1030; roll = 5; value = 4 }
02:27:25 3d dice.accumulate_dice_rolls / { power = -1; acc = 1034; result = 1035 }
__assert_eq { expected = Some(1035, Cons(4, Cons(3, Cons(2, Nil)))) }
test accumulate_dice_rolls_3: passed
///- --test

accumulate_dice_rolls [ 6; 5; 4; 3; 2 ] 2 1000i32
|> _assert_eq (Some (1208, [ 3; 2 ]))
02:27:26 1d dice.accumulate_dice_rolls / { power = 2; acc = 1000; roll = 6; value = 180 }
02:27:26 2d dice.accumulate_dice_rolls / { power = 1; acc = 1180; roll = 5; value = 24 }
02:27:26 3d dice.accumulate_dice_rolls / { power = 0; acc = 1204; roll = 4; value = 3 }
02:27:26 4d dice.accumulate_dice_rolls / { power = -1; acc = 1207; result = 1208 }
__assert_eq { expected = Some(1208, Cons(3, Cons(2, Nil))) }

roll_within_bounds

inl roll_within_bounds max rolls =
    inl power = listm.length rolls - 1
    match accumulate_dice_rolls rolls power 0 with
    | Some (result, _) when result >= 1 && result <= max => Some result
    | _ => None
test roll_within_bounds: passed
///- --test

roll_within_bounds 2000i32 [ 1; 5; 4; 4; 5 ]
|> _assert_eq (Some 995)
02:27:29 1d dice.accumulate_dice_rolls / { power = 4; acc = 0; roll = 1 }
02:27:29 2d dice.accumulate_dice_rolls / { power = 3; acc = 0; roll = 5; value = 864 }
02:27:29 3d dice.accumulate_dice_rolls / { power = 2; acc = 864; roll = 4; value = 108 }
02:27:29 4d dice.accumulate_dice_rolls / { power = 1; acc = 972; roll = 4; value = 18 }
02:27:29 5d dice.accumulate_dice_rolls / { power = 0; acc = 990; roll = 5; value = 4 }
02:27:29 6d dice.accumulate_dice_rolls / { power = -1; acc = 994; result = 995 }
__assert_eq { expected = Some(995) }
test roll_within_bounds_2: passed
///- --test

roll_within_bounds 2000i32 [ 2; 2; 6; 4; 5 ]
|> _assert_eq (Some 1715)
02:27:29 1d dice.accumulate_dice_rolls / { power = 4; acc = 0; roll = 2; value = 1296 }
02:27:29 2d dice.accumulate_dice_rolls / { power = 3; acc = 1296; roll = 2; value = 216 }
02:27:29 3d dice.accumulate_dice_rolls / { power = 2; acc = 1512; roll = 6; value = 180 }
02:27:29 4d dice.accumulate_dice_rolls / { power = 1; acc = 1692; roll = 4; value = 18 }
02:27:29 5d dice.accumulate_dice_rolls / { power = 0; acc = 1710; roll = 5; value = 4 }
02:27:29 6d dice.accumulate_dice_rolls / { power = -1; acc = 1714; result = 1715 }
__assert_eq { expected = Some(1715) }
test roll_within_bounds_3: passed
///- --test

roll_within_bounds 2000i32 [ 4; 1; 1; 2; 3 ]
|> _assert_eq None
02:27:31 1d dice.accumulate_dice_rolls / { power = 4; acc = 0; roll = 4; value = 3888 }
02:27:31 2d dice.accumulate_dice_rolls / { power = 3; acc = 3888; roll = 1 }
02:27:31 3d dice.accumulate_dice_rolls / { power = 2; acc = 3888; roll = 1 }
02:27:31 4d dice.accumulate_dice_rolls / { power = 1; acc = 3888; roll = 2; value = 6 }
02:27:31 5d dice.accumulate_dice_rolls / { power = 0; acc = 3894; roll = 3; value = 2 }
02:27:31 6d dice.accumulate_dice_rolls / { power = -1; acc = 3896; result = 3897 }
__assert_eq { expected = None }

calculate_dice_count

inl calculate_dice_count forall a {number} b {number}. (max : a) : b =
    inl rec body forall t {number}. (n : t) (p : a) : t =
        inl return () =
            trace Debug
                fun () => "dice.calculate_dice_count"
                fun () => { max p n }
            n
        if p < max then
            if p > (limit.max : a) / 6
            then failwith ("dice.calculate_dice_count / max: " ++# #?max ++# " is above the largest supported bound " ++# #?p)
            else 루프 (n + 1) (p * 6)
        else return ()
    and inl 루프 forall t {number}. (n : t) (p : a) : t =
        if var_is max |> not
        then body n p
        else
            inl n = dyn n
            inl p = dyn p
            join body n p
    if max = 1
    then 1
    else 루프 0 1
test calculate_dice_count: passed
///- --test

calculate_dice_count 36i32
|> _assert_eq 2i32
02:27:31 1d dice.calculate_dice_count / { max = 36; p = 36; n = 2 }
__assert_eq { expected = 2 }
test calculate_dice_count_2: passed
///- --test

calculate_dice_count 7777i32
|> _assert_eq 6i32
02:27:32 1d dice.calculate_dice_count / { max = 7777; p = 46656; n = 6 }
__assert_eq { expected = 6 }
test calculate_dice_count_3: passed
///- --test

calculate_dice_count 4738381338321616896u64
|> _assert_eq 24i32
02:27:33 1d dice.calculate_dice_count / { max = 4738381338321616896; p = 4738381338321616896; n = 24 }
__assert_eq { expected = 24 }
test calculate_dice_count_4: passed
///- --test

calculate_dice_count 362797056i32
|> _assert_eq 11i32
02:27:34 1d dice.calculate_dice_count / { max = 362797056; p = 362797056; n = 11 }
__assert_eq { expected = 11 }
test calculate_dice_count_5: passed
///- --test
///> rust

fun () =>
    (calculate_dice_count 4738381338321616897u64 : i32)
    |> ignore
|> _throws
|> optionm.map (sm'.format_exception >> sm'.contains "is above the largest supported bound 4738381338321616896")
|> _assert_eq (Some true)
__assert_eq { expected = Some(true) }
0
test calculate_dice_count_6: passed
///- --test
///> rust

fun () =>
    (calculate_dice_count (limit.max : i64) : i32)
    |> ignore
|> _throws
|> optionm.map (sm'.format_exception >> sm'.contains "is above the largest supported bound 4738381338321616896")
|> _assert_eq (Some true)
__assert_eq { expected = Some(true) }
0
test calculate_dice_count_7: passed
///- --test
///> rust

fun () =>
    (calculate_dice_count 362797057i32 : i32)
    |> ignore
|> _throws
|> optionm.map (sm'.format_exception >> sm'.contains "is above the largest supported bound 362797056")
|> _assert_eq (Some true)
__assert_eq { expected = Some(true) }
0

roll_dice

let roll_dice () : u8 =
    random' 1 7

rotate_number

inl rotate_number forall t {number}. (max : i64) (n : t) : t =
    (conv n - 1 + max) % max + 1 |> conv

rotate_numbers

inl rotate_numbers max items =
    items |> stream.map (rotate_number max)
test rotate_numbers: passed
///- --test

listm'.init_series -1 14 1i32
|> stream.from_list
|> rotate_numbers 6
|> stream.to_list
|> _assert_eq [ 5; 6; 1; 2; 3; 4; 5; 6; 1; 2; 3; 4; 5; 6; 1; 2 ]
__assert_eq { expected = Cons(5, Cons(6, Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Cons(6, Cons(1, Cons(2, Nil)))))))))))))))) }

create_sequential_roller

inl create_sequential_roller list =
    inl s = list |> listm.rev |> listm.append list |> stream.from_list |> stream.memoize
    inl current_index = mut 0i64
    inl acc = mut 1
    inl len = mut -1
    inl last_item = mut None
    let rec 루프 () =
        fun () =>
            inl current_index = *current_index
            inl acc = *acc
            inl len = *len
            inl last_item = *last_item |> optionm'.box
            trace Debug
                fun () => "dice.create_sequential_roller / roll"
                fun () => { current_index acc len last_item = #?*last_item }
        |> fun x => x ()
        match s () |> stream.try_item *current_index with
        | Some item =>
            current_index <- *current_index + 1
            last_item <- Some item
            item
        | None =>
            trace Debug (fun () => "dice.create_sequential_roller / roll / None") id
            if *len = -1
            then len <- *current_index
            acc <-
                if *acc >= *len
                then 1
                else *acc + 1
            current_index <- *acc - 1
            last_item <- None
            루프 ()
    루프
test create_sequential_roller: passed
///- --test

inl sequential_roll = create_sequential_roller [ 1i32; 2; 3; 4 ]

arraym.init 50i32 (ignore >> sequential_roll)
|> _assert_eq (;[ 1; 2; 3; 4; 4; 3; 2; 1; 2; 3; 4; 4; 3; 2; 1; 3; 4; 4; 3; 2; 1; 4; 4; 3; 2; 1;
4; 3; 2; 1; 3; 2; 1; 2; 1; 1; 1; 2; 3; 4; 4; 3; 2; 1; 2; 3; 4; 4; 3; 2 ] : array i32)
02:27:37 1d dice.create_sequential_roller / roll / { current_index = 0; acc = 1; len = -1; last_item = null }
02:27:37 2d dice.create_sequential_roller / roll / { current_index = 1; acc = 1; len = -1; last_item = 1 }
02:27:37 3d dice.create_sequential_roller / roll / { current_index = 2; acc = 1; len = -1; last_item = 2 }
02:27:37 4d dice.create_sequential_roller / roll / { current_index = 3; acc = 1; len = -1; last_item = 3 }
02:27:37 5d dice.create_sequential_roller / roll / { current_index = 4; acc = 1; len = -1; last_item = 4 }
02:27:37 6d dice.create_sequential_roller / roll / { current_index = 5; acc = 1; len = -1; last_item = 4 }
02:27:37 7d dice.create_sequential_roller / roll / { current_index = 6; acc = 1; len = -1; last_item = 3 }
02:27:37 8d dice.create_sequential_roller / roll / { current_index = 7; acc = 1; len = -1; last_item = 2 }
02:27:37 9d dice.create_sequential_roller / roll / { current_index = 8; acc = 1; len = -1; last_item = 1 }
02:27:37 10d dice.create_sequential_roller / roll / None
02:27:37 11d dice.create_sequential_roller / roll / { current_index = 1; acc = 2; len = 8; last_item = null }
02:27:37 12d dice.create_sequential_roller / roll / { current_index = 2; acc = 2; len = 8; last_item = 2 }
02:27:37 13d dice.create_sequential_roller / roll / { current_index = 3; acc = 2; len = 8; last_item = 3 }
02:27:37 14d dice.create_sequential_roller / roll / { current_index = 4; acc = 2; len = 8; last_item = 4 }
02:27:37 15d dice.create_sequential_roller / roll / { current_index = 5; acc = 2; len = 8; last_item = 4 }
02:27:37 16d dice.create_sequential_roller / roll / { current_index = 6; acc = 2; len = 8; last_item = 3 }
02:27:37 17d dice.create_sequential_roller / roll / { current_index = 7; acc = 2; len = 8; last_item = 2 }
02:27:37 18d dice.create_sequential_roller / roll / { current_index = 8; acc = 2; len = 8; last_item = 1 }
02:27:37 19d dice.create_sequential_roller / roll / None
02:27:37 20d dice.create_sequential_roller / roll / { current_index = 2; acc = 3; len = 8; last_item = null }
02:27:37 21d dice.create_sequential_roller / roll / { current_index = 3; acc = 3; len = 8; last_item = 3 }
02:27:37 22d dice.create_sequential_roller / roll / { current_index = 4; acc = 3; len = 8; last_item = 4 }
02:27:37 23d dice.create_sequential_roller / roll / { current_index = 5; acc = 3; len = 8; last_item = 4 }
02:27:37 24d dice.create_sequential_roller / roll / { current_index = 6; acc = 3; len = 8; last_item = 3 }
02:27:37 25d dice.create_sequential_roller / roll / { current_index = 7; acc = 3; len = 8; last_item = 2 }
02:27:37 26d dice.create_sequential_roller / roll / { current_index = 8; acc = 3; len = 8; last_item = 1 }
02:27:37 27d dice.create_sequential_roller / roll / None
02:27:37 28d dice.create_sequential_roller / roll / { current_index = 3; acc = 4; len = 8; last_item = null }
02:27:37 29d dice.create_sequential_roller / roll / { current_index = 4; acc = 4; len = 8; last_item = 4 }
02:27:37 30d dice.create_sequential_roller / roll / { current_index = 5; acc = 4; len = 8; last_item = 4 }
02:27:37 31d dice.create_sequential_roller / roll / { current_index = 6; acc = 4; len = 8; last_item = 3 }
02:27:37 32d dice.create_sequential_roller / roll / { current_index = 7; acc = 4; len = 8; last_item = 2 }
02:27:37 33d dice.create_sequential_roller / roll / { current_index = 8; acc = 4; len = 8; last_item = 1 }
02:27:37 34d dice.create_sequential_roller / roll / None
02:27:37 35d dice.create_sequential_roller / roll / { current_index = 4; acc = 5; len = 8; last_item = null }
02:27:37 36d dice.create_sequential_roller / roll / { current_index = 5; acc = 5; len = 8; last_item = 4 }
02:27:37 37d dice.create_sequential_roller / roll / { current_index = 6; acc = 5; len = 8; last_item = 3 }
02:27:37 38d dice.create_sequential_roller / roll / { current_index = 7; acc = 5; len = 8; last_item = 2 }
02:27:37 39d dice.create_sequential_roller / roll / { current_index = 8; acc = 5; len = 8; last_item = 1 }
02:27:37 40d dice.create_sequential_roller / roll / None
02:27:37 41d dice.create_sequential_roller / roll / { current_index = 5; acc = 6; len = 8; last_item = null }
02:27:37 42d dice.create_sequential_roller / roll / { current_index = 6; acc = 6; len = 8; last_item = 3 }
02:27:37 43d dice.create_sequential_roller / roll / { current_index = 7; acc = 6; len = 8; last_item = 2 }
02:27:37 44d dice.create_sequential_roller / roll / { current_index = 8; acc = 6; len = 8; last_item = 1 }
02:27:37 45d dice.create_sequential_roller / roll / None
02:27:37 46d dice.create_sequential_roller / roll / { current_index = 6; acc = 7; len = 8; last_item = null }
02:27:37 47d dice.create_sequential_roller / roll / { current_index = 7; acc = 7; len = 8; last_item = 2 }
02:27:37 48d dice.create_sequential_roller / roll / { current_index = 8; acc = 7; len = 8; last_item = 1 }
02:27:37 49d dice.create_sequential_roller / roll / None
02:27:37 50d dice.create_sequential_roller / roll / { current_index = 7; acc = 8; len = 8; last_item = null }
02:27:37 51d dice.create_sequential_roller / roll / { current_index = 8; acc = 8; len = 8; last_item = 1 }
02:27:37 52d dice.create_sequential_roller / roll / None
02:27:37 53d dice.create_sequential_roller / roll / { current_index = 0; acc = 1; len = 8; last_item = null }
02:27:37 54d dice.create_sequential_roller / roll / { current_index = 1; acc = 1; len = 8; last_item = 1 }
02:27:37 55d dice.create_sequential_roller / roll / { current_index = 2; acc = 1; len = 8; last_item = 2 }
02:27:37 56d dice.create_sequential_roller / roll / { current_index = 3; acc = 1; len = 8; last_item = 3 }
02:27:37 57d dice.create_sequential_roller / roll / { current_index = 4; acc = 1; len = 8; last_item = 4 }
02:27:37 58d dice.create_sequential_roller / roll / { current_index = 5; acc = 1; len = 8; last_item = 4 }
02:27:37 59d dice.create_sequential_roller / roll / { current_index = 6; acc = 1; len = 8; last_item = 3 }
02:27:37 60d dice.create_sequential_roller / roll / { current_index = 7; acc = 1; len = 8; last_item = 2 }
02:27:37 61d dice.create_sequential_roller / roll / { current_index = 8; acc = 1; len = 8; last_item = 1 }
02:27:37 62d dice.create_sequential_roller / roll / None
02:27:37 63d dice.create_sequential_roller / roll / { current_index = 1; acc = 2; len = 8; last_item = null }
02:27:37 64d dice.create_sequential_roller / roll / { current_index = 2; acc = 2; len = 8; last_item = 2 }
02:27:37 65d dice.create_sequential_roller / roll / { current_index = 3; acc = 2; len = 8; last_item = 3 }
02:27:37 66d dice.create_sequential_roller / roll / { current_index = 4; acc = 2; len = 8; last_item = 4 }
02:27:37 67d dice.create_sequential_roller / roll / { current_index = 5; acc = 2; len = 8; last_item = 4 }
02:27:37 68d dice.create_sequential_roller / roll / { current_index = 6; acc = 2; len = 8; last_item = 3 }
__assert_eq { expected = [1, 2, 3, 4, 4, 3, 2, 1, 2, 3, 4, 4, 3, 2, 1, 3, 4, 4, 3, 2, 1, 4, 4, 3, 2, 1, 4, 3, 2, 1, 3, 2, 1, 2, 1, 1, 1, 2, 3, 4, 4, 3, 2, 1, 2, 3, 4, 4, 3, 2] }

roll_progressively

inl roll_progressively roll reroll max =
    inl power = (calculate_dice_count max) - 1
    let rec 루프 rolls size =
        if size < power + 1
        then 루프 (roll () :: rolls) (size + 1)
        else accumulate_dice_rolls rolls power 0 |> function
            | Some (result, _) when result <= max => result
            | _ when reroll => 루프 (listm.init power (fun _ => roll ())) power
            | _ => 루프 (roll () :: rolls) (size + 1)
    루프 [] 0
test roll_progressively: passed
///- --test

roll_progressively roll_dice false 1
|> _assert_eq 1i32
02:27:37 1d dice.accumulate_dice_rolls / { power = 0; acc = 0; roll = 3; value = 2 }
02:27:37 2d dice.accumulate_dice_rolls / { power = -1; acc = 2; result = 3 }
02:27:37 3d dice.accumulate_dice_rolls / { power = 0; acc = 0; roll = 3; value = 2 }
02:27:37 4d dice.accumulate_dice_rolls / { power = -1; acc = 2; result = 3 }
02:27:37 5d dice.accumulate_dice_rolls / { power = 0; acc = 0; roll = 4; value = 3 }
02:27:37 6d dice.accumulate_dice_rolls / { power = -1; acc = 3; result = 4 }
02:27:37 7d dice.accumulate_dice_rolls / { power = 0; acc = 0; roll = 5; value = 4 }
02:27:37 8d dice.accumulate_dice_rolls / { power = -1; acc = 4; result = 5 }
02:27:37 9d dice.accumulate_dice_rolls / { power = 0; acc = 0; roll = 1 }
02:27:37 10d dice.accumulate_dice_rolls / { power = -1; acc = 0; result = 1 }
__assert_eq { expected = 1 }
test roll_progressively_2: passed
///- --test

inl sequential_roll = create_sequential_roller [ 5; 4; 4; 5; 1 ]

roll_progressively sequential_roll false 2000i32
|> _assert_eq 995
02:27:38 1d dice.calculate_dice_count / { max = 2000; p = 7776; n = 5 }
02:27:38 2d dice.create_sequential_roller / roll / { current_index = 0; acc = 1; len = -1; last_item = null }
02:27:38 3d dice.create_sequential_roller / roll / { current_index = 1; acc = 1; len = -1; last_item = 5 }
02:27:38 4d dice.create_sequential_roller / roll / { current_index = 2; acc = 1; len = -1; last_item = 4 }
02:27:38 5d dice.create_sequential_roller / roll / { current_index = 3; acc = 1; len = -1; last_item = 4 }
02:27:38 6d dice.create_sequential_roller / roll / { current_index = 4; acc = 1; len = -1; last_item = 5 }
02:27:38 7d dice.accumulate_dice_rolls / { power = 4; acc = 0; roll = 1 }
02:27:38 8d dice.accumulate_dice_rolls / { power = 3; acc = 0; roll = 5; value = 864 }
02:27:38 9d dice.accumulate_dice_rolls / { power = 2; acc = 864; roll = 4; value = 108 }
02:27:38 10d dice.accumulate_dice_rolls / { power = 1; acc = 972; roll = 4; value = 18 }
02:27:38 11d dice.accumulate_dice_rolls / { power = 0; acc = 990; roll = 5; value = 4 }
02:27:38 12d dice.accumulate_dice_rolls / { power = -1; acc = 994; result = 995 }
__assert_eq { expected = 995 }
test roll_progressively_3: passed
///- --test

inl sequential_roll = create_sequential_roller [ 5; 4; 4; 5; 2 ]

roll_progressively sequential_roll false 2000i32
|> _assert_eq 1678
02:27:39 1d dice.calculate_dice_count / { max = 2000; p = 7776; n = 5 }
02:27:39 2d dice.create_sequential_roller / roll / { current_index = 0; acc = 1; len = -1; last_item = null }
02:27:39 3d dice.create_sequential_roller / roll / { current_index = 1; acc = 1; len = -1; last_item = 5 }
02:27:39 4d dice.create_sequential_roller / roll / { current_index = 2; acc = 1; len = -1; last_item = 4 }
02:27:39 5d dice.create_sequential_roller / roll / { current_index = 3; acc = 1; len = -1; last_item = 4 }
02:27:39 6d dice.create_sequential_roller / roll / { current_index = 4; acc = 1; len = -1; last_item = 5 }
02:27:39 7d dice.accumulate_dice_rolls / { power = 4; acc = 0; roll = 2; value = 1296 }
02:27:39 8d dice.accumulate_dice_rolls / { power = 3; acc = 1296; roll = 5; value = 864 }
02:27:39 9d dice.accumulate_dice_rolls / { power = 2; acc = 2160; roll = 4; value = 108 }
02:27:39 10d dice.accumulate_dice_rolls / { power = 1; acc = 2268; roll = 4; value = 18 }
02:27:39 11d dice.accumulate_dice_rolls / { power = 0; acc = 2286; roll = 5; value = 4 }
02:27:39 12d dice.accumulate_dice_rolls / { power = -1; acc = 2290; result = 2291 }
02:27:39 13d dice.create_sequential_roller / roll / { current_index = 5; acc = 1; len = -1; last_item = 2 }
02:27:39 14d dice.accumulate_dice_rolls / { power = 4; acc = 0; roll = 2; value = 1296 }
02:27:39 15d dice.accumulate_dice_rolls / { power = 3; acc = 1296; roll = 2; value = 216 }
02:27:39 16d dice.accumulate_dice_rolls / { power = 2; acc = 1512; roll = 5; value = 144 }
02:27:39 17d dice.accumulate_dice_rolls / { power = 1; acc = 1656; roll = 4; value = 18 }
02:27:39 18d dice.accumulate_dice_rolls / { power = 0; acc = 1674; roll = 4; value = 3 }
02:27:39 19d dice.accumulate_dice_rolls / { power = -1; acc = 1677; result = 1678 }
__assert_eq { expected = 1678 }
test roll_progressively_4: passed
///- --test

inl sequential_roll = create_sequential_roller [ 6u8; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6; 6 ]

roll_progressively sequential_roll false 4738381338321616896u64
|> _assert_eq 4738381338321616896u64
02:27:41 1d dice.calculate_dice_count / { max = 4738381338321616896; p = 4738381338321616896; n = 24 }
02:27:41 2d dice.create_sequential_roller / roll / { current_index = 0; acc = 1; len = -1; last_item = null }
02:27:41 3d dice.create_sequential_roller / roll / { current_index = 1; acc = 1; len = -1; last_item = 6 }
02:27:41 4d dice.create_sequential_roller / roll / { current_index = 2; acc = 1; len = -1; last_item = 6 }
02:27:41 5d dice.create_sequential_roller / roll / { current_index = 3; acc = 1; len = -1; last_item = 6 }
02:27:41 6d dice.create_sequential_roller / roll / { current_index = 4; acc = 1; len = -1; last_item = 6 }
02:27:41 7d dice.create_sequential_roller / roll / { current_index = 5; acc = 1; len = -1; last_item = 6 }
02:27:41 8d dice.create_sequential_roller / roll / { current_index = 6; acc = 1; len = -1; last_item = 6 }
02:27:41 9d dice.create_sequential_roller / roll / { current_index = 7; acc = 1; len = -1; last_item = 6 }
02:27:41 10d dice.create_sequential_roller / roll / { current_index = 8; acc = 1; len = -1; last_item = 6 }
02:27:41 11d dice.create_sequential_roller / roll / { current_index = 9; acc = 1; len = -1; last_item = 6 }
02:27:41 12d dice.create_sequential_roller / roll / { current_index = 10; acc = 1; len = -1; last_item = 6 }
02:27:41 13d dice.create_sequential_roller / roll / { current_index = 11; acc = 1; len = -1; last_item = 6 }
02:27:41 14d dice.create_sequential_roller / roll / { current_index = 12; acc = 1; len = -1; last_item = 6 }
02:27:41 15d dice.create_sequential_roller / roll / { current_index = 13; acc = 1; len = -1; last_item = 6 }
02:27:41 16d dice.create_sequential_roller / roll / { current_index = 14; acc = 1; len = -1; last_item = 6 }
02:27:41 17d dice.create_sequential_roller / roll / { current_index = 15; acc = 1; len = -1; last_item = 6 }
02:27:41 18d dice.create_sequential_roller / roll / { current_index = 16; acc = 1; len = -1; last_item = 6 }
02:27:41 19d dice.create_sequential_roller / roll / { current_index = 17; acc = 1; len = -1; last_item = 6 }
02:27:41 20d dice.create_sequential_roller / roll / { current_index = 18; acc = 1; len = -1; last_item = 6 }
02:27:41 21d dice.create_sequential_roller / roll / { current_index = 19; acc = 1; len = -1; last_item = 6 }
02:27:41 22d dice.create_sequential_roller / roll / { current_index = 20; acc = 1; len = -1; last_item = 6 }
02:27:41 23d dice.create_sequential_roller / roll / { current_index = 21; acc = 1; len = -1; last_item = 6 }
02:27:41 24d dice.create_sequential_roller / roll / { current_index = 22; acc = 1; len = -1; last_item = 6 }
02:27:41 25d dice.create_sequential_roller / roll / { current_index = 23; acc = 1; len = -1; last_item = 6 }
02:27:41 26d dice.accumulate_dice_rolls / { power = 23; acc = 0; roll = 6; value = 3948651115268014080 }
02:27:41 27d dice.accumulate_dice_rolls / { power = 22; acc = 3948651115268014080; roll = 6; value = 658108519211335680 }
02:27:41 28d dice.accumulate_dice_rolls / { power = 21; acc = 4606759634479349760; roll = 6; value = 109684753201889280 }
02:27:41 29d dice.accumulate_dice_rolls / { power = 20; acc = 4716444387681239040; roll = 6; value = 18280792200314880 }
02:27:41 30d dice.accumulate_dice_rolls / { power = 19; acc = 4734725179881553920; roll = 6; value = 3046798700052480 }
02:27:41 31d dice.accumulate_dice_rolls / { power = 18; acc = 4737771978581606400; roll = 6; value = 507799783342080 }
02:27:41 32d dice.accumulate_dice_rolls / { power = 17; acc = 4738279778364948480; roll = 6; value = 84633297223680 }
02:27:41 33d dice.accumulate_dice_rolls / { power = 16; acc = 4738364411662172160; roll = 6; value = 14105549537280 }
02:27:41 34d dice.accumulate_dice_rolls / { power = 15; acc = 4738378517211709440; roll = 6; value = 2350924922880 }
02:27:41 35d dice.accumulate_dice_rolls / { power = 14; acc = 4738380868136632320; roll = 6; value = 391820820480 }
02:27:41 36d dice.accumulate_dice_rolls / { power = 13; acc = 4738381259957452800; roll = 6; value = 65303470080 }
02:27:41 37d dice.accumulate_dice_rolls / { power = 12; acc = 4738381325260922880; roll = 6; value = 10883911680 }
02:27:41 38d dice.accumulate_dice_rolls / { power = 11; acc = 4738381336144834560; roll = 6; value = 1813985280 }
02:27:41 39d dice.accumulate_dice_rolls / { power = 10; acc = 4738381337958819840; roll = 6; value = 302330880 }
02:27:41 40d dice.accumulate_dice_rolls / { power = 9; acc = 4738381338261150720; roll = 6; value = 50388480 }
02:27:41 41d dice.accumulate_dice_rolls / { power = 8; acc = 4738381338311539200; roll = 6; value = 8398080 }
02:27:41 42d dice.accumulate_dice_rolls / { power = 7; acc = 4738381338319937280; roll = 6; value = 1399680 }
02:27:41 43d dice.accumulate_dice_rolls / { power = 6; acc = 4738381338321336960; roll = 6; value = 233280 }
02:27:41 44d dice.accumulate_dice_rolls / { power = 5; acc = 4738381338321570240; roll = 6; value = 38880 }
02:27:41 45d dice.accumulate_dice_rolls / { power = 4; acc = 4738381338321609120; roll = 6; value = 6480 }
02:27:41 46d dice.accumulate_dice_rolls / { power = 3; acc = 4738381338321615600; roll = 6; value = 1080 }
02:27:41 47d dice.accumulate_dice_rolls / { power = 2; acc = 4738381338321616680; roll = 6; value = 180 }
02:27:41 48d dice.accumulate_dice_rolls / { power = 1; acc = 4738381338321616860; roll = 6; value = 30 }
02:27:41 49d dice.accumulate_dice_rolls / { power = 0; acc = 4738381338321616890; roll = 6; value = 5 }
02:27:41 50d dice.accumulate_dice_rolls / { power = -1; acc = 4738381338321616895; result = 4738381338321616896 }
__assert_eq { expected = 4738381338321616896 }
test roll_progressively_5: passed
///- --test

inl histogram (reroll : bool) : i32 =
    inl { level } = get_trace_state_or_init None
    level <- Info
    let rec insert (row : i32 * i32) (rows : list (i32 * i32)) : list (i32 * i32) =
        match rows with
        | [] => [ row ]
        | (k2, v2) :: rest =>
            inl k, v = row
            if k <= k2 then (k, v) :: (k2, v2) :: rest
            else (k2, v2) :: insert row rest
    join dyn listm'.init_series 1i32 10000 1
    |> listm.map (fun _ => roll_progressively roll_dice reroll 10i32)
    |> listm'.group_by id
    |> listm.map (fun (k, v) => k, (listm.length v : i32))
    |> listm.fold (fun acc row => insert row acc) []
    |> listm.fold (fun acc (k, v) => if acc = "" then #?k ++# " = " ++# #?v else acc ++# "; " ++# #?k ++# " = " ++# #?v) ""
    |> console.write_line
    0i32

histogram false
1 = 273; 2 = 693; 3 = 1346; 4 = 1331; 5 = 1368; 6 = 1349; 7 = 281; 8 = 698; 9 = 1282; 10 = 1379
test roll_progressively_6: passed
///- --test

inl histogram (reroll : bool) : i32 =
    inl { level } = get_trace_state_or_init None
    level <- Info
    let rec insert (row : i32 * i32) (rows : list (i32 * i32)) : list (i32 * i32) =
        match rows with
        | [] => [ row ]
        | (k2, v2) :: rest =>
            inl k, v = row
            if k <= k2 then (k, v) :: (k2, v2) :: rest
            else (k2, v2) :: insert row rest
    join dyn listm'.init_series 1i32 10000 1
    |> listm.map (fun _ => roll_progressively roll_dice reroll 10i32)
    |> listm'.group_by id
    |> listm.map (fun (k, v) => k, (listm.length v : i32))
    |> listm.fold (fun acc row => insert row acc) []
    |> listm.fold (fun acc (k, v) => if acc = "" then #?k ++# " = " ++# #?v else acc ++# "; " ++# #?k ++# " = " ++# #?v) ""
    |> console.write_line
    0i32

histogram true
1 = 994; 2 = 1029; 3 = 971; 4 = 1020; 5 = 1035; 6 = 1003; 7 = 936; 8 = 1047; 9 = 983; 10 = 982
test roll_progressively_7: passed
///- --test

inl check_progress () : i32 =
    inl { level } = get_trace_state_or_init None
    level <- Info
    join dyn listm'.init_series 1i32 100 1
    |> listm.iter fun n =>
        listm'.init_series 0i32 1 1
        |> listm.iter fun reroll =>
            join dyn listm'.init_series 1i32 3500 1
            |> listm.map fun _ => roll_progressively roll_dice (reroll = 1) n
            |> listm'.group_by id
            |> listm.length
            |> __assert_eq Silent n
    0i32

check_progress ()

main

inl main (_args : array_base string) =
    inl result = roll_progressively roll_dice true 4738381338321616896i64
    trace Debug
        fun () => "dice.main"
        fun () => { result }
    0i32

inl main () =
    inl rotate_numbers' : i64 -> stream.stream u8 -> stream.stream u8 =
        rotate_numbers

    inl create_sequential_roller' :
        list u8
        -> (() -> u8)
        =
        create_sequential_roller

    inl roll_progressively' :
        (() -> u8)
        -> bool
        -> u64
        -> u64
        =
        roll_progressively

    inl roll_within_bounds' :
        u64
        -> list u8
        -> optionm'.option' u64
        =
        fun a b =>
            roll_within_bounds a b
            |> optionm'.box

    backend_switch {
        Fsharp = fun () => $'let rotate_numbers x = !rotate_numbers' x' : ()
        Rust = fun () => ()
        TypeScript = fun () => ()
    } : ()
    backend_switch {
        Fsharp = fun () => $'let create_sequential_roller x = !create_sequential_roller' x' : ()
        Rust = fun () => ()
        TypeScript = fun () => ()
    } : ()
    backend_switch {
        Fsharp = fun () => $'let roll_progressively x = !roll_progressively' x' : ()
        Rust = fun () => ()
        TypeScript = fun () => ()
    } : ()
    backend_switch {
        Fsharp = fun () => $'let roll_within_bounds x = !roll_within_bounds' x' : ()
        Rust = fun () => ()
        TypeScript = fun () => ()
    } : ()
    backend_switch {
        Fsharp = fun () => $'let main args = !main args' : ()
        Rust = fun () => main (;[] |> am'.base) |> ignore
        Python = fun () => main (;[] |> am'.base) |> ignore
        TypeScript = fun () => main (;[] |> am'.base) |> ignore
    } : ()