Created
July 4, 2024 08:16
-
-
Save zhonger/3546f08c0fe5b3e4ea360288a6b15d42 to your computer and use it in GitHub Desktop.
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
################################################################# | |
# | |
# Bus-ticket Solution by Python | |
# Author: @zhonger (https://github.com/zhonger) | |
# | |
# This snippet is designed for solving the bus-ticket problem. | |
# | |
# For more details, please refer to | |
# https://lisz.me/tech/algorithm/bus-ticket.html | |
# | |
# 2024-07-04, first version | |
# | |
################################################################# | |
import math | |
from tabulate import tabulate | |
class Bus: | |
def __init__(self, **kws) -> None: | |
self.distances = kws.get( | |
"distances", | |
[ | |
0.2, | |
0.3, | |
0.35, | |
0.45, | |
0.4, | |
0.3, | |
0.35, | |
0.3, | |
0.3, | |
0.35, | |
0.35, | |
0.4, | |
0.3, | |
0.4, | |
0.35, | |
0.3, | |
0.45, | |
0.45, | |
], | |
) | |
self.lineStops = kws.get("lineStops", [1, 2, 3, 18, 19, 20]) | |
self.circleStops = kws.get("circleStop", range(4, 18)) | |
self.ticketBase = kws.get("ticketBase", 190.00) | |
self.ticketStep = kws.get("ticketStep", 50.00) | |
self.ticketUnit = kws.get("ticketUnit", "JPY") | |
self.baseDistance = kws.get("baseDistance", 2.00) | |
self.stepDistance = kws.get("stepDistance", 1.00) | |
self.distanceUnit = kws.get("distanceUnit", "km") | |
self.totalStops = int(len(self.lineStops) + len(self.circleStops) + 1) | |
self.validDistances = [None] * self.totalStops | |
self.prices = [None] * self.totalStops | |
self.currentStop = 0 | |
self.validStops = [0] | |
self.style = kws.get("style", "fancy_grid") | |
self.driving() | |
def driving(self): | |
print( | |
"\n\n**************************************************\n" | |
"* Welcome to use the Bus-ticket !!! *\n" | |
"* Shall we start to drive ? *\n" | |
"**************************************************\n\n" | |
) | |
input("") | |
for i in range(1, self.totalStops + 1): | |
currentStop = ( | |
f"Stop {self.currentStop}" if self.currentStop != 0 else "Terminal" | |
) | |
nextStop = ( | |
f"Stop {self.currentStop + 1}" | |
if self.currentStop < self.totalStops - 1 | |
else "Terminal" | |
) | |
print( | |
f" Now is driving: {currentStop:<9} >>>>>>> {nextStop:<26}" | |
f"Unit: {self.ticketUnit}" | |
) | |
self.calPrices() | |
self.printPrices() | |
input("") | |
if i != self.totalStops: | |
endFlag = False | |
self.currentStop = i | |
self.validStops.append(i) | |
else: | |
endFlag = True | |
self.currentStop = 0 | |
if self.currentStop + 1 == self.totalStops: | |
nextStop = "Terminal" | |
elif not endFlag: | |
nextStop = f"Stop {self.currentStop + 1}" | |
else: | |
nextStop = "None" | |
currentStop = ( | |
f"Stop {self.currentStop}" if self.currentStop != 0 else "Terminal" | |
) | |
print( | |
f" Now stop at: {currentStop:<17}" | |
f"Next: {nextStop:<24}" | |
f"Unit: {self.ticketUnit}" | |
) | |
self.calPrices() | |
self.printPrices() | |
if i != self.totalStops: | |
input("") | |
print( | |
"\n\n**************************************************\n" | |
"* We have arrived at the terminal. *\n" | |
"* Thank you for using the Bus-ticket! *\n" | |
"**************************************************\n\n" | |
) | |
def printPrices(self): | |
table_data = [] | |
n = 7 | |
for i in range(math.ceil(self.totalStops / n)): | |
if i < math.ceil(self.totalStops / n): | |
table_data.append([f"Stop {j}" for j in range(i * n, (i + 1) * n)]) | |
table_data.append(self.prices[(i * n) : ((i + 1) * n)]) | |
else: | |
table_data.append([f"Stop {j}" for j in range(i * n, self.totalStops)]) | |
table_data.append()(self.prices[(i * n) : self.totalStops]) | |
print(tabulate(table_data, tablefmt=self.style)) | |
def calPrice(self, distance): | |
if distance <= self.baseDistance: | |
return self.ticketBase | |
times = math.ceil((distance - self.baseDistance) / self.stepDistance) | |
return self.ticketBase + self.ticketStep * times | |
def calPrices(self): | |
distances = [None] * self.totalStops | |
prices = [None] * self.totalStops | |
# Only calculate prices for the stations already passed | |
for i in self.validStops: | |
distance = self.calDistance(i) | |
distances[i] = distance | |
prices[i] = self.calPrice(distance) | |
self.prices = prices | |
self.validDistances = distances | |
def calDistance(self, i): | |
# When the boarding and alighting stations are the same | |
if i == self.currentStop: | |
return 0 | |
# When they are different | |
positiveDistance = self.getDistance(i, self.currentStop) | |
negativeDistance = self.getDistance(self.currentStop, i) | |
return min(positiveDistance, negativeDistance) | |
def getDistance(self, a, b): | |
distance = 0 | |
lineStops = set(self.lineStops) | |
lineStops.add(0) | |
# When both are in the line stations | |
if {a, b} < lineStops: | |
a, b = self.getIndex(a), self.getIndex(b) | |
for i in range(min(a, b), max(a, b)): | |
distance += self.distances[i] | |
return distance | |
# When both are in the circle stations | |
if {a, b} < set(self.circleStops): | |
if a < b: | |
for i in range(a, b): | |
distance += self.distances[i] | |
return distance | |
for i in range(a, self.circleStops[-1]): | |
distance += self.distances[i] | |
for i in range(self.circleStops[0], b): | |
distance += self.distances[i] | |
return distance | |
# When a is in the line stations, and b is different | |
if a in lineStops: | |
a = self.getIndex(a) | |
for i in range(a, b): | |
distance += self.distances[i] | |
return distance | |
# When b is in the line stations, and a is different | |
for i in range(a, self.circleStops[-1] + 1): | |
distance += self.distances[i] | |
b = self.getIndex(b) | |
for i in range(b, self.lineStops[int(len(self.lineStops) / 2) - 1]): | |
distance += self.distances[i] | |
return distance | |
def getIndex(self, i): | |
"""Standard the index into small one for line stations.""" | |
if i <= (len(self.lineStops) / 2): | |
return i | |
return self.lineStops[int(len(self.lineStops) - self.lineStops.index(i) - 1)] | |
if __name__ == "__main__": | |
Bus() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment