3.3. RG – klocki 2A¶
Wersja A oparta jest na funkcjach, czyli metodach klasy Robot
.
Tip
- Każdy “klocek” można testować osobno, a później w połączeniu z innymi. Warto i trzeba zmieniać kolejność stosowanych reguł!
3.3.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 kilka pomocniczych funkcji.
3.3.1.1. Czy to wejście?¶
# funkcja zwróci prawdę, jeżeli "poz" wskazuje punkt wejścia
def czy_wejscie(poz):
if 'spawn' in rg.loc_types(poz):
return True
return False
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.3.1.2. Czy obok jest wróg?¶
# funkcja zwróci prawdę, jeżeli "poz" wskazuje wroga
def czy_wrog(poz):
if game.robots.get(poz) != None:
if game.robots[poz].player_id != self.player_id:
return True
return False
# lista wrogów obok
wrogowie_obok = []
for poz in rg.locs_around(self.location):
if czy_wrog(poz):
wrogowie_obok.append(poz)
# warunek sprawdzający, czy obok są wrogowie
if len(wrogowie_obok):
pass
W powyższym kodzie metoda .get(poz)
pozwala pobrać dane robota, którego
kluczem w słowniku jest poz
.
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'))
.
Tip
Definicje funkcji i list 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.3.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):
def czy_wejscie(poz):
if 'spawn' in rg.loc_types(poz):
return True
return False
def czy_wrog(poz):
if game.robots.get(poz) != None:
if game.robots[poz].player_id != self.player_id:
return True
return False
# lista wrogów obok
wrogowie_obok = []
for poz in rg.locs_around(self.location):
if czy_wrog(poz):
wrogowie_obok.append(poz)
# jeżeli jesteś w punkcie wejścia, opuść go
if czy_wejscie(self.location):
return ['move', rg.toward(self.location, rg.CENTER_POINT)]
# jeżeli obok są przeciwnicy, atakuj
if len(wrogowie_obok):
return ['attack', wrogowie_obok.pop()]
# jeżeli jesteś w środku, broń się
if self.location == rg.CENTER_POINT:
return ['guard']
# idź do środka planszy
return ['move', rg.toward(self.location, rg.CENTER_POINT)]
|
Metoda .pop()
zastosowana do listy zwraca jej ostatni element.
3.3.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!
3.3.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 A
# jeżeli suma potencjalnych uszkodzeń jest mniejsza od naszego zdrowia
# funkcja zwróci prawdę
def czy_atak():
if 9*len(wrogowie_obok) < self.hp:
return True
return False
Metody i właściwości biblioteki rg:
self.hp
– ilość punktów HP robota.
3.3.2.1. Ćwiczenie 2¶
Dodaj powyższą regułę do poprzedniej wersji robota.
3.3.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 A
# funkcja zwróci prawdę jeżeli pole poz będzie puste
def czy_puste(poz):
if ('normal' in rg.loc_types(poz)) and not ('obstacle' in rg.loc_types(poz)):
if game.robots.get(poz) == None:
return True
return False
puste = [] # lista pustych pól obok
bezpieczne = [] # lista bezpiecznych pól obok
for poz in rg.locs_around(self.location):
if czy_puste(poz):
puste.append(poz)
if czy_puste(poz) and not czy_wejscie(poz):
bezpieczne.append(poz)
3.3.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ć.
# funkcja zwróci prawdę, jeżeli w odległości 2 kroków z przodu jest wróg
def zprzodu(l1, l2):
if rg.wdist(l1, l2) == 2:
if abs(l1[0] - l2[0]) == 1:
return False
else:
return True
return False
# funkcja zwróci współrzędne pola środkowego między dwoma innymi
# oddalonymi o 2 kroki
def miedzypole(p1, p2):
return (int((p1[0]+p2[0]) / 2), int((p1[1]+p2[1]) / 2))
for poz, robot in game.get('robots').items():
if czy_wrog(poz):
if rg.wdist(poz, self.location) == 2:
if zprzodu(poz, self.location):
return ['attack', miedzypole(poz, self.location)]
if rg.wdist(rg.toward(loc, rg.CENTER_POINT), self.location) == 1:
return ['attack', rg.toward(poz, rg.CENTER_POINT)]
else:
return ['attack', (self.location[0], poz[1])]
3.3.5. Składamy reguły¶
3.3.5.1. Ćwiczenie 3¶
Jeżeli czujesz się na siłach, spróbuj dokładać do robota w wersji A (opartego na funkcjach) 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:
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | #! /usr/bin/env python
# -*- coding: utf-8 -*-
import rg
class Robot:
def act(self, game):
def czy_wejscie(poz):
if 'spawn' in rg.loc_types(poz):
return True
return False
def czy_wrog(poz):
if game.robots.get(poz) != None:
if game.robots[poz].player_id != self.player_id:
return True
return False
def czy_atak():
if 9*len(wrogowie_obok) < self.hp:
return True
return False
def czy_puste(poz):
if ('normal' in rg.loc_types(poz)) and not ('obstacle' in rg.loc_types(poz)):
if game.robots.get(poz) == None:
return True
return False
puste = [] # lista pustych pól obok
bezpieczne = [] # lista bezpiecznych pól obok
for poz in rg.locs_around(self.location):
if czy_puste(poz):
puste.append(poz)
if czy_puste(poz) and not czy_wejscie(poz):
bezpieczne.append(poz)
# funkcja zwróci prawdę, jeżeli w odległości 2 kroków z przodu jest wróg
def zprzodu(l1, l2):
if rg.wdist(l1, l2) == 2:
if abs(l1[0] - l2[0]) == 1:
return False
else:
return True
return False
# funkcja zwróci współrzędne pola środkowego między dwoma innymi
# oddalonymi o 2 kroki
def miedzypole(p1, p2):
return (int((p1[0]+p2[0]) / 2), int((p1[1]+p2[1]) / 2))
# lista wrogów obok
wrogowie_obok = []
for poz in rg.locs_around(self.location):
if czy_wrog(poz):
wrogowie_obok.append(poz)
# jeżeli jesteś w punkcie wejścia, opuść go
if czy_wejscie(self.location):
return ['move', rg.toward(self.location, rg.CENTER_POINT)]
# jeżeli obok są przeciwnicy, atakuj, o ile to bezpieczne
if len(wrogowie_obok):
if czy_atak():
return ['attack', wrogowie_obok.pop()]
elif bezpieczne:
return ['move', bezpieczne.pop()]
# jeżeli wróg jest o dwa kroki, atakuj
for poz, robot in game.get('robots').items():
if czy_wrog(poz) and rg.wdist(poz, self.location) == 2:
if zprzodu(poz, self.location):
return ['attack', miedzypole(poz, self.location)]
if rg.wdist(rg.toward(poz, rg.CENTER_POINT), self.location) == 1:
return ['attack', rg.toward(poz, rg.CENTER_POINT)]
else:
return ['attack', (self.location[0], poz[1])]
# jeżeli jesteś w środku, broń się
if self.location == rg.CENTER_POINT:
return ['guard']
# idź do środka planszy
return ['move', rg.toward(self.location, rg.CENTER_POINT)]
|
3.3.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.3.6.1. Atakuj najsłabszego¶
# funkcja zwracająca atak na najsłabszego wroga obok
def atakuj():
r = wrogowie_obok[0]
for poz in wrogowie_obok:
if game.robots[poz]['hp'] > game.robots[r]['hp']:
r = poz
return ['attack', r]
3.3.6.2. 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...
Proponujemy, żebyś sam zaczął wprowadzać i testować zasugerowane ulepszenia. Możesz też zajrzeć do drugiego drugiego i trzeciego zestawu klocków opartych na zbiorach.
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: | 2017-09-08 o 19:38 w Sphinx 1.4.5 |
---|---|
Autorzy: | Patrz plik “Autorzy” |