F1Manager-Calc/libs/setup.js

196 lines
6.3 KiB
JavaScript
Raw Normal View History

2023-04-12 03:59:13 +02:00
import {BiasParams, CarSetupParams} from "../consts/params";
2023-05-25 18:13:51 +02:00
export const MAX_SETUP_CANDIDATES = 99;
2023-04-12 03:59:13 +02:00
export const eps = 1e-6;
export const optimalBreakpoint = 0.007; // technically 39/5600 = 0.0069642857142857146 but fine
export const greatBreakpoint = 0.04 + eps;
export const goodBreakpoint = 0.1 + eps;
export const errorConst = 1e20;
export const arrayFloatEqual = (a, b) => {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; ++i) {
if (Math.abs(a[i] - b[i]) > eps) return false;
}
return true;
}
export const setupToBias = (carSetup) => {
2023-04-21 07:28:48 +02:00
try {
2023-06-02 06:21:19 +02:00
return BiasParams.map(biasRow => {
const r = carSetup.map(
(x, idx) => x * biasRow.effect[idx]
).reduce((a,b) => a+b) + biasRow.offset;
return Math.round(r * 56000) / 56000;
}
2023-04-21 07:28:48 +02:00
)
} catch {
return [0.5, 0.5, 0.5, 0.5, 0.5];
}
2023-04-12 03:59:13 +02:00
}
export const biasToSetup = (biasParam) => {
2023-04-21 07:28:48 +02:00
try {
return CarSetupParams.map(carRow =>
biasParam.map(
(x, idx) => (x - BiasParams[idx].offset) * carRow.effect[idx]
).reduce((a,b) => a+b)
)
} catch {
return [0.5, 0.5, 0.5, 0.5, 0.5];
}
2023-04-12 03:59:13 +02:00
}
2023-05-25 11:25:28 +02:00
export const validateFeedbackBreaks = (_result, feedbacks, maxBreaks, validates = [0, 1, 2, 3, 4]) => {
let ruleBreaks = 0;
for (const idx of validates) {
const x = _result[idx];
for(const fs of feedbacks[idx]) {
const dx = Math.abs(x - fs.value);
const f = fs.feedback;
// const scale = {bad: 1, good: 2, great: 3, optimal: 4}
if (f !== "unknown") {
if (
(f === 'bad' && (dx < goodBreakpoint))
||
(f === 'bad+' && (fs.value - x < goodBreakpoint))
||
(f === 'bad-' && (fs.value - x > - goodBreakpoint))
||
(f === 'good' && ((dx > goodBreakpoint) || (dx < greatBreakpoint)))
||
(f === 'great' && ((dx > greatBreakpoint) || (dx < optimalBreakpoint)))
||
(f === 'optimal' && (dx >= optimalBreakpoint))
) {
ruleBreaks += 1;
if (ruleBreaks > maxBreaks) {
return ruleBreaks;
}
}
}
}
}
return ruleBreaks;
}
2023-04-12 03:59:13 +02:00
export const nearestSetup = (biasParam, feedbacks) => {
let nearestResult = null;
let nearestDiff = errorConst;
let lowestRuleBreak = 15;
let possibleSetups = 0;
let possibleSetupList = [];
2023-05-25 11:12:20 +02:00
const steps = CarSetupParams.map(x => Math.round((x.max - x.min) / x.step));
let si = [0, 0, 0, 0, 0];
let v = [0, 0, 0, 0, 0];
let bias = BiasParams.map(x => x.offset);
for(si[0] = 0; si[0] <= steps[0]; si[0]++) {
v[0] = si[0] / steps[0];
for(let i= 0; i < 5; i++) bias[i] += BiasParams[i].effect[0] * v[0];
for(si[1] = 0; si[1] <= steps[1]; si[1]++) {
v[1] = si[1] / steps[1];
for(let i= 0; i < 5; i++) bias[i] += BiasParams[i].effect[1] * v[1];
2023-04-12 03:59:13 +02:00
2023-05-25 11:25:28 +02:00
// maybe we can do something here
// straights are already determined here. validate 4 only
const ruleBreaks = validateFeedbackBreaks(bias, feedbacks, lowestRuleBreak, [4]);
if (ruleBreaks <= lowestRuleBreak) { // added guards
for(si[2] = 0; si[2] <= steps[2]; si[2]++) {
v[2] = si[2] / steps[2];
for(let i= 0; i < 5; i++) bias[i] += BiasParams[i].effect[2] * v[2];
for(si[3] = 0; si[3] <= steps[3]; si[3]++) {
v[3] = si[3] / steps[3];
for(let i= 0; i < 5; i++) bias[i] += BiasParams[i].effect[3] * v[3];
// maybe we can do something here
// toe-out only affects breaking and cornering, so let's validate [0, 3, 4]
const ruleBreaks = validateFeedbackBreaks(bias, feedbacks, lowestRuleBreak, [0, 3]); // 4 doesn't need to revalidate
if (ruleBreaks <= lowestRuleBreak) { // added guards
for(si[4] = 0; si[4] <= steps[4]; si[4]++) {
v[4] = si[4] / steps[4];
for(let i= 0; i < 5; i++) {
bias[i] += BiasParams[i].effect[4] * v[4];
bias[i] = Math.round(bias[i] * 56000) / 56000;
2023-05-25 11:12:20 +02:00
}
2023-05-25 11:25:28 +02:00
{
2023-05-25 11:12:20 +02:00
2023-05-25 11:25:28 +02:00
const ruleBreaks = validateFeedbackBreaks(bias, feedbacks, lowestRuleBreak);
if (ruleBreaks <= lowestRuleBreak) {
if (ruleBreaks < lowestRuleBreak) {
lowestRuleBreak = ruleBreaks;
possibleSetups = 0;
nearestDiff = errorConst;
nearestResult = null;
possibleSetupList = [];
}
2023-05-25 11:12:20 +02:00
2023-05-25 11:25:28 +02:00
const arr = [...v];
let diff = bias.map((x, idx) => {
return (Math.min(Math.abs(x - biasParam[idx]), 0.2) * 100)
}).reduce((x, y) => x+y)
2023-05-25 11:12:20 +02:00
2023-05-25 11:25:28 +02:00
if (diff < errorConst) {
if (diff < nearestDiff) {
nearestDiff = diff;
nearestResult = arr;
}
possibleSetups++;
if (
possibleSetupList.length < MAX_SETUP_CANDIDATES ||
diff < possibleSetupList[MAX_SETUP_CANDIDATES - 1].diff) {
possibleSetupList.push({arr, diff});
possibleSetupList = possibleSetupList.sort(
(x, y) => x.diff - y.diff
).slice(0, MAX_SETUP_CANDIDATES)
}
}
2023-05-25 11:12:20 +02:00
}
}
2023-05-25 11:25:28 +02:00
for(let i= 0; i < 5; i++) bias[i] -= BiasParams[i].effect[4] * v[4];
2023-05-25 11:12:20 +02:00
}
2023-04-12 03:59:13 +02:00
2023-05-25 11:25:28 +02:00
}
for(let i= 0; i < 5; i++) bias[i] -= BiasParams[i].effect[3] * v[3];
2023-04-12 03:59:13 +02:00
}
2023-05-25 11:25:28 +02:00
for(let i= 0; i < 5; i++) bias[i] -= BiasParams[i].effect[2] * v[2];
2023-04-12 03:59:13 +02:00
}
2023-05-25 11:25:28 +02:00
2023-04-12 03:59:13 +02:00
}
2023-05-25 11:25:28 +02:00
2023-05-25 11:12:20 +02:00
for(let i= 0; i < 5; i++) bias[i] -= BiasParams[i].effect[1] * v[1];
2023-04-12 03:59:13 +02:00
}
2023-05-25 11:12:20 +02:00
for(let i= 0; i < 5; i++) bias[i] -= BiasParams[i].effect[0] * v[0];
2023-04-12 03:59:13 +02:00
}
possibleSetupList = possibleSetupList.sort((x, y) => x.diff - y.diff).slice(0, MAX_SETUP_CANDIDATES)
return {setup: nearestResult, possibleSetups, lowestRuleBreak, possibleSetupList};
}
2023-04-28 16:20:04 +02:00
export const randomSetup = () => CarSetupParams.map(params => {
const s = (params.max - params.min) / params.step;
return Math.floor(Math.random() * (s + 1)) / s;
})