Enoder commited on
Commit
0cadddf
·
verified ·
1 Parent(s): 3cc6861

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +64 -143
app.py CHANGED
@@ -1,19 +1,33 @@
1
  import random
2
- import copy
3
  import gradio as gr
4
 
5
- # === Unicode pour les pièces ===
6
- UNICODE_PIECES = {
 
 
 
7
  ("W", "p"): "♙", ("B", "p"): "♟",
8
  ("W", "t"): "♖", ("B", "t"): "♜",
9
  ("W", "c"): "♘", ("B", "c"): "♞",
10
  ("W", "f"): "♗", ("B", "f"): "♝",
11
  ("W", "R"): "♕", ("B", "R"): "♛",
12
- ("W", "k"): "♔", ("B", "k"): "♚"
13
  }
14
 
15
- FILES = "abcdefgh"
16
- RANKS = "12345678"
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  def sq_to_coords(sq):
19
  sq = sq.lower()
@@ -22,120 +36,34 @@ def sq_to_coords(sq):
22
  def coords_to_sq(x, y):
23
  return FILES[x] + RANKS[y]
24
 
25
- def in_bounds(x, y):
26
- return 0 <= x < 8 and 0 <= y < 8
27
-
28
- # --- initial board ---
29
- def initial_board():
30
- board = [[None for _ in range(8)] for _ in range(8)]
31
- white_row = ['t','c','f','R','k','f','c','t']
32
- for x, p in enumerate(white_row):
33
- board[x][0] = ('W', p)
34
- board[x][1] = ('W', 'p')
35
- black_row = ['t','c','f','R','k','f','c','t']
36
- for x, p in enumerate(black_row):
37
- board[x][7] = ('B', p)
38
- board[x][6] = ('B', 'p')
39
- return board
40
-
41
- # --- mines ---
42
  def generate_mines(board, density=0.15):
43
  empty = [(x, y) for x in range(8) for y in range(8) if board[x][y] is None]
44
  n = max(1, int(len(empty) * density))
45
  mines = set(random.sample(empty, n))
46
- revealed = set()
47
- exploded = set()
48
- return mines, revealed, exploded
 
 
49
 
50
- # --- affichage ---
51
- def render_board(board, mines, revealed, exploded):
52
  grid = []
53
  for y in range(7, -1, -1):
54
  row = []
55
  for x in range(8):
56
  if (x, y) in exploded:
57
- row.append("💥")
58
  elif board[x][y]:
59
- row.append(UNICODE_PIECES[board[x][y]])
60
  elif (x, y) in revealed:
61
- row.append("")
62
  else:
63
- row.append("" if (x+y) % 2 else "")
 
64
  grid.append(row)
65
- return "\n".join(" ".join(r) for r in grid)
66
-
67
- # --- mouvements ---
68
- DIRECTIONS_ROOK = [(1,0),(-1,0),(0,1),(0,-1)]
69
- DIRECTIONS_BISHOP = [(1,1),(1,-1),(-1,1),(-1,-1)]
70
- KNIGHT_MOVES = [(2,1),(2,-1),(-2,1),(-2,-1),(1,2),(1,-2),(-1,2),(-1,-2)]
71
-
72
- def generate_pseudo_legal_moves(board, x, y):
73
- piece = board[x][y]
74
- if piece is None:
75
- return []
76
- color, ptype = piece
77
- moves = []
78
- if ptype == "p":
79
- diry = 1 if color == "W" else -1
80
- start_rank = 1 if color == "W" else 6
81
- nx, ny = x, y + diry
82
- if in_bounds(nx, ny) and board[nx][ny] is None:
83
- moves.append((nx, ny))
84
- if y == start_rank and board[x][y + 2 * diry] is None:
85
- moves.append((x, y + 2 * diry))
86
- for dx in (-1, 1):
87
- cx, cy = x + dx, y + diry
88
- if in_bounds(cx, cy) and board[cx][cy] and board[cx][cy][0] != color:
89
- moves.append((cx, cy))
90
- elif ptype == "c":
91
- for dx, dy in KNIGHT_MOVES:
92
- nx, ny = x + dx, y + dy
93
- if in_bounds(nx, ny) and (not board[nx][ny] or board[nx][ny][0] != color):
94
- moves.append((nx, ny))
95
- elif ptype == "t":
96
- for dx, dy in DIRECTIONS_ROOK:
97
- nx, ny = x + dx, y + dy
98
- while in_bounds(nx, ny):
99
- if not board[nx][ny]:
100
- moves.append((nx, ny))
101
- else:
102
- if board[nx][ny][0] != color:
103
- moves.append((nx, ny))
104
- break
105
- nx += dx; ny += dy
106
- elif ptype == "f":
107
- for dx, dy in DIRECTIONS_BISHOP:
108
- nx, ny = x + dx, y + dy
109
- while in_bounds(nx, ny):
110
- if not board[nx][ny]:
111
- moves.append((nx, ny))
112
- else:
113
- if board[nx][ny][0] != color:
114
- moves.append((nx, ny))
115
- break
116
- nx += dx; ny += dy
117
- elif ptype == "R":
118
- for dx, dy in DIRECTIONS_ROOK + DIRECTIONS_BISHOP:
119
- nx, ny = x + dx, y + dy
120
- while in_bounds(nx, ny):
121
- if not board[nx][ny]:
122
- moves.append((nx, ny))
123
- else:
124
- if board[nx][ny][0] != color:
125
- moves.append((nx, ny))
126
- break
127
- nx += dx; ny += dy
128
- elif ptype == "k":
129
- for dx in (-1, 0, 1):
130
- for dy in (-1, 0, 1):
131
- if dx == dy == 0: continue
132
- nx, ny = x + dx, y + dy
133
- if in_bounds(nx, ny) and (not board[nx][ny] or board[nx][ny][0] != color):
134
- moves.append((nx, ny))
135
- return moves
136
-
137
- # --- gestion du tour ---
138
- def apply_move(board, mines, revealed, exploded, move, turn):
139
  from_sq, to_sq = move
140
  fx, fy = sq_to_coords(from_sq)
141
  tx, ty = sq_to_coords(to_sq)
@@ -144,69 +72,62 @@ def apply_move(board, mines, revealed, exploded, move, turn):
144
  if not piece or piece[0] != turn:
145
  return board, "Coup invalide"
146
 
 
 
 
 
 
 
147
  if (tx, ty) in mines:
148
  exploded.add((tx, ty))
149
- board[fx][fy] = None
 
 
150
  revealed.add((tx, ty))
151
- return board, f"💥 {from_sq}->{to_sq} : une mine explose ! Pièce détruite."
152
-
153
- legal = generate_pseudo_legal_moves(board, fx, fy)
154
- if (tx, ty) not in legal:
155
- return board, "Coup illégal."
156
-
157
- capture = board[tx][ty] is not None
158
- board[tx][ty] = piece
159
- board[fx][fy] = None
160
- revealed.add((tx, ty))
161
- text = f"{from_sq}->{to_sq}"
162
- if capture: text += " (prise)"
163
  return board, text
164
 
165
- # --- partie ---
166
  def new_game(density):
167
  board = initial_board()
168
  mines, revealed, exploded = generate_mines(board, density)
169
- state = dict(
170
- board=board,
171
- mines=mines,
172
- revealed=revealed,
173
- exploded=exploded,
174
- turn="W",
175
- log=["Nouvelle partie ! Blanc commence."]
176
- )
177
- return render_board(board, mines, revealed, exploded), "\n".join(state["log"]), state
178
 
179
  def play_move(move_input, state):
180
- if not state: return "Aucune partie", "Aucune", None
 
181
  move_input = move_input.strip().lower()
182
- if len(move_input) != 4: return render_board(**state), "Format invalide (ex: e2e4)", state
 
 
183
  from_sq, to_sq = move_input[:2], move_input[2:]
184
- board, text = apply_move(state["board"], state["mines"], state["revealed"], state["exploded"], (from_sq, to_sq), state["turn"])
 
185
  state["board"] = board
186
  state["log"].append(text)
187
  state["turn"] = "B" if state["turn"] == "W" else "W"
188
- board_text = render_board(board, state["mines"], state["revealed"], state["exploded"])
189
- return board_text, "\n".join(state["log"]), state
190
 
191
- # --- interface ---
192
  with gr.Blocks() as demo:
193
- gr.Markdown("# ♟️ Échecs + 💣 Démineur")
194
- gr.Markdown("Deux joueurs. Les cases vides cachent parfois des mines... Si une pièce marche dessus, elle est détruite 💥 !")
195
 
196
- density = gr.Slider(0.05, 0.4, value=0.18, label="Densité de mines")
197
  start = gr.Button("Nouvelle partie")
198
-
199
  board_box = gr.Textbox(label="Plateau", lines=10, interactive=False)
200
- move_in = gr.Textbox(label="Entrez votre coup (ex: e2e4)")
201
- play = gr.Button("Jouer le coup")
202
-
203
  log_box = gr.Textbox(label="Journal", lines=8, interactive=False)
204
  state = gr.State()
205
 
206
  start.click(new_game, inputs=[density], outputs=[board_box, log_box, state])
207
  play.click(play_move, inputs=[move_in, state], outputs=[board_box, log_box, state])
208
 
209
- gr.Markdown("**Pièces :** ♙♟ Pions | ♖♜ Tours | ♘♞ Cavaliers | ♗♝ Fous | ♕♛ Reines | ♔♚ Rois")
210
 
211
  if __name__ == "__main__":
212
  demo.launch()
 
1
  import random
 
2
  import gradio as gr
3
 
4
+ FILES = "abcdefgh"
5
+ RANKS = "12345678"
6
+
7
+ # Pièces Unicode
8
+ PIECES = {
9
  ("W", "p"): "♙", ("B", "p"): "♟",
10
  ("W", "t"): "♖", ("B", "t"): "♜",
11
  ("W", "c"): "♘", ("B", "c"): "♞",
12
  ("W", "f"): "♗", ("B", "f"): "♝",
13
  ("W", "R"): "♕", ("B", "R"): "♛",
14
+ ("W", "k"): "♔", ("B", "k"): "♚",
15
  }
16
 
17
+ def in_bounds(x, y):
18
+ return 0 <= x < 8 and 0 <= y < 8
19
+
20
+ def initial_board():
21
+ b = [[None for _ in range(8)] for _ in range(8)]
22
+ white_row = ['t', 'c', 'f', 'R', 'k', 'f', 'c', 't']
23
+ for x, p in enumerate(white_row):
24
+ b[x][0] = ('W', p)
25
+ b[x][1] = ('W', 'p')
26
+ black_row = ['t', 'c', 'f', 'R', 'k', 'f', 'c', 't']
27
+ for x, p in enumerate(black_row):
28
+ b[x][7] = ('B', p)
29
+ b[x][6] = ('B', 'p')
30
+ return b
31
 
32
  def sq_to_coords(sq):
33
  sq = sq.lower()
 
36
  def coords_to_sq(x, y):
37
  return FILES[x] + RANKS[y]
38
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  def generate_mines(board, density=0.15):
40
  empty = [(x, y) for x in range(8) for y in range(8) if board[x][y] is None]
41
  n = max(1, int(len(empty) * density))
42
  mines = set(random.sample(empty, n))
43
+ return mines, set(), set()
44
+
45
+ def count_adjacent_mines(x, y, mines):
46
+ return sum((nx, ny) in mines for nx in range(x-1, x+2) for ny in range(y-1, y+2)
47
+ if (nx, ny) != (x, y) and in_bounds(nx, ny))
48
 
49
+ def render_board(board, mines, revealed, exploded, hints):
 
50
  grid = []
51
  for y in range(7, -1, -1):
52
  row = []
53
  for x in range(8):
54
  if (x, y) in exploded:
55
+ cell = "💥"
56
  elif board[x][y]:
57
+ cell = PIECES[board[x][y]]
58
  elif (x, y) in revealed:
59
+ cell = str(hints.get((x, y), 0)) if hints.get((x, y), 0) > 0 else "·"
60
  else:
61
+ cell = "" if (x + y) % 2 else ""
62
+ row.append(cell)
63
  grid.append(row)
64
+ return "\n".join(" ".join(row) for row in grid)
65
+
66
+ def apply_move(board, mines, revealed, exploded, hints, move, turn):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  from_sq, to_sq = move
68
  fx, fy = sq_to_coords(from_sq)
69
  tx, ty = sq_to_coords(to_sq)
 
72
  if not piece or piece[0] != turn:
73
  return board, "Coup invalide"
74
 
75
+ if not in_bounds(tx, ty):
76
+ return board, "Coup hors du plateau"
77
+
78
+ # Déplacement
79
+ board[fx][fy] = None
80
+
81
  if (tx, ty) in mines:
82
  exploded.add((tx, ty))
83
+ text = f"💥 {from_sq}->{to_sq} : Mine ! Pièce détruite."
84
+ else:
85
+ board[tx][ty] = piece
86
  revealed.add((tx, ty))
87
+ count = count_adjacent_mines(tx, ty, mines)
88
+ hints[(tx, ty)] = count
89
+ text = f"{from_sq}->{to_sq} (zone sûre, {count} mines autour)"
 
 
 
 
 
 
 
 
 
90
  return board, text
91
 
 
92
  def new_game(density):
93
  board = initial_board()
94
  mines, revealed, exploded = generate_mines(board, density)
95
+ hints = {}
96
+ state = dict(board=board, mines=mines, revealed=revealed,
97
+ exploded=exploded, hints=hints, turn="W", log=["Nouvelle partie"])
98
+ return render_board(board, mines, revealed, exploded, hints), "\n".join(state["log"]), state
 
 
 
 
 
99
 
100
  def play_move(move_input, state):
101
+ if not state:
102
+ return "Pas de partie", "Commence une nouvelle partie", None
103
  move_input = move_input.strip().lower()
104
+ if len(move_input) != 4:
105
+ return render_board(**state), "Format invalide (ex: e2e4)", state
106
+
107
  from_sq, to_sq = move_input[:2], move_input[2:]
108
+ board, text = apply_move(state["board"], state["mines"], state["revealed"],
109
+ state["exploded"], state["hints"], (from_sq, to_sq), state["turn"])
110
  state["board"] = board
111
  state["log"].append(text)
112
  state["turn"] = "B" if state["turn"] == "W" else "W"
113
+ return render_board(board, state["mines"], state["revealed"], state["exploded"], state["hints"]), "\n".join(state["log"]), state
 
114
 
 
115
  with gr.Blocks() as demo:
116
+ gr.Markdown("# ♟️ ChessMine — Échecs + 💣 Démineur")
117
+ 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 🔢.")
118
 
119
+ density = gr.Slider(0.05, 0.4, value=0.15, label="Densité de mines")
120
  start = gr.Button("Nouvelle partie")
 
121
  board_box = gr.Textbox(label="Plateau", lines=10, interactive=False)
122
+ move_in = gr.Textbox(label="Coup (ex: e2e4)")
123
+ play = gr.Button("Jouer")
 
124
  log_box = gr.Textbox(label="Journal", lines=8, interactive=False)
125
  state = gr.State()
126
 
127
  start.click(new_game, inputs=[density], outputs=[board_box, log_box, state])
128
  play.click(play_move, inputs=[move_in, state], outputs=[board_box, log_box, state])
129
 
130
+ 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 ♙♟♖♜♘♞♗♝♕♛♔♚.")
131
 
132
  if __name__ == "__main__":
133
  demo.launch()