3.3. RG – klocki 2A
Wersja A oparta jest na funkcjach, czyli metodach klasy Robot
.
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.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'))
.
Wskazówka
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#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import rg
5
6class Robot:
7
8 def act(self, game):
9
10 def czy_wejscie(poz):
11 if 'spawn' in rg.loc_types(poz):
12 return True
13 return False
14
15 def czy_wrog(poz):
16 if game.robots.get(poz) != None:
17 if game.robots[poz].player_id != self.player_id:
18 return True
19 return False
20
21 # lista wrogów obok
22 wrogowie_obok = []
23 for poz in rg.locs_around(self.location):
24 if czy_wrog(poz):
25 wrogowie_obok.append(poz)
26
27 # jeżeli jesteś w punkcie wejścia, opuść go
28 if czy_wejscie(self.location):
29 return ['move', rg.toward(self.location, rg.CENTER_POINT)]
30
31 # jeżeli obok są przeciwnicy, atakuj
32 if len(wrogowie_obok):
33 return ['attack', wrogowie_obok.pop()]
34
35 # jeżeli jesteś w środku, broń się
36 if self.location == rg.CENTER_POINT:
37 return ['guard']
38
39 # idź do środka planszy
40 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#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import rg
5
6class Robot:
7
8 def act(self, game):
9
10 def czy_wejscie(poz):
11 if 'spawn' in rg.loc_types(poz):
12 return True
13 return False
14
15 def czy_wrog(poz):
16 if game.robots.get(poz) != None:
17 if game.robots[poz].player_id != self.player_id:
18 return True
19 return False
20
21 def czy_atak():
22 if 9*len(wrogowie_obok) < self.hp:
23 return True
24 return False
25
26 def czy_puste(poz):
27 if ('normal' in rg.loc_types(poz)) and not ('obstacle' in rg.loc_types(poz)):
28 if game.robots.get(poz) == None:
29 return True
30 return False
31
32 puste = [] # lista pustych pól obok
33 bezpieczne = [] # lista bezpiecznych pól obok
34
35 for poz in rg.locs_around(self.location):
36 if czy_puste(poz):
37 puste.append(poz)
38 if czy_puste(poz) and not czy_wejscie(poz):
39 bezpieczne.append(poz)
40
41 # funkcja zwróci prawdę, jeżeli w odległości 2 kroków z przodu jest wróg
42 def zprzodu(l1, l2):
43 if rg.wdist(l1, l2) == 2:
44 if abs(l1[0] - l2[0]) == 1:
45 return False
46 else:
47 return True
48 return False
49
50 # funkcja zwróci współrzędne pola środkowego między dwoma innymi
51 # oddalonymi o 2 kroki
52 def miedzypole(p1, p2):
53 return (int((p1[0]+p2[0]) / 2), int((p1[1]+p2[1]) / 2))
54
55 # lista wrogów obok
56 wrogowie_obok = []
57 for poz in rg.locs_around(self.location):
58 if czy_wrog(poz):
59 wrogowie_obok.append(poz)
60
61 # jeżeli jesteś w punkcie wejścia, opuść go
62 if czy_wejscie(self.location):
63 return ['move', rg.toward(self.location, rg.CENTER_POINT)]
64
65 # jeżeli obok są przeciwnicy, atakuj, o ile to bezpieczne
66 if len(wrogowie_obok):
67 if czy_atak():
68 return ['attack', wrogowie_obok.pop()]
69 elif bezpieczne:
70 return ['move', bezpieczne.pop()]
71
72 # jeżeli wróg jest o dwa kroki, atakuj
73 for poz, robot in game.get('robots').items():
74 if czy_wrog(poz) and rg.wdist(poz, self.location) == 2:
75 if zprzodu(poz, self.location):
76 return ['attack', miedzypole(poz, self.location)]
77 if rg.wdist(rg.toward(poz, rg.CENTER_POINT), self.location) == 1:
78 return ['attack', rg.toward(poz, rg.CENTER_POINT)]
79 else:
80 return ['attack', (self.location[0], poz[1])]
81
82 # jeżeli jesteś w środku, broń się
83 if self.location == rg.CENTER_POINT:
84 return ['guard']
85
86 # idź do środka planszy
87 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:
2025-04-12 o 10:21 w Sphinx 7.3.7
- Autorzy: