From 20638827f623a32d46e4e7140c7c8d059df4a492 Mon Sep 17 00:00:00 2001 From: Alexander Luzgarev Date: Sat, 9 May 2026 20:17:23 +0200 Subject: [PATCH] Solver --- nirmana.jai | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 nirmana.jai diff --git a/nirmana.jai b/nirmana.jai new file mode 100644 index 0000000..bc7716a --- /dev/null +++ b/nirmana.jai @@ -0,0 +1,197 @@ +#import "Basic"; +#import "Random"; +#import "Hash_Table"; + +Position :: struct { + values : [12] u8; + moves : [] u8; + score : int; + index : int; + peril : bool; +} + +position_create_start :: (values: [12] u8) -> Position { + result: Position; + result.values = values; + result.score = 0; + result.peril = false; + return result; +} + +position_print :: (p: Position) -> () { + print("["); + for v, i: p.values { + if i > 0 print(" "); + print("%", v); + } + print("]"); + if p.peril then print("*"); else print(" "); + print("(%)", p.score); +} + +position_print_moves :: (p: Position) -> () { + for m: p.moves + print("% ", m); +} + +position_is_final :: (p: Position) -> bool { + for v: p.values { + if v != 0 return false; + } + return true; +} + +position_make_moves :: (p: Position, results: *[..] Position) -> () { + for i: 0..11 { + if p.values[i] == 0 then continue; + n := p.values[i]; + new_values: [12] u8; + new_score := p.score; + for j: 0..11 { + if j != i then new_values[j] = p.values[j]; + } + for j: 1..n { + idx := (i + j) % 12; + new_values[idx] += 1; + } + last := (i + n) % 12; + k := new_values[last]; + if p.peril && k != 2 && k != 3 then continue; + new_moves := NewArray(p.moves.count + 1, u8); + for j: 0..p.moves.count-1 { + new_moves[j] = p.moves[j]; + } + new_moves[p.moves.count] = cast(u8) i; + new_peril: bool; + if k == 2 || k ==3 { + new_values[last] = 0; + new_score += k; + new_peril = false; + while true { + last = (last + 11) % 12; + kk := new_values[last]; + if kk == 2 || kk == 3 { + new_values[last] = 0; + new_score += kk * kk; + } else { + break; + } + } + + } else { + new_peril = true; + } + new_position := Position.{ + values = new_values, + moves = new_moves, + score = new_score, + peril = new_peril }; + array_add(results, new_position); + } +} + +position_equal :: (left: Position, right: Position) -> bool { + for i: 0..11 { + if left.values[i] != right.values[i] then return false; + } + if left.peril != right.peril then return false; + if left.score != right.score then return false; + return true; +} + +position_hash :: (p: Position) -> u32 { + h : u32 = 17; + for v: p.values h = (h + v) * 37 + 89; + h = (h + cast(u32) p.score) * 37 + 89; + h = (h + cast(u32)(ifx p.peril then 1 else 2)) * 37 + 89; + return h; +} + +Position_List :: struct { + list: [..] Position; +} + +position_max_remaining_score :: (p: Position) -> int { + sum := 0; + for v: p.values sum += v; + return 3 + 9 * ((sum - 1) / 3); +} + +position_solve :: (start: Position) -> (moves: [] u8, best_score: int) { + stacks: [256] Position_List; + all_positions: Table(Position, bool, position_hash, position_equal); + array_add(*stacks[0].list, start); + results: [..] Position; + best_score := 0; + best_moves: [] u8; + previous_time_ms := 0; + longest_moves_count := 0; + longest_moves: [] u8; + steps := 0; + while true { + steps += 1; + found := false; + score := -1; + for diff: 1..256 { + score = 256 - diff; + if stacks[score].list.count > 0 { + found = true; + break; + } + } + if !found then break; + + index := cast(s64) (random_get() % cast(u64) stacks[score].list.count); + current := stacks[score].list[index]; + array_unordered_remove_by_index(*stacks[score].list, index); + current_time_ms := to_milliseconds(current_time_consensus()); + if score + position_max_remaining_score(current) < best_score then continue; + if current_time_ms - previous_time_ms > 1000 { + print("Steps: %, current: ", steps); + position_print_moves(current); + print(" --> "); + position_print(current); + print("\n"); + previous_time_ms = current_time_ms; + } + if current.moves.count > longest_moves_count { + longest_moves = current.moves; + longest_moves_count = current.moves.count; + } + + if position_is_final(current) { + if current.score > best_score { + print("*** Reached score %: ", current.score); + for m: current.moves { + print("% ", m); + } + print("\n"); + best_score = current.score; + best_moves = current.moves; + } + continue; + } + array_reset(*results); + position_make_moves(current, *results); + for *r: results { + if table_contains(*all_positions, r) then continue; + array_add(*stacks[r.score].list, r); + table_add(*all_positions, r, true); + } + } + return best_moves, best_score; +} + + + +main :: () -> () { + print("Hello.\n"); + start := position_create_start(.[4, 1, 3, 1, 1, 3, 8, 5, 2, 8, 6, 8]); + position_print(start); + print("\n"); + moves, best_score := position_solve(start); + print("Best score: % in % moves\n", best_score, moves.count); + for m, i: moves { + print("Move %: %\n", i + 1, m); + } +}