Last active
February 3, 2023 19:36
-
-
Save jnorthrup/5234d62630a6bddc17d556cc83f1de91 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ed.fumes.ed.fumes | |
import ed.fumes.ed.fumes.FrameShiftDrive.FSDClass.* | |
import ed.fumes.ed.fumes.FrameShiftDrive.FSDRating.* | |
import kotlin.math.min | |
import kotlin.math.pow | |
typealias Tons = Double | |
typealias LY = Double | |
data class FrameShiftDrive( | |
val rating: FSDRating, | |
val fsdClass: FSDClass, | |
var optimalMass: Tons, | |
var maxFuelPerJump: Tons | |
) { | |
enum class FSDRating(val linearConstant: Double) { A(12.0), B(10.0), C(8.0), D(10.0), E(11.0), } | |
enum class FSDClass(val powerConstant: Double) { C2(2.00), C3(2.15), C4(2.30), C5(2.45), C6(2.60), C7(2.75), C8(2.90) } | |
var boostFactor = 1.0 | |
val linearConstant: Double get() = rating.linearConstant | |
val powerConstant: Double get() = fsdClass.powerConstant | |
} | |
enum class BaseFSD(val fsd: FrameShiftDrive) { | |
base2A(FrameShiftDrive(A, C2, 48.0, 0.60)), | |
base2B(FrameShiftDrive(B, C2, 54.0, 0.60)), | |
base2C(FrameShiftDrive(C, C2, 60.0, 0.60)), | |
base2D(FrameShiftDrive(D, C2, 75.0, 0.80)), | |
base2E(FrameShiftDrive(E, C2, 90.0, 0.90)), | |
base3A(FrameShiftDrive(A, C3, 80.0, 1.20)), | |
base3B(FrameShiftDrive(B, C3, 90.0, 1.20)), | |
base3C(FrameShiftDrive(C, C3, 100.0, 1.20)), | |
base3D(FrameShiftDrive(D, C3, 125.0, 1.50)), | |
base3E(FrameShiftDrive(E, C3, 150.0, 1.80)), | |
base4A(FrameShiftDrive(A, C4, 280.0, 2.00)), | |
base4B(FrameShiftDrive(B, C4, 315.0, 2.00)), | |
base4C(FrameShiftDrive(C, C4, 350.0, 2.00)), | |
base4D(FrameShiftDrive(D, C4, 438.0, 2.50)), | |
base4E(FrameShiftDrive(E, C4, 525.0, 3.00)), | |
base5A(FrameShiftDrive(A, C5, 560.0, 3.30)), | |
base5B(FrameShiftDrive(B, C5, 630.0, 3.30)), | |
base5C(FrameShiftDrive(C, C5, 700.0, 3.30)), | |
base5D(FrameShiftDrive(D, C5, 875.0, 4.10)), | |
base5E(FrameShiftDrive(E, C5, 1050.0, 5.00)), | |
base6A(FrameShiftDrive(A, C6, 960.0, 5.30)), | |
base6B(FrameShiftDrive(B, C6, 1080.0, 5.30)), | |
base6C(FrameShiftDrive(C, C6, 1200.0, 5.30)), | |
base6D(FrameShiftDrive(D, C6, 1500.0, 6.60)), | |
base6E(FrameShiftDrive(E, C6, 1800.0, 8.00)), | |
base7A(FrameShiftDrive(A, C7, 1440.0, 8.50)), | |
base7B(FrameShiftDrive(B, C7, 1620.0, 8.50)), | |
base7C(FrameShiftDrive(C, C7, 1800.0, 8.50)), | |
base7D(FrameShiftDrive(D, C7, 2250.0, 10.60)), | |
base7E(FrameShiftDrive(E, C7, 2700.0, 12.80)), | |
} | |
enum class FSDBooster( | |
/** jump range increase in LY */ | |
val jumpRangeIncrease: LY | |
) { | |
booster1H(4.00), | |
booster2H(6.00), | |
booster3H(7.75), | |
booster4H(9.25), | |
booster5H(10.50), | |
} | |
data class Ship( | |
val name: String, | |
val unladedMass: Tons, | |
val fsd: FrameShiftDrive, | |
val fuelCapacity: Tons, | |
val cargoCapacity: Tons, | |
val fsdBooster: FSDBooster? = null, | |
var fuelRemaining: Tons = fuelCapacity, | |
var cargoRemaining: Tons = 0.0, | |
) { | |
var boostFactor: Double | |
get() = fsd.boostFactor | |
set(value) { | |
fsd.boostFactor = value | |
} | |
val totalMass: Tons = unladedMass + cargoRemaining + fuelRemaining | |
/* csharp code we want to port | |
// from EDCD 20/11/22 updated fuel use . Note refill tank is not taken into account in frontiers calc | |
public double FuelUse(double cargo, double unladenmass, double fuel, double distance, double boost) | |
{ | |
double mass = unladenmass + cargo + fuel; // weight | |
double basemaxrange = (OptimalMass / mass) * Math.Pow((MaxFuelPerJump * 1000 / LinearConstant), (1 / PowerConstant)); | |
double boostfactor = Math.Pow((basemaxrange / (basemaxrange + FSDGuardianBoosterRange)), PowerConstant); | |
return boostfactor * LinearConstant * 0.001 * Math.Pow(((distance / boost) * mass / OptimalMass), PowerConstant); | |
} | |
// Eahlstan special, account for small fuel loads | |
public double JumpRange(int currentCargo, double unladenMassHullModules, double fuel, double boost) | |
{ | |
double mass = currentCargo + unladenMassHullModules + fuel; | |
double massf = OptimalMass / mass; | |
double fuelmultiplier = (LinearConstant * 0.001); | |
double powerf = Math.Pow(MaxFuelPerJump / fuelmultiplier, 1 / PowerConstant); | |
double basev = powerf * massf; | |
if (fuel >= MaxFuelPerJump) | |
{ | |
return (basev + FSDGuardianBoosterRange) * boost; | |
} | |
else | |
{ | |
double basemaxrange = (OptimalMass / mass) * Math.Pow((MaxFuelPerJump * 1000 / LinearConstant), (1 / PowerConstant)); | |
double boostfactor = Math.Pow((basemaxrange / (basemaxrange + FSDGuardianBoosterRange)), PowerConstant); | |
return (Math.Pow((fuel / (boostfactor * fuelmultiplier)), (1 / PowerConstant)) * massf) * boost; | |
} | |
} | |
*/ | |
fun jumpRangeForFuel(fuel1: Tons): LY { | |
val fuel = min(fuel1, fsd.maxFuelPerJump) | |
val mass = totalMass | |
val massf = fsd.optimalMass / mass | |
val fuelmultiplier = fsd.linearConstant * 0.001 | |
val grdnBoost: LY = fsdBooster?.jumpRangeIncrease ?: 0.0 | |
return if (fuel >= fsd.maxFuelPerJump) { | |
val powerf = (fsd.maxFuelPerJump / fuelmultiplier).pow(1 / fsd.powerConstant) | |
val basev = powerf * massf | |
(basev + grdnBoost) * boostFactor | |
} else { | |
val basemaxrange: LY = | |
fsd.optimalMass / mass * (fsd.maxFuelPerJump * 1000 / fsd.linearConstant).pow(1 / fsd.powerConstant) | |
val boostfactor = basemaxrange.pow(fsd.powerConstant) / (basemaxrange + grdnBoost).pow(fsd.powerConstant) | |
(fuel / (boostfactor * fuelmultiplier)).pow(1 / fsd.powerConstant) * massf * boostFactor | |
} | |
} | |
fun fuelUse(distance: LY): Tons { | |
val baseMaxRange = | |
(fsd.optimalMass / totalMass) * (fsd.maxFuelPerJump * 1000 / fsd.linearConstant).pow(1 / fsd.powerConstant) | |
val boostFactor = baseMaxRange.pow(fsd.powerConstant) / (baseMaxRange + (fsdBooster?.jumpRangeIncrease | |
?: 0.0)).pow(fsd.powerConstant) | |
return boostFactor * fsd.linearConstant * 0.001 * ((distance / boostFactor) * totalMass / fsd.optimalMass).pow( | |
fsd.powerConstant | |
) | |
} | |
fun jump(distance: LY): Boolean { | |
val fuel = fuelUse(distance) | |
if (fuel > fuelRemaining) { | |
return false | |
} | |
fuelRemaining -= fuel | |
return true | |
} | |
fun refuel(): Ship { | |
fuelRemaining = fuelCapacity | |
return this | |
} | |
fun maxJumpRange(remaining: Tons = Double.MAX_VALUE): LY = copy().run { | |
fuelRemaining = min(fuelCapacity, remaining) | |
//loop through jumps until we run out of fuel | |
var range = 0.0 | |
do { | |
val jumpRange = jumpRangeForFuel(fuelRemaining) | |
fuelRemaining -= fuelUse(jumpRange) | |
range += jumpRange | |
} while (fuelRemaining > 0.0) | |
range | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Ship(name=SideWinder, unladedMass=43.2, fsd=FrameShiftDrive(rating=E, fsdClass=C2, optimalMass=90.0, maxFuelPerJump=0.9), fuelCapacity=2.0, cargoCapacity=4.0, fsdBooster=null, fuelRemaining=2.0, cargoRemaining=0.0) | |
jump 2 LY: uses 0.011097995061728394 tons | |
jump 10 LY: uses 0.27744987654320985 tons | |
jump with 2 tons of fuel: 18.0106334150434 LY | |
jump with full fuel empty cargo: 18.0106334150434 LY | |
max range: 44.51156098569662 LY | |
Ship(name=Anaconda, unladedMass=1066.4, fsd=FrameShiftDrive(rating=E, fsdClass=C6, optimalMass=1800.0, maxFuelPerJump=8.0), fuelCapacity=32.0, cargoCapacity=114.0, fsdBooster=booster5H, fuelRemaining=32.0, cargoRemaining=0.0) | |
jump 2 LY: uses 0.10202684812309537 tons | |
jump 10 LY: uses 6.6994088347747605 tons | |
jump with 2 tons of fuel: 18.283476175626898 LY | |
jump with full fuel empty cargo: 31.16163995087626 LY |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ed.fumes.ed.fumes | |
import ed.fumes.ed.fumes.Ship | |
import kotlin.test.* | |
class TestShip { | |
@Test | |
fun testShip1() { | |
// show me a SideWinder ship with no booster | |
val sidey = Ship("SideWinder", 43.2, BaseFSD.base2E.fsd.copy(), 2.0, 4.0) | |
// anaconda with a 5H booster | |
val anaconda = Ship("Anaconda", 1066.4, BaseFSD.base6E.fsd.copy(), 32.0, 114.0, FSDBooster.booster5H) | |
listOf(sidey, anaconda).forEach { | |
println(it) | |
// jump 2 LY | |
val jump2 = it.fuelUse(2.0) | |
println("jump 2 LY: uses $jump2 tons") | |
// jump 10 LY | |
val jump10 = it.fuelUse(10.0) | |
println("jump 10 LY: uses $jump10 tons") | |
//jump with 2 tons of fuel | |
val jumpRange2 = it.jumpRangeForFuel(2.0) | |
println("jump with 2 tons of fuel: $jumpRange2 LY") | |
//jump with full fuel empty cargo | |
val jumpRangeFull = it.jumpRangeForFuel(it.fuelCapacity) | |
println("jump with full fuel empty cargo: $jumpRangeFull LY") | |
//show maxrange with a copy of the ship | |
val maxRange = it.copy().maxJumpRange() | |
println("max range: $maxRange LY") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment