Magazyn Enter, styczeń 1994

Adam Ryba

Przygotowania do zawodów

Coraz więcej napływa do redakcji listów z pytaniami, na czym polega gra w Wojny Rdzeniowe, jakie jest niezbędne oprogramowanie i gdzie je można kupić. Dlatego też zajmiemy się dziś przypomnieniem zasad gry. To się przyda nie tylko młodym adeptom.

Wojny Rdzeniowe są grą dla miłośników programowania. Polega ona na pisaniu programów-wojowników, które walczą ze sobą na wydzielonej w pamięci komputera arenie, zwanej rdzeniem. Programy te tworzy się w asemblerowym języku Redcode, wymyślonym specjalnie dla Wojen Rdzeniowych przez ich twórców: A.K.Dewdneya i D.Jonesa. Nad przebiegiem zmagań czuwa program-sędzia, który umieszcza dwóch wojowników na arenie i dba, by wykonywały one ruchy na przemian. Walczące programy mogą przemieszczać się po rdzeniu, dzielić się na niezależne procesy (multitasking!) lub starać się uszkodzić kod przeciwnika. Wojownik, który z powodu uszkodzeń nie jest w stanie wykonać poprawnego rozkazu — przegrywa. Jeśli po wyczerpaniu się ustalonego limitu ruchów walka nie zostanie rozstrzygnięta, ogłaszany jest remis. Warto zauważyć, że po rozpoczęciu walki wojownicy są zdani wyłącznie na siebie. Nam pozostaje obserwacja toczącej się bitwy i oczekiwanie na wynik.

Idea walczących programów nie jest nowa. Znamy ją chocby z filmu Tron Stevena Lisbergera oraz ze zmagań programów antywirusowych z wirusami. Jednak Wojny Rdzeniowe realizują tę koncepcję w sposób całkowicie niegroźny dla innych zastosowań naszych komputerów.

Polem walki wojowników jest rdzeń, czyli specjalnie wydzielony obszar pamięci. Rdzeń jest zamkniętym pierścieniem komórek, który nie ma ani początku, ani końca. Wojownik nigdy więc nie wie, gdzie dokładnie znajduje się na rdzeniu i jedynym punktem odniesienia jest dla niego jego własna pozycja. W konsekwencji wszystkie adresy muszą być względne. Komórki mają następującą strukturę: Pole operacji…Pole argumetu A…Pole argumentu B.

W każdej komórce mieści się dokłądnie jedna instrukcja Redcode. Składa się ona z rozkazu umieszczanego w polu operacji oraz z dwóch argumentów umieszczanych odpowiednio w polu argumentu A i polu argumentu B. Przed każdą walką sędzia czyści arenę, wpisując do każdej komórki instrukcję DAT $0 $0.

Zanim zapoznamy się z instrukcjami Redcode, musimy poznać typy argumentów i sposoby ich adresowania. Jest to rzecz bardzo istotna, gdyż niektóre rozkazy działają różnie z różnymi typami argumentów. W Redcode istnieją cztery typy argumentów:

Typ argumentu określa się umieszczając przed nim jeden z symboli: #, $, @, <. Jeśli nie umieścimy żadnego symbolu, sędzia uzna, że ma do czynienia z argumentem bezpośrednim.

Sędzia zanim przystąpi do wykonywania jakiegokolwiek rozkazu musi wyznaczyć faktyczne adresy argumentów i ich rzeczywiste wartości. Aby predstawić ten proces, wygodnie jest posłużyć się następującymi terminami:

Zobaczymy teraz jak się wyznacza argumenty poszczególnych typów.

         DAT $-2      $3
  start  ADD #5       $-1

Wyznaczamy argumenty rozkazu znajdującego się w linii oznaczonej etykietą start. Argument A jest stałą, którą bierzemy taką, jaka ona jest. Nie musimy zatem wyznaczać adresu argumentu, gdyż znajduje się on w tej samej linii, co wykonywany rozkaz: A-adres = 0 (czyli start).

Wyciągamy teraz spod tego adresu wartość argumentu:

Argument B jest typu $, czyli jest adresem właściwego argumentu:

Bardziej skomplikowane jest opracowywanie argumentów pośrednich i pośrednich zmniejszanych:

         DAT $0       #1
         DAT $3       $5
  start  MOV @-2      <1
         DAT $0       $-2

Znów zabieramy się za argumenty rozkazu znajdującego się w linii startowej. Argument A jest pośredni, czyli jest adresem adresu faktycznego argumentu: A-adres = -1 (czyli start - 2 + 1).

Podobnie wyznaczamy adres argumentu B, której jest typu <, ale ostateczny wynik zmniejszemy o jeden: B-adres = -2 (czyli start + 1 - 2 - 1).

Aby wyznaczyć wartości argumentów, zaglądamy do wskazanych komórek:

Sędzie zawsze wyznacza oba adresy i komplet wartości, nawet wtedy, gdy dany rozkaz nie potrzebuje ich wszystkich. Teraz, gdy znamy już typu argumentów, możemy zabrać się za rozkazy Redcode. Jest to dośc ubogi język, składający się z zaledwie 11 instrukcji. Prawie wszystkie wymagają podania dwóch argumentów — wyjątkami są DAT, JMP i SPL, w których można opuścić argument wzięty w nawias. W miejsce pominiętego argumentu sędzia domyślnie wstawia wartość $0.

DAT (A) B
Próba wykonania tego rozkazu powoduje zniszczenie procesu, który usiłuje go wykonać. Dzięki temu można osiągnąć zwycięstwo w Wojnach Rdzeniowych; wystarczy zmusić wszystkie procesy przeciwnika do wykonania tej instrukcji. DAT spełnia oprócz tego funkcję przechowalni danych.
MOV A B
Ten rozkaz przepisuje A do B. Jeśli argument A jest typu #, wykonywana jest operacja B-adres-B-wartość = A-adres-A-wartość. Jeśli zaś argument A jest innego typu, przepisywana jest cała zawartośc komórki (wszystkie trzy pola) spod A-adresu pod B-adres.
ADD A B
Ten rozkaz dodaje A do B. Jeśli argument A jest typu #, wykonywana jest operacja B-adres-B-wartość = B-adres-B-wartość + A-adres-A-wartość. Jeśli argument A jest innego typu, dodawane są do siebie obie wartości argumentów według poniższego schematu: B-adres-A-wartość = B-adres-A-wartość + A-adres-A-wartość B-adres-B-wartość = B-adres-B-wartość + A-adres-B-wartość.
SUB A B
Działanie tej instrukcji jest identyczne jak ADD, jedynie zamiast dodawania jest odejmowanie.
JMP A (B)
To jest rozkaz bezwarunkowego skoku do komórki wskazywanej przez argument A, czyli pod A-adres.
JMZ A B
To również rozkaz skoku pod A-adres. Jest to jednak skok warunkowy, wykonywany tylko wtedy, gdy B-adres-B-wartość = 0.
JMN A B
Rozkaz podobny w działaniu do JMZ, z tym, że skok pod A-adres jest wykonywany, gdy B-adres-B-wartość <> 0.
DJN A B
To także rozkaz skoku warunkowego, ale już bardziej złożony. Najpierw zmniejsza on argument B o jeden (a dokładnie B-adres-B-wartość), po czym, jeśli jest on różny od zera, wykonuje skok pod A-adres.
CMP A B
Ten rozkaz porównuje argumenty A i B i jeśli są one identyczne, przeskakuje nastepną instrukcję. Jego działanie znów zależy od typu argumentu A. Jeśli jest on typu #, porównywane są A-adres-A-wartość i B-adres-B-wartość, zaś jeśli nie, porównywane są całe komórki spod adresów A-adres i B-adres. :SLT A B To również rozkaz porównania. Jeśli argument A jest typu #, porównuje się A-adres-A-wartość i B-adres-B-wartość, a jeśli jest innego typu, porównuje się A-adres-B-wartość i B-adres-B-wartość. Porównanie polega na sprawdzeniu, czy pierwszy argument jest mniejszy od drugiego. Jeśli tak, program przeskakuje instrukcję leżącą zaraz za SLT.
SPL A (B)
Rozkaz ten powoduje uruchomienie niezależnego procesu od adresu A-adres. Sprzymierzone procesy działają wolnie, gdyż wykonują ruchy na zmianę:
  1. Program-ojciec wykonuje SPL
  2. Program-przeciwnik
  3. Program-syn
  4. Program-przeciwnik
  5. Program-ojciec

Wojownik może się dalej dzielić, aż do uzyskania 64 procesów. Wszystkie sprzymierzone procesy są równouprawne i wykonują swoje rozkazy tak samo często, jak pozostałe. Jeśli któryś z procesów zginie, zostanie automatycznie usunięty z podziału czasu i pozostałe będą wykonywane szybciej. Wykonywanie rozkazu SPL w sytuacji, gdy na arenie znajdują się już 64 procesu nie wywołuje żadnej akcji.

Pozostała jeszcze do omówienia kwestia dopuszczalności różnych typów argumentów w różnych rozkazach. W naszym Klubie obowiązuje standard WA'91 (Wolna Amerykanka, 1991) opracowany przez pana Andrzeja Stasiewicza, istnieje natomiast jeszcze światowy standard CWS'88 (Core Wars Standard of 1988). Według WA'91 wszystkie argumenty we wszystkich rozkazch mogą być dowolnego typu. CWS'88 dopuszca różne typy argumentów w różnych rozkazach według poniższego schematu:

Ograniczenia wprowadzane przez CWS'88 są podyktowane tym, że niektóre rozkazy np. skoki wyraźnie domagają się argumentów adresowych. Stąd też zabrania się stosowania w takich przypadkach argumentów natychmiastowych. WA'91 natomiast konsekwentnie intepretuje argumenty typu # i wyznacza wówczas adres równy zeru. Instrukcja JMP #3 według CWS'88 jest niepoprawna, gdyż argument natychmiastowy nie ma określonego adresu, a według WA'91 jest ona poprawna i działa tak, jak JMP $0. Jak widać, programy napisane w CWS'88 będą działać pod WA'91, ale odwrotnej relacji nie ma.

Do gry w Wojny Rdzeniowe, oprócz komputera, niezbędne jest specjalne oprogramowanie. W chwili obecnej mamy dwa środowiska umożlwiające trenowanie wojowników oraz rozgrywanie walk między nimi. Są to Arbiter i CORDAT CoreWars, przeznaczone na komputery PC. Opis porównawczy tych programów został zamieszczony w Enter-ze 12'93. Arbiter jest sprzedawny przez Shareware House Wydawnictwa LUPUS — wystarczy zwrócić się listownie (lub osobiście) załączając wypełniony kupon oraz kopię odcinka przekazu wpłaty kwoty 100 000 zł na konto Wydawnictwa. Dystrybutorem CORDAT CoreWars jest natomiast firma CORDAT, Kraków, ul. Wadowicka 3, 30-415 Kraków. Cena programu 345 000 zł.

Dziś możemy już precyzyjnie (i ostatecznie) określić termin zawodów. Otóż na nadsyłanych wojowników będziemy czekać do 1 kwietnia 1994 r. Mistrzostwa rozegrane będą pod kontrolą Arbitra na arenie o wielkości 5000 komórek, przy losowanej odległości początkowej i limicie ruchów ustalonym na 30 000. Każdy zawodnik będzie walczył z każdym z pozostałych. Walki będą powtarzane 50-krotnie, przy czym za każde zwycięstwo przyznamy 10 punktów, za remis 5 i za porażkę 0 pkt.

Programy o identycznej pliczbie punktów rozegrają między sobą dogrywkę o kolejność na liście rankingowej, polegającą na serii powtarzanych 100-krotnie walk systemem „każdy z każdym”.

Każdy uczestnik może zgłosić do turnieju tylko jednego wojownika. Powinien on być napisany zgodnie ze standardem WA'91 oraz zawierać co najwyżej 100 instrukcji. Wojowników prosimy przysyłać na dyskietkach. Niech każda nadesłana dyskietka będzie dokładnie opisana (dane autora, nazwa wojownika), a i sam wojownik niech zawiera parametry swego autora w linii komentarza. Proszę wyraźnie zaznaczyć, ze jest to dyskietka bojowa.