Out of control trains!

This commit is contained in:
Tyler Hallada 2018-04-06 01:07:16 -04:00
parent edae3f76f4
commit 4b7bf5353c
4 changed files with 96 additions and 28 deletions

View File

@ -1,37 +1,20 @@
import Station from './Station'; import Station from './Station';
import { distance, randomInt, randomPoint } from './utils'; 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 { export default class Line {
public stations: Station[]; public stations: Station[];
constructor(stations: Station[], numStations: number) { constructor(stations: Station[], numStations: number) {
this.stations = []; this.stations = [];
let stationsLeft = stations; let stationsLeft = stations;
let largest = largestStation(stationsLeft); let largest = Station.largestStation(stationsLeft);
stationsLeft = stationsLeft.filter(s => s !== largest); stationsLeft = stationsLeft.filter(s => s !== largest);
this.stations.push(largest); this.stations.push(largest);
while (this.stations.length < numStations) { while (this.stations.length < numStations) {
largest = largestStation(stationsWithinRadius(stationsLeft, largest.location, 500)); largest = Station.largestStation(
Station.stationsWithinRadius(stationsLeft, largest.location, 500),
);
if (largest === null) { if (largest === null) {
break; break;
} }

View File

@ -1,4 +1,28 @@
import { distance } from './utils';
export default class Station { 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 location: PIXI.Point;
public population: number; public population: number;
public connections: Station[]; public connections: Station[];

View File

@ -2,10 +2,13 @@ import * as PIXI from 'pixi.js';
import Line from './Line'; import Line from './Line';
import Station from './Station'; import Station from './Station';
import Train from './Train'; import Train from './Train';
import { distance, randomInt, randomPoint } from './utils'; import { distance, pointsEqual, randomInt, randomPoint, weightedRandom } from './utils';
import './style.css'; import './style.css';
const maxSpeed = 10.0;
const acceleration = 0.25;
const isPointDistant = (point: PIXI.Point, stations: Station[], minDistance: number): boolean => { const isPointDistant = (point: PIXI.Point, stations: Station[], minDistance: number): boolean => {
for (const station of stations) { for (const station of stations) {
if (distance(point, station.location) < minDistance) { if (distance(point, station.location) < minDistance) {
@ -45,12 +48,53 @@ const initTrains = (numTrains: number, stations: Station[]): Train[] => {
const trains = []; const trains = [];
for (let i = 0; i < numTrains; i += 1) { for (let i = 0; i < numTrains; i += 1) {
const originStation = stations[Math.floor(Math.random() * stations.length)]; const originStation = stations[Math.floor(Math.random() * stations.length)];
const destStation = 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)); trains.push(new Train(originStation.location, 0, 0, originStation, undefined));
} }
return trains; 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 drawLine = (line: Line, graphics: PIXI.Graphics) => {
const start = line.stations[0].location; const start = line.stations[0].location;
graphics.moveTo(start.x, start.y); graphics.moveTo(start.x, start.y);
@ -76,21 +120,22 @@ const run = () => {
// make these const // make these const
let stations = initStations(30); let stations = initStations(30);
let trains = initTrains(15, stations); let trains = initTrains(15, stations);
let line = new Line(stations, 10); // let line = new Line(stations, 10);
stations = initStations(30); stations = initStations(30);
trains = initTrains(15, stations); trains = initTrains(15, stations);
line = new Line(stations, 10);
ticker.stop(); ticker.stop();
ticker.add((deltaTime) => { ticker.add((deltaTime) => {
moveTrains(trains, stations);
graphics.clear(); graphics.clear();
fpsText.text = `${Math.round(ticker.FPS)}`; fpsText.text = `${Math.round(ticker.FPS)}`;
graphics.lineStyle(1, 0xaeaeae, 1); graphics.lineStyle(1, 0xaeaeae, 1);
drawStations(stations, graphics); drawStations(stations, graphics);
drawLine(line, graphics); drawTrains(trains, graphics);
// drawLine(line, graphics);
}); });
ticker.start(); ticker.start();

View File

@ -5,10 +5,26 @@ export const randomInt = (min: number, max: number): number => (
Math.floor(Math.random() * (max - (min + 1))) + min 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 = () => ( export const randomPoint = () => (
new PIXI.Point(randomInt(0, window.innerWidth), randomInt(0, window.innerHeight)) 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 => { export const distance = (pointA: PIXI.Point, pointB: PIXI.Point): number => {
const distX = pointA.x - pointB.x; const distX = pointA.x - pointB.x;
const distY = pointA.y - pointB.y; const distY = pointA.y - pointB.y;