Fixed a connection points bug with storage tanks. Added 2 videos for the readme.

This commit is contained in:
Jack Hopkins
2025-02-10 14:26:44 +00:00
parent 005c0dd8ac
commit 372fe383bd
22 changed files with 125 additions and 70 deletions

View File

@@ -13,8 +13,19 @@ both settings that models still lack strong spatial reasoning. In lab-play, we f
exhibit promising short-horizon skills, yet are unable to operate effectively in constrained environments, reflecting limitations in error analysis. In open-play, while LLMs discover automation strategies that improve growth (e.g electric-powered drilling), they fail to achieve complex
automation (e.g electronic-circuit manufacturing).
<img src="docs/assets/videos/claude-500.webp" width="364" height="364" controls></video>
<img src="docs/assets/videos/4o-mini-1500.webp" width="364" height="364" controls></video>
## Table of Contents
- Getting Started
- Environment
- Agents
- Tools
- Repository Structure
- Contribution
- License
## Getting Started
Download the repository and install dependencies:
@@ -64,13 +75,86 @@ To do this, open the Factorio client, navigate to 'Multiplayer' and enter the UD
If you are running multiple servers (via docker compose), you will need to activate each one.
## Data
## Environment
We provide a dataset of 50,000 trajectories of gameplay. These trajectories were generated by running an agent on the server and recording its actions.
The dataset can be downloaded [here]().
## Agents
To generate your own dataset, you should perform an MCTS run by following the instructions [here](environment/src/datasetgen/mcts/readme.md)
### Tools
Agents have access to an API of 27 tools, which can be found at `env/src/tools/agent`.
## Repository
```
factorio-learning-environment/
├── cluster/
│ ├── docker/
│ │ ├── config/
│ │ └── mods/
│ ├── local/
│ │ └── assets/
│ ├── remote/
│ └── scenarios/
│ ├── default_lab_scenario/
│ └── open_world/
├── data/
│ ├── blueprints_to_policies/
│ ├── icons/
│ ├── plans/
│ ├── prompts/
│ ├── recipes/
│ ├── screenshots/
│ └── scripts/
├── docs/
│ ├── assets/
│ │ └── images/
│ └── static/
│ ├── css/
│ └── js/
├── env/
│ ├── src/
│ │ ├── exceptions/
│ │ ├── gym/
│ │ ├── lib/
│ │ ├── models/
│ │ ├── rcon/
│ │ ├── tools/
│ │ └── utils/
│ └── tests
│ ├── actions/
│ ├── benchmarks/
│ ├── blueprints/
│ ├── complex/
│ ├── connect/
│ ├── entities/
│ ├── functional/
│ ├── mcts/
│ └── status/
└── eval/
├── open/
│ ├── auto_curriculum/
│ ├── beam/
│ ├── independent_runs/
│ ├── mcts/
│ ├── model/
│ └── plots
└── tasks
└── supervised_results
```
[//]: # (## Data)
[//]: # ()
[//]: # (We provide a dataset of 50,000 trajectories of gameplay. These trajectories were generated by running an agent on the server and recording its actions.)
[//]: # ()
[//]: # (The dataset can be downloaded [here]&#40;&#41;.)
[//]: # ()
[//]: # (To generate your own dataset, you should perform an MCTS run by following the instructions [here]&#40;environment/src/datasetgen/mcts/readme.md&#41;)
[//]: # (## Evaluate Agent)

View File

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -42,8 +42,6 @@ class RecipeName(enum.Enum):
class PrototypeMetaclass(enum.EnumMeta):
def __repr__(self):
return self
def __getattr__(cls, name):
try:
attr = super().__getattr__(name)
@@ -142,6 +140,7 @@ class Prototype(enum.Enum, metaclass=PrototypeMetaclass):
SulfuricAcid = "sulfuric-acid", None
Uranium235 = "uranium-235", None
Uranium238 = "uranium-238", None
Concrete = "concrete", None
Lubricant = "lubricant", None
AdvancedOilProcessing = "advanced-oil-processing", None # These are recipes, not prototypes.

View File

@@ -12,7 +12,8 @@ global.utils.get_storage_tank_connection_points = function(entity)
-- Note: entity.direction is in Factorio's 8-way direction system (0-7)
-- We need to handle both orientations (0/2 and 1/3)
if entity.direction == 1 or entity.direction == 3 then
game.print("en "..entity.direction)
if entity.direction == 1 or entity.direction == 2 then
-- TopRight/BottomLeft connections
table.insert(connection_points, {x = x + 1, y = y - 2}) -- Top right - Top
table.insert(connection_points, {x = x + 2, y = y - 1}) -- Top right - Right

View File

@@ -626,6 +626,16 @@ function get_entity_direction(entity, direction)
else
return defines.direction.west
end
elseif prototype.type == "storage-tank" then
if direction == 0 then
return defines.direction.north
elseif direction == 1 then
return defines.direction.east -- Only 2 directions
elseif direction == 2 then
return defines.direction.south -- Only 2 directions
else
return defines.direction.west -- Only 2 directions
end
else
return direction
end
@@ -1493,19 +1503,19 @@ global.utils.serialize_entity = function(entity)
serialized.connection_points = {}
-- Assembling machine connection points are similar to chemical plants
if direction == defines.direction.north then
if entity.direction == defines.direction.north then
table.insert(serialized.connection_points,
{x = x, y = y - 2
})
elseif direction == defines.direction.south then
elseif entity.direction == defines.direction.south then
table.insert(serialized.connection_points,
{x = x, y = y + 2
})
elseif direction == defines.direction.east then
elseif entity.direction == defines.direction.east then
table.insert(serialized.connection_points,
{x = x + 2, y
{x = x + 2, y = y
})
elseif direction == defines.direction.west then
elseif entity.direction == defines.direction.west then
table.insert(serialized.connection_points,
{x = x - 2, y = y
})
@@ -1514,6 +1524,7 @@ global.utils.serialize_entity = function(entity)
-- Filter out any invalid connection points
local filtered_connection_points = {}
for _, point in ipairs(serialized.connection_points) do
game.print(serpent.line(point))
if global.utils.is_valid_connection_point(game.surfaces[1], point) then
table.insert(filtered_connection_points, point)
end

View File

@@ -377,14 +377,4 @@ def extend_resource_network(game, existing_belt_end, new_consumers):
return False
return True
```
### 3. Power Grid Expansion
```python
def expand_power_grid(game, power_source, new_machines):
for machine in new_machines:
connection = game.connect_entities(power_source,
machine,
Prototype.SmallElectricPole)
if not connection:
print(f"Failed to connect power to machine at {machine.
```

43
env/src/utils/rcon.py vendored
View File

@@ -121,49 +121,6 @@ def _remove_numerical_keys(dictionary):
pruned = parts
return pruned
# def _lua2python(command, response, *parameters, trace=False, start=0):
# if trace:
# print(command, parameters, response)
# if response:
# if trace:
# print(f"success: {command}")
# end = timer()
#
# if response[0] != '{':
#
# splitted = response.split("\n")[-1]
#
# if "[string" in splitted:
# a, b = splitted.split("[string")
# splitted = a + '[\"' + b.replace('"', '!!')
# # remove trailing ',} '
# splitted = re.sub(r',\s*}\s*$', '', splitted) + "\"]}"
#
# output = lua.decode(splitted)
# else:
# output = lua.decode(response)
#
# ##output = luadata.unserialize(splitted[-1], encoding="utf-8", multival=False)
#
# if trace:
# print("{hbar}\nCOMMAND: {command}\nPARAMETERS: {parameters}\n\n{response}\n\nOUTPUT:{output}"
# .format(hbar="-" * 100, command=command, parameters=parameters, response=response, output=output))
#
# # remove numerical keys
# if isinstance(output, dict) and 'b' in output:
# pruned = _remove_numerical_keys(output['b'])
# output['b'] = pruned
# # Only the last transmission is considered the output - the rest are just messages
# return output, (end - start)
# else:
# if trace:
# print(f"failure: {command} \t")
# end = timer()
#
# try:
# return lua.decode(response), (end - start)
# except Exception as e:
# return None, (end - start)
class LuaConversionError(Exception):
"""Custom exception for Lua conversion errors"""

View File

@@ -12,9 +12,10 @@ def game(instance):
'accumulator': 3,
'steam-engine': 3,
'small-electric-pole': 4,
'assembling-machine-2': 1,
'assembling-machine-2': 2,
'offshore-pump': 1,
'pipe': 100
'pipe': 100,
'storage-tank': 4,
}
instance.speed(10)
instance.reset()
@@ -37,4 +38,18 @@ def test_solar_panel_charge_accumulator(game):
print( f"Connected ass_machine to water {ass_machine.position} with {group}")
def test_assembler_2_connect_to_storage(game):
for direction in [Direction.UP, Direction.LEFT, Direction.RIGHT, Direction.DOWN]:
assembly_pos = Position(x=-37, y=-16.5)
game.move_to(assembly_pos)
ass_machine = game.place_entity(Prototype.AssemblingMachine2, position=assembly_pos, direction=Direction.LEFT)
game.set_entity_recipe(entity=ass_machine, prototype=Prototype.Concrete)
ass_machine = game.rotate_entity(ass_machine, direction)
tank_pos = Position(x=-37, y=-6.5)
game.move_to(tank_pos)
tank = game.place_entity(Prototype.StorageTank, position=tank_pos, direction=direction)
print(f"Placed storage tank at {tank.position}")
game.connect_entities(tank, ass_machine, Prototype.Pipe)
game.instance.reset()

View File

@@ -1,2 +0,0 @@
sudo scp -i factorio.pem server-settings.json ec2-user@ec2-18-133-239-115.eu-west-2.compute.amazonaws.com:/opt/factorio/config/server-settings.json
sudo ssh -i factorio.pem ec2-user@ec2-18-133-239-115.eu-west-2.compute.amazonaws.com 'docker stop factorio; docker start factorio'