Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Advanced search  

News:

Starsector 0.97a is out! (02/02/24); New blog post: Simulator Enhancements (03/13/24)

Pages: 1 ... 10 11 [12] 13 14 ... 32

Author Topic: Optimizing the Conquest: a Mathematical Model of Space Combat  (Read 24657 times)

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #165 on: November 24, 2022, 10:38:01 AM »

Liral, did you see the python code I posted a little earlier? Pretty sure that is correct unless you can find some errors. It is based on code that matched other results previously.

CapnHector, it looks like your armor calculation for an armor cell only use the value of that same armor cell. That sort of defeats the purpose of the armor gird. The whole idea is that adjacent cells are contributing armor? I don't really see the point in using something that is 'weird and possibly wrong in some way' when there is code that (I think) is correct?

I mean I'm not going to argue if Liral decides that way of calculating armor is better. If you tested it vs a uniform distribution and single shots then that doesn't confirm it at all, but if it produces the correct results for weapon combinations using the correct (convolved normal) distributions it is obviously the better code and formulation.

The best way to find out is just write the utility to use that code to handle actual weapons and see if the results are correct.
Logged
5 ships vs 5 Ordos: Executor · Invictus · Paragon · Astral · Legion · Onslaught · Odyssey | Video LibraryHiruma Kai's Challenge

intrinsic_parity

  • Admiral
  • *****
  • Posts: 3071
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #166 on: November 24, 2022, 10:54:58 AM »

@Thaago
Yeah, I knew there were a bunch of unnecessary temporary variables (mentioned it in an edit lol). I guess I could probably use comments to give the same level of readability, but it just feels nice.

In terms of pre-initializing the arrays outside of the functions, they would have to get passed down a significant function call stack because initializing them in the armor damage function doesn't do anything, and that get's called expected armor function too, so possibly 3-4 functions. Also, kinda difficult to do without having the rest of the higher level code yet. Another option is to try and generate a list of indices instead of a full boolean array for the inner/outer cell arrays which at least would be a much smaller temporary variable. I might try that later.

Also, all those weird idiosyncrasies are why I like to just use Julia these days lmao.

@CapHector
The distribution shouldn't matter that much because expected value doesn't care! E[f(x)] = sum(f(X)*p(X)) for X in x. f(X) doesn't care what the probability distribution is, it's all handled by p(X). All I've done is implement that E[f(x)] expression. The p(X) stuff can all be done independently. If you wanted to calculate variance, you would have a lot more trouble though.
Logged

Thaago

  • Global Moderator
  • Admiral
  • *****
  • Posts: 7174
  • Harpoon Affectionado
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #167 on: November 24, 2022, 11:01:27 AM »

Oh yeah, if a full implementation isn't running and verified to be outputting correct data (and to which an optimized version can be compared with to check integrity) then any optimization is a total waste! I haven't used Julia myself but I have a friend who really likes it for numerical work.
Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #168 on: November 24, 2022, 11:13:58 AM »

E(f(x))=sum(f(X)p(X)) for a discrete random variable undoubtedly. The main thing is what is f(x). In terms of the total damage dealt to the armor matrix around x by the shot hitting x it should be exactly correct to say that for that damage it must be d*h/(h+sum of armor around cell x)). You get to the equations I was doing by trying to figure the value of the damage distribution cell by cell around x.

Now it seems like, though, that you could just calculate the damage matrix D on the fly instead of pre-computing it. Then what you would get is this: the damage to be distributed when the armor hits central cell x, it is d*h/(h+sum of armor around cell x)) damage to distribute around the cell x. You distribute it around the cell according to the default damage distribution matrix (1/30 peripheral and 1/15 central). Then you repeat this for all central cells before reducing armor so the hit strength calc is still correct*. Then the damage distributed to the peripheral cell is exactly the same as in the D_ij in the latex, so it is completely equivalent in this way.

Conversely, to go back from the latex matrix thing to this, you must pool d*D_ij*h/(h+armor value of cell)). Now if it should happen that the pooling process is just that you reverse the D_ij distribution and sum the armor value then the two formulations are actually exactly equivalent.

Given that the two formulations appear to be equivalent when thought about in this way, then it does seem that actually your formalism is better because it is simpler and computing the probability distribution on the fly is no longer any issue now that we have the analytical distribution (remember, I started with the simulated one, so I wanted to compute it beforehand).

I'd still feel safer if you give it a whirl with a normal distribution, but having gone through this mental calculation I'm actually going to switch to supporting your version of the code. Just integrate it as the armor damage calculation into the Pythonized version of the R code I posted earlier, and if it produces the same results we're done here.

Edit: added * because it's an important point that you can't do it central cell by central cell if you subtract the armor before you are done looping over the central cells as otherwise previous subtraction affects next hit strength while in reality they don't as they are simultaneous (literally part of one hit). So make sure to store it somewhere and subtract from all cells only when done in one step. Then it's correct.
« Last Edit: November 24, 2022, 11:56:49 AM by CapnHector »
Logged
5 ships vs 5 Ordos: Executor · Invictus · Paragon · Astral · Legion · Onslaught · Odyssey | Video LibraryHiruma Kai's Challenge

Vanshilar

  • Admiral
  • *****
  • Posts: 585
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #169 on: November 24, 2022, 12:06:41 PM »

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.)
Logged

intrinsic_parity

  • Admiral
  • *****
  • Posts: 3071
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #170 on: November 24, 2022, 04:40:13 PM »

E(f(x))=sum(f(X)p(X)) for a discrete random variable undoubtedly. The main thing is what is f(x). In terms of the total damage dealt to the armor matrix around x by the shot hitting x it should be exactly correct to say that for that damage it must be d*h/(h+sum of armor around cell x)). You get to the equations I was doing by trying to figure the value of the damage distribution cell by cell around x.
f(x) is just the calculations to determine armor damage given the shot location x. Exactly what you would do if there was no probabilistic aspect. If you compute the deterministic damage done for every possible shot location, multiply the damage by the probability of each shot location, and sum, you should get the expected value.

Conversely, to go back from the latex matrix thing to this, you must pool d*D_ij*h/(h+armor value of cell)). Now if it should happen that the pooling process is just that you reverse the D_ij distribution and sum the armor value then the two formulations are actually exactly equivalent.
I think this is where you're going wrong. A single armor cell value should not appear in the denominator of that fraction by itself, it has to be the pooled value. There's no way for sum(d*h/(h+armor value of cell))) to be equivalent to d*h/(h+sum(armor value of cell))) because the expression is non-linear.

If you wanted to think about it in terms of that D matrix, you're gonna have a different armor damage reductions for each probability pk because the pooled armor value is different for each possible shot location. So I don't think you can decompose it that way (separate the probabilities from the armor calculations like that) and still be correct. To put it another way, each shot location will  have an associated p*(adjusted damage) and the damage to a specific cell would be a weighted sum of those p*dmg terms over all the relevant shot locations. The best you could do to separate the probabilities would be to take the dot product of the probability vector with the adjusted damage, but that's already what I'm doing more or less. I just calculate all the adjusted damages first and then take the dot product at the end (I actually do a loop but looping a sum of products is equivalent to a dot product).

I'd still feel safer if you give it a whirl with a normal distribution, but having gone through this mental calculation I'm actually going to switch to supporting your version of the code. Just integrate it as the armor damage calculation into the Pythonized version of the R code I posted earlier, and if it produces the same results we're done here.
I would expect the results to not exactly match what you did because I don't think the two approaches are equivalent. Should definitely test and verify as much as possible though, and I'm happy to use all the probability distribution code that exists. I tried to make it modular so it could slot into other stuff easily.
Logged

intrinsic_parity

  • Admiral
  • *****
  • Posts: 3071
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #171 on: November 24, 2022, 05:12:20 PM »

Updated code to eliminate some temporary variables. Now I have a big gross mess of a line of code though lol. Oh well, I'll consider trying to improve the approach to getting the correct armor cell indexes later if necessary. For now, this works.
Code
def armorUpdate(dmg, location, armor_grid, minimum_armor):
    # dmg is a vector with elements:
    # [shield_damage, armor_damage, hull_damage, shield_hit_strength, armor_hit_strength, hull_hit_strength]
    # location is a tuple of the coordinates in the armor grid where the shot lands
    # armor_grid is an array with each element corresponding to an armor cell
    # minimum armor is the smallest armor value the pooled armor is allowed to take. Typically .05*max_armor

    inner_cells, outer_cells = getArmorIndexes(location, armor_grid)

    damage_armor = (1 / 15 * inner_cells + 1 / 30 * outer_cells) * \
        np.maximum(dmg[4] / (dmg[4] + np.maximum(np.sum(armor_grid[inner_cells]) + 1 / 2 * np.sum(armor_grid[outer_cells]), minimum_armor)), .15) * dmg[1]

    damage_hull = np.sum(np.maximum(damage_armor - armor_grid, 0) * dmg[2] / dmg[1])

    return damage_armor, damage_hull
Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #172 on: November 24, 2022, 08:22:31 PM »


I would expect the results to not exactly match what you did because I don't think the two approaches are equivalent. Should definitely test and verify as much as possible though, and I'm happy to use all the probability distribution code that exists. I tried to make it modular so it could slot into other stuff easily.

Yeah let's rephrase: if your code with real weapons prints out correct results compared to experimental data with a reasonable adjustment of the adjustable SD parameter f, then it is the superior way to calculate this.

The concerns I had about parameters from outside the shot's field of view affecting armor damage reduction specifically disappear when you deduct all the damage from the entire distribution from the armor matrix in one step, as then it's not the case that previously computed cells affect damage reduction for the next, so then it should be the correct thing.

If not then let's try Vanshilar's tensor approach next. Sounds cool. :D
Logged
5 ships vs 5 Ordos: Executor · Invictus · Paragon · Astral · Legion · Onslaught · Odyssey | Video LibraryHiruma Kai's Challenge

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #173 on: November 25, 2022, 12:21:42 AM »

Alright don't let this distract from any programming effort as I think the important thing right now is just to get the utility working with intrinsic_parity's code for the armor damage and the proper distributions and weapons time series from the R code. But here is an attempt to quantify analytically the error in intrinsic_parity's method, and it should be pretty small overall.
(edit: I have to re-do this again because of a silly computational error, doing this while doing something else again! repost soon)

E2: okay hit some problems. I've got this far:



but then having problems applying this http://elib.mi.sanu.ac.rs/files/journals/publ/105/n099p107.pdf . So this might have to wait until I'm better at this stuff. Were we to pretend armor strength at cell is a random variable again then wr'd end up with epsilon/d < variance / hit strength ^2 again so very small errors expected. In fact running this estimate numerically with armor hp in cell assumed to be a uniform random variable between the expected damage and maximum armor, with armor and hit strength going from 150 to 1750 and 10 to 1000, I'm getting upper bounds of error typically at some single digit % of damage per shot over the entire range and mean upper bound of error as less than 1% of damage per shot over the entire range (original lower estimates revised a little higher as I looked at larger pieces of random armor). The largest errors are at the lowest hit strength compared to armor as we might expect, because if h >> a then h/(h+a) is approximately h/h so then there is no problem with the expected value.

If you're interested in what the raw error term would look like if the armor state were completely random before the shot, with each cell randomly (uniform) between shot damage and the armor's starting value, and we were to apply this method (it is not, that is a gross exaggeration, in real terms we should not even characterize it as a random variable in the simulation) then here is some code and output and a plot for mean (h/(h+a))- h/(h+mean(a)) when a is computed from 15 uniform random cells 10 times for each combination of hit str and armor.

code
Code

df <- data.frame()
 
for(a in seq(150,1750,5)){
  for(h in seq(10,1000,5)){
    if(h^2/(h+a) < a){
    randomnumbers <- vector(mode="double",length = 10)
    #to keep it simple we are going to look at 15 armor cells that are given equal weight rather than the real 21
    for (i in 1:10){
    armorcells <- runif(15,h^2/(h+a)/15,a/15)
    randomnumbers[i] <- sum(armorcells)
    }
    errorterm1 <- mean(h/(h+randomnumbers))
    errorterm2 <- h/(h+mean(randomnumbers))
    df <- rbind(df, c(errorterm1 - errorterm2, a, h))
    }
  }
}

colnames(df) <- c("error","armor","hitstrength")
library(ggplot2)
ggplot(df,aes(x=armor,y=hitstrength,col=error*100))+
  geom_tile()+
  labs(y="Hit strength", x="Ship starting armor", col="Error (%)")+
  scale_color_viridis_c()

max(df$error,na.rm=TRUE)

mean(df$error,na.rm=TRUE)

Output:
> max(df$error,na.rm=TRUE)
[1] 0.01162053
>
> mean(df$error,na.rm=TRUE)
[1] 0.001375372
[close]



So it looks like even in this extreme circumstance the error in approximation is very small (scale is epsilon/d ie. error as % of shot damage).
« Last Edit: November 25, 2022, 05:43:16 AM by CapnHector »
Logged
5 ships vs 5 Ordos: Executor · Invictus · Paragon · Astral · Legion · Onslaught · Odyssey | Video LibraryHiruma Kai's Challenge

Liral

  • Admiral
  • *****
  • Posts: 717
  • Realistic Combat Mod Author
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #174 on: November 25, 2022, 08:18:38 AM »

I have reformatted and partly-documented the NumPy code from instrinsic_parity

import numpy as np


def inner_and_outer_cells(location: tuple, armor_grid):
    """
    Return coordinates of armor cells affected
    by a hit at this location on an armor grid.
   
     OOO
    OIIIO       I = inner cell
    OIIIO       O = outer cell
    OIIIO
     OOO
   
    location - (x, y) integer pair relative to
               (0, 0) at lower-left
    armor_grid - a 2D array of armor cell values
    """
   
    inner_cells = np.full(armor_grid.shape, False)
    outer_cells = np.full(armor_grid.shape, False)

    inner_cells[location[0] - 1: location[0] + 2,
                location[1] - 1: location[1] + 2] = True

    outer_cells[location[0] - 2, location[1] - 1: location[1] + 2] = True
    outer_cells[location[0] + 2, location[1] - 1: location[1] + 2] = True
    outer_cells[location[0] - 1: location[0] + 2, location[1] - 2] = True
    outer_cells[location[0] - 1: location[0] + 2, location[1] + 2] = True
   
    return inner_cells, outer_cells


def pooled_armor(armor_grid, inner_cells, outer_cells):
    """
    Return the armor value pooled from the inner and
    outer cells per the Starsector game logic.
   
    armor_grid - a 2D array of armor cell values
    inner_cells - the 3x3 grid encompassing the hit cell
    outer_cells - the 3-long fringes around the inner_cells
    """
    return np.sum(armor_grid[inner_cells]) + np.sum(armor_grid[outer_cells]) / 2


def armor_and_hull_damage(
    armor_damage: float,
    hull_damage: float,
    hit_strength: float,
    minimum_armor: float,
    armor_grid,
    inner_cells,
    outer_cells
):
    """
    Return the damage of this hit to the hull and armor

    armor_damage - damage per shot (or per second) times
                   the corresponding damage type armor
                   damage factor of this hit
    hull_damage - damage per shot (or per second) of
                  this hit
    hit_strength - if hit is a beam tick, one half
                   armor_damage, else armor_damage
    location - armor grid coordinates of hit
    minimum_armor - least armor value the pooled
                    armor is allowed to take.
                    Typically .05*max_armor
    armor_grid - array with each element
                 corresponding to an armor cell
    inner_cells - 3x3 cell coordinate grid encompassing
                  the hit
    outer_cells - 3-long fringe outside each edge of
                  inner_cells
    """
    armor_strength = max(pooled_armor(armor_grid, inner_cells, outer_cells),
                         minimum_armor)
    armor_damage_factor = max(hit_strength / (hit_strength + armor_strength),
                              .15)
    damage_armor = ((inner_cells / 15 + outer_cells / 30)
                    * armor_damage_factor
                    * armor_damage)
    damage_hull = (np.sum(np.maximum(damage_armor - armor_grid, 0))
                    * hull_damage
                    / armor_damage)
    return damage_armor, damage_hull
   

def main():
    """
    A mudskipper hit with a 100 damage energy shot.
    """
    #ship
    armor_rating = 150
    minimum_armor = armor_rating * 0.05
    armor_grid = np.full((5, 7), armor_rating / 15)
   
    #hit
    location = (2, 3)
    inner_cells, outer_cells = inner_and_outer_cells(location, armor_grid)
    armor_damage, hull_damage, hit_strength = 100, 100, 100
   
    #result
    print(armor_and_hull_damage(armor_damage, hull_damage, hit_strength,
                                minimum_armor, armor_grid, inner_cells,
                                outer_cells))
main()


And verified its output to be the same as that of the original code.  I hope you find it easier to read and debug; I have also moved the reusable inner and outer cell determination outside the hit calculation.

intrinsic_parity

  • Admiral
  • *****
  • Posts: 3071
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #175 on: November 25, 2022, 09:49:53 AM »

There will always be a tradeoff between readability and performance I suppose. I guess for now we can leave things in the most readable state until we get to a point where performance improvements might be worthwhile.

Also, CapnHector. The weights for pooling should be 1 or 1/2 depending on inner and outer cells. The initial values of the armor cells are 1/15th of the total armor, so they start on the 1/15th scale. The adjusted shot damage gets 1/15 and 1/30 multipliers when it is distributed amongst the cells so that the damage is on the same 1/15th scale as the cell armor values.

Here are the equations explicitly written out:


Edit: looking at this closely, I'm realizing I could implement it with a single 'indexes' array instead of two separate 'inner' and 'outer' index arrays. That would theoretically save some time because I avoid some element wise additions and also only allocate one array, however, that array would be numerical instead of boolean, and I would be doing the pooling operations (multiplications and summations) over the entire array element-wise instead of the current implementation where I use logical indexing to only do the math on the non-zero multipliers. I'm sort of curious which would be faster.
« Last Edit: November 25, 2022, 10:01:27 AM by intrinsic_parity »
Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #176 on: November 25, 2022, 10:24:31 AM »

It's good that we started doing latex. What you wrote is super crisp to read and makes it quite clear what exactly is going on.

Regardless of any pooling or distribution, the error should still be due to using hit strength based on expected value of armor, instead of expected value of hit strength, no? Ie. E((h)/(h+armor)) vs h/(h+E(armor)). All the other operations are linear - or almost always linear at least. The upside is this should be small like you found out yourself in the monte carlo you did earlier. So this is looking very good. Unless I'm missing another source of error?
Logged
5 ships vs 5 Ordos: Executor · Invictus · Paragon · Astral · Legion · Onslaught · Odyssey | Video LibraryHiruma Kai's Challenge

intrinsic_parity

  • Admiral
  • *****
  • Posts: 3071
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #177 on: November 25, 2022, 10:51:50 AM »

It's good that we started doing latex. What you wrote is super crisp to read and makes it quite clear what exactly is going on.

Regardless of any pooling or distribution, the error should still be due to using hit strength based on expected value of armor, instead of expected value of hit strength, no? Ie. E((h)/(h+armor)) vs h/(h+E(armor)). All the other operations are linear - or almost always linear at least. The upside is this should be small like you found out yourself in the monte carlo you did earlier. So this is looking very good. Unless I'm missing another source of error?
I think the max() operations are non-linear too.

Also, I realized I forgot summations in the hull equation. And while I was at it, I moved some stuff out of the ArmorDamage equation so that it is not a function of the armor cell, and all that i,j stuff is handled in the armor and hull equations.

Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #178 on: November 25, 2022, 11:39:08 AM »

It's good that we started doing latex. What you wrote is super crisp to read and makes it quite clear what exactly is going on.

Regardless of any pooling or distribution, the error should still be due to using hit strength based on expected value of armor, instead of expected value of hit strength, no? Ie. E((h)/(h+armor)) vs h/(h+E(armor)). All the other operations are linear - or almost always linear at least. The upside is this should be small like you found out yourself in the monte carlo you did earlier. So this is looking very good. Unless I'm missing another source of error?
I think the max() operations are non-linear too.

Absolutely, but they are composed of 2 linear functions with 1 point where you switch functions - almost always linear, so if you look at the equation at either side of those points they are linear. The argument is that since the non-linearity is contained to 1 point in the simulation for each max() function, then that should not contribute significantly to the error in the whole simulation. You could also support this argument by saying the error is expected to transition from small to 0 at this point so that's another reason why those points shouldn't be significant and you can get rid of the max's for the analysis of maximum error.
Logged
5 ships vs 5 Ordos: Executor · Invictus · Paragon · Astral · Legion · Onslaught · Odyssey | Video LibraryHiruma Kai's Challenge

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #179 on: November 26, 2022, 07:41:43 AM »

Vanshilar's idea of computing it using matrices and dots got stuck in my head, so out of sheer love of the subject here is a basic matrix and dot product implementation of intrinsic_parity's method (that is, true to the game damage calculations) using just 2-dimensional matrices. I do not know enough about computing to know whether it might speed up or slow down anything, but do note the matrix D is the exact same I've been harping on about and can be computed just once at the beginning of the simulation for each weapon and ship using just the overlapping sums method rather than matrix multiplication. But the big dream is that expressing the equations more concisely might lead to us one day being able to calculate E(h/(h+armor)) directly, maybe using law of the unconscious statistician, therefore eliminating the main source of error in the method.

Edit2: found a serious issue. Upping fixed version when I have the time to fix.
« Last Edit: November 26, 2022, 11:59:38 AM by CapnHector »
Logged
5 ships vs 5 Ordos: Executor · Invictus · Paragon · Astral · Legion · Onslaught · Odyssey | Video LibraryHiruma Kai's Challenge
Pages: 1 ... 10 11 [12] 13 14 ... 32