Journey to Solving the Traffic Management Problem
What is traffic management in Screeps?
Movement is one of the significant challenges in Screeps. In fact, the official Discord has a dedicated pathfinding channel. Traffic management, a critical aspect of movement control, refers to the efficient control and movement of creeps to prevent congestion and collisions. Without proper management, creeps can get stuck or move slowly, severely hindering their efficiency.
Common methods to manage traffic
The most intuitive solution is swapping and pushing. This means if a creep is blocking another creep’s path, you swap their positions or push the blocking creep out of the way. While effective in many cases, this method can fail in crowded areas, creating edge cases. Advanced techniques to handle these scenarios include prioritizing creeps and recursive shoving, which involves pushing a chain of creeps until one is moved to an empty tile.
What’s different about my solution?
I found the common methods unsatisfactory due to the numerous edge cases they created, especially in extreme situations. Each new edge case required additional handling, complicating the solution. I aimed to develop a general solution to this problem. Through algorithmic research, I discovered that the Bipartite Matching Problem (BMP) could help solve this issue. Essentially, traffic management involves assigning each creep to a specific position for the next tick. By tweaking the Ford-Fulkerson method, I successfully addressed the traffic management problem.
Basic Idea
This solution leverages graph theory. Imagine two sets of vertices: one representing creeps and the other representing room positions. We connect these vertices with edges, indicating a creep’s potential move or stay. There are two types of creeps: those intending to move to an adjacent tile and those without such an intent. Our goal is to maximize the number of fulfilled move intents.
How the method works
We start by connecting each creep to its current position with edges. For each creep with a move intent, we search for augmenting paths in our graph. If a path increases the number of fulfilled intents, we send flow along that path. We use depth-first search (DFS) to explore these paths, scoring each path as follows: +1 if it connects a creep to its intended position, and -1 if it cancels an existing connection. After iterating through all creeps, the results indicate where each creep should be in the next tick.
WARNING: I thought this method ensures that we can maximize the number of creeps reaching their intended positions while adhering to the given constraints. But it turned out that it does not. To ensure that, we have to add while loop to check it’s really an optimal solution. I’m not certain that will be worth it considering it will use more CPU. So I’ll leave the method unchanged for a while.
Further Explanation of the Traffic Management Algorithm with an Example
Note: You can skip this part if it seems too complicated. It’s an optional detailed explanation of the algorithm.
Suppose we have a situation like the picture below. Here, A
, B
, C
, and D
are creeps, and a
, b
, c
, d
, e
, and f
are positions.
- Creep
B
wants to move toa
- Creep
C
wants to move tob
- Creep
D
wants to move toa
- Creep
A
doesn’t care about its movement.
We can represent this situation with the following graph:
- Blue edges indicate current positions
- Red edges indicate intended positions
- Black edges indicate possible positions
Our task is to match each creep to a position, satisfying the following conditions:
- Each creep must be matched to one of the current, intended, or possible positions.
- Each creep must be matched to exactly one position.
- No two creeps can be matched to the same position.
Our goal is to maximize the number of creeps matched to their intended positions.
Here is a desired solution, with green edges representing the solution:
Finding the Solution
Step-by-Step Algorithm:
- Start with Creep
B
(sinceA
has no intended position).-
Delete the blue edge between
B
andb
, then mark it as a black edge. -
Find a path following these rules:
- You must choose a red or black edge when moving from a creep to a position.
- You must choose a blue or green edge when moving from a position to a creep.
- Visit each vertex (either creep or position) only once.
- When choosing a red edge, add +1 to the score.
- When choosing a green edge, subtract -1 from the score.
- If you arrive at a position which don’t have blue nor green edge, return the path.
-
- Finding Paths:
-
Choose the edge from
B
toa
and get a score of +1. -
Next, choose
A
and thenb
. Sinceb
has no blue or green edge, return this path. -
Calculate the total score of the path. If it’s positive, apply the path to the graph; if not, ignore the path and revert the graph. The score here is +1, so we apply the path.
-
- Update the Graph:
-
Convert the edges along the path to green(when it was red before) or blue(when it was blue or black before) edges when it’s from a creep to a position. When it’s from a position to a creep, convert it back to the red(when it was red before) or black(for the rest) edges accordingly.
-
The updated graph shows that
B
is now matched to its intended positiona
.
-
- Repeat for Remaining Creeps:
-
Now, process
C
. Start fromC
tob
, then go toA
. -
At
A
, try different positions (a
,d
,e
) until you find a successful path. In this case,A
toe
works. -
Apply this path, making
C
happy by matching it tob
. -
Process
D
next. Start fromD
to find possible paths. -
If the path results in a negative score, revert and end the process. Here, the path has a score of -1, so we do not apply it.
-
Final Solution:
- Result: Move
Creep A
toe
,B
toa
,C
tob
, andD
stays in place (d
).
Pseudocode
Declare movementMap
Declare visitedCreeps
Function run(room):
For each creep in creepsInRoom:
Match creep to its current coordinate
If creep has intended move, add to creepsWithMovementIntent
For each creep in creepsWithMovementIntent:
If creep is matched to its intended coordinates, continue
Clear visitedCreeps
Remove creep from movementMap
If dfs(creep) > 0, continue
Reassign creep to its current coordinate
For each creep in creepsInRoom:
Move creep towards matched coordinate
Function dfs(creep, score = 0):
Mark creep as visited
For each possible position:
If creep is matched to its intended coordinate, increase score
If position is unoccupied:
If score > 0, match creep to position
Return score
If position is occupied by anotherCreep and anotherCreep is not visited
If dfs(anotherCreep, score) > 0:
match creep to position
return the result of dfs
Return -Infinity
Function getPossiblePositions(creep):
Initialize possiblePositions with current position
If intended coordinate exists:
add to possiblePositions
return possiblePositions
Add valid adjacent positions to possiblePositions
Return possiblePositions
Git repository of my solution
You can read or use my code if you want to try. My screeps traffic manager is available here
Leave a comment