198 lines
5.4 KiB
Plaintext
198 lines
5.4 KiB
Plaintext
#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);
|
|
}
|
|
}
|