Skip to content

Instantly share code, notes, and snippets.

@zhonger
Created July 4, 2024 08:16
Show Gist options
  • Save zhonger/3546f08c0fe5b3e4ea360288a6b15d42 to your computer and use it in GitHub Desktop.
Save zhonger/3546f08c0fe5b3e4ea360288a6b15d42 to your computer and use it in GitHub Desktop.
#################################################################
#
# 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