[Game Development] Lux AI Challenge – AI Strategy Battle

2.8k words

Overview

The Lux AI Challenge is a competitive AI programming competition where players develop autonomous agents to manage resources, build cities, and outmaneuver opponents in a turn-based environment.

🔗 LUX AI Challenge

As part of a team project, we designed and implemented our own AI strategy to compete against other teams in our class. By refining our approach and optimizing decision-making, we ended up winning every match!

Key Strategy Elements

Strategic Movement of Units

In our AI, one of the key components was optimizing unit movements. To achieve this, we utilized a custom Position class that allowed us to easily calculate the Manhattan distance between units and targets, and find the optimal direction to move toward resources or cities.

component.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
float Position::distanceTo(const Position &pos) const
{
return abs(pos.x - x) + abs(pos.y - y);
}

// Method to find the closest direction to a target position
DIRECTIONS Position::directionTo(const Position &targetPos) const
{
DIRECTIONS closestDirection = DIRECTIONS::CENTER;
float closestDist = distanceTo(targetPos);
for (const DIRECTIONS dir : ALL_DIRECTIONS)
{
const Position newpos = translate(dir, 1);
float dist = targetPos.distanceTo(newpos);
if (dist < closestDist)
{
closestDist = dist;
closestDirection = dir;
}
}
return closestDirection;
}
main.cpp
1
2
auto dir = unit.pos.directionTo(closestCityTile->pos);
actions.push_back(unit.move(dir));

Unit Management and Resource Gathering

One core aspect of our strategy was to manage the workers effectively by directing them to gather resources in the most efficient way. We dynamically selected the closest resource tile and prioritized filling the workers’ cargo spaces before moving them back to the cities.

main.cpp
1
2
3
4
5
6
if (unit.getCargoSpaceLeft() > 0)
{
// Find the closest resource tile and send the worker to mine it
Cell *closestResourceTile = GetClosestResource(unit, resourceTiles, player, ResourceType::wood);
actions.push_back(unit.move(dir));
}

City Building and Worker Creation

Another critical part of our strategy was deciding when to build new workers or cities. This decision was based on the current state of the game, such as the number of available units and the resources researched by the player. For example, when coal or uranium were researched, the AI would prioritize building workers to increase production.

main.cpp
1
2
3
4
if (it->second.citytiles.size() <= player.units.size())
actions.push_back(it->second.citytiles[i].research());
else if (player.units.size() <= 0)
actions.push_back(it->second.citytiles[i].buildWorker());

Day, Dawn, and Night Phases

Our strategy was also influenced by the game’s cycle, where different actions were taken depending on whether it was day, dawn, or night. During the day, we focused on unit actions and city building, while during dawn and night, we prioritized resource gathering and preparation for the next cycle.

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
if (gameState.turn % 40 <= 20)
{
ActOnDay(gameState, actions);
}
else if (gameState.turn % 40 < 30)
{
ActOnDawn(gameState, actions);
}
else
{
ActOnNight(gameState, actions);
}