This repository contains the game engine and starter bot for the LightCycles arena in CodeClash.
LightCycles is Tron light-cycles. Each player drives a cycle around a bordered grid, leaving a solid trail behind it. Every tick, all cycles move one cell at the same time. You crash (and are eliminated) if your move takes you into:
- a wall / the grid border,
- any trail — yours or an opponent's,
- the same cell as another cycle that tick (a head-on), or a head-on swap.
The board may also contain static rock obstacles (solid, like walls) scattered symmetrically around the field — route around them.
The last cycle still riding wins. If everyone still alive crashes on the same tick, it's a draw. If the tick cap is reached with more than one survivor, the one holding the most territory (trail cells) wins.
Because moves are simultaneous and the whole grid is visible, doing well is about looking ahead — keeping your own space open while cutting the opponent's down.
LightCycles/
├── engine.py # Game engine — runs games between bots (do not edit for the contest)
├── main.py # Starter bot implementation (EDIT THIS)
└── README.md
- Edit
main.pyto implement your bot logic. - Test locally (bot vs itself):
python engine.py main.py main.py -r 10 -o /tmp/lc_out
- Submit
main.pyto CodeClash.
Your bot must implement one function in main.py:
Return exactly one of "N", "S", "E", "W" each tick — the direction to steer:
| Move | Meaning | Effect on (x, y) |
|---|---|---|
"N" |
up | y − 1 |
"S" |
down | y + 1 |
"E" |
right | x + 1 |
"W" |
left | x − 1 |
Reversing directly back into your own neck (the opposite of your current direction) is ignored — you keep going straight. Likewise, an invalid return value, an exception, or taking longer than the per-tick time limit makes you continue straight.
obs is a plain dict giving you the complete, deterministic game state:
obs = {
"tick": 42, # current tick (starts at 1)
"max_ticks": 2000, # hard cap on game length
"width": 48, # grid width (x in [0, width))
"height": 36, # grid height (y in [0, height))
"you": 0, # your player id
"players": [ # every cycle, including you
{"id": 0, "x": 12, "y": 18, "dir": "E", "alive": True},
{"id": 1, "x": 35, "y": 18, "dir": "W", "alive": True},
],
"grid": [[...], ...], # grid[y][x] = player id (>=0), -1 empty, or -2 rock
}- Origin is the top-left;
xincreases right,yincreases down. grid[y][x]is-1if empty,-2if a rock obstacle, otherwise the id of the player whose trail (or current head) occupies it. Anything off the grid is a wall. Both rocks and trails are solid — moving into either crashes you.- All cycles move simultaneously, so an opponent's head cell this tick becomes their trail next tick — moving into where they are still crashes you.
# 10 games, write replay files to a directory
python engine.py path/to/bot1.py path/to/bot2.py -r 10 -o /tmp/lc_out
# reproduce a specific game with a fixed base seed
python engine.py bot1.py bot2.py -r 1 -s 123Options:
-r, --rounds: number of games (default: 10)-o, --output-dir: directory forsim_*.jsonreplay files (optional)-s, --seed: base RNG seed; game g usesseed + g(default: 0)
The engine prints a FINAL_RESULTS block the tournament parses:
FINAL_RESULTS
Bot_1: 6 games won (player1)
Bot_2: 4 games won (player2)
Draws: 0
Starting positions and directions are seeded (so sims within a round differ), but otherwise the game is fully deterministic given the bots.
- Don't just avoid crashing now — avoid moves that trap you in a small pocket later. Flood-fill the space each candidate move leaves you and prefer the roomiest.
- Use walls and your own trail to wall off territory the opponent can't reach.
- Watch for head-ons: two cycles entering the same cell both die, so you can force trades — good when you have more open space to retreat into than they do.
- The board is finite; late game is about who runs out of room first.
MIT License — see the CodeClash repository for details.