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
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
from source.core.enums import BombState
from source.core.error import InvalidBombPosition, PositionAlreadyShot
from source.gui.scene import Result
from source.gui.scene.abc import Scene
from source.gui import widget, texture
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
if TYPE_CHECKING:
@ -18,7 +20,7 @@ class Game(Scene):
def __init__(self, window: "Window",
connection: socket.socket,
boat_sizes: list,
boats_length: list,
name_ally: str,
name_enemy: str,
grid_width: int,
@ -29,12 +31,11 @@ class Game(Scene):
super().__init__(window, **kwargs)
self.connection = connection
self.boat_sizes = boat_sizes
self.boats_length = boats_length
self.name_ally = name_ally
self.name_enemy = name_enemy
self.grid_width = grid_width
self.grid_height = grid_height
self.my_turn = my_turn # is it the player turn ?
self.batch_label = 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,
boats_length=self.boat_sizes,
boats_length=self.boats_length,
grid_style=texture.Grid.Style1,
boat_style=texture.Grid.Boat.Style1,
@ -74,10 +75,6 @@ class Game(Scene):
def board_ally_ready(widget):
self.boat_ready_ally = True
self.me_ready()
if self.boat_ready_enemy: self.refresh_turn_text()
PacketBoatPlaced().send_connection(connection)
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):
if not (self.boat_ready_ally and self.boat_ready_enemy): return
if not self.my_turn: return
PacketBombPlaced(position=cell).send_connection(connection)
self.my_turn = False
@ -222,7 +220,6 @@ class Game(Scene):
anchor_x="center",
text="Placer vos bateaux",
font_size=20,
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_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.boat_ready_enemy: bool = False # does the opponent finished placing his boat ?
self._my_turn = my_turn # is it the player turn ?
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_enemy: int = 0
def boat_broken_ally(self):
self._boat_broken_ally += 1
self.score_ally.text = str(self._boat_broken_ally)
self._refresh_turn_text()
def boat_broken_enemy(self):
self._boat_broken_enemy += 1
self.score_enemy.text = str(self._boat_broken_enemy)
# function
def me_ready(self):
self.label_state.text = "L'adversaire place ses bateaux"
def refresh_turn_text(self):
self.label_state.text = "Placer vos bombes" if self.my_turn else "L'adversaire place ses bombes"
def _refresh_turn_text(self):
self.label_state.text = (
"Placer vos bateaux" if not self.boat_ready_ally else
"L'adversaire place ses bateaux..." if not self.boat_ready_enemy else
"Placer vos bombes" if self.my_turn else
"L'adversaire place ses bombes..."
)
def game_end(self, won: bool):
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):
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.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.button_boat_size_previous = self.add_widget(
@ -199,7 +199,6 @@ class RoomCreate(Scene):
)
def next_boat_size():
if self.boat_size >= min(int(self.input_width.text), int(self.input_height.text)): return
self.boat_size += 1
update_boat_size_text()
@ -269,7 +268,7 @@ class RoomCreate(Scene):
grid_width=int(self.input_width.text),
grid_height=int(self.input_height.text),
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)

View file

@ -47,7 +47,8 @@ class GameGrid(BoxWidget):
self.rows = rows
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.board = Board(rows=self.rows, columns=self.columns)

View file

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

View file

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

View file

@ -1,8 +1,6 @@
import socket
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.network.packet.abc import Packet
from source.network import packet
@ -23,6 +21,13 @@ def game_network(
: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:
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
continue
match type(data):
case packet.PacketChat:
print(data.message)
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
if in_pyglet_context(
game_methods[type(data)], # récupère la methode relié ce type de donnée
connection, data
): return # Appelle la méthode. Si elle renvoie True, arrête le thread

View file

@ -10,13 +10,13 @@ class PacketSettings(Packet):
grid_width: int = field()
grid_height: int = field()
host_start: bool = field()
boat_size: list = field()
boats_length: list = field()
packet_size: int = 51
packet_format: str = ">16sBB?32B"
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(
self.packet_format,
@ -31,12 +31,12 @@ class PacketSettings(Packet):
@classmethod
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(
username=username.replace(b"\x00", b"").decode("utf-8"),
grid_width=grid_width,
grid_height=grid_height,
host_start=host_start,
boat_size=list(filter(lambda value: value != 0, boat_size))
boats_length=list(filter(lambda value: value != 0, boats_length))
)