comment in the _test et core modules
This commit is contained in:
parent
f1edbeb8ac
commit
88e89209ac
11 changed files with 141 additions and 47 deletions
|
@ -8,8 +8,15 @@ from source.core.error import InvalidBoatPosition, InvalidBombPosition, Position
|
|||
|
||||
|
||||
class TestCore(unittest.TestCase):
|
||||
"""
|
||||
Unité de test pour tester l'implémentation du jeu.
|
||||
"""
|
||||
|
||||
def test_boats(self):
|
||||
"""
|
||||
Test pour le placement des bateaux
|
||||
"""
|
||||
|
||||
board = Board(width=5, height=5)
|
||||
board.add_boat(Boat(5, Orientation.HORIZONTAL), (0, 0))
|
||||
|
||||
|
@ -46,6 +53,10 @@ class TestCore(unittest.TestCase):
|
|||
)
|
||||
|
||||
def test_bombs(self):
|
||||
"""
|
||||
Test pour le placement des bombes
|
||||
"""
|
||||
|
||||
board = Board(width=5, height=5)
|
||||
board.add_boat(Boat(5, Orientation.HORIZONTAL), (0, 0))
|
||||
board.add_boat(Boat(4, Orientation.VERTICAL), (1, 1))
|
||||
|
|
|
@ -8,13 +8,15 @@ from typing import Optional
|
|||
import numpy as np
|
||||
|
||||
from source.core.enums import BombState
|
||||
from source.network.packet import PacketChat, PacketUsername, PacketQuit, PacketAskSave, PacketBoatPlaced, \
|
||||
PacketLoadOldSave, PacketResponseSave, PacketHaveSaveBeenFound, PacketBombPlaced, PacketBombState, PacketSettings, \
|
||||
PacketBoatsData
|
||||
from source.network.packet import *
|
||||
from source.network.packet.abc import Packet
|
||||
|
||||
|
||||
class TestNetwork(unittest.TestCase):
|
||||
"""
|
||||
Unité de test pour le réseau
|
||||
"""
|
||||
|
||||
PORT: int = 54200
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -39,11 +41,11 @@ class TestNetwork(unittest.TestCase):
|
|||
thread_client.join()
|
||||
|
||||
def __del__(self):
|
||||
# ferme les connexions lorsque l'objet est supprimé
|
||||
self.co_client.close()
|
||||
self.so_server.close()
|
||||
|
||||
# tous les tests de packet sont réunis dans la même fonction pour éviter de réouvrir des sockets sur le mêmes
|
||||
# ports encore et encore
|
||||
# tous les tests de packet sont réunis dans la même fonction pour éviter de rouvrir des sockets sur le même port
|
||||
def test_packet(self):
|
||||
# PacketChat
|
||||
for _ in range(100):
|
||||
|
@ -161,7 +163,7 @@ class TestNetwork(unittest.TestCase):
|
|||
|
||||
# Packet Générique
|
||||
for _ in range(100):
|
||||
# prend un packet signal aléatoire (sont plus simples a initialisé)
|
||||
# prend un packet "signal" ou "variable lengh" aléatoire (sont les plus simples a initialisé)
|
||||
packet_sent_type = random.choice([PacketQuit, PacketAskSave, PacketBoatPlaced, PacketUsername, PacketChat])
|
||||
|
||||
if packet_sent_type in [PacketUsername, PacketChat]:
|
||||
|
|
|
@ -21,33 +21,62 @@ class TestBoxWidget(BoxWidget):
|
|||
super().__init__(x=100, y=200, width=150, height=175, scene=scene)
|
||||
|
||||
|
||||
# Créer un objet widget qui pourra être utilisé dans les tests
|
||||
window = TestWindow()
|
||||
scene = TestScene(window)
|
||||
widget = TestBoxWidget(scene)
|
||||
|
||||
|
||||
class TestPosition(unittest.TestCase):
|
||||
"""
|
||||
Unité de test pour les unités de positionnement
|
||||
"""
|
||||
|
||||
def test_unit_px(self):
|
||||
"""
|
||||
Test des unités px (pixel)
|
||||
"""
|
||||
|
||||
for value in range(1, 500):
|
||||
self.assertEqual((value*px)(widget), value)
|
||||
|
||||
def test_unit_vw(self):
|
||||
"""
|
||||
Test des unités vw (viewport width)
|
||||
"""
|
||||
|
||||
for value in range(1, 200):
|
||||
self.assertEqual((value*vw)(widget), int(window.width * (value / 100)))
|
||||
|
||||
def test_unit_vh(self):
|
||||
"""
|
||||
Test des unités vh (viewport height)
|
||||
"""
|
||||
|
||||
for value in range(1, 200):
|
||||
self.assertEqual((value*vh)(widget), int(window.height * (value / 100)))
|
||||
|
||||
def test_unit_ww(self):
|
||||
"""
|
||||
Test des unités ww (widget width)
|
||||
"""
|
||||
|
||||
for value in range(1, 200):
|
||||
self.assertEqual((value*ww)(widget), int(widget.width * (value / 100)))
|
||||
|
||||
def test_unit_wh(self):
|
||||
"""
|
||||
Test des unités wh (widget height)
|
||||
"""
|
||||
|
||||
for value in range(1, 200):
|
||||
self.assertEqual((value * wh)(widget), int(widget.height * (value / 100)))
|
||||
|
||||
def test_unit_add(self):
|
||||
"""
|
||||
Test des additions d'unités
|
||||
"""
|
||||
|
||||
for value_px in range(1, 100):
|
||||
for value_vw in range(1, 100):
|
||||
self.assertEqual(
|
||||
|
@ -56,6 +85,10 @@ class TestPosition(unittest.TestCase):
|
|||
)
|
||||
|
||||
def test_unit_sub(self):
|
||||
"""
|
||||
Test des soustractions d'unités
|
||||
"""
|
||||
|
||||
for value_px in range(1, 100):
|
||||
for value_vw in range(1, 100):
|
||||
self.assertEqual(
|
||||
|
@ -64,6 +97,10 @@ class TestPosition(unittest.TestCase):
|
|||
)
|
||||
|
||||
def test_unit_rsub(self):
|
||||
"""
|
||||
Test des soustractions d'unités (inversé)
|
||||
"""
|
||||
|
||||
for value_px in range(1, 100):
|
||||
for value_vw in range(1, 100):
|
||||
self.assertEqual(
|
||||
|
|
|
@ -5,7 +5,15 @@ from source.utils import dict_filter, dict_filter_prefix, dict_add_prefix, copy_
|
|||
|
||||
|
||||
class TestDict(unittest.TestCase):
|
||||
"""
|
||||
Unité de test des fonctionnalités utilitaire pour dictionnaire
|
||||
"""
|
||||
|
||||
def test_dict_filter(self):
|
||||
"""
|
||||
Test du filtre de dictionnaire
|
||||
"""
|
||||
|
||||
self.assertEqual(
|
||||
dict_filter(
|
||||
lambda key, value: key.startswith("valeur"),
|
||||
|
@ -31,6 +39,10 @@ class TestDict(unittest.TestCase):
|
|||
)
|
||||
|
||||
def test_dict_filter_prefix(self):
|
||||
"""
|
||||
Test du filtre de dictionnaire par prefix
|
||||
"""
|
||||
|
||||
self.assertEqual(
|
||||
dict_filter_prefix(
|
||||
"valeur",
|
||||
|
@ -56,6 +68,10 @@ class TestDict(unittest.TestCase):
|
|||
)
|
||||
|
||||
def test_dict_add_prefix(self):
|
||||
"""
|
||||
Test de l'ajout de prefix dans un dictionnaire
|
||||
"""
|
||||
|
||||
self.assertEqual(
|
||||
dict_add_prefix(
|
||||
"valeur",
|
||||
|
@ -82,7 +98,15 @@ class TestDict(unittest.TestCase):
|
|||
|
||||
|
||||
class TestMatrice(unittest.TestCase):
|
||||
"""
|
||||
Unité de test des fonctionnalités utilitaire pour matrice
|
||||
"""
|
||||
|
||||
def test_copy_array_offset(self):
|
||||
"""
|
||||
Test de la copie d'une matrice dans une autre avec décalage
|
||||
"""
|
||||
|
||||
src = np.array([
|
||||
[1, 2, 3, 4],
|
||||
[5, 6, 7, 8]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import numpy as np
|
||||
|
||||
from source.core import Boat
|
||||
from source.core.enums import Orientation, BombState
|
||||
from source.core.enums import BombState
|
||||
from source.core.error import InvalidBoatPosition, PositionAlreadyShot, InvalidBombPosition
|
||||
from source.type import Point2D
|
||||
from source.utils import copy_array_offset
|
||||
|
@ -9,8 +9,8 @@ from source.utils import copy_array_offset
|
|||
|
||||
class Board:
|
||||
"""
|
||||
Represent a board for the game.
|
||||
Boat can be added and bomb can be placed.
|
||||
Représente la planche de jeu.
|
||||
Des bateaux et des bombes peuvent y être placé.
|
||||
"""
|
||||
|
||||
__slots__ = ("width", "height", "boats", "bombs")
|
||||
|
@ -41,76 +41,76 @@ class Board:
|
|||
|
||||
def add_boat(self, boat: Boat, position: Point2D) -> None:
|
||||
"""
|
||||
Add a boat to the board. Check before if the position is valid.
|
||||
:boat: the boat to add
|
||||
:position: the position where to add the boat
|
||||
:raise: InvalidBoatPosition if the boat position is not valid
|
||||
Ajoute un bateau à la planche. Vérifie avant si la position est valide.
|
||||
:param boat: le bateau a placé
|
||||
:param position: la position du bateau sur la planche
|
||||
:raise: InvalidBoatPosition si la position du bateau est invalide
|
||||
"""
|
||||
|
||||
# get the old board matrice sum
|
||||
# récupère l'ancienne somme total de la grille matriciel
|
||||
board_matrice = self.boats.copy()
|
||||
board_matrice_sum_old: int = board_matrice.sum()
|
||||
board_matrice_max = np.max(board_matrice)
|
||||
|
||||
# get the sum of the boat
|
||||
# récupère la somme du bateau matriciel
|
||||
boat_matrice: np.array = boat.get_matrice(board_matrice_max+1)
|
||||
boat_matrice_sum: int = boat_matrice.sum()
|
||||
|
||||
# add the boat to the board matrice
|
||||
# ajoute la matrice du bateau à la matrice de la grille
|
||||
try:
|
||||
copy_array_offset(boat_matrice, board_matrice, offset=position)
|
||||
except ValueError:
|
||||
raise InvalidBoatPosition(boat, position)
|
||||
|
||||
# get the new board matrice sum
|
||||
# récupère la nouvelle somme de la grille matricielle
|
||||
board_matrice_sum_new: int = board_matrice.sum()
|
||||
|
||||
# if the sum of the old board plus the boat sum is different from the new board sum,
|
||||
# then the boat have been incorrectly placed (overlapping, outside of bounds, ...)
|
||||
# si la somme de l'ancienne planche et de la matrice du bateau n'est pas égal à celle de la nouvelle grille,
|
||||
# alors le bateau n'est pas correctement placé (hors de la grille, par dessus un autre bateau, ...)
|
||||
if board_matrice_sum_old + boat_matrice_sum != board_matrice_sum_new:
|
||||
raise InvalidBoatPosition(boat, position)
|
||||
|
||||
# otherwise accept the boat in the boats dict
|
||||
# sinon remplace l'ancienne matrice par la nouvelle
|
||||
self.boats = board_matrice
|
||||
|
||||
def bomb(self, position: Point2D) -> BombState:
|
||||
"""
|
||||
Hit a position on the board
|
||||
:position: the position where to shoot
|
||||
:raise: PositionAlreadyShot if the position have already been shot before
|
||||
Place une bombe sur la grille
|
||||
:position: la position de la bombe
|
||||
:raise: PositionAlreadyShot si la bombe a déjà été placé ici, InvalidBombPosition si la position est invalide.
|
||||
"""
|
||||
|
||||
# if the bomb is inside the board
|
||||
# si la bombe est bien dans les limites de la grille
|
||||
x, y = position
|
||||
if x >= self.width or y >= self.height: raise InvalidBombPosition(position)
|
||||
|
||||
# if this position have already been shot
|
||||
# si une bombe a déjà été placé ici
|
||||
if not self.bombs[y, x]: raise PositionAlreadyShot(position)
|
||||
|
||||
# get the old board matrice
|
||||
# récupère l'ancienne somme de la matrice de la grille
|
||||
board_mat_old_sum = self.get_matrice().sum()
|
||||
|
||||
# place the bomb (setting the position to False cause the matrice multiplication to remove the boat if any)
|
||||
# place la bombe dessus (False équivaut à placer une bombe)
|
||||
self.bombs[y, x] = False
|
||||
|
||||
# get the new board matrice
|
||||
# récupère la nouvelle somme de la matrice de la grille
|
||||
board_mat_new = self.get_matrice()
|
||||
board_mat_new_sum = board_mat_new.sum()
|
||||
|
||||
# if the board sum is 0, then there is no boat left on the board
|
||||
# si la somme de la grille matricielle est 0, alors il n'y a plus de bateau sur la grille
|
||||
if board_mat_new_sum == 0: return BombState.WON
|
||||
|
||||
# get the difference between the old and new board sum.
|
||||
# if the board sum changed, then the difference is the number of the boat that have been hit
|
||||
# récupère la différence entre l'ancienne et la nouvelle somme de la grille
|
||||
# si la somme a changé, alors un bateau a été touché.
|
||||
boat_touched: int = board_mat_old_sum - board_mat_new_sum
|
||||
|
||||
# if no boat have been touched, ignore
|
||||
# si aucun bateau n'a été touché, ignore
|
||||
if boat_touched == 0: return BombState.NOTHING
|
||||
|
||||
# if the boat have sinked (no more tile with the boat on it)
|
||||
# si le bateau a coulé (il n'y a plus de case correspondant à ce bateau)
|
||||
if not np.isin(boat_touched, board_mat_new): return BombState.SUNKEN
|
||||
|
||||
# if the boat have been touched, but without sinking
|
||||
# si le bateau a été touché partiellement
|
||||
return BombState.TOUCHED
|
||||
|
||||
def remove_bomb(self, cell: Point2D):
|
||||
|
@ -129,10 +129,12 @@ class Board:
|
|||
|
||||
def get_matrice(self) -> np.array:
|
||||
"""
|
||||
:return: the boats and bombs represented as a matrice
|
||||
:return: les bateaux et les bombes représentés sur une même matrice
|
||||
"""
|
||||
|
||||
return self.boats * self.bombs # Remove the position that have been bombed
|
||||
# En multipliant la matrice des bombes par la matrice des bateaux,
|
||||
# tous les bateaux avec une bombe dessus seront mis à 0 puisqu'une bombe placée vaut "False".
|
||||
return self.boats * self.bombs
|
||||
|
||||
def get_score(self) -> int:
|
||||
"""
|
||||
|
@ -145,6 +147,7 @@ class Board:
|
|||
return boat_total - boat_left
|
||||
|
||||
def to_json(self) -> dict:
|
||||
# converti en json les données
|
||||
return {
|
||||
"boats": self.boats.tolist(),
|
||||
"bombs": self.bombs.tolist()
|
||||
|
@ -152,12 +155,14 @@ class Board:
|
|||
|
||||
@classmethod
|
||||
def from_json(cls, json_: dict) -> "Board":
|
||||
# charge à partir de json les données
|
||||
return Board(
|
||||
boats=np.array(json_["boats"], dtype=np.ushort),
|
||||
bombs=np.array(json_["bombs"], dtype=np.bool_)
|
||||
)
|
||||
|
||||
def __copy__(self):
|
||||
# fait une copie de la grille
|
||||
return self.__class__(
|
||||
boats=self.boats.copy(),
|
||||
bombs=self.bombs.copy(),
|
||||
|
|
|
@ -5,8 +5,8 @@ from source.core.enums import Orientation
|
|||
|
||||
class Boat:
|
||||
"""
|
||||
Represent a boat.
|
||||
It can be added to a board.
|
||||
Représente un bateau.
|
||||
Il peut être ajouté à une grille.
|
||||
"""
|
||||
|
||||
__slots__ = ("orientation", "length")
|
||||
|
@ -20,7 +20,8 @@ class Boat:
|
|||
|
||||
def get_matrice(self, value: int = 1) -> np.array:
|
||||
"""
|
||||
:return: the boat represented as a matrice
|
||||
Représente le bateau sous la forme d'une matrice
|
||||
:return: le bateau sous la forme d'une matrice
|
||||
"""
|
||||
return np.full(
|
||||
(1, self.length) if self.orientation == Orientation.HORIZONTAL else
|
||||
|
@ -30,6 +31,7 @@ class Boat:
|
|||
)
|
||||
|
||||
def to_json(self) -> dict:
|
||||
# converti le bateau en json
|
||||
return {
|
||||
"length": self.length,
|
||||
"orientation": self.orientation.to_json(),
|
||||
|
@ -37,6 +39,7 @@ class Boat:
|
|||
|
||||
@classmethod
|
||||
def from_json(cls, json_: dict) -> "Boat":
|
||||
# charge le bateau à partir de json
|
||||
return Boat(
|
||||
length=json_["length"],
|
||||
orientation=Orientation.from_json(json_["orientation"]),
|
||||
|
|
|
@ -3,19 +3,19 @@ from enum import Enum
|
|||
|
||||
class BombState(Enum):
|
||||
"""
|
||||
This class represent the state of a bomb after being place on the board.
|
||||
Cette classe représente les états d'une bombe après avoir été placé sur la grille.
|
||||
"""
|
||||
|
||||
NOTHING = 0 # the bomb missed
|
||||
TOUCHED = 1 # the bomb touched a boat
|
||||
SUNKEN = 2 # the bomb touched the last part of a boat
|
||||
WON = 3 # the bomb sunk the last boat
|
||||
NOTHING = 0 # la bombe a manqué
|
||||
TOUCHED = 1 # la bombe a touché un bateau
|
||||
SUNKEN = 2 # la bombe a coulé un bateau
|
||||
WON = 3 # la bombe a coulé le dernier bateau
|
||||
|
||||
ERROR = -1 # the bomb could not be placed
|
||||
ERROR = -1 # la bombe n'a pas été placé
|
||||
|
||||
@property
|
||||
def success(self):
|
||||
"""
|
||||
:return: Vrai si la valeur correspond à une case qui a été touché
|
||||
:return: Vrai si une case a été touché
|
||||
"""
|
||||
return self in [self.TOUCHED, self.SUNKEN, self.WON]
|
||||
|
|
|
@ -3,7 +3,7 @@ from enum import Enum
|
|||
|
||||
class Orientation(Enum):
|
||||
"""
|
||||
Represent the orientation of a boat.
|
||||
Les possibles orientations pour un bateau
|
||||
"""
|
||||
|
||||
HORIZONTAL = "H"
|
||||
|
|
|
@ -3,5 +3,9 @@ from source.type import Point2D
|
|||
|
||||
|
||||
class InvalidBoatPosition(Exception):
|
||||
"""
|
||||
Erreur utilisée lorsque le bateau n'a pas pu être placé
|
||||
"""
|
||||
|
||||
def __init__(self, boat: Boat, position: Point2D):
|
||||
super().__init__(f"The boat {boat} can't be placed at {position}.")
|
||||
|
|
|
@ -2,5 +2,9 @@ from source.type import Point2D
|
|||
|
||||
|
||||
class InvalidBombPosition(Exception):
|
||||
"""
|
||||
Erreur utilisée lorsque la bombe n'a pas pu être placé
|
||||
"""
|
||||
|
||||
def __init__(self, position: Point2D):
|
||||
super().__init__(f"The bomb can't be placed at {position}.")
|
||||
|
|
|
@ -2,6 +2,10 @@ from source.type import Point2D
|
|||
|
||||
|
||||
class PositionAlreadyShot(Exception):
|
||||
"""
|
||||
Erreur utilisée lorsque la bombe vise une case déjà touchée
|
||||
"""
|
||||
|
||||
def __init__(self, position: Point2D):
|
||||
super().__init__(f"The position {position} have already been shot.")
|
||||
|
||||
|
|
Loading…
Reference in a new issue