added settings for the game
This commit is contained in:
parent
5dd5663ac6
commit
385adb2cf1
13 changed files with 361 additions and 34 deletions
3
NOTE.md
3
NOTE.md
|
@ -1,5 +1,6 @@
|
|||
A faire :
|
||||
- Ecran de configuration de la partie
|
||||
- Limite des bateaux, taille des pseudo, ...
|
||||
|
||||
- Nom dans les options
|
||||
- Faire marcher le tchat
|
||||
- Sauvegarde / Quitter
|
||||
|
|
|
@ -15,10 +15,26 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
class Game(Scene):
|
||||
def __init__(self, window: "Window", connection: socket.socket, **kwargs):
|
||||
def __init__(self, window: "Window",
|
||||
connection: socket.socket,
|
||||
|
||||
boat_sizes: list,
|
||||
name_ally: str,
|
||||
name_enemy: str,
|
||||
grid_width: int,
|
||||
grid_height: int,
|
||||
my_turn: bool,
|
||||
|
||||
**kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
|
||||
self.connection = connection
|
||||
self.boat_sizes = boat_sizes
|
||||
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()
|
||||
|
@ -42,12 +58,12 @@ class Game(Scene):
|
|||
|
||||
x=75, y=0.25, width=0.35, height=0.5,
|
||||
|
||||
boats_length=[5, 5, 4, 3, 2],
|
||||
boats_length=self.boat_sizes,
|
||||
|
||||
grid_style=texture.Grid.Style1,
|
||||
boat_style=texture.Grid.Boat.Style1,
|
||||
bomb_style=texture.Grid.Bomb.Style1,
|
||||
rows=8, columns=8,
|
||||
rows=self.grid_height, columns=self.grid_width,
|
||||
|
||||
background_batch=self.batch_grid_background,
|
||||
line_batch=self.batch_grid_line,
|
||||
|
@ -74,7 +90,7 @@ class Game(Scene):
|
|||
grid_style=texture.Grid.Style1,
|
||||
boat_style=texture.Grid.Boat.Style1,
|
||||
bomb_style=texture.Grid.Bomb.Style1,
|
||||
rows=8, columns=8,
|
||||
rows=self.grid_height, columns=self.grid_width,
|
||||
|
||||
background_batch=self.batch_grid_background,
|
||||
line_batch=self.batch_grid_line,
|
||||
|
@ -91,24 +107,24 @@ class Game(Scene):
|
|||
|
||||
self.grid_enemy.add_listener("on_request_place_bomb", board_enemy_bomb)
|
||||
|
||||
self.name_ally = self.add_widget(
|
||||
self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.27, y=0.995,
|
||||
|
||||
text="Raphael",
|
||||
text=self.name_ally,
|
||||
font_size=20,
|
||||
anchor_x="center", anchor_y="center",
|
||||
|
||||
batch=self.batch_label,
|
||||
)
|
||||
|
||||
self.name_enemy = self.add_widget(
|
||||
self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.73, y=0.995,
|
||||
|
||||
text="Leo",
|
||||
text=self.name_enemy,
|
||||
font_size=20,
|
||||
anchor_x="center", anchor_y="center",
|
||||
|
||||
|
@ -212,10 +228,9 @@ class Game(Scene):
|
|||
batch=self.batch_label
|
||||
)
|
||||
|
||||
self.board_ally = core.Board(rows=8, columns=8)
|
||||
self.board_enemy = core.Board(rows=8, columns=8)
|
||||
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.my_turn: bool = False # 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
|
||||
|
|
|
@ -5,6 +5,7 @@ import pyglet
|
|||
from source.gui import widget, texture
|
||||
from source.gui.scene import RoomHost
|
||||
from source.gui.scene.abc import Scene
|
||||
from source.network.packet import PacketSettings
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
|
@ -17,6 +18,7 @@ class RoomCreate(Scene):
|
|||
self.batch_label = pyglet.graphics.Batch()
|
||||
self.batch_input_background = pyglet.graphics.Batch()
|
||||
self.batch_button_background = pyglet.graphics.Batch()
|
||||
self.batch_checkbox = pyglet.graphics.Batch()
|
||||
|
||||
self.back = self.add_widget(
|
||||
widget.Button,
|
||||
|
@ -33,6 +35,35 @@ class RoomCreate(Scene):
|
|||
from source.gui.scene import MainMenu
|
||||
self.back.add_listener("on_click_release", lambda *_: self.window.set_scene(MainMenu))
|
||||
|
||||
# Username
|
||||
|
||||
self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.1, y=0.5,
|
||||
|
||||
anchor_x="center", anchor_y="center",
|
||||
|
||||
text="Pseudonyme",
|
||||
|
||||
batch=self.batch_label
|
||||
)
|
||||
|
||||
self.input_username = self.add_widget(
|
||||
widget.Input,
|
||||
|
||||
x=0.2, y=0.45, width=0.15, height=0.1,
|
||||
|
||||
style=texture.Input.Style1,
|
||||
|
||||
label_text="Host",
|
||||
|
||||
background_batch=self.batch_input_background,
|
||||
label_batch=self.batch_label
|
||||
)
|
||||
|
||||
# Grid configuration
|
||||
|
||||
self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
|
@ -43,7 +74,7 @@ class RoomCreate(Scene):
|
|||
batch=self.batch_label
|
||||
)
|
||||
|
||||
input_width = self.add_widget(
|
||||
self.input_width = self.add_widget(
|
||||
widget.Input,
|
||||
|
||||
x=0.2, y=0.86, width=0.1, height=0.08,
|
||||
|
@ -68,7 +99,7 @@ class RoomCreate(Scene):
|
|||
batch=self.batch_label
|
||||
)
|
||||
|
||||
input_height = self.add_widget(
|
||||
self.input_height = self.add_widget(
|
||||
widget.Input,
|
||||
|
||||
x=0.2, y=0.76, width=0.1, height=0.08,
|
||||
|
@ -83,6 +114,141 @@ class RoomCreate(Scene):
|
|||
label_batch=self.batch_label
|
||||
)
|
||||
|
||||
# Tour
|
||||
|
||||
self.checkbox_host_start = self.add_widget(
|
||||
widget.Checkbox,
|
||||
|
||||
x=0.4, y=0.8, width=0.05, height=0.1,
|
||||
|
||||
style=texture.Checkbox.Style1,
|
||||
|
||||
state=True,
|
||||
|
||||
batch=self.batch_checkbox
|
||||
)
|
||||
|
||||
self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.46, y=0.85,
|
||||
|
||||
anchor_y="center",
|
||||
|
||||
text="Premier tour pour l'hôte",
|
||||
|
||||
batch=self.batch_label
|
||||
)
|
||||
|
||||
# taille et quantité des bateaux
|
||||
|
||||
self.boat_size: int = 1
|
||||
self.boat_size_amount: dict[int, int] = {2: 1, 3: 1, 4: 2, 5: 1}
|
||||
|
||||
def update_boat_size_text():
|
||||
self.label_boat_size.text = f"Taille: {self.boat_size}"
|
||||
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():
|
||||
self.label_boat_recap.text += f"Taille: {size}, Quantité: {amount}\n"
|
||||
|
||||
self.button_boat_size_previous = self.add_widget(
|
||||
widget.Button,
|
||||
|
||||
x=0.7, y=0.8, width=0.03, height=0.1,
|
||||
|
||||
label_text="<",
|
||||
label_font_size=25,
|
||||
|
||||
style=texture.Button.Style1,
|
||||
|
||||
background_batch=self.batch_button_background,
|
||||
label_batch=self.batch_label
|
||||
)
|
||||
|
||||
def previous_boat_size():
|
||||
if self.boat_size <= 1: return
|
||||
self.boat_size -= 1
|
||||
update_boat_size_text()
|
||||
|
||||
self.button_boat_size_previous.add_listener("on_click_release", lambda *_: previous_boat_size())
|
||||
|
||||
self.label_boat_size = self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.8, y=0.85,
|
||||
|
||||
anchor_x="center", anchor_y="center",
|
||||
|
||||
batch=self.batch_label,
|
||||
)
|
||||
|
||||
self.button_boat_size_next = self.add_widget(
|
||||
widget.Button,
|
||||
|
||||
x=0.87, y=0.8, width=0.03, height=0.1,
|
||||
|
||||
label_text=">",
|
||||
label_font_size=25,
|
||||
|
||||
style=texture.Button.Style1,
|
||||
|
||||
background_batch=self.batch_button_background,
|
||||
label_batch=self.batch_label
|
||||
)
|
||||
|
||||
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()
|
||||
|
||||
self.button_boat_size_next.add_listener("on_click_release", lambda *_: next_boat_size())
|
||||
|
||||
self.input_boat_amount = self.add_widget(
|
||||
widget.Input,
|
||||
|
||||
x=0.7, y=0.68, width=0.2, height=0.08,
|
||||
|
||||
regex=r"\d+",
|
||||
|
||||
style=texture.Input.Style1,
|
||||
|
||||
label_text="8",
|
||||
|
||||
background_batch=self.batch_input_background,
|
||||
label_batch=self.batch_label
|
||||
)
|
||||
|
||||
def change_boat_amount():
|
||||
quantity = int(self.input_boat_amount.text)
|
||||
|
||||
if quantity > 0:
|
||||
self.boat_size_amount[self.boat_size] = quantity
|
||||
|
||||
elif self.boat_size in self.boat_size_amount:
|
||||
self.boat_size_amount.pop(self.boat_size)
|
||||
|
||||
update_boat_size_text()
|
||||
|
||||
self.input_boat_amount.add_listener("on_valid_text", lambda *_: change_boat_amount())
|
||||
|
||||
self.label_boat_recap = self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.7, y=0.60, width=0.2, height=0.1,
|
||||
|
||||
multiline=True,
|
||||
|
||||
batch=self.batch_label
|
||||
)
|
||||
|
||||
update_boat_size_text()
|
||||
|
||||
# TODO: si on diminue la taille de la grille après avoir mis des bateaux de plus longue taille, faire un check
|
||||
|
||||
# Démarrer
|
||||
|
||||
self.start = self.add_widget(
|
||||
widget.Button,
|
||||
x=lambda widget: widget.scene.window.width - 20 - widget.width, y=20, width=0.2, height=0.1,
|
||||
|
@ -95,9 +261,21 @@ class RoomCreate(Scene):
|
|||
label_batch=self.batch_label
|
||||
)
|
||||
|
||||
self.start.add_listener("on_click_release", lambda *_: self.window.set_scene(RoomHost))
|
||||
self.start.add_listener("on_click_release", lambda *_: self.confirm())
|
||||
|
||||
def confirm(self):
|
||||
settings = PacketSettings(
|
||||
username=self.input_username.text,
|
||||
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)]
|
||||
)
|
||||
|
||||
self.window.set_scene(RoomHost, settings=settings)
|
||||
|
||||
def on_draw(self):
|
||||
self.batch_input_background.draw()
|
||||
self.batch_button_background.draw()
|
||||
self.batch_checkbox.draw()
|
||||
self.batch_label.draw()
|
||||
|
|
|
@ -9,10 +9,11 @@ from source.gui import widget, texture
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
from source.network.packet import PacketSettings
|
||||
|
||||
|
||||
class RoomHost(Scene):
|
||||
def __init__(self, window: "Window", **kwargs):
|
||||
def __init__(self, window: "Window", settings: "PacketSettings", **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
|
||||
"""r = requests.get('https://api.ipify.org')
|
||||
|
@ -63,7 +64,7 @@ class RoomHost(Scene):
|
|||
batch=self.batch_label
|
||||
)
|
||||
|
||||
self.thread = network.Host(window=self.window, daemon=True, username="Host")
|
||||
self.thread = network.Host(window=self.window, daemon=True, settings=settings)
|
||||
self.thread.start()
|
||||
|
||||
def button_back_callback(self, *_):
|
||||
|
|
|
@ -32,9 +32,25 @@ class RoomJoin(Scene):
|
|||
|
||||
self.back.add_listener("on_click_release", self.button_back_callback)
|
||||
|
||||
# Pseudo
|
||||
|
||||
self.entry_username = self.add_widget(
|
||||
widget.Input,
|
||||
x=0.4, y=0.55, width=0.2, height=0.1,
|
||||
|
||||
style=texture.Input.Style1,
|
||||
|
||||
label_text="Client",
|
||||
|
||||
background_batch=self.batch_input_background,
|
||||
label_batch=self.batch_label
|
||||
)
|
||||
|
||||
# IP / Port
|
||||
|
||||
self.entry_ip = self.add_widget(
|
||||
widget.Input,
|
||||
x=0.4, y=0.5, width=0.13, height=0.1,
|
||||
x=0.4, y=0.45, width=0.13, height=0.1,
|
||||
|
||||
regex=r"\d{1,3}(\.\d{1,3}){3}",
|
||||
|
||||
|
@ -48,7 +64,7 @@ class RoomJoin(Scene):
|
|||
|
||||
self.entry_port = self.add_widget(
|
||||
widget.Input,
|
||||
x=0.53, y=0.5, width=0.07, height=0.1,
|
||||
x=0.53, y=0.45, width=0.07, height=0.1,
|
||||
|
||||
regex=r"\d{0,5}",
|
||||
|
||||
|
@ -60,7 +76,7 @@ class RoomJoin(Scene):
|
|||
|
||||
self.connect = self.add_widget(
|
||||
widget.Button,
|
||||
x=0.4, y=0.4, width=0.2, height=0.1,
|
||||
x=0.4, y=0.35, width=0.2, height=0.1,
|
||||
|
||||
label_text="Se connecter",
|
||||
|
||||
|
@ -77,7 +93,7 @@ class RoomJoin(Scene):
|
|||
window=self.window,
|
||||
ip_address=self.entry_ip.text,
|
||||
daemon=True,
|
||||
username="Client"
|
||||
username=self.entry_username.text
|
||||
).start()
|
||||
|
||||
def button_back_callback(self, widget, *_):
|
||||
|
|
|
@ -121,6 +121,9 @@ class Input(BoxWidget):
|
|||
if self.regex is not None: # si il y a un regex de validation, applique le pour vérifier le texte
|
||||
self.invalid = self.regex.fullmatch(self.text) is None
|
||||
|
||||
if not self.invalid:
|
||||
self.trigger_event("on_valid_text")
|
||||
|
||||
def on_resize(self, width: int, height: int):
|
||||
self._refresh_size()
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import socket
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from source.gui import scene
|
||||
from source.network import game_network
|
||||
from source.network.packet import PacketUsername
|
||||
from source.network.packet.abc import Packet
|
||||
from source.utils import StoppableThread
|
||||
from source.utils.thread import in_pyglet_context
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
|
@ -30,4 +34,25 @@ class Client(StoppableThread):
|
|||
|
||||
print(f"[Client] Connecté avec {connection}")
|
||||
|
||||
game_network(self, self.window, connection, False)
|
||||
settings: Any = Packet.from_connection(connection)
|
||||
PacketUsername(username=self.username).send_connection(connection)
|
||||
|
||||
game_scene = in_pyglet_context(
|
||||
self.window.set_scene,
|
||||
scene.Game,
|
||||
|
||||
connection=connection,
|
||||
|
||||
boat_sizes=settings.boat_size,
|
||||
name_ally=self.username,
|
||||
name_enemy=settings.username,
|
||||
grid_width=settings.grid_width,
|
||||
grid_height=settings.grid_height,
|
||||
my_turn=not settings.host_start
|
||||
)
|
||||
|
||||
game_network(
|
||||
thread=self,
|
||||
connection=connection,
|
||||
game_scene=game_scene,
|
||||
)
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
import socket
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from source.gui import scene
|
||||
from source.network import game_network
|
||||
from source.network.packet.abc import Packet
|
||||
from source.utils import StoppableThread
|
||||
from source.utils.thread import in_pyglet_context
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
from source.network.packet import PacketSettings
|
||||
|
||||
|
||||
class Host(StoppableThread):
|
||||
|
@ -13,11 +17,11 @@ class Host(StoppableThread):
|
|||
The thread executed on the person who create a room.
|
||||
"""
|
||||
|
||||
def __init__(self, window: "Window", username: str, port: int = 52321, **kw):
|
||||
def __init__(self, window: "Window", settings: "PacketSettings", port: int = 52321, **kw):
|
||||
super().__init__(**kw)
|
||||
|
||||
self.window = window
|
||||
self.username = username
|
||||
self.settings = settings
|
||||
self.port = port
|
||||
|
||||
def run(self) -> None:
|
||||
|
@ -39,4 +43,25 @@ class Host(StoppableThread):
|
|||
|
||||
print(f"[Serveur] Connecté avec {address}")
|
||||
|
||||
game_network(self, self.window, connection, True)
|
||||
self.settings.send_connection(connection)
|
||||
packet_username: Any = Packet.from_connection(connection)
|
||||
|
||||
game_scene = in_pyglet_context(
|
||||
self.window.set_scene,
|
||||
scene.Game,
|
||||
|
||||
connection=connection,
|
||||
|
||||
boat_sizes=self.settings.boat_size,
|
||||
name_ally=self.settings.username,
|
||||
name_enemy=packet_username.username,
|
||||
grid_width=self.settings.grid_width,
|
||||
grid_height=self.settings.grid_height,
|
||||
my_turn=self.settings.host_start
|
||||
)
|
||||
|
||||
game_network(
|
||||
thread=self,
|
||||
connection=connection,
|
||||
game_scene=game_scene
|
||||
)
|
||||
|
|
|
@ -3,26 +3,26 @@ from typing import Any
|
|||
|
||||
from source.core.enums import BombState
|
||||
from source.core.error import InvalidBombPosition, PositionAlreadyShot
|
||||
from source.gui import scene
|
||||
from source.gui.scene import Game
|
||||
from source.network.packet.abc import Packet
|
||||
from source.network import packet
|
||||
|
||||
from source.gui.window import Window
|
||||
from source.utils import StoppableThread
|
||||
from source.utils.thread import in_pyglet_context
|
||||
|
||||
|
||||
def game_network(thread: "StoppableThread", window: "Window", connection: socket.socket, host: bool):
|
||||
def game_network(
|
||||
thread: "StoppableThread",
|
||||
connection: socket.socket,
|
||||
game_scene: Game,
|
||||
):
|
||||
"""
|
||||
Run the networking to make the game work and react with the other player
|
||||
:param game_scene: the scene of the game
|
||||
:param thread: the thread where this function is called.
|
||||
:param window: the window of the game
|
||||
:param connection: the connection with the other player
|
||||
"""
|
||||
|
||||
game_scene = in_pyglet_context(window.set_scene, scene.Game, connection=connection)
|
||||
game_scene.my_turn = host
|
||||
|
||||
while True:
|
||||
data: Any = Packet.from_connection(connection)
|
||||
|
||||
|
|
42
source/network/packet/PacketSettings.py
Normal file
42
source/network/packet/PacketSettings.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
import struct
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketSettings(Packet):
|
||||
username: str = field()
|
||||
grid_width: int = field()
|
||||
grid_height: int = field()
|
||||
host_start: bool = field()
|
||||
boat_size: 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)))
|
||||
|
||||
return struct.pack(
|
||||
self.packet_format,
|
||||
|
||||
self.username.encode("utf-8"),
|
||||
self.grid_width,
|
||||
self.grid_height,
|
||||
self.host_start,
|
||||
|
||||
*boat_size
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
username, grid_width, grid_height, host_start, *boat_size = 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))
|
||||
)
|
17
source/network/packet/PacketUsername.py
Normal file
17
source/network/packet/PacketUsername.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from source.network.packet.abc import Packet
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketUsername(Packet):
|
||||
username: str = field()
|
||||
|
||||
packet_size: int = 16
|
||||
|
||||
def to_bytes(self):
|
||||
return self.username.encode("utf-8")
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes):
|
||||
return cls(username=data.decode("utf-8"))
|
|
@ -2,3 +2,5 @@ from .PacketChat import PacketChat
|
|||
from .PacketBombPlaced import PacketBombPlaced
|
||||
from .PacketBombState import PacketBombState
|
||||
from .PacketBoatPlaced import PacketBoatPlaced
|
||||
from .PacketSettings import PacketSettings
|
||||
from .PacketUsername import PacketUsername
|
||||
|
|
|
@ -2,6 +2,8 @@ import socket
|
|||
from abc import abstractmethod, ABC
|
||||
from typing import Type, Optional
|
||||
|
||||
# TODO: struct.calcsize() au lieu de packet_size
|
||||
|
||||
|
||||
class Packet(ABC):
|
||||
packed_header: bytes
|
||||
|
|
Loading…
Reference in a new issue