Bataille navale

l’arène de la promo
Connexion…

Le projet

Une bataille navale entre binômes, arbitrée par ce serveur.

Le jeu. Deux joueurs, deux grilles de dix cases sur dix, et cinq navires à placer sur la sienne. On tire chacun son tour sur une case adverse et le serveur répond : à l’eau, touché, ou coulé. Le premier qui envoie toute la flotte d’en face par le fond gagne.

L’arbitre. Ce serveur détient les deux grilles et applique les règles. Personne ne voit la flotte adverse, personne ne joue deux fois de suite, et un tour non joué en trente secondes est perdu. Vous n’avez donc ni règles à coder, ni arbitre à écrire.

Ce qui est demandé. En binôme, vous écrivez un client : un programme qui s’inscrit, place sa flotte, joue la partie entière sans intervention et l’affiche à l’écran. Vous ne partez pas de la page blanche : le kit ci-dessous se télécharge et tourne en un quart d’heure, il ne vous restera que trois fichiers à remplir. Deux jalons, deux prosits retour, une soutenance et un tournoi pour finir. À deux sur un projet de cette taille, les deux écrivent du code et les deux répondent aux questions.

Démarrer

le kit de départ, à récupérer dès le prosit aller

Le kit est découpé comme un projet : le réseau d’un côté, la boucle de jeu de l’autre, et vos décisions isolées dans trois fichiers. Ce découpage vous permettra de changer d’interface ou de stratégie sans rien casser ailleurs.

Télécharger le kit battleship-kit.zip
Terminalcd battleship-kit
python -m venv .venv
.venv\Scripts\activate            # sous Windows
source .venv/bin/activate         # sous macOS et Linux
pip install -r requirements.txt

python main.py --serveur $SERVEUR --nom "Les Corsaires"

Le client s’inscrit, trouve une partie, envoie sa flotte, puis s’arrête sur NotImplementedError: jalon 1 : tir aleatoire sans repetition. C’est le point de départ prévu. À partir de là, ce sont les tests qui vous guident.

arborescencebattleship-kit/
    main.py               assemble les morceaux
    requirements.txt
    battleship/
        api.py            les appels au serveur
        game.py           la boucle d'une partie
        fleet.py          le placement des navires
        strategy.py       le choix du tir
        display.py        l'affichage
    tests/
        test_fleet.py     ce qu'exige le serveur
        test_strategy.py  ce qu'exige une strategie
FichierÉtat
api.pyfourni
game.pyfourni
main.pyfourni
tests/fournis
FlotteAuHasardfournie
strategy.pyjalons 1 et 2
display.pyjalon 1
Flottejalon 2
Terminalpytest -q

Cinq tests passent déjà : ils portent sur la flotte fournie et montrent ce qu’un placement valide veut dire. Les autres échouent, et chacun décrit une contrainte que le serveur appliquera pendant la partie. Les faire passer un par un vaut mieux que découvrir une règle en plein tournoi. Ils tournent sans serveur, donc aussi souvent que vous voulez. Au jalon 2, vous en ajouterez deux à vous, sur votre propre stratégie : lire une spécification est une chose, en écrire une en est une autre.

Le dépôt GitHub

tout ce que vous rendez passe par lui

Chaque binôme crée un dépôt GitHub dès le lundi, y pousse le kit tel quel, puis y travaille toute la semaine. Il n’y a pas d’autre rendu : le code, les diapositives des prosits retour et celles de la soutenance sont lus sur le dépôt, dans l’état où il se trouve à chaque échéance.

arborescencevotre-depot/
    README.md             les deux noms, et comment lancer le client
    .gitignore            fourni, il écarte .venv et les caches
    main.py
    requirements.txt
    battleship/           votre code
    tests/                ceux du kit, plus les vôtres au jalon 2
    slides/
        prosit-1.pdf      avant le prosit retour du mercredi
        prosit-2.pdf      avant le prosit retour du jeudi
        soutenance.pdf    avant la soutenance du vendredi

Le kit contient déjà le .gitignore et le dossier slides/. Les diapositives sont en PDF, pour s’ouvrir partout sans surprise de police.

  • Un dépôt par binôme, créé lundi, son adresse envoyée à l’enseignant le soir même.
  • Les deux membres poussent leurs propres commits, chacun depuis son compte.
  • Les diapositives sont sur le dépôt avant le début de chaque prosit retour.
  • Le dépôt n’est plus modifié pendant la soutenance : le code montré est celui qui y est.
L’historique est lu. Des commits réguliers à deux noms racontent une semaine de travail. Un seul commit le vendredi matin, signé par un seul membre, raconte autre chose, et c’est ce qui sera demandé en soutenance.
Terminal# dans le dossier du kit, une fois le dépôt créé sur github.com
git init
git add .
git commit -m "Kit de depart"
git branch -M main
git remote add origin https://github.com/VOTRE_COMPTE/VOTRE_DEPOT.git
git push -u origin main

# puis, tout au long de la semaine, à chaque étape qui tient debout
git add .
git commit -m "Tir aleatoire sans repetition"
git push

Le second membre clone le dépôt et pousse de la même manière. Public ou privé, à vous de voir : s’il est privé, invitez l’enseignant comme collaborateur.

Le planning de la semaine

les temps encadrés sont signalés par un filet vert

Lundi

Matin Accueil de la promotion
Après-midi Prosit aller Lancement du projet et du jalon 1

Mardi

Journée Travail en binôme Jalon 1 : jouer contre un bot

Mercredi

Matin Prosit retour Jalon 1 : démonstration et diapositives
Fin de matinée Prosit aller Lancement du jalon 2
Après-midi Travail en binôme

Jeudi

Matin Prosit retour Jalon 2 : la stratégie de tir
Après-midi Travail en binôme

Vendredi

Matin Projet Dernières finitions et répétition
Début d’après-midi Soutenance Un quart d’heure par binôme

Deux jours et demi de développement effectif, prosits compris. C’est court : le jalon 1 doit être bouclé mardi soir, sans quoi le jalon 2 ne tiendra pas.

Le projet en deux jalons

chacun se termine par un prosit retour

D’abord jouer, ensuite bien jouer. Le premier jalon produit une partie complète contre un bot, avec une interface pour la suivre. Le second remplace le hasard par une stratégie, au tir comme au placement.

Jalon 1

Jouer contre un bot

Le programme s’inscrit, demande un entraînement contre le bot Facile, envoie la flotte fournie par le kit et joue la partie jusqu’au bout sans intervention. L’affichage se fait en console, deux grilles de caractères : c’est la consigne, pas un pis-aller. Les tirs restent aléatoires, à condition qu’aucune case ne soit jouée deux fois.

  • Le client sur le dépôt, avec un README qui dit comment le lancer.
  • Un affichage console des deux grilles et du tour en cours.
  • Les six diapositives du prosit retour, dans slides/prosit-1.pdf.
  • Dix parties s’enchaînent sans intervention ni plantage.
  • Aucun tir n’est refusé par le serveur.
  • La flotte est acceptée avant la fin du chronomètre.
  • En regardant l’écran, on sait qui a la main et où en sont les navires.
Jalon 2

La stratégie

Le hasard laisse la place à une stratégie. Le client exploite ce qu’il a déjà touché au lieu de repartir de zéro à chaque tour, et vous choisissez enfin où poser vos navires plutôt que de laisser le kit le faire.

  • Une stratégie de tir, chasse et achèvement au minimum.
  • Votre propre placement de flotte, que vous savez justifier.
  • Deux tests que vous avez écrits vous-mêmes, dans tests/.
  • Les six diapositives du prosit retour, dans slides/prosit-2.pdf.
  • Le bot Moyen est battu au moins six fois sur une série de dix parties.
  • Vos deux tests passent, et vérifient autre chose que ceux du kit.
  • Une touche oriente visiblement les tirs suivants.
  • Une erreur du serveur est affichée sans arrêter la partie.
  • Le client traverse le tournoi sans intervention.

Dix minutes devant le groupe, diapositives projetées, puis les questions. L’objectif n’est pas de montrer que tout fonctionne, mais d’expliquer où vous en êtes et pourquoi. Une difficulté rencontrée et analysée vaut mieux qu’une démonstration lisse dont personne ne comprend le fonctionnement.

SlideContenu attendu
1Le binôme, et ce que vous aviez à faire pour ce jalon.
2Ce qui fonctionne aujourd’hui, une phrase par point.
3Une capture de votre interface, commentée.
4Un choix technique, et la raison qui l’a emporté.
5Une difficulté rencontrée, et comment vous l’avez traitée.
6Ce qui reste à faire, et la répartition entre vous deux.

Six diapositives, dans cet ordre, plus deux annexes au maximum. Pas de code sur les diapositives, sauf trois lignes quand elles portent le propos : le code se montre à l’écran, pas en image.

L’interface

jalon 1

Le serveur n’envoie pas d’images, il envoie des faits. Afficher la partie consiste à traduire l’état reçu à chaque tour en une représentation lisible. Voici ce dont vous disposez :

ChampUsage à l’écran
your_shotsLa grille adverse, chacun de vos tirs avec son résultat.
shots_receivedVotre grille, avec les coups reçus.
your_fleetVos navires un par un : intacts, touchés, coulés.
opponent_ships_remainingCombien de navires restent en face, sans dire où.
your_turn, seconds_leftQui a la main, et le temps qui reste.
Au jalon 1, la console suffit et c’est ce qui est demandé. Une fenêtre ou une page web est un bonus, à ne tenter qu’une fois la partie complète qui tourne : coupler une boucle de jeu séquentielle à une boucle d’affichage graphique est un problème en soi, et il en a coulé de plus solides que vous un mardi soir.

Dans la console jalon 1

Deux grilles de caractères côte à côte, une ligne par tir. Rapide à écrire, et c’est ce qui vous aidera le plus à déboguer.

Dans une fenêtre bonus

tkinter est livré avec Python et suffit : une grille de boutons, deux couleurs. Attention à ne pas geler l’affichage pendant l’attente du serveur.

Dans le navigateur bonus

Une page qui interroge votre propre programme. Plus de travail, mais en terrain connu. Le code de ce tableau de bord est dans le dossier du serveur.

Gardez une fonction qui parle au serveur et une autre qui dessine. Le jour où vous changez d’interface, vous ne réécrivez que la seconde. Et espacez vos requêtes : deux à cinq par seconde suffisent, une boucle sans pause sature le réseau de la salle.

La stratégie de tir

jalon 2

Quatre paliers, du plus simple au plus ambitieux. Les deux premiers sont attendus, les autres sont là pour ceux qui veulent aller plus loin.

  1. Le hasard sans répétition

    Une case au hasard, jamais deux fois la même, sinon le serveur refuse le tir et vous perdez votre tour. C’est ce que fait votre client au jalon 1.

    Dans quelle structure gardez-vous les cases déjà jouées ?

  2. Chasse et achèvement

    Tant que rien n’est touché, vous cherchez. Dès qu’un tir répond HIT, le navire continue forcément dans une des quatre cases voisines : vous les explorez avant de repartir ailleurs. Et si deux touches s’alignent, vous connaissez sa direction.

    Comment tenez-vous la liste des cases à explorer ? Et qu’en faites-vous quand le serveur répond SUNK, alors qu’elle peut contenir des touches d’un autre navire ?

  3. Le damier

    Le plus petit navire occupe deux cases collées. Il ne peut donc pas se cacher entre les cases d’un damier bien espacé, ce qui vous dispense d’en viser la moitié tant qu’il n’est pas coulé.

    Quel espacement choisissez-vous, et quand cesse-t-il d’être valable ?

  4. La carte de probabilités

    Pour chaque case libre, comptez dans combien de positions possibles des navires encore à flot elle apparaît, puis visez le maximum. C’est ce que fait le bot Difficile : du dénombrement, contraint par ce que vous avez déjà observé.

    Comment recalculer cette carte à chaque tir sans y passer trois secondes ?

Au jalon 2, vous remplacez la flotte au hasard du kit par la vôtre. On pense surtout au tir, mais la moitié du sort se joue en posant les navires. Ils ont le droit de se toucher : est-ce imprudent face à un adversaire qui explore les cases voisines d’une touche, ou est-ce au contraire ce qui l’égare ? Les bords protègent-ils vraiment ? Et garder le même placement quand on affronte trois fois la même équipe, est-ce tenable ? Essayez, comparez, et venez défendre votre réponse.

Les réglages de cette arène

à lire depuis /api/config
chargement…

Les bots d’entraînement

en battre un vous donne son badge
chargement…

La soutenance

vendredi, un quart d’heure par binôme

La soutenance reprend le format des prosits retour, en plus complet : une dizaine de diapositives, une démonstration en direct, puis les questions sur le code.

  1. Présentation, quatre minutes

    Le projet, votre organisation, vos choix techniques et ce que vous avez appris. Les diapositives du jalon 2, reprises et complétées.

  2. Démonstration, quatre minutes

    Votre client contre le bot Moyen, affichage projeté, sans rien manipuler pendant la partie.

  3. Modification en direct, quatre minutes

    On vous demande un changement de comportement, et vous le codez devant nous, puis vous relancez. Par exemple : ne plus tirer sur les bords tant que le plus petit navire n’est pas coulé, ou repartir en chasse dès qu’un navire coule. Rien qui dépasse trois ou quatre lignes, mais dans du code que vous devez connaître.

  4. Questions, trois minutes

    Sur le code affiché à l’écran. L’enseignant désigne lequel des deux répond, et l’autre n’intervient pas.

  • Comment le programme sait-il que c’est son tour ?
  • Que se passe-t-il si le serveur renvoie une erreur en pleine partie ?
  • Où sont stockées les cases déjà jouées, et pourquoi ainsi ?
  • Qu’est-ce qui change dans la stratégie quand un navire coule ?
  • Pourquoi ce placement de flotte plutôt qu’un autre ?
  • Qui a écrit quoi, et comment vous vous êtes réparti le travail ?
  • Vos heuristiques de tir et de placement, et les raisons qui les motivent
  • Votre manière de structurer les données de la partie et de suivre son état
  • La tenue de votre programme : erreurs affichées, dix parties sans planter
  • Votre capacité à modifier votre propre code sans le relire pendant dix minutes
  • L’historique du dépôt : deux auteurs, des commits tout au long de la semaine

Le protocole HTTP n’est pas évalué : le kit s’en charge. Le code présenté est le code qui tourne, et préparer la soutenance consiste surtout à vous interroger mutuellement sur les parties que vous n’avez pas écrites vous-même.

Annexe : le contrat de données

le kit fait déjà ces appels, cette annexe sert à comprendre et à déboguer

x est la colonne, de gauche à droite. y est la ligne, de haut en bas. Les deux partent de 0, donc la case A1 s’écrit x=0, y=0.

Un navire est ancré sur sa case de départ. Il s’étend vers la droite en orientation "H", vers le bas en "V". Les lettres et les chiffres ne servent qu’à l’affichage : sur le réseau, tout est en nombres.

En vert, un croiseur de 4 cases placé en x=1, y=2, orientation "V".

S’inscrire : POST /api/register
envoyé{
  "name": "Les Corsaires"
}
reçu{
  "token": "kJ8...",
  "player_id": "3f9a1c02",
  "name": "Les Corsaires",
  "message": "..."
}

Le jeton identifie l’équipe sur toutes les requêtes suivantes, dans l’en-tête X-Token. Une réinscription sous le même nom rend le compte et son Elo, avec un jeton neuf.

Placer sa flotte : POST /api/match/<id>/fleet
envoyé{
  "ships": [
    { "ship": "porte_avions",      "x": 0, "y": 0, "orientation": "H" },
    { "ship": "croiseur",          "x": 4, "y": 3, "orientation": "V" },
    { "ship": "contre_torpilleur", "x": 7, "y": 1, "orientation": "V" },
    { "ship": "sous_marin",        "x": 2, "y": 6, "orientation": "H" },
    { "ship": "torpilleur",        "x": 9, "y": 8, "orientation": "V" }
  ]
}

La flotte doit être complète, chaque navire une seule fois, tout dans la grille et sans chevauchement. Le contact bord à bord est autorisé.

Consulter l’état : GET /api/match/<id>/state
reçu{
  "status": "playing",
  "opponent": "Les Flibustiers",
  "your_turn": true,
  "seconds_left": 24.3,
  "turn_number": 17,
  "your_shots":      [ { "x": 4, "y": 2, "result": "HIT",  "ship": "croiseur" } ],
  "shots_received":  [ { "x": 0, "y": 0, "result": "MISS" } ],
  "your_fleet":      [ { "ship": "croiseur", "name": "Croiseur",
                         "size": 4, "hits": 1, "sunk": false } ],
  "opponent_ships_remaining": 4,
  "winner": null,
  "finished": false
}

De quoi dessiner les deux grilles, et rien de plus : la position des navires adverses ne circule jamais avant la fin de la partie.

Tirer : POST /api/match/<id>/fire
envoyé{ "x": 4, "y": 2 }
reçu{
  "result": "SUNK",
  "ship": "croiseur",
  "ship_name": "Croiseur",
  "ship_size": 4,
  "your_turn": false,
  "remaining_ships": 3,
  "finished": false,
  "winner": null
}

Trois résultats : MISS, HIT, SUNK. Le nom et la taille du navire n’arrivent qu’avec SUNK.

Une erreur arrive toujours sous la forme { "error": "..." }, avec un code HTTP. Le kit la transforme en message lisible : affichez-le.

CodeCe que le serveur signale
401 Jeton absent, inconnu ou périmé.
404 Le match n’existe pas, ou il n’appartient pas à cette équipe.
409 Mauvais moment : ce n’est pas votre tour, la partie n’a pas commencé, elle est finie, ou vous êtes déjà dans un autre match.
422 Contenu invalide : placement refusé, tir hors grille, case déjà tirée, x ou y manquant.

Facultatif, mais instructif : jouer un tour depuis le terminal montre exactement ce qui circule, et aide beaucoup le jour où le code ne fait pas ce que vous croyez. Sous Windows, écrivez curl.exe, car curl est un alias vers autre chose dans PowerShell.

Terminal# les réglages de la partie
curl $SERVEUR/api/config

# inscription, on note le jeton renvoyé
curl -X POST $SERVEUR/api/register \
     -H "Content-Type: application/json" \
     -d '{"name": "Les Corsaires"}'

# un entraînement contre le bot Facile
curl -X POST $SERVEUR/api/sparring \
     -H "Content-Type: application/json" \
     -H "X-Token: MON_JETON" \
     -d '{"level": "facile"}'

# le match en cours, puis son état
curl $SERVEUR/api/match/current -H "X-Token: MON_JETON"
curl $SERVEUR/api/match/MON_MATCH/state -H "X-Token: MON_JETON"

# un tir en E3, soit x=4 et y=2
curl -X POST $SERVEUR/api/match/MON_MATCH/fire \
     -H "Content-Type: application/json" \
     -H "X-Token: MON_JETON" \
     -d '{"x": 4, "y": 2}'

Les pièges

  • Tirer sans regarder à qui est la main. Le serveur répond 409 et le chronomètre continue de tourner. Lisez your_turn avant chaque tir.
  • Intervertir x et y. Les tirs visent le symétrique de la cible. Si les résultats n’ont pas de sens, vérifiez cela en premier.
  • Laisser filer le chronomètre de placement et perdre la partie avant le premier tir.
  • Avaler les erreurs. Un except silencieux transforme un détail en soirée de recherche. Affichez le message du serveur.
  • Coder les règles en dur. Taille de grille, flotte et chronomètres viennent de /api/config : ils peuvent changer le jour du tournoi.
  • Pousser tout d’un coup le vendredi matin. Le dépôt est lu à chaque échéance et son historique aussi : un commit par étape, à deux noms, vaut mieux qu’un seul gros commit final.