Skip to content

Instantly share code, notes, and snippets.

@MrYossu
Last active January 31, 2022 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MrYossu/ecfed6773c88a65c955d8e6a1bdc3430 to your computer and use it in GitHub Desktop.
Save MrYossu/ecfed6773c88a65c955d8e6a1bdc3430 to your computer and use it in GitHub Desktop.
First pass at simulating buses
Random r = new();
int delay = 250;
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 timeBetweenBuses = 10;
// The maximum number of passengers that can arrive at a bus stop at any one time
int maxNewPassengers = 5;
// 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 = 5;
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));
}
// Have passengers arrive at three randomly chosen stops
for (int i = 0; i < r.Next() % 3; i++) {
// get the index of the bus stop, so we can add the passengers
int n = r.Next() % busStops.Count;
busStops[n] = busStops[n] with { PassengersWaiting = busStops[n].PassengersWaiting + r.Next() % maxNewPassengers };
}
Util.ClearResults();
// 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 the index of the stop, so we can update in place (if we take on passengers)
int stopIndex = busStops.Select((bs, n) => (bs, n)).Where(t => t.bs.Distance == bus.Distance).Select(t => t.n).First();
if (busStops[stopIndex].PassengersWaiting > 0) {
// There are passengers, so take on the number that can board in one time unit and don't move on
busStops[stopIndex] = busStops[stopIndex]
with { PassengersWaiting = Math.Max(0, busStops[stopIndex].PassengersWaiting - boardingRate) };
} else {
// There aren't any passengers, just move on if the next location is empty.
if (!buses.Any(b => b.Number != bus.Number && b.Distance == bus.Distance + 1)) {
buses[n] = bus with { Distance = bus.Distance + 1 };
}
}
} 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)) {
buses[n] = bus with { Distance = bus.Distance + 1 };
}
}
}
$"Time: {time}".DumpFixed();
ShowPassengers().DumpFixed();
stopsDisplay.DumpFixed();
ShowBuses().DumpFixed();
await Task.Delay(delay);
time++;
} while (buses.First().Distance < maxDistance);
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 thna 26 buses on the route at any one time, we can display the buses as a digit or letter.
// 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()
};
record Bus(int Number, int Distance = 0);
record BusStop(int Distance, int PassengersWaiting = 0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment