diff --git a/Dockerfile b/Dockerfile index ce6f5f3..1b9fbcf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ FROM python:3 RUN mkdir /code WORKDIR /code +ADD ./ /code RUN pip install -r requirements.txt diff --git a/requirements.txt b/requirements.txt index a8e7b5b..0d86898 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -pycodestyle \ No newline at end of file +pycodestyle +requests \ No newline at end of file diff --git a/src/game/game.py b/src/game/game.py index 7d4a1a0..cdb3390 100644 --- a/src/game/game.py +++ b/src/game/game.py @@ -6,7 +6,9 @@ from .turn import Turn from .state import State from .tools import GRID_SIZE, PIECES_NUMBER +from .players import Players from .ui import UIRender +from .network import call_api class Game: @@ -15,6 +17,20 @@ class Game: def start(self): game_state = State() + ui = UIRender() + players = Players() + + ui.clear_terminal() + players_count = ui.prompt_playing_mode() + if players_count == 1 : + players.change_player_name(1, ui.prompt_player_name(1, players.player1_name)) + players.change_player_name(2, "Computer") + elif players_count == 0 : + players.change_player_name(1, "Computer 1") + players.change_player_name(2, "Computer 2") + elif players_count == 2 : + players.change_player_name(1, ui.prompt_player_name(1, players.player1_name)) + players.change_player_name(2, ui.prompt_player_name(2, players.player2_name)) initial_state = "" try: @@ -28,18 +44,56 @@ def start(self): if (len(game_state.message) > 0): game_state.message += """\nValid sample : --state='{"grid" : {"A2": 10,"C1":3,"D1":12},"turn" :{"player" : 1,"selected" : 7}}'""" - ui = UIRender() - while not game_state.check_draw(): - if game_state.check_winner(): - break - ui.display_game(game_state) - if not game_state.is_selected_piece(): - ui.prompt_piece_selection(game_state) - game_state.switch_player() - ui.display_game(game_state) - ui.prompt_piece_location(game_state) - - ui.display_game(game_state) + replay = True + while replay: + if players_count == 0: + game_state.game_turn.selected_piece = 1 + while not game_state.check_draw(): + ui.display_game(game_state, players) + + if call_api(game_state) == False: + game_state.message += "Error, server didn't respond : Game end" + break + game_state.switch_player() + if game_state.check_winner(players): + break + elif players_count == 1: + while not game_state.check_draw(): + if game_state.check_winner(players): + break + ui.display_game(game_state, players) + if not game_state.is_selected_piece(): + if not self.is_computer_turn(players_count, game_state): + ui.prompt_piece_selection(game_state, players) + game_state.switch_player() + ui.display_game(game_state, players) + elif self.is_computer_turn(players_count, game_state): + game_state.switch_player() + ui.display_game(game_state, players) + if self.is_computer_turn(players_count, game_state): + if call_api(game_state) == False: + game_state.message += "Error, server didn't respond : Game end" + break + if game_state.check_winner(players): + break + else: + ui.prompt_piece_location(game_state, players) + elif players_count == 2: + while not game_state.check_draw(): + if game_state.check_winner(players): + break + ui.display_game(game_state, players) + if not game_state.is_selected_piece(): + ui.prompt_piece_selection(game_state, players) + game_state.switch_player() + ui.display_game(game_state, players) + ui.prompt_piece_location(game_state, players) + + ui.display_game(game_state, players) + replay = ui.prompt_restart() + + if replay: + game_state = State() def parse_state_from_args(self, argv): if 'quarto.py' in argv[0]: @@ -54,3 +108,7 @@ def parse_state_from_args(self, argv): raise ValueError("[The state to load is not wellformed] : Ignored") return parameter + + def is_computer_turn(self, players_count, game_state): + return players_count == 0 \ + or (players_count == 1 and not game_state.game_turn.player_one_active) diff --git a/src/game/network.py b/src/game/network.py new file mode 100644 index 0000000..fb390f4 --- /dev/null +++ b/src/game/network.py @@ -0,0 +1,26 @@ +import requests +import json +from .state import State + + + + +def call_api(state): + print("call_api") + + url = 'http://localhost:8080/suggestMove' + post_fields = "{\"Grid\":" + str(state.grid).replace(".","0") .replace("'","") + ", \"Piece\":" + str(state.game_turn.selected_piece) + "}" + + try: + r = requests.post(url, data=post_fields) + if r.status_code == 200: + new_computer_state = json.loads(r.text) + computer_piece = state.game_turn.selected_piece + state.grid[new_computer_state["Move"][0]][new_computer_state["Move"][1]] = computer_piece + state.remaining_pieces.remove(computer_piece) + state.game_turn.selected_piece = new_computer_state["Piece"] + return True + except (TypeError, ValueError, UnboundLocalError): + return False + return False + diff --git a/src/game/players.py b/src/game/players.py new file mode 100644 index 0000000..67a195c --- /dev/null +++ b/src/game/players.py @@ -0,0 +1,13 @@ +class Players: + + """Definition of the users names:""" + + def __init__(self, initial_state=""): + self.player1_name = "Player 1" + self.player2_name = "Player 2" + + def change_player_name(self, player_id, player_name): + if player_id == 1: + self.player1_name = player_name + if player_id == 2: + self.player2_name = player_name diff --git a/src/game/state.py b/src/game/state.py index 9c9624b..6acf065 100644 --- a/src/game/state.py +++ b/src/game/state.py @@ -1,6 +1,7 @@ from .turn import Turn from .piece import Piece from .tools import GRID_SIZE, PIECES_NUMBER, EMPTY_POSITION, get_coordinates +from .players import Players class State: @@ -66,13 +67,13 @@ def check_position_availability(self, x, y): def switch_player(self): self.game_turn.player_one_active = not self.game_turn.player_one_active - def check_winner(self): + def check_winner(self, players): if self.check_raws_winning() or self.check_columns_winning() or self.check_diags_winning(): if self.game_turn.player_one_active: - player_name = "Player 1" + player_name = players.player1_name else: - player_name = "Player 2" - self.message = player_name + " WINNNNS !!!!!!\n" + player_name = players.player2_name + self.message = player_name + " WINNNNS !!!!!! (with a score of " + str(len(self.remaining_pieces)) + ")\n" return True return False diff --git a/src/game/tests/test_grid.py b/src/game/tests/test_grid.py index 39ed680..52cc419 100644 --- a/src/game/tests/test_grid.py +++ b/src/game/tests/test_grid.py @@ -1,6 +1,7 @@ import unittest from ..state import State from ..tools import GRID_SIZE, EMPTY_POSITION +from ..players import Players from ..game import Game from ..ui import UIRender from ..piece import Piece @@ -131,7 +132,7 @@ def test_win_on_grid(self): "D4":4},"turn" :{"player" : 1}}"""] initial_state = Game().parse_state_from_args(arg) game_state.import_state_from_dictionary(initial_state) - self.assertTrue(game_state.check_winner()) + self.assertTrue(game_state.check_winner(Players())) def test_place_piece_is_placed_at_good_position(self): game_state = State() diff --git a/src/game/tests/test_players.py b/src/game/tests/test_players.py index 9f36cfd..72cce52 100644 --- a/src/game/tests/test_players.py +++ b/src/game/tests/test_players.py @@ -1,6 +1,7 @@ import unittest from ..turn import Turn from ..ui import UIRender +from ..players import Players class TestPlayersMethods(unittest.TestCase): @@ -15,8 +16,9 @@ def test_init_game_turn_should_start_without_selected_piece(self): def test_players_to_string_should_create_the_reference_string(self): game_turn = Turn() - players_display = UIRender().players_to_string(game_turn) - reference_string = "=> Player 1 <= Player 2 " + players = Players() + players_display = UIRender().players_to_string(game_turn, players) + reference_string = "=> " + players.player1_name + " <= " + players.player2_name + " " self.assertEqual(players_display, reference_string) diff --git a/src/game/ui.py b/src/game/ui.py index b45c500..dae1a1c 100644 --- a/src/game/ui.py +++ b/src/game/ui.py @@ -13,7 +13,7 @@ class UIRender: """Definition of the user interface and the interactions:""" - def prompt_piece_selection(self, game_state): + def prompt_piece_selection(self, game_state, players): while True: try: piece = int(input("Choose the next piece of the opponent : ")) @@ -22,12 +22,12 @@ def prompt_piece_selection(self, game_state): game_state.message = "You must choose a number available in the list" except ValueError: game_state.message = "You have to type number between 1 and " + str(PIECES_NUMBER) - self.display_game(game_state) + self.display_game(game_state, players) except KeyboardInterrupt: print("\nGame aborted") exit() - def prompt_piece_location(self, game_state): + def prompt_piece_location(self, game_state, players): while True: try: position = input("Choose the position to place your piece : ") @@ -35,11 +35,39 @@ def prompt_piece_location(self, game_state): return except ValueError: game_state.message = "You have to type a free coordinate using this format : 'A1'" - self.display_game(game_state) + self.display_game(game_state, players) except KeyboardInterrupt: print("\nGame aborted") exit() + def prompt_player_name(self, player_id, default_name): + try: + name = input("Player " + str(player_id) + ", what is your name (" + default_name + " if empty) ? ").strip() + if len(name) > 0: + return name + return default_name + except KeyboardInterrupt: + print("\nGame aborted") + exit() + + def prompt_restart(self): + try: + return input("Let's play again ? (y/n) ") == 'y' + except KeyboardInterrupt: + print("\nGame aborted") + exit() + + def prompt_playing_mode(self): + + try: + players_count = input("How many players : (0, 1 or 2, default is 1) ") + if players_count in ['0','1','2']: + return int(players_count) + return 1 + except KeyboardInterrupt: + print("\nGame aborted") + exit() + def piece_to_string(self, piece_id): piece_display = str(piece_id) pieces = list(filter(lambda x: x.id == piece_id, pieces_list_definition)) @@ -62,8 +90,6 @@ def piece_to_string(self, piece_id): if pieces[0].light_color: piece_display = "\033[47m" + piece_display - else: - piece_display = "\033[100m" + piece_display return ' ' + piece_display + " \033[0m" @@ -91,7 +117,6 @@ def pieces_to_string(self, remaining_pieces, game_turn): if piece_id >= 10: display_string += ' ' display_string += ' . ' - display_string += ' ' display_string += '\n' for piece_id in range(1, PIECES_NUMBER + 1): @@ -102,7 +127,7 @@ def pieces_to_string(self, remaining_pieces, game_turn): if piece_id >= 10: display_string += ' ' display_string += ' ' - display_string += ' ' + display_string += ' ' return display_string def selected_piece_to_string(self, piece_number, game_turn): @@ -110,9 +135,9 @@ def selected_piece_to_string(self, piece_number, game_turn): return "[" + str(piece_number) + "]" return " " + str(piece_number) + " " - def players_to_string(self, game_turn): - player_1 = self.selected_player_to_string('Player 1', game_turn.player_one_active) - player_2 = self.selected_player_to_string('Player 2', not game_turn.player_one_active) + def players_to_string(self, game_turn, players): + player_1 = self.selected_player_to_string(players.player1_name, game_turn.player_one_active) + player_2 = self.selected_player_to_string(players.player2_name, not game_turn.player_one_active) return player_1 + " " + player_2 def selected_player_to_string(self, player_name, selected): @@ -123,7 +148,7 @@ def selected_player_to_string(self, player_name, selected): def clear_terminal(self): subprocess.call(["printf", "'\033c'"]) - def display_game(self, game_state): + def display_game(self, game_state, players): self.clear_terminal() print() print("\033[32;1mWelcome to Quarto-Py\033[0m") @@ -131,7 +156,7 @@ def display_game(self, game_state): print() print(self.grid_to_string(game_state.grid)) print() - print(self.players_to_string(game_state.game_turn)) + print(self.players_to_string(game_state.game_turn, players)) print() print(self.pieces_to_string(game_state.remaining_pieces, game_state.game_turn)) print()