-
Notifications
You must be signed in to change notification settings - Fork 12
/
D0S.DustManager
116 lines (98 loc) · 5.68 KB
/
D0S.DustManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
:local int dust_tier
:local string dust_multipliers
:local double acc
:local int i
:local int state
:local double ore_buffer
wakeup()
open.factory()
isopen("factory")
; Amount of ore to save at each tier. Ore will only be saved if there is
; more dust than ore, otherwise it will be crushed anyway (so that saving
; ore doesn't prevent you from progressing.) This function exists to allow
; for scanning ore with the Crafter.
ore_buffer = 1000.
; Initialize multipliers from a string. This used to be much worse, but is
; easy now that there is native s2d().
dust_multipliers = "1 1 .625 .5 .07872 .06312 .01 .01 .0021"
; "i" combines the position in the string with the tier currently being
; initialized. The encoding is pos + tier * 10000.
; (I.e. the string can only be 10000 long.)
#pos (i%10000)
#tier (i/10000)
#nextspace index(dust_multipliers . " ", " ", {pos})
; This is a simple loop: Each iteration converts one number and advances
; both the read position "pos" and the "tier" appropriately. The only subtle
; wrinkle is that once it gets to the end of the list, it will keep reading
; the same entry to fill in the remaining tiers.
init_multipliers:
lds("buffer".({tier} + 1), s2d(sub(dust_multipliers, {pos}, {nextspace} - {pos}), 0./0.))
i = i + 10000 + if(index(dust_multipliers, " ", {pos}) == -1, 0, {nextspace} + 1 - {pos})
gotoif(init_multipliers, i < 10 * 10000)
; The overall plan is to count down through the ores, so that the best ones
; (with the least count) get processed first, and to count down through
; the lumps as well. We process lumps in "packets" that are based on a
; power of the stack size, which ensures that uptiering
; doesn't consume too much dust while processing but also allows
; for the chunks to process for a considerable time.
; This also makes for a simple expression that equals 0 when the size
; is 1 (so that we don't use the last dust), and 1 when the size is 2
; (so that we do start immediately after that point.)
; The tier variable is a local, so the process can be restarted from
; the top if it's taking too long.
top:
; Nested expressions for calculating the tier of ore to crush. This factors in
; the need to save 1000 ore based on ore_buffer, and returns
; 0 if there is no crushable ore.
#nest2_9(macro, final) {{macro}(2,{{macro}(3,{{macro}(4,{{macro}(5,{{macro}(6,{{macro}(7,{{macro}(8,{{macro}(9,{final})})})})})})})})}
#orecount_cond(value) (count("ore", {value}) <= min(ore_buffer, count("dust", {value})))
#orestate_nest(value, rest) if({orecount_cond(10 - {value})}, {rest}, 10 - {value})
#dust_inc if(active("mixer"), 0, 11)
#orestate if(active("crusher"), {dust_inc}, {orestate_nest(0,{orestate_nest(1,{nest2_9(orestate_nest,{dust_inc})})})})
; The main state calculation. We first calculate whether ore needs to be
; crushed, and if it does not, we possibly increment the current dust that is
; being mixed.
; The dust_tier for mixing and ore_tier for crushing are stored together in state.
; Mixing will only occur if ore_tier is 0, since crushing takes priority.
; (Generally the mixing will happen in the next iteration.)
#raw_dust_tier (state / 11)
#curr_dust_tier 1 + (({raw_dust_tier} + 8) % 9)
#next_dust_tier 2 + (({raw_dust_tier} + 8) % 9)
; The reason for the max() here is so that ore_tier never returns 0.
; That way, we never try to count() something with a tier of 0, even in
; if() branches that aren't used. This is because counting invalid tiers
; has a side-effect of logging a warning, which can fill the hard drive with
; spammy messages.
#ore_tier max(1, state % 11)
#has_ore (state % 11 != 0)
state = ({raw_dust_tier} * 11 + {orestate}) % 99
; These expressions calculate how much dust to uptier. It used to be a more
; complicated expression that calculated everything in T1-equivilant units,
; (i.e. if you down-converted it all), but that isn't actually needed: Everything
; works (better actually) if you just make local decisions based on the ratios
; between the current and next tier.
; In both cases, Ore Lumps are also counted as dust, so that unmixed lumps don't
; throw off the calculations. We know that currently processing lumps won't be
; an issue, because we only craft when the mixer is empty.
#count_dust_and_lumps(dust_tier) (if({dust_tier} == 1, 0., count("lump", max(1, -1 + {dust_tier}))) + count("dust", {dust_tier}))
#needed_by_ratio(dust_tier) (({count_dust_and_lumps({dust_tier})} * ldg("buffer" . (1 + {dust_tier}))\
- {count_dust_and_lumps(1 + {dust_tier})} * ldg("buffer" . {dust_tier}))\
/ (ldg("buffer" . {dust_tier}) + 8. * ldg("buffer" . (1 + {dust_tier}))))
; We always save one dust on the producing side, so that dust ordering doesn't
; get messed up in the inventory.
; We produce lumps in chunks. We consume up to 5% of the destination;
; this allows for large-ish mixing regardless of stack size, and
; dynamically adjusts well.
; Not adding more to the mixer when it's running greatly reduces
; complications. We just move on to the next tier when it's done,
; and come back around soon enough.
; The "+ 0.5" on the consuming side is to adjust for Chemical Lumps.
; That way, in the terminal case it'll hit the safety target exactly. If you
; don't have lumps, you'll be up to 3 under the target. However, we always
; ensure that you'll leave one leftover no matter what.
craft("lump", {curr_dust_tier}, if({has_ore} || active("mixer"), 0., min(0.999 * count("dust", {next_dust_tier})^ 0.84, {needed_by_ratio({curr_dust_tier})} + 0.5)))
produce(if({has_ore}, "ore", "lump"),\
if({has_ore}, {ore_tier}, {curr_dust_tier}),\
if({has_ore}, max(count("ore", {ore_tier}) - ore_buffer, 1. + (count("ore", {ore_tier}) - count("dust", {ore_tier})) / 3.), count("lump", {curr_dust_tier})),\
if({has_ore}, "crusher", "mixer"))
gotoif(top, isopen("factory"))