Anyway, your code seems mostly right, however there is one extra thing which is that hit strength and damage must be two different things. This is because for beams we have hit strength = dps / 2 but damage = beam ticks(adjusted for chargeup/chargedown)*dps/beam tick. We get the adjusted beam ticks from the hit time series function for each second. And, I'm not an expert on armor, but I think Vanshilar said earlier that cells with 0 armor can contribute to minimum armor even if pooled armor is not yet at the minimum armor threshold so that affects the order of sum and max operations. Vanshilar, can you confirm if I have this right?
Sorry, I haven't had time to look through the code, but I gave the mathematical operations for armor calculation
here.
If you assume that shots are only going to hit along a single row, then you only need a one-dimensional vector of size 1 x (N-4) representing the probability distribution of which cells the shots may hit. That's what I called the "hittable armor cells". The actual armor grid that you have to simulate will have dimensions 5 x N. (I define N as the width of the armor grid you have to simulate, but some have defined N as the width of the hittable cells, which is 4 less; either is fine as long as it's kept consistent.)
You only have to pool the armor values for the cells in that vector, since those are the only ones that can be hit. The pooling will be that for each hittable cell, add the armor contributions of the cells around it, then if this sum is less than 5% of the base armor rating, it'll get to count as 5% of the base armor rating when calculating the hitstr/(hitstr+armor) damage reduction. This 5% minimum armor doesn't apply anywhere else.
So in terms of code, I'd expect it to be something like, calculate the pooled armor values (reducing the 5 x N armor matrix into a 1 x (N-4) vector), then just say armor_for_dam_reduction_calc (vector) = max(pooled armor values (vector), 0.05*base armor rating (scalar)). From there, the damage reduction multiplier is just hit_strength/(hit_strength + armor_for_dam_reduction_calc), noting that hit_strength is a scalar and armor_for_dam_reduction_calc is a one-dimensional vector. This vector then needs to have a max(vector, 0.15) applied to it due to 15% minimum damage. (Although I would recommend having that 0.15 as a parameter instead since it could be 0.10 if the target has Polarized Armor.) Then multiply this vector by the weapon damage, then dot with the probability distribution (which is a vector of the same size), then you spread out the damage into a 5 X N matrix, then you apply it to the armor matrix. All that is done in the Excel spreadsheet I posted.
I *think* it should be possible to vectorize the calculations with the armor matrix being pooled into a single hittable cell vector, and then the damage from the vector being spread out into the armor matrix again, by using the kth dimension and creating a matrix of size 5 x N x (N-4), which is just the 5 x 5 armor distribution (with the 1/15's and the 1/30's) being offset by 1 at each kth index. (Mentally, I have the image of books being offset when stacked, like
here.) Then you can multiply the armor grid (N-4) times in the kth direction, dot it with this box, then sum along the ith and jth dimensions, and the resulting 1 x (N-4) vector will be the pooled armor values for the hittable cells. The same process in reverse to spread out the damage after it's calculated, from the 1 x (N-4) vector to the 5 x N armor matrix. This removes the need to cycle through the calculations for each hittable armor cell, but I'm not sure if it's 1) easily understandable and 2) actually faster in practice. It's basically just a convolution and using the kth index to do it. (It might need to be divided by the scalar (N-4) or something to make it work out mathematically, I'm not sure right now since I don't have the time to work it out.)