Skip to content

Commit 94322bb

Browse files
committed
polish: take in account code review
1 parent 3e47dae commit 94322bb

File tree

10 files changed

+171
-155
lines changed

10 files changed

+171
-155
lines changed

apps/fxc-front/src/app/components/2d/path-element.ts

+15-56
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ import { FaiSectors } from '../../gm/fai-sectors';
1313
import { addAltitude } from '../../logic/elevation';
1414
import { getCurrentUrl, pushCurrentState } from '../../logic/history';
1515
import { drawRoute } from '../../logic/messages';
16-
import { CircuitType, Score } from '../../logic/score/scorer';
16+
import { CircuitType, getCircuitType, Score } from '../../logic/score/scorer';
1717
import { setDistance, setEnabled, setRoute, setScore } from '../../redux/planner-slice';
1818
import { RootState, store } from '../../redux/store';
1919
import { PlannerElement } from './planner-element';
20-
import { OptimizationResult, optimize, ScoringRules, ScoringTrack } from 'optimizer';
21-
import { LeagueCode } from '../../logic/score/league/leagues';
20+
import { getOptimizer, ScoringTrack } from 'optimizer';
21+
import { getScoringRules } from '../../logic/score/league/leagues';
2222

2323
// Route color by circuit type.
2424
const ROUTE_STROKE_COLORS = {
@@ -225,10 +225,10 @@ export class PathElement extends connect(store)(LitElement) {
225225
this.closingSector.addListener('rightclick', (e) => this.appendToPath(e.latLng));
226226
}
227227

228-
if (score.closingRadius) {
228+
if (score.closingRadiusM) {
229229
const center = points[score.indexes[0]];
230230
this.closingSector.center = center;
231-
this.closingSector.radius = score.closingRadius;
231+
this.closingSector.radius = score.closingRadiusM;
232232
this.closingSector.update();
233233
this.closingSector.setMap(this.map);
234234
} else {
@@ -252,67 +252,26 @@ export class PathElement extends connect(store)(LitElement) {
252252

253253
private computeScore(points: LatLon[]): Score {
254254
const track: ScoringTrack = {
255-
points: points.map((point, i) => {
256-
return {
257-
...point,
258-
alt: 0,
259-
timeSec: i * 60,
260-
};
261-
}),
262-
minTimeSec: new Date().getTime() / 1000,
255+
points: points.map((point, i) => ({ ...point, alt: 0, timeSec: i * 60 })),
256+
startTimeSec: new Date().getTime() / 1000,
263257
};
264-
const result = optimize({ track }, this.getLeague()).next().value;
265-
const score = new Score({
266-
circuit: this.getCircuitType(result),
267-
distance: result.lengthKm * 1000,
258+
const result = getOptimizer({ track }, getScoringRules(this.league)).next().value;
259+
return new Score({
260+
circuit: getCircuitType(result.circuit),
261+
distanceM: result.lengthKm * 1000,
268262
multiplier: result.multiplier,
269-
closingRadius: result.closingRadius ? result.closingRadius * 1000 : null,
263+
closingRadiusM: result.closingRadius ? result.closingRadius * 1000 : null,
270264
indexes: result.solutionIndices,
265+
points: result.score,
271266
});
272-
// force the score as computed because of an unwanted side effect in constructor.
273-
score.forcePoints(result.score);
274-
return score;
275-
}
276-
277-
private getLeague(): ScoringRules {
278-
switch (this.league as LeagueCode) {
279-
case 'czl':
280-
return ScoringRules.CzechLocal;
281-
case 'cze':
282-
return ScoringRules.CzechEuropean;
283-
case 'czo':
284-
return ScoringRules.CzechOutsideEurope;
285-
case 'fr':
286-
return ScoringRules.FederationFrancaiseVolLibre;
287-
case 'leo':
288-
return ScoringRules.Leonardo;
289-
case 'nor':
290-
return ScoringRules.Norway;
291-
case 'ukc':
292-
return ScoringRules.UnitedKingdomClub;
293-
case 'uki':
294-
return ScoringRules.UnitedKingdomInternational;
295-
case 'ukn':
296-
return ScoringRules.UnitedKingdomNational;
297-
case 'xc':
298-
return ScoringRules.XContest;
299-
case 'xcppg':
300-
return ScoringRules.XContestPPG;
301-
case 'wxc':
302-
return ScoringRules.WorldXC;
303-
}
304-
}
305-
306-
private getCircuitType(result: OptimizationResult) {
307-
return result.circuit as unknown as CircuitType;
308267
}
309268

310269
// Sends a message to the iframe host with the changes.
311270
private postScoreToHost(score: Score) {
312271
let kms = '';
313272
let circuit = '';
314-
if (score.distance && window.parent) {
315-
kms = (score.distance / 1000).toFixed(1);
273+
if (score.distanceM && window.parent) {
274+
kms = (score.distanceM / 1000).toFixed(1);
316275
circuit = CIRCUIT_SHORT_NAME[score.circuit];
317276
if (score.circuit == CircuitType.OpenDistance) {
318277
circuit += score.indexes.length - 2;

apps/fxc-front/src/app/components/2d/planner-element.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export class PlannerElement extends connect(store)(LitElement) {
139139
<div>
140140
<div>${this.score.circuit}</div>
141141
<div class="large">
142-
${unsafeHTML(units.formatUnit(this.score.distance / 1000, this.units.distance, undefined, 'unit'))}
142+
${unsafeHTML(units.formatUnit(this.score.distanceM / 1000, this.units.distance, undefined, 'unit'))}
143143
</div>
144144
</div>
145145
<div class="collapsible">

apps/fxc-front/src/app/logic/score/league/leagues.ts

+36-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
export const LEAGUES: { [name: string]: string } = {
1+
import { ScoringRules } from 'optimizer';
2+
3+
// allowed league codes
4+
export const leagueCodes = ['czl', 'cze', 'czo', 'fr', 'leo', 'nor', 'ukc', 'uki', 'ukn', 'xc', 'xcppg', 'wxc'];
5+
export type LeagueCode = (typeof leagueCodes)[number];
6+
7+
export const LEAGUES: Readonly<Record<LeagueCode, string>> = {
28
czl: 'Czech (ČPP local)',
39
cze: 'Czech (ČPP Europe)',
410
czo: 'Czech (ČPP outside Europe)',
@@ -13,7 +19,32 @@ export const LEAGUES: { [name: string]: string } = {
1319
wxc: 'World XC Online Contest',
1420
};
1521

16-
// allowed league codes
17-
// ensure that all league codes defined in each League sub classes are in this
18-
// closed set.
19-
export type LeagueCode = 'czl' | 'cze' | 'czo' | 'fr' | 'leo' | 'nor' | 'ukc' | 'uki' | 'ukn' | 'xc' | 'xcppg' | 'wxc';
22+
export function getScoringRules(league: string): ScoringRules {
23+
switch (league) {
24+
case 'czl':
25+
return ScoringRules.CzechLocal;
26+
case 'cze':
27+
return ScoringRules.CzechEuropean;
28+
case 'czo':
29+
return ScoringRules.CzechOutsideEurope;
30+
case 'fr':
31+
return ScoringRules.FederationFrancaiseVolLibre;
32+
case 'leo':
33+
return ScoringRules.Leonardo;
34+
case 'nor':
35+
return ScoringRules.Norway;
36+
case 'ukc':
37+
return ScoringRules.UnitedKingdomClub;
38+
case 'uki':
39+
return ScoringRules.UnitedKingdomInternational;
40+
case 'ukn':
41+
return ScoringRules.UnitedKingdomNational;
42+
case 'xc':
43+
return ScoringRules.XContest;
44+
case 'xcppg':
45+
return ScoringRules.XContestPPG;
46+
case 'wxc':
47+
return ScoringRules.WorldXC;
48+
}
49+
throw Error('no corresponding rule for ' + league);
50+
}
+16-12
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
1+
import { CircuitType as OptimizerCircuitType } from 'optimizer';
2+
13
export enum CircuitType {
24
OpenDistance = 'Open distance',
35
FlatTriangle = 'Flat triangle',
46
FaiTriangle = 'Fai triangle',
57
OutAndReturn = 'Out and return',
68
}
79

10+
export function getCircuitType(circuit?: OptimizerCircuitType) {
11+
return circuit as unknown as CircuitType;
12+
}
13+
14+
815
export class Score {
9-
distance: number;
16+
distanceM: number;
1017
indexes: number[];
1118
multiplier: number;
1219
circuit: CircuitType;
13-
closingRadius: number | null;
20+
closingRadiusM: number | null;
1421
points: number;
1522

16-
constructor(score: Partial<Omit<Score,'points'>>) {
17-
this.distance = score.distance || 0;
18-
this.indexes = score.indexes || [];
19-
this.multiplier = score.multiplier || 1;
20-
this.circuit = score.circuit || CircuitType.OpenDistance;
21-
this.closingRadius = score.closingRadius || null;
22-
this.points = (this.distance * this.multiplier) / 1000;
23-
}
24-
public forcePoints(points: number){
25-
this.points = points;
23+
constructor(score: Partial<Score>) {
24+
this.distanceM = score.distanceM ?? 0;
25+
this.indexes = score.indexes ?? [];
26+
this.multiplier = score.multiplier ?? 1;
27+
this.circuit = score.circuit ?? CircuitType.OpenDistance;
28+
this.closingRadiusM = score.closingRadiusM ?? null;
29+
this.points = score.points ?? 0;
2630
}
2731
}

libs/optimizer/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
export { optimize } from './lib/optimizer';
1+
export { getOptimizer } from './lib/optimizer';
22
export type {
33
LatLonAltTime,
4-
OptimizedCircuitType,
4+
CircuitType,
55
ScoringTrack,
66
OptimizationResult,
77
OptimizationOptions,

libs/optimizer/src/lib/fixtures/optimizer.fixtures.ts

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { OptimizationRequest, OptimizationResult, OptimizedCircuitType } from '../optimizer';
1+
import { OptimizationRequest, OptimizationResult, CircuitType } from '../optimizer';
22
import { computeDestinationPoint, getGreatCircleBearing, getPreciseDistance } from 'geolib';
33
import { createSegments } from '../utils/createSegments';
44
import { concatTracks } from '../utils/concatTracks';
@@ -25,7 +25,7 @@ export type OptimizerFixture = {
2525
export function createEmptyTrackFixture(): OptimizerFixture {
2626
return {
2727
givenRequest: {
28-
track: { points: [], minTimeSec: 0 },
28+
track: { points: [], startTimeSec: 0 },
2929
},
3030
givenRules: ScoringRules.FederationFrancaiseVolLibre,
3131
expectedResult: {
@@ -73,7 +73,7 @@ export function createFreeDistanceFixture(
7373
score: distance * multiplier,
7474
lengthKm: distance,
7575
multiplier,
76-
circuit: OptimizedCircuitType.OpenDistance,
76+
circuit: CircuitType.OpenDistance,
7777
optimal: true,
7878
},
7979
};
@@ -119,7 +119,7 @@ export function createFreeDistance1PointFixture(
119119
score: distance * multiplier,
120120
lengthKm: distance,
121121
multiplier,
122-
circuit: OptimizedCircuitType.OpenDistance,
122+
circuit: CircuitType.OpenDistance,
123123
optimal: true,
124124
},
125125
};
@@ -177,7 +177,7 @@ export function createFreeDistance2PointsFixture(
177177
score: distance * multiplier,
178178
lengthKm: distance,
179179
multiplier,
180-
circuit: OptimizedCircuitType.OpenDistance,
180+
circuit: CircuitType.OpenDistance,
181181
optimal: true,
182182
},
183183
};
@@ -244,7 +244,7 @@ export function createFreeDistance3PointsFixture(
244244
score: distance * multiplier,
245245
lengthKm: distance,
246246
multiplier,
247-
circuit: OptimizedCircuitType.OpenDistance,
247+
circuit: CircuitType.OpenDistance,
248248
optimal: true,
249249
},
250250
};
@@ -270,7 +270,7 @@ export function createClosedFlatTriangleFixture(
270270
throw new Error('invalid test data: not a flat triangle');
271271
}
272272
const multiplier = getFlatTriangleMultiplier(givenRules);
273-
return createTriangleFixture(start, p1, p2, nbSegments, givenRules, multiplier, OptimizedCircuitType.FlatTriangle);
273+
return createTriangleFixture(start, p1, p2, nbSegments, givenRules, multiplier, CircuitType.FlatTriangle);
274274
}
275275

276276
/**
@@ -298,7 +298,7 @@ export function createClosedFaiTriangleFixture(
298298
throw new Error('invalid test data: not a FAI triangle');
299299
}
300300
const multiplier = getFaiTriangleMultiplier(givenRules);
301-
return createTriangleFixture(start, p1, p2, nbSegments, givenRules, multiplier, OptimizedCircuitType.FaiTriangle);
301+
return createTriangleFixture(start, p1, p2, nbSegments, givenRules, multiplier, CircuitType.FaiTriangle);
302302
}
303303

304304
/**
@@ -430,7 +430,7 @@ function createTriangleFixture(
430430
nbSegments: number,
431431
givenRules: ScoringRules,
432432
multiplier: number,
433-
circuit: OptimizedCircuitType,
433+
circuit: CircuitType,
434434
): OptimizerFixture {
435435
const distance1 = getPreciseDistance(start, p1);
436436
const distance2 = getPreciseDistance(p1, p2);

libs/optimizer/src/lib/optimizer.spec.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { OptimizationResult, optimize } from './optimizer';
1+
import { OptimizationResult, getOptimizer } from './optimizer';
22
import {
33
createClosedFaiTriangleFixture,
44
createClosedFaiTriangleFixtureWithSmallCycle,
@@ -34,11 +34,11 @@ describe('optimizer', () => {
3434
ScoringRules.XContestPPG,
3535
ScoringRules.WorldXC,
3636
].forEach((rules) => {
37-
describe(ScoringRules[rules] + ' rules', () => {
37+
describe(`${ScoringRules[rules]} rules`, () => {
3838
const oneSegmentPerBranch = 1;
3939
const tenSegmentsPerBranch = 10;
4040
[oneSegmentPerBranch, tenSegmentsPerBranch].forEach((nbSegmentsPerBranch) => {
41-
describe('given a free distance request (' + nbSegmentsPerBranch + ' segments(s)/branch)', () => {
41+
describe(`given a free distance request (${nbSegmentsPerBranch} segments(s)/branch)`, () => {
4242
const fixture = createFreeDistanceFixture(
4343
{ lat: 45, lon: 5 },
4444
{ lat: 45, lon: 6 },
@@ -51,7 +51,7 @@ describe('optimizer', () => {
5151
});
5252

5353
describe(
54-
'given a free distance with 1 intermediate point request (' + nbSegmentsPerBranch + ' segment(s)/branch)',
54+
`given a free distance with 1 intermediate point request (${nbSegmentsPerBranch} segment(s)/branch)`,
5555
() => {
5656
const fixture = createFreeDistance1PointFixture(
5757
{ lat: 45, lon: 5 },
@@ -67,7 +67,7 @@ describe('optimizer', () => {
6767
);
6868

6969
describe(
70-
'given a free distance with 2 intermediate points request (' + nbSegmentsPerBranch + ' segment(s)/branch)',
70+
`given a free distance with 2 intermediate points request (${nbSegmentsPerBranch} segment(s)/branch)`,
7171
() => {
7272
const fixture = createFreeDistance2PointsFixture(
7373
{ lat: 45, lon: 5 },
@@ -84,7 +84,7 @@ describe('optimizer', () => {
8484
);
8585

8686
describe(
87-
'given a free distance with 3 intermediate points request (' + nbSegmentsPerBranch + ' segment(s)/branch)',
87+
`given a free distance with 3 intermediate points request (${nbSegmentsPerBranch} segment(s)/branch)`,
8888
() => {
8989
const fixture = createFreeDistance3PointsFixture(
9090
{ lat: 45, lon: 5 },
@@ -101,7 +101,7 @@ describe('optimizer', () => {
101101
},
102102
);
103103

104-
describe('given a closed flat triangle request (' + nbSegmentsPerBranch + ' segment(s)/branch)', () => {
104+
describe(`given a closed flat triangle request (${nbSegmentsPerBranch} segment(s)/branch)`, () => {
105105
const fixture = createClosedFlatTriangleFixture(
106106
{ lat: 45, lon: 5 },
107107
{ lat: 45, lon: 6 },
@@ -114,7 +114,7 @@ describe('optimizer', () => {
114114
});
115115
});
116116

117-
describe('given a closed FAI triangle request (' + nbSegmentsPerBranch + ' segment(s)/branch)', () => {
117+
describe(`given a closed FAI triangle request (${nbSegmentsPerBranch} segment(s)/branch)`, () => {
118118
const fixture = createClosedFaiTriangleFixture(
119119
{ lat: 45, lon: 5 },
120120
{ lat: 45, lon: 6 },
@@ -153,8 +153,10 @@ describe('optimizer', () => {
153153
});
154154
});
155155

156+
// TODO: IsAsExpected does not really describe the behavior. Something with expect(optimize(...)).toHaveScore(...);
157+
// should be better
156158
function expectOptimizationIsAsExpected(fixture: OptimizerFixture) {
157-
const optimization = optimize(fixture.givenRequest, fixture.givenRules);
159+
const optimization = getOptimizer(fixture.givenRequest, fixture.givenRules);
158160
let currentResult: IteratorResult<OptimizationResult, OptimizationResult>,
159161
done = false;
160162
while (!done) {

0 commit comments

Comments
 (0)