Esempio n. 1
0
def generate_list(permalink: Permalink,
                  status_update: Optional[Callable[[str], None]],
                  timeout: Optional[int] = 120) -> LayoutDescription:
    if status_update is None:
        status_update = id

    data = permalink.layout_configuration.game_data

    create_patches_params = {
        "permalink": permalink,
        "game": data_reader.decode_data(data, False),
        "status_update": status_update
    }
    resolver_game = data_reader.decode_data(data)

    def create_failure(message: str):
        return GenerationFailure(message, permalink=permalink)

    new_patches = None
    final_state_by_resolve = None

    with multiprocessing.dummy.Pool(1) as dummy_pool:
        patches_async = dummy_pool.apply_async(func=_create_patches,
                                               kwds=create_patches_params)
        try:
            new_patches = patches_async.get(timeout)
        except multiprocessing.TimeoutError:
            raise create_failure("Timeout reached when generating patches.")

        resolve_params = {
            "configuration": permalink.layout_configuration,
            "game": resolver_game,
            "patches": new_patches,
            "status_update": status_update,
        }
        final_state_async = dummy_pool.apply_async(func=resolver.resolve,
                                                   kwds=resolve_params)
        try:
            final_state_by_resolve = final_state_async.get(60)
        except multiprocessing.TimeoutError:
            raise create_failure("Timeout reached when validating possibility")

    if final_state_by_resolve is None:
        # Why is final_state_by_distribution not OK?
        raise create_failure(
            "Generated seed was considered impossible by the solver")
    else:
        solver_path = _state_to_solver_path(final_state_by_resolve,
                                            resolver_game)

    return LayoutDescription(permalink=permalink,
                             version=VERSION,
                             patches=new_patches,
                             solver_path=solver_path)
Esempio n. 2
0
def test_round_trip_full():
    original_data = default_data.decode_default_prime2()

    game = data_reader.decode_data(original_data)
    encoded_data = data_writer.write_game_description(game)

    assert original_data == encoded_data
Esempio n. 3
0
def _create_test_layout_description(
        configuration: LayoutConfiguration,
        pickup_mapping: Iterable[int],
) -> LayoutDescription:
    """
    Creates a LayoutDescription for the given configuration, with the patches being for the given pickup_mapping
    :param configuration:
    :param pickup_mapping:
    :return:
    """
    game = data_reader.decode_data(configuration.game_data)
    pickup_database = game.pickup_database

    return LayoutDescription(
        version=VERSION,
        permalink=Permalink(
            seed_number=0,
            spoiler=True,
            patcher_configuration=PatcherConfiguration.default(),
            layout_configuration=configuration,
        ),
        patches=GamePatches.with_game(game).assign_new_pickups([
            (PickupIndex(i), pickup_database.original_pickup_mapping[PickupIndex(new_index)])
            for i, new_index in enumerate(pickup_mapping)
        ]),
        solver_path=())
Esempio n. 4
0
    def __init__(self, data: dict, edit_mode: bool):
        super().__init__()
        self.setupUi(self)
        set_default_window_icon(self)
        self.edit_mode = edit_mode

        self.world_selector_box.currentIndexChanged.connect(
            self.on_select_world)
        self.area_selector_box.currentIndexChanged.connect(self.on_select_area)
        self.other_node_connection_edit_button.clicked.connect(
            self._open_edit_connection)
        self.save_database_button.clicked.connect(self._save_database)
        self.verticalLayout.setAlignment(Qt.AlignTop)
        self.alternatives_grid_layout = QGridLayout(
            self.other_node_alternatives_contents)

        self.game_description = data_reader.decode_data(data, False)

        self.resource_database = self.game_description.resource_database
        self.world_list = self.game_description.world_list

        for world in sorted(self.world_list.worlds, key=lambda x: x.name):
            self.world_selector_box.addItem(world.name, userData=world)

        self.update_edit_mode()
Esempio n. 5
0
    def as_json(self) -> dict:
        result = {
            "info": {
                "version": self.version,
                "permalink": self.permalink.as_json,
            }
        }

        if self.permalink.spoiler:
            world_list = data_reader.decode_data(self.permalink.layout_configuration.game_data).world_list

            result["locations"] = {
                key: value
                for key, value in sorted(_pickup_assignment_to_item_locations(world_list,
                                                                              self.patches.pickup_assignment).items())
            }
            result["elevators"] = {
                world_list.node_name(_find_node_with_teleporter(world_list, teleporter_id), with_world=True):
                    world_list.node_name(world_list.resolve_teleporter_connection(connection), with_world=True)
                for teleporter_id, connection in self.patches.elevator_connection.items()
            }
            result["playthrough"] = [
                {
                    "path_from_previous": path.previous_nodes,
                    "node": path.node_name,
                }
                for path in self.solver_path
            ]

        return result
Esempio n. 6
0
    def from_json_dict(cls, json_dict: dict) -> "LayoutDescription":
        version = json_dict["info"]["version"]
        version_as_obj = StrictVersion(version)

        if version_as_obj < StrictVersion("0.21.0"):
            raise RuntimeError("Unsupported log file version '{}'.".format(version))

        # TODO: add try/catch to throw convert potential errors in "seed from future version broke"
        permalink = Permalink.from_json_dict(json_dict["info"]["permalink"])

        if not permalink.spoiler:
            raise ValueError("Unable to read details of seed log with spoiler disabled")

        game = data_reader.decode_data(permalink.layout_configuration.game_data)
        patches = GamePatches(
            _item_locations_to_pickup_assignment(game, json_dict["locations"]),
            _node_mapping_to_elevator_connection(game.world_list, json_dict["elevators"]),
            {},
            {},
            (),
            game.starting_location
        )

        return LayoutDescription(
            version=version,
            permalink=permalink,
            patches=patches,
            solver_path=_playthrough_list_to_solver_path(json_dict["playthrough"]),
        )
Esempio n. 7
0
    def __init__(self, game_connection: GameConnection, options: Options):
        super().__init__()
        self.setupUi(self)
        self.game_connection = game_connection
        common_qt_lib.set_default_window_icon(self)

        self.game_data = data_reader.decode_data(
            default_data.decode_default_prime2())
        self._energy_tank_item = find_resource_info_with_long_name(
            self.game_data.resource_database.item, "Energy Tank")

        self._item_to_label: Dict[ItemResourceInfo, ClickableLabel] = {}
        self._labels_for_keys = []
        self.create_tracker()

        self.game_connection_setup = GameConnectionSetup(
            self, self.game_connection_tool, self.connection_status_label,
            self.game_connection, options)
        self.force_update_button.setEnabled(not options.tracking_inventory)
        self.force_update_button.clicked.connect(self.on_force_update_button)

        self._update_timer = QTimer(self)
        self._update_timer.setInterval(100)
        self._update_timer.timeout.connect(self._on_timer_update)
        self._update_timer.setSingleShot(True)
Esempio n. 8
0
def test_add_elevator_connections_to_patches_random(echoes_game_data):
    # Setup
    game = data_reader.decode_data(echoes_game_data)
    permalink = dataclasses.replace(Permalink.default(),
                                    layout_configuration=dataclasses.replace(
                                        LayoutConfiguration.default(),
                                        elevators=LayoutElevators.RANDOMIZED))
    expected = dataclasses.replace(GamePatches.with_game(game),
                                   elevator_connection={
                                       589851:
                                       AreaLocation(1039999561, 1868895730),
                                       1572998:
                                       AreaLocation(1039999561, 3479543630),
                                       1966093:
                                       AreaLocation(2252328306, 408633584),
                                       2097251:
                                       AreaLocation(1119434212, 3331021649),
                                       136970379:
                                       AreaLocation(2252328306, 2068511343),
                                       3342446:
                                       AreaLocation(1039999561, 3205424168),
                                       3538975:
                                       AreaLocation(1119434212, 2806956034),
                                       152:
                                       AreaLocation(1006255871, 2889020216),
                                       393260:
                                       AreaLocation(464164546, 3145160350),
                                       524321:
                                       AreaLocation(464164546, 900285955),
                                       589949:
                                       AreaLocation(1006255871, 2278776548),
                                       122:
                                       AreaLocation(464164546, 3528156989),
                                       1245307:
                                       AreaLocation(1006255871, 1345979968),
                                       2949235:
                                       AreaLocation(1006255871, 1287880522),
                                       129:
                                       AreaLocation(1006255871, 2918020398),
                                       2162826:
                                       AreaLocation(1006255871, 1660916974),
                                       4522032:
                                       AreaLocation(1006255871, 3455543403),
                                       38:
                                       AreaLocation(1119434212, 1473133138),
                                       1245332:
                                       AreaLocation(2252328306, 2399252740),
                                       1638535:
                                       AreaLocation(2252328306, 2556480432),
                                   })

    # Run
    result = base_patches_factory.add_elevator_connections_to_patches(
        permalink.layout_configuration,
        Random(permalink.seed_number),
        GamePatches.with_game(game),
    )

    # Assert
    assert result == expected
Esempio n. 9
0
    def __init__(self, layout_configuration: LayoutConfiguration):
        super().__init__()
        self.setupUi(self)
        set_default_window_icon(self)

        self.layout_configuration = layout_configuration
        self.game_description = data_reader.decode_data(
            layout_configuration.game_data, True)

        self.logic, self._initial_state = logic_bootstrap(
            layout_configuration, self.game_description,
            GamePatches.with_game(self.game_description))
        self.resource_filter_check.stateChanged.connect(
            self.update_locations_tree_for_reachable_nodes)
        self.hide_collected_resources_check.stateChanged.connect(
            self.update_locations_tree_for_reachable_nodes)
        self.undo_last_action_button.clicked.connect(self._undo_last_action)

        self.configuration_label.setText(
            "Trick Level: {}; Elevators: Vanilla; Item Loss: {}".format(
                layout_configuration.trick_level.value,
                layout_configuration.starting_resources.configuration.value,
            ))

        self.setup_pickups_box()
        self.setup_possible_locations_tree()

        self._starting_nodes = {
            node
            for node in self.game_description.world_list.all_nodes
            if node.is_resource_node
            and node.resource() in self._initial_state.resources
        }
        self._add_new_action(self._initial_state.node)
Esempio n. 10
0
def test_round_trip_generated_patches(echoes_game_data, preset_manager):
    # Setup
    preset = dataclasses.replace(
        preset_manager.default_preset,
        layout_configuration=dataclasses.replace(
            preset_manager.default_preset.layout_configuration,
            trick_level_configuration=TrickLevelConfiguration(
                global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS,
                specific_levels={},
            )
        )
    )

    patches = generator._create_randomized_patches(
        permalink=Permalink(
            seed_number=1000,
            spoiler=True,
            preset=preset,
        ),
        game=data_reader.decode_data(echoes_game_data),
        status_update=lambda x: None,
    )

    # Run
    encoded = game_patches_serializer.serialize(patches, echoes_game_data)
    decoded = game_patches_serializer.decode(encoded, preset.layout_configuration)

    # Assert
    assert patches == decoded
def test_round_trip_generated_patches(echoes_game_data):
    # Setup
    configuration = LayoutConfiguration.from_params(
        trick_level_configuration=TrickLevelConfiguration(
            global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS,
            specific_levels={},
        ))

    patches = generator._create_randomized_patches(
        permalink=Permalink(
            seed_number=1000,
            spoiler=True,
            patcher_configuration=PatcherConfiguration.default(),
            layout_configuration=configuration,
        ),
        game=data_reader.decode_data(echoes_game_data),
        status_update=lambda x: None,
    )

    # Run
    encoded = game_patches_serializer.serialize(patches, echoes_game_data)
    decoded = game_patches_serializer.decode(encoded, configuration)

    # Assert
    assert patches == decoded
Esempio n. 12
0
def test_create_node_and_save(tmp_path, echoes_game_data, skip_qtbot):
    # Setup
    tmp_path.joinpath("test-game", "game").mkdir(parents=True)
    tmp_path.joinpath("human-readable").mkdir()

    db_path = Path(tmp_path.joinpath("test-game", "game"))

    window = DataEditorWindow(echoes_game_data, db_path, True, True)
    window.set_warning_dialogs_disabled(True)
    skip_qtbot.addWidget(window)

    # Run
    window._do_create_node("Some Node", None)
    window._save_as_internal_database()

    # Assert
    exported_data = data_reader.read_split_file(db_path)
    exported_game = data_reader.decode_data(exported_data)

    pretty_print.write_human_readable_game(exported_game,
                                           tmp_path.joinpath("human-readable"))
    new_files = {
        f.name: f.read_text("utf-8")
        for f in tmp_path.joinpath("human-readable").glob("*.txt")
    }

    existing_files = {
        f.name: f.read_text("utf-8")
        for f in tmp_path.joinpath("test-game", "game").glob("*.txt")
    }

    assert list(new_files.keys()) == list(existing_files.keys())
    assert new_files == existing_files
Esempio n. 13
0
def create_patcher_file(description: LayoutDescription,
                        cosmetic_patches: CosmeticPatches) -> dict:
    result = {}

    patcher_config = description.permalink.patcher_configuration
    layout = description.permalink.layout_configuration
    patches = description.patches
    game = data_reader.decode_data(layout.game_data,
                                   add_self_as_requirement_to_resources=False)

    result["spawn_point"] = _create_spawn_point_field(
        game.resource_database, layout.starting_resources, patches)

    result["pickups"] = _create_pickup_list(patches, game.pickup_database)

    result["elevators"] = _create_elevators_field(game.world_list, patches)

    result["specific_patches"] = {
        "hive_chamber_b_post_state": not is_vanilla_starting_location(layout),
        "warp_to_start": patcher_config.warp_to_start,
        "speed_up_credits": cosmetic_patches.speed_up_credits,
        "disable_hud_popup": cosmetic_patches.disable_hud_popup,
    }

    return result
Esempio n. 14
0
def test_create_node_and_save(mock_prime2_human_readable_path,
                              mock_prime2_json_path, tmpdir, echoes_game_data,
                              skip_qtbot):
    # Setup
    mock_prime2_human_readable_path.return_value = Path(tmpdir).joinpath(
        "human")
    mock_prime2_json_path.return_value = Path(tmpdir).joinpath("database")

    window = DataEditorWindow(echoes_game_data, True)
    skip_qtbot.addWidget(window)

    # Run
    window._do_create_node("Some Node")
    window._save_as_internal_database()

    # Assert
    with mock_prime2_json_path.return_value.open() as data_file:
        exported_data = json.load(data_file)
    exported_game = data_reader.decode_data(exported_data)

    output = io.StringIO()
    data_writer.write_human_readable_world_list(exported_game, output)

    assert mock_prime2_human_readable_path.return_value.read_text(
        "utf-8") == output.getvalue()
Esempio n. 15
0
def test_round_trip_full(game_enum: RandovaniaGame):
    original_data = default_data.read_json_then_binary(game_enum)[1]

    game = data_reader.decode_data(original_data)
    encoded_data = data_writer.write_game_description(game)

    assert list(encoded_data.keys()) == list(original_data.keys())
    assert encoded_data == original_data
def test_round_trip_small(test_files_dir):
    # Setup
    with test_files_dir.joinpath("prime2_small_v1.json").open("r") as data_file:
        original_data = game_migration.migrate_to_current(json.load(data_file))

    game = data_reader.decode_data(original_data)

    encoded_data = data_writer.write_game_description(game)
    assert encoded_data == original_data
Esempio n. 17
0
def test_round_trip_small(test_files_dir):
    # Setup
    with test_files_dir.joinpath("prime_data_as_json.json").open("r") as data_file:
        original_data = json.load(data_file)

    game = data_reader.decode_data(original_data)
    encoded_data = data_writer.write_game_description(game)

    assert original_data == encoded_data
Esempio n. 18
0
def update_human_readable_logic(args):
    from randovania.game_description import pretty_print
    from randovania.game_description import data_reader
    game = RandovaniaGame(args.game)

    path, data = default_data.read_json_then_binary(game)
    gd = data_reader.decode_data(data)

    path.with_suffix("").mkdir(parents=True, exist_ok=True)
    pretty_print.write_human_readable_game(gd, path.with_suffix(""))
Esempio n. 19
0
def test_create_elevators_field_no_elevator(empty_patches):
    # Setup
    game = data_reader.decode_data(default_data.decode_default_prime2())

    # Run
    with pytest.raises(ValueError) as exp:
        patcher_file._create_elevators_field(empty_patches, game)

    # Assert
    assert str(exp.value) == "Invalid elevator count. Expected 22, got 0."
Esempio n. 20
0
def test_create_elevators_field_no_elevator(empty_patches):
    # Setup
    game = data_reader.decode_data(default_data.decode_default_prime2(), False)

    # Run
    result = patcher_file._create_elevators_field(game.world_list,
                                                  empty_patches)

    # Assert
    assert result == []
Esempio n. 21
0
def add_elevator_connections_to_patches(
        layout_configuration: EchoesConfiguration, rng: Random,
        patches: GamePatches) -> GamePatches:
    """
    :param layout_configuration:
    :param rng:
    :param patches:
    :return:
    """
    elevator_connection = copy.copy(patches.elevator_connection)

    if layout_configuration.elevators != LayoutElevators.VANILLA:
        if rng is None:
            raise MissingRng("Elevator")

        world_list = data_reader.decode_data(
            layout_configuration.game_data).world_list
        areas_to_not_change = {
            2278776548,  # Sky Temple Gateway
            2068511343,  # Sky Temple Energy Controller
            3136899603,  # Aerie Transport Station
            1564082177,  # Aerie
        }

        elevator_db = elevator_distributor.create_elevator_database(
            world_list, areas_to_not_change)

        if layout_configuration.elevators in {
                LayoutElevators.TWO_WAY_RANDOMIZED,
                LayoutElevators.TWO_WAY_UNCHECKED
        }:
            connections = elevator_distributor.two_way_elevator_connections(
                rng=rng,
                elevator_database=elevator_db,
                between_areas=layout_configuration.elevators ==
                LayoutElevators.TWO_WAY_RANDOMIZED)
        else:
            connections = elevator_distributor.one_way_elevator_connections(
                rng=rng,
                elevator_database=elevator_db,
                world_list=world_list,
                elevator_target=layout_configuration.elevators !=
                LayoutElevators.ONE_WAY_ANYTHING,
                replacement=layout_configuration.elevators ==
                LayoutElevators.ONE_WAY_ELEVATOR_REPLACEMENT,
            )

        elevator_connection.update(connections)

    if layout_configuration.skip_final_bosses:
        elevator_connection[136970379] = AreaLocation(1006255871, 1393588666)

    return dataclasses.replace(patches,
                               elevator_connection=elevator_connection)
def test_decode(patches_with_data, default_layout_configuration):
    encoded, expected = patches_with_data

    game = data_reader.decode_data(default_layout_configuration.game_data)
    pool = pool_creator.calculate_pool_results(default_layout_configuration, game.resource_database)

    # Run
    decoded = game_patches_serializer.decode_single(0, {0: pool}, game, encoded, default_layout_configuration)

    # Assert
    assert decoded == expected
def test_add_elevator_connections_to_patches_vanilla(
        echoes_game_data, default_layout_configuration):
    # Setup
    game = data_reader.decode_data(echoes_game_data)

    # Run
    result = base_patches_factory.add_elevator_connections_to_patches(
        default_layout_configuration, Random(0), game.create_game_patches())

    # Assert
    assert result == game.create_game_patches()
Esempio n. 24
0
def decode(game_modifications: List[dict],
           layout_configurations: Dict[int, EchoesConfiguration],
           ) -> Dict[int, GamePatches]:

    all_games = {index: data_reader.decode_data(configuration.game_data)
                 for index, configuration in layout_configurations.items()}
    all_pools = {index: pool_creator.calculate_pool_results(configuration, all_games[index].resource_database)
                 for index, configuration in layout_configurations.items()}
    return {
        index: decode_single(index, all_pools, all_games[index], modifications, layout_configurations[index])
        for index, modifications in enumerate(game_modifications)
    }
Esempio n. 25
0
def test_add_elevator_connections_to_patches_vanilla(echoes_game_data):
    # Setup
    game = data_reader.decode_data(echoes_game_data)
    permalink = Permalink.default()

    # Run
    result = base_patches_factory.add_elevator_connections_to_patches(
        permalink.layout_configuration, Random(permalink.seed_number),
        GamePatches.with_game(game))

    # Assert
    assert result == GamePatches.with_game(game)
Esempio n. 26
0
def test_round_trip_small(test_files_dir, small_name):
    # Setup
    with test_files_dir.joinpath(small_name).open("r") as data_file:
        original_data = json.load(data_file)

    game = data_reader.decode_data(original_data)
    encoded_data = data_writer.write_game_description(game)

    # # Uncomment the following to update the file
    # with test_files_dir.joinpath(small_name).open("w", encoding="utf-8") as meta:
    #     json.dump(encoded_data, meta, indent=4); assert False

    assert encoded_data == original_data
def run_bootstrap(preset: Preset):
    game = data_reader.decode_data(preset.configuration.game_data)
    permalink = Permalink(
        seed_number=15000,
        spoiler=True,
        presets={0: preset},
    )
    patches = base_patches_factory.create_base_patches(preset.configuration,
                                                       Random(15000), game,
                                                       False)
    _, state = logic_bootstrap(preset.configuration, game, patches)

    return game, state, permalink
Esempio n. 28
0
def validate_command_logic(args):
    debug.set_level(args.debug)
    data = prime_database.decode_data_file(args)
    game = data_reader.decode_data(data)

    description = LayoutDescription.from_file(args.layout_file)
    configuration = description.permalink.layout_configuration
    patches = description.patches

    final_state_by_resolve = resolver.resolve(configuration=configuration,
                                              game=game,
                                              patches=patches)
    print(final_state_by_resolve)
Esempio n. 29
0
def serialize(patches: GamePatches, game_data: dict) -> dict:
    """
    Encodes a given GamePatches into a JSON-serializable dict.
    :param patches:
    :param game_data:
    :return:
    """
    game = data_reader.decode_data(game_data)
    world_list = game.world_list
    ordered_pickups = []

    result = {
        "starting_location":
        world_list.area_name(
            world_list.area_by_area_location(patches.starting_location)),
        "starting_items": {
            resource_info.long_name: quantity
            for resource_info, quantity in patches.starting_items.items()
        },
        "elevators": {
            world_list.area_name(
                world_list.nodes_to_area(
                    _find_node_with_teleporter(world_list, teleporter_id))):
            world_list.area_name(
                world_list.nodes_to_area(
                    world_list.resolve_teleporter_connection(connection)))
            for teleporter_id, connection in
            patches.elevator_connection.items()
        },
        "translators": {
            _name_for_gate(gate): requirement.long_name
            for gate, requirement in patches.translator_gates.items()
        },
        "locations": {
            key: value
            for key, value in _pickup_assignment_to_item_locations(
                world_list, patches.pickup_assignment,
                ordered_pickups).items()
        },
        "hints": {
            str(asset.asset_id): hint.as_json
            for asset, hint in patches.hints.items()
        }
    }

    b = bitpacking.pack_value(
        BitPackPickupEntryList(ordered_pickups, game.resource_database))
    result["_locations_internal"] = base64.b64encode(b).decode("utf-8")

    return result
Esempio n. 30
0
def rename_docks_logic(args):
    from randovania.game_description import data_reader
    from randovania.game_description import data_writer
    from randovania.game_description import pretty_print
    from randovania.game_description.editor import Editor
    from randovania.game_description.world.dock_node import DockNode
    from randovania.game_description import integrity_check

    game = RandovaniaGame(args.game)

    path, data = default_data.read_json_then_binary(game)
    gd = data_reader.decode_data(data)

    # Make the changes
    editor = Editor(gd)

    for world in gd.world_list.worlds:
        for area in world.areas:
            for i in range(len(area.nodes)):
                node = area.nodes[i]
                if not isinstance(node, DockNode):
                    continue

                valid_name, suffix = integrity_check.dock_has_correct_name(
                    area, node)

                if not valid_name:
                    expected_name = integrity_check.base_dock_name(node)
                    docks_to_same_target = integrity_check.docks_with_same_base_name(
                        area, expected_name)

                    if suffix is None:
                        suffix = f" ({docks_to_same_target.index(node) + 1})"

                    print(
                        f"In {area.name}, renaming '{node.name}' to '{expected_name}{suffix}'"
                    )
                    editor.replace_node(
                        area, node,
                        dataclasses.replace(node,
                                            name=f"{expected_name}{suffix}"))

    # Write it back
    logging.info("Writing database files")
    new_data = data_writer.write_game_description(gd)
    data_writer.write_as_split_files(new_data, path)

    logging.info("Writing human readable")
    path.with_suffix("").mkdir(parents=True, exist_ok=True)
    pretty_print.write_human_readable_game(gd, path.with_suffix(""))