3.4. RG – klocki 2B¶
Wersja B oparta jest na zbiorach i operacjach na nich.
Wskazówka
- Każdy “klocek” można testować osobno, a później w połączeniu z innymi. Warto i trzeba zmieniać kolejność stosowanych reguł!
3.4.1. Typy pól¶
Zobaczmy, w jaki sposób dowiedzieć się, w jakim miejscu się znajdujemy, gdzie wokół mamy wrogów lub pola, na które można wejść. Dysponując takimi informacjami, będziemy mogli podejmować bardziej przemyślane działania. Wykorzystamy wyrażenia zbiorów (ang. set comprehensions) (zob. wyrażenie listowe) i operacje na zbiorach (zob. zbiór).
3.4.1.1. Czy to wejście?¶
# wszystkie pola na planszy jako współrzędne (x, y)
wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)}
# punkty wejścia (spawn)
wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)}
# warunek sprawdzający, czy "poz" jest w punkcie wejścia
if poz in wejscia:
pass
Metody i właściwości biblioteki rg:
gr.loc_types(poz)
– zwraca typ pola wskazywanego przezpoz
:invalid
– poza granicami planszy(np. (-1, -5) lub (23, 66));normal
– w ramach planszy;spawn
– punkt wejścia robotów;obstacle
– pola zablokowane ograniczające arenę.
3.4.1.2. Czy obok jest wróg?¶
Wersja oparta na zbiorach wykorzystuje różnicę i cześć wspólną zbiorów.
# pola zablokowane
zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)}
# pola zajęte przez nasze roboty
przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id}
# pola zajęte przez wrogów
wrogowie = set(game.robots) - przyjaciele
# pola sąsiednie
sasiednie = set(rg.locs_around(self.location)) - zablokowane
# pola obok zajęte przez wrogów
wrogowie_obok = sasiednie & wrogowie
# warunek sprawdzający, czy obok są wrogowie
if wrogowie_obok:
pass
Metody i właściwości biblioteki rg:
rg.locs_around(poz, filter_out=None)
– zwraca listę położeń sąsiadujących zpoz
. Jakofilter_out
można podać typy położeń do wyeliminowania, np.:rg.locs_around(self.location, filter_out=('invalid', 'obstacle'))
.
Wskazówka
Definicje zbiorów należy wstawić na początku
metody Robot.act()
– przed pierwszym użyciem.
Wykorzystując powyższe “klocki” możemy napisać robota realizującego następujące reguły:
- Opuść jak najszybciej wejście;
- Atakuj wrogów obok;
- W środku broń się;
- W ostateczności idź do środka.
3.4.1.3. Implementacja¶
Przykładowa implementacja może wyglądać następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #! /usr/bin/env python
# -*- coding: utf-8 -*-
import rg
class Robot:
def act(self, game):
# wszystkie pola
wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)}
# punkty wejścia
wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)}
# pola zablokowane
zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)}
# pola zajęte przez nasze roboty
przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id}
# pola zajęte przez wrogów
wrogowie = set(game.robots) - przyjaciele
# pola sąsiednie
sasiednie = set(rg.locs_around(self.location)) - zablokowane
# pola sąsiednie zajęte przez wrogów
wrogowie_obok = sasiednie & wrogowie
# działanie domyślne:
ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)]
# jeżeli jesteś w punkcie wejścia, opuść go
if self.location in wejscia:
ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)]
# jeżeli jesteś w środku, broń się
if self.location == rg.CENTER_POINT:
ruch = ['guard']
# jeżeli obok są przeciwnicy, atakuj
if wrogowie_obok:
ruch = ['attack', wrogowie_obok.pop()]
return ruch
|
Metoda .pop()
zastosowana do zbioru zwraca element losowy.
3.4.1.4. Ćwiczenie 1¶
Zapisz powyższą implementację w katalogu robot
i przetestuj
ją w symulatorze, a następnie wystaw ją do walki z robotem podstawowym.
Poeksperymentuj z kolejnością reguł, która określa ich priorytety!
Wskazówka
Do kontrolowania logiki działania robota zamiast rozłącznych instrukcji
warunkowych: if war1: ... if war2: ...
itd. można użyć instrukcji
złożonej: if war1: ... elif war2: ... [elif war3: ...] else: ...
.
3.4.2. Atakuj, jeśli nie umrzesz¶
Warto atakować, ale nie wtedy, gdy grozi nam śmierć. Można przyjąć zasadę, że atakujemy tylko wtedy, kiedy suma potencjalnych uszkodzeń będzie mniejsza niż zdrowie naszego robota. Zmień więc dotychczasowe reguły ataku wroga korzystając z poniższych “klocków”:
# WERSJA B
# jeżeli obok są przeciwnicy, atakuj
if wrogowie_obok:
if 9*len(wrogowie_obok) >= self.hp:
pass
else:
ruch = ['attack', wrogowie_obok.pop()]
Metody i właściwości biblioteki rg:
self.hp
– ilość punktów HP robota.
3.4.2.1. Ćwiczenie 2¶
Dodaj powyższą regułę do poprzedniej wersji robota.
3.4.3. Ruszaj się bezpiecznie¶
Zamiast iść na oślep lepiej wchodzić czy uciekać na bezpieczne pola. Za “bezpieczne” przyjmiemy na razie pole puste, niezablokowane i nie będące punktem wejścia.
# WERSJA B
# zbiór bezpiecznych pól
bezpieczne = sasiednie - wrogowie_obok - wejscia - przyjaciele
3.4.4. Atakuj 2 kroki obok¶
Jeżeli w odległości 2 kroków jest przeciwnik, zamiast iść w jego kierunku i narażać się na szkody, lepiej go zaatakuj, aby nie mógł bezkarnie się do nas zbliżyć.
# WERSJA B
wrogowie_obok2 = {poz for poz in sasiednie if (set(rg.locs_around(poz)) & wrogowie)} - przyjaciele
if wrogowie_obok2:
ruch = ['attack', wrogowie_obok2.pop()]
3.4.5. Składamy reguły¶
3.4.5.1. Ćwiczenie 3¶
Jeżeli czujesz się na siłach, spróbuj dokładać do robota w wersji B (opartego na zbiorach) po jednej z przedstawionych reguł, czyli: 1) Atakuj, jeśli nie umrzesz; 2) Ruszaj się bezpiecznie; 3) Atakuj na 2 kroki. Przetestuj w symulatorze każdą zmianę.
Omówione reguły można poskładać w różny sposób, np. tak:
W wersji B opartej na zbiorach:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #! /usr/bin/env python
# -*- coding: utf-8 -*-
import rg
class Robot:
def act(self, game):
# wszystkie pola
wszystkie = {(x, y) for x in xrange(19) for y in xrange(19)}
# punkty wejścia
wejscia = {poz for poz in wszystkie if 'spawn' in rg.loc_types(poz)}
# pola zablokowane
zablokowane = {poz for poz in wszystkie if 'obstacle' in rg.loc_types(poz)}
# pola zajęte przez nasze roboty
przyjaciele = {poz for poz in game.robots if game.robots[poz].player_id == self.player_id}
# pola zajęte przez wrogów
wrogowie = set(game.robots) - przyjaciele
# pola sąsiednie
sasiednie = set(rg.locs_around(self.location)) - zablokowane
# pola sąsiednie zajęte przez wrogów
wrogowie_obok = sasiednie & wrogowie
wrogowie_obok2 = {poz for poz in sasiednie if (set(rg.locs_around(poz)) & wrogowie)} - przyjaciele
# pola bezpieczne
bezpieczne = sasiednie - wrogowie_obok - wejscia - przyjaciele
# działanie domyślne:
ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)]
# jeżeli jesteś w punkcie wejścia, opuść go
if self.location in wejscia:
ruch = ['move', rg.toward(self.location, rg.CENTER_POINT)]
# jeżeli jesteś w środku, broń się
if self.location == rg.CENTER_POINT:
ruch = ['guard']
# jeżeli obok są przeciwnicy, atakuj, o ile to bezpieczne
if wrogowie_obok:
if 9*len(wrogowie_obok) >= self.hp:
if bezpieczne:
ruch = ['move', bezpieczne.pop()]
else:
ruch = ['attack', wrogowie_obok.pop()]
if wrogowie_obok2:
ruch = ['attack', wrogowie_obok2.pop()]
return ruch
|
3.4.6. Możliwe ulepszenia¶
Poniżej pokazujemy “klocki”, których możesz użyć, aby zoptymalizować robota. Zamieszczamy również listę pytań do przemyślenia, aby zachęcić cię do samodzielnego konstruowania najlepszego robota :-)
3.4.6.1. Atakuj najsłabszego¶
# wersja B
# funkcja znajdująca najsłabszego wroga obok z podanego zbioru (bots)
def minhp(bots):
return min(bots, key=lambda x: game.robots[x].hp)
if wrogowie_obok:
...
else:
ruch = ['attack', minhp(wrogowie_obok)]
3.4.6.2. Najkrócej do celu¶
Funkcji mindist()
można użyć do znalezienia najbliższego wroga,
aby iść w jego kierunku, kiedy opuścimy punkt wejścia:
# WERSJA B
# funkcja zwraca ze zbioru pól (bots) pole najbliższe podanego celu (poz)
def mindist(bots, poz):
return min(bots, key=lambda x: rg.dist(x, poz))
najblizszy_wrog = mindist(wrogowie,self.location)
3.4.6.3. Inne¶
- Czy warto atakować, jeśli obok jest więcej niż 1 wróg?
- Czy warto atakować 1 wroga obok, ale mocniejszego od nas?
- Jeżeli nie można bezpiecznie się ruszyć, może lepiej się bronić?
- Jeśli jesteśmy otoczeni przez wrogów, może lepiej popełnić samobójstwo...
- Spróbuj zmienić akcję domyślną.
- Spróbuj użyć jednej złożonej instrukcji warunkowej!
Proponujemy, żebyś sam zaczął wprowadzać i testować zasugerowane ulepszenia. Możesz też zajrzeć do trzeciego zestawu klocków.
Materiały Python 101
udostępniane przez
Centrum Edukacji Obywatelskiej na licencji
Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.
Utworzony: | 2022-05-22 o 19:52 w Sphinx 1.5.3 |
---|---|
Autorzy: | Patrz plik “Autorzy” |