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 ... 7 8 [9] 10 11 ... 32

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

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #120 on: November 20, 2022, 08:42:55 PM »

Another idea is to write the function in C++ (weirdly enough the only other language I have experience in writing, other than shell scripts) and embed it into the R code using Rcpp. This might increase performance by such as a factor of 10. But I won't be doing it now since I don't know if that's possible in Python.
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 #121 on: November 20, 2022, 09:05:34 PM »

I'm pretty sure everything vaguely performant in python is actually calling a C or C++ library or function lmao. But I've never tried doing something like that myself.

I also know that cython exists https://cython.org, but I've never used it.
Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #122 on: November 20, 2022, 10:53:56 PM »

Alright then, working on it. Here is the new armor damage function
Code
#this function is absolutely performance critical (run millions of times) so write it in C++ using
#elementary operations
library(Rcpp)
#what this function shall do is:
#1 ) modify the armor matrix in the R global environment by subtracting armor damage
#2 ) return hull damage
#A is the armor matrix
#rows_A is the number of rows in A
#cols_A is the number of columns in A
#D is the damage matrix (note: we do not need a rows_D etc as these matrices must be same size)
#a is starting armor for the whole ship
#a_mod is minimum armor
#d is raw damage from whole weapon shot
#h is hit strength of weapon shot
#m is modifier, 2= kinetic, 1 = energy, 0.5 = he, 4 = frag
#hd is hull damage
#do NOT pass a hit strength of 0 to this function as it does not check for dividing by zero
#overall, this function does no sanity or safety checking so be careful with it
cppFunction('double damage(NumericMatrix A, NumericMatrix D, double a, double a_mod, double d, double h, double m){
  double hd = 0;
  int rows_A = A.nrow();
  int cols_A = A.ncol();
  a = a/(rows_A*cols_A);
  for (int j = 0; j < cols_A; j++){
  for (int i = 0; i < rows_A; i++){
    double armor = std::max(A(i,j), a_mod*a);
    double probmult = D(i,j);
    double adjusted_d = d*h/m/(armor+h/m)*probmult;
    if (adjusted_d <= armor){
      A(i,j) = A(i,j) - adjusted_d;
      A(i,j) = std::max(A(i,j), 0.0);
    }
    if (adjusted_d > armor){
      A(i,j) = 0.0;
      adjusted_d = (adjusted_d - armor)*m;
      hd = hd + adjusted_d;
    }
  }
  }
  return hd;
}
')

Edit: Here is WIP. Working on the script to produce a graph first to test that all is working correctly. To be continued. Currently only works for one Squall in slot 1 but includes what I intend to do to handle beam weapons. E2: got it I think. Here is a combo of squall x 2 and "super ion beam" (ion beam except 1000 dps). E3: added a single check to determine whether we are using shield to block this second rather than if we are using it to block this weapon (ie. you are not allowed to let missiles through while blocking the beam with shield). E4: added multiply by beam_tick_time to beam damage which I had forgotten.


Spoiler
Code

library(ggplot2)
library(ggthemes)

#operating modes DO NOT CHANGE CODE WILL BREAK IF GUN IS NOT 0 AND BEAM IS NOT 1 AS THE LITERAL INTEGER VALUE IS USED

GUN <- 0
BEAM <- 1
#SHIP
#dominator, hullhp, shieldregen, shieldmax, startingarmor, widthinpixels, armorcells, shieldwidth, shieldefficacy, shieldupkeep
ship <- c(14000, 500, 10000, 1500, 220, 12, 440, 1.0, 200)

#engagementrange
range <- 1000

minimumarmormultiplier <- 0.05

#weaponaccuracy - this will be made a function of time and weapon later. the accuracy of a hellbore is 10
acc <- 10

#fudge factor
errorsd <- 0.05
#the fudge factor should be a function of range (more error in position at greater range), but not a function of weapon firing angle, and be expressed in terms of pixels
error <- errorsd*range

#time limit for a single combat
time_limit <- 30



#how much is the visual arc of the ship in rad?
shipangle <- ship[5]/(2* pi *range)

#how much is the visual arc of a single cell of armor in rad?
cellangle <- shipangle/ship[6]

#now assume the weapon is targeting the center of the ship's visual arc and that the ship is in the center of the weapon's firing arc
#which cell will the shot hit, or will it miss?
#call the cells (MISS, cell1, cell2, ... ,celli, MISS) and get a vector giving the (maximum for negative / minimum for positive) angles for hitting each
anglerangevector <- vector(mode="double", length = ship[6]+1)
anglerangevector[1] <- -shipangle/2
for (i in 1:(length(anglerangevector)-1)) anglerangevector[i+1] <- anglerangevector[i]+cellangle

#now convert it to pixels
anglerangevector <- anglerangevector*2*pi*range

#this vector will store the hits
shipcellvector <- vector(mode="double", length = ship[6]+2)




G <- function(y) return(y*pnorm(y) + dnorm(y))
#a is the SD of the normal distribution and b is the parameter of the uniform distribution
hit_probability_coord_lessthan_x <- function(z, a, b) return(a/2/b*(G(z/a+b/a)-G(z/a-b/a)))


# this function generates the shot distribution (a bhattacharjee distribution for the
#non-trivial case)
hit_distribution <- function(upperbounds, standard_deviation, spread){
  vector <- vector(mode="double", length = length(upperbounds))
  if (standard_deviation == 0){
    if (spread == 0){
      vector[1] <- 0
      for (j in 2:(length(upperbounds))) {
        #if both spread and standard deviation are 0 then all shots hit 1 cell. this should be so even if
        #the ship has an even number of cells to prevent ships with even no. cells appearing tougher which is not
        #the case in the real game most likely
        if ((upperbounds[j] >= 0) & (upperbounds[j-1] < 0)) vector[j] <- 1
      }
      #return part of a box
    } else {
      vector[1] <- min(1,max(0,(upperbounds[1]+spread))/(2*spread))
      for (j in 2:(length(upperbounds)-1)) vector[j] <- min(1,max(0,(upperbounds[j]+spread))/(2*spread)) - min(1,max(0,(upperbounds[j-1]+spread))/(2*spread))
      vector[length(upperbounds)] <- 1-min(1,max(0,(upperbounds[length(upperbounds)]+spread))/(2*spread))
    }
  } else {
    if (spread != 0){
      vector[1] <- hit_probability_coord_lessthan_x(upperbounds[1], standard_deviation, spread)
      for (j in 2:(length(upperbounds)-1)) vector[j] <- (hit_probability_coord_lessthan_x(upperbounds[j], standard_deviation, spread)-hit_probability_coord_lessthan_x(upperbounds[j-1], standard_deviation, spread))
      vector[length(upperbounds)] <- (1-hit_probability_coord_lessthan_x(upperbounds[length(upperbounds)-1], standard_deviation, spread))
    } else {
      #if spread is 0 but standard deviation is not 0 we have a normal distribution
      for (j in 1:(length(upperbounds)-1)) vector[j] <- pnorm(upperbounds[j], mean=0, sd=standard_deviation)
      for (j in 2:(length(upperbounds)-1)) vector[j] <- vector[j] - pnorm(upperbounds[j-1], mean=0, sd=standard_deviation)
      vector[length(upperbounds)] <- 1-pnorm(upperbounds[length(upperbounds)-1], mean=0, sd=standard_deviation)
    }
   
  }
  return(vector)
}

#this is not really necessary, just a wrapper for the above new function to fit into the old code
createdistribution <- function(acc,mode){
  return(hit_distribution(anglerangevector,error,acc))
}

# this is the default distribution of damage to armor cells
b <- matrix(0,nrow=5,ncol=5)
b[1:5,2:4] <- 1/30
b[2:4,1:5] <- 1/30
b[2:4,2:4] <- 1/15
b[1,1] <- 0
b[1,5] <- 0
b[5,1] <- 0
b[5,5] <- 0

#this function returns the chance to hit a simple width
hitchance <- function(acc,error,lowerpoint,higherpoint){
  return(hit_distribution(c(lowerpoint,higherpoint,0),error,acc)[[2]])
}
#this function generates a sum of matrices multiplied by the distribution

createhitmatrix <- function(acc){
  hitmatrix <- matrix(0,5,ship[6]+4)
  distributionvector <- createdistribution(acc)
  for (i in 1:ship[6]){
    hitmatrix[,i:(i+4)] <- hitmatrix[,i:(i+4)]+b*(distributionvector[i+1])
  }
  return(hitmatrix)
}

#for weapons with damage changing over time we need a sequence of matrices
createhitmatrixsequence <- function(accvector){
  hitmatrixsequence <- list()
  for (i in 1:length(accvector)){
    hitmatrixsequence[[i]] <- createhitmatrix(accvector[i])
  }
  return(hitmatrixsequence)
}
 

#this function is absolutely performance critical (run millions of times) so write it in C++ using
#elementary operations
library(Rcpp)
#what this function shall do is:
#1 ) modify the armor matrix in the R global environment by subtracting armor damage
#2 ) return hull damage
#A is the armor matrix
#rows_A is the number of rows in A
#cols_A is the number of columns in A
#D is the damage matrix (note: we do not need a rows_D etc as these matrices must be same size)
#a is starting armor for the whole ship
#a_mod is minimum armor
#d is raw damage from whole weapon shot
#h is hit strength of weapon shot
#m is modifier, 2= kinetic, 1 = energy, 0.5 = he, 4 = frag
#hd is hull damage
#do NOT pass a hit strength of 0 to this function as it does not check for dividing by zero
#overall, this function does no sanity or safety checking so be careful with it
cppFunction('double damage(NumericMatrix A, NumericMatrix D, double a, double a_mod, double d, double h, double m){
  double hd = 0;
  int rows_A = A.nrow();
  int cols_A = A.ncol();
  a = a/(rows_A*cols_A);
  for (int j = 0; j < cols_A; j++){
  for (int i = 0; i < rows_A; i++){
    double armor = std::max(A(i,j), a_mod*a);
    double probmult = D(i,j);
    double adjusted_d = d*h/m/(armor+h/m)*probmult;
    if (adjusted_d <= armor){
      A(i,j) = A(i,j) - adjusted_d;
      A(i,j) = std::max(A(i,j), 0.0);
    }
    if (adjusted_d > armor){
      A(i,j) = 0.0;
      adjusted_d = (adjusted_d - armor)*m;
      hd = hd + adjusted_d;
    }
  }
  }
  return hd;
}
')

#general function to generate ticks
#1. general constants
#the interval of discrete time (time lattice parameter) we are using in the model, in seconds
time_interval <- 1
#how long 1 tick of a beam lasts, in seconds
beam_tick <- 1/10
#minimum interval that exists in the game, in case a modder has somehow specified a lower value for something
global_minimum_time <- 0.05
#operating modes
UNLIMITED <- -1

#times in seconds, ammoregen is in ammo / second
hits <- function(chargeup, chargedown, burstsize, burstdelay, ammo=UNLIMITED, ammoregen=0, reloadsize=0, traveltime=0, mode=GUN){
  #specify sane minimum delays, since the game enforces weapons can only fire once every 0.05 sec
  #for beams, refiring delay is given by burstdelay, for guns it is burstdelay in case burstdelay is > 0 (==0 is shotgun) and chargedown
  if(burstdelay > 0 | mode == BEAM) burstdelay <- max(burstdelay, global_minimum_time)
  if(mode == GUN) chargedown <- max(chargedown, global_minimum_time)
  #this vector will store all the hit time coordinates
  #current time
  #insert a very small fraction here to make time round correctly
  time <- 0.001
  #maximum ammo count is ammo given at start
  maxammo <- ammo
  #this is used to do ammo regeneration, 0 = not regenerating ammo, 1 = regenerating ammo
  regeneratingammo <- 0
  ammoregentimecoordinate <- 0
  ammoregenerated <- 0
 
  #we are firing a gun
  if (mode == GUN) {
    Hits <- vector(mode="double", length = 0)
    while(time < time_limit){
      time <- time + chargeup
      if(time - ammoregentimecoordinate > 1/ammoregen){
        ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
        ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
        if(ammoregenerated >= reloadsize){
          ammo <- ammo+ ammoregenerated
          ammoregenerated <- 0
        }
        if(ammo >= maxammo){
          ammo <- maxammo
          regeneratingammo <- 0
        }
      }
     
      if (burstdelay == 0) {
        for (i in 1:burstsize) {
          if (ammo != 0){
            Hits <- c(Hits, time + traveltime)
            ammo <- ammo - 1
            if (regeneratingammo == 0) {
              ammoregentimecoordinate <- time
              regeneratingammo <- 1
            }
          }
        }
      }
      if (burstdelay > 0) {
        for (i in 1:burstsize) {
          if (ammo != 0){
            Hits <- c(Hits, time + traveltime)
            time <- time + burstdelay
            ammo <- ammo -1
            if (regeneratingammo == 0) {
              ammoregentimecoordinate <- time
              regeneratingammo <- 1
            }
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
           
          }
        }
      }
      time <- time+chargedown
      if(time - ammoregentimecoordinate > 1/ammoregen){
        ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
        ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
        if(ammoregenerated >= reloadsize){
          ammo <- ammo+ ammoregenerated
          ammoregenerated <- 0
        }
        if(ammo >= maxammo){
          ammo <- maxammo
          regeneratingammo <- 0
        }
      }
    }
    timeseries <- vector(mode="integer", length = time_limit/time_interval)
    timeseries[1] <- length(Hits[Hits >= 0 & Hits <= 1*time_interval])
    for (i in 2:time_limit/time_interval) timeseries[i] <- length(Hits[Hits > (i-1)*time_interval & Hits <= i*time_interval])
    return(timeseries)
  }
  #we are firing a beam
  if (mode == BEAM) {
    chargeup_ticks <- chargeup/beam_tick
    chargedown_ticks <- chargedown/beam_tick
    burst_ticks <- burstsize/beam_tick
    #for a beam we will instead use a matrix to store timepoint and beam intensity at timepoint
    beam_matrix <- matrix(nrow=0,ncol=2)
    #burst size 0 <- the beam never stops firing
    if(burstsize == 0){
      for (i in 1:chargeup_ticks) {
        #beam intensity scales quadratically during chargeup, so
      }
      while ( time < time_limit) {
        beam_matrix <- rbind(beam_matrix,c(time, 1))
        time <- time+beam_tick
      }
    } else {
      while (time < time_limit) {
        if (ammo != 0){
          ammo <- ammo - 1
          if (chargeup_ticks > 0){
            for (i in 1:chargeup_ticks) {
              beam_matrix <- rbind(beam_matrix,c(time, (i*beam_tick)^2))
              time <- time+beam_tick
              if (regeneratingammo == 0) {
                ammoregentimecoordinate <- time
                regeneratingammo <- 1
              }
              if(time - ammoregentimecoordinate > 1/ammoregen){
                ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
                ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
                if(ammoregenerated >= reloadsize){
                  ammo <- ammo+ ammoregenerated
                  ammoregenerated <- 0
                }
                if(ammo >= maxammo){
                  ammo <- maxammo
                  regeneratingammo <- 0
                }
              }
            }
          }
          for (i in 1:burst_ticks){
            beam_matrix <- rbind(beam_matrix,c(time, 1))
            time <- time+beam_tick
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
          }
         
          if (chargedown_ticks > 0){
            for (i in 1:chargedown_ticks){
              beam_matrix <- rbind(beam_matrix,c(time, ((chargedown_ticks-i)*beam_tick)^2))
              time <- time+beam_tick
            }
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
          }
          time <- time + burstdelay
          if(time - ammoregentimecoordinate > 1/ammoregen){
            ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
            ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
            if(ammoregenerated >= reloadsize){
              ammo <- ammo+ ammoregenerated
              ammoregenerated <- 0
            }
            if(ammo >= maxammo){
              ammo <- maxammo
              regeneratingammo <- 0
            }
          }
        }
        time <- time + global_minimum_time
        if(time - ammoregentimecoordinate > 1/ammoregen){
          ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
          ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
          if(ammoregenerated >= reloadsize){
            ammo <- ammo+ ammoregenerated
            ammoregenerated <- 0
          }
          if(ammo >= maxammo){
            ammo <- maxammo
            regeneratingammo <- 0
          }
        }
      }
    }
    timeseries <- vector(mode="double", length = time_limit/time_interval)
    for (i in 1:length(timeseries)) {
      timeseries[i] <- sum(beam_matrix[beam_matrix[,1] < i & beam_matrix[,1] > i-1,2])
    }
    return(timeseries)
  }
}

squalltics <- hits(0,10,20,0.5)
locusttics <- hits(0,5,40,0.1)
#special
hurricanetics <- hits(0,15,9,0)
harpoontics <- hits(0,8.25,4,0.25)
sabottics <- hits(0,8.75,2,0.25)
gausstics <- hits(1,1,1,0)
ionbeamtics <- hits(0.1,0.1,0,0,mode=BEAM)

#WEAPON ACCURACY
#missiles do not have spread
squallacc <- c(0)
locustacc <- c(0)
hurricaneacc <- c(0)
harpoonacc <- c(0)
sabotacc <- c(0)

#gauss has a spread of 0 and no increase per shot
gaussacc <- c(0)
#hephaestus has a spread of 0 and it increases by 2 per shot to a max of 10
#hephaestusacc <- c(seq(0,10,2))
#mark ix has a spread of 0 and it increases by 2 per shot to a max of 15
#markixacc <- c(seq(0,15,2),15)
#mjolnir has a spread of 0 and it increases by 1 per shot to a max of 5
#mjolniracc <- c(seq(1,5,1))
#hellbore has a spread of 10
#hellboreacc <- c(10)
#storm needler has a spread of 10
#stormneedleracc <- c(10)
ionbeamacc <- c(0)

#damage per shot, damage type (2=kinetic, 0.5=he, 0.25=frag, 1=energy), tics, weapon name, weapon accuracy over time, hit chance, mode
squall <- list(250, 2, squalltics, "Squall", squallacc, GUN)
locust <- list(200, 0.25, locusttics, "Locust", locustacc, GUN)
hurricane <- list(500, 0.5, hurricanetics, "Hurricane", hurricaneacc, GUN)
harpoon <- list(750, 0.5, harpoontics, "Harpoon", harpoonacc, GUN)
sabot <- list(200, 2, sabottics, "Sabot", sabotacc, GUN)
gauss <- list(700, 2, gausstics, "Gauss", gaussacc, GUN)
#hephaestus <- list(120, 0.5, hephaestustics, "Hephaestus", hephaestusacc)
#markix <- list(200, 2, markixtics, "Mark IX", markixacc)
#mjolnir <- list(400, 1, mjolnirtics, "Mjolnir", mjolniracc)
#hellbore <- list(750, 0.5, hellboretics, "Hellbore", hellboreacc)
#stormneedler <- list(50, 2, stormneedlertics, "Storm Needler", stormneedleracc)

#for beams, damage per second, and then the rest as previously
ionbeam <- list(1000, 1, ionbeamtics, "Super Ion Beam", ionbeamacc, BEAM)
dummy <- list(0,0,c(seq(0,time_limit,1)),"",c(0),c(0),GUN)


weapon1 <- squall
weapon2 <- squall
weapon3 <- ionbeam
weapon4 <- dummy
weapon5 <- dummy
weapon6 <- dummy
weapon7 <- dummy
weapon8 <- dummy

#now create the sequences of hit matrices and hit chances for each weapon

if(weapon1[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon1[[5]]))
  for (i in 1:length(weapon1[[5]])){
    hitchancevector[i] <- hitchance(weapon1[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon1[[7]] <- hitchancevector
  weapon1[[8]] <- createhitmatrixsequence(weapon1[[5]])
}

if(weapon2[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon2[[5]]))
  for (i in 1:length(weapon2[[5]])){
    hitchancevector[i] <- hitchance(weapon2[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon2[[7]] <- hitchancevector
  weapon2[[8]] <- createhitmatrixsequence(weapon2[[5]])
}

if(weapon3[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon3[[5]]))
  for (i in 1:length(weapon3[[5]])){
    hitchancevector[i] <- hitchance(weapon3[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon3[[7]] <- hitchancevector
  weapon3[[8]] <- createhitmatrixsequence(weapon3[[5]])
}

if(weapon4[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon4[[5]]))
  for (i in 1:length(weapon4[[5]])){
    hitchancevector[i] <- hitchance(weapon4[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon4[[7]] <- hitchancevector
  weapon4[[8]] <- createhitmatrixsequence(weapon4[[5]])
}
if(weapon5[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon5[[5]]))
  for (i in 1:length(weapon5[[5]])){
    hitchancevector[i] <- hitchance(weapon5[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon5[[7]] <- hitchancevector
  weapon5[[8]] <- createhitmatrixsequence(weapon5[[5]])
}
if(weapon6[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon6[[5]]))
  for (i in 1:length(weapon6[[5]])){
    hitchancevector[i] <- hitchance(weapon6[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon6[[7]] <- hitchancevector
  weapon6[[8]] <- createhitmatrixsequence(weapon6[[5]])
}
if(weapon7[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon7[[5]]))
  for (i in 1:length(weapon7[[5]])){
    hitchancevector[i] <- hitchance(weapon7[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon7[[7]] <- hitchancevector
  weapon7[[8]] <- createhitmatrixsequence(weapon7[[5]])
}
if(weapon8[4] != ""){
  hitchancevector <- vector(mode = "double", length = length(weapon8[[5]]))
  for (i in 1:length(weapon8[[5]])){
    hitchancevector[i] <- hitchance(weapon8[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
  }
  weapon8[[7]] <- hitchancevector
  weapon8[[8]] <- createhitmatrixsequence(weapon8[[5]])
}


shieldblock <- 0

weapon1shots <- 1
weapon2shots <- 1
weapon3shots <- 1
weapon4shots <- 1
weapon5shots <- 1
weapon6shots <- 1
weapon7shots <- 1
weapon8shots <- 1


armormatrix <- matrix(ship[4]/15,5,ship[6]+4)

timeseries <- function(timepoint, softflux, hardflux, armorhp, hullhp, shieldregen, shieldmax, startingarmor,armormatrix){
  weaponacc <- 0

  #are we using shield to block?
  shieldblock <- 0
  hulldamage <- 0

 
  #weapon 1
  weapon1mult <- weapon1[[2]]
  weapon2mult <- weapon2[[2]]
  weapon3mult <- weapon3[[2]]
  weapon4mult <- weapon4[[2]]
  weapon5mult <- weapon5[[2]]
  weapon6mult <- weapon6[[2]]
  weapon7mult <- weapon7[[2]]
  weapon8mult <- weapon8[[2]]
 
  shots <- weapon1[[3]][[timepoint]]
  #here we must convert beam ticks to fractional shots
  shots1 <- weapon1[[3]][[timepoint]]*beam_tick_time^(weapon1[[6]])
  shots2 <- weapon2[[3]][[timepoint]]*beam_tick_time^(weapon2[[6]])
  shots3 <- weapon3[[3]][[timepoint]]*beam_tick_time^(weapon3[[6]])
  shots4 <- weapon4[[3]][[timepoint]]*beam_tick_time^(weapon4[[6]])
  shots5 <- weapon5[[3]][[timepoint]]*beam_tick_time^(weapon5[[6]])
  shots6 <- weapon6[[3]][[timepoint]]*beam_tick_time^(weapon6[[6]])
  shots7 <- weapon7[[3]][[timepoint]]*beam_tick_time^(weapon7[[6]])
  shots8 <- weapon8[[3]][[timepoint]]*beam_tick_time^(weapon8[[6]])
  #test is used to determine if we are firing or blocking this turn
  test <- (fluxcap-softflux-hardflux - weapon1[[1]]*weapon1mult*shots1 - weapon2[[1]]*weapon2mult*shots2 - weapon3[[1]]*weapon3mult*shots3 - weapon4[[1]]*weapon4mult*shots4 - weapon5[[1]]*weapon5mult*shots5 - weapon6[[1]]*weapon6mult*shots6 - weapon7[[1]]*weapon7mult*shots7 - weapon8[[1]]*weapon8mult*shots8)   
  #skip the whole thing if we are not firing
  if (weapon1[[4]] !="" & shots > 0){
    mode <- weapon1[[6]]
    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
        hardflux <- hardflux + weapon1[[1]]*weapon1mult*weapon1[[7]][min(weapon1shots,length(weapon1[[7]]))]*shieldefficacy
        hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon1[[1]]*weapon1[[3]][[timepoint]]*beam_tick_time*weapon1mult*weapon1[[7]][min(weapon1shots,length(weapon1[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
      #2. if you did not use shield to block, damage armor and hull
      #frag is a special case wrt multiplier
      if(unlist(weapon1[2])==0.25){weapon1mult = 4}
      #2.1. damage armor and hull
      hitstrength <- 0
      damage <- 0
      if(mode == GUN) {
        hitstrength <- weapon1[[1]]
        damage <- weapon1[[1]]
      }
      if(mode == BEAM) {
        hitstrength <- weapon1[[1]]/2
        damage <- weapon1[[1]]*weapon1[[3]][[timepoint]]*beam_tick_time
      }
      hulldamage <- damage(armormatrix,weapon1[[8]][[min(weapon1shots,length(weapon1[[8]]))]],startingarmor,minimumarmormultiplier,damage,hitstrength,weapon1mult)
      hullhp <- hullhp - hulldamage
      hullhp <- max(hullhp, 0)
      }
   }
  weapon1shots <- weapon1shots + weapon1[[3]][timepoint]
  }
 
  #repeat for other weapons
  shots <- weapon2[[3]][[timepoint]]
 
 
  #skip the whole thing if we are not firing
  if (weapon2[[4]] !="" & shots > 0){
    weapon2mult <- weapon2[[2]]
    mode <- weapon2[[6]]

    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
          hardflux <- hardflux + weapon2[[1]]*weapon2mult*weapon2[[7]][min(weapon2shots,length(weapon2[[7]]))]*shieldefficacy
          hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon2[[1]]*weapon2[[3]][[timepoint]]*beam_tick_time*weapon2mult*weapon2[[7]][min(weapon2shots,length(weapon2[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
        #2. if you did not use shield to block, damage armor and hull
        #frag is a special case wrt multiplier
        if(unlist(weapon2[2])==0.25){weapon2mult = 4}
        #2.1. damage armor and hull
        hitstrength <- 0
        damage <- 0
        if(mode == GUN) {
          hitstrength <- weapon2[[1]]
          damage <- weapon2[[1]]
        }
        if(mode == BEAM) {
          hitstrength <- weapon2[[1]]/2
          damage <- weapon2[[1]]*weapon2[[3]][[timepoint]]*beam_tick_time
        }
        hulldamage <- damage(armormatrix,weapon2[[8]][[min(weapon2shots,length(weapon2[[8]]))]],startingarmor,minimumarmormultiplier,weapon2[[1]],hitstrength,weapon2mult)
        hullhp <- hullhp - hulldamage
        hullhp <- max(hullhp, 0)
      }
    }
    weapon2shots <- weapon2shots + weapon2[[3]][timepoint]
  }
 
 
  shots <- weapon3[[3]][[timepoint]]
 
 
  #skip the whole thing if we are not firing
  if (weapon3[[4]] !="" & shots > 0){
    weapon3mult <- weapon3[[2]]
    mode <- weapon3[[6]]
    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
          hardflux <- hardflux + weapon3[[1]]*weapon3mult*weapon3[[7]][min(weapon3shots,length(weapon3[[7]]))]*shieldefficacy
          hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon3[[1]]*weapon3[[3]][[timepoint]]*beam_tick_time*weapon3mult*weapon3[[7]][min(weapon3shots,length(weapon3[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
        #2. if you did not use shield to block, damage armor and hull
        #frag is a special case wrt multiplier
        if(unlist(weapon3[2])==0.25){weapon3mult = 4}
        #2.1. damage armor and hull
        hitstrength <- 0
        damage <- 0
        if(mode == GUN) {
          hitstrength <- weapon3[[1]]
          damage <- weapon3[[1]]
        }
        if(mode == BEAM) {
          hitstrength <- weapon3[[1]]/2
          damage <- weapon3[[1]]*weapon3[[3]][[timepoint]]*beam_tick_time
        }
        hulldamage <- damage(armormatrix,weapon3[[8]][[min(weapon3shots,length(weapon3[[8]]))]],startingarmor,minimumarmormultiplier,weapon3[[1]],hitstrength,weapon3mult)
        hullhp <- hullhp - hulldamage
        hullhp <- max(hullhp, 0)
      }
    }
    weapon3shots <- weapon3shots + weapon3[[3]][timepoint]
  }
 
 
  shots <- weapon4[[3]][[timepoint]]
 
 
  #skip the whole thing if we are not firing
  if (weapon4[[4]] !="" & shots > 0){
    weapon4mult <- weapon4[[2]]
    mode <- weapon4[[6]]
    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
          hardflux <- hardflux + weapon4[[1]]*weapon4mult*weapon4[[7]][min(weapon4shots,length(weapon4[[7]]))]*shieldefficacy
          hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon4[[1]]*weapon4[[3]][[timepoint]]*beam_tick_time*weapon4mult*weapon4[[7]][min(weapon4shots,length(weapon4[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
        #2. if you did not use shield to block, damage armor and hull
        #frag is a special case wrt multiplier
        if(unlist(weapon4[2])==0.25){weapon4mult = 4}
        #2.1. damage armor and hull
        hitstrength <- 0
        damage <- 0
        if(mode == GUN) {
          hitstrength <- weapon4[[1]]
          damage <- weapon4[[1]]
        }
        if(mode == BEAM) {
          hitstrength <- weapon4[[1]]/2
          damage <- weapon4[[1]]*weapon4[[3]][[timepoint]]*beam_tick_time
        }
        hulldamage <- damage(armormatrix,weapon4[[8]][[min(weapon4shots,length(weapon4[[8]]))]],startingarmor,minimumarmormultiplier,weapon4[[1]],hitstrength,weapon4mult)
        hullhp <- hullhp - hulldamage
        hullhp <- max(hullhp, 0)
      }
    }
    weapon4shots <- weapon4shots + weapon4[[3]][timepoint]
  }
 
 
  shots <- weapon5[[3]][[timepoint]]
 
 
  #skip the whole thing if we are not firing
  if (weapon5[[4]] !="" & shots > 0){
    weapon5mult <- weapon5[[2]]
    mode <- weapon5[[6]]
    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
          hardflux <- hardflux + weapon5[[1]]*weapon5mult*weapon5[[7]][min(weapon5shots,length(weapon5[[7]]))]*shieldefficacy
          hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon5[[1]]*weapon5[[3]][[timepoint]]*beam_tick_time*weapon5mult*weapon5[[7]][min(weapon5shots,length(weapon5[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
        #2. if you did not use shield to block, damage armor and hull
        #frag is a special case wrt multiplier
        if(unlist(weapon5[2])==0.25){weapon5mult = 4}
        #2.1. damage armor and hull
        hitstrength <- 0
        damage <- 0
        if(mode == GUN) {
          hitstrength <- weapon5[[1]]
          damage <- weapon5[[1]]
        }
        if(mode == BEAM) {
          hitstrength <- weapon5[[1]]/2
          damage <- weapon5[[1]]*weapon5[[3]][[timepoint]]*beam_tick_time
        }
        hulldamage <- damage(armormatrix,weapon5[[8]][[min(weapon5shots,length(weapon5[[8]]))]],startingarmor,minimumarmormultiplier,weapon5[[1]],hitstrength,weapon5mult)
        hullhp <- hullhp - hulldamage
        hullhp <- max(hullhp, 0)
      }
    }
    weapon5shots <- weapon5shots + weapon5[[3]][timepoint]
  }
 
  shots <- weapon6[[3]][[timepoint]]
 
 
  #skip the whole thing if we are not firing
  if (weapon6[[4]] !="" & shots > 0){
    weapon6mult <- weapon6[[2]]
    mode <- weapon6[[6]]
    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
          hardflux <- hardflux + weapon6[[1]]*weapon6mult*weapon6[[7]][min(weapon6shots,length(weapon6[[7]]))]*shieldefficacy
          hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon6[[1]]*weapon6[[3]][[timepoint]]*beam_tick_time*weapon6mult*weapon6[[7]][min(weapon6shots,length(weapon6[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
        #2. if you did not use shield to block, damage armor and hull
        #frag is a special case wrt multiplier
        if(unlist(weapon6[2])==0.25){weapon6mult = 4}
        #2.1. damage armor and hull
        hitstrength <- 0
        damage <- 0
        if(mode == GUN) {
          hitstrength <- weapon6[[1]]
          damage <- weapon6[[1]]
        }
        if(mode == BEAM) {
          hitstrength <- weapon6[[1]]/2
          damage <- weapon6[[1]]*weapon6[[3]][[timepoint]]*beam_tick_time
        }
        hulldamage <- damage(armormatrix,weapon6[[8]][[min(weapon6shots,length(weapon6[[8]]))]],startingarmor,minimumarmormultiplier,weapon6[[1]],hitstrength,weapon6mult)
        hullhp <- hullhp - hulldamage
        hullhp <- max(hullhp, 0)
      }
    }
    weapon6shots <- weapon6shots + weapon6[[3]][timepoint]
  }
 
  shots <- weapon7[[3]][[timepoint]]
 
 
  #skip the whole thing if we are not firing
  if (weapon7[[4]] !="" & shots > 0){
    weapon7mult <- weapon7[[2]]
    mode <- weapon7[[6]]
    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
          hardflux <- hardflux + weapon7[[1]]*weapon7mult*weapon7[[7]][min(weapon7shots,length(weapon7[[7]]))]*shieldefficacy
          hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon7[[1]]*weapon7[[3]][[timepoint]]*beam_tick_time*weapon7mult*weapon7[[7]][min(weapon7shots,length(weapon7[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
        #2. if you did not use shield to block, damage armor and hull
        #frag is a special case wrt multiplier
        if(unlist(weapon7[2])==0.25){weapon7mult = 4}
        #2.1. damage armor and hull
        hitstrength <- 0
        damage <- 0
        if(mode == GUN) {
          hitstrength <- weapon7[[1]]
          damage <- weapon7[[1]]
        }
        if(mode == BEAM) {
          hitstrength <- weapon7[[1]]/2
          damage <- weapon7[[1]]*weapon7[[3]][[timepoint]]*beam_tick_time
        }
        hulldamage <- damage(armormatrix,weapon7[[8]][[min(weapon7shots,length(weapon7[[8]]))]],startingarmor,minimumarmormultiplier,weapon7[[1]],hitstrength,weapon7mult)
        hullhp <- hullhp - hulldamage
        hullhp <- max(hullhp, 0)
      }
    }
    weapon7shots <- weapon7shots + weapon7[[3]][timepoint]
  }
 
  shots <- weapon8[[3]][[timepoint]]
 
 
  #skip the whole thing if we are not firing
  if (weapon8[[4]] !="" & shots > 0){
    weapon8mult <- weapon8[[2]]
    mode <- weapon8[[6]]
    if(mode == BEAM) {
      shots <- 1
    }
    for (s in 1:shots){
      #1. use shield to block if you can
      if (test > 0){
        #hard flux
        if(mode == GUN){
          hardflux <- hardflux + weapon8[[1]]*weapon8mult*weapon8[[7]][min(weapon8shots,length(weapon8[[7]]))]*shieldefficacy
          hardflux <- min(hardflux, fluxcap-softflux)
        }
        if(mode == BEAM){
          softflux <- softflux + weapon8[[1]]*weapon8[[3]][[timepoint]]*beam_tick_time*weapon8mult*weapon8[[7]][min(weapon8shots,length(weapon8[[7]]))]*shieldefficacy
          softflux <- min(softflux, fluxcap-hardflux)
        }
        shieldblock <- 1
      } else {
        #2. if you did not use shield to block, damage armor and hull
        #frag is a special case wrt multiplier
        if(unlist(weapon8[2])==0.25){weapon8mult = 4}
        #2.1. damage armor and hull
        hitstrength <- 0
        damage <- 0
        if(mode == GUN) {
          hitstrength <- weapon8[[1]]
          damage <- weapon8[[1]]
        }
        if(mode == BEAM) {
          hitstrength <- weapon8[[1]]/2
          damage <- weapon8[[1]]*weapon8[[3]][[timepoint]]*beam_tick_time
        }
        hulldamage <- damage(armormatrix,weapon8[[8]][[min(weapon8shots,length(weapon8[[8]]))]],startingarmor,minimumarmormultiplier,weapon8[[1]],hitstrength,weapon8mult)
        hullhp <- hullhp - hulldamage
        hullhp <- max(hullhp, 0)
      }
    }
    weapon8shots <- weapon8shots + weapon8[[3]][timepoint]
  }
 
  armorhp <- sum(armormatrix)*15/((ship[[6]]+4)*5)
  if(hullhp==0) armorhp <- 0
 
  if (shieldblock != 0) fluxdissip <- fluxdissip - shieldupkeep
 
  if (softflux > 0){
    if (softflux > fluxdissip) softflux <- softflux - fluxdissip
    else {
      fluxdissip <- max(0,fluxdissip - softflux)
      softflux <- 0
    }
  }
  if (hardflux > 0 & shieldblock == 0){
    hardflux <- max(0,hardflux - fluxdissip)
  }
  if(hullhp > 0){} else {
    softflux <- 0
    hardflux <- 0
  }
  return(list(timepoint, softflux, hardflux, armorhp, hullhp, shieldregen, shieldmax, startingarmor,armormatrix))
}

totaltime <- time_limit

armorhp <- ship[4]
shieldhp <- ship[3]
hullhp <- ship[1]
fluxdissip <- ship[2]
softflux <- 0
hardflux <- 0
fluxcap <- ship[3]
armorhp <- ship[4]
startingarmor <- ship[4]
shieldefficacy <- ship[8]
shieldupkeep <- ship[9]

timeseriesarray <- data.frame(matrix(ncol = 4,nrow=0))

for (t in 1:totaltime){
  state <- timeseries(t,softflux,hardflux,armorhp,hullhp,shieldregen,shieldmax,startingarmor,armormatrix)
  softflux <- state[[2]]
  hardflux <- state[[3]]
  armorhp <- state[[4]]
  hullhp <- state[[5]]
  flux <- softflux + hardflux
  armormatrix <- state[[9]]
  if(hullhp == 0){flux <- 0}
  timeseriesarray <- rbind(timeseriesarray , c(state[[1]], flux/fluxcap*100, hardflux/fluxcap*100, softflux/fluxcap*100, state[[4]]/startingarmor*100, state[[5]]/ship[1]*100))
 
}

colnames(timeseriesarray) <-  c("Time", "Flux", "Hardflux", "Softflux", "Armor", "Hull")
weaponstitle <- paste(unlist(weapon1[4]),unlist(weapon2[4]),unlist(weapon3[4]),unlist(weapon4[4]),unlist(weapon5[4]),unlist(weapon6[4]),unlist(weapon7[4]),unlist(weapon8[4]))


ggplot(timeseriesarray, aes(x=Time))  +
  geom_line(aes(y = Flux, color = "Flux")) +
  geom_line(aes(y = Hardflux, color = "Hardflux")) +
  geom_line(aes(y = Softflux, color = "Softflux")) +
  geom_line(aes(y = Armor, color="Armor")) +
  geom_line(aes(y = Hull, color="Hull")) +
  scale_colour_manual("",
                      breaks = c("Flux", "Softflux", "Hardflux", "Armor", "Hull"),
                      values = c("magenta", "lightsteelblue", "blue", "red", "maroon")) +
  ylab("% max") +
  xlab("Time (s)") +
  labs(title=weaponstitle)


[close]
« Last Edit: November 21, 2022, 05:24:25 AM by CapnHector »
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 #123 on: November 21, 2022, 09:10:24 AM »

All right here's the code for weapons optimization. Sorry if it's a little janky, I wrote most of it on the bus again and then during a break. And most of the ship data isn't here.

However, let me tell you, this is blazing fast with the new functions. Like the result is printed instantly when the function is done loading, instead of taking 20 minutes per ship. The speedup must be 100x rather than 10x.

code
Code


#operating modes DO NOT CHANGE CODE WILL BREAK IF GUN IS NOT 0 AND BEAM IS NOT 1 AS THE LITERAL INTEGER VALUE IS USED

GUN <- 0
BEAM <- 1
#ships -  NOT FIXED YET EXCEPT DOMINATOR
#ship, hullhp, flux dissipation, maximum flux, startingarmor, widthinpixels, armorcells, shieldwidth, shieldefficacy, shiedlupkeep, name
#glimmer <- c(1500, 250/0.6, 2500/0.6, 200, 78, 5, 78*2, 0.6, "glimmer")
#brawlerlp <- c(2000, 500/0.8, 3000/0.8, 450,110,floor(110/15), "brawlerlp")
#vanguard <- c(3000, 150, 2000, 600, 104, floor(104/15),"vanguard")
#tempest <- c(1250, 225/0.6, 2500/0.6, 200,64,floor(64/15), "tempest")
#medusa <- c(3000,400/0.6,6000/0.6,300,134,floor(134/15), "medusa")
#hammerhead <- c(5000,250/0.8,4200/0.8,500,108,floor(108/16.4), "hammerhead")
#enforcer <- c(4000,200,4000,900,136,floor(136/15), "enforcer")
dominator <- c(14000, 500, 10000, 1500, 220, 12, 440, 1.0, 200, "dominator")
#fulgent <- c(5000,300/0.6,5000/0.6,450, 160, floor(160/15), "fulgent")
#brilliant <- c(8000,600/0.6,10000/0.6,900,160,floor(160/20),"brilliant")
#radiant <- c(20000,1500/0.6,25000/0.6,1500,316,floor(316/30),"radiant")
#onslaught <- c(20000,600,17000,1750,288,floor(288/30),"onslaught")
#aurora <- c(8000,800/0.8,11000/0.8,800,128,floor(128/28), "aurora")
#paragon <- c(18000,1250/0.6,25000/0.6,1500,330,floor(330/30),"paragon")
#conquest <- c(12000,1200/1.4,20000/1.4,1200,190,floor(190/30),"conquest")
#champion <- c(10000,550/0.8,10000/0.8,1250, 180,floor(180/24),"champion")

#ships <- list(glimmer,brawlerlp,vanguard,tempest,medusa,hammerhead,enforcer,dominator,fulgent,brilliant,radiant,onslaught,aurora,paragon,conquest,champion)
ships <- list(dominator)
#engagementrange
range <- 1000

minimumarmormultiplier <- 0.05

#weaponaccuracy - this will be made a function of time and weapon later. the accuracy of a hellbore is 10
acc <- 10

#fudge factor
errorsd <- 0.05
#the fudge factor should be a function of range (more error in position at greater range), but not a function of weapon firing angle, and be expressed in terms of pixels
error <- errorsd*range

#time limit for a single combat
time_limit <- 500



beam_tick <- 1/10


G <- function(y) return(y*pnorm(y) + dnorm(y))
#a is the SD of the normal distribution and b is the parameter of the uniform distribution
hit_probability_coord_lessthan_x <- function(z, a, b) return(a/2/b*(G(z/a+b/a)-G(z/a-b/a)))


# this function generates the shot distribution (a bhattacharjee distribution for the
#non-trivial case)
hit_distribution <- function(upperbounds, standard_deviation, spread){
  vector <- vector(mode="double", length = length(upperbounds))
  if (standard_deviation == 0){
    if (spread == 0){
      vector[1] <- 0
      for (j in 2:(length(upperbounds))) {
        #if both spread and standard deviation are 0 then all shots hit 1 cell. this should be so even if
        #the ship has an even number of cells to prevent ships with even no. cells appearing tougher which is not
        #the case in the real game most likely
        if ((upperbounds[j] >= 0) & (upperbounds[j-1] < 0)) vector[j] <- 1
      }
      #return part of a box
    } else {
      vector[1] <- min(1,max(0,(upperbounds[1]+spread))/(2*spread))
      for (j in 2:(length(upperbounds)-1)) vector[j] <- min(1,max(0,(upperbounds[j]+spread))/(2*spread)) - min(1,max(0,(upperbounds[j-1]+spread))/(2*spread))
      vector[length(upperbounds)] <- 1-min(1,max(0,(upperbounds[length(upperbounds)]+spread))/(2*spread))
    }
  } else {
    if (spread != 0){
      vector[1] <- hit_probability_coord_lessthan_x(upperbounds[1], standard_deviation, spread)
      for (j in 2:(length(upperbounds)-1)) vector[j] <- (hit_probability_coord_lessthan_x(upperbounds[j], standard_deviation, spread)-hit_probability_coord_lessthan_x(upperbounds[j-1], standard_deviation, spread))
      vector[length(upperbounds)] <- (1-hit_probability_coord_lessthan_x(upperbounds[length(upperbounds)-1], standard_deviation, spread))
    } else {
      #if spread is 0 but standard deviation is not 0 we have a normal distribution
      for (j in 1:(length(upperbounds)-1)) vector[j] <- pnorm(upperbounds[j], mean=0, sd=standard_deviation)
      for (j in 2:(length(upperbounds)-1)) vector[j] <- vector[j] - pnorm(upperbounds[j-1], mean=0, sd=standard_deviation)
      vector[length(upperbounds)] <- 1-pnorm(upperbounds[length(upperbounds)-1], mean=0, sd=standard_deviation)
    }
   
  }
  return(vector)
}

#this is not really necessary, just a wrapper for the above new function to fit into the old code
createdistribution <- function(acc,mode){
  return(hit_distribution(anglerangevector,error,acc))
}

# this is the default distribution of damage to armor cells
b <- matrix(0,nrow=5,ncol=5)
b[1:5,2:4] <- 1/30
b[2:4,1:5] <- 1/30
b[2:4,2:4] <- 1/15
b[1,1] <- 0
b[1,5] <- 0
b[5,1] <- 0
b[5,5] <- 0

#this function returns the chance to hit a simple width
hitchance <- function(acc,error,lowerpoint,higherpoint){
  return(hit_distribution(c(lowerpoint,higherpoint,0),error,acc)[[2]])
}
#this function generates a sum of matrices multiplied by the distribution

createhitmatrix <- function(acc){
  hitmatrix <- matrix(0,5,ship[6]+4)
  distributionvector <- createdistribution(acc)
  for (i in 1:ship[6]){
    hitmatrix[,i:(i+4)] <- hitmatrix[,i:(i+4)]+b*(distributionvector[i+1])
  }
  return(hitmatrix)
}

#for weapons with damage changing over time we need a sequence of matrices
createhitmatrixsequence <- function(accvector){
  hitmatrixsequence <- list()
  for (i in 1:length(accvector)){
    hitmatrixsequence[[i]] <- createhitmatrix(accvector[i])
  }
  return(hitmatrixsequence)
}


#this function is absolutely performance critical (run millions of times) so write it in C++ using
#elementary operations
library(Rcpp)
#what this function shall do is:
#1 ) modify the armor matrix in the R global environment by subtracting armor damage
#2 ) return hull damage
#A is the armor matrix
#rows_A is the number of rows in A
#cols_A is the number of columns in A
#D is the damage matrix (note: we do not need a rows_D etc as these matrices must be same size)
#a is starting armor for the whole ship
#a_mod is minimum armor
#d is raw damage from whole weapon shot
#h is hit strength of weapon shot
#m is modifier, 2= kinetic, 1 = energy, 0.5 = he, 4 = frag
#hd is hull damage
#do NOT pass a hit strength of 0 to this function as it does not check for dividing by zero
#overall, this function does no sanity or safety checking so be careful with it
cppFunction('double damage(NumericMatrix A, NumericMatrix D, double a, double a_mod, double d, double h, double m){
  double hd = 0;
  int rows_A = A.nrow();
  int cols_A = A.ncol();
  a = a/(rows_A*cols_A);
  for (int j = 0; j < cols_A; j++){
  for (int i = 0; i < rows_A; i++){
    double armor = std::max(A(i,j), a_mod*a);
    double probmult = D(i,j);
    double adjusted_d = d*h/m/(armor+h/m)*probmult;
    if (adjusted_d <= armor){
      A(i,j) = A(i,j) - adjusted_d;
      A(i,j) = std::max(A(i,j), 0.0);
    }
    if (adjusted_d > armor){
      A(i,j) = 0.0;
      adjusted_d = (adjusted_d - armor)*m;
      hd = hd + adjusted_d;
    }
  }
  }
  return hd;
}
')

#general function to generate ticks
#1. general constants
#the interval of discrete time (time lattice parameter) we are using in the model, in seconds
time_interval <- 1
#how long 1 tick of a beam lasts, in seconds
beam_tick <- 1/10
#minimum interval that exists in the game, in case a modder has somehow specified a lower value for something
global_minimum_time <- 0.05
#operating modes
UNLIMITED <- -1

#times in seconds, ammoregen is in ammo / second
hits <- function(chargeup, chargedown, burstsize, burstdelay, ammo=UNLIMITED, ammoregen=0, reloadsize=0, traveltime=0, mode=GUN){
  #specify sane minimum delays, since the game enforces weapons can only fire once every 0.05 sec
  #for beams, refiring delay is given by burstdelay, for guns it is burstdelay in case burstdelay is > 0 (==0 is shotgun) and chargedown
  if(burstdelay > 0 | mode == BEAM) burstdelay <- max(burstdelay, global_minimum_time)
  if(mode == GUN) chargedown <- max(chargedown, global_minimum_time)
  #this vector will store all the hit time coordinates
  #current time
  #insert a very small fraction here to make time round correctly
  time <- 0.001
  #maximum ammo count is ammo given at start
  maxammo <- ammo
  #this is used to do ammo regeneration, 0 = not regenerating ammo, 1 = regenerating ammo
  regeneratingammo <- 0
  ammoregentimecoordinate <- 0
  ammoregenerated <- 0
 
  #we are firing a gun
  if (mode == GUN) {
    Hits <- vector(mode="double", length = 0)
    while(time < time_limit){
      time <- time + chargeup
      if(time - ammoregentimecoordinate > 1/ammoregen){
        ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
        ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
        if(ammoregenerated >= reloadsize){
          ammo <- ammo+ ammoregenerated
          ammoregenerated <- 0
        }
        if(ammo >= maxammo){
          ammo <- maxammo
          regeneratingammo <- 0
        }
      }
     
      if (burstdelay == 0) {
        for (i in 1:burstsize) {
          if (ammo != 0){
            Hits <- c(Hits, time + traveltime)
            ammo <- ammo - 1
            if (regeneratingammo == 0) {
              ammoregentimecoordinate <- time
              regeneratingammo <- 1
            }
          }
        }
      }
      if (burstdelay > 0) {
        for (i in 1:burstsize) {
          if (ammo != 0){
            Hits <- c(Hits, time + traveltime)
            time <- time + burstdelay
            ammo <- ammo -1
            if (regeneratingammo == 0) {
              ammoregentimecoordinate <- time
              regeneratingammo <- 1
            }
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
           
          }
        }
      }
      time <- time+chargedown
      if(time - ammoregentimecoordinate > 1/ammoregen){
        ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
        ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
        if(ammoregenerated >= reloadsize){
          ammo <- ammo+ ammoregenerated
          ammoregenerated <- 0
        }
        if(ammo >= maxammo){
          ammo <- maxammo
          regeneratingammo <- 0
        }
      }
    }
    timeseries <- vector(mode="integer", length = time_limit/time_interval)
    timeseries[1] <- length(Hits[Hits >= 0 & Hits <= 1*time_interval])
    for (i in 2:time_limit/time_interval) timeseries[i] <- length(Hits[Hits > (i-1)*time_interval & Hits <= i*time_interval])
    return(timeseries)
  }
  #we are firing a beam
  if (mode == BEAM) {
    chargeup_ticks <- chargeup/beam_tick
    chargedown_ticks <- chargedown/beam_tick
    burst_ticks <- burstsize/beam_tick
    #for a beam we will instead use a matrix to store timepoint and beam intensity at timepoint
    beam_matrix <- matrix(nrow=0,ncol=2)
    #burst size 0 <- the beam never stops firing
    if(burstsize == 0){
      for (i in 1:chargeup_ticks) {
        #beam intensity scales quadratically during chargeup, so
      }
      while ( time < time_limit) {
        beam_matrix <- rbind(beam_matrix,c(time, 1))
        time <- time+beam_tick
      }
    } else {
      while (time < time_limit) {
        if (ammo != 0){
          ammo <- ammo - 1
          if (chargeup_ticks > 0){
            for (i in 1:chargeup_ticks) {
              beam_matrix <- rbind(beam_matrix,c(time, (i*beam_tick)^2))
              time <- time+beam_tick
              if (regeneratingammo == 0) {
                ammoregentimecoordinate <- time
                regeneratingammo <- 1
              }
              if(time - ammoregentimecoordinate > 1/ammoregen){
                ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
                ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
                if(ammoregenerated >= reloadsize){
                  ammo <- ammo+ ammoregenerated
                  ammoregenerated <- 0
                }
                if(ammo >= maxammo){
                  ammo <- maxammo
                  regeneratingammo <- 0
                }
              }
            }
          }
          for (i in 1:burst_ticks){
            beam_matrix <- rbind(beam_matrix,c(time, 1))
            time <- time+beam_tick
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
          }
         
          if (chargedown_ticks > 0){
            for (i in 1:chargedown_ticks){
              beam_matrix <- rbind(beam_matrix,c(time, ((chargedown_ticks-i)*beam_tick)^2))
              time <- time+beam_tick
            }
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
          }
          time <- time + burstdelay
          if(time - ammoregentimecoordinate > 1/ammoregen){
            ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
            ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
            if(ammoregenerated >= reloadsize){
              ammo <- ammo+ ammoregenerated
              ammoregenerated <- 0
            }
            if(ammo >= maxammo){
              ammo <- maxammo
              regeneratingammo <- 0
            }
          }
        }
        time <- time + global_minimum_time
        if(time - ammoregentimecoordinate > 1/ammoregen){
          ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
          ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
          if(ammoregenerated >= reloadsize){
            ammo <- ammo+ ammoregenerated
            ammoregenerated <- 0
          }
          if(ammo >= maxammo){
            ammo <- maxammo
            regeneratingammo <- 0
          }
        }
      }
    }
    timeseries <- vector(mode="double", length = time_limit/time_interval)
    for (i in 1:length(timeseries)) {
      timeseries[i] <- sum(beam_matrix[beam_matrix[,1] < i & beam_matrix[,1] > i-1,2])
    }
    return(timeseries)
  }
}

squalltics <- hits(0,10,20,0.5)
locusttics <- hits(0,5,40,0.1)
#special
hurricanetics <- hits(0,15,9,0)
harpoontics <- hits(0,8.25,4,0.25)
sabottics <- hits(0,8.75,2,0.25)
gausstics <- hits(1,1,1,0)
ionbeamtics <- hits(0.1,0.1,0,0,mode=BEAM)

#WEAPON ACCURACY
#missiles do not have spread
squallacc <- c(0)
locustacc <- c(0)
hurricaneacc <- c(0)
harpoonacc <- c(0)
sabotacc <- c(0)

#gauss has a spread of 0 and no increase per shot
gaussacc <- c(0)
#hephaestus has a spread of 0 and it increases by 2 per shot to a max of 10
#hephaestusacc <- c(seq(0,10,2))
#mark ix has a spread of 0 and it increases by 2 per shot to a max of 15
#markixacc <- c(seq(0,15,2),15)
#mjolnir has a spread of 0 and it increases by 1 per shot to a max of 5
#mjolniracc <- c(seq(1,5,1))
#hellbore has a spread of 10
#hellboreacc <- c(10)
#storm needler has a spread of 10
#stormneedleracc <- c(10)
ionbeamacc <- c(0)

#damage per shot, damage type (2=kinetic, 0.5=he, 0.25=frag, 1=energy), tics, weapon name, weapon accuracy over time, hit chance, mode
squall <- list(250, 2, squalltics, "Squall", squallacc, GUN)
locust <- list(200, 0.25, locusttics, "Locust", locustacc, GUN)
hurricane <- list(500, 0.5, hurricanetics, "Hurricane", hurricaneacc, GUN)
harpoon <- list(750, 0.5, harpoontics, "Harpoon", harpoonacc, GUN)
sabot <- list(200, 2, sabottics, "Sabot", sabotacc, GUN)
gauss <- list(700, 2, gausstics, "Gauss", gaussacc, GUN)
#hephaestus <- list(120, 0.5, hephaestustics, "Hephaestus", hephaestusacc)
#markix <- list(200, 2, markixtics, "Mark IX", markixacc)
#mjolnir <- list(400, 1, mjolnirtics, "Mjolnir", mjolniracc)
#hellbore <- list(750, 0.5, hellboretics, "Hellbore", hellboreacc)
#stormneedler <- list(50, 2, stormneedlertics, "Storm Needler", stormneedleracc)

#for beams, damage per second, and then the rest as previously
ionbeam <- list(1000, 1, ionbeamtics, "Super Ion Beam", ionbeamacc, BEAM)
dummy <- list(0,0,c(seq(0,time_limit,1)),"",c(0),c(0),GUN)

#which weapons are we studying?

weapon1choices <- list(squall, locust, hurricane)
weapon2choices <- list(squall, locust, hurricane)
weapon3choices <- list(harpoon, sabot)
weapon4choices <- list(harpoon, sabot)
weapon5choices <- list(ionbeam, gauss)
weapon6choices <- list(ionbeam, gauss)
weapon7choices <- list(ionbeam, gauss)
weapon8choices <- list(ionbeam, gauss)

#how many unique weapon loadouts are there?

#get names of weapons from a choices list x
getweaponnames <- function(x){
  vector <- vector(mode="character")
  for (i in 1:length(x)){
    vector <- cbind(vector, x[[i]][[4]])
  }
  return(vector)
}
#convert the names back to numbers when we are done based on a weapon choices list y
convertweaponnames <- function(x, y){
  vector <- vector(mode="integer")
  for (j in 1:length(x)) {
    for (i in 1:length(y)){
      if(x[j] == y[[i]][[4]]) vector <- cbind(vector, i)
    }
  }
  return(vector)
}

#this section of code generates a table of all unique loadouts that we can create using the weapon choices available
generatepermutations <- 1
if (generatepermutations == 1){
  #enumerate weapon choices as integers
 
  perm1 <- seq(1,length(weapon1choices),1)
  perm2 <- seq(1,length(weapon2choices),1)
  perm3 <- seq(1,length(weapon3choices),1)
  perm4 <- seq(1,length(weapon4choices),1)
  perm5 <- seq(1,length(weapon5choices),1)
  perm6 <- seq(1,length(weapon6choices),1)
  perm7 <- seq(1,length(weapon7choices),1)
  perm8 <- seq(1,length(weapon8choices),1)
 
  #create a matrix of all combinations
  perm1x2 <- expand.grid(perm1,perm2)
  #sort, then only keep unique rows
  perm1x2 <- unique(t(apply(perm1x2, 1, sort)))
 
  perm3x4 <- expand.grid(perm3,perm4)
  perm3x4 <- unique(t(apply(perm3x4, 1, sort)))
 
  perm5x6 <- expand.grid(perm5,perm6)
  perm5x6 <- unique(t(apply(perm5x6, 1, sort)))
 
  perm7x8 <- expand.grid(perm7,perm8)
  perm7x8 <- unique(t(apply(perm7x8, 1, sort)))
 
  #now that we have all unique combinations of all two weapons, create a matrix containing all combinations of these unique combinations
  allperms <- matrix(0,0,(length(perm1x2[1,])+length(perm3x4[1,])+length(perm5x6[1,])+length(perm7x8[1,])))
  for(i in 1:length(perm1x2[,1])) for(j in 1:length(perm3x4[,1])) for(k in 1:length(perm5x6[,1])) for(l in 1:length(perm7x8[,1])) allperms <- rbind(allperms, c(perm1x2[i,],perm3x4[j,],perm5x6[k,],perm7x8[l,])
  )
  #this is just for testing, can remove
  allperms
  #we save this so we don't have to compute it again
  saveRDS(allperms, file="allperms.RData")
 
} else {
  allperms <- readRDS("allperms.RData")
}

#now compute a main lookuptable to save on computing time
#the lookuptable should be a list of lists, so that
#lookuptable[[ship]][[weapon]][[1]] returns hit chance vector and
#lookuptable[[ship]][[weapon]][[2]] returns hit probability matrix
#time for some black R magic

#note: the lookuptable will be formulated such that there is a running index of weapons rather than sub-lists, so all weapons will be indexed consecutively so we have lookuptable [[1]][[1]] = [[ship1]][[weaponchoices1_choice1]], etc. So that is what the below section does.

#read or generate lookuptable
generatelookuptable <- 1
if(generatelookuptable == 1){
 
  lookuptable <- list()
 
  for (f in 1:length(ships)){
    lookuptable[[f]] <- list()
    ship <- ships[[f]]
    ship <- as.double(ship[1:9])
    #how much is the visual arc of the ship in rad?
    shipangle <- ship[5]/(2* pi *range)
   
    #how much is the visual arc of a single cell of armor in rad?
    cellangle <- shipangle/ship[6]
   
    #now assume the weapon is targeting the center of the ship's visual arc and that the ship is in the center of the weapon's firing arc
    #which cell will the shot hit, or will it miss?
    #call the cells (MISS, cell1, cell2, ... ,celli, MISS) and get a vector giving the (maximum for negative / minimum for positive) angles for hitting each
    anglerangevector <- vector(mode="double", length = ship[6]+1)
    anglerangevector[1] <- -shipangle/2
    for (i in 1:(length(anglerangevector)-1)) anglerangevector[i+1] <- anglerangevector[i]+cellangle
    #now convert it to pixels
    anglerangevector <- anglerangevector*2*pi*range
   
    weaponindexmax <- length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+length(weapon7choices)+length(weapon8choices)
   
    for (x in 1:weaponindexmax) {
      print(x)
      if(x <= length(weapon1choices)){
        weapon1<-weapon1choices[[x]]
        if(weapon1[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon1[[5]]))
          for (i in 1:length(weapon1[[5]])){
            hitchancevector[i] <- hitchance(weapon1[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon1[[5]])
        }
      }
      if((x > length(weapon1choices)) &  (x <= length(weapon1choices) + length(weapon2choices))){
        weapon2<-weapon2choices[[x-length(weapon1choices)]]
        if(weapon2[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon2[[5]]))
          for (i in 1:length(weapon2[[5]])){
            hitchancevector[i] <- hitchance(weapon2[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon2[[5]])
        }
      }
     
      if((x > length(weapon1choices) + length(weapon2choices)) &  (x <= length(weapon2choices) + length(weapon1choices) + length(weapon3choices))){
        weapon3<-weapon3choices[[x-length(weapon2choices)-length(weapon1choices)]]
        if(weapon3[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon3[[5]]))
          for (i in 1:length(weapon3[[5]])){
            hitchancevector[i] <- hitchance(weapon3[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon3[[5]])
        }
      } 
     
      if((x > length(weapon2choices) + length(weapon1choices) + length(weapon3choices)) &  (x <= length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon4choices))){
        weapon4<-weapon4choices[[x-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon4[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon4[[5]]))
          for (i in 1:length(weapon4[[5]])){
            hitchancevector[i] <- hitchance(weapon4[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon4[[5]])
        }
      }
      if((x > length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon4choices)) &  (x <= length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon5choices))){
        weapon5<-weapon5choices[[x-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon5[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon5[[5]]))
          for (i in 1:length(weapon5[[5]])){
            hitchancevector[i] <- hitchance(weapon5[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon5[[5]])
        }
      }
      if((x > length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon5choices)) &  (x <= length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon6choices))){
        weapon6<-weapon6choices[[x-length(weapon5choices)-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon6[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon6[[5]]))
          for (i in 1:length(weapon6[[5]])){
            hitchancevector[i] <- hitchance(weapon6[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon6[[5]])
        }
      }
      if((x > length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon6choices)) &  (x <= length(weapon6choices) + length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon7choices))){
        weapon7<-weapon7choices[[x-length(weapon6choices)-length(weapon5choices)-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon7[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon7[[5]]))
          for (i in 1:length(weapon7[[5]])){
            hitchancevector[i] <- hitchance(weapon7[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon7[[5]])
        }
      }
      if((x > length(weapon6choices) + length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon7choices)) &  (x <= length(weapon7choices) + length(weapon6choices) + length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon8choices))){
        weapon8<-weapon8choices[[x-length(weapon7choices)-length(weapon6choices)-length(weapon5choices)-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon8[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon8[[5]]))
          for (i in 1:length(weapon8[[5]])){
            hitchancevector[i] <- hitchance(weapon8[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon8[[5]])
        }
      }
     
    }
  }
  # save this so we don't have to re-compute it
  saveRDS(lookuptable, file="lookuptable.RData")
} else {
  lookuptable <- readRDS("lookuptable.RData")
}

lookup <- function(ship, weapon, var) return(lookuptable[[ship]][[weapon]][[var]])

#go through all ships
for (f in 1:length(ships)){
 
  ship <- ships[[f]]
  #format ship data types appropriately
  shipname <- ship[[10]]
  ship <- as.double(ship[1:9])
 
 
 
 
 
 
 
  timeseriesarray <- data.frame(matrix(ncol = 7,nrow=0))
 
 
 
  timetokill=0
 
 
  shieldblock <- 0
 

 
  timeseries <- function(timepoint, softflux, hardflux, armorhp, hullhp, fluxdissip, fluxcap, startingarmor,armormatrix){
    weaponacc <- 0

    #are we using shield to block?
    shieldblock <- 0
    hulldamage <- 0
   
   
    #weapon 1
    weapon1mult <- weapon1[[2]]
    weapon2mult <- weapon2[[2]]
    weapon3mult <- weapon3[[2]]
    weapon4mult <- weapon4[[2]]
    weapon5mult <- weapon5[[2]]
    weapon6mult <- weapon6[[2]]
    weapon7mult <- weapon7[[2]]
    weapon8mult <- weapon8[[2]]
   
    shots <- weapon1[[3]][[timepoint]]
    #here we must convert beam ticks to fractional shots
    shots1 <- weapon1[[3]][[timepoint]]*beam_tick^(weapon1[[6]])
    shots2 <- weapon2[[3]][[timepoint]]*beam_tick^(weapon2[[6]])
    shots3 <- weapon3[[3]][[timepoint]]*beam_tick^(weapon3[[6]])
    shots4 <- weapon4[[3]][[timepoint]]*beam_tick^(weapon4[[6]])
    shots5 <- weapon5[[3]][[timepoint]]*beam_tick^(weapon5[[6]])
    shots6 <- weapon6[[3]][[timepoint]]*beam_tick^(weapon6[[6]])
    shots7 <- weapon7[[3]][[timepoint]]*beam_tick^(weapon7[[6]])
    shots8 <- weapon8[[3]][[timepoint]]*beam_tick^(weapon8[[6]])
    #test is used to determine if we are firing or blocking this turn
    test <- (fluxcap-softflux-hardflux - weapon1[[1]]*weapon1mult*shots1 - weapon2[[1]]*weapon2mult*shots2 - weapon3[[1]]*weapon3mult*shots3 - weapon4[[1]]*weapon4mult*shots4 - weapon5[[1]]*weapon5mult*shots5 - weapon6[[1]]*weapon6mult*shots6 - weapon7[[1]]*weapon7mult*shots7 - weapon8[[1]]*weapon8mult*shots8)   
    #skip the whole thing if we are not firing
    if (weapon1[[4]] !="" & shots > 0){
      mode <- weapon1[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon1[[1]]*weapon1mult*weapon1[[7]][min(weapon1shots,length(weapon1[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon1[[1]]*weapon1[[3]][[timepoint]]*beam_tick*weapon1mult*weapon1[[7]][min(weapon1shots,length(weapon1[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon1[2])==0.25){weapon1mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon1[[1]]
            damage <- weapon1[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon1[[1]]/2
            damage <- weapon1[[1]]*weapon1[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon1[[8]][[min(weapon1shots,length(weapon1[[8]]))]],startingarmor,minimumarmormultiplier,damage,hitstrength,weapon1mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon1shots <- weapon1shots + weapon1[[3]][timepoint]
    }
   
    #repeat for other weapons
    shots <- weapon2[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon2[[4]] !="" & shots > 0){
      weapon2mult <- weapon2[[2]]
      mode <- weapon2[[6]]
     
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon2[[1]]*weapon2mult*weapon2[[7]][min(weapon2shots,length(weapon2[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon2[[1]]*weapon2[[3]][[timepoint]]*beam_tick*weapon2mult*weapon2[[7]][min(weapon2shots,length(weapon2[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon2[2])==0.25){weapon2mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon2[[1]]
            damage <- weapon2[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon2[[1]]/2
            damage <- weapon2[[1]]*weapon2[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon2[[8]][[min(weapon2shots,length(weapon2[[8]]))]],startingarmor,minimumarmormultiplier,weapon2[[1]],hitstrength,weapon2mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon2shots <- weapon2shots + weapon2[[3]][timepoint]
    }
   
   
    shots <- weapon3[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon3[[4]] !="" & shots > 0){
      weapon3mult <- weapon3[[2]]
      mode <- weapon3[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon3[[1]]*weapon3mult*weapon3[[7]][min(weapon3shots,length(weapon3[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon3[[1]]*weapon3[[3]][[timepoint]]*beam_tick*weapon3mult*weapon3[[7]][min(weapon3shots,length(weapon3[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon3[2])==0.25){weapon3mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon3[[1]]
            damage <- weapon3[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon3[[1]]/2
            damage <- weapon3[[1]]*weapon3[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon3[[8]][[min(weapon3shots,length(weapon3[[8]]))]],startingarmor,minimumarmormultiplier,weapon3[[1]],hitstrength,weapon3mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon3shots <- weapon3shots + weapon3[[3]][timepoint]
    }
   
   
    shots <- weapon4[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon4[[4]] !="" & shots > 0){
      weapon4mult <- weapon4[[2]]
      mode <- weapon4[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon4[[1]]*weapon4mult*weapon4[[7]][min(weapon4shots,length(weapon4[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon4[[1]]*weapon4[[3]][[timepoint]]*beam_tick*weapon4mult*weapon4[[7]][min(weapon4shots,length(weapon4[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon4[2])==0.25){weapon4mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon4[[1]]
            damage <- weapon4[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon4[[1]]/2
            damage <- weapon4[[1]]*weapon4[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon4[[8]][[min(weapon4shots,length(weapon4[[8]]))]],startingarmor,minimumarmormultiplier,weapon4[[1]],hitstrength,weapon4mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon4shots <- weapon4shots + weapon4[[3]][timepoint]
    }
   
   
    shots <- weapon5[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon5[[4]] !="" & shots > 0){
      weapon5mult <- weapon5[[2]]
      mode <- weapon5[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon5[[1]]*weapon5mult*weapon5[[7]][min(weapon5shots,length(weapon5[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon5[[1]]*weapon5[[3]][[timepoint]]*beam_tick*weapon5mult*weapon5[[7]][min(weapon5shots,length(weapon5[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon5[2])==0.25){weapon5mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon5[[1]]
            damage <- weapon5[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon5[[1]]/2
            damage <- weapon5[[1]]*weapon5[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon5[[8]][[min(weapon5shots,length(weapon5[[8]]))]],startingarmor,minimumarmormultiplier,weapon5[[1]],hitstrength,weapon5mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon5shots <- weapon5shots + weapon5[[3]][timepoint]
    }
   
    shots <- weapon6[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon6[[4]] !="" & shots > 0){
      weapon6mult <- weapon6[[2]]
      mode <- weapon6[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon6[[1]]*weapon6mult*weapon6[[7]][min(weapon6shots,length(weapon6[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon6[[1]]*weapon6[[3]][[timepoint]]*beam_tick*weapon6mult*weapon6[[7]][min(weapon6shots,length(weapon6[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon6[2])==0.25){weapon6mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon6[[1]]
            damage <- weapon6[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon6[[1]]/2
            damage <- weapon6[[1]]*weapon6[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon6[[8]][[min(weapon6shots,length(weapon6[[8]]))]],startingarmor,minimumarmormultiplier,weapon6[[1]],hitstrength,weapon6mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon6shots <- weapon6shots + weapon6[[3]][timepoint]
    }
   
    shots <- weapon7[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon7[[4]] !="" & shots > 0){
      weapon7mult <- weapon7[[2]]
      mode <- weapon7[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon7[[1]]*weapon7mult*weapon7[[7]][min(weapon7shots,length(weapon7[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon7[[1]]*weapon7[[3]][[timepoint]]*beam_tick*weapon7mult*weapon7[[7]][min(weapon7shots,length(weapon7[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon7[2])==0.25){weapon7mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon7[[1]]
            damage <- weapon7[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon7[[1]]/2
            damage <- weapon7[[1]]*weapon7[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon7[[8]][[min(weapon7shots,length(weapon7[[8]]))]],startingarmor,minimumarmormultiplier,weapon7[[1]],hitstrength,weapon7mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon7shots <- weapon7shots + weapon7[[3]][timepoint]
    }
   
    shots <- weapon8[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon8[[4]] !="" & shots > 0){
      weapon8mult <- weapon8[[2]]
      mode <- weapon8[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon8[[1]]*weapon8mult*weapon8[[7]][min(weapon8shots,length(weapon8[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon8[[1]]*weapon8[[3]][[timepoint]]*beam_tick*weapon8mult*weapon8[[7]][min(weapon8shots,length(weapon8[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon8[2])==0.25){weapon8mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon8[[1]]
            damage <- weapon8[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon8[[1]]/2
            damage <- weapon8[[1]]*weapon8[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon8[[8]][[min(weapon8shots,length(weapon8[[8]]))]],startingarmor,minimumarmormultiplier,weapon8[[1]],hitstrength,weapon8mult)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon8shots <- weapon8shots + weapon8[[3]][timepoint]
    }
   
    armorhp <- sum(armormatrix)*15/((ship[[6]]+4)*5)
    if(hullhp==0) armorhp <- 0
   
    if (shieldblock != 0) fluxdissip <- fluxdissip - shieldupkeep
   
    if (softflux > 0){
      if (softflux > fluxdissip) softflux <- softflux - fluxdissip
      else {
        fluxdissip <- max(0,fluxdissip - softflux)
        softflux <- 0
      }
    }
    if (hardflux > 0 & shieldblock == 0){
      hardflux <- max(0,hardflux - fluxdissip)
    }
    if(hullhp > 0){} else {
      softflux <- 0
      hardflux <- 0
    }
    return(list(timepoint, softflux, hardflux, armorhp, hullhp, fluxdissip, fluxcap, startingarmor,armormatrix))
  }
 
  totaltime = 500
 
 
 
  armorhp <- ship[4]
  shieldhp <- ship[3]
  hullhp <- ship[1]
  fluxdissip <- ship[2]
  softflux <- 0
  hardflux <- 0
  fluxcap <- ship[3]
  armorhp <- ship[4]
  startingarmor <- ship[4]
  shieldefficacy <- ship[8]
  shieldupkeep <- ship[9]
 
  weapon1shots <- 1
  weapon2shots <- 1
  weapon3shots <- 1
  weapon4shots <- 1
  weapon5shots <- 1
  weapon6shots <- 1
  weapon7shots <- 1
  weapon8shots <- 1
 
  armormatrix <- matrix(ship[4]/15,5,ship[6]+4)
 
  #now what we do here is we go through all the permutations using the running index, which is i+j+k+l+m+n+o+p for weapons 8
  for (z in 1:length(allperms[,1])) {
    i <- allperms[z,1]
    j <- allperms[z,2]
    k <- allperms[z,3]
    l <- allperms[z,4]
    m <- allperms[z,5]
    n <- allperms[z,6]
    o <- allperms[z,7]
    p <- allperms[z,8]
   
    #for (i in 1:length(weapon1choices)) {
    weapon1<-weapon1choices[[i]]
    #  for (j in 1:length(weapon2choices)) {
    weapon2<-weapon2choices[[j]]
    #    for (k in 1:length(weapon3choices)) {
    weapon3<-weapon3choices[[k]]
    #      for (l in 1:length(weapon4choices)) {
    weapon4<-weapon4choices[[l]]
    #        for (m in 1:length(weapon5choices)) {
    weapon5<-weapon5choices[[m]]
    #          for (n in 1:length(weapon6choices)) {
    weapon6<-weapon6choices[[n]]
    #            for (o in 1:length(weapon7choices)) {
    weapon7<-weapon7choices[[o]]
    #              for (p in 1:length(weapon8choices)) {
    weapon8<-weapon8choices[[p]]
    #lookup <- function(ship, weapon, var) return(lookuptable[[ship]][[weapon]][[var]])
    if(weapon1[4] != ""){
      weapon1[[7]] <- lookup(f,i,1)
      weapon1[[8]] <- lookup(f,i,2)
    }
   
    if(weapon2[4] != ""){
      weapon2[[7]] <- lookup(f,length(weapon1choices)+j,1)
      weapon2[[8]] <- lookup(f,length(weapon1choices)+j,2)
    }
   
    if(weapon3[4] != ""){
      weapon3[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+k,1)
      weapon3[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+k,2)
    }
   
    if(weapon4[4] != ""){
      weapon4[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+l,1)
      weapon4[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+l,2)
    }
   
    if(weapon5[4] != ""){
      weapon5[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+m,1)
      weapon5[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+m,2)
    }
   
    if(weapon6[4] != ""){
      weapon6[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+n,1)
      weapon6[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+n,2)
    }
    if(weapon7[4] != ""){
      weapon7[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+o,1)
      weapon7[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+o,2)
    }
    if(weapon8[4] != ""){
      weapon8[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+length(weapon7choices)+p,1)
      weapon8[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+length(weapon7choices)+p,2)
    }
   
    #time series - run time series at point t, save it to state, update values according to state, re-run time series, break if ship dies
    for (t in 1:totaltime){
      state <- timeseries(t,softflux,hardflux,armorhp,hullhp,fluxdissip,fluxcap,startingarmor,armormatrix)
      softflux <- state[[2]]
      hardflux <- state[[3]]
      armorhp <- state[[4]]
      hullhp <- state[[5]]
      flux <- softflux + hardflux
      armormatrix <- state[[9]]
      if(hullhp == 0){flux <- 0
      if (timetokill == 0){timetokill <- t
      break}
      }
     
    }
    if (timetokill ==0){timetokill <- NA}
   
    tobind <- c(timetokill,unlist(weapon1[4]),unlist(weapon2[4]),unlist(weapon3[4]),unlist(weapon4[4]),unlist(weapon5[4]),unlist(weapon6[4]),unlist(weapon7[4]),unlist(weapon8[4]))
    timeseriesarray <- rbind(timeseriesarray,tobind)
   
    armorhp <- ship[4]
    shieldhp <- ship[3]
    hullhp <- ship[1]
    fluxdissip <- ship[2]
    softflux <- 0
    hardflux <- 0
    fluxcap <- ship[3]
    armorhp <- ship[4]
    startingarmor <- ship[4]
    shieldefficacy <- ship[8]
    shieldupkeep <- ship[9]
   
    weapon1shots <- 1
    weapon2shots <- 1
    weapon3shots <- 1
    weapon4shots <- 1
    weapon5shots <- 1
    weapon6shots <- 1
    weapon7shots <- 1
    weapon8shots <- 1
    armormatrix <- matrix(ship[4]/15,5,ship[6]+4)
    timetokill <- 0
    #          }
    #        }
    #      }
    #    }
    #  }
    #}
    # }
  }
  #}
  colnames(timeseriesarray) <-  c("Timetokill", "Weapon1", "Weapon2", "Weapon3", "Weapon4", "Weapon5", "Weapon6", "Weapon7", "Weapon8")
 
  sortbytime <- timeseriesarray[order(as.integer(timeseriesarray$Timetokill)),]
 
  write.table(sortbytime, file = paste("optimizeweaponsbytime",shipname,"allweaponswithacc.txt", sep=""), row.names=FALSE, sep="\t")
}
[close]

Output
Spoiler

"Timetokill"   "Weapon1"   "Weapon2"   "Weapon3"   "Weapon4"   "Weapon5"   "Weapon6"   "Weapon7"   "Weapon8"
"4"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"4"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Locust"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Locust"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"4"   "Locust"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Locust"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Locust"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"4"   "Locust"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"4"   "Locust"   "Hurricane"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"5"   "Squall"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"5"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"5"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"5"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"5"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"5"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"5"   "Locust"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"5"   "Hurricane"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"6"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"6"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"6"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"6"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Squall"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"6"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"6"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"6"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"6"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"6"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"6"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Locust"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"6"   "Locust"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"6"   "Locust"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Locust"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"6"   "Locust"   "Locust"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Hurricane"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"6"   "Hurricane"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Hurricane"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"6"   "Hurricane"   "Hurricane"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"7"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"7"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"7"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"7"   "Squall"   "Hurricane"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"
"7"   "Squall"   "Hurricane"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Squall"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"8"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Squall"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Locust"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Locust"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Locust"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Gauss"   "Gauss"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Hurricane"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Squall"   "Hurricane"   "Sabot"   "Sabot"   "Super Ion Beam"   "Super Ion Beam"   "Gauss"   "Gauss"
"8"   "Squall"   "Hurricane"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Squall"   "Hurricane"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Squall"   "Hurricane"   "Sabot"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Super Ion Beam"
"8"   "Locust"   "Locust"   "Harpoon"   "Harpoon"   "Gauss"   "Gauss"   "Gauss"   "Gauss"
"8"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"8"   "Locust"   "Locust"   "Harpoon"   "Sabot"   "Gauss"   "Gauss"   "Super Ion Beam"   "Gauss"
"8"   "Locust"   "Locust"   "Sabot"   "Sabot"   "Super Ion Beam"   "Gauss"   "Gauss"   "Gauss"
"
[close]

This code already has the support for studying particular loadouts btw, as we are already doing combinations listed in the allperms variable rather than all permutations.
« Last Edit: November 21, 2022, 09:31:35 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 #124 on: November 21, 2022, 09:32:01 AM »

All right here's the code for weapons optimization. Sorry if it's a little janky, I wrote most of it on the bus again and then during a break. And most of the ship data isn't here.

However, let me tell you, this is blazing fast with the new functions. Like the result is printed instantly when the function is done loading, instead of taking 20 minutes per ship. The speedup must be 100x rather than 10x.

Fantastic!  If the R code is done and polished to your satisfaction, then please tell me so I can convert it to Python.  I cannot compile the inline C++ code to call from Python because the NumericMatrix within is from RCpp rather than C++ itself.  Also, I will organize the project should have several parts, each with one responsibility, and want to know if you find any responsibility to be missing.

  • Driver: loads a configuration file and calls the other code accordingly
  • Database: loads vanilla and mod ship and weapon information into memory for the project to access
  • Analysis: a library of functions, each of which fulfills some actual purpose of ours, from optimizing loadouts to determining balance
  • Plotting: another library of functions, each of which shows the results of some analysis function
  • Interface: lets the user interact with the program without having to edit the config file and reload
« Last Edit: November 21, 2022, 09:39:45 AM by Liral »
Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #125 on: November 21, 2022, 09:56:35 AM »

The R code should be good enough, I don't think that it's necessarily worth polishing since it won't be the final product. Even if there's a hidden error in some calculation the overall structure should be correct and it appears to produce reasonable results. I think if there are such errors we'll catch them in translation - let me know if you find anything suspicious and I'll comment. Changing to C++ for the most intensively used loops seems to produce a huge performance boost so it's probably worth trying to replicate in Python.

That list seems overall correct. You could consider having the combat simulation as its own file as it's shared between several functions.
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 #126 on: November 21, 2022, 10:06:59 AM »

Are there plans to support arbitrary numbers and types of weapons rather than exactly 8 (hard coded)? Feels like that whole aspect of the code could be handled much more cleanly with loops or functions.

Also, are you planning on using an actual optimization algorithm, or are you still just brute force searching all possibilities?
Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #127 on: November 21, 2022, 10:17:46 AM »

I didn't come up with how to do it in R at the time was the main issue. I suppose in retrospect there could have been a list of lists of weapons to which weapons are added, and then you loop through that list, and use the length of that list for outputs, instead of hardcoded weapons.

Please improve the code as you see fit! I have no illusions of being (or plans to become) a competent programmer. It's just a tool for math and science for me.
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 #128 on: November 21, 2022, 11:13:59 AM »

The R code should be good enough, I don't think that it's necessarily worth polishing since it won't be the final product. Even if there's a hidden error in some calculation the overall structure should be correct and it appears to produce reasonable results. I think if there are such errors we'll catch them in translation - let me know if you find anything suspicious and I'll comment.

I just hope that catching errors won't entail re-translating R code.  :(

Quote
Changing to C++ for the most intensively used loops seems to produce a huge performance boost so it's probably worth trying to replicate in Python.

I don't need the RCpp code replicated in Python but rather written in C++ itself, to compile with a C++ compiler into a module for Python to call.

Quote
That list seems overall correct. You could consider having the combat simulation as its own file as it's shared between several functions.

Great idea!

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #129 on: November 21, 2022, 11:43:15 AM »

Writing it in pure C++ shouldn't be too bad. But is there a way to access global variables from the Python environment within the C++ code? Ie must a way be devised to pass the matrices to the C++ code and then back to Python, or can the C++ code access them from the Python environment somehow, like with NumericMatrix here?
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 #130 on: November 21, 2022, 12:52:11 PM »

Writing it in pure C++ shouldn't be too bad. But is there a way to access global variables from the Python environment within the C++ code? Ie must a way be devised to pass the matrices to the C++ code and then back to Python, or can the C++ code access them from the Python environment somehow, like with NumericMatrix here?

Python objects can be passed to C/C++ functions called through the Python C/C++ API but must be converted to C/C++ variables for C/C++ to work on them.  The nearest Python equivalent to NumericMatrix seems to be nested lists, and the nearest C/C++ equivalent to them seems to be vectors.  The next problem is that the damage function must return those vectors alongside the hull damage because the Python C/C++ API passes by value rather than reference.  We don't want to waste time building Python objects just to hold numbers.  Could we port more of this code into C++ and return the result?

Here's the C++ code reformatted to use only C++ objects and with descriptive variable names.  Also, I do not understand why the damage modifier for FRAGMENTATION is 4 rather than 0.25 if the modifier is meant for shields as the modifiers for KINETIC and HIGH_EXPLOSIVE imply.  Also, what is the raw damage, and how does it compare to the per shot damage, and why are they multiplied together?   The minimum armor calculation confuses me because I'm not sure the armor is evenly distributed but rather is ~1/15th of the armor rainng in each cell.  Note that I have added an & to armor_grid and hit_probability_distribution in the arguments, thereby passing them by reference rather than by value, letting this function mutate armor grid and saving effort.

Code
#include <stdio.h>

#include <vector>

using namespace std;

static double apply_damage(
    vector<vector<double>> &armor_grid,
    vector<vector<double>> &hit_probability_distribution,
    double armor_rating,
    double minimum_armor_for_damage_reduction,
    double damage_per_shot,
    double shield_damage_modifier
)
{
    double hull_damage = 0;
    double armor_damage_per_shot = damage_per_shot / shield_damage_modifier;
   
    for (int j = 0; j < sizeof(armor_grid[0]); j++)
    {
        for (int i = 0; i < sizeof(armor_grid); i++)
        {
            double probable_armor_damage =
                armor_damage_per_shot
                / (max(armor_grid[i][j], minimum_armor_for_damage_reduction)
                   + armor_damage_per_shot)
                * hit_probability_distribution[i][j];
           
            armor_grid[i][j] = max(armor_grid[i][j]
                                   - probable_armor_damage, 0.0);
            hull_damage += max(probable_armor_damage - armor_grid[i][j], 0.0)
                           * shield_damage_modifier;
        }
    }

    return hull_damage;
}
« Last Edit: November 21, 2022, 02:49:45 PM by Liral »
Logged

Thaago

  • Global Moderator
  • Admiral
  • *****
  • Posts: 7174
  • Harpoon Affectionado
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #131 on: November 21, 2022, 04:10:44 PM »

If you are writing in Python and the actual math is done using matrices, then you should be able to get nearly all of the speed of C/Fortran by using the Numpy library to do the matrix computations. Numpy has a true array object (set size/data type, contiguous memory, etc) implemented in lower level languages and already wrapped, so you don't need to do that yourself. Depending on the actual operations being done you can even call the BLAS/LAPACK Fortran functions directly (though this is a bit of a pain as you need to wade through the obscure naming conventions of the functions from the 70's).

Also, update to Python 3.11 if you haven't yet: it offers a pretty decent performance upgrade over past versions!

[Edit] Reading the above code, because your matrix operations are element-wise, with Numpy you can do the whole operation in 3 lines with no if statements/loops: 1 line to do the whole armor grid modification, 1 line to sum up the hull damage , 1 line to reset negative armor sections to 0. All looping is handled by numpy's element-wise coding (low level language), the functions being called are Sum, Numpy.maximum (elementwise maximum of an array, IE doing it for the individual components), and arithmetic.
« Last Edit: November 21, 2022, 04:25:31 PM by Thaago »
Logged

Liral

  • Admiral
  • *****
  • Posts: 717
  • Realistic Combat Mod Author
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #132 on: November 21, 2022, 04:47:31 PM »

If you are writing in Python and the actual math is done using matrices, then you should be able to get nearly all of the speed of C/Fortran by using the Numpy library to do the matrix computations. Numpy has a true array object (set size/data type, contiguous memory, etc) implemented in lower level languages and already wrapped, so you don't need to do that yourself. Depending on the actual operations being done you can even call the BLAS/LAPACK Fortran functions directly (though this is a bit of a pain as you need to wade through the obscure naming conventions of the functions from the 70's).

Also, update to Python 3.11 if you haven't yet: it offers a pretty decent performance upgrade over past versions!

[Edit] Reading the above code, because your matrix operations are element-wise, with Numpy you can do the whole operation in 3 lines with no if statements/loops: 1 line to do the whole armor grid modification, 1 line to sum up the hull damage , 1 line to reset negative armor sections to 0. All looping is handled by numpy's element-wise coding (low level language), the functions being called are Sum, Max (possibly the Numpy variants of those to work on their data structure), and arithmetic, so all of that should be low level as well.

Yes, that would work!  No more C++ awfulness!  Hooray!  Here's what I ended up with while refactoring the code because I had noticed it seemed to be applying one 'operation' across a matrix and could therefore be structured accordingly.  I remain proud of it even though it's useless now because I taught myself a little about how to use pointers in C++ to do such a neat trick as passing an element of a 2D vector as an argument.

Code
#include <stdio.h>

#include <vector>

using namespace std;

static double damage_armor_cell(
    double* armor_cell,
    double* hit_probability,
    double maximum_armor_damage_per_shot,
    double armor_for_damage_reduction,
    double shield_damage_modifier
)
{
    double probable_armor_damage = maximum_armor_damage_per_shot
                                    / (armor_for_damage_reduction
                                        + maximum_armor_damage_per_shot)
                                    * *hit_probability;
    *armor_cell = max(*armor_cell - probable_armor_damage, 0.0);
    return max(probable_armor_damage - *armor_cell, 0.0)
            * shield_damage_modifier;
}

static double apply_damage(
    vector<vector<double>> &armor_grid,
    vector<vector<double>> &hit_probability_distribution,
    double armor_rating,
    double minimum_armor_for_damage_reduction,
    double damage_per_shot,
    double shield_damage_modifier
)
{
    double hull_damage = 0;
    double maximum_armor_damage_per_shot = damage_per_shot
                                            / shield_damage_modifier;
   
    for (int j = 0; j < sizeof(armor_grid[0]); j++)
    {
        for (int i = 0; i < sizeof(armor_grid); i++)
        {
            hull_damage += damage_armor_cell(
                &armor_grid[i][j],
                &hit_probability_distribution[i][j],
                maximum_armor_damage_per_shot,
                minimum_armor_for_damage_reduction,
                shield_damage_modifier
            );
        }
    }

    return hull_damage;
}
« Last Edit: November 21, 2022, 04:49:16 PM by Liral »
Logged

CapnHector

  • Admiral
  • *****
  • Posts: 1056
    • View Profile
Re: Optimizing the Conquest: a Mathematical Model of Space Combat
« Reply #133 on: November 21, 2022, 09:37:44 PM »

Here's the C++ code reformatted to use only C++ objects and with descriptive variable names.  Also, I do not understand why the damage modifier for FRAGMENTATION is 4 rather than 0.25 if the modifier is meant for shields as the modifiers for KINETIC and HIGH_EXPLOSIVE imply.  Also, what is the raw damage, and how does it compare to the per shot damage, and why are they multiplied together?   The minimum armor calculation confuses me because I'm not sure the armor is evenly distributed but rather is ~1/15th of the armor rainng in each cell.  Note that I have added an & to armor_grid and hit_probability_distribution in the arguments, thereby passing them by reference rather than by value, letting this function mutate armor grid and saving effort.

You are correct on the minimum armor part. We can easily solve what starting armor a_cell should be, since we know cells have equal armor and 9*a_cell+12*a_cell/2=a_ship -> a_cell=1/15 a_ship. This is even computed correctly elsewhere in the code (    armormatrix <- matrix(ship[4]/15,5,ship[6]+4)), for some reason my brain just short-circuited here. Thanks for finding the error. The effect was to lower the minimum damage reduction for armor by a great deal, which is why fixing this error leads to more correct TTK numbers (the code now produces
""7"   "Squall"   "Hurricane"   "Harpoon"   "Harpoon"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam"   "Super Ion Beam""

as the fastest TTK).

I'm going to comment the corrected original version of the code since it sounds like you'll be re-implementing it with Numpy (that library might be a good reason to learn Python eventually it sounds like).
Code
/* NumericMatrix leads to the code modifying the matrix in the R global environment on lines 10 to 17. If this is not possible in Python, then the function should return the damaged matrix. The function should also return a double containing the hull damage that goes through. */
double damage(NumericMatrix A, NumericMatrix D, double a, double a_mod, double d, double h, double m){
  double hd = 0;
  int rows_A = A.nrow();
  int cols_A = A.ncol();
/* see above - we were passed the starting armor of the ship, and now we calculate the starting armor of an individual cell */
  a = a/15;
/* go through the armor matrix */
  for (int j = 0; j < cols_A; j++){
  for (int i = 0; i < rows_A; i++){
/* "armor" is the value we will use for the hit strength calculation. IF the armor matrix hp at (i,j) is greater than minimum armor (e.g a_mod=0.05, a=1500/15 (standard dominator) -> minimum armor is 0.05*100) THEN use armor matrix hp ELSE use the minimum armor value for the hit strength calculation */
    double armor = std::max(A(i,j), a_mod*a);
/* probmult refers to the fraction of damage we are assigning to the armor cell according to the probability distribution and it is given by the overall damage distribution matrix D (ie. the one stored in the lookup table, the one we calculate using the normal and uniform distributions) at (i,j) */
    double probmult = D(i,j);
/* adjusted damage is: damage assigned to cell (d = total damage times probmult = fraction of total damage assigned to cell), adjusted by hit strength / (armor + hit strength). In addition, because the modifiers are he = 0.5, kinetic = 2, frag = 4, then the correct adjustment for hit strength to hit armor is hit strength / modifier*/
    double adjusted_d = d*h/m/(armor+h/m)*probmult;
/* if we are dealing less damage than either armor hp or minimum armor then we are not dealing hull damage */
    if (adjusted_d <= armor){
/* reduce armor hp by adjusted allocated damage */
      A(i,j) = A(i,j) - adjusted_d;
/* if armor went below 0 set armor to 0.0 since it is a double */
      A(i,j) = std::max(A(i,j), 0.0);
    }
/* if we are dealing more damage than either armor hp or minimum armor */
    if (adjusted_d > armor){
/* set armor cell hp to 0 */
      A(i,j) = 0.0;
/* now calculate the damage to hull. It is adjusted damage minus minimum armor or armor hp, re-scaled to hull. Due to the way we defined the modifier above, we can  re-scale the damage back to the appropriate modifier for hull by multiplying it by m (since all weapons deal 100% damage to hull) */
      adjusted_d = (adjusted_d - armor)*m;
/* this next line might just as well read hd = adjusted_d; */
      hd = hd + adjusted_d;
    }
  }
  }
  return hd;
}

The reason frag multiplier here is 4 but it is 0.25 for the shield calculation is the asymmetry in frag's multiplier. Specifically for both kinetic and HE, if multipliers are 2 and 0.5 respectively, then shielddamage = damage * multiplier and armordamage = damage / multiplier. And we can then use the same definition for energy when multiplier is 1. But for frag this does not work since the multiplier is 0.25 vs shields and 0.25 vs armor. That is why frag's multiplier should be 0.25 for the shield calculation and 4 for the armor calculation so we have shielddamage = damage * multiplier and armordamage = damage / multiplier for frag also. This is a highly convenient definition since if we define the multiplier this way, then we can simply do (damage/multiplier - armor)*multiplier, with the appropriate max(0,) function and minimum armor added of course, to get hull damage.

I just realized another thing that's missing from this code and that is that damage should not be able to be reduced below a certain threshold. To fix, add
Code
double minimumdamageafterarmorreduction
to the function's arguments and change "double adjusted_d = d*h/m/(armor+h/m)*probmult;" to
Code
double adjusted_d = std::max(d*h/m/(armor+h/m)*probmult, minimumdamageafterarmorreduction*d*probmult);

Full code with fix
code
Code


#operating modes DO NOT CHANGE CODE WILL BREAK IF GUN IS NOT 0 AND BEAM IS NOT 1 AS THE LITERAL INTEGER VALUE IS USED

GUN <- 0
BEAM <- 1
#ships -  NOT FIXED YET EXCEPT DOMINATOR
#ship, hullhp, flux dissipation, maximum flux, startingarmor, widthinpixels, armorcells, shieldwidth, shieldefficacy, shiedlupkeep, name
#glimmer <- c(1500, 250/0.6, 2500/0.6, 200, 78, 5, 78*2, 0.6, "glimmer")
#brawlerlp <- c(2000, 500/0.8, 3000/0.8, 450,110,floor(110/15), "brawlerlp")
#vanguard <- c(3000, 150, 2000, 600, 104, floor(104/15),"vanguard")
#tempest <- c(1250, 225/0.6, 2500/0.6, 200,64,floor(64/15), "tempest")
#medusa <- c(3000,400/0.6,6000/0.6,300,134,floor(134/15), "medusa")
#hammerhead <- c(5000,250/0.8,4200/0.8,500,108,floor(108/16.4), "hammerhead")
#enforcer <- c(4000,200,4000,900,136,floor(136/15), "enforcer")
dominator <- c(14000, 500, 10000, 1500, 220, 12, 440, 1.0, 200, "dominator")
#fulgent <- c(5000,300/0.6,5000/0.6,450, 160, floor(160/15), "fulgent")
#brilliant <- c(8000,600/0.6,10000/0.6,900,160,floor(160/20),"brilliant")
#radiant <- c(20000,1500/0.6,25000/0.6,1500,316,floor(316/30),"radiant")
#onslaught <- c(20000,600,17000,1750,288,floor(288/30),"onslaught")
#aurora <- c(8000,800/0.8,11000/0.8,800,128,floor(128/28), "aurora")
#paragon <- c(18000,1250/0.6,25000/0.6,1500,330,floor(330/30),"paragon")
#conquest <- c(12000,1200/1.4,20000/1.4,1200,190,floor(190/30),"conquest")
#champion <- c(10000,550/0.8,10000/0.8,1250, 180,floor(180/24),"champion")

#ships <- list(glimmer,brawlerlp,vanguard,tempest,medusa,hammerhead,enforcer,dominator,fulgent,brilliant,radiant,onslaught,aurora,paragon,conquest,champion)
ships <- list(dominator)
#engagementrange
range <- 1000

minimumarmormultiplier <- 0.05
minimumdamageafterarmorreduction <- 0.15

#weaponaccuracy - this will be made a function of time and weapon later. the accuracy of a hellbore is 10
acc <- 10

#fudge factor
errorsd <- 0.05
#the fudge factor should be a function of range (more error in position at greater range), but not a function of weapon firing angle, and be expressed in terms of pixels
error <- errorsd*range

#time limit for a single combat
time_limit <- 500



beam_tick <- 1/10


G <- function(y) return(y*pnorm(y) + dnorm(y))
#a is the SD of the normal distribution and b is the parameter of the uniform distribution
hit_probability_coord_lessthan_x <- function(z, a, b) return(a/2/b*(G(z/a+b/a)-G(z/a-b/a)))


# this function generates the shot distribution (a bhattacharjee distribution for the
#non-trivial case)
hit_distribution <- function(upperbounds, standard_deviation, spread){
  vector <- vector(mode="double", length = length(upperbounds))
  if (standard_deviation == 0){
    if (spread == 0){
      vector[1] <- 0
      for (j in 2:(length(upperbounds))) {
        #if both spread and standard deviation are 0 then all shots hit 1 cell. this should be so even if
        #the ship has an even number of cells to prevent ships with even no. cells appearing tougher which is not
        #the case in the real game most likely
        if ((upperbounds[j] >= 0) & (upperbounds[j-1] < 0)) vector[j] <- 1
      }
      #return part of a box
    } else {
      vector[1] <- min(1,max(0,(upperbounds[1]+spread))/(2*spread))
      for (j in 2:(length(upperbounds)-1)) vector[j] <- min(1,max(0,(upperbounds[j]+spread))/(2*spread)) - min(1,max(0,(upperbounds[j-1]+spread))/(2*spread))
      vector[length(upperbounds)] <- 1-min(1,max(0,(upperbounds[length(upperbounds)]+spread))/(2*spread))
    }
  } else {
    if (spread != 0){
      vector[1] <- hit_probability_coord_lessthan_x(upperbounds[1], standard_deviation, spread)
      for (j in 2:(length(upperbounds)-1)) vector[j] <- (hit_probability_coord_lessthan_x(upperbounds[j], standard_deviation, spread)-hit_probability_coord_lessthan_x(upperbounds[j-1], standard_deviation, spread))
      vector[length(upperbounds)] <- (1-hit_probability_coord_lessthan_x(upperbounds[length(upperbounds)-1], standard_deviation, spread))
    } else {
      #if spread is 0 but standard deviation is not 0 we have a normal distribution
      for (j in 1:(length(upperbounds)-1)) vector[j] <- pnorm(upperbounds[j], mean=0, sd=standard_deviation)
      for (j in 2:(length(upperbounds)-1)) vector[j] <- vector[j] - pnorm(upperbounds[j-1], mean=0, sd=standard_deviation)
      vector[length(upperbounds)] <- 1-pnorm(upperbounds[length(upperbounds)-1], mean=0, sd=standard_deviation)
    }
   
  }
  return(vector)
}

#this is not really necessary, just a wrapper for the above new function to fit into the old code
createdistribution <- function(acc,mode){
  return(hit_distribution(anglerangevector,error,acc))
}

# this is the default distribution of damage to armor cells
b <- matrix(0,nrow=5,ncol=5)
b[1:5,2:4] <- 1/30
b[2:4,1:5] <- 1/30
b[2:4,2:4] <- 1/15
b[1,1] <- 0
b[1,5] <- 0
b[5,1] <- 0
b[5,5] <- 0

#this function returns the chance to hit a simple width
hitchance <- function(acc,error,lowerpoint,higherpoint){
  return(hit_distribution(c(lowerpoint,higherpoint,0),error,acc)[[2]])
}
#this function generates a sum of matrices multiplied by the distribution

createhitmatrix <- function(acc){
  hitmatrix <- matrix(0,5,ship[6]+4)
  distributionvector <- createdistribution(acc)
  for (i in 1:ship[6]){
    hitmatrix[,i:(i+4)] <- hitmatrix[,i:(i+4)]+b*(distributionvector[i+1])
  }
  return(hitmatrix)
}

#for weapons with damage changing over time we need a sequence of matrices
createhitmatrixsequence <- function(accvector){
  hitmatrixsequence <- list()
  for (i in 1:length(accvector)){
    hitmatrixsequence[[i]] <- createhitmatrix(accvector[i])
  }
  return(hitmatrixsequence)
}


#this function is absolutely performance critical (run millions of times) so write it in C++ using
#elementary operations
library(Rcpp)
#what this function shall do is:
#1 ) modify the armor matrix in the R global environment by subtracting armor damage
#2 ) return hull damage
#A is the armor matrix
#rows_A is the number of rows in A
#cols_A is the number of columns in A
#D is the damage matrix (note: we do not need a rows_D etc as these matrices must be same size)
#a is starting armor for the whole ship
#a_mod is minimum armor
#d is raw damage from whole weapon shot
#h is hit strength of weapon shot
#m is modifier, 2= kinetic, 1 = energy, 0.5 = he, 4 = frag
#hd is hull damage
#do NOT pass a hit strength of 0 to this function as it does not check for dividing by zero
#overall, this function does no sanity or safety checking so be careful with it
cppFunction('double damage(NumericMatrix A, NumericMatrix D, double a, double a_mod, double d, double h, double m, double minimumdamageafterarmorreduction){
  double hd = 0;
  int rows_A = A.nrow();
  int cols_A = A.ncol();
  a = a/15;
  for (int j = 0; j < cols_A; j++){
  for (int i = 0; i < rows_A; i++){
    double armor = std::max(A(i,j), a_mod*a);
    double probmult = D(i,j);
double adjusted_d = std::max(d*h/m/(armor+h/m)*probmult, minimumdamageafterarmorreduction*d*probmult);    if (adjusted_d <= armor){
      A(i,j) = A(i,j) - adjusted_d;
      A(i,j) = std::max(A(i,j), 0.0);
    }
    if (adjusted_d > armor){
      A(i,j) = 0.0;
      adjusted_d = (adjusted_d - armor)*m;
      hd = hd + adjusted_d;
    }
  }
  }
  return hd;
}
')

#general function to generate ticks
#1. general constants
#the interval of discrete time (time lattice parameter) we are using in the model, in seconds
time_interval <- 1
#how long 1 tick of a beam lasts, in seconds
beam_tick <- 1/10
#minimum interval that exists in the game, in case a modder has somehow specified a lower value for something
global_minimum_time <- 0.05
#operating modes
UNLIMITED <- -1

#times in seconds, ammoregen is in ammo / second
hits <- function(chargeup, chargedown, burstsize, burstdelay, ammo=UNLIMITED, ammoregen=0, reloadsize=0, traveltime=0, mode=GUN){
  #specify sane minimum delays, since the game enforces weapons can only fire once every 0.05 sec
  #for beams, refiring delay is given by burstdelay, for guns it is burstdelay in case burstdelay is > 0 (==0 is shotgun) and chargedown
  if(burstdelay > 0 | mode == BEAM) burstdelay <- max(burstdelay, global_minimum_time)
  if(mode == GUN) chargedown <- max(chargedown, global_minimum_time)
  #this vector will store all the hit time coordinates
  #current time
  #insert a very small fraction here to make time round correctly
  time <- 0.001
  #maximum ammo count is ammo given at start
  maxammo <- ammo
  #this is used to do ammo regeneration, 0 = not regenerating ammo, 1 = regenerating ammo
  regeneratingammo <- 0
  ammoregentimecoordinate <- 0
  ammoregenerated <- 0
 
  #we are firing a gun
  if (mode == GUN) {
    Hits <- vector(mode="double", length = 0)
    while(time < time_limit){
      time <- time + chargeup
      if(time - ammoregentimecoordinate > 1/ammoregen){
        ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
        ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
        if(ammoregenerated >= reloadsize){
          ammo <- ammo+ ammoregenerated
          ammoregenerated <- 0
        }
        if(ammo >= maxammo){
          ammo <- maxammo
          regeneratingammo <- 0
        }
      }
     
      if (burstdelay == 0) {
        for (i in 1:burstsize) {
          if (ammo != 0){
            Hits <- c(Hits, time + traveltime)
            ammo <- ammo - 1
            if (regeneratingammo == 0) {
              ammoregentimecoordinate <- time
              regeneratingammo <- 1
            }
          }
        }
      }
      if (burstdelay > 0) {
        for (i in 1:burstsize) {
          if (ammo != 0){
            Hits <- c(Hits, time + traveltime)
            time <- time + burstdelay
            ammo <- ammo -1
            if (regeneratingammo == 0) {
              ammoregentimecoordinate <- time
              regeneratingammo <- 1
            }
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
           
          }
        }
      }
      time <- time+chargedown
      if(time - ammoregentimecoordinate > 1/ammoregen){
        ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
        ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
        if(ammoregenerated >= reloadsize){
          ammo <- ammo+ ammoregenerated
          ammoregenerated <- 0
        }
        if(ammo >= maxammo){
          ammo <- maxammo
          regeneratingammo <- 0
        }
      }
    }
    timeseries <- vector(mode="integer", length = time_limit/time_interval)
    timeseries[1] <- length(Hits[Hits >= 0 & Hits <= 1*time_interval])
    for (i in 2:time_limit/time_interval) timeseries[i] <- length(Hits[Hits > (i-1)*time_interval & Hits <= i*time_interval])
    return(timeseries)
  }
  #we are firing a beam
  if (mode == BEAM) {
    chargeup_ticks <- chargeup/beam_tick
    chargedown_ticks <- chargedown/beam_tick
    burst_ticks <- burstsize/beam_tick
    #for a beam we will instead use a matrix to store timepoint and beam intensity at timepoint
    beam_matrix <- matrix(nrow=0,ncol=2)
    #burst size 0 <- the beam never stops firing
    if(burstsize == 0){
      for (i in 1:chargeup_ticks) {
        #beam intensity scales quadratically during chargeup, so
      }
      while ( time < time_limit) {
        beam_matrix <- rbind(beam_matrix,c(time, 1))
        time <- time+beam_tick
      }
    } else {
      while (time < time_limit) {
        if (ammo != 0){
          ammo <- ammo - 1
          if (chargeup_ticks > 0){
            for (i in 1:chargeup_ticks) {
              beam_matrix <- rbind(beam_matrix,c(time, (i*beam_tick)^2))
              time <- time+beam_tick
              if (regeneratingammo == 0) {
                ammoregentimecoordinate <- time
                regeneratingammo <- 1
              }
              if(time - ammoregentimecoordinate > 1/ammoregen){
                ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
                ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
                if(ammoregenerated >= reloadsize){
                  ammo <- ammo+ ammoregenerated
                  ammoregenerated <- 0
                }
                if(ammo >= maxammo){
                  ammo <- maxammo
                  regeneratingammo <- 0
                }
              }
            }
          }
          for (i in 1:burst_ticks){
            beam_matrix <- rbind(beam_matrix,c(time, 1))
            time <- time+beam_tick
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
          }
         
          if (chargedown_ticks > 0){
            for (i in 1:chargedown_ticks){
              beam_matrix <- rbind(beam_matrix,c(time, ((chargedown_ticks-i)*beam_tick)^2))
              time <- time+beam_tick
            }
            if(time - ammoregentimecoordinate > 1/ammoregen){
              ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
              ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
              if(ammoregenerated >= reloadsize){
                ammo <- ammo+ ammoregenerated
                ammoregenerated <- 0
              }
              if(ammo >= maxammo){
                ammo <- maxammo
                regeneratingammo <- 0
              }
            }
          }
          time <- time + burstdelay
          if(time - ammoregentimecoordinate > 1/ammoregen){
            ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
            ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
            if(ammoregenerated >= reloadsize){
              ammo <- ammo+ ammoregenerated
              ammoregenerated <- 0
            }
            if(ammo >= maxammo){
              ammo <- maxammo
              regeneratingammo <- 0
            }
          }
        }
        time <- time + global_minimum_time
        if(time - ammoregentimecoordinate > 1/ammoregen){
          ammoregenerated <- ammoregenerated + floor((time - ammoregentimecoordinate)/(1/ammoregen))
          ammoregentimecoordinate <- ammoregentimecoordinate + 1/ammoregen*floor((time - ammoregentimecoordinate)/(1/ammoregen))
          if(ammoregenerated >= reloadsize){
            ammo <- ammo+ ammoregenerated
            ammoregenerated <- 0
          }
          if(ammo >= maxammo){
            ammo <- maxammo
            regeneratingammo <- 0
          }
        }
      }
    }
    timeseries <- vector(mode="double", length = time_limit/time_interval)
    for (i in 1:length(timeseries)) {
      timeseries[i] <- sum(beam_matrix[beam_matrix[,1] < i & beam_matrix[,1] > i-1,2])
    }
    return(timeseries)
  }
}

squalltics <- hits(0,10,20,0.5)
locusttics <- hits(0,5,40,0.1)
#special
hurricanetics <- hits(0,15,9,0)
harpoontics <- hits(0,8.25,4,0.25)
sabottics <- hits(0,8.75,2,0.25)
gausstics <- hits(1,1,1,0)
ionbeamtics <- hits(0.1,0.1,0,0,mode=BEAM)

#WEAPON ACCURACY
#missiles do not have spread
squallacc <- c(0)
locustacc <- c(0)
hurricaneacc <- c(0)
harpoonacc <- c(0)
sabotacc <- c(0)

#gauss has a spread of 0 and no increase per shot
gaussacc <- c(0)
#hephaestus has a spread of 0 and it increases by 2 per shot to a max of 10
#hephaestusacc <- c(seq(0,10,2))
#mark ix has a spread of 0 and it increases by 2 per shot to a max of 15
#markixacc <- c(seq(0,15,2),15)
#mjolnir has a spread of 0 and it increases by 1 per shot to a max of 5
#mjolniracc <- c(seq(1,5,1))
#hellbore has a spread of 10
#hellboreacc <- c(10)
#storm needler has a spread of 10
#stormneedleracc <- c(10)
ionbeamacc <- c(0)

#damage per shot, damage type (2=kinetic, 0.5=he, 0.25=frag, 1=energy), tics, weapon name, weapon accuracy over time, hit chance, mode
squall <- list(250, 2, squalltics, "Squall", squallacc, GUN)
locust <- list(200, 0.25, locusttics, "Locust", locustacc, GUN)
hurricane <- list(500, 0.5, hurricanetics, "Hurricane", hurricaneacc, GUN)
harpoon <- list(750, 0.5, harpoontics, "Harpoon", harpoonacc, GUN)
sabot <- list(200, 2, sabottics, "Sabot", sabotacc, GUN)
gauss <- list(700, 2, gausstics, "Gauss", gaussacc, GUN)
#hephaestus <- list(120, 0.5, hephaestustics, "Hephaestus", hephaestusacc)
#markix <- list(200, 2, markixtics, "Mark IX", markixacc)
#mjolnir <- list(400, 1, mjolnirtics, "Mjolnir", mjolniracc)
#hellbore <- list(750, 0.5, hellboretics, "Hellbore", hellboreacc)
#stormneedler <- list(50, 2, stormneedlertics, "Storm Needler", stormneedleracc)

#for beams, damage per second, and then the rest as previously
ionbeam <- list(1000, 1, ionbeamtics, "Super Ion Beam", ionbeamacc, BEAM)
dummy <- list(0,0,c(seq(0,time_limit,1)),"",c(0),c(0),GUN)

#which weapons are we studying?

weapon1choices <- list(squall, locust, hurricane)
weapon2choices <- list(squall, locust, hurricane)
weapon3choices <- list(harpoon, sabot)
weapon4choices <- list(harpoon, sabot)
weapon5choices <- list(ionbeam, gauss)
weapon6choices <- list(ionbeam, gauss)
weapon7choices <- list(ionbeam, gauss)
weapon8choices <- list(ionbeam, gauss)

#how many unique weapon loadouts are there?

#get names of weapons from a choices list x
getweaponnames <- function(x){
  vector <- vector(mode="character")
  for (i in 1:length(x)){
    vector <- cbind(vector, x[[i]][[4]])
  }
  return(vector)
}
#convert the names back to numbers when we are done based on a weapon choices list y
convertweaponnames <- function(x, y){
  vector <- vector(mode="integer")
  for (j in 1:length(x)) {
    for (i in 1:length(y)){
      if(x[j] == y[[i]][[4]]) vector <- cbind(vector, i)
    }
  }
  return(vector)
}

#this section of code generates a table of all unique loadouts that we can create using the weapon choices available
generatepermutations <- 0
if (generatepermutations == 1){
  #enumerate weapon choices as integers
 
  perm1 <- seq(1,length(weapon1choices),1)
  perm2 <- seq(1,length(weapon2choices),1)
  perm3 <- seq(1,length(weapon3choices),1)
  perm4 <- seq(1,length(weapon4choices),1)
  perm5 <- seq(1,length(weapon5choices),1)
  perm6 <- seq(1,length(weapon6choices),1)
  perm7 <- seq(1,length(weapon7choices),1)
  perm8 <- seq(1,length(weapon8choices),1)
 
  #create a matrix of all combinations
  perm1x2 <- expand.grid(perm1,perm2)
  #sort, then only keep unique rows
  perm1x2 <- unique(t(apply(perm1x2, 1, sort)))
 
  perm3x4 <- expand.grid(perm3,perm4)
  perm3x4 <- unique(t(apply(perm3x4, 1, sort)))
 
  perm5x6 <- expand.grid(perm5,perm6)
  perm5x6 <- unique(t(apply(perm5x6, 1, sort)))
 
  perm7x8 <- expand.grid(perm7,perm8)
  perm7x8 <- unique(t(apply(perm7x8, 1, sort)))
 
  #now that we have all unique combinations of all two weapons, create a matrix containing all combinations of these unique combinations
  allperms <- matrix(0,0,(length(perm1x2[1,])+length(perm3x4[1,])+length(perm5x6[1,])+length(perm7x8[1,])))
  for(i in 1:length(perm1x2[,1])) for(j in 1:length(perm3x4[,1])) for(k in 1:length(perm5x6[,1])) for(l in 1:length(perm7x8[,1])) allperms <- rbind(allperms, c(perm1x2[i,],perm3x4[j,],perm5x6[k,],perm7x8[l,])
  )
  #this is just for testing, can remove
  allperms
  #we save this so we don't have to compute it again
  saveRDS(allperms, file="allperms.RData")
 
} else {
  allperms <- readRDS("allperms.RData")
}

#now compute a main lookuptable to save on computing time
#the lookuptable should be a list of lists, so that
#lookuptable[[ship]][[weapon]][[1]] returns hit chance vector and
#lookuptable[[ship]][[weapon]][[2]] returns hit probability matrix
#time for some black R magic

#note: the lookuptable will be formulated such that there is a running index of weapons rather than sub-lists, so all weapons will be indexed consecutively so we have lookuptable [[1]][[1]] = [[ship1]][[weaponchoices1_choice1]], etc. So that is what the below section does.

#read or generate lookuptable
generatelookuptable <- 0
if(generatelookuptable == 1){
 
  lookuptable <- list()
 
  for (f in 1:length(ships)){
    lookuptable[[f]] <- list()
    ship <- ships[[f]]
    ship <- as.double(ship[1:9])
    #how much is the visual arc of the ship in rad?
    shipangle <- ship[5]/(2* pi *range)
   
    #how much is the visual arc of a single cell of armor in rad?
    cellangle <- shipangle/ship[6]
   
    #now assume the weapon is targeting the center of the ship's visual arc and that the ship is in the center of the weapon's firing arc
    #which cell will the shot hit, or will it miss?
    #call the cells (MISS, cell1, cell2, ... ,celli, MISS) and get a vector giving the (maximum for negative / minimum for positive) angles for hitting each
    anglerangevector <- vector(mode="double", length = ship[6]+1)
    anglerangevector[1] <- -shipangle/2
    for (i in 1:(length(anglerangevector)-1)) anglerangevector[i+1] <- anglerangevector[i]+cellangle
    #now convert it to pixels
    anglerangevector <- anglerangevector*2*pi*range
   
    weaponindexmax <- length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+length(weapon7choices)+length(weapon8choices)
   
    for (x in 1:weaponindexmax) {
      print(x)
      if(x <= length(weapon1choices)){
        weapon1<-weapon1choices[[x]]
        if(weapon1[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon1[[5]]))
          for (i in 1:length(weapon1[[5]])){
            hitchancevector[i] <- hitchance(weapon1[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon1[[5]])
        }
      }
      if((x > length(weapon1choices)) &  (x <= length(weapon1choices) + length(weapon2choices))){
        weapon2<-weapon2choices[[x-length(weapon1choices)]]
        if(weapon2[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon2[[5]]))
          for (i in 1:length(weapon2[[5]])){
            hitchancevector[i] <- hitchance(weapon2[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon2[[5]])
        }
      }
     
      if((x > length(weapon1choices) + length(weapon2choices)) &  (x <= length(weapon2choices) + length(weapon1choices) + length(weapon3choices))){
        weapon3<-weapon3choices[[x-length(weapon2choices)-length(weapon1choices)]]
        if(weapon3[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon3[[5]]))
          for (i in 1:length(weapon3[[5]])){
            hitchancevector[i] <- hitchance(weapon3[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon3[[5]])
        }
      } 
     
      if((x > length(weapon2choices) + length(weapon1choices) + length(weapon3choices)) &  (x <= length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon4choices))){
        weapon4<-weapon4choices[[x-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon4[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon4[[5]]))
          for (i in 1:length(weapon4[[5]])){
            hitchancevector[i] <- hitchance(weapon4[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon4[[5]])
        }
      }
      if((x > length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon4choices)) &  (x <= length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon5choices))){
        weapon5<-weapon5choices[[x-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon5[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon5[[5]]))
          for (i in 1:length(weapon5[[5]])){
            hitchancevector[i] <- hitchance(weapon5[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon5[[5]])
        }
      }
      if((x > length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon5choices)) &  (x <= length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon6choices))){
        weapon6<-weapon6choices[[x-length(weapon5choices)-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon6[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon6[[5]]))
          for (i in 1:length(weapon6[[5]])){
            hitchancevector[i] <- hitchance(weapon6[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon6[[5]])
        }
      }
      if((x > length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon6choices)) &  (x <= length(weapon6choices) + length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon7choices))){
        weapon7<-weapon7choices[[x-length(weapon6choices)-length(weapon5choices)-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon7[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon7[[5]]))
          for (i in 1:length(weapon7[[5]])){
            hitchancevector[i] <- hitchance(weapon7[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon7[[5]])
        }
      }
      if((x > length(weapon6choices) + length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon7choices)) &  (x <= length(weapon7choices) + length(weapon6choices) + length(weapon5choices) + length(weapon4choices) + length(weapon3choices) + length(weapon2choices) + length(weapon1choices) + length(weapon8choices))){
        weapon8<-weapon8choices[[x-length(weapon7choices)-length(weapon6choices)-length(weapon5choices)-length(weapon4choices)-length(weapon3choices)-length(weapon2choices)-length(weapon1choices)]]
        if(weapon8[4] != ""){
          hitchancevector <- vector(mode = "double", length = length(weapon8[[5]]))
          for (i in 1:length(weapon8[[5]])){
            hitchancevector[i] <- hitchance(weapon8[[5]][i],error,-ship[[7]]/2,ship[[7]]/2)
          }
          lookuptable[[f]][[x]] <- list()
          lookuptable[[f]][[x]][[1]] <- hitchancevector
          lookuptable[[f]][[x]][[2]] <- createhitmatrixsequence(weapon8[[5]])
        }
      }
     
    }
  }
  # save this so we don't have to re-compute it
  saveRDS(lookuptable, file="lookuptable.RData")
} else {
  lookuptable <- readRDS("lookuptable.RData")
}

lookup <- function(ship, weapon, var) return(lookuptable[[ship]][[weapon]][[var]])

#go through all ships
for (f in 1:length(ships)){
 
  ship <- ships[[f]]
  #format ship data types appropriately
  shipname <- ship[[10]]
  ship <- as.double(ship[1:9])
 
 
 
 
 
 
 
  timeseriesarray <- data.frame(matrix(ncol = 7,nrow=0))
 
 
 
  timetokill=0
 
 
  shieldblock <- 0
 

 
  timeseries <- function(timepoint, softflux, hardflux, armorhp, hullhp, fluxdissip, fluxcap, startingarmor,armormatrix){
    weaponacc <- 0

    #are we using shield to block?
    shieldblock <- 0
    hulldamage <- 0
   
   
    #weapon 1
    weapon1mult <- weapon1[[2]]
    weapon2mult <- weapon2[[2]]
    weapon3mult <- weapon3[[2]]
    weapon4mult <- weapon4[[2]]
    weapon5mult <- weapon5[[2]]
    weapon6mult <- weapon6[[2]]
    weapon7mult <- weapon7[[2]]
    weapon8mult <- weapon8[[2]]
   
    shots <- weapon1[[3]][[timepoint]]
    #here we must convert beam ticks to fractional shots
    shots1 <- weapon1[[3]][[timepoint]]*beam_tick^(weapon1[[6]])
    shots2 <- weapon2[[3]][[timepoint]]*beam_tick^(weapon2[[6]])
    shots3 <- weapon3[[3]][[timepoint]]*beam_tick^(weapon3[[6]])
    shots4 <- weapon4[[3]][[timepoint]]*beam_tick^(weapon4[[6]])
    shots5 <- weapon5[[3]][[timepoint]]*beam_tick^(weapon5[[6]])
    shots6 <- weapon6[[3]][[timepoint]]*beam_tick^(weapon6[[6]])
    shots7 <- weapon7[[3]][[timepoint]]*beam_tick^(weapon7[[6]])
    shots8 <- weapon8[[3]][[timepoint]]*beam_tick^(weapon8[[6]])
    #test is used to determine if we are firing or blocking this turn
    test <- (fluxcap-softflux-hardflux - weapon1[[1]]*weapon1mult*shots1 - weapon2[[1]]*weapon2mult*shots2 - weapon3[[1]]*weapon3mult*shots3 - weapon4[[1]]*weapon4mult*shots4 - weapon5[[1]]*weapon5mult*shots5 - weapon6[[1]]*weapon6mult*shots6 - weapon7[[1]]*weapon7mult*shots7 - weapon8[[1]]*weapon8mult*shots8)   
    #skip the whole thing if we are not firing
    if (weapon1[[4]] !="" & shots > 0){
      mode <- weapon1[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon1[[1]]*weapon1mult*weapon1[[7]][min(weapon1shots,length(weapon1[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon1[[1]]*weapon1[[3]][[timepoint]]*beam_tick*weapon1mult*weapon1[[7]][min(weapon1shots,length(weapon1[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon1[2])==0.25){weapon1mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon1[[1]]
            damage <- weapon1[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon1[[1]]/2
            damage <- weapon1[[1]]*weapon1[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon1[[8]][[min(weapon1shots,length(weapon1[[8]]))]],startingarmor,minimumarmormultiplier,damage,hitstrength,weapon1mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon1shots <- weapon1shots + weapon1[[3]][timepoint]
    }
   
    #repeat for other weapons
    shots <- weapon2[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon2[[4]] !="" & shots > 0){
      weapon2mult <- weapon2[[2]]
      mode <- weapon2[[6]]
     
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon2[[1]]*weapon2mult*weapon2[[7]][min(weapon2shots,length(weapon2[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon2[[1]]*weapon2[[3]][[timepoint]]*beam_tick*weapon2mult*weapon2[[7]][min(weapon2shots,length(weapon2[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon2[2])==0.25){weapon2mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon2[[1]]
            damage <- weapon2[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon2[[1]]/2
            damage <- weapon2[[1]]*weapon2[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon2[[8]][[min(weapon2shots,length(weapon2[[8]]))]],startingarmor,minimumarmormultiplier,weapon2[[1]],hitstrength,weapon2mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon2shots <- weapon2shots + weapon2[[3]][timepoint]
    }
   
   
    shots <- weapon3[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon3[[4]] !="" & shots > 0){
      weapon3mult <- weapon3[[2]]
      mode <- weapon3[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon3[[1]]*weapon3mult*weapon3[[7]][min(weapon3shots,length(weapon3[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon3[[1]]*weapon3[[3]][[timepoint]]*beam_tick*weapon3mult*weapon3[[7]][min(weapon3shots,length(weapon3[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon3[2])==0.25){weapon3mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon3[[1]]
            damage <- weapon3[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon3[[1]]/2
            damage <- weapon3[[1]]*weapon3[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon3[[8]][[min(weapon3shots,length(weapon3[[8]]))]],startingarmor,minimumarmormultiplier,weapon3[[1]],hitstrength,weapon3mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon3shots <- weapon3shots + weapon3[[3]][timepoint]
    }
   
   
    shots <- weapon4[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon4[[4]] !="" & shots > 0){
      weapon4mult <- weapon4[[2]]
      mode <- weapon4[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon4[[1]]*weapon4mult*weapon4[[7]][min(weapon4shots,length(weapon4[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon4[[1]]*weapon4[[3]][[timepoint]]*beam_tick*weapon4mult*weapon4[[7]][min(weapon4shots,length(weapon4[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon4[2])==0.25){weapon4mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon4[[1]]
            damage <- weapon4[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon4[[1]]/2
            damage <- weapon4[[1]]*weapon4[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon4[[8]][[min(weapon4shots,length(weapon4[[8]]))]],startingarmor,minimumarmormultiplier,weapon4[[1]],hitstrength,weapon4mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon4shots <- weapon4shots + weapon4[[3]][timepoint]
    }
   
   
    shots <- weapon5[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon5[[4]] !="" & shots > 0){
      weapon5mult <- weapon5[[2]]
      mode <- weapon5[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon5[[1]]*weapon5mult*weapon5[[7]][min(weapon5shots,length(weapon5[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon5[[1]]*weapon5[[3]][[timepoint]]*beam_tick*weapon5mult*weapon5[[7]][min(weapon5shots,length(weapon5[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon5[2])==0.25){weapon5mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon5[[1]]
            damage <- weapon5[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon5[[1]]/2
            damage <- weapon5[[1]]*weapon5[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon5[[8]][[min(weapon5shots,length(weapon5[[8]]))]],startingarmor,minimumarmormultiplier,weapon5[[1]],hitstrength,weapon5mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon5shots <- weapon5shots + weapon5[[3]][timepoint]
    }
   
    shots <- weapon6[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon6[[4]] !="" & shots > 0){
      weapon6mult <- weapon6[[2]]
      mode <- weapon6[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon6[[1]]*weapon6mult*weapon6[[7]][min(weapon6shots,length(weapon6[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon6[[1]]*weapon6[[3]][[timepoint]]*beam_tick*weapon6mult*weapon6[[7]][min(weapon6shots,length(weapon6[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon6[2])==0.25){weapon6mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon6[[1]]
            damage <- weapon6[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon6[[1]]/2
            damage <- weapon6[[1]]*weapon6[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon6[[8]][[min(weapon6shots,length(weapon6[[8]]))]],startingarmor,minimumarmormultiplier,weapon6[[1]],hitstrength,weapon6mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon6shots <- weapon6shots + weapon6[[3]][timepoint]
    }
   
    shots <- weapon7[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon7[[4]] !="" & shots > 0){
      weapon7mult <- weapon7[[2]]
      mode <- weapon7[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon7[[1]]*weapon7mult*weapon7[[7]][min(weapon7shots,length(weapon7[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon7[[1]]*weapon7[[3]][[timepoint]]*beam_tick*weapon7mult*weapon7[[7]][min(weapon7shots,length(weapon7[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon7[2])==0.25){weapon7mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon7[[1]]
            damage <- weapon7[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon7[[1]]/2
            damage <- weapon7[[1]]*weapon7[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon7[[8]][[min(weapon7shots,length(weapon7[[8]]))]],startingarmor,minimumarmormultiplier,weapon7[[1]],hitstrength,weapon7mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon7shots <- weapon7shots + weapon7[[3]][timepoint]
    }
   
    shots <- weapon8[[3]][[timepoint]]
   
   
    #skip the whole thing if we are not firing
    if (weapon8[[4]] !="" & shots > 0){
      weapon8mult <- weapon8[[2]]
      mode <- weapon8[[6]]
      if(mode == BEAM) {
        shots <- 1
      }
      for (s in 1:shots){
        #1. use shield to block if you can
        if (test > 0){
          #hard flux
          if(mode == GUN){
            hardflux <- hardflux + weapon8[[1]]*weapon8mult*weapon8[[7]][min(weapon8shots,length(weapon8[[7]]))]*shieldefficacy
            hardflux <- min(hardflux, fluxcap-softflux)
          }
          if(mode == BEAM){
            softflux <- softflux + weapon8[[1]]*weapon8[[3]][[timepoint]]*beam_tick*weapon8mult*weapon8[[7]][min(weapon8shots,length(weapon8[[7]]))]*shieldefficacy
            softflux <- min(softflux, fluxcap-hardflux)
          }
          shieldblock <- 1
        } else {
          #2. if you did not use shield to block, damage armor and hull
          #frag is a special case wrt multiplier
          if(unlist(weapon8[2])==0.25){weapon8mult = 4}
          #2.1. damage armor and hull
          hitstrength <- 0
          damage <- 0
          if(mode == GUN) {
            hitstrength <- weapon8[[1]]
            damage <- weapon8[[1]]
          }
          if(mode == BEAM) {
            hitstrength <- weapon8[[1]]/2
            damage <- weapon8[[1]]*weapon8[[3]][[timepoint]]*beam_tick
          }
          hulldamage <- damage(armormatrix,weapon8[[8]][[min(weapon8shots,length(weapon8[[8]]))]],startingarmor,minimumarmormultiplier,weapon8[[1]],hitstrength,weapon8mult,minimumdamageafterarmorreduction)
          hullhp <- hullhp - hulldamage
          hullhp <- max(hullhp, 0)
        }
      }
      weapon8shots <- weapon8shots + weapon8[[3]][timepoint]
    }
   
    armorhp <- sum(armormatrix)*15/((ship[[6]]+4)*5)
    if(hullhp==0) armorhp <- 0
   
    if (shieldblock != 0) fluxdissip <- fluxdissip - shieldupkeep
   
    if (softflux > 0){
      if (softflux > fluxdissip) softflux <- softflux - fluxdissip
      else {
        fluxdissip <- max(0,fluxdissip - softflux)
        softflux <- 0
      }
    }
    if (hardflux > 0 & shieldblock == 0){
      hardflux <- max(0,hardflux - fluxdissip)
    }
    if(hullhp > 0){} else {
      softflux <- 0
      hardflux <- 0
    }
    return(list(timepoint, softflux, hardflux, armorhp, hullhp, fluxdissip, fluxcap, startingarmor,armormatrix))
  }
 
  totaltime = 500
 
 
 
  armorhp <- ship[4]
  shieldhp <- ship[3]
  hullhp <- ship[1]
  fluxdissip <- ship[2]
  softflux <- 0
  hardflux <- 0
  fluxcap <- ship[3]
  armorhp <- ship[4]
  startingarmor <- ship[4]
  shieldefficacy <- ship[8]
  shieldupkeep <- ship[9]
 
  weapon1shots <- 1
  weapon2shots <- 1
  weapon3shots <- 1
  weapon4shots <- 1
  weapon5shots <- 1
  weapon6shots <- 1
  weapon7shots <- 1
  weapon8shots <- 1
 
  armormatrix <- matrix(ship[4]/15,5,ship[6]+4)
 
  #now what we do here is we go through all the permutations using the running index, which is i+j+k+l+m+n+o+p for weapons 8
  for (z in 1:length(allperms[,1])) {
    i <- allperms[z,1]
    j <- allperms[z,2]
    k <- allperms[z,3]
    l <- allperms[z,4]
    m <- allperms[z,5]
    n <- allperms[z,6]
    o <- allperms[z,7]
    p <- allperms[z,8]
   
    #for (i in 1:length(weapon1choices)) {
    weapon1<-weapon1choices[[i]]
    #  for (j in 1:length(weapon2choices)) {
    weapon2<-weapon2choices[[j]]
    #    for (k in 1:length(weapon3choices)) {
    weapon3<-weapon3choices[[k]]
    #      for (l in 1:length(weapon4choices)) {
    weapon4<-weapon4choices[[l]]
    #        for (m in 1:length(weapon5choices)) {
    weapon5<-weapon5choices[[m]]
    #          for (n in 1:length(weapon6choices)) {
    weapon6<-weapon6choices[[n]]
    #            for (o in 1:length(weapon7choices)) {
    weapon7<-weapon7choices[[o]]
    #              for (p in 1:length(weapon8choices)) {
    weapon8<-weapon8choices[[p]]
    #lookup <- function(ship, weapon, var) return(lookuptable[[ship]][[weapon]][[var]])
    if(weapon1[4] != ""){
      weapon1[[7]] <- lookup(f,i,1)
      weapon1[[8]] <- lookup(f,i,2)
    }
   
    if(weapon2[4] != ""){
      weapon2[[7]] <- lookup(f,length(weapon1choices)+j,1)
      weapon2[[8]] <- lookup(f,length(weapon1choices)+j,2)
    }
   
    if(weapon3[4] != ""){
      weapon3[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+k,1)
      weapon3[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+k,2)
    }
   
    if(weapon4[4] != ""){
      weapon4[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+l,1)
      weapon4[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+l,2)
    }
   
    if(weapon5[4] != ""){
      weapon5[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+m,1)
      weapon5[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+m,2)
    }
   
    if(weapon6[4] != ""){
      weapon6[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+n,1)
      weapon6[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+n,2)
    }
    if(weapon7[4] != ""){
      weapon7[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+o,1)
      weapon7[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+o,2)
    }
    if(weapon8[4] != ""){
      weapon8[[7]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+length(weapon7choices)+p,1)
      weapon8[[8]] <- lookup(f,length(weapon1choices)+length(weapon2choices)+length(weapon3choices)+length(weapon4choices)+length(weapon5choices)+length(weapon6choices)+length(weapon7choices)+p,2)
    }
   
    #time series - run time series at point t, save it to state, update values according to state, re-run time series, break if ship dies
    for (t in 1:totaltime){
      state <- timeseries(t,softflux,hardflux,armorhp,hullhp,fluxdissip,fluxcap,startingarmor,armormatrix)
      softflux <- state[[2]]
      hardflux <- state[[3]]
      armorhp <- state[[4]]
      hullhp <- state[[5]]
      flux <- softflux + hardflux
      armormatrix <- state[[9]]
      if(hullhp == 0){flux <- 0
      if (timetokill == 0){timetokill <- t
      break}
      }
     
    }
    if (timetokill ==0){timetokill <- NA}
   
    tobind <- c(timetokill,unlist(weapon1[4]),unlist(weapon2[4]),unlist(weapon3[4]),unlist(weapon4[4]),unlist(weapon5[4]),unlist(weapon6[4]),unlist(weapon7[4]),unlist(weapon8[4]))
    timeseriesarray <- rbind(timeseriesarray,tobind)
   
    armorhp <- ship[4]
    shieldhp <- ship[3]
    hullhp <- ship[1]
    fluxdissip <- ship[2]
    softflux <- 0
    hardflux <- 0
    fluxcap <- ship[3]
    armorhp <- ship[4]
    startingarmor <- ship[4]
    shieldefficacy <- ship[8]
    shieldupkeep <- ship[9]
   
    weapon1shots <- 1
    weapon2shots <- 1
    weapon3shots <- 1
    weapon4shots <- 1
    weapon5shots <- 1
    weapon6shots <- 1
    weapon7shots <- 1
    weapon8shots <- 1
    armormatrix <- matrix(ship[4]/15,5,ship[6]+4)
    timetokill <- 0
    #          }
    #        }
    #      }
    #    }
    #  }
    #}
    # }
  }
  #}
  colnames(timeseriesarray) <-  c("Timetokill", "Weapon1", "Weapon2", "Weapon3", "Weapon4", "Weapon5", "Weapon6", "Weapon7", "Weapon8")
 
  sortbytime <- timeseriesarray[order(as.integer(timeseriesarray$Timetokill)),]
 
  write.table(sortbytime, file = paste("optimizeweaponsbytime",shipname,"allweaponswithacc.txt", sep=""), row.names=FALSE, sep="\t")
}
[close]
« Last Edit: November 21, 2022, 10:59:10 PM by CapnHector »
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 #134 on: November 22, 2022, 01:56:04 AM »

I think Liral already got this in their code (If I am interpreting C++ correctly), but if you want to make things fast, you should pre-multiply the armor/shield damage multipliers before the simulation and pass in the computed values of damage against each target type, so you avoid doing the multiplications over and over in every damage calculation. In that case, it really doesn't matter how you represent the damage multipliers and using the conventional form with separate armor/shield/hull multipliers is much less confusing IMO (rather than having shield damage multipliers floating around in armor calculations).

Also allowing for support of arbitrary damage types where hull_mult =/= 1 and or armor_mult =/= 1/shield_mult is good IMO. In that case, you should multiply the overkill armor damage by (hull_mult/armor_mult) rather than multiplying by shield_mult. I think that is much clearer to the reader as well since it conveys that we are 'converting armor damage into hull damage' almost like a unit conversion. That covers frag damage systematically, and also theoretically could cover any custom modded damage types (with whatever multipliers) that you could imagine.

But looking at Liral's code, I think there is some stuff missing (where is the pooling of armor values over adjacent cells and all that?). I'm just gonna post my MATLAB code (with some minor modifications to help non-MATLAB users read it).  I think this is correct (matched results with other peoples sims) and maybe we can confirm we are all doing the same thing.

Code
# a function to update the armor grid and hull. It should be called in a loop over all possible shot locations when doing expected value calculations, otherwise, set hit_prob to 1 for exact damage calculations 

function [armor_grid, hull] = armorUpdate(dmg, location, armor_grid, hull, armor_max, hit_prob)
    # function to get the locations of inner and outer cells given the hit location
    [innerCells, outerCells] = getArmorIndexes(location, armor_grid);

    # total armor value of the weighted sum of inner and outer armor cells, accounting for minimum armor
    armor_pooled = max( (sum(armor_grid[innerCells]) + 1/2 * sum(armor_grid[outerCells])) , .05 * armor_max);

    # calculates the armor damage multiplier accounting for the maximum damage reduction
    armor_damage_mult = max(dmg[2] / (dmg[2] + armor_pooled), .15);

    # compute an array that determines the fraction of the total shot damage that goes to each of the armor cells
    damage_grid_mult = (1/15 * innerCells .* + 1/30 * outerCells) .* armor_damage_mult;
   
    # compute the damage dealt to each armor cell (could combine this with the last line to avoid a temporary variable, but this is     
    # more readable IMO) also accounts for hit probability
    damage_armor = damage_grid_mult .* dmg[2] * hit_prob;

    # compute the damage dealt to hull from each armor cell, accounting for overkill damage to armor
    damage_hull = max(damage_armor - armor_grid, 0) * dmg[3]/dmg[2];

    # add up the total damage to hull and reduce the true hull by that amount
    hull = hull - sum(damage_hull, 'all');
 
    # update the armor grid, note that it is very important to do this after the hull damage is calculated
    armor_grid = max(armor_grid - damage_armor, 0);
   
end

a couple notes:
[innerCells, outerCells] = getArmorIndexes(location, armor_grid);
Calls a function I wrote which gets the indexes of the inner and outer cells given the hit location. innerCells and outerCells are boolean arrays of the same size as armor_grid which have true in all the locations of the inner ring of cells around the hit and false everywhere else (and similarly for outerCells having the second ring of cells around the hit, excluding corners). Then an expression like armor_grid[innerCells] is using logical indexing to return a vector of the armor values of all of the inner cells.

dmg is a vector with the damage to shield, armor and hull pre-calculated accounting for damage multipliers (in that order). This is also a good time to note that MATLAB indexing starts at 1.

also 'dot operations' like .* are element-wise versions of standard operations. So if I have two array A and B of the same size, A .* B will return the element-wise multiplication, while A*B will try to return the matrix multiplication (which would error out if the arrays are not square).

another note: MATLAB implies element-wise multiplication and addition if you have a scalar and an array. So A + b where A is a matrix/array and b is a scalar would return b added to each element of A.

I think that with numpy, python code should look very similar (obviously adjusting syntax for python), but I'm not sure if all the quirks/tricks with logical indexing and element wise operations are 1 to 1 in numpy.
Logged
Pages: 1 ... 7 8 [9] 10 11 ... 32