Lycanphoenix

MechEngine Beta (0.30.67)

May 21st, 2018
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 34.79 KB | None | 0 0
  1. print("MechEngine Beta, 0.30.67");
  2. print("Build Date: 2018-05-21. Designed using Python 3.6.4 Stackless Shell");
  3. import math, os, sys, time, datetime, threading
  4. now = datetime.datetime.now()
  5. while now != datetime.datetime.now():
  6.      now = datetime.datetime.now()
  7. print("Current Date:", now.strftime("%A, %Y-%m-%d, %H:%M:%S (local)"))
  8. print();
  9.  
  10. #Development Goals:
  11.      #Add options to restart program and/or export log upon completion.
  12.      #Add engine descriptions to help the user make an informed decision.
  13.     #Add example mechs to choose from
  14.  
  15. def fib(n):    # write Fibonacci series up to n
  16.      """Print a Fibonacci series up to n."""
  17.      a, b = 0, 1
  18.      while a != n:
  19.          print(a, end=" ");
  20.          a, b = b, a+b
  21.      print();
  22.  
  23. def RoundUp5(x):
  24.   return math.ceil(x / 5.0) * 5 #Round up to nearest multiple of 5
  25.  
  26. def RoundUpHalf(x):
  27.   return math.ceil(x*2.0)/2.0
  28.  
  29. def RoundUpQuarter(x):
  30.   return math.ceil(x*4.0)/4.0
  31.  
  32. def RoundUpFractional(x):
  33.   return math.ceil(x*1000.0)/1000.0
  34.  
  35. def RoundDown5(x):
  36.   return math.floor(x / 5.0) * 5
  37.  
  38. def RoundDownHalf(x):
  39.   return math.floor(x*2.0)/2.0
  40.  
  41. def RoundDownQuarter(x):
  42.   return math.floor(x*4.0)/4.0
  43.  
  44. def RoundDownFractional(x):
  45.   return math.floor(x*1000.0)/1000.0
  46.  
  47. def roundNearest(f):
  48.     return round(f + .00000000000001);
  49.  
  50. #Deprecated
  51. def roundHalfUp(f): #deprecated!
  52.     return RoundUpHalf(f) #round(f + 0.00000000000001)
  53.  
  54. def roundEngine(x, base=5): #deprecated!
  55.     return int(base * RoundUpHalf(float(x)/base));
  56.  
  57. def roundTonnage(x, base=5): #deprecated!
  58.     return int(base * RoundUpHalf(float(x)/base));
  59. #End Deprecation
  60.  
  61. #HYN-DRN-5813 Core Input Dictionary:
  62. def clamp(min_value, max_value, x):
  63.   """Returns x, limited to the range min_val to max_val."""
  64.   return max(min(x, max_value), min_value)
  65.  
  66. def RepresentsInt(s):
  67.   try:
  68.     int(s)
  69.     return True
  70.   except ValueError:
  71.     return False
  72.  
  73. def ask(question, min_val, max_val):
  74.   x = ''
  75.   while not RepresentsInt(x) or int(x) < min_val or int(x) > max_val:
  76.     x = input(question + " (min %d, max %d)"%(min_val, max_val) + ": ")
  77.   return clamp(min_val, max_val, int(x))
  78.  
  79. MechType = ("Biped")
  80. #def PromptMechType()
  81. #NewMechType = PromptMechType()
  82. #MechType = NewMechType
  83. CurrentMechType = MechType
  84.  
  85. Inputs = {"BattleMech’s Tonnage": {"min":2, "max":200, "value": None},
  86.           "desired Walk MP" : {"min":1, "max":30 , "value": None},
  87. #          "desired number of additional Heat Sinks": {"min:":0, "max":100, "value": None},
  88.           }
  89.  
  90. def load_inputs(inputs_dict):
  91.   """Iterate over the given inputs dictionary, asking the user for each
  92.   and setting their values"""
  93.   for name in inputs_dict:
  94.     #name takes each key in the dictionary, which in this case is names of inputs
  95.     inputs_dict[name]["value"] = ask("Please provide your %s"%name,
  96.                                      inputs_dict[name]["min"],
  97.                                      inputs_dict[name]["max"])
  98.  
  99. #"Load inputs"
  100. load_inputs(Inputs)
  101.  
  102. SuspFact = 0 #SuspFact = (input("Suspension Factor: "))
  103.  
  104. MechTonnage = (Inputs["BattleMech’s Tonnage"]["value"])
  105. if MechTonnage < 10:
  106.           MechClass = ("ProtoMech")
  107.           MechName = ("ProtoMech")
  108.  
  109. elif 9 < MechTonnage < 20:
  110.           MechClass = ("Ultralight")
  111.           MechName = ("Ultralight BattleMech")
  112. elif 19 < MechTonnage < 36:
  113.           MechClass = ("Light")
  114.           MechName = ("Light BattleMech")
  115. elif 35 < MechTonnage < 56:
  116.           MechClass = ("Medium")
  117.           MechName = ("Medium BattleMech")
  118. elif 55 < MechTonnage < 76:
  119.           MechClass = ("Heavy")
  120.           MechName = ("Heavy BattleMech")
  121. elif 75 < MechTonnage < 101:
  122.           MechClass = ("Assault")
  123.           MechName = ("Assault BattleMech")
  124. elif 100 < MechTonnage:
  125.           MechClass = ("SuperHeavy")
  126.           MechName = ("SuperHeavy BattleMech")
  127.  
  128. NewMechName = input("Name your BattleMech: ");
  129. if NewMechName != '': MechName = NewMechName
  130. print();
  131.  
  132. CruisingSpeed = math.ceil(Inputs["desired Walk MP"]["value"]);
  133. def kph(s):
  134.     return (math.ceil(s*108))/10
  135. def metersec(s):
  136.     return (math.ceil(s*30))/10
  137. def mph(s):
  138.     return (math.ceil(s*671))/100
  139. #UserHeatSinks = (#(Inputs["desired number of additional Heat Sinks"]["value"]);
  140.  
  141. #"Do calculations"
  142. FlankingSpeed = math.ceil(CruisingSpeed * 1.5)
  143. if MechTonnage > 9:
  144.     rating = max(10, RoundUp5((Inputs["BattleMech’s Tonnage"]["value"] * Inputs["desired Walk MP"]["value"]) - SuspFact));
  145. else:
  146.     rating = max(2, (Inputs["BattleMech’s Tonnage"]["value"] * Inputs["desired Walk MP"]["value"]) - SuspFact);
  147.    
  148.  
  149. if rating >500:
  150.     print ("WARNING! This engine rating is outside normal parameters, and the weight has been extrapolated through a polynomial function.",
  151.             "Consent among all  participating players must be reached before it can be used in an actual game.");
  152.  
  153. #"Master Engine Table:"
  154. FusionEngineTable = {#See TechManual and Tactical Operations for more details
  155.   5: 0.5, 10: 0.5, 15: 0.5, 20: 0.5, 25: 0.5, 30: 1.0, 35: 1.0, 40: 1.0, 45: 1.0, 50: 1.5, 55: 1.5, 60: 1.5, 65: 2.0, 70: 2.0, 75: 2.0, 80: 2.5, 85: 2.5, 90: 3.0, 95: 3.0, 100: 3.0,#Standard Gyro: 1 Ton. Compact Gyro: 1.5 Tons. XL Gyro: 0.5 Tons. Heavy-Duty Gyro: 2 Tons.
  156.   105: 3.5, 110: 3.5, 115: 4.0, 120: 4.0, 125: 4.0, 130: 4.5, 135: 4.5, 140: 5.0, 145: 5.0, 150: 5.5, 155: 5.5, 160: 6.0, 165: 6.0, 170: 6.0, 175: 7.0, 180: 7.0, 185: 7.5, 190: 7.5, 195: 8.0, 200: 8.5,#Standard Gyro: 2 Tons. Compact Gyro: 3 Tons. XL Gyro: 1 Ton. Heavy-Duty/SuperHeavy Gyro: 4 Tons.
  157.   205: 8.5, 210: 9.0, 215: 9.5, 220: 10.0, 225: 10.0, 230: 10.5, 235: 11.0, 240: 11.5, 245: 12.0, 250: 12.5, 255: 13.0, 260: 13.5, 265: 14.0, 270: 14.5, 275: 15.5, 280: 16.0, 285: 16.5, 290: 17.5, 295: 18.0, 300: 19.0,#Standard Gyro: 3 Tons. Compact Gyro: 4.5 Tons. XL Gyro: 1.5 Tons. Heavy-Duty/SuperHeavy Gyro: 6 Tons.
  158.   305: 19.5, 310: 20.5, 315: 21.5, 320: 22.5, 325: 23.5, 330: 24.5, 335: 25.5, 340: 27.0, 345: 28.5, 350: 29.5, 355: 31.5, 360: 33.0, 365: 34.5, 370: 36.5, 375: 38.5, 380: 41.0, 385: 43.5, 390: 46.0, 395: 49.0, 400: 52.5,#Standard Gyro: 4 Tons. Compact Gyro: 6 Tons. XL Gyro: 2 Tons. Heavy-Duty/SuperHeavy Gyro: 8 Tons.
  159.   405: 56.5, 410: 61.0, 415: 66.5, 420: 72.5, 425: 79.5, 430: 87.5, 435: 97.0, 440: 107.5, 445: 119.5, 450: 133.5, 455: 150.0, 460: 168.5, 465: 190.0, 470: 214.5, 475: 243.0, 480: 275.5, 485: 313.0, 490: 356.0, 495: 405.5, 500: 462.5#Standard Gyro: 5 Tons. Compact Gyro: 7.5 Tons. XL Gyro: N/A. Heavy-Duty/SuperHeavy Gyro: 10 Tons.
  160.   }
  161.  
  162. ProtoMechEngineTable = {#ProtoMechs, for engine ratings below 40, multiply the engine rating by 0.025 to find the mass. Afterwards, they use Standard Fusion Engine weights.
  163.     1: .025, 2: .050, 3: .075, 4: .100, 5: .125, 6: .150, 7: .175, 8: .200, 9: .225, 10: .250,
  164.     11: .275, 12: .300, 13: .325, 14: .350, 15: .375, 16: .400, 17: .425, 18: .450, 19: .475, 20: .500,
  165.     21: .525, 22: .550, 23: .575, 24: .600, 25: .625, 26: .650, 27: .675, 28: .700, 29: .725, 30: .750,
  166.     31: .775, 32: .800, 33: .825, 34: .850, 35: .875, 36: .900, 37: .925, 38: .950, 39: .975, 40: 1.0,}
  167.  
  168. def PromptEngineType():
  169.     """Ask the user for their desired engine type, then return it."""
  170.     #engine list:
  171.     if 15 < MechTonnage < 101:
  172.         if rating < 401:
  173.             el = [#Basic Engine Types: 10 Heat Sinks each.
  174.                   "Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine",
  175.                   #Advanced Engine Types: 10 Heat Sinks each, but XXL will produce double heat from all types of movement.
  176.                   "XXL Fusion Engine", "Compact Fusion Engine",
  177.                   #Non-Fusion Engines:
  178.                   "Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine",
  179.                   #Primitive Engines:
  180.                   "Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine",
  181.                   #Special Engine Types:
  182.                   "Lithium-Fusion Engine", #ProtoMech Engine",
  183.                   ]
  184.             print("Engine types:");
  185.             for i in range(0, len(el)):
  186.                 print("  (%d) %s"%(i, el[i]));
  187.             num = ask("Please select your desired Engine type", 0, len(el)-1);
  188.             print();
  189.             return el[num]
  190.         else:
  191.             el = [#Basic Engine Types: 10 Heat Sinks each.
  192.                   "Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine",
  193.                   #Advanced Engine Types: 10 Heat Sinks each, but XXL will produce double heat from all types of movement.
  194.                   "XXL Fusion Engine",# "Compact Fusion Engine",
  195.                   #Non-Fusion Engines:
  196.                   "Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine",
  197.                   #Primitive Engines:
  198.                   "Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine",
  199.                   #Special Engine Types:
  200.                   "Lithium-Fusion Engine", #ProtoMech Engine",
  201.                   ]
  202.             print("Large Engine types:");
  203.             for i in range(0, len(el)):
  204.                 print("  (%d) %s"%(i, el[i]));
  205.             num = ask("Please select your Large Engine type", 0, len(el)-1);
  206.             print();
  207.             return el[num]
  208.     elif MechTonnage < 16:
  209.         if rating < 401:
  210.             el = [#Basic Engine Types: 10 Heat Sinks each.
  211.                   "Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine",
  212.                   #Advanced Engine Types: 10 Heat Sinks each, but XXL will produce double heat from all types of movement.
  213.                   "XXL Fusion Engine", "Compact Fusion Engine",
  214.                   #Non-Fusion Engines:
  215.                   "Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine",
  216.                   #Primitive Engines:
  217.                   "Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine",
  218.                   #Special Engine Types:
  219.                   "Lithium-Fusion Engine", "ProtoMech Engine",
  220.                   ]
  221.             print("Engine types:");
  222.             for i in range(0, len(el)):
  223.                 print("  (%d) %s"%(i, el[i]));
  224.             num = ask("Please select your desired Engine type", 0, len(el)-1);
  225.             print();
  226.             return el[num]
  227.         else:
  228.             el = [#Basic Engine Types: 10 Heat Sinks each.
  229.                   "Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine",
  230.                   #Advanced Engine Types: 10 Heat Sinks each, but XXL will produce double heat from all types of movement.
  231.                   "XXL Fusion Engine",# "Compact Fusion Engine",
  232.                   #Non-Fusion Engines:
  233.                   "Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine",
  234.                   #Primitive Engines:
  235.                   "Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine",
  236.                   #Special Engine Types:
  237.                   "Lithium-Fusion Engine", "ProtoMech Engine",
  238.                   ]
  239.             print("Large Engine types:");
  240.             for i in range(0, len(el)):
  241.                 print("  (%d) %s"%(i, el[i]));
  242.             num = ask("Please select your desired Large Engine type", 0, len(el)-1);
  243.             print();
  244.             return el[num]
  245.     else:
  246.         if rating < 401:
  247.             el = [#Basic Engine Types: 10 Heat Sinks each.
  248.                       "Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine",
  249.                       #Advanced Engine Types: 10 Heat Sinks each, but XXL will produce double heat from all types of movement.
  250.                       "XXL Fusion Engine", "Compact Fusion Engine",
  251.                       #Non-Fusion Engines:
  252.                       #"Fuel Cell",
  253.                       "Fission Engine",
  254.                       #Special Engine Types:
  255.                       "Lithium-Fusion Engine",
  256.                       ]
  257.             print("Engine types:")
  258.             for i in range(0, len(el)):
  259.                 print("  (%d) %s"%(i, el[i]));
  260.             num = ask("Please select your desired Engine type", 0, len(el)-1);
  261.             print();
  262.             return el[num]
  263.         else:
  264.             el = [#Basic Engine Types: 10 Heat Sinks each.
  265.                       "Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine",
  266.                       #Advanced Engine Types: 10 Heat Sinks each, but XXL will produce double heat from all types of movement.
  267.                       "XXL Fusion Engine",# "Compact Fusion Engine",
  268.                       #Non-Fusion Engines:
  269.                       #"Fuel Cell",
  270.                       "Fission Engine",
  271.                       #Special Engine Types:
  272.                       "Lithium-Fusion Engine",
  273.                       ]
  274.             print("Large Engine types:");
  275.             for i in range(0, len(el)):
  276.                 print("  (%d) %s"%(i, el[i]));
  277.             num = ask("Please select your desired Large Engine type", 0, len(el)-1);
  278.             print();
  279.             return el[num]
  280.  
  281. if MechTonnage < 10:
  282.     EngineType = ("ProtoMech Engine")
  283. else:
  284.     EngineType = PromptEngineType()
  285.  
  286. if EngineType in {"ProtoMech Engine"}:
  287.     rating = (MechTonnage * FlankingSpeed)
  288.     if MechClass in {"Ultralight"}:
  289.           MechClass = ("UltraHeavy ProtoMech")
  290. else:
  291.     rating = (MechTonnage * CruisingSpeed) - SuspFact
  292.  
  293. if EngineType in {"Lithium-Fusion Engine"}:
  294.     if CruisingSpeed < 2:
  295.         CruisingSpeed = 2
  296.         FlankingSpeed = math.ceil(CruisingSpeed * 1.5)
  297.     elif CruisingSpeed > 1:
  298.         CruisingSpeed = (Inputs["desired Walk MP"]["value"])
  299.         FlankingSpeed = math.ceil(CruisingSpeed * 1.5)
  300.  
  301. def FusionWeight(rating):
  302.     if rating <501:
  303.         return FusionEngineTable[RoundUp5(rating)]
  304.     elif EngineType in {"ProtoMech Engine"}:
  305.         if rating <41:
  306.             return rating * (25.0/1000.0) #ProtoMechEngineTable[rating]
  307.         else:
  308.             return FusionEngineTable[RoundUp5(rating)]
  309.     else: #Uses polynomial curves and floating-point math to extrapolate mass for engine ratings beyond 500. Experimental feature, not recommended for everyday use.
  310.         x = RoundUp5(rating)
  311.         coeff = [7.05283012941420e-23, -1.58264921514316e-19,  1.58814453473840e-16,
  312.                  -9.19203426420176e-14,  3.32942410135420e-11, -7.72438823285226e-09,
  313.                  1.13925338769604e-06, -0.000102985669746005,  0.00538423547801741,
  314.                  -0.112116210954985, 1.24919663674987]
  315.         icoeff = zip(range(0, len(coeff)), reversed(coeff))
  316.         weight = 0.0
  317.         for p,c in icoeff:
  318.           weight += c * (x**p)
  319.         weight = RoundUpFractional(roundNearest(weight * 1000.0)/1000.0)
  320.         return weight
  321.         print ("WARNING! This Engine Rating is outside normal Rating parameters, and its weight has been extrapolated. Do not use it in an actual game.");
  322.         print();
  323.  
  324. def FusionWeightPoly(rating): #Uses polynomial curves and floating-point math to extrapolate mass for engine ratings beyond 500. Experimental feature, not recommended for everyday use.
  325.   x = RoundUp5(rating)
  326.   coeff = [7.05283012941420e-23, -1.58264921514316e-19,  1.58814453473840e-16,
  327.           -9.19203426420176e-14,  3.32942410135420e-11, -7.72438823285226e-09,
  328.            1.13925338769604e-06, -0.000102985669746005,  0.00538423547801741,
  329.           -0.112116210954985, 1.24919663674987]
  330.   icoeff = zip(range(0, len(coeff)), reversed(coeff))
  331.   weight = 0.0
  332.   for p,c in icoeff:
  333.     weight += c * (x**p)
  334.   weight = roundNearest(weight * 2.0)/2.0
  335.   return weight
  336.  
  337. #for k in FusionEngineTable:
  338. #  t = FusionWeight(k)
  339. #  p = FusionWeightPoly(k)
  340. #  d = t - p
  341. #  if d > .01:
  342. #    print(k, t, p, d)
  343.  
  344. def EngineWeight(engine_type, rating):
  345.    #Using a dictionary to match engine name to a formula
  346.    weight = FusionWeight(rating)
  347.    primitive = FusionWeight(rating * 1.2)
  348.    lithium = FusionWeight(max(MechTonnage, rating - MechTonnage))
  349.    if rating < 40:
  350.       proto = rating * (25.0/1000.0) #ProtoMechEngineTable[rating]
  351.       lithium_proto = max(MechTonnage, rating - MechTonnage) * (25.0/1000.0)
  352.    else:
  353.       proto = weight
  354.       lithium_proto = lithium
  355.    ed = {"Standard Fusion Engine" : max(0.5, weight),
  356.          "Light Fusion Engine"    : max(0.5, RoundUpHalf(weight * .75)),
  357.          "XL Fusion Engine"       : max(0.5, RoundUpHalf(weight * .50)),
  358.          "XXL Fusion Engine"      : max(0.5, RoundUpHalf(weight / 3.0)),
  359.          "Compact Fusion Engine"  : max(1.0, RoundUpHalf(weight * 1.5)),
  360.          "Internal Combustion Engine (ICE)"  : max(1.0, RoundUpHalf(weight * 2.0)), #No Heat Sinks at all. Can't use energy weapons or Jump Jets. Don't forget fuel!
  361.          "Fuel Cell"  : max(1.0, RoundUpHalf((weight * 2.0) * 0.5)), #Only 1 Heat Sink. Can't use energy weapons or Jump Jets. Don't forget fuel!
  362.          "Fission Engine"  : max(5.0, RoundUpHalf(weight * float(7.0/4.0))), #Only 5 Heat Sinks. Illegal in the Inner Sphere, and expensive.
  363.          "Lithium-Fusion Engine"  : max(0.5, RoundUpHalf(lithium)), #Non-Canon. 10 Heat Sinks. Increases a mech's Walking Speed by 1.
  364.          "Primitive Fusion Engine"  : max(0.5, RoundUpHalf(primitive)), #Multiplies engine rating by 1.2, but does not increase performance.
  365.          "Primitive Internal Combustion Engine (ICE)"  : max(1.0, RoundUpHalf(primitive * 2.0)), #Multiplies engine rating by 1.2, but does not increase performance.
  366.          "Primitive Fuel Cell"  : max(1.0, RoundUpHalf(primitive * 1.2)), #Multiplies engine rating by 1.2, but does not increase performance.
  367.          "Primitive Fission Engine"  : max(5.0, RoundUpHalf(primitive * (7.0/4.0))), #Multiplies engine rating by 1.2, but does not increase performance.
  368.          "ProtoMech Engine" : RoundUpFractional((proto * 1000.0)/1000.0),
  369.          "ProtoMech XL Lithium-Fusion Engine" : RoundUpFractional(max(0.017, lithium_proto/3)),
  370.          "No Engine" : 0.00,
  371.          }
  372.    return ed[engine_type]
  373.  
  374. if EngineType in {"Lithium-Fusion Engine"}:
  375.     LithiumBatteryRating = max(MechTonnage, rating - MechTonnage)
  376. elif EngineType in {"Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell," "Primitive Fission Engine"}:
  377.     LithiumBatteryRating = RoundUp5(rating * 1.2)
  378. else:
  379.     LithiumBatteryRating = rating
  380.  
  381. #Cockpits
  382. def PromptCockpitType():
  383.     """Ask the user for their desired cockpit type, then return it."""
  384.     #cockpit list:
  385.     if LithiumBatteryRating < 401:
  386.             cl = ["Standard", "Small", "Armored", "Torso", "Interface", "Drone Operating System"]
  387.     elif LithiumBatteryRating > 400:
  388.           cl = ["Standard", "Small", "Armored", "Interface", "Drone Operating System"]
  389.     print("Cockpit types:");
  390.     for i in range(0, len(cl)):
  391.         print("  (%d) %s"%(i, cl[i]));
  392.     num = ask("Please select your desired Cockpit type", 0, len(cl)-1);
  393.     print();
  394.     return cl[num]
  395.  
  396. def CockpitWeight(cockpit_type):
  397.    #Using a dictionary to match cockpit name to a formula
  398.    cd = {
  399.         "ProtoMech": 0.5,
  400.         "UltraHeavy ProtoMech": 0.75,
  401.         "Drone Operating System": max(1.0, RoundUpHalf((MechTonnage/10.0) + 0.5)),
  402.         "Small": 2.0,
  403.         ("Standard", "AutoMech", "Industrial", "Armored Small"): 3.0,
  404.         #"AutoMech": 3.0,
  405.         ("Armored", "Torso", "Tripod", "Interface", "SuperHeavy"): 4.0,
  406.         #"Torso": 4.0,
  407.         #"Tripod": 4.0,
  408.         #"Interface": 4.0,
  409.         #"SuperHeavy": 4.0,
  410.         ("Primitive", "Primitive Industrial", "SuperHeavy Tripod"): 5.0
  411.         }
  412. #   return ed[cockpit_type]
  413.    for key in cd:
  414.         if cockpit_type in key:
  415.              return cd[key]
  416.  
  417. #Skeletons
  418. def PromptInternalType():
  419.    """Ask the user for their desired internal structure type, then return it."""
  420.    #internal structure list:
  421.    if MechTonnage < 101:
  422.         il = ["Standard", "Endo Steel", "Composite", "Hybrid", "Reinforced", "Industrial"]
  423.    elif MechTonnage > 100:
  424.         il = ["SuperHeavy Standard", "SuperHeavy Hybrid", "SuperHeavy Endo Steel", "SuperHeavy Reinforced", "SuperHeavy Industrial"] #You have balls if you pick that last one.
  425.    elif EngineType in {"Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine"}:
  426.         il = ["Primitive Standard", "Primitive Industrial"]
  427.    print("Internal Structure types:");
  428.    for i in range(0, len(il)):
  429.        print("  (%d) %s"%(i, il[i]))
  430.    num = ask("Please select your desired Internal Structure type", 0, len(il)-1);
  431.    print();
  432.    return il[num]
  433.  
  434. def InternalWeight(internal_type):
  435.     #Using a dictionary to match internal structure name to a formula
  436.     isd = {
  437.          ("Standard", "Primitive Standard", "SuperHeavy Endo Steel", "ProtoMech"): RoundUpHalf(MechTonnage/10.0),
  438.          ("Endo Steel", "Composite"): RoundUpHalf(MechTonnage/20.0),
  439.          #"Composite": RoundUpHalf(MechTonnage/20.0),
  440.          "Hybrid": RoundUpHalf(MechTonnage*7.5/100.0),
  441.          #"Reinforced": RoundUpHalf(MechTonnage/5.0),
  442.          #"Industrial": RoundUpHalf(MechTonnage/5.0),
  443.          #"Primitive Standard": RoundUpHalf(MechTonnage/10.0),
  444.          ("Reinforced", "Industrial", "Primitive Industrial", "SuperHeavy Standard") : RoundUpHalf(MechTonnage/5.0),
  445.          #"SuperHeavy Standard": RoundUpHalf(MechTonnage/5.0),
  446.          "SuperHeavy Hybrid": RoundUpHalf(MechTonnage*15.0/100.0),
  447.          #"SuperHeavy Endo Steel": RoundUpHalf(MechTonnage/10.0),
  448.          ("SuperHeavy Industrial", "SuperHeavy Reinforced"): RoundUpHalf(MechTonnage/2.5),
  449.          #"SuperHeavy Reinforced": RoundUpHalf(MechTonnage/2.5),
  450.          #"ProtoMech": RoundUpFractional(MechTonnage/10.0)}
  451.          }
  452. #   return ed[internal_type] #Trying to select keys from individual items out of a tuple, but the program is instead reading the entire tuple as a key.
  453.     for key in isd:
  454.        if internal_type in key:
  455.            return isd[key]
  456.  
  457. if MechTonnage > 100:
  458.      InternalType = PromptInternalType()
  459.      CockpitType = ("SuperHeavy")
  460.      if MechType in ["Tripod"]:
  461.           MechType = ("SuperHeavy Tripod")
  462.      else:
  463.           Mechtype = ("SuperHeavy" + CurrentMechType)
  464. elif EngineType in ["ProtoMech Engine"]:
  465.      InternalType = ("ProtoMech")
  466.      MechType = ("ProtoMech")
  467.      if MechTonnage < 10:
  468.           CockpitType = ("ProtoMech")
  469.      elif MechTonnage > 9:
  470.           CockpitType = ("UltraHeavy ProtoMech")
  471. else:
  472.      InternalType = PromptInternalType()
  473.      CockpitType = PromptCockpitType()
  474.      MechType = CurrentMechType
  475.  
  476. #Gyros
  477. def PromptGyroType():
  478.     """Ask the user for their desired gyro type, then return it."""
  479.     #gyro list:
  480.     if LithiumBatteryRating < 401 or EngineType in ["Compact Fusion Engine"]:
  481.           gl = ["Standard", "XL (Extra-Light)", "Compact", "Heavy-Duty",]
  482.     elif EngineType in {"Lithium-Fusion Engine"} and rating < 501:
  483.           gl = ["Standard", "XL (Extra-Light)", "Compact", "Heavy-Duty",]
  484.     elif LithiumBatteryRating > 400 or CockpitType in ["Torso"]:
  485.           if EngineType not in ["Compact Fusion Engine"]:
  486.               gl = ["Standard", "Compact", "Heavy-Duty",]
  487.     print("Gyro types:")
  488.     for i in range(0, len(gl)):
  489.         print("  (%d) %s"%(i, gl[i]))
  490.     num = ask("Please select your desired Gyro type", 0, len(gl)-1)
  491.     print();
  492.     return gl[num]
  493.  
  494. FinalRating = LithiumBatteryRating
  495. GyroRating = math.ceil(LithiumBatteryRating / 100)
  496.  
  497. if MechTonnage > 100:
  498.     GyroType = ("SuperHeavy")
  499. elif EngineType in ["Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine"]:
  500.     if MechTonnage < 101:
  501.          GyroType = ("Primitive")
  502.     elif MechTonnage > 100:
  503.          GyroType = ("SuperHeavy Primitive")
  504. elif CockpitType in ["ProtoMech", "UltraHeavy ProtoMech", "Interface"]:
  505.     GyroType = ("None")
  506. else:
  507.     GyroType = PromptGyroType()
  508.  
  509. def GyroWeight(gyro_type):
  510.    #Using a dictionary to match gyro name to a formula
  511.    gmass = GyroRating
  512.    gd = {
  513.         ("Standard", "Primitive Standard") : max(1.0, gmass*1.0),
  514.         "XL (Extra-Light)" : max(1.0, RoundUpHalf(gmass/2.0)),
  515.         "Compact" : max(1.5, RoundUpHalf(gmass*1.5)),
  516.         ("Heavy-Duty", "SuperHeavy", "Primitive SuperHeavy") : max(2.0, math.ceil(gmass * 2.0)),
  517.         #"SuperHeavy" : max(2.0, math.ceil(gmass*2.0)),
  518.         #"Primitive" : max(1.0, gmass*1.0),
  519.         #"Primitive SuperHeavy" : max(2.0, math.ceil(gmass*2.0)), #unused
  520.         "None": (gmass*0.0),
  521.         "Armored" : (gmass + 2.0),
  522.         "Armored Compact" :  1.0 + RoundUpHalf(gmass*1.5),
  523.         "Armored XL" : 3.0 + RoundUpHalf(gmass/2.0),
  524.         "Armored Heavy-Duty" : 2.0 + (gmass*2.0),
  525.         }
  526. #   return ed[gyro_type]
  527.    for key in gd:
  528.         if gyro_type in key:
  529.              return gd[key]
  530.  
  531. #Heat Sinks
  532. def PromptHeatSinkType():
  533.     """Ask the user for their desired heat sink type, then return it."""
  534.     #heat sink list:
  535.     hsl = ["Single", "Double (Inner Sphere)", "Double (Clan)", "Laser", "Compact"]
  536.     print("Heat Sink types:");
  537.     for i in range(0, len(hsl)):
  538.         print("  (%d) %s"%(i, hsl[i]));
  539.     num = ask("Please select your desired Heat Sink type", 0, len(hsl)-1);
  540.     print();
  541.     return hsl[num]
  542.  
  543. if EngineType in {"Fission Engine", "Primitive Fission Engine"}:
  544.     FreeHeatSinks = 5
  545.     HeatSinkType = PromptHeatSinkType()
  546. elif EngineType in {"Fuel Cell", "Primitive Fuel Cell"}:
  547.     FreeHeatSinks = 1
  548.     HeatSinkType = PromptHeatSinkType()
  549. elif EngineType in {"Internal Combustion Engine (ICE)", "Primitive Internal Combustion Engine (ICE)"}:
  550.     FreeHeatSinks = 0
  551.     HeatSinkType = PromptHeatSinkType()
  552. elif EngineType in {"ProtoMech Engine"}:
  553.     FreeHeatSinks = 0
  554.     HeatSinkType = ("ProtoMech")
  555. else:
  556.     FreeHeatSinks = 10
  557.     HeatSinkType = PromptHeatSinkType()
  558.  
  559. if EngineType in ["ProtoMech Engine"]:
  560.      UserHeatSinks = ask("Please provide your desired quantity of ProtoMech Heat Sinks", 0, MechTonnage*4)
  561.      
  562. else:
  563.      print ("You currently have", int(FreeHeatSinks), HeatSinkType, "Heat Sinks.")
  564.      UserHeatSinks = ask("If you wish to install additional Heat Sinks, enter the quantity you wish to add", 0, MechTonnage)
  565.      
  566. TotalHeatSinks = UserHeatSinks + FreeHeatSinks
  567.  
  568. if TotalHeatSinks > 0:
  569.      print()
  570.  
  571. if EngineType in {"ProtoMech Engine"}:
  572.         HeatSinkWeight = RoundUpFractional(UserHeatSinks/4.0)
  573.         IntegralHeatSinks = int(TotalHeatSinks)
  574.         HeatSinkSlots = 0
  575. elif HeatSinkType in {"Compact"}:
  576.         HeatSinkWeight = RoundUpHalf(UserHeatSinks * 1.5)
  577.         IntegralHeatSinks = int(min(TotalHeatSinks, math.floor(LithiumBatteryRating/12.5)))
  578. else:
  579.         HeatSinkWeight = UserHeatSinks * 1.0
  580.         IntegralHeatSinks = int(min(TotalHeatSinks, math.floor(LithiumBatteryRating/25.0)))
  581.  
  582. PodHeatSinks = TotalHeatSinks - IntegralHeatSinks
  583.  
  584. if MechTonnage < 101:
  585.      if HeatSinkType in ["Single", "Liquid Metal"]:
  586.          HeatSinkSlots = PodHeatSinks
  587.      elif HeatSinkType in ["Double (Inner Sphere)", "Double (ProtoType)"]:
  588.          HeatSinkSlots = PodHeatSinks * 3
  589.      elif HeatSinkType in ["Double (Clan)", "Double (Star Empire)", "Laser"]:
  590.          HeatSinkSlots = PodHeatSinks * 2
  591.      elif HeatSinkType in ["Compact"]:
  592.          HeatSinkSlots = math.ceil(PodHeatSinks/2)
  593.      elif HeatSinkType in ["ProtoMech"]:
  594.          HeatSinkSlots = 0
  595.  
  596. elif MechTonnage > 100:
  597.      if HeatSinkType in ["Single"]:
  598.          HeatSinkSlots = math.ceil(PodHeatSinks/2)
  599.      elif HeatSinkType in ["Double (Inner Sphere)"]:
  600.          HeatSinkSlots = math.ceil(PodHeatSinks * 1.5)
  601.      elif HeatSinkType in ["Double (Clan)", "Laser"]:
  602.          HeatSinkSlots = PodHeatSinks
  603.      elif HeatSinkType in ["Compact"]:
  604.          HeatSinkSlots = math.ceil(PodHeatSinks/4)
  605.  
  606. #Podspace
  607. skeleton = InternalWeight(InternalType)
  608. tripod = RoundUpHalf(skeleton * 1.1)
  609. cockpit = CockpitWeight(CockpitType)
  610. gyro = GyroWeight(GyroType)
  611. EngineTonnage = EngineWeight(EngineType, rating)
  612.  
  613. if MechType in ["Combat Vehicle", "Support Vehicle"]:
  614.      if EngineType in ["Internal Combustion Engine (ICE)", "Fuel Cell", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell"]:
  615.           VehicleEngine = EngineTonnage
  616.      else:
  617.           if MechType in ["Combat Vehicle"]:
  618.                VehicleEngine = RoundUpHalf(EngineTonnage * 1.5)
  619.           elif MechType in ["Support Vehicle"]:
  620.                VehicleEngine = RoundUpFractional(EngineTonnage * 1.5)
  621. else:
  622.      VehicleEngine = EngineTonnage
  623.  
  624. print()
  625. if MechType in ["Tripod", "SuperHeavy Tripod"]:
  626.      podspace = RoundDownHalf(MechTonnage - EngineTonnage - tripod - gyro - cockpit - HeatSinkWeight)
  627. elif EngineType in ["ProtoMech Engine", "ProtoMech XL Lithium-Fusion Engine"] or MechType in ["ProtoMech", "Quad ProtoMech", "Glider ProtoMech"]:
  628.      podspace = RoundDownFractional(MechTonnage - EngineTonnage - skeleton - gyro - cockpit - HeatSinkWeight)
  629. elif MechType in ["Combat Vehicle"]:
  630.      podspace = RoundDownHalf(MechTonnage - VehicleEngine - skeleton - gyro - cockpit - HeatSinkWeight)
  631. elif MechType in ["Support Vehicle"]:
  632.      podspace = RoundDownFractional(MechTonnage - VehicleEngine - skeleton - gyro - cockpit - HeatSinkWeight)
  633. else:
  634.      podspace = RoundDownHalf(MechTonnage - EngineTonnage - skeleton - gyro - cockpit - HeatSinkWeight)
  635.  
  636. if VehicleEngine > MechTonnage:
  637.      print ("WARNING! Your engine is heavier than the mech!");
  638. if podspace <0.0:
  639.      print ("WARNING! Your mech has negative podspace, and is Overweight!");
  640.  
  641. #Finalize
  642. if MechName in {"BattleMech", "ProtoMech", "Ultralight BattleMech", "Light BattleMech", "Medium BattleMech", "Heavy BattleMech", "Assault BattleMech", "SuperHeavy BattleMech"}:
  643.      if InternalType in {"Industrial"}:
  644.          if MechClass in {"Ultralight", "Light", "Medium", "Heavy", "Assault", "SuperHeavy"}:
  645.               MechName = (MechClass + "IndustrialMech")
  646.      if InternalType in {"Primitive Standard"}:
  647.          if MechClass in {"Ultralight", "Light", "Medium", "Heavy", "Assault", "SuperHeavy"}:
  648.               MechName = ("Primitive" + MechClass + "BattleMech")
  649.      if InternalType in {"Primitive Industrial"}:
  650.          if MechClass in {"Ultralight", "Light", "Medium", "Heavy", "Assault", "SuperHeavy"}:
  651.               MechName = ("Primitive" + MechClass + "Industrial")
  652.      elif MechClass in {"ProtoMech", "UltraHeavy ProtoMech"}:
  653.           MechName = MechClass
  654.      elif CockpitType in {"AutoMech"}:
  655.           MechName = (MechClass + "AutoMech")
  656.  
  657. #Technical Readout
  658. print ("Your", MechName, "has a Cruising MP of", CruisingSpeed, "hexes per turn, and a Flanking MP of", FlankingSpeed, "hexes per turn.");
  659.  
  660. print ("This speed is provided by a", FinalRating, "rated", EngineType, "weighing", EngineTonnage, "metric tons.");
  661.  
  662. if EngineType in ["ProtoMech Engine", "ProtoMech XL Lithium-Fusion Engine"]:
  663.      print("You have", TotalHeatSinks, HeatSinkType, "Heat Sinks weighing", HeatSinkWeight, "metric tons.")
  664.  
  665. else:
  666.      print("You have", TotalHeatSinks, HeatSinkType, "Heat Sinks weighing", HeatSinkWeight, "metric tons, of which", IntegralHeatSinks, "of them are Integral,")
  667.      print ("and out of those", TotalHeatSinks, HeatSinkType, "Heat Sinks," + FreeHeatSinks, "of them come with the engine.")
  668.      if PodHeatSinks > 0:
  669.           print ("The remaining", HeatSinkType, "Heat Sinks occupy", HeatSinkSlots, "Critical Spaces in total.")    
  670.  
  671. print()
  672.  
  673. if MechType in ["Tripod"]:
  674.      print ("The Mech's weight is supported by a", InternalType, "Internal Structure with a mass of", tripod, "metric tons.");
  675.  
  676. elif MechType in ["AutoMech"]:
  677.      print ("The AutoMech’s weight is supported by a", InternalType, "Internal Structure with a mass of", skeleton, "metric tons.");
  678.      
  679. elif MechType in ["ProtoMech"]:
  680.      print ("The ProtoMech’s weight is supported by a", InternalType, "Internal Structure with a mass of", skeleton, "metric tons.");
  681.  
  682. else:
  683.      print ("The Mech’s weight is supported by a", InternalType, "Internal Structure with a mass of", skeleton, "metric tons.");
  684.  
  685. if GyroType in ["None"] and MechTonnage > 9:
  686.      print("The pilot is cocooned in an", CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  687.  
  688. elif GyroType in ["None"] and MechTonnage <10:
  689.      print("The pilot is cocooned in a", CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  690.  
  691. elif GyroType in ["SuperHeavy"]:
  692.      print ("and stopping your", MechName, "from toppling over is a monstrous", GyroType, "Gyro weighing", gyro, "metric tons.");
  693.      print ("The pilots are huddled together in a", CockpitType, "Cockpit, weighing a massive", cockpit, "metric tons.");
  694.  
  695. elif GyroType in ["XL (Extra-Light)"]:
  696.      print ("and keeping the", MechName, "upright is an", GyroType, "Gyro weighing", gyro, "metric tons.");
  697.      print ("The pilot sits ’comfortably’ in a", CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  698.  
  699. elif CockpitType in ["Drone Operating System", "AutoMech"]:
  700.      if GyroType in ["XL (Extra-Light)"]:
  701.           print ("and keeping the", MechName, "upright is an", GyroType, "Gyro weighing", gyro, "metric tons.")
  702.      else:
  703.           print ("and keeping the", MechName, "upright is a", GyroType, "Gyro weighing", gyro, "metric tons.")
  704.      
  705.      if CockpitType in ["Drone Operating System"]:
  706.           print ("The brains of this machine consist of a", CockpitType, "weighing", cockpit, "metric tons.")
  707.      
  708.      elif CockpitType in ["AutoMech"]:
  709.           print ("The brains of this machine consist of an", CockpitType, "weighing", cockpit, "metric tons.")
  710.  
  711. else:
  712.      print ("and keeping the", MechName, "upright is an", GyroType, "Gyro weighing", gyro, "metric tons.");
  713.      print ("The pilot sits 'comfortably' in a", CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  714.  
  715. print()
  716. print ("The", MechName, "has an average walking speed of", kph(CruisingSpeed), "km/h, and a maximum running speed of", kph(FlankingSpeed), "km/h.");
  717.  
  718. if podspace > 0.0:
  719.    print ("The", MechName, "has", podspace, "tons of Pod Space available to mount weapons, armor and equipment.");
  720.    print();
  721. else:
  722.    print ("Oh, stravag! It would seem that the", MechName, "has", podspace, "tons of Pod Space left, and now it cannot mount any weapons, armor or equipment!");
  723.    print();
  724.  
  725. ## Closing sequence.
  726. print ("Please visit http://www.sarna.net for more information on these terms and components.");
  727. print ("If you have any questions or feedback about the software, such as feature requests or bug reports,");
  728. print ("if you need technical support, or would simply like to contribute to development, then please visit:");
  729. print ("https://tinyurl.com/Sarna-Forums-MechEngine");
  730. print();
  731. now = datetime.datetime.now()
  732. print("The current time and date is", now.strftime("%A, %Y-%m-%d, %H:%M:%S (local)"))
  733. input("Press ENTER to exit the program.") #Change this line to a menu option once Restart has been implemented.
  734. print ("Ejecting.")
  735.  
  736. ## End of Line.
  737.  
  738. #Contribution Links:
  739. #     https://pastebin.com/dpMnWPjp
  740. #     https://docs.google.com/document/d/1A6Y79pelpzdRg00DHcZNO3tyrdvthFSaU4tihSvGkE8/edit?usp=sharing
Add Comment
Please, Sign In to add comment