| import random |
| import gradio as gr |
|
|
| FILES = "abcdefgh" |
| RANKS = "12345678" |
|
|
| |
| 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" |
|
|
| |
| 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() |
|
|