File size: 4,970 Bytes
f216aac
c82ad6c
f216aac
0cadddf
 
 
 
 
3cc6861
 
 
 
 
0cadddf
3cc6861
 
0cadddf
 
 
 
 
 
 
 
 
 
 
 
 
 
f216aac
 
c82ad6c
 
f216aac
3cc6861
f216aac
 
 
3cc6861
c82ad6c
 
0cadddf
 
 
 
 
c82ad6c
0cadddf
3cc6861
 
 
f216aac
3cc6861
0cadddf
3cc6861
0cadddf
3cc6861
0cadddf
f216aac
0cadddf
 
3cc6861
0cadddf
 
 
3cc6861
 
 
 
f216aac
3cc6861
 
c82ad6c
0cadddf
 
 
 
 
 
3cc6861
 
0cadddf
 
 
3cc6861
0cadddf
 
 
3cc6861
f216aac
3cc6861
f216aac
3cc6861
0cadddf
 
 
 
3cc6861
 
0cadddf
 
3cc6861
0cadddf
 
 
3cc6861
0cadddf
 
3cc6861
 
 
0cadddf
c82ad6c
 
0cadddf
 
3cc6861
0cadddf
3cc6861
 
0cadddf
 
3cc6861
 
c82ad6c
3cc6861
 
c82ad6c
0cadddf
c82ad6c
f216aac
c82ad6c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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()