executables.cli
1import random 2import pathlib 3 4from typing import Optional 5 6import click 7from schnapsen.alternative_engines.ace_one_engine import AceOneGamePlayEngine 8 9from schnapsen.bots import MLDataBot, train_ML_model, MLPlayingBot, RandBot 10 11from schnapsen.bots.example_bot import ExampleBot 12 13from schnapsen.game import (Bot, GamePlayEngine, Move, PlayerPerspective, 14 SchnapsenGamePlayEngine, TrumpExchange) 15from schnapsen.alternative_engines.twenty_four_card_schnapsen import TwentyFourSchnapsenGamePlayEngine 16 17from schnapsen.bots.rdeep import RdeepBot 18 19 20@click.group() 21def main() -> None: 22 """Various Schnapsen Game Examples""" 23 24 25def play_games_and_return_stats(engine: GamePlayEngine, bot1: Bot, bot2: Bot, pairs_of_games: int) -> int: 26 """ 27 Play 2 * pairs_of_games games between bot1 and bot2, using the SchnapsenGamePlayEngine, and return how often bot1 won. 28 Prints progress. Each pair of games is the same original dealing of cards, but the roles of the bots are swapped. 29 """ 30 bot1_wins: int = 0 31 lead, follower = bot1, bot2 32 for game_pair in range(pairs_of_games): 33 for lead, follower in [(bot1, bot2), (bot2, bot1)]: 34 winner, _, _ = engine.play_game(lead, follower, random.Random(game_pair)) 35 if winner == bot1: 36 bot1_wins += 1 37 if game_pair > 0 and (game_pair + 1) % 500 == 0: 38 print(f"Progress: {game_pair + 1}/{pairs_of_games} game pairs played") 39 return bot1_wins 40 41 42@main.command() 43def random_game() -> None: 44 engine = SchnapsenGamePlayEngine() 45 bot1 = RandBot(random.Random(12112121)) 46 bot2 = RandBot(random.Random(464566)) 47 for i in range(1000): 48 winner_id, game_points, score = engine.play_game(bot1, bot2, random.Random(i)) 49 print(f"Game ended. Winner is {winner_id} with {game_points} points, score {score}") 50 51 52class NotificationExampleBot(Bot): 53 54 def get_move(self, perspective: PlayerPerspective, leader_move: Optional[Move]) -> Move: 55 moves = perspective.valid_moves() 56 return moves[0] 57 58 def notify_game_end(self, won: bool, perspective: PlayerPerspective) -> None: 59 print(f'result {"win" if won else "lost"}') 60 print(f'I still have {len(perspective.get_hand())} cards left') 61 62 def notify_trump_exchange(self, move: TrumpExchange) -> None: 63 print(f"That trump exchanged! {move.jack}") 64 65 66@main.command() 67def notification_game() -> None: 68 engine = TwentyFourSchnapsenGamePlayEngine() 69 bot1 = NotificationExampleBot() 70 bot2 = RandBot(random.Random(464566)) 71 engine.play_game(bot1, bot2, random.Random(94)) 72 73 74class HistoryBot(Bot): 75 def get_move(self, perspective: PlayerPerspective, leader_move: Optional[Move]) -> Move: 76 history = perspective.get_game_history() 77 print(f'the initial state of this game was {history[0][0]}') 78 moves = perspective.valid_moves() 79 return moves[0] 80 81 82@main.command() 83def try_example_bot_game() -> None: 84 engine = SchnapsenGamePlayEngine() 85 bot1 = ExampleBot() 86 bot2 = RandBot(random.Random(464566)) 87 winner, points, score = engine.play_game(bot1, bot2, random.Random(1)) 88 print(f"Winner is: {winner}, with {points} points, score {score}!") 89 90 91@main.command() 92def rdeep_game() -> None: 93 bot1: Bot 94 bot2: Bot 95 engine = SchnapsenGamePlayEngine() 96 rdeep = bot1 = RdeepBot(num_samples=16, depth=4, rand=random.Random(4564654644)) 97 bot2 = RandBot(random.Random(464566)) 98 wins = 0 99 amount = 100 100 for game_number in range(1, amount + 1): 101 if game_number % 2 == 0: 102 bot1, bot2 = bot2, bot1 103 winner_id, _, _ = engine.play_game(bot1, bot2, random.Random(game_number)) 104 if winner_id == rdeep: 105 wins += 1 106 if game_number % 10 == 0: 107 print(f"won {wins} out of {game_number}") 108 109 110@main.group() 111def ml() -> None: 112 """Commands for the ML bot""" 113 114 115@ml.command() 116def create_replay_memory_dataset() -> None: 117 # define replay memory database creation parameters 118 num_of_games: int = 10000 119 replay_memory_dir: str = 'ML_replay_memories' 120 replay_memory_filename: str = 'random_random_10k_games.txt' 121 replay_memory_location = pathlib.Path(replay_memory_dir) / replay_memory_filename 122 123 bot_1_behaviour: Bot = RandBot(random.Random(5234243)) 124 # bot_1_behaviour: Bot = RdeepBot(num_samples=4, depth=4, rand=random.Random(4564654644)) 125 bot_2_behaviour: Bot = RandBot(random.Random(54354)) 126 # bot_2_behaviour: Bot = RdeepBot(num_samples=4, depth=4, rand=random.Random(68438)) 127 delete_existing_older_dataset = False 128 129 # check if needed to delete any older versions of the dataset 130 if delete_existing_older_dataset and replay_memory_location.exists(): 131 print(f"An existing dataset was found at location '{replay_memory_location}', which will be deleted as selected.") 132 replay_memory_location.unlink() 133 134 # in any case make sure the directory exists 135 replay_memory_location.parent.mkdir(parents=True, exist_ok=True) 136 137 # create new replay memory dataset, according to the behaviour of the provided bots and the provided random seed 138 engine = SchnapsenGamePlayEngine() 139 replay_memory_recording_bot_1 = MLDataBot(bot_1_behaviour, replay_memory_location=replay_memory_location) 140 replay_memory_recording_bot_2 = MLDataBot(bot_2_behaviour, replay_memory_location=replay_memory_location) 141 for i in range(1, num_of_games + 1): 142 if i % 500 == 0: 143 print(f"Progress: {i}/{num_of_games}") 144 engine.play_game(replay_memory_recording_bot_1, replay_memory_recording_bot_2, random.Random(i)) 145 print(f"Replay memory dataset recorder for {num_of_games} games.\nDataset is stored at: {replay_memory_location}") 146 147 148@ml.command() 149def train_model() -> None: 150 # directory where the replay memory is saved 151 replay_memory_filename: str = 'random_random_10k_games.txt' 152 # filename of replay memory within that directory 153 replay_memories_directory: str = 'ML_replay_memories' 154 # Whether to train a complicated Neural Network model or a simple one. 155 # Tips: a neural network usually requires bigger datasets to be trained on, and to play with the parameters of the model. 156 # Feel free to play with the hyperparameters of the model in file 'ml_bot.py', function 'train_ML_model', 157 # under the code of body of the if statement 'if use_neural_network:' 158 replay_memory_location = pathlib.Path(replay_memories_directory) / replay_memory_filename 159 model_name: str = 'simple_model' 160 model_dir: str = "ML_models" 161 model_location = pathlib.Path(model_dir) / model_name 162 overwrite: bool = False 163 164 if overwrite and model_location.exists(): 165 print(f"Model at {model_location} exists already and will be overwritten as selected.") 166 model_location.unlink() 167 168 train_ML_model(replay_memory_location=replay_memory_location, model_location=model_location, 169 model_class='LR') 170 171 172@ml.command() 173def try_bot_game() -> None: 174 engine = SchnapsenGamePlayEngine() 175 model_dir: str = 'ML_models' 176 model_name: str = 'simple_model' 177 model_location = pathlib.Path(model_dir) / model_name 178 bot1: Bot = MLPlayingBot(model_location=model_location) 179 bot2: Bot = RandBot(random.Random(464566)) 180 number_of_games: int = 10000 181 pairs_of_games = number_of_games // 2 182 183 # play games with altering leader position on first rounds 184 ml_bot_wins_against_random = play_games_and_return_stats(engine=engine, bot1=bot1, bot2=bot2, pairs_of_games=pairs_of_games) 185 print(f"The ML bot with name {model_name}, won {ml_bot_wins_against_random} times out of {number_of_games} games played.") 186 187 188@main.command() 189def game_24() -> None: 190 engine = TwentyFourSchnapsenGamePlayEngine() 191 bot1 = RandBot(random.Random(12112121)) 192 bot2 = RandBot(random.Random(464566)) 193 for i in range(1000): 194 winner_id, game_points, score = engine.play_game(bot1, bot2, random.Random(i)) 195 print(f"Game ended. Winner is {winner_id} with {game_points} points, score {score}") 196 197 198@main.command() 199def game_ace_one() -> None: 200 engine = AceOneGamePlayEngine() 201 bot1 = RandBot(random.Random(12112121)) 202 bot2 = RdeepBot(num_samples=16, depth=4, rand=random.Random(464566)) 203 for i in range(1000): 204 winner_id, game_points, score = engine.play_game(bot1, bot2, random.Random(i)) 205 print(f"Game ended. Winner is {winner_id} with {game_points} points, score {score}") 206 207 208if __name__ == "__main__": 209 main()
Various Schnapsen Game Examples
26def play_games_and_return_stats(engine: GamePlayEngine, bot1: Bot, bot2: Bot, pairs_of_games: int) -> int: 27 """ 28 Play 2 * pairs_of_games games between bot1 and bot2, using the SchnapsenGamePlayEngine, and return how often bot1 won. 29 Prints progress. Each pair of games is the same original dealing of cards, but the roles of the bots are swapped. 30 """ 31 bot1_wins: int = 0 32 lead, follower = bot1, bot2 33 for game_pair in range(pairs_of_games): 34 for lead, follower in [(bot1, bot2), (bot2, bot1)]: 35 winner, _, _ = engine.play_game(lead, follower, random.Random(game_pair)) 36 if winner == bot1: 37 bot1_wins += 1 38 if game_pair > 0 and (game_pair + 1) % 500 == 0: 39 print(f"Progress: {game_pair + 1}/{pairs_of_games} game pairs played") 40 return bot1_wins
Play 2 * pairs_of_games games between bot1 and bot2, using the SchnapsenGamePlayEngine, and return how often bot1 won. Prints progress. Each pair of games is the same original dealing of cards, but the roles of the bots are swapped.
53class NotificationExampleBot(Bot): 54 55 def get_move(self, perspective: PlayerPerspective, leader_move: Optional[Move]) -> Move: 56 moves = perspective.valid_moves() 57 return moves[0] 58 59 def notify_game_end(self, won: bool, perspective: PlayerPerspective) -> None: 60 print(f'result {"win" if won else "lost"}') 61 print(f'I still have {len(perspective.get_hand())} cards left') 62 63 def notify_trump_exchange(self, move: TrumpExchange) -> None: 64 print(f"That trump exchanged! {move.jack}")
The Bot baseclass. Derive your own bots from this class and implement the get_move method to use it in games. Besides the get_move method, it is also possible to override notify_trump_exchange and notify_game_end to get notified when these events happen.
Parameters
- name: (str): Optionally, specify a name for the bot. Defaults to None.
55 def get_move(self, perspective: PlayerPerspective, leader_move: Optional[Move]) -> Move: 56 moves = perspective.valid_moves() 57 return moves[0]
Get the move this Bot wants to play. This is the method that gets called by the engine to get the bot's next move. If this Bot is leading, the leader_move will be None. If this both is following, the leader_move will contain the move the opponent just played
Parameters
- perspective: (PlayerPerspective): The PlayerPerspective which contains the information on the current state of the game from the perspective of this player
- leader_move: (Optional[Move]): The move made by the leader of the trick. This is None if this bot is the leader.
59 def notify_game_end(self, won: bool, perspective: PlayerPerspective) -> None: 60 print(f'result {"win" if won else "lost"}') 61 print(f'I still have {len(perspective.get_hand())} cards left')
The engine will call this method when the game ends. Override this method to get notified about the end of the game.
Parameters
- won: (bool): Did this bot win the game?
- perspective: (PlayerPerspective) The final perspective of the game.
63 def notify_trump_exchange(self, move: TrumpExchange) -> None: 64 print(f"That trump exchanged! {move.jack}")
The engine will call this method when a trump exchange is made. Overide this method to get notified about trump exchanges. Note that this notification is sent to both bots.
Parameters
- move: (TrumpExchange): the Trump Exchange move that was played.
Inherited Members
75class HistoryBot(Bot): 76 def get_move(self, perspective: PlayerPerspective, leader_move: Optional[Move]) -> Move: 77 history = perspective.get_game_history() 78 print(f'the initial state of this game was {history[0][0]}') 79 moves = perspective.valid_moves() 80 return moves[0]
The Bot baseclass. Derive your own bots from this class and implement the get_move method to use it in games. Besides the get_move method, it is also possible to override notify_trump_exchange and notify_game_end to get notified when these events happen.
Parameters
- name: (str): Optionally, specify a name for the bot. Defaults to None.
76 def get_move(self, perspective: PlayerPerspective, leader_move: Optional[Move]) -> Move: 77 history = perspective.get_game_history() 78 print(f'the initial state of this game was {history[0][0]}') 79 moves = perspective.valid_moves() 80 return moves[0]
Get the move this Bot wants to play. This is the method that gets called by the engine to get the bot's next move. If this Bot is leading, the leader_move will be None. If this both is following, the leader_move will contain the move the opponent just played
Parameters
- perspective: (PlayerPerspective): The PlayerPerspective which contains the information on the current state of the game from the perspective of this player
- leader_move: (Optional[Move]): The move made by the leader of the trick. This is None if this bot is the leader.
Inherited Members
Commands for the ML bot