plancraft 0.3.14__py3-none-any.whl → 0.3.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plancraft/environment/planner.py +391 -1
- plancraft/environment/sampler.py +2 -9
- plancraft/models/oracle.py +9 -203
- {plancraft-0.3.14.dist-info → plancraft-0.3.16.dist-info}/METADATA +1 -1
- {plancraft-0.3.14.dist-info → plancraft-0.3.16.dist-info}/RECORD +7 -7
- {plancraft-0.3.14.dist-info → plancraft-0.3.16.dist-info}/WHEEL +0 -0
- {plancraft-0.3.14.dist-info → plancraft-0.3.16.dist-info}/licenses/LICENSE +0 -0
plancraft/environment/planner.py
CHANGED
@@ -1,8 +1,23 @@
|
|
1
|
+
import copy
|
1
2
|
import time
|
3
|
+
from collections import Counter
|
2
4
|
|
3
5
|
import networkx as nx
|
4
6
|
|
5
|
-
from plancraft.environment.
|
7
|
+
from plancraft.environment.actions import (
|
8
|
+
MoveAction,
|
9
|
+
SmeltAction,
|
10
|
+
StopAction,
|
11
|
+
)
|
12
|
+
from plancraft.environment.recipes import (
|
13
|
+
RECIPES,
|
14
|
+
BaseRecipe,
|
15
|
+
ShapedRecipe,
|
16
|
+
ShapelessRecipe,
|
17
|
+
SmeltingRecipe,
|
18
|
+
id_to_item,
|
19
|
+
)
|
20
|
+
from plancraft.environment.items import all_data
|
6
21
|
|
7
22
|
RECIPE_GRAPH = nx.DiGraph()
|
8
23
|
|
@@ -14,6 +29,14 @@ for item, recipes in RECIPES.items():
|
|
14
29
|
RECIPE_GRAPH.add_edge(ingredient, recipe.result.item)
|
15
30
|
|
16
31
|
|
32
|
+
MAX_STACK_SIZE = {}
|
33
|
+
for data_item in all_data["items"]:
|
34
|
+
if data_item["stackable"]:
|
35
|
+
MAX_STACK_SIZE[data_item["type"]] = data_item["stackSize"]
|
36
|
+
else:
|
37
|
+
MAX_STACK_SIZE[data_item["type"]] = 1
|
38
|
+
|
39
|
+
|
17
40
|
def get_ancestors(target: str):
|
18
41
|
return list(nx.ancestors(RECIPE_GRAPH, source=target))
|
19
42
|
|
@@ -107,3 +130,370 @@ def optimal_planner(
|
|
107
130
|
|
108
131
|
except TimeoutError:
|
109
132
|
return None
|
133
|
+
|
134
|
+
|
135
|
+
def item_set_id_to_type(item_set_ids: set[int]):
|
136
|
+
return set(id_to_item(i) for i in item_set_ids)
|
137
|
+
|
138
|
+
|
139
|
+
def find_free_inventory_slot(
|
140
|
+
inventory: dict, from_slot: int, from_item_type=None, from_item_quantity=None
|
141
|
+
) -> int:
|
142
|
+
# find a free slot in the inventory for the item in from_slot
|
143
|
+
if from_item_type is None:
|
144
|
+
from_item_type = inventory[from_slot]["type"]
|
145
|
+
if from_item_quantity is None:
|
146
|
+
from_item_quantity = inventory[from_slot]["quantity"]
|
147
|
+
|
148
|
+
empty_slots = set(range(10, 46)) - set(inventory.keys()) - set([from_slot])
|
149
|
+
|
150
|
+
type_to_slot = {}
|
151
|
+
slot_to_quantity = {}
|
152
|
+
for slot, item in inventory.items():
|
153
|
+
if slot == from_slot:
|
154
|
+
continue
|
155
|
+
item_type = item["type"]
|
156
|
+
if item_type not in type_to_slot:
|
157
|
+
type_to_slot[item_type] = [slot]
|
158
|
+
else:
|
159
|
+
type_to_slot[item_type].append(slot)
|
160
|
+
|
161
|
+
slot_to_quantity[slot] = item["quantity"]
|
162
|
+
|
163
|
+
assert from_item_type is not None, f"Item not found in slot {from_slot}"
|
164
|
+
|
165
|
+
# if there is a free slot with the same item type
|
166
|
+
if from_item_type in type_to_slot:
|
167
|
+
for slot in type_to_slot[from_item_type]:
|
168
|
+
if (
|
169
|
+
slot_to_quantity[slot] + from_item_quantity
|
170
|
+
<= MAX_STACK_SIZE[from_item_type]
|
171
|
+
):
|
172
|
+
return slot
|
173
|
+
if len(empty_slots) > 0:
|
174
|
+
return empty_slots.pop()
|
175
|
+
|
176
|
+
raise ValueError("No free slot found")
|
177
|
+
|
178
|
+
|
179
|
+
def find_item_in_inventory(
|
180
|
+
target: str, inventory: dict, min_quantity_needed: set = set()
|
181
|
+
) -> int:
|
182
|
+
for slot, item in inventory.items():
|
183
|
+
if item["type"] == target and item["quantity"] > 0:
|
184
|
+
# if we don't need to keep an item in the slot, we can use it
|
185
|
+
if slot not in min_quantity_needed:
|
186
|
+
return slot
|
187
|
+
# if we need to keep an item in the slot, we can only use it quantity>1
|
188
|
+
elif item["quantity"] > 1:
|
189
|
+
return slot
|
190
|
+
|
191
|
+
|
192
|
+
def get_inventory_counter(inventory: dict) -> Counter:
|
193
|
+
counter = Counter()
|
194
|
+
for slot, item in inventory.items():
|
195
|
+
if slot == 0:
|
196
|
+
continue
|
197
|
+
counter[item["type"]] += item["quantity"]
|
198
|
+
return counter
|
199
|
+
|
200
|
+
|
201
|
+
def get_crafting_slot_item(inventory: dict) -> dict:
|
202
|
+
for slot, item in inventory.items():
|
203
|
+
if slot == 0 and item["quantity"] > 0:
|
204
|
+
return item
|
205
|
+
return None
|
206
|
+
|
207
|
+
|
208
|
+
def update_inventory(
|
209
|
+
inventory: dict, slot_from: int, slot_to: int, quantity: int
|
210
|
+
) -> dict:
|
211
|
+
"""
|
212
|
+
decrements quantity of item in slot_from
|
213
|
+
NOTE: we don't care about incrementing the items in slot_to
|
214
|
+
|
215
|
+
"""
|
216
|
+
new_inventory = dict(inventory)
|
217
|
+
from_item = new_inventory[slot_from]
|
218
|
+
if slot_to not in new_inventory:
|
219
|
+
new_inventory[slot_to] = {"type": from_item["type"], "quantity": quantity}
|
220
|
+
else:
|
221
|
+
new_inventory[slot_to]["quantity"] += quantity
|
222
|
+
|
223
|
+
new_inventory[slot_from]["quantity"] -= quantity
|
224
|
+
if new_inventory[slot_from]["quantity"] <= 0:
|
225
|
+
del new_inventory[slot_from]
|
226
|
+
return new_inventory
|
227
|
+
|
228
|
+
|
229
|
+
def get_plan(observation: dict):
|
230
|
+
# this simply recovering the target item to craft
|
231
|
+
inventory_counter = get_inventory_counter(observation["inventory"])
|
232
|
+
return optimal_planner(target=observation["target"], inventory=inventory_counter)
|
233
|
+
|
234
|
+
|
235
|
+
def decompose_subgoal(
|
236
|
+
current_inventory: dict, plan_recipe, new_inventory: dict[str, int]
|
237
|
+
) -> list[str]:
|
238
|
+
"""
|
239
|
+
For a given plan_recipe and inventory, output the list of action to craft recipe
|
240
|
+
"""
|
241
|
+
subplan = []
|
242
|
+
new_inventory_counter = Counter(new_inventory)
|
243
|
+
current_inventory_counter = get_inventory_counter(current_inventory)
|
244
|
+
items_to_use_counter = current_inventory_counter - new_inventory_counter
|
245
|
+
new_items = new_inventory_counter - current_inventory_counter
|
246
|
+
assert len(new_items) == 1
|
247
|
+
|
248
|
+
# if plan_recipe is a smelting recipe
|
249
|
+
if isinstance(plan_recipe, SmeltingRecipe):
|
250
|
+
assert len(items_to_use_counter) == 1, "smelting only supports one item"
|
251
|
+
for item, quantity in items_to_use_counter.items():
|
252
|
+
from_slot = find_item_in_inventory(item, current_inventory)
|
253
|
+
out_item_type, out_item_quantity = (
|
254
|
+
plan_recipe.result.item,
|
255
|
+
plan_recipe.result.count,
|
256
|
+
)
|
257
|
+
# find a free slot in the inventory for the item that will be smelted
|
258
|
+
free_slot = find_free_inventory_slot(
|
259
|
+
current_inventory,
|
260
|
+
from_slot=from_slot,
|
261
|
+
from_item_type=out_item_type,
|
262
|
+
from_item_quantity=out_item_quantity,
|
263
|
+
)
|
264
|
+
action = SmeltAction(
|
265
|
+
slot_from=from_slot, slot_to=free_slot, quantity=quantity
|
266
|
+
)
|
267
|
+
subplan.append(str(action))
|
268
|
+
|
269
|
+
# update inventory to decrement quantity of item in from_slot and increment quantity of item in free_slot
|
270
|
+
current_inventory = dict(current_inventory)
|
271
|
+
if free_slot not in current_inventory:
|
272
|
+
current_inventory[free_slot] = {
|
273
|
+
"type": out_item_type,
|
274
|
+
"quantity": out_item_quantity,
|
275
|
+
}
|
276
|
+
else:
|
277
|
+
current_inventory[free_slot]["quantity"] += quantity
|
278
|
+
|
279
|
+
current_inventory[from_slot]["quantity"] -= quantity
|
280
|
+
if current_inventory[from_slot]["quantity"] <= 0:
|
281
|
+
del current_inventory[from_slot]
|
282
|
+
|
283
|
+
return subplan, current_inventory
|
284
|
+
|
285
|
+
elif isinstance(plan_recipe, ShapelessRecipe):
|
286
|
+
crafting_slot = 1
|
287
|
+
min_quantity_needed = set()
|
288
|
+
while crafting_slot < 10:
|
289
|
+
# if something is already in the crafting slot
|
290
|
+
if (
|
291
|
+
crafting_slot in current_inventory
|
292
|
+
and current_inventory[crafting_slot]["quantity"] > 0
|
293
|
+
):
|
294
|
+
# check it is a desired item
|
295
|
+
crafting_slot_item = current_inventory[crafting_slot]["type"]
|
296
|
+
# if it is a desired item, skip it
|
297
|
+
if (
|
298
|
+
crafting_slot_item in items_to_use_counter
|
299
|
+
and items_to_use_counter[crafting_slot_item] > 0
|
300
|
+
):
|
301
|
+
items_to_use_counter[crafting_slot_item] -= 1
|
302
|
+
if items_to_use_counter[crafting_slot_item] == 0:
|
303
|
+
del items_to_use_counter[crafting_slot_item]
|
304
|
+
min_quantity_needed.add(crafting_slot)
|
305
|
+
crafting_slot += 1
|
306
|
+
continue
|
307
|
+
# if it is not a desired item, move it to a free slot
|
308
|
+
else:
|
309
|
+
free_slot = find_free_inventory_slot(
|
310
|
+
current_inventory, from_slot=crafting_slot
|
311
|
+
)
|
312
|
+
action = MoveAction(
|
313
|
+
slot_from=crafting_slot,
|
314
|
+
slot_to=free_slot,
|
315
|
+
quantity=current_inventory[crafting_slot]["quantity"],
|
316
|
+
)
|
317
|
+
subplan.append(str(action))
|
318
|
+
current_inventory = update_inventory(
|
319
|
+
current_inventory,
|
320
|
+
crafting_slot,
|
321
|
+
free_slot,
|
322
|
+
current_inventory[crafting_slot]["quantity"],
|
323
|
+
)
|
324
|
+
|
325
|
+
# if there are still items to add
|
326
|
+
if len(items_to_use_counter) != 0:
|
327
|
+
item = next(iter(items_to_use_counter))
|
328
|
+
from_slot = find_item_in_inventory(
|
329
|
+
item, current_inventory, min_quantity_needed
|
330
|
+
)
|
331
|
+
action = MoveAction(
|
332
|
+
slot_from=from_slot, slot_to=crafting_slot, quantity=1
|
333
|
+
)
|
334
|
+
subplan.append(str(action))
|
335
|
+
current_inventory = update_inventory(
|
336
|
+
current_inventory, from_slot, crafting_slot, 1
|
337
|
+
)
|
338
|
+
items_to_use_counter[item] -= 1
|
339
|
+
if items_to_use_counter[item] == 0:
|
340
|
+
del items_to_use_counter[item]
|
341
|
+
|
342
|
+
# update state of inventory
|
343
|
+
crafting_slot += 1
|
344
|
+
|
345
|
+
# if plan_recipe is a shaped recipe
|
346
|
+
elif isinstance(plan_recipe, ShapedRecipe):
|
347
|
+
min_quantity_needed = set()
|
348
|
+
seen_kernel = set()
|
349
|
+
for i, row in enumerate(plan_recipe.kernel):
|
350
|
+
for j, item_set in enumerate(row):
|
351
|
+
inventory_position = (i * 3) + j + 1
|
352
|
+
seen_kernel.add(inventory_position)
|
353
|
+
valid_items = item_set_id_to_type(item_set)
|
354
|
+
|
355
|
+
# if the inventory position is needed to be empty
|
356
|
+
if (
|
357
|
+
valid_items == {None}
|
358
|
+
and inventory_position in current_inventory
|
359
|
+
and current_inventory[inventory_position]["quantity"] > 0
|
360
|
+
):
|
361
|
+
free_slot = find_free_inventory_slot(
|
362
|
+
current_inventory, from_slot=inventory_position
|
363
|
+
)
|
364
|
+
action = MoveAction(
|
365
|
+
slot_from=inventory_position,
|
366
|
+
slot_to=free_slot,
|
367
|
+
quantity=current_inventory[inventory_position]["quantity"],
|
368
|
+
)
|
369
|
+
current_inventory = update_inventory(
|
370
|
+
current_inventory,
|
371
|
+
inventory_position,
|
372
|
+
free_slot,
|
373
|
+
current_inventory[inventory_position]["quantity"],
|
374
|
+
)
|
375
|
+
subplan.append(str(action))
|
376
|
+
continue
|
377
|
+
|
378
|
+
# if the inventory position is needed to be filled
|
379
|
+
added_item = False
|
380
|
+
for item in valid_items:
|
381
|
+
# check if item is already added
|
382
|
+
if added_item:
|
383
|
+
break
|
384
|
+
|
385
|
+
# if item is already in the correct position, skip
|
386
|
+
if (
|
387
|
+
inventory_position in current_inventory
|
388
|
+
and current_inventory[inventory_position]["type"] == item
|
389
|
+
) and current_inventory[inventory_position]["quantity"] > 0:
|
390
|
+
min_quantity_needed.add(inventory_position)
|
391
|
+
# decrement the quantity of the items to use
|
392
|
+
items_to_use_counter[item] -= 1
|
393
|
+
if items_to_use_counter[item] == 0:
|
394
|
+
del items_to_use_counter[item]
|
395
|
+
break
|
396
|
+
|
397
|
+
if items_to_use_counter[item] > 0:
|
398
|
+
# check and remove any item in the inventory position
|
399
|
+
if (
|
400
|
+
inventory_position in current_inventory
|
401
|
+
and current_inventory[inventory_position]["quantity"] > 0
|
402
|
+
and current_inventory[inventory_position]["type"] != item
|
403
|
+
):
|
404
|
+
free_slot = find_free_inventory_slot(
|
405
|
+
current_inventory, from_slot=inventory_position
|
406
|
+
)
|
407
|
+
action = MoveAction(
|
408
|
+
slot_from=inventory_position,
|
409
|
+
slot_to=free_slot,
|
410
|
+
quantity=current_inventory[inventory_position][
|
411
|
+
"quantity"
|
412
|
+
],
|
413
|
+
)
|
414
|
+
current_inventory = update_inventory(
|
415
|
+
current_inventory,
|
416
|
+
inventory_position,
|
417
|
+
free_slot,
|
418
|
+
current_inventory[inventory_position]["quantity"],
|
419
|
+
)
|
420
|
+
subplan.append(str(action))
|
421
|
+
|
422
|
+
# move item to correct position
|
423
|
+
from_slot = find_item_in_inventory(
|
424
|
+
item, current_inventory, min_quantity_needed
|
425
|
+
)
|
426
|
+
action = MoveAction(
|
427
|
+
slot_from=from_slot,
|
428
|
+
slot_to=inventory_position,
|
429
|
+
quantity=1,
|
430
|
+
)
|
431
|
+
items_to_use_counter[item] -= 1
|
432
|
+
added_item = True
|
433
|
+
# update state of inventory
|
434
|
+
current_inventory = update_inventory(
|
435
|
+
current_inventory, from_slot, inventory_position, 1
|
436
|
+
)
|
437
|
+
subplan.append(str(action))
|
438
|
+
|
439
|
+
# ensure all other items are removed
|
440
|
+
leftover_kernel = set(range(1, 10)) - seen_kernel
|
441
|
+
for slot in leftover_kernel:
|
442
|
+
if slot in current_inventory and current_inventory[slot]["quantity"] > 0:
|
443
|
+
free_slot = find_free_inventory_slot(current_inventory, from_slot=slot)
|
444
|
+
action = MoveAction(
|
445
|
+
slot_from=slot,
|
446
|
+
slot_to=free_slot,
|
447
|
+
quantity=current_inventory[slot]["quantity"],
|
448
|
+
)
|
449
|
+
current_inventory = update_inventory(
|
450
|
+
current_inventory,
|
451
|
+
slot,
|
452
|
+
free_slot,
|
453
|
+
current_inventory[slot]["quantity"],
|
454
|
+
)
|
455
|
+
subplan.append(str(action))
|
456
|
+
else:
|
457
|
+
raise NotImplementedError(f"Recipe type {type(plan_recipe)} not supported")
|
458
|
+
|
459
|
+
# move crafted item to free slot
|
460
|
+
current_inventory[0] = {
|
461
|
+
"type": plan_recipe.result.item,
|
462
|
+
"quantity": plan_recipe.result.count,
|
463
|
+
}
|
464
|
+
free_slot = find_free_inventory_slot(current_inventory, from_slot=0)
|
465
|
+
subplan.append(
|
466
|
+
str(
|
467
|
+
MoveAction(
|
468
|
+
slot_from=0, slot_to=free_slot, quantity=plan_recipe.result.count
|
469
|
+
)
|
470
|
+
)
|
471
|
+
)
|
472
|
+
current_inventory = update_inventory(
|
473
|
+
current_inventory, 0, free_slot, plan_recipe.result.count
|
474
|
+
)
|
475
|
+
# decrement all quantities of items present in crafting slots
|
476
|
+
for i in range(1, 10):
|
477
|
+
if i in current_inventory:
|
478
|
+
current_inventory[i]["quantity"] -= 1
|
479
|
+
if current_inventory[i]["quantity"] <= 0:
|
480
|
+
del current_inventory[i]
|
481
|
+
|
482
|
+
return subplan, current_inventory
|
483
|
+
|
484
|
+
|
485
|
+
def get_subplans(observation: dict) -> tuple[list[list[str]], list]:
|
486
|
+
current_inventory = copy.deepcopy(observation["inventory"])
|
487
|
+
plan = get_plan(observation)
|
488
|
+
# get action
|
489
|
+
if plan is None or len(plan) == 0:
|
490
|
+
return [[str(StopAction())]], []
|
491
|
+
# plan_recipe, new_inventory = plan[0]
|
492
|
+
subplans = []
|
493
|
+
# Calculate the subplans for each step in the plan
|
494
|
+
for plan_recipe, new_inventory in plan:
|
495
|
+
subplan, current_inventory = decompose_subgoal(
|
496
|
+
current_inventory, plan_recipe, new_inventory
|
497
|
+
)
|
498
|
+
subplans.append(subplan)
|
499
|
+
return subplans, plan
|
plancraft/environment/sampler.py
CHANGED
@@ -5,17 +5,10 @@ from collections import Counter
|
|
5
5
|
import numpy as np
|
6
6
|
from loguru import logger
|
7
7
|
|
8
|
-
from plancraft.environment.items import ALL_ITEMS
|
9
|
-
from plancraft.environment.planner import get_ancestors, optimal_planner
|
8
|
+
from plancraft.environment.items import ALL_ITEMS
|
9
|
+
from plancraft.environment.planner import get_ancestors, optimal_planner, MAX_STACK_SIZE
|
10
10
|
from plancraft.environment.recipes import RECIPES
|
11
11
|
|
12
|
-
MAX_STACK_SIZE = {}
|
13
|
-
for data_item in all_data["items"]:
|
14
|
-
if data_item["stackable"]:
|
15
|
-
MAX_STACK_SIZE[data_item["type"]] = data_item["stackSize"]
|
16
|
-
else:
|
17
|
-
MAX_STACK_SIZE[data_item["type"]] = 1
|
18
|
-
|
19
12
|
|
20
13
|
def sample_distractors(
|
21
14
|
exclude_set: set = None, num_distractors: int = 16
|
plancraft/models/oracle.py
CHANGED
@@ -1,108 +1,17 @@
|
|
1
|
-
import copy
|
2
|
-
from collections import Counter
|
3
|
-
|
4
1
|
import torch
|
5
2
|
|
6
3
|
from plancraft.config import EvalConfig
|
7
|
-
from plancraft.environment.
|
8
|
-
MoveAction,
|
9
|
-
SmeltAction,
|
10
|
-
StopAction,
|
11
|
-
)
|
12
|
-
from plancraft.environment.planner import optimal_planner
|
13
|
-
from plancraft.environment.recipes import (
|
14
|
-
ShapedRecipe,
|
15
|
-
ShapelessRecipe,
|
16
|
-
SmeltingRecipe,
|
17
|
-
id_to_item,
|
18
|
-
)
|
19
|
-
from plancraft.environment.sampler import MAX_STACK_SIZE
|
4
|
+
from plancraft.environment.planner import get_subplans
|
20
5
|
from plancraft.models.base import PlancraftBaseModel
|
21
6
|
from plancraft.models.bbox_model import IntegratedBoundingBoxModel
|
22
7
|
|
23
8
|
|
24
|
-
def item_set_id_to_type(item_set_ids: set[int]):
|
25
|
-
return set(id_to_item(i) for i in item_set_ids)
|
26
|
-
|
27
|
-
|
28
|
-
def find_free_inventory_slot(inventory: dict, from_slot: int) -> int:
|
29
|
-
# find a free slot in the inventory for the item in from_slot
|
30
|
-
from_item_type = inventory[from_slot]["type"]
|
31
|
-
from_item_quantity = inventory[from_slot]["quantity"]
|
32
|
-
|
33
|
-
empty_slots = set(range(10, 46)) - set(inventory.keys()) - set([from_slot])
|
34
|
-
|
35
|
-
type_to_slot = {}
|
36
|
-
slot_to_quantity = {}
|
37
|
-
for slot, item in inventory.items():
|
38
|
-
if slot == from_slot:
|
39
|
-
continue
|
40
|
-
item_type = item["type"]
|
41
|
-
if item_type not in type_to_slot:
|
42
|
-
type_to_slot[item_type] = [slot]
|
43
|
-
else:
|
44
|
-
type_to_slot[item_type].append(slot)
|
45
|
-
|
46
|
-
slot_to_quantity[slot] = item["quantity"]
|
47
|
-
|
48
|
-
assert from_item_type is not None, f"Item not found in slot {from_slot}"
|
49
|
-
|
50
|
-
# if there is a free slot with the same item type
|
51
|
-
if from_item_type in type_to_slot:
|
52
|
-
for slot in type_to_slot[from_item_type]:
|
53
|
-
if (
|
54
|
-
slot_to_quantity[slot] + from_item_quantity
|
55
|
-
<= MAX_STACK_SIZE[from_item_type]
|
56
|
-
):
|
57
|
-
return slot
|
58
|
-
if len(empty_slots) > 0:
|
59
|
-
return empty_slots.pop()
|
60
|
-
|
61
|
-
raise ValueError("No free slot found")
|
62
|
-
|
63
|
-
|
64
|
-
def find_item_in_inventory(target: str, inventory: dict) -> int:
|
65
|
-
for slot, item in inventory.items():
|
66
|
-
if item["type"] == target and item["quantity"] > 0:
|
67
|
-
return slot
|
68
|
-
|
69
|
-
|
70
|
-
def get_inventory_counter(inventory: dict) -> Counter:
|
71
|
-
counter = Counter()
|
72
|
-
for slot, item in inventory.items():
|
73
|
-
if slot == 0:
|
74
|
-
continue
|
75
|
-
counter[item["type"]] += item["quantity"]
|
76
|
-
return counter
|
77
|
-
|
78
|
-
|
79
|
-
def get_crafting_slot_item(inventory: dict) -> dict:
|
80
|
-
for slot, item in inventory.items():
|
81
|
-
if slot == 0 and item["quantity"] > 0:
|
82
|
-
return item
|
83
|
-
return None
|
84
|
-
|
85
|
-
|
86
|
-
def update_inventory(
|
87
|
-
inventory: dict, slot_from: int, slot_to: int, quantity: int
|
88
|
-
) -> dict:
|
89
|
-
"""
|
90
|
-
decrements quantity of item in slot_from
|
91
|
-
NOTE: we don't care about incrementing the items in slot_to
|
92
|
-
|
93
|
-
"""
|
94
|
-
new_inventory = dict(inventory)
|
95
|
-
new_inventory[slot_from]["quantity"] -= quantity
|
96
|
-
return new_inventory
|
97
|
-
|
98
|
-
|
99
9
|
class OracleModel(PlancraftBaseModel):
|
100
10
|
"""
|
101
11
|
Oracle model returns actions that solve the task optimally
|
102
12
|
"""
|
103
13
|
|
104
14
|
def __init__(self, cfg: EvalConfig):
|
105
|
-
self.plans = []
|
106
15
|
self.subplans = []
|
107
16
|
self.use_fasterrcnn = cfg.plancraft.use_fasterrcnn
|
108
17
|
|
@@ -117,118 +26,15 @@ class OracleModel(PlancraftBaseModel):
|
|
117
26
|
self.bbox_model.cuda()
|
118
27
|
|
119
28
|
def reset(self):
|
120
|
-
self.plans = []
|
121
|
-
self.subplans = []
|
122
|
-
|
123
|
-
def get_plan(self, observation: dict):
|
124
|
-
# this simply recovering the target item to craft
|
125
|
-
inventory_counter = get_inventory_counter(observation["inventory"])
|
126
|
-
return optimal_planner(
|
127
|
-
target=observation["target"], inventory=inventory_counter
|
128
|
-
)
|
129
|
-
|
130
|
-
def get_next_action(self, observation: dict) -> MoveAction | SmeltAction:
|
131
|
-
if len(self.subplans) > 0:
|
132
|
-
return self.subplans.pop(0)
|
133
|
-
if len(self.plans) == 0:
|
134
|
-
raise ValueError("No more steps in plan")
|
135
|
-
|
136
|
-
if self.bbox_model is not None:
|
137
|
-
observed_inventory = self.bbox_model.get_inventory(
|
138
|
-
observation["image"].copy()
|
139
|
-
)
|
140
|
-
else:
|
141
|
-
observed_inventory = copy.deepcopy(observation["inventory"])
|
142
|
-
|
143
|
-
# take item from crafting slot
|
144
|
-
if slot_item := get_crafting_slot_item(observed_inventory):
|
145
|
-
# move item from crafting slot to inventory
|
146
|
-
free_slot = find_free_inventory_slot(observed_inventory, from_slot=0)
|
147
|
-
return MoveAction(
|
148
|
-
slot_from=0, slot_to=free_slot, quantity=slot_item["quantity"]
|
149
|
-
)
|
150
|
-
|
151
|
-
plan_recipe, new_inventory = self.plans.pop(0)
|
152
29
|
self.subplans = []
|
153
|
-
new_inventory_counter = Counter(new_inventory)
|
154
|
-
current_inventory = observed_inventory
|
155
|
-
current_inventory_counter = get_inventory_counter(current_inventory)
|
156
|
-
items_to_use_counter = current_inventory_counter - new_inventory_counter
|
157
|
-
new_items = new_inventory_counter - current_inventory_counter
|
158
|
-
if not self.use_fasterrcnn:
|
159
|
-
assert len(new_items) == 1
|
160
|
-
|
161
|
-
if isinstance(plan_recipe, ShapelessRecipe):
|
162
|
-
crafting_slot = 1
|
163
|
-
# add each item to crafting slots
|
164
|
-
for item, quantity in items_to_use_counter.items():
|
165
|
-
n = 0
|
166
|
-
while n < quantity:
|
167
|
-
from_slot = find_item_in_inventory(item, current_inventory)
|
168
|
-
|
169
|
-
# skip if from_slot is the crafting slot
|
170
|
-
if from_slot == crafting_slot:
|
171
|
-
crafting_slot += 1
|
172
|
-
n += 1
|
173
|
-
continue
|
174
|
-
|
175
|
-
action = MoveAction(
|
176
|
-
slot_from=from_slot, slot_to=crafting_slot, quantity=1
|
177
|
-
)
|
178
|
-
# update state of inventory
|
179
|
-
current_inventory = update_inventory(
|
180
|
-
current_inventory, from_slot, crafting_slot, 1
|
181
|
-
)
|
182
|
-
self.subplans.append(action)
|
183
|
-
crafting_slot += 1
|
184
|
-
n += 1
|
185
|
-
|
186
|
-
# if plan_recipe is a smelting recipe
|
187
|
-
elif isinstance(plan_recipe, SmeltingRecipe):
|
188
|
-
assert len(items_to_use_counter) == 1, "smelting only supports one item"
|
189
|
-
for item, quantity in items_to_use_counter.items():
|
190
|
-
from_slot = find_item_in_inventory(item, current_inventory)
|
191
|
-
free_slot = find_free_inventory_slot(
|
192
|
-
current_inventory, from_slot=from_slot
|
193
|
-
)
|
194
|
-
action = SmeltAction(
|
195
|
-
slot_from=from_slot, slot_to=free_slot, quantity=quantity
|
196
|
-
)
|
197
|
-
self.subplans.append(action)
|
198
|
-
|
199
|
-
# if plan_recipe is a shaped recipe
|
200
|
-
elif isinstance(plan_recipe, ShapedRecipe):
|
201
|
-
for i, row in enumerate(plan_recipe.kernel):
|
202
|
-
for j, item_set in enumerate(row):
|
203
|
-
inventory_position = (i * 3) + j + 1
|
204
|
-
valid_items = item_set_id_to_type(item_set)
|
205
|
-
for item in valid_items:
|
206
|
-
if items_to_use_counter[item] > 0:
|
207
|
-
from_slot = find_item_in_inventory(item, current_inventory)
|
208
|
-
action = MoveAction(
|
209
|
-
slot_from=from_slot,
|
210
|
-
slot_to=inventory_position,
|
211
|
-
quantity=1,
|
212
|
-
)
|
213
|
-
items_to_use_counter[item] -= 1
|
214
|
-
# update state of inventory
|
215
|
-
current_inventory = update_inventory(
|
216
|
-
current_inventory, from_slot, inventory_position, 1
|
217
|
-
)
|
218
|
-
self.subplans.append(action)
|
219
|
-
break
|
220
|
-
else:
|
221
|
-
raise NotImplementedError(f"Recipe type {type(plan_recipe)} not supported")
|
222
|
-
|
223
|
-
return self.subplans.pop(0)
|
224
30
|
|
225
31
|
def step(self, observation: dict, **kwargs) -> str:
|
226
32
|
# get action
|
227
|
-
if len(self.
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
action = self.
|
234
|
-
return
|
33
|
+
if len(self.subplans) == 0:
|
34
|
+
subplans, _ = get_subplans(observation)
|
35
|
+
# flatten subplans since they are nested for each subgoal
|
36
|
+
flattened_subplans = [item for sublist in subplans for item in sublist]
|
37
|
+
self.subplans = flattened_subplans
|
38
|
+
|
39
|
+
action = self.subplans.pop(0)
|
40
|
+
return action
|
@@ -14,10 +14,10 @@ plancraft/environment/__init__.py,sha256=XFsFny4lH195AwAmL-WeCaF9ZCMgc7IgXIwhQ8F
|
|
14
14
|
plancraft/environment/actions.py,sha256=AQxFaK4YW53mPwhuPhHrDF9wENSVjPHSWk0v77I1thw,9460
|
15
15
|
plancraft/environment/env.py,sha256=A4532st7JFBYBF_Nh0CEEi3ZTLJAeaB3t9PAIVSemj0,16390
|
16
16
|
plancraft/environment/items.py,sha256=Z9rhSyVDEoHF1pxRvhyiT94tyQJaWHi3wUHVcamz82o,221
|
17
|
-
plancraft/environment/planner.py,sha256=
|
17
|
+
plancraft/environment/planner.py,sha256=uIOJjIoyT_4pxeWeTKb8BkLJyKZG0-AMoEOkZs6Ua9A,19340
|
18
18
|
plancraft/environment/prompts.py,sha256=8QXclX0ygpL02uZichE1AVkbdn_0HGteD5bzo0FZGOU,6947
|
19
19
|
plancraft/environment/recipes.py,sha256=0vwzOU86eZmGN2EpZVSIvzxpx0AOBWNPxTtAOFBN2A0,19570
|
20
|
-
plancraft/environment/sampler.py,sha256=
|
20
|
+
plancraft/environment/sampler.py,sha256=79hLpTU0ajvMPoBsvSe8tE88x31c8Vlczb3tJZJcau0,7441
|
21
21
|
plancraft/environment/search.py,sha256=Dmdvj04kMvPlwvoWSc2261LTXV8RbMpS4FODV1YoZKs,1847
|
22
22
|
plancraft/environment/assets/constants.json,sha256=kyOIOh82CTTMMGEIS60k5k6M-6fkEmYDoGAnvi3Zx5k,1379016
|
23
23
|
plancraft/environment/assets/minecraft_font.ttf,sha256=AzoK9cgggXwjFPHtIO7uz-YaDrminl3nvB-VsaTvTAk,60992
|
@@ -1917,10 +1917,10 @@ plancraft/models/base.py,sha256=uhG1tRmsBerJzW8qHoLyLEYpveDv0co7AAhi4mSfyO4,661
|
|
1917
1917
|
plancraft/models/bbox_model.py,sha256=3b1IEspoHiVUR6GOWjEbp4YoxRhGkzKt-eOiwaN8NXo,17091
|
1918
1918
|
plancraft/models/dummy.py,sha256=856oEX6NquXSIIfQLTEFFeB8ib7VUUs5cB0TVHAiFvI,1248
|
1919
1919
|
plancraft/models/generators.py,sha256=F76_iPiqxUjDIrQwF58tzM0bLM91OkZJ0sBqBuki5wY,13939
|
1920
|
-
plancraft/models/oracle.py,sha256=
|
1920
|
+
plancraft/models/oracle.py,sha256=tMp9mTwD70T3qohj-LZhJFjHYWyiVHDh8gu27asVimI,1342
|
1921
1921
|
plancraft/models/utils.py,sha256=E-sZohvolWgGbpHQKgAgkgIfUJoVnT5pMt6JP8xLHKg,4034
|
1922
1922
|
plancraft/train/dataset.py,sha256=oFqEd4LG9oEQ-71teh0Wf7-jJbtybT2ZibfM2bBdBkM,5474
|
1923
|
-
plancraft-0.3.
|
1924
|
-
plancraft-0.3.
|
1925
|
-
plancraft-0.3.
|
1926
|
-
plancraft-0.3.
|
1923
|
+
plancraft-0.3.16.dist-info/METADATA,sha256=FIfCMBzCVuDWFCf5cPPKXLdm1EHbgKRpQ2eT5khTMN0,11148
|
1924
|
+
plancraft-0.3.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
1925
|
+
plancraft-0.3.16.dist-info/licenses/LICENSE,sha256=YGR8ehDB4t-T-lOQKMfKNR-2zsOU7E3E5NA8t25HKE0,1070
|
1926
|
+
plancraft-0.3.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|