-
Notifications
You must be signed in to change notification settings - Fork 708
Implement local train routing with discovery and greedy path selection #2468
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,13 @@ export class GameImpl implements Game { | |
| private updates: GameUpdates = createGameUpdatesMap(); | ||
| private unitGrid: UnitGrid; | ||
|
|
||
| // Train statistics tracking | ||
| private arrivalsSinceLastPrint = 0; | ||
| private completedStepsSinceLastPrint = 0; | ||
| private activeTrainSteps = 0; // total steps taken by currently active trains (updated each tick) | ||
| private lastStatsPrint = 0; // last time we printed stats | ||
| private hopLimitRemovalsSinceLastPrint = 0; | ||
|
|
||
| private playerTeams: Team[]; | ||
| private botTeam: Team = ColoredTeams.Bot; | ||
| private _railNetwork: RailNetwork = createRailNetwork(this); | ||
|
|
@@ -347,12 +354,21 @@ export class GameImpl implements Game { | |
|
|
||
| executeNextTick(): GameUpdates { | ||
| this.updates = createGameUpdatesMap(); | ||
|
|
||
| // Reset active train steps counter for this tick | ||
| this.activeTrainSteps = 0; | ||
|
|
||
| this.execs.forEach((e) => { | ||
| if ( | ||
| (!this.inSpawnPhase() || e.activeDuringSpawnPhase()) && | ||
| e.isActive() | ||
| ) { | ||
| e.tick(this._ticks); | ||
|
|
||
| // Track steps for active trains | ||
| if (e.constructor.name === "TrainExecution") { | ||
| this.activeTrainSteps += (e as any).journeyHopCount ?? 0; | ||
| } | ||
| } | ||
| }); | ||
|
Comment on lines
355
to
373
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chainexecuteNextTick: fragile reliance on constructor.name and private fields Inside the tick loop: if (e.constructor.name === "TrainExecution") {
this.activeTrainSteps += (e as any).journeyHopCount ?? 0;
}This has a few problems:
A cleaner, more compositional approach:
This keeps type safety and avoids using 🏁 Script executed: #!/bin/bash
# Check all Execution implementations; see if we can introduce a shared interface
rg -nP 'implements Execution' src/core/execution --type=ts -C2Length of output: 15669 |
||
| const inited: Execution[] = []; | ||
|
|
@@ -381,6 +397,13 @@ export class GameImpl implements Game { | |
| hash: this.hash(), | ||
| }); | ||
| } | ||
|
|
||
| // Print train statistics every 60 ticks (~60 seconds) | ||
| if (this._ticks - this.lastStatsPrint >= 60) { | ||
| this.printTrainStats(); | ||
| this.lastStatsPrint = this._ticks; | ||
| } | ||
|
|
||
| this._ticks++; | ||
| return this.updates; | ||
| } | ||
|
|
@@ -440,6 +463,53 @@ export class GameImpl implements Game { | |
| ); | ||
| } | ||
|
|
||
| // Train statistics tracking methods | ||
| recordTrainArrival(steps: number) { | ||
| this.arrivalsSinceLastPrint++; | ||
| this.completedStepsSinceLastPrint += steps; | ||
| } | ||
|
|
||
| recordTrainRemovedDueToHopLimit(_steps: number) { | ||
| this.hopLimitRemovalsSinceLastPrint++; | ||
| } | ||
|
|
||
| getActiveTrainCount(): number { | ||
| return this.executions().filter( | ||
| (exec) => exec.constructor.name === "TrainExecution" && exec.isActive(), | ||
| ).length; | ||
| } | ||
|
|
||
| getAverageCompletedTrainSteps(): number { | ||
| if (this.arrivalsSinceLastPrint === 0) return 0; | ||
| return this.completedStepsSinceLastPrint / this.arrivalsSinceLastPrint; | ||
| } | ||
|
|
||
| getAverageActiveTrainSteps(): number { | ||
| const activeTrains = this.getActiveTrainCount(); | ||
| if (activeTrains === 0) return 0; | ||
|
|
||
| // Return average steps for currently active trains | ||
| return this.activeTrainSteps / activeTrains; | ||
| } | ||
|
|
||
| printTrainStats() { | ||
| const arrivalsLastInterval = this.arrivalsSinceLastPrint; | ||
| const activeTrains = this.getActiveTrainCount(); | ||
| const avgCompletedSteps = | ||
| Math.round(this.getAverageCompletedTrainSteps() * 100) / 100; | ||
| const avgActiveSteps = | ||
| Math.round(this.getAverageActiveTrainSteps() * 100) / 100; | ||
| const hopLimitRemovals = this.hopLimitRemovalsSinceLastPrint; | ||
|
|
||
| console.log( | ||
| `🚂 Trains: ${arrivalsLastInterval} arrived (${avgCompletedSteps} avg steps), ${activeTrains} active (${avgActiveSteps} avg steps), ${hopLimitRemovals} removed (hop limit)`, | ||
| ); | ||
|
|
||
| this.arrivalsSinceLastPrint = 0; | ||
| this.completedStepsSinceLastPrint = 0; | ||
| this.hopLimitRemovalsSinceLastPrint = 0; | ||
| } | ||
|
|
||
| playerView(id: PlayerID): Player { | ||
| return this.player(id); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
activeSourceOrDestination: clarify semantics
activeSourceOrDestinationreturnsthis.source.isActive() && this.destination.isActive(). This means if either endpoint dies mid‑journey, the train is deleted.If that’s intentional, consider renaming to
bothEndpointsActive()or adding a short comment. If instead the intent is “source or destination still exists”, you probably want||instead of&&.🤖 Prompt for AI Agents