# MechEngine Beta (0.30.67)

May 21st, 2018
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
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.
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.
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
96.                                      inputs_dict[name]["min"],
97.                                      inputs_dict[name]["max"])
98.
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]));
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]));
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]));
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]));
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]));
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]));
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]));
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]))
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]))
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]));
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"]:
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.
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.
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.