Jest to adaptacja Core War , programu KOTH z 20 wieku. Mówiąc ściślej, używa niesamowicie uproszczonego zestawu instrukcji, opartego głównie na oryginalnej propozycji .
tło
W Core War istnieją dwa programy walczące o kontrolę nad komputerem. Celem każdego programu jest zwycięstwo poprzez zlokalizowanie i zakończenie programu przeciwnego.
Bitwa odbywa się w głównej pamięci komputera. Ta pamięć nazywa się Core i zawiera 8192 adresów. Na początku bitwy kod każdego zawodnika (zwanego wojownikiem) jest umieszczany w losowej części pamięci. Wykonanie programu przebiega na przemian między wojownikami, wykonując jedną instrukcję dla każdego z nich. Każda instrukcja może modyfikować część rdzenia, co prowadzi do możliwości samodzielnej modyfikacji programów.
Celem jest zakończenie programu przeciwnika. Program kończy się, gdy próbuje wykonać niepoprawną instrukcję, którą jest dowolna DAT
instrukcja.
Zestaw instrukcji
Każdy program składa się z szeregu instrukcji niskiego poziomu, z których każde zajmuje dwa pola, zwane polami A i B.
Ten zestaw instrukcji czerpie głównie z oryginalnej specyfikacji. Główne zmiany to 1) wyjaśnienie dotyczące dodawania / odejmowania poleceń oraz 2) zmiana #
trybu adresowania, aby można go było używać w dowolnym miejscu. Większość pełnych wersji Core Wars ma ponad 20 kodów, 8 trybów adresowania i zestaw „modyfikatorów instrukcji”.
Kody
Każda instrukcja musi mieć jeden z siedmiu różnych kodów.
DAT A B
- (dane) - To po prostu zawiera liczbyA
iB
. Co ważne, proces umiera, gdy próbuje wykonać instrukcję DAT.MOV A B
- (przenieś) - Przenosi zawartość lokalizacjiA
pamięci do lokalizacji pamięciB
. Oto demonstracja „przed i po”:MOV 2 1 ADD @4 #5 JMP #1 -1
MOV 2 1 JMP #1 -1 JMP #1 -1
ADD A B
- (dodaj) - Dodaje zawartość lokalizacjiA
pamięci do lokalizacji pamięciB
. Dwa pierwsze pola obu zostaną dodane, a drugie pola zostaną dodane.ADD 2 1 MOV @4 #5 JMP #1 -1
ADD 2 1 MOV @5 #4 JMP #1 -1
SUB A B
- (odejmij) - Odejmuje zawartość miejsca w pamięciA
od (i zapisuje wynik w) miejscu w pamięciB
.SUB 2 1 MOV @4 #5 JMP #1 -1
SUB 2 1 MOV @3 #6 JMP #1 -1
JMP A B
- (skok) - Przejdź do lokalizacjiA
, która zostanie wykonana w następnym cyklu.B
musi być liczbą, ale nic nie robi (możesz jej użyć do przechowywania informacji).JMP 2 1337 ADD 1 2 ADD 2 3
Skok oznacza, że
ADD 2 3
zostanie wykonany następny cykl.JMZ A B
- (skok, jeśli zero) - Jeśli oba pola liniiB
mają wartość 0, program przeskakuje do lokalizacjiA
.JMZ 2 1 SUB 0 @0 DAT 23 45
Ponieważ dwa pola instrukcji 1 mają wartość 0, polecenie DAT zostanie wykonane w następnej turze, co doprowadzi do nieuchronnej śmierci.
CMP A B
- (porównaj i pomiń, jeśli nie są równe) - Jeśli pola w instrukcjachA
iB
nie są równe, pomiń następną instrukcję.CMP #1 2 ADD 2 #3 SUB @2 3
Ponieważ dwa pola instrukcji 1 i 2 mają jednakową wartość, polecenie ADD nie jest pomijane i jest wykonywane w następnej turze.
Kiedy dwie instrukcje są dodawane / odejmowane, dwa pola (A i B) są dodawane / odejmowane parami. Tryb adresowania i kod operacji nie są zmieniane.
Tryby adresowania
Istnieją trzy rodzaje trybów adresowania. Każde z dwóch pól instrukcji ma jeden z tych trzech trybów adresowania.
Natychmiastowe
#X
-X
to linia, która ma być używana bezpośrednio w obliczeniach. Na przykład#0
jest pierwszą linią programu. Linie ujemne odnoszą się do linii w rdzeniu przed rozpoczęciem programu.... //just a space-filler ... ADD #3 #4 DAT 0 1 DAT 2 4
Spowoduje to dodanie pierwszej z dwóch linii DAT do drugiej, ponieważ znajdują się one odpowiednio w wierszach 3 i 4. Nie chciałbyś jednak używać tego kodu, ponieważ DAT zabije twojego bota w następnym cyklu.
Względne
X
- liczbaX
reprezentuje lokalizację docelowego adresu pamięci w stosunku do bieżącego adresu. Liczba w tej lokalizacji jest używana do obliczeń. Jeśli linia#35
jest wykonywana i zawiera-5
, wówczas#30
używana jest linia .... //just a space-filler ... ADD 2 1 DAT 0 1 DAT 2 4
Spowoduje to dodanie drugiej linii DAT do pierwszej.
Pośrednie
@X
- liczbaX
reprezentuje adres względny. Zawartość w tym miejscu jest tymczasowo dodawana do liczby X w celu utworzenia nowego adresu względnego, z którego pobierana jest liczba. Jeśli linia#35
jest wykonywana, a jej drugim polem jest@4
, a drugie pole linii#39
zawiera liczbę-7
, wówczas#32
używana jest linia .... //just a space-filler ... ADD @1 @1 DAT 0 1 DAT 2 4
To doda pierwszy DAT do drugiego, ale w bardziej skomplikowany sposób. Pierwsze pole to @ 1, które pobiera dane z tego adresu względnego, który jest pierwszym polem pierwszego DAT, a 0. To jest interpretowane jako drugi adres względny z tej lokalizacji, więc 1 + 0 = 1 daje sumę przesunięcie w stosunku do oryginalnej instrukcji. Dla drugiego pola @ 1 pobiera wartość z tego względnego adresu (1 w drugim polu pierwszego DAT) i dodaje ją do siebie w ten sam sposób. Całkowite przesunięcie wynosi wtedy 1 + 1 = 2. Tak więc ta instrukcja jest wykonywana podobnie do
ADD 1 2
.
Każdy program może zawierać do 64 instrukcji.
Po rozpoczęciu rundy oba programy są losowo umieszczane w banku pamięci z 8192 lokalizacjami. Wskaźnik instrukcji dla każdego programu rozpoczyna się na początku programu i jest zwiększany po każdym cyklu wykonywania. Program umiera, gdy wskaźnik instrukcji spróbuje wykonać DAT
instrukcję.
Parametry rdzenia
Rozmiar rdzenia to 8192, a limit czasu to 8192 * 8 = 65536 tyknięć. Rdzeń ma charakter cykliczny, więc zapis na adres 8195 jest taki sam jak zapis na adres 3. Wszystkie nieużywane adresy są inicjowane DAT #0 #0
.
Każdy zawodnik nie może być dłuższy niż 64 linie. Liczby całkowite będą przechowywane jako liczby całkowite ze znakiem 32-bitowym.
Rozbiór gramatyczny zdania
Aby ułatwić programowanie konkurentom, dodam funkcję parowania linii do parsera. Wszelkie słowa występujące w linii przed kodem operacji będą interpretowane jako etykiety linii. Na przykład tree mov 4 6
ma etykietę linii tree
. Jeśli gdziekolwiek w programie znajduje się pole zawierające tree
#tree
lub @tree
, liczba zostanie zastąpiona. Również wielkie litery są ignorowane.
Oto przykład zastępowania etykiet linii:
labelA add labelB @labelC
labelB add #labelC labelC
labelC sub labelA @labelB
Tutaj etykiety A, B i C znajdują się w wierszach 0, 1 i 2. Wystąpienia #label
zostaną zastąpione numerem wiersza etykiety. Wystąpienia label
lub @label
są zastąpione względną lokalizacją etykiety. Tryby adresowania są zachowane.
ADD 1 @2
ADD #2 1
SUB -2 @-1
Punktacja
Dla każdej pary uczestników odbywa się każda możliwa bitwa. Ponieważ wynik bitwy zależy od względnych przesunięć obu programów, wypróbowywane jest każde możliwe przesunięcie (około 8000 z nich). Co więcej, każdy program ma szansę na pierwsze przesunięcie w każdym przesunięciu. Program, który wygrywa większość tych przesunięć, jest zwycięzcą pary.
Za każdą parę wygraną przez wojownika przyznaje się 2 punkty. Za każdą remis wojownik otrzymuje 1 punkt.
Możesz przesłać więcej niż jednego wojownika. Obowiązują typowe zasady wielokrotnego przesyłania, takie jak brak łączenia tagów, brak współpracy, tworzenia króla itp. W Core War i tak nie ma miejsca na to, więc nie powinno to być wielkim problemem.
Kontroler
Kod kontrolera wraz z dwoma łatwymi przykładowymi botami znajduje się tutaj . Ponieważ ta konkurencja (uruchamiana przy użyciu oficjalnych ustawień) jest całkowicie deterministyczna, tworzona tabela wyników będzie dokładnie taka sama jak oficjalna tabela wyników.
Przykład Bot
Oto przykładowy bot pokazujący niektóre funkcje języka.
main mov bomb #-1
add @main main
jmp #main 0
bomb dat 0 -1
Ten bot działa, powoli usuwając całą pozostałą pamięć w rdzeniu, zastępując ją „bombą”. Ponieważ bomba jest DAT
instrukcją, każdy program, który dotrze do bomby, zostanie zniszczony.
Istnieją dwie etykiety linii, „główna” i „bomba”, które służą do zastąpienia liczb. Po wstępnym przetwarzaniu program wygląda następująco:
MOV 3 #-1
ADD @-1 -1
JMP #0 0
DAT 0 -1
Pierwszy wiersz kopiuje bombę do linii bezpośrednio nad programem. Następny wiersz dodaje wartość bomby ( 0 -1
) do polecenia ruchu, a także demonstruje użycie @
trybu adresowania. To dodanie powoduje, że polecenie move wskazuje nowy cel. Następne polecenie bezwarunkowo przeskakuje z powrotem na początek programu.
Aktualny ranking
24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Dwarf
14 - ScanBomber
10 - Paranoid
10 - FirstTimer
10 - Janitor
10 - Evolved
6 - EasterBunny
6 - CopyPasta
4 - Imp
2 - Slug
Wyniki parami:
Dwarf > Imp
CopyPasta > Imp
Evolved > Imp
FirstTimer > Imp
Imp > Janitor
Imp > ScanBomber
Slug > Imp
DwarvenEngineer > Imp
HanShotFirst > Imp
Turbo > Imp
EasterBunny > Imp
Paranoid > Imp
Dwarf > CopyPasta
Dwarf > Evolved
Dwarf > FirstTimer
Dwarf > Janitor
Dwarf > ScanBomber
Dwarf > Slug
DwarvenEngineer > Dwarf
HanShotFirst > Dwarf
Turbo > Dwarf
Dwarf > EasterBunny
Dwarf > Paranoid
Evolved > CopyPasta
FirstTimer > CopyPasta
Janitor > CopyPasta
ScanBomber > CopyPasta
CopyPasta > Slug
DwarvenEngineer > CopyPasta
HanShotFirst > CopyPasta
Turbo > CopyPasta
CopyPasta > EasterBunny
Paranoid > CopyPasta
Evolved > FirstTimer
Evolved > Janitor
ScanBomber > Evolved
Evolved > Slug
DwarvenEngineer > Evolved
HanShotFirst > Evolved
Turbo > Evolved
EasterBunny > Evolved
Paranoid > Evolved
Janitor > FirstTimer
ScanBomber > FirstTimer
FirstTimer > Slug
DwarvenEngineer > FirstTimer
HanShotFirst > FirstTimer
Turbo > FirstTimer
FirstTimer > EasterBunny
FirstTimer > Paranoid
ScanBomber > Janitor
Janitor > Slug
DwarvenEngineer > Janitor
HanShotFirst > Janitor
Turbo > Janitor
Janitor > EasterBunny
Janitor > Paranoid
ScanBomber > Slug
DwarvenEngineer > ScanBomber
HanShotFirst > ScanBomber
Turbo > ScanBomber
ScanBomber > EasterBunny
ScanBomber > Paranoid
DwarvenEngineer > Slug
HanShotFirst > Slug
Turbo > Slug
EasterBunny > Slug
Paranoid > Slug
DwarvenEngineer > HanShotFirst
Turbo > DwarvenEngineer
DwarvenEngineer > EasterBunny
DwarvenEngineer > Paranoid
Turbo > HanShotFirst
HanShotFirst > EasterBunny
HanShotFirst > Paranoid
Turbo > EasterBunny
Turbo > Paranoid
Paranoid > EasterBunny
Najnowsza aktualizacja (nowe wersje Turbo i Paranoid) działała na starym laptopie około 5 minut. Chciałbym podziękować Ilmari Karonenowi za jego ulepszenia w kontrolerze . Jeśli masz lokalną kopię kontrolera, powinieneś zaktualizować swoje pliki.