}
public function updatePlacement(): void {
- if (!$this->tournament->hasFixedRunners()) {
+ if ($this->tournament->hasTimeBasedScoring()) {
+ $results = $this->results->sort([Result::class, 'compareResult']);
+ $reversed = $results->reverse();
+
+ $reference_times = [];
+ foreach ($reversed as $result) {
+ $time = $result->getEffectiveTime();
+ if ($time > 0) {
+ $reference_times[] = $time;
+ }
+ if (count($reference_times) >= 5) {
+ break;
+ }
+ }
+ $reference_time = empty($reference_times) ? 0 : array_sum($reference_times) / count($reference_times);
+
+ $running = 0;
+ $bonus = 1;
+ $lastResult = null;
+ foreach ($reversed as $result) {
+ $betterThanLast = is_null($lastResult) || $result->getEffectiveTime() < $lastResult;
+ if (!$result->disqualified && !$result->forfeit && $betterThanLast) {
+ $running += $bonus;
+ $lastResult = $result->getEffectiveTime();
+ $bonus = 1;
+ } else {
+ ++$bonus;
+ }
+ if ($result->disqualified) {
+ $result->updatePlacement(0, count($results) + 1);
+ } elseif ($result->forfeit) {
+ $result->updatePlacement(0, count($results));
+ } else {
+ $score = $reference_time > 0 ? ($reference_time / $result->getEffectiveTime() * 100) : 0;
+ $result->updatePlacement($score, count($results) - $running + 1);
+ }
+ }
+ } elseif (!$this->tournament->hasFixedRunners()) {
$results = $this->results->sort([Result::class, 'compareResult']);
$reversed = $results->reverse();
}
+ public function hasAssignedGroups(): bool {
+ return in_array($this->type, ['open-grouped-async']);
+ }
+
public function hasFixedRunners(): bool {
return in_array($this->type, ['signup-async']);
}
+ public function hasTimeBasedScoring(): bool {
+ return in_array($this->type, ['open-grouped-async']);
+ }
+
public function getRunners() {
$runners = [];
foreach ($this->participants as $participant) {
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+ /**
+ * Run the migrations.
+ */
+ public function up(): void
+ {
+ Schema::table('participants', function (Blueprint $table) {
+ $table->double('score')->nullable()->default(null)->change();
+ });
+ Schema::table('results', function (Blueprint $table) {
+ $table->double('score')->nullable()->default(null)->change();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('participants', function (Blueprint $table) {
+ $table->integer('score')->nullable()->default(null)->change();
+ });
+ Schema::table('results', function (Blueprint $table) {
+ $table->integer('score')->nullable()->default(null)->change();
+ });
+ }
+};
import { formatTime, getTime } from '../../helpers/Result';
import { formatNumberAlways } from '../../helpers/Round';
import { mayModifyResult, maySeeResult, mayVerifyResult } from '../../helpers/permissions';
+import { getScoreFormatOptions } from '../../helpers/Tournament';
import { findResult } from '../../helpers/User';
import { useUser } from '../../hooks/user';
+import i18n from '../../i18n';
-const getPlacement = (result, t) =>
- `${result.placement}. (${t('results.points', { count: result.score })})`;
+const getPlacement = (tournament, result, t) =>
+ `${result.placement}. (${t('results.points', {
+ count: result.score,
+ score: i18n.number(result.score, getScoreFormatOptions(tournament)),
+ })})`;
const DetailDialog = ({
actions,
<Form.Label>{t('results.placement')}</Form.Label>
<div>
{(maySee || mayVerify) && result && result.placement
- ? getPlacement(result, t)
+ ? getPlacement(tournament, result, t)
: t('results.pending')}
</div>
</Form.Group>
import ResultProtocol from '../protocol/ResultProtocol';
import Box from '../users/Box';
import { maySeeResult, mayVerifyResult } from '../../helpers/permissions';
+import { getScoreFormatOptions } from '../../helpers/Tournament';
import { findResult } from '../../helpers/User';
import { useUser } from '../../hooks/user';
+import i18n from '../../i18n';
const TableRow = ({
actions,
{showPlacement ?
<td className="result-placement">
{maySee && result && result.placement
- ? `${result.placement}. (${t('results.points', { count: result.score })})`
+ ? `${result.placement}. (${t('results.points', {
+ count: result.score,
+ score: i18n.number(result.score, getScoreFormatOptions(tournament)),
+ })})`
: t('results.pending')}
</td>
: null}
export const hasSignup = tournament => !!(tournament && tournament.type === 'signup-async');
+export const hasTimeBasedScoring = tournament => (tournament?.type === 'open-grouped-async');
+
+export const getScoreFormatOptions = tournament => (hasTimeBasedScoring(tournament)
+ ? { decimals: 2 }
+ : { decimals: 0 }
+);
+
export const getScoreTable = tournament => {
if (!tournament || !tournament.rounds || !tournament.rounds.length) return [];
const runners = getRunners(tournament);
modifySuccess: 'Ergebnis angepasst',
pending: 'Ausstehend',
placement: 'Platzierung',
- points_one: '{{ count }} Punkt',
- points_other: '{{ count }} Punkte',
+ points_one: '{{ score }} Punkt',
+ points_other: '{{ score }} Punkte',
report: 'Ergebnis eintragen',
reportError: 'Fehler beim Eintragen :(',
reportPreview: 'Wird als {{ time }} festgehalten',
modifySuccess: 'Result modified',
pending: 'Pending',
placement: 'Placement',
- points_one: '{{ count }} point',
- points_other: '{{ count }} points',
+ points_one: '{{ score }} point',
+ points_other: '{{ score }} points',
report: 'Report result',
reportError: 'Error saving :(',
reportPreview: 'Will be recorded as {{ time }}',