"""Module providing Random variable generators."""
import random
import json
import os
import time
from statistiques import Statistiques
[docs]
def choisir_difficulte():
"""Demande à l'utilisateur de choisir un niveau de difficulté
et retourne les paramètres du jeu."""
niveau_difficulte = input(
"Choisissez un niveau de difficulté (facile, moyen, difficile, custom) : "
).lower()
if niveau_difficulte not in ['facile', 'moyen', 'difficile', 'custom']:
raise ValueError(
"Le niveau de difficulté doit être 'facile', 'moyen', 'difficile' ou 'custom'."
)
if niveau_difficulte == 'facile':
return 'facile',8, 10 # Taille, Nombre de mines
if niveau_difficulte == 'moyen':
return 'moyen',10, 20
if niveau_difficulte == 'difficile':
return 'difficile',16, 40
print("Vous avez choisi le niveau 'custom'.")
while True:
try:
grille_taille = int(input("Entrez la taille de la grille (minimum 5, maximum 20) : "))
if 5 <= grille_taille <= 20:
break
print("Erreur : La taille doit être comprise entre 5 et 20.")
except ValueError:
print("Erreur : Veuillez entrer un nombre valide.")
while True:
try:
max_mines = grille_taille * grille_taille - 1
mines_nombre = int(
input(f"Entrez le nombre de mines (minimum 1, maximum {max_mines}) : ")
)
if 1 <= mines_nombre <= max_mines:
break
print(f"Erreur : Le nombre de mines doit être compris entre 1 et {max_mines}.")
except ValueError:
print("Erreur : Veuillez entrer un nombre valide.")
return niveau_difficulte, grille_taille, mines_nombre
[docs]
class Demineur:
"""
Classe représentant le jeu de Démineur.
"""
def __init__(self, fichier_sauvegarde='demineur.json',
taille=10, nombre_mines=20,
exploratoire=False,difficulte='moyen',mode_chronometre=False):
"""
Initialize the game with a grid and place mines based on parameters.
:param fichier_sauvegarde: Save file name.
:param taille: Grid size.
:param nombre_mines: Number of mines.
"""
self.taille = taille
self.nombre_mines = nombre_mines
self.fichier_sauvegarde = fichier_sauvegarde
# Initialise l'attribut statistiques
self.statistiques = Statistiques() # Exemple, si `Statistiques` est une classe
self.mode_chronometre = mode_chronometre
self.temps_limite = {
'facile': 180,
'moyen': 300,
'difficile': 600,
'custom': 600 }[difficulte]
self.debut_partie = None
self.grille = [['■' for _ in range(self.taille)] for _ in range(self.taille)]
self.grille_visible = [['■' for _ in range(self.taille)] for _ in range(self.taille)]
self.__placer_mines()
self.__calculer_indices()
self.mouvements = 0
self.exploratoire = exploratoire
def __placer_mines(self):
mines_placees = 0
while mines_placees < self.nombre_mines:
x = random.randint(0, self.taille - 1)
y = random.randint(0, self.taille - 1)
if self.grille[y][x] != 'M':
self.grille[y][x] = 'M'
mines_placees += 1
def __calculer_indices(self):
for y in range(self.taille):
for x in range(self.taille):
if self.grille[y][x] == 'M':
continue
mines_autour = 0
for dx in range(-1, 2):
for dy in range(-1, 2):
nx, ny = x + dx, y + dy
if (
0 <= nx < self.taille
and 0 <= ny < self.taille
and self.grille[ny][nx] == 'M'):
mines_autour += 1
self.grille[y][x] = str(mines_autour)
[docs]
def decouvrir_cases(self, x, y):
"""A Function to uncover a cell"""
if not (0 <= x < self.taille and 0 <= y < self.taille):
return
if self.grille_visible[y][x] != '■':
return
self.grille_visible[y][x] = self.grille[y][x]
if self.grille[y][x] == '0':
self.decouvrir_cases(x - 1, y)
self.decouvrir_cases(x + 1, y)
self.decouvrir_cases(x, y - 1)
self.decouvrir_cases(x, y + 1)
[docs]
def afficher_grille(self):
"""A Function to show the game's board"""
print(" " + " ".join([str(i) for i in range(self.taille)]))
for idx, ligne in enumerate(self.grille_visible):
print(f"{idx:2}| " + ' '.join(ligne) + " |")
mines_restantes = self.nombre_mines - sum(row.count('M') for row in self.grille_visible)
if self.statistiques.timer_start:
temps_ecoule = int(time.time() - self.statistiques.timer_start)
else:
temps_ecoule = 0
hours, remainder = divmod(temps_ecoule, 3600)
minutes, seconds = divmod(remainder, 60)
print(
f"\nMines restantes: {mines_restantes} | "
f"Mouvements: {self.mouvements} | "
f"Temps: {hours:02}:{minutes:02}:{seconds:02}")
if self.mode_chronometre and self.debut_partie:
temps_restant = max(0, self.temps_limite - (time.time() - self.debut_partie))
minutes, seconds = divmod(int(temps_restant), 60)
print(f"⏳ Temps restant : {minutes:02}:{seconds:02}")
if temps_restant <= 30:
print("⚠️ Attention, il vous reste moins de 30 secondes !")
[docs]
def charger_jeu(self):
"""Load the game state from a JSON file."""
if os.path.exists(self.fichier_sauvegarde):
with open(self.fichier_sauvegarde, 'r', encoding='utf-8') as file:
data = json.load(file)
self.taille = data.get('taille', 10)
self.nombre_mines = data.get('nombre_mines', 10)
self.grille = data.get(
'grille', [['.' for _ in range(self.taille)] for _ in range(self.taille)]
)
self.grille_visible = data.get(
'grille_visible',
[['.' for _ in range(self.taille)] for _ in range(self.taille)]
)
[docs]
def marquer_case(self, x, y):
""" Mark or unmark a cell with a flag. """
if not (0 <= x < self.taille and 0 <= y < self.taille):
return
if self.grille_visible[y][x] == 'F':
self.grille_visible[y][x] = '■'
elif self.grille_visible[y][x] == '■':
self.grille_visible[y][x] = 'F'
else:
print("La case est déjà découverte et ne peut pas être marquée.")
[docs]
def suggerer_case(self):
"""Suggest randomly a safe cell that doesn't contain a mine"""
cases_sures = []
for y in range(self.taille):
for x in range(self.taille):
if self.grille_visible[y][x] in ('■', 'F') and self.grille[y][x] != 'M':
cases_sures.append((x, y))
if cases_sures:
case_suggeree = random.choice(cases_sures)
print(f"Suggestion : essayez la case ({case_suggeree[0]}, {case_suggeree[1]}).")
else:
print("Aucune case à suggérer.")
[docs]
def sauvegarder_jeu(self):
"""
Save the current game state to a JSON file.
"""
data = {
'taille': self.taille,
'nombre_mines': self.nombre_mines,
'grille': self.grille,
'grille_visible': self.grille_visible,
}
with open(self.fichier_sauvegarde, 'w', encoding='utf-8') as file:
json.dump(data, file, ensure_ascii=False, indent=4)
print(f"Jeu sauvegardé dans {self.fichier_sauvegarde}.")
[docs]
def verifier_temps(self):
"""
Check if the time limit has been reached.
"""
if self.mode_chronometre and self.debut_partie:
temps_ecoule = time.time() - self.debut_partie
if temps_ecoule >= self.temps_limite:
print("\n Temps écoulé ! Vous avez perdu.")
return True
return False
[docs]
def traiter_choix(self, choix):
"""Used for player choice inputs"""
if choix[0].lower() == 'save':
self.sauvegarder_jeu()
return True
if choix[0].lower() == 'help':
self.suggerer_case()
return True
return False
[docs]
def jouer(self):
"""A Function to launch the game."""
game_in_progress = True
self.statistiques.start_timer()
self.debut_partie = time.time() if self.mode_chronometre else None
while game_in_progress:
print("\n [ Bienvenue au Démineur ! ] \n")
self.afficher_grille()
print("Tapez 'save' pour sauvegarder la partie,"
"'help' pour une suggestion,"
"ou entrez les coordonnées.")
if self.verifier_temps():
game_in_progress = False
temps_ecoule = self.statistiques.stop_timer() # Arrêter le chronomètre
self.statistiques.record_loss() # Enregistrer la défaite
print("\n⏰ Temps écoulé ! Vous avez perdu. Temps "
f"joué : {temps_ecoule:.2f} secondes.")
break
print("Tapez 'save' pour sauvegarder la partie ou entrez les coordonnées.")
choix = input(
"Entrez 'f x y' pour marquer/démarquer, 'x y' pour découvrir, "
"'help' pour suggérer une case, "
"ou 'save' pour sauvegarder : "
).split()
if self.traiter_choix(choix):
continue
try:
if len(choix) == 3 and choix[0] == 'f':
x, y = map(int, choix[1:])
self.marquer_case(x, y)
continue
if len(choix) == 2:
x, y = map(int, choix)
else:
print("Erreur : entrez 'f x y' pour marquer/démarquer"
"ou 'x y' pour découvrir.")
continue
except ValueError:
print("Coordonnées invalides. Veuillez réessayer.")
continue
if self.grille[y][x] == 'M':
self.grille_visible[y][x] = 'M'
self.afficher_grille()
print("⚠️ Attention : Vous avez découvert une mine !"
" La partie continue en mode exploratoire.")
if not self.exploratoire:
print("Perdu !")
game_in_progress = False
temps_ecoule = self.statistiques.stop_timer()
self.statistiques.record_loss()
break
self.decouvrir_cases(x, y)
self.mouvements += 1
if sum(row.count('■') for row in self.grille_visible) == self.nombre_mines:
print("Gagne !")
game_in_progress = False
temps_ecoule = self.statistiques.stop_timer()
self.statistiques.record_victory()
break
print(f"Temps écoulé : {temps_ecoule:.2f} secondes")
self.statistiques.display_statistics()
# Demande si le joueur souhaite recommencer une partie
while True:
restart = input("Voulez-vous recommencer une partie ? (oui/non) : ").lower()
if restart == 'oui':
nouveau_jeu = Demineur(self.nombre_mines)
nouveau_jeu.jouer()
break
if restart == 'non':
print("Partie terminée !")
break
print("Réponse invalide, veuillez répondre par 'oui' ou 'non'.")
if __name__ == "__main__":
try:
niv_difficulte, real_taille, nb_mines = choisir_difficulte()
mode_exploratoire = input("Activer le mode exploratoire ? (oui/non) : ").lower() == 'oui'
mode_chrono = input("Voulez-vous activer le mode chronométré ?"
"(oui/non) : ").strip().lower()
jeu = Demineur(taille=real_taille, nombre_mines=nb_mines,exploratoire=mode_exploratoire,
difficulte=niv_difficulte,mode_chronometre=mode_chrono == 'oui')
jeu.charger_jeu()
jeu.jouer()
except ValueError as e:
print(e)