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 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;
|
||||||
}
|
}
|
||||||
|
@ -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[];
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
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
|
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user