Board each trip where the scan boarded it - #23
Merged
Merged
Conversation
A journey could change trains at a station where another trip happened to arrive first, with less than the station's interchange time, when the passenger had really boarded that trip at an earlier call. getLegs split legs wherever the last connection into a station was on a different trip, which does not say where a trip was boarded. The scan now records the connection each trip was first reachable from, and the connection index holds that connection for each station, so each entry is one leg. Claude-Session: https://claude.ai/code/session_018mxfah5W4un64ZieFiEx8S
Boarding each trip at the first call it was reachable from could take a passenger past a later call of the trip only to ride back through it, such as riding into Edinburgh through Haymarket to board a train back out through Haymarket. A trip now changes where it is boarded when a call is reached in fewer legs, or in as many legs at a later call. Claude-Session: https://claude.ai/code/session_018mxfah5W4un64ZieFiEx8S
A connection overwriting a station at the same arrival time, through staysAboard, could replace a path with one of more legs, and the footpaths from it were not walked again, so stations reached from it kept a count from the path that was replaced. An equal arrival now wins on fewer legs, and on staying aboard only when the legs are equal, and a station reached in fewer legs has its footpaths walked again. Legs are only known for the earliest arrival at each call, so a trip may pass a later call of the next one in as few legs without the scan seeing it. Where consecutive timetable legs share a call the second picks up at in time to change, the journey changes at the first such call. Claude-Session: https://claude.ai/code/session_018mxfah5W4un64ZieFiEx8S
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The scan finds the right earliest arrival, but
JourneyFactory.getLegscould describe a differentjourney, including a change with less than the station's interchange time.
getLegswalked back through the last connection into each station and started a new leg whereverconsecutive connections were on different trips. If another trip reached one of a trip's
intermediate stations slightly earlier, the walk recorded a change there, even though the scan had
kept the passenger aboard from an earlier call.
For example, LTK → ORN on 2026-09-15 departing 06:00 over the GB rail feed returned
… LVJ 11:39>MRF 11:40 | MRF 11:43>ORN 12:00, a 3 minute change at Moorfields, whereinterchange is 10 minutes. The scan actually boarded G30586 at Liverpool Lime Street (LPY) at 11:25.
Fix
Boarding pointers
ScanResultsrecords the connection each trip is boarded from, in a per-trip array held by thefactory next to
tripArrivals. It needs no clearing between scans: it is only read for trips thathave carried the passenger in the current scan.
setConnectionstores that boarding connection in the connection index rather than the connectioninto the station, so each entry is one leg.
getLegsfollows one entry per leg; the leg splitting is gone.getCompactedLegsis unchanged.Where a trip is boarded
then at the latest call. Always boarding at the first reachable call could take the passenger past a
later call of the trip only to ride back through it (e.g. CBS → EDB through Haymarket, then a train
from EDB back out through Haymarket).
the comparison reads nothing from the large connection arrays.
Keeping the leg counts right
the legs are equal. Before,
staysAboardalone could replace a 4-leg path with a 6-leg one (FRL → PFLat Stratford).
setConnectionreports it, andisTransferBetteraccepts an equal time in fewer legs). Otherwise stations reached from it keptcounts from the path that was replaced.
Changing where consecutive trips share a call
next one in as few legs without the scan seeing it (LOB → DGY rode C17869 through Runcorn to
Liverpool South Parkway, then G38358 back through Runcorn).
getCompactedLegs,JourneyFactorylooks at each pair of consecutive timetable legs for acall the first sets down at and the second picks up at after boarding, with time to change. It cuts
the first leg at the earliest such call and boards the second there. The leg count and arrival are
unchanged.
Fixing the original bug on the way out instead (boarding each trip at a reachable call while building
legs) was also prototyped. It gave identical journeys but needed
scanto return arrival times aswell, changed
getJourneys's signature, and duplicated the scan's reachability rule inJourneyFactory. Tracking a second fewest-legs label per station in the scan was not pursued.Verification
Over 1,100 seeded random station pairs and departure times on the GB rail feed (921 journeys found):
Reviewer examples on 2026-09-15:
same 20:33 arrival.
Speed, 6 alternating runs in separate processes:
current boarding connection directly instead cost about 7%.
Tests, each failing before its fix:
ScanResultstest that a station is indexed by theconnection its trip was boarded from.
extra leg.
ScanResultstests for a connection and a transfer arriving at the same time in fewer legs.Notes:
setConnectionmust now followisReachablefor the same connection, as the scan does, so specsuse a
take()helper that does both.ScanResultsdirectly, or reading the connectionindex as the connection into each station, is affected.
https://claude.ai/code/session_018mxfah5W4un64ZieFiEx8S