ShubhanshuBansod commited on
Commit
ef8890c
ยท
verified ยท
1 Parent(s): a7b6178

Upload 5 files

Browse files
Files changed (1) hide show
  1. app.py +310 -91
app.py CHANGED
@@ -10,19 +10,230 @@ st.set_page_config(page_title="Connect Four AI Pro", layout="wide", initial_side
10
 
11
  st.markdown('''
12
  <style>
13
- .board-display { font-family: 'Courier New', monospace; white-space: pre; line-height: 1.4; font-size: 16px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 12px; box-shadow: 0 8px 16px rgba(0,0,0,0.2); margin: 0 auto; display: inline-block; }
14
- .board-container { text-align: center; }
15
- .stats-card { background: white; padding: 15px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); margin: 10px 0; border-left: 4px solid #667eea; }
16
- .stat-number { font-size: 24px; font-weight: bold; color: #667eea; }
17
- .stat-label { font-size: 12px; color: #666; text-transform: uppercase; }
18
- .move-card { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; padding: 20px; border-radius: 12px; margin: 10px 0; box-shadow: 0 6px 12px rgba(0,0,0,0.15); }
19
- .player-section { background: white; padding: 20px; border-radius: 12px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); margin: 15px 0; }
20
- .player1-section { border-left: 5px solid #ef4444; }
21
- .player2-section { border-left: 5px solid #f59e0b; }
22
- .mode-badge { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; margin-right: 8px; background: #10b981; color: white; }
23
- .two-player { background: #f59e0b; }
24
- .human-section { background: linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 100%); border-left: 5px solid #8b5cf6; }
25
- .ai-section { background: linear-gradient(135deg, #fef3c7 0%, #fecaca 100%); border-left: 5px solid #f97316; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  </style>
27
  ''', unsafe_allow_html=True)
28
 
@@ -47,16 +258,20 @@ if 'game' not in st.session_state:
47
  st.session_state.last_human_move_analysis = None
48
  st.session_state.last_ai_move_analysis = None
49
 
 
 
50
  col_title1, col_title2 = st.columns([3, 1])
51
  with col_title1:
52
  st.title("๐ŸŽฎ Connect Four AI Pro")
53
  with col_title2:
54
  if st.session_state.game_mode == "vs AI":
55
- st.markdown(f'<span class="mode-badge">Ultra AI</span>', unsafe_allow_html=True)
56
  else:
57
- st.markdown('<span class="mode-badge two-player">Two Player</span>', unsafe_allow_html=True)
 
58
 
59
- st.sidebar.header("โš™๏ธ Game Settings")
 
60
 
61
  game_mode = st.sidebar.radio("๐ŸŽฎ Game Mode", ["vs AI", "Two Player"],
62
  index=0 if st.session_state.game_mode == "vs AI" else 1)
@@ -75,8 +290,8 @@ if game_mode != st.session_state.game_mode:
75
 
76
  if st.session_state.game_mode == "vs AI":
77
  st.sidebar.markdown("---")
78
- st.sidebar.subheader("๐ŸŽš๏ธ Search Depth")
79
- depth = st.sidebar.slider("AI Depth (3-8)", 3, 8, 5)
80
  st.session_state.ai.depth = depth
81
  st.session_state.current_depth = depth
82
 
@@ -118,34 +333,39 @@ with col2:
118
  st.rerun()
119
 
120
  st.sidebar.markdown("---")
121
- st.sidebar.subheader("๐Ÿ“Š Game Stats")
122
- st.sidebar.write(f"**Moves:** {st.session_state.move_count}")
123
  if st.session_state.game_mode == "Two Player":
124
- st.sidebar.write(f"**P1:** {len(st.session_state.player1_analyses)}")
125
- st.sidebar.write(f"**P2:** {len(st.session_state.player2_analyses)}")
 
 
 
126
 
127
  if st.session_state.move_history:
128
  with st.sidebar.expander("๐Ÿ“œ Move History"):
129
  for i, col in enumerate(st.session_state.move_history, 1):
130
  player = "๐Ÿ”ด" if i % 2 == 1 else "๐ŸŸก"
131
- st.write(f"Move {i}: {player} โ†’ Col {col}")
132
 
133
  # Main layout
134
  if st.session_state.game_mode == "vs AI":
135
  col1, col2 = st.columns([2, 3])
136
 
137
  with col1:
138
- st.subheader("๐ŸŽฏ Game Board")
 
139
  board_str = st.session_state.game.board_to_string()
140
- st.markdown(f'<div class="board-container"><div class="board-display">{board_str}</div></div>', unsafe_allow_html=True)
 
141
 
142
  if not st.session_state.game_over:
143
- st.markdown("### Your Move ๐Ÿ”ด")
144
  valid_cols = st.session_state.game.get_valid_moves()
145
  if valid_cols:
146
  human_move = st.selectbox("Choose column:", valid_cols, key="move_select")
147
 
148
- if st.button("๐Ÿ“ Play Move", use_container_width=True, type="primary"):
149
  current_player = 1
150
  st.session_state.game.make_move(human_move, player=current_player)
151
  st.session_state.move_history.append(human_move)
@@ -171,7 +391,7 @@ if st.session_state.game_mode == "vs AI":
171
  st.session_state.winner = "Draw"
172
  st.session_state.show_winner_popup = True
173
  else:
174
- with st.spinner("๐Ÿค– AI thinking..."):
175
  ai_result = st.session_state.ai.get_best_move()
176
 
177
  st.session_state.game.make_move(ai_result['move'], player=2)
@@ -213,26 +433,28 @@ if st.session_state.game_mode == "vs AI":
213
  st.rerun()
214
 
215
  with col2:
216
- st.subheader("๐Ÿ“Š Analysis Performance")
217
 
218
  # Human Move Analysis
219
  if st.session_state.last_human_move_analysis:
220
  human_analysis = st.session_state.last_human_move_analysis
221
  st.markdown('<div class="player-section human-section">', unsafe_allow_html=True)
222
- st.markdown("### ๐Ÿ”ด Your Move Analysis")
223
 
224
- st.write(f"**Your Last Move:** Column {st.session_state.move_history[-2] if len(st.session_state.move_history) >= 2 else '?'}")
225
- st.write(f"**Score Evaluation:** {human_analysis['score']} points")
226
 
227
  if human_analysis['threats']:
228
- st.markdown(f"โš”๏ธ **Strategic Threats:** {', '.join(human_analysis['threats'])}")
 
 
229
 
230
- st.markdown("**Algorithm Metrics:**")
231
  perf_col1, perf_col2, perf_col3 = st.columns(3)
232
  with perf_col1:
233
- st.markdown(f'<div class="stats-card"><div class="stat-number">{human_analysis["nodes_explored"]:,}</div><div class="stat-label">Explored</div></div>', unsafe_allow_html=True)
234
  with perf_col2:
235
- st.markdown(f'<div class="stats-card"><div class="stat-number">{human_analysis["nodes_pruned"]:,}</div><div class="stat-label">Pruned</div></div>', unsafe_allow_html=True)
236
  with perf_col3:
237
  st.markdown(f'<div class="stats-card"><div class="stat-number">{human_analysis["pruning_efficiency"]:.1f}%</div><div class="stat-label">Efficiency</div></div>', unsafe_allow_html=True)
238
 
@@ -249,52 +471,52 @@ if st.session_state.game_mode == "vs AI":
249
  })
250
 
251
  st.markdown('<div class="player-section ai-section">', unsafe_allow_html=True)
252
- st.markdown("### ๐ŸŸก AI Move Analysis")
 
 
253
 
254
- st.markdown(f'''<div class="move-card"><h3>AI Played: Column {ai_move['move']}</h3><p><strong>Score:</strong> {ai_move['score']} points</p></div>''', unsafe_allow_html=True)
255
- st.success(f"๐Ÿ’ก **Why This Move:**\n{explanation['explanation']}")
256
 
257
  if explanation.get('threat_analysis'):
258
- st.warning(f"โš”๏ธ **Threat Analysis:**\n{explanation['threat_analysis']}")
259
 
260
  if explanation.get('key_insight'):
261
- st.info(f"๐Ÿ”‘ **Strategic Insight:**\n{explanation['key_insight']}")
262
 
263
- st.markdown("**Algorithm Metrics:**")
264
  perf_col1, perf_col2, perf_col3 = st.columns(3)
265
  with perf_col1:
266
- st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["nodes_explored"]:,}</div><div class="stat-label">Explored</div></div>', unsafe_allow_html=True)
267
  with perf_col2:
268
- st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["nodes_pruned"]:,}</div><div class="stat-label">Pruned</div></div>', unsafe_allow_html=True)
269
  with perf_col3:
270
  st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["pruning_efficiency"]:.1f}%</div><div class="stat-label">Efficiency</div></div>', unsafe_allow_html=True)
271
 
272
  stat_col4, stat_col5 = st.columns(2)
273
  with stat_col4:
274
- st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["depth"]}</div><div class="stat-label">Depth</div></div>', unsafe_allow_html=True)
275
  with stat_col5:
276
- st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["time_ms"]:.0f}ms</div><div class="stat-label">Time</div></div>', unsafe_allow_html=True)
277
-
278
- if not explanation.get('success', False):
279
- st.info("โ„น๏ธ Using smart rule-based explanations")
280
 
281
  st.markdown('</div>', unsafe_allow_html=True)
282
 
283
  else: # Two Player Mode
284
- st.subheader("๐ŸŽฏ Game Board")
 
285
  board_str = st.session_state.game.board_to_string()
286
- st.markdown(f'<div class="board-container"><div class="board-display">{board_str}</div></div>', unsafe_allow_html=True)
 
287
 
288
  if not st.session_state.game_over:
289
  player_name = f"Player {st.session_state.current_player}"
290
  player_emoji = "๐Ÿ”ด" if st.session_state.current_player == 1 else "๐ŸŸก"
291
- st.markdown(f"### {player_name} Move {player_emoji}")
292
 
293
  valid_cols = st.session_state.game.get_valid_moves()
294
  if valid_cols:
295
  human_move = st.selectbox("Choose column:", valid_cols, key="move_select")
296
 
297
- if st.button("๐Ÿ“ Play Move", use_container_width=True, type="primary"):
298
  current_player = st.session_state.current_player
299
  st.session_state.game.make_move(human_move, player=current_player)
300
  st.session_state.move_history.append(human_move)
@@ -334,86 +556,83 @@ else: # Two Player Mode
334
 
335
  st.rerun()
336
 
337
- # PLAYER ANALYSIS BELOW BOARD
338
  st.markdown("---")
339
- st.subheader("๐Ÿ“Š Move Analysis")
340
 
341
  col1, col2 = st.columns(2)
342
 
343
  with col1:
344
  if st.session_state.player1_analyses:
345
- st.markdown("### ๐Ÿ”ด Player 1")
346
- latest_p1 = st.session_state.player1_analyses[-1]
347
  st.markdown('<div class="player-section player1-section">', unsafe_allow_html=True)
348
- st.write(f"**Last Move:** Column {latest_p1['column']}")
 
 
 
349
  if latest_p1['recommendation']['is_optimal']:
350
- st.success("โœ… Excellent Move!")
351
  else:
352
- st.warning(f"๐Ÿค” AI suggests Column {latest_p1['recommendation']['suggested_column']}")
353
 
354
  if latest_p1['analysis']['threats']:
355
- st.markdown(f"โš”๏ธ **Threats:** {', '.join(latest_p1['analysis']['threats'])}")
356
-
357
- st.markdown(f"**Score:** {latest_p1['analysis']['score']} points")
358
 
359
- st.markdown("**AI Insights:**")
360
- st.write(f"๐Ÿ’ญ This move evaluates to {latest_p1['analysis']['score']} points. " +
361
- ("AI recommends this column for the best position." if latest_p1['recommendation']['is_optimal'] else
362
- f"AI would prefer Column {latest_p1['recommendation']['suggested_column']} for better positioning."))
363
 
364
  perf_col1, perf_col2 = st.columns(2)
365
  with perf_col1:
366
- st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p1["analysis"]["nodes_explored"]:,}</div><div class="stat-label">Explored</div></div>', unsafe_allow_html=True)
367
  with perf_col2:
368
- st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p1["analysis"]["nodes_pruned"]:,}</div><div class="stat-label">Pruned</div></div>', unsafe_allow_html=True)
369
  st.markdown('</div>', unsafe_allow_html=True)
370
 
371
  with col2:
372
  if st.session_state.player2_analyses:
373
- st.markdown("### ๐ŸŸก Player 2")
374
- latest_p2 = st.session_state.player2_analyses[-1]
375
  st.markdown('<div class="player-section player2-section">', unsafe_allow_html=True)
376
- st.write(f"**Last Move:** Column {latest_p2['column']}")
 
 
 
377
  if latest_p2['recommendation']['is_optimal']:
378
- st.success("โœ… Excellent Move!")
379
  else:
380
- st.warning(f"๐Ÿค” AI suggests Column {latest_p2['recommendation']['suggested_column']}")
381
 
382
  if latest_p2['analysis']['threats']:
383
- st.markdown(f"โš”๏ธ **Threats:** {', '.join(latest_p2['analysis']['threats'])}")
 
 
384
 
385
- st.markdown(f"**Score:** {latest_p2['analysis']['score']} points")
386
-
387
- st.markdown("**AI Insights:**")
388
- st.write(f"๐Ÿ’ญ This move evaluates to {latest_p2['analysis']['score']} points. " +
389
- ("AI recommends this column for the best position." if latest_p2['recommendation']['is_optimal'] else
390
- f"AI would prefer Column {latest_p2['recommendation']['suggested_column']} for better positioning."))
391
 
392
  perf_col1, perf_col2 = st.columns(2)
393
  with perf_col1:
394
- st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p2["analysis"]["nodes_explored"]:,}</div><div class="stat-label">Explored</div></div>', unsafe_allow_html=True)
395
  with perf_col2:
396
- st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p2["analysis"]["nodes_pruned"]:,}</div><div class="stat-label">Pruned</div></div>', unsafe_allow_html=True)
397
  st.markdown('</div>', unsafe_allow_html=True)
398
 
 
399
  if st.session_state.show_winner_popup and st.session_state.game_over:
400
  if st.session_state.winner == "Human":
401
  st.balloons()
402
- st.success("# ๐ŸŽ‰ YOU WON! ๐ŸŽ‰")
403
  elif st.session_state.winner == "AI":
404
- st.info("# ๐Ÿค– AI WON!")
405
  elif "Player" in st.session_state.winner:
406
  st.balloons()
407
- st.success(f"# ๐ŸŽ‰ {st.session_state.winner.upper()} WON! ๐ŸŽ‰")
408
  else:
409
- st.info("# ๐Ÿค IT'S A DRAW!")
410
 
411
  st.session_state.show_winner_popup = False
412
 
 
413
  if st.session_state.game_over:
414
  st.markdown("---")
415
- st.markdown("### ๐Ÿ“„ Download Match Report")
416
- st.info("๐Ÿ“ฅ Complete analysis with all algorithm metrics")
417
 
418
  report = generate_match_report(
419
  st.session_state.game_mode,
@@ -428,10 +647,10 @@ if st.session_state.game_over:
428
  filename = f"connect_four_report_{timestamp}.txt"
429
 
430
  st.download_button(
431
- label="๐Ÿ“ฅ Download Match Report",
432
  data=report,
433
  file_name=filename,
434
  mime="text/plain",
435
  use_container_width=True,
436
  type="primary"
437
- )
 
10
 
11
  st.markdown('''
12
  <style>
13
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
14
+
15
+ * {
16
+ font-family: 'Poppins', sans-serif;
17
+ }
18
+
19
+ .stApp {
20
+ background: linear-gradient(135deg, #1e3c72 0%, #2a5298 50%, #7e22ce 100%);
21
+ }
22
+
23
+ .board-display {
24
+ font-family: 'Courier New', monospace;
25
+ white-space: pre;
26
+ line-height: 1.4;
27
+ font-size: 18px;
28
+ padding: 30px;
29
+ background: linear-gradient(145deg, #2d3748, #1a202c);
30
+ color: white;
31
+ border-radius: 20px;
32
+ box-shadow: 0 20px 60px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.1);
33
+ margin: 0 auto;
34
+ display: inline-block;
35
+ border: 3px solid rgba(255,255,255,0.1);
36
+ }
37
+
38
+ .board-container {
39
+ text-align: center;
40
+ padding: 20px;
41
+ background: rgba(255,255,255,0.05);
42
+ border-radius: 25px;
43
+ backdrop-filter: blur(10px);
44
+ }
45
+
46
+ .game-header {
47
+ background: linear-gradient(135deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05));
48
+ padding: 25px;
49
+ border-radius: 20px;
50
+ margin-bottom: 20px;
51
+ backdrop-filter: blur(10px);
52
+ border: 1px solid rgba(255,255,255,0.2);
53
+ box-shadow: 0 8px 32px rgba(0,0,0,0.3);
54
+ }
55
+
56
+ .stats-card {
57
+ background: linear-gradient(135deg, rgba(255,255,255,0.95), rgba(240,240,255,0.95));
58
+ padding: 20px;
59
+ border-radius: 15px;
60
+ box-shadow: 0 8px 20px rgba(0,0,0,0.2);
61
+ margin: 10px 0;
62
+ border: none;
63
+ transition: transform 0.3s ease;
64
+ }
65
+
66
+ .stats-card:hover {
67
+ transform: translateY(-5px);
68
+ box-shadow: 0 12px 30px rgba(0,0,0,0.3);
69
+ }
70
+
71
+ .stat-number {
72
+ font-size: 32px;
73
+ font-weight: 700;
74
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
75
+ -webkit-background-clip: text;
76
+ -webkit-text-fill-color: transparent;
77
+ background-clip: text;
78
+ }
79
+
80
+ .stat-label {
81
+ font-size: 11px;
82
+ color: #666;
83
+ text-transform: uppercase;
84
+ letter-spacing: 1px;
85
+ font-weight: 600;
86
+ }
87
+
88
+ .move-card {
89
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
90
+ color: white;
91
+ padding: 25px;
92
+ border-radius: 18px;
93
+ margin: 15px 0;
94
+ box-shadow: 0 10px 30px rgba(245,87,108,0.4);
95
+ border: 2px solid rgba(255,255,255,0.3);
96
+ }
97
+
98
+ .player-section {
99
+ background: linear-gradient(145deg, rgba(255,255,255,0.95), rgba(250,250,255,0.95));
100
+ padding: 25px;
101
+ border-radius: 18px;
102
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
103
+ margin: 20px 0;
104
+ border: 2px solid rgba(255,255,255,0.5);
105
+ }
106
+
107
+ .human-section {
108
+ background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%);
109
+ border-left: 6px solid #6366f1;
110
+ box-shadow: 0 10px 30px rgba(99,102,241,0.3);
111
+ }
112
+
113
+ .ai-section {
114
+ background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
115
+ border-left: 6px solid #f59e0b;
116
+ box-shadow: 0 10px 30px rgba(245,158,11,0.3);
117
+ }
118
+
119
+ .player1-section {
120
+ border-left: 6px solid #ef4444;
121
+ box-shadow: 0 8px 25px rgba(239,68,68,0.3);
122
+ }
123
+
124
+ .player2-section {
125
+ border-left: 6px solid #f59e0b;
126
+ box-shadow: 0 8px 25px rgba(245,158,11,0.3);
127
+ }
128
+
129
+ .mode-badge {
130
+ display: inline-block;
131
+ padding: 8px 20px;
132
+ border-radius: 25px;
133
+ font-size: 13px;
134
+ font-weight: 700;
135
+ margin-right: 8px;
136
+ background: linear-gradient(135deg, #10b981, #059669);
137
+ color: white;
138
+ box-shadow: 0 4px 15px rgba(16,185,129,0.4);
139
+ letter-spacing: 0.5px;
140
+ }
141
+
142
+ .two-player {
143
+ background: linear-gradient(135deg, #f59e0b, #d97706);
144
+ box-shadow: 0 4px 15px rgba(245,158,11,0.4);
145
+ }
146
+
147
+ .section-title {
148
+ font-size: 20px;
149
+ font-weight: 700;
150
+ margin-bottom: 15px;
151
+ color: #1f2937;
152
+ display: flex;
153
+ align-items: center;
154
+ gap: 10px;
155
+ }
156
+
157
+ .metric-row {
158
+ display: flex;
159
+ gap: 10px;
160
+ margin: 15px 0;
161
+ }
162
+
163
+ div[data-testid="stSidebar"] {
164
+ background: linear-gradient(180deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.95) 100%);
165
+ backdrop-filter: blur(10px);
166
+ }
167
+
168
+ div[data-testid="stSidebar"] * {
169
+ color: white !important;
170
+ }
171
+
172
+ .winner-banner {
173
+ background: linear-gradient(135deg, #10b981, #059669);
174
+ color: white;
175
+ padding: 30px;
176
+ border-radius: 20px;
177
+ text-align: center;
178
+ font-size: 28px;
179
+ font-weight: 700;
180
+ box-shadow: 0 15px 40px rgba(16,185,129,0.5);
181
+ margin: 20px 0;
182
+ animation: slideDown 0.5s ease;
183
+ }
184
+
185
+ @keyframes slideDown {
186
+ from {
187
+ opacity: 0;
188
+ transform: translateY(-20px);
189
+ }
190
+ to {
191
+ opacity: 1;
192
+ transform: translateY(0);
193
+ }
194
+ }
195
+
196
+ h1 {
197
+ color: white !important;
198
+ font-weight: 700 !important;
199
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
200
+ }
201
+
202
+ h2, h3 {
203
+ color: white !important;
204
+ font-weight: 600 !important;
205
+ }
206
+
207
+ .stButton > button {
208
+ border-radius: 12px;
209
+ font-weight: 600;
210
+ transition: all 0.3s ease;
211
+ border: none;
212
+ box-shadow: 0 4px 15px rgba(0,0,0,0.2);
213
+ }
214
+
215
+ .stButton > button:hover {
216
+ transform: translateY(-2px);
217
+ box-shadow: 0 6px 20px rgba(0,0,0,0.3);
218
+ }
219
+
220
+ .stSelectbox > div > div {
221
+ border-radius: 12px;
222
+ border: 2px solid rgba(255,255,255,0.3);
223
+ background: rgba(255,255,255,0.95);
224
+ }
225
+
226
+ .analysis-badge {
227
+ display: inline-block;
228
+ padding: 5px 12px;
229
+ border-radius: 20px;
230
+ font-size: 12px;
231
+ font-weight: 600;
232
+ margin: 5px 5px 5px 0;
233
+ background: rgba(99,102,241,0.1);
234
+ color: #4f46e5;
235
+ border: 1px solid rgba(99,102,241,0.3);
236
+ }
237
  </style>
238
  ''', unsafe_allow_html=True)
239
 
 
258
  st.session_state.last_human_move_analysis = None
259
  st.session_state.last_ai_move_analysis = None
260
 
261
+ # Header
262
+ st.markdown('<div class="game-header">', unsafe_allow_html=True)
263
  col_title1, col_title2 = st.columns([3, 1])
264
  with col_title1:
265
  st.title("๐ŸŽฎ Connect Four AI Pro")
266
  with col_title2:
267
  if st.session_state.game_mode == "vs AI":
268
+ st.markdown(f'<span class="mode-badge">๐Ÿค– VS AI</span>', unsafe_allow_html=True)
269
  else:
270
+ st.markdown('<span class="mode-badge two-player">๐Ÿ‘ฅ Two Player</span>', unsafe_allow_html=True)
271
+ st.markdown('</div>', unsafe_allow_html=True)
272
 
273
+ # Sidebar
274
+ st.sidebar.markdown("### โš™๏ธ Game Settings")
275
 
276
  game_mode = st.sidebar.radio("๐ŸŽฎ Game Mode", ["vs AI", "Two Player"],
277
  index=0 if st.session_state.game_mode == "vs AI" else 1)
 
290
 
291
  if st.session_state.game_mode == "vs AI":
292
  st.sidebar.markdown("---")
293
+ st.sidebar.markdown("### ๐ŸŽš๏ธ AI Difficulty")
294
+ depth = st.sidebar.slider("Search Depth", 3, 8, 5)
295
  st.session_state.ai.depth = depth
296
  st.session_state.current_depth = depth
297
 
 
333
  st.rerun()
334
 
335
  st.sidebar.markdown("---")
336
+ st.sidebar.markdown("### ๐Ÿ“Š Game Stats")
337
+ st.sidebar.metric("Total Moves", st.session_state.move_count)
338
  if st.session_state.game_mode == "Two Player":
339
+ col1, col2 = st.sidebar.columns(2)
340
+ with col1:
341
+ st.metric("๐Ÿ”ด P1", len(st.session_state.player1_analyses))
342
+ with col2:
343
+ st.metric("๐ŸŸก P2", len(st.session_state.player2_analyses))
344
 
345
  if st.session_state.move_history:
346
  with st.sidebar.expander("๐Ÿ“œ Move History"):
347
  for i, col in enumerate(st.session_state.move_history, 1):
348
  player = "๐Ÿ”ด" if i % 2 == 1 else "๐ŸŸก"
349
+ st.write(f"**Move {i}:** {player} โ†’ Col {col}")
350
 
351
  # Main layout
352
  if st.session_state.game_mode == "vs AI":
353
  col1, col2 = st.columns([2, 3])
354
 
355
  with col1:
356
+ st.markdown('<div class="board-container">', unsafe_allow_html=True)
357
+ st.markdown("### ๐ŸŽฏ Game Board")
358
  board_str = st.session_state.game.board_to_string()
359
+ st.markdown(f'<div class="board-display">{board_str}</div>', unsafe_allow_html=True)
360
+ st.markdown('</div>', unsafe_allow_html=True)
361
 
362
  if not st.session_state.game_over:
363
+ st.markdown("### ๐Ÿ”ด Your Turn")
364
  valid_cols = st.session_state.game.get_valid_moves()
365
  if valid_cols:
366
  human_move = st.selectbox("Choose column:", valid_cols, key="move_select")
367
 
368
+ if st.button("๐ŸŽฏ Play Move", use_container_width=True, type="primary"):
369
  current_player = 1
370
  st.session_state.game.make_move(human_move, player=current_player)
371
  st.session_state.move_history.append(human_move)
 
391
  st.session_state.winner = "Draw"
392
  st.session_state.show_winner_popup = True
393
  else:
394
+ with st.spinner("๐Ÿค– AI is thinking..."):
395
  ai_result = st.session_state.ai.get_best_move()
396
 
397
  st.session_state.game.make_move(ai_result['move'], player=2)
 
433
  st.rerun()
434
 
435
  with col2:
436
+ st.markdown("### ๐Ÿ“Š Move Analysis")
437
 
438
  # Human Move Analysis
439
  if st.session_state.last_human_move_analysis:
440
  human_analysis = st.session_state.last_human_move_analysis
441
  st.markdown('<div class="player-section human-section">', unsafe_allow_html=True)
442
+ st.markdown('<div class="section-title">๐Ÿ”ด Your Last Move</div>', unsafe_allow_html=True)
443
 
444
+ st.markdown(f"**Column Played:** {st.session_state.move_history[-2] if len(st.session_state.move_history) >= 2 else '?'}")
445
+ st.markdown(f"**Position Score:** {human_analysis['score']} points")
446
 
447
  if human_analysis['threats']:
448
+ st.markdown("**Strategic Threats:**")
449
+ for threat in human_analysis['threats']:
450
+ st.markdown(f'<span class="analysis-badge">{threat}</span>', unsafe_allow_html=True)
451
 
452
+ st.markdown("**Performance Metrics:**")
453
  perf_col1, perf_col2, perf_col3 = st.columns(3)
454
  with perf_col1:
455
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{human_analysis["nodes_explored"]:,}</div><div class="stat-label">Nodes Explored</div></div>', unsafe_allow_html=True)
456
  with perf_col2:
457
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{human_analysis["nodes_pruned"]:,}</div><div class="stat-label">Nodes Pruned</div></div>', unsafe_allow_html=True)
458
  with perf_col3:
459
  st.markdown(f'<div class="stats-card"><div class="stat-number">{human_analysis["pruning_efficiency"]:.1f}%</div><div class="stat-label">Efficiency</div></div>', unsafe_allow_html=True)
460
 
 
471
  })
472
 
473
  st.markdown('<div class="player-section ai-section">', unsafe_allow_html=True)
474
+ st.markdown('<div class="section-title">๐ŸŸก AI Move Analysis</div>', unsafe_allow_html=True)
475
+
476
+ st.markdown(f'''<div class="move-card"><div style="font-size: 20px; font-weight: 700; margin-bottom: 10px;">Column {ai_move['move']}</div><div style="font-size: 16px;">Score: {ai_move['score']} points</div></div>''', unsafe_allow_html=True)
477
 
478
+ st.success(f"๐Ÿ’ก **Strategy:** {explanation['explanation']}")
 
479
 
480
  if explanation.get('threat_analysis'):
481
+ st.warning(f"โš”๏ธ **Threats:** {explanation['threat_analysis']}")
482
 
483
  if explanation.get('key_insight'):
484
+ st.info(f"๐Ÿ”‘ **Insight:** {explanation['key_insight']}")
485
 
486
+ st.markdown("**Performance Metrics:**")
487
  perf_col1, perf_col2, perf_col3 = st.columns(3)
488
  with perf_col1:
489
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["nodes_explored"]:,}</div><div class="stat-label">Nodes Explored</div></div>', unsafe_allow_html=True)
490
  with perf_col2:
491
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["nodes_pruned"]:,}</div><div class="stat-label">Nodes Pruned</div></div>', unsafe_allow_html=True)
492
  with perf_col3:
493
  st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["pruning_efficiency"]:.1f}%</div><div class="stat-label">Efficiency</div></div>', unsafe_allow_html=True)
494
 
495
  stat_col4, stat_col5 = st.columns(2)
496
  with stat_col4:
497
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["depth"]}</div><div class="stat-label">Search Depth</div></div>', unsafe_allow_html=True)
498
  with stat_col5:
499
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{ai_move["time_ms"]:.0f}ms</div><div class="stat-label">Response Time</div></div>', unsafe_allow_html=True)
 
 
 
500
 
501
  st.markdown('</div>', unsafe_allow_html=True)
502
 
503
  else: # Two Player Mode
504
+ st.markdown('<div class="board-container">', unsafe_allow_html=True)
505
+ st.markdown("### ๐ŸŽฏ Game Board")
506
  board_str = st.session_state.game.board_to_string()
507
+ st.markdown(f'<div class="board-display">{board_str}</div>', unsafe_allow_html=True)
508
+ st.markdown('</div>', unsafe_allow_html=True)
509
 
510
  if not st.session_state.game_over:
511
  player_name = f"Player {st.session_state.current_player}"
512
  player_emoji = "๐Ÿ”ด" if st.session_state.current_player == 1 else "๐ŸŸก"
513
+ st.markdown(f"### {player_emoji} {player_name}'s Turn")
514
 
515
  valid_cols = st.session_state.game.get_valid_moves()
516
  if valid_cols:
517
  human_move = st.selectbox("Choose column:", valid_cols, key="move_select")
518
 
519
+ if st.button("๐ŸŽฏ Play Move", use_container_width=True, type="primary"):
520
  current_player = st.session_state.current_player
521
  st.session_state.game.make_move(human_move, player=current_player)
522
  st.session_state.move_history.append(human_move)
 
556
 
557
  st.rerun()
558
 
559
+ # Player Analysis
560
  st.markdown("---")
561
+ st.markdown("### ๐Ÿ“Š Player Analysis")
562
 
563
  col1, col2 = st.columns(2)
564
 
565
  with col1:
566
  if st.session_state.player1_analyses:
 
 
567
  st.markdown('<div class="player-section player1-section">', unsafe_allow_html=True)
568
+ st.markdown('<div class="section-title">๐Ÿ”ด Player 1</div>', unsafe_allow_html=True)
569
+ latest_p1 = st.session_state.player1_analyses[-1]
570
+
571
+ st.markdown(f"**Last Move:** Column {latest_p1['column']}")
572
  if latest_p1['recommendation']['is_optimal']:
573
+ st.success("โœ… Optimal Move!")
574
  else:
575
+ st.warning(f"๐Ÿ’ก AI suggests Column {latest_p1['recommendation']['suggested_column']}")
576
 
577
  if latest_p1['analysis']['threats']:
578
+ st.markdown("**Strategic Threats:**")
579
+ for threat in latest_p1['analysis']['threats']:
580
+ st.markdown(f'<span class="analysis-badge">{threat}</span>', unsafe_allow_html=True)
581
 
582
+ st.markdown(f"**Position Score:** {latest_p1['analysis']['score']} points")
 
 
 
583
 
584
  perf_col1, perf_col2 = st.columns(2)
585
  with perf_col1:
586
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p1["analysis"]["nodes_explored"]:,}</div><div class="stat-label">Nodes Explored</div></div>', unsafe_allow_html=True)
587
  with perf_col2:
588
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p1["analysis"]["pruning_efficiency"]:.1f}%</div><div class="stat-label">Efficiency</div></div>', unsafe_allow_html=True)
589
  st.markdown('</div>', unsafe_allow_html=True)
590
 
591
  with col2:
592
  if st.session_state.player2_analyses:
 
 
593
  st.markdown('<div class="player-section player2-section">', unsafe_allow_html=True)
594
+ st.markdown('<div class="section-title">๐ŸŸก Player 2</div>', unsafe_allow_html=True)
595
+ latest_p2 = st.session_state.player2_analyses[-1]
596
+
597
+ st.markdown(f"**Last Move:** Column {latest_p2['column']}")
598
  if latest_p2['recommendation']['is_optimal']:
599
+ st.success("โœ… Optimal Move!")
600
  else:
601
+ st.warning(f"๐Ÿ’ก AI suggests Column {latest_p2['recommendation']['suggested_column']}")
602
 
603
  if latest_p2['analysis']['threats']:
604
+ st.markdown("**Strategic Threats:**")
605
+ for threat in latest_p2['analysis']['threats']:
606
+ st.markdown(f'<span class="analysis-badge">{threat}</span>', unsafe_allow_html=True)
607
 
608
+ st.markdown(f"**Position Score:** {latest_p2['analysis']['score']} points")
 
 
 
 
 
609
 
610
  perf_col1, perf_col2 = st.columns(2)
611
  with perf_col1:
612
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p2["analysis"]["nodes_explored"]:,}</div><div class="stat-label">Nodes Explored</div></div>', unsafe_allow_html=True)
613
  with perf_col2:
614
+ st.markdown(f'<div class="stats-card"><div class="stat-number">{latest_p2["analysis"]["pruning_efficiency"]:.1f}%</div><div class="stat-label">Efficiency</div></div>', unsafe_allow_html=True)
615
  st.markdown('</div>', unsafe_allow_html=True)
616
 
617
+ # Winner Display
618
  if st.session_state.show_winner_popup and st.session_state.game_over:
619
  if st.session_state.winner == "Human":
620
  st.balloons()
621
+ st.markdown('<div class="winner-banner">๐ŸŽ‰ YOU WON! ๐ŸŽ‰</div>', unsafe_allow_html=True)
622
  elif st.session_state.winner == "AI":
623
+ st.markdown('<div class="winner-banner" style="background: linear-gradient(135deg, #f59e0b, #d97706);">๐Ÿค– AI WINS!</div>', unsafe_allow_html=True)
624
  elif "Player" in st.session_state.winner:
625
  st.balloons()
626
+ st.markdown(f'<div class="winner-banner">๐ŸŽ‰ {st.session_state.winner.upper()} WINS! ๐ŸŽ‰</div>', unsafe_allow_html=True)
627
  else:
628
+ st.markdown('<div class="winner-banner" style="background: linear-gradient(135deg, #6366f1, #4f46e5);">๐Ÿค IT\'S A DRAW!</div>', unsafe_allow_html=True)
629
 
630
  st.session_state.show_winner_popup = False
631
 
632
+ # Download Report
633
  if st.session_state.game_over:
634
  st.markdown("---")
635
+ st.markdown("### ๐Ÿ“„ Match Report")
 
636
 
637
  report = generate_match_report(
638
  st.session_state.game_mode,
 
647
  filename = f"connect_four_report_{timestamp}.txt"
648
 
649
  st.download_button(
650
+ label="๐Ÿ“ฅ Download Full Match Report",
651
  data=report,
652
  file_name=filename,
653
  mime="text/plain",
654
  use_container_width=True,
655
  type="primary"
656
+ )