simplified some part of the game code, made some quality of life change

This commit is contained in:
Faraphel 2023-02-24 19:37:44 +01:00
parent 385adb2cf1
commit 25a3c0fa86
8 changed files with 152 additions and 85 deletions

View file

@ -12,3 +12,10 @@ class BombState(Enum):
WON = 3 # the bomb sunk the last boat WON = 3 # the bomb sunk the last boat
ERROR = -1 # the bomb could not be placed ERROR = -1 # the bomb could not be placed
@property
def success(self):
"""
:return: Vrai si la valeur correspond à une case qui a été touché
"""
return self in [self.TOUCHED, self.SUNKEN, self.WON]

View file

@ -3,11 +3,13 @@ from typing import TYPE_CHECKING
import pyglet import pyglet
from source.core.enums import BombState
from source.core.error import InvalidBombPosition, PositionAlreadyShot
from source.gui.scene import Result from source.gui.scene import Result
from source.gui.scene.abc import Scene from source.gui.scene.abc import Scene
from source.gui import widget, texture from source.gui import widget, texture
from source import core from source import core
from source.network.packet import PacketChat, PacketBombPlaced, PacketBoatPlaced from source.network.packet import PacketChat, PacketBombPlaced, PacketBoatPlaced, PacketBombState
from source.type import Point2D from source.type import Point2D
if TYPE_CHECKING: if TYPE_CHECKING:
@ -18,7 +20,7 @@ class Game(Scene):
def __init__(self, window: "Window", def __init__(self, window: "Window",
connection: socket.socket, connection: socket.socket,
boat_sizes: list, boats_length: list,
name_ally: str, name_ally: str,
name_enemy: str, name_enemy: str,
grid_width: int, grid_width: int,
@ -29,12 +31,11 @@ class Game(Scene):
super().__init__(window, **kwargs) super().__init__(window, **kwargs)
self.connection = connection self.connection = connection
self.boat_sizes = boat_sizes self.boats_length = boats_length
self.name_ally = name_ally self.name_ally = name_ally
self.name_enemy = name_enemy self.name_enemy = name_enemy
self.grid_width = grid_width self.grid_width = grid_width
self.grid_height = grid_height self.grid_height = grid_height
self.my_turn = my_turn # is it the player turn ?
self.batch_label = pyglet.graphics.Batch() self.batch_label = pyglet.graphics.Batch()
self.batch_button_background = pyglet.graphics.Batch() self.batch_button_background = pyglet.graphics.Batch()
@ -58,7 +59,7 @@ class Game(Scene):
x=75, y=0.25, width=0.35, height=0.5, x=75, y=0.25, width=0.35, height=0.5,
boats_length=self.boat_sizes, boats_length=self.boats_length,
grid_style=texture.Grid.Style1, grid_style=texture.Grid.Style1,
boat_style=texture.Grid.Boat.Style1, boat_style=texture.Grid.Boat.Style1,
@ -74,10 +75,6 @@ class Game(Scene):
def board_ally_ready(widget): def board_ally_ready(widget):
self.boat_ready_ally = True self.boat_ready_ally = True
self.me_ready()
if self.boat_ready_enemy: self.refresh_turn_text()
PacketBoatPlaced().send_connection(connection) PacketBoatPlaced().send_connection(connection)
self.grid_ally.add_listener("on_all_boats_placed", board_ally_ready) self.grid_ally.add_listener("on_all_boats_placed", board_ally_ready)
@ -102,6 +99,7 @@ class Game(Scene):
def board_enemy_bomb(widget, cell: Point2D): def board_enemy_bomb(widget, cell: Point2D):
if not (self.boat_ready_ally and self.boat_ready_enemy): return if not (self.boat_ready_ally and self.boat_ready_enemy): return
if not self.my_turn: return if not self.my_turn: return
PacketBombPlaced(position=cell).send_connection(connection) PacketBombPlaced(position=cell).send_connection(connection)
self.my_turn = False self.my_turn = False
@ -222,7 +220,6 @@ class Game(Scene):
anchor_x="center", anchor_x="center",
text="Placer vos bateaux",
font_size=20, font_size=20,
batch=self.batch_label batch=self.batch_label
@ -231,28 +228,132 @@ class Game(Scene):
self.board_ally = core.Board(rows=self.grid_height, columns=self.grid_width) self.board_ally = core.Board(rows=self.grid_height, columns=self.grid_width)
self.board_enemy = core.Board(rows=self.grid_height, columns=self.grid_width) self.board_enemy = core.Board(rows=self.grid_height, columns=self.grid_width)
self.boat_ready_ally: bool = False # does the player finished placing his boat ? self._my_turn = my_turn # is it the player turn ?
self.boat_ready_enemy: bool = False # does the opponent finished placing his boat ? self._boat_ready_ally: bool = False # does the player finished placing his boat ?
self._boat_ready_enemy: bool = False # does the opponent finished placing his boat ?
self._boat_broken_ally: int = 0 self._boat_broken_ally: int = 0
self._boat_broken_enemy: int = 0 self._boat_broken_enemy: int = 0
def boat_broken_ally(self): self._refresh_turn_text()
self._boat_broken_ally += 1
self.score_ally.text = str(self._boat_broken_ally)
def boat_broken_enemy(self): # function
self._boat_broken_enemy += 1
self.score_enemy.text = str(self._boat_broken_enemy)
def me_ready(self): def _refresh_turn_text(self):
self.label_state.text = "L'adversaire place ses bateaux" self.label_state.text = (
"Placer vos bateaux" if not self.boat_ready_ally else
def refresh_turn_text(self): "L'adversaire place ses bateaux..." if not self.boat_ready_enemy else
self.label_state.text = "Placer vos bombes" if self.my_turn else "L'adversaire place ses bombes" "Placer vos bombes" if self.my_turn else
"L'adversaire place ses bombes..."
)
def game_end(self, won: bool): def game_end(self, won: bool):
self.window.add_scene(Result, won=won) self.window.add_scene(Result, won=won)
# property
@property
def boat_broken_ally(self):
return self._boat_broken_ally
@boat_broken_ally.setter
def boat_broken_ally(self, boat_broken_ally: int):
self._boat_broken_ally = boat_broken_ally
self.score_ally.text = str(self._boat_broken_ally)
@property
def boat_broken_enemy(self):
return self._boat_broken_enemy
@boat_broken_enemy.setter
def boat_broken_enemy(self, boat_broken_enemy: int):
self._boat_broken_enemy = boat_broken_enemy
self.score_enemy.text = str(self._boat_broken_enemy)
@property
def my_turn(self):
return self._my_turn
@my_turn.setter
def my_turn(self, my_turn: bool):
self._my_turn = my_turn
self._refresh_turn_text()
@property
def boat_ready_ally(self):
return self._boat_ready_ally
@boat_ready_ally.setter
def boat_ready_ally(self, boat_ready_ally: bool):
self._boat_ready_ally = boat_ready_ally
self._refresh_turn_text()
@property
def boat_ready_enemy(self):
return self._boat_ready_enemy
@boat_ready_enemy.setter
def boat_ready_enemy(self, boat_ready_enemy: bool):
self._boat_ready_enemy = boat_ready_enemy
self._refresh_turn_text()
# network
def network_on_chat(self, connection: socket.socket, packet: PacketChat):
print(packet.message)
def network_on_boat_placed(self, connection: socket.socket, packet: PacketBoatPlaced):
self.boat_ready_enemy = True
def network_on_bomb_placed(self, connection: socket.socket, packet: PacketBombPlaced):
try:
# essaye de poser la bombe sur la grille alliée
bomb_state = self.grid_ally.board.bomb(packet.position)
except (InvalidBombPosition, PositionAlreadyShot):
# si une erreur se produit, signale l'erreur
bomb_state = BombState.ERROR
# l'opposant va rejouer, ce n'est donc pas notre tour
self.my_turn = False
else:
# si la bombe a bien été placé, affiche la sur la grille visuel allié
self.grid_ally.place_bomb(packet.position, bomb_state.success)
# c'est à notre tour si l'opposant à loupé sa bombe
self.my_turn = not bomb_state.success
# envoie le résultat à l'autre joueur
PacketBombState(position=packet.position, bomb_state=bomb_state).send_connection(connection)
if bomb_state.success:
# si la bombe a touché un bateau, incrémente le score
self.boat_broken_enemy += 1
if bomb_state is BombState.WON:
# si l'ennemie à gagner, alors l'on a perdu
self.game_end(won=False)
return True # coupe la connexion
def network_on_bomb_state(self, connection: socket.socket, packet: PacketBombState):
if packet.bomb_state is BombState.ERROR:
# si une erreur est survenue, on rejoue
self.my_turn = True
return
# on rejoue uniquement si la bombe à toucher
self.my_turn = packet.bomb_state.success
if packet.bomb_state.success:
# si la bombe à toucher, incrémente le score
self.boat_broken_ally += 1
# place la bombe sur la grille ennemie visuelle
self.grid_enemy.place_bomb(packet.position, packet.bomb_state.success)
if packet.bomb_state is BombState.WON:
# si cette bombe a touché le dernier bateau, alors l'on a gagné
self.game_end(won=True)
return True # coupe la connexion
# event
def on_draw(self): def on_draw(self):
self.background.draw() self.background.draw()

View file

@ -150,7 +150,7 @@ class RoomCreate(Scene):
self.input_boat_amount.text = str(self.boat_size_amount.get(self.boat_size, 0)) self.input_boat_amount.text = str(self.boat_size_amount.get(self.boat_size, 0))
self.label_boat_recap.text = "" self.label_boat_recap.text = ""
for size, amount in self.boat_size_amount.items(): for size, amount in sorted(self.boat_size_amount.items(), key=lambda v: v[0]):
self.label_boat_recap.text += f"Taille: {size}, Quantité: {amount}\n" self.label_boat_recap.text += f"Taille: {size}, Quantité: {amount}\n"
self.button_boat_size_previous = self.add_widget( self.button_boat_size_previous = self.add_widget(
@ -199,7 +199,6 @@ class RoomCreate(Scene):
) )
def next_boat_size(): def next_boat_size():
if self.boat_size >= min(int(self.input_width.text), int(self.input_height.text)): return
self.boat_size += 1 self.boat_size += 1
update_boat_size_text() update_boat_size_text()
@ -269,7 +268,7 @@ class RoomCreate(Scene):
grid_width=int(self.input_width.text), grid_width=int(self.input_width.text),
grid_height=int(self.input_height.text), grid_height=int(self.input_height.text),
host_start=self.checkbox_host_start.state, host_start=self.checkbox_host_start.state,
boat_size=[size for size, quantity in self.boat_size_amount.items() for _ in range(quantity)] boats_length=[size for size, quantity in self.boat_size_amount.items() for _ in range(quantity)]
) )
self.window.set_scene(RoomHost, settings=settings) self.window.set_scene(RoomHost, settings=settings)

View file

@ -47,7 +47,8 @@ class GameGrid(BoxWidget):
self.rows = rows self.rows = rows
self.columns = columns self.columns = columns
self.boats_length = [] if boats_length is None else boats_length # the list of the size of the boats to place # the list of the size of the boats to place
self.boats_length = [] if boats_length is None else sorted(boats_length, reverse=True)
self.preview_color = preview_color self.preview_color = preview_color
self.board = Board(rows=self.rows, columns=self.columns) self.board = Board(rows=self.rows, columns=self.columns)

View file

@ -43,7 +43,7 @@ class Client(StoppableThread):
connection=connection, connection=connection,
boat_sizes=settings.boat_size, boats_length=settings.boats_length,
name_ally=self.username, name_ally=self.username,
name_enemy=settings.username, name_enemy=settings.username,
grid_width=settings.grid_width, grid_width=settings.grid_width,

View file

@ -52,7 +52,7 @@ class Host(StoppableThread):
connection=connection, connection=connection,
boat_sizes=self.settings.boat_size, boats_length=self.settings.boats_length,
name_ally=self.settings.username, name_ally=self.settings.username,
name_enemy=packet_username.username, name_enemy=packet_username.username,
grid_width=self.settings.grid_width, grid_width=self.settings.grid_width,

View file

@ -1,8 +1,6 @@
import socket import socket
from typing import Any from typing import Any
from source.core.enums import BombState
from source.core.error import InvalidBombPosition, PositionAlreadyShot
from source.gui.scene import Game from source.gui.scene import Game
from source.network.packet.abc import Packet from source.network.packet.abc import Packet
from source.network import packet from source.network import packet
@ -23,6 +21,13 @@ def game_network(
:param connection: the connection with the other player :param connection: the connection with the other player
""" """
game_methods = {
packet.PacketChat: game_scene.network_on_chat,
packet.PacketBoatPlaced: game_scene.network_on_boat_placed,
packet.PacketBombPlaced: game_scene.network_on_bomb_placed,
packet.PacketBombState: game_scene.network_on_bomb_state,
}
while True: while True:
data: Any = Packet.from_connection(connection) data: Any = Packet.from_connection(connection)
@ -30,53 +35,7 @@ def game_network(
if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter
continue continue
match type(data): if in_pyglet_context(
case packet.PacketChat: game_methods[type(data)], # récupère la methode relié ce type de donnée
print(data.message) connection, data
): return # Appelle la méthode. Si elle renvoie True, arrête le thread
case packet.PacketBoatPlaced:
game_scene.boat_ready_enemy = True
if game_scene.boat_ready_ally:
in_pyglet_context(game_scene.refresh_turn_text)
case packet.PacketBombPlaced:
try:
bomb_state = game_scene.grid_ally.board.bomb(data.position)
except (InvalidBombPosition, PositionAlreadyShot):
bomb_state = BombState.ERROR
packet.PacketBombState(position=data.position, bomb_state=bomb_state).send_connection(connection)
touched = bomb_state in [BombState.TOUCHED, BombState.SUNKEN, BombState.WON]
if bomb_state is not BombState.ERROR:
in_pyglet_context(game_scene.grid_ally.place_bomb, data.position, touched)
if touched:
in_pyglet_context(game_scene.boat_broken_enemy)
game_scene.my_turn = not (touched or (bomb_state is BombState.ERROR))
in_pyglet_context(game_scene.refresh_turn_text)
if bomb_state is BombState.WON:
in_pyglet_context(game_scene.game_end, won=False)
return # coupe la connexion
case packet.PacketBombState:
print(data.bomb_state)
if data.bomb_state is BombState.ERROR:
game_scene.my_turn = True
continue
touched = data.bomb_state in [BombState.TOUCHED, BombState.SUNKEN, BombState.WON]
game_scene.my_turn = touched
in_pyglet_context(game_scene.refresh_turn_text)
if touched:
in_pyglet_context(game_scene.boat_broken_ally)
in_pyglet_context(game_scene.grid_enemy.place_bomb, data.position, touched)
if data.bomb_state is BombState.WON:
in_pyglet_context(game_scene.game_end, won=True)
return # coupe la connexion

View file

@ -10,13 +10,13 @@ class PacketSettings(Packet):
grid_width: int = field() grid_width: int = field()
grid_height: int = field() grid_height: int = field()
host_start: bool = field() host_start: bool = field()
boat_size: list = field() boats_length: list = field()
packet_size: int = 51 packet_size: int = 51
packet_format: str = ">16sBB?32B" packet_format: str = ">16sBB?32B"
def to_bytes(self): def to_bytes(self):
boat_size = self.boat_size + ([0] * (32 - len(self.boat_size))) boat_size = self.boats_length + ([0] * (32 - len(self.boats_length)))
return struct.pack( return struct.pack(
self.packet_format, self.packet_format,
@ -31,12 +31,12 @@ class PacketSettings(Packet):
@classmethod @classmethod
def from_bytes(cls, data: bytes): def from_bytes(cls, data: bytes):
username, grid_width, grid_height, host_start, *boat_size = struct.unpack(cls.packet_format, data) username, grid_width, grid_height, host_start, *boats_length = struct.unpack(cls.packet_format, data)
return cls( return cls(
username=username.replace(b"\x00", b"").decode("utf-8"), username=username.replace(b"\x00", b"").decode("utf-8"),
grid_width=grid_width, grid_width=grid_width,
grid_height=grid_height, grid_height=grid_height,
host_start=host_start, host_start=host_start,
boat_size=list(filter(lambda value: value != 0, boat_size)) boats_length=list(filter(lambda value: value != 0, boats_length))
) )