Update app.py
Browse files
app.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import random
|
|
|
|
|
|
|
| 3 |
|
| 4 |
# trạng thái game
|
| 5 |
state = {
|
|
@@ -33,7 +35,6 @@ def draw_scene(keeper_pos="giữa", ball_dir="giữa", is_goal=True, reset=False
|
|
| 33 |
"phải": "translate(180px, -50px)"
|
| 34 |
}[ball_dir]
|
| 35 |
|
| 36 |
-
# Chọn màu áo và số cầu thủ
|
| 37 |
if state["turn"] == "A":
|
| 38 |
shirt_color = "#2196F3"
|
| 39 |
pants_color = "#1565C0"
|
|
@@ -49,9 +50,7 @@ def draw_scene(keeper_pos="giữa", ball_dir="giữa", is_goal=True, reset=False
|
|
| 49 |
player_number = "10"
|
| 50 |
player_name = "Messi"
|
| 51 |
|
| 52 |
-
# Nếu có champion, vẽ cầu thủ nâng cúp
|
| 53 |
if champion:
|
| 54 |
-
# màu áo và số theo đội vô địch
|
| 55 |
shirt_color = "#2196F3" if champion=="A" else "#e53935"
|
| 56 |
pants_color = "#1565C0" if champion=="A" else "#b71c1c"
|
| 57 |
player_number = "7" if champion=="A" else "10"
|
|
@@ -59,11 +58,9 @@ def draw_scene(keeper_pos="giữa", ball_dir="giữa", is_goal=True, reset=False
|
|
| 59 |
html = f"""
|
| 60 |
<div style="position:relative;width:100%;height:360px;background:linear-gradient(#4CAF50,#2e7d32);border-radius:20px;overflow:hidden;">
|
| 61 |
<div style="position:absolute;bottom:30px;left:50%;transform:translateX(-50%);text-align:center;">
|
| 62 |
-
<!-- Cúp vàng -->
|
| 63 |
<div style="width:20px;height:60px;background:gold;margin:auto;border-radius:4px 4px 0 0;position:relative;">
|
| 64 |
<div style="width:40px;height:15px;background:gold;position:absolute;top:-15px;left:-10px;border-radius:50%;"></div>
|
| 65 |
</div>
|
| 66 |
-
<!-- Cầu thủ -->
|
| 67 |
<div style="width:18px;height:18px;background:#ffd1a4;border-radius:50%;margin:auto;"></div>
|
| 68 |
<div style="width:30px;height:45px;background:{shirt_color};border-radius:8px;margin:auto;position:relative;">
|
| 69 |
<span style="position:absolute;top:5px;left:50%;transform:translateX(-50%);color:white;font-weight:bold;font-family:sans-serif;font-size:18px;">{player_number}</span>
|
|
@@ -82,16 +79,12 @@ def draw_scene(keeper_pos="giữa", ball_dir="giữa", is_goal=True, reset=False
|
|
| 82 |
"""
|
| 83 |
return html
|
| 84 |
|
| 85 |
-
# Nếu không phải champion, vẽ sân bình thường
|
| 86 |
html = f"""
|
| 87 |
<div style="position:relative;width:100%;height:360px;background:linear-gradient(#4CAF50,#2e7d32);border-radius:20px;overflow:hidden;">
|
| 88 |
-
<!-- Khung thành -->
|
| 89 |
<div style="position:absolute;top:40px;left:15%;width:70%;height:140px;border:6px solid white;border-bottom:none;"></div>
|
| 90 |
-
<!-- Lưới -->
|
| 91 |
<div style="position:absolute;top:40px;left:15%;width:70%;height:140px;
|
| 92 |
background:repeating-linear-gradient(to right, transparent, transparent 10px, rgba(255,255,255,0.2) 12px),
|
| 93 |
repeating-linear-gradient(to bottom, transparent, transparent 10px, rgba(255,255,255,0.2) 12px);"></div>
|
| 94 |
-
<!-- Thủ môn -->
|
| 95 |
<div style="position:absolute;top:120px;left:50%;transform:{keeper_move[keeper_pos]};transition: all 0.4s ease;">
|
| 96 |
<div style="width:18px;height:18px;background:#ffd1a4;border-radius:50%;margin:auto;"></div>
|
| 97 |
<div style="width:30px;height:45px;background:{keeper_shirt};border-radius:8px;margin:auto;"></div>
|
|
@@ -104,7 +97,6 @@ def draw_scene(keeper_pos="giữa", ball_dir="giữa", is_goal=True, reset=False
|
|
| 104 |
<div style="width:8px;height:25px;background:{keeper_pants};"></div>
|
| 105 |
</div>
|
| 106 |
</div>
|
| 107 |
-
<!-- Cầu thủ -->
|
| 108 |
<div style="position:absolute;bottom:30px;left:50%;transform:translateX(-50%);text-align:center;">
|
| 109 |
<div style="width:18px;height:18px;background:#ffd1a4;border-radius:50%;margin:auto;"></div>
|
| 110 |
<div style="width:30px;height:45px;background:{shirt_color};border-radius:8px;margin:auto;position:relative;">
|
|
@@ -119,7 +111,6 @@ def draw_scene(keeper_pos="giữa", ball_dir="giữa", is_goal=True, reset=False
|
|
| 119 |
<div style="width:8px;height:25px;background:{pants_color};"></div>
|
| 120 |
</div>
|
| 121 |
</div>
|
| 122 |
-
<!-- Bóng -->
|
| 123 |
<div style="position:absolute;bottom:90px;left:50%;width:14px;height:14px;background:white;border-radius:50%;
|
| 124 |
animation: shoot 0.7s ease-out forwards;"></div>
|
| 125 |
<style>
|
|
@@ -137,15 +128,6 @@ def shoot_penalty(direction):
|
|
| 137 |
hit = direction != keeper
|
| 138 |
return keeper, hit
|
| 139 |
|
| 140 |
-
def next_turn(direction):
|
| 141 |
-
keeper, hit = shoot_penalty(direction)
|
| 142 |
-
if state["turn"] == "A" and hit: state["score_a"] +=1
|
| 143 |
-
if state["turn"] == "B" and hit: state["score_b"] +=1
|
| 144 |
-
msg = f"Đội {state['turn']} sút {direction}, thủ môn đoán {keeper} → "
|
| 145 |
-
msg += "⚽ Bàn thắng!" if hit else "🧤 Cản phá, bóng dính thủ môn!"
|
| 146 |
-
scene = draw_scene(keeper, direction, hit)
|
| 147 |
-
return msg, scene
|
| 148 |
-
|
| 149 |
def prepare_next_turn():
|
| 150 |
state["turn"] = "B" if state["turn"]=="A" else "A"
|
| 151 |
if state["turn"]=="A": state["round"] +=1
|
|
@@ -171,6 +153,24 @@ def reset_game():
|
|
| 171 |
scene = draw_scene(reset=True)
|
| 172 |
return "**Điểm A: 0 | Điểm B: 0 | Lượt: 1/5**", scene, "Game mới bắt đầu! Đội A sút trước."
|
| 173 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
with gr.Blocks() as demo:
|
| 175 |
gr.Markdown("# ⚽ Penalty – Cầu thủ nâng cúp bằng CSS khi vô địch")
|
| 176 |
|
|
@@ -182,13 +182,11 @@ with gr.Blocks() as demo:
|
|
| 182 |
btn_left = gr.Button("⬅️ Trái")
|
| 183 |
btn_center = gr.Button("⬆️ Giữa")
|
| 184 |
btn_right = gr.Button("➡️ Phải")
|
| 185 |
-
btn_next = gr.Button("➡️ Tiếp theo")
|
| 186 |
btn_restart = gr.Button("🔄 Chơi lại")
|
| 187 |
|
| 188 |
-
btn_left.click(lambda _:
|
| 189 |
-
btn_center.click(lambda _:
|
| 190 |
-
btn_right.click(lambda _:
|
| 191 |
-
btn_next.click(lambda _: prepare_next_turn(), inputs=None, outputs=[score_text, field, result])
|
| 192 |
btn_restart.click(lambda _: reset_game(), inputs=None, outputs=[score_text, field, result])
|
| 193 |
|
| 194 |
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import random
|
| 3 |
+
import threading
|
| 4 |
+
import time
|
| 5 |
|
| 6 |
# trạng thái game
|
| 7 |
state = {
|
|
|
|
| 35 |
"phải": "translate(180px, -50px)"
|
| 36 |
}[ball_dir]
|
| 37 |
|
|
|
|
| 38 |
if state["turn"] == "A":
|
| 39 |
shirt_color = "#2196F3"
|
| 40 |
pants_color = "#1565C0"
|
|
|
|
| 50 |
player_number = "10"
|
| 51 |
player_name = "Messi"
|
| 52 |
|
|
|
|
| 53 |
if champion:
|
|
|
|
| 54 |
shirt_color = "#2196F3" if champion=="A" else "#e53935"
|
| 55 |
pants_color = "#1565C0" if champion=="A" else "#b71c1c"
|
| 56 |
player_number = "7" if champion=="A" else "10"
|
|
|
|
| 58 |
html = f"""
|
| 59 |
<div style="position:relative;width:100%;height:360px;background:linear-gradient(#4CAF50,#2e7d32);border-radius:20px;overflow:hidden;">
|
| 60 |
<div style="position:absolute;bottom:30px;left:50%;transform:translateX(-50%);text-align:center;">
|
|
|
|
| 61 |
<div style="width:20px;height:60px;background:gold;margin:auto;border-radius:4px 4px 0 0;position:relative;">
|
| 62 |
<div style="width:40px;height:15px;background:gold;position:absolute;top:-15px;left:-10px;border-radius:50%;"></div>
|
| 63 |
</div>
|
|
|
|
| 64 |
<div style="width:18px;height:18px;background:#ffd1a4;border-radius:50%;margin:auto;"></div>
|
| 65 |
<div style="width:30px;height:45px;background:{shirt_color};border-radius:8px;margin:auto;position:relative;">
|
| 66 |
<span style="position:absolute;top:5px;left:50%;transform:translateX(-50%);color:white;font-weight:bold;font-family:sans-serif;font-size:18px;">{player_number}</span>
|
|
|
|
| 79 |
"""
|
| 80 |
return html
|
| 81 |
|
|
|
|
| 82 |
html = f"""
|
| 83 |
<div style="position:relative;width:100%;height:360px;background:linear-gradient(#4CAF50,#2e7d32);border-radius:20px;overflow:hidden;">
|
|
|
|
| 84 |
<div style="position:absolute;top:40px;left:15%;width:70%;height:140px;border:6px solid white;border-bottom:none;"></div>
|
|
|
|
| 85 |
<div style="position:absolute;top:40px;left:15%;width:70%;height:140px;
|
| 86 |
background:repeating-linear-gradient(to right, transparent, transparent 10px, rgba(255,255,255,0.2) 12px),
|
| 87 |
repeating-linear-gradient(to bottom, transparent, transparent 10px, rgba(255,255,255,0.2) 12px);"></div>
|
|
|
|
| 88 |
<div style="position:absolute;top:120px;left:50%;transform:{keeper_move[keeper_pos]};transition: all 0.4s ease;">
|
| 89 |
<div style="width:18px;height:18px;background:#ffd1a4;border-radius:50%;margin:auto;"></div>
|
| 90 |
<div style="width:30px;height:45px;background:{keeper_shirt};border-radius:8px;margin:auto;"></div>
|
|
|
|
| 97 |
<div style="width:8px;height:25px;background:{keeper_pants};"></div>
|
| 98 |
</div>
|
| 99 |
</div>
|
|
|
|
| 100 |
<div style="position:absolute;bottom:30px;left:50%;transform:translateX(-50%);text-align:center;">
|
| 101 |
<div style="width:18px;height:18px;background:#ffd1a4;border-radius:50%;margin:auto;"></div>
|
| 102 |
<div style="width:30px;height:45px;background:{shirt_color};border-radius:8px;margin:auto;position:relative;">
|
|
|
|
| 111 |
<div style="width:8px;height:25px;background:{pants_color};"></div>
|
| 112 |
</div>
|
| 113 |
</div>
|
|
|
|
| 114 |
<div style="position:absolute;bottom:90px;left:50%;width:14px;height:14px;background:white;border-radius:50%;
|
| 115 |
animation: shoot 0.7s ease-out forwards;"></div>
|
| 116 |
<style>
|
|
|
|
| 128 |
hit = direction != keeper
|
| 129 |
return keeper, hit
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
def prepare_next_turn():
|
| 132 |
state["turn"] = "B" if state["turn"]=="A" else "A"
|
| 133 |
if state["turn"]=="A": state["round"] +=1
|
|
|
|
| 153 |
scene = draw_scene(reset=True)
|
| 154 |
return "**Điểm A: 0 | Điểm B: 0 | Lượt: 1/5**", scene, "Game mới bắt đầu! Đội A sút trước."
|
| 155 |
|
| 156 |
+
def next_turn_auto(direction):
|
| 157 |
+
keeper, hit = shoot_penalty(direction)
|
| 158 |
+
if state["turn"] == "A" and hit: state["score_a"] +=1
|
| 159 |
+
if state["turn"] == "B" and hit: state["score_b"] +=1
|
| 160 |
+
msg = f"Đội {state['turn']} sút {direction}, thủ môn đoán {keeper} → "
|
| 161 |
+
msg += "⚽ Bàn thắng!" if hit else "🧤 Cản phá, bóng dính thủ môn!"
|
| 162 |
+
scene = draw_scene(keeper, direction, hit)
|
| 163 |
+
|
| 164 |
+
def delayed_next():
|
| 165 |
+
time.sleep(2)
|
| 166 |
+
score_text_val, scene_reset, msg_end = prepare_next_turn()
|
| 167 |
+
score_text.update(score_text_val)
|
| 168 |
+
field.update(scene_reset)
|
| 169 |
+
result.update(msg_end)
|
| 170 |
+
|
| 171 |
+
threading.Thread(target=delayed_next, daemon=True).start()
|
| 172 |
+
return msg, scene
|
| 173 |
+
|
| 174 |
with gr.Blocks() as demo:
|
| 175 |
gr.Markdown("# ⚽ Penalty – Cầu thủ nâng cúp bằng CSS khi vô địch")
|
| 176 |
|
|
|
|
| 182 |
btn_left = gr.Button("⬅️ Trái")
|
| 183 |
btn_center = gr.Button("⬆️ Giữa")
|
| 184 |
btn_right = gr.Button("➡️ Phải")
|
|
|
|
| 185 |
btn_restart = gr.Button("🔄 Chơi lại")
|
| 186 |
|
| 187 |
+
btn_left.click(lambda _: next_turn_auto("trái"), inputs=None, outputs=[result, field])
|
| 188 |
+
btn_center.click(lambda _: next_turn_auto("giữa"), inputs=None, outputs=[result, field])
|
| 189 |
+
btn_right.click(lambda _: next_turn_auto("phải"), inputs=None, outputs=[result, field])
|
|
|
|
| 190 |
btn_restart.click(lambda _: reset_game(), inputs=None, outputs=[score_text, field, result])
|
| 191 |
|
| 192 |
demo.launch()
|