Skip to content

Commit

Permalink
- Tentative Distance code (not finished)
Browse files Browse the repository at this point in the history
  • Loading branch information
agilbert1412 committed Aug 28, 2024
1 parent eaf1af6 commit 5dfe3c0
Show file tree
Hide file tree
Showing 8 changed files with 385 additions and 36 deletions.
9 changes: 7 additions & 2 deletions worlds/apgo/Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def __call__(self, item_data: Union[str, APGOItemData]) -> APGOItem:
item_table = {item.name: item for item in all_items}


def create_items(item_factory: APGOItemFactory, trips: List[Trip], options: APGOOptions, random: Random) -> List[APGOItem]:
def create_items(item_factory: APGOItemFactory, trips: Dict[str, Trip], options: APGOOptions, random: Random) -> List[APGOItem]:
items = []
create_goal_items(item_factory, items, options)
create_keys(item_factory, items, trips, options)
Expand Down Expand Up @@ -111,13 +111,18 @@ def create_short_macguffin_items(item_factory: APGOItemFactory, items: List[APGO
items.append(item_factory(ItemName.macguffin_exclamation)),


def create_keys(item_factory: APGOItemFactory, items: List[APGOItem], trips: List[Trip], options: APGOOptions) -> None:
def create_keys(item_factory: APGOItemFactory, items: List[APGOItem], trips: Dict[str, Trip], options: APGOOptions) -> None:
if options.number_of_locks <= 0:
return

items.extend([item_factory(item) for item in [ItemName.key] * options.number_of_locks])


def create_at_least_one_distance_reduction(item_factory: APGOItemFactory, items: List[APGOItem], trips: Dict[str, Trip], options: APGOOptions) -> None:
if options.enable_distance_reductions:
items.append(item_factory(ItemName.distance_reduction))


def create_traps(item_factory: APGOItemFactory, items: List[APGOItem], number_filler_items: int, options: APGOOptions, random: Random) -> None:
trap_rate = options.trap_rate.value
number_of_traps = (number_filler_items * trap_rate) // 100
Expand Down
9 changes: 4 additions & 5 deletions worlds/apgo/Locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ def __call__(self, name: str, code: Optional[int], region: str) -> None:
raise NotImplementedError


def create_locations(location_factory: APGOLocationFactory, trips: List[Trip]) -> None:
for trip in trips:
trip_name = trip.location_name
trip_id = location_table[trip_name]
def create_locations(location_factory: APGOLocationFactory, trips: Dict[str, Trip]) -> None:
for location_name, trip in trips.items():
trip_id = location_table[location_name]
trip_region = area_number(trip.template.key_needed)
location_factory(trip_name, trip_id, trip_region)
location_factory(location_name, trip_id, trip_region)
51 changes: 27 additions & 24 deletions worlds/apgo/Options.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,32 @@
from dataclasses import dataclass

from Options import DeathLink, NamedRange, PerGameCommonOptions, Range, Toggle, Choice
from Options import DeathLink, NamedRange, PerGameCommonOptions, Range, Toggle, Choice, ProgressionBalancing

standard_race_lengths = {
"2k": 2000,
"5k": 5000,
"10k": 10000,
"half_marathon": 21098,
"marathon": 42195,
"50k": 50000,
"50_miler": 80467,
"100k": 100000,
"100_miler": 160934
}

"2k": 2000,
"5k": 5000,
"10k": 10000,
"half_marathon": 21098,
"marathon": 42195,
"50k": 50000,
"50_miler": 80467,
"100k": 100000,
"100_miler": 160934
}

standard_race_speeds = {
"no_speed_requirements": 0,
"slow_walk": 2,
"fast_walk": 5,
"slow_jog": 7,
"fast_jog": 9,
"slow_run": 10,
"fast_run": 14,
"sprint": 16,
"slow_bicycle": 15,
"medium_bicycle": 22,
"fast_bicycle": 30,
}
"no_speed_requirements": 0,
"slow_walk": 2,
"fast_walk": 5,
"slow_jog": 7,
"fast_jog": 9,
"slow_run": 10,
"fast_run": 14,
"sprint": 16,
"slow_bicycle": 15,
"medium_bicycle": 22,
"fast_bicycle": 30,
}


class Goal(Choice):
Expand Down Expand Up @@ -151,6 +150,9 @@ class TrapRate(NamedRange):
}


# class ApGoProgressionBalancing(ProgressionBalancing):


@dataclass
class APGOOptions(PerGameCommonOptions):
goal: Goal
Expand All @@ -163,4 +165,5 @@ class APGOOptions(PerGameCommonOptions):
enable_scouting_distance_bonuses: EnableScoutingDistanceBonuses
enable_collection_distance_bonuses: EnableCollectionDistanceBonuses
trap_rate: TrapRate
# progression_balancing: ApGoProgressionBalancing
death_link: DeathLink
4 changes: 2 additions & 2 deletions worlds/apgo/Regions.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
from .Options import APGOOptions


def create_regions(multiworld: MultiWorld, player: int, options: APGOOptions, trips: List[Trip]) -> Dict[str, Region]:
def create_regions(multiworld: MultiWorld, player: int, options: APGOOptions, trips: Dict[str, Trip]) -> Dict[str, Region]:
created_regions = dict()
created_regions["Menu"] = Region("Menu", player, multiworld)
created_regions[area_number(0)] = Region(area_number(0), player, multiworld)
created_regions["Menu"].connect(created_regions[area_number(0)])

max_key = 0
for trip in trips:
for trip in trips.values():
if trip.template.key_needed > max_key:
max_key = trip.template.key_needed

Expand Down
13 changes: 10 additions & 3 deletions worlds/apgo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from BaseClasses import Region, Location, Item, ItemClassification, Tutorial
from worlds.AutoWorld import World, WebWorld
from .ItemNames import ItemName

from .Regions import create_regions
from .Options import APGOOptions
Expand Down Expand Up @@ -48,10 +49,13 @@ class APGOWorld(World):
options_dataclass = APGOOptions
options: APGOOptions

trips: List[Trip]
trips: Dict[str, Trip]
number_distance_reductions: int

def generate_early(self):
self.trips = generate_trips(self.options.as_dict(*[option_name for option_name in self.options_dataclass.type_hints]), self.random)
generated_trips = generate_trips(self.options.as_dict(*[option_name for option_name in self.options_dataclass.type_hints]), self.random)
self.trips = {trip.location_name: trip for trip in generated_trips}
self.number_distance_reductions = 0

def create_regions(self) -> None:
world_regions = create_regions(self.multiworld, self.player, self.options, self.trips)
Expand All @@ -68,14 +72,17 @@ def create_items(self) -> None:
created_items = create_items(self.create_item, self.trips, self.options, self.random)
self.multiworld.itempool += created_items

# This is a weird way to count but it works...
self.number_distance_reductions += sum(item.name == ItemName.distance_reduction for item in created_items)

def create_item(self, item: Union[str, APGOItemData]) -> APGOItem:
if isinstance(item, str):
item = item_table[item]

return APGOItem(item.name, item.classification, item.id, self.player)

def fill_slot_data(self) -> Mapping[str, Any]:
trips_dictionary = {trip.location_name: trip.as_dict() for trip in self.trips}
trips_dictionary = {location_name: trip.as_dict() for location_name, trip in self.trips.items()}
slot_data = {
self.options.goal.internal_name: self.options.goal.value,
self.options.minimum_distance.internal_name: self.options.minimum_distance.value,
Expand Down
75 changes: 75 additions & 0 deletions worlds/apgo/distance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import math
from typing import Union

from . import Trip
from .Options import MinimumDistance, MaximumDistance


def get_current_distance(trip: Trip, distance_reductions: int,
minimum_distance: Union[int, MinimumDistance],
maximum_distance: Union[int, MaximumDistance],
expected_number_of_reductions: int,
expected_number_of_tiers: int) -> int:
distance_tier = trip.template.distance_tier

reduction_percent = 1 / (expected_number_of_reductions + 4)
remainder_percent = 1 - reduction_percent

distance_range = maximum_distance - minimum_distance
if distance_range <= 0:
return minimum_distance
smallest_distance_multiplier = math.pow(remainder_percent, expected_number_of_reductions)
distance_range_out_of_logic = distance_range / smallest_distance_multiplier
distance_range_per_tier_out_of_logic = distance_range_out_of_logic / expected_number_of_tiers

distance_multiplier = math.pow(remainder_percent, distance_reductions)

reduced_range_per_tier = distance_range_per_tier_out_of_logic * distance_multiplier
extra_distance = distance_tier * reduced_range_per_tier
final_distance = minimum_distance + int(extra_distance)
return final_distance


def get_reductions_needed_to_be_reachable(trip: Trip,
minimum_distance: Union[int, MinimumDistance],
maximum_distance: Union[int, MaximumDistance],
expected_number_of_reductions: int,
expected_number_of_tiers: int) -> int:
distance_tier = trip.template.distance_tier

reduction_percent = 1 / (expected_number_of_reductions + 4)
remainder_percent = 1 - reduction_percent

distance_range = maximum_distance - minimum_distance
if distance_range <= 0:
return 0
smallest_distance_multiplier = math.pow(remainder_percent, expected_number_of_reductions)
distance_range_out_of_logic = distance_range / smallest_distance_multiplier
distance_range_per_tier_out_of_logic = distance_range_out_of_logic / expected_number_of_tiers

# Initial distance without reductions
initial_distance = minimum_distance + int(distance_tier * distance_range_per_tier_out_of_logic)

if initial_distance <= maximum_distance:
return 0 # No reductions needed if initial distance is already within max distance.

# Calculate reductions needed using the formula derived above
required_ratio = distance_range / (distance_tier * distance_range_per_tier_out_of_logic)

if required_ratio <= 0:
return int('inf') # Infinite reductions needed if the ratio is invalid (shouldn't happen in normal cases)

reductions_needed = math.log(required_ratio) / math.log(remainder_percent)

return math.ceil(reductions_needed) # Round up to ensure we meet or exceed the target reduction

# distance_range_per_tier_in_logic = distance_range / expected_number_of_tiers
# required_multiplier = distance_range_per_tier_in_logic / distance_range_per_tier_out_of_logic

# distance_reductions = math.log(required_multiplier, remainder_percent)
# return math.ceil(distance_reductions)

# reduced_range_per_tier = distance_range_per_tier_out_of_logic * distance_multiplier
# extra_distance = distance_tier * reduced_range_per_tier
# final_distance = minimum_distance + int(extra_distance)
# return distance_reductions
26 changes: 26 additions & 0 deletions worlds/apgo/rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import math

from BaseClasses import ItemClassification
from worlds.generic.Rules import add_rule, item_name_in_locations, set_rule
from . import Options, ItemName


def set_rules(world, player: int, world_options: Options.APGOOptions):
set_key_rules(world, player, world_options)
set_distance_rules(world, player, world_options)


def set_key_rules(world, player: int, world_options: Options.APGOOptions):
if world_options.number_of_locks <= 0:
return

for lock in range(1, world_options.number_of_locks+1):
set_rule(world.get_entrance(f"Area {lock-1} -> Area {lock}", player), lambda state: state.has(ItemName.key, player, lock))


def set_distance_rules(world, player: int, world_options: Options.APGOOptions):
if not world_options.enable_distance_reductions:
return

for trip_name, trip in world.trips.items():
set_rule(world.get_entrance(f"Area {lock-1} -> Area {lock}", player), lambda state: state.has(ItemName.key, player, lock))
Loading

0 comments on commit 5dfe3c0

Please sign in to comment.