-
Notifications
You must be signed in to change notification settings - Fork 3
/
productionestimator.go
144 lines (123 loc) · 4.55 KB
/
productionestimator.go
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package cs
import "math"
// The CompletionEstimator is used for populating completion estimates in a planet's production queue
type CompletionEstimator interface {
// get the estimated years to build one item with minerals on hand and some yearly mineral/resource output
GetYearsToBuildOne(item ProductionQueueItem, cost Cost, mineralsOnHand Mineral, yearlyAvailableToSpend Cost) int
// get a ProductionQueue with estimates filled in
GetProductionWithEstimates(rules *Rules, player *Player, planet Planet) ([]ProductionQueueItem, int)
}
type completionEstimate struct {
}
func NewCompletionEstimator() CompletionEstimator {
return &completionEstimate{}
}
// get the estimated years to build one item
func (e *completionEstimate) GetYearsToBuildOne(item ProductionQueueItem, cost Cost, mineralsOnHand Mineral, yearlyAvailableToSpend Cost) int {
numBuiltInAYear := yearlyAvailableToSpend.Divide(cost.Minus(item.Allocated).MinusMineral(mineralsOnHand).MinZero())
if numBuiltInAYear == 0 || math.IsInf(numBuiltInAYear, 1) {
return Infinite
}
return int(math.Ceil(1 / numBuiltInAYear))
}
// simulate up to 100 years of production to determine the time each item will take to build
// this function will take a copy of the planet and do the following:
// * clone the production queue
// * add an index to each production queue item so we can track it in the produce() result
// * default each item to never being completed
// * simulate 100 years of growth
// - mine for resources
// - run production (including terraforming the planet, building mines and factories, etc)
// - grow pop on the planet
//
// For each year of growth, it checks what was built. If an item was built for the first time
// it records the year. If the item completed building, it records the last year
// when all items are complete or 100 years have passed, iit returns
func (e *completionEstimate) GetProductionWithEstimates(rules *Rules, player *Player, planet Planet) (items []ProductionQueueItem, leftoverResourcesForResearch int) {
// copy the queue so we can update it
items = make([]ProductionQueueItem, len(planet.ProductionQueue))
copy(items, planet.ProductionQueue)
// reset any estimates
for i := range items {
planet.ProductionQueue[i].index = i
item := &items[i]
item.QueueItemCompletionEstimate = QueueItemCompletionEstimate{
YearsToBuildOne: Infinite,
YearsToBuildAll: Infinite,
YearsToSkipAuto: Infinite,
}
}
// keep track of items built so we know how many auto items are completed
numBuilt := make([]int, len(planet.ProductionQueue))
producer := newProducer(&planet, player)
for year := 1; year <= 100; year++ {
// mine for minerals
planet.mine(rules)
// remote mine for AR
//remoteMine()
// build!
result := producer.produce()
if year == 1 {
leftoverResourcesForResearch = result.leftoverResources
}
for _, itemBuilt := range result.itemsBuilt {
if itemBuilt.index == -1 {
// skip partial auto builds
continue
}
item := &items[itemBuilt.index]
maxBuildable := planet.maxBuildable(player, item.Type)
// this will be skipped if we've hit the max allowed
if itemBuilt.skipped {
if year == 1 && maxBuildable == 0 {
item.Skipped = true
item.YearsToSkipAuto = 1
} else {
if item.YearsToSkipAuto == Infinite {
item.YearsToSkipAuto = year
}
}
continue
}
// this item will never complete
if itemBuilt.never {
continue
}
numBuiltSoFar := numBuilt[itemBuilt.index] + itemBuilt.numBuilt
numBuilt[itemBuilt.index] = numBuiltSoFar
// see if we already recorded when the first item was built
first := item.YearsToBuildOne
if first == Infinite {
// we built one, update the years to build one
item.YearsToBuildOne = year
}
// check if we built the last one of this group
// if we've built the item's original quantity, or we've built some and the maxBuildable remaining is 0
// we're done
last := item.YearsToBuildAll
if last == Infinite {
if item.Type.IsAuto() {
if itemBuilt.numBuilt >= item.Quantity || (itemBuilt.numBuilt >= maxBuildable) {
item.YearsToBuildAll = year
}
} else {
if numBuiltSoFar >= item.Quantity || (itemBuilt.numBuilt >= maxBuildable) {
item.YearsToBuildAll = year
}
}
}
}
if result.completed {
// we built everything in the queue, no need to loop anymore
break
}
// grow pop
planet.grow(player)
planet.Spec = computePlanetSpec(rules, player, &planet)
// colonists died off, no more production
if planet.population() < 0 {
break
}
}
return items, leftoverResourcesForResearch
}