MineChess / app.py
Enoder's picture
Update app.py
0cadddf verified
Raw
History Blame
4.97 kB
import random
import gradio as gr
FILES = "abcdefgh"
RANKS = "12345678"
# Pièces Unicode
PIECES = {
("W", "p"): "♙", ("B", "p"): "♟",
("W", "t"): "♖", ("B", "t"): "♜",
("W", "c"): "♘", ("B", "c"): "♞",
("W", "f"): "♗", ("B", "f"): "♝",
("W", "R"): "♕", ("B", "R"): "♛",
("W", "k"): "♔", ("B", "k"): "♚",
}
def in_bounds(x, y):
return 0 <= x < 8 and 0 <= y < 8
def initial_board():
b = [[None for _ in range(8)] for _ in range(8)]
white_row = ['t', 'c', 'f', 'R', 'k', 'f', 'c', 't']
for x, p in enumerate(white_row):
b[x][0] = ('W', p)
b[x][1] = ('W', 'p')
black_row = ['t', 'c', 'f', 'R', 'k', 'f', 'c', 't']
for x, p in enumerate(black_row):
b[x][7] = ('B', p)
b[x][6] = ('B', 'p')
return b
def sq_to_coords(sq):
sq = sq.lower()
return FILES.index(sq[0]), RANKS.index(sq[1])
def coords_to_sq(x, y):
return FILES[x] + RANKS[y]
def generate_mines(board, density=0.15):
empty = [(x, y) for x in range(8) for y in range(8) if board[x][y] is None]
n = max(1, int(len(empty) * density))
mines = set(random.sample(empty, n))
return mines, set(), set()
def count_adjacent_mines(x, y, mines):
return sum((nx, ny) in mines for nx in range(x-1, x+2) for ny in range(y-1, y+2)
if (nx, ny) != (x, y) and in_bounds(nx, ny))
def render_board(board, mines, revealed, exploded, hints):
grid = []
for y in range(7, -1, -1):
row = []
for x in range(8):
if (x, y) in exploded:
cell = "💥"
elif board[x][y]:
cell = PIECES[board[x][y]]
elif (x, y) in revealed:
cell = str(hints.get((x, y), 0)) if hints.get((x, y), 0) > 0 else "·"
else:
cell = "⬜" if (x + y) % 2 else "⬛"
row.append(cell)
grid.append(row)
return "\n".join(" ".join(row) for row in grid)
def apply_move(board, mines, revealed, exploded, hints, move, turn):
from_sq, to_sq = move
fx, fy = sq_to_coords(from_sq)
tx, ty = sq_to_coords(to_sq)
piece = board[fx][fy]
if not piece or piece[0] != turn:
return board, "Coup invalide"
if not in_bounds(tx, ty):
return board, "Coup hors du plateau"
# Déplacement
board[fx][fy] = None
if (tx, ty) in mines:
exploded.add((tx, ty))
text = f"💥 {from_sq}->{to_sq} : Mine ! Pièce détruite."
else:
board[tx][ty] = piece
revealed.add((tx, ty))
count = count_adjacent_mines(tx, ty, mines)
hints[(tx, ty)] = count
text = f"{from_sq}->{to_sq} (zone sûre, {count} mines autour)"
return board, text
def new_game(density):
board = initial_board()
mines, revealed, exploded = generate_mines(board, density)
hints = {}
state = dict(board=board, mines=mines, revealed=revealed,
exploded=exploded, hints=hints, turn="W", log=["Nouvelle partie"])
return render_board(board, mines, revealed, exploded, hints), "\n".join(state["log"]), state
def play_move(move_input, state):
if not state:
return "Pas de partie", "Commence une nouvelle partie", None
move_input = move_input.strip().lower()
if len(move_input) != 4:
return render_board(**state), "Format invalide (ex: e2e4)", state
from_sq, to_sq = move_input[:2], move_input[2:]
board, text = apply_move(state["board"], state["mines"], state["revealed"],
state["exploded"], state["hints"], (from_sq, to_sq), state["turn"])
state["board"] = board
state["log"].append(text)
state["turn"] = "B" if state["turn"] == "W" else "W"
return render_board(board, state["mines"], state["revealed"], state["exploded"], state["hints"]), "\n".join(state["log"]), state
with gr.Blocks() as demo:
gr.Markdown("# ♟️ ChessMine — Échecs + 💣 Démineur")
gr.Markdown("Deux joueurs alternent leurs coups. Si une pièce marche sur une mine 💥 elle explose, sinon la case révèle le nombre de mines autour 🔢.")
density = gr.Slider(0.05, 0.4, value=0.15, label="Densité de mines")
start = gr.Button("Nouvelle partie")
board_box = gr.Textbox(label="Plateau", lines=10, interactive=False)
move_in = gr.Textbox(label="Coup (ex: e2e4)")
play = gr.Button("Jouer")
log_box = gr.Textbox(label="Journal", lines=8, interactive=False)
state = gr.State()
start.click(new_game, inputs=[density], outputs=[board_box, log_box, state])
play.click(play_move, inputs=[move_in, state], outputs=[board_box, log_box, state])
gr.Markdown("**Règles :**\n- Entrez vos coups en format `e2e4`.\n- Si la case cible contient une 💣 → explosion !\n- Sinon, un nombre indique combien de mines se trouvent autour.\n- Les pièces utilisent les symboles Unicode standards ♙♟♖♜♘♞♗♝♕♛♔♚.")
if __name__ == "__main__":
demo.launch()