Aller au contenu

Une grille "démineur"

Vous connaissez sans doute le jeu "démineur" (sinon cherchez sur le web).

L'objectif est ici de créer la grille de jeu.

Un affichage

Pour afficher nos grilles de démineur, on va ici créer un fichier html.

On propose le code suivant enregistré dans le fichier affichage_html.py.

texte du fichier affichage_html.py
def grille_html(tab):
    """
    tab -- matrice  
    renvoie le code html d'une page html affichant le contenu
    de tab dans un élément  html table.
    """

    html = """<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>  Grille   </title>
    <style>
        html{font-size: 20px;}
        body{width: 80%; margin: 20px auto;}
        td{padding: 5px; text-align: center;}
    </style>
</head>
<body>
<table>
<tr>
"""

    nb_lignes = len(tab)
    nb_colonnes = len(tab[0])

    # on place les numéros de colonne:
    html += """<td style="border: none;"></td>\n""" 
    for col in range(nb_colonnes):
        html += f"""<td style="color: green; border: none; font-size: 0.5rem;"> {col} </td>\n"""
    html += "</tr>\n"  # fermeture de la ligne du tableau portant les numéros de colonne

    # on remplit les lignes:
    for lig in range(nb_lignes):
        html += "<tr>"
        # on inscrit le numéro de ligne:
        html += f"""<td style="color: green; border: none;   font-size: 0.5rem;">{lig}</td>\n"""
        # on crée les cellules de l'élément html table en y inscrivant les valeurs contenues dans tab:
        for col in range(nb_colonnes):
            html += f"""<td style="background-color: white; border: 1px solid black; "> {tab[lig][col]} </td>\n"""
        html += "</tr>\n"

    html += "</table>\n"
    html += "</body>\n</html>"

    return html

def creation_fichier_html(nom_fichier, tab):
    """
    nom -- chaîne de caractères, nom d'un fichier à créer
    tab -- matrice à afficher

    La fonction ne renvoie rien, elle crée un fichier html.
    """
    # on crée un fichier html qui sera nommé nom_fichier:
    with open(nom_fichier + '.html', 'w') as f:
        # on écrit la chaine créée par la fonction précédente dans ce fichier:
        print(grille_html(tab), file=f)

Vous devez lire ce code et en comprendre l'essentiel pour vous en servir.

Dans un autre fichier python, créez une matrice de votre choix et affichez la avec le script du fichier précédent.

Solution

Dans un fichier utilisation.py placé dans le même répertoire que le fichier affichage_html.py , on importe tout d'abord ce fichier affichage_html.py puis on crée une matrice quelconque et on appelle la fonction creation_fichier_html :

from affichage_html import *
matrice = [ [ i+j for j in range(7)] for i in range(5) ]
creation_fichier_html("fichier_essai", matrice)

Il reste à vérifier qu'un fichier fichier_essai.html est bien présent dans le dossier du script puis à l'ouvrir avec un navigateur.

Variante

On peut écrire une variante du fichier affichage_html.py en utilisant la bibliothèque jinja2 : voir le fichier affiche_matrice.py.

Texte du fichier .py
from jinja2 import Template

# création du modèle de page html:
template = Template("""
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>  Grille   </title>
    <style>
        html{font-size: 20px;}
        body{width: 80%; margin: 20px auto;}
        td{padding: 5px; text-align: center;}
    </style>
</head>
<body>    
<table>
    <tr>
        <td style="border: none;"></td>
        {% for  col in range(nb_colonnes) %}
            <td style="color: green; border: none; font-size: 0.5rem;"> {{col}} </td>
        {% endfor %}
    </tr>
    {% for lig in range(nb_lignes) %}
        <tr>
            <td style="color: green; border: none;   font-size: 0.5rem;">{{lig}}</td>
            {% for  col in range(nb_colonnes) %}
                <td style="background-color: white; border: 1px solid black; "> 
                {{tab[lig][col]}} 
                </td>
            {% endfor %}
        </tr>
    {% endfor %}
</table> 
</body>
</html>
""")


def creation_fichier_html(nom_fichier, tab):
    """
    nom -- chaîne de caractères, nom d'un fichier à créer
    tab -- matrice à afficher

    La fonction ne renvoie rien, elle crée un fichier html.
    """
    with open(nom_fichier + '.html', 'w') as f:
        texte = template.render(nb_colonnes=len(tab[0]), nb_lignes=len(tab), tab=tab)
        print(texte, file=f)

On pourra faire appel à la fonction créant un fichier html affichant le tableau de la même façon que dans l'exemple précédent.

from affiche_matrice import *
matrice = [ [ i+j for j in range(7)] for i in range(5) ]
creation_fichier_html("fichier_essai", matrice)
Autre templating

La bibliothèque mako est une autre bibliothèque de templating.

from mako.template import Template

# création du modèle de page html:
template = Template("""
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>  Grille   </title>
    <style>
        html{font-size: 20px;}
        body{width: 80%; margin: 20px auto;}
        td{padding: 5px; text-align: center;}
    </style>
</head>
<body>    
<table>
    <tr>
        <td style="border: none;"></td>
        % for  col in range(nb_colonnes) : 
            <td style="color: green; border: none; font-size: 0.5rem;"> ${col} </td>
        % endfor 
    </tr>
    % for lig in range(nb_lignes) :
        <tr>
            <td style="color: green; border: none;   font-size: 0.5rem;"> ${lig}</td>
            % for  col in range(nb_colonnes) :
                <td style="background-color: white; border: 1px solid black; "> 
                    ${tab[lig][col]} 
                </td>
            % endfor 
        </tr>
    % endfor 
</table> 
</body>
</html>
""")


def creation_fichier_html(nom_fichier, tab):
    """
    nom -- chaîne de caractères, nom d'un fichier à créer
    tab -- matrice à afficher

    La fonction ne renvoie rien, elle crée un fichier html.
    """
    with open(nom_fichier + '.html', 'w') as f:
        texte = template.render(nb_colonnes=len(tab[0]), nb_lignes=len(tab), tab=tab)
        print(texte, file=f)

Exemple d'utilisation:

matrice = [ [ 10*i+j for j in range(7)] for i in range(5) ]
creation_fichier_html("grille_mako", matrice)

Création du champ de mines.

Le champ de mines sera ici représenté par une matrice carrée, liste de listes en langage Python.

Compléter le texte de la fonction suivante:

def cree_champ_de_mines(n, p):
    """
    crée une matrice carrée de taille n
    avec des mines disséminées au hasard.
    Chaque cellule reçoit une mine avec une proba p.
    """

Exemple.

En appelant la fonction d'affichage de l'exercice précédent avec cree_champ_de_mines(10, 0.3) :

>>> creation_fichier_html("essai", cree_champ_de_mines(10, 0.3))

on a obtenu :

Random

On fera appel au module random pour décider, pour chaque cellule, si elle reçoit ou non une bombe.

Solution

Un code possible.

from affichage import *
from random import random

def cree_champ_de_mines(n, p):
    """
    crée une matrice carrée de taille n
    avec des mines disséminées au hasard.
    Chaque cellule reçoit une mine avec une proba p.
    """
    champ = [[0 for col in range(n)] for lig in range(n)]
    for lig in range(n):
        for col in range(n):
            if 0 <= random() < p: champ[lig][col] = "💣" 
    return champ

# essai:   
creation_fichier_html("essai", cree_champ_de_mines(10, 0.3)) 

Création des indications

Chaque cellule qui ne contient pas une bombe doit contenir le nombre de bombes présentes dans les cellules voisines.

Une cellule est considérée voisine d'une autre si elle présente un côté ou un sommet commun. Ainsi, une cellule hors des bords a 8 voisines.

Liste des voisins

Compléter la fonction ci-dessous permettant de calculer la liste des voisins d'une cellule (contenant ou non des bombes).

def voisinage(colonne, ligne, n):
    """
    colonne: entier entre 0 et n-1
    ligne: entier entre 0 et n-1
    n: entier représentant le nombre de lignes 
    (et de colonnes) d'une matrice champ de mines.

    renvoie la liste des voisins sous la forme 
    de couples (numéro de colonne, numéro de ligne)
    de la cellule de coordonnées (colonne, ligne).
    (Attention aux cellules de bordure...)
    """

Exemples.

>>> voisinage(0,0,5)
[(1, 0), (0, 1), (1, 1)]
>>> voisinage(1,1,5)
[(0, 0), (1, 0), (2, 0), (0, 1), (2, 1), (0, 2), (1, 2), (2, 2)]
Solution

Un code possible:

def voisinage(colonne, ligne, n):
    """
    colonne: entier entre 0 et n-1
    ligne: entier entre 0 et n-1
    n: entier représentant le nombre de lignes 
    (et de colonnes) d'une matrice champ de mines.

    renvoie la liste des voisins sous la forme 
    de couples (numéro de colonne, numéro de ligne)
    de la cellule de coordonnées (colonne, ligne).
    (Attention aux cellules de bordure...)
    """
    x = colonne
    y = ligne
    voisins = [ (x-1,y-1), (x,y-1), (x+1,y-1),
                (x-1,y),  (x+1,y),
                (x-1,y+1), (x,y+1), (x+1,y+1) ]

    return [ v for v in voisins if  0<= v[0] < n and 0 <= v[1] < n]

Nombre de bombes voisines

Compléter:

def compte_bombes_voisines(colonne, ligne, champ):
    """
    champ est une matrice carrée représentant un champ de mines.
    colonne, ligne: coordonnées d'une cellule du champ.

    renvoie le nombre de bombes présentes dans les cellules voisines
    de la cellule (colonne, ligne).
    """
Solution
def compte_bombes_voisines(colonne, ligne, champ):
    """
    champ est une matrice carrée représentant un champ de mines.
    colonne, ligne: coordonnées d'une cellule du champ.

    renvoie le nombre de bombes présentes dans les cellules voisines
    de la cellule (colonne, ligne).
    """
    compteur_bombes = 0
    for v in voisinage(colonne, ligne, len(champ)):
        if champ[v[1]][v[0]] == "💣" :
            compteur_bombes += 1
    return compteur_bombes

Mise à jour des indications de la grille

Il s'agit maintenant de parcourir toute la grille et de placer, dans chaque cellule qui ne contient pas une bombe, le nombre de bombes présentes dans les cellules voisines.

def place_indications(champ):
    """
    champ est une matrice carrée représentant un champ de mines.

    la fonction met à jour cette matrice en plaçant
    dans chaque cellule qui ne contient pas une bombe,
    le nombre de bombes présentes dans les cellules voisines.
    """
Solution
def place_indications(champ):
    """
    champ est une matrice carrée représentant un champ de mines.

    la fonction met à jour cette matrice en plaçant
    dans chaque cellule qui ne contient pas une bombe,
    le nombre de bombes présentes dans les cellules voisines.
    """
    for lig in range(n):
        for col in range(n):
            if champ[lig][col] != "💣" :
                champ[lig][col] = compte_bombes_voisines(col, lig, champ)

Code complet

Récapitulatif du code des fonctions
from affichage import *
from random import random

def cree_champ_de_mines(n, p):
    """
    crée une matrice carrée de taille n
    avec des mines disséminées au hasard.
    Chaque cellule reçoit une mine avec une proba p.
    """
    champ = [[0 for col in range(n)] for lig in range(n)]
    for lig in range(n):
        for col in range(n):
            if 0 <= random() < p: champ[lig][col] = "💣" 
    return champ



def voisinage(colonne, ligne, n):
    """
    colonne: entier entre 0 et n-1
    ligne: entier entre 0 et n-1
    n: entier représentant le nombre de lignes 
    (et de colonnes) d'une matrice champ de mines.

    renvoie la liste des voisins sous la forme 
    de couples (numéro de colonne, numéro de ligne)
    de la cellule de coordonnées (colonne, ligne).
    (Attention aux cellules de bordure...)
    """
    x = colonne
    y = ligne
    voisins = [ (x-1,y-1), (x,y-1), (x+1,y-1),
                (x-1,y),  (x+1,y),
                (x-1,y+1), (x,y+1), (x+1,y+1) ]

    return [ v for v in voisins if  0<= v[0] < n and 0 <= v[1] < n]





def compte_bombes_voisines(colonne, ligne, champ):
    """
    champ est une matrice carrée représentant un champ de mines.
    colonne, ligne: coordonnées d'une cellule du champ.

    renvoie le nombre de bombes présentes dans les cellules voisines
    de la cellule (colonne, ligne).
    """
    compteur_bombes = 0
    for v in voisinage(colonne, ligne, len(champ)):
        if champ[v[1]][v[0]] == "💣" :
            compteur_bombes += 1
    return compteur_bombes



def place_indications(champ):
    """
    champ est une matrice carrée représentant un champ de mines.

    la fonction met à jour cette matrice  en plaçant
    dans chaque cellule qui ne contient pas une bombe,
    le nombre de bombres présentes dans les cellules voisines.
    """
    for lig in range(n):
        for col in range(n):
            if champ[lig][col] != "💣" :
                champ[lig][col] = compte_bombes_voisines(col, lig, champ)



# un test avec création fichier html:  
n = 10
p = 0.3
champ = cree_champ_de_mines(n, p)
place_indications(champ)
creation_fichier_html("essai", cree_champ_de_mines(10, 0.3))