-
Notifications
You must be signed in to change notification settings - Fork 4
/
inventory.hpp
329 lines (276 loc) · 8.67 KB
/
inventory.hpp
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
/*
* Copyright (c) 2017, 2018 Florian Jung
*
* This file is part of factorio-bot.
*
* factorio-bot is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
* version 3, as published by the Free Software Foundation.
*
* factorio-bot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with factorio-bot. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "util.hpp"
#include "defines.h"
#include <memory>
#include <algorithm>
#include <numeric>
#include <boost/container/flat_map.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <assert.h>
#include <unordered_map>
#include <string>
struct Recipe;
struct ItemPrototype;
struct ItemStack;
using owner_t = intptr_t; // FIXME
// DEBUG only
struct owner_names_t : public std::unordered_map<owner_t, std::string>
{
std::string get(owner_t owner);
};
extern owner_names_t owner_names;
struct TaggedAmount
{
struct Tag
{
owner_t owner;
size_t amount;
};
using claims_t = vector_map< Tag, owner_t, &Tag::owner >;
size_t amount;
claims_t claims;
/** checks internal consistency */
void invariant() const
{
assert(n_claimed() <= amount);
}
/** returns the number of claimed items. amount - n_claimed() equals to the maximum amount a very-low-priority owner can claim */
size_t n_claimed() const;
/** claims up to n items for `owner`, only considering unclaimed items. claims at
* most as many items as are still available. Returns the number of actually
* claimed items.
*/
size_t add_claim(owner_t owner, size_t n);
/** adds / removes `n` items for `owner`, also updating the claims. If n is
* negative and available_to(owner) is not sufficient, this function fails
* by returning false. Otherwise it returns true. */
bool update(std::optional<owner_t> owner, int n)
{
if (n >= 0)
{
amount += n;
if (owner.has_value())
add_claim(*owner, n);
}
else
{
if (available_to(owner) < size_t(-n))
return false;
amount += n; // n is negative.
if (owner.has_value())
{
auto& claim = claims.get(*owner).amount;
claim -= std::min(size_t(-n), claim);
}
}
return true;
}
/** returns the amount of items claimed for the specified owner. */
size_t claimed_by(owner_t owner) const
{
auto idx = claims.find(owner);
if (idx == SIZE_MAX)
return 0;
else
return claims[idx].amount;
}
/** returns the amount of items that is unclaimed */
size_t unclaimed() const
{
assert(n_claimed() <= amount);
return amount - n_claimed();
}
/** returns the amount of items available to the specified owner. This includes claimed and free-for-use items. */
size_t available_to(std::optional<owner_t> owner) const
{
if (owner.has_value())
return unclaimed() + claimed_by(*owner);
else
return unclaimed();
}
};
using item_balance_t = boost::container::flat_map<const ItemPrototype*, int>;
// this is a glorified vector<pair<>>
struct TaggedInventory : boost::container::flat_map<const ItemPrototype*, TaggedAmount>
{
void dump() const;
bool apply(const item_balance_t& bal, std::optional<owner_t> owner);
bool can_satisfy(const std::vector<ItemStack>& items, std::optional<owner_t> owner);
};
struct Inventory : public boost::container::flat_map<const ItemPrototype*, size_t>
{
/** applies the recipe. if successful, the inventory is altered and true is returned, otherwise the inventory remains unchanged and false is returned.
If already_started == true, the ingredients are not removed, but the products are added. In this
case, the function always returns true.
*/
bool apply(const Recipe*, bool already_started = false);
bool apply(const item_balance_t& bal);
/** constructs the empty inventory */
Inventory() : flat_map() {}
Inventory(std::initializer_list<value_type> il) : flat_map(il) {}
Inventory& operator+=(const Inventory& other)
{
for (const auto& [key,value] : other)
(*this)[key] += value;
return *this;
}
Inventory& operator-=(const Inventory& other)
{
for (const auto& [key,value] : other)
(*this)[key] -= value;
return *this;
}
Inventory& operator*=(size_t mult)
{
for (auto& [key,value] : (*this))
value *= mult;
return *this;
}
Inventory operator+(const Inventory& other) const
{
Inventory result = *this;
result += other;
return result;
}
Inventory operator-(const Inventory& other) const
{
Inventory result = *this;
result -= other;
return result;
}
Inventory operator*(size_t mult) const
{
Inventory result = *this;
result *= mult;
return result;
}
Inventory max(const Inventory& other) const
{
Inventory result = *this;
for (const auto& [key,value] : other)
result[key] = std::max(result[key], value);
return result;
}
Inventory min(const Inventory& other) const
{
Inventory result = *this;
for (const auto& [key,value] : other)
{
size_t amount = std::min(value, this->get_or(key, 0));
if (amount)
result[key] = amount;
}
return result;
}
size_t get_or(const ItemPrototype* item, size_t default_value) const {
auto it = find(item);
if (it != end())
return it->second;
else
return default_value;
}
/** constructs an inventory from a TaggedInventory, considering only items claimed by the specified owner */
static Inventory get_claimed_by(const TaggedInventory& inv, owner_t owner)
{
Inventory result;
for (const auto& entry : inv)
if (size_t claimed = entry.second.claimed_by(owner))
result[entry.first] = claimed;
return result;
}
/** constructs an inventory from a TaggedInventory, considering only unclaimed items */
static Inventory get_unclaimed(const TaggedInventory& inv)
{
Inventory result;
for (const auto& entry : inv)
if (size_t claimed = entry.second.unclaimed())
result[entry.first] = claimed;
return result;
}
void dump() const;
std::string str() const;
};
inline Inventory operator*(size_t mult, const Inventory& inv) { return inv * mult; }
struct MultiInventory
{
struct key_t
{
const ItemPrototype* item;
inventory_t inv;
bool operator< (const key_t& o) const
{
if (item < o.item) return true;
if (item > o.item) return false;
return inv < o.inv;
}
};
using container_t = boost::container::flat_map<key_t, size_t>;
using iterator_t = container_t::iterator;
using item_iterator_t = iterator_t;
using const_iterator_t = container_t::const_iterator;
using const_item_iterator_t = const_iterator_t;
struct Predicate
{
inventory_t inv;
bool operator()(const container_t::value_type& x)
{
return x.first.inv == inv;
}
};
using inv_iterator_t = boost::filter_iterator<Predicate, iterator_t>;
using const_inv_iterator_t = boost::filter_iterator<Predicate, const_iterator_t>;
size_t& get(const ItemPrototype* item, inventory_t inv) { return container[key_t{item,inv}]; }
size_t get_or(const ItemPrototype* item, inventory_t inv, size_t default_value) const {
auto it = container.find(key_t{item,inv});
if (it != container.end())
return it->second;
else
return default_value;
}
iterator_t begin() { return container.begin(); }
iterator_t end() { return container.end(); }
item_iterator_t item_begin(const ItemPrototype* item) { return container.lower_bound(key_t{item, INV_MIN}); }
item_iterator_t item_end(const ItemPrototype* item) { return container.upper_bound(key_t{item, INV_MAX}); }
inv_iterator_t inv_begin(inventory_t inv) {
return inv_iterator_t(Predicate{inv}, container.begin(), container.end());
}
inv_iterator_t inv_end(inventory_t inv) {
return inv_iterator_t(Predicate{inv}, container.end(), container.end());
}
const_iterator_t begin() const { return container.begin(); }
const_iterator_t end() const { return container.end(); }
const_item_iterator_t item_begin(const ItemPrototype* item) const { return container.lower_bound(key_t{item, INV_MIN}); }
const_item_iterator_t item_end(const ItemPrototype* item) const { return container.upper_bound(key_t{item, INV_MAX}); }
const_inv_iterator_t inv_begin(inventory_t inv) const {
return const_inv_iterator_t(Predicate{inv}, container.begin(), container.end());
}
const_inv_iterator_t inv_end(inventory_t inv) const {
return const_inv_iterator_t(Predicate{inv}, container.end(), container.end());
}
void clear() { container.clear(); }
std::pair<iterator_t, bool> insert(inventory_t inv, const ItemPrototype* item, size_t val)
{
return container.insert(std::pair{ key_t{item,inv}, val });
}
void dump() const;
Inventory get_inventory(inventory_t type) const;
private:
container_t container;
};