Releases: Auzzy/18xx-routes
Disallow track reuse
Validate railroad trains
This adds validation for the trains a railroad has by phase. Each game can implement its train validation by phase, which are then used to evaluate the list of trains held by each railroad.
I am intentionally avoiding the term "train limit" because it's not quite so straightforward. For example, in 1846, 2 trains only phase out in phase three, and 4 and 3/5 trains only phase out in phase four. Both cases allow a railroad to (temporarily) legally be in violation of the train limit. Rather than trying to capture this some "phased out" indication, it's much easier (both to code and for users) to just treat these as under the effective "train limit".
To provide more precise validation, each game can further define the maximum number of a list of train types. For example, in phase three of 1846, the following 3 train validations are applied:
- Combined total of three trains of the types 3/5, 4, 4/6, or 5
- Max of four trains of type 2
- Combined total of six trains of the types 2, 3/5, 4, 4/6, or 5
Removed railroads hold home station
This was a regression introduced in 0.7. In 1846, a railroad removed from the game places one of its tokens on its home station, thus occupying it and potentially blocking it.
A test was added to the test suite to prevent this regression from recurring.
Allow closing railroads
If the game allows railroads to close, we should accept a railroad marked closed. For route finding purposes, this is the same as an unopened railroad. However, with the addition of station validation in 0.7, it does matter for the handling of reserved station slots. In 1846, a closed railroad forfeits its station reservations.
Missing data file
Correct missing the tile database file in the release via setup.py.
Add more validation
Adds a few new validations to be performed at board validation time.
- The number of each tile placed shouldn't exceed it's availability in the game.
- If a space is home to a railroad, either it must contain that railroad's station, or a space must be left for it. This is true even if that railroad is removed from the game.
- If a space has a reserved slot for a railroad, either it must contain that railroad's station or a space must be left for it, unless it's either been removed from the game or a particular phase has been reached (e.g. in 1846, phase 4)
- A board space defines its upgrade_attrs as a list of lists. When placing a tile, its upgrade_attrs must match one of these sublists in its entirety. For example, if the space has [["port", "z"], []], then any tile must either have ["port", "z"] exactly, or [].
I also made a couple behind the scenes tweaks. Namely:
- Combined all the tile info into one file and had each game just define the tile IDs and their quantities
- Moved private company close phase into the "rules" structure.
Validate tile upgrades by phase
Upon board validation, check that the phase we're in allows placing all the tiles on the board. This requires the addition of a mapping between tile upgrade levels and phases.
Fix removing railroads
A typo that I thought I'd previously fixed resurfaced. The check for whether a railroad is removable was flipped; removable railroads raised an exception, and non-removable ones were accepted.
Correct private company corner case
There was a bug I discovered in 1846's Mail Contract which could result in a route being selected worth less than the best route. The situation is very rare in actual game play, but not impossible. And as I was able to adjust the algorithm without greatly increasing the runtime, it was worth fixing. See #32 for more details.
The change which necessitated a minor version increment was creating and returning RouteSet objects. They act like a list (the previous return type) in many ways, but not all.
Unify placing tiles, and accept phase in Game.privates_is_closed
Made PlacedTile.place into a better factory by encapsulating within it the logic for whether or not to return the split version.
Unrelated, allowed the user to pass a phase into Game.private_is_closed. If it's omitted, fall back to the captured phase.