initial commit
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from blokus_gym.core.bots import GreedyBot, GreedyCornersBot, RandomBot
|
||||
from blokus_gym.core.game import BlokusGame
|
||||
|
||||
|
||||
class TestBlokusGame:
|
||||
def test_game_creation(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
assert game.board_size == 20
|
||||
assert game.num_players == 4
|
||||
assert game.current_player == 0
|
||||
assert len(game.players) == 4
|
||||
|
||||
def test_reset(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
assert game.current_player == 0
|
||||
for player in game.players:
|
||||
assert len(player.available_pieces) == 21
|
||||
|
||||
def test_action_space_size(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
assert game.num_actions > 0
|
||||
|
||||
def test_valid_actions_initial(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
valid = game.get_valid_actions(0)
|
||||
assert np.any(valid)
|
||||
|
||||
def test_play_first_move_corner(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
valid = game.get_valid_actions(0)
|
||||
valid_indices = np.where(valid)[0]
|
||||
action = valid_indices[0]
|
||||
assert game.play_move(0, action)
|
||||
assert game.board.grid.sum() > 0
|
||||
|
||||
def test_invalid_action(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
# Use an action that's likely invalid (very high index)
|
||||
assert not game.play_move(0, game.num_actions - 1)
|
||||
|
||||
def test_next_player(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
assert game.current_player == 0
|
||||
game.next_player()
|
||||
assert game.current_player == 1
|
||||
game.next_player()
|
||||
assert game.current_player == 2
|
||||
game.next_player()
|
||||
assert game.current_player == 3
|
||||
game.next_player()
|
||||
assert game.current_player == 0
|
||||
|
||||
def test_has_valid_moves(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
assert game.has_valid_moves(0)
|
||||
|
||||
def test_is_game_over(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
assert not game.is_game_over()
|
||||
|
||||
def test_get_scores(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
scores = game.get_scores()
|
||||
assert len(scores) == 4
|
||||
for score in scores:
|
||||
assert isinstance(score, (int, float))
|
||||
|
||||
def test_get_action_mask(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
mask = game.get_action_mask(0)
|
||||
assert mask.shape == (game.num_actions,)
|
||||
assert mask.dtype == bool
|
||||
|
||||
def test_get_current_observation(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
obs = game.get_current_observation()
|
||||
assert "board" in obs
|
||||
assert "pieces" in obs
|
||||
assert "corners" in obs
|
||||
assert obs["board"].shape == (20, 20)
|
||||
assert obs["pieces"].shape == (21,)
|
||||
assert obs["corners"].shape == (20, 20)
|
||||
|
||||
def test_two_player_game(self):
|
||||
game = BlokusGame(board_size=14, num_players=2)
|
||||
game.reset()
|
||||
assert game.num_players == 2
|
||||
assert game.has_valid_moves(0)
|
||||
|
||||
def test_three_player_game(self):
|
||||
game = BlokusGame(board_size=20, num_players=3)
|
||||
game.reset()
|
||||
assert game.num_players == 3
|
||||
|
||||
def test_copy(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
game.play_move(0, 0)
|
||||
game_copy = game.copy()
|
||||
assert game_copy.current_player == game.current_player
|
||||
|
||||
def test_play_multiple_moves(self):
|
||||
game = BlokusGame(board_size=20, num_players=2)
|
||||
game.reset()
|
||||
# Play a few moves
|
||||
for _ in range(5):
|
||||
valid = game.get_valid_actions(game.current_player)
|
||||
valid_indices = np.where(valid)[0]
|
||||
if len(valid_indices) > 0:
|
||||
game.play_move(game.current_player, valid_indices[0])
|
||||
game.next_player()
|
||||
else:
|
||||
break
|
||||
|
||||
def test_get_winners_not_over(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
assert game.get_winners() is None
|
||||
|
||||
def test_valid_move_first_corner_only(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
from blokus_gym.core.pieces import Move
|
||||
# First move must be in corner (0, 0) for player 0
|
||||
move = Move(piece_id=0, orientation_id=0, x=0, y=0)
|
||||
assert game.valid_move(0, move)
|
||||
# Try a non-corner first move
|
||||
move2 = Move(piece_id=0, orientation_id=0, x=5, y=5)
|
||||
assert not game.valid_move(0, move2)
|
||||
|
||||
|
||||
class TestBots:
|
||||
def test_random_bot(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
bot = RandomBot(player_idx=0, seed=42)
|
||||
valid = game.get_valid_actions(0)
|
||||
action = bot.select_action(game, valid)
|
||||
assert action is not None
|
||||
assert valid[action]
|
||||
|
||||
def test_greedy_bot(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
bot = GreedyBot(player_idx=0)
|
||||
valid = game.get_valid_actions(0)
|
||||
action = bot.select_action(game, valid)
|
||||
assert action is not None
|
||||
assert valid[action]
|
||||
|
||||
def test_greedy_corners_bot(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
bot = GreedyCornersBot(player_idx=0)
|
||||
valid = game.get_valid_actions(0)
|
||||
action = bot.select_action(game, valid)
|
||||
assert action is not None
|
||||
assert valid[action]
|
||||
|
||||
def test_bot_no_valid_moves(self):
|
||||
game = BlokusGame(board_size=20, num_players=4)
|
||||
game.reset()
|
||||
bot = RandomBot(player_idx=0, seed=42)
|
||||
valid = np.zeros(game.num_actions, dtype=bool)
|
||||
action = bot.select_action(game, valid)
|
||||
assert action is None
|
||||
|
||||
def test_bots_play_game(self):
|
||||
game = BlokusGame(board_size=7, num_players=2)
|
||||
game.reset()
|
||||
bots = [RandomBot(player_idx=i, seed=i) for i in range(2)]
|
||||
steps = 0
|
||||
while not game.is_game_over() and steps < 100:
|
||||
bot = bots[game.current_player]
|
||||
valid = game.get_valid_actions(game.current_player)
|
||||
action = bot.select_action(game, valid)
|
||||
if action is not None:
|
||||
game.play_move(game.current_player, action)
|
||||
game.next_player()
|
||||
steps += 1
|
||||
assert game.is_game_over()
|
||||
Reference in New Issue
Block a user