Showing preview only (386K chars total). Download the full file or copy to clipboard to get everything.
Repository: konne88/functorio
Branch: main
Commit: d4d09d0c7ee9
Files: 49
Total size: 363.7 KB
Directory structure:
gitextract_pckfgd1u/
├── .devcontainer/
│ ├── Dockerfile
│ └── devcontainer.json
├── .gitignore
├── .vscode/
│ └── settings.json
├── Fulgora150.lean
├── Functorio/
│ ├── Abbreviations.lean
│ ├── Adapter.lean
│ ├── AdapterTest.lean
│ ├── Ascii.lean
│ ├── AssemblyLine.lean
│ ├── AssemblyLineTest.lean
│ ├── AssemblyStation.lean
│ ├── AssemblyStationTest.lean
│ ├── Blueprint.lean
│ ├── Bus.lean
│ ├── BusTest.lean
│ ├── Cap.lean
│ ├── Column.lean
│ ├── Config.lean
│ ├── Crop.lean
│ ├── Direction.lean
│ ├── Entity.lean
│ ├── Expand.lean
│ ├── ExpandTest.lean
│ ├── Factory.lean
│ ├── Fraction.lean
│ ├── Readme.lean
│ ├── Recipe.lean
│ ├── Row.lean
│ ├── Test.lean
│ └── Util.lean
├── Functorio.lean
├── Gleba300.lean
├── LICENSE
├── Nauvis150.lean
├── README.md
├── RedScience150.lean
├── Rocket.lean
├── Spaceship.lean
├── fulgora-150.sh
├── generate-recipe.py
├── gleba-300.sh
├── lake-manifest.json
├── lakefile.toml
├── lean-toolchain
├── nauvis-150.sh
├── red-science-150.sh
├── rocket.sh
└── spaceship.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/Dockerfile
================================================
# FROM mcr.microsoft.com/devcontainers/universal:2
FROM mcr.microsoft.com/devcontainers/base:ubuntu
RUN apt-get update && apt-get install -y \
ripgrep \
pigz
USER vscode
ENV ELAN_HOME="/home/vscode/.elan"
ENV PATH="${ELAN_HOME}/bin:${PATH}"
RUN sh -c 'curl https://elan.lean-lang.org/elan-init.sh -sSf | sh -s -- -y'
RUN elan self update
RUN elan default leanprover/lean4:stable
RUN lean --version
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"leanprover.lean4"
]
}
}
}
================================================
FILE: .gitignore
================================================
.lake
================================================
FILE: .vscode/settings.json
================================================
{
"cSpell.words": [
"biochamber",
"Bioflux",
"biosulfur",
"electromagnetics",
"Factorio",
"foldl",
"Functorio",
"jellynut",
"nauvis",
"pentapod",
"println",
"repr",
"roboport",
"Roboports",
"Supercapacitor",
"yumako"
],
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
".lake": true
},
}
================================================
FILE: Fulgora150.lean
================================================
import Functorio
instance : Config where
generateBigPoles := true
generateRoboports := true
providerChestCapacity := 3
adapterMinHeight := 3
def makeWater : Ice 120 -> Bus (Water 2400) :=
busAssemblyLine (recipe .iceMelting) 2
def makeLightOil : Water 900 -> HeavyOil 1200 -> Bus (LightOil 900) :=
busAssemblyLine (recipe .heavyOilCracking) 1
def makeHolmiumSolution : Water 960 -> HolmiumOre 192 -> Stone 96 -> Bus (HolmiumSolution 9600) :=
busAssemblyLine (recipe .holmiumSolution) 16
def makeElectrolyte : HeavyOil 2400 -> HolmiumSolution 2400 -> Stone 240 -> Bus (Electrolyte 3600) :=
busAssemblyLine (recipe .electrolyte) 10
def makeHolmiumPlate : HolmiumSolution 4500 -> Bus (Holmium 225) :=
busAssemblyLine (recipe .holmiumPlate) 3
def makeSuperconductor : LightOil 240 -> Holmium 48 -> Copper 48 -> Plastic 48 -> Bus (Superconductor 144) :=
busAssemblyLine (recipe .superconductor) 2
def makeSupercapacitor : Electrolyte 720 -> Holmium 144 -> Superconductor 144 -> GreenCircuit 288 -> Battery 72 -> Bus (Supercapacitor 108) :=
busAssemblyLine (recipe .supercapacitor) 6
def makeAccumulator : Iron 144 -> Battery 360 -> Bus (Accumulator 108) :=
busAssemblyLine { recipe := .accumulator, fabricator := .electromagneticPlant } 6
def makeElectromagneticScience : Electrolyte 2700 -> HolmiumSolution 2700 -> Supercapacitor 108 -> Accumulator 108 -> Bus (ElectromagneticScience 162) :=
busAssemblyLine (recipe .electromagneticSciencePack) 9
def makeRocketFuel : LightOil 200 -> SolidFuel 200 -> Bus (RocketFuel 20) :=
busAssemblyLine (recipe .rocketFuel) 4
def makeRocket : BlueCircuit 20 -> LowDensityStructure 20 -> RocketFuel 20 -> Bus Unit :=
busAssemblyLine (recipe .rocketPart) 1
def fulgora150 := bus do
let ice <- input .ice 120
let stone <- input .stone 336
let holmiumOre <- input .holmiumOre 192
let iron <- input .ironPlate 144
let copper <- input .copperPlate 48
let solidFuel <- input .solidFuel 200
let plastic <- input .plasticBar 48
let lowDensityStruct <- input .lowDensityStructure 20
let greenCircuit <- input .electronicCircuit 288
let blueCircuit <- input .processingUnit 20
let battery <- input .battery 432
let heavyOil <- input .heavyOil 3600
let (stone0, stone1) <- split stone
let (battery0, battery1) <- split battery
let (heavyOil0, heavyOil1) <- split heavyOil
let water <- makeWater ice
let (water0, water1) <- split water
let lightOil <- makeLightOil water0 heavyOil0
let (lightOil0, lightOil1) <- split lightOil
let rocketFuel <- makeRocketFuel lightOil0 solidFuel
let holmiumSolution <- makeHolmiumSolution water1.less holmiumOre stone0
let (holmiumSolution0, holmiumSolution) <- split holmiumSolution
let (holmiumSolution1, holmiumSolution2) <- split holmiumSolution
let electrolyte <- makeElectrolyte heavyOil1 holmiumSolution0 stone1
let (electrolyte0, electrolyte1) <- split electrolyte
let holmiumPlate <- makeHolmiumPlate holmiumSolution1
let (holmiumPlate0, holmiumPlate) <- split holmiumPlate
let (holmiumPlate1, _holmiumExport) <- split holmiumPlate
let superconductor <- makeSuperconductor lightOil.less holmiumPlate0 copper plastic
let supercapacitor <- makeSupercapacitor electrolyte0 holmiumPlate1 superconductor.less greenCircuit battery0
let accumulator <- makeAccumulator iron battery1
let _ <- makeElectromagneticScience electrolyte1.less holmiumSolution2 supercapacitor accumulator.less
makeRocket blueCircuit lowDensityStruct rocketFuel
def main : IO Unit :=
IO.println (fulgora150.toBlueprint) -- (bootstrap := true))
================================================
FILE: Functorio/Abbreviations.lean
================================================
import Functorio.Bus
abbrev Coal := BusLane .coal
abbrev Stone := BusLane .stone
abbrev CopperOre := BusLane .copperOre
abbrev IronOre := BusLane .ironOre
abbrev Copper := BusLane .copperPlate
abbrev Iron := BusLane .ironPlate
abbrev Steel := BusLane .steelPlate
abbrev Brick := BusLane .stoneBrick
abbrev Water := BusLane .water
abbrev CrudeOil := BusLane .crudeOil
abbrev LightOil := BusLane .lightOil
abbrev HeavyOil := BusLane .heavyOil
abbrev Lubricant := BusLane .lubricant
abbrev Acid := BusLane .sulfuricAcid
abbrev Petrolium := BusLane .petroleumGas
abbrev Cable := BusLane .copperCable
abbrev Plastic := BusLane .plasticBar
abbrev GreenCircuit := BusLane .electronicCircuit
abbrev RedCircuit := BusLane .advancedCircuit
abbrev BlueCircuit := BusLane .processingUnit
abbrev Gear := BusLane .ironGearWheel
abbrev RobotFrame := BusLane .flyingRobotFrame
abbrev LowDensityStructure := BusLane .lowDensityStructure
abbrev ElectricEngine := BusLane .electricEngineUnit
abbrev Battery := BusLane .battery
abbrev Engine := BusLane .engineUnit
abbrev IronStick := BusLane .ironStick
abbrev Sulfur := BusLane .sulfur
abbrev RocketFuel := BusLane .rocketFuel
abbrev Pipe := BusLane .pipe
abbrev Inserter := BusLane .inserter
abbrev YellowBelt := BusLane .transportBelt
abbrev Furnace := BusLane .electricFurnace
abbrev Rail := BusLane .rail
abbrev ProdModule := BusLane .productivityModule
abbrev Wall := BusLane .stoneWall
abbrev YellowAmmo := BusLane .firearmMagazine
abbrev RedAmmo := BusLane .piercingRoundsMagazine
abbrev Grenade := BusLane .grenade
abbrev RedScience := BusLane .automationSciencePack
abbrev GreenScience := BusLane .logisticSciencePack
abbrev BlackScience := BusLane .militarySciencePack
abbrev BlueScience := BusLane .chemicalSciencePack
abbrev PurpleScience := BusLane .productionSciencePack
abbrev YellowScience := BusLane .utilitySciencePack
abbrev HotFluoroketone := BusLane .fluoroketoneHot
abbrev ColdFluoroketone := BusLane .fluoroketoneCold
abbrev Ice := BusLane .ice
abbrev LithiumPlate := BusLane .lithiumPlate
abbrev Lithium := BusLane .lithium
abbrev LithiumBrine := BusLane .lithiumBrine
abbrev Fluorine := BusLane .fluorine
abbrev SolidFuel := BusLane .solidFuel
abbrev Ammonia := BusLane .ammonia
abbrev IcePlatform := BusLane .icePlatform
abbrev AmmoniacalSolution := BusLane .ammoniacalSolution
abbrev CryogenicScience := BusLane .cryogenicSciencePack
abbrev Electrolyte := BusLane .electrolyte
abbrev HolmiumOre := BusLane .holmiumOre
abbrev Holmium := BusLane .holmiumPlate
abbrev HolmiumSolution := BusLane .holmiumSolution
abbrev Superconductor := BusLane .superconductor
abbrev Supercapacitor := BusLane .supercapacitor
abbrev Accumulator := BusLane .accumulator
abbrev ElectromagneticScience := BusLane .electromagneticSciencePack
abbrev Carbon := BusLane .carbon
abbrev Calcite := BusLane .calcite
abbrev ThrusterFuel := BusLane .thrusterFuel
abbrev ThrusterOxidizer := BusLane .thrusterOxidizer
abbrev MoltenIron := BusLane .moltenIron
abbrev MoltenCopper:= BusLane .moltenCopper
abbrev Explosives := BusLane .explosives
abbrev Rocket := BusLane .rocket
abbrev RailgunAmmo := BusLane .railgunAmmo
abbrev AgriculturalScience := BusLane .agriculturalSciencePack
abbrev Bioflux := BusLane .bioflux
abbrev PentapodEgg := BusLane .pentapodEgg
abbrev Jelly := BusLane .jelly
abbrev Spoilage := BusLane .spoilage
abbrev YumakoMash := BusLane .yumakoMash
abbrev IronBacteria := BusLane .ironBacteria
abbrev CopperBacteria := BusLane .copperBacteria
abbrev Nutrients := BusLane .nutrients
abbrev Jellynut := BusLane .jellynut
abbrev JellynutSeed := BusLane .jellynutSeed
abbrev Yumako := BusLane .yumako
abbrev YumakoSeed := BusLane .yumakoSeed
================================================
FILE: Functorio/Adapter.lean
================================================
import Functorio.Factory
import Functorio.Util
private inductive Orientation where
| vertical
| horizontalTopLtBot
| horizontalTopGtBot
| turnNE
| turnES
| turnSW
| turnWN
private def connector (x y : Nat) (isLiquid:Bool) (dir:DirectionV) (orient:Orientation) : Entity :=
if isLiquid then pipe x y
else belt x y (
match dir, orient with
| .N, .vertical => .N
| .N, .horizontalTopLtBot => .W
| .N, .horizontalTopGtBot => .E
| .N, .turnNE => .N
| .N, .turnES => .E
| .N, .turnSW => .W
| .N, .turnWN => .N
| .S, .vertical => .S
| .S, .horizontalTopLtBot => .E
| .S, .horizontalTopGtBot => .W
| .S, .turnNE => .E
| .S, .turnES => .S
| .S, .turnSW => .S
| .S, .turnWN => .W
)
private def singleConnection (isLiquid:Bool) (dir:DirectionV) (topOffset botOffset : InterfaceImpl) (height crossingY : Nat) : Array Entity :=
if height == 0 then #[] else
let lowX := min topOffset botOffset
let highX := max topOffset botOffset
let diff := highX - lowX - 1
let vertical := (List.range height).flatMap fun y =>
if (y < crossingY) then [connector topOffset y isLiquid dir .vertical] else
if (y > crossingY) then [connector botOffset y isLiquid dir .vertical] else
[]
let horizontal :=
if topOffset < botOffset then
(List.range' (topOffset + 1) diff).map fun x => connector x crossingY isLiquid dir .horizontalTopLtBot else
if topOffset > botOffset then
(List.range' (botOffset + 1) diff).map fun x => connector x crossingY isLiquid dir .horizontalTopGtBot
else
[connector topOffset crossingY isLiquid dir Orientation.vertical]
let turns :=
if topOffset < botOffset then
[
connector topOffset crossingY isLiquid dir .turnNE,
connector botOffset crossingY isLiquid dir .turnSW
] else
if topOffset > botOffset then
[
connector topOffset crossingY isLiquid dir .turnWN,
connector botOffset crossingY isLiquid dir .turnES
]
else
[]
(vertical ++ horizontal ++ turns).toArray
def createAdapter (interfaces:List InterfaceV) (top bot:List InterfaceImpl) (height:Nat) : List Entity × Nat := Id.run do
let mut topUsed := 0 -- Amount of space already used at the top by the adapter.
let mut botUsed := 0 -- Amount of space already used at the bottom by the adapter.
let mut neededHeight := 0
let mut entities : Array Entity := #[]
for ((ingredient, direction), i) in interfaces.zipIdx do
let isLiquid := ingredient.isLiquid
let topOffset := top[i]!
let botOffset := bot[i]!
-- Adjacent pipes need some padding between them
let paddingNeeded := isLiquid && i+1 < interfaces.length && interfaces[i+1]!.fst.isLiquid
-- If the interfaces line up, connect them directly
if (topOffset == botOffset) then
entities := entities ++ singleConnection isLiquid direction topOffset botOffset height 0
topUsed := 0
botUsed := 0
-- If the top interface is to the left of the bottom interface, build connector along the bottom.
else if (topOffset < botOffset) then
entities := entities ++ singleConnection isLiquid direction topOffset botOffset height (height - botUsed - 1)
topUsed := 0
botUsed := botUsed + (if paddingNeeded then 2 else 1)
-- If the bottom interface is to the left of the top interface, build connector along the top.
else
entities := entities ++ singleConnection isLiquid direction topOffset botOffset height topUsed
topUsed := topUsed + (if paddingNeeded then 2 else 1)
botUsed := 0
neededHeight := max (max topUsed botUsed) neededHeight
(entities.toList, neededHeight)
def adapterV {interface}
(top:Vector InterfaceImpl interface.length)
(bot:Vector InterfaceImpl interface.length)
(width := (top ++ bot).toList.max?.getD 0)
(minHeight := 0)
: Factory interface [] interface []
:=
let initialHeight := (interface.map fun (ingredient,_) => if ingredient.isLiquid then 2 else 1).sum
let (_,neededHeight) := createAdapter interface top.toList bot.toList initialHeight
let height := max neededHeight minHeight
let (entities,_) := createAdapter interface top.toList bot.toList height
{
width := width
height:= height
entities:= entities
wires := []
interface := {
n := top
e := Array.toVector #[]
s := bot
w := Array.toVector #[]
}
name := "adapter"
}
================================================
FILE: Functorio/AdapterTest.lean
================================================
import Functorio.Adapter
import Functorio.Ascii
#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[0,1] #v[0,1]).toAscii == s!"
^^
^^
"
#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[0,1] #v[2,3]).toAscii == s!"
^^
↑↑←←
↑←←↑
^^
"
#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[2,3] #v[0,1]).toAscii == s!"
^^
→→↑↑
↑→→↑
^^
"
#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[0,1] #v[0,3]).toAscii == s!"
^^
↑↑←←
^ ^
"
#guard (adapterV (interface:=[(.water,.N), (.lubricant,.N), (.crudeOil,.N)]) #v[0,2,4] #v[5,8,10]).toAscii == s!"
^ ^ ^
| | |||||||
| | |
| ||||||| |
| | |
|||||| | |
^ ^ ^
"
#guard (adapterV (interface:=[(.water,.N), (.ironOre,.N), (.crudeOil,.N)]) #v[0,2,4] #v[5,8,10]).toAscii == s!"
^ ^ ^
| ↑ |||||||
| ↑←←←←←← |
|||||| ↑ |
^ ^ ^
"
================================================
FILE: Functorio/Ascii.lean
================================================
import Functorio.Entity
import Functorio.Factory
/-
Legend:
↑ belt
⤒ belt going under-ground
↥ belt going above-ground
| pipe (no directionality)
┴ pipe going underground (above-ground on the top, under-ground on the bottom)
⇨ inserter (moving items from left to right)
↠ long inserter
⚡ power pole
↯ big power pole
A assembly machine
C chemical plant
R refinery
F furnace
^ interface direction (pointing north)
* building
! overlapping entities
-/
private def entitySymbol (e:Entity) : Option Char :=
match e.type with
| .belt .N _ => '↑'
| .belt .E _ => '→'
| .belt .S _ => '↓'
| .belt .W _ => '←'
| .beltDown .N => '⤒'
| .beltDown .E => '⇥'
| .beltDown .S => '⤓'
| .beltDown .W => '⇤'
| .beltUp .N => '↥'
| .beltUp .E => '↦'
| .beltUp .S => '↧'
| .beltUp .W => '↤'
| .pipe => '|'
| .pipeToGround .N => '┴'
| .pipeToGround .E => '├'
| .pipeToGround .S => '┬'
| .pipeToGround .W => '┤'
| .inserter .N _ => '⇩'
| .inserter .E _ => '⇦'
| .inserter .S _ => '⇧'
| .inserter .W _ => '⇨'
| .longInserter .N _ => '↡'
| .longInserter .E _ => '↞'
| .longInserter .S _ => '↟'
| .longInserter .W _ => '↠'
| .pole => '⚡'
| .bigPole => '↯'
| .splitter _ _ _ => 'S'
| .fabricator .assemblingMachine3 _ _ _ => 'A'
| .fabricator .electricFurnace _ _ _ => 'F'
| .fabricator .stoneFurnace _ _ _ => 'F'
| .fabricator .steelFurnace _ _ _ => 'F'
| .fabricator .electromagneticPlant _ _ _ => 'E'
| .fabricator .biochamber _ _ _ => 'B'
| .fabricator .chemicalPlant _ _ _ => 'C'
| .fabricator .oilRefinery _ _ _ => 'O'
| .fabricator .rocketSilo _ _ _ => 'L' -- L is for Launch-site
| .deciderCombinator _ _ _ => '≥'
| .arithmeticCombinator _ _ => '+'
| .heatingTower => 'H'
| .roboport => 'R'
| .pump _ => 'P'
| .passiveProviderChest _ => '🄿'
| .ironChest => '☐'
| .refinedConcrete => .none
| _ =>
dbg_trace s!"Couldn't print {reprStr e}"
'?'
private def set {w h} (v:Vector (Vector Char w) h) (x y:Nat) (c:Char) : Vector (Vector Char w) h :=
-- Mark overlapping entities with !
v.modify y fun inner => inner.modify x fun element => if element == ' ' then c else '!'
private def dirSymbol (dir:Direction) : Char :=
match dir with
| .N => '^'
| .E => '>'
| .S => 'v'
| .W => 'w'
namespace Factory
def toAscii {n e s w} (f:Factory n e s w) : String := Id.run do
let mut data := Vector.replicate (f.height + 2) (Vector.replicate (f.width + 2) ' ')
-- draw entities
for entity in f.entities do
match entitySymbol entity with
| .none => pure ()
| .some symbol =>
-- draw entity symbol and a box if it's a big entity
for dx in List.range (entity.width) do
for dy in List.range (entity.height) do
let widerSymbol :=
if dx == entity.width/2 && dy == entity.height/2
then symbol
else '*'
-- +1 so we have space for interface indicators
data := set data (entity.x + dx + 1) (entity.y + dy + 1) widerSymbol
-- draw interfaces
for (offset,i) in f.interface.n.zipIdx do
data := set data (offset + 1) 0 (dirSymbol n[i]!.snd)
for (offset,i) in f.interface.e.zipIdx do
data := set data (f.width + 1) (offset + 1) (dirSymbol e[i]!.snd)
for (offset,i) in f.interface.s.zipIdx do
data := set data (offset + 1) (f.height+1) (dirSymbol s[i]!.snd)
for (offset,i) in f.interface.w.zipIdx do
data := set data 0 (offset + 1) (dirSymbol w[i]!.snd)
return "\n" ++ String.intercalate "\n" (data.toList.map fun inner => (String.mk inner.toList).trimRight) ++ "\n"
end Factory
================================================
FILE: Functorio/AssemblyLine.lean
================================================
import Functorio.Entity
import Functorio.Factory
import Functorio.Crop
import Functorio.Recipe
import Functorio.Row
import Functorio.Bus
import Functorio.Cap
import Functorio.Fraction
import Functorio.Util
import Functorio.Config
import Functorio.AssemblyStation
-- Item's per minute
@[simp]
def inputThroughput (process:Process) (stations:Fraction) (items:Fraction) : Fraction :=
stations * items * process.fabricator.speedup * 60 / process.getRecipe.time
@[simp]
def outputThroughput (process:Process) (stations:Fraction) (ingredient:Ingredient) (items:Fraction) : Fraction :=
let t := stations * items * process.fabricator.speedup * (1 + process.fabricator.productivity) * 60 / process.getRecipe.time
if ingredient.isLiquid then t else min expressBeltThroughput t
def expressBeltHalfThroughput := expressBeltThroughput / 2
def findGap (offsets : Array Nat) (minGap:Nat) := Id.run do
let mut leftBound := 0
for offset in offsets do
let gap := offset - leftBound
if gap >= minGap then
return (leftBound, gap)
leftBound := offset + 1
error! s!"Couldn't find a gap of {minGap}"
def bigPoleInsert [config:Config] {interface} (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=
if !config.generateBigPoles then emptyFactoryH offsets else
let factory := (emptyFactoryH offsets).expand .S 2
let (gapStart, gapWidth) := (findGap offsets.toArray 2)
{
factory with
entities := factory.entities ++ [bigPole (gapStart + (gapWidth - 2) / 2) 0]
}
def roboportInsert [config:Config] {interface} (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=
if !config.generateRoboports then emptyFactoryH offsets else
let factory := (emptyFactoryH offsets).expand .S 4
let (gapStart, gapWidth) := (findGap offsets.toArray 2)
{
factory with
entities :=
factory.entities ++
[roboport (gapStart + (gapWidth - 4) / 2) 0] ++
if gapWidth > 4 then [pole (gapStart + gapWidth - 1) 3] else []
}
def providerChestInsert [config:Config] {interface} (process:Process) (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=
let recipe := process.getRecipe
if config.providerChestCapacity == 0 || recipe.outputs.isEmpty || recipe.outputs[0]!.snd.isLiquid then emptyFactoryH offsets else
let outputOffset :=
(offsets.zipIdx.filter fun (_, i) => interface[i]!.snd == .S)[0]!.fst
let chest := [
passiveProviderChest (outputOffset - 2) 1 (capacity:=config.providerChestCapacity),
inserter (outputOffset - 1) 1 .E
]
let factory := (emptyFactoryH offsets).expand .S 3
{
factory with
entities := (eraseRectangle (outputOffset - 1) 0 1 3 factory.entities) ++ chest ++
if ((recipe.inputs ++ recipe.outputs).filter (fun input => !input.snd.isLiquid)).length <= 2 then [pole (outputOffset - 3) 1]
else [pole (outputOffset - 4) 1,
beltUp (outputOffset - 1) 0 .N,
beltDown (outputOffset - 1) 2 .N]
}
def outputBalancerInsert {interface} (offsets : Vector InterfaceImpl interface.length) : Factory interface [] interface [] :=
let factory := (emptyFactoryH offsets).expand .S 4
let (x,_) := findGap offsets.toArray 5
let balancer : List Entity := [
belt (x+2) 0 .S,
belt (x+3) 0 .W,
splitter (x+4) 0 .W (outputPriority := "right"),
belt x 1 .S,
belt (x+1) 1 .W,
belt (x+2) 1 .W,
belt (x+3) 1 .S,
belt x 2 .S,
belt (x+2) 2 .N,
belt (x+3) 2 .W,
belt (x+4) 2 .E,
belt x 3 .E,
beltDown (x+1) 3 .E,
pole (x+2) 3,
beltUp (x+3) 3 .E,
belt (x+4) 3 .N,
]
let outputOffset :=
(offsets.zipIdx.filter fun (_, i) => interface[i]!.snd == .S)[0]!.fst
let adapter :=
let x := outputOffset
[belt x 0 .S, belt x 1 .W, belt x 2 .S, belt x 3 .S]
let avoider :=
let x := outputOffset - 1
[beltUp x 0 .N, belt x 1 .W, belt x 2 .E, beltDown x 3 .N]
let connector :=
match outputOffset - x with
| 5 => adapter
| 6 => adapter ++ avoider
| _ => error! s!"Couldn't generate outputBalancerInsert for {reprStr interface}, x:= {x}, outputOffset := {outputOffset}"
{
factory with
entities := (eraseRectangle (outputOffset - 1) 0 2 4 factory.entities) ++
balancer ++ connector
}
def maxRoboportLogisticsDistance := 46
def assemblyLineNoReturnedInputs [Config] (process:Process) (stations:Nat) : Factory (stationInterface process) [] (stationInterface process) [] :=
Id.run do
let output := process.getRecipe.outputs[0]!
let stationOutput := inputThroughput process 1 output.fst
let station := station process
let mut factories : Array (Factory (stationInterface process) [] (stationInterface process) []) := #[
bigPoleInsert station.interface.s,
providerChestInsert process station.interface.s,
roboportInsert station.interface.s
]
let mut outputSinceBalance : Fraction := 0
let mut distanceFromRoboport : Nat := 0
for _ in List.range stations do
if !output.snd.isLiquid && outputSinceBalance + stationOutput > expressBeltHalfThroughput && outputSinceBalance != 0 then
factories := factories.push (outputBalancerInsert station.interface.s)
outputSinceBalance := 0
distanceFromRoboport := distanceFromRoboport + 4
if distanceFromRoboport + station.height > maxRoboportLogisticsDistance then
factories := factories.push (roboportInsert station.interface.s)
distanceFromRoboport := 0
factories := factories.push station
outputSinceBalance := outputSinceBalance + stationOutput
distanceFromRoboport := distanceFromRoboport + station.height
columnList factories.toList.reverse
def assemblyLineInterface (process:Process) : List InterfaceV :=
process.inputIngredients.map (., .N) ++
process.outputIngredients.map (., .S) ++
process.returnedInputs.map fun (_,ingredient) => (ingredient, .S)
namespace List
def lastIdxOf {A} [BEq A] (l:List A) (a:A) : Nat := Id.run do
let mut index := l.length
for (a', i) in l.zipIdx do
if a == a' then
index := i
return index
end List
def filterInterfaceN {n e s w} (factory:Factory n e s w) (n':List InterfaceV) : Factory n' e s w :=
{
factory with
interface := {
n := n'.toVector.map fun interface => factory.interface.n[n.lastIdxOf interface]!
e := factory.interface.e
s := factory.interface.s
w := factory.interface.w
}
}
def connectorInterface (process:Process) : List InterfaceV :=
(process.returnedInputs.reverse.map fun (_,ingredient) => (ingredient, .N)) ++
(process.returnedInputs.map fun (_,ingredient) => (ingredient, .S))
def returnedInputsConnector (process:Process) : Factory [] [] (connectorInterface process) [] :=
let n := process.returnedInputs.length
let leftEntities : List Entity :=
(List.range n).flatMap fun x =>
(List.range n).map fun y =>
belt x y (if x < y then .N else .E)
let rightEntities : List Entity :=
(List.range n).flatMap fun x =>
(List.range n).map fun y =>
belt (x+n) y (if (n-x-1) <= y then .S else .E)
{
entities := leftEntities ++ rightEntities
interface := {
n := #v[]
e := #v[]
s := Vector.range (connectorInterface process).length
w := #v[]
}
width := 2*n
height := n
wires := []
name := s!"returnedInputsConnector {reprStr process.returnedInputs}"
}
def connectReturnedInputs (process:Process) (factory:Factory (assemblyLineInterface process) [] (assemblyLineInterface process) []) :Factory [] [] (assemblyLineInterface process) [] :=
if process.returnedInputs.isEmpty then capN factory else
let filteredFactory := (filterInterfaceN factory (connectorInterface process)).expand .N 1
column (returnedInputsConnector process) filteredFactory
def assemblyLine [Config] (process:Process) (stations:Nat) : Factory [] [] (assemblyLineInterface process) [] :=
let line := assemblyLineNoReturnedInputs process stations
let returns := emptyFactoryH
let factory := row line returns
connectReturnedInputs process factory
def tupleType {T} (ts:List T) (type:T->Type) : Type :=
match ts with
| [] => Unit
| [t] => type t
| t::types => type t × tupleType types type
def tuple {T} {ts:List T} {type:T->Type} (value : (t:T) -> Nat -> type t) (index:=0) : tupleType ts type :=
match ts with
| [] => ()
| [t] => value t index
| t::_::_ => (value t index, tuple value (index + 1))
@[simp]
def BusAssemblyLineReturn (process:Process) (stations:Fraction) : Type :=
let outputs := (process.getRecipe.outputs.map fun (items, ingredient) => (outputThroughput process stations ingredient items, ingredient)) ++ process.returnedInputs
Bus (tupleType outputs fun (throughput, ingredient) => BusLane ingredient throughput)
@[simp]
def BusAssemblyLineType (process:Process) (stations:Fraction) (remainingInputs: List (Fraction × Ingredient) := process.getRecipe.inputs): Type :=
match remainingInputs with
| [] => BusAssemblyLineReturn process stations
| (items,ingredient)::inputs =>
let throughput :=
inputThroughput process stations items +
List.sum (process.returnedInputs.map fun (throughput, ingredient') => if ingredient' == ingredient then throughput else 0)
BusLane ingredient throughput -> BusAssemblyLineType process stations inputs
def processBusAssemblyLineArguments
(process:Process)
(stations:Fraction)
(processor: List BusLane' -> BusAssemblyLineReturn process stations)
(remainingInputs: List (Fraction × Ingredient) := process.getRecipe.inputs)
(args: List BusLane' := [])
: BusAssemblyLineType process stations remainingInputs := by
revert args
refine (match remainingInputs with
| [] => processor
| _::inputs => fun args arg =>
processBusAssemblyLineArguments process stations processor inputs (args ++ [arg.toBusLane'])
)
def busAssemblyLine [config:Config] (process: Process) (stations:Fraction) : BusAssemblyLineType process stations :=
processBusAssemblyLineArguments process stations fun inputs => do
let factory := assemblyLine process stations.roundUp
let namedFactory := factory.setName s!"{stations}x{reprStr process.recipe}"
let indexes <- busTapGeneric
inputs
((process.getRecipe.outputs ++ process.returnedInputs).map Prod.snd)
(unsafeFactoryCast namedFactory)
(adapterMinHeight := config.adapterMinHeight)
return tuple (fun (_, _) i => {index:=indexes[i]!})
================================================
FILE: Functorio/AssemblyLineTest.lean
================================================
import Functorio.AssemblyLine
import Functorio.Ascii
#guard (assemblyLine (recipe .advancedCircuit) 3).toAscii == s!"
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑⚡***⚡↑↓
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑⚡***⚡↑↓
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑⚡***⚡↑↓
^^ ^v
"
#guard (bus do
let acid <- input .sulfuricAcid (75/2)
let green <- input .electronicCircuit 150
let red <- input .advancedCircuit 15
let _ <- busAssemblyLine (recipe .processingUnit) 1 acid.exact green red
).toAscii == s!"
| ↑⇨***⇦↑↓
|┤↑├*A*↠↑↓
| ↑⚡***⚡↑↓
|→↑ ↑↓
|↑→→→→→→↑↓
|↑↑↓←←←←←←
|↑↑↓
>||↑↑→→→→→→→>
>→→↑↑
>→→→↑
"
#guard (bus do
let copper <- input .copperPlate 750
let _ <- busAssemblyLine (recipe .copperCable) 5 copper
).toAscii == s!"
↑⇨***⇨↓
↑ *A* ↓
↑⚡***⚡↓
↑ ↓←*↓
↑↓←←↓S←
↑↓ ↑←→↓
↑→⇥⚡↦↑↓
↑⇨***⇨↓
↑ *A* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *A* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *A* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *A* ↓
↑⚡***⚡↓
↑←↓←←←←
↑↓
>→↑→→→→→>
"
#guard (bus do
let _ <- input .copperPlate 4
let water <- input .water 600
let crude <- input .crudeOil 1200
let _ <- busAssemblyLine (recipe .advancedOilProcessing) 1 water crude
).toAscii == s!"
| | *****|| | |
|┤|├***** | | |
| |⚡**O**┤|├| |
| ||***** | | |
| | *****┤| |├|
| | ||||||| | |
| | | | |
| | | ||||||| |
| | | | |
| | | | |||||
| | | | |
>⇥| | | |↦→⇥|↦→→→→>
>|| | | | ||||||>
| | |
>|||| |┤|├||||||||>
|
||||||||||>
"
#guard (bus do
let nutrients <- input .nutrients 15
let jellynut <- input .jellynut 120
let _ <- busAssemblyLine (recipe .jellynutProcessing) 1 nutrients jellynut
).toAscii == s!"
↑↑⇨***⇨↓↓
↑↑↠*B*↠↓↓
↑↑⚡***⚡↓↓
↑↑←↓←←←←↓
↑←↑↓↓←←←←
↑↑↓↓
>→↑↑↓→→→→→>
>→→↑→→→→→→>
"
#guard (bus do
let nutrients <- input .nutrients 115
let jellynut <- input .jellynut 120
let _ <- busAssemblyLine (recipe .jellynutProcessing [(100, .nutrients)]) 1 nutrients jellynut
).toAscii == s!"
→↓
↑→→→→→→→→↓
↑ ↓
↑↑⇨***⇨↓↓↓
↑↑↠*B*↠↓↓↓
↑↑⚡***⚡↓↓↓
↑↑ ↓←←←←↓↓
↑↑←↓↓←←←←↓
↑←↑↓↓↓←←←←
↑↑↓↓↓
>→↑↑↓↓→→→→→>
>→→↑↓→→→→→→>
→→→→→→→>
"
#guard (bus do
let nutrients <- input .nutrients 15
let mash <- input .yumakoMash 300
let jelly <- input .jelly 240
let _ <- busAssemblyLine (recipe .bioflux) 1 nutrients mash jelly
).toAscii == s!"
↑↑⇨***⇦↑↓
↑↑↠*B*↠↑↓
↑↑⚡***⚡↑↓
↑↑←→→→→↑↓
↑←↑↑↓←←←←
↑↑↑↓
>→↑↑↑→→→→→>
>→→↑↑
>→→→↑
"
#guard (bus do
let nutrients <- input .nutrients 100
let mash <- input .yumakoMash 400
let jelly <- input .jelly 400
let _ <- busAssemblyLine (recipe .bioflux [(160, .jelly), (100, .yumakoMash), (85, .nutrients) ]) 1 nutrients mash jelly
).toAscii == s!"
→→→→→↓
↑→→→↓↓
↑↑→↓↓↓
↑↑↑↓↓→→→→→→↓
↑↑↑↓→→→→→→↓↓
↑↑↑→→→→→→↓↓↓
↑↑↑←←←←← ↓↓↓
↑↑ ↑ ↓↓↓
↑↑⇨***⇦↑↓↓↓↓
↑↑↠*B*↠↑↓↓↓↓
↑↑⚡***⚡↑↓↓↓↓
↑↑ →→→→↑↓↓↓↓
↑↑ ↑↓←←←←↓↓↓
↑↑ ↑↓↓←←←←↓↓
↑↑←↑↓↓↓←←←←↓
↑←↑↑↓↓↓↓←←←←
↑↑↑↓↓↓↓
>→↑↑↑↓↓↓→→→→→>
>→→↑↑↓↓→→→→→→>
>→→→↑↓→→→→→→→>
→→→→→→→→>
"
instance : Config where
generateBigPoles := true
generateRoboports := true
providerChestCapacity := 3
adapterMinHeight := 3
#guard (bus do
let ice <- input .ice 60
let _ <- busAssemblyLine (recipe .iceMelting) 1 ice
).toAscii == s!"
***┤↑├|
*C*⇦↑ |
***⚡↑ |
****↑ |
****↑ |
**R*↑ |
****↑ |
** ↑ |
*↯ ↑ |
↑||
↑|
↑|
↑|
>→→→→↑|||>
"
#guard (bus do
let ironOre <- input .ironOre 600
let _ <- busAssemblyLine (recipe .ironPlate) 16 ironOre
).toAscii = s!"
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑**** ↓
↑**** ↓
↑**R* ↓
↑****⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
↑**** ↓
↑**** ↓
↑**R* ↓
↑****⚡↓
↑ ↓
↑ ⚡🄿⇦↓
↑ ↓
↑ ** ↓
↑ *↯ ↓
↑ ↓←←←←
↑ ↓
↑←↓
↑↓
>→↑→→→→→>
"
#guard (bus do
let stone <- input .stone 750
let steel <- input .steelPlate 750
let stick <- input .ironStick 750
let _ <- busAssemblyLine (recipe .rail) 5 stone stick steel
).toAscii == s!"
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑ ***↠↑↓
↑↑ ⚡ ↑↓
↑↑ ↓←*↥↓
↑↑↓←←↓S←←
↑↑↓ ↑←→→↓
↑↑→⇥⚡↦↑⤒↓
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑ ***↠↑↓
↑↑ ⚡ ↑↓
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑ ***↠↑↓
↑↑ ⚡ ↑↓
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑ ***↠↑↓
↑↑ ⚡ ↑↓
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑ ***↠↑↓
↑↑ ⚡ ↑↓
↑↑**** ↑↓
↑↑**** ↑↓
↑↑**R* ↑↓
↑↑****⚡↑↓
↑↑ ↥↓
↑↑ ⚡ 🄿⇦↓
↑↑ ⤒↓
↑↑ ** ↑↓
↑↑ *↯ ↑↓
↑↑ →→→↑↓
↑↑← ↑↓←←←
↑←↑ ↑↓
↑↑ ↑↓
>→↑↑ ↑→→→→>
>→⇥↑↦↑
>→→↑
"
================================================
FILE: Functorio/AssemblyStation.lean
================================================
import Functorio.Recipe
import Functorio.Factory
import Functorio.Util
import Functorio.Row
structure Process where
recipe:RecipeName
fabricator:Fabricator
returnedInputs : List (Fraction × Ingredient) := []
-- fabricatorOk : fabricator.handlesCategory recipeName.getRecipe.category := by decide
deriving Repr, DecidableEq
namespace Process
@[simp]
def getRecipe (process:Process) : Recipe :=
let original := process.recipe.getRecipe
if process.fabricator != .biochamber then original else
let liquidInputs := original.inputs.filter fun input => input.snd.isLiquid
let solidInputs := original.inputs.filter fun input => !input.snd.isLiquid
let extraNutrients := original.time / process.fabricator.speedup / 4
let solidInputs :=
if solidInputs.any fun (_,ingredient) => ingredient == .nutrients
then solidInputs.map fun (items, ingredient) => (items + if ingredient == .nutrients then extraNutrients else 0, ingredient)
else [( extraNutrients , Ingredient.nutrients )] ++ solidInputs
{ original with
inputs := liquidInputs ++ solidInputs
}
def liquidInputs (process:Process) : List Ingredient :=
(process.getRecipe.inputs.map Prod.snd).filter Ingredient.isLiquid
def liquidOutputs (process:Process): List Ingredient :=
(process.getRecipe.outputs.map Prod.snd).filter Ingredient.isLiquid
def solidInputs (process:Process): List Ingredient :=
(process.getRecipe.inputs.map Prod.snd).filter (!Ingredient.isLiquid .)
def solidOutputs (process:Process): List Ingredient :=
(process.getRecipe.outputs.map Prod.snd).filter (!Ingredient.isLiquid .)
@[simp]
def inputIngredients (process:Process) : List Ingredient :=
process.getRecipe.inputs.map (Prod.snd)
@[simp]
def outputIngredients (process:Process) : List Ingredient :=
process.getRecipe.outputs.map (Prod.snd)
end Process
@[simp]
def defaultCategoryFabricator : RecipeCategory -> Fabricator
| .chemistry
| .organicOrChemistry
| .chemistryOrCryogenics => .chemicalPlant
| .basicCrafting
| .crafting
| .advancedCrafting
| .craftingWithFluid
| .craftingWithFluidOrMetallurgy
| .electronics
| .electronicsOrAssembling
| .electronicsWithFluid
| .metallurgyOrAssembling
| .organicOrAssembling
| .pressing
| .parameters
| .cryogenicsOrAssembling => .assemblingMachine3
| .oilProcessing => .oilRefinery
| .rocketBuilding => .rocketSilo
| .smelting => .electricFurnace
| .captiveSpawnerProcess => .captiveBiterSpawner
| .centrifuging => .centrifuge
| .crushing => .crusher
| .cryogenics => .cryogenicPlant
| .electromagnetics => .electromagneticPlant
| .metallurgy => .foundry
| .organic
| .organicOrHandCrafting => .biochamber
| .recyclingOrHandCrafting
| .recycling => .recycler
@[simp]
def recipe (recipe:RecipeName) (returnedInputs : List (Fraction × Ingredient) := []) : Process := {
recipe := recipe,
fabricator := defaultCategoryFabricator recipe.getRecipe.category,
returnedInputs := returnedInputs
}
structure FabricatorConfig where
direction : Direction
mirror : Bool
inputOffsets : List Nat
outputOffsets : List Nat
def fabricatorConfig : Fabricator -> FabricatorConfig
| .assemblingMachine1
| .recycler
| .steelFurnace
| .stoneFurnace
| .electricFurnace
| .rocketSilo
| .centrifuge
| .crusher
| .captiveBiterSpawner => {
direction := .N
mirror := false
inputOffsets := []
outputOffsets := []
}
| .assemblingMachine2
| .assemblingMachine3 => {
direction := .W
mirror := false
inputOffsets := [1]
outputOffsets := [1]
}
| .chemicalPlant
| .biochamber => {
direction := .W
mirror := true
inputOffsets := [0,2]
outputOffsets := [0,2]
}
| .oilRefinery => {
direction := .E
mirror := false
inputOffsets := [1,3]
outputOffsets := [0,2,4]
}
| .cryogenicPlant => {
direction := .E
mirror := false
inputOffsets := [0,2,4]
outputOffsets := [0,2,4]
}
| .foundry => {
direction := .E
mirror := false
inputOffsets := [1,3]
outputOffsets := [1,3]
}
| .electromagneticPlant => {
direction := .N
mirror := false
inputOffsets := [2]
outputOffsets := [1]
}
def interfaceE (process:Process) : List InterfaceH :=
process.liquidOutputs.map (.,.E)
def interfaceW (process:Process) : List InterfaceH :=
process.liquidInputs.map (.,.E)
def plainStation (process:Process) : Factory [] (interfaceE process) [] (interfaceW process) :=
let details := fabricatorConfig process.fabricator
{
entities := [
fabricator 0 0 process.fabricator process.recipe details.direction details.mirror
]
wires := []
width := process.fabricator.width
height := process.fabricator.height
interface := {
n := #v[]
e := (details.outputOffsets.splitAt process.liquidOutputs.length).fst.castToVector!
s := #v[]
w := (details.inputOffsets.splitAt process.liquidInputs.length).fst.castToVector!
}
name := reprStr process.getRecipe.name
}
private def beltline (x:Nat) (dir:Direction) (height:Nat) : List Entity :=
(List.range height).map (fun y => belt x y dir)
private def pipeline (x:Nat) (height:Nat): List Entity :=
(List.range height).map fun y => pipe x y
structure Access where
direction: DirectionV
ingredient: Ingredient
-- factoryDir = direction in which the factory lies
def accessEntities (x:Nat) (height:Nat) (ewOffsets:List InterfaceImpl) (ns : List InterfaceV) (factoryDir: DirectionH) (numSolidOutputs:Nat) : List Entity :=
Id.run do
let mut entities : Vector (Option EntityType) height := Vector.replicate height .none
let inputInserterDir : DirectionH := match factoryDir with | .E => .W | .W => .E
let outputInserterDir : DirectionH := factoryDir
for y in ewOffsets do
entities := entities.set! y (.some (.pipeToGround factoryDir))
for ((ingredient, dir), i) in ns.zipIdx do
for y in List.range height do
if entities[y]! == .none then
let inserterDir := match dir with | .N => inputInserterDir | .S => outputInserterDir
let filter := if dir == .S && numSolidOutputs > 1 then [ingredient] ++ ingredient.spoilResult.toList else []
match i with
| 0 => entities := entities.set! y (.some (.inserter inserterDir filter))
| 1 => entities := entities.set! y (.some (.longInserter inserterDir filter))
| _ => error! s!"More than 2 belt inputs/outputs per side are not supported, belt index = {i}"
break
for y in (List.range height).reverse do
if entities[y]! == .none then
entities := entities.set! y (.some .pole)
break
return entities.toList.zipIdx.flatMap fun (type, y) =>
match type with | .none => [] | .some type => [{x:=x, y:=y, type:=type}]
def rightAccessor {ew} (ewOffsets: Vector InterfaceImpl ew.length) (height:Nat) (ns : List InterfaceV) (numSolidOutputs:Nat): Factory ns ew ns ew :=
match ns with
| [] => emptyFactoryV ewOffsets
| _ =>
{
width := ns.length + 1
height := height
wires := []
interface := {
n := (ns.mapIdx fun i _ => i + 1).castToVector!
e := ewOffsets
s := (ns.mapIdx fun i _ => i + 1).castToVector!
w := ewOffsets
}
name := "rightAccessor"
entities :=
accessEntities 0 height ewOffsets.toList ns .W numSolidOutputs ++
(ns.zipIdx.flatMap fun ((_, dir), i) => beltline (i + 1) dir height)
}
def leftAccessor {ew} (ewOffsets: Vector InterfaceImpl ew.length) (height:Nat) (ns : List InterfaceV) (numSolidOutputs:Nat): Factory ns ew ns ew :=
match ns with
| [] => emptyFactoryV ewOffsets
| _ =>
{
width := ns.length + 1
height := height
wires := []
interface := {
n := (ns.mapIdx fun i _ => i).castToVector!
e := ewOffsets
s := (ns.mapIdx fun i _ => i).castToVector!
w := ewOffsets
}
name := "leftAccessor"
entities :=
accessEntities ns.length height ewOffsets.toList ns .E numSolidOutputs ++
(ns.zipIdx.flatMap fun ((_, dir), i) => beltline i dir height)
}
def interfaceNS (process:Process) : List InterfaceV :=
process.solidInputs.map (.,.N) ++ process.solidOutputs.map (.,.S)
def pipesOnSideStation (process:Process) : Factory
(interfaceNS process)
(interfaceE process)
(interfaceNS process)
(interfaceW process)
:=
let station := plainStation process
let ns := interfaceNS process
let (leftNS, rightNS) := ns.splitAt (ns.length / 2)
let leftAccess := leftAccessor station.interface.w station.height leftNS process.solidOutputs.length
let rightAccess := rightAccessor station.interface.e station.height rightNS process.solidOutputs.length
unsafeFactoryCast (row3 leftAccess station rightAccess)
private def pipesIn (ingredients:List Ingredient) (underground:Bool := false) (powerPole:Bool := false)
: Factory (ingredients.map (.,.N)) (ingredients.map (.,.E)) (ingredients.map (.,.N)) []
:=
let pipes := ingredients.length
let width := if pipes == 0 then 0 else pipes * 2 + 1
let height := if powerPole then max 2 (pipes * 2 - 1) else pipes * 2 - 1
let pipelines : List Entity :=
(List.range pipes).flatMap fun i =>
(List.range height).map fun y =>
let x := i * 2 + 1
pipe x y
let goDown : List Entity :=
(List.range pipes).map fun i =>
let x := 2 * i + 2
let y := 2 * i
if i == pipes - 1 && !underground
then pipe x y
else pipeToGround x y .W
let comeUp : List Entity :=
(List.range pipes).flatMap fun i =>
let x := 2 * pipes
let y := 2 * i
if i == pipes - 1 || underground
then []
else [pipeToGround x y .E]
let power : List Entity :=
if powerPole then [pole (width-1) 1] else []
let interfaceNS := ingredients.toVector.mapIdx fun i _ => (i * 2 + 1 : InterfaceImpl)
let interfaceE := ingredients.toVector.mapIdx fun i _ => (i * 2 : InterfaceImpl)
{
width := width
height := height
wires := []
entities := pipelines ++ goDown ++ comeUp ++ power
interface := {
n := cast (by simp) interfaceNS
e := cast (by simp) interfaceE
s := cast (by simp) interfaceNS
w := #v[]
}
name := s!"pipesIn {reprStr ingredients}"
}
private def pipesOut (ingredients:List Ingredient) (underground:Bool := false)
: Factory (ingredients.map (.,.S)) [] (ingredients.map (.,.S)) (ingredients.map (.,.E))
:=
let pipes := ingredients.length
let width := if pipes == 0 then 0 else pipes * 2 + 1
let height := pipes * 2 - 1
let pipelines : List Entity :=
(List.range pipes).flatMap fun i =>
(List.range height).map fun y =>
let x := i * 2 + 1
pipe x y
let goDown : List Entity :=
(List.range pipes).map fun i =>
let x := 2 * i
let y := 2 * i
if i == 0 && !underground
then pipe x y
else pipeToGround x y .E
let comeUp : List Entity :=
(List.range pipes).flatMap fun i =>
let x := 0
let y := 2 * i
if i == 0 || underground
then []
else [pipeToGround x y .W]
let interfaceNS := ingredients.toVector.mapIdx fun i _ => (i * 2 + 1 : InterfaceImpl)
let interfaceW := ingredients.toVector.mapIdx fun i _ => (i * 2 : InterfaceImpl)
{
wires := []
width := width
height := height
entities := pipelines ++ goDown ++ comeUp
interface := {
n := cast (by simp) interfaceNS
e := #v[]
s := cast (by simp) interfaceNS
w := cast (by simp) interfaceW
}
name := s!"pipesOut {reprStr ingredients}"
}
def stationInterface (process:Process) : List InterfaceV :=
process.inputIngredients.map (., .N) ++
process.outputIngredients.map (., .S)
abbrev Station process := Factory (stationInterface process) [] (stationInterface process) []
def stationWithoutOverride (process:Process) : Station process :=
let station := pipesOnSideStation process
let height := process.fabricator.height
let ns := interfaceNS process
let (leftNS, rightNS) := ns.splitAt (ns.length / 2)
unsafeFactoryCast (row3
(pipesIn process.liquidInputs (underground:=!leftNS.isEmpty) (powerPole:=
ns.length == 0 ||
process.liquidInputs.length + leftNS.length >= height))
station
(pipesOut process.liquidOutputs (underground:=!rightNS.isEmpty)))
-- Special case, because it takes 4 inputs.
private def flyingRobotFrameStation : Station (recipe .flyingRobotFrame) :=
let height := 3
let entities : List Entity :=
beltline (x:=0) .N height ++
beltline (x:=1) .N height ++
[
beltUp 2 0 .N,
longInserter 2 1 .W,
beltDown 2 2 .N,
longInserter 3 0 .W,
pole 3 1,
inserter 3 2 .W,
assembler RecipeName.flyingRobotFrame 4 0,
longInserter 7 0 .W,
pole 7 1,
inserter 7 2 .E
] ++
beltline (x:=8) .N height ++
beltline (x:=9) .S height
{
wires := []
width:= 10, height:=height, entities := entities
interface := {
n := #v[0,1,2,8,9]
e := #v[]
s := #v[0,1,2,8,9]
w := #v[]
}
name := "flyingRobotFrame"
}
-- Special case, because it has incredible requirements on output speed
private def nutrientsFromBiofluxStation : Station (recipe .nutrientsFromBioflux) :=
let height := 5
let entities : List Entity :=
beltline (x:=0) .N height ++
beltline (x:=1) .N height ++
[
inserter 2 0 .W,
longInserter 2 1 .W,
longInserter 2 2 .W,
pole 2 3,
fabricator 3 0 .biochamber .nutrientsFromBioflux,
inserter 6 0 .W,
inserter 6 1 .W,
inserter 6 2 .W,
inserter 3 3 .N,
inserter 4 3 .N,
inserter 5 3 .N,
pole 6 3,
belt 3 4 .E,
belt 4 4 .E,
belt 5 4 .E,
belt 6 4 .E
] ++
beltline (x:=7) .S height
{
wires := []
width:= 8, height:=height, entities := entities
interface := {
n := #v[0,1,7]
e := #v[]
s := #v[0,1,7]
w := #v[]
}
name := "nutrientsFromBioflux"
}
-- Special case, needs two output inserters to keep up with the production rate.
def railStation : Station (recipe .rail) :=
let factory := (pipesOnSideStation (recipe .rail)).expand .S 1
let removedLeftPole := eraseRectangle 2 2 1 1 factory.entities
let removedPoles := eraseRectangle 6 2 1 1 removedLeftPole
{factory with
entities := removedPoles.append [
longInserter 6 2 .W,
pole 4 3
]
}
-- Special case, needs more powerpoles to covert the huge size of the building
def rocketPart : Station (recipe .rocketPart) :=
let factory := pipesOnSideStation (recipe .rocketPart)
{factory with
entities := factory.entities.append [
pole 1 3, pole 11 3,
]
}
def acccessPipe (x:Nat) (ingredient:Ingredient): Factory [] [] [] [(ingredient, .E)] := {
width := 4, height := 1
wires := []
entities := [pipe x 0, pipeToGround (x-1) 0 .E]
name := "accessPipe"
interface := {n := #v[], e := #v[], s := #v[], w := #v[0]}
}
-- Special case, because the plant's pipes come out in weird spots
def electrolyteStation : Station (recipe .electrolyte) :=
{
wires := []
width := 13, height := 6,
name := ".electrolyte"
interface := {n := #v[1,3,5,11], e := #v[], s := #v[1,3,5,11], w := #v[]}
entities :=
pipeline 1 6 ++
pipeline 3 6 ++
beltline 5 .N 6 ++
[
pipeToGround 2 0 .W, pipeToGround 7 0 .E, pipe 8 0,
pipeToGround 4 5 .W, pipeToGround 8 5 .E, pipe 9 5,
inserter 6 1 .W, pole 6 4,
fabricator 7 1 .electromagneticPlant .electrolyte .E,
] ++
pipeline 11 6
}
-- Special case, because the plant's pipes come out in weird spots
def electromagneticScienceStation : Station (recipe .electromagneticSciencePack) :=
{
wires := []
width := 14, height := 6,
name := ".electromagneticSciencePack"
interface := {n := #v[1,3,5,6,13], e := #v[], s := #v[1,3,5,6,13], w := #v[]}
entities :=
pipeline 1 6 ++
pipeline 3 6 ++
beltline 5 .N 6 ++
beltline 6 .N 6 ++
[
pipeToGround 2 0 .W, pipeToGround 8 0 .E, pipe 9 0,
pipeToGround 4 5 .W, pipeToGround 9 5 .E, pipe 10 5,
inserter 7 1 .W, longInserter 7 2 .W, pole 7 4,
fabricator 8 1 .electromagneticPlant .electromagneticSciencePack .E,
inserter 12 1 .W, pole 12 4,
] ++
beltline 13 .S 6
}
-- Special case, because of 4 solid inputs
private def supercapacitorStation : Station (recipe .supercapacitor) :=
let height := 4
let entities : List Entity :=
pipeline (x:=1) height ++
beltline (x:=3) .N height ++
beltline (x:=4) .N height ++
[
beltUp 5 0 .N,
longInserter 5 1 .W,
beltDown 5 2 .N,
belt 5 3 .N,
inserter 6 0 .W,
longInserter 6 1 .W,
pipeToGround 2 2 .W, pipeToGround 6 2 .E,
pole 6 3,
fabricator 7 0 .electromagneticPlant .supercapacitor,
longInserter 11 0 .W,
inserter 11 1 .E,
pole 11 3,
] ++
beltline (x:=12) .N height ++
beltline (x:=13) .S height
{
wires := []
width:= 14, height:=height, entities := entities
interface := {
n := #v[1,3,4,5,12,13]
e := #v[]
s := #v[1,3,4,5,12,13]
w := #v[]
}
name := ".supercapacitor"
}
def station (process:Process) : Station process :=
match process.recipe with
| .flyingRobotFrame => unsafeFactoryCast flyingRobotFrameStation
| .electrolyte => unsafeFactoryCast electrolyteStation
| .electromagneticSciencePack => unsafeFactoryCast electromagneticScienceStation
| .supercapacitor => unsafeFactoryCast supercapacitorStation
| .rail => unsafeFactoryCast railStation
| .rocketPart => unsafeFactoryCast rocketPart
| .nutrientsFromBioflux => unsafeFactoryCast nutrientsFromBiofluxStation
| _ => stationWithoutOverride process
================================================
FILE: Functorio/AssemblyStationTest.lean
================================================
import Functorio.AssemblyStation
import Functorio.Ascii
#guard (station (recipe .ironPlate)).toAscii == s!"
^ v
↑⇨***⇨↓
↑ *F* ↓
↑⚡***⚡↓
^ v
"
#guard (station (recipe .electronicCircuit)).toAscii == s!"
^ ^v
↑⇨***⇦↑↓
↑ *A*↠↑↓
↑⚡***⚡↑↓
^ ^v
"
#guard (station (recipe .advancedCircuit)).toAscii == s!"
^^ ^v
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑⚡***⚡↑↓
^^ ^v
"
#guard (station (recipe .superconductor)).toAscii == s!"
^ ^^ ^v
| ↑↑⇨****⇦↑↓
| ↑↑↠****↠↑↓
|┤↑↑├**E* ↑↓
| ↑↑⚡****⚡↑↓
^ ^^ ^v
"
#guard (station { recipe := .accumulator, fabricator := .electromagneticPlant }).toAscii == s!"
^ ^v
↑⇨****⇦↑↓
↑ ****↠↑↓
↑ **E* ↑↓
↑⚡****⚡↑↓
^ ^v
"
#guard (station (recipe .supercapacitor)).toAscii == s!"
^ ^^^ ^v
| ↑↑↥⇨****↠↑↓
| ↑↑↠↠****⇦↑↓
|┤↑↑⤒├**E* ↑↓
| ↑↑↑⚡****⚡↑↓
^ ^^^ ^v
"
#guard (station (recipe .electromagneticSciencePack)).toAscii == s!"
^ ^ ^^ v
|┤| ↑↑ ├| ↓
| | ↑↑⇨****⇨↓
| | ↑↑↠**** ↓
| | ↑↑ **E* ↓
| | ↑↑⚡****⚡↓
| |┤↑↑ ├| ↓
^ ^ ^^ v
"
#guard (station (recipe .electrolyte)).toAscii == s!"
^ ^ ^ v
|┤| ↑ ├| |
| | ↑⇨****|
| | ↑ ****|
| | ↑ **E*|
| | ↑⚡****|
| |┤↑ ├| |
^ ^ ^ v
"
#guard (station (recipe .flyingRobotFrame)).toAscii == s!"
^^^ ^v
↑↑↥↠***↠↑↓
↑↑↠⚡*A*⚡↑↓
↑↑⤒⇨***⇦↑↓
^^^ ^v
"
#guard (station (recipe .rail)).toAscii == s!"
^^ ^v
↑↑⇨***⇦↑↓
↑↑↠*A*↠↑↓
↑↑ ***↠↑↓
↑↑ ⚡ ↑↓
^^ ^v
"
#guard (station (recipe .processingUnit)).toAscii == s!"
^ ^ ^v
| ↑⇨***⇦↑↓
|┤↑├*A*↠↑↓
| ↑⚡***⚡↑↓
^ ^ ^v
"
#guard (station (recipe .battery)).toAscii == s!"
^ ^ ^v
|┤↑├***⇦↑↓
| ↑⇨*C*↠↑↓
| ↑⚡***⚡↑↓
^ ^ ^v
"
#guard (station (recipe .sulfuricAcid)).toAscii == s!"
^ ^ ^ v
|┤↑├***┤↑├|
| ↑⇨*C*⇦↑ |
| ↑⚡***⚡↑ |
^ ^ ^ v
"
#guard (station (recipe .sulfur)).toAscii == s!"
^ ^ v
|┤|├***⇨↓
| | *C* ↓
| ||***⚡↓
^ ^ v
"
#guard (station (recipe .solidFuelFromLightOil)).toAscii == s!"
^ v
||***⇨↓
| *C* ↓
| ***⚡↓
^ v
"
#guard (station (recipe .rocketFuel)).toAscii == s!"
^ ^ v
| ↑⇨***⇨↓
|┤↑├*A* ↓
| ↑⚡***⚡↓
^ ^ v
"
#guard (station (recipe .heavyOilCracking)).toAscii == s!"
^ ^ v
|┤|├***||
| |⚡*C* |
| ||*** |
^ ^ v
"
#guard (station (recipe .advancedOilProcessing)).toAscii == s!"
^ ^ v v v
| | *****|| | |
|┤|├***** | | |
| |⚡**O**┤|├| |
| ||***** | | |
| | *****┤| |├|
^ ^ v v v
"
#guard (station (recipe .rocketPart)).toAscii == s!"
^ ^^
↑⇨*********⇦↑↑
↑ *********↞↑↑
↑ ********* ↑↑
↑⚡*********⚡↑↑
↑ ****L**** ↑↑
↑ ********* ↑↑
↑ ********* ↑↑
↑ ********* ↑↑
↑⚡*********⚡↑↑
^ ^^
"
#guard (station (recipe .pentapodEgg)).toAscii == s!"
^ ^ ^v
|┤↑├***⇦↑↓
| ↑⇨*B*↠↑↓
| ↑⚡***⚡↑↓
^ ^ ^v
"
#guard (station (recipe .nutrientsFromBioflux)).toAscii == s!"
^^ v
↑↑⇨***⇨↓
↑↑↠*B*⇨↓
↑↑↠***⇨↓
↑↑⚡⇩⇩⇩⚡↓
↑↑ →→→→↓
^^ v
"
================================================
FILE: Functorio/Blueprint.lean
================================================
import Lean.Data.Json
import Lean.Data.Json.FromToJson
import Functorio.Entity
import Functorio.Factory
open Lean
open Lean (Json)
def coppperWire := 5
private def wireTypeNumber (type:WireType) : Nat :=
match type with
| .redInput => 1
| .greenInput => 2
| .redOutput => 3
| .greenOutput => 4
| .copper => coppperWire
private structure BlueprintInner where
entities: List Json
tiles: List Json
wires : List (List Nat)
item: String
version: Nat
deriving ToJson
private structure Blueprint where
blueprint: BlueprintInner
deriving ToJson
private structure Position where
x : Float
y : Float
deriving ToJson, Repr
private def entityName (e:Entity) : String :=
match e.type with
| .belt _ _ => "express-transport-belt"
| .beltDown _ | .beltUp _ => "express-underground-belt"
| .splitter _ _ _ => "express-splitter"
| .pipe => "pipe"
| .pipeToGround _ => "pipe-to-ground"
| .pump _ => "pump"
| .inserter _ _ => "bulk-inserter"
| .longInserter _ _ => "long-handed-inserter"
| .pole => "medium-electric-pole"
| .bigPole => "big-electric-pole"
| .roboport => "roboport"
| .ironChest => "iron-chest"
| .passiveProviderChest _ => "passive-provider-chest"
| .refinedConcrete => "refined-concrete"
| .heatingTower => "heating-tower"
| .deciderCombinator _ _ _ => "decider-combinator"
| .arithmeticCombinator _ _ => "arithmetic-combinator"
| .fabricator f _ _ _ => f.name
private def entityDirection (e:Entity) : Option Direction :=
match e.type with
| .belt d _ | .beltDown d | .beltUp d | .splitter d _ _
| .pipeToGround d | .pump d | .inserter d _ | .longInserter d _
| .fabricator _ _ d _ | .deciderCombinator d _ _ | .arithmeticCombinator d _ => d
| .pipe | .pole | .bigPole | .roboport | .ironChest | .passiveProviderChest _
| .heatingTower | .refinedConcrete => .none
private def signalToJson (s:Signal) : Json :=
Json.mkObj ([
("name", Json.str s.name)
] ++
(s.type.map ("type", Json.str .)).toList)
private def conditionToJson (c:Condition) : Json :=
Json.mkObj ([
("first_signal", signalToJson c.firstSignal),
("comparator", c.comparator)
] ++
(c.constantValue.map ("constant", Json.num .)).toList ++
(c.secondSignal.map ("second_signal", signalToJson .)).toList)
private def outputToJson (o:Output) : Json :=
Json.mkObj [
("signal", signalToJson o.signal),
("copy_count_from_input", o.copyCountFromInput)
]
private def arithmeticConditionToJson (c:ArithmeticCondition) : Json :=
Json.mkObj [
("first_signal", signalToJson c.firstSignal),
("second_constant", c.secondConstant),
("operation", c.operation),
("output_signal", signalToJson c.outputSignal)
]
private def entityProps (e:Entity) : List (String × Json) :=
match e.type with
| .pipe | .pipeToGround _ | .pump _
| .pole | .bigPole | .roboport | .ironChest | .heatingTower | .refinedConcrete => []
| .inserter _ filter | .longInserter _ filter => [
("use_filters", !filter.isEmpty),
("filters", Json.arr (filter.mapIdx (fun i ingredient =>
Json.mkObj [
("index", s!"{i + 1}"),
("name", ingredient.name),
("quality", "normal"),
("comparator", "="),
]
)).toArray)
]
| .deciderCombinator _ conditions outputs => [
("control_behavior", Json.mkObj [
("decider_conditions", Json.mkObj [
("conditions", Json.arr (conditions.map conditionToJson).toArray),
("outputs", Json.arr (outputs.map outputToJson).toArray),
])
])
]
| .arithmeticCombinator _ condition => [
("control_behavior", Json.mkObj [
("arithmetic_conditions", arithmeticConditionToJson condition)
])
]
| .belt _ behavior => [
("control_behavior",
if behavior.circuitCondition.isNone
then Json.mkObj [
("circuit_enabled", false),
("circuit_read_hand_contents", behavior.circuitReadHandContents),
("circuit_contents_read_mode", behavior.circuitContentsReadMode)
]
else Json.mkObj [
("circuit_enabled", true),
("circuit_condition", conditionToJson behavior.circuitCondition.get!),
("circuit_read_hand_contents", behavior.circuitReadHandContents),
("circuit_contents_read_mode", behavior.circuitContentsReadMode)
]
),
]
| .beltDown _ => [("type", "input")]
| .beltUp _ => [("type", "output")]
| .passiveProviderChest capacity => match capacity with | .none => [] | .some capacity => [("bar", capacity)]
| .splitter _ priority filter =>
match priority with | .none => [] | .some p => [("output_priority", Json.str p)] ++
match filter with | .none => [] | .some f => [
("filter", Json.mkObj [
("name", f.name),
("quality", "normal"),
("comparator", "=")
])
]
| .fabricator _ r _ m => [("recipe", r.getRecipe.name), ("recipe_quality", "normal"), ("mirror", m)]
private def directionToNat (d:Direction) :=
match d with
| .N => 0
| .E => 4
| .S => 8
| .W => 12
private def entityToJson (id:Nat) (e:Entity) : Json :=
let dir := entityDirection e
let p : Position := {
x := Float.ofInt e.x + e.width.toFloat/2,
y := Float.ofInt e.y + e.height.toFloat/2
}
Json.mkObj ([
([
("name", entityName e),
("position", ToJson.toJson p),
("entity_number", id),
] : List (String × Json)),
match dir with | .none => [] | .some dir => [ ("direction", directionToNat dir) ],
entityProps e
].flatten)
private def tileToJson (e:Entity) : Json :=
let p : Position := {
x := Float.ofInt e.x
y := Float.ofInt e.y
}
Json.mkObj [
("name", entityName e),
("position", ToJson.toJson p),
]
def isTile (e:Entity) : Bool :=
match e.type with
| .refinedConcrete => true
| _ => false
private def distance (a b : Entity) : Nat :=
((Int.ofNat a.x) - (Int.ofNat b.x)).natAbs +
((Int.ofNat a.y) - (Int.ofNat b.y)).natAbs
namespace Factory
def withinDistance (a b:Entity) :=
let d := distance a b
d <= 9 || (a.type == .bigPole && b.type == .bigPole && d <= 30)
def neededForBootStrap (e:Entity) : Bool :=
match e.type with
| .pole | .bigPole | .roboport => true
| _ => false
def wireToJson (wire:Wire) : List Nat :=
[wire.src, wireTypeNumber wire.srcType, wire.dst, wireTypeNumber wire.dstType]
def toBlueprint {n e s w} (factory:Factory n e s w) (bootstrap := false) : String :=
let entities := (factory.entities.filter (fun e =>
!isTile e && (!bootstrap || neededForBootStrap e))).zipIdx
let tiles := factory.entities.filter isTile
let poles := entities.filter (fun (e,_) => e.type == .pole || e.type == .bigPole)
let wires := poles.flatMap (fun (a,aIdx) => poles.flatMap (fun (b,bIdx) =>
if aIdx < bIdx && withinDistance a b then [[
aIdx, coppperWire, bIdx, coppperWire
]] else []
))
let blueprint : Blueprint := {
blueprint := {
entities:=entities.map (fun (e,idx) => entityToJson idx e),
tiles:=tiles.map tileToJson,
wires:= wires ++ factory.wires.map wireToJson,
item:="blueprint",
version:= 562949957025792
}
}
Lean.Json.pretty (ToJson.toJson blueprint)
end Factory
================================================
FILE: Functorio/Bus.lean
================================================
import Functorio.Entity
import Functorio.Factory
import Functorio.Column
import Functorio.Row
import Functorio.Fraction
import Functorio.Util
structure LaneConfig where
ingredient: Ingredient
refCount : Nat
deriving Inhabited, Repr
def depletedLane : LaneConfig := {
ingredient := default, refCount := 0
}
namespace LaneConfig
def depleted (l:LaneConfig) : Bool :=
l.refCount == 0
end LaneConfig
abbrev LaneConfigs := List LaneConfig
namespace LaneConfigs
def height (lanes:LaneConfigs) : Nat :=
lanes.length
def useLane (index:Nat) (lanes:LaneConfigs) : LaneConfigs :=
lanes.modify index fun lane =>
if lane.refCount == 0 then error! s!"Accessing depleted lane {index}" else
{
refCount := lane.refCount - 1
ingredient := if lane.refCount <= 1 then default else lane.ingredient
}
def allocLane (ingredient:Ingredient) (lanes:LaneConfigs) (skip : Nat:=0): (Nat × LaneConfigs) := Id.run do
let newLane := {ingredient := ingredient, refCount := 1}
let mut skip := skip
for (lane, i) in lanes.zipIdx do
if i == 0 then continue -- Don't alloc the 0th lane, we need the space for exits
if lane.depleted then
-- We found an empty lane!
if ingredient.isLiquid then
-- Don't alloc if the previous or next lane is already a pipe
if i > 1 && lanes[i - 1]!.ingredient.isLiquid then continue
if i < lanes.length - 1 && lanes[i + 1]!.ingredient.isLiquid then continue
if skip > 0 then
skip := skip - 1
continue
return (i, lanes.set i newLane)
-- Couldn't find a depleted lane, so we need to alloc a new one
if ingredient.isLiquid && skip == 0 && lanes.length > 0 && lanes[lanes.length - 1]!.ingredient.isLiquid then
-- Padding between pipes
(lanes.length + 1, lanes ++ [depletedLane, newLane])
else
let newLanes := lanes ++ List.replicate skip depletedLane ++ [newLane]
(newLanes.length - 1, newLanes)
def available (lanes:LaneConfigs): List (LaneConfig × Nat) :=
lanes.zipIdx.filter fun (lane, _) => !lane.depleted
end LaneConfigs
abbrev Throughput := Fraction -- items per minute
structure BusLane (_ : Ingredient) (throughput:Throughput) where
index: Nat
deriving Inhabited, Repr
structure BusLane' where
ingredient:Ingredient
throughput:Throughput
index: Nat
deriving Inhabited, Repr
namespace BusLane
def less {i m n} (l:BusLane i m) (_: n≤m := by decide) : BusLane i n :=
{index := l.index}
-- TODO: why is this needed?
def exact {i n m} (l:BusLane i m) (_: n=m := by decide): BusLane i n :=
{index := l.index}
def toBusLane' {i n} (l:BusLane i n) : BusLane' :=
{ingredient := i, throughput := n, index := l.index }
end BusLane
instance {i n} : CoeOut (BusLane i n) BusLane' where
coe l := l.toBusLane'
@[simp]
def busInterface (lanes:LaneConfigs) : List InterfaceH :=
lanes.available.map fun (lane, _) => (lane.ingredient, .E)
def busInterfaceImpl (lanes:LaneConfigs) : Vector InterfaceImpl (busInterface lanes).length :=
cast (by simp) (lanes.available.toVector.map Prod.snd)
structure BusState where
input: LaneConfigs
output: LaneConfigs
factory : Factory [] (busInterface output) [] (busInterface input)
deriving Inhabited, Repr
private def emptyBusState : BusState := {
input := [depletedLane]
output := [depletedLane]
factory := emptyFactoryV.setName "emptyBus"
}
abbrev Bus T := StateM BusState T
private abbrev BusFactory (bus:Bus Unit) : Type :=
Factory [] (busInterface (bus.run emptyBusState).snd.output) [] (busInterface (bus emptyBusState).snd.input)
def bus (b:Bus Unit) : BusFactory b :=
(b emptyBusState).snd.factory
def input (ingredient:Ingredient) (throughput:Fraction) : Bus (BusLane ingredient throughput) :=
fun state =>
if (state.factory.width != 0) then error! s!"input must be called at beginning; here bus is already {state.factory.width} tiles wide" else
let (index, newConfig) := state.output.allocLane ingredient
({index:=index}, {
input := newConfig
output := newConfig
factory := (emptyFactoryV (busInterfaceImpl newConfig))
})
def inputs (n:Nat) (ingredient:Ingredient) (throughput:Fraction) : Bus (Vector (BusLane ingredient throughput) n) := do
let mut lanes : Array (BusLane ingredient throughput) := #[]
for _ in List.range n do
let lane <- input ingredient throughput
lanes := lanes.push lane
return lanes.toList.castToVector!
inductive Cell where
| entity (type:EntityType)
| blocked -- blocked by another entity that's larger than 1 cell
| empty
deriving Repr, DecidableEq, Inhabited
private inductive AccessType
| put
| get
deriving Inhabited, DecidableEq, Repr
abbrev Matrix w h := Vector (Vector Cell h) w
namespace Matrix
def modifyCell {w h} (matrix:Matrix w h) (x y : Nat) (cell:Cell -> Cell) : Matrix w h :=
matrix.modify x fun column => column.modify y cell
def setCell {w h} (matrix:Matrix w h) (x y : Nat) (cell:Cell) : Matrix w h :=
modifyCell matrix x y fun _ => cell
def canApplyEntities {w h} (matrix:Matrix w h) (entities:Option (List Entity)) : Bool :=
match entities with
| .none => false | .some entities =>
entities.all fun e =>
if e.x >= w then error! s!"Bus is wider than the maximum supported {e.x}" else
if e.y >= h then error! s!"Bus is taller than the maximum supported {e.y}" else
match matrix[e.x]![e.y]!, e.type with
-- Belt/pipe down followed by up cancels out to just a straight belt/pipe, so overriding is fine.
| .entity (.beltDown .E), .beltUp .E => true
| .entity (.pipeToGround .W), .pipeToGround .E => true
-- For splitters, the box below also has to be free.
| .empty, .splitter .E .none .none =>
matrix[e.x]![e.y + 1]! == .empty
| .empty, _ => true
| _,_ => false
def applyEntities {w h} (matrix:Matrix w h) (entities:Option (List Entity)) : Matrix w h := Id.run do
match entities with
| .none => error! s!"Trying to apply invalid entities {reprStr entities}"
| .some entities =>
let mut matrix := matrix
for e in entities do
if e.type == .splitter .E .none .none then
matrix := matrix.setCell e.x e.y (.entity e.type)
matrix := matrix.setCell e.x (e.y + 1) .blocked
else
matrix := matrix.modifyCell e.x e.y fun cell =>
match cell, e.type with
-- Belt/pipe down followed by up cancels out to just a straight belt/pipe.
| .entity (.beltDown .E), .beltUp .E => .entity (.belt .E)
| .entity (.pipeToGround .W), .pipeToGround .E => .entity .pipe
| .empty,_ => .entity e.type
| _,_ => error! s!"Trying to override entity {reprStr cell} with {reprStr e}"
return matrix
def toEntities {w h} (matrix : Matrix w h) : List Entity := Id.run do
let mut entities : Array Entity := #[]
for (column, x) in matrix.zipIdx do
for (cell, y) in column.zipIdx do
match cell with
| .entity type =>
entities := entities.push {x:=x, y:=y, type:=type}
| _ =>
pure ()
return entities.toList
def reduceUndergroundEntities {w h} (matrix:Matrix w h) : Matrix w h := Id.run do
let mut matrix := matrix
let width := w
let height := h
-- Go through all the columns
for x in List.range matrix.size do
let mut isUndergroundPipe := false
let mut isUndergroundBeltN := false
let mut isUndergroundBeltS := false
for y in List.range height do
if y + 1 >= height then continue
let cell := matrix[x]![y]!
let nextCell := matrix[x]![y+1]!
-- Simplify bus belt gets
if cell == .entity (.beltUp .N) && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.belt .N))
matrix := matrix.setCell x (y+1) (.entity (.beltUp .N))
if cell == .entity (.beltUp .N) && nextCell == .entity (.beltDown .N) then
matrix := matrix.setCell x y (.entity (.belt .N))
matrix := matrix.setCell x (y+1) (.entity (.belt .N))
if isUndergroundBeltN && cell == .empty && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.beltDown .N))
matrix := matrix.setCell x (y+1) (.entity (.beltUp .N))
if isUndergroundBeltN && cell == .empty && nextCell == .entity (.beltDown .N) then
matrix := matrix.setCell x y (.entity (.beltDown .N))
matrix := matrix.setCell x (y+1) (.entity (.belt .N))
-- Simplify bus belt puts
if cell == .entity (.beltDown .S) && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.belt .S))
matrix := matrix.setCell x (y+1) (.entity (.beltDown .S))
if cell == .entity (.beltDown .S) && nextCell == .entity (.beltUp .S) then
matrix := matrix.setCell x y (.entity (.belt .S))
matrix := matrix.setCell x (y+1) (.entity (.belt .S))
if isUndergroundBeltS && cell == .empty && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.beltUp .S))
matrix := matrix.setCell x (y+1) (.entity (.beltDown .S))
if isUndergroundBeltS && cell == .empty && nextCell == .entity (.beltUp .N) then
matrix := matrix.setCell x y (.entity (.beltUp .S))
matrix := matrix.setCell x (y+1) (.entity (.belt .S))
-- Simplify pipe put/get
if cell == .entity (.pipeToGround .N) && nextCell == .empty then
matrix := matrix.setCell x y (.entity .pipe)
matrix := matrix.setCell x (y+1) (.entity (.pipeToGround .N))
if cell == .entity (.pipeToGround .N) && nextCell == .entity (.pipeToGround .S) then
matrix := matrix.setCell x y (.entity .pipe)
matrix := matrix.setCell x (y+1) (.entity .pipe)
if isUndergroundPipe && cell == .empty && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.pipeToGround .S))
matrix := matrix.setCell x (y+1) (.entity (.pipeToGround .N))
if isUndergroundPipe && cell == .empty && nextCell == .entity (.pipeToGround .S) then
matrix := matrix.setCell x y (.entity (.pipeToGround .S))
matrix := matrix.setCell x (y+1) (.entity .pipe)
-- Determine whether we are underground
let cell := matrix[x]![y]!
if cell == .entity (.beltUp .N) then isUndergroundBeltN := true -- yes, beltUp means y + 1 is underground
if cell == .entity (.beltDown .N) then isUndergroundBeltN := false
if cell == .entity (.beltUp .S) then isUndergroundBeltS := false
if cell == .entity (.beltDown .S) then isUndergroundBeltS := true
if cell == .entity (.pipeToGround .N) then isUndergroundPipe := true
if cell == .entity (.pipeToGround .S) then isUndergroundPipe := false
-- Go through all the rows
for y in List.range height do
let mut isUndergroundPipe := false
let mut isUndergroundBelt := false
for x in List.range width do
if x + 1 >= width then continue
let cell := matrix[x]![y]!
let nextCell := matrix[x+1]![y]!
-- Simplify belt lanes
if cell == .entity (.beltDown .E) && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.belt .E))
matrix := matrix.setCell (x+1) y (.entity (.beltDown .E))
if cell == .entity (.beltDown .E) && nextCell == .entity (.beltUp .E) then
matrix := matrix.setCell x y (.entity (.belt .E))
matrix := matrix.setCell (x+1) y (.entity (.belt .E))
if isUndergroundBelt && cell == .empty && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.beltUp .E))
matrix := matrix.setCell (x+1) y (.entity (.beltDown .E))
if isUndergroundBelt && cell == .empty && nextCell == .entity (.beltUp .E) then
matrix := matrix.setCell x y (.entity (.beltUp .E))
matrix := matrix.setCell (x+1) y (.entity (.belt .E))
-- Simplify pipe lanes
if cell == .entity (.pipeToGround .W) && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.pipe))
matrix := matrix.setCell (x+1) y (.entity (.pipeToGround .W))
if cell == .entity (.pipeToGround .W) && nextCell == .entity (.pipeToGround .E) then
matrix := matrix.setCell x y (.entity .pipe)
matrix := matrix.setCell (x+1) y (.entity .pipe)
if isUndergroundPipe && cell == .empty && nextCell == .empty then
matrix := matrix.setCell x y (.entity (.pipeToGround .E))
matrix := matrix.setCell (x+1) y (.entity (.pipeToGround .W))
if isUndergroundPipe && cell == .empty && nextCell == .entity (.pipeToGround .E) then
matrix := matrix.setCell x y (.entity (.pipeToGround .E))
matrix := matrix.setCell (x+1) y (.entity .pipe)
-- Determine whether we are underground
let cell := matrix[x]![y]!
if cell == .entity (.beltDown .E) then isUndergroundBelt := true
if cell == .entity (.beltUp .E) then isUndergroundBelt := false
if cell == .entity (.pipeToGround .W) then isUndergroundPipe := true
if cell == .entity (.pipeToGround .E) then isUndergroundPipe := false
return matrix
end Matrix
def busTapInterface (inputs: List BusLane') (outputIngredients: List Ingredient) : List InterfaceV :=
(inputs.map fun input => (input.ingredient, .N)) ++
(outputIngredients.map fun output => (output, .S))
def pipeAccess (type:AccessType) (split:Bool) (x y:Nat) : List Entity :=
let ramp :=
match y with
| 0 => error! s!"0th lane should never be accessed {y}"
| 1 => [pipe x 0]
| _ => [pipeToGround x 0 .N, pipeToGround x (y-1) .S]
let lane :=
match type with
| .put => [pipe x y, pipeToGround (x+1) y .W]
| .get =>
if split then [pipeToGround (x-1) y .E, pipe x y, pipeToGround (x+1) y .W]
else [pipeToGround (x-1) y .E, pipe x y]
ramp ++ lane
def beltAccessGetWithSplit (x y:Nat) : Option (List Entity) :=
-- We don't have enough room to access a belt with a splitter if x < 2
if x < 2 then .none else
let ramp :=
match y with
| 0 => error! s!"0th lane should never be accessed {y}"
| 1 => []
| 2 => [belt x 0 .N]
| _ => [beltDown x (y-2) .N, beltUp x 0 .N]
.some (ramp ++ [
beltUp (x-2) y .E,
splitter (x-1) (y-1) .E,
beltDown x y .E,
belt x (y-1) .N,
])
def beltAccessGetWithoutSplit (x y:Nat) : List Entity :=
let lane :=
match x with
| 0 => []
| _ => [beltUp (x-1) y .E]
let ramp :=
match y with
| 0 => error! s!"0th lane should never be accessed {y}"
| 1 => [belt x 0 .N]
| _ => [beltDown x (y-1) .N, beltUp x 0 .N]
lane ++ [belt x y .N] ++ ramp
def beltAccessPut (x y:Nat) : List Entity :=
let ramp :=
match y with
| 0 => error! s!"0th lane should never be allocated {y}"
| 1 => [belt x 0 .S]
| _ => [beltUp x (y-1) .S, beltDown x 0 .S]
[belt x y .E, beltDown (x+1) y .E] ++ ramp
def laneAccess (type:AccessType) (config:LaneConfigs) (ingredient:Ingredient) (yIndex:Nat) (x:Nat) : Option (List Entity) :=
let split := config[yIndex]!.refCount > 0
if ingredient.isLiquid
then pipeAccess type split x yIndex
else
match type with
| .get =>
if split
then beltAccessGetWithSplit x yIndex
else beltAccessGetWithoutSplit x yIndex
| .put =>
beltAccessPut x yIndex
def bringDownAllLanes {w h} (lanes:LaneConfigs) (x:Nat) (matrix:Matrix w h) : Nat × Matrix w h := Id.run do
let mut x := x
let mut matrix := matrix
let entities x :=
lanes.available.map fun (lane, y) =>
if lane.ingredient.isLiquid then pipeToGround x y .W else beltDown x y .E
matrix := matrix.applyEntities (entities x)
return (x+1, matrix)
def bringUpAllLanes {w h} (lanes:LaneConfigs) (x:Nat) (matrix:Matrix w h) : Nat × Matrix w h := Id.run do
let mut x := x
let mut matrix := matrix
let entities x :=
lanes.available.map fun (lane, y) =>
if lane.ingredient.isLiquid then pipeToGround x y .E else beltUp x y .E
while !matrix.canApplyEntities (entities x) do x := x + 1
matrix := matrix.applyEntities (entities x)
return (x+1, matrix)
def busTapGeneric
(inputs:List BusLane')
(outputs:List Ingredient)
(factory:Factory [] [] (busTapInterface inputs outputs) [])
(adapterMinHeight:=0)
: Bus (List Nat) := fun state => Id.run do
-- Worst case, all outputs are pipes so we add 2 that
let height := state.output.height + outputs.length * 2
-- Longer than that, and we'll probably have problems
-- with underground pipes running out of length anyway.
let width := 15
let mut matrix : Matrix width height := Vector.replicate width (Vector.replicate height .empty)
let mut offsets : Array Nat := #[]
let mut lanes := state.output
let mut previousWasLiquid := false
let mut x := 0
(x, matrix) := bringDownAllLanes lanes x matrix
-- Handle all inputs
for input in inputs do
lanes := lanes.useLane input.index
if previousWasLiquid && input.ingredient.isLiquid then x := x + 1 -- Gap between pipes so they don't connect.
let entities x := laneAccess .get lanes input.ingredient input.index x
while !matrix.canApplyEntities (entities x) do x := x + 1
matrix := matrix.applyEntities (entities x)
previousWasLiquid := input.ingredient.isLiquid
offsets := offsets.push x
x := x + 1
-- Handle all outputs
let mut outputLanes : Array Nat := #[]
for (ingredient, i) in outputs.zipIdx do
if previousWasLiquid && ingredient.isLiquid then x := x + 1 -- Gap between pipes so they don't connect.
if x > 8 then
(x, matrix) := bringUpAllLanes lanes x matrix
(x, matrix) := bringDownAllLanes lanes x matrix
let (index, newLanes) := lanes.allocLane ingredient (skip := outputs.length - 1 - i)
lanes := newLanes
let entities x := laneAccess .put lanes ingredient index x
while !matrix.canApplyEntities (entities x) do x := x + 1
matrix := matrix.applyEntities (entities x)
previousWasLiquid := ingredient.isLiquid
offsets := offsets.push x
outputLanes := outputLanes.push index
x := x + 1
(x, matrix) := bringUpAllLanes lanes x matrix
let tapFactory : Factory (busTapInterface inputs outputs) (busInterface lanes) [] (busInterface state.output) := {
width:= x
height:= max state.output.height lanes.height
wires := []
entities := matrix.reduceUndergroundEntities.toEntities
interface := {
n := offsets.toList.castToVector!
e := busInterfaceImpl lanes
s := #v[]
w := busInterfaceImpl state.output
}
name := "tapBus'"
}
-- TODO: make adapterMinHeight part of Config
let factoryAndTap := column factory tapFactory adapterMinHeight
let busFactory := row state.factory factoryAndTap
(outputLanes.toList, {
input := state.input
output := lanes
factory := busFactory
})
def busTapNoOutput
(inputs:List BusLane')
(factory:Factory [] [] (busTapInterface inputs []) [])
(adapterMinHeight:=0)
: Bus Unit := do
let _ <- busTapGeneric inputs [] factory adapterMinHeight
def busTap
{outputIngredient} {outputThroughput} (inputs:List BusLane')
(factory:Factory [] [] (busTapInterface inputs [outputIngredient]) [])
(adapterMinHeight:=0)
: Bus (BusLane outputIngredient outputThroughput) := do
let outputs <- busTapGeneric inputs [outputIngredient] factory adapterMinHeight
return {index := outputs[0]!}
def busTap2
{outputIngredient} {outputThroughput} {outputIngredient'} {outputThroughput'} (inputs:List BusLane')
(factory:Factory [] [] (busTapInterface inputs [outputIngredient, outputIngredient']) [])
(adapterMinHeight:=0)
: Bus (BusLane outputIngredient outputThroughput × BusLane outputIngredient' outputThroughput') := do
let outputs <- busTapGeneric inputs [outputIngredient, outputIngredient'] factory adapterMinHeight
return ({index := outputs[0]!}, {index := outputs[1]!})
def split {i left input} (l:BusLane i input) (right := input - left) (_:left + right = input := by decide) : Bus (BusLane i left × BusLane i right) :=
fun state =>
(({index:= l.index}, {index:=l.index}),
{state with
output :=
state.output.modify l.index fun lane => {lane with
refCount := lane.refCount + 1
}
factory := unsafeFactoryCast state.factory
}
)
@[simp]
def expressBeltThroughput : Throughput := 45 * 60 -- 2700
def mergeSolid {i a b} (l:BusLane i a) (l':BusLane i b) : Bus (BusLane i (a + b)) :=
busTap [l.toBusLane',l'.toBusLane'] {
entities:=[
pole 0 0,
belt 1 0 .E,
belt 2 0 .S,
belt 1 1 .N,
beltDown 2 1 .S,
belt 0 2 .E,
belt 1 2 .N,
belt 2 2 .W,
belt 0 3 .N,
belt 1 3 .E,
belt 2 3 .N,
splitter 0 4 .N (outputPriority:="left"),
beltUp 2 4 .S,
],
wires := []
width:=3,
height:=5,
interface:={
n := #v[]
e := #v[]
s := #v[0,1,2]
w := #v[]
}
name := s!"merge {reprStr i}"
}
def mergeLiquid {i a b} (l:BusLane i a) (l':BusLane i b) : Bus (BusLane i (a + b)) :=
busTap [l.toBusLane',l'.toBusLane'] {
entities:= (List.range 5).map (pipe . 0)
width:=5,
height:=1,
wires := []
interface:={
n := #v[]
e := #v[]
s := #v[0,2,4]
w := #v[]
}
name := s!"merge {reprStr i}"
}
def merge {i a b} (l:BusLane i a) (l':BusLane i b) (_ : i.isLiquid || a+b ≤ expressBeltThroughput := by decide) : Bus (BusLane i (a + b)) :=
if i.isLiquid then mergeLiquid l l' else mergeSolid l l'
def splitBalanced {i left input} (l:BusLane i input) (right := input - left) (h:left + right = input := by decide) : Bus (BusLane i left × BusLane i right) :=
let inputSignal : Signal := {name:= i.name, type := .none}
let leftSignal : Signal := {name:="signal-L", type:="virtual"}
let rightSignal : Signal := {name:="signal-R", type:="virtual"}
let enableSignal : Signal := {name:="signal-check", type:="virtual"}
let counter x y :=
deciderCombinator x y .N [
{
firstSignal := inputSignal
secondSignal := .none
constantValue := .some 0
comparator:= "≥"
}
] [
{
signal:= inputSignal
copyCountFromInput:=true
}
]
let multiplier x y outputSignal c :=
arithmeticCombinator x y .N
{
firstSignal:=inputSignal
outputSignal := outputSignal
secondConstant:=c
operation:= "*"
}
if i.isLiquid then split l right h else
let fraction := left / right
busTap2 [l.toBusLane'] {
width:=4,
height:=8,
wires := [
-- Hookup left
{ src:= 0, dst:= 1, srcType:= .greenInput, dstType:= .greenInput},
{ src:= 1, dst:= 1, srcType:= .redOutput, dstType:= .redInput},
{ src:= 1, dst:= 2, srcType:= .greenOutput, dstType:= .greenInput},
-- Hookup right
{ src:= 3, dst:= 4, srcType:= .greenInput, dstType:= .greenInput},
{ src:= 4, dst:= 4, srcType:= .redOutput, dstType:= .redInput},
{ src:= 4, dst:= 5, srcType:= .greenOutput, dstType:= .greenInput},
-- Hookup combiner
{ src:= 2, dst:= 6, srcType:= .greenOutput, dstType:= .greenInput},
{ src:= 5, dst:= 6, srcType:= .greenOutput, dstType:= .greenInput},
{ src:= 6, dst:= 0, srcType:= .redOutput, dstType:= .redInput},
{ src:= 6, dst:= 3, srcType:= .redOutput, dstType:= .redInput},
]
entities:= [
-- Left logic
belt 0 6 .N {
circuitCondition := .some {
firstSignal:= enableSignal, secondSignal:=.none, constantValue:=.some 1, comparator:="="
}
circuitReadHandContents := true
circuitContentsReadMode := 0
},
counter 0 2,
multiplier 0 0 leftSignal fraction.num,
-- Right logic
belt 1 6 .N {
circuitCondition := .some {
firstSignal:= enableSignal, secondSignal:=.none, constantValue:=.some 1, comparator:="≠"
}
circuitReadHandContents := true
circuitContentsReadMode := 0
},
counter 1 2,
multiplier 1 0 rightSignal fraction.den,
-- Combine left and right
deciderCombinator 2 2 .S [
{
firstSignal:= leftSignal
secondSignal:= rightSignal
constantValue:=.none
comparator:= "<"
}
] [
{
signal:=enableSignal
copyCountFromInput:=false
}
],
pole 2 1,
splitter 0 7 .N,
belt 2 7 .S,
belt 3 7 .S,
belt 2 6 .S,
belt 3 6 .S,
belt 0 5 .N,
belt 1 5 .E,
belt 2 5 .S,
belt 3 5 .S,
belt 0 4 .E,
belt 1 4 .E,
belt 2 4 .E,
belt 3 4 .S,
]
interface:={
n := #v[]
e := #v[]
s := #v[1,2,3]
w := #v[]
}
name := s!"splitBalanced {reprStr i}"
}
def bigPoleFactory : Factory [] [] [] [] := {
entities := [bigPole 0 0]
width := 2, height := 2
name := "bigPole"
wires := []
interface := { n:= #v[], e:= #v[], s:= #v[], w:= #v[] }
}
def pipePumps : Bus Unit :=
fun state =>
let config := state.output
let entities := config.zipIdx.flatMap fun (lane, y) =>
let poles := if y % 7 == 0 then [pole 1 y] else []
let lanes :=
if lane.depleted then []
else if lane.ingredient.isLiquid then
[pump 3 y .E] ++
if y % 7 == 0
then [pipeToGround 0 y .E, pipeToGround 2 y .E]
else [pipe 0 y, pipe 1 y, pipe 2 y]
else
[belt 3 y .E, belt 4 y .E] ++
if y % 7 == 0
then [ beltDown 0 y .E, beltUp 2 y .E ]
else [ belt 0 y .E, belt 1 y .E, belt 2 y .E]
poles ++ lanes
let factory : Factory [] (busInterface config) [] (busInterface config) := {
entities := entities
width := 5
wires := []
height := config.height
name := "pipePumps"
interface := {
n:= #v[]
e:= busInterfaceImpl config
s:= #v[]
w:= busInterfaceImpl config
}
}
((), {
input := state.input
output := config
factory := row state.factory (column bigPoleFactory factory)
})
def spoilingChamber {n} {input:Ingredient} {output:Ingredient} (bacteria:BusLane input n) : Bus (BusLane output n) :=
let factory : Factory [] [] [(input, .N), (output, .S)] [] := {
name := s!"spoilingChamber {reprStr input}",
width := 4,
height := 9,
entities := [
belt 1 0 .E,
belt 2 0 .E,
belt 3 0 .S,
belt 0 1 .E,
belt 1 1 .N,
belt 2 1 .W,
belt 3 1 .S,
belt 0 2 .N,
splitter 1 2 .N,
belt 3 2 .S,
belt 0 3 .N,
splitter 1 3 .E,
belt 2 3 .N,
belt 3 3 .S,
beltUp 0 4 .N,
beltUp 2 4 .N,
belt 3 4 .S,
inserter 0 5 .S [output, .spoilage],
inserter 1 5 .S [output, .spoilage],
inserter 2 5 .S [output, .spoilage],
beltDown 3 5 .S,
ironChest 0 6,
ironChest 1 6,
ironChest 2 6,
pole 3 6,
inserter 0 7 .S,
inserter 1 7 .S,
inserter 2 7 .S,
beltUp 3 7 .S,
belt 0 8 .W,
belt 1 8 .W,
belt 2 8 .W,
belt 3 8 .S,
],
wires := [],
interface := {
n := #v[],
e := #v[],
s := #v[2, 3],
w := #v[]
}
}
busTap [bacteria] (unsafeFactoryCast factory)
def removeExcess {i n} (l:BusLane i n) : Bus (BusLane i n × BusLane i 0) :=
busTap2 [l.toBusLane'] {
entities:=[
belt 0 0 .E,
belt 1 0 .S,
belt 0 1 .N,
beltDown 1 1 .S,
pole 2 1,
belt 0 2 .N,
belt 1 2 .E,
belt 2 2 .S,
splitter 0 3 .N (outputPriority:="left"),
belt 2 3 .S,
belt 0 4 .N,
belt 1 4 .E,
belt 2 4 .S,
splitter 0 5 .N (outputPriority:="right") (filter:=Ingredient.spoilage),
belt 2 5 .S,
belt 0 6 .N,
beltUp 1 6 .S,
belt 2 6 .S,
],
wires := []
width:=3,
height:=7,
interface:={
n := #v[]
e := #v[]
s := #v[0,1,2]
w := #v[]
}
name := s!"removeExcess {reprStr i}"
}
================================================
FILE: Functorio/BusTest.lean
================================================
import Functorio.Bus
import Functorio.Cap
import Functorio.Ascii
namespace Test
#guard (bus do
let iron <- inputs 10 .ironOre 2700
busTapNoOutput [iron[9], iron[1]] (capN emptyFactoryH)
).toAscii == s!"
↑↑
>⇥↥↑↦>
>→→↑
>⇥⤒↦→>
>⇥↑↦→>
>⇥↑↦→>
>⇥↑↦→>
>⇥↑↦→>
>⇥↑↦→>
>⇥↑↦→>
>→↑
"
#guard (bus do
let iron <- inputs 6 .ironOre 2700
busTapNoOutput [iron[4], iron[1]] (capN emptyFactoryH)
).toAscii == s!"
↑↑
>⇥↥↑↦>
>→→↑
>⇥⤒↦→>
>⇥↑↦→>
>→↑
>→→→→>
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
busTapNoOutput [iron[0], iron[1], iron[2]] (capN emptyFactoryH)
).toAscii == s!"
↑↑↑
>→↑↑↑
>→→↑↑
>→→→↑
>→→→→→>
>→→→→→>
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
busTapNoOutput [iron[0], iron[2], iron[4]] (capN emptyFactoryH)
).toAscii == s!"
↑↑↑
>→↑↑↑
>→⇥↑↑↦>
>→→↑↑
>→→⇥↑↦>
>→→→↑
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
busTapNoOutput [iron[4], iron[3]] (capN emptyFactoryH)
).toAscii == s!"
↑↑←
↑ ↑
>⇥↑ ↑↦>
>⇥↑ ↑↦>
>⇥↑ ↑↦>
>⇥↑↦↑
>→↑
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
busTapNoOutput [iron[4], iron[2]] (capN emptyFactoryH)
).toAscii == s!"
↑↑
>⇥↑↑↦>
>⇥↥↑↦>
>→→↑
>⇥⤒↦→>
>→↑
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
busTapNoOutput [iron[4], iron[0], iron[1], iron[2]] (capN emptyFactoryH)
).toAscii == s!"
↥↑↑↑
>→→↑↑↑
>⇥⤒↦↑↑
>⇥↑↦→↑
>⇥↑↦→→→>
>→↑
"
#guard (bus do
let petrol <- input .petroleumGas 1
let water <- input .water 1
let _ <- inputs 5 .ironOre 2700
busTapNoOutput [petrol, water] (capN (emptyFactoryH #v[0,2]))
).toAscii == s!"
| |
>|| |
>→→⇥|↦>
>||||
>→→→→→>
>→→→→→>
>→→→→→>
>→→→→→>
"
#guard (bus do
let _ <- inputs 5 .ironOre 2700
let petrol <- input .petroleumGas 1
let water <- input .water 1
busTapNoOutput [petrol, water] (capN (emptyFactoryH #v[0,2]))
).toAscii == s!"
| |
>⇥| |↦>
>⇥| |↦>
>⇥| |↦>
>⇥| |↦>
>⇥| |↦>
>|| |
|
>||||
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
let petrol <- input .petroleumGas 1
let water <- input .water 1
busTapNoOutput [petrol, iron[2], water] (capN emptyFactoryH)
).toAscii == s!"
|↑|
>⇥|↑|↦>
>⇥┴↑|↦>
>→→↑|
>⇥┬ |↦>
>⇥| |↦>
>|| |
|
>||||
"
#guard (bus do
let iron <- inputs 2 .ironOre 2700
let _coal : BusLane .coal 100 <- busTap [iron[0]] (capN emptyFactoryH)
).toAscii == s!"
↑↓
>→↑→→>
>→→→→>
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
let _coal : BusLane .coal 100 <- busTap [iron[2]] (capN emptyFactoryH)
).toAscii == s!"
↑↓
>⇥↑↓↦>
>⇥↑↓↦>
>→↑→→>
>→→→→>
>→→→→>
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
let _gear : BusLane .coal 100 <- busTap [iron[1], iron[3]] (capN emptyFactoryH)
).toAscii == s!"
↑↑↓
>⇥↑↑↓↦>
>→↑↑→→>
>→⇥↑↦→>
>→→↑
>→→→→→>
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
let _water : BusLane .water 100 <- busTap [iron[2]] (capN emptyFactoryH)
).toAscii == s!"
↑|
>⇥↑|↦>
>⇥↑|↦>
>→↑||>
>→→→→>
>→→→→>
"
#guard (bus do
let iron <- inputs 3 .ironOre 2700
let (iron0, _) <- split iron[0] (left:=100)
busTapNoOutput [iron0] (capN emptyFactoryH)
).toAscii == s!"
*↑
>→S→→>
>→→→→>
>→→→→>
"
#guard (bus do
let iron <- inputs 4 .ironOre 2700
let (iron0, _) <- split iron[2] (left:=100)
busTapNoOutput [iron0] (capN emptyFactoryH)
).toAscii == s!"
↑
>→⇥↑↦>
>⇥*↑↦>
>→S→→>
>→→→→>
"
#guard (bus do
let iron <- inputs 4 .ironOre 2700
let water <- input .water 1000
let (iron0, _) <- split iron[2] (left:=100)
busTapNoOutput [water, iron0] (capN emptyFactoryH)
).toAscii == s!"
|↑
>⇥┴↑↦>
>⇥*↑↦>
>→S→→>
>⇥┬↦→>
>||
"
#guard (bus do
let iron <- inputs 5 .ironOre 2700
let petrol <- input .petroleumGas 1
let water <- input .water 10
let (water0, _) <- split (left:=5) water
busTapNoOutput [petrol, iron[2], water0] (capN emptyFactoryH)
).toAscii == s!"
|↑|
>⇥|↑|↦>
>⇥┴↑|↦>
>→→↑|
>⇥┬ |↦>
>⇥| |↦>
>|| |
|
>|||||>
"
#guard (bus do
let iron <- inputs 10 .ironOre 2700
let petrol <- input .petroleumGas 1
let water <- input .water 10
let (water0, _) <- split (left:=5) water
busTapNoOutput [petrol, iron[2], water0] (capN emptyFactoryH)
).toAscii == s!"
|↑|
>⇥|↑|↦>
>⇥┴↑|↦>
>→→↑|
>⇥┬ |↦>
>⇥| |↦>
>⇥| |↦>
>⇥| |↦>
>⇥| |↦>
>⇥| |↦>
>⇥| |↦>
>|| |
|
>|||||>
"
#guard (bus do
let iron0 <- inputs 3 .ironOre 2700
let water <- input .water 1000
let iron1 <- inputs 3 .ironOre 2700
let (water0, _) <- split (left:=1) water
busTapNoOutput [water0, iron1[0], iron1[1], iron0[0], iron0[1], iron0[2]] (capN emptyFactoryH)
).toAscii == s!"
|↑↑↑↑↑←
|↑↑↑↑←↑
|↑↑↑←↑↑
|↑↑←↑↑↑
|↑←↑↑↑↑
| ↑↥↑↑↑
>⇥| ↑↦↑↑↑
>⇥| ↑⤒↦↑↑
>⇥| ↑↑↦→↑
>||┤↑↑├|||>
>→→→↑↑
>→→→→↑
>→→→→→→→→→>
"
#guard (bus do
let iron0 <- inputs 3 .ironOre 2700
let water <- input .water 1000
let iron1 <- inputs 3 .ironOre 2700
let (water0, _) <- split (left:=1) water
busTapNoOutput [water0, iron1[0], iron1[1], iron0[0]] (capN emptyFactoryH)
).toAscii == s!"
|↑↑↑←
|↑↑←↑
|↑←↑↑
| ↑↥↑
>⇥| ↑↦↑
>⇥| ↑⤒↦→>
>⇥| ↑↑↦→>
>||┤↑↑├|>
>→→→↑↑
>→→→→↑
>→→→→→→→>
"
#guard (bus do
let iron <- inputs 6 .ironOre 2700
let water <- input .water 1000
let (iron0, _) <- split iron[2] (left:=100)
let (iron1, _) <- split iron[4] (left:=100)
busTapNoOutput [water, iron0, iron1] (capN emptyFactoryH)
).toAscii == s!"
|↑↑
>⇥┴↑↑↦>
>⇥*↑↑↦>
>→S⇥↑↦>
>→⇥*↑↦>
>→→S→→>
>⇥┬↦→→>
>||
"
#guard (bus do
let iron <- inputs 10 .ironOre 2700
let (iron0, _) <- split iron[6] (left:=100)
let (iron1, _) <- split iron[4] (left:=100)
let (iron2, _) <- split iron[2] (left:=100)
let _coal : BusLane .coal 4 <- busTap [iron0, iron1, iron2] (capN emptyFactoryH)
).toAscii == s!"
↑↑↑→→→→→→↓
↑↑↑←←←← ↓
↑↑←← ↑ ↓
↑ ↑ ↑ ↓
>→⇥↑↦⇥↑↦⇥↑↦⇥↓↦>
>→⇥↑↦⇥↑ *↑↦⇥↓↦>
>→⇥↑↦⇥↑↦S→→⇥↓↦>
>→⇥↑ *↑↦→→→⇥↓↦>
>→⇥↑↦S→→→→→⇥↓↦>
>⇥*↑↦→→→→→→⇥↓↦>
>→S→→→→→→→→⇥↓↦>
>→→→→→→→→→→⇥↓↦>
>→→→→→→→→→→⇥↓↦>
>→→→→→→→→→→⇥↓↦>
→→>
"
#guard (bus do
let iron <- inputs 15 .ironOre 2700
let petrol <- input .petroleumGas 2
let (petrol0, petrol1) <- split (left:=1) petrol
let water <- input .water 2
let (water0, water1) <- split (left:=1) water
busTapNoOutput [petrol0, iron[0], water0] (capN emptyFactoryH)
pipePumps
busTapNoOutput [water1, iron[5], petrol1] (capN emptyFactoryH)
).toAscii == s!"
**
*↯
┴↑| ⚡ |↑|
>→→↑| |↑|
>⇥┬ |↦→→→→→⇥|↑|↦>
>⇥| |↦→→→→→⇥|↑|↦>
>⇥| |↦→→→→→⇥|↑|↦>
>⇥| |↦→→→→→⇥┴↑|↦>
>⇥| |↦→→→→→→→↑|
>⇥| |↦⇥⚡↦→→⇥┬ |↦>
>⇥| |↦→→→→→⇥| |↦>
>⇥| |↦→→→→→⇥| |↦>
>⇥| |↦→→→→→⇥| |↦>
>⇥| |↦→→→→→⇥| |↦>
>⇥| |↦→→→→→⇥| |↦>
>⇥| |↦→→→→→⇥| |↦>
>⇥| |↦⇥⚡↦→→⇥| |↦>
>⇥| |↦→→→→→⇥| |↦>
>||┤|├|||*P┤|├|
| |
>||||||||*P||
"
#guard (bus do
let iron0 <- input .ironPlate 10
let iron1 <- input .ironPlate 15
let _ <- merge iron0 iron1
).toAscii == s!"
⚡→↓
↑⤓
→↑←
↑→↑
*S↧
↑↑↓
>→↑↑→→>
>→→↑
"
#guard (bus do
let water0 <- input .water 10
let water1 <- input .water 15
let _ <- merge water0 water1
).toAscii == s!"
|||||
| | |
>|| | ||>
|
>||||
"
#guard (bus do
let iron <- input .ironOre 10
let _ <- splitBalanced iron (left:=3)
).toAscii == s!"
**
++⚡
***
≥≥≥
→→→↓
↑→↓↓
↑↑↓↓
*S↓↓
↑↓↓
>→↑↓→→>
→→→>
"
#guard (bus do
let bacteria <- input .ironBacteria 10
let _iron : BusLane .ironOre 10 <- spoilingChamber bacteria
).toAscii == s!"
→→↓
→↑←↓
↑*S↓
↑*↑↓
↥S↥↓
⇧⇧⇧⤓
☐☐☐⚡
⇧⇧⇧↧
←←←↓
→↑↓
↑↓←
↑↓
>→↑→→>
"
================================================
FILE: Functorio/Cap.lean
================================================
import Functorio.Factory
def capN {n e s w} (f:Factory n e s w) : Factory [] e s w :=
{
width:= f.width,
height:= f.height,
entities := f.entities,
wires := f.wires,
interface := {
n := #v[]
e := f.interface.e
s := f.interface.s
w := f.interface.w
}
name := f.name
}
def capE {n e s w} (f:Factory n e s w) : Factory n [] s w :=
{
width:= f.width,
height:= f.height,
entities := f.entities,
wires := f.wires,
interface := {
n := f.interface.n
e := #v[]
s := f.interface.s
w := f.interface.w
}
name := f.name
}
def capS {n e s w} (f:Factory n e s w) : Factory n e [] w :=
{
width:= f.width,
height:= f.height,
entities := f.entities,
wires := f.wires,
interface := {
n := f.interface.n
e := f.interface.e
s := #v[]
w := f.interface.w
}
name := f.name
}
def capW {n e s w} (f:Factory n e s w) : Factory n e s [] :=
{
width:= f.width
height:= f.height
entities := f.entities
wires := f.wires
interface := {
n := f.interface.n
e := f.interface.e
s := f.interface.s
w := #v[]
}
name := f.name
}
def capAll {n e s w} (f:Factory n e s w) : Factory [] [] [] [] :=
capN (capE (capS (capW f)))
================================================
FILE: Functorio/Column.lean
================================================
import Functorio.Factory
import Functorio.Expand
import Functorio.Adapter
import Functorio.Util
private def columnPerfect {n e s w e' s' w'} (top : Factory n e s w) (bot:Factory s e' s' w'): Factory n (e ++ e') s' (w ++ w') :=
if top.interface.s != bot.interface.n then impossible! "perfect column must have exactly the same interface positions" else
if top.width != bot.width then impossible! "perfect column must have exactly the same width" else
let width := top.width
{
width := width,
height := top.height + bot.height,
entities := top.entities ++ bot.entities.map (Entity.offsetPosition 0 top.height)
wires := top.wires ++ bot.wires.map fun wire => wire.incrementLabels top.entities.length
interface :=
{
n := top.interface.n
e := cast (by simp) (top.interface.e ++ increaseOffset top.height (bot.interface.e))
s := bot.interface.s
w := cast (by simp) (top.interface.w ++ increaseOffset top.height (bot.interface.w))
}
name := s!"col({top.name},{bot.name})"
}
private def columnSameWidth {n e s w e' s' w'} (top : Factory n e s w) (bot:Factory s e' s' w') (adapterMinHeight:Nat) : Factory n (e ++ e') s' (w ++ w') :=
if top.width != bot.width then impossible! s!"same width of factories expected {top.width}" else
let width := top.width
let adapter := adapterV (top.interface.s) (bot.interface.n) width adapterMinHeight
columnPerfect top (columnPerfect adapter bot)
def column {n e s w e' s' w'} (top : Factory n e s w) (bot:Factory s e' s' w') (adapterMinHeight := 0) : Factory n (e ++ e') s' (w ++ w') :=
-- Similar code in rowTwo
-- Align the two factories on their 0th interface,
-- then expand the smaller one to match the bigger one's size.
if (top.width > bot.width) then
let diff := top.width - bot.width
let shift := if top.interface.s.isEmpty then 0
else min diff ((top.interface.s)[0]! - (bot.interface.n)[0]!)
columnSameWidth top ((bot.expand .E (diff - shift)).expand .W shift) adapterMinHeight else
if (top.width < bot.width) then
let diff := bot.width - top.width
let shift := if top.interface.s.isEmpty then 0
else min diff ((bot.interface.n)[0]! - (top.interface.s)[0]!)
columnSameWidth ((top.expand .E (diff - shift)).expand .W shift) bot adapterMinHeight else
columnSameWidth top bot adapterMinHeight
def columnList {i} (fs:List (Factory i [] i [])) : Factory i [] i [] :=
match fs with
| f::fs => fs.foldl column f
| [] => error! "can't make a column with empty list of factories yet"
def column3 {n e s w e' s' w' e'' s'' w''} (top : Factory n e s w) (middle:Factory s e' s' w') (bot:Factory s' e'' s'' w'') : Factory n (e ++ e' ++ e'') s'' (w ++ w' ++ w'') :=
column (column top middle) bot
================================================
FILE: Functorio/Config.lean
================================================
class Config where
generateRoboports : Bool := false
generateBigPoles : Bool := false
providerChestCapacity : Nat := 0
adapterMinHeight : Nat := 0
instance : Config where
================================================
FILE: Functorio/Crop.lean
================================================
import Functorio.Factory
private def cropOption {n e s w} (f:Factory n e s w) : Option (Factory n e s w) :=
let xs := f.entities.map (fun e => e.x)
let ys := f.entities.map (fun e => e.y)
do
let xMin <- List.min? xs
let xMax <- List.max? xs
let yMin <- List.min? ys
let yMax <- List.max? ys
some {
width := xMax - xMin + 1,
height:= yMax - yMin + 1,
entities:= f.entities.map (fun e => {
x := e.x - xMin,
y := e.y - yMin,
type := e.type
}),
wires := f.wires
interface := {
n := decreaseOffset xMin (f.interface.n)
e := decreaseOffset yMin (f.interface.e)
s := decreaseOffset xMin (f.interface.s)
w := decreaseOffset yMin (f.interface.w)
}
name := f.name
}
-- Removes empty space from all sides of the factory.
def crop {n e s w} (f:Factory n e s w) : Factory n e s w :=
(cropOption f).getD f
================================================
FILE: Functorio/Direction.lean
================================================
inductive Direction where
| N
| E
| S
| W
deriving DecidableEq, Repr, Inhabited
================================================
FILE: Functorio/Entity.lean
================================================
import Lean.Data.Json
import Lean.Data.Json.Printer
import Lean.Data.Json.FromToJson
import Functorio.Direction
import Functorio.Recipe
open Lean
open Lean (Json)
structure Signal where
type: Option String
name: String
deriving DecidableEq, Inhabited, Repr
structure Condition where
firstSignal: Signal
secondSignal: Option Signal
constantValue: Option Nat
comparator: String
deriving DecidableEq, Inhabited, Repr
structure Output where
signal: Signal
copyCountFromInput : Bool
deriving DecidableEq, Inhabited, Repr
structure BeltControlBehavior where
circuitCondition: Option Condition
circuitReadHandContents: Bool
circuitContentsReadMode: Nat
deriving DecidableEq, Inhabited, Repr
structure ArithmeticCondition where
firstSignal: Signal
secondConstant: Nat
operation: String
outputSignal: Signal
deriving DecidableEq, Inhabited, Repr
inductive EntityType
| belt (dir:Direction) (behavior:BeltControlBehavior := {
circuitCondition := .none
circuitReadHandContents := false
circuitContentsReadMode := 0
})
| beltDown (direction:Direction)
| beltUp (direction:Direction)
| splitter (direction:Direction) (outputPriority:Option String) (filter:Option Ingredient)
| pipe
| pipeToGround (direction:Direction)
| pump (direction:Direction)
| inserter (direction:Direction) (filter:List Ingredient)
| longInserter (direction:Direction) (filter:List Ingredient)
| pole
| bigPole
| fabricator (fabricator:Fabricator) (recipe:RecipeName) (direction:Direction) (mirror:Bool)
| heatingTower
| roboport
| ironChest
| passiveProviderChest (capacity:Option Nat)
| deciderCombinator (direction:Direction) (conditions:List Condition) (outputs:List Output)
| arithmeticCombinator (direction:Direction) (condition:ArithmeticCondition)
| refinedConcrete
deriving DecidableEq, Inhabited, Repr
structure Entity where
x : Nat
y : Nat
type : EntityType
deriving DecidableEq, Inhabited, Repr
def belt x y d (behavior : BeltControlBehavior := {
circuitCondition := .none
circuitReadHandContents := false
circuitContentsReadMode := 0
})
:=
({x:=x,y:=y,type:=.belt d behavior} : Entity)
def beltDown x y d := ({x:=x,y:=y,type:=.beltDown d} : Entity)
def beltUp x y d := ({x:=x,y:=y,type:=.beltUp d} : Entity)
def splitter x y d (outputPriority : Option String := .none) (filter := Option.none) := ({x:=x,y:=y,type:=.splitter d outputPriority filter} : Entity)
def pipe x y := ({x:=x,y:=y,type:=.pipe} : Entity)
def pipeToGround x y d := ({x:=x,y:=y,type:=.pipeToGround d} : Entity)
def pump x y d := ({x:=x,y:=y,type:=.pump d} : Entity)
def inserter x y d (filter:=[]) := ({x:=x,y:=y,type:=.inserter d filter} : Entity)
def longInserter x y d (filter:=[]) := ({x:=x,y:=y,type:=.longInserter d filter} : Entity)
def pole x y := ({x:=x,y:=y,type:=.pole} : Entity)
def bigPole x y := ({x:=x,y:=y,type:=.bigPole} : Entity)
def fabricator x y f r (d := Direction.N) (mirror:=false) := ({x:=x,y:=y,type:=.fabricator f r d mirror} : Entity)
def heatingTower x y := ({x:=x,y:=y,type:=.heatingTower} : Entity)
def assembler r x y (d := Direction.W) := fabricator x y .assemblingMachine3 r d
def furnace r x y := fabricator x y .electricFurnace r
def chemicalPlant r x y (mirror:=false) (d := Direction.W) := fabricator x y .chemicalPlant r d mirror
def refinery r x y (mirror:=false) (d := Direction.E) := fabricator x y .oilRefinery r d mirror
def rocketSilo x y := fabricator x y .rocketSilo .rocketPart
def roboport x y := ({x:=x,y:=y,type:=.roboport} : Entity)
def ironChest x y := ({x:=x,y:=y,type:=.ironChest} : Entity)
def passiveProviderChest x y (capacity : Option Nat := .none) := ({x:=x,y:=y,type:=.passiveProviderChest capacity} : Entity)
def refinedConcrete x y := ({x:=x,y:=y,type:=.refinedConcrete} : Entity)
def deciderCombinator x y (direction:Direction) (conditions:List Condition) (outputs:List Output) :=
({x:=x, y:=y, type:=.deciderCombinator direction conditions outputs} : Entity)
def arithmeticCombinator x y (direction:Direction) (condition:ArithmeticCondition) :=
({x:=x, y:=y, type:=.arithmeticCombinator direction condition} : Entity)
def recyler x y d (mirror:=false) := ({x:=x,y:=y,type:=.fabricator .recycler RecipeName.itemUnknownRecycling d mirror} : Entity)
namespace Entity
def width (e:Entity) : Nat :=
match e.type with
| .belt _ _ | .beltDown _ | .beltUp _ | .pipe | .pipeToGround _ | .inserter _ _ | .longInserter _ _
| .pole | .ironChest | .passiveProviderChest _ | .refinedConcrete => 1
| .bigPole => 2
| .splitter dir _ _ => if dir == .N || dir == .S then 2 else 1
| .deciderCombinator dir _ _ | .arithmeticCombinator dir _ | .pump dir => if dir == .N || dir == .S then 1 else 2
| .heatingTower => 3
| .roboport => 4
| .fabricator f _ dir _ => if dir == .N || dir == .S then f.width else f.height
def height (e:Entity) : Nat :=
match e.type with
| .belt _ _ | .beltDown _ | .beltUp _ | .pipe | .pipeToGround _ | .inserter _ _
| .longInserter _ _ | .pole | .ironChest | .passiveProviderChest _ | .refinedConcrete => 1
| .bigPole => 2
| .splitter dir _ _ => if dir == .N || dir == .S then 1 else 2
| .deciderCombinator dir _ _ | .arithmeticCombinator dir _ | .pump dir => if dir == .N || dir == .S then 2 else 1
| .heatingTower => 3
| .roboport => 4
| .fabricator f _ dir _ => if dir == .N || dir == .S then f.height else f.width
def offsetPosition (dx dy:Nat) (entity:Entity) : Entity := {
x := entity.x + dx
y := entity.y + dy
type := entity.type
}
end Entity
def eraseRectangle (x y width height:Nat) (es:List Entity) : List Entity :=
es.filter fun e => !(
x <= e.x && e.x < x + width &&
y <= e.y && e.y < y + height
)
================================================
FILE: Functorio/Expand.lean
================================================
import Functorio.Factory
private def expansionEntity (ingredient:Ingredient) (direction:Direction) (x y:Nat) : Entity :=
if ingredient.isLiquid then pipe x y else belt x y direction
private def expandS {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=
let expansion := (List.range' f.height distance).flatMap fun y =>
s.mapIdx fun i (ingredient, dir) =>
let x := f.interface.s[i]!
expansionEntity ingredient dir x y
{
width := f.width,
height:= f.height + distance,
entities:= f.entities ++ expansion
wires := f.wires,
interface := f.interface
name := f.name
}
private def expandN {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=
let expansion := (List.range distance).flatMap fun y =>
n.mapIdx fun i (ingredient, dir) =>
let x := f.interface.n[i]!
expansionEntity ingredient dir x y
{
width := f.width
height:= f.height + distance
entities:= f.entities.map (Entity.offsetPosition 0 distance) ++ expansion
wires := f.wires
interface := {
n := f.interface.n
e := increaseOffset distance (f.interface.e)
s := f.interface.s
w := increaseOffset distance (f.interface.w)
}
name := f.name
}
private def expandE {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=
let expansion := (List.range' f.width distance).flatMap fun x =>
e.mapIdx fun i (ingredient, dir) =>
let y := f.interface.e[i]!
expansionEntity ingredient dir x y
{
width := f.width + distance,
height:= f.height,
entities:= f.entities ++ expansion,
wires := f.wires
interface := f.interface
name := f.name
}
private def expandW {n e s w} (distance:Nat) (f:Factory n e s w) : Factory n e s w:=
let expansion := (List.range distance).flatMap fun x =>
w.mapIdx fun i (ingredient, dir) =>
let y := f.interface.w[i]!
expansionEntity ingredient dir x y
{
width := f.width + distance,
height:= f.height,
entities:= f.entities.map (Entity.offsetPosition distance 0) ++ expansion
wires := f.wires
interface := {
n := increaseOffset distance f.interface.n
e := f.interface.e
s := increaseOffset distance (f.interface.s)
w := f.interface.w
}
name := f.name
}
namespace Factory
def expand {n e s w} (direction:Direction) (distance:Nat) (f:Factory n e s w) : Factory n e s w :=
match direction with
| .N => expandN distance f
| .S => expandS distance f
| .E => expandE distance f
| .W => expandW distance f
end Factory
================================================
FILE: Functorio/ExpandTest.lean
================================================
import Functorio.Factory
import Functorio.Expand
import Functorio.Column
import Functorio.Ascii
#guard (
(@emptyFactoryH [(.coal,.S), (.coal,.N), (.water,.N)] #v[0,2,4]).expand .S 3
).toAscii == s!"
v ^ ^
↓ ↑ |
↓ ↑ |
↓ ↑ |
v ^ ^
"
#guard (column
((@emptyFactoryH [(.coal,.S), (.coal,.N), (.water,.N)] #v[0,2,4]).expand .N 2)
((@emptyFactoryH [(.coal,.S), (.coal,.N), (.water,.N)] #v[0,2,4]).expand .S 3)
).toAscii == s!"
v ^ ^
↓ ↑ |
↓ ↑ |
↓ ↑ |
↓ ↑ |
↓ ↑ |
v ^ ^
"
================================================
FILE: Functorio/Factory.lean
================================================
import Functorio.Entity
import Functorio.Util
import Functorio.Recipe
-- coordinates increase from N to S, and W to E
--
-- +--->
-- |
-- V
abbrev InterfaceImpl := Nat -- offset
def increaseOffset {l} (n:Nat) (is:Vector InterfaceImpl l) : Vector InterfaceImpl l :=
is.map fun offset => offset + n
def decreaseOffset {l} (n:Nat) (is:Vector InterfaceImpl l) : Vector InterfaceImpl l :=
is.map fun offset => offset - n
-- Horizontal (H) directions
inductive DirectionH where
| E
| W
deriving DecidableEq, Inhabited, Repr
instance : Coe DirectionH Direction where
coe d := match d with | .E => .E | .W => .W
-- Vertical (V) directions
inductive DirectionV where
| N
| S
deriving DecidableEq, Inhabited, Repr
instance : Coe DirectionV Direction where
coe d := match d with | .N => .N | .S => .S
abbrev InterfaceH := Ingredient × DirectionH
abbrev InterfaceV := Ingredient × DirectionV
structure InterfaceImpls (n:List InterfaceV) (e:List InterfaceH) (s:List InterfaceV) (w:List InterfaceH) where
n: Vector InterfaceImpl (n.length)
e: Vector InterfaceImpl (e.length)
s: Vector InterfaceImpl (s.length)
w: Vector InterfaceImpl (w.length)
deriving Inhabited, Repr
inductive WireType where
| redInput
| greenInput
| redOutput
| greenOutput
| copper
deriving DecidableEq, Inhabited, Repr
structure Wire where
src: Nat
srcType: WireType
dst: Nat
dstType: WireType
deriving DecidableEq, Inhabited, Repr
namespace Wire
def incrementLabels (wire:Wire) (n:Nat) : Wire :=
{src:=wire.src+n, dst:=wire.dst+n, srcType:=wire.srcType, dstType:=wire.dstType}
end Wire
structure Factory (n:List InterfaceV) (e:List InterfaceH) (s:List InterfaceV) (w:List InterfaceH) where
width: Nat
height: Nat
entities: List Entity
wires: List Wire
interface: InterfaceImpls n e s w
name: String
deriving Repr
def errorFactory {n e s w} : Factory n e s w := {
entities := []
wires := []
height := max n.length s.length
width := max e.length w.length
interface := {
n := Vector.range (n.length)
e := Vector.range (e.length)
s := Vector.range (s.length)
w := Vector.range (w.length)
}
name := "error"
}
instance {n e s w} : Inhabited (Factory n e s w) where
default := errorFactory
instance {n e s w} : ToString (Factory n e s w) where
toString f := f.name
def unsafeFactoryCast {n e s w n' e' s' w'} (f:Factory n e s w) : Factory n' e' s' w' :=
match decEq n n', decEq e e', decEq s s', decEq w w' with
| isTrue _, isTrue _, isTrue _, isTrue _ => cast (by subst_eqs; simp) f
| _, _, _, _ => error! s!"unsafeFactoryCast failed for {f.name}"
namespace Factory
def setName {n e s w} (name:String) (f:Factory n e s w) : Factory n e s w :=
{
width:=f.width
height:=f.height
entities:=f.entities
wires := f.wires
interface:=f.interface
name:=name
}
end Factory
def emptyFactoryV {i} (offsets:Vector InterfaceImpl i.length := Vector.range i.length) : Factory [] i [] i := {
entities := []
wires := []
interface := {
n := #v[]
e := offsets
s := #v[]
w := offsets
}
width := 0
height := if i.isEmpty then 0 else offsets[i.length-1]! + 1
name := s!"emptyFactoryV {reprStr i}"
}
def emptyFactoryH {i} (offsets:Vector InterfaceImpl i.length := Vector.range i.length) : Factory i [] i [] := {
entities := []
wires := []
interface := {
n := offsets
e := #v[]
s := offsets
w := #v[]
}
width := if i.isEmpty then 0 else offsets[i.length-1]! + 1
height := 0
name := s!"emptyFactoryH {reprStr i}"
}
namespace Factory
def refinedConcreteFloor {n e s w} (f:Factory n e s w) : Factory n e s w := Id.run do
let mut tiles : Array Entity := #[]
for x in List.range f.width do
for y in List.range f.height do
tiles := tiles.push (refinedConcrete x y)
{ f with entities := f.entities ++ tiles.toList }
end Factory
================================================
FILE: Functorio/Fraction.lean
================================================
-- We define our structure, because the standard library
-- adds a proof to the structure. This means that equal
-- numbers may not be definitionally equal.
abbrev Fraction := Nat × Nat
namespace Fraction
@[simp]
def num (f:Fraction) : Nat := f.fst
@[simp]
def den (f:Fraction) : Nat := f.snd
@[simp]
def mk (n m:Nat) : Fraction := (n,m)
def roundUp (f:Fraction) : Nat :=
if f.den == 1
then f.num
else (f.num / f.den) + 1
end Fraction
instance : ToString Fraction where
toString r := s!"{r.num}/{r.den}"
@[simp]
instance (n:Nat) : OfNat Fraction n where
ofNat := Fraction.mk n 1
attribute [simp] OfNat.ofNat
-- attribute [simp] Nat.gcd
@[simp]
def normalize (r:Fraction) : Fraction :=
let gcd : Nat := Nat.gcd r.num r.den
Fraction.mk (r.num / gcd) (r.den / gcd)
@[simp]
instance : Zero Fraction where
zero := 0
@[simp]
instance : HMul Fraction Fraction Fraction where
hMul a b := normalize (Fraction.mk (a.num * b.num) (a.den * b.den))
@[simp]
instance : HDiv Fraction Fraction Fraction where
hDiv a b := normalize (Fraction.mk (a.num * b.den) (a.den * b.num))
@[simp]
instance : HAdd Fraction Fraction Fraction where
hAdd a b := normalize (Fraction.mk (a.num * b.den + b.num * a.den) (a.den * b.den))
@[simp]
instance : Add Fraction where
add a b := normalize (Fraction.mk (a.num * b.den + b.num * a.den) (a.den * b.den))
@[simp]
instance : HSub Fraction Fraction Fraction where
hSub a b := normalize (Fraction.mk (a.num * b.den - b.num * a.den) (a.den * b.den))
@[simp]
instance : Coe Nat Fraction where
coe n := Fraction.mk n 1
@[simp]
instance : LE Fraction where
le a b := a.num * b.den ≤ b.num * a.den
@[simp]
instance : LT Fraction where
lt a b := a.num * b.den < b.num * a.den
@[simp]
instance : DecidableLE Fraction :=
fun a b => Nat.decLe (a.num * b.den) (b.num * a.den)
@[simp]
instance : DecidableLT Fraction :=
fun a b => Nat.decLt (a.num * b.den) (b.num * a.den)
@[simp]
instance : Min Fraction where
min a b := if a ≤ b then a else b
================================================
FILE: Functorio/Readme.lean
================================================
import Functorio.Abbreviations
import Functorio.AssemblyLine
import Functorio.Bus
namespace Readme
def makeIron : IronOre 300 -> Bus (Iron 300) :=
busAssemblyLine (recipe .ironPlate) 8
def makeCopper : CopperOre 150 -> Bus (Copper 150) :=
busAssemblyLine (recipe .copperPlate) 4
def makeGear : Iron 300 -> Bus (Gear 150) :=
busAssemblyLine (recipe .ironGearWheel) 1
def makeRedScience : Copper 150 -> Gear 150 -> Bus (RedScience 150) :=
busAssemblyLine (recipe .automationSciencePack) 10
def makeSteel : Iron 2700 -> Bus (Steel 540) :=
busAssemblyLine (recipe .steelPlate) 72
def makeBelt : Iron 150 -> Gear 150 -> Bus (YellowBelt 300):=
busAssemblyLine (recipe .transportBelt) 1
def makeAcid : Water 6000 -> Sulfur 300 -> Iron 60 ->Bus (Acid 3000) :=
busAssemblyLine (recipe .sulfuricAcid) 1
def redScience := bus do
let ironOre <- input .ironOre 300
let copperOre <- input .copperOre 150
let iron : Iron 300 <- makeIron ironOre
let copper : Copper 150 <- makeCopper copperOre
let gear : Gear 150 <- makeGear iron
let _science : RedScience 150 <- makeRedScience copper gear
def gearFactory := bus do
let ironOre : IronOre 300 <- input .ironOre 300
let iron : Iron 300 <- makeIron ironOre
let _gear <- makeGear iron
def splitIron := bus do
let iron : Iron 450 <- input .ironPlate 450
let (iron0, iron1) <- split (left:=300) (right:=150) iron
let gear <- makeGear iron0
let belt <- makeBelt iron1 gear
def mergeSteel := bus do
let iron0 <- input .ironPlate 2700
let iron1 <- input .ironPlate 2700
let steel0 <- makeSteel iron0
let steel1 <- makeSteel iron1
let steel <- merge steel0 steel1
def acidFactory := bus do
let iron <- input .ironPlate 60
let sulfur <- input .sulfur 300
let water <- input .water 6000
let _ <- makeAcid water sulfur iron
def gearFactory1 :=
station (recipe .ironGearWheel)
def gearFactory3 :=
assemblyLine (recipe .ironGearWheel) 3
def readme :=
columnList [
capAll redScience.refinedConcreteFloor,
capAll gearFactory.refinedConcreteFloor,
capAll splitIron.refinedConcreteFloor,
capAll mergeSteel.refinedConcreteFloor,
capAll acidFactory.refinedConcreteFloor,
capAll gearFactory1.refinedConcreteFloor,
capAll gearFactory3.refinedConcreteFloor
]
end Readme
================================================
FILE: Functorio/Recipe.lean
================================================
-- Generated by generate-recipe.py. Do not modify.
import Functorio.Fraction
import Functorio.Direction
inductive Ingredient
| accumulator
| activeProviderChest
| advancedCircuit
| agriculturalSciencePack
| agriculturalTower
| ammonia
| ammoniacalSolution
| arithmeticCombinator
| artificialJellynutSoil
| artificialYumakoSoil
| artilleryShell
| artilleryTurret
| artilleryWagon
| assemblingMachine1
| assemblingMachine2
| assemblingMachine3
| asteroidCollector
| atomicBomb
| automationSciencePack
| barrel
| battery
| batteryEquipment
| batteryMk2Equipment
| batteryMk3Equipment
| beacon
| beltImmunityEquipment
| bigElectricPole
| bigMiningDrill
| biochamber
| bioflux
| biolab
| biterEgg
| blueprint
| blueprintBook
| boiler
| bottomlessChest
| bufferChest
| bulkInserter
| burnerGenerator
| burnerInserter
| burnerMiningDrill
| calcite
| cannonShell
| captiveBiterSpawner
| captureRobotRocket
| car
| carbon
| carbonFiber
| carbonicAsteroidChunk
| cargoBay
| cargoLandingPad
| cargoWagon
| centrifuge
| chemicalPlant
| chemicalSciencePack
| cliffExplosives
| clusterGrenade
| coal
| coin
| combatShotgun
| concrete
| constantCombinator
| constructionRobot
| copperBacteria
| copperCable
| copperOre
| copperPlate
| crudeOil
| crudeOilBarrel
| crusher
| cryogenicPlant
| cryogenicSciencePack
| deciderCombinator
| deconstructionPlanner
| defenderCapsule
| depletedUraniumFuelCell
| destroyerCapsule
| dischargeDefenseEquipment
| displayPanel
| distractorCapsule
| efficiencyModule
| efficiencyModule2
| efficiencyModule3
| electricEnergyInterface
| electricEngineUnit
| electricFurnace
| electricMiningDrill
| electrolyte
| electromagneticPlant
| electromagneticSciencePack
| electronicCircuit
| emptyModuleSlot
| energyShieldEquipment
| energyShieldMk2Equipment
| engineUnit
| exoskeletonEquipment
| explosiveCannonShell
| explosiveRocket
| explosiveUraniumCannonShell
| explosives
| expressLoader
| expressSplitter
| expressTransportBelt
| expressUndergroundBelt
| fastInserter
| fastLoader
| fastSplitter
| fastTransportBelt
| fastUndergroundBelt
| firearmMagazine
| fissionReactorEquipment
| flamethrower
| flamethrowerAmmo
| flamethrowerTurret
| fluidWagon
| fluorine
| fluoroketoneCold
| fluoroketoneColdBarrel
| fluoroketoneHot
| fluoroketoneHotBarrel
| flyingRobotFrame
| foundation
| foundry
| fusionGenerator
| fusionPowerCell
| fusionReactor
| fusionReactorEquipment
| gate
| grenade
| gunTurret
| hazardConcrete
| heatExchanger
| heatInterface
| heatPipe
| heatingTower
| heavyArmor
| heavyOil
| heavyOilBarrel
| holmiumOre
| holmiumPlate
| holmiumSolution
| ice
| icePlatform
| infinityCargoWagon
| infinityChest
| infinityPipe
| inserter
| ironBacteria
| ironChest
| ironGearWheel
| ironOre
| ironPlate
| ironStick
| itemUnknown
| jelly
| jellynut
| jellynutSeed
| lab
| landMine
| landfill
| laneSplitter
| laserTurret
| lava
| lightArmor
| lightOil
| lightOilBarrel
| lightningCollector
| lightningRod
| linkedBelt
| linkedChest
| lithium
| lithiumBrine
| lithiumPlate
| loader
| locomotive
| logisticRobot
| logisticSciencePack
| longHandedInserter
| lowDensityStructure
| lubricant
| lubricantBarrel
| mechArmor
| mediumElectricPole
| metallicAsteroidChunk
| metallurgicSciencePack
| militarySciencePack
| modularArmor
| moltenCopper
| moltenIron
| nightVisionEquipment
| nuclearFuel
| nuclearReactor
| nutrients
| offshorePump
| oilRefinery
| oneWayValve
| overflowValve
| overgrowthJellynutSoil
| overgrowthYumakoSoil
| oxideAsteroidChunk
| passiveProviderChest
| pentapodEgg
| personalLaserDefenseEquipment
| personalRoboportEquipment
| personalRoboportMk2Equipment
| petroleumGas
| petroleumGasBarrel
| piercingRoundsMagazine
| piercingShotgunShell
| pipe
| pipeToGround
| pistol
| plasticBar
| poisonCapsule
| powerArmor
| powerArmorMk2
| powerSwitch
| processingUnit
| productionSciencePack
| productivityModule
| productivityModule2
| productivityModule3
| programmableSpeaker
| promethiumAsteroidChunk
| promethiumSciencePack
| proxyContainer
| pump
| pumpjack
| qualityModule
| qualityModule2
| qualityModule3
| quantumProcessor
| radar
| rail
| railChainSignal
| railRamp
| railSignal
| railSupport
| railgun
| railgunAmmo
| railgunTurret
| rawFish
| recycler
| refinedConcrete
| refinedHazardConcrete
| repairPack
| requesterChest
| roboport
| rocket
| rocketFuel
| rocketLauncher
| rocketPart
| rocketSilo
| rocketTurret
| science
| scrap
| selectionTool
| selectorCombinator
| shotgun
| shotgunShell
| simpleEntityWithForce
| simpleEntityWithOwner
| slowdownCapsule
| smallElectricPole
| smallLamp
| solarPanel
| solarPanelEquipment
| solidFuel
| spacePlatformFoundation
| spacePlatformHub
| spacePlatformStarterPack
| spaceSciencePack
| speedModule
| speedModule2
| speedModule3
| spidertron
| splitter
| spoilage
| stackInserter
| steam
| steamEngine
| steamTurbine
| steelChest
| steelFurnace
| steelPlate
| stone
| stoneBrick
| stoneFurnace
| stoneWall
| storageChest
| storageTank
| submachineGun
| substation
| sulfur
| sulfuricAcid
| sulfuricAcidBarrel
| supercapacitor
| superconductor
| tank
| teslaAmmo
| teslaTurret
| teslagun
| thruster
| thrusterFuel
| thrusterOxidizer
| toolbeltEquipment
| topUpValve
| trainStop
| transportBelt
| treeSeed
| tungstenCarbide
| tungstenOre
| tungstenPlate
| turboLoader
| turboSplitter
| turboTransportBelt
| turboUndergroundBelt
| undergroundBelt
| upgradePlanner
| uranium235
| uranium238
| uraniumCannonShell
| uraniumFuelCell
| uraniumOre
| uraniumRoundsMagazine
| utilitySciencePack
| water
| waterBarrel
| wood
| woodenChest
| yumako
| yumakoMash
| yumakoSeed
deriving DecidableEq, Repr, Inhabited
inductive RecipeCategory
| advancedCrafting
| basicCrafting
| captiveSpawnerProcess
| centrifuging
| chemistry
| chemistryOrCryogenics
| crafting
| craftingWithFluid
| craftingWithFluidOrMetallurgy
| crushing
| cryogenics
| cryogenicsOrAssembling
| electromagnetics
| electronics
| electronicsOrAssembling
| electronicsWithFluid
| metallurgy
| metallurgyOrAssembling
| oilProcessing
| organic
| organicOrAssembling
| organicOrChemistry
| organicOrHandCrafting
| parameters
| pressing
| recycling
| recyclingOrHandCrafting
| rocketBuilding
| smelting
deriving DecidableEq, Repr, Inhabited
namespace Ingredient
def isLiquid : Ingredient -> Bool
| .ammonia => true
| .ammoniacalSolution => true
| .crudeOil => true
| .electrolyte => true
| .fluorine => true
| .fluoroketoneCold => true
| .fluoroketoneHot => true
| .heavyOil => true
| .holmiumSolution => true
| .lava => true
| .lightOil => true
| .lithiumBrine => true
| .lubricant => true
| .moltenCopper => true
| .moltenIron => true
| .petroleumGas => true
| .steam => true
| .sulfuricAcid => true
| .thrusterFuel => true
| .thrusterOxidizer => true
| .water => true
| _ => false
def spoilResult : Ingredient -> Option Ingredient
| .agriculturalSciencePack => .some spoilage
| .bioflux => .some spoilage
| .copperBacteria => .some copperOre
| .ironBacteria => .some ironOre
| .jelly => .some spoilage
| .jellynut => .some spoilage
| .nutrients => .some spoilage
| .rawFish => .some spoilage
| .yumako => .some spoilage
| .yumakoMash => .some spoilage
| _ => .none
def name : Ingredient -> String
| .accumulator => "accumulator"
| .activeProviderChest => "active-provider-chest"
| .advancedCircuit => "advanced-circuit"
| .agriculturalSciencePack => "agricultural-science-pack"
| .agriculturalTower => "agricultural-tower"
| .ammonia => "ammonia"
| .ammoniacalSolution => "ammoniacal-solution"
| .arithmeticCombinator => "arithmetic-combinator"
| .artificialJellynutSoil => "artificial-jellynut-soil"
| .artificialYumakoSoil => "artificial-yumako-soil"
| .artilleryShell => "artillery-shell"
| .artilleryTurret => "artillery-turret"
| .artilleryWagon => "artillery-wagon"
| .assemblingMachine1 => "assembling-machine-1"
| .assemblingMachine2 => "assembling-machine-2"
| .assemblingMachine3 => "assembling-machine-3"
| .asteroidCollector => "asteroid-collector"
| .atomicBomb => "atomic-bomb"
| .automationSciencePack => "automation-science-pack"
| .barrel => "barrel"
| .battery => "battery"
| .batteryEquipment => "battery-equipment"
| .batteryMk2Equipment => "battery-mk2-equipment"
| .batteryMk3Equipment => "battery-mk3-equipment"
| .beacon => "beacon"
| .beltImmunityEquipment => "belt-immunity-equipment"
| .bigElectricPole => "big-electric-pole"
| .bigMiningDrill => "big-mining-drill"
| .biochamber => "biochamber"
| .bioflux => "bioflux"
| .biolab => "biolab"
| .biterEgg => "biter-egg"
| .blueprint => "blueprint"
| .blueprintBook => "blueprint-book"
| .boiler => "boiler"
| .bottomlessChest => "bottomless-chest"
| .bufferChest => "buffer-chest"
| .bulkInserter => "bulk-inserter"
| .burnerGenerator => "burner-generator"
| .burnerInserter => "burner-inserter"
| .burnerMiningDrill => "burner-mining-drill"
| .calcite => "calcite"
| .cannonShell => "cannon-shell"
| .captiveBiterSpawner => "captive-biter-spawner"
| .captureRobotRocket => "capture-robot-rocket"
| .car => "car"
| .carbon => "carbon"
| .carbonFiber => "carbon-fiber"
| .carbonicAsteroidChunk => "carbonic-asteroid-chunk"
| .cargoBay => "cargo-bay"
| .cargoLandingPad => "cargo-landing-pad"
| .cargoWagon => "cargo-wagon"
| .centrifuge => "centrifuge"
| .chemicalPlant => "chemical-plant"
| .chemicalSciencePack => "chemical-science-pack"
| .cliffExplosives => "cliff-explosives"
| .clusterGrenade => "cluster-grenade"
| .coal => "coal"
| .coin => "coin"
| .combatShotgun => "combat-shotgun"
| .concrete => "concrete"
| .constantCombinator => "constant-combinator"
| .constructionRobot => "construction-robot"
| .copperBacteria => "copper-bacteria"
| .copperCable => "copper-cable"
| .copperOre => "copper-ore"
| .copperPlate => "copper-plate"
| .crudeOil => "crude-oil"
| .crudeOilBarrel => "crude-oil-barrel"
| .crusher => "crusher"
| .cryogenicPlant => "cryogenic-plant"
| .cryogenicSciencePack => "cryogenic-science-pack"
| .deciderCombinator => "decider-combinator"
| .deconstructionPlanner => "deconstruction-planner"
| .defenderCapsule => "defender-capsule"
| .depletedUraniumFuelCell => "depleted-uranium-fuel-cell"
| .destroyerCapsule => "destroyer-capsule"
| .dischargeDefenseEquipment => "discharge-defense-equipment"
| .displayPanel => "display-panel"
| .distractorCapsule => "distractor-capsule"
| .efficiencyModule => "efficiency-module"
| .efficiencyModule2 => "efficiency-module-2"
| .efficiencyModule3 => "efficiency-module-3"
| .electricEnergyInterface => "electric-energy-interface"
| .electricEngineUnit => "electric-engine-unit"
| .electricFurnace => "electric-furnace"
| .electricMiningDrill => "electric-mining-drill"
| .electrolyte => "electrolyte"
| .electromagneticPlant => "electromagnetic-plant"
| .electromagneticSciencePack => "electromagnetic-science-pack"
| .electronicCircuit => "electronic-circuit"
| .emptyModuleSlot => "empty-module-slot"
| .energyShieldEquipment => "energy-shield-equipment"
| .energyShieldMk2Equipment => "energy-shield-mk2-equipment"
| .engineUnit => "engine-unit"
| .exoskeletonEquipment => "exoskeleton-equipment"
| .explosiveCannonShell => "explosive-cannon-shell"
| .explosiveRocket => "explosive-rocket"
| .explosiveUraniumCannonShell => "explosive-uranium-cannon-shell"
| .explosives => "explosives"
| .expressLoader => "express-loader"
| .expressSplitter => "express-splitter"
| .expressTransportBelt => "express-transport-belt"
| .expressUndergroundBelt => "express-underground-belt"
| .fastInserter => "fast-inserter"
| .fastLoader => "fast-loader"
| .fastSplitter => "fast-splitter"
| .fastTransportBelt => "fast-transport-belt"
| .fastUndergroundBelt => "fast-underground-belt"
| .firearmMagazine => "firearm-magazine"
| .fissionReactorEquipment => "fission-reactor-equipment"
| .flamethrower => "flamethrower"
| .flamethrowerAmmo => "flamethrower-ammo"
| .flamethrowerTurret => "flamethrower-turret"
| .fluidWagon => "fluid-wagon"
| .fluorine => "fluorine"
| .fluoroketoneCold => "fluoroketone-cold"
| .fluoroketoneColdBarrel => "fluoroketone-cold-barrel"
| .fluoroketoneHot => "fluoroketone-hot"
| .fluoroketoneHotBarrel => "fluoroketone-hot-barrel"
| .flyingRobotFrame => "flying-robot-frame"
| .foundation => "foundation"
| .foundry => "foundry"
| .fusionGenerator => "fusion-generator"
| .fusionPowerCell => "fusion-power-cell"
| .fusionReactor => "fusion-reactor"
| .fusionReactorEquipment => "fusion-reactor-equipment"
| .gate => "gate"
| .grenade => "grenade"
| .gunTurret => "gun-turret"
| .hazardConcrete => "hazard-concrete"
| .heatExchanger => "heat-exchanger"
| .heatInterface => "heat-interface"
| .heatPipe => "heat-pipe"
| .heatingTower => "heating-tower"
| .heavyArmor => "heavy-armor"
| .heavyOil => "heavy-oil"
| .heavyOilBarrel => "heavy-oil-barrel"
| .holmiumOre => "holmium-ore"
| .holmiumPlate => "holmium-plate"
| .holmiumSolution => "holmium-solution"
| .ice => "ice"
| .icePlatform => "ice-platform"
| .infinityCargoWagon => "infinity-cargo-wagon"
| .infinityChest => "infinity-chest"
| .infinityPipe => "infinity-pipe"
| .inserter => "inserter"
| .ironBacteria => "iron-bacteria"
| .ironChest => "iron-chest"
| .ironGearWheel => "iron-gear-wheel"
| .ironOre => "iron-ore"
| .ironPlate => "iron-plate"
| .ironStick => "iron-stick"
| .itemUnknown => "item-unknown"
| .jelly => "jelly"
| .jellynut => "jellynut"
| .jellynutSeed => "jellynut-seed"
| .lab => "lab"
| .landMine => "land-mine"
| .landfill => "landfill"
| .laneSplitter => "lane-splitter"
| .laserTurret => "laser-turret"
| .lava => "lava"
| .lightArmor => "light-armor"
| .lightOil => "light-oil"
| .lightOilBarrel => "light-oil-barrel"
| .lightningCollector => "lightning-collector"
| .lightningRod => "lightning-rod"
| .linkedBelt => "linked-belt"
| .linkedChest => "linked-chest"
| .lithium => "lithium"
| .lithiumBrine => "lithium-brine"
| .lithiumPlate => "lithium-plate"
| .loader => "loader"
| .locomotive => "locomotive"
| .logisticRobot => "logistic-robot"
| .logisticSciencePack => "logistic-science-pack"
| .longHandedInserter => "long-handed-inserter"
| .lowDensityStructure => "low-density-structure"
| .lubricant => "lubricant"
| .lubricantBarrel => "lubricant-barrel"
| .mechArmor => "mech-armor"
| .mediumElectricPole => "medium-electric-pole"
| .metallicAsteroidChunk => "metallic-asteroid-chunk"
| .metallurgicSciencePack => "metallurgic-science-pack"
| .militarySciencePack => "military-science-pack"
| .modularArmor => "modular-armor"
| .moltenCopper => "molten-copper"
| .moltenIron => "molten-iron"
| .nightVisionEquipment => "night-vision-equipment"
| .nuclearFuel => "nuclear-fuel"
| .nuclearReactor => "nuclear-reactor"
| .nutrients => "nutrients"
| .offshorePump => "offshore-pump"
| .oilRefinery => "oil-refinery"
| .oneWayValve => "one-way-valve"
| .overflowValve => "overflow-valve"
| .overgrowthJellynutSoil => "overgrowth-jellynut-soil"
| .overgrowthYumakoSoil => "overgrowth-yumako-soil"
| .oxideAsteroidChunk => "oxide-asteroid-chunk"
| .passiveProviderChest => "passive-provider-chest"
| .pentapodEgg => "pentapod-egg"
| .personalLaserDefenseEquipment => "personal-laser-defense-equipment"
| .personalRoboportEquipment => "personal-roboport-equipment"
| .personalRoboportMk2Equipment => "personal-roboport-mk2-equipment"
| .petroleumGas => "petroleum-gas"
| .petroleumGasBarrel => "petroleum-gas-barrel"
| .piercingRoundsMagazine => "piercing-rounds-magazine"
| .piercingShotgunShell => "piercing-shotgun-shell"
| .pipe => "pipe"
| .pipeToGround => "pipe-to-ground"
| .pistol => "pistol"
| .plasticBar => "plastic-bar"
| .poisonCapsule => "poison-capsule"
| .powerArmor => "power-armor"
| .powerArmorMk2 => "power-armor-mk2"
| .powerSwitch => "power-switch"
| .processingUnit => "processing-unit"
| .productionSciencePack => "production-science-pack"
| .productivityModule => "productivity-module"
| .productivityModule2 => "productivity-module-2"
| .productivityModule3 => "productivity-module-3"
| .programmableSpeaker => "programmable-speaker"
| .promethiumAsteroidChunk => "promethium-asteroid-chunk"
| .promethiumSciencePack => "promethium-science-pack"
| .proxyContainer => "proxy-container"
| .pump => "pump"
| .pumpjack => "pumpjack"
| .qualityModule => "quality-module"
| .qualityModule2 => "quality-module-2"
| .qualityModule3 => "quality-module-3"
| .quantumProcessor => "quantum-processor"
| .radar => "radar"
| .rail => "rail"
| .railChainSignal => "rail-chain-signal"
| .railRamp => "rail-ramp"
| .railSignal => "rail-signal"
| .railSupport => "rail-support"
| .railgun => "railgun"
| .railgunAmmo => "railgun-ammo"
| .railgunTurret => "railgun-turret"
| .rawFish => "raw-fish"
| .recycler => "recycler"
| .refinedConcrete => "refined-concrete"
| .refinedHazardConcrete => "refined-hazard-concrete"
| .repairPack => "repair-pack"
| .requesterChest => "requester-chest"
| .roboport => "roboport"
| .rocket => "rocket"
| .rocketFuel => "rocket-fuel"
| .rocketLauncher => "rocket-launcher"
| .rocketPart => "rocket-part"
| .rocketSilo => "rocket-silo"
| .rocketTurret => "rocket-turret"
| .science => "science"
| .scrap => "scrap"
| .selectionTool => "selection-tool"
| .selectorCombinator => "selector-combinator"
| .shotgun => "shotgun"
| .shotgunShell => "shotgun-shell"
| .simpleEntityWithForce => "simple-entity-with-force"
| .simpleEntityWithOwner => "simple-entity-with-owner"
| .slowdownCapsule => "slowdown-capsule"
| .smallElectricPole => "small-electric-pole"
| .smallLamp => "small-lamp"
| .solarPanel => "solar-panel"
| .solarPanelEquipment => "solar-panel-equipment"
| .solidFuel => "solid-fuel"
| .spacePlatformFoundation => "space-platform-foundation"
| .spacePlatformHub => "space-platform-hub"
| .spacePlatformStarterPack => "space-platform-starter-pack"
| .spaceSciencePack => "space-science-pack"
| .speedModule => "speed-module"
| .speedModule2 => "speed-module-2"
| .speedModule3 => "speed-module-3"
| .spidertron => "spidertron"
| .splitter => "splitter"
| .spoilage => "spoilage"
| .stackInserter => "stack-inserter"
| .steam => "steam"
| .steamEngine => "steam-engine"
| .steamTurbine => "steam-turbine"
| .steelChest => "steel-chest"
| .steelFurnace => "steel-furnace"
| .steelPlate => "steel-plate"
| .stone => "stone"
| .stoneBrick => "stone-brick"
| .stoneFurnace => "stone-furnace"
| .stoneWall => "stone-wall"
| .storageChest => "storage-chest"
| .storageTank => "storage-tank"
| .submachineGun => "submachine-gun"
| .substation => "substation"
| .sulfur => "sulfur"
| .sulfuricAcid => "sulfuric-acid"
| .sulfuricAcidBarrel => "sulfuric-acid-barrel"
| .supercapacitor => "supercapacitor"
| .superconductor => "superconductor"
| .tank => "tank"
| .teslaAmmo => "tesla-ammo"
| .teslaTurret => "tesla-turret"
| .teslagun => "teslagun"
| .thruster => "thruster"
| .thrusterFuel => "thruster-fuel"
| .thrusterOxidizer => "thruster-oxidizer"
| .toolbeltEquipment => "toolbelt-equipment"
| .topUpValve => "top-up-valve"
| .trainStop => "train-stop"
| .transportBelt => "transport-belt"
| .treeSeed => "tree-seed"
| .tungstenCarbide => "tungsten-carbide"
| .tungstenOre => "tungsten-ore"
| .tungstenPlate => "tungsten-plate"
| .turboLoader => "turbo-loader"
| .turboSplitter => "turbo-splitter"
| .turboTransportBelt => "turbo-transport-belt"
| .turboUndergroundBelt => "turbo-underground-belt"
| .undergroundBelt => "underground-belt"
| .upgradePlanner => "upgrade-planner"
| .uranium235 => "uranium-235"
| .uranium238 => "uranium-238"
| .uraniumCannonShell => "uranium-cannon-shell"
| .uraniumFuelCell => "uranium-fuel-cell"
| .uraniumOre => "uranium-ore"
| .uraniumRoundsMagazine => "uranium-rounds-magazine"
| .utilitySciencePack => "utility-science-pack"
| .water => "water"
| .waterBarrel => "water-barrel"
| .wood => "wood"
| .woodenChest => "wooden-chest"
| .yumako => "yumako"
| .yumakoMash => "yumako-mash"
| .yumakoSeed => "yumako-seed"
end Ingredient
structure Recipe where
name: String
-- The `Fraction` indicates how many items are needed to execute the recipe.
inputs : List (Fraction × Ingredient)
-- The `Fraction` indicates how many output items are generated by executing the recipe.
outputs : List (Fraction × Ingredient)
category : RecipeCategory
-- Number of seconds that it takes the user to execute the recipe.
time : Fraction
deriving DecidableEq, Repr, Inhabited
inductive FluidBoxType where
| input
| output
| inputOutput
deriving DecidableEq, Repr, Inhabited
structure FluidBox where
side: Direction
offset: Nat
type: FluidBoxType
deriving DecidableEq, Repr, Inhabited
structure FabricatorDetails where
name : String
width : Nat
height : Nat
speedup : Fraction
productivity : Fraction
moduleSlots : Nat
fluidBoxes : List FluidBox
categories : List RecipeCategory
deriving DecidableEq, Repr, Inhabited
inductive RecipeName
| accumulator
| accumulatorRecycling
| acidNeutralisation
| activeProviderChest
| activeProviderChestRecycling
| advancedCarbonicAsteroidCrushing
| advancedCircuit
| advancedCircuitRecycling
| advancedMetallicAsteroidCrushing
| advancedOilProcessing
| advancedOxideAsteroidCrushing
| advancedThrusterFuel
| advancedThrusterOxidizer
| agriculturalSciencePack
| agriculturalSciencePackRecycling
| agriculturalTower
| agriculturalTowerRecycling
| ammoniaRocketFuel
| ammoniacalSolutionSeparation
| arithmeticCombinator
| arithmeticCombinatorRecycling
| artificialJellynutSoil
| artificialJellynutSoilRecycling
| artificialYumakoSoil
| artificialYumakoSoilRecycling
| artilleryShell
| artilleryShellRecycling
| artilleryTurret
| artilleryTurretRecycling
| artilleryWagon
| artilleryWagonRecycling
| assemblingMachine1
| assemblingMachine1Recycling
| assemblingMachine2
| assemblingMachine2Recycling
| assemblingMachine3
| assemblingMachine3Recycling
| asteroidCollector
| asteroidCollectorRecycling
| atomicBomb
| atomicBombRecycling
| automationSciencePack
| automationSciencePackRecycling
| barrel
| barrelRecycling
| basicOilProcessing
| battery
| batteryEquipment
| batteryEquipmentRecycling
| batteryMk2Equipment
| batteryMk2EquipmentRecycling
| batteryMk3Equipment
| batteryMk3EquipmentRecycling
| batteryRecycling
| beacon
| beaconRecycling
| beltImmunityEquipment
| beltImmunityEquipmentRecycling
| bigElectricPole
| bigElectricPoleRecycling
| bigMiningDrill
| bigMiningDrillRecycling
| biochamber
| biochamberRecycling
| bioflux
| biofluxRecycling
| biolab
| biolabRecycling
| biolubricant
| bioplastic
| biosulfur
| biterEgg
| biterEggRecycling
| blueprintBookRecycling
| blueprintRecycling
| boiler
| boilerRecycling
| bottomlessChestRecycling
| bufferChest
| bufferChestRecycling
| bulkInserter
| bulkInserterRecycling
| burnerGeneratorRecycling
| burnerInserter
| burnerInserterRecycling
| burnerMiningDrill
| burnerMiningDrillRecycling
| burntSpoilage
| calciteRecycling
| cannonShell
| cannonShellRecycling
| captiveBiterSpawner
| captiveBiterSpawnerRecycling
| captureRobotRocket
| captureRobotRocketRecycling
| car
| carRecycling
| carbon
| carbonFiber
| carbonFiberRecycling
| carbonRecycling
| carbonicAsteroidChunkRecycling
| carbonicAsteroidCrushing
| carbonicAsteroidReprocessing
| cargoBay
| cargoBayRecycling
| cargoLandingPad
| cargoLandingPadRecycling
| cargoWagon
| cargoWagonRecycling
| castingCopper
| castingCopperCable
| castingIron
| castingIronGearWheel
| castingIronStick
| castingLowDensityStructure
| castingPipe
| castingPipeToGround
| castingSteel
| centrifuge
| centrifugeRecycling
| chemicalPlant
| chemicalPlantRecycling
| chemicalSciencePack
| chemicalSciencePackRecycling
| cliffExplosives
| cliffExplosivesRecycling
| clusterGrenade
| clusterGrenadeRecycling
| coalLiquefaction
| coalRecycling
| coalSynthesis
| coinRecycling
| combatShotgun
| combatShotgunRecycling
| concrete
| concreteFromMoltenIron
| concreteRecycling
| constantCombinator
| constantCombinatorRecycling
| constructionRobot
| constructionRobotRecycling
| copperBacteria
| copperBacteriaCultivation
| copperBacteriaRecycling
| copperCable
| copperCableRecycling
| copperOreRecycling
| copperPlate
| copperPlateRecycling
| crudeOilBarrel
| crudeOilBarrelRecycling
| crusher
| crusherRecycling
| cryogenicPlant
| cryogenicPlantRecycling
| cryogenicSciencePack
| cryogenicSciencePackRecycling
| deciderCombinator
| deciderCombinatorRecycling
| deconstructionPlannerRecycling
| defenderCapsule
| defenderCapsuleRecycling
| depletedUraniumFuelCellRecycling
| destroyerCapsule
| destroyerCapsuleRecycling
| dischargeDefenseEquipment
| dischargeDefenseEquipmentRecycling
| displayPanel
| displayPanelRecycling
| distractorCapsule
| distractorCapsuleRecycling
| efficiencyModule
| efficiencyModule2
| efficiencyModule2Recycling
| efficiencyModule3
| efficiencyModule3Recycling
| efficiencyModuleRecycling
| electricEnergyInterfaceRecycling
| electricEngineUnit
| electricEngineUnitRecycling
| electricFurnace
| electricFurnaceRecycling
| electricMiningDrill
| electricMiningDrillRecycling
| electrolyte
| electromagneticPlant
| electromagneticPlantRecycling
| electromagneticSciencePack
| electromagneticSciencePackRecycling
| electronicCircuit
| electronicCircuitRecycling
| emptyCrudeOilBarrel
| emptyFluoroketoneColdBarrel
| emptyFluoroketoneHotBarrel
| emptyHeavyOilBarrel
| emptyLightOilBarrel
| emptyLubricantBarrel
| emptyModuleSlotRecycling
| emptyPetroleumGasBarrel
| emptySulfuricAcidBarrel
| emptyWaterBarrel
| energyShieldEquipment
| energyShieldEquipmentRecycling
| energyShieldMk2Equipment
| energyShieldMk2EquipmentRecycling
| engineUnit
| engineUnitRecycling
| exoskeletonEquipment
| exoskeletonEquipmentRecycling
| explosiveCannonShell
| explosiveCannonShellRecycling
| explosiveRocket
| explosiveRocketRecycling
| explosiveUraniumCannonShell
| explosiveUraniumCannonShellRecycling
| explosives
| explosivesRecycling
| expressLoader
| expressLoaderRecycling
| expressSplitter
| expressSplitterRecycling
| expressTransportBelt
| expressTransportBeltRecycling
| expressUndergroundBelt
| expressUndergroundBeltRecycling
| fastInserter
| fastInserterRecycling
| fastLoader
| fastLoaderRecycling
| fastSplitter
| fastSplitterRecycling
| fastTransportBelt
| fastTransportBeltRecycling
| fastUndergroundBelt
| fastUndergroundBeltRecycling
| firearmMagazine
| firearmMagazineRecycling
| fishBreeding
| fissionReactorEquipment
| fissionReactorEquipmentRecycling
| flamethrower
| flamethrowerAmmo
| flamethrowerAmmoRecycling
| flamethrowerRecycling
| flamethrowerTurret
| flamethrowerTurretRecycling
| fluidWagon
| fluidWagonRecycling
| fluoroketone
| fluoroketoneColdBarrel
| fluoroketoneColdBarrelRecycling
| fluoroketoneCooling
| fluoroketoneHotBarrel
| fluoroketoneHotBarrelRecycling
| flyingRobotFrame
| flyingRobotFrameRecycling
| foundation
| foundationRecycling
| foundry
| foundryRecycling
| fusionGenerator
| fusionGeneratorRecycling
| fusionPowerCell
| fusionPowerCellRecycling
| fusionReactor
| fusionReactorEquipment
| fusionReactorEquipmentRecycling
| fusionReactorRecycling
| gate
| gateRecycling
| grenade
| grenadeRecycling
| gunTurret
| gunTurretRecycling
| hazardConcrete
| hazardConcreteRecycling
| heatExchanger
| heatExchangerRecycling
| heatInterface
| heatInterfaceRecycling
| heatPipe
| heatPipeRecycling
| heatingTower
| heatingTowerRecycling
| heavyArmor
| heavyArmorRecycling
| heavyOilBarrel
| heavyOilBarrelRecycling
| heavyOilCracking
| holmiumOreRecycling
| holmiumPlate
| holmiumPlateRecycling
| holmiumSolution
| iceMelting
| icePlatform
| icePlatformRecycling
| iceRecycling
| infinityCargoWagonRecycling
| infinityChest
| infinityChestRecycling
| infinityPipe
| infinityPipeRecycling
| inserter
| inserterRecycling
| ironBacteria
| ironBacteriaCultivation
| ironBacteriaRecycling
| ironChest
| ironChestRecycling
| ironGearWheel
| ironGearWheelRecycling
| ironOreRecycling
| ironPlate
| ironPlateRecycling
| ironStick
| ironStickRecycling
| itemUnknownRecycling
| jellyRecycling
| jellynutProcessing
| jellynutRecycling
| jellynutSeedRecycling
| kovarexEnrichmentProcess
| lab
| labRecycling
| landMine
| landMineRecycling
| landfill
| landfillRecycling
| laneSplitterRecycling
| laserTurret
| laserTurretRecycling
| lightArmor
| lightArmorRecycling
| lightOilBarrel
| lightOilBarrelRecycling
| lightOilCracking
| lightningCollector
| lightningCollectorRecycling
| lightningRod
| lightningRodRecycling
| linkedBeltRecycling
| linkedChestRecycling
| lithium
| lithiumPlate
| lithiumPlateRecycling
| lithiumRecycling
| loader
| loaderRecycling
| locomotive
| locomotiveRecycling
| logisticRobot
| logisticRobotRecycling
| logisticSciencePack
| logisticSciencePackRecycling
| longHandedInserter
| longHandedInserterRecycling
| lowDensityStructure
| lowDensityStructureRecycling
| lubricant
| lubricantBarrel
| lubricantBarrelRecycling
| mechArmor
| mechArmorRecycling
| mediumElectricPole
| mediumElectricPoleRecycling
| metallicAsteroidChunkRecycling
| metallicAsteroidCrushing
| metallicAsteroidReprocessing
| metallurgicSciencePack
| metallurgicSciencePackRecycling
| militarySciencePack
| militarySciencePackRecycling
| modularArmor
| modularArmorRecycling
| moltenCopper
| moltenCopperFromLava
| moltenIron
| moltenIronFromLava
| nightVisionEquipment
| nightVisionEquipmentRecycling
| nuclearFuel
| nuclearFuelRecycling
| nuclearFuelReprocessing
| nuclearReactor
| nuclearReactorRecycling
| nutrientsFromBioflux
| nutrientsFromBiterEgg
| nutrientsFromFish
| nutrientsFromSpoilage
| nutrientsFromYumakoMash
| nutrientsRecycling
| offshorePump
| offshorePumpRecycling
| oilRefinery
| oilRefineryRecycling
| oneWayValveRecycling
| overflowValveRecycling
| overgrowthJellynutSoil
| overgrowthJellynutSoilRecycling
| overgrowthYumakoSoil
| overgrowthYumakoSoilRecycling
| oxideAsteroidChunkRecycling
| oxideAsteroidCrushing
| oxideAsteroidReprocessing
| parameter0
| parameter1
| parameter2
| parameter3
| parameter4
| parameter5
| parameter6
| parameter7
| parameter8
| parameter9
| passiveProviderChest
| passiveProviderChestRecycling
| pentapodEgg
| pentapodEggRecycling
| personalLaserDefenseEquipment
| personalLaserDefenseEquipmentRecycling
| personalRoboportEquipment
| personalRoboportEquipmentRecycling
| personalRoboportMk2Equipment
| personalRoboportMk2EquipmentRecycling
| petroleumGasBarrel
| petroleumGasBarrelRecycling
| piercingRoundsMagazine
| piercingRoundsMagazineRecycling
| piercingShotgunShell
| piercingShotgunShellRecycling
| pipe
| pipeRecycling
| pipeToGround
| pipeToGroundRecycling
| pistol
| pistolRecycling
| plasticBar
| plasticBarRecycling
| poisonCapsule
| poisonCapsuleRecycling
| powerArmor
| powerArmorMk2
| powerArmorMk2Recycling
| powerArmorRecycling
| powerSwitch
| powerSwitchRecycling
| processingUnit
| processingUnitRecycling
| productionSciencePack
| productionSciencePackRecycling
| productivityModule
| productivityModule2
| productivityModule2Recycling
| productivityModule3
| productivityModule3Recycling
| productivityModuleRecycling
| programmableSpeaker
| programmableSpeakerRecycling
| promethiumAsteroidChunkRecycling
| promethiumSciencePack
| promethiumSciencePackRecycling
| proxyContainerRecycling
| pump
| pumpRecycling
| pumpjack
| pumpjackRecycling
| qualityModule
| qualityModule2
| qualityModule2Recycling
| qualityModule3
| qualityModule3Recycling
| qualityModuleRecycling
| quantumProcessor
| quantumProcessorRecycling
| radar
| radarRecycling
| rail
| railChainSignal
| railChainSignalRecycling
| railRamp
| railRampRecycling
| railRecycling
| railSignal
| railSignalRecycling
| railSupport
| railSupportRecycling
| railgun
| railgunAmmo
| railgunAmmoRecycling
| railgunRecycling
| railgunTurret
| railgunTurretRecycling
| rawFishRecycling
| recycler
| recyclerRecycling
| refinedConcrete
| refinedConcreteRecycling
| refinedHazardConcrete
| refinedHazardConcreteRecycling
| repairPack
| repairPackRecycling
| requesterChest
| requesterChestRecycling
| roboport
| roboportRecycling
| rocket
| rocketFuel
| rocketFuelFromJelly
| rocketFuelRecycling
| rocketLauncher
| rocketLauncherRecycling
| rocketPart
| rocketRecycling
| rocketSilo
| rocketSiloRecycling
| rocketTurret
| rocketTurretRecycling
| scienceRecycling
| scrapRecycling
| selectionToolRecycling
| selectorCombinator
| selectorCombinatorRecycling
| shotgun
| shotgunRecycling
| shotgunShell
| shotgunShellRecycling
| simpleCoalLiquefaction
| simpleEntityWithForceRecycling
| simpleEntityWithOwnerRecycling
| slowdownCapsule
| slowdownCapsuleRecycling
| smallElectricPole
| smallElectricPoleRecycling
| smallLamp
| smallLampRecycling
| solarPanel
| solarPanelEquipment
| solarPanelEquipmentRecycling
| solarPanelRecycling
| solidFuelFromAmmonia
| solidFuelFromHeavyOil
| solidFuelFromLightOil
| solidFuelFromPetroleumGas
| solidFuelRecycling
| spacePlatformFoundation
| spacePlatformFoundationRecycling
| spacePlatformHubRecycling
| spacePlatformStarterPack
| spacePlatformStarterPackRecycling
| spaceSciencePack
| spaceSciencePackRecycling
| speedModule
| speedModule2
| speedModule2Recycling
| speedModule3
| speedModule3Recycling
| speedModuleRecycling
| spidertron
| spidertronRecycling
| splitter
| splitterRecycling
| spoilageRecycling
| stackInserter
| stackInserterRecycling
| steamCondensation
| steamEngine
| steamEngineRecycling
| steamTurbine
| steamTurbineRecycling
| steelChest
| steelChestRecycling
| steelFurnace
| steelFurnaceRecycling
| steelPlate
| steelPlateRecycling
| stoneBrick
| stoneBrickRecycling
| stoneFurnace
| stoneFurnaceRecycling
| stoneRecycling
| stoneWall
| stoneWallRecycling
| storageChest
| storageChestRecycling
| storageTank
| storageTankRecycling
| submachineGun
| submachineGunRecycling
| substation
| substationRecycling
| sulfur
| sulfurRecycling
| sulfuricAcid
| sulfuricAcidBarrel
| sulfuricAcidBarrelRecycling
| supercapacitor
| supercapacitorRecycling
| superconductor
| superconductorRecycling
| tank
| tankRecycling
| teslaAmmo
| teslaAmmoRecycling
| teslaTurret
| teslaTurretRecycling
| teslagun
| teslagunRecycling
| thruster
| thrusterFuel
| thrusterOxidizer
| thrusterRecycling
| toolbeltEquipment
| toolbeltEquipmentRecycling
| topUpValveRecycling
| trainStop
| trainStopRecycling
| transportBelt
| transportBeltRecycling
| treeSeedRecycling
| tungstenCarbide
| tungstenCarbideRecycling
| tungstenOreRecycling
| tungstenPlate
| tungstenPlateRecycling
| turboLoader
| turboLoaderRecycling
| turboSplitter
| turboSplitterRecycling
| turboTransportBelt
| turboTransportBeltRecycling
| turboUndergroundBelt
| turboUndergroundBeltRecycling
| undergroundBelt
| undergroundBeltRecycling
| upgradePlannerRecycling
| uranium235Recycling
| uranium238Recycling
| uraniumCannonShell
| uraniumCannonShellRecycling
| uraniumFuelCell
| uraniumFuelCellRecycling
| uraniumOreRecycling
| uraniumProcessing
| uraniumRoundsMagazine
| uraniumRoundsMagazineRecycling
| utilitySciencePack
| utilitySciencePackRecycling
| waterBarrel
| waterBarrelRecycling
| woodProcessing
| woodRecycling
| woodenChest
| woodenChestRecycling
| yumakoMashRecycling
| yumakoProcessing
| yumakoRecycling
| yumakoSeedRecycling
deriving DecidableEq, Repr, Inhabited
namespace RecipeName
def getRecipe : RecipeName -> Recipe
| .accumulator => {
name := "accumulator",
inputs := [(2, .ironPlate), (5, .battery)],
outputs := [(1, .accumulator)],
category := .electronics
time := 10
}
| .accumulatorRecycling => {
name := "accumulator-recycling",
inputs := [(1, .accumulator)],
outputs := [(5/4, .battery), (1/2, .ironPlate)],
category := .recycling
time := 5/8
}
| .acidNeutralisation => {
name := "acid-neutralisation",
inputs := [(1000, .sulfuricAcid), (1, .calcite)],
outputs := [(10000, .steam)],
category := .chemistryOrCryogenics
time := 5
}
| .activeProviderChest => {
name := "active-provider-chest",
inputs := [(1, .steelChest), (3, .electronicCircuit), (1, .advancedCircuit)],
outputs := [(1, .activeProviderChest)],
category := .crafting
time := 1/2
}
| .activeProviderChestRecycling => {
name := "active-provider-chest-recycling",
inputs := [(1, .activeProviderChest)],
outputs := [(3/4, .electronicCircuit), (1/4, .steelChest), (1/4, .advancedCircuit)],
category := .recycling
time := 1/32
}
| .advancedCarbonicAsteroidCrushing => {
name := "advanced-carbonic-asteroid-crushing",
inputs := [(1, .carbonicAsteroidChunk)],
outputs := [(5, .carbon), (2, .sulfur), (1/20, .carbonicAsteroidChunk)],
category := .crushing
time := 5
}
| .advancedCircuit => {
name := "advanced-circuit",
inputs := [(2, .electronicCircuit), (2, .plasticBar), (4, .copperCable)],
outputs := [(1, .advancedCircuit)],
category := .electronics
time := 6
}
| .advancedCircuitRecycling => {
name := "advanced-circuit-recycling",
inputs := [(1, .advancedCircuit)],
outputs := [(1, .copperCable), (1/2, .electronicCircuit), (1/2, .plasticBar)],
category := .recycling
time := 3/8
}
| .advancedMetallicAsteroidCrushing => {
name := "advanced-metallic-asteroid-crushing",
inputs := [(1, .metallicAsteroidChunk)],
outputs := [(10, .ironOre), (4, .copperOre), (1/20, .metallicAsteroidChunk)],
category := .crushing
time := 5
}
| .advancedOilProcessing => {
name := "advanced-oil-processing",
inputs := [(50, .water), (100, .crudeOil)],
outputs := [(25, .heavyOil), (45, .lightOil), (55, .petroleumGas)],
category := .oilProcessing
time := 5
}
| .advancedOxideAsteroidCrushing => {
name := "advanced-oxide-asteroid-crushing",
inputs := [(1, .oxideAsteroidChunk)],
outputs := [(3, .ice), (2, .calcite), (1/20, .oxideAsteroidChunk)],
category := .crushing
time := 5
}
| .advancedThrusterFuel => {
name := "advanced-thruster-fuel",
inputs := [(100, .water), (2, .carbon), (1, .calcite)],
outputs := [(1500, .thrusterFuel)],
category := .chemistry
time := 10
}
| .advancedThrusterOxidizer => {
name := "advanced-thruster-oxidizer",
inputs := [(100, .water), (2, .ironOre), (1, .calcite)],
outputs := [(1500, .thrusterOxidizer)],
category := .chemistry
time := 10
}
| .agriculturalSciencePack => {
name := "agricultural-science-pack",
inputs := [(1, .bioflux), (1, .pentapodEgg)],
outputs := [(1, .agriculturalSciencePack)],
category := .organic
time := 4
}
| .agriculturalSciencePackRecycling => {
name := "agricultural-science-pack-recycling",
inputs := [(1, .agriculturalSciencePack)],
outputs := [(1/4, .agriculturalSciencePack)],
category := .recycling
time := 1/4
}
| .agriculturalTower => {
name := "agricultural-tower",
inputs := [(10, .steelPlate), (3, .electronicCircuit), (20, .spoilage), (1, .landfill)],
outputs := [(1, .agriculturalTower)],
category := .crafting
time := 10
}
| .agriculturalTowerRecycling => {
name := "agricultural-tower-recycling",
inputs := [(1, .agriculturalTower)],
outputs := [(5, .spoilage), (5/2, .steelPlate), (3/4, .electronicCircuit), (1/4, .landfill)],
category := .recycling
time := 5/8
}
| .ammoniaRocketFuel => {
name := "ammonia-rocket-fuel",
inputs := [(50, .water), (500, .ammonia), (10, .solidFuel)],
outputs := [(1, .rocketFuel)],
category := .chemistryOrCryogenics
time := 10
}
| .ammoniacalSolutionSeparation => {
name := "ammoniacal-solution-separation",
inputs := [(50, .ammoniacalSolution)],
outputs := [(5, .ice), (50, .ammonia)],
category := .chemistryOrCryogenics
time := 1
}
| .arithmeticCombinator => {
name := "arithmetic-combinator",
inputs := [(5, .copperCable), (5, .electronicCircuit)],
outputs := [(1, .arithmeticCombinator)],
category := .crafting
time := 1/2
}
| .arithmeticCombinatorRecycling => {
name := "arithmetic-combinator-recycling",
inputs := [(1, .arithmeticCombinator)],
outputs := [(5/4, .copperCable), (5/4, .electronicCircuit)],
category := .recycling
time := 1/32
}
| .artificialJellynutSoil => {
name := "artificial-jellynut-soil",
inputs := [(2, .jellynutSeed), (50, .nutrients), (5, .landfill)],
outputs := [(10, .artificialJellynutSoil)],
category := .crafting
time := 2
}
| .artificialJellynutSoilRecycling => {
name := "artificial-jellynut-soil-recycling",
inputs := [(1, .artificialJellynutSoil)],
outputs := [(5/4, .nutrients), (1/8, .landfill), (1/20, .jellynutSeed)],
category := .recycling
time := 1/8
}
| .artificialYumakoSoil => {
name := "artificial-yumako-soil",
inputs := [(2, .yumakoSeed), (50, .nutrients), (5, .landfill)],
outputs := [(10, .artificialYumakoSoil)],
category := .crafting
time := 2
}
| .artificialYumakoSoilRecycling => {
name := "artificial-yumako-soil-recycling",
inputs := [(1, .artificialYumakoSoil)],
outputs := [(5/4, .nutrients), (1/8, .landfill), (1/20, .yumakoSeed)],
category := .recycling
time := 1/8
}
| .artilleryShell => {
name := "artillery-shell",
inputs := [(1, .radar), (1, .calcite), (4, .tungstenPlate), (8, .explosives)],
outputs := [(1, .artilleryShell)],
category := .crafting
time := 15
}
| .artilleryShellRecycling => {
name := "artillery-shell-recycling",
inputs := [(1, .artilleryShell)],
outputs := [(2, .explosives), (1, .tungstenPlate), (1/4, .radar), (1/4, .calcite)],
category := .recycling
time := 15/16
}
| .artilleryTurret => {
name := "artillery-turret",
inputs := [(60, .tungstenPlate), (60, .refinedConcrete), (40, .ironGearWheel), (10, .processingUnit)],
outputs := [(1, .artilleryTurret)],
category := .crafting
time := 40
}
| .artilleryTurretRecycling => {
name := "artillery-turret-recycling",
inputs := [(1, .artilleryTurret)],
outputs := [(15, .tungstenPlate), (15, .refinedConcrete), (10, .ironGearWheel), (5/2, .processingUnit)],
category := .recycling
time := 5/2
}
| .artilleryWagon => {
name := "artillery-wagon",
inputs := [(60, .engineUnit), (60, .tungstenPlate), (60, .refinedConcrete), (40, .ironGearWheel), (10, .processingUnit)],
outputs := [(1, .artilleryWagon)],
category := .crafting
time := 4
}
| .artilleryWagonRecycling => {
name := "artillery-wagon-recycling",
inputs := [(1, .artilleryWagon)],
outputs := [(15, .engineUnit), (15, .tungstenPlate), (15, .refinedConcrete), (10, .ironGearWheel), (5/2, .processingUnit)],
category := .recycling
time := 1/4
}
| .assemblingMachine1 => {
name := "assembling-machine-1",
inputs := [(3, .electronicCircuit), (5, .ironGearWheel), (9, .ironPlate)],
outputs := [(1, .assemblingMachine1)],
category := .crafting
time := 1/2
}
| .assemblingMachine1Recycling => {
name := "assembling-machine-1-recycling",
inputs := [(1, .assemblingMachine1)],
outputs := [(9/4, .ironPlate), (5/4, .ironGearWheel), (3/4, .electronicCircuit)],
category := .recycling
time := 1/32
}
| .assemblingMachine2 => {
name := "assembling-machine-2",
inputs := [(2, .steelPlate), (3, .electronicCircuit), (5, .ironGearWheel), (1, .assemblingMachine1)],
outputs := [(1, .assemblingMachine2)],
category := .crafting
time := 1/2
}
| .assemblingMachine2Recycling => {
name := "assembling-machine-2-recycling",
inputs := [(1, .assemblingMachine2)],
outputs := [(5/4, .ironGearWheel), (3/4, .electronicCircuit), (1/2, .steelPlate), (1/4, .assemblingMachine1)],
category := .recycling
time := 1/32
}
| .assemblingMachine3 => {
name := "assembling-machine-3",
inputs := [(2, .assemblingMachine2), (4, .speedModule)],
outputs := [(1, .assemblingMachine3)],
category := .crafting
time := 1/2
}
| .assemblingMachine3Recycling => {
name := "assembling-machine-3-recycling",
inputs := [(1, .assemblingMachine3)],
outputs := [(1, .speedModule), (1/2, .assemblingMachine2)],
category := .recycling
time := 1/32
}
| .asteroidCollector => {
name := "asteroid-collector",
inputs := [(20, .lowDensityStructure), (8, .electricEngineUnit), (5, .processingUnit)],
outputs := [(1, .asteroidCollector)],
category := .crafting
time := 10
}
| .asteroidCollectorRecycling => {
name := "asteroid-collector-recycling",
inputs := [(1, .asteroidCollector)],
outputs := [(5, .lowDensityStructure), (2, .electricEngineUnit), (5/4, .processingUnit)],
category := .recycling
time := 5/8
}
| .atomicBomb => {
name := "atomic-bomb",
inputs := [(10, .processingUnit), (10, .explosives), (100, .uranium235)],
outputs := [(1, .atomicBomb)],
category := .crafting
time := 50
}
| .atomicBombRecycling => {
name := "atomic-bomb-recycling",
inputs := [(1, .atomicBomb)],
outputs := [(25, .uranium235), (5/2, .processingUnit), (5/2, .explosives)],
category := .recycling
time := 25/8
}
| .automationSciencePack => {
name := "automation-science-pack",
inputs := [(1, .copperPlate), (1, .ironGearWheel)],
outputs := [(1, .automationSciencePack)],
category := .crafting
time := 5
}
| .automationSciencePackRecycling => {
name := "automation-science-pack-recycling",
inputs := [(1, .automationSciencePack)],
outputs := [(1/4, .automationSciencePack)],
category := .recycling
time := 5/16
}
| .barrel => {
name := "barrel",
inputs := [(1, .steelPlate)],
outputs := [(1, .barrel)],
category := .crafting
time := 1
}
| .barrelRecycling => {
name := "barrel-recycling",
inputs := [(1, .barrel)],
outputs := [(1/4, .steelPlate)],
category := .recycling
time := 1/16
}
| .basicOilProcessing => {
name := "basic-oil-processing",
inputs := [(100, .crudeOil)],
outputs := [(45, .petroleumGas)],
category := .oilProcessing
time := 5
}
| .battery => {
name := "battery",
inputs := [(20, .sulfuricAcid), (1, .ironPlate), (1, .copperPlate)],
outputs := [(1, .battery)],
category := .chemistryOrCryogenics
time := 4
}
| .batteryEquipment => {
name := "battery-equipment",
inputs := [(5, .battery), (10, .steelPlate)],
outputs := [(1, .batteryEquipment)],
category := .crafting
time := 10
}
| .batteryEquipmentRecycling => {
name := "battery-equipment-recycling",
inputs := [(1, .batteryEquipment)],
outputs := [(5/2, .steelPlate), (5/4, .battery)],
category := .recycling
time := 5/8
}
| .batteryMk2Equipment => {
name := "battery-mk2-equipment",
inputs := [(10, .batteryEquipment), (15, .processingUnit), (5, .lowDensityStructure)],
outputs := [(1, .batteryMk2Equipment)],
category := .crafting
time := 10
}
| .batteryMk2EquipmentRecycling => {
name := "battery-mk2-equipment-recycling",
inputs := [(1, .batteryMk2Equipment)],
outputs := [(15/4, .processingUnit), (5/2, .batteryEquipment), (5/4, .lowDensityStructure)],
category := .recycling
time := 5/8
}
| .batteryMk3Equipment => {
name := "battery-mk3-equipment",
inputs := [(5, .batteryMk2Equipment), (10, .supercapacitor)],
outputs := [(1, .batteryMk3Equipment)],
category := .crafting
time := 10
}
| .batteryMk3EquipmentRecycling => {
name := "battery-mk3-equipment-recycling",
inputs := [(1, .batteryMk3Equipment)],
outputs := [(5/2, .supercapacitor), (5/4, .batteryMk2Equipment)],
category := .recycling
time := 5/8
}
| .batteryRecycling => {
name := "battery-recycling",
inputs := [(1, .battery)],
outputs := [(1/4, .ironPlate), (1/4, .copperPlate)],
category := .recycling
time := 1/4
}
| .beacon => {
name := "beacon",
inputs := [(20, .electronicCircuit), (20, .advancedCircuit), (10, .steelPlate), (10, .copperCable)],
outputs := [(1, .beacon)],
category := .electronics
time := 15
}
| .beaconRecycling => {
name := "beacon-recycling",
inputs := [(1, .beacon)],
outputs := [(5, .electronicCircuit), (5, .advancedCircuit), (5/2, .steelPlate), (5/2, .copperCable)],
category := .recycling
time := 15/16
}
| .beltImmunityEquipment => {
name := "belt-immunity-equipment",
inputs := [(5, .advancedCircuit), (10, .steelPlate)],
outputs := [(1, .beltImmunityEquipment)],
category := .crafting
time := 10
}
| .beltImmunityEquipmentRecycling => {
name := "belt-immunity-equipment-recycling",
inputs := [(1, .beltImmunityEquipment)],
outputs := [(5/2, .steelPlate), (5/4, .advancedCircuit)],
category := .recycling
time := 5/8
}
| .bigElectricPole => {
name := "big-electric-pole",
inputs := [(8, .ironStick), (5, .steelPlate), (4, .copperCable)],
outputs := [(1, .bigElectricPole)],
category := .electronics
time := 1/2
}
| .bigElectricPoleRecycling => {
name := "big-electric-pole-recycling",
inputs := [(1, .bigElectricPole)],
outputs := [(2, .ironStick), (5/4, .steelPlate), (1, .copperCable)],
category := .recycling
time := 1/32
}
| .bigMiningDrill => {
name := "big-mining-drill",
inputs := [(200, .moltenIron), (1, .electricMiningDrill), (20, .tungstenCarbide), (10, .electricEngineUnit), (10, .advancedCircuit)],
outputs := [(1, .bigMiningDrill)],
category := .metallurgy
time := 30
}
| .bigMiningDrillRecycling => {
name := "big-mining-drill-recycling",
inputs := [(1, .bigMiningDrill)],
outputs := [(5, .tungstenCarbide), (5/2, .electricEngineUnit), (5/2, .advancedCircuit), (1/4, .electricMiningDrill)],
category := .recycling
time := 15/8
}
| .biochamber => {
name := "biochamber",
inputs := [(5, .nutrients), (1, .pentapodEgg), (20, .ironPlate), (5, .electronicCircuit), (1, .landfill)],
outputs := [(1, .biochamber)],
category := .organicOrAssembling
time := 20
}
| .biochamberRecycling => {
name := "biochamber-recycling",
inputs := [(1, .biochamber)],
outputs := [(5, .ironPlate), (5/4, .nutrients), (5/4, .electronicCircuit), (1/4, .pentapodEgg), (1/4, .landfill)],
category := .recycling
time := 5/4
}
| .bioflux => {
name := "bioflux",
inputs := [(15, .yumakoMash), (12, .jelly)],
outputs := [(4, .bioflux)],
category := .organic
time := 6
}
| .biofluxRecycling => {
name := "bioflux-recycling",
inputs := [(1, .bioflux)],
outputs := [(1/4, .bioflux)],
category := .recycling
time := 3/8
}
| .biolab => {
name := "biolab",
inputs := [(1, .lab), (10, .biterEgg), (25, .refinedConcrete), (2, .captureRobotRocket), (3, .uranium235)],
outputs := [(1, .biolab)],
category := .crafting
time := 10
}
| .biolabRecycling => {
name := "biolab-recycling",
inputs := [(1, .biolab)],
outputs := [(1/4, .biolab)],
category := .recycling
time := 5/8
}
| .biolubricant => {
name := "biolubricant",
inputs := [(60, .jelly)],
outputs := [(20, .lubricant)],
category := .organic
time := 3
}
| .bioplastic => {
name := "bioplastic",
inputs := [(1, .bioflux), (4, .yumakoMash)],
outputs := [(3, .plasticBar)],
category := .organic
time := 2
}
| .biosulfur => {
name := "biosulfur",
inputs := [(5, .spoilage), (1, .bioflux)],
outputs := [(2, .sulfur)],
category := .organic
time := 2
}
| .biterEgg => {
name := "biter-egg",
inputs := [],
outputs := [(5, .biterEgg)],
category := .captiveSpawnerProcess
time := 10
}
| .biterEggRecycling => {
name := "biter-egg-recycling",
inputs := [(1, .biterEgg)],
outputs := [(1/4, .biterEgg)],
category := .recycling
time := 5/8
}
| .blueprintBookRecycling => {
name := "blueprint-book-recycling",
inputs := [(1, .blueprintBook)],
outputs := [(1/4, .blueprintBook)],
category := .recycling
time := 1/32
}
| .blueprintRecycling => {
name := "blueprint-recycling",
inputs := [(1, .blueprint)],
outputs := [(1/4, .blueprint)],
category := .recycling
time := 1/32
}
| .boiler => {
name := "boiler",
inputs := [(1, .stoneFurnace), (4, .pipe)],
outputs := [(1, .boiler)],
category := .crafting
time := 1/2
}
| .boilerRecycling => {
name := "boiler-recycling",
inputs := [(1, .boiler)],
outputs := [(1, .pipe), (1/4, .stoneFurnace)],
category := .recycling
time := 1/32
}
| .bottomlessChestRecycling => {
name := "bottomless-chest-recycling",
inputs := [(1, .bottomlessChest)],
outputs := [(1/4, .bottomlessChest)],
category := .recycling
time := 1/32
}
| .bufferChest => {
name := "buffer-chest",
inputs := [(1, .steelChest), (3, .electronicCircuit), (1, .advancedCircuit)],
outputs := [(1, .bufferChest)],
category := .crafting
time := 1/2
}
| .bufferChestRecycling => {
name := "buffer-chest-recycling",
inputs := [(1, .bufferChest)],
outputs := [(3/4, .electronicCircuit), (1/4, .steelChest), (1/4, .advancedCircuit)],
category := .recycling
time := 1/32
}
| .bulkInserter => {
name := "bulk-inserter",
inputs := [(15, .ironGearWheel), (15, .electronicCircuit), (1, .advancedCircuit), (1, .fastInserter)],
outputs := [(1, .bulkInserter)],
category := .crafting
time := 1/2
}
| .bulkInserterRecycling => {
name := "bulk-inserter-recycling",
inputs := [(1, .bulkInserter)],
outputs := [(15/4, .ironGearWheel), (15/4, .electronicCircuit), (1/4, .advancedCircuit), (1/4, .fastInserter)],
category := .recycling
time := 1/32
}
| .burnerGeneratorRecycling => {
name := "burner-generator-recycling",
inputs := [(1, .burnerGenerator)],
outputs := [(1/4, .burnerGenerator)],
category := .recycling
time := 1/32
}
| .burnerInserter => {
name := "burner-inserter",
inputs := [(1, .ironPlate), (1, .ironGearWheel)],
outputs := [(1, .burnerInserter)],
category := .crafting
time := 1/2
}
| .burnerInserterRecycling => {
name := "burner-inserter-recycling",
inputs := [(1, .burnerInserter)],
outputs := [(1/4, .ironPlate), (1/4, .ironGearWheel)],
category := .recycling
time := 1/32
}
| .burnerMiningDrill => {
name := "burner-mining-drill",
inputs := [(3, .ironGearWheel), (1, .stoneFurnace), (3, .ironPlate)],
outputs := [(1, .burnerMiningDrill)],
category := .crafting
time := 2
}
| .burnerMiningDrillRecycling => {
name := "burner-mining-drill-recycling",
inputs := [(1, .burnerMiningDrill)],
outputs := [(3/4, .ironGearWheel), (3/4, .ironPlate), (1/4, .stoneFurnace)],
category := .recycling
time := 1/8
}
| .burntSpoilage => {
name := "burnt-spoilage",
inputs := [(6, .spoilage)],
outputs := [(1, .carbon)],
category := .organic
time := 12
}
| .calciteRecycling => {
name := "calcite-recycling",
inputs := [(1, .calcite)],
outputs := [(1/4, .calcite)],
category := .recycling
time := 1/32
}
| .cannonShell => {
name := "cannon-shell",
inputs := [(2, .steelPlate), (2, .plasticBar), (1, .explosives)],
outputs := [(1, .cannonShell)],
category := .crafting
time := 8
}
| .cannonShellRecycling => {
name := "cannon-shell-recycling",
inputs := [(1, .cannonShell)],
outputs := [(1/2, .steelPlate), (1/2, .plasticBar), (1/4, .explosives)],
category := .recycling
time := 1/2
}
| .captiveBiterSpawner => {
name := "captive-biter-spawner",
inputs := [(100, .fluoroketoneCold), (10, .biterEgg), (1, .captureRobotRocket), (15, .uranium235)],
outputs := [(1, .captiveBiterSpawner)],
category := .cryogenics
time := 10
}
| .captiveBiterSpawnerRecycling => {
name := "captive-biter-spawner-recycling",
inputs := [(1, .captiveBiterSpawner)],
outputs := [(1/4, .captiveBiterSpawner)],
category := .recycling
time := 5/8
}
| .captureRobotRocket => {
name := "capture-robot-rocket",
inputs := [(1, .flyingRobotFrame), (2, .steelPlate), (20, .bioflux), (2, .processingUnit)],
outputs := [(1, .captureRobotRocket)],
category := .crafting
time := 10
}
| .captureRobotRocketRecycling => {
name := "capture-robot-rocket-recycling",
inputs := [(1, .captureRobotRocket)],
outputs := [(5, .bioflux), (1/2, .steelPlate), (1/2, .processingUnit), (1/4, .flyingRobotFrame)],
category := .recycling
time := 5/8
}
| .car => {
name := "car",
inputs := [(8, .engineUnit), (20, .ironPlate), (5, .steelPlate)],
outputs := [(1, .car)],
category := .crafting
time := 2
}
| .carRecycling => {
name := "car-recycling",
inputs := [(1, .car)],
outputs := [(5, .ironPlate), (2, .engineUnit), (5/4, .steelPlate)],
category := .recycling
time := 1/8
}
| .carbon => {
name := "carbon",
inputs := [(20, .sulfuricAcid), (2, .coal)],
outputs := [(1, .carbon)],
category := .chemistryOrCryogenics
time := 1
}
| .carbonFiber => {
name := "carbon-fiber",
inputs := [(10, .yumakoMash), (1, .carbon)],
outputs := [(1, .carbonFiber)],
category := .organic
time := 5
}
| .carbonFiberRecycling => {
name := "carbon-fiber-recycling",
inputs := [(1, .carbonFiber)],
outputs := [(1/4, .carbonFiber)],
category := .recycling
time := 5/16
}
| .carbonRecycling => {
name := "carbon-recycling",
inputs := [(1, .carbon)],
outputs := [(1/4, .carbon)],
category := .recycling
time := 1/16
}
| .carbonicAsteroidChunkRecycling => {
name := "carbonic-asteroid-chunk-recycling",
inputs := [(1, .carbonicAsteroidChunk)],
outputs := [(1/4, .carbonicAsteroidChunk)],
category := .recycling
time := 1/32
}
| .carbonicAsteroidCrushing => {
name := "carbonic-asteroid-crushing",
inputs := [(1, .carbonicAsteroidChunk)],
outputs := [(10, .carbon), (1/5, .carbonicAsteroidChunk)],
category := .crushing
time := 2
}
| .carbonicAsteroidReprocessing => {
name := "carbonic-asteroid-reprocessing",
inputs := [(1, .carbonicAsteroidChunk)],
outputs := [(2/5, .carbonicAsteroidChunk), (1/5, .metallicAsteroidChunk), (1/5, .oxideAsteroidChunk)],
category := .crushing
time := 2
}
| .cargoBay => {
name := "cargo-bay",
inputs := [(20, .steelPlate), (20, .lowDensityStructure), (5, .processingUnit)],
outputs := [(1, .cargoBay)],
category := .crafting
time := 10
}
| .cargoBayRecycling => {
name := "cargo-bay-recycling",
inputs := [(1, .cargoBay)],
outputs := [(5, .steelPlate), (5, .lowDensityStructure), (5/4, .processingUnit)],
category := .recycling
time := 5/8
}
| .cargoLandingPad => {
name := "cargo-landing-pad",
inputs := [(200, .concrete), (25, .steelPlate), (10, .processingUnit)],
outputs := [(1, .cargoLandingPad)],
category := .crafting
time := 30
}
| .cargoLandingPadRecycling => {
name := "cargo-landing-pad-recycling",
inputs := [(1, .cargoLandingPad)],
outputs := [(50, .concrete), (25/4, .steelPlate), (5/2, .processingUnit)],
category := .recycling
time := 15/8
}
| .cargoWagon => {
name := "cargo-wagon",
inputs := [(10, .ironGearWheel), (20, .ironPlate), (20, .steelPlate)],
outputs := [(1, .cargoWagon)],
category := .crafting
time := 1
}
| .cargoWagonRecycling => {
name := "cargo-wagon-recycling",
inputs := [(1, .cargoWagon)],
outputs := [(5, .ironPlate), (5, .steelPlate), (5/2, .ironGearWheel)],
category := .recycling
time := 1/16
}
| .castingCopper => {
name := "casting-copper",
inputs := [(20, .moltenCopper)],
outputs := [(2, .copperPlate)],
category := .metallurgy
time := 16/5
}
| .castingCopperCable => {
name := "casting-copper-cable",
inputs := [(5, .moltenCopper)],
outputs := [(2, .copperCable)],
category := .metallurgy
time := 1
}
| .castingIron => {
name := "casting-iron",
inputs := [(20, .moltenIron)],
outputs := [(2, .ironPlate)],
category := .metallurgy
time := 16/5
}
| .castingIronGearWheel => {
name := "casting-iron-gear-wheel",
inputs := [(10, .moltenIron)],
outputs := [(1, .ironGearWheel)],
category := .metallurgy
time := 1
}
| .castingIronStick => {
name := "casting-iron-stick",
inputs := [(20, .moltenIron)],
outputs := [(4, .ironStick)],
category := .metallurgy
time := 1
}
| .castingLowDensityStructure => {
name := "casting-low-density-structure",
inputs := [(80, .moltenIron), (250, .moltenCopper), (5, .plasticBar)],
outputs := [(1, .lowDensityStructure)],
category := .metallurgy
time := 15
}
| .castingPipe => {
name := "casting-pipe",
inputs := [(10, .moltenIron)],
outputs := [(1, .pipe)],
category := .metallurgy
time := 1
}
| .castingPipeToGround => {
name := "casting-pipe-to-ground",
inputs := [(50, .moltenIron), (10, .pipe)],
outputs := [(2, .pipeToGround)],
category := .metallurgy
time := 1
}
| .castingSteel => {
name := "casting-steel",
inputs := [(30, .moltenIron)],
outputs := [(1, .steelPlate)],
category := .metallurgy
time := 16/5
}
| .centrifuge => {
name := "centrifuge",
inputs := [(100, .concrete), (50, .steelPlate), (100, .advancedCircuit), (100, .ironGearWheel)],
outputs := [(1, .centrifuge)],
category := .crafting
time := 4
}
| .centrifugeRecycling => {
name := "centrifuge-recycling",
inputs := [(1, .centrifuge)],
outputs := [(25, .concrete), (25, .advancedCircuit), (25, .ironGearWheel), (25/2, .steelPlate)],
category := .recycling
time := 1/4
}
| .chemicalPlant => {
name := "chemical-plant",
inputs := [(5, .steelPlate), (5, .ironGearWheel), (5, .electronicCircuit), (5, .pipe)],
outputs := [(1, .chemicalPlant)],
category := .crafting
time := 5
}
| .chemicalPlantRecycling => {
name := "chemical-plant-recycling",
inputs := [(1, .chemicalPlant)],
outputs := [(5/4, .steelPlate), (5/4, .ironGearWheel), (5/4, .electronicCircuit), (5/4, .pipe)],
category := .recycling
time := 5/16
}
| .chemicalSciencePack => {
name := "chemical-science-pack",
inputs := [(2, .engineUnit), (3, .advancedCircuit), (1, .sulfur)],
outputs := [(2, .chemicalSciencePack)],
category := .crafting
time := 24
}
| .chemicalSciencePackRecycling => {
name := "chemical-science-pack-recycling",
inputs := [(1, .chemicalSciencePack)],
outputs := [(1/4, .chemicalSciencePack)],
category := .recycling
time := 3/2
}
| .cliffExplosives => {
name := "cliff-explosives",
inputs := [(10, .explosives), (10, .calcite), (1, .grenade), (1, .barrel)],
outputs := [(1, .cliffExplosives)],
category := .crafting
time := 8
}
| .cliffExplosivesRecycling => {
name := "cliff-explosives-recycling",
inputs := [(1, .cliffExplosives)],
outputs := [(5/2, .explosives), (5/2, .calcite), (1/4, .grenade), (1/4, .barrel)],
category := .recycling
time := 1/2
}
| .clusterGrenade => {
name := "cluster-grenade",
inputs := [(7, .grenade), (5, .explosives), (5, .steelPlate)],
outputs := [(1, .clusterGrenade)],
category := .crafting
time := 8
}
| .clusterGrenadeRecycling => {
name := "cluster-grenade-recycling",
inputs := [(1, .clusterGrenade)],
outputs := [(7/4, .grenade), (5/4, .explosives), (5/4, .steelPlate)],
category := .recycling
time := 1/2
}
| .coalLiquefaction => {
name := "coal-liquefaction",
inputs := [(25, .heavyOil), (50, .steam), (10, .coal)],
outputs := [(90, .heavyOil), (20, .lightOil), (10, .petroleumGas)],
category := .oilProcessing
time := 5
}
| .coalRecycling => {
name := "coal-recycling",
inputs := [(1, .coal)],
outputs := [(1/4, .coal)],
category := .recycling
time := 1/32
}
| .coalSynthesis => {
name := "coal-synthesis",
inputs := [(10, .water), (5, .carbon), (1, .sulfur)],
outputs := [(1, .coal)],
category := .chemistry
time := 2
}
| .coinRecycling => {
name := "coin-recycling",
inputs := [(1, .coin)],
outputs := [(1/4, .coin)],
category := .recycling
time := 1/32
}
| .combatShotgun => {
name := "combat-shotgun",
inputs := [(15, .steelPlate), (5, .ironGearWheel), (10, .copperPlate), (10, .wood)],
outputs := [(1, .combatShotgun)],
category := .crafting
time := 10
}
| .combatShotgunRecycling => {
name := "combat-shotgun-recycling",
inputs := [(1, .combatShotgun)],
outputs := [(15/4, .steelPlate), (5/2, .copperPlate), (5/2, .wood), (5/4, .ironGearWheel)],
category := .recycling
time := 5/8
}
| .concrete => {
name := "concrete",
inputs := [(100, .water), (5, .stoneBrick), (1, .ironOre)],
outputs := [(10, .concrete)],
category := .craftingWithFluid
time := 10
}
| .concreteFromMoltenIron => {
name := "concrete-from-molten-iron",
inputs := [(20, .moltenIron), (100, .water), (5, .stoneBrick)],
outputs := [(10, .concrete)],
category := .metallurgy
time := 10
}
| .concreteRecycling => {
name := "concrete-recycling",
inputs := [(1, .concrete)],
outputs := [(1/8, .stoneBrick), (1/40, .ironOre)],
category := .recycling
time := 5/8
}
| .constantCombinator => {
name := "constant-combinator",
inputs := [(5, .copperCable), (2, .electronicCircuit)],
outputs := [(1, .constantCombinator)],
category := .crafting
time := 1/2
}
| .constantCombinatorRecycling => {
name := "constant-combinator-recycling",
inputs := [(1, .constantCombinator)],
outputs := [(5/4, .copperCable), (1/2, .electronicCircuit)],
category := .recycling
time := 1/32
}
| .constructionRobot => {
name := "construction-robot",
inputs := [(1, .flyingRobotFrame), (2, .electronicCircuit)],
outputs := [(1, .constructionRobot)],
category := .crafting
time := 1/2
}
| .constructionRobotRecycling => {
name := "construction-robot-recycling",
inputs := [(1, .constructionRobot)],
outputs := [(1/2, .electronicCircuit), (1/4, .flyingRobotFrame)],
category := .recycling
time := 1/32
}
| .copperBacteria => {
name := "copper-bacteria",
inputs := [(3, .yumakoMash)],
outputs := [(1/10, .copperBacteria), (1, .spoilage)],
category := .organicOrHandCrafting
time := 1
}
| .copperBacteriaCultivation => {
name := "copper-bacteria-cultivation",
inputs := [(1, .copperBacteria), (1, .bioflux)],
outputs := [(4, .copperBacteria)],
category := .organic
time := 4
}
| .copperBacteriaRecycling => {
name := "copper-bacteria-recycling",
inputs := [(1, .copperBacteria)],
outputs := [(1/4, .copperBacteria)],
category := .recycling
time := 1/16
}
| .copperCable => {
name := "copper-cable",
inputs := [(1, .copperPlate)],
outputs := [(2, .copperCable)],
category := .electronics
time := 1/2
}
| .copperCableRecycling => {
name := "copper-cable-recycling",
inputs := [(1, .copperCable)],
outputs := [(1/8, .copperPlate)],
category := .recycling
time := 1/32
}
| .copperOreRecycling => {
name := "copper-ore-recycling",
inputs := [(1, .copperOre)],
outputs := [(1/4, .copperOre)],
category := .recycling
time := 1/32
}
| .copperPlate => {
name := "copper-plate",
inputs := [(1, .copperOre)],
outputs := [(1, .copperPlate)],
category := .smelting
time := 16/5
}
| .copperPlateRecycling => {
name := "copper-plate-recycling",
inputs := [(1, .copperPlate)],
outputs := [(1/4, .copperPlate)],
category := .recycling
time := 1/5
}
| .crudeOilBarrel => {
name := "crude-oil-barrel",
inputs := [(50, .crudeOil), (1, .barrel)],
outputs := [(1, .crudeOilBarrel)],
category := .craftingWithFluid
time := 1/5
}
| .crudeOilBarrelRecycling => {
name := "crude-oil-barrel-recycling",
inputs := [(1, .crudeOilBarrel)],
outputs := [(1/4, .barrel)],
category := .recycling
time := 1/80
}
| .crusher => {
name := "crusher",
inputs := [(20, .lowDensityStructure), (10, .steelPlate), (10, .electricEngineUnit)],
outputs := [(1, .crusher)],
category := .crafting
time := 10
}
| .crusherRecycling => {
name := "crusher-recycling",
inputs := [(1, .crusher)],
outputs := [(5, .lowDensityStructure), (5/2, .steelPlate), (5/2, .electricEngineUnit)],
category := .recycling
time := 5/8
}
| .cryogenicPlant => {
name := "cryogenic-plant",
inputs := [(40, .refinedConcrete), (20, .superconductor), (20, .processingUnit), (20, .lithiumPlate)],
outputs := [(1, .cryogenicPlant)],
category := .cryogenicsOrAssembling
time := 10
}
| .cryogenicPlantRecycling => {
name := "cryogenic-plant-recycling",
inputs := [(1, .cryogenicPlant)],
outputs := [(10, .refinedConcrete), (5, .superconductor), (5, .processingUnit), (5, .lithiumPlate)],
category := .recycling
time := 5/8
}
| .cryogenicSciencePack => {
name := "cryogenic-science-pack",
inputs := [(6, .fluoroketoneCold), (3, .ice), (1, .lithiumPlate)],
outputs := [(1, .cryogenicSciencePack), (3, .fluoroketoneHot)],
category := .cryogenics
time := 20
}
| .cryogenicSciencePackRecycling => {
name := "cryogenic-science-pack-recycling",
inputs := [(1, .cryogenicSciencePack)],
outputs := [(1/4, .cryogenicSciencePack)],
category := .recycling
time := 5/4
}
| .deciderCombinator => {
name := "decider-combinator",
inputs := [(5, .copperCable), (5, .electronicCircuit)],
outputs := [(1, .deciderCombinator)],
category := .crafting
time := 1/2
}
| .deciderCombinatorRecycling => {
name := "decider-combinator-recycling",
inputs := [(1, .deciderCombinator)],
outputs := [(5/4, .copperCable), (5/4, .electronicCircuit)],
category := .recycling
time := 1/32
}
| .deconstructionPlannerRecycling => {
name := "deconstruction-planner-recycling",
inputs := [(1, .deconstructionPlanner)],
outputs := [(1/4, .deconstructionPlanner)],
category := .recycling
time := 1/32
}
| .defenderCapsule => {
name := "defender-capsule",
inputs := [(3, .piercingRoundsMagazine), (3, .electronicCircuit), (3, .ironGearWheel)],
outputs := [(1, .defenderCapsule)],
category := .crafting
time := 8
}
| .defenderCapsuleRecycling => {
name := "defender-capsule-recycling",
inputs := [(1, .defenderCapsule)],
outputs := [(3/4, .piercingRoundsMagazine), (3/4, .electronicCircuit), (3/4, .ironGearWheel)],
category := .recycling
time := 1/2
}
| .depletedUraniumFuelCellRecycling => {
name := "depleted-uranium-fuel-cell-recycling",
inputs := [(1, .depletedUraniumFuelCell)],
outputs := [(1/4, .depletedUraniumFuelCell)],
category := .recycling
time := 1/32
}
| .destroyerCapsule => {
name := "destroyer-capsule",
inputs := [(4, .distractorCapsule), (4, .steelPlate), (1, .processingUnit)],
outputs := [(1, .destroyerCapsule)],
category := .crafting
time := 15
}
| .destroyerCapsuleRecycling => {
name := "destroyer-capsule-recycling",
inputs := [(1, .destroyerCapsule)],
outputs := [(1, .distractorCapsule), (1, .steelPlate), (1/4, .processingUnit)],
category := .recycling
time := 15/16
}
| .dischargeDefenseEquipment => {
name := "discharge-defense-equipment",
inputs := [(5, .processingUnit), (20, .steelPlate), (10, .laserTurret)],
outputs := [(1, .dischargeDefenseEquipment)],
category := .electronics
time := 10
}
| .dischargeDefenseEquipmentRecycling => {
name := "discharge-defense-equipment-recycling",
inputs := [(1, .dischargeDefenseEquipment)],
outputs := [(5, .steelPlate), (5/2, .laserTurret), (5/4, .processingUnit)],
category := .recycling
time := 5/8
}
| .displayPanel => {
name := "display-panel",
inputs := [(1, .ironPlate), (1, .electronicCircuit)],
outputs := [(1, .displayPanel)],
category := .crafting
time := 1/2
}
| .displayPanelRecycling => {
name := "display-panel-recycling",
inputs := [(1, .displayPanel)],
outputs := [(1/4, .ironPlate), (1/4, .electronicCircuit)],
category := .recycling
time := 1/32
}
| .distractorCapsule => {
name := "distractor-capsule",
inputs := [(4, .defenderCapsule), (3, .advancedCircuit)],
outputs := [(1, .distractorCapsule)],
category := .crafting
time := 15
}
| .distractorCapsuleRecycling => {
name := "distractor-capsule-recycling",
inputs := [(1, .distractorCapsule)],
outputs := [(1, .defenderCapsule), (3/4, .advancedCircuit)],
category := .recycling
time := 15/16
}
| .efficiencyModule => {
name := "efficiency-module",
inputs := [(5, .advancedCircuit), (5, .electronicCircuit)],
outputs := [(1, .efficiencyModule)],
category := .electronics
time := 15
}
| .efficiencyModule2 => {
name := "efficiency-module-2",
inputs := [(4, .efficiencyModule), (5, .advancedCircuit), (5, .processingUnit)],
outputs := [(1, .efficiencyModule2)],
category := .electronics
time := 30
}
| .efficiencyModule2Recycling => {
name := "efficiency-module-2-recycling",
inputs := [(1, .efficiencyModule2)],
outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (1, .efficiencyModule)],
category := .recycling
time := 15/8
}
| .efficiencyModule3 => {
name := "efficiency-module-3",
inputs := [(4, .efficiencyModule2), (5, .advancedCircuit), (5, .processingUnit), (5, .spoilage)],
outputs := [(1, .efficiencyModule3)],
category := .electronics
time := 60
}
| .efficiencyModule3Recycling => {
name := "efficiency-module-3-recycling",
inputs := [(1, .efficiencyModule3)],
outputs := [(5/4, .advancedCircuit), (5/4, .processingUnit), (5/4, .spoilage), (1, .efficiencyModule2)],
category := .recycling
time := 15/4
}
| .efficiencyModuleRecycling => {
name := "efficiency-module-recycling",
inputs := [(1, .efficiencyModule)],
outputs := [(5/4, .advancedCircuit), (5/4, .electronicCircuit)],
category := .recycling
time := 15/16
}
| .electricEnergyInterfaceRecycling => {
name := "electric-energy-interface-recycling",
inputs := [(1, .electricEnergyInterface)],
outputs := [(1/4, .electricEnergyInterface)],
category := .recycling
time := 1/32
}
| .electricEngineUnit => {
name := "electric-engine-unit",
inputs := [(15, .lubricant), (1, .engineUnit), (2, .electronicCircuit)],
outputs := [(1, .electricEngineUnit)],
category := .craftingWithFluid
time := 10
}
| .electricEngineUnitRecycling => {
name := "electric-engine-unit-recycling",
inputs := [(1, .electricEngineUnit)],
outputs := [(1/2, .electronicCircuit), (1/4, .engineUnit)],
category := .recycling
time := 5/8
}
| .electricFurnace => {
name := "electric-furnace",
inputs := [(10, .steelPlate), (5, .advancedCircuit), (10, .stoneBrick)],
outputs := [(1, .electricFurnace)],
category := .crafting
time := 5
}
| .electricFurnaceRecycling => {
name := "electric-furnace-recycling",
inputs := [(1, .electricFurnace)],
outputs := [(5/2, .steelPlate), (5/2, .stoneBrick), (5/4, .advancedCircuit)],
category := .recycling
time := 5/16
}
| .electricMiningDrill => {
name := "electric-mining-drill",
inputs := [(3, .electronicCircuit), (5, .ironGearWheel), (10, .ironPlate)],
outputs := [(1, .electricMiningDrill)],
category := .crafting
time := 2
}
| .electricMiningDrillRecycling => {
name := "electric-mining-drill-recycling",
inputs := [(1, .electricMiningDrill)],
outputs := [(5/2, .ironPlate), (5/4, .ironGearWheel), (3/4, .electronicCircuit)],
category := .recycling
time := 1/8
}
| .electrolyte => {
name := "electrolyte",
inputs := [(10, .heavyOil), (10, .holmiumSolution), (1, .stone)],
outputs := [(10, .electrolyte)],
category := .electromagnetics
time := 5
}
| .electromagneticPlant => {
name := "electromagnetic-plant",
inputs := [(150, .holmiumPlate), (50, .steelPlate), (50, .processingUnit), (50, .refinedConcrete)],
outputs := [(1, .electromagneticPlant)],
category := .electronicsOrAssembling
time := 10
}
| .electromagneticPlantRecycling => {
name := "electromagnetic-plant-recycling",
inputs := [(1, .electromagneticPlant)],
outputs := [(75/2, .holmiumPlate), (25/2, .steelPlate), (25/2, .processingUnit), (25/2, .refinedConcrete)],
category := .recycling
time := 5/8
}
| .electromagneticSciencePack => {
name := "electromagnetic-science-pack",
inputs := [(25, .electrolyte), (25, .holmiumSolution), (1, .supercapacitor), (1, .accumulator)],
outputs := [(1, .electromagneticSciencePack)],
category := .electromagnetics
time := 10
}
| .electromagneticSciencePackRecycling => {
name := "electromagnetic-science-pack-recycling",
inputs := [(1, .electromagneticSciencePack)],
outputs := [(1/4, .electromagneticSciencePack)],
category := .recycling
time := 5/8
}
| .electronicCircuit => {
name := "electronic-circuit",
inputs := [(1, .ironPlate), (3, .copperCable)],
outputs := [(1, .electronicCircuit)],
category := .electronics
time := 1/2
}
| .electronicCircuitRecycling => {
name := "electronic-circuit-recycling",
inputs := [(1, .electronicCircuit)],
outputs := [(3/4, .copperCable), (1/4, .ironPlate)],
category := .recycling
time := 1/32
}
| .emptyCrudeOilBarrel => {
name := "empty-crude-oil-barrel",
inputs := [(1, .crudeOilBarrel)],
outputs := [(1, .barrel), (50, .crudeOil)],
category := .craftingWithFluid
time := 1/5
}
| .emptyFluoroketoneColdBarrel => {
name := "empty-fluoroketone-cold-barrel",
inputs := [(1, .fluoroketoneColdBarrel)],
outputs := [(1, .barrel), (50, .fluoroketoneCold)],
category := .craftingWithFluid
time := 1/5
}
| .emptyFluoroketoneHotBarrel => {
name := "empty-fluoroketone-hot-barrel",
inputs := [(1, .fluoroketoneHotBarrel)],
outputs := [(1, .barrel), (50, .fluoroketoneHot)],
category := .craftingWithFluid
time := 1/5
}
| .emptyHeavyOilBarrel => {
name := "empty-heavy-oil-barrel",
inputs := [(1, .heavyOilBarrel)],
outputs := [(1, .barrel), (50, .heavyOil)],
category := .craftingWithFluid
time := 1/5
}
| .emptyLightOilBarrel => {
name := "empty-light-oil-barrel",
inputs := [(1, .lightOilBarrel)],
outputs := [(1, .barrel), (50, .lightOil)],
category := .craftingWithFluid
time := 1/5
}
| .emptyLubricantBarrel => {
name := "empty-lubricant-barrel",
inputs := [(1, .lubricantBarrel)],
outputs := [(1, .barrel), (50, .lubricant)],
category := .craftingWithFluid
time := 1/5
}
| .emptyModuleSlotRecycling => {
name := "empty-module-slot-recycling",
inputs := [(1, .emptyModuleSlot)],
outputs := [(1/4, .emptyModuleSlot)],
category := .recycling
time := 1/32
}
| .emptyPetroleumGasBarrel => {
name := "empty-petroleum-
gitextract_pckfgd1u/ ├── .devcontainer/ │ ├── Dockerfile │ └── devcontainer.json ├── .gitignore ├── .vscode/ │ └── settings.json ├── Fulgora150.lean ├── Functorio/ │ ├── Abbreviations.lean │ ├── Adapter.lean │ ├── AdapterTest.lean │ ├── Ascii.lean │ ├── AssemblyLine.lean │ ├── AssemblyLineTest.lean │ ├── AssemblyStation.lean │ ├── AssemblyStationTest.lean │ ├── Blueprint.lean │ ├── Bus.lean │ ├── BusTest.lean │ ├── Cap.lean │ ├── Column.lean │ ├── Config.lean │ ├── Crop.lean │ ├── Direction.lean │ ├── Entity.lean │ ├── Expand.lean │ ├── ExpandTest.lean │ ├── Factory.lean │ ├── Fraction.lean │ ├── Readme.lean │ ├── Recipe.lean │ ├── Row.lean │ ├── Test.lean │ └── Util.lean ├── Functorio.lean ├── Gleba300.lean ├── LICENSE ├── Nauvis150.lean ├── README.md ├── RedScience150.lean ├── Rocket.lean ├── Spaceship.lean ├── fulgora-150.sh ├── generate-recipe.py ├── gleba-300.sh ├── lake-manifest.json ├── lakefile.toml ├── lean-toolchain ├── nauvis-150.sh ├── red-science-150.sh ├── rocket.sh └── spaceship.sh
SYMBOL INDEX (2 symbols across 1 files) FILE: generate-recipe.py function to_camel_case (line 8) | def to_camel_case(kebab_case_str: str) -> str: function float_to_fraction (line 12) | def float_to_fraction(f: float) -> str:
Condensed preview — 49 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (396K chars).
[
{
"path": ".devcontainer/Dockerfile",
"chars": 408,
"preview": "# FROM mcr.microsoft.com/devcontainers/universal:2\nFROM mcr.microsoft.com/devcontainers/base:ubuntu\n\nRUN apt-get update "
},
{
"path": ".devcontainer/devcontainer.json",
"chars": 199,
"preview": "{\n \"build\": {\n \"dockerfile\": \"Dockerfile\"\n },\n \"customizations\": {\n \"vscode\": {\n \"exte"
},
{
"path": ".gitignore",
"chars": 5,
"preview": ".lake"
},
{
"path": ".vscode/settings.json",
"chars": 465,
"preview": "{\n \"cSpell.words\": [\n \"biochamber\",\n \"Bioflux\",\n \"biosulfur\",\n \"electromagnetics\",\n "
},
{
"path": "Fulgora150.lean",
"chars": 3591,
"preview": "import Functorio\n\ninstance : Config where\n generateBigPoles := true\n generateRoboports := true\n providerChestCapacity"
},
{
"path": "Functorio/Abbreviations.lean",
"chars": 3695,
"preview": "import Functorio.Bus\n\nabbrev Coal := BusLane .coal\nabbrev Stone := BusLane .stone\nabbrev CopperOre := BusLane .copperOre"
},
{
"path": "Functorio/Adapter.lean",
"chars": 4431,
"preview": "import Functorio.Factory\nimport Functorio.Util\n\nprivate inductive Orientation where\n| vertical\n| horizontalTopLtBot\n| ho"
},
{
"path": "Functorio/AdapterTest.lean",
"chars": 852,
"preview": "import Functorio.Adapter\nimport Functorio.Ascii\n\n#guard (adapterV (interface:=[(.coal,.N), (.coal,.N)]) #v[0,1] #v[0,1])"
},
{
"path": "Functorio/Ascii.lean",
"chars": 3599,
"preview": "import Functorio.Entity\nimport Functorio.Factory\n\n/-\nLegend:\n\n↑ belt\n⤒ belt going under-ground\n↥ belt going above-ground"
},
{
"path": "Functorio/AssemblyLine.lean",
"chars": 10529,
"preview": "import Functorio.Entity\nimport Functorio.Factory\nimport Functorio.Crop\nimport Functorio.Recipe\nimport Functorio.Row\nimpo"
},
{
"path": "Functorio/AssemblyLineTest.lean",
"chars": 4608,
"preview": "import Functorio.AssemblyLine\nimport Functorio.Ascii\n\n#guard (assemblyLine (recipe .advancedCircuit) 3).toAscii == s!\"\n\n"
},
{
"path": "Functorio/AssemblyStation.lean",
"chars": 17731,
"preview": "import Functorio.Recipe\nimport Functorio.Factory\nimport Functorio.Util\nimport Functorio.Row\n\nstructure Process where\n r"
},
{
"path": "Functorio/AssemblyStationTest.lean",
"chars": 2984,
"preview": "import Functorio.AssemblyStation\nimport Functorio.Ascii\n\n#guard (station (recipe .ironPlate)).toAscii == s!\"\n ^ v\n ↑"
},
{
"path": "Functorio/Blueprint.lean",
"chars": 7185,
"preview": "import Lean.Data.Json\nimport Lean.Data.Json.FromToJson\n\nimport Functorio.Entity\nimport Functorio.Factory\n\nopen Lean\nopen"
},
{
"path": "Functorio/Bus.lean",
"chars": 27963,
"preview": "import Functorio.Entity\nimport Functorio.Factory\nimport Functorio.Column\nimport Functorio.Row\nimport Functorio.Fraction\n"
},
{
"path": "Functorio/BusTest.lean",
"chars": 7283,
"preview": "import Functorio.Bus\nimport Functorio.Cap\nimport Functorio.Ascii\n\nnamespace Test\n\n#guard (bus do\n let iron <- inputs 10"
},
{
"path": "Functorio/Cap.lean",
"chars": 1314,
"preview": "import Functorio.Factory\n\ndef capN {n e s w} (f:Factory n e s w) : Factory [] e s w :=\n {\n width:= f.width,\n heig"
},
{
"path": "Functorio/Column.lean",
"chars": 2778,
"preview": "import Functorio.Factory\nimport Functorio.Expand\nimport Functorio.Adapter\nimport Functorio.Util\n\nprivate def columnPerfe"
},
{
"path": "Functorio/Config.lean",
"chars": 180,
"preview": "class Config where\n generateRoboports : Bool := false\n generateBigPoles : Bool := false\n providerChestCapacity : Nat "
},
{
"path": "Functorio/Crop.lean",
"chars": 930,
"preview": "import Functorio.Factory\n\nprivate def cropOption {n e s w} (f:Factory n e s w) : Option (Factory n e s w) :=\n let xs :="
},
{
"path": "Functorio/Direction.lean",
"chars": 91,
"preview": "\ninductive Direction where\n | N\n | E\n | S\n | W\n deriving DecidableEq, Repr, Inhabited\n"
},
{
"path": "Functorio/Entity.lean",
"chars": 5750,
"preview": "import Lean.Data.Json\nimport Lean.Data.Json.Printer\nimport Lean.Data.Json.FromToJson\n\nimport Functorio.Direction\nimport "
},
{
"path": "Functorio/Expand.lean",
"chars": 2571,
"preview": "import Functorio.Factory\n\nprivate def expansionEntity (ingredient:Ingredient) (direction:Direction) (x y:Nat) : Entity :"
},
{
"path": "Functorio/ExpandTest.lean",
"chars": 485,
"preview": "import Functorio.Factory\nimport Functorio.Expand\nimport Functorio.Column\nimport Functorio.Ascii\n\n#guard (\n (@emptyFacto"
},
{
"path": "Functorio/Factory.lean",
"chars": 3901,
"preview": "import Functorio.Entity\nimport Functorio.Util\nimport Functorio.Recipe\n\n-- coordinates increase from N to S, and W to E\n-"
},
{
"path": "Functorio/Fraction.lean",
"chars": 2011,
"preview": "-- We define our structure, because the standard library\n-- adds a proof to the structure. This means that equal\n-- numb"
},
{
"path": "Functorio/Readme.lean",
"chars": 2293,
"preview": "import Functorio.Abbreviations\nimport Functorio.AssemblyLine\nimport Functorio.Bus\n\nnamespace Readme\n\ndef makeIron : Iron"
},
{
"path": "Functorio/Recipe.lean",
"chars": 178693,
"preview": "-- Generated by generate-recipe.py. Do not modify.\n\nimport Functorio.Fraction\n\nimport Functorio.Direction\n\ninductive Ing"
},
{
"path": "Functorio/Row.lean",
"chars": 3189,
"preview": "import Functorio.Factory\nimport Functorio.Expand\nimport Functorio.Util\n\nprivate def rowPerfect {n e s w n' e' s'} (left "
},
{
"path": "Functorio/Test.lean",
"chars": 2755,
"preview": "import Functorio.AssemblyLine\nimport Functorio.Blueprint\nimport Functorio.Column\nimport Functorio.Bus\nimport Functorio.E"
},
{
"path": "Functorio/Util.lean",
"chars": 1087,
"preview": "\ndef error! {T} [Inhabited T] (msg:String) : T :=\n dbg_trace (\"ERROR: \" ++ msg)\n default\n\ndef unimplemented! {T} [Inha"
},
{
"path": "Functorio.lean",
"chars": 646,
"preview": "import Functorio.Abbreviations\nimport Functorio.Adapter\nimport Functorio.AdapterTest\nimport Functorio.Ascii\nimport Funct"
},
{
"path": "Gleba300.lean",
"chars": 14858,
"preview": "import Functorio\nimport Functorio.AssemblyLine\n\ninstance : Config where\n generateBigPoles := true\n generateRoboports :"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "Nauvis150.lean",
"chars": 12970,
"preview": "import Functorio\n\ninstance : Config where\n generateBigPoles := true\n generateRoboports := true\n providerChestCapacity"
},
{
"path": "README.md",
"chars": 11958,
"preview": "# Functorio\n\nFunctorio lets you build your Factorio factories in the Lean programming language; giving you conveniences "
},
{
"path": "RedScience150.lean",
"chars": 800,
"preview": "import Functorio\n\ndef makeIron : IronOre 300 -> Bus (Iron 300) :=\n busAssemblyLine (recipe .ironPlate) 8\n\ndef makeCoppe"
},
{
"path": "Rocket.lean",
"chars": 3199,
"preview": "import Functorio\n\ninstance : Config where\n generateBigPoles := true\n generateRoboports := true\n providerChestCapacity"
},
{
"path": "Spaceship.lean",
"chars": 3052,
"preview": "import Functorio\n\ninstance : Config where\n adapterMinHeight := 3\n\ndef makeWater : Ice 240 -> Bus (Water 4800) :=\n busA"
},
{
"path": "fulgora-150.sh",
"chars": 117,
"preview": "#!/bin/bash\n\nlake build fulgora-150 > /dev/null\necho -n 0; .lake/build/bin/fulgora-150 | pigz -zc | base64 -w0; echo\n"
},
{
"path": "generate-recipe.py",
"chars": 8749,
"preview": "#!/usr/bin/env python3\n\n# Generates the Recipe.lean file from a factorio data dump, get it by passing --dump-data to fac"
},
{
"path": "gleba-300.sh",
"chars": 115,
"preview": "#!/bin/bash\n\nlake build gleba-300 > /dev/null\necho -n 0; .lake/build/bin/gleba-300 | pigz -zc | base64 -w0; echo\n\n\n"
},
{
"path": "lake-manifest.json",
"chars": 115,
"preview": "{\"version\": \"1.1.0\",\n \"packagesDir\": \".lake/packages\",\n \"packages\": [],\n \"name\": \"functorio\",\n \"lakeDir\": \".lake\"}\n"
},
{
"path": "lakefile.toml",
"chars": 456,
"preview": "name = \"functorio\"\nversion = \"0.1.0\"\ndefaultTargets = [\"nauvis-150\"]\n\n[leanOptions]\nautoImplicit = false\n\n[[lean_lib]]\nn"
},
{
"path": "lean-toolchain",
"chars": 25,
"preview": "leanprover/lean4:v4.20.1\n"
},
{
"path": "nauvis-150.sh",
"chars": 115,
"preview": "#!/bin/bash\n\nlake build nauvis-150 > /dev/null\necho -n 0; .lake/build/bin/nauvis-150 | pigz -zc | base64 -w0; echo\n"
},
{
"path": "red-science-150.sh",
"chars": 125,
"preview": "#!/bin/bash\n\nlake build red-science-150 > /dev/null\necho -n 0; .lake/build/bin/red-science-150 | pigz -zc | base64 -w0; "
},
{
"path": "rocket.sh",
"chars": 107,
"preview": "#!/bin/bash\n\nlake build rocket > /dev/null\necho -n 0; .lake/build/bin/rocket | pigz -zc | base64 -w0; echo\n"
},
{
"path": "spaceship.sh",
"chars": 113,
"preview": "#!/bin/bash\n\nlake build spaceship > /dev/null\necho -n 0; .lake/build/bin/spaceship | pigz -zc | base64 -w0; echo\n"
}
]
About this extraction
This page contains the full source code of the konne88/functorio GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 49 files (363.7 KB), approximately 119.6k tokens, and a symbol index with 2 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.