4.5 KiB
Specifikace
Následující dokument obsahuje specifikaci závodu, pokud ji chcete implementovat ve vašem oblíbeném jazyce. Aby závod fungoval stejně v každé implementaci, všechny výpočty jsou prováděny pomocí 64-bitových celých čísel.
Objekty
V rámci závodu definujeme tyto objekty:
class Racer: # (loď)
x: int
y: int
vx: int
vy: int
radius: int
class Asteroid: # (asteroid)
x: int
y: int
radius: int
class Goal: # (gól)
x: int
y: int
radius: int
class Instruction: # (instrukce)
vx: InstType
vy: InstType
class BoundingBox: # (okraj)
min_x: int
min_y: int
max_x: int
max_y: int
Simulace
Každý krok simulace lze rozdělit do následujících tří fází:
- pohyb lodi podle zadané instrukce
- řešení kolizí
- kontrola dosažení cíle (označení splněných cílů)
Tyto kroky jsou podrobně vysvětleny v následujících sekcích.
Poznámka 1: Pro účely optimalizace je k ukládání asteroidů a cílů vhodné použít nějakou datovou strukturu (například 2D slovník nebo K-d strom), aby stačilo kontrolovat kolize pouze okolo aktuální pozice lodi. Nezapomeňte zohlednit poloměr lodi!
Poznámka 2: Celá část druhé odmocniny, kterou simulace používá, odpovídá Python funkci math.isqrt
, která vrací „největší celé číslo takové, že a² ≤ n.“ Tato implementace je přesná a nevyužívá plovoucí desetinnou čárku.
1) Pohyb lodi
S danou instrukcí (vx, vy)
se loď pohybuje podle následujících pravidel:
- zpomalí se o 10 %:
racer.velocity = (racer.velocity * 9) // 10
- k její rychlosti se přidá instrukce:
racer.velocity += (vx, vy)
- závodník se posune podle své rychlosti:
racer.position += racer.velocity
Je důležité poznamenat, že operátor dělení zahodí desetinnou část čísla.
To znamená, že Pythoní celočíselné dělení nefunguje korektně pro záporná čísla (-5 // 2 = -3
), což je v našem případě špatně.
Jedním ze způsobů, jak to napravit, je použít abs(x) // v * signum(x)
, což zajistí správný výsledek.
Instrukce je platná pouze tehdy, pokud její délka (euklidovská vzdálenost) nepřesáhne 127
.
Kontrola délky instrukce se provádí porovnáním čtverců délky instrukce a maximálního zrychlení (distance_squared(vx, vy) > 127 ** 2
).
2) Řešení kolizí
Kolize se řeší v 1 až 5 podkrocích. V rámci každého podkroku se nejprve kontrolují kolize se všemi asteroidy a poté s ohraničujícím boxem. Pokud dojde ke kolizi během podkroku, kolize se vyřeší (viz. níže) a okamžitě se zahájí další podkrok, dokud není dosažen limit (5).
Pokud během celého kroku dojde ke kolizi, rychlost závodníka se sníží na polovinu. Toto zpomalení může nastat nejvýše jednou za krok, bez ohledu na počet kolizí v individuálních podkrocích.
Asteroidy
Iterujeme přes všechny asteroidy asteroid
v pořadí, v jakém byly přidány do simulace, a pro každý provádíme následující kroky:
- Pokud
euclidean_distance(asteroid, racer) > (asteroid.radius + racer.radius)
, kolize nenastala (pokračujeme k dalšímu asteroidu). - V případě kolize provedeme následující:
- Spočítáme vzdálenost:
distance = euclidean_distance(asteroid, racer)
- Vektor k vytlačení závodníka:
vn = racer.position - asteroid.position
. - Vzdálenost posunutí:
push_by = distance - (asteroid.radius + racer.radius)
. - Posuneme závodníka:
racer.position -= (push_by * vn) / distance
.
- Spočítáme vzdálenost:
Ohraničující box
Pro každou stranu boxu zkontrolujeme kolizi (například pokud racer.x - racer.radius < box.min_x
pro levou stranu).
Pokud ke kolizi dojde, posuneme závodníka zpět k hranici boxu.
3) Kontrola dosažení cíle
Iterujeme přes všechny cíle goal
a označíme je jako dosažené, pokud euclidean_distance(racer, goal) <= (racer.radius + goal.radius)
, tedy pokud dochází k jejich průniku.
Poznámky
Všimněte si, že simulace používá několik neintuitivních zjednodušení:
- při úvodním posunu lodi o její rychlost nekontrolujeme kolize
- kolize kontrolujeme až jako průsečík finální pozice lodi s asteroidy či cíle
- při kolizi s asteroidem se loď neodrazí, tedy nezmění se směr její rychlosti
- při řešení kolizí loď pouze posouváme ven z asteroidů
- při kolizi je jedno v jaké fázi řešení podkroků vydělíme rychlost lodi dvěma