Skip to content
This repository was archived by the owner on Apr 17, 2019. It is now read-only.

[WIP] Players can choose a name and they can replay a party #15

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
38 changes: 25 additions & 13 deletions src/game/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .piece import Piece
from .turn import Turn
from .state import State
from .tools import GRID_SIZE, PIECES_NUMBER
from .tools import GRID_SIZE, PIECES_NUMBER, Players
from .ui import UIRender


Expand All @@ -15,6 +15,12 @@ class Game:

def start(self):
game_state = State()
ui = UIRender()
players = Players()

ui.clear_terminal()
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:
Expand All @@ -28,18 +34,24 @@ 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:

while not game_state.check_draw():
if game_state.check_winner():
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]:
Expand Down
6 changes: 3 additions & 3 deletions src/game/state.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .turn import Turn
from .piece import Piece
from .tools import GRID_SIZE, PIECES_NUMBER, EMPTY_POSITION, get_coordinates
from .tools import GRID_SIZE, PIECES_NUMBER, EMPTY_POSITION, get_coordinates, Players


class State:
Expand Down Expand Up @@ -69,9 +69,9 @@ def switch_player(self):
def check_winner(self):
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"
player_name = Players.player2_name
self.message = player_name + " WINNNNS !!!!!!\n"
return True
return False
Expand Down
6 changes: 4 additions & 2 deletions src/game/tests/test_players.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest
from ..turn import Turn
from ..ui import UIRender
from ..tools import Players


class TestPlayersMethods(unittest.TestCase):
Expand All @@ -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)


Expand Down
14 changes: 14 additions & 0 deletions src/game/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@
EMPTY_POSITION = '.'


class Players:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's very strange to have a "Players" class in "tools.py". Moreover, it seems that you use Players class as a property container, i'm not sure that it's the best way to achieve this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was also destined to store players scores (other story)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, all this informations are stored in only one object (a "game" state) to ensure data integrity and synchronicity.


"""Definition of the users names:"""

player1_name = "Player 1"
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


def get_coordinates(position):
"""Convert a postion of format 'A3' into coordinates x= 0 and y = 2 in the grid"""
if len(position) != 2:
Expand Down
40 changes: 27 additions & 13 deletions src/game/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 : "))
Expand All @@ -22,24 +22,41 @@ 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange to not integrate the players into the game_state, why have you choice this architecture ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

State represent the state of the current round game.
Players remains between rounds

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can still integrate player into the game state even if they don't change. This way, you manipulate one state only and it's easier to reason about and test

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 : ")
game_state.place_piece(position, game_state.game_turn.selected_piece)
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) ? ")
if len(name) > 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's still possible to add an empty name with space

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right. I don't see some real reason to forbid "spaces names" if the player has entered it, but I 'll manage for the game visibility

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bonus ;)

return name
return default_name
except KeyboardInterrupt:
print("\nGame aborted")
exit()

def prompt_restart(self):
try:
return input("Let's play again ? (o/n) ") == 'o'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

y/n

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))
Expand All @@ -62,8 +79,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"

Expand Down Expand Up @@ -91,7 +106,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):
Expand All @@ -102,17 +116,17 @@ 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):
if game_turn.selected_piece == piece_number:
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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case you've create a "Player" class, it would be possible to implement a "special" method (str) to achieve this task (cast an object to a string representation). This is the Java equivalent of toString().

More informations here: https://openclassrooms.com/courses/apprenez-a-programmer-en-python/les-methodes-speciales-1

Copy link
Collaborator Author

@JulienMattiussi JulienMattiussi Jun 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thinked about it, but I prefer to keep all render methods in the UI class
In this case : Changing UI doesn't impact other games classes

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):
Expand All @@ -123,15 +137,15 @@ 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")
print()
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()
Expand Down