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
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
FROM python:3
RUN mkdir /code
WORKDIR /code
ADD ./ /code

RUN pip install -r requirements.txt
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pycodestyle
pycodestyle
requests
82 changes: 70 additions & 12 deletions src/game/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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]:
Expand All @@ -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)
26 changes: 26 additions & 0 deletions src/game/network.py
Original file line number Diff line number Diff line change
@@ -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

13 changes: 13 additions & 0 deletions src/game/players.py
Original file line number Diff line number Diff line change
@@ -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
9 changes: 5 additions & 4 deletions src/game/state.py
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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):
Copy link
Contributor

Choose a reason for hiding this comment

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

"check_winner" is not a good name for this function. Moreover, 1 function = 1 task. Here, the function is responsible of "message" change and return a boolean

I would create a function to get the current winner (or nul if no one) and another to assign the message from the returned value instead.

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

Expand Down
3 changes: 2 additions & 1 deletion src/game/tests/test_grid.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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()
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 ..players 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
51 changes: 38 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,52 @@ 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) ? ").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))
Expand All @@ -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"

Expand Down Expand Up @@ -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):
Expand All @@ -102,17 +127,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 +148,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