simplified some part of the game code, made some quality of life change
This commit is contained in:
parent
385adb2cf1
commit
25a3c0fa86
8 changed files with 152 additions and 85 deletions
|
@ -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]
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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))
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue