User Tools

Site Tools


elemental_fighters

This is an old revision of the document!


Zadatak s Hacknite platforme - Elemental Fighters

 
Možete li odabrati pravog borca i pobijediti sve neprijatelje?
Spoji se Linux naredbom nc chal.platforma.hacknite.hr 14037 ili Windows naredbom telnet chal.platforma.hacknite.hr 14037

Uz zadatak je dan i izvorni kod.

Spajanjem na zadatak otvara se izbor borca. Mogući izbor su tri borca i tri elementa za svakog borca.

 Slika 1 - izbor 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 neprijatelji.

 Slika 2 - scale varijabla

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.

 Slika 3 - Player default

Najključniji dio koda je funkcija kojom se inicijalizira varijabla *player* iz izbora igrača.

 Slika 4 - inicijalizacija player varijable iz igračevog izbora

Ovdje se može vidjeti da se koristi eval funkcija, kojom se *fighter* unos poziva kao funkcija, a *fighterType* kao argument.

Sukladno tome, može se vidjeti da su *fighteri* zapravo funkcije:

 Slika 5 - fighter funkcije

Vrijednosti *fighterType* definirane su u rječniku te se parsiraju pri pozivanju *fighter* funkcije.

To znači da se unosom *fighterType* (“Odaberite element borca”) zapravo definira argument funkcije, a unosom *fighter* (“Odaberite borca”) definira funkcija koja će se izvršiti s proslijeđenim argumentom u eval funkciji.

No svaki unos prolazi kroz validate_input, koji je filter koji određuje koji su unosi dozvoljeni, a koji nisu.

 Slika 6 - validate funkcija

Kako bismo provjerili možemo li 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

 Slika 7 - poziv list("aaa")

Vidimo da je program vratio listu u kojoj se nalaze svi znakovi iz stringa koji smo poslali kao prvi argument, te oko njih znakovi zagrada koje se dodaju oko *fighterType* varijable (vidljivo na slici 4).

Slično, možemo pozvati:

len("aaa")
Odaberite element borca (Ice, Fire, Acid): aaa
Odaberite borca (Zorn, Krev, Milo)len

 Slika 8 - poziv len("aaa")

Vidimo da program vraća 5, što odgovara trima znakovima “a” koje smo poslali i dvama znakovima zagrada.

No flag nije zapisan ni u jednoj varijabli, nego je hardkodiran u zadnjem printu programa. Zato 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 bio bi mijenjanje varijable s (vidi sliku 2), koja predstavlja skalu jačine protivnika. Ako bismo tu varijablu mogli smanjiti ili postaviti na 0, protivnici bi bili znatno slabiji. Činjenica da je input potrošen na mijenjanje varijable “s” umjesto na inicijalizaciju borca ne bi bila problem jer postoji *default* odabir borca (vidi sliku 3).

No cijeli unos igrača izvršava se unutar eval funkcije, koja je namijenjena za evaluiranje *expressiona*. *Expression* je izraz koji nakon izvršavanja vraća neku vrijednost — primjerice, izraz je `“2+2”`, a vraćena vrijednost je 4; izraz je `len(“(aaa)”)` (kao na slici 8), a vraćena vrijednost 5. Svaka vrijednost vraćena nakon izvršavanja izraza u eval funkciji bit će pridružena varijabli *player* (vidi sliku 4).

Potrebno je pronaći način kako mijenjati druge varijable u globalnom kontekstu programa (poput varijable *s*) unutar eval funkcije, čiji se rezultat pridružuje varijabli *player*.

Osim expressiona koje izvršava *eval*, u Pythonu postoji i statement, koji izvršava akciju, ali ne vraća vrijednost. *Statement* je, primjerice, `a=0`. No budući da *statement* ne vraća vrijednost, `b = (a = 0)` nije validan Python kod; također, ni `eval(“a = 0”)` nije validan Python kod.

Slično eval, postoji funkcija exec, koja je namijenjena za izvršavanje *statementa* (ali može izvršavati i *expressione*). Ona može definirati ili mijenjati varijable u globalnom kontekstu programa ako se pokreće u globalnom kontekstu. Primjer: `exec(“a=0”)` je validan kod i postavit će (ili promijeniti) vrijednost varijable *a* na 0. Pregledom funkcije za validaciju unosa (slika 6.) vidi se da funkcija exec nije zabranjena.

Dakle, exec se može koristiti za mijenjanje vrijednosti varijable *s*, koja je globalna.

Iz ovoga se može zaključiti da bi potencijalno rješenje bilo izvršiti:

exec(s=0)

Unos bi izgledao ovako:

Odaberite element borca (Ice, Fire, Acid): s=0
Odaberite borca (Zorn, Krev, Milo)exec

Pokretanjem ovog unosa (slika 9.) pojavljuje se greška.

 Slika 9 - greška pri pokušaju

Greška je *invalid syntax*, koju zapravo baca parser eval funkcije. On pregledava kod prije izvršavanja da bi odredio je li kod validan *expression*. Iako je kod namijenjen da bude proslijeđen exec funkciji, eval parser ga ne prihvaća.

Opisana ponašanja prikazana su na slici 10.

 Slika 10 - eval parser i exec funkcija

Na slici je prikazano da eval može izvršiti `a==3`, jer je to validan *expression* koji se evaluira u *False*.

Može se zaključiti da je cilj napisati takav kod koji će eval parser odobriti, a koji će imati logiku jednaku *statementu* `s=0`. Ovdje pomaže činjenica da exec može izvršavati i *expression*, pa treba napisati *expression* koji će eval parser odobriti, a exec će ga izvršiti i promijeniti globalnu varijablu *s*.

Način da se to postigne je korištenjem Pythonovog walrus operatora (`:=`), koji je zapravo *expression*. On vraća rezultat evaluacije izraza, ali također postavlja evaluiranu vrijednost u varijablu.

 Slika 11 - walrus operator

Na slici 11. može se vidjeti kako funkcionira *walrus* operator. On evaluira izraz `1 == 1`, što je `True`, tu vrijednost vraća kao rezultat *if* statementu, ali istovremeno postavlja tu vrijednost varijabli *a*.

*Walrus* operatorom se može napisati validan *expression* koji nakon evaluacije mijenja vrijednost odabrane varijable (npr. varijable *s*).

No *walrus* operator se ne može sam izvršavati unutar eval ni exec funkcije, jer mora biti dio *expressiona*. Ako se stave zagrade oko njega, izraz

(a:=3)

postaje validan *expression*, što je prikazano na slici ispod.

 Slika 12 - rješenje zadatka

Ovo je zapravo slučaj u kodu zadatka, jer se oko unosa dodaju zagrade (vidi sliku ispod).

Kada su zagrade oko unosa, unutar njih mora biti validan *expression*. `s=0` to nije jer je *statement*, zato rješenje

Odaberite element borca (Ice, Fire, Acid): s=0
Odaberite borca (Zorn, Krev, Milo)milo

nije moguće.

Ali *walrus* operator je validan *expression* i može se izvršiti unutar eval i exec funkcije, gdje u oba slučaja uspješno mijenja vrijednost varijable (ako se izvršava u globalnom kontekstu).

Zato se može izvršiti i ovako:

Odaberite element borca (Ice, Fire, Acid): s:=0
Odaberite borca (Zorn, Krev, Milo)exec

Time se izvršava postupak koji postavlja varijablu *s* na 0, a *player* na *None*, što zatim aktivira *default* vrijednost *player = Milo(“ice”)*.

Ovo ne bi bilo moguće bez *walrus* operatora jer eval parser vidi samo *statement*, dok je *walrus* operator unutar zagrada validan *expression* koji mijenja vrijednost varijable.

Pošto se ovo izvršava u global scopeu, exec može promijeniti vrijednost globalne varijable *s*. Da se izvršavalo unutar funkcije, ne bi moglo promijeniti ni lokalne varijable u istom *scopeu*.

Pri izvršavanju u globalnom *scopeu* ovo postaje moguće i daje konačno rješenje zadatka.

elemental_fighters.1761842116.txt.gz · Last modified: 2025/12/01 11:40 (external edit)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki