-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
70 lines (56 loc) · 2.48 KB
/
Copy pathmain.py
File metadata and controls
70 lines (56 loc) · 2.48 KB
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
#!/usr/bin/env python3
"""
LightCycles Starter Bot
=======================
Edit this file to implement your bot. You must define ONE function:
def get_move(obs: dict) -> str
Return one of: "N" "S" "E" "W" (up / down / right / left).
`obs` is the full, deterministic game state each tick. See README.md for the schema:
obs["tick"], obs["max_ticks"], obs["width"], obs["height"], obs["you"] (your id),
obs["players"] (list of {id, x, y, dir, alive}), and
obs["grid"] (grid[y][x] = player id >=0, -1 empty, or -2 rock; off-grid is a wall).
This starter bot survives by keeping its options open: for each move that doesn't
crash immediately, it flood-fills the open space it could still reach afterward and
takes the direction that leaves the most room. It prefers going straight on ties.
Beat it by looking further ahead, cutting off opponents' space, or forcing head-ons
where you come out ahead.
"""
from __future__ import annotations
DIRS = {"N": (0, -1), "S": (0, 1), "E": (1, 0), "W": (-1, 0)}
OPPOSITE = {"N": "S", "S": "N", "E": "W", "W": "E"}
def get_move(obs: dict) -> str:
W, H = obs["width"], obs["height"]
grid = obs["grid"]
me = next(p for p in obs["players"] if p["id"] == obs["you"])
x, y, cur = me["x"], me["y"], me["dir"]
def free(cx, cy):
return 0 <= cx < W and 0 <= cy < H and grid[cy][cx] == -1
best_dir, best_score = cur, -1
# Consider every direction except reversing into our own neck.
for d in ("N", "S", "E", "W"):
if d == OPPOSITE[cur]:
continue
dx, dy = DIRS[d]
nx, ny = x + dx, y + dy
if not free(nx, ny):
continue # would crash immediately
score = _reachable(grid, W, H, nx, ny)
# Prefer straight-ahead on ties for smoother, less self-trapping paths.
if score > best_score or (score == best_score and d == cur):
best_dir, best_score = d, score
return best_dir
def _reachable(grid, W, H, sx, sy, limit=600):
"""Flood-fill count of empty cells reachable from (sx, sy), treating that start
cell as open. Capped so it stays fast on large boards."""
seen = {(sx, sy)}
stack = [(sx, sy)]
count = 0
while stack and count < limit:
cx, cy = stack.pop()
count += 1
for dx, dy in DIRS.values():
nx, ny = cx + dx, cy + dy
if 0 <= nx < W and 0 <= ny < H and grid[ny][nx] == -1 and (nx, ny) not in seen:
seen.add((nx, ny))
stack.append((nx, ny))
return count