| import numpy as np
|
| from src.bitboard import get_bit, bit_to_row_col, popcount
|
|
|
| class OthelloGame:
|
| def __init__(self):
|
|
|
|
|
|
|
| self.player_bb = 0x0000000810000000
|
| self.opponent_bb = 0x0000001008000000
|
| self.turn = 1
|
|
|
| def get_valid_moves(self, player, opponent):
|
| """Calculates valid moves for 'player' against 'opponent'."""
|
| empty = ~(player | opponent) & 0xFFFFFFFFFFFFFFFF
|
|
|
|
|
|
|
|
|
| mask_h = 0x0101010101010101
|
| mask_a = 0x8080808080808080
|
|
|
|
|
| shifts = [
|
| (lambda x: (x & ~mask_h) >> 1),
|
| (lambda x: (x & ~mask_a) << 1),
|
| (lambda x: (x << 8) & 0xFFFFFFFFFFFFFFFF),
|
| (lambda x: (x >> 8) & 0xFFFFFFFFFFFFFFFF),
|
| (lambda x: (x & ~mask_h) << 7),
|
| (lambda x: (x & ~mask_a) << 9),
|
| (lambda x: (x & ~mask_h) >> 9),
|
| (lambda x: (x & ~mask_a) >> 7)
|
| ]
|
|
|
| valid_moves = 0
|
| for shift_func in shifts:
|
| candidates = shift_func(player) & opponent
|
| for _ in range(6):
|
| candidates |= shift_func(candidates) & opponent
|
| valid_moves |= shift_func(candidates) & empty
|
|
|
| return valid_moves
|
|
|
| def apply_move(self, player, opponent, move_bit):
|
| """Calculates new boards after move_bit."""
|
| if move_bit == 0:
|
| return player, opponent
|
|
|
| flipped = 0
|
| mask_h = 0x0101010101010101
|
| mask_a = 0x8080808080808080
|
|
|
| shifts = [
|
| (lambda x: (x & ~mask_h) >> 1),
|
| (lambda x: (x & ~mask_a) << 1),
|
| (lambda x: (x << 8) & 0xFFFFFFFFFFFFFFFF),
|
| (lambda x: (x >> 8) & 0xFFFFFFFFFFFFFFFF),
|
| (lambda x: (x & ~mask_h) << 7),
|
| (lambda x: (x & ~mask_a) << 9),
|
| (lambda x: (x & ~mask_h) >> 9),
|
| (lambda x: (x & ~mask_a) >> 7)
|
| ]
|
|
|
| for shift_func in shifts:
|
| mask = shift_func(move_bit)
|
| potential_flips = 0
|
| while mask & opponent:
|
| potential_flips |= mask
|
| mask = shift_func(mask)
|
| if mask & player:
|
| flipped |= potential_flips
|
|
|
| new_player = player | move_bit | flipped
|
| new_opponent = opponent & ~flipped
|
| return new_player, new_opponent
|
|
|
| def play_move(self, move_bit):
|
| if move_bit != 0:
|
| self.player_bb, self.opponent_bb = self.apply_move(self.player_bb, self.opponent_bb, move_bit)
|
|
|
|
|
| self.player_bb, self.opponent_bb = self.opponent_bb, self.player_bb
|
| self.turn *= -1
|
|
|
| def is_terminal(self):
|
| p_moves = self.get_valid_moves(self.player_bb, self.opponent_bb)
|
| o_moves = self.get_valid_moves(self.opponent_bb, self.player_bb)
|
| return (p_moves == 0) and (o_moves == 0)
|
|
|