[Odin] Recursively build all possible paths[Odin] Recursively build all possible paths

🐍🪜

Snakes & Ladders

Week 40, 2026

package main import "core:fmt" main :: proc () { // board := board_create([3] [3] int { // {-1,0,0}, // {3,-2,-3}, // {0,1,0}, // }) board := board_create([6] [6] int { {0,0,-32,0,-5,0}, {0,-6,8,0,-16,0}, {-5,0,5,0,-13,0}, {0,18,-10,0,1,-8}, {0,-3,0,3,-6,0}, {0,-1,4,8,0,0}, }) board_print_rows(board) board_play(&board) fmt.printfln("--- Valid Paths: %i ---", board.total_paths) } Board :: struct ($N: int) { rows : [N] [N] int, moves : [dynamic; 100] Board_Move, current_pos : int, total_paths : int, } Board_Move :: struct { start_pos, finish_pos, dice, jump: int, } board_create :: proc (rows: [$N] [N] int) -> Board(N) { return { rows=rows, current_pos=1 } } board_play :: proc (b: ^Board($N)) { for dice in 1..=6 { result := board_push_move(b, dice) switch result { case .invalid: /**/ case .ok: board_play(b) board_pop_move(b) case .ok_finish: b.total_paths += 1 board_print_moves(b^) board_pop_move(b) } } } Board_Push_Move_Result :: enum { invalid, ok, ok_finish } board_push_move :: proc (b: ^Board($N), dice: int) -> Board_Push_Move_Result { m := Board_Move { start_pos = b.current_pos, finish_pos = b.current_pos + dice, dice = dice, } if jump, has_jump := board_jump(b^, m.finish_pos); has_jump do switch { case jump > 0: // ladder m.jump = jump m.finish_pos += jump // We can land on non-zero jump (the next code check for that and panics) // We ignore such cases, meaning we will do next move from an existing ladder/snake // if _, has_next_jump := board_jump(b^, m.finish_pos); has_next_jump { // fmt.println("ERROR: Multiple snake/ladder jumps per single dice roll detected") // fmt.println("Current moves:"); board_print_moves(b^) // fmt.println("Failed next move:", m) // panic("") // } case jump < 0: // snake return .invalid } if m.finish_pos > N*N do return .invalid num_appended := append(&b.moves, m) assert(num_appended == 1) b.current_pos = m.finish_pos return m.finish_pos == N*N ? .ok_finish : .ok } board_pop_move :: proc (b: ^Board($N)) { m := pop(&b.moves) b.current_pos = m.start_pos } // Returns only non-zero jumps with `ok == true` board_jump :: proc (b: Board($N), pos: int) -> (jump: int, ok: bool) { row, col := board_idx(b, pos) or_return j := b.rows[row][col] if j != 0 do return j, true return } board_idx :: proc (b: Board($N), pos: int) -> (row, col: int, ok: bool) { if pos >= 1 && pos <= N*N { N0, pos0 := N-1, pos-1 row = N0 - pos0/N col = (N0-row)%2==0 ? pos0%N : N0-pos0%N ok = true } return } board_print_rows :: proc (b: Board($N)) { fmt.println("---------------------------------------------") for column in b.rows { for value in column do fmt.printf("%i\t", value) fmt.println() } fmt.println("---------------------------------------------") } board_print_moves :: proc (b: Board($N)) { for m, i in b.moves { if i > 0 do fmt.print(" ") if m.jump > 0 do fmt.printf("%i+%i^%i->%i", m.start_pos, m.dice, m.jump, m.finish_pos) else do fmt.printf("%i+%i->%i", m.start_pos, m.dice, m.finish_pos) } fmt.println() }