Skip to content

Commit

Permalink
ChecksFinder: Refactor/Cleaning (ArchipelagoMW#3725)
Browse files Browse the repository at this point in the history
* Update ChecksFinder

* minor cleanup

* Check for compatible name

* Enable APWorld

* Update setup_en.md

* Update en_ChecksFinder.md

* The client is getting updated instead

* Qwint suggestions, ' -> ", streamline fill_slot_data

* Oops, too many refactors

---------

Co-authored-by: SunCat <[email protected]>
  • Loading branch information
Exempt-Medic and SunCatMC authored Aug 6, 2024
1 parent 98bb851 commit 90446ad
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 170 deletions.
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
"Adventure",
"ArchipIDLE",
"Archipelago",
"ChecksFinder",
"Clique",
"Final Fantasy",
"Lufia II Ancient Cave",
Expand Down
19 changes: 6 additions & 13 deletions worlds/checksfinder/Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,18 @@


class ItemData(typing.NamedTuple):
code: typing.Optional[int]
progression: bool
code: int
progression: bool = True


class ChecksFinderItem(Item):
game: str = "ChecksFinder"


item_table = {
"Map Width": ItemData(80000, True),
"Map Height": ItemData(80001, True),
"Map Bombs": ItemData(80002, True),
"Map Width": ItemData(80000),
"Map Height": ItemData(80001),
"Map Bombs": ItemData(80002),
}

required_items = {
}

item_frequencies = {

}

lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items()}
44 changes: 6 additions & 38 deletions worlds/checksfinder/Locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,14 @@


class AdvData(typing.NamedTuple):
id: typing.Optional[int]
region: str
id: int
region: str = "Board"


class ChecksFinderAdvancement(Location):
class ChecksFinderLocation(Location):
game: str = "ChecksFinder"


advancement_table = {
"Tile 1": AdvData(81000, 'Board'),
"Tile 2": AdvData(81001, 'Board'),
"Tile 3": AdvData(81002, 'Board'),
"Tile 4": AdvData(81003, 'Board'),
"Tile 5": AdvData(81004, 'Board'),
"Tile 6": AdvData(81005, 'Board'),
"Tile 7": AdvData(81006, 'Board'),
"Tile 8": AdvData(81007, 'Board'),
"Tile 9": AdvData(81008, 'Board'),
"Tile 10": AdvData(81009, 'Board'),
"Tile 11": AdvData(81010, 'Board'),
"Tile 12": AdvData(81011, 'Board'),
"Tile 13": AdvData(81012, 'Board'),
"Tile 14": AdvData(81013, 'Board'),
"Tile 15": AdvData(81014, 'Board'),
"Tile 16": AdvData(81015, 'Board'),
"Tile 17": AdvData(81016, 'Board'),
"Tile 18": AdvData(81017, 'Board'),
"Tile 19": AdvData(81018, 'Board'),
"Tile 20": AdvData(81019, 'Board'),
"Tile 21": AdvData(81020, 'Board'),
"Tile 22": AdvData(81021, 'Board'),
"Tile 23": AdvData(81022, 'Board'),
"Tile 24": AdvData(81023, 'Board'),
"Tile 25": AdvData(81024, 'Board'),
}

exclusion_table = {
}

events_table = {
}

lookup_id_to_name: typing.Dict[int, str] = {data.id: item_name for item_name, data in advancement_table.items() if data.id}
base_id = 81000
advancement_table = {f"Tile {i+1}": AdvData(base_id+i) for i in range(25)}
lookup_id_to_name: typing.Dict[int, str] = {data.id: item_name for item_name, data in advancement_table.items()}
6 changes: 0 additions & 6 deletions worlds/checksfinder/Options.py

This file was deleted.

52 changes: 16 additions & 36 deletions worlds/checksfinder/Rules.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,24 @@
from ..generic.Rules import set_rule
from BaseClasses import MultiWorld, CollectionState
from worlds.generic.Rules import set_rule
from BaseClasses import MultiWorld


def _has_total(state: CollectionState, player: int, total: int):
return (state.count('Map Width', player) + state.count('Map Height', player) +
state.count('Map Bombs', player)) >= total
items = ["Map Width", "Map Height", "Map Bombs"]


# Sets rules on entrances and advancements that are always applied
def set_rules(world: MultiWorld, player: int):
set_rule(world.get_location("Tile 6", player), lambda state: _has_total(state, player, 1))
set_rule(world.get_location("Tile 7", player), lambda state: _has_total(state, player, 2))
set_rule(world.get_location("Tile 8", player), lambda state: _has_total(state, player, 3))
set_rule(world.get_location("Tile 9", player), lambda state: _has_total(state, player, 4))
set_rule(world.get_location("Tile 10", player), lambda state: _has_total(state, player, 5))
set_rule(world.get_location("Tile 11", player), lambda state: _has_total(state, player, 6))
set_rule(world.get_location("Tile 12", player), lambda state: _has_total(state, player, 7))
set_rule(world.get_location("Tile 13", player), lambda state: _has_total(state, player, 8))
set_rule(world.get_location("Tile 14", player), lambda state: _has_total(state, player, 9))
set_rule(world.get_location("Tile 15", player), lambda state: _has_total(state, player, 10))
set_rule(world.get_location("Tile 16", player), lambda state: _has_total(state, player, 11))
set_rule(world.get_location("Tile 17", player), lambda state: _has_total(state, player, 12))
set_rule(world.get_location("Tile 18", player), lambda state: _has_total(state, player, 13))
set_rule(world.get_location("Tile 19", player), lambda state: _has_total(state, player, 14))
set_rule(world.get_location("Tile 20", player), lambda state: _has_total(state, player, 15))
set_rule(world.get_location("Tile 21", player), lambda state: _has_total(state, player, 16))
set_rule(world.get_location("Tile 22", player), lambda state: _has_total(state, player, 17))
set_rule(world.get_location("Tile 23", player), lambda state: _has_total(state, player, 18))
set_rule(world.get_location("Tile 24", player), lambda state: _has_total(state, player, 19))
set_rule(world.get_location("Tile 25", player), lambda state: _has_total(state, player, 20))
def set_rules(multiworld: MultiWorld, player: int):
for i in range(20):
set_rule(multiworld.get_location(f"Tile {i+6}", player), lambda state, i=i: state.has_from_list(items, player, i+1))


# Sets rules on completion condition
def set_completion_rules(world: MultiWorld, player: int):

width_req = 10-5
height_req = 10-5
bomb_req = 20-5
completion_requirements = lambda state: \
state.has("Map Width", player, width_req) and \
state.has("Map Height", player, height_req) and \
state.has("Map Bombs", player, bomb_req)
world.completion_condition[player] = lambda state: completion_requirements(state)
def set_completion_rules(multiworld: MultiWorld, player: int):
width_req = 5 # 10 - 5
height_req = 5 # 10 - 5
bomb_req = 15 # 20 - 5
multiworld.completion_condition[player] = lambda state: state.has_all_counts(
{
"Map Width": width_req,
"Map Height": height_req,
"Map Bombs": bomb_req,
}, player)
78 changes: 31 additions & 47 deletions worlds/checksfinder/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification
from .Items import ChecksFinderItem, item_table, required_items
from .Locations import ChecksFinderAdvancement, advancement_table, exclusion_table
from .Options import checksfinder_options
from BaseClasses import Region, Entrance, Tutorial, ItemClassification
from .Items import ChecksFinderItem, item_table
from .Locations import ChecksFinderLocation, advancement_table
from Options import PerGameCommonOptions
from .Rules import set_rules, set_completion_rules
from ..AutoWorld import World, WebWorld
from worlds.AutoWorld import World, WebWorld

client_version = 7

Expand All @@ -25,67 +25,51 @@ class ChecksFinderWorld(World):
ChecksFinder is a game where you avoid mines and find checks inside the board
with the mines! You win when you get all your items and beat the board!
"""
game: str = "ChecksFinder"
option_definitions = checksfinder_options
topology_present = True
game = "ChecksFinder"
options_dataclass = PerGameCommonOptions
web = ChecksFinderWeb()

item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = {name: data.id for name, data in advancement_table.items()}

def _get_checksfinder_data(self):
return {
'world_seed': self.multiworld.per_slot_randoms[self.player].getrandbits(32),
'seed_name': self.multiworld.seed_name,
'player_name': self.multiworld.get_player_name(self.player),
'player_id': self.player,
'client_version': client_version,
'race': self.multiworld.is_race,
}
def create_regions(self):
menu = Region("Menu", self.player, self.multiworld)
board = Region("Board", self.player, self.multiworld)
board.locations += [ChecksFinderLocation(self.player, loc_name, loc_data.id, board)
for loc_name, loc_data in advancement_table.items()]

def create_items(self):
connection = Entrance(self.player, "New Board", menu)
menu.exits.append(connection)
connection.connect(board)
self.multiworld.regions += [menu, board]

def create_items(self):
# Generate item pool
itempool = []
# Add all required progression items
for (name, num) in required_items.items():
itempool += [name] * num
# Add the map width and height stuff
itempool += ["Map Width"] * (10-5)
itempool += ["Map Height"] * (10-5)
itempool += ["Map Width"] * 5 # 10 - 5
itempool += ["Map Height"] * 5 # 10 - 5
# Add the map bombs
itempool += ["Map Bombs"] * (20-5)
itempool += ["Map Bombs"] * 15 # 20 - 5
# Convert itempool into real items
itempool = [item for item in map(lambda name: self.create_item(name), itempool)]
itempool = [self.create_item(item) for item in itempool]

self.multiworld.itempool += itempool

def set_rules(self):
set_rules(self.multiworld, self.player)
set_completion_rules(self.multiworld, self.player)

def create_regions(self):
menu = Region("Menu", self.player, self.multiworld)
board = Region("Board", self.player, self.multiworld)
board.locations += [ChecksFinderAdvancement(self.player, loc_name, loc_data.id, board)
for loc_name, loc_data in advancement_table.items() if loc_data.region == board.name]

connection = Entrance(self.player, "New Board", menu)
menu.exits.append(connection)
connection.connect(board)
self.multiworld.regions += [menu, board]

def fill_slot_data(self):
slot_data = self._get_checksfinder_data()
for option_name in checksfinder_options:
option = getattr(self.multiworld, option_name)[self.player]
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}:
slot_data[option_name] = int(option.value)
return slot_data
return {
"world_seed": self.random.getrandbits(32),
"seed_name": self.multiworld.seed_name,
"player_name": self.player_name,
"player_id": self.player,
"client_version": client_version,
"race": self.multiworld.is_race,
}

def create_item(self, name: str) -> Item:
def create_item(self, name: str) -> ChecksFinderItem:
item_data = item_table[name]
item = ChecksFinderItem(name,
ItemClassification.progression if item_data.progression else ItemClassification.filler,
item_data.code, self.player)
return item
return ChecksFinderItem(name, ItemClassification.progression, item_data.code, self.player)
5 changes: 0 additions & 5 deletions worlds/checksfinder/docs/en_ChecksFinder.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,3 @@ next to an icon, the number is how many you have gotten and the icon represents
Victory is achieved when the player wins a board they were given after they have received all of their Map Width, Map
Height, and Map Bomb items. The game will say at the bottom of the screen how many of each you have received.

## Unique Local Commands

The following command is only available when using the ChecksFinderClient to play with Archipelago.

- `/resync` Manually trigger a resync.
34 changes: 10 additions & 24 deletions worlds/checksfinder/docs/setup_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

- ChecksFinder from
the [Github releases Page for the game](https://github.com/jonloveslegos/ChecksFinder/releases) (latest version)
- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases)

## Configuring your YAML file

Expand All @@ -17,28 +16,15 @@ guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)

You can customize your options by visiting the [ChecksFinder Player Options Page](/games/ChecksFinder/player-options)

### Generating a ChecksFinder game
## Joining a MultiWorld Game

**ChecksFinder is meant to be played _alongside_ another game! You may not be playing it for long periods of time if
you play it by itself with another person!**

When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done,
the host will provide you with either a link to download your data file, or with a zip file containing everyone's data
files. You do not have a file inside that zip though!

You need to start ChecksFinder client yourself, it is located within the Archipelago folder.

### Connect to the MultiServer

First start ChecksFinder.

Once both ChecksFinder and the client are started. In the client at the top type in the spot labeled `Server` type the
`Ip Address` and `Port` separated with a `:` symbol.

The client will then ask for the username you chose, input that in the text box at the bottom of the client.

### Play the game

When the console tells you that you have joined the room, you're all set. Congratulations on successfully joining a
multiworld game!
1. Start ChecksFinder
2. Enter the following information:
- Enter the server url (starting from `wss://` for https connection like archipelago.gg, and starting from `ws://` for http connection and local multiserver)
- Enter server port
- Enter the name of the slot you wish to connect to
- Enter the room password (optional)
- Press `Play Online` to connect
3. Start playing!

Game options and controls are described in the readme on the github repository for the game

0 comments on commit 90446ad

Please sign in to comment.