Skip to content

Commit d143117

Browse files
committed
Add an include directive to data files
to include other data files. This lets us easily create 'derivative' data files for specific configurations or tasks. Also let building speed be written as a fraction
1 parent 45a189b commit d143117

File tree

1 file changed

+74
-56
lines changed

1 file changed

+74
-56
lines changed

factoriocalc.py

Lines changed: 74 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
import math
3+
import os
34
import re
45
import sys
56
from collections import OrderedDict, Counter
@@ -10,8 +11,27 @@
1011
from fractions import Fraction
1112

1213

14+
def get_datafile_lines(datafile):
15+
"""Resolve includes and yield (lineno, line) where lineno is a descriptive string of
16+
'line number', eg. for an include it might look like "1:foo:5" for line 5 of file foo
17+
included from line 1."""
18+
with open(datafile) as f:
19+
for n, line in enumerate(f):
20+
if line.startswith('include '):
21+
path = line[len('include '):-1] # -1 for trailing newline
22+
path = os.path.join(os.path.dirname(datafile), path)
23+
for lineno, line in get_datafile_lines(path):
24+
yield '{}:{}:{}'.format(n, path, lineno), line
25+
else:
26+
yield n+1, line
27+
28+
1329
def get_recipes(datafile, module_priorities, verbose=False, beacon_speed=0):
14-
"""Data file consists of one entry per line. Each entry is either a recipe, building or module.
30+
"""Data file consists of one entry per line. Each entry is either an include, a recipe, building or module.
31+
Include lines look like:
32+
include PATH
33+
and result in the other path (relative to the directory this file is in) being read as though it were
34+
part of this file.
1535
Building lines look like:
1636
BUILDING builds at SPEED[ with N modules]
1737
For example:
@@ -33,63 +53,61 @@ def get_recipes(datafile, module_priorities, verbose=False, beacon_speed=0):
3353
3454
This function returns a dict {item: (building, throughput per building, {input: input amount for 1 output}, list of modules used in building)}
3555
"""
36-
with open(datafile) as f:
37-
buildings = {}
38-
items = {}
39-
modules = {}
40-
for n, line in enumerate(f):
41-
n += 1 # 1-based, not 0-based line numbers
42-
line = line.strip()
43-
if not line or line.startswith('#'):
56+
buildings = {}
57+
items = {}
58+
modules = {}
59+
for lineno, line in get_datafile_lines(datafile):
60+
line = line.strip()
61+
if not line or line.startswith('#'):
62+
continue
63+
64+
try:
65+
match = re.match('^([^,]+) builds at ([0-9.]+(?:/[0-9.]+)?)(?: with (\d+) modules)?$', line)
66+
if match:
67+
name, speed, mods = match.groups()
68+
mods = int(mods) if mods else 0
69+
name = name.lower()
70+
if name in buildings:
71+
raise ValueError('Building {!r} already declared'.format(name))
72+
buildings[name] = Fraction(speed), mods
73+
continue
74+
75+
match = re.match('^(\d+ )?(.+) takes ([0-9.]+) in ([^,]+)((?:, \d+ [^,]+)*)(, can take productivity)?$', line)
76+
if match:
77+
amount, name, time, building, inputs_str, prod = match.groups()
78+
amount = int(amount) if amount else 1
79+
time = Fraction(time)
80+
name = name.lower()
81+
building = building.lower()
82+
inputs = {}
83+
if inputs_str:
84+
for part in inputs_str.split(','):
85+
part = part.strip()
86+
if not part:
87+
continue
88+
input_amount, input_name = part.split(' ', 1)
89+
input_amount = int(input_amount)
90+
inputs[input_name] = input_amount
91+
if name in items:
92+
raise ValueError('Recipe for {!r} already declared'.format(name))
93+
items[name] = amount, time, building, inputs, prod
94+
continue
95+
96+
match = re.match('^([^,]+) module affects speed ([^,]+)(?:, prod ([^,]+))?$', line)
97+
if match:
98+
name, speed, prod = match.groups()
99+
speed = Fraction(speed)
100+
prod = Fraction(prod) if prod else 0
101+
name = name.lower()
102+
if name in modules:
103+
raise ValueError('Module {!r} already declared'.format(name))
104+
modules[name] = speed, prod
44105
continue
45106

46-
try:
47-
match = re.match('^([^,]+) builds at ([0-9.]+)(?: with (\d+) modules)?$', line)
48-
if match:
49-
name, speed, mods = match.groups()
50-
mods = int(mods) if mods else 0
51-
name = name.lower()
52-
if name in buildings:
53-
raise ValueError('Building {!r} already declared'.format(name))
54-
buildings[name] = Fraction(speed), mods
55-
continue
56-
57-
match = re.match('^(\d+ )?(.+) takes ([0-9.]+) in ([^,]+)((?:, \d+ [^,]+)*)(, can take productivity)?$', line)
58-
if match:
59-
amount, name, time, building, inputs_str, prod = match.groups()
60-
amount = int(amount) if amount else 1
61-
time = Fraction(time)
62-
name = name.lower()
63-
building = building.lower()
64-
inputs = {}
65-
if inputs_str:
66-
for part in inputs_str.split(','):
67-
part = part.strip()
68-
if not part:
69-
continue
70-
input_amount, input_name = part.split(' ', 1)
71-
input_amount = int(input_amount)
72-
inputs[input_name] = input_amount
73-
if name in items:
74-
raise ValueError('Recipe for {!r} already declared'.format(name))
75-
items[name] = amount, time, building, inputs, prod
76-
continue
77-
78-
match = re.match('^([^,]+) module affects speed ([^,]+)(?:, prod ([^,]+))?$', line)
79-
if match:
80-
name, speed, prod = match.groups()
81-
speed = Fraction(speed)
82-
prod = Fraction(prod) if prod else 0
83-
name = name.lower()
84-
if name in modules:
85-
raise ValueError('Module {!r} already declared'.format(name))
86-
modules[name] = speed, prod
87-
continue
88-
89-
raise ValueError("Unknown entry syntax")
90-
except Exception as e:
91-
_, _, tb = sys.exc_info()
92-
raise ValueError, ValueError("Error in line {} of {!r}: {}".format(n, datafile, e)), tb
107+
raise ValueError("Unknown entry syntax")
108+
except Exception as e:
109+
_, _, tb = sys.exc_info()
110+
raise ValueError, ValueError("Error in line {} of {!r}: {}".format(lineno, datafile, e)), tb
93111

94112
# validate module list
95113
for mod in module_priorities:

0 commit comments

Comments
 (0)