Get AI summaries of any video or article — Sign up free
Coding Adventure with Kaggle and Lux AI thumbnail

Coding Adventure with Kaggle and Lux AI

sentdex·
5 min read

Based on sentdex's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Start with the provided Lux AI “simple” bot, run local matches, and use replay inspection as the primary debugging loop.

Briefing

Lux AI’s Kaggle 1v1 competition rewards the simplest end condition: finish with more city tiles than the opponent. The core takeaway from this coding run is that a bot can jump from “bottom-tier” to “competitive” with incremental, test-driven improvements—starting from a minimal working submission, then adding just enough strategy to reliably expand city area before nightfall punishes low fuel.

The walkthrough begins by setting up Lux AI locally via Node.js and installing the competition package, then using the provided Python “simple” bot as a baseline. Running “simple vs simple” on a 24x24 map generates replay files that can be inspected through the Lux viewer. That replay-driven loop becomes the development workflow: make a change, run a match, inspect behavior, and iterate—because stepping through full-speed games with logging alone quickly becomes painful.

Early on, the author frames the competitive reality: many teams sit at a single city tile, so building just one additional tile can move a bot from the bottom into a much higher percentile. But strategy code tends to sprawl, so the next move is structural cleanup—extracting repeated logic into helper functions like “get resource tiles,” “get close resource,” and “get close city.” The goal isn’t fewer lines; it’s isolating strategy decisions from mechanics so future expansions don’t turn the agent into an unmaintainable tangle.

With the bot cleaned up, the strategy shifts to expansion. A city tile requires 100 resources, and the bot’s workers carry 100 each, so the author adds logic to build a second city tile adjacent to an existing one. To do that correctly, the code must interpret Lux’s data model: “cities” are collections of “city tiles,” and adjacent tiles belong to the same city. The bot also logs and then derives city tile lists from the city objects, ensuring it can decide when it has enough tiles to stop building.

A major practical bug appears when workers naively navigate onto city tiles: Lux deposits cargo immediately, so a worker can repeatedly cross a city tile, dump resources, then return to refill—stalling the intended build. The fix is to improve movement so workers avoid running over city tiles while heading to a build location. Later, as the bot expands, it adds more workers and more city tiles using global dictionaries that track which worker is assigned to which city tile and which resource tile. It also addresses collisions and “stuck” units by tracking recent positions and applying a random cardinal move when a worker hasn’t changed coordinates over multiple steps.

Finally, the author introduces a simple evaluation harness for hyperparameter tuning: log the number of city tiles at the end of each game (especially watching for runs that end with zero city tiles, which often indicate the city died during night). A small change—searching for build locations within a larger radius—produces a noticeable improvement in average city tiles. The resulting bot scores 656, with clear room to improve via better navigation, smarter fuel management as night approaches, and more robust collision avoidance. The submission process is straightforward: compress to tar.gz and upload to Kaggle, then compare against other teams after matches begin.

Cornell Notes

The competition’s win condition is straightforward: end the game with more city tiles than the opponent. Starting from Lux AI’s provided “simple” bot, the author iteratively adds city expansion while keeping the agent maintainable by extracting repeated mechanics into helper functions. The bot then learns the game’s data model (cities are collections of city tiles) and uses worker cargo rules (100 resources per city tile) to build additional tiles adjacent to existing ones. Practical issues—workers depositing cargo by stepping on city tiles, units colliding or getting stuck, and cities dying before night—are handled with improved movement logic, position tracking, and fuel-based decisions about when to expand. Hyperparameters are tuned by logging final city-tile counts and comparing outcomes across runs.

Why does building just one extra city tile matter so much early in the competition?

Many teams are effectively stuck at a single city tile, matching the behavior of the baseline “simple” bot. In that environment, even a small improvement—reliably reaching two city tiles—can move a bot out of the bottom tier. The author treats this as a practical target: get to “one more tile” first, then iterate toward more expansion and better survival through night.

What’s the key data-model detail needed to manage expansion correctly?

Lux represents “cities” as collections of “city tiles,” and adjacent city tiles are part of the same city. That means the bot can’t just treat a city as a single tile; it must derive the full list of city tiles from the city objects (e.g., using player.cities.values and iterating through each city’s tiles). This derived city-tile list drives decisions like whether to build more tiles and where workers should deposit resources.

Why do naive navigation choices prevent city expansion even when the build logic is correct?

Workers can deposit cargo immediately when they step onto city tiles. If the bot’s movement sends a worker across existing city tiles while trying to reach an empty build location, the worker repeatedly dumps resources and refills, causing a loop that prevents the intended build. The author fixes this by adding movement logic that tries to avoid stepping onto city tiles while traveling to the build location.

How does the bot scale from “one city tile” to “multiple city tiles” without losing control of assignments?

It introduces global dictionaries to track relationships: which worker is assigned to which city tile and which resource tile each worker targets. When searching for resources, it can skip resource tiles already assigned to other workers (at least initially). This prevents chaotic behavior as the bot adds more workers and expands the city footprint, and it supports decisions like building more city tiles based on a worker-to-city-tile ratio.

What mechanism helps when units collide or get stuck?

The bot tracks recent unit positions over steps using a dictionary of “last positions” (stored as a short queue). If a unit’s recent positions show only one unique coordinate pair, it’s treated as stuck. The bot then moves the unit randomly among cardinal directions (north/south/east/west) to break the deadlock. It also uses try/except handling to avoid crashes when build locations are temporarily unavailable.

How does the author tune hyperparameters without building a complex analytics pipeline?

A lightweight approach logs the number of city tiles at the final observation step (e.g., step 359). By comparing runs—such as the effect of searching for empty build locations within a larger radius—the author can see which changes improve average city-tile counts. It also flags “zero city tile” outcomes as likely wasted games caused by city death during night.

Review Questions

  1. What specific Lux mechanic makes workers deposit cargo when they step on city tiles, and how does that interfere with building adjacent tiles?
  2. How does tracking recent unit positions help distinguish a stuck unit from normal movement, and what action is taken afterward?
  3. Why does deriving city tiles from player.cities matter for both expansion decisions and worker assignment?

Key Points

  1. 1

    Start with the provided Lux AI “simple” bot, run local matches, and use replay inspection as the primary debugging loop.

  2. 2

    Keep agent logic maintainable by extracting repeated mechanics (resource discovery, closest-tile selection) into helper functions.

  3. 3

    Build additional city tiles by leveraging the 100-resources-per-city-tile rule and by correctly deriving city tiles from Lux’s city data model.

  4. 4

    Avoid sending workers across existing city tiles while they’re trying to reach a build location, since immediate cargo deposition can create loops that prevent building.

  5. 5

    Scale expansion by tracking worker-to-city-tile and worker-to-resource assignments using global dictionaries, then decide when to expand based on worker-to-city-tile ratios and fuel needs.

  6. 6

    Handle collisions and stuck units by tracking recent positions and applying a corrective move when a unit hasn’t changed coordinates over multiple steps.

  7. 7

    Tune strategy parameters by logging final city-tile counts and comparing outcomes across runs, especially watching for games that end with zero city tiles.

Highlights

The fastest early path to better standings is often mechanical: reliably reaching two city tiles, since many competitors remain at one.
Workers can sabotage expansion by stepping onto city tiles, instantly depositing cargo and causing repeated refill cycles instead of reaching the build location.
A practical collision fix uses short-term position history: if a unit’s last few positions are identical, it’s treated as stuck and nudged with a random cardinal move.
Fuel-based expansion decisions matter: expanding too quickly can leave the city with insufficient fuel to survive the night, wiping out all progress.
Hyperparameter testing can be simple: log final city-tile counts at the last step and compare averages across small changes like build-location search radius.

Topics

  • Lux AI Setup
  • Kaggle Bot Iteration
  • City Tile Expansion
  • Worker Navigation
  • Collision Avoidance

Mentioned

  • AI