use super::rand::{thread_rng, Rng};
use super::direction::Direction;
struct Moves {
pub left: Vec<u64>,
pub right: Vec<u64>,
pub down: Vec<u64>,
pub up: Vec<u64>,
pub scores: Vec<u64>
}
impl Moves {
pub fn column_from(board: u64) -> u64 {
(board | (board << 12) | (board << 24) | (board << 36)) & COL_MASK
}
}
lazy_static! {
static ref MOVES: Moves = {
let mut left_moves = vec![0; 65536];
let mut right_moves = vec![0; 65536];
let mut up_moves = vec![0; 65536];
let mut down_moves = vec![0; 65536];
let mut scores = vec![0; 65536];
for row in 0 .. 65536 {
let mut line = [
(row >> 0) & 0xF,
(row >> 4) & 0xF,
(row >> 8) & 0xF,
(row >> 12) & 0xF
];
let mut s = 0;
for i in 0 .. 4 {
if line[i] >= 2 { s += (line[i] - 1) * (1 << line[i]) }
}
scores[row as usize] = s;
let mut i = 0;
while i < 3 {
let mut j = i + 1;
while j < 4 {
if line[j] != 0 { break };
j += 1;
};
if j == 4 { break };
if line[i] == 0 {
line[i] = line[j];
line[j] = 0;
continue;
} else if line[i] == line[j] {
if line[i] != 0xF { line[i] += 1 };
line[j] = 0;
}
i += 1;
}
let result = (line[0] << 0) |
(line[1] << 4) |
(line[2] << 8) |
(line[3] << 12);
let rev_row = (row >> 12) & 0x000F | (row >> 4) & 0x00F0 | (row << 4) & 0x0F00 | (row << 12) & 0xF000;
let rev_res = (result >> 12) & 0x000F | (result >> 4) & 0x00F0 | (result << 4) & 0x0F00 | (result << 12) & 0xF000;
let row_idx = row as usize;
let rev_idx = rev_row as usize;
right_moves[row_idx] = row ^ result;
left_moves[rev_idx] = rev_row ^ rev_res;
up_moves[rev_idx] = Moves::column_from(rev_row) ^ Moves::column_from(rev_res);
down_moves[row_idx] = Moves::column_from(row) ^ Moves::column_from(result);
};
Moves { left: left_moves, right: right_moves, down: down_moves, up: up_moves, scores: scores }
};
}
pub static ROW_MASK: u64 = 0xFFFF;
pub static COL_MASK: u64 = 0x000F_000F_000F_000F_u64;
pub struct Game { pub board: u64 }
impl Game {
pub fn new() -> Self {
let mut game = Game { board: 0x0000_0000_0000_0000_u64 };
game.board |= Self::spawn_tile(game.board);
game.board |= Self::spawn_tile(game.board);
game
}
pub fn play<F: Fn(u64, &Vec<Direction>) -> Direction>(mv: F) -> Self {
let mut game = Self::new();
let mut attempted: Vec<Direction> = Vec::with_capacity(4);
loop {
let mv = mv(game.board, &attempted);
if !attempted.iter().any(|dir| dir == &mv) {
let result_board = Self::execute(game.board, &mv);
if game.board == result_board {
if attempted.len() == 3 { break }
attempted.push(mv);
} else {
game.board = result_board;
game.board |= Self::spawn_tile(game.board);
attempted.clear();
}
}
}
game
}
pub fn execute(board: u64, direction: &Direction) -> u64 {
match direction {
Direction::Left => Self::move_left(board),
Direction::Right => Self::move_right(board),
Direction::Down => Self::move_down(board),
Direction::Up => Self::move_up(board)
}
}
pub fn transpose(board: u64) -> u64 {
let a1 = board & 0xF0F0_0F0F_F0F0_0F0F_u64;
let a2 = board & 0x0000_F0F0_0000_F0F0_u64;
let a3 = board & 0x0F0F_0000_0F0F_0000_u64;
let a = a1 | (a2 << 12) | (a3 >> 12);
let b1 = a & 0xFF00_FF00_00FF_00FF_u64;
let b2 = a & 0x00FF_00FF_0000_0000_u64;
let b3 = a & 0x0000_0000_FF00_FF00_u64;
b1 | (b2 >> 24) | (b3 << 24)
}
pub fn move_up(board: u64) -> u64 {
let mut result = board;
let transposed = Self::transpose(board);
result ^= MOVES.up[((transposed >> 0) & ROW_MASK) as usize] << 0;
result ^= MOVES.up[((transposed >> 16) & ROW_MASK) as usize] << 4;
result ^= MOVES.up[((transposed >> 32) & ROW_MASK) as usize] << 8;
result ^= MOVES.up[((transposed >> 48) & ROW_MASK) as usize] << 12;
result
}
pub fn move_down(board: u64) -> u64 {
let mut result = board;
let transposed = Self::transpose(board);
result ^= MOVES.down[((transposed >> 0) & ROW_MASK) as usize] << 0;
result ^= MOVES.down[((transposed >> 16) & ROW_MASK) as usize] << 4;
result ^= MOVES.down[((transposed >> 32) & ROW_MASK) as usize] << 8;
result ^= MOVES.down[((transposed >> 48) & ROW_MASK) as usize] << 12;
result
}
pub fn move_right(board: u64) -> u64 {
let mut result = board;
result ^= MOVES.right[((board >> 0) & ROW_MASK) as usize] << 0;
result ^= MOVES.right[((board >> 16) & ROW_MASK) as usize] << 16;
result ^= MOVES.right[((board >> 32) & ROW_MASK) as usize] << 32;
result ^= MOVES.right[((board >> 48) & ROW_MASK) as usize] << 48;
result
}
pub fn move_left(board: u64) -> u64 {
let mut result: u64 = board;
result ^= MOVES.left[((board >> 0) & ROW_MASK) as usize] << 0;
result ^= MOVES.left[((board >> 16) & ROW_MASK) as usize] << 16;
result ^= MOVES.left[((board >> 32) & ROW_MASK) as usize] << 32;
result ^= MOVES.left[((board >> 48) & ROW_MASK) as usize] << 48;
result
}
pub fn count_empty(board: u64) -> u32 {
let mut empty = 0;
for i in 0 .. 16 { if ((board >> (i * 4)) & 0xF) == 0 { empty += 1 } }
empty
}
pub fn table_helper(board: u64, table: &Vec<u64>) -> u64 {
table[((board >> 0) & ROW_MASK) as usize] +
table[((board >> 16) & ROW_MASK) as usize] +
table[((board >> 32) & ROW_MASK) as usize] +
table[((board >> 48) & ROW_MASK) as usize]
}
pub fn score(board: u64) -> u64 {
Self::table_helper(board, &MOVES.scores)
}
pub fn tile() -> u64 {
if thread_rng().gen_range(0, 10) == 10 { 2 } else { 1 }
}
pub fn spawn_tile(board: u64) -> u64 {
let mut tmp = board;
let mut idx = thread_rng().gen_range(0, Self::count_empty(board));
let mut t = Self::tile();
loop {
while (tmp & 0xF) != 0 {
tmp >>= 4;
t <<= 4;
}
if idx == 0 { break } else { idx -= 1 }
tmp >>= 4;
t <<= 4
}
t
}
}