Process swaped items last

This commit is contained in:
Brad Humphrey 2021-12-21 22:55:10 -07:00 committed by Fabian Dill
parent dc82b384c5
commit e5fedb90a6
2 changed files with 163 additions and 99 deletions

11
Fill.py
View File

@ -2,7 +2,7 @@ import logging
import typing import typing
import collections import collections
import itertools import itertools
from collections import Counter from collections import Counter, deque
from BaseClasses import CollectionState, Location, MultiWorld, Item from BaseClasses import CollectionState, Location, MultiWorld, Item
@ -14,7 +14,7 @@ class FillError(RuntimeError):
pass pass
def sweep_from_pool(base_state: CollectionState, itempool: list[Item]): def sweep_from_pool(base_state: CollectionState, itempool):
new_state = base_state.copy() new_state = base_state.copy()
for item in itempool: for item in itempool:
new_state.collect(item, True) new_state.collect(item, True)
@ -28,9 +28,9 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations,
placements = [] placements = []
swapped_items = Counter() swapped_items = Counter()
reachable_items = {} reachable_items: dict[str, deque] = {}
for item in itempool: for item in itempool:
reachable_items.setdefault(item.player, []).append(item) reachable_items.setdefault(item.player, deque()).append(item)
while any(reachable_items.values()) and locations: while any(reachable_items.values()) and locations:
# grab one item per player # grab one item per player
@ -75,8 +75,7 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations,
# add the old item to the back of the queue # add the old item to the back of the queue
spot_to_fill = placements.pop(i) spot_to_fill = placements.pop(i)
swapped_items[placed_item.player, placed_item.name] += 1 swapped_items[placed_item.player, placed_item.name] += 1
reachable_items.setdefault( reachable_items[placed_item.player].appendleft(placed_item)
placed_item.player, []).append(placed_item)
itempool.append(placed_item) itempool.append(placed_item)
break break
else: else:

View File

@ -1,3 +1,4 @@
from typing import NamedTuple
import unittest import unittest
import pytest import pytest
from worlds.AutoWorld import World from worlds.AutoWorld import World
@ -6,13 +7,19 @@ from BaseClasses import MultiWorld, Region, RegionType, Item, Location
from worlds.generic.Rules import set_rule from worlds.generic.Rules import set_rule
def generate_multi_world() -> MultiWorld: def generate_multi_world(players: int = 1) -> MultiWorld:
multi_world = MultiWorld(1) multi_world = MultiWorld(players)
player1_id = 1 multi_world.player_name = {}
world = World(multi_world, player1_id) for i in range(players):
multi_world.game[player1_id] = world player_id = i+1
multi_world.worlds[player1_id] = world world = World(multi_world, player_id)
multi_world.player_name = {player1_id: "Test Player 1"} multi_world.game[player_id] = world
multi_world.worlds[player_id] = world
multi_world.player_name[player_id] = "Test Player " + str(player_id)
region = Region("Menu", RegionType.Generic,
"Menu Region Hint", player_id, multi_world)
multi_world.regions.append(region)
multi_world.set_seed() multi_world.set_seed()
# args = Namespace() # args = Namespace()
# for name, option in world_type.options.items(): # for name, option in world_type.options.items():
@ -20,13 +27,24 @@ def generate_multi_world() -> MultiWorld:
# multi_world.set_options(args) # multi_world.set_options(args)
multi_world.set_default_common_options() multi_world.set_default_common_options()
region = Region("Menu", RegionType.Generic,
"Menu Region Hint", player1_id, multi_world)
multi_world.regions.append(region)
return multi_world return multi_world
class PlayerDefinition(NamedTuple):
id: int
menu: Region
locations: list[Location]
prog_items: list[Item]
def generate_player_data(multi_world: MultiWorld, player_id: int, location_count: int, prog_item_count: int) -> PlayerDefinition:
menu = multi_world.get_region("Menu", player_id)
locations = generate_locations(location_count, player_id, None, menu)
prog_items = generate_items(prog_item_count, player_id, True)
return PlayerDefinition(player_id, menu, locations, prog_items)
def generate_locations(count: int, player_id: int, address: int = None, region: Region = None) -> list[Location]: def generate_locations(count: int, player_id: int, address: int = None, region: Region = None) -> list[Location]:
locations = [] locations = []
for i in range(count): for i in range(count):
@ -48,124 +66,171 @@ def generate_items(count: int, player_id: int, advancement: bool = False, code:
class TestBase(unittest.TestCase): class TestBase(unittest.TestCase):
def test_basic_fill_restrictive(self): def test_basic_fill_restrictive(self):
multi_world = generate_multi_world() multi_world = generate_multi_world()
player1_id = 1 player1 = generate_player_data(multi_world, 1, 2, 2)
player1_menu = multi_world.get_region("Menu", player1_id)
locations = generate_locations(2, player1_id, None, player1_menu) item0 = player1.prog_items[0]
items = generate_items(2, player1_id, True) item1 = player1.prog_items[1]
loc0 = player1.locations[0]
loc1 = player1.locations[1]
item0 = items[0] fill_restrictive(multi_world, multi_world.state,
item1 = items[1] player1.locations, player1.prog_items)
loc0 = locations[0]
loc1 = locations[1]
fill_restrictive(multi_world, multi_world.state, locations, items)
self.assertEqual(loc0.item, item1) self.assertEqual(loc0.item, item1)
self.assertEqual(loc1.item, item0) self.assertEqual(loc1.item, item0)
self.assertEqual([], locations) self.assertEqual([], player1.locations)
self.assertEqual([], items) self.assertEqual([], player1.prog_items)
def test_ordered_fill_restrictive(self): def test_ordered_fill_restrictive(self):
multi_world = generate_multi_world() multi_world = generate_multi_world()
player1_id = 1 player1 = generate_player_data(multi_world, 1, 2, 2)
player1_menu = multi_world.get_region("Menu", player1_id)
locations = generate_locations(2, player1_id, None, player1_menu) item0 = player1.prog_items[0]
items = generate_items(2, player1_id, True) item1 = player1.prog_items[1]
loc0 = player1.locations[0]
loc1 = player1.locations[1]
item0 = items[0] multi_world.completion_condition[player1.id] = lambda state: state.has(
item1 = items[1] item0.name, player1.id) and state.has(item1.name, player1.id)
loc0 = locations[0] set_rule(loc1, lambda state: state.has(item0.name, player1.id))
loc1 = locations[1] fill_restrictive(multi_world, multi_world.state,
player1.locations, player1.prog_items)
multi_world.completion_condition[player1_id] = lambda state: state.has(
item0.name, player1_id) and state.has(item1.name, player1_id)
set_rule(loc1, lambda state: state.has(item0.name, player1_id))
fill_restrictive(multi_world, multi_world.state, locations, items)
self.assertEqual(loc0.item, item0) self.assertEqual(loc0.item, item0)
self.assertEqual(loc1.item, item1) self.assertEqual(loc1.item, item1)
def test_reversed_fill_restrictive(self): def test_reversed_fill_restrictive(self):
multi_world = generate_multi_world() multi_world = generate_multi_world()
player1_id = 1 player1 = generate_player_data(multi_world, 1, 2, 2)
player1_menu = multi_world.get_region("Menu", player1_id)
locations = generate_locations(2, player1_id, None, player1_menu) item0 = player1.prog_items[0]
items = generate_items(2, player1_id, True) item1 = player1.prog_items[1]
loc0 = player1.locations[0]
loc1 = player1.locations[1]
item0 = items[0] multi_world.completion_condition[player1.id] = lambda state: state.has(
item1 = items[1] item0.name, player1.id) and state.has(item1.name, player1.id)
loc0 = locations[0] set_rule(loc1, lambda state: state.has(item1.name, player1.id))
loc1 = locations[1] fill_restrictive(multi_world, multi_world.state,
player1.locations, player1.prog_items)
multi_world.completion_condition[player1_id] = lambda state: state.has(
item0.name, player1_id) and state.has(item1.name, player1_id)
set_rule(loc1, lambda state: state.has(item1.name, player1_id))
fill_restrictive(multi_world, multi_world.state, locations, items)
self.assertEqual(loc0.item, item1) self.assertEqual(loc0.item, item1)
self.assertEqual(loc1.item, item0) self.assertEqual(loc1.item, item0)
def test_multi_step_fill_restrictive(self):
multi_world = generate_multi_world()
player1 = generate_player_data(multi_world, 1, 4, 4)
items = player1.prog_items
locations = player1.locations
multi_world.completion_condition[player1.id] = lambda state: state.has(
items[2].name, player1.id) and state.has(items[3].name, player1.id)
set_rule(locations[1], lambda state: state.has(items[0].name, player1.id))
set_rule(locations[2], lambda state: state.has(items[1].name, player1.id))
set_rule(locations[3], lambda state: state.has(items[1].name, player1.id))
fill_restrictive(multi_world, multi_world.state,
player1.locations.copy(), player1.prog_items.copy())
self.assertEqual(locations[0].item, items[1])
self.assertEqual(locations[1].item, items[2])
self.assertEqual(locations[2].item, items[0])
self.assertEqual(locations[3].item, items[3])
def test_impossible_fill_restrictive(self): def test_impossible_fill_restrictive(self):
multi_world = generate_multi_world() multi_world = generate_multi_world()
player1_id = 1 player1 = generate_player_data(multi_world, 1, 2, 2)
player1_menu = multi_world.get_region("Menu", player1_id)
locations = generate_locations(2, player1_id, None, player1_menu) item0 = player1.prog_items[0]
items = generate_items(2, player1_id, True) item1 = player1.prog_items[1]
loc0 = player1.locations[0]
loc1 = player1.locations[1]
item0 = items[0] multi_world.completion_condition[player1.id] = lambda state: state.has(
item1 = items[1] item0.name, player1.id) and state.has(item1.name, player1.id)
loc0 = locations[0] set_rule(loc1, lambda state: state.has(item1.name, player1.id))
loc1 = locations[1] set_rule(loc0, lambda state: state.has(item0.name, player1.id))
multi_world.completion_condition[player1_id] = lambda state: state.has(
item0.name, player1_id) and state.has(item1.name, player1_id)
set_rule(loc1, lambda state: state.has(item1.name, player1_id))
set_rule(loc0, lambda state: state.has(item0.name, player1_id))
with pytest.raises(FillError): with pytest.raises(FillError):
fill_restrictive(multi_world, multi_world.state, locations, items) fill_restrictive(multi_world, multi_world.state,
player1.locations, player1.prog_items)
def test_circular_fill_restrictive(self): def test_circular_fill_restrictive(self):
multi_world = generate_multi_world() multi_world = generate_multi_world()
player1_id = 1 player1 = generate_player_data(multi_world, 1, 3, 3)
player1_menu = multi_world.get_region("Menu", player1_id)
locations = generate_locations(3, player1_id, None, player1_menu) item0 = player1.prog_items[0]
items = generate_items(3, player1_id, True) item1 = player1.prog_items[1]
item2 = player1.prog_items[2]
loc0 = player1.locations[0]
loc1 = player1.locations[1]
loc2 = player1.locations[2]
item0 = items[0] multi_world.completion_condition[player1.id] = lambda state: state.has(
item1 = items[1] item0.name, player1.id) and state.has(item1.name, player1.id) and state.has(item2.name, player1.id)
item2 = items[2] set_rule(loc1, lambda state: state.has(item0.name, player1.id))
loc0 = locations[0] set_rule(loc2, lambda state: state.has(item1.name, player1.id))
loc1 = locations[1] set_rule(loc0, lambda state: state.has(item2.name, player1.id))
loc2 = locations[2]
multi_world.completion_condition[player1_id] = lambda state: state.has(
item0.name, player1_id) and state.has(item1.name, player1_id) and state.has(item2.name, player1_id)
set_rule(loc1, lambda state: state.has(item0.name, player1_id))
set_rule(loc2, lambda state: state.has(item1.name, player1_id))
set_rule(loc0, lambda state: state.has(item2.name, player1_id))
with pytest.raises(FillError): with pytest.raises(FillError):
fill_restrictive(multi_world, multi_world.state, locations, items) fill_restrictive(multi_world, multi_world.state,
player1.locations, player1.prog_items)
def test_competing_fill_restrictive(self): def test_competing_fill_restrictive(self):
multi_world = generate_multi_world() multi_world = generate_multi_world()
player1_id = 1 player1 = generate_player_data(multi_world, 1, 2, 2)
player1_menu = multi_world.get_region("Menu", player1_id)
locations = generate_locations(2, player1_id, None, player1_menu) item0 = player1.prog_items[0]
items = generate_items(2, player1_id, True) item1 = player1.prog_items[1]
loc1 = player1.locations[1]
item0 = items[0] multi_world.completion_condition[player1.id] = lambda state: state.has(
item1 = items[1] item0.name, player1.id) and state.has(item0.name, player1.id) and state.has(item1.name, player1.id)
loc1 = locations[1] set_rule(loc1, lambda state: state.has(item0.name, player1.id)
and state.has(item1.name, player1.id))
multi_world.completion_condition[player1_id] = lambda state: state.has(
item0.name, player1_id) and state.has(item0.name, player1_id) and state.has(item1.name, player1_id)
set_rule(loc1, lambda state: state.has(item0.name, player1_id)
and state.has(item1.name, player1_id))
with pytest.raises(FillError): with pytest.raises(FillError):
fill_restrictive(multi_world, multi_world.state, locations, items) fill_restrictive(multi_world, multi_world.state,
player1.locations, player1.prog_items)
def test_multiplayer_fill_restrictive(self):
multi_world = generate_multi_world(2)
player1 = generate_player_data(multi_world, 1, 2, 2)
player2 = generate_player_data(multi_world, 2, 2, 2)
multi_world.completion_condition[player1.id] = lambda state: state.has(
player1.prog_items[0].name, player1.id) and state.has(
player1.prog_items[1].name, player1.id)
multi_world.completion_condition[player2.id] = lambda state: state.has(
player2.prog_items[0].name, player2.id) and state.has(
player2.prog_items[1].name, player2.id)
fill_restrictive(multi_world, multi_world.state, player1.locations +
player2.locations, player1.prog_items + player2.prog_items)
self.assertEqual(player1.locations[0].item, player1.prog_items[1])
self.assertEqual(player1.locations[1].item, player2.prog_items[1])
self.assertEqual(player2.locations[0].item, player1.prog_items[0])
self.assertEqual(player2.locations[1].item, player2.prog_items[0])
def test_multiplayer_rules_fill_restrictive(self):
multi_world = generate_multi_world(2)
player1 = generate_player_data(multi_world, 1, 2, 2)
player2 = generate_player_data(multi_world, 2, 2, 2)
multi_world.completion_condition[player1.id] = lambda state: state.has(
player1.prog_items[0].name, player1.id) and state.has(
player1.prog_items[1].name, player1.id)
multi_world.completion_condition[player2.id] = lambda state: state.has(
player2.prog_items[0].name, player2.id) and state.has(
player2.prog_items[1].name, player2.id)
set_rule(player2.locations[1], lambda state: state.has(
player2.prog_items[0].name, player2.id))
# set_rule(player2.locations[1], lambda state: state.has(player2.prog_items[1]))
fill_restrictive(multi_world, multi_world.state, player1.locations +
player2.locations, player1.prog_items + player2.prog_items)
self.assertEqual(player1.locations[0].item, player2.prog_items[0])
self.assertEqual(player1.locations[1].item, player2.prog_items[1])
self.assertEqual(player2.locations[0].item, player1.prog_items[0])
self.assertEqual(player2.locations[1].item, player1.prog_items[1])