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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +290 -203
app.py CHANGED
@@ -10,229 +10,318 @@ st.set_page_config(page_title="Connect Four AI Pro", layout="wide", initial_side
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)
@@ -259,16 +348,14 @@ if 'game' not in st.session_state:
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")
@@ -353,7 +440,7 @@ 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)
@@ -433,30 +520,29 @@ if st.session_state.game_mode == "vs AI":
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
 
461
  st.markdown('</div>', unsafe_allow_html=True)
462
 
@@ -471,9 +557,9 @@ if st.session_state.game_mode == "vs AI":
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
 
@@ -483,25 +569,24 @@ if st.session_state.game_mode == "vs AI":
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)
@@ -558,60 +643,62 @@ else: # Two Player Mode
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
@@ -620,19 +707,19 @@ if st.session_state.show_winner_popup and st.session_state.game_over:
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,
 
10
 
11
  st.markdown('''
12
  <style>
13
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
14
 
15
  * {
16
+ font-family: 'Inter', sans-serif;
17
  }
18
 
19
  .stApp {
20
+ background: #0f172a;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
 
23
+ /* Sidebar Styling */
24
+ [data-testid="stSidebar"] {
25
+ background: #1e293b;
26
+ border-right: 1px solid #334155;
 
 
27
  }
28
 
29
+ [data-testid="stSidebar"] * {
30
+ color: #e2e8f0 !important;
 
 
 
 
 
 
31
  }
32
 
33
+ [data-testid="stSidebar"] .stRadio > label {
34
+ color: #f1f5f9 !important;
35
+ font-weight: 600;
 
 
 
 
 
36
  }
37
 
38
+ [data-testid="stSidebar"] h3 {
39
+ color: #f8fafc !important;
40
+ font-size: 16px !important;
41
+ font-weight: 700 !important;
42
+ margin-bottom: 16px !important;
43
  }
44
 
45
+ /* Main Content */
46
+ h1 {
47
+ color: #f8fafc !important;
48
+ font-weight: 700 !important;
49
+ font-size: 36px !important;
50
+ margin-bottom: 8px !important;
 
51
  }
52
 
53
+ h2 {
54
+ color: #f1f5f9 !important;
55
+ font-weight: 600 !important;
56
+ font-size: 24px !important;
57
+ margin-top: 24px !important;
58
+ margin-bottom: 16px !important;
59
  }
60
 
61
+ h3 {
62
+ color: #e2e8f0 !important;
63
+ font-weight: 600 !important;
64
+ font-size: 18px !important;
65
+ margin-bottom: 12px !important;
 
 
 
66
  }
67
 
68
+ /* Board Container */
69
+ .board-container {
70
+ background: #1e293b;
71
+ border-radius: 16px;
72
+ padding: 24px;
73
+ margin-bottom: 24px;
74
+ border: 1px solid #334155;
75
  }
76
 
77
+ .board-display {
78
+ font-family: 'Courier New', monospace;
79
+ white-space: pre;
80
+ line-height: 1.5;
81
+ font-size: 20px;
82
+ padding: 24px;
83
+ background: #0f172a;
84
+ color: #f8fafc;
85
+ border-radius: 12px;
86
+ border: 2px solid #475569;
87
+ display: inline-block;
88
+ margin: 0 auto;
89
  }
90
 
91
+ /* Stats Cards */
92
+ .stats-card {
93
+ background: #1e293b;
94
+ padding: 20px;
95
+ border-radius: 12px;
96
+ border: 1px solid #334155;
97
+ text-align: center;
98
+ margin: 8px 0;
99
  }
100
 
101
+ .stat-number {
102
+ font-size: 28px;
103
+ font-weight: 700;
104
+ color: #60a5fa;
105
+ display: block;
106
+ margin-bottom: 4px;
107
  }
108
 
109
+ .stat-label {
110
+ font-size: 11px;
111
+ font-weight: 600;
112
+ color: #94a3b8;
113
+ text-transform: uppercase;
114
+ letter-spacing: 0.5px;
115
  }
116
 
117
+ /* Player Sections */
118
+ .player-section {
119
+ background: #1e293b;
120
+ padding: 24px;
121
+ border-radius: 12px;
122
+ border: 1px solid #334155;
123
+ margin-bottom: 20px;
 
 
 
 
124
  }
125
 
126
+ .human-section {
127
+ border-left: 4px solid #3b82f6;
 
128
  }
129
 
130
+ .ai-section {
131
+ border-left: 4px solid #f59e0b;
132
+ }
133
+
134
+ .player1-section {
135
+ border-left: 4px solid #ef4444;
136
+ }
137
+
138
+ .player2-section {
139
+ border-left: 4px solid #f59e0b;
140
+ }
141
+
142
+ .section-header {
143
+ font-size: 16px;
144
  font-weight: 700;
145
+ color: #f1f5f9;
146
+ margin-bottom: 16px;
147
  display: flex;
148
  align-items: center;
149
+ gap: 8px;
150
  }
151
 
152
+ .info-row {
153
+ color: #cbd5e1;
154
+ font-size: 14px;
155
+ margin: 8px 0;
156
+ line-height: 1.6;
157
  }
158
 
159
+ .info-row strong {
160
+ color: #f1f5f9;
161
+ font-weight: 600;
162
  }
163
 
164
+ /* Badges */
165
+ .badge {
166
+ display: inline-block;
167
+ padding: 4px 12px;
168
+ border-radius: 20px;
169
+ font-size: 12px;
170
+ font-weight: 600;
171
+ margin: 4px 4px 4px 0;
172
  }
173
 
174
+ .badge-success {
175
+ background: #10b981;
176
  color: white;
177
+ }
178
+
179
+ .badge-warning {
180
+ background: #f59e0b;
181
+ color: white;
182
+ }
183
+
184
+ .badge-info {
185
+ background: #3b82f6;
186
+ color: white;
187
+ }
188
+
189
+ .badge-threat {
190
+ background: #1e293b;
191
+ color: #94a3b8;
192
+ border: 1px solid #334155;
193
+ }
194
+
195
+ /* Move Card */
196
+ .move-card {
197
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
198
+ color: white;
199
+ padding: 20px;
200
+ border-radius: 12px;
201
+ margin-bottom: 16px;
202
  text-align: center;
203
+ }
204
+
205
+ .move-card-title {
206
+ font-size: 18px;
207
  font-weight: 700;
208
+ margin-bottom: 8px;
 
 
209
  }
210
 
211
+ .move-card-score {
212
+ font-size: 14px;
213
+ opacity: 0.9;
 
 
 
 
 
 
214
  }
215
 
216
+ /* Winner Banner */
217
+ .winner-banner {
218
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
219
+ color: white;
220
+ padding: 32px;
221
+ border-radius: 16px;
222
+ text-align: center;
223
+ font-size: 32px;
224
+ font-weight: 700;
225
+ margin: 24px 0;
226
+ box-shadow: 0 10px 30px rgba(16, 185, 129, 0.3);
227
  }
228
 
229
+ .winner-banner.ai {
230
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
231
+ box-shadow: 0 10px 30px rgba(245, 158, 11, 0.3);
232
  }
233
 
234
+ .winner-banner.draw {
235
+ background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
236
+ box-shadow: 0 10px 30px rgba(99, 102, 241, 0.3);
237
+ }
238
+
239
+ /* Buttons */
240
  .stButton > button {
241
+ background: #3b82f6;
242
+ color: white;
 
243
  border: none;
244
+ border-radius: 8px;
245
+ font-weight: 600;
246
+ padding: 8px 16px;
247
+ transition: all 0.2s;
248
  }
249
 
250
  .stButton > button:hover {
251
+ background: #2563eb;
252
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
253
  }
254
 
255
+ .stButton > button[kind="primary"] {
256
+ background: #10b981;
257
+ }
258
+
259
+ .stButton > button[kind="primary"]:hover {
260
+ background: #059669;
261
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
262
+ }
263
+
264
+ /* Select Box */
265
  .stSelectbox > div > div {
266
+ background: #1e293b;
267
+ border: 1px solid #334155;
268
+ color: #f1f5f9;
269
+ border-radius: 8px;
270
  }
271
 
272
+ /* Metric boxes */
273
+ [data-testid="stMetricValue"] {
274
+ color: #60a5fa !important;
275
+ font-size: 24px !important;
276
+ font-weight: 700 !important;
277
+ }
278
+
279
+ [data-testid="stMetricLabel"] {
280
+ color: #94a3b8 !important;
281
+ font-size: 13px !important;
282
+ }
283
+
284
+ /* Streamlit native elements */
285
+ .stAlert {
286
+ background: #1e293b;
287
+ border: 1px solid #334155;
288
+ border-radius: 8px;
289
+ color: #e2e8f0;
290
+ }
291
+
292
+ div[data-baseweb="notification"] {
293
+ background: #1e293b;
294
+ border: 1px solid #334155;
295
+ }
296
+
297
+ /* Success/Warning/Info boxes */
298
+ .stSuccess {
299
+ background: rgba(16, 185, 129, 0.1) !important;
300
+ border-left: 4px solid #10b981 !important;
301
+ color: #a7f3d0 !important;
302
+ }
303
+
304
+ .stWarning {
305
+ background: rgba(245, 158, 11, 0.1) !important;
306
+ border-left: 4px solid #f59e0b !important;
307
+ color: #fde68a !important;
308
+ }
309
+
310
+ .stInfo {
311
+ background: rgba(59, 130, 246, 0.1) !important;
312
+ border-left: 4px solid #3b82f6 !important;
313
+ color: #bfdbfe !important;
314
+ }
315
+
316
+ /* Hide Streamlit elements */
317
+ #MainMenu {visibility: hidden;}
318
+ footer {visibility: hidden;}
319
+
320
+ /* Expander */
321
+ .streamlit-expanderHeader {
322
+ background: #1e293b;
323
+ color: #f1f5f9 !important;
324
+ border-radius: 8px;
325
  }
326
  </style>
327
  ''', unsafe_allow_html=True)
 
348
  st.session_state.last_ai_move_analysis = None
349
 
350
  # Header
351
+ col_title1, col_title2 = st.columns([4, 1])
 
352
  with col_title1:
353
  st.title("๐ŸŽฎ Connect Four AI Pro")
354
  with col_title2:
355
  if st.session_state.game_mode == "vs AI":
356
+ st.markdown('<span class="badge badge-info">๐Ÿค– VS AI</span>', unsafe_allow_html=True)
357
  else:
358
+ st.markdown('<span class="badge badge-warning">๐Ÿ‘ฅ Two Player</span>', unsafe_allow_html=True)
 
359
 
360
  # Sidebar
361
  st.sidebar.markdown("### โš™๏ธ Game Settings")
 
440
  col1, col2 = st.columns([2, 3])
441
 
442
  with col1:
443
+ st.markdown('<div class="board-container" style="text-align: center;">', unsafe_allow_html=True)
444
  st.markdown("### ๐ŸŽฏ Game Board")
445
  board_str = st.session_state.game.board_to_string()
446
  st.markdown(f'<div class="board-display">{board_str}</div>', unsafe_allow_html=True)
 
520
  st.rerun()
521
 
522
  with col2:
523
+ st.markdown("## ๐Ÿ“Š Move Analysis")
524
 
525
  # Human Move Analysis
526
  if st.session_state.last_human_move_analysis:
527
  human_analysis = st.session_state.last_human_move_analysis
528
  st.markdown('<div class="player-section human-section">', unsafe_allow_html=True)
529
+ st.markdown('<div class="section-header">๐Ÿ”ด Your Last Move</div>', unsafe_allow_html=True)
530
 
531
+ st.markdown(f'<div class="info-row"><strong>Column:</strong> {st.session_state.move_history[-2] if len(st.session_state.move_history) >= 2 else "?"}</div>', unsafe_allow_html=True)
532
+ st.markdown(f'<div class="info-row"><strong>Score:</strong> {human_analysis["score"]} points</div>', unsafe_allow_html=True)
533
 
534
  if human_analysis['threats']:
535
+ st.markdown('<div class="info-row"><strong>Threats:</strong></div>', unsafe_allow_html=True)
536
  for threat in human_analysis['threats']:
537
+ st.markdown(f'<span class="badge badge-threat">{threat}</span>', unsafe_allow_html=True)
538
 
 
539
  perf_col1, perf_col2, perf_col3 = st.columns(3)
540
  with perf_col1:
541
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{human_analysis["nodes_explored"]:,}</span><span class="stat-label">Explored</span></div>', unsafe_allow_html=True)
542
  with perf_col2:
543
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{human_analysis["nodes_pruned"]:,}</span><span class="stat-label">Pruned</span></div>', unsafe_allow_html=True)
544
  with perf_col3:
545
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{human_analysis["pruning_efficiency"]:.1f}%</span><span class="stat-label">Efficiency</span></div>', unsafe_allow_html=True)
546
 
547
  st.markdown('</div>', unsafe_allow_html=True)
548
 
 
557
  })
558
 
559
  st.markdown('<div class="player-section ai-section">', unsafe_allow_html=True)
560
+ st.markdown('<div class="section-header">๐ŸŸก AI Move Analysis</div>', unsafe_allow_html=True)
561
 
562
+ st.markdown(f'<div class="move-card"><div class="move-card-title">Column {ai_move["move"]}</div><div class="move-card-score">Score: {ai_move["score"]} points</div></div>', unsafe_allow_html=True)
563
 
564
  st.success(f"๐Ÿ’ก **Strategy:** {explanation['explanation']}")
565
 
 
569
  if explanation.get('key_insight'):
570
  st.info(f"๐Ÿ”‘ **Insight:** {explanation['key_insight']}")
571
 
 
572
  perf_col1, perf_col2, perf_col3 = st.columns(3)
573
  with perf_col1:
574
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{ai_move["nodes_explored"]:,}</span><span class="stat-label">Explored</span></div>', unsafe_allow_html=True)
575
  with perf_col2:
576
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{ai_move["nodes_pruned"]:,}</span><span class="stat-label">Pruned</span></div>', unsafe_allow_html=True)
577
  with perf_col3:
578
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{ai_move["pruning_efficiency"]:.1f}%</span><span class="stat-label">Efficiency</span></div>', unsafe_allow_html=True)
579
 
580
  stat_col4, stat_col5 = st.columns(2)
581
  with stat_col4:
582
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{ai_move["depth"]}</span><span class="stat-label">Depth</span></div>', unsafe_allow_html=True)
583
  with stat_col5:
584
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{ai_move["time_ms"]:.0f}ms</span><span class="stat-label">Time</span></div>', unsafe_allow_html=True)
585
 
586
  st.markdown('</div>', unsafe_allow_html=True)
587
 
588
  else: # Two Player Mode
589
+ st.markdown('<div class="board-container" style="text-align: center;">', unsafe_allow_html=True)
590
  st.markdown("### ๐ŸŽฏ Game Board")
591
  board_str = st.session_state.game.board_to_string()
592
  st.markdown(f'<div class="board-display">{board_str}</div>', unsafe_allow_html=True)
 
643
 
644
  # Player Analysis
645
  st.markdown("---")
646
+ st.markdown("## ๐Ÿ“Š Player Analysis")
647
 
648
  col1, col2 = st.columns(2)
649
 
650
  with col1:
651
  if st.session_state.player1_analyses:
652
  st.markdown('<div class="player-section player1-section">', unsafe_allow_html=True)
653
+ st.markdown('<div class="section-header">๐Ÿ”ด Player 1</div>', unsafe_allow_html=True)
654
  latest_p1 = st.session_state.player1_analyses[-1]
655
 
656
+ st.markdown(f'<div class="info-row"><strong>Last Move:</strong> Column {latest_p1["column"]}</div>', unsafe_allow_html=True)
657
+
658
  if latest_p1['recommendation']['is_optimal']:
659
+ st.markdown('<span class="badge badge-success">โœ… Optimal Move!</span>', unsafe_allow_html=True)
660
  else:
661
+ st.markdown(f'<span class="badge badge-warning">๐Ÿ’ก AI suggests Column {latest_p1["recommendation"]["suggested_column"]}</span>', unsafe_allow_html=True)
662
 
663
  if latest_p1['analysis']['threats']:
664
+ st.markdown('<div class="info-row" style="margin-top: 12px;"><strong>Threats:</strong></div>', unsafe_allow_html=True)
665
  for threat in latest_p1['analysis']['threats']:
666
+ st.markdown(f'<span class="badge badge-threat">{threat}</span>', unsafe_allow_html=True)
667
 
668
+ st.markdown(f'<div class="info-row" style="margin-top: 12px;"><strong>Score:</strong> {latest_p1["analysis"]["score"]} points</div>', unsafe_allow_html=True)
669
 
670
  perf_col1, perf_col2 = st.columns(2)
671
  with perf_col1:
672
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{latest_p1["analysis"]["nodes_explored"]:,}</span><span class="stat-label">Nodes Explored</span></div>', unsafe_allow_html=True)
673
  with perf_col2:
674
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{latest_p1["analysis"]["pruning_efficiency"]:.1f}%</span><span class="stat-label">Efficiency</span></div>', unsafe_allow_html=True)
675
  st.markdown('</div>', unsafe_allow_html=True)
676
 
677
  with col2:
678
  if st.session_state.player2_analyses:
679
  st.markdown('<div class="player-section player2-section">', unsafe_allow_html=True)
680
+ st.markdown('<div class="section-header">๐ŸŸก Player 2</div>', unsafe_allow_html=True)
681
  latest_p2 = st.session_state.player2_analyses[-1]
682
 
683
+ st.markdown(f'<div class="info-row"><strong>Last Move:</strong> Column {latest_p2["column"]}</div>', unsafe_allow_html=True)
684
+
685
  if latest_p2['recommendation']['is_optimal']:
686
+ st.markdown('<span class="badge badge-success">โœ… Optimal Move!</span>', unsafe_allow_html=True)
687
  else:
688
+ st.markdown(f'<span class="badge badge-warning">๐Ÿ’ก AI suggests Column {latest_p2["recommendation"]["suggested_column"]}</span>', unsafe_allow_html=True)
689
 
690
  if latest_p2['analysis']['threats']:
691
+ st.markdown('<div class="info-row" style="margin-top: 12px;"><strong>Threats:</strong></div>', unsafe_allow_html=True)
692
  for threat in latest_p2['analysis']['threats']:
693
+ st.markdown(f'<span class="badge badge-threat">{threat}</span>', unsafe_allow_html=True)
694
 
695
+ st.markdown(f'<div class="info-row" style="margin-top: 12px;"><strong>Score:</strong> {latest_p2["analysis"]["score"]} points</div>', unsafe_allow_html=True)
696
 
697
  perf_col1, perf_col2 = st.columns(2)
698
  with perf_col1:
699
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{latest_p2["analysis"]["nodes_explored"]:,}</span><span class="stat-label">Nodes Explored</span></div>', unsafe_allow_html=True)
700
  with perf_col2:
701
+ st.markdown(f'<div class="stats-card"><span class="stat-number">{latest_p2["analysis"]["pruning_efficiency"]:.1f}%</span><span class="stat-label">Efficiency</span></div>', unsafe_allow_html=True)
702
  st.markdown('</div>', unsafe_allow_html=True)
703
 
704
  # Winner Display
 
707
  st.balloons()
708
  st.markdown('<div class="winner-banner">๐ŸŽ‰ YOU WON! ๐ŸŽ‰</div>', unsafe_allow_html=True)
709
  elif st.session_state.winner == "AI":
710
+ st.markdown('<div class="winner-banner ai">๐Ÿค– AI WINS!</div>', unsafe_allow_html=True)
711
  elif "Player" in st.session_state.winner:
712
  st.balloons()
713
  st.markdown(f'<div class="winner-banner">๐ŸŽ‰ {st.session_state.winner.upper()} WINS! ๐ŸŽ‰</div>', unsafe_allow_html=True)
714
  else:
715
+ st.markdown('<div class="winner-banner draw">๐Ÿค IT\'S A DRAW!</div>', unsafe_allow_html=True)
716
 
717
  st.session_state.show_winner_popup = False
718
 
719
  # Download Report
720
  if st.session_state.game_over:
721
  st.markdown("---")
722
+ st.markdown("## ๐Ÿ“„ Match Report")
723
 
724
  report = generate_match_report(
725
  st.session_state.game_mode,