Voici la fonction gagnant rendue par un groupe
Exercice de TD · niveau 3 (difficile) · NSI (première), chapitre 11 — Conduire un projet · Tester pour de vrai
Énoncé
Voici la fonction gagnant rendue par un groupe. Elle a été « testée : ça marche ».
def gagnant(g):
"""Renvoie 'X', 'O' ou None."""
for i in range(3):
if g[i][0] == g[i][1] == g[i][2] != ".":
return g[i][0]
for j in range(3):
if g[0][j] == g[1][j] == g[2][j] != ".":
return g[0][j]
return None
- Écrire une grille sur laquelle cette fonction se trompe, et dire ce qu'elle renvoie.
- Écrire le test qui révèle le défaut, avec son message.
- Corriger la fonction. On demande une version où les huit alignements sont écrits une seule fois.
- Pourquoi ce défaut a-t-il pu traverser une séance entière de parties d'essai ?
Corrigé
1. Les diagonales ne sont pas examinées. Sur la grille
diag = [["X", ".", "."],
[".", "X", "."],
[".", ".", "X"]]
la fonction renvoie None : elle ne trouve aucune ligne complète, aucune colonne complète, et s'arrête. Le programme continuerait donc la partie alors qu'elle est gagnée — et le joueur suivant pourrait, en théorie, gagner à son tour.
2.
def test_diagonales():
diag = [["X", ".", "."], [".", "X", "."], [".", ".", "X"]]
anti = [[".", ".", "O"], [".", "O", "."], ["O", ".", "."]]
assert gagnant(diag) == "X", "la diagonale n'est pas detectee"
assert gagnant(anti) == "O", "l'antidiagonale n'est pas detectee"
Le message compte autant que l'assertion : AssertionError sans texte oblige à relire le test pour comprendre ce qui a échoué.
3. Plutôt que d'ajouter deux if de plus — ce qui donnerait quatre morceaux de code presque identiques —, on énumère les huit alignements une fois pour toutes :
def gagnant(g):
"""Renvoie 'X', 'O' ou None si personne n'a encore gagne."""
lignes = [[(i, 0), (i, 1), (i, 2)] for i in range(3)]
colonnes = [[(0, j), (1, j), (2, j)] for j in range(3)]
diagonales = [[(0, 0), (1, 1), (2, 2)], [(0, 2), (1, 1), (2, 0)]]
for trio in lignes + colonnes + diagonales:
a, b, c = [g[i][j] for i, j in trio]
if a == b == c != ".":
return a
return None
Huit trios, un seul test. Le jour où la grille passe en , une seule liste est à changer.
Contrôle : sur les cinq grilles du jeu de tests — ligne, colonne, diagonale, antidiagonale, grille vide — la fonction rend respectivement X, O, X, O, None.
4. Parce qu'une partie d'essai ne se termine presque jamais par une diagonale. Deux joueurs qui découvrent leur propre programme jouent lentement, au milieu, se bloquent, et finissent le plus souvent sur une ligne ou une colonne — ou en match nul. Le cas manquant est précisément celui qu'on ne rencontre pas par hasard.
C'est l'argument central du chapitre : « on a joué toute la séance, ça marche » n'est pas un jeu de tests. Un jeu de tests énumère les cas délibérément, y compris — surtout — ceux qui ne se présentent pas spontanément. Ici, la seule question « de combien de façons peut-on gagner ? » aurait suffi : la réponse est huit, et le code n'en traitait que six.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.