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:
-
#
— argument natychmiastowy; jest on stałą i należy go brać taki, jaki jest -
$
— argument bezpośredni; jest on adresem, pod którym należy szukać właściwego argumentu -
@
— argument pośredni; jest on adresem wskazującym komórkę, która w poluB
zawiera adres właściwego argumentu. -
<
— argument pośredni zmniejszany; jest on, podobnie jak argument pośredni, adresem adresu właściwego argumentu. Różnica tkwi w tym, że przed znalezieniem argumentu adres wyciągnięty ze wskazywanej komórki jest zmniejszany o jeden. Zawartość tej komórki również jest zmniejszana, ale dopiero po zakończeniu opracowywania argumentów rozkazy.
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:
-
A-adres : adres
, pod którym należy szukać wartości argumentuA
wykonywanego rozkazu. Jest on względny, liczony od pozycji tego rozkazu na rdzeniu. -
B-adres : adres
, pod którym należy szukać wartości argumentuB
bieżącego rozkazu. -
A-adres-A-wartość
,A-adres-B-wartość
: wartości wyciągnięte odpowiednio z pola argumentuA
i argumentuB
komórki znajdujacej się podA-adresem
. -
B-adres-A-wartość
,B-adres-B-wartość
: wartości znalezione w komórce podB-adresem
.
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:
-
A-adres-A-wartość = 5
-
A-adres-B-wartość = -1
Argument B
jest typu $
, czyli jest adresem
właściwego argumentu:
-
B-adres = -1
(czylistart-1
) -
B-adres-A-wartość = -2
-
B-adres-B-wartość = 3
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:
-
A-adres-A-wartość = 3
-
A-adres-B-wartość = 5
-
B-adres-A-wartość = 0
-
B-adres-B-wartość = 1
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
doB
. Jeśli argumentA
jest typu#
, wykonywana jest operacjaB-adres-B-wartość = A-adres-A-wartość
. Jeśli zaś argumentA
jest innego typu, przepisywana jest cała zawartośc komórki (wszystkie trzy pola) spodA-adresu
podB-adres
. -
ADD A B
-
Ten rozkaz dodaje
A
doB
. Jeśli argumentA
jest typu#
, wykonywana jest operacjaB-adres-B-wartość = B-adres-B-wartość + A-adres-A-wartość
. Jeśli argumentA
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 podA-adres
. -
JMZ A B
-
To również rozkaz skoku pod
A-adres
. Jest to jednak skok warunkowy, wykonywany tylko wtedy, gdyB-adres-B-wartość = 0
. -
JMN A B
-
Rozkaz podobny w działaniu do
JMZ
, z tym, że skok podA-adres
jest wykonywany, gdyB-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ładnieB-adres-B-wartość
), po czym, jeśli jest on różny od zera, wykonuje skok podA-adres
. -
CMP A B
-
Ten rozkaz porównuje argumenty
A
iB
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ść
iB-adres-B-wartość
, zaś jeśli nie, porównywane są całe komórki spod adresówA-adres
iB-adres
. :SLT A B
To również rozkaz porównania. Jeśli argumentA
jest typu#
, porównuje sięA-adres-A-wartość
iB-adres-B-wartość
, a jeśli jest innego typu, porównuje sięA-adres-B-wartość
iB-adres-B-wartość
. Porównanie polega na sprawdzeniu, czy pierwszy argument jest mniejszy od drugiego. Jeśli tak, program przeskakuje instrukcję leżącą zaraz zaSLT
. -
SPL A (B)
-
Rozkaz ten powoduje uruchomienie niezależnego procesu od adresu
A-adres
. Sprzymierzone procesy działają wolnie, gdyż wykonują ruchy na zmianę:-
Program-ojciec wykonuje
SPL
- Program-przeciwnik
- Program-syn
- Program-przeciwnik
- 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. -
Program-ojciec wykonuje
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:
-
DAT ( $ <)A ( $ <)B
-
MOV (#$@<)A ( $@<)B
-
ADD (#$@<)A ( $@<)B
-
SUB (#$@<)A ( $@<)B
-
JMP ( $@<)A (#$@<)B
-
JMN ( $@<)A (#$@<)B
-
JMZ ( $@<)A (#$@<)B
-
DJN ( $@<)A (#$@<)B
-
CMP (#$@<)A ( $@<)B
-
SPL ( $@<)A (#$@<)B
-
SLT (#$@<)A ( $@<)B
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.