Advertisement
SumZer00

HOI4 Summer Beta Combat Width Meta

Jul 6th, 2023
1,021
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.35 KB | Software | 0 0
  1. import matplotlib.pyplot as plt
  2. import matplotlib.ticker as ticker
  3. import numpy as np
  4.  
  5. map_type = int(input("0 for general, 1 for across the rhine, 2 for sonnenblum, 3 for custom, 4 for no weighting: "))
  6.  
  7. min_width = 6 # minimum width checked
  8. max_width = 50 # maximum width checked
  9.  
  10. total_provinces = 2667
  11.  
  12. """
  13. # IS COMBINED WITH JUNGLE
  14. forest_base = 84
  15. forest_increase = 42
  16.  
  17. hills_base = 80
  18. hills_increase = 40
  19.  
  20. marsh_base = 78
  21. marsh_increase = 26
  22.  
  23. mountain_base = 75
  24. mountain_increase = 25
  25.  
  26. # IS COMBINED WITH DESERT
  27. plains_base = 90
  28. plains_increase = 45
  29.  
  30. urban_base = 96
  31. urban_increase = 32
  32. """
  33.  
  34. forest_base = 76
  35. forest_increase = 40
  36.  
  37. hills_base = 72
  38. hills_increase = 36
  39.  
  40. jungle_base = 74 #new
  41. jungle_increase = 34 #new
  42.  
  43. marsh_base = 68
  44. marsh_increase = 22
  45.  
  46. mountain_base = 65
  47. mountain_increase = 25
  48.  
  49. # IS COMBINED WITH DESERT
  50. plains_base = 82
  51. plains_increase = 49
  52.  
  53. urban_base = 86
  54. urban_increase = 28
  55.  
  56.  
  57. # You can change the weights as you like. Currently they are based on fabricensis's document and are therefore written as a divison.
  58. # Right now they are set up to be a sum of 1 for general scope, but that should not be necessary.
  59. # You may get wierd results if you have really large numbers tho (computer rounding errors).
  60.  
  61.  
  62.  
  63. if map_type == 0: #General
  64.     forest_weight = 527/total_provinces # 703 - jungle
  65.     hills_weight = 401/total_provinces
  66.     jungle_weight = 176/total_provinces # 1/4 of 703
  67.     marsh_weight = 51/total_provinces
  68.     mountain_weight = 297/total_provinces
  69.     plains_weight = 1094/total_provinces
  70.     urban_weight = 121/total_provinces
  71.    
  72. elif map_type == 1: #Across the Rhine   from Commanders in Conflict (CiC) - Speed5 Mod
  73.     forest_weight = 46/125 # dividing is actually not necessary
  74.     hills_weight = 18/125
  75.     jungle_weight = 0
  76.     marsh_weight = 2/125
  77.     mountain_weight = 3/125
  78.     plains_weight = 45/125
  79.     urban_weight = 11/125
  80.    
  81. elif map_type == 2: #Sonnenblum   from Commanders in Conflict (CiC) - Speed5 Mod
  82.     forest_weight = 0
  83.     hills_weight = 27
  84.     jungle_weight = 0
  85.     marsh_weight = 0
  86.     mountain_weight = 17
  87.     plains_weight = 57
  88.     urban_weight = 4
  89.      
  90. elif map_type == 3: #Custom
  91.     forest_weight = 1
  92.     hills_weight = 0
  93.     jungle_weight = 0
  94.     marsh_weight = 0
  95.     mountain_weight = 0
  96.     plains_weight = 1
  97.     urban_weight = 0
  98.  
  99. elif map_type == 4: #No Weights
  100.     forest_weight = 1
  101.     hills_weight = 1
  102.     jungle_weight = 1
  103.     marsh_weight = 1
  104.     mountain_weight = 1
  105.     plains_weight = 1
  106.     urban_weight = 1
  107.  
  108. # Weights are from fabricensis, remember that forest & jungle and plains & desert are combined when changing the weights
  109.  
  110.  
  111.  
  112. # These could probably be set up a lot smarter:
  113. terrain_weight_list = [forest_weight, hills_weight, jungle_weight, marsh_weight, mountain_weight, plains_weight, urban_weight]
  114. terrain_list = [forest_base, hills_base, jungle_base, marsh_base, mountain_base, plains_base, urban_base]
  115. terrain_increase_list = [forest_increase, hills_increase, jungle_increase, marsh_increase, mountain_increase, plains_increase, urban_increase]
  116.  
  117.  
  118. # weights for the different directions. Not too difficult to add more.
  119. one_direction_weight = 1
  120. two_direction_weight = 2
  121. three_direction_weight = 1
  122.  
  123. direction_weight_list = [one_direction_weight, two_direction_weight, three_direction_weight]
  124.  
  125. """
  126. # removing empty weights from the terrain list
  127. newlist = []
  128. for i in terrain_weight_list:
  129.    if i != 0:
  130.        newlist.append(i)
  131.  
  132. terrain_weight_list = newlist
  133. """
  134.  
  135.  
  136. def width_modifier(cw): #cw = combat width
  137.    
  138.     total_weight = 0
  139.     current_modifier = 0 # used for the modifier of the unit later
  140.     test_number = 0 # number of the current test (basically n)
  141.    
  142.    
  143.    
  144.     for terrain in range(len(terrain_weight_list)): # cycles through every terrain
  145.        
  146.         # terrain weight variable
  147.         current_terrain_weight = terrain_weight_list[terrain]
  148.            
  149.            
  150.         if current_terrain_weight > 0:
  151.             for flanking_directions in range(len(direction_weight_list)): # cycles through 1, 2 or 3 direction attack
  152.                 current_width = 0 # current width occupied by our troops in the battle
  153.                 battle_width = (terrain_list[terrain] + terrain_increase_list[terrain]*flanking_directions) #combat width of the battle
  154.                 divisions_used = 0 # number of divions in combat
  155.                
  156.                
  157.                 # creates weight variable as flanking direction weight
  158.                 current_weight = direction_weight_list[flanking_directions]
  159.                    
  160.                 # adds terrain weight to the variable
  161.                 current_weight *= current_terrain_weight
  162.            
  163.            
  164.            
  165.                 # Calculates how many divisions to be used in the combat
  166.                 while current_width+cw < (battle_width*1.22) and not current_width >= battle_width:
  167.                     current_width += cw
  168.                     divisions_used += 1
  169.                    
  170.                 # creates modifier penalty used in the final calculation to find modifier/effectiveness
  171.                 modifier_penalty = 0
  172.                
  173.    
  174.    
  175.    
  176.                 # if it doesnt fit perfect, then calculate all penalties to the total modifier
  177.                 if battle_width % cw != 0:
  178.                    
  179.                     if current_width > battle_width: # checks for any overstacked combat width
  180.                         overstacked_width = current_width - battle_width
  181.                         modifier_penalty += overstacked_width*0.015 #adds the overstacked width to the modifier penalty
  182.                        
  183.                         #modifier_penalty += (current_width/battle_width) * ((-1.5*(current_width/battle_width)+2.5)**2) # attempting to replicate fabricensis's calculation
  184.                    
  185.                    
  186.                     elif current_width < battle_width:
  187.                         modifier_penalty += (battle_width-current_width)*0.01 #adds the unused width as modifier penalty
  188.                        
  189.                        
  190.                        
  191.                        
  192.                        
  193.                 # calculates penalty for any overstacked divisions
  194.                 if divisions_used > (8 + 4*flanking_directions):
  195.                     overstacked_divisions = divisions_used-(8 + 4*flanking_directions)
  196.                    
  197.                     if overstacked_divisions * 0.02 < 0.99: # makes sure to only do the calculation while the total is under 99% (because that is the limit for stacking penalty)
  198.                         modifier_penalty += overstacked_divisions * 0.02 #add the overstacked divisons to the modifier penalty
  199.                     else: # set the overstacking penalty to 99% if the actual value is over
  200.                         modifier_penalty = 0.99
  201.                    
  202.                
  203.                
  204.                
  205.                
  206.                 # actual calculation for adding the modifier for this cycle into the average
  207.                 # one is commented out because of their different methods of calculating the average
  208.                
  209.                 # first method:
  210.                 #current_modifier = (current_modifier*test_number + ((1-modifier_penalty)*current_weight)) / (test_number + current_weight) # adjusts the current modifier average to add 1 - modifier penalties
  211.                 #test_number += 1
  212.                
  213.         #return current_modifier
  214.        
  215.        
  216.                 # second method:
  217.                 # i currently prefer this method since its more straight forward than trying to add the average along the way
  218.                 # dont change unless you know what you are doing
  219.                 current_modifier += (1-modifier_penalty)*current_weight # adjusts the current modifier to add 1 - modifier penalties, will later divide by total to make average
  220.                    
  221.                 total_weight += current_weight
  222.     return current_modifier/total_weight
  223.    
  224.    
  225.  
  226.  
  227. # Runs through all widths between 6 and 50 and makes them into y values of a future plot
  228. y_markers = []
  229. for y in range(min_width, max_width+1):
  230.     y_markers.append(width_modifier(y)*100)
  231.  
  232.  
  233. ### Plot Customization
  234. fig, ax = plt.subplots()
  235. ax.plot(np.arange(6,51), y_markers)
  236. plt.rcParams['figure.dpi'] = 300
  237.  
  238. # Grid types and colors
  239. ax.grid(color="black", which="major", linestyle="solid", linewidth = "1.1")  
  240. ax.grid(color="slategray", which="minor", linestyle="solid", linewidth = "0.2")  
  241.  
  242. # Ticks
  243. ax.xaxis.set_major_locator(ticker.MultipleLocator(5))
  244. ax.xaxis.set_minor_locator(ticker.MultipleLocator(1))
  245. ax.yaxis.set_major_locator(ticker.MultipleLocator(5))
  246. ax.yaxis.set_minor_locator(ticker.MultipleLocator(1))
  247.  
  248. # Limt
  249. ax.set_xlim(6, 50)
  250. ax.set_ylim(75, 100)
  251. plt.xlabel("Combat Width")
  252. plt.ylabel("Average Effectiveness")
  253. plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%.2f %%'))
  254.  
  255. plt.savefig('cw_plot.png')
  256.  
  257. # Title
  258. if map_type != 4:
  259.     plt.title("Width Modifier with Terrain Weights")
  260. else:
  261.     plt.title("Width Modifier without Terrain Weights")
  262.  
  263.  
  264. plt.show()
  265.  
  266.  
  267. # Manual width checking
  268. print("Input combat width to check exact value. This will disappear after 3 uses.")
  269. for i in range(3):
  270.     print(3-i, "use(s) left")
  271.     test_width = float(input(""))
  272.     print(round(width_modifier(test_width),5))
  273.    
Tags: hoi4
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement