Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -21,6 +21,8 @@ st.markdown('''
|
|
| 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 |
</style>
|
| 25 |
''', unsafe_allow_html=True)
|
| 26 |
|
|
@@ -42,6 +44,8 @@ if 'game' not in st.session_state:
|
|
| 42 |
st.session_state.show_winner_popup = False
|
| 43 |
st.session_state.move_count = 0
|
| 44 |
st.session_state.current_depth = 5
|
|
|
|
|
|
|
| 45 |
|
| 46 |
col_title1, col_title2 = st.columns([3, 1])
|
| 47 |
with col_title1:
|
|
@@ -91,6 +95,8 @@ with col1:
|
|
| 91 |
st.session_state.current_player = 1
|
| 92 |
st.session_state.show_winner_popup = False
|
| 93 |
st.session_state.move_count = 0
|
|
|
|
|
|
|
| 94 |
st.rerun()
|
| 95 |
|
| 96 |
with col2:
|
|
@@ -146,6 +152,8 @@ if st.session_state.game_mode == "vs AI":
|
|
| 146 |
st.session_state.move_count += 1
|
| 147 |
|
| 148 |
ai_analysis = st.session_state.ai.get_best_move()
|
|
|
|
|
|
|
| 149 |
move_detail = {
|
| 150 |
'player': current_player,
|
| 151 |
'player_name': "Human",
|
|
@@ -174,7 +182,13 @@ if st.session_state.game_mode == "vs AI":
|
|
| 174 |
explanation = st.session_state.explainer.explain_move(ai_result)
|
| 175 |
explanation_text = explanation.get('explanation', '')
|
| 176 |
else:
|
| 177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
|
| 179 |
ai_move_detail = {
|
| 180 |
'player': 2,
|
|
@@ -184,7 +198,8 @@ if st.session_state.game_mode == "vs AI":
|
|
| 184 |
'explanation': explanation_text
|
| 185 |
}
|
| 186 |
st.session_state.move_history_detailed.append(ai_move_detail)
|
| 187 |
-
st.session_state.
|
|
|
|
| 188 |
|
| 189 |
if st.session_state.game.check_winner(2):
|
| 190 |
st.session_state.game_over = True
|
|
@@ -198,24 +213,45 @@ if st.session_state.game_mode == "vs AI":
|
|
| 198 |
st.rerun()
|
| 199 |
|
| 200 |
with col2:
|
| 201 |
-
st.subheader("
|
| 202 |
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
-
if st.session_state.
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
}
|
| 216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
|
| 218 |
-
st.markdown(f'''<div class="move-card"><h3
|
| 219 |
st.success(f"๐ก **Why This Move:**\n{explanation['explanation']}")
|
| 220 |
|
| 221 |
if explanation.get('threat_analysis'):
|
|
@@ -224,14 +260,13 @@ if st.session_state.game_mode == "vs AI":
|
|
| 224 |
if explanation.get('key_insight'):
|
| 225 |
st.info(f"๐ **Strategic Insight:**\n{explanation['key_insight']}")
|
| 226 |
|
| 227 |
-
st.markdown("
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
with stat_col1:
|
| 231 |
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)
|
| 232 |
-
with
|
| 233 |
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)
|
| 234 |
-
with
|
| 235 |
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)
|
| 236 |
|
| 237 |
stat_col4, stat_col5 = st.columns(2)
|
|
@@ -240,8 +275,10 @@ if st.session_state.game_mode == "vs AI":
|
|
| 240 |
with stat_col5:
|
| 241 |
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)
|
| 242 |
|
| 243 |
-
if not
|
| 244 |
-
st.info("โน๏ธ Using
|
|
|
|
|
|
|
| 245 |
|
| 246 |
else: # Two Player Mode
|
| 247 |
st.subheader("๐ฏ Game Board")
|
|
@@ -319,6 +356,11 @@ else: # Two Player Mode
|
|
| 319 |
|
| 320 |
st.markdown(f"**Score:** {latest_p1['analysis']['score']} points")
|
| 321 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
perf_col1, perf_col2 = st.columns(2)
|
| 323 |
with perf_col1:
|
| 324 |
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)
|
|
@@ -342,6 +384,11 @@ else: # Two Player Mode
|
|
| 342 |
|
| 343 |
st.markdown(f"**Score:** {latest_p2['analysis']['score']} points")
|
| 344 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 345 |
perf_col1, perf_col2 = st.columns(2)
|
| 346 |
with perf_col1:
|
| 347 |
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)
|
|
|
|
| 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 |
|
|
|
|
| 44 |
st.session_state.show_winner_popup = False
|
| 45 |
st.session_state.move_count = 0
|
| 46 |
st.session_state.current_depth = 5
|
| 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:
|
|
|
|
| 95 |
st.session_state.current_player = 1
|
| 96 |
st.session_state.show_winner_popup = False
|
| 97 |
st.session_state.move_count = 0
|
| 98 |
+
st.session_state.last_human_move_analysis = None
|
| 99 |
+
st.session_state.last_ai_move_analysis = None
|
| 100 |
st.rerun()
|
| 101 |
|
| 102 |
with col2:
|
|
|
|
| 152 |
st.session_state.move_count += 1
|
| 153 |
|
| 154 |
ai_analysis = st.session_state.ai.get_best_move()
|
| 155 |
+
st.session_state.last_human_move_analysis = ai_analysis
|
| 156 |
+
|
| 157 |
move_detail = {
|
| 158 |
'player': current_player,
|
| 159 |
'player_name': "Human",
|
|
|
|
| 182 |
explanation = st.session_state.explainer.explain_move(ai_result)
|
| 183 |
explanation_text = explanation.get('explanation', '')
|
| 184 |
else:
|
| 185 |
+
explanation = {
|
| 186 |
+
'explanation': f"Column {ai_result['move']} scores {ai_result['score']} points.",
|
| 187 |
+
'threat_analysis': "; ".join(ai_result['threats']) if ai_result['threats'] else "Strategic move",
|
| 188 |
+
'key_insight': "Strong position",
|
| 189 |
+
'success': False
|
| 190 |
+
}
|
| 191 |
+
explanation_text = explanation['explanation']
|
| 192 |
|
| 193 |
ai_move_detail = {
|
| 194 |
'player': 2,
|
|
|
|
| 198 |
'explanation': explanation_text
|
| 199 |
}
|
| 200 |
st.session_state.move_history_detailed.append(ai_move_detail)
|
| 201 |
+
st.session_state.last_ai_move_analysis = ai_result
|
| 202 |
+
st.session_state.last_explanation = explanation
|
| 203 |
|
| 204 |
if st.session_state.game.check_winner(2):
|
| 205 |
st.session_state.game_over = True
|
|
|
|
| 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 |
+
|
| 239 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 240 |
+
|
| 241 |
+
# AI Move Analysis
|
| 242 |
+
if hasattr(st.session_state, 'last_ai_move_analysis') and st.session_state.last_ai_move_analysis:
|
| 243 |
+
ai_move = st.session_state.last_ai_move_analysis
|
| 244 |
+
explanation = getattr(st.session_state, 'last_explanation', {
|
| 245 |
+
'explanation': f"Column {ai_move['move']} scores {ai_move['score']} points.",
|
| 246 |
+
'threat_analysis': "; ".join(ai_move['threats']) if ai_move['threats'] else "Strategic move",
|
| 247 |
+
'key_insight': "Strong position",
|
| 248 |
+
'success': False
|
| 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'):
|
|
|
|
| 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)
|
|
|
|
| 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")
|
|
|
|
| 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)
|
|
|
|
| 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)
|