.. _strategia1: Strategia podstawowa ##################### Przykład robota ***************** .. raw:: html
Kod nr
.. highlight:: python .. literalinclude:: robot_p.py :linenos: Z powyższego kodu wynikają trzy zasady: * broń się, jeżeli jesteś w środku planszy; * atakuj przeciwnika, jeżeli jest obok; * idź do środka. To pozwala nam rozpocząć grę, ale wiele możemy ulepszyć. Większość usprawnień (ang. *feature*), które zostaną omówione, to rozszerzenia wersji podstawowej. Konstruując robota, można je stosować wybiórczo. Kolejne reguły *********************** Rozbudujemy przykład podstawowy. Oto lista reguł, które warto rozważyć: * **Reguła 1: Opuść punkt wejścia.** Pozostawanie w punkcie wejścia nie jest dobre. Sprawdźmy, czy jesteśmy w punkcie wejścia i czy powinniśmy z niego wyjść. Nawet wtedy, gdy jest ktoś do zaatakowania, ponieważ nie chcemy zostać zamknięci w pułapce wejścia. * **Reguła 2: Uciekaj, jeśli masz zginąć.** Przykładowy robot atakuje aż do śmierci. Ponieważ jednak wygrana zależy od liczby pozostałych robotów, a nie ich zdrowia, bardziej opłaca się zachować robota niż poświęcać go, żeby zadał dodakowe obrażenia przeciwnikowi. Jeżeli więc jesteśmy zagrożeni śmiercią, uciekamy, a nie giniemy na próżno. * **Reguła 3: Atakuje przeciwnika o dwa kroki od ciebie.** Przyjrzyj się grającemu wg reguł robotowi, zauważysz, że kiedy wchodzi na pole atakowane przez przeciwnika, odnosi obrażenia. Dlatego, jeśli prawdopodobne jest, że przeciwnik może znaleźć się w naszym sąsiedztwie, trzeba go zatakować. Dzięki temu nit się do nas bezkarnie nie zbliży. .. note:: Połączenie ucieczki i ataku w kierunku przeciwnika naprawdę jest skuteczne. Każdy agresywny wróg zanim nas zaatakuje, sam spotyka się z atakiem. Jeżeli w porę odskoczysz, zanim się zbliży, działanie takie możesz powtórzyć. Technika ta nazywana jest w grach `kiting `_, a jej działanie ilustruje poniższa animacja: .. figure:: img/kiting.gif Zwróć uwagę na słabego robota ze zdrowiem 8 HP, który podchodzi do mocnego robota z 50 HP, a następnie ucieka. Zbliżając się atakuje pole, na które wchodzi przeciwnik, ucieka i ponawia działanie. Trwa to do momentu, kiedy silniejszy robot popełni samobójstwo (co w tym wypadku jest mało przydatne). Wszystko bez uszczerbku na zdrowiu słabszego robota. * **Reguła 4: Wchodź tylko na wolne pola.** Przykładowy robot idzie do środka planszy, ale w wielu wypadkach lepiej zrobić coś innego. Np. iść tam, gdzie jest bezpiecznie, zamiast narażać się na bezużyteczne niebezpieczeństwo. Co jest bowiem ryzykowne? Po wejściu na planszę ruch na pole przeciwnika lub wchodzenie w jego sąsiedztwo. Wiadomo też, że nie możemy wchodzić na zajęte pola i że możemy zmniejszyć ilość kolizji, nie wchodząc na pola zajęte przez naszą drużynę. * **Reguła 5: Idź na wroga, jeżeli go nie ma w zasięgu dwóch kroków.** Po co iść do środka, skoro mamy inne bezpieczne możliwości? Wprawdzie stanie w punkcie wejścia jest złe, ale to nie znaczy, że środek planszy jest dobry. Lepszym wyborem jest ruch w kierunku, ale nie na pole, przeciwnika. W połączeniu z atakiem daje nam to lepszą kontrolę nad planszą. Później przekonamy się jeszcze, że są sytuacje, kiedy wejście na potencjalnie niebezpieczne pole warte jest ryzyka, ale na razie poprzestańmy na tym, co ustaliliśmy. Łączenie ulepszeń ******************* Zapiszmy wszystkie reguły w pseudokodzie. Możemy użyć do tego jednej rozbudowanej instrukcji warunkowej if/else. .. code-block:: html jeżeli jesteś w punkcie wejścia: rusz się bezpiecznie (np. poza wejście) jeżeli jeddnak mamy przeciwnika o krok dalej: jeżeli możemy umrzeć: ruszamy się w bezpieczne miejsce w przeciwnym razie: atakujemy przeciwnika jeżeli jednak mamy przeciwnika o dwa kroki dalej: atakujemy w jego kierunku jeżeli mamy bezpieczny ruch (i nikogo wokół siebie): ruszamy się bezpiecznie, ale w kierunku przeciwnika w przeciwnym razie: bronimy się w miejscu, bo nie ma gdzie ruszyć się lub atakować Implementacja **************** Do zakodowania omówionej logiki potrzebujemy struktury danych gry z jej ustawieniami i kilku funkcji. Pamiętajmy, że jest wiele sobosobów na zapisanie kodu w Pythonie. Poniższy w żdanym razie nie jest optymalny, ale działa jako przykład. Zbiory zamiast list ******************** Dla ułatwienia użyjemy pythonowych zbiorów razem z funkcją ``set()`` i wyrażeniami zbiorów (ang. *set comprehensions*). .. note:: Zbiory i operacje na nich omówiono w `dokumentacji zbiorów `_, podobnie przykłady `wyrażeń listowych i odpowiadających im pętli `_. Podstawowe operacje na zbiorach, których użyjemy to: * ``|`` lub suma – zwraca zbiór wszystkich elementów zbiorów; * ``-`` lub różnica – zbiór elementów obecnych tylko w pierwszym zbiorze; * ``&`` lub iloczyn – zwraca zbiór elementów występujących w obydwu zbiorach. Załóżmy, że zaczniemy od wygenerowania następujących list: ``drużyna`` – członkowie drużyny, ``wrogowie`` – przeciwnicy, ``wejścia`` – punkty wejścia oraz ``przeszkody`` – położenia zablokowane, tzn. szare kwadraty. Zbiory pól **************************** Aby ułatwić implementację omówionych ulepszeń, przygotujemy kilka zbiorów reprezentujących pola różnych kategorii na planszy gry. W tym celu używamy wyrażeń listowych (ang. *list comprehensions*). .. code-block:: python # zbiory pól na planszy # wszystkie pola wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)} # punkty wejścia (spawn) wejscia = {loc for loc in wszystkie if 'spawn' in rg.loc_types(loc)} # pola zablokowane (obstacle) zablokowane = {loc for loc in wszystkie if 'obstacle' in rg.loc_types(loc)} # pola zajęte przez nasze roboty przyjaciele = {loc for loc in game.robots if game.robots[loc].player_id == self.player_id} # pola zajęte przez wrogów wrogowie = set(game.robots) - przyjaciele Warto zauważyć, że zbiór wrogich robotów otrzymujemy jako różnicę zbioru wszystkich robotów i tych z naszej drużyny. Wykorzystanie zbiorów **************************** Przy poruszaniu się i atakowaniu mamy tylko cztery możliwe kierunki, które zwraca funkcja ``rg.locs_around``. Możemy wykluczyć położenia zablokowane (ang. *obstacle*), ponieważ nigdy ich nie zajmujemy i nie atakujemy. Iloczyn zbiorów ``sasiednie & wrogowie`` da nam zbiór przeciwników w sąsiedztwie: .. code-block:: python # pola sąsiednie sasiednie = set(rg.locs_around(self.location)) - zablokowane # pola sąsiednie zajęte przez wrogów wrogowie_obok = sasiednie & wrogowie Aby odnaleźć wrogów oddalonych o dwa kroki, szukamy przyległych kwadratów, obok których są przeciwnicy. Wyłączamy sąsiednie pola zajęte przez członków drużyny. .. code-block:: python # pola zajęte przez wrogów w odległości 2 kroków wrogowie_obok2 = {loc for loc in sasiednie if (set(rg.locs_around(loc)) & wrogowie)} - przyjaciele Teraz musimy sprawdzić, które z położeń są bezpieczne. Usuwamy pola zajmowane przez przeciwników w odległości 1 i 2 kroków. Pozbywamy się także punktów wejścia, nie chcemy na nie wracać. Podobnie, aby zmniejszyć możliwość kolizji, wyrzucamy pola zajmowane przez drużynę. W miarę komplikowania logiki będzie można zastąpić to ograniczenie dodatkowym warunkiem, ale na razie to najlepsze, co możemy zrobić. .. code-block:: python bezpieczne = sasiednie - wrogowie_obok - wrogowie_obok2 - wejscia - przyjaciele Potrzebujemy funkcji, która wybierze ze zbioru położeń pole najbliższe podanego. Możemy użyć tej funkcji do znajdowania najbliższego wroga, jak również do wyboru pola z bezpiecznej listy. Możemy więc wybrać ruch najbardziej przybliżający nas do założonego celu. .. code-block:: python def mindist(bots, loc): return min(bots, key=lambda x: rg.dist(x, loc)) Możemy użyć metody ``pop()`` zbioru, aby pobrać jego dowolny element, np. przeciwnika, którego zaatakujemy. Żeby dowiedzieć się, czy jesteśmy zagrożeni śmiercią, możemy pomnożyć liczbę sąsiadujących przeciwników przez średni poziom uszkodzeń (9 punktów HP) i sprawdzić, czy mamy więcej siły. Ze względu na sposób napisania funkcji ``minidist()`` trzeba pamiętać o przekazywaniu jej niepustych zbiorów. Jeśli np. zbiór przeciwników będzie pusty, funkcja zwróci błąd. Składamy wszystko razem ************************ Po złożeniu wszystkich kawałków kodu razem otrzymujemy przykładową implemetację robota wyposażonego we wszystkie założone wyżej właściwości: .. raw:: html
Kod nr
.. highlight:: python .. literalinclude:: robot_b.py :linenos: .. raw:: html
.. note:: Niniejsza dokumentacja jest swobodnym i nieautoryzowanym tłumaczeniem materiałów dostępnych na stonie `Robotgame basic strategy `_.