This is an old revision of the document!
Elemental Fighters
Uz zadatak je dan i izvorni kod.
Spajanjem na zadatak otvara se izbor borca. Mogući izbor su 3 borca i 3 elementa za svakog borca.
No, analizom koda u zadatku može se zaključiti da zapravo ne postoji kombinacija borca koja bi ni u najsretnijem slučaju uspjela pobijediti sve neprijatelje i doći do flaga — znači da je potrebno nešto drugo napraviti jer izbor pravog borca ne postoji.
Analizom koda, zanimljiva je varijabla s (scale), koja određuje koliko će se oslabiti ili ojačati neprijatelji.
Ova varijabla se kasnije u kodu množi s atributima neprijatelja: što je manja, neprijatelji su slabiji; što je veća, neprijatelji su jači.
Također, zanimljiv dio koda je player default, koji definira varijablu _player_ kao `milo(“ice”)` ako je _player_ `None`.
Najključniji dio koda je funkcija kojom se inicijalizira varijabla _player_ iz izbora igrača.
Ovdje se koristi funkcija eval, kojom se unos _fighter_ poziva kao funkcija, a _fighterType_ kao argument.
Sukladno tome, fighteri su zapravo funkcije:
Vrijednosti _fighterType_ definirane su u rječniku i parsiraju se pri pozivanju _fighter_ funkcije.
To znači da unosom _fighterType_ (“Odaberite element borca”) definiraš argument funkcije, a unosom _fighter_ (“Odaberite borca”) definiraš funkciju kojoj će se argument proslijediti i koja će se izvršiti u `eval`.
No, svaki unos prolazi kroz validate_input, filter koji određuje koji su unosi dozvoljeni.
Kako bismo provjerili možemo li pozvati proizvoljnu funkciju i argument (ako validate_function ne zabrani), možemo probati:
list("aaa")
Unos izgleda ovako:
Odaberite element borca (Ice, Fire, Acid): aaaa Odaberite borca (Zorn, Krev, Milo)list
Program vraća listu u kojoj su svi znakovi iz stringa koji smo poslali kao prvi argument, s dodanim zagradama oko _fighterType_ varijable (vidi sliku 4).
Slično možemo pozvati:
len("aaa")
Unos:
Odaberite element borca (Ice, Fire, Acid): aaa Odaberite borca (Zorn, Krev, Milo)len
Program vraća 5, što odgovara trima znakovima `“a”` koje smo poslali i dvama znakovima zagrada.
Flag nije pohranjen u varijabli; hardkodiran je u zadnjem `print`u programa. Potrebno je osmisliti način kako pozvati odgovarajuću funkciju i argument da bi program izvršio flag.
Dobar pristup je mijenjanje varijable s (vidi sliku 2), koja predstavlja skalu jačine protivnika. Ako je postavimo na 0, protivnici su slabiji, a činjenica da se input koristi za mijenjanje varijable _s_ umjesto inicijalizacije borca nije problem jer postoji *default* borac (vidi sliku 3).
Cijeli unos igrača se izvršava unutar eval, koji evaluira *expression*. Primjeri: `“2+2”` → `4`, `len(“(aaa)”)` → `5`. Bilo koja vrijednost vraćena iz eval dodjeljuje se _player_ varijabli (vidi sliku 4).
Potrebno je mijenjati druge varijable u globalnom kontekstu (npr. _s_) unutar eval-a.
Python razlikuje *expression* i *statement*. *Statement* (npr. `a = 0`) izvršava akciju, ali ne vraća vrijednost — pa `eval(“a=0”)` nije validan kod. Funkcija exec izvršava *statement* i može mijenjati varijable u globalnom kontekstu: `exec(“a=0”)`. Pregledom validate_input (slika 6) vidi se da exec nije zabranjen.
Dakle, exec se može koristiti za mijenjanje globalne varijable s.
Primjer pokušaja:
exec("s=0")
Unos:
Odaberite element borca (Ice, Fire, Acid): s=0 Odaberite borca (Zorn, Krev, Milo)exec
Pokretanjem pojavit će se greška `invalid syntax` (slika 9) jer parser eval zahtijeva validan *expression*.
Rješenje: koristiti Pythonov walrus operator (`:=`), koji je *expression* i vraća vrijednost dok istovremeno dodjeljuje varijabli.
Primjer:
(a := 3)
U zagradama postaje validan *expression*, koji se može evaluirati unutar eval i izvršiti s exec u globalnom scopeu.
Rješenje unosa:
Odaberite element borca (Ice, Fire, Acid): s:=0 Odaberite borca (Zorn, Krev, Milo)exec
Time se postavlja varijabla s na 0, a _player_ ostaje `None` — aktivira se *default* `player = Milo(“ice”)`.
Walrus operator je validan *expression*, može se izvršavati unutar eval i exec, mijenja vrijednost varijable samo ako se izvršava u globalnom scopeu.
Ovaj postupak reproducira originalnu logiku inicijalizacije _player_ varijable u zadatku (slika 4).
Rješenje unosa:
Odaberite element borca (Ice, Fire, Acid): s:=0 Odaberite borca (Zorn, Krev, Milo)exec
Postavlja s = 0 i _player = None_, zatim se _player_ inicijalizira na *default* Milo(“ice”).
Pošto se izvršava u global scopeu, exec može mijenjati globalnu varijablu s. Ako bi se izvršavalo unutar funkcije, lokalne varijable ne bi bile promijenjene.
Ovo vrijedi i za eval i za exec.