Skip to content

Instantly share code, notes, and snippets.

@ion1
Last active September 20, 2023 03:25
Show Gist options
  • Save ion1/d269c9b293b83d46ea347ef7b1f54649 to your computer and use it in GitHub Desktop.
Save ion1/d269c9b293b83d46ea347ef7b1f54649 to your computer and use it in GitHub Desktop.
Factorio Blueprints
from enum import Enum
from draftsman.blueprintable import Blueprint, BlueprintBook
from draftsman.entity import ElectricPole, Roboport, Radar, Lamp
from draftsman.tile import Tile
def main():
book = base_grid_book()
print(book.to_string())
def base_grid_book():
book = BlueprintBook()
book.label = "Base Grid"
book.icons = ["big-electric-pole", "roboport", "radar"]
for variant in BaseGrid.VARIANTS:
book.blueprints.append(BaseGrid(variant=variant).blueprint)
return book
# https://stackoverflow.com/a/5192374
class classproperty:
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
class BaseGrid:
class Variant:
def __init__(
self,
label_suffix=None,
grid_size_x=8,
grid_size_y=8,
range_x=(0, 9),
range_y=(0, 9),
):
self.label_suffix = label_suffix
self.grid_size_x = grid_size_x
self.grid_size_y = grid_size_y
self.range_x = range_x
self.range_y = range_y
def label(self):
suffix = self.label_suffix
return "Base Grid" + (" " + suffix if suffix is not None else "")
def coordinates(self):
return ((x, y) for y in range(*self.range_y) for x in range(*self.range_x))
def __repr__(self):
return f"{self.__class__.__name__}(label_suffix={self.label_suffix!r}, grid_size_x={self.grid_size_x!r}, grid_size_y={self.grid_size_y!r}, range_x={self.range_x!r}, range_y={self.range_y!r})"
VARIANTS = [
Variant(),
Variant(label_suffix="(Horizontal A)", range_y=(0, 1)),
Variant(label_suffix="(Horizontal B)", range_y=(4, 5)),
Variant(label_suffix="(Horizontal C)", grid_size_y=4, range_y=(2, 3)),
Variant(label_suffix="(Vertical A)", range_x=(0, 1)),
Variant(label_suffix="(Vertical B)", range_x=(4, 5)),
Variant(label_suffix="(Vertical C)", grid_size_x=4, range_x=(2, 3)),
Variant(label_suffix="(Quadrant 1)", range_x=(0, 5), range_y=(0, 5)),
Variant(label_suffix="(Quadrant 2)", range_x=(4, 9), range_y=(0, 5)),
Variant(label_suffix="(Quadrant 3)", range_x=(0, 5), range_y=(4, 9)),
Variant(label_suffix="(Quadrant 4)", range_x=(4, 9), range_y=(4, 9)),
]
def __init__(self, variant=Variant()):
self.blueprint = Blueprint()
scale = 24
self.blueprint.label = variant.label()
self.blueprint.icons = ["big-electric-pole", "roboport", "radar"]
self.blueprint.absolute_snapping = True
self.blueprint.snapping_grid_position = (0, 0)
self.blueprint.snapping_grid_size = (
variant.grid_size_x * scale,
variant.grid_size_y * scale,
)
poles = {}
for x_unscaled, y_unscaled in variant.coordinates():
x = x_unscaled * scale
y = y_unscaled * scale
if self.legal_pole_position(x_unscaled, y_unscaled):
pole = self.add_pole(x, y)
poles[(x_unscaled, y_unscaled)] = pole.id
pole_up = poles.get((x_unscaled, y_unscaled - 1))
pole_left = poles.get((x_unscaled - 1, y_unscaled))
if pole_up:
self.blueprint.add_power_connection(pole_up, pole.id)
if pole_left:
self.blueprint.add_power_connection(pole_left, pole.id)
self.add_small_lamp(x + 2, y)
if self.legal_roboport_position(x_unscaled, y_unscaled):
self.add_roboport(x - 1, y - 4)
if self.legal_radar_position(x_unscaled, y_unscaled):
self.add_radar(x, y + 2)
def to_string(self):
return self.blueprint.to_string()
def legal_pole_position(self, x_unscaled, y_unscaled):
return x_unscaled % 2 == 0 or y_unscaled % 2 == 0
def legal_roboport_position(self, x_unscaled, y_unscaled):
return x_unscaled % 2 == 0 and y_unscaled % 2 == 0
def legal_radar_position(self, x_unscaled, y_unscaled):
return (x_unscaled % 4 == 0 and y_unscaled % 4 == 0) and (
(x_unscaled + y_unscaled) % 8 == 0
)
def add_pole(self, x, y):
pole = ElectricPole(
"big-electric-pole", tile_position=(x, y), id=f"pole_{x}_{y}"
)
self.blueprint.entities.append(pole)
self.add_landfill(x, y, 2, 2)
return pole
def add_roboport(self, x, y):
roboport = Roboport(tile_position=(x, y), id=f"roboport_{x}_{y}")
self.blueprint.entities.append(roboport)
self.add_landfill(x, y, 4, 4)
return roboport
def add_radar(self, x, y):
radar = Radar(tile_position=(x, y), id=f"radar_{x}_{y}")
self.blueprint.entities.append(radar)
self.add_landfill(x, y, 3, 3)
return radar
def add_small_lamp(self, x, y):
lamp = Lamp("small-lamp", tile_position=(x, y), id=f"small_lamp_{x}_{y}")
self.blueprint.entities.append(lamp)
self.add_landfill(x, y, 1, 1)
def add_landfill(self, x, y, w, h):
for y_off in range(h):
for x_off in range(w):
tile = Tile("landfill", position=(x + x_off, y + y_off))
self.blueprint.tiles.append(tile)
if __name__ == "__main__":
main()
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment