All SolutionsAll Solutions
🐍🪜
Snakes & Ladders
Week 40, 2026
Recursively build all possible paths | greenya | Odin Solutions
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()
}