File size: 8,704 Bytes
21afd14 dd4ffd5 10abfd0 dd4ffd5 10abfd0 21afd14 ecbf7a3 21afd14 dd4ffd5 21afd14 10abfd0 21afd14 97f7acf dd4ffd5 21afd14 10abfd0 21afd14 10abfd0 21afd14 dd4ffd5 21afd14 10abfd0 21afd14 dd4ffd5 97f7acf dd4ffd5 21afd14 2d290f9 97f7acf dd4ffd5 2d290f9 10abfd0 21afd14 10abfd0 dd4ffd5 ecbf7a3 dd4ffd5 21afd14 dd4ffd5 21afd14 dd4ffd5 21afd14 dd4ffd5 10abfd0 21afd14 dd4ffd5 21afd14 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 ac1797d dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 10abfd0 dd4ffd5 ecbf7a3 dd4ffd5 10abfd0 21afd14 dd4ffd5 21afd14 dd4ffd5 10abfd0 dd4ffd5 21afd14 dd4ffd5 f7d71d8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | import gradio as gr
import importlib.util
import time
import random
import os
import sys
import shutil
import zipfile
import tempfile
import uuid
# --- MEGA WORLD CLASS (SERVER VERSION) ---
RADAR_ENCODING = {"EMPTY": 0,"WALL": 1,"GOAL": 2,"ICE": 3,"MUD": 4,"DANGER": 5,"CHARGER": 6,"ENEMY": 7}
class MegaWorldEnv:
def __init__(self):
self.start = (1, 1)
# CHANGE 1: Goal moved to absolute top right (19, 19)
self.goal = (19, 19)
self.walls = self._generate_walls()
self.ice = [(5,y) for y in range(5,15)] + [(15,y) for y in range(5,15)]
self.mud = [(x,10) for x in range(2,18)]
self.traps = [(3,3),(8,8),(12,12),(17,17),(9,10),(11,10)]; random.shuffle(self.traps)
self.chargers = [(18,2),(10,10)]
self.enemies = [{"pos":[5,5],"type":"patrol","axis":"x","range":(5,10),"dir":1},{"pos":[15,5],"type":"patrol","axis":"x","range":(12,17),"dir":1},{"pos":[12,12],"type":"hunter", "step": 0}, {"pos":[16,16],"type":"hunter", "step": 0}]
random.shuffle(self.enemies)
def _generate_walls(self):
walls = []
for y in range(20):
if y not in [3, 16]: walls.append((9, y))
for x in range(20):
if x not in [3, 16]: walls.append((x, 9))
walls.extend([(4,4), (4,5), (4,6), (5,4), (6,4), (14,4), (14,5), (14,6), (15,4), (16,4), (4,14), (4,15), (4,16), (5,14), (6,14)])
for i in range(15, 19): walls.append((i, 15))
walls.extend([(14,14), (13,13)])
return walls
# *** ENABLED SHAPED REWARD HERE ***
def shaped_reward(self, old_pos, new_pos):
old_dist = abs(old_pos[0]-self.goal[0]) + abs(old_pos[1]-self.goal[1])
new_dist = abs(new_pos[0]-self.goal[0]) + abs(new_pos[1]-self.goal[1])
return 3.0 * (old_dist - new_dist)
def get_radar(self, pos):
x,y=pos; radar={}
dirs={"up":(x,y+1),"down":(x,y-1),"left":(x-1,y),"right":(x+1,y)}
for d,(nx,ny) in dirs.items():
info="EMPTY"
if not (0<=nx<20 and 0<=ny<20): info="WALL"
elif (nx,ny) in self.walls: info="WALL"
elif (nx,ny)==self.goal: info="GOAL"
elif (nx,ny) in self.ice: info="ICE"
elif (nx,ny) in self.mud: info="MUD"
elif (nx,ny) in self.traps: info="DANGER"
elif (nx,ny) in self.chargers: info="CHARGER"
for e in self.enemies:
if tuple(e["pos"])==(nx,ny): info="ENEMY"
radar[d]=RADAR_ENCODING[info]
return radar
def update_enemies(self, player_pos):
for e in self.enemies:
if e["type"]=="patrol":
e["pos"][0]+=e["dir"]; nx = e["pos"][0]
if nx>=e["range"][1] or nx<=e["range"][0] or (nx, e["pos"][1]) in self.walls:
e["dir"]*=-1; e["pos"][0]+=e["dir"]
else:
path = [(1,0), (1,0), (0,1), (0,1), (-1,0), (-1,0), (0,-1), (0,-1)]
move = path[e["step"] % len(path)]
nx, ny = e["pos"][0] + move[0], e["pos"][1] + move[1]
if (nx, ny) not in self.walls and 0<=nx<20 and 0<=ny<20: e["pos"]=[nx,ny]
e["step"] += 1
def render(self, player_pos, history, battery, score):
# HTML Visualizer
html="<div style='background:#000;padding:10px;border-radius:12px;font-family:monospace'>"
html+=f"<div style='color:white;margin-bottom:5px'>π {battery} | π {score:.1f}</div>"
html+="<div style='display:grid;grid-template-columns:repeat(20,20px);gap:1px;width:fit-content;margin:auto'>"
enemy_pos=[tuple(e["pos"]) for e in self.enemies]
for y in range(19,-1,-1):
for x in range(20):
pos=(x,y); color="#111"; char=""
if pos in self.walls: color="#555"
elif pos in self.ice: color="#29b6f6"
elif pos in self.mud: color="#4e342e"
elif pos in history: color="#263238"
if pos==self.goal: char="π"; color="#4caf50"
if pos in self.chargers: char="β‘"; color="#fdd835"
if pos in enemy_pos: char="πΉ"; color="#d500f9"
if pos==player_pos: char="π€"; color="#2196f3" if battery>20 else "#ff6f00"
html+=f"<div style='width:20px;height:20px;background:{color};display:flex;align-items:center;justify-content:center;font-size:12px'>{char}</div>"
html+="</div></div>"
return html
# ---------------------------------------------------------
# SERVER CONFIG
# ---------------------------------------------------------
FLAG = "CTF{r3w4rd_sh4p1ng_1s_th3_k3y}"
def run_mega_simulation(zip_file):
env = MegaWorldEnv()
if zip_file is None:
yield env.render(env.start, [], 100, 0), {"status": "Waiting for upload..."}
return
# Setup Temp Directory
run_id = str(uuid.uuid4())
temp_dir = os.path.join(tempfile.gettempdir(), "ctf_run_" + run_id)
os.makedirs(temp_dir, exist_ok=True)
try:
# Extract Zip
try:
with zipfile.ZipFile(zip_file.name, 'r') as zip_ref:
zip_ref.extractall(temp_dir)
except Exception as e:
yield env.render(env.start, [], 0, 0), {"error": f"Invalid Zip: {e}"}
return
# Load Agent
agent_path = os.path.join(temp_dir, "agent.py")
if not os.path.exists(agent_path):
yield env.render(env.start, [], 0, 0), {"error": "agent.py not found!"}
return
sys.path.append(temp_dir)
try:
spec = importlib.util.spec_from_file_location("agent_module", agent_path)
agent = importlib.util.module_from_spec(spec)
spec.loader.exec_module(agent)
except Exception as e:
yield env.render(env.start, [], 0, 0), {"error": f"Code Error: {e}"}
return
# Run Simulation
pos = list(env.start)
battery = 100
score = 0
history = []
for step in range(1000):
radar = env.get_radar(pos)
try:
action = agent.get_action(tuple(pos), radar, battery)
if action not in [0, 1, 2, 3]: action = 0
except Exception as e:
yield env.render(tuple(pos), history, 0, score), {"error": f"Runtime Error: {e}"}
break
dx, dy = [(0,1), (0,-1), (-1,0), (1,0)][action]
prev_pos = pos[:]
nx, ny = pos[0]+dx, pos[1]+dy
# Wall Collision
if not (0 <= nx < 20 and 0 <= ny < 20) or (nx, ny) in env.walls:
nx, ny = pos
pos = [nx, ny]
# *** APPLY SHAPED REWARD ***
step_reward = env.shaped_reward(tuple(prev_pos), tuple(pos))
score += step_reward
env.update_enemies(pos)
history.append(tuple(pos))
battery -= 1
if tuple(pos) in env.mud: battery -= 5
# Win
if tuple(pos) == env.goal:
score += 1000
yield env.render(tuple(pos), history, battery, score), {"RESULT": f"VICTORY! {FLAG}"}
break
# Loss
enemy_pos = [tuple(e["pos"]) for e in env.enemies]
if battery <= 0:
yield env.render(tuple(pos), history, 0, score), {"RESULT": "DIED: Battery Empty"}
break
if tuple(pos) in enemy_pos:
yield env.render(tuple(pos), history, 0, score), {"RESULT": "DIED: Caught by Enemy"}
break
if tuple(pos) in env.traps:
battery -= 10
# Render
yield env.render(tuple(pos), history, battery, score), {"step": step}
# CHANGE 2: Slower speed (0.15s delay per frame)
time.sleep(0.15)
finally:
if temp_dir in sys.path:
sys.path.remove(temp_dir)
shutil.rmtree(temp_dir, ignore_errors=True)
with gr.Blocks(theme=gr.themes.Monochrome()) as demo:
gr.Markdown("# π© CTF: The Fortress Run")
gr.Markdown("Upload `solution.zip` to run your agent.")
with gr.Row():
game = gr.HTML(MegaWorldEnv().render((1,1), [], 100, 0))
with gr.Column():
file_input = gr.File(label="Upload Submission (.zip)", file_types=[".zip"])
run_btn = gr.Button("Deploy Agent", variant="primary")
logs = gr.JSON(label="Status")
run_btn.click(run_mega_simulation, file_input, [game, logs])
if __name__ == "__main__":
demo.launch()
# --- TRAINING SCRIPT SNIPPET FOR REFERENCE --- |