Created
February 28, 2022 15:41
-
-
Save MrYossu/05f2e0a7aa43ebcd588bf41f7091c8d4 to your computer and use it in GitHub Desktop.
A second, and slightly more realistic simulation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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