Skip to content

Instantly share code, notes, and snippets.

@MrYossu
Created Feb 28, 2022
Embed
What would you like to do?
A second, and slightly more realistic simulation
// Variables that control the simulation...
int timeBetweenBuses = 10;
// Capacity of a bus
int busCapacity = 50;
// The maximum number of passengers that can arrive at a bus stop at any one time
int maxNewPassengers = 10;
// The number of passengers that can board the bus in one time interval.
// If the number of passengers waiting exceeds this, the bus will be at the stop for longer
int boardingRate = 10;
// How far in advance of the first bus (assuming it didn't stop) that passengers arrive at a stop
int passengerAdvance = 10;
// Percentage chance that passengers will arrive at a stop (smaller means less likely)
int chanceOfPassengerArrival = 10;
// Number of passengers that get off at any one stop
int departureRate = 35;
// Number of milliseconds between each frame. Smaller number makes the animation go faster
int delay = 250;
// The simulation
Random r = new();
List<BusStop> busStops = new() { new(10), new(20), new(30), new(40), new(45), new(55), new(70), new(82), new(87), new(95), new(110) };
int maxDistance = busStops.Max(l => l.Distance);
string stopsDisplay = string.Join("", Enumerable.Range(0, maxDistance).Select(n => busStops.Any(bs => bs.Distance == n) ? "|" : "-")) + "|";
List<Bus> buses = new();
int time = 0;
do {
// Is it time for the next bus to start?
if (time % timeBetweenBuses == 0) {
int newNumber = buses.Any() ? buses.Max(b => b.Number) + 1 : 1;
buses.Add(new(newNumber));
}
Util.ClearResults();
PassengersArriveAtStops(time);
// See what each bus should do in this time interval
for (int n = 0; n < buses.Count; n++) {
Bus bus = buses[n];
if (busStops.Any(l => l.Distance == bus.Distance)) {
// We are at a bus stop. Get hold of the stop object
BusStop stop = busStops.Single(l => l.Distance == bus.Distance);
// See how many passengers are getting off
int maxGettingOff = Math.Max(0, bus.Passengers - departureRate);
int gettingOff = maxGettingOff * ((r.Next() % 100) / 100);
bus.Passengers -= gettingOff;
if (bus.Passengers < busCapacity && stop.PassengersWaiting > 0) {
// There are passengers, and we have space for some, so take on the number that can
// board in one time unit, up to the capacity of the bus and don't move on
int spaceOnBus = busCapacity - bus.Passengers;
int numberOfPassengersThatCouldBoard = Math.Min(stop.PassengersWaiting, boardingRate);
int numberThatWillBoard = Math.Min(spaceOnBus, numberOfPassengersThatCouldBoard);
int passengersBoarding = Math.Max(0, numberThatWillBoard);
stop.PassengersWaiting = stop.PassengersWaiting - passengersBoarding;
bus.Passengers += passengersBoarding;
} else {
// There aren't any passengers or we are full, just move on if the next location is empty.
if (!buses.Any(b => b.Number != bus.Number && b.Distance == bus.Distance + 1)) {
bus.Distance++;
}
}
} else {
// We are not at a bus stop. If the next location is empty, move on, otherwise stay here
if (!buses.Any(b => b.Number != bus.Number && b.Distance == bus.Distance + 1)) {
bus.Distance++;
}
}
}
$"Time: {time}".DumpFixed();
ShowPassengers().DumpFixed();
stopsDisplay.DumpFixed();
ShowBuses().DumpFixed();
ShowNumberOfPassengersOnBuses().DumpFixed();
await Task.Delay(delay);
time++;
} while (buses.First().Distance < maxDistance);
void PassengersArriveAtStops(int time) {
foreach (BusStop stop in busStops.Where(s => s.Distance <= time + passengerAdvance)) {
int rn = r.Next() % 100;
if (rn < chanceOfPassengerArrival) {
stop.PassengersWaiting += r.Next() % maxNewPassengers;
}
}
}
string ShowPassengers() {
string s = "";
for (int i = 0; i < busStops.Count; i++) {
// Work out how far we are from the previous stop
int dist = i == 0 ? busStops[i].Distance : busStops[i].Distance - busStops[i - 1].Distance;
s += "".PadLeft(dist - 2, ' ') + busStops[i].PassengersWaiting.ToString("00");
}
return s;
}
string ShowBuses() =>
string.Join("", Enumerable.Range(0, maxDistance + 1).Select(n => buses.Any(b => b.Distance == n) ? Num(buses.First(b => b.Distance == n).Number) : " "));
// Assuming there won't be more than 35 buses on the route at any one time, we can display the buses as a digit or letter (1-9 and A-Z).
// This keeps it to one character, making the display line up correctly.
string Num(int n) =>
n switch {
_ when n < 10 => n.ToString(),
_ => ((char)(n + 55)).ToString()
};
string ShowNumberOfPassengersOnBuses() =>
$"{Environment.NewLine}Number of passengers on each bus:{Environment.NewLine} {"".PadRight(busCapacity, '-')}{Environment.NewLine}" + buses.Select(b => $"{Num(b.Number)}: {"".PadLeft(b.Passengers, '*')}")
.JoinStr(Environment.NewLine);
class Bus {
public Bus(int number) =>
Number = number;
public int Number { get; set; }
public int Distance { get; set; }
public int Passengers { get; set; }
}
class BusStop {
public BusStop(int distance) =>
Distance = distance;
public int Distance { get; set; }
public int PassengersWaiting { get; set; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment