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()
main = <Group main>

Various Schnapsen Game Examples

def play_games_and_return_stats( engine: schnapsen.game.GamePlayEngine, bot1: schnapsen.game.Bot, bot2: schnapsen.game.Bot, pairs_of_games: int) -> int:
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.

class NotificationExampleBot(schnapsen.game.Bot):
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.
def get_move( self, perspective: schnapsen.game.PlayerPerspective, leader_move: Optional[schnapsen.game.Move]) -> schnapsen.game.Move:
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.
def notify_game_end(self, won: bool, perspective: schnapsen.game.PlayerPerspective) -> None:
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.
def notify_trump_exchange(self, move: schnapsen.game.TrumpExchange) -> None:
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
schnapsen.game.Bot
Bot
class HistoryBot(schnapsen.game.Bot):
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.
def get_move( self, perspective: schnapsen.game.PlayerPerspective, leader_move: Optional[schnapsen.game.Move]) -> schnapsen.game.Move:
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.
ml = <Group ml>

Commands for the ML bot