Lycanphoenix

MechEngine Beta (0.31.32)

Jun 4th, 2018
159
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 37.40 KB | None | 0 0
  1. import math, os, sys, time, datetime, threading, builtins, lib2to3
  2.  
  3. print('''MechEngine - BattleMech design aid by New Avalon Institute of Science.
  4. Build Version: 0.31.32 (Beta, Python Interpreter)
  5. Build Date: Monday, 2018-06-04.
  6. Designed using Python 3.6 Shell.''')
  7.  
  8. #Python 3 Backwards Compatibility Wrapper (Alpha)
  9. """This wrapper is intended to reduce compatibility issues when running Python 3 code in Python 2.
  10. However, it is far from perfect, and is meant primarily for idiot-proofing.
  11. Either rewriting your code, or switching to a newer version of Python, is strongly recommended."""
  12.  
  13. if sys.version_info[0] > 2:
  14.     """Executes the following code if the user is running Python 3."""
  15.     def raw_input(*args, **kwargs):
  16.         """This definition is included so that the version check below doesn't kill the program."""
  17.         return input(*args, **kwargs)
  18.     def failUnlessEqual(a, b):
  19.         return assertEqual(a, b)
  20.     def assertEquals(a, b):
  21.         return assertEqual(a, b)
  22.     def failIfEqual(a, b):
  23.         return assertNotEqual(a, b)
  24.     def assertNotEquals(a, b):
  25.         return failIfEqual(a, b)
  26.     def assert_(a):
  27.         return assertTrue(a)
  28.     def failUnless(a):
  29.         return assert_(a)
  30.     def failIf(a):
  31.         return assertFalse(a)
  32.     def failUnlessRaises(exc, cal):
  33.         return assertRaises(esc, cal)
  34.     def failUnlessAlmostEqual(a, b):
  35.         return assertAlmostEqual(a, b)
  36.     def assertAlmostEquals(a, b):
  37.         return assertAlmostEqual(a, b)
  38.     def failIfAlmostEqual(a, b):
  39.         return assertNotAlmostEqual(a, b)
  40.     def assertNotAlmostEquals(a, b):
  41.         return failIfAlmostEqual(a, b)
  42.     """MISC"""
  43.     def intern():
  44.         return sys.intern()
  45.     def long():
  46.         """This definition is included so that Long Integers from Python 2 will just be treated as Python 3 Integers."""
  47.         return int
  48.     def apply(function, *args, **kwargs):
  49.         return function(*args, **kwargs)
  50.     def basestring():
  51.         return str
  52.     def unicode():
  53.         return str
  54.     def buffer():
  55.         return memoryview()
  56.     def xrange():
  57.         return range()
  58.     def __nonzero__():
  59.         return __bool__()
  60.  
  61. if sys.version_info[0] < 3:
  62.     """Executes the following code if the user is running Python 2."""
  63.     raw_input("Incompatible Python version detected. Please switch to Python 3.6 before running this program, or crashes may occur.")
  64.     def int():
  65.         """This definition is included to automatically treat all Integers as Long Integers, as in Python 3."""
  66.         return long()
  67.  
  68. def floor(x):
  69.     """Always rounds down."""
  70.     return math.floor(x)
  71.  
  72. def ceil(x):
  73.     """Always rounds up."""
  74.     return math.ceil(x)
  75.  
  76. def gettime():
  77.     """Updates and prints the current time and date."""
  78.     now = datetime.datetime.now()
  79.     return print("Current Time and Date, Local:", now.strftime("%A, %Y-%m-%d, %H:%M:%S %z"))
  80. gettime()
  81. print();
  82.  
  83. print("""Development Goals:
  84. 01. Add option to restart the program upon completion.
  85. 02. Add option to export a created unit for sharing, record keeping and analysis.
  86. 03. Add component descriptions to help the user make informed decisions.
  87. 04. Fully implement Critical Spaces and alternate unit types.
  88. 05. Add ability to open and edit existing units, excluding hard-coded examples.
  89. 06. Add example units to choose from.
  90. 07. Create compiled versions of the program for various platforms.
  91. 08. Optimize for stability, compatibility and portability.
  92. 09. Make program easier to understand for people not familiar with BattleTech.
  93. 10. Create webtool version(?)
  94. 11. Clean up code however possible to reduce filesize, fix bugs and making the code easier to understand.
  95. 12. Develop graphical user interface.
  96. """)
  97.  
  98. def fib(n):    # write Fibonacci series up to n
  99.      """Print a Fibonacci series up to a final value of n.
  100. Purely for style and is not necessary for program use."""
  101.      a, b = 0, 1
  102.      while a <= n:
  103.          print(a, end=" ");
  104.          a, b = b, a + b
  105.      print();
  106.  
  107. def mem_fib(n, _cache={}):
  108.     '''Efficiently memoized recursive function, returns a Fibonacci number with n iterations.
  109. Again, entirely optional.'''
  110.     if n in _cache:
  111.         return _cache[n]
  112.     elif n > 1:
  113.         return _cache.setdefault(n, mem_fib(n-1) + mem_fib(n-2))
  114.     return n
  115.  
  116. def printfib(n):
  117.     for i in range(n):
  118.         print(i, mem_fib(i))
  119.  
  120. def RoundUp5(x):
  121.   return ceil(x / 5.0) * 5 #Round up to nearest multiple of 5
  122.  
  123. def RoundUpHalf(x):
  124.   return ceil(x*2.0)/2.0
  125.  
  126. def RoundUpQuarter(x):
  127.   return ceil(x*4.0)/4.0
  128.  
  129. def RoundUpFractional(x):
  130.   return ceil(x*1000.0)/1000.0
  131.  
  132. def RoundDown5(x):
  133.   return floor(x / 5.0) * 5
  134.  
  135. def RoundDownHalf(x):
  136.   return floor(x*2.0)/2.0
  137.  
  138. def RoundDownQuarter(x):
  139.   return floor(x*4.0)/4.0
  140.  
  141. def RoundDownFractional(x):
  142.   return floor(x*1000.0)/1000.0
  143.  
  144. def roundNearest(f):
  145.     return int(round(f + .00000000000001));
  146.  
  147. ###Deprecated
  148. ##def roundHalfUp(f): #deprecated!
  149. ##    return RoundUpHalf(f) #round(f + 0.00000000000001)
  150. ##
  151. ##def roundEngine(x, base=5): #deprecated!
  152. ##    return int(base * RoundUpHalf(float(x)/base));
  153. ##
  154. ##def roundTonnage(x, base=5): #deprecated!
  155. ##    return int(base * RoundUpHalf(float(x)/base));
  156.  
  157. #HYN-DRN-5813 Core Input Dictionary:
  158. def clamp(min_value, max_value, x):
  159.   """Returns x, limited to the range min_val to max_val."""
  160.   return max(min(x, max_value), min_value)
  161.  
  162. def RepresentsInt(s):
  163.     try:
  164.         int(s)
  165.         return True
  166.     except ValueError:
  167.         try:
  168.             ceil(float(s))
  169.             return True
  170.         except ValueError:
  171.             return False
  172. ##        return False
  173.  
  174. def ask(question, min_val, max_val):
  175.   x = ''
  176.   while not RepresentsInt(x) or int(x) < min_val or int(x) > max_val:
  177.     if x != '':
  178.           print ("This is not a number. Please enter a valid input.")
  179.     x = input(question + " (min %d, max %d)"%(min_val, max_val) + ": ")
  180.   return clamp(min_val, max_val, int(x))
  181.  
  182. MechType = ("Biped")
  183. def PromptMechType():
  184.     """Ask the user for their desired Unit type, then return it. These options do not do anything yet, though."""
  185.     #unit list:
  186.     ml = ["Biped", "Quad", "Tripod", "Land-Air Mech", "HoverTank Mech",
  187.           "ProtoMech", "Glider ProtoMech", "Quad ProtoMech",
  188.           "Combat Vehicle - Wheeled", "Support Vehicle - Wheeled", "Combat Vehicle - Tracked", "Support Vehicle - Tracked",
  189.           "Combat Vehicle - Hovercraft", "Support Vehicle - Hovercraft", "Combat Vehicle - VTOL", "Support Vehicle - VTOL",
  190.           "Combat Vehicle - WiGE", "Support Vehicle - WiGE", "Aerospace Fighter", "Conventional Fighter"]
  191.     print("Unit types - These do nothing yet:");
  192.     for i in range(0, len(ml)):
  193.         print("  (%d) %s"%(i, ml[i]));
  194.     num = ask('''
  195. Please select your desired Unit type''', 0, len(ml)-1);
  196.     print();
  197.     return ml[num]
  198. NewMechType = PromptMechType()
  199. #MechType = NewMechType
  200.  
  201. if NewMechType in {"Combat Vehicle - Wheeled", "Support Vehicle - Wheeled", "Combat Vehicle - Tracked", "Support Vehicle - Tracked",
  202.           "Combat Vehicle - Hovercraft", "Support Vehicle - Hovercraft", "Combat Vehicle - VTOL", "Support Vehicle - VTOL",
  203.           "Combat Vehicle - WiGE", "Support Vehicle - WiGE"}:
  204.     IsVehicle = True
  205. else:
  206.     IsVehicle = False
  207.  
  208. print ("IsVehicle =", IsVehicle, '''
  209. ''')
  210.  
  211. CurrentMechType = MechType
  212.  
  213. MinTon = 2
  214. MaxTon = 200
  215. """Minimum and Maximum unit tonnage will vary with unit type, as will many other options."""
  216.  
  217. Inputs = {"BattleMech’s Tonnage": {"min":MinTon, "max":MaxTon, "value": None},
  218.           "desired Walk MP" : {"min":1, "max":30 , "value": None},
  219.           }
  220.  
  221. def load_inputs(inputs_dict):
  222.   """Iterate over the given inputs dictionary, asking the user for each
  223.   and setting their values"""
  224.   for name in inputs_dict:
  225.     #name takes each key in the dictionary, which in this case is names of inputs
  226.     inputs_dict[name]["value"] = ask("Please provide your %s"%name,
  227.                                      inputs_dict[name]["min"],
  228.                                      inputs_dict[name]["max"])
  229.  
  230. #"Load inputs"
  231. print ("Hint: One ground hex = 30 meters, one turn = 10 seconds.")
  232. load_inputs(Inputs)
  233.  
  234. SuspFact = 0 #SuspFact = (input("Suspension Factor: "))
  235.  
  236. MechTonnage = (Inputs["BattleMech’s Tonnage"]["value"])
  237.  
  238. if MechTonnage < 10:
  239.     MechClass = ("ProtoMech")
  240.     MechName = ("ProtoMech")
  241. elif 9 < MechTonnage:
  242.     if MechTonnage < 20: MechClass = ("Ultralight")
  243.     elif 19 < MechTonnage < 36: MechClass = ("Light")
  244.     elif 35 < MechTonnage < 56: MechClass = ("Medium")
  245.     elif 55 < MechTonnage < 76: MechClass = ("Heavy")
  246.     elif 75 < MechTonnage < 101: MechClass = ("Assault")
  247.     elif 100 < MechTonnage: MechClass = ("SuperHeavy")
  248.     MechName = (MechClass + " BattleMech")
  249. else:
  250.     MechName = ("Unit")
  251.  
  252. NewMechName = input("Name your BattleMech: ");
  253. if NewMechName != '': MechName = NewMechName
  254. print()
  255.  
  256. CruisingSpeed = ceil(Inputs["desired Walk MP"]["value"]);
  257. def kph(s):
  258.     return (ceil(s*108))/10
  259. def metersec(s):
  260.     return (ceil(s*30))/10
  261. def mph(s):
  262.     return (ceil(s*671))/100
  263.  
  264. print('''Press ENTER to confirm Unit Statistics:
  265. Unit Name:''', MechName,'''
  266. Unit Type:''', MechType,'''
  267. Tonnage:''', MechTonnage, '''Metric Tons.
  268. Weightclass:''', MechClass,'''
  269. Cruising Speed:''', CruisingSpeed, "Hexes per Turn.")
  270. input()
  271.  
  272. fib(MechTonnage)
  273. print()
  274.  
  275. #"Do calculations"
  276. FlankingSpeed = ceil(CruisingSpeed * 1.5)
  277. if MechTonnage > 15:
  278.     rating = max(10, RoundUp5((Inputs["BattleMech’s Tonnage"]["value"] * Inputs["desired Walk MP"]["value"]) - SuspFact));
  279. else:
  280.     rating = max(2, (Inputs["BattleMech’s Tonnage"]["value"] * Inputs["desired Walk MP"]["value"]) - SuspFact);
  281.  
  282. if rating >500:
  283.     print ("WARNING! This engine rating of", rating, '''is outside normal parameters, and the weight will be extrapolated through a polynomial function.
  284. Consent among all participating players must be reached before it can be used for an actual game.''')
  285.  
  286.  
  287. #"Master Engine Table:"
  288. FusionEngineTable = {#See TechManual and Tactical Operations for more details
  289.   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,#Gyro: 1 Ton
  290.   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,#Gyro: 2 Tons
  291.   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,#Gyro: 3 Tons
  292.   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,#Gyro: 4 Tons
  293.   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#Gyro: 5 Tons
  294.   }
  295.  
  296. def PromptEngineType():
  297.     """Ask the user for their desired engine type, then return it."""
  298.     #engine list:
  299.     if MechTonnage < 16 and rating < 401:
  300.         el = [#Basic Engine Types:
  301.               "Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine",
  302.               #Advanced Engine Types:
  303.               "XXL Fusion Engine", "Compact Fusion Engine",
  304.               #Non-Fusion Engines:
  305.               "Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine",
  306.               #Primitive Engines:
  307.               "Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine",
  308.               #Special Engine Types:
  309.               "Lithium-Fusion Engine", "ProtoMech Engine",
  310.               ]
  311.     elif MechTonnage < 16 and rating > 400:
  312.         el = ["Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine", "XXL Fusion Engine", "Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine", "Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine", "Lithium-Fusion Engine", "ProtoMech Engine"]
  313.     elif 15 < MechTonnage < 101 and rating < 401:
  314.         el = ["Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine","XXL Fusion Engine", "Compact Fusion Engine","Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine","Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine", "Lithium-Fusion Engine"]
  315.     elif 15 < MechTonnage < 101 and rating > 400:
  316.         el = ["Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine", "XXL Fusion Engine", "Internal Combustion Engine (ICE)", "Fuel Cell", "Fission Engine", "Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine", "Lithium-Fusion Engine"]
  317.     elif MechTonnage > 100 and rating < 401:
  318.         el = ["Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine", "XXL Fusion Engine", "Compact Fusion Engine", "Fission Engine", "Lithium-Fusion Engine"]
  319.     elif MechTonnage > 100 and rating > 400:
  320.         el = ["Standard Fusion Engine", "Light Fusion Engine", "XL Fusion Engine", "XXL Fusion Engine", "Fission Engine", "Lithium-Fusion Engine"]
  321.  
  322.     if rating < 401:
  323.         print('''Engine types:
  324. (Rule of thumb: XL is lighter, but bulkier, than Standard.)''')
  325.         for i in range(0, len(el)):
  326.             print("  (%d) %s"%(i, el[i]));
  327.         num = ask('''
  328. Please select your desired Engine type''', 0, len(el)-1);
  329.         print();
  330.         return el[num]
  331.     else:
  332.         print('''Large Engine types:
  333. (Rule of thumb: XL is lighter, but bulkier, than Standard.)''');
  334.         for i in range(0, len(el)):
  335.             print("  (%d) %s"%(i, el[i]));
  336.         num = ask('''
  337. Please select your desired Large Engine type''', 0, len(el)-1);
  338.         print();
  339.         return el[num]
  340.  
  341. if MechTonnage < 10:
  342.     EngineType = ("ProtoMech Engine")
  343. else:
  344.     EngineType = PromptEngineType()
  345.  
  346. if EngineType in {"ProtoMech Engine"}:
  347.     rating = (MechTonnage * FlankingSpeed) - SuspFact
  348.     if rating < 41:
  349.         rating = RoundUp5(rating)
  350.     if MechClass in {"Ultralight"}:
  351.         MechClass = ("UltraHeavy ProtoMech")
  352. else:
  353.     rating = RoundUp5((MechTonnage * CruisingSpeed) - SuspFact)
  354.  
  355. if EngineType in {"Lithium-Fusion Engine"}:
  356.     if CruisingSpeed < 2:
  357.         CruisingSpeed = 2
  358.         FlankingSpeed = ceil(CruisingSpeed * 1.5)
  359.     elif CruisingSpeed > 1:
  360.         CruisingSpeed = (Inputs["desired Walk MP"]["value"])
  361.         FlankingSpeed = ceil(CruisingSpeed * 1.5)
  362.  
  363. def FusionWeight(rating):
  364.     if EngineType in ["ProtoMech Engine"]:
  365.         """ProtoMechs, for engine ratings below 40, multiply the engine rating by 0.025 to find the mass.
  366.                Afterwards, they use Standard Fusion Engine weights."""
  367.         if rating < 41:
  368.             weight = rating * (25.0/1000.0)
  369.             #print (weight)
  370.             return rating * (25.0/1000.0)
  371.         else:
  372.             return FusionEngineTable[RoundUp5(rating)]
  373.  
  374.     elif rating < 501:
  375.         return FusionEngineTable[RoundUp5(rating)]
  376.  
  377.     elif rating > 500: #Uses polynomial curves and floating-point math to extrapolate mass for engine ratings beyond 500. Experimental feature, not recommended for everyday use.
  378.         x = RoundUp5(rating)
  379.         coeff = [7.05283012941420e-23, -1.58264921514316e-19,  1.58814453473840e-16,
  380.                  -9.19203426420176e-14,  3.32942410135420e-11, -7.72438823285226e-09,
  381.                  1.13925338769604e-06, -0.000102985669746005,  0.00538423547801741,
  382.                  -0.112116210954985, 1.24919663674987]
  383.         icoeff = zip(range(0, len(coeff)), reversed(coeff))
  384.         weight = 0.0
  385.         for p,c in icoeff:
  386.             weight += c * (x**p)
  387.         weight = RoundUpFractional(weight*1000.0)/1000.0
  388.  
  389. ##        for k in FusionEngineTable:
  390. ##          t = FusionWeight(k)
  391. ##          p = FusionWeightPoly(k)
  392. ##          d = t - p
  393. ##          if d > .01:
  394. ##            print(k, t, p, d)
  395.  
  396.         #print (weight)
  397.         return weight
  398.         print('''WARNING! This Engine Rating is outside normal Rating parameters, and its weight has been extrapolated. Player discretion is advised.
  399. ''')
  400.  
  401. def EngineWeight(engine_type, rating):
  402.    #Using a dictionary to match engine name to a formula
  403.    weight = FusionWeight(rating * 1.0)
  404.    #print (weight)
  405.    primitive = FusionWeight(rating * 1.2)
  406.    lithium = FusionWeight(max(MechTonnage, rating - MechTonnage))
  407.    if rating < 41:
  408.       proto = rating * (25.0/1000.0)
  409.       lithium_proto = max(MechTonnage, rating - MechTonnage) * (25.0/1000.0)
  410.    else:
  411.       proto = weight * 1.0
  412.       lithium_proto = lithium * 1.0
  413.  
  414.    #print (weight)
  415.    ed = {"Standard Fusion Engine" : max(0.5, weight * 1.0),
  416.          "Light Fusion Engine"    : max(0.5, RoundUpHalf(weight * .75)),
  417.          "XL Fusion Engine"       : max(0.5, RoundUpHalf(weight * .50)),
  418.          "XXL Fusion Engine"      : max(0.5, RoundUpHalf(weight / 3.0)),
  419.          "Compact Fusion Engine"  : max(1.0, RoundUpHalf(weight * 1.5)),
  420.          "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!
  421.          "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!
  422.          "Fission Engine"  : max(5.0, RoundUpHalf(weight * float(7.0/4.0))), #Only 5 Heat Sinks. Illegal in the Inner Sphere, and expensive.
  423.          "Lithium-Fusion Engine"  : max(0.5, RoundUpHalf(lithium)), #Non-Canon. 10 Heat Sinks. Increases a mech's Walking Speed by 1.
  424.          "Primitive Fusion Engine"  : max(0.5, RoundUpHalf(primitive)), #Multiplies engine rating by 1.2, but does not increase performance.
  425.          "Primitive Internal Combustion Engine (ICE)"  : max(1.0, RoundUpHalf(primitive * 2.0)), #Multiplies engine rating by 1.2, but does not increase performance.
  426.          "Primitive Fuel Cell"  : max(1.0, RoundUpHalf(primitive * 1.2)), #Multiplies engine rating by 1.2, but does not increase performance.
  427.          "Primitive Fission Engine"  : max(5.0, RoundUpHalf(primitive * (7.0/4.0))), #Multiplies engine rating by 1.2, but does not increase performance.
  428.          "ProtoMech Engine" : RoundUpFractional((proto * 1000.0)/1000.0),
  429.          "ProtoMech XL Lithium-Fusion Engine" : RoundUpFractional(max(0.013, lithium_proto/2)),
  430.          "Battery (Tech Level C)" : RoundUpFractional(weight * 1.5),
  431.          "Battery (Tech Level D)" : RoundUpFractional(weight * 1.2),
  432.          "Battery (Tech Level E)" : weight,
  433.          "Battery (Tech Level F)" : RoundUpFractional(weight * 0.8),
  434.          "Solar (Tech Level C)" : RoundUpFractional(weight * 5),
  435.          "Solar (Tech Level D)" : RoundUpFractional(weight * 4.5),
  436.          "Solar (Tech Level E)" : RoundUpFractional(weight * 4),
  437.          "Solar (Tech Level F)" : RoundUpFractional(weight * 3.5),
  438.          "No Engine" : 0,
  439.          }
  440.    return ed[engine_type]
  441.  
  442. if EngineType in {"Lithium-Fusion Engine"}:
  443.     LithiumBatteryRating = max(MechTonnage, rating - MechTonnage)
  444. elif EngineType in {"Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell," "Primitive Fission Engine"}:
  445.     LithiumBatteryRating = RoundUp5(rating * 1.2)
  446. else:
  447.     LithiumBatteryRating = rating
  448.  
  449. #Cockpits
  450. def PromptCockpitType():
  451.     """Ask the user for their desired cockpit type, then return it."""
  452.     #cockpit list:
  453.     if LithiumBatteryRating < 401:
  454.             cl = ["Standard", "Small", "Armored", "Torso", "Interface", "Drone Operating System"]
  455.     elif LithiumBatteryRating > 400:
  456.           cl = ["Standard", "Small", "Armored", "Interface", "Drone Operating System"]
  457.     print("Cockpit types:");
  458.     for i in range(0, len(cl)):
  459.         print("  (%d) %s"%(i, cl[i]));
  460.     num = ask('''
  461. Please select your desired Cockpit type''', 0, len(cl)-1);
  462.     print();
  463.     return cl[num]
  464.  
  465. def CockpitWeight(cockpit_type):
  466.    #Using a dictionary to match cockpit name to a formula
  467.    cd = {
  468.         "ProtoMech": 0.5,
  469.         "UltraHeavy ProtoMech": 0.75,
  470.         "Drone Operating System": max(1.0, RoundUpHalf((MechTonnage/10.0) + 0.5)),
  471.         "Small": 2.0,
  472.         ("Standard", "AutoMech", "Industrial", "Armored Small"): 3.0,
  473.         ("Armored", "Torso", "Tripod", "Interface", "SuperHeavy"): 4.0,
  474.         ("Primitive", "Primitive Industrial", "SuperHeavy Tripod"): 5.0
  475.         }
  476. #   return ed[cockpit_type]
  477.    for key in cd:
  478.         if cockpit_type in key:
  479.              return cd[key]
  480.  
  481. #Skeletons
  482. def PromptInternalType():
  483.    """Ask the user for their desired internal structure type, then return it."""
  484.    #internal structure list:
  485.    if MechTonnage < 101:
  486.         if EngineType in {"Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine"}:
  487.             il = ["Primitive Standard", "Primitive Industrial"]
  488.         else:
  489.             il = ["Standard", "Endo Steel", "Composite", "Hybrid", "Reinforced", "Industrial"]
  490.    elif MechTonnage > 100:
  491.         il = ["SuperHeavy Standard", "SuperHeavy Hybrid", "SuperHeavy Endo Steel", "SuperHeavy Reinforced", "SuperHeavy Industrial"] #You have balls if you pick that last one.
  492.    print('''Internal Structure types:
  493. (Rule of thumb: Endo Steel is lighter, but bulkier, than Standard.)''')
  494.    for i in range(0, len(il)):
  495.        print("  (%d) %s"%(i, il[i]))
  496.    num = ask('''
  497. Please select your desired Internal Structure type''', 0, len(il)-1);
  498.    print();
  499.    return il[num]
  500.  
  501. def InternalWeight(internal_type):
  502.     #Using a dictionary to match internal structure name to a formula
  503.     isd = {
  504.          ("Standard", "Primitive Standard", "SuperHeavy Endo Steel", "ProtoMech"): RoundUpHalf(MechTonnage/10.0),
  505.          ("Endo Steel", "Composite"): RoundUpHalf(MechTonnage/20.0),
  506.          "Hybrid": RoundUpHalf(MechTonnage*7.5/100.0),
  507.          ("Reinforced", "Industrial", "Primitive Industrial", "SuperHeavy Standard") : RoundUpHalf(MechTonnage/5.0),
  508.          "SuperHeavy Hybrid": RoundUpHalf(MechTonnage*15.0/100.0),
  509.          ("SuperHeavy Industrial", "SuperHeavy Reinforced"): RoundUpHalf(MechTonnage/2.5),
  510.          }
  511. #   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.
  512.     for key in isd:
  513.        if internal_type in key:
  514.            return isd[key]
  515.  
  516. if MechTonnage > 100:
  517.      InternalType = PromptInternalType()
  518.      CockpitType = ("SuperHeavy")
  519.      if MechType in ["Tripod"]:
  520.           MechType = ("SuperHeavy Tripod")
  521.      else:
  522.           Mechtype = ("SuperHeavy" + CurrentMechType)
  523. elif EngineType in ["ProtoMech Engine"]:
  524.      InternalType = ("ProtoMech")
  525.      MechType = ("ProtoMech")
  526.      if MechTonnage < 10:
  527.           CockpitType = ("ProtoMech")
  528.      elif MechTonnage > 9:
  529.           CockpitType = ("UltraHeavy ProtoMech")
  530. else:
  531.      MechType = CurrentMechType
  532.      InternalType = PromptInternalType()
  533.      if EngineType in {"Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine"}:
  534.          CockpitType = ("Primitive")
  535.      else:
  536.          CockpitType = PromptCockpitType()
  537.  
  538. #Gyros
  539. def PromptGyroType():
  540.     """Ask the user for their desired gyro type, then return it."""
  541.     #gyro list:
  542.     if LithiumBatteryRating < 401 or EngineType in ["Compact Fusion Engine"]:
  543.           gl = ["Standard", "XL (Extra-Light)", "Compact", "Heavy-Duty",]
  544.     elif EngineType in {"Lithium-Fusion Engine"} and rating < 501:
  545.           gl = ["Standard", "XL (Extra-Light)", "Compact", "Heavy-Duty",]
  546.     elif LithiumBatteryRating > 400 or CockpitType in ["Torso"]:
  547.           if EngineType not in ["Compact Fusion Engine"]:
  548.               gl = ["Standard", "Compact", "Heavy-Duty",]
  549.     print("Gyro types:")
  550.     for i in range(0, len(gl)):
  551.         print("  (%d) %s"%(i, gl[i]))
  552.     num = ask("Please select your desired Gyro type", 0, len(gl)-1)
  553.     print();
  554.     return gl[num]
  555.  
  556. if EngineType in ["Lithium-Fusion Engine"]:
  557.     FinalRating = LithiumBatteryRating
  558. elif EngineType in ["Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine"]:
  559.     FinalRating = MechTonnage * CruisingSpeed * 1.2
  560. elif EngineType in ["ProtoMech Engine"]:
  561.     FinalRating = MechTonnage * FlankingSpeed
  562. else:
  563.     FinalRating = MechTonnage * CruisingSpeed
  564.  
  565. GyroRating = ceil(FinalRating / 100)
  566.  
  567. if MechTonnage > 100:
  568.     GyroType = ("SuperHeavy")
  569. elif EngineType in ["Primitive Fusion Engine", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell", "Primitive Fission Engine"]:
  570.     if MechTonnage < 101:
  571.          GyroType = ("Primitive")
  572.     elif MechTonnage > 100:
  573.          GyroType = ("SuperHeavy Primitive")
  574. elif CockpitType in ["ProtoMech", "UltraHeavy ProtoMech", "Interface"]:
  575.     GyroType = ("None")
  576. else:
  577.     GyroType = PromptGyroType()
  578.  
  579. def GyroWeight(gyro_type):
  580.    #Using a dictionary to match gyro name to a formula
  581.    gmass = GyroRating
  582.    gd = {
  583.         ("Standard", "Primitive") : max(1.0, gmass*1.0),
  584.         "XL (Extra-Light)" : max(1.0, RoundUpHalf(gmass/2.0)),
  585.         "Compact" : max(1.5, RoundUpHalf(gmass*1.5)),
  586.         ("Heavy-Duty", "SuperHeavy", "Primitive SuperHeavy") : max(2.0, ceil(gmass * 2.0)),
  587.         "None": (gmass*0.0),
  588.         "Armored" : (gmass + 2.0),
  589.         "Armored Compact" :  1.0 + RoundUpHalf(gmass*1.5),
  590.         "Armored XL" : 3.0 + RoundUpHalf(gmass/2.0),
  591.         "Armored Heavy-Duty" : 2.0 + (gmass*2.0),
  592.         }
  593. #   return ed[gyro_type]
  594.    for key in gd:
  595.         if gyro_type in key:
  596.              return gd[key]
  597.  
  598. #Heat Sinks
  599. def PromptHeatSinkType():
  600.     """Ask the user for their desired heat sink type, then return it."""
  601.     #heat sink list:
  602.     hsl = ["Single", "Double (Inner Sphere)", "Double (Clan)", "Laser", "Compact"]
  603.     print("Heat Sink types:");
  604.     for i in range(0, len(hsl)):
  605.         print("  (%d) %s"%(i, hsl[i]));
  606.     num = ask('''
  607. Please select your desired Heat Sink type''', 0, len(hsl)-1);
  608.     print();
  609.     return hsl[num]
  610.  
  611. if EngineType in {"Fission Engine", "Primitive Fission Engine"}:
  612.     FreeHeatSinks = 5
  613.     HeatSinkType = PromptHeatSinkType()
  614. elif EngineType in {"Fuel Cell", "Primitive Fuel Cell"}:
  615.     FreeHeatSinks = 1
  616.     HeatSinkType = PromptHeatSinkType()
  617. elif EngineType in {"Internal Combustion Engine (ICE)", "Primitive Internal Combustion Engine (ICE)"}:
  618.     FreeHeatSinks = 0
  619.     HeatSinkType = PromptHeatSinkType()
  620. elif EngineType in {"ProtoMech Engine"}:
  621.     FreeHeatSinks = 0
  622.     HeatSinkType = ("ProtoMech")
  623. else:
  624.     FreeHeatSinks = 10
  625.     HeatSinkType = PromptHeatSinkType()
  626.  
  627. if EngineType in ["ProtoMech Engine"]:
  628.      UserHeatSinks = ask("Please provide your desired quantity of ProtoMech Heat Sinks", 0, MechTonnage*4)
  629.      
  630. elif HeatSinkType in ["Compact"]:
  631.      print ("You currently have", int(FreeHeatSinks), HeatSinkType, "Heat Sinks.")
  632.      UserHeatSinks = ask("If you wish to install additional Heat Sinks, enter the quantity you wish to add", 0, floor(MechTonnage*2/3))
  633. else:
  634.      print ("You currently have", int(FreeHeatSinks), HeatSinkType, "Heat Sinks.")
  635.      UserHeatSinks = ask("If you wish to install additional Heat Sinks, enter the quantity you wish to add", 0, MechTonnage)
  636.      
  637. TotalHeatSinks = UserHeatSinks + FreeHeatSinks
  638.  
  639. if UserHeatSinks > 0:
  640.      print("You now have", TotalHeatSinks, "total", HeatSinkType, "HeatSinks.")
  641.  
  642. if EngineType in {"ProtoMech Engine"}:
  643.         HeatSinkWeight = RoundUpFractional(UserHeatSinks/4.0)
  644.         IntegralHeatSinks = int(TotalHeatSinks)
  645.         HeatSinkSlots = 0
  646. elif HeatSinkType in {"Compact"}:
  647.         HeatSinkWeight = RoundUpHalf(UserHeatSinks * 1.5)
  648.         IntegralHeatSinks = int(min(TotalHeatSinks, floor(LithiumBatteryRating/12.5)))
  649. else:
  650.         HeatSinkWeight = UserHeatSinks * 1.0
  651.         IntegralHeatSinks = int(min(TotalHeatSinks, floor(LithiumBatteryRating/25.0)))
  652.  
  653. PodHeatSinks = TotalHeatSinks - IntegralHeatSinks
  654.  
  655. if MechTonnage < 101:
  656.      if HeatSinkType in ["Single", "Liquid Metal"]:
  657.          HeatSinkSlots = PodHeatSinks
  658.      elif HeatSinkType in ["Double (Inner Sphere)", "Double (ProtoType)"]:
  659.          HeatSinkSlots = PodHeatSinks * 3
  660.      elif HeatSinkType in ["Double (Clan)", "Double (Star Empire)", "Laser"]:
  661.          HeatSinkSlots = PodHeatSinks * 2
  662.      elif HeatSinkType in ["Compact"]:
  663.          HeatSinkSlots = ceil(PodHeatSinks/2)
  664.      elif HeatSinkType in ["ProtoMech"]:
  665.          HeatSinkSlots = 0
  666.  
  667. elif MechTonnage > 100:
  668.      if HeatSinkType in ["Single"]:
  669.          HeatSinkSlots = ceil(PodHeatSinks/2)
  670.      elif HeatSinkType in ["Double (Inner Sphere)"]:
  671.          HeatSinkSlots = ceil(PodHeatSinks * 1.5)
  672.      elif HeatSinkType in ["Double (Clan)", "Laser"]:
  673.          HeatSinkSlots = PodHeatSinks
  674.      elif HeatSinkType in ["Compact"]:
  675.          HeatSinkSlots = ceil(PodHeatSinks/4)
  676.  
  677. #Podspace
  678. skeleton = InternalWeight(InternalType)
  679. tripod = RoundUpHalf(skeleton * 1.1)
  680. cockpit = CockpitWeight(CockpitType)
  681. gyro = GyroWeight(GyroType)
  682. EngineTonnage = EngineWeight(EngineType, rating)
  683.  
  684. ##if MechType in ["Combat Vehicle", "Support Vehicle"]:
  685. ##     if EngineType in ["Internal Combustion Engine (ICE)", "Fuel Cell", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell"]:
  686. ##          VehicleEngine = EngineTonnage
  687. ##     else:
  688. ##          if MechType in ["Combat Vehicle"]:
  689. ##               VehicleEngine = RoundUpHalf(EngineTonnage * 1.5)
  690. ##          elif MechType in ["Support Vehicle"]:
  691. ##               VehicleEngine = RoundUpFractional(EngineTonnage * 1.5)
  692. ##else:
  693. ##     if EngineType in ["Internal Combustion Engine (ICE)", "Fuel Cell", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell"]:
  694. ##          VehicleEngine = EngineTonnage
  695. ##     else:
  696. ##          VehicleEngine = RoundUpHalf(EngineTonnage * 1.5)
  697.  
  698. if IsVehicle is True:
  699.     if EngineType not in ["Internal Combustion Engine (ICE)", "Fuel Cell", "Primitive Internal Combustion Engine (ICE)", "Primitive Fuel Cell"]:
  700.           VehicleEngine = EngineTonnage * 1.5
  701. else:
  702.     VehicleEngine = EngineTonnage
  703.  
  704. print()
  705.  
  706. #DEBUG READOUT - UNCOMMENT IF CRASHES OCCUR
  707. print('''DEBUG READOUT - RECOMMENT IF CRASHES NO LONGER OCCUR:
  708. MechTonnage =''', MechTonnage,'''
  709. EngineTonnage =''', EngineTonnage,'''
  710. VehicleEngine =''', VehicleEngine,'''
  711. FinalRating =''', FinalRating,'''
  712. skeleton =''', skeleton,'''
  713. tripod =''', tripod,'''
  714. gyro =''', gyro,'''
  715. cockpit =''', cockpit,'''
  716. HeatSinkWeight =''', HeatSinkWeight,'''
  717.  
  718. Press ENTER to confirm, or Ctrl+C to abort.''')
  719. input()
  720. #^^^^^^^^^^^^^
  721.        
  722. if MechType in ["Tripod", "SuperHeavy Tripod"]:
  723.      podspace = RoundDownHalf(MechTonnage - EngineTonnage - tripod - gyro - cockpit - HeatSinkWeight)
  724. elif EngineType in ["ProtoMech Engine", "ProtoMech XL Lithium-Fusion Engine"] or MechType in ["ProtoMech", "Quad ProtoMech", "Glider ProtoMech"]:
  725.      podspace = RoundDownFractional(MechTonnage - EngineTonnage - skeleton - gyro - cockpit - HeatSinkWeight)
  726. elif MechType in ["Combat Vehicle"]:
  727.      podspace = RoundDownHalf(MechTonnage - VehicleEngine - skeleton - gyro - cockpit - HeatSinkWeight)
  728. elif MechType in ["Support Vehicle"]:
  729.      podspace = RoundDownFractional(MechTonnage - VehicleEngine - skeleton - gyro - cockpit - HeatSinkWeight)
  730. else:
  731.      podspace = RoundDownHalf(MechTonnage - EngineTonnage - skeleton - gyro - cockpit - HeatSinkWeight)
  732.  
  733. if VehicleEngine > MechTonnage:
  734.     print('''WARNING! Your engine is heavier than the mech!
  735. WARNING! Your mech has negative podspace, and is Overweight!''')
  736.     input()
  737. elif podspace < 0.0:
  738.     print("WARNING! Your mech has negative podspace, and is Overweight!")
  739.     input()
  740. else:
  741.     print("ALL SYSTEMS NOMINAL.")
  742.     input()
  743.  
  744. #Finalize
  745. if MechName in {"BattleMech", "Ultralight BattleMech", "Light BattleMech", "Medium BattleMech", "Heavy BattleMech", "Assault BattleMech", "SuperHeavy BattleMech"}:
  746.      if InternalType in {"Industrial"}:
  747.          if MechClass in {"Ultralight", "Light", "Medium", "Heavy", "Assault", "SuperHeavy"}:
  748.               MechName = (MechClass + " IndustrialMech")
  749.      if InternalType in {"Primitive Standard"}:
  750.          if MechClass in {"Ultralight", "Light", "Medium", "Heavy", "Assault", "SuperHeavy"}:
  751.               MechName = ("Primitive " + MechClass + " BattleMech")
  752.      if InternalType in {"Primitive Industrial"}:
  753.          if MechClass in {"Ultralight", "Light", "Medium", "Heavy", "Assault", "SuperHeavy"}:
  754.               MechName = ("Primitive " + MechClass + " IndustrialMech")
  755.      elif MechClass in {"ProtoMech", "UltraHeavy ProtoMech"}:
  756.           MechName = MechClass
  757.           MechType = ("ProtoMech")
  758.      elif CockpitType in {"AutoMech"}:
  759.           MechName = (MechClass + " AutoMech")
  760.           MechType = ("AutoMech")
  761.  
  762. #Technical Readout
  763. fib(FinalRating)
  764. print('''
  765. Your''', MechName, "has a Cruising MP of", CruisingSpeed, "hexes per turn, and a Flanking MP of", FlankingSpeed, '''hexes per turn.
  766. This speed is provided by a''', FinalRating,'rated', EngineType, "weighing", EngineTonnage, "metric tons.");
  767.  
  768. if EngineType in ["ProtoMech Engine", "ProtoMech XL Lithium-Fusion Engine"]:
  769.      print("You have", TotalHeatSinks, HeatSinkType, "Heat Sinks weighing", HeatSinkWeight, "metric tons.")
  770.  
  771. else:
  772.      print("You have", TotalHeatSinks, HeatSinkType, "Heat Sinks weighing", HeatSinkWeight, "metric tons, of which", IntegralHeatSinks, '''of them are Integral,
  773. and out of those''', TotalHeatSinks, HeatSinkType, "Heat Sinks,",FreeHeatSinks, "of them come with the engine.")
  774.      if PodHeatSinks > 0:
  775.           print ("The remaining", HeatSinkType, "Heat Sinks occupy", HeatSinkSlots, "Critical Spaces in total.")    
  776.  
  777. print()
  778.  
  779. if MechType in ["Tripod"]:
  780.      print("The Mech's weight is supported by a", InternalType, "Internal Structure with a mass of", tripod, "metric tons.");
  781.  
  782. elif MechType in ["AutoMech"]:
  783.      print ("The AutoMech’s weight is supported by a", InternalType, "Internal Structure with a mass of", skeleton, "metric tons.");
  784.      
  785. elif MechType in ["ProtoMech"]:
  786.      print ("The ProtoMech’s weight is supported by a", InternalType, "Internal Structure with a mass of", skeleton*1000, "kilograms.");
  787.  
  788. else:
  789.      print ("The Mech’s weight is supported by a", InternalType, "Internal Structure with a mass of", skeleton, "metric tons.");
  790.  
  791. if GyroType in ["None"] and MechTonnage > 9:
  792.      print("The pilot is cocooned in an", CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  793.  
  794. elif GyroType in ["None"] and MechTonnage <10:
  795.      print("The pilot is cocooned in a", CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  796.  
  797. elif CockpitType in ["Drone Operating System", "AutoMech"]:
  798.      if GyroType in ["XL (Extra-Light)"]:
  799.           print ("and keeping the", MechName, "upright is an", GyroType, "Gyro weighing", gyro, "metric tons.")
  800.      else:
  801.           print ("and keeping the", MechName, "upright is a", GyroType, "Gyro weighing", gyro, "metric tons.")
  802.      
  803.      if CockpitType in ["Drone Operating System"]:
  804.           print ("The brains of this machine consist of a", CockpitType, "weighing", cockpit, "metric tons.")
  805.      
  806.      elif CockpitType in ["AutoMech"]:
  807.           print ("The brains of this machine consist of an", CockpitType, "computer weighing", cockpit, "metric tons.")
  808.  
  809. elif GyroType in ["SuperHeavy"]:
  810.      print ("and the only thing stopping your", MechName, "from toppling over is a monstrous", GyroType, "Gyro weighing", gyro, '''metric tons.
  811. The pilots are 'efficiently' huddled together in a''', CockpitType, "Cockpit, weighing a massive", cockpit, "metric tons.");
  812.  
  813. elif GyroType in ["XL (Extra-Light)"]:
  814.      print ("and keeping the", MechName, "upright is an", GyroType, "Gyro weighing", gyro, '''metric tons.
  815. The pilot sits ’comfortably’ in a''', CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  816.  
  817. else:
  818.      print("and keeping the", MechName, "upright is a", GyroType, "Gyro weighing", gyro, '''metric tons.
  819. The pilot sits 'comfortably' in a''', CockpitType, "Cockpit, which weighs", cockpit, "metric tons.");
  820.  
  821. print('''
  822. The''', MechName, "has an average walking speed of", kph(CruisingSpeed), "km/h, and a maximum running speed of", kph(FlankingSpeed), "km/h.");
  823.  
  824. if podspace > 0.0:
  825.    print("The", MechName, "has", podspace, '''tons of Pod Space available to mount weapons, armor and equipment.
  826. ''')
  827.    fib(podspace)
  828.    print();
  829. else:
  830.    print("Oh, stravag! It would seem that your engineers left your", MechName, "with only", podspace, '''tons of Pod Space,
  831. and thanks to their incompetence, the mech cannot mount any weapons, armor or equipment!
  832.  
  833. +++ MELON MELON MELON. OUT-OF-CHEESE ERROR. REDO FROM START. +++''')
  834.    input()
  835.  
  836. ## Closing sequence.
  837. print('''Please visit http://www.sarna.net for more information on these terms and components.
  838. If you have any questions or feedback about the software, such as feature requests or bug reports,
  839. if you need technical support, or if you would simply like to contribute to development, then please visit:
  840. https://tinyurl.com/Sarna-Forums-MechEngine
  841. ''')
  842. gettime()
  843. input("Shutdown Sequence: Initiated.") #Change this line to a menu option once Restart has been implemented.
  844. print ("Shutting Down.")
  845.  
  846. ## End of Line.
  847.  
  848. #Contribution Links:
  849. #     https://pastebin.com/VfJ5Sz7Y
  850. #     https://docs.google.com/document/d/1A6Y79pelpzdRg00DHcZNO3tyrdvthFSaU4tihSvGkE8/edit?usp=sharing
Add Comment
Please, Sign In to add comment