Out of control trains!
This commit is contained in:
parent
edae3f76f4
commit
4b7bf5353c
27
src/Line.ts
27
src/Line.ts
@ -1,37 +1,20 @@
|
||||
import Station from './Station';
|
||||
import { distance, randomInt, randomPoint } from './utils';
|
||||
|
||||
const largestStation = (stations: Station[]): Station => {
|
||||
let largest: Station = null;
|
||||
for (const station of stations) {
|
||||
if (largest === null || station.population > largest.population) {
|
||||
largest = station;
|
||||
}
|
||||
}
|
||||
return largest;
|
||||
};
|
||||
|
||||
const stationsWithinRadius = (stations: Station[], point: PIXI.Point,
|
||||
radius: number): Station[] => (
|
||||
stations.filter(station => distance(point, station.location) <= radius)
|
||||
);
|
||||
|
||||
const closestStations = (stations: Station[], point: PIXI.Point, num: number): Station[] => {
|
||||
// bleh, i'm done
|
||||
return stations;
|
||||
};
|
||||
|
||||
export default class Line {
|
||||
public stations: Station[];
|
||||
|
||||
constructor(stations: Station[], numStations: number) {
|
||||
this.stations = [];
|
||||
let stationsLeft = stations;
|
||||
let largest = largestStation(stationsLeft);
|
||||
let largest = Station.largestStation(stationsLeft);
|
||||
stationsLeft = stationsLeft.filter(s => s !== largest);
|
||||
this.stations.push(largest);
|
||||
|
||||
while (this.stations.length < numStations) {
|
||||
largest = largestStation(stationsWithinRadius(stationsLeft, largest.location, 500));
|
||||
largest = Station.largestStation(
|
||||
Station.stationsWithinRadius(stationsLeft, largest.location, 500),
|
||||
);
|
||||
if (largest === null) {
|
||||
break;
|
||||
}
|
||||
|
@ -1,4 +1,28 @@
|
||||
import { distance } from './utils';
|
||||
|
||||
export default class Station {
|
||||
// Utility methods for working with arrays of Stations
|
||||
public static largestStation(stations: Station[]): Station {
|
||||
let largest: Station = null;
|
||||
for (const station of stations) {
|
||||
if (largest === null || station.population > largest.population) {
|
||||
largest = station;
|
||||
}
|
||||
}
|
||||
return largest;
|
||||
}
|
||||
|
||||
public static stationsWithinRadius(stations: Station[], point: PIXI.Point,
|
||||
radius: number): Station[] {
|
||||
return stations.filter(station => distance(point, station.location) <= radius);
|
||||
}
|
||||
|
||||
public static closestStation(stations: Station[], point: PIXI.Point, num: number): Station {
|
||||
return stations.reduce(
|
||||
(prev, curr) => distance(point, prev.location) > distance(point, curr.location) ? prev : curr,
|
||||
);
|
||||
}
|
||||
|
||||
public location: PIXI.Point;
|
||||
public population: number;
|
||||
public connections: Station[];
|
||||
|
@ -2,10 +2,13 @@ import * as PIXI from 'pixi.js';
|
||||
import Line from './Line';
|
||||
import Station from './Station';
|
||||
import Train from './Train';
|
||||
import { distance, randomInt, randomPoint } from './utils';
|
||||
import { distance, pointsEqual, randomInt, randomPoint, weightedRandom } from './utils';
|
||||
|
||||
import './style.css';
|
||||
|
||||
const maxSpeed = 10.0;
|
||||
const acceleration = 0.25;
|
||||
|
||||
const isPointDistant = (point: PIXI.Point, stations: Station[], minDistance: number): boolean => {
|
||||
for (const station of stations) {
|
||||
if (distance(point, station.location) < minDistance) {
|
||||
@ -45,12 +48,53 @@ const initTrains = (numTrains: number, stations: Station[]): Train[] => {
|
||||
const trains = [];
|
||||
for (let i = 0; i < numTrains; i += 1) {
|
||||
const originStation = stations[Math.floor(Math.random() * stations.length)];
|
||||
const destStation = stations[Math.floor(Math.random() * stations.length)];
|
||||
trains.push(new Train(originStation.location, 0, 0, originStation, destStation));
|
||||
// const destStation = stations[Math.floor(Math.random() * stations.length)];
|
||||
trains.push(new Train(originStation.location, 0, 0, originStation, undefined));
|
||||
}
|
||||
return trains;
|
||||
};
|
||||
|
||||
const moveTrains = (trains: Train[], stations: Station[]) => {
|
||||
for (const train of trains) {
|
||||
// choose a destination randomly with a bias towards larger stations
|
||||
if (train.destination === undefined) {
|
||||
const closeStations = Station.stationsWithinRadius(stations, train.location, 500);
|
||||
const closeStationWeights = closeStations.map(station => station.population);
|
||||
train.destination = weightedRandom(closeStations, closeStationWeights);
|
||||
}
|
||||
|
||||
// train reached destination, stop moving.
|
||||
if (pointsEqual(train.location, train.destination.location)) {
|
||||
train.speed = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
const journeyLeft = distance(train.location, train.destination.location);
|
||||
// speeding up
|
||||
if (train.speed < maxSpeed) {
|
||||
train.speed += acceleration;
|
||||
}
|
||||
|
||||
// slowing down
|
||||
if ((train.speed / acceleration) >= (journeyLeft / train.speed)) {
|
||||
train.speed -= acceleration;
|
||||
}
|
||||
|
||||
// advance train
|
||||
const progress = train.speed / journeyLeft;
|
||||
train.location = new PIXI.Point(
|
||||
train.location.x + (Math.abs(train.location.x - train.destination.location.x) * progress),
|
||||
train.location.y + (Math.abs(train.location.y - train.destination.location.y) * progress),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const drawTrains = (trains: Train[], graphics: PIXI.Graphics) => {
|
||||
for (const train of trains) {
|
||||
graphics.drawCircle(train.location.x, train.location.y, 2);
|
||||
}
|
||||
};
|
||||
|
||||
const drawLine = (line: Line, graphics: PIXI.Graphics) => {
|
||||
const start = line.stations[0].location;
|
||||
graphics.moveTo(start.x, start.y);
|
||||
@ -76,21 +120,22 @@ const run = () => {
|
||||
// make these const
|
||||
let stations = initStations(30);
|
||||
let trains = initTrains(15, stations);
|
||||
let line = new Line(stations, 10);
|
||||
// let line = new Line(stations, 10);
|
||||
|
||||
stations = initStations(30);
|
||||
trains = initTrains(15, stations);
|
||||
line = new Line(stations, 10);
|
||||
|
||||
ticker.stop();
|
||||
ticker.add((deltaTime) => {
|
||||
moveTrains(trains, stations);
|
||||
|
||||
graphics.clear();
|
||||
fpsText.text = `${Math.round(ticker.FPS)}`;
|
||||
graphics.lineStyle(1, 0xaeaeae, 1);
|
||||
|
||||
drawStations(stations, graphics);
|
||||
drawLine(line, graphics);
|
||||
drawTrains(trains, graphics);
|
||||
// drawLine(line, graphics);
|
||||
});
|
||||
ticker.start();
|
||||
|
||||
|
16
src/utils.ts
16
src/utils.ts
@ -5,10 +5,26 @@ export const randomInt = (min: number, max: number): number => (
|
||||
Math.floor(Math.random() * (max - (min + 1))) + min
|
||||
);
|
||||
|
||||
export const weightedRandom = (choices: any[], weights: number[]): any => {
|
||||
const totalWeight = weights.reduce((a, b) => a + b, 0);
|
||||
const rand = randomInt(0, totalWeight);
|
||||
let cumulWeight = 0;
|
||||
for (let i = 0; i < weights.length; i += 1) {
|
||||
cumulWeight += weights[i];
|
||||
if (rand < cumulWeight) {
|
||||
return choices[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const randomPoint = () => (
|
||||
new PIXI.Point(randomInt(0, window.innerWidth), randomInt(0, window.innerHeight))
|
||||
);
|
||||
|
||||
export const pointsEqual = (pointA: PIXI.Point, pointB: PIXI.Point): boolean => (
|
||||
(pointA.x === pointB.x && pointA.y === pointB.y)
|
||||
);
|
||||
|
||||
export const distance = (pointA: PIXI.Point, pointB: PIXI.Point): number => {
|
||||
const distX = pointA.x - pointB.x;
|
||||
const distY = pointA.y - pointB.y;
|
||||
|
Loading…
Reference in New Issue
Block a user