|
|
@@ -55,17 +55,17 @@ function phase(course: racecourse.Course, op: Op, phase: race.Phase, type: SpanT
|
|
|
|
if (phase === race.Phase.EarlyRace) {
|
|
|
|
if (phase === race.Phase.EarlyRace) {
|
|
|
|
return { start: 0, end: p[race.Phase.EarlyRace], type };
|
|
|
|
return { start: 0, end: p[race.Phase.EarlyRace], type };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { start: p[phase - 1 as race.Phase], end: p[phase], type };
|
|
|
|
return { start: p[(phase - 1) as race.Phase], end: p[phase], type };
|
|
|
|
case Op.Le:
|
|
|
|
case Op.Le:
|
|
|
|
return { start: 0, end: p[phase], type };
|
|
|
|
return { start: 0, end: p[phase], type };
|
|
|
|
case Op.Ge:
|
|
|
|
case Op.Ge:
|
|
|
|
if (phase === race.Phase.EarlyRace) {
|
|
|
|
if (phase === race.Phase.EarlyRace) {
|
|
|
|
return { start: 0, end: course.distance, type };
|
|
|
|
return { start: 0, end: course.distance, type };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { start: p[phase - 1 as race.Phase], end: course.distance, type };
|
|
|
|
return { start: p[(phase - 1) as race.Phase], end: course.distance, type };
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
// Other operators aren't used.
|
|
|
|
// Other operators aren't used.
|
|
|
|
throw new Error("weird operator on phase condition");
|
|
|
|
throw new Error('weird operator on phase condition');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -90,10 +90,10 @@ function distrate(course: racecourse.Course, cond: Condition, type: SpanType): S
|
|
|
|
switch (cond.op) {
|
|
|
|
switch (cond.op) {
|
|
|
|
case Op.Le:
|
|
|
|
case Op.Le:
|
|
|
|
case Op.Lt:
|
|
|
|
case Op.Lt:
|
|
|
|
return { start: 0, end: Math.round(course.distance * cond.arg / 100), type };
|
|
|
|
return { start: 0, end: Math.round((course.distance * cond.arg) / 100), type };
|
|
|
|
case Op.Ge:
|
|
|
|
case Op.Ge:
|
|
|
|
case Op.Gt:
|
|
|
|
case Op.Gt:
|
|
|
|
return { start: Math.round(course.distance * cond.arg / 100), end: course.distance, type };
|
|
|
|
return { start: Math.round((course.distance * cond.arg) / 100), end: course.distance, type };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { start: 0, end: 0, type: SpanType.Never };
|
|
|
|
return { start: 0, end: 0, type: SpanType.Never };
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -103,7 +103,7 @@ function remaining(course: racecourse.Course, cond: Condition, type: SpanType):
|
|
|
|
case Op.Eq:
|
|
|
|
case Op.Eq:
|
|
|
|
return { start: course.distance - cond.arg - 1, end: course.distance - cond.arg + 1, type };
|
|
|
|
return { start: course.distance - cond.arg - 1, end: course.distance - cond.arg + 1, type };
|
|
|
|
case Op.Ne:
|
|
|
|
case Op.Ne:
|
|
|
|
throw new Error("weird operator on remaining distance");
|
|
|
|
throw new Error('weird operator on remaining distance');
|
|
|
|
case Op.Le:
|
|
|
|
case Op.Le:
|
|
|
|
case Op.Lt:
|
|
|
|
case Op.Lt:
|
|
|
|
return { start: course.distance - cond.arg, end: course.distance, type };
|
|
|
|
return { start: course.distance - cond.arg, end: course.distance, type };
|
|
|
@@ -169,33 +169,58 @@ function must(x: boolean): SpanType {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const perCond: Readonly<Partial<Record<Condition['type'], (course: racecourse.Course, cond: Condition) => Span[]>>> = {
|
|
|
|
const perCond: Readonly<Partial<Record<Condition['type'], (course: racecourse.Course, cond: Condition) => Span[]>>> = {
|
|
|
|
'accumulatetime': (course, cond) => [accumulatetime(course, cond.arg, SpanType.Whenever)],
|
|
|
|
accumulatetime: (course, cond) => [accumulatetime(course, cond.arg, SpanType.Whenever)],
|
|
|
|
'activate_count_all_team': (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
activate_count_all_team: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
'activate_count_end_after': (course) => [endafter(course, SpanType.Skill)],
|
|
|
|
activate_count_end_after: (course) => [endafter(course, SpanType.Skill)],
|
|
|
|
'activate_count_heal': (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
activate_count_heal: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
'activate_count_later_half': (course) => [{ start: course.distance / 2, end: course.distance, type: SpanType.Skill }],
|
|
|
|
activate_count_later_half: (course) => [{ start: course.distance / 2, end: course.distance, type: SpanType.Skill }],
|
|
|
|
'activate_count_middle': (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Skill)],
|
|
|
|
activate_count_middle: (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Skill)],
|
|
|
|
'activate_count_start': (course) => [phase(course, Op.Eq, race.Phase.EarlyRace, SpanType.Skill)],
|
|
|
|
activate_count_start: (course) => [phase(course, Op.Eq, race.Phase.EarlyRace, SpanType.Skill)],
|
|
|
|
'all_corner_random': (course) => coursespans(course.spans.filter((s) => s.corner != null), SpanType.Random),
|
|
|
|
all_corner_random: (course) =>
|
|
|
|
'change_order_up_end_after': (course) => [endafter(course, SpanType.Whenever)],
|
|
|
|
coursespans(
|
|
|
|
'change_order_up_finalcorner_after': (course) => [is_finalcorner(course, true, SpanType.Whenever)],
|
|
|
|
course.spans.filter((s) => s.corner != null),
|
|
|
|
'change_order_up_middle': (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Whenever)],
|
|
|
|
SpanType.Random,
|
|
|
|
'compete_fight_count': (course) => [laststraight(course, SpanType.Whenever)],
|
|
|
|
),
|
|
|
|
'corner': (course, cond) => coursespans(course.spans.filter((s) => opcompare(cond, s.corner ?? 0)), SpanType.Whenever),
|
|
|
|
change_order_up_end_after: (course) => [endafter(course, SpanType.Whenever)],
|
|
|
|
'corner_count': (course, cond) => [{ start: 0, end: course.distance, type: opcompare(cond, course.spans.reduce((n, s) => n + (s.corner != null ? 1 : 0), 0)) ? SpanType.Never : SpanType.Whenever }],
|
|
|
|
change_order_up_finalcorner_after: (course) => [is_finalcorner(course, true, SpanType.Whenever)],
|
|
|
|
'corner_random': (course, cond) => {
|
|
|
|
change_order_up_middle: (course) => [phase(course, Op.Eq, race.Phase.MidRace, SpanType.Whenever)],
|
|
|
|
|
|
|
|
compete_fight_count: (course) => [laststraight(course, SpanType.Whenever)],
|
|
|
|
|
|
|
|
corner: (course, cond) =>
|
|
|
|
|
|
|
|
coursespans(
|
|
|
|
|
|
|
|
course.spans.filter((s) => opcompare(cond, s.corner ?? 0)),
|
|
|
|
|
|
|
|
SpanType.Whenever,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
corner_count: (course, cond) => [
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
start: 0,
|
|
|
|
|
|
|
|
end: course.distance,
|
|
|
|
|
|
|
|
type: opcompare(
|
|
|
|
|
|
|
|
cond,
|
|
|
|
|
|
|
|
course.spans.reduce((n, s) => n + (s.corner != null ? 1 : 0), 0),
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
? SpanType.Never
|
|
|
|
|
|
|
|
: SpanType.Whenever,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
corner_random: (course, cond) => {
|
|
|
|
const c = course.spans.findLast((s) => s.corner === cond.arg);
|
|
|
|
const c = course.spans.findLast((s) => s.corner === cond.arg);
|
|
|
|
if (c == null) {
|
|
|
|
if (c == null) {
|
|
|
|
return [{ start: 0, end: 0, type: SpanType.Never }];
|
|
|
|
return [{ start: 0, end: 0, type: SpanType.Never }];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return [{ start: c.start, end: c.end, type: SpanType.Random }];
|
|
|
|
return [{ start: c.start, end: c.end, type: SpanType.Random }];
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'course_distance': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.distance)) }],
|
|
|
|
course_distance: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.distance)) }],
|
|
|
|
'distance_rate': (course, cond) => [distrate(course, cond, SpanType.Whenever)],
|
|
|
|
distance_rate: (course, cond) => [distrate(course, cond, SpanType.Whenever)],
|
|
|
|
'distance_rate_after_random': (course, cond) => [distrate(course, cond, SpanType.Random)],
|
|
|
|
distance_rate_after_random: (course, cond) => [distrate(course, cond, SpanType.Random)],
|
|
|
|
'distance_type': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, racecourse.distanceType(course))) }],
|
|
|
|
distance_type: (course, cond) => [
|
|
|
|
'down_slope_random': (course) => coursespans(course.slopes.filter((s) => s.per < 0), SpanType.Random),
|
|
|
|
{ start: 0, end: course.distance, type: must(opcompare(cond, racecourse.distanceType(course))) },
|
|
|
|
'furlong': (course, cond) => {
|
|
|
|
],
|
|
|
|
|
|
|
|
down_slope_random: (course) =>
|
|
|
|
|
|
|
|
coursespans(
|
|
|
|
|
|
|
|
course.slopes.filter((s) => s.per < 0),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
furlong: (course, cond) => {
|
|
|
|
switch (cond.op) {
|
|
|
|
switch (cond.op) {
|
|
|
|
case Op.Eq:
|
|
|
|
case Op.Eq:
|
|
|
|
return [{ start: cond.arg * 201.168, end: (cond.arg + 1) * 201.168, type: SpanType.Whenever }];
|
|
|
|
return [{ start: cond.arg * 201.168, end: (cond.arg + 1) * 201.168, type: SpanType.Whenever }];
|
|
|
@@ -212,9 +237,9 @@ const perCond: Readonly<Partial<Record<Condition['type'], (course: racecourse.Co
|
|
|
|
return [{ start: cond.arg * 201.168, end: course.distance, type: SpanType.Whenever }];
|
|
|
|
return [{ start: cond.arg * 201.168, end: course.distance, type: SpanType.Whenever }];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'ground_type': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.ground)) }],
|
|
|
|
ground_type: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.ground)) }],
|
|
|
|
// hp_per would be an interesting challenge...
|
|
|
|
// hp_per would be an interesting challenge...
|
|
|
|
'is_finalcorner': (course, cond) => {
|
|
|
|
is_finalcorner: (course, cond) => {
|
|
|
|
// Either is_finalcorner==1 or is_finalcorner==0.
|
|
|
|
// Either is_finalcorner==1 or is_finalcorner==0.
|
|
|
|
if (cond.arg === 1) {
|
|
|
|
if (cond.arg === 1) {
|
|
|
|
return [is_finalcorner(course, true, SpanType.Whenever)];
|
|
|
|
return [is_finalcorner(course, true, SpanType.Whenever)];
|
|
|
@@ -226,84 +251,122 @@ const perCond: Readonly<Partial<Record<Condition['type'], (course: racecourse.Co
|
|
|
|
return [{ start: 0, end: c.start, type: SpanType.Whenever }];
|
|
|
|
return [{ start: 0, end: c.start, type: SpanType.Whenever }];
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// TODO(zeph): is_abroad
|
|
|
|
// TODO(zeph): is_abroad
|
|
|
|
'is_activate_any_skill': (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
is_activate_any_skill: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
'is_activate_heal_skill': (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
is_activate_heal_skill: (course) => [{ start: 0, end: course.distance, type: SpanType.Skill }],
|
|
|
|
'is_basis_distance': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.distance % 400 === 0 ? 1 : 0)) }],
|
|
|
|
is_basis_distance: (course, cond) => [
|
|
|
|
'is_dirtgrade': (course, cond) => {
|
|
|
|
{ start: 0, end: course.distance, type: must(opcompare(cond, course.distance % 400 === 0 ? 1 : 0)) },
|
|
|
|
|
|
|
|
],
|
|
|
|
|
|
|
|
is_dirtgrade: (course, cond) => {
|
|
|
|
// NAR tracks: Ooi, Kawasaki, Funabashi, Morioka
|
|
|
|
// NAR tracks: Ooi, Kawasaki, Funabashi, Morioka
|
|
|
|
const is = [10101, 10103, 10104, 10105].includes(course.race_track_id);
|
|
|
|
const is = [10101, 10103, 10104, 10105].includes(course.race_track_id);
|
|
|
|
return [{ start: 0, end: course.distance, type: must(opcompare(cond, is ? 1 : 0)) }];
|
|
|
|
return [{ start: 0, end: course.distance, type: must(opcompare(cond, is ? 1 : 0)) }];
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'is_finalcorner_laterhalf': (course) => [later(is_finalcorner(course, false, SpanType.Whenever), 0.5)],
|
|
|
|
is_finalcorner_laterhalf: (course) => [later(is_finalcorner(course, false, SpanType.Whenever), 0.5)],
|
|
|
|
'is_finalcorner_random': (course) => [is_finalcorner(course, false, SpanType.Random)],
|
|
|
|
is_finalcorner_random: (course) => [is_finalcorner(course, false, SpanType.Random)],
|
|
|
|
'is_last_straight': (course) => [laststraight(course, SpanType.Whenever)],
|
|
|
|
is_last_straight: (course) => [laststraight(course, SpanType.Whenever)],
|
|
|
|
'is_last_straight_onetime': (course) => [first(laststraight(course, SpanType.Whenever), 0.02)],
|
|
|
|
is_last_straight_onetime: (course) => [first(laststraight(course, SpanType.Whenever), 0.02)],
|
|
|
|
'is_lastspurt': (course) => [endafter(course, SpanType.Whenever)],
|
|
|
|
is_lastspurt: (course) => [endafter(course, SpanType.Whenever)],
|
|
|
|
// could implement is_temptation!=0, but that condition isn't used
|
|
|
|
// could implement is_temptation!=0, but that condition isn't used
|
|
|
|
'is_tight_track': (course, cond) => {
|
|
|
|
is_tight_track: (course, cond) => {
|
|
|
|
// Sapporo, Hakodate, Fukushima, Kokura, Kawasaki, Funabashi
|
|
|
|
// Sapporo, Hakodate, Fukushima, Kokura, Kawasaki, Funabashi
|
|
|
|
// TODO(zeph): GameTora says "afaik" for this list...
|
|
|
|
// TODO(zeph): GameTora says "afaik" for this list...
|
|
|
|
const is = [10001, 10002, 10004, 10010, 10103, 10104];
|
|
|
|
const is = [10001, 10002, 10004, 10010, 10103, 10104];
|
|
|
|
return [{ start: 0, end: course.distance, type: must(opcompare(cond, is ? 1 : 0)) }];
|
|
|
|
return [{ start: 0, end: course.distance, type: must(opcompare(cond, is ? 1 : 0)) }];
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'last_straight_random': (course) => [laststraight(course, SpanType.Random)],
|
|
|
|
last_straight_random: (course) => [laststraight(course, SpanType.Random)],
|
|
|
|
'lastspurt': (course) => [endafter(course, SpanType.Whenever)],
|
|
|
|
lastspurt: (course) => [endafter(course, SpanType.Whenever)],
|
|
|
|
'order_rate_in20_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_in20_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'order_rate_in40_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_in40_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'order_rate_in50_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_in50_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'order_rate_in80_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_in80_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'order_rate_out20_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_out20_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'order_rate_out40_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_out40_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'order_rate_out50_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_out50_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'order_rate_out70_continue': (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
order_rate_out70_continue: (course) => [accumulatetime(course, 5, SpanType.Whenever)],
|
|
|
|
'phase': (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Whenever)],
|
|
|
|
phase: (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Whenever)],
|
|
|
|
'phase_corner_random': (course, cond) => {
|
|
|
|
phase_corner_random: (course, cond) => {
|
|
|
|
const p = phase(course, Op.Eq, cond.arg, SpanType.Random);
|
|
|
|
const p = phase(course, Op.Eq, cond.arg, SpanType.Random);
|
|
|
|
return coursespans(course.spans.filter((s) => s.corner != null), SpanType.Random).map((s) => intersection(s, p));
|
|
|
|
return coursespans(
|
|
|
|
|
|
|
|
course.spans.filter((s) => s.corner != null),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
).map((s) => intersection(s, p));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'phase_first_half_straight_random': (course, cond) => {
|
|
|
|
phase_first_half_straight_random: (course, cond) => {
|
|
|
|
const p = first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5);
|
|
|
|
const p = first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5);
|
|
|
|
return coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random).map((s) => intersection(s, p));
|
|
|
|
return coursespans(
|
|
|
|
|
|
|
|
course.spans.filter((s) => s.straight != null),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
).map((s) => intersection(s, p));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'phase_firsthalf': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)],
|
|
|
|
phase_firsthalf: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)],
|
|
|
|
'phase_firsthalf_random': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)],
|
|
|
|
phase_firsthalf_random: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)],
|
|
|
|
'phase_firstquarter': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.25)],
|
|
|
|
phase_firstquarter: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.25)],
|
|
|
|
'phase_firstquarter_random': (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.25)],
|
|
|
|
phase_firstquarter_random: (course, cond) => [first(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.25)],
|
|
|
|
'phase_laterhalf': (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)],
|
|
|
|
phase_laterhalf: (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Whenever), 0.5)],
|
|
|
|
'phase_laterhalf_random': (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)],
|
|
|
|
phase_laterhalf_random: (course, cond) => [later(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5)],
|
|
|
|
'phase_latter_half_straight_random': (course, cond) => {
|
|
|
|
phase_latter_half_straight_random: (course, cond) => {
|
|
|
|
const p = later(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5);
|
|
|
|
const p = later(phase(course, Op.Eq, cond.arg, SpanType.Random), 0.5);
|
|
|
|
return coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random).map((s) => intersection(s, p));
|
|
|
|
return coursespans(
|
|
|
|
|
|
|
|
course.spans.filter((s) => s.straight != null),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
).map((s) => intersection(s, p));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'phase_random': (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Random)],
|
|
|
|
phase_random: (course, cond) => [phase(course, cond.op, cond.arg, SpanType.Random)],
|
|
|
|
'phase_straight_random': (course, cond) => {
|
|
|
|
phase_straight_random: (course, cond) => {
|
|
|
|
const p = phase(course, Op.Eq, cond.arg, SpanType.Random);
|
|
|
|
const p = phase(course, Op.Eq, cond.arg, SpanType.Random);
|
|
|
|
return coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random).map((s) => intersection(s, p));
|
|
|
|
return coursespans(
|
|
|
|
|
|
|
|
course.spans.filter((s) => s.straight != null),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
).map((s) => intersection(s, p));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'remain_distance': (course, cond) => [remaining(course, cond, SpanType.Whenever)],
|
|
|
|
remain_distance: (course, cond) => [remaining(course, cond, SpanType.Whenever)],
|
|
|
|
'remain_distance_viewer_id': (course, cond) => [remaining(course, cond, SpanType.Whenever)],
|
|
|
|
remain_distance_viewer_id: (course, cond) => [remaining(course, cond, SpanType.Whenever)],
|
|
|
|
'rotation': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.turn)) }],
|
|
|
|
rotation: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.turn)) }],
|
|
|
|
'run_at_full_speed_random': (course) => [endafter(course, SpanType.Random)],
|
|
|
|
run_at_full_speed_random: (course) => [endafter(course, SpanType.Random)],
|
|
|
|
'slope': (course, cond) => {
|
|
|
|
slope: (course, cond) => {
|
|
|
|
switch (cond.arg) {
|
|
|
|
switch (cond.arg) {
|
|
|
|
case 0:
|
|
|
|
case 0:
|
|
|
|
return coursespans(racecourse.fillSlopes(course.slopes, course.distance).filter((s) => s.per === 0), SpanType.Whenever);
|
|
|
|
return coursespans(
|
|
|
|
|
|
|
|
racecourse.fillSlopes(course.slopes, course.distance).filter((s) => s.per === 0),
|
|
|
|
|
|
|
|
SpanType.Whenever,
|
|
|
|
|
|
|
|
);
|
|
|
|
case 1:
|
|
|
|
case 1:
|
|
|
|
return coursespans(course.slopes.filter((s) => s.per > 0), SpanType.Whenever);
|
|
|
|
return coursespans(
|
|
|
|
|
|
|
|
course.slopes.filter((s) => s.per > 0),
|
|
|
|
|
|
|
|
SpanType.Whenever,
|
|
|
|
|
|
|
|
);
|
|
|
|
case 2:
|
|
|
|
case 2:
|
|
|
|
return coursespans(course.slopes.filter((s) => s.per < 0), SpanType.Whenever);
|
|
|
|
return coursespans(
|
|
|
|
|
|
|
|
course.slopes.filter((s) => s.per < 0),
|
|
|
|
|
|
|
|
SpanType.Whenever,
|
|
|
|
|
|
|
|
);
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
throw new Error("weird argument to slope condition");
|
|
|
|
throw new Error('weird argument to slope condition');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'straight_front_type': (course, cond) => coursespans(course.spans.filter((s) => s.straight === cond.arg), SpanType.Whenever),
|
|
|
|
straight_front_type: (course, cond) =>
|
|
|
|
'straight_random': (course) => coursespans(course.spans.filter((s) => s.straight != null), SpanType.Random),
|
|
|
|
coursespans(
|
|
|
|
'track_id': (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.race_track_id)) }],
|
|
|
|
course.spans.filter((s) => s.straight === cond.arg),
|
|
|
|
'up_slope_random': (course) => coursespans(course.slopes.filter((s) => s.per > 0), SpanType.Random),
|
|
|
|
SpanType.Whenever,
|
|
|
|
'up_slope_random_later_half': (course) => {
|
|
|
|
),
|
|
|
|
|
|
|
|
straight_random: (course) =>
|
|
|
|
|
|
|
|
coursespans(
|
|
|
|
|
|
|
|
course.spans.filter((s) => s.straight != null),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
track_id: (course, cond) => [{ start: 0, end: course.distance, type: must(opcompare(cond, course.race_track_id)) }],
|
|
|
|
|
|
|
|
up_slope_random: (course) =>
|
|
|
|
|
|
|
|
coursespans(
|
|
|
|
|
|
|
|
course.slopes.filter((s) => s.per > 0),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
up_slope_random_later_half: (course) => {
|
|
|
|
const p = { start: course.distance / 2, end: course.distance, type: SpanType.Random };
|
|
|
|
const p = { start: course.distance / 2, end: course.distance, type: SpanType.Random };
|
|
|
|
return coursespans(course.slopes.filter((s) => s.per > 0), SpanType.Random).map((s) => intersection(s, p));
|
|
|
|
return coursespans(
|
|
|
|
}
|
|
|
|
course.slopes.filter((s) => s.per > 0),
|
|
|
|
|
|
|
|
SpanType.Random,
|
|
|
|
|
|
|
|
).map((s) => intersection(s, p));
|
|
|
|
|
|
|
|
},
|
|
|
|
} as const;
|
|
|
|
} as const;
|
|
|
|
|
|
|
|
|
|
|
|
export function spans(course: racecourse.Course, cond: Condition): Span[] {
|
|
|
|
export function spans(course: racecourse.Course, cond: Condition): Span[] {
|
|
|
|