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
, koja je “scale”, odnosno određuje koliko će se oslabiti ili ojačati neprijatelje.
Ova varijabla se kasnije u kodu množi s atributima neprijatelja i što je manja, neprijatelji su slabiji, što je veća, neprijatelji su jači.
Također zanimljiv dio koda je player default, koji definira player varijablu kao:
Milo("ice")
ako je player None.
Najključniji dio koda je funkcija kojom se inicijalizira player varijabla iz izbora igrača.
Ovdje se može vidjeti da se koristi
eval
funkcija, kojime se fighter unos poziva kao funkcija, a fighterType kao argument.
Sukladno tome, može se vidjeti da su fighteri zapravo funkcije:
a fighterType vrijednosti su definirane u rječniku, te se parsiraju pri pozivanju fighter funkcije.
To znači da je unosom fighterType (“Odaberite element borca”) zapravo se definira argument funkcije, a unosom fighter (“Odaberite borca”) se definira funkcija kojoj će se argument proslijediti i koja će se izvršiti s proslijeđenim argumentom u
eval
funkciji.
No svaki unos prolazi kroz validate_input, koja je filter koji su unosi dozvoljeni a koji nisu.
Kako bi provjerili da možemo pozvati proizvoljnu funkciju i argument, ako nemamo unos koji bi validate_function zabranio, možemo probati pozvati:
list("aaa")
Odaberite element borca (Ice, Fire, Acid): aaaa Odaberite borca (Zorn, Krev, Milo)list
Vidimo da je program vratio listu u kojoj se nalaze svi znakovi iz stringa koji smo poslali kao prvi argument, te oko njih znakovi za zagrade, koji su znakovi zagrada oko fighterType varijable (vidi sliku 4).
Slično, možemo pozvati:
len("aaa")
Odaberite element borca (Ice, Fire, Acid): aaa Odaberite borca (Zorn, Krev, Milo)len
Vidimo da program vraća 5, što je taman 3 znakova a koje smo poslali i dva znaka zagrada.
No flag nije zapisan niti u jednoj varijabli, nego je samo hard kodiran u zadnjem printu programa, pa je potrebno osmisliti način kako pozvati odgovarajuću funkciju i argument, da bi se program uspješno izvršio do kraja i ispisao flag.
Dobar pristup bi bio mijenjanje varijable “s” na slici 2, koja je skala jačine protivnika. Ako bi tu varijablu mogli smanjiti ili pretvoriti u 0, protivnici bi bili puno slabiji, dok činjenica da je input potrošen na mijenjane varijable “s” umjesto na inicijalizaciju našeg borca ne bi bio problem, jer postoji “default” odabir borca, ako borac nije definiran, prikazan na slici 3.
No cijeli unos igrača se izvršava unutar
eval
funkcije, koje je namijenjena samo za evaluiranje
expression
,
expression
je izraz koji nakon izvršavanja vraća neku vrijednost, na primjer
"2+2"
, vraćena vrijednost je 4,
len("(aaa)")
, kao na slici 8, vraćena vrijednost je 5, i bilo koja vrijednost koja će biti vraćena nakon izvršavanja ekspresije u
eval
funkciji će biti pridružena varijabli
player
(vidi sliku 4).
Potrebno je pronaći način kako mijenjati druge varijable u globalnom kontekstu programa, kao varijablu
s
, unutar
eval
funkcije, čiji se rezultat izvršavanja pridružuje varijabli
player
Osim “expressiona” u Pythonu, koji izvršava izraz i vraća izračunatu vrijednost izraza, postoji i
statement
, koji izvršava definiranu akciju, no ne vraća ništa.
statement
je na primjer
a = 0
, no pošto
statement
ne vraća ništa, također:
b = (a = 0)
je invalidan Python kod, također zato što
eval("a = 0")
je također invalidan Python kod (probajte u Python interpreteru).
Slično funkciji
eval
, postoji funkcija
exec
, koja je upravo namijenjena za izvršavanje
statement
(a može izvršavati i
expression
), te može definirati ili mijenjati varijable u globalnom kontekstu programa, ako se također pokreće u globalnom kontekstu programa.
exec("a=0")
Pregledom funkcije za validaciju unosa na slici 6 vidi se da funkcija
exec
nije zabranjena.
Znači da se funkcija
exec
može koristiti za mijenjanje vrijednosti scale varijable
s
, koja je globalna varijabla.
Iz ovoga se može zaključiti da potencijalni način rješavanja ovog programa bi bio izvršiti:
exec(s=0)
Čime bi unos izgledao ovako:
Odaberite element borca (Ice, Fire, Acid): s=0 Odaberite borca (Zorn, Krev, Milo)exec
Pokretanjem ovog unosa, prikazanog na slici 9, pojavljuje se greška.
Greška je invalid syntax, koju zapravo baca parser
eval
funkcije, koji pregledava kod prije nego će se izvršiti da odredi je li kod zapravo validan
expression
, iako je kod namijenjen da bude proslijeđen
exec
funkciji koja bi ovaj kod mogla normalno izvršiti.
Opisana ponašanja su prikazana na slici 10.
Na slici je prikazano da
eval
može izvršiti:
a==3
jer je to zapravo validan
expression
koji se evaluira u False.
Može se zaključiti da je cilj napisati takav kod, koji će parser
eval
funkcije odobriti, a koji će imati logiku jednaku
s=0
statement
, iako je valid
expression
. Ovdje pomaže činjenica da
exec
funkcija može izvršavati i
expression
, zato se treba napisati
expression
, koji će
eval
parser odobriti, koji će
exec
izvršiti i koji će pri izvršavanju u
exec
funkciji, promijeniti vrijednost globalne varijable scale
s
Način kako se ovo može postići je korištenjem Pythonovog “walrus” operatora, koji je zapravo
expression
, vraća rezultat evaluacije
expression
, ali također evaluirani
expression
, nakon evaluacije postavlja kao vrijednost dane varijable.
Walrus operator se označava znakovima:
:=
{{ elemental_fighters:slika11.png?nolink&500 | Slika 11.