ChecksFinder: Refactor/Cleaning (#3725)

* 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 <suncat.game@ya.ru>
This commit is contained in:
Exempt-Medic 2024-08-06 10:39:56 -04:00 committed by GitHub
parent 98bb8517e1
commit 90446ad175
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 69 additions and 170 deletions

View File

@ -66,7 +66,6 @@ non_apworlds: set = {
"Adventure", "Adventure",
"ArchipIDLE", "ArchipIDLE",
"Archipelago", "Archipelago",
"ChecksFinder",
"Clique", "Clique",
"Final Fantasy", "Final Fantasy",
"Lufia II Ancient Cave", "Lufia II Ancient Cave",

View File

@ -3,8 +3,8 @@ import typing
class ItemData(typing.NamedTuple): class ItemData(typing.NamedTuple):
code: typing.Optional[int] code: int
progression: bool progression: bool = True
class ChecksFinderItem(Item): class ChecksFinderItem(Item):
@ -12,16 +12,9 @@ class ChecksFinderItem(Item):
item_table = { item_table = {
"Map Width": ItemData(80000, True), "Map Width": ItemData(80000),
"Map Height": ItemData(80001, True), "Map Height": ItemData(80001),
"Map Bombs": ItemData(80002, True), "Map Bombs": ItemData(80002),
} }
required_items = { lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.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}

View File

@ -3,46 +3,14 @@ import typing
class AdvData(typing.NamedTuple): class AdvData(typing.NamedTuple):
id: typing.Optional[int] id: int
region: str region: str = "Board"
class ChecksFinderAdvancement(Location): class ChecksFinderLocation(Location):
game: str = "ChecksFinder" game: str = "ChecksFinder"
advancement_table = { base_id = 81000
"Tile 1": AdvData(81000, 'Board'), advancement_table = {f"Tile {i+1}": AdvData(base_id+i) for i in range(25)}
"Tile 2": AdvData(81001, 'Board'), lookup_id_to_name: typing.Dict[int, str] = {data.id: item_name for item_name, data in advancement_table.items()}
"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}

View File

@ -1,6 +0,0 @@
import typing
from Options import Option
checksfinder_options: typing.Dict[str, type(Option)] = {
}

View File

@ -1,44 +1,24 @@
from ..generic.Rules import set_rule from worlds.generic.Rules import set_rule
from BaseClasses import MultiWorld, CollectionState from BaseClasses import MultiWorld
def _has_total(state: CollectionState, player: int, total: int): items = ["Map Width", "Map Height", "Map Bombs"]
return (state.count('Map Width', player) + state.count('Map Height', player) +
state.count('Map Bombs', player)) >= total
# Sets rules on entrances and advancements that are always applied # Sets rules on entrances and advancements that are always applied
def set_rules(world: MultiWorld, player: int): def set_rules(multiworld: MultiWorld, player: int):
set_rule(world.get_location("Tile 6", player), lambda state: _has_total(state, player, 1)) for i in range(20):
set_rule(world.get_location("Tile 7", player), lambda state: _has_total(state, player, 2)) set_rule(multiworld.get_location(f"Tile {i+6}", player), lambda state, i=i: state.has_from_list(items, player, i+1))
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))
# Sets rules on completion condition # Sets rules on completion condition
def set_completion_rules(world: MultiWorld, player: int): def set_completion_rules(multiworld: MultiWorld, player: int):
width_req = 5 # 10 - 5
width_req = 10-5 height_req = 5 # 10 - 5
height_req = 10-5 bomb_req = 15 # 20 - 5
bomb_req = 20-5 multiworld.completion_condition[player] = lambda state: state.has_all_counts(
completion_requirements = lambda state: \ {
state.has("Map Width", player, width_req) and \ "Map Width": width_req,
state.has("Map Height", player, height_req) and \ "Map Height": height_req,
state.has("Map Bombs", player, bomb_req) "Map Bombs": bomb_req,
world.completion_condition[player] = lambda state: completion_requirements(state) }, player)

View File

@ -1,9 +1,9 @@
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification from BaseClasses import Region, Entrance, Tutorial, ItemClassification
from .Items import ChecksFinderItem, item_table, required_items from .Items import ChecksFinderItem, item_table
from .Locations import ChecksFinderAdvancement, advancement_table, exclusion_table from .Locations import ChecksFinderLocation, advancement_table
from .Options import checksfinder_options from Options import PerGameCommonOptions
from .Rules import set_rules, set_completion_rules from .Rules import set_rules, set_completion_rules
from ..AutoWorld import World, WebWorld from worlds.AutoWorld import World, WebWorld
client_version = 7 client_version = 7
@ -25,38 +25,34 @@ class ChecksFinderWorld(World):
ChecksFinder is a game where you avoid mines and find checks inside the board 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! with the mines! You win when you get all your items and beat the board!
""" """
game: str = "ChecksFinder" game = "ChecksFinder"
option_definitions = checksfinder_options options_dataclass = PerGameCommonOptions
topology_present = True
web = ChecksFinderWeb() web = ChecksFinderWeb()
item_name_to_id = {name: data.code for name, data in item_table.items()} 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()} location_name_to_id = {name: data.id for name, data in advancement_table.items()}
def _get_checksfinder_data(self): def create_regions(self):
return { menu = Region("Menu", self.player, self.multiworld)
'world_seed': self.multiworld.per_slot_randoms[self.player].getrandbits(32), board = Region("Board", self.player, self.multiworld)
'seed_name': self.multiworld.seed_name, board.locations += [ChecksFinderLocation(self.player, loc_name, loc_data.id, board)
'player_name': self.multiworld.get_player_name(self.player), for loc_name, loc_data in advancement_table.items()]
'player_id': self.player,
'client_version': client_version, connection = Entrance(self.player, "New Board", menu)
'race': self.multiworld.is_race, menu.exits.append(connection)
} connection.connect(board)
self.multiworld.regions += [menu, board]
def create_items(self): def create_items(self):
# Generate item pool # Generate item pool
itempool = [] itempool = []
# Add all required progression items
for (name, num) in required_items.items():
itempool += [name] * num
# Add the map width and height stuff # Add the map width and height stuff
itempool += ["Map Width"] * (10-5) itempool += ["Map Width"] * 5 # 10 - 5
itempool += ["Map Height"] * (10-5) itempool += ["Map Height"] * 5 # 10 - 5
# Add the map bombs # Add the map bombs
itempool += ["Map Bombs"] * (20-5) itempool += ["Map Bombs"] * 15 # 20 - 5
# Convert itempool into real items # 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 self.multiworld.itempool += itempool
@ -64,28 +60,16 @@ class ChecksFinderWorld(World):
set_rules(self.multiworld, self.player) set_rules(self.multiworld, self.player)
set_completion_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): def fill_slot_data(self):
slot_data = self._get_checksfinder_data() return {
for option_name in checksfinder_options: "world_seed": self.random.getrandbits(32),
option = getattr(self.multiworld, option_name)[self.player] "seed_name": self.multiworld.seed_name,
if slot_data.get(option_name, None) is None and type(option.value) in {str, int}: "player_name": self.player_name,
slot_data[option_name] = int(option.value) "player_id": self.player,
return slot_data "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_data = item_table[name]
item = ChecksFinderItem(name, return ChecksFinderItem(name, ItemClassification.progression, item_data.code, self.player)
ItemClassification.progression if item_data.progression else ItemClassification.filler,
item_data.code, self.player)
return item

View File

@ -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 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. 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.

View File

@ -4,7 +4,6 @@
- ChecksFinder from - ChecksFinder from
the [Github releases Page for the game](https://github.com/jonloveslegos/ChecksFinder/releases) (latest version) 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 ## Configuring your YAML file
@ -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) 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 1. Start ChecksFinder
you play it by itself with another person!** 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)
When you join a multiworld game, you will be asked to provide your YAML file to whoever is hosting. Once that is done, - Enter server port
the host will provide you with either a link to download your data file, or with a zip file containing everyone's data - Enter the name of the slot you wish to connect to
files. You do not have a file inside that zip though! - Enter the room password (optional)
- Press `Play Online` to connect
You need to start ChecksFinder client yourself, it is located within the Archipelago folder. 3. Start playing!
### 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!
Game options and controls are described in the readme on the github repository for the game