diff --git a/game_of_sticks.py b/game_of_sticks.py new file mode 100644 index 0000000..7a3b38e --- /dev/null +++ b/game_of_sticks.py @@ -0,0 +1,223 @@ +import os +import random + + +class SticksGame: + # X Print out rules + # X create and start turns + # X remove turn sticks from pile + # X display remaining sticks + # X Stop when player starts turn with one stick in pile + def __init__(self, player1, player2): + self.pile = 20 + self.player1 = player1 + self.player2 = player2 + self.current_player = player1 + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def display_pile(self): + return "Pile contains {} sticks.".format(self.pile) \ + + (" / " * self.pile) + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def switch_players(self): + if self.current_player == self.player1: + self.current_player = self.player2 + else: + self.current_player = self.player1 + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def remove(self, silent=False): + if not silent: + print("Currently {}'s turn\n".format(self.current_player.name)) + self.current_player.pick_up(self.pile) + self.pile -= self.current_player.sticks + self.current_player.sticks = 0 + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def winner(self): + if self.pile > 0: + return None + else: + return self.current_player + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def start(self): + self.pile = 20 + while not self.winner(): + os.system('clear') + print(self.display_pile()) + self.remove() + self.switch_players() + + self.current_player.win_state = True + os.system('clear') + print("Game Over!") + self.player1.win_check() + self.player2.win_check() + self.play_again() + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def play_again(self): + go_again = input("Would you like to play again (y/n)? ").lower() + + if go_again[0] == "y": + self.start() + elif go_again[0] == "n": + return + else: + self.play_again() + +############################################################################### + + +class Player: + # X decide how many sticks to pick up + def __init__(self, name="Player"): + self.name = name + self.sticks = 0 + self.win_state = False + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def pick_up(self, remaining): + try: + num = int(input("How many sticks would you like to pick up? ")) + + if num in [1, 2, 3]: + self.sticks = num + else: + print("Invalid choice!") + return self.pick_up(remaining) + except ValueError: + print("Invalid choice!") + return self.pick_up(remaining) + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def win_check(self): + if self.win_state: + print("The winner is {}!\n".format(self.name)) + self.win_state = False + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def __repr__(self): + return self.name + +############################################################################### + + +class AIPlayer(Player): + def __init__(self, name="AI"): + super().__init__(name) + self.chosen = {} + self.possibilities = {} + self.tally = 0 + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def choices(self, remaining): + return self.possibilities.setdefault(remaining, [1, 2, 3]) + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def pick_up(self, remaining): + choice = random.choice(self.choices(remaining)) + self.chosen[remaining] = choice + self.sticks = choice + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def win_check(self, silent=False): + if self.win_state: + if not silent: + print("The winner is {}!\n".format(self.name)) + self.integrate_win() + self.tally += 1 + self.win_state = False + else: + self.chosen = {} + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + + def integrate_win(self): + """for each choice made, add that choice into the + possible choices""" + for key in self.chosen: + self.possibilities[key].append(self.chosen[key]) + + self.chosen = {} + + +############################################################################### + + +class AISticksGame(SticksGame): + def start(self): + count = 100000 + while count > 0: + self.pile = 20 + while not self.winner(): + self.remove(silent=True) + self.switch_players() + + self.current_player.win_state = True + self.player1.win_check(silent=True) + self.player2.win_check(silent=True) + count -= 1 + if self.player1.tally >= self.player2.tally: + return self.player1 + else: + return self.player2 + + +############################################################################### + + +def menu(): + os.system('clear') + print("Welcome to the Game of Sticks!\n\ +In the Game of Sticks there is a heap of sticks on a board.\n\ +On their turn, each player picks up 1 to 3 sticks.\n\ +The one who has to pick the final stick will be the loser.\n") + + set_up = input("Would you like to play against another [h]uman, \ +a [c]omputer, or a [t]rained computer? ").lower() + + if set_up[0] == "h": + player_name1 = input("First Player's Name: ") + player_name2 = input("Second Player's Name: ") + player1 = Player(player_name1) + player2 = Player(player_name2) + game = SticksGame(player1, player2) + game.start() + elif set_up[0] == "c": + player_name = input("Your Name: ") + player1 = Player(player_name) + player2 = AIPlayer() + game = SticksGame(player1, player2) + game.start() + elif set_up[0] == "t": + ai1 = AIPlayer() + ai2 = AIPlayer() + AI_game = AISticksGame(ai1, ai2) + player_name = input("Your Name: ") + player1 = Player(player_name) + player2 = AI_game.start() + game = SticksGame(player1, player2) + game.start() + else: + os.system('clear') + print("Invalid choice, please try again") + return menu() + + +if __name__ == '__main__': + menu() diff --git a/test_game_of_sticks.py b/test_game_of_sticks.py new file mode 100644 index 0000000..ccdc04d --- /dev/null +++ b/test_game_of_sticks.py @@ -0,0 +1,113 @@ +from game_of_sticks import SticksGame, Player, AIPlayer, AISticksGame + + +def test_SticksGame_has_pile(): + game = SticksGame("p1", "p2") + assert game.pile == 20 + + +class TestGame(SticksGame): + def play_again(self): + return None + + +def test_SticksGame_ends(): + game = TestGame("p1", "p2") + assert not game.winner() == "p1" + assert not game.winner() == "p2" + game.pile = 0 + assert game.winner() == "p1" + assert not game.winner() == "p2" + + +def test_SticksGame_displays_pile(): + game = SticksGame("p1", "p2") + assert game.display_pile() == "Pile contains 20 sticks." + game.pile = 3 + assert game.display_pile() == "Pile contains 3 sticks." + + +def test_SticksGame_switches_players(): + game = SticksGame("p1", "p2") + game.switch_players() + assert game.current_player == "p2" + game.switch_players() + assert game.current_player == "p1" + + +class TestPlayer(Player): + def __init__(self): + super().__init__() + self.check = 2 + + def pick_up(self, remaining): + if self.check in [1, 2, 3]: + self.sticks = self.check + + +def test_player_decides_how_many_sticks_to_take(): + player = TestPlayer() + assert player.sticks == 0 + player.pick_up(1) + assert player.sticks == 2 + player.check = 1 + player.pick_up(1) + assert player.sticks == 1 + player.check = 3 + player.pick_up(1) + assert player.sticks == 3 + player.check = 4 + player.pick_up(1) + assert not player.sticks == 4 + + +def test_SticksGame_removes_sticks_from_pile(): + player = TestPlayer() + game = SticksGame(player, player) + assert game.pile == 20 + game.remove() + assert game.pile == 18 + player.check = 4 + game.remove() + assert game.pile == 18 + + +def test_SticksGame_goes_until_winner(): + p1 = TestPlayer() + p2 = TestPlayer() + game = TestGame(p1, p2) + game.start() + print(game.pile) + print(game.current_player) + assert game.winner() is not None + + +def test_AI_test_game(): + ai = AIPlayer() + ai.pick_up(17) + ai.pick_up(12) + ai.pick_up(3) + ai.integrate_win() + assert len(ai.choices(17)) == 4 + ai.pick_up(17) + ai.pick_up(10) + ai.integrate_win() + assert len(ai.choices(17)) == 5 + assert len(ai.choices(10)) == 4 + ai.pick_up(17) + ai.pick_up(10) + ai.win_check() + assert len(ai.choices(17)) == 5 + assert len(ai.choices(10)) == 4 + + +def test_AI_choices(): + """An initial player should have 1, 2, 3 as choices for all numbers.""" + ai = AIPlayer() + assert ai.choices(13) == [1, 2, 3] + +def test_AISticksGame(): + HAL = AIPlayer() + GLaDOS = AIPlayer() + game = AISticksGame(HAL, GLaDOS) + assert game.start() == HAL or game.start() == GLaDOS