Advertisement
Guest User

Untitled

a guest
Oct 12th, 2023
3,755
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 12.50 KB | Gaming | 0 0
  1. import matplotlib.pyplot as plt
  2. import matplotlib.ticker as ticker
  3. import numpy as np
  4. import matplotlib
  5.  
  6. print("Custom values have to be added into the program code itself. It should be relatively easy to find (line 59).")
  7. map_type = 0
  8.  
  9. min_width = 5 # minimum width checked
  10. max_width = 50 # maximum width checked
  11.  
  12. total_provinces = 2667 # Used for general scope calculation
  13.  
  14. # Forested is an umbrella-term for forested terrains
  15. # It combines jungle and forest
  16. forested_base = 60
  17. forested_increase = 30
  18.  
  19. # Harshlands is an umbrella-term for the harshest terrains
  20. # It combines Mountains and Marshes
  21. harsh_base = 50
  22. harsh_increase = 25
  23.  
  24. # Steppe is an umbrella-term for all open landscapes.
  25. # It combines Plains, Hills, and Desert
  26. steppe_base = 70
  27. steppe_increase = 35
  28.  
  29. urban_base = 80
  30. urban_increase = 40
  31.  
  32.  
  33. # You can change the weights as you like. Currently map_type = 0 are based on fabricensis's document and are therefore written as a divison.
  34. # Right now they are set up to be a sum of 1 for general scope, but that should not be necessary.
  35. # You may get wierd results if you have really large numbers tho (computer rounding errors).
  36.  
  37.  
  38. if map_type == 0: #General
  39.     forested_weight = 703/total_provinces
  40.     harsh_weight = 348/total_provinces
  41.     steppe_weight = 1495/total_provinces
  42.     urban_weight = 121/total_provinces
  43.    
  44. elif map_type == 1: #Across the Rhine   from Commanders in Conflict (CiC) - Speed5 Mod
  45.     forested_weight = 46
  46.     harsh_weight = 5
  47.     steppe_weight = 63
  48.     urban_weight = 11
  49.    
  50. elif map_type == 2: #Sonnenblum   from Commanders in Conflict (CiC) - Speed5 Mod
  51.     forested_weight = 0
  52.     harsh_weight = 17
  53.     steppe_weight = 84
  54.     urban_weight = 4
  55.  
  56.  
  57. ## IF YOU WANT TO MAKE YOUR OWN SCENARIO/COMPOSITION OF TERRAINS
  58. ## YOU CAN MAKE IT HERE:
  59. ## PUT IN YOUR OWN WEIGHTS, RUN THE PROGRAM AND ENTER 3, IT WILL THEN SHOW YOUR GRAPH FOR YOUR WEIGHTS
  60. elif map_type == 3: #Custom, put whatever value you want here, too many decimals or too high values are not recommended, here'S an approx. example for barbarossa
  61.     forested_weight = 1.2
  62.     harsh_weight = 0.1
  63.     steppe_weight = 1
  64.     urban_weight = 0.1
  65.  
  66. elif map_type == 4: #north africa
  67.     forested_weight = 0
  68.     harsh_weight = 1
  69.     steppe_weight = 3
  70.     urban_weight = 0.05
  71.  
  72.  
  73. # Weights are from fabricensis, remember that certain terrains are combined because they share the same combat width modifiers
  74.  
  75.  
  76.  
  77. # These could probably be set up a lot smarter:
  78. terrain_weight_list = [forested_weight, harsh_weight, steppe_weight, urban_weight]
  79. terrain_list = [forested_base, harsh_base, steppe_base, urban_base]
  80. terrain_increase_list = [forested_increase, harsh_increase, steppe_increase, urban_increase]
  81.  
  82.  
  83. # weights for the different directions. Not too difficult to add more.
  84. one_direction_weight = 1.8
  85. two_direction_weight = 2
  86. three_direction_weight = 1
  87. four_direction_weight = 0.2
  88. direction_weight_list = [one_direction_weight, two_direction_weight, three_direction_weight, four_direction_weight]
  89.  
  90. """
  91. # removing empty weights from the terrain list
  92. newlist = []
  93. for i in terrain_weight_list:
  94.    if i != 0:
  95.        newlist.append(i)
  96.  
  97. terrain_weight_list = newlist
  98. """
  99.  
  100.  
  101. def width_modifier(cw): #cw = combat width
  102.    
  103.     total_weight = 0
  104.     current_modifier = 0 # used for the modifier of the unit later
  105.    
  106.    
  107.     for terrain in range(len(terrain_weight_list)): # cycles through every terrain
  108.        
  109.         # terrain weight variable
  110.         current_terrain_weight = terrain_weight_list[terrain]
  111.            
  112.            
  113.         if current_terrain_weight > 0:
  114.             for flanking_directions in range(len(direction_weight_list)): # cycles through 1, 2 or 3 direction attack
  115.                 current_width = 0 # current width occupied by our troops in the battle
  116.                 battle_width = (terrain_list[terrain] + terrain_increase_list[terrain]*flanking_directions) #combat width of the battle
  117.                 divisions_used = 0 # number of divions in combat
  118.                
  119.                
  120.                 # creates weight variable as flanking direction weight
  121.                 current_weight = direction_weight_list[flanking_directions]
  122.                    
  123.                 # adds terrain weight to the variable
  124.                 current_weight *= current_terrain_weight
  125.            
  126.            
  127.            
  128.                 # Calculates how many divisions to be used in the combat
  129.                 while current_width+cw <= (battle_width*1.33) and not current_width >= battle_width:   #0.33 not 0.30
  130.                     current_width += cw
  131.                     divisions_used += 1
  132.                    
  133.                 # creates modifier penalty used in the final calculation to find modifier/effectiveness, 1 meaning no penalty, 0.9 meaning 10% penalty
  134.                 modifier_penalty = 1
  135.    
  136.    
  137.    
  138.                 # if it doesnt fit perfect, then calculate all penalties to the total modifier
  139.                 if battle_width % cw != 0:
  140.                    
  141.                     if current_width > battle_width: # checks for any overstacked combat width
  142.                         overstacked_width = current_width - battle_width
  143.                         #modifier_penalty += overstacked_width*0.011 #adds the overstacked width to the modifier penalty  
  144.                        
  145.                         #modifier_penalty += (current_width/battle_width) * ((-1.5*(current_width/battle_width)+2.5)**2) # attempting to replicate fabricensis's calculation
  146.                         modifier_penalty *= current_width/battle_width*(1-overstacked_width/battle_width)  
  147.                    
  148.                    
  149.                     elif current_width < battle_width:
  150.                         modifier_penalty *= (current_width/battle_width) #adds the unused width as modifier penalty
  151.                        
  152.                        
  153.                        
  154.                 # calculates penalty for any overstacked divisions
  155.                 if divisions_used > (5 + 3*flanking_directions):
  156.                     overstacked_divisions = divisions_used-(5 + 3*flanking_directions)
  157.                    
  158.                     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)
  159.                         modifier_penalty *= (1-overstacked_divisions * 0.02) #add the overstacked divisons to the modifier penalty  
  160.                     else: # set the overstacking penalty to 99% if the actual value is over
  161.                         modifier_penalty *= 0.01
  162.                    
  163.                
  164.                 # dont change unless you know what you are doing
  165.                 current_modifier += (1-modifier_penalty)*current_weight # adjusts the current modifier to add 1 - modifier penalties, will later divide by total to make average
  166.                    
  167.                 total_weight += current_weight
  168.     return current_modifier/total_weight
  169.    
  170.    
  171.  
  172.  
  173. # Runs through all widths between 6 and 50 and makes them into y values of a future plot
  174. y_markers = []
  175. for y in range(min_width, max_width+1):
  176.     y_markers.append(width_modifier(y)*100)
  177.  
  178.  
  179. ### Plot Customization
  180. fig, ax = plt.subplots()
  181. ax.plot(np.arange(min_width,max_width+1), y_markers)
  182. plt.rcParams['figure.dpi'] = 300
  183.  
  184. for y in range(min_width, max_width+1):
  185.     print((y,round(y_markers[y-min_width],2)))
  186.  
  187. """<- Means commented out
  188. # If you want to count with decimals. Only relevant for Mass Assault doctrine or special mods
  189. # You must comment (put # in front of) the other parts out too (the ones that this code replaces)
  190. # WARNING: THIS DOES NOT LOOK PRETTY
  191. for y in np.arange(min_width, max_width+1, 0.1):
  192.    y_markers.append(width_modifier(y)*100)
  193.  
  194. x_list = np.arange(6,51,0.1)
  195. ax.plot(np.round(x_list, 2), y_markers)
  196. """
  197.  
  198. # Grid types and colors
  199. ax.grid(color="black", which="major", linestyle="solid", linewidth = "1.1")  
  200. ax.grid(color="slategray", which="minor", linestyle="solid", linewidth = "0.2")  
  201.  
  202. # Ticks
  203. ax.xaxis.set_major_locator(ticker.MultipleLocator(5))
  204. ax.xaxis.set_minor_locator(ticker.MultipleLocator(1))
  205. ax.yaxis.set_major_locator(ticker.MultipleLocator(5))
  206. ax.yaxis.set_minor_locator(ticker.MultipleLocator(1))
  207.  
  208. # Limt
  209. ax.set_xlim(min_width, max_width)
  210. ax.set_ylim(0, 15)
  211. plt.xlabel("Combat Width")
  212. plt.ylabel("Average Effectiveness")
  213. plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('%.2f %%'))
  214.  
  215. plt.savefig('cw_plot.png')
  216.  
  217. # Title
  218. if map_type == 0:
  219.     plt.title("Including Terrain Weights")
  220. elif map_type == 1:
  221.     plt.title("Across the Rhine (From CiC, Speed5 Mod)")
  222. elif map_type == 2:
  223.     plt.title("Sonnenblum (From CiC, Speed5 Mod)")
  224. elif map_type == 3:
  225.     plt.title("Using Custom Terrain Weights")
  226. else:
  227.     plt.title("Excluding Terrain Weights")
  228.  
  229.  
  230. plt.show()
  231. def width_modifier_noweight(cw,battle_width,flanking_directions): #cw = combat width
  232.     current_width = 0 # current width occupied by our troops in the battle
  233.     divisions_used = 0 # number of divions in combat
  234.    
  235.                 # Calculates how many divisions to be used in the combat
  236.     while current_width+cw <= (battle_width*1.33) and not current_width >= battle_width:   #0.33 not 0.30
  237.         current_width += cw
  238.         divisions_used += 1
  239.                    
  240.                 # creates modifier penalty used in the final calculation to find modifier/effectiveness, 1 meaning no penalty, 0.9 meaning 10% penalty
  241.     modifier_penalty = 1
  242.    
  243.    
  244.    
  245.                 # if it doesnt fit perfect, then calculate all penalties to the total modifier
  246.     if battle_width % cw != 0:
  247.                    
  248.         if current_width > battle_width: # checks for any overstacked combat width
  249.             overstacked_width = current_width - battle_width
  250.                         #modifier_penalty += overstacked_width*0.011 #adds the overstacked width to the modifier penalty  
  251.                        
  252.                         #modifier_penalty += (current_width/battle_width) * ((-1.5*(current_width/battle_width)+2.5)**2) # attempting to replicate fabricensis's calculation
  253.             modifier_penalty *= current_width/battle_width*(1-overstacked_width/battle_width)
  254.                    
  255.                    
  256.         elif current_width < battle_width:
  257.             modifier_penalty *= (current_width/battle_width) #adds the unused width as modifier penalty
  258.                        
  259.                        
  260.                        
  261.                 # calculates penalty for any overstacked divisions
  262.     if divisions_used > (5 + 3*flanking_directions):
  263.         overstacked_divisions = divisions_used-(5 + 3*flanking_directions)
  264.                    
  265.         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)
  266.             modifier_penalty *= (1-overstacked_divisions * 0.02) #add the overstacked divisons to the modifier penalty
  267.         else: # set the overstacking penalty to 99% if the actual value is over
  268.             modifier_penalty *= 0.01
  269.                    
  270.                
  271.            
  272.     return 1-modifier_penalty
  273.    
  274.    
  275.  
  276.  
  277. direction_weight_list_norm = direction_weight_list[0]+direction_weight_list[1]+direction_weight_list[2]+direction_weight_list[3]
  278. # generate some example data
  279.  
  280. #create a empty matrix in the shape of the spreadsheet
  281. matrix = np.zeros((21,max_width-min_width+1))
  282.  
  283. targetwidths = [50,75,100,125,60,90,120,150,70,105,140,175,80,120,160,200]
  284. #iterate through the first 16 columns
  285. for j in range(0,16):
  286.     for i in range(min_width,max_width+1):
  287.         matrix[j][i-min_width]=width_modifier_noweight(i,targetwidths[j],j%4)*100
  288. #iterate through the next 4 columns
  289. for j in range(0,4):
  290.     for i in range(min_width,max_width+1):
  291.         for k in range(0,4):
  292.             matrix[j+16][i-min_width]+=matrix[4*j+k][i-min_width]*direction_weight_list[k]
  293.         matrix[j+16][i-min_width]*= 1/direction_weight_list_norm
  294.  
  295. #compute the last column
  296. for i in range(min_width,max_width+1):
  297.     matrix[20][i-min_width]=width_modifier(i)*100
  298.  
  299. #slightly change to color distribution
  300. normalize = matplotlib.colors.Normalize(vmin=-3.5, vmax=10)
  301.  
  302.  
  303. plt.figure(figsize = (20,20))
  304. # plot the matrix as an image with an appropriate colormap
  305. plt.imshow(matrix.T, aspect ='auto', cmap="gist_rainbow_r", norm = normalize)
  306.  
  307. # add the values
  308. for (i, j), value in np.ndenumerate(matrix):
  309.     plt.text(i, j, "%.2f"%value, va='center', ha='center')
  310.  
  311. plt.axis('off')
  312. plt.show()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement