Jouez contre une composition aléatoire au grand classique du jeu de société : la bataille navale ! Devinez ou sont cachés les bateaux en un minimum de temps.
Sommaire
Introduction
Projet réalisé par A. Juge, P. Moucadeau et B. 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
def grille(): ''' Tracé de la grille ''' for i in range(11): if i<11:fr(50,10+20*i,200,1,N) fr(50+20*i,10,1,200,N)
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.
def wait(b): while Partie: for i in b: if keydown(i): while keydown(i):True return i
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
bateau | x1,y1 | x2,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.
def is_touch(self,x,y): ''''Méthode revoyant Vrai si le bateau est touché et Faux si il ne l'est pas ''' l = [x,y] for i,elt in enumerate(self.LC): if elt == l: self.etat = 1 self.L_etats[i] = 1 return True return False def is_coule(self): '''Méthode revoyant Vrai si le bateau est coulé et Faux si il ne l'est pas ''' if self.etat == 2: return True for i in self.L_etats: if i != 1: return False for j in self.LC: ca(j[0],j[1],N) self.L_etats = [] for i in range(len(self.LC)): self.L_etats.append(2) self.etat=2 print(self, "est coulé") return True
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.
def Main(n): '''Boucle principale''' global X,Y,Partie,NBcoups,NBtouches,LB LB = compo(n) grille() ca(X,Y,G) print(Partie) while Partie: t = wait([0,1,2,3,4,]) if t == 3 and X+1<10: majcase(X,Y,X+1,Y) X+=1 elif t == 0 and X>0: majcase(X,Y,X-1,Y) X-=1 elif t == 1 and Y>0: majcase(X,Y,X,Y-1) Y-=1 elif t == 2 and Y+1<10: majcase(X,Y,X,Y+1) Y+=1 elif t == 4: touche(X,Y) ds("Coups",255,10,Bl) ds(str(NBcoups),255,30) ds("Touches",251,50,R) ds(str(NBtouches),255,70)
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, y1 de ce dernier.
def compo(n): '''Générateur de composition aléatoire''' L= [] for i in range(n): ori = randint(0,1) sens = randrange(-1,2,2) longueur = randint(2,4) x1 = randint(0,10) y1 = randint(0,10) if ori == 0: x2 = x1 y2 = y1+(longueur*sens) c1 = y1 c2 = y2 else : x2 = x1+(longueur*sens) y2 = y1 c1 = x1 c2 = x2 if c2 > 9: c1 = c1 - (c2-9) c2 = c1+(longueur*sens) elif c2 < 0: c1 = c1 - c2 c2 = c1+(longueur*sens) if ori == 0: L.append(bateau(x1,c1,x2,c2)) else : L.append(bateau(c1,y1,c2,y2)) return L
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.
def play(): fill_rect(0,0,320,222,(255,255,255)) texte_center("Battleship", 160, 20, col_os()) texte_center("nsi.xyz/battleship ", 160, 50, N) texte_center("par", 160, 80, (101,101,101)) texte_center("Alexandre Juge", 160, 100, (42,42,42)) texte_center("Pierre Moucadeau", 160, 120, (42,42,42)) texte_center("Baptiste Kerneis ", 160, 140, (42,42,42)) sleep(2) fill_rect(0,0,320,222,(255,255,255)) Main(5)
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.
def end(): global NBcoups fill_rect(0,0,320,222,B) texte_center("Battleship", 160, 20, col_os()) texte_center("Vous avez fait un score de :", 160, 50, N) texte_center(str(NBcoups), 160, 80, col_os()) texte_center("Vous pouvez faire mieux !", 160, 100, (42,42,42))
Mention spéciale
Mention spéciale à la toute petite fonction ca qui malgré ses 2 lignes 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 »
def ca(x,y,c=G): ''' Remplis la case x,y avec la couleur c ''' fr(51+20*x,11+20*y,19,19,c)
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.
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 (réalisé avec @cent20).
Ainsi que M.Schraf pour ses tutos très utiles sur la couche graphique de Python
Téléchargement
Cet article a été écrit par des élèves du lycée Louis Pasteur.
Ils étudiaient alors la spécialité NSI, en classe de 1ère.
Promotion 2020 – 2021