Une bataille navale codée en python sur NumWorks


Accueil > Projets > Une bataille navale codée en python sur NumWorks

Par Juge A., Kerneïs B., Moucadeau P. en novembre 2020

Introduction

Projet réalisé par Alexandre Juge, Pierre Moucadeau et Baptiste Kerneïs de la classe de première en spécialité NSI. Nous sommes également les auteurs de ce compte rendu. Pour en savoir plus : Les projets en spécialité NSI »

Lien court vers ce document : https://nsi.xyz/battleship

Jouez à l’incontournable jeu de la bataille navale directement sur votre calculatrice NumWorks ou sur un émulateur compatible avec le module kandinsky (Omega - IDE) !

La bataille navale ou touché-coulé est un jeu de société commercialisé en 1831 qui se joue à deux joueurs. Le principe du jeu, si vous ne le connaissez pas, est de placer des « navires » sur une grille tenue secrète et de tenter de couler les navires adverses. La partie s’arrête lorsque l’un des deux joueur a réussi à couler tous les navires de l’autre joueur.

Dans le cadre de notre projet, le jeu codé en python a été simplifié : le joueur essaie de deviner la composition de l’ordinateur qui a été générée aléatoirement et ainsi, le jeu ne se joue que dans un seul sens. C’est à dire que le joueur ne peut que gagner ! Incroyable non ?

Vous pouvez jouer contre avec vos amis et tenter de gagner en le moins de coup possible !

Évolution

Le processus de création s’est divisé en différentes parties.

La première grille
Avec le module kandinsky, nous avons commencé par reprendre et améliorer le code de Schraf : Maths-info qui nous a permit de construire une grille de 10*10

  1. def grille():
  2.     '''
  3.    Tracé de la grille
  4.    '''
  5.     for i in range(11):
  6.         if i<11:fr(50,10+20*i,200,1,N)
  7.         fr(50+20*i,10,1,200,N)

Télécharger

Interaction homme/machine
Ensuite, nous avons crée un "curseur" qui peut se déplacer dans cette grille en récupérant les touches pressées par le joueur grâce au module ion.

Cette portion de code sert à attendre que l’utilisateur appuie sur une touche et la récupérer.

  1. def wait(b):
  2.     while Partie:
  3.         for i in b:
  4.             if keydown(i):
  5.                 while keydown(i):True
  6.                 return i

Télécharger

code inspiré de Arthur Jacquin

La classe bateau
La création d’une classe bateau nous a permit de simplifier le code, notamment pour ce qui est de détecter si un bateau est touché et/ou coulé uniquement avec les coordonnées du début et de la fin

Exemple possibles de bateaux
bateaux1,y1x2,y2état
1 1,2 1,4 touché
2 6,4 9,4 safe
3 8,2 8,5 coulé

Les méthodes is_touche et is_coule sont la pour repérer si un bateau est touché ou coulé lorsque le joueur décide de tirer sur une case. Elles utilisent la liste des cases composant le bateau.

  1.     def is_touch(self,x,y):
  2.         ''''Méthode revoyant Vrai si le bateau est touché
  3.        et Faux si il ne l'est pas
  4.        '''
  5.         l = [x,y]
  6.         for i,elt in enumerate(self.LC):
  7.             if elt == l:
  8.                 self.etat = 1
  9.                 self.L_etats[i] = 1
  10.                 return True
  11.      
  12.         return False
  13.    
  14.     def is_coule(self):
  15.         '''Méthode revoyant Vrai si le bateau est coulé
  16.        et Faux si il ne l'est pas
  17.        '''
  18.         if self.etat == 2:
  19.             return True
  20.  
  21.         for i in self.L_etats:
  22.             if i != 1:
  23.                 return False
  24.  
  25.         for j in self.LC:
  26.             ca(j[0],j[1],N)
  27.  
  28.         self.L_etats = []
  29.         for i in range(len(self.LC)):
  30.             self.L_etats.append(2)
  31.  
  32.         self.etat=2
  33.         print(self, "est coulé")
  34.         return True

Télécharger

Le cerveau du programme
La fonction Main représente la boucle principale, c’est elle qui récupère les boutons pressés par les joueurs pour appeler les autres fonctions. La boucle s’arrête lorsque tous les bateaux sont dans l’état coulé.
Puisque c’est une boucle qui tourne à toutes les actions du joueur, c’est l’endroit idéal pour mettre à jour les infos du joueur, comme le nombre de coups.

  1. def Main(n):
  2.     '''Boucle principale'''
  3.     global X,Y,Partie,NBcoups,NBtouches,LB
  4.     LB = compo(n)
  5.     grille()
  6.     ca(X,Y,G)
  7.     print(Partie)
  8.     while Partie:
  9.         t = wait([0,1,2,3,4,])
  10.         if t == 3 and X+1<10:
  11.             majcase(X,Y,X+1,Y)
  12.             X+=1
  13.         elif t == 0 and X>0:
  14.             majcase(X,Y,X-1,Y)
  15.             X-=1
  16.         elif t == 1 and Y>0:
  17.             majcase(X,Y,X,Y-1)
  18.             Y-=1
  19.         elif t == 2 and Y+1<10:
  20.             majcase(X,Y,X,Y+1)
  21.             Y+=1
  22.         elif t == 4:
  23.             touche(X,Y)
  24.         ds("Coups",255,10,Bl)
  25.         ds(str(NBcoups),255,30)
  26.         ds("Touches",251,50,R)
  27.         ds(str(NBtouches),255,70)

Télécharger

Le tirage aléatoire de la composition
Il a fallu ensuite générer aléatoirement la composition des bateaux de l’adversaire. Le module random nous a été très utile. La difficulté principale a été de faire que les bateaux ne dépassent pas de la grille. Nous avons donc décalés les bateaux étant dans cette situation en modifiant les coordonnées x1,y1de ce dernier.

  1. def compo(n):
  2.     '''Générateur de composition aléatoire'''
  3.     L= []
  4.  
  5.     for i in range(n):
  6.         ori = randint(0,1)
  7.         sens = randrange(-1,2,2)
  8.         longueur = randint(2,4)
  9.         x1 = randint(0,10)
  10.         y1 = randint(0,10)
  11.  
  12.         if ori == 0:
  13.             x2 = x1
  14.             y2 = y1+(longueur*sens)
  15.             c1 = y1
  16.             c2 = y2
  17.         else :
  18.             x2 = x1+(longueur*sens)
  19.             y2 = y1
  20.             c1 = x1
  21.             c2 = x2
  22.        
  23.         if c2 > 9:
  24.             c1 = c1 - (c2-9)
  25.             c2 = c1+(longueur*sens)
  26.         elif c2 < 0:
  27.             c1 = c1 - c2
  28.             c2 = c1+(longueur*sens)
  29.        
  30.         if ori == 0:
  31.             L.append(bateau(x1,c1,x2,c2))
  32.         else :
  33.             L.append(bateau(c1,y1,c2,y2))
  34.    
  35.     return L

Télécharger

Interface graphique
Puisque les conditions de jeu ne changent pas (mode de jeu, nombre de bateaux...), la présence d’un menu augmente inutilement la taille du fichier. Nous avons alors opté pour insérer une image d’accueil qui s’efface au bout de 2 secondes. Le jeu se lance lorsque les 2 secondes sont écoulées.

  1. def play():
  2.     fill_rect(0,0,320,222,(255,255,255))
  3.     texte_center("Battleship", 160, 20, col_os())
  4.     texte_center("nsi.xyz/battleship ", 160, 50, N)
  5.     texte_center("par", 160, 80, (101,101,101))
  6.     texte_center("Alexandre Juge", 160, 100, (42,42,42))
  7.     texte_center("Pierre Moucadeau", 160, 120, (42,42,42))    
  8.     texte_center("Baptiste Kerneis ", 160, 140, (42,42,42))
  9.     sleep(2)
  10.     fill_rect(0,0,320,222,(255,255,255))
  11.     Main(5)

Télécharger

Résultat des crédits

Par la suite nous avons rajouté 2 compteurs, un comptant le nombre de coup et l’autre le nombre de bateau touché.

Lorsque le nombre de bateau touché est égale au nombre de bateau il lance un menu de fin.

  1. def end():
  2.     global NBcoups
  3.     fill_rect(0,0,320,222,B)
  4.     texte_center("Battleship", 160, 20, col_os())
  5.     texte_center("Vous avez fait un score de :", 160, 50, N)
  6.     texte_center(str(NBcoups), 160, 80, col_os())
  7.     texte_center("Vous pouvez faire mieux !", 160, 100, (42,42,42))

Télécharger

Mention spéciale

Mention spéciale à la toute petite fonction ca qui malgré ses 2 lignes [1] est la fonction la plus utile puisque elle permet de remplir un case. Et oui, tout simplement

Ce n’est pas la taille qui compte , "Un grand sage"

  1. def ca(x,y,c=G):
  2.     ''' Remplis la case x,y avec la couleur c '''
  3.     fr(51+20*x,11+20*y,19,19,c)

Télécharger

Problèmes rencontrés
A la fin du processus de création nous nous sommes heurtés à de nombreux problèmes le premier étant la taille du fichier, supérieur à 8ko donc inutilisable sur la calculatrice, ont a alors dut supprimer le mode multijoueur car prenant trop de place.
Deuxièmement les bateaux généré étaient magnifique mais complètement en dehors de la grille.

Certains bateaux étaient générés en dehors de la grille

Pour régler ce problème nous avons rajouter une condition à la formation des bateaux qui fait ,que lorsqu’ils se trouvent en dehors de l’intervalle de la grille ,ils sont décalés à l’intérieur de la grille .

Anecdote

Pour l’anecdote, il faut savoir que nous voulions originellement créer un jeu uno ! Mais suite à de nombreuses difficultés, notamment la gestion de la pioche pour deux joueurs, nous nous sommes réorientés vers la bataille navale et rabattu sur une parti en solo.

Conclusion

Pour pouvoir jouer à cette version du jeu bataille navale, téléchargez le code par l’intermédiaire du lien ci-dessous. Les commandes sont assez intuitives : les flèches pour se déplacer, et le bouton OK pour interagir.
Amusez-vous bien !

Crédits / Remerciements

Tout d’abord un grand merci aux fragments de codes de Arthur Jacquin, notamment son menu.
Ainsi que M.Schraf pour ses tutos très utiles sur la couche graphique de Python

Téléchargement

battleship.py.zip (ZIP - 2.2 ko)
battleship.py.zip

Mots-clés