Advertisement
Faschz

DKR - Optimal Tapping Sim

Nov 2nd, 2024
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.74 KB | None | 0 0
  1. from copy import deepcopy
  2.  
  3. import pyqtgraph as pg
  4.  
  5. INITIAL_VELOCITY = 0.0
  6. INITIAL_BOOST = 0
  7. NUM_UPDATES = 120
  8. VI_PER_VISUAL_FRAME = 2
  9.  
  10. class Kart:
  11.     def __init__(self):
  12.         self.velocity = INITIAL_VELOCITY
  13.         self.throttle = 0.0
  14.         self.distance = 0.0
  15.         self.boost = INITIAL_BOOST
  16.         self.inputs = []
  17.  
  18.     def update(self, is_holding_a: bool) -> None:
  19.         """Update the kart's properties for a single frame"""
  20.  
  21.         self.inputs.append(is_holding_a)
  22.  
  23.         self.update_throttle(is_holding_a)
  24.         self.update_boost()
  25.         self.update_velocity(is_holding_a)
  26.         self.distance += self.velocity
  27.  
  28.     def update_boost(self) -> None:
  29.         """Update the kart's boost"""
  30.  
  31.         self.boost -= VI_PER_VISUAL_FRAME
  32.  
  33.         if self.boost < 0:
  34.             self.boost = 0
  35.  
  36.     def update_throttle(self, is_holding_a: bool) -> None:
  37.         """Update the kart's throttle"""
  38.  
  39.         if self.throttle > 0.0:
  40.             self.throttle -= 0.1
  41.  
  42.         if is_holding_a or self.boost > 0:
  43.             self.throttle = 1.0
  44.  
  45.     def update_velocity(self, is_holding_a: bool) -> None:
  46.         """Update the kart's velocity"""
  47.  
  48.         self.velocity -= self.calculate_drag_from_traction(is_holding_a)
  49.         self.velocity += self.calculate_thrust(self.calculate_interpolated_stats())
  50.  
  51.         if self.velocity < 0.04:
  52.             self.velocity = 0.0
  53.  
  54.     def calculate_drag_from_traction(self, is_holding_a: bool) -> float:
  55.         """Return the drag calculated from traction"""
  56.  
  57.         if is_holding_a:
  58.             return 1.0 * 0.004 * self.velocity * self.velocity
  59.  
  60.         return 8.0 * 0.004 * self.velocity
  61.  
  62.     def calculate_thrust(self, acceleration: float) -> float:
  63.         """Return the thrust calculated from throttle"""
  64.  
  65.         if self.boost > 0:
  66.             return 2.0
  67.  
  68.         return 1.7 * self.throttle * acceleration
  69.  
  70.     def calculate_interpolated_stats(self) -> float:
  71.         """Calculate the acceleration based on the character's stats"""
  72.  
  73.         stats = [0.10, 0.20, 0.22, 0.25, 0.26, 0.27, 0.28, 0.29, 0.30, 0.32, 0.35, 0.38, 0.43, 0.43]
  74.  
  75.         idx = int(self.velocity)
  76.         remainder = self.velocity - idx
  77.  
  78.         if idx > 12:
  79.             idx = 12
  80.  
  81.         lower_acceleration = stats[idx]
  82.         upper_acceleration = stats[idx + 1]
  83.  
  84.         return lower_acceleration * (1.0 - remainder) + upper_acceleration * remainder
  85.  
  86. def get_best_karts(karts: list[Kart]) -> list[Kart]:
  87.     """Filter the karts down based on velocity and distance"""
  88.  
  89.     best_karts = []
  90.  
  91.     for throttle in set(kart.throttle for kart in karts):
  92.         karts_at_throttle = [kart for kart in karts if kart.throttle == throttle]
  93.  
  94.         # If any other kart has both higher velocity AND a higher travelled distance at a given throttle, than the kart
  95.         # is a poor performer and should not be included in the list.
  96.         best_karts += [kart for kart in karts_at_throttle
  97.                        if not any(k.velocity > kart.velocity and k.distance > kart.distance for k in karts_at_throttle)]
  98.  
  99.     return best_karts
  100.  
  101. def get_optimal_kart(frames: int) -> Kart:
  102.     """Get the kart that would travel the furthest distance after the supplied number of visual frames"""
  103.  
  104.     karts = [Kart()]
  105.  
  106.     for _ in range(frames):
  107.         copies_of_karts = [deepcopy(kart) for kart in karts]
  108.  
  109.         for kart in karts:
  110.             kart.update(False)
  111.  
  112.         for kart in copies_of_karts:
  113.             kart.update(True)
  114.  
  115.         karts += copies_of_karts
  116.  
  117.         karts = get_best_karts(karts)
  118.  
  119.     return max(karts, key=lambda k: k.distance)
  120.  
  121. def simulate_holding_a_kart(frames: int) -> tuple[list[float], list[float]]:
  122.     """Return the simulated kart's distances and velocities of a player just holding A"""
  123.  
  124.     kart = Kart()
  125.  
  126.     distances = []
  127.     velocities = []
  128.  
  129.     for _ in range(frames):
  130.         distances.append(kart.distance)
  131.         velocities.append(kart.velocity)
  132.  
  133.         kart.update(True)
  134.  
  135.     return distances, velocities
  136.  
  137. def simulate_best_kart(frames: int) -> tuple[list[float], list[float], list[bool]]:
  138.     """Return the simulated kart's distances and velocities of a player performing optimally"""
  139.  
  140.     optimal_kart = get_optimal_kart(frames)
  141.     #print(optimal_kart.distance)
  142.     #print(*[f"{i}: {input}\n" for i, input in enumerate(optimal_kart.inputs, start=1)], end="")
  143.  
  144.     kart = Kart()
  145.  
  146.     distances = []
  147.     velocities = []
  148.     inputs = [1.0 if input_ else 0.0 for input_ in optimal_kart.inputs]
  149.  
  150.     for i in range(frames):
  151.         distances.append(kart.distance)
  152.         velocities.append(kart.velocity)
  153.  
  154.         kart.update(optimal_kart.inputs[i])
  155.  
  156.     return distances, velocities, inputs
  157.  
  158. def main():
  159.     velocity_widget = pg.plot(title="DKR Velocity vs Time")
  160.     velocity_widget.addLegend()
  161.     velocity_widget.setLabel("left", "Velocity")
  162.     velocity_widget.setLabel("bottom", "Visual Frames")
  163.  
  164.     distance_widget = pg.plot(title="DKR Distance vs Time")
  165.     distance_widget.addLegend()
  166.     distance_widget.setLabel("left", "Distance")
  167.     distance_widget.setLabel("bottom", "Visual Frames")
  168.  
  169.     inputs_widget = pg.plot(title="DKR Inputs vs Time")
  170.     inputs_widget.addLegend()
  171.     inputs_widget.setLabel("left", "Inputs")
  172.     inputs_widget.setLabel("bottom", "Visual Frames")
  173.  
  174.     frames = list(range(NUM_UPDATES))
  175.  
  176.     distances, velocities = simulate_holding_a_kart(NUM_UPDATES)
  177.     velocity_widget.plot(frames, velocities)
  178.     distance_widget.plot(frames, distances)
  179.  
  180.     distances, velocities, inputs = simulate_best_kart(NUM_UPDATES)
  181.     velocity_widget.plot(frames, velocities)
  182.     distance_widget.plot(frames, distances)
  183.     inputs_widget.plot(frames, inputs)
  184.  
  185.     pg.exec()
  186.  
  187. if __name__ == "__main__":
  188.     main()
  189.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement