Développement d'une bibliothèque de logique pour le jeu de Bataille navale en Python : Architecture et pièges courants
La bibliothèque implémente la logique du jeu classique de Bataille navale en se concentrant sur des algorithmes mathématiquement optimaux. Les classes principales sont Champ pour représenter le plateau de jeu et Navire pour gérer les bateaux. Le champ est stocké sous forme de matrice 10x10 en utilisant des listes de listes, où des symboles indiquent les états : espace pour une cellule vide, '1' pour une partie de navire intacte, '.' pour un tir manqué, 'X' pour une cellule détruite.
La classe Champ gère la création du plateau, la validation des coordonnées, le placement des navires et le traitement des tirs. Les coordonnées sont saisies au format 'a1', où les lettres A-J correspondent aux lignes et les nombres 1-10 aux colonnes. Les règles sont fixes : 1 navire de quatre cellules, 2 navires de trois cellules, 3 navires de deux cellules, 4 navires d'une cellule ; les navires ne doivent pas se toucher, même aux coins.
Implémentation de la classe Champ
L'initialisation crée une grille vide :
class Champ:
def __init__(self):
hauteur = 10
largeur = 10
self.grille = [[' ' for i in range(largeur)] for i in range(hauteur)]
La méthode afficher() affiche le champ dans la console avec les coordonnées. La première version contenait une erreur—la méthode line.insert(0, letter) modifiait la grille originale en ajoutant des lettres lors d'appels répétés. La correction : imprimer la lettre séparément sans altérer la structure.
def afficher(self):
print(' 1 2 3 4 5 6 7 8 9 10')
lettres = 'ABCDEFGHIJ'
compteur_lettres = 0
for ligne in self.grille:
print(lettres[compteur_lettres], *ligne)
compteur_lettres += 1
La validation des coordonnées est extraite dans la méthode privée _validation_coordonnee(). Elle vérifie la longueur de la chaîne (2-3 caractères), la présence d'une lettre dans A-J, et d'un nombre dans 1-10. La méthode trouver_coordonnee() détermine l'état de la cellule : vide, touchée, détruite, non_endommagée, déjà_touchée.
Les dictionnaires pour la conversion des coordonnées sont stockés comme attributs self.lignes et self.colonnes :
- lignes : {'a': 0, 'b': 1, ..., 'j': 9}
- colonnes : {'1': 0, '2': 1, ..., '10': 9}
La classe Navire et le placement des navires
La classe Navire stocke les paramètres dans un dictionnaire :
class Navire:
def __init__(self):
self.parametres = {
'coordonnees_vivantes': [],
'coordonnees_touchees': [],
'vivant': True
}
self.champ = None
def definir_champ(self, champ):
self.champ = champ
La méthode ajouter_navire(navire, ligne_coordonnees, longueur) dans Champ place un navire basé sur une chaîne comme 'a1-a2-a3'. D'abord, validation de la ligne _validation_ligne_navire() : analyse en coordonnées, vérification de la linéarité (horizontal/vertical), et correspondance de la longueur. Ensuite, pour chaque coordonnée, appel navire.definir_coordonnee_dans_navire(coordonnee), définissant '1' dans la grille.
def ajouter_navire(self, navire, ligne_coordonnees, longueur):
navire.definir_champ(self)
if not self._validation_ligne_navire(ligne_coordonnees):
return False
coordonnees = ligne_coordonnees.split('-')
if longueur != len(coordonnees):
return False
for coordonnee in coordonnees:
resultat = navire.definir_coordonnee_dans_navire(coordonnee)
if resultat:
lignes = self.lignes[coordonnee[0]]
colonnes = self.colonnes[coordonnee[1:]]
self.grille[lignes][colonnes] = '1'
return True
La relation Champ-Navire est bidirectionnelle : Champ appelle navire.definir_champ(self), et Navire utilise self.champ pour la validation. Cela viole le principe d'encapsulation des couches de base mais simplifie l'API.
Perspectives de refactorisation
Le projet a révélé des pièges clés pour débutants :
- Surcharge de méthode : trouver_coordonnee() combinait validation, recherche et sortie—divisée en _validation_coordonnee() et obtenir_etat_cellule().
- Mutation des données : Éviter insert() dans afficher() ; travailler avec des copies.
- Dictionnaires codés en dur : Extraire en constantes de classe ou utiliser ord() pour les conversions.
- Valeurs de retour : Standardiser : Vrai/Faux pour le succès, chaînes pour les erreurs.
Pour un gameplay optimal, intégrez des algorithmes de recherche probabiliste : après un coup touché, scannez les cellules adjacentes ; après destruction, excluez les ponts des probabilités.
Points clés à retenir
- Champ comme matrice de listes avec des symboles pour les états ; validation des coordonnées via les dictionnaires self.lignes/self.colonnes.
- Navire stocke les coordonnées vivantes/touchées ; lien bidirectionnel avec Champ via definir_champ().
- Placement via ligne_coordonnees ('a1-a2') ; vérifier la linéarité et les zones tampons.
- Éviter de muter la grille dans afficher() ; extraire les vérifications communes dans des méthodes privées.
- Pour des algorithmes avancés : stockez une carte de probabilité des tirs séparément de la grille.
— Editorial Team
Aucun commentaire pour le moment.