Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiServer: make !hint prefer early sphere #2862

Merged
merged 5 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,17 @@ def precollect_hint(location):

checks_in_area: Dict[int, Dict[str, Union[int, List[int]]]] = {}

# get spheres -> filter address==None -> skip empty
spheres: List[Dict[int, Set[int]]] = []
for sphere in multiworld.get_spheres():
current_sphere: Dict[int, Set[int]] = collections.defaultdict(set)
for sphere_location in sphere:
if type(sphere_location.address) is int:
current_sphere[sphere_location.player].add(sphere_location.address)

if current_sphere:
spheres.append(dict(current_sphere))

multidata = {
"slot_data": slot_data,
"slot_info": slot_info,
Expand All @@ -386,6 +397,7 @@ def precollect_hint(location):
"tags": ["AP"],
"minimum_versions": minimum_versions,
"seed_name": multiworld.seed_name,
"spheres": spheres,
"datapackage": data_package,
}
AutoWorld.call_all(multiworld, "modify_multidata", multidata)
Expand Down
21 changes: 20 additions & 1 deletion MultiServer.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ class Context:
all_item_and_group_names: typing.Dict[str, typing.Set[str]]
all_location_and_group_names: typing.Dict[str, typing.Set[str]]
non_hintable_names: typing.Dict[str, typing.Set[str]]
spheres: typing.List[typing.Dict[int, typing.Set[int]]]
Berserker66 marked this conversation as resolved.
Show resolved Hide resolved
""" each sphere is { player: { location_id, ... } } """

def __init__(self, host: str, port: int, server_password: str, password: str, location_check_points: int,
hint_cost: int, item_cheat: bool, release_mode: str = "disabled", collect_mode="disabled",
Expand Down Expand Up @@ -236,6 +238,7 @@ def __init__(self, host: str, port: int, server_password: str, password: str, lo
self.stored_data = {}
self.stored_data_notification_clients = collections.defaultdict(weakref.WeakSet)
self.read_data = {}
self.spheres = []

# init empty to satisfy linter, I suppose
self.gamespackage = {}
Expand Down Expand Up @@ -464,6 +467,9 @@ def _load(self, decoded_obj: dict, game_data_packages: typing.Dict[str, typing.A
for game_name, data in self.location_name_groups.items():
self.read_data[f"location_name_groups_{game_name}"] = lambda lgame=game_name: self.location_name_groups[lgame]

# sorted access spheres
self.spheres = decoded_obj.get("spheres", [])

# saving

def save(self, now=False) -> bool:
Expand Down Expand Up @@ -621,6 +627,16 @@ def get_rechecked_hints(self, team: int, slot: int):
self.recheck_hints(team, slot)
return self.hints[team, slot]

def get_sphere(self, player: int, location_id: int) -> int:
"""Get sphere of a location, -1 if spheres are not available."""
if self.spheres:
for i, sphere in enumerate(self.spheres):
if location_id in sphere.get(player, set()):
return i
raise KeyError(f"No Sphere found for location ID {location_id} belonging to player {player}. "
f"Location or player may not exist.")
return -1

def get_players_package(self):
return [NetworkPlayer(t, p, self.get_aliased_name(t, p), n) for (t, p), n in self.player_names.items()]

Expand Down Expand Up @@ -1548,6 +1564,9 @@ def get_hints(self, input_text: str, for_location: bool = False) -> bool:
self.ctx.random.shuffle(not_found_hints)
# By popular vote, make hints prefer non-local placements
not_found_hints.sort(key=lambda hint: int(hint.receiving_player != hint.finding_player))
# By another popular vote, prefer early sphere
not_found_hints.sort(key=lambda hint: self.ctx.get_sphere(hint.finding_player, hint.location),
reverse=True)
Comment on lines 1567 to +1571
Copy link
Collaborator

@beauxq beauxq Feb 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there any discussion about prioritizing early spheres over non-local placements (as it looks like this is doing)?

If there's an earlier sphere in my world, and a later sphere in someone else's world,
which do players prefer to be hinted?

Copy link
Collaborator

@remyjette remyjette Feb 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only related poll question about !hint was "Should !hint prioritize the earliest sphere?" but there was no poll question about the priority, and I didn't see any discussion about it in the thread. That's a good point.

I think it would be worth getting community feedback to decide which of these two "by popular vote"s should take priority.

My personal take is that I'm not hinting unless im stuck. So given that, I'd still prefer it to be non-local even if it's a later sphere, since hinting and seeing an item in my own world doesn't help me get un-stuck. An item one sphere later in another world on the other hand might, since players play at different speeds and dont breadth-first-search the locations in their world.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends on the game, I think. I'll frequently hint QOL stuff in Factorio because it makes my build easier. If I have research for progressive (long handed) inserters accessible within my world, I definitely want to know it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I hint QOL stuff a lot too. Though IDK if there's generally many "QOL" things I hint that have multiple of the same hint in any game; maybe tools in stardew, but that's not always QOL, sometimes it's necessary.

I think the approach of "trying to get unstuck" as a default behavior makes good sense.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes I worry that when I hint something in another players world, that it might be an annoyance or a chore that I'm putting them through.
But for me personally, I like it when another player hints something in my world. It makes it feel like there's more interaction (when sometimes the interaction feels lacking).
While if I hint something, and it's in my own world, there's no interaction with other players that comes from that.

So this could be another consideration on this question.


hints = found_hints
while can_pay > 0:
Expand All @@ -1557,9 +1576,9 @@ def get_hints(self, input_text: str, for_location: bool = False) -> bool:
hints.append(hint)
can_pay -= 1
self.ctx.hints_used[self.client.team, self.client.slot] += 1
points_available = get_client_points(self.ctx, self.client)

if not_found_hints:
points_available = get_client_points(self.ctx, self.client)
if hints and cost and int((points_available // cost) == 0):
self.output(
f"There may be more hintables, however, you cannot afford to pay for any more. "
Expand Down
Loading