Change implementation to use LUT
This commit is contained in:
parent
2244651aa1
commit
0767d5d8c1
91
lut.py
Normal file
91
lut.py
Normal file
@ -0,0 +1,91 @@
|
||||
s = set()
|
||||
|
||||
for a in range(7):
|
||||
for b in range(7):
|
||||
for c in range(7):
|
||||
s.add(tuple(sorted((a, b, c))))
|
||||
|
||||
mapping = sorted(s)
|
||||
|
||||
n = 128
|
||||
filler = 0
|
||||
|
||||
print(f"const MAPPING: [(u8, u8, u8); {len(mapping)}] = {list(mapping)};")
|
||||
print()
|
||||
|
||||
################################################################################
|
||||
|
||||
|
||||
def score(column):
|
||||
values = {die: 0 for die in range(1, 7)}
|
||||
for die in column:
|
||||
if die == 0:
|
||||
continue
|
||||
values[die] += 1
|
||||
|
||||
multiplier = {0: 1, 1: 1, 2: 2, 3: 9}
|
||||
|
||||
return sum(value * count * multiplier[count] for (value, count) in values.items())
|
||||
|
||||
|
||||
score_lut = [filler for _ in range(n)]
|
||||
for i, column in enumerate(mapping):
|
||||
score_lut[i] = score(column)
|
||||
|
||||
print(f"const SCORE_LUT: [u8; {n}] = {score_lut};")
|
||||
print()
|
||||
|
||||
################################################################################
|
||||
|
||||
|
||||
def add(die, column):
|
||||
if 0 not in column:
|
||||
raise ValueError("Invalid Move")
|
||||
|
||||
new_column = list(column)
|
||||
new_column[0] = die # sorted and a zero is present -> first index is 0
|
||||
|
||||
return tuple(sorted(new_column))
|
||||
|
||||
|
||||
add_lut = {die: [filler for _ in range(32)] for die in range(1, 7)}
|
||||
for die in range(1, 7):
|
||||
for i, column in enumerate(mapping):
|
||||
try:
|
||||
new_column = add(die, column)
|
||||
except ValueError:
|
||||
continue
|
||||
add_lut[die][i] = mapping.index(new_column)
|
||||
|
||||
print(f"const ADD_LUT: [[u8; 32]; 8] = [")
|
||||
print(f" [{filler}; 32],")
|
||||
for die in range(1, 7):
|
||||
print(f" {add_lut[die]},")
|
||||
print(f" [{filler}; 32],")
|
||||
print("];");
|
||||
print()
|
||||
|
||||
################################################################################
|
||||
|
||||
|
||||
def remove(die, opposite):
|
||||
new_opposite = list(opposite)
|
||||
for i in range(3):
|
||||
if new_opposite[i] == die:
|
||||
new_opposite[i] = 0
|
||||
return tuple(sorted(new_opposite))
|
||||
|
||||
|
||||
remove_lut = {die: [filler for _ in range(n)] for die in range(1, 7)}
|
||||
for die in range(1, 7):
|
||||
for i, column in enumerate(mapping):
|
||||
new_column = remove(die, column)
|
||||
remove_lut[die][i] = mapping.index(new_column)
|
||||
|
||||
print(f"const REMOVE_LUT: [[u8; {n}]; 8] = [")
|
||||
print(f" [{filler}; {n}],")
|
||||
for die in range(1, 7):
|
||||
print(f" {remove_lut[die]},")
|
||||
print(f" [{filler}; {n}],")
|
||||
print("];");
|
||||
print()
|
374
src/game.rs
374
src/game.rs
@ -1,152 +1,166 @@
|
||||
const MAPPING: [(u8, u8, u8); 84] = [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (0, 0, 5), (0, 0, 6), (0, 1, 1), (0, 1, 2), (0, 1, 3), (0, 1, 4), (0, 1, 5), (0, 1, 6), (0, 2, 2), (0, 2, 3), (0, 2, 4), (0, 2, 5), (0, 2, 6), (0, 3, 3), (0, 3, 4), (0, 3, 5), (0, 3, 6), (0, 4, 4), (0, 4, 5), (0, 4, 6), (0, 5, 5), (0, 5, 6), (0, 6, 6), (1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 1, 5), (1, 1, 6), (1, 2, 2), (1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 2, 6), (1, 3, 3), (1, 3, 4), (1, 3, 5), (1, 3, 6), (1, 4, 4), (1, 4, 5), (1, 4, 6), (1, 5, 5), (1, 5, 6), (1, 6, 6), (2, 2, 2), (2, 2, 3), (2, 2, 4), (2, 2, 5), (2, 2, 6), (2, 3, 3), (2, 3, 4), (2, 3, 5), (2, 3, 6), (2, 4, 4), (2, 4, 5), (2, 4, 6), (2, 5, 5), (2, 5, 6), (2, 6, 6), (3, 3, 3), (3, 3, 4), (3, 3, 5), (3, 3, 6), (3, 4, 4), (3, 4, 5), (3, 4, 6), (3, 5, 5), (3, 5, 6), (3, 6, 6), (4, 4, 4), (4, 4, 5), (4, 4, 6), (4, 5, 5), (4, 5, 6), (4, 6, 6), (5, 5, 5), (5, 5, 6), (5, 6, 6), (6, 6, 6)];
|
||||
|
||||
const SCORE_LUT: [u8; 128] = [
|
||||
0, 1, 2, 3, 4, 5, 6, 4, 3, 4, 5, 6, 7, 8, 5, 6, 7, 8, 12, 7, 8, 9, 16, 9, 10, 20, 11, 24, 27,
|
||||
6, 7, 8, 9, 10, 9, 6, 7, 8, 9, 13, 8, 9, 10, 17, 10, 11, 21, 12, 25, 54, 11, 12, 13, 14, 14, 9,
|
||||
10, 11, 18, 11, 12, 22, 13, 26, 81, 16, 17, 18, 19, 12, 13, 23, 14, 27, 108, 21, 22, 24, 15,
|
||||
28, 135, 26, 29, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
];
|
||||
|
||||
const ADD_LUT: [[u8; 32]; 8] = [
|
||||
[0; 32],
|
||||
[
|
||||
1, 7, 8, 9, 10, 11, 12, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
|
||||
45, 46, 47, 48, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
2, 8, 13, 14, 15, 16, 17, 29, 34, 35, 36, 37, 38, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
|
||||
59, 60, 61, 62, 63, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
3, 9, 14, 18, 19, 20, 21, 30, 35, 39, 40, 41, 42, 50, 54, 55, 56, 57, 64, 65, 66, 67, 68,
|
||||
69, 70, 71, 72, 73, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
4, 10, 15, 19, 22, 23, 24, 31, 36, 40, 43, 44, 45, 51, 55, 58, 59, 60, 65, 68, 69, 70, 74,
|
||||
75, 76, 77, 78, 79, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
5, 11, 16, 20, 23, 25, 26, 32, 37, 41, 44, 46, 47, 52, 56, 59, 61, 62, 66, 69, 71, 72, 75,
|
||||
77, 78, 80, 81, 82, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
6, 12, 17, 21, 24, 26, 27, 33, 38, 42, 45, 47, 48, 53, 57, 60, 62, 63, 67, 70, 72, 73, 76,
|
||||
78, 79, 81, 82, 83, 0, 0, 0, 0,
|
||||
],
|
||||
[0; 32],
|
||||
];
|
||||
|
||||
const REMOVE_LUT: [[u8; 128]; 8] = [
|
||||
[0; 128],
|
||||
[
|
||||
0, 0, 2, 3, 4, 5, 6, 0, 2, 3, 4, 5, 6, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 27, 0, 2, 3, 4, 5, 6, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 49,
|
||||
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
|
||||
73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
0, 1, 0, 3, 4, 5, 6, 7, 1, 9, 10, 11, 12, 0, 3, 4, 5, 6, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 27, 28, 7, 30, 31, 32, 33, 1, 9, 10, 11, 12, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 0,
|
||||
3, 4, 5, 6, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
|
||||
74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
0, 1, 2, 0, 4, 5, 6, 7, 8, 1, 10, 11, 12, 13, 2, 15, 16, 17, 0, 4, 5, 6, 22, 23, 24, 25,
|
||||
26, 27, 28, 29, 7, 31, 32, 33, 34, 8, 36, 37, 38, 1, 10, 11, 12, 43, 44, 45, 46, 47, 48,
|
||||
49, 13, 51, 52, 53, 2, 15, 16, 17, 58, 59, 60, 61, 62, 63, 0, 4, 5, 6, 22, 23, 24, 25, 26,
|
||||
27, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
0, 1, 2, 3, 0, 5, 6, 7, 8, 9, 1, 11, 12, 13, 14, 2, 16, 17, 18, 3, 20, 21, 0, 5, 6, 25, 26,
|
||||
27, 28, 29, 30, 7, 32, 33, 34, 35, 8, 37, 38, 39, 9, 41, 42, 1, 11, 12, 46, 47, 48, 49, 50,
|
||||
13, 52, 53, 54, 14, 56, 57, 2, 16, 17, 61, 62, 63, 64, 18, 66, 67, 3, 20, 21, 71, 72, 73,
|
||||
0, 5, 6, 25, 26, 27, 80, 81, 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
0, 1, 2, 3, 4, 0, 6, 7, 8, 9, 10, 1, 12, 13, 14, 15, 2, 17, 18, 19, 3, 21, 22, 4, 24, 0, 6,
|
||||
27, 28, 29, 30, 31, 7, 33, 34, 35, 36, 8, 38, 39, 40, 9, 42, 43, 10, 45, 1, 12, 48, 49, 50,
|
||||
51, 13, 53, 54, 55, 14, 57, 58, 15, 60, 2, 17, 63, 64, 65, 18, 67, 68, 19, 70, 3, 21, 73,
|
||||
74, 22, 76, 4, 24, 79, 0, 6, 27, 83, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
[
|
||||
0, 1, 2, 3, 4, 5, 0, 7, 8, 9, 10, 11, 1, 13, 14, 15, 16, 2, 18, 19, 20, 3, 22, 23, 4, 25,
|
||||
5, 0, 28, 29, 30, 31, 32, 7, 34, 35, 36, 37, 8, 39, 40, 41, 9, 43, 44, 10, 46, 11, 1, 49,
|
||||
50, 51, 52, 13, 54, 55, 56, 14, 58, 59, 15, 61, 16, 2, 64, 65, 66, 18, 68, 69, 19, 71, 20,
|
||||
3, 74, 75, 22, 77, 23, 4, 80, 25, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
[0; 128],
|
||||
];
|
||||
|
||||
pub fn add_is_valid(column: u8) -> bool {
|
||||
column < 28
|
||||
}
|
||||
|
||||
pub fn add(die: u8, column: u8) -> u8 {
|
||||
ADD_LUT[(die & 0b111) as usize][(column & 0b11111) as usize]
|
||||
}
|
||||
|
||||
pub fn remove(die: u8, column: u8) -> u8 {
|
||||
REMOVE_LUT[(die & 0b111) as usize][(column & 0b1111111) as usize]
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Column {
|
||||
L = 0,
|
||||
C = 2,
|
||||
R = 4,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Player {
|
||||
Bottom,
|
||||
Top,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Column {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
/// Encodes the state of the game
|
||||
///
|
||||
/// Each cell can take 7 values (empty or 1-6) -> 3 * 18 = 54 bits required.
|
||||
/// We need one additional bit to store which player's turn it is.
|
||||
///
|
||||
/// Cell Layout:
|
||||
/// ```
|
||||
/// 11 14 17
|
||||
/// 10 13 16
|
||||
/// 9 12 15
|
||||
///
|
||||
/// 0 3 6
|
||||
/// 1 4 7
|
||||
/// 2 5 8
|
||||
/// ```
|
||||
///
|
||||
/// Offsets:
|
||||
/// ```
|
||||
/// 33 42 51
|
||||
/// 30 39 48
|
||||
/// 27 36 45
|
||||
///
|
||||
/// 0 9 18
|
||||
/// 3 12 21
|
||||
/// 6 15 24
|
||||
/// ```
|
||||
///
|
||||
/// current_player: `54`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Board(u64);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IllegalMove(());
|
||||
|
||||
pub struct Board {
|
||||
columns: [u8; 6],
|
||||
pub current_player: Player,
|
||||
}
|
||||
|
||||
impl Board {
|
||||
pub fn empty() -> Self {
|
||||
Self::from_values([&[], &[], &[]], [&[], &[], &[]], Player::Bottom)
|
||||
}
|
||||
|
||||
pub fn from_values(bottom: [&[u8]; 3], top: [&[u8]; 3], current_player: Player) -> Self {
|
||||
let mut bits = 0;
|
||||
for (start, half) in [(0, bottom), (27, top)] {
|
||||
for (index, &col) in half.iter().enumerate() {
|
||||
for (offset, &val) in col.iter().enumerate() {
|
||||
let shift = start + index * 9 + offset * 3;
|
||||
bits |= (val as u64) << shift;
|
||||
}
|
||||
}
|
||||
Self {
|
||||
columns: [0, 0, 0, 0, 0, 0],
|
||||
current_player: Player::Bottom,
|
||||
}
|
||||
|
||||
if current_player == Player::Top {
|
||||
bits |= 1 << 54
|
||||
}
|
||||
|
||||
Self(bits)
|
||||
}
|
||||
|
||||
pub fn visualize(&self) {
|
||||
let &Self(bits) = self;
|
||||
|
||||
let print_half = |rows| {
|
||||
for row in rows {
|
||||
for offset in row {
|
||||
let value = (bits >> offset & 0b111u64) as u8;
|
||||
if value > 0 {
|
||||
print!("{value} ");
|
||||
} else {
|
||||
print!("_ ");
|
||||
}
|
||||
}
|
||||
println!()
|
||||
}
|
||||
};
|
||||
|
||||
print_half([[33, 42, 51], [30, 39, 48], [27, 36, 45]]);
|
||||
println!();
|
||||
print_half([[0, 9, 18], [3, 12, 21], [6, 15, 24]]);
|
||||
}
|
||||
|
||||
pub fn current_player(&self) -> Player {
|
||||
let &Self(bits) = self;
|
||||
if bits >> 54 & 1 == 0 {
|
||||
Player::Bottom
|
||||
} else {
|
||||
Player::Top
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_game_over(&self) -> bool {
|
||||
let &Self(bits) = self;
|
||||
let has_empty_slots = |offset| -> bool {
|
||||
for i in [0, 3, 6] {
|
||||
if (bits >> offset + i) & 0b111u64 == 0u64 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
let [a, b, c, d, e, f] = self.columns;
|
||||
|
||||
!((has_empty_slots(0) || has_empty_slots(9) || has_empty_slots(18))
|
||||
&& (has_empty_slots(27) || has_empty_slots(36) || has_empty_slots(45)))
|
||||
println!("{b:02X} {d:02X} {f:02X}");
|
||||
println!("{a:02X} {c:02X} {e:02X}");
|
||||
|
||||
let (l1, l2, l3) = MAPPING[b as usize];
|
||||
let (l4, l5, l6) = MAPPING[a as usize];
|
||||
let (c1, c2, c3) = MAPPING[d as usize];
|
||||
let (c4, c5, c6) = MAPPING[c as usize];
|
||||
let (r1, r2, r3) = MAPPING[f as usize];
|
||||
let (r4, r5, r6) = MAPPING[e as usize];
|
||||
|
||||
println!("{l1} {c1} {r1}");
|
||||
println!("{l2} {c2} {r2}");
|
||||
println!("{l3} {c3} {r3}");
|
||||
println!();
|
||||
println!("{l6} {c6} {r6}");
|
||||
println!("{l5} {c5} {r5}");
|
||||
println!("{l4} {c4} {r4}");
|
||||
}
|
||||
|
||||
/// (bottom, top)
|
||||
pub fn column_scores(&self) -> ((u8, u8, u8), (u8, u8, u8)) {
|
||||
let &Self(bits) = self;
|
||||
|
||||
let score_col = |offset| {
|
||||
let mut counts = [0; 6];
|
||||
for i in [0, 3, 6] {
|
||||
let value = ((bits >> offset + i) & 0b111u64) as usize;
|
||||
if value == 0 {
|
||||
continue;
|
||||
}
|
||||
counts[value - 1] += 1;
|
||||
}
|
||||
|
||||
counts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(value, &count)| {
|
||||
let value = (value + 1) as u8;
|
||||
value
|
||||
* count
|
||||
* match count {
|
||||
2 => 2,
|
||||
3 => 9,
|
||||
_ => 1,
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
};
|
||||
let [a, b, c, d, e, f] = self.columns;
|
||||
|
||||
(
|
||||
(score_col(0), score_col(9), score_col(18)),
|
||||
(score_col(27), score_col(36), score_col(45)),
|
||||
(
|
||||
SCORE_LUT[(a & 0x7F) as usize],
|
||||
SCORE_LUT[(b & 0x7F) as usize],
|
||||
SCORE_LUT[(c & 0x7F) as usize],
|
||||
),
|
||||
(
|
||||
SCORE_LUT[(d & 0x7F) as usize],
|
||||
SCORE_LUT[(e & 0x7F) as usize],
|
||||
SCORE_LUT[(f & 0x7F) as usize],
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -155,83 +169,37 @@ impl Board {
|
||||
a as i16 + b as i16 + c as i16 - d as i16 - e as i16 - f as i16
|
||||
}
|
||||
|
||||
pub fn play(&self, column: Column, value: u8) -> Result<Self, IllegalMove> {
|
||||
let Self(mut bits) = *self;
|
||||
|
||||
let mut apply_move = |offset, opposite| -> Result<(), IllegalMove> {
|
||||
// push value to the first zero after offset
|
||||
let mut found = false;
|
||||
for index in [0, 3, 6] {
|
||||
if ((bits >> offset + index) & 0b111u64) != 0u64 {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
bits &= !((0b111u64 << offset + index) as u64);
|
||||
bits |= (value as u64) << offset + index;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// raise if all three are non-zero
|
||||
if !found {
|
||||
return Err(IllegalMove(()));
|
||||
}
|
||||
|
||||
// eliminate all occurences from value after opposite
|
||||
let mut tmp = [0, 0, 0];
|
||||
let mut i = 0;
|
||||
for index in [0, 3, 6] {
|
||||
let other = ((bits >> opposite + index) & 0b111u64) as u8;
|
||||
if other == value {
|
||||
continue;
|
||||
}
|
||||
tmp[i] = other;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i != 3 {
|
||||
for (index, &value) in tmp.iter().enumerate() {
|
||||
bits &= !((0b111u64 << opposite + index) as u64);
|
||||
bits |= (value as u64) << opposite + index;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match (column, self.current_player()) {
|
||||
(Column::Left, Player::Bottom) => apply_move(0, 27)?,
|
||||
(Column::Left, Player::Top) => apply_move(27, 0)?,
|
||||
(Column::Center, Player::Bottom) => apply_move(9, 36)?,
|
||||
(Column::Center, Player::Top) => apply_move(36, 9)?,
|
||||
(Column::Right, Player::Bottom) => apply_move(18, 45)?,
|
||||
(Column::Right, Player::Top) => apply_move(45, 18)?,
|
||||
pub fn play(&self, column: Column, die: u8) -> Result<Self, IllegalMove> {
|
||||
let a = &self.columns;
|
||||
if !add_is_valid(a[column as usize]) {
|
||||
return Err(IllegalMove(()));
|
||||
}
|
||||
|
||||
// switch current player
|
||||
bits ^= 1 << 54;
|
||||
let mut a = a.clone();
|
||||
|
||||
Ok(Self(bits))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_example() {
|
||||
let board = Board::from_values(
|
||||
[&[6, 5, 5], &[5, 5], &[1, 5]],
|
||||
[&[2, 2, 1], &[4, 3, 3], &[4, 2, 3]],
|
||||
Player::Bottom,
|
||||
);
|
||||
|
||||
board.visualize();
|
||||
|
||||
assert_eq!(board.column_scores(), ((26, 20, 6), (9, 16, 9)));
|
||||
assert!(board.score() > 0);
|
||||
assert!(board.is_game_over());
|
||||
let (current, opposite) = match self.current_player {
|
||||
Player::Bottom => (column as usize, column as usize + 1),
|
||||
Player::Top => (column as usize + 1, column as usize)
|
||||
};
|
||||
|
||||
a[current] = add(die, a[current]);
|
||||
a[opposite] = remove(die, a[opposite]);
|
||||
|
||||
let player = match self.current_player {
|
||||
Player::Top => Player::Bottom,
|
||||
Player::Bottom => Player::Top,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
columns: a,
|
||||
current_player: player,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_game_over(&self) -> bool {
|
||||
let [a, b, c, d, e, f] = self.columns;
|
||||
|
||||
!((add_is_valid(a) | add_is_valid(b) | add_is_valid(c))
|
||||
& (add_is_valid(d) | add_is_valid(e) | add_is_valid(f)))
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ fn main() {
|
||||
|
||||
while !b.is_game_over() {
|
||||
b.visualize();
|
||||
println!("Current player: {:?}", b.current_player());
|
||||
println!("Current player: {:?}", b.current_player);
|
||||
|
||||
let d6 = (Select::new().items(&[1, 2, 3, 4, 5, 6]).interact().unwrap() + 1) as u8;
|
||||
|
||||
let column = [Column::Left, Column::Center, Column::Right][Select::new()
|
||||
let column = [Column::L, Column::C, Column::R][Select::new()
|
||||
.items(&["Left", "Center", "Right"])
|
||||
.interact()
|
||||
.unwrap()];
|
||||
|
Loading…
Reference in New Issue
Block a user