Process swaped items last
This commit is contained in:
		
							parent
							
								
									dc82b384c5
								
							
						
					
					
						commit
						e5fedb90a6
					
				
							
								
								
									
										11
									
								
								Fill.py
								
								
								
								
							
							
						
						
									
										11
									
								
								Fill.py
								
								
								
								
							| 
						 | 
				
			
			@ -2,7 +2,7 @@ import logging
 | 
			
		|||
import typing
 | 
			
		||||
import collections
 | 
			
		||||
import itertools
 | 
			
		||||
from collections import Counter
 | 
			
		||||
from collections import Counter, deque
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from BaseClasses import CollectionState, Location, MultiWorld, Item
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ class FillError(RuntimeError):
 | 
			
		|||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def sweep_from_pool(base_state: CollectionState, itempool: list[Item]):
 | 
			
		||||
def sweep_from_pool(base_state: CollectionState, itempool):
 | 
			
		||||
    new_state = base_state.copy()
 | 
			
		||||
    for item in itempool:
 | 
			
		||||
        new_state.collect(item, True)
 | 
			
		||||
| 
						 | 
				
			
			@ -28,9 +28,9 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations,
 | 
			
		|||
    placements = []
 | 
			
		||||
 | 
			
		||||
    swapped_items = Counter()
 | 
			
		||||
    reachable_items = {}
 | 
			
		||||
    reachable_items: dict[str, deque] = {}
 | 
			
		||||
    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:
 | 
			
		||||
        # 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
 | 
			
		||||
                        spot_to_fill = placements.pop(i)
 | 
			
		||||
                        swapped_items[placed_item.player, placed_item.name] += 1
 | 
			
		||||
                        reachable_items.setdefault(
 | 
			
		||||
                            placed_item.player, []).append(placed_item)
 | 
			
		||||
                        reachable_items[placed_item.player].appendleft(placed_item)
 | 
			
		||||
                        itempool.append(placed_item)
 | 
			
		||||
                        break
 | 
			
		||||
                    else:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,4 @@
 | 
			
		|||
from typing import NamedTuple
 | 
			
		||||
import unittest
 | 
			
		||||
import pytest
 | 
			
		||||
from worlds.AutoWorld import World
 | 
			
		||||
| 
						 | 
				
			
			@ -6,13 +7,19 @@ from BaseClasses import MultiWorld, Region, RegionType, Item, Location
 | 
			
		|||
from worlds.generic.Rules import set_rule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_multi_world() -> MultiWorld:
 | 
			
		||||
    multi_world = MultiWorld(1)
 | 
			
		||||
    player1_id = 1
 | 
			
		||||
    world = World(multi_world, player1_id)
 | 
			
		||||
    multi_world.game[player1_id] = world
 | 
			
		||||
    multi_world.worlds[player1_id] = world
 | 
			
		||||
    multi_world.player_name = {player1_id: "Test Player 1"}
 | 
			
		||||
def generate_multi_world(players: int = 1) -> MultiWorld:
 | 
			
		||||
    multi_world = MultiWorld(players)
 | 
			
		||||
    multi_world.player_name = {}
 | 
			
		||||
    for i in range(players):
 | 
			
		||||
        player_id = i+1
 | 
			
		||||
        world = World(multi_world, player_id)
 | 
			
		||||
        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()
 | 
			
		||||
    # args = Namespace()
 | 
			
		||||
    # 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_default_common_options()
 | 
			
		||||
 | 
			
		||||
    region = Region("Menu", RegionType.Generic,
 | 
			
		||||
                    "Menu Region Hint", player1_id, multi_world)
 | 
			
		||||
    multi_world.regions.append(region)
 | 
			
		||||
 | 
			
		||||
    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]:
 | 
			
		||||
    locations = []
 | 
			
		||||
    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):
 | 
			
		||||
    def test_basic_fill_restrictive(self):
 | 
			
		||||
        multi_world = generate_multi_world()
 | 
			
		||||
        player1_id = 1
 | 
			
		||||
        player1_menu = multi_world.get_region("Menu", player1_id)
 | 
			
		||||
        player1 = generate_player_data(multi_world, 1, 2, 2)
 | 
			
		||||
 | 
			
		||||
        locations = generate_locations(2, player1_id, None, player1_menu)
 | 
			
		||||
        items = generate_items(2, player1_id, True)
 | 
			
		||||
        item0 = player1.prog_items[0]
 | 
			
		||||
        item1 = player1.prog_items[1]
 | 
			
		||||
        loc0 = player1.locations[0]
 | 
			
		||||
        loc1 = player1.locations[1]
 | 
			
		||||
 | 
			
		||||
        item0 = items[0]
 | 
			
		||||
        item1 = items[1]
 | 
			
		||||
        loc0 = locations[0]
 | 
			
		||||
        loc1 = locations[1]
 | 
			
		||||
 | 
			
		||||
        fill_restrictive(multi_world, multi_world.state, locations, items)
 | 
			
		||||
        fill_restrictive(multi_world, multi_world.state,
 | 
			
		||||
                         player1.locations, player1.prog_items)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(loc0.item, item1)
 | 
			
		||||
        self.assertEqual(loc1.item, item0)
 | 
			
		||||
        self.assertEqual([], locations)
 | 
			
		||||
        self.assertEqual([], items)
 | 
			
		||||
        self.assertEqual([], player1.locations)
 | 
			
		||||
        self.assertEqual([], player1.prog_items)
 | 
			
		||||
 | 
			
		||||
    def test_ordered_fill_restrictive(self):
 | 
			
		||||
        multi_world = generate_multi_world()
 | 
			
		||||
        player1_id = 1
 | 
			
		||||
        player1_menu = multi_world.get_region("Menu", player1_id)
 | 
			
		||||
        player1 = generate_player_data(multi_world, 1, 2, 2)
 | 
			
		||||
 | 
			
		||||
        locations = generate_locations(2, player1_id, None, player1_menu)
 | 
			
		||||
        items = generate_items(2, player1_id, True)
 | 
			
		||||
        item0 = player1.prog_items[0]
 | 
			
		||||
        item1 = player1.prog_items[1]
 | 
			
		||||
        loc0 = player1.locations[0]
 | 
			
		||||
        loc1 = player1.locations[1]
 | 
			
		||||
 | 
			
		||||
        item0 = items[0]
 | 
			
		||||
        item1 = items[1]
 | 
			
		||||
        loc0 = locations[0]
 | 
			
		||||
        loc1 = locations[1]
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
        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,
 | 
			
		||||
                         player1.locations, player1.prog_items)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(loc0.item, item0)
 | 
			
		||||
        self.assertEqual(loc1.item, item1)
 | 
			
		||||
 | 
			
		||||
    def test_reversed_fill_restrictive(self):
 | 
			
		||||
        multi_world = generate_multi_world()
 | 
			
		||||
        player1_id = 1
 | 
			
		||||
        player1_menu = multi_world.get_region("Menu", player1_id)
 | 
			
		||||
        player1 = generate_player_data(multi_world, 1, 2, 2)
 | 
			
		||||
 | 
			
		||||
        locations = generate_locations(2, player1_id, None, player1_menu)
 | 
			
		||||
        items = generate_items(2, player1_id, True)
 | 
			
		||||
        item0 = player1.prog_items[0]
 | 
			
		||||
        item1 = player1.prog_items[1]
 | 
			
		||||
        loc0 = player1.locations[0]
 | 
			
		||||
        loc1 = player1.locations[1]
 | 
			
		||||
 | 
			
		||||
        item0 = items[0]
 | 
			
		||||
        item1 = items[1]
 | 
			
		||||
        loc0 = locations[0]
 | 
			
		||||
        loc1 = locations[1]
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
        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,
 | 
			
		||||
                         player1.locations, player1.prog_items)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(loc0.item, item1)
 | 
			
		||||
        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):
 | 
			
		||||
        multi_world = generate_multi_world()
 | 
			
		||||
        player1_id = 1
 | 
			
		||||
        player1_menu = multi_world.get_region("Menu", player1_id)
 | 
			
		||||
        player1 = generate_player_data(multi_world, 1, 2, 2)
 | 
			
		||||
 | 
			
		||||
        locations = generate_locations(2, player1_id, None, player1_menu)
 | 
			
		||||
        items = generate_items(2, player1_id, True)
 | 
			
		||||
        item0 = player1.prog_items[0]
 | 
			
		||||
        item1 = player1.prog_items[1]
 | 
			
		||||
        loc0 = player1.locations[0]
 | 
			
		||||
        loc1 = player1.locations[1]
 | 
			
		||||
 | 
			
		||||
        item0 = items[0]
 | 
			
		||||
        item1 = items[1]
 | 
			
		||||
        loc0 = locations[0]
 | 
			
		||||
        loc1 = locations[1]
 | 
			
		||||
 | 
			
		||||
        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))
 | 
			
		||||
        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):
 | 
			
		||||
            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):
 | 
			
		||||
        multi_world = generate_multi_world()
 | 
			
		||||
        player1_id = 1
 | 
			
		||||
        player1_menu = multi_world.get_region("Menu", player1_id)
 | 
			
		||||
        player1 = generate_player_data(multi_world, 1, 3, 3)
 | 
			
		||||
 | 
			
		||||
        locations = generate_locations(3, player1_id, None, player1_menu)
 | 
			
		||||
        items = generate_items(3, player1_id, True)
 | 
			
		||||
        item0 = player1.prog_items[0]
 | 
			
		||||
        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]
 | 
			
		||||
        item1 = items[1]
 | 
			
		||||
        item2 = items[2]
 | 
			
		||||
        loc0 = locations[0]
 | 
			
		||||
        loc1 = locations[1]
 | 
			
		||||
        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))
 | 
			
		||||
        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):
 | 
			
		||||
            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):
 | 
			
		||||
        multi_world = generate_multi_world()
 | 
			
		||||
        player1_id = 1
 | 
			
		||||
        player1_menu = multi_world.get_region("Menu", player1_id)
 | 
			
		||||
        player1 = generate_player_data(multi_world, 1, 2, 2)
 | 
			
		||||
 | 
			
		||||
        locations = generate_locations(2, player1_id, None, player1_menu)
 | 
			
		||||
        items = generate_items(2, player1_id, True)
 | 
			
		||||
        item0 = player1.prog_items[0]
 | 
			
		||||
        item1 = player1.prog_items[1]
 | 
			
		||||
        loc1 = player1.locations[1]
 | 
			
		||||
 | 
			
		||||
        item0 = items[0]
 | 
			
		||||
        item1 = items[1]
 | 
			
		||||
        loc1 = locations[1]
 | 
			
		||||
 | 
			
		||||
        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))
 | 
			
		||||
        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):
 | 
			
		||||
            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])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue