| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| elemental_fighters [2025/10/31 14:33] – mbunic | elemental_fighters [2025/12/01 11:40] (current) – external edit 127.0.0.1 |
|---|
| ==== Elemental Fighters ==== | ==== Zadatak s Hacknite platforme - Elemental Fighters ==== |
| |
| Uz zadatak je dan i izvorni kod. | 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. | Spajanjem na zadatak otvara se izbor borca, mogući izbor su 3 borca i 3 elementa za svakog borca. |
| |
| {{ elemental_fighters:slika1.png?nolink&500 | Slika 1. - izbor borca}} | {{ elemental_fighters:slika1.png?nolink&500 | 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 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, to znači da je potrebno nešto drugo učiniti jer ne postoji izbor pravog borca. |
| |
| Analizom koda, zanimljiva je varijabla <code>s</code>, koja je "scale", odnosno određuje koliko će se oslabiti ili ojačati neprijatelje. | Analizom koda zanimljiva je varijabla **s**, koja je "scale", odnosno određuje koliko će se oslabiti ili ojačati neprijatelje. |
| |
| {{ elemental_fighters:slika2.png?nolink&500 | Slika 2 - scale varijabla}} | {{ elemental_fighters:slika2.png?nolink&500 | Slika 2 - scale varijabla}} |
| |
| 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. | Ova varijabla se kasnije u kodu množi s atributima neprijatelja i što je manja, neprijatelji su slabiji, a što je veća, neprijatelji su jači. |
| |
| Također zanimljiv dio koda je player default, koji definira varijablu <code>player</code> kao <code>milo("ice")</code>, ako je <code>player</code> None. | Također zanimljiv dio koda je player default, koji definira player varijablu kao **Milo("ice")**, ako je player None. |
| |
| {{ elemental_fighters:slika3.png?nolink&500 | Slika 3. - Player default}} | {{ elemental_fighters:slika3.png?nolink&500 | Slika 3. - Player default}} |
| |
| Najključniji dio koda je funkcija kojom se inicijalizira varijabla <code>player</code> iz izbora igrača. | Ključan dio koda je funkcija kojom se inicijalizira player varijabla iz izbora igrača. |
| |
| {{ elemental_fighters:slika4.png?nolink&500 | Slika 4. - inicijalizacija player varijable iz igračevog izbora}} | {{ elemental_fighters:slika4.png?nolink&500 | Slika 4. - inicijalizacija player varijable iz igračevog izbora}} |
| |
| Ovdje se može vidjeti da se koristi <code>eval</code> funkcija, kojom se <code>fighter</code> unos poziva kao funkcija, a <code>fighterType</code> kao argument. | Ovdje se može vidjeti da se koristi funkcija **eval**, kojime se fighter unos poziva kao funkcija, a fighterType kao argument. |
| |
| Sukladno tome, može se vidjeti da su <code>fighteri</code> zapravo funkcije: | Može se vidjeti da su fighteri zapravo funkcije: |
| |
| {{ elemental_fighters:slika5.png?nolink&500 | Slika 5. - fighter funkcije}} | {{ elemental_fighters:slika5.png?nolink&500 | Slika 5. - fighter funkcije}} |
| |
| Vrijednosti <code>fighterType</code> definirane su u rječniku te se parsiraju pri pozivanju <code>fighter</code> funkcije. | a fighterType vrijednosti su definirane u rječniku, te se parsiraju pri pozivanju fighter funkcije. |
| |
| To znači da se unosom <code>fighterType</code> ("Odaberite element borca") definira argument funkcije, a unosom <code>fighter</code> ("Odaberite borca") definira funkcija kojoj će se argument proslijediti i koja će se izvršiti s proslijeđenim argumentom u <code>eval</code> funkciji. | 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 <code>validate_input</code>, koja je filter koji određuje koji su unosi dozvoljeni, a koji nisu. | Svaki unos prolazi kroz validate_input funkciju, koja je filter koji određuje koji su unosi dozvoljeni, a koji nisu. |
| |
| {{ elemental_fighters:slika6.png?nolink&500 | Slika 6. - validate funkcija}} | {{ elemental_fighters:slika6.png?nolink&500 | Slika 6. - validate funkcija}} |
| |
| Kako bismo provjerili da možemo pozvati proizvoljnu funkciju i argument, ako nemamo unos koji bi <code>validate_function</code> zabranio, možemo probati pozvati: | Kako bi provjerili da možemo pozvati proizvoljnu funkciju i argument, ako nemamo unos koji bi funkcija validacije zabranila, možemo probati pozvati: |
| |
| <code>list("aaa")</code> | <file> |
| | list("aaa") |
| | </file> |
| |
| <code>Odaberite element borca (Ice, Fire, Acid): aaaa | Odaberite element borca (Ice, Fire, Acid): aaaa |
| Odaberite borca (Zorn, Krev, Milo)list</code> | Odaberite borca (Zorn, Krev, Milo)list |
| |
| {{ elemental_fighters:slika7.png?nolink&500 | Slika 7. - poziv list("aaa")}} | {{ elemental_fighters:slika7.png?nolink&500 | 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 oko <code>fighterType</code> varijable (vidi sliku 4). | 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: | Slično se može pozvati len funkcija. |
| |
| <code>len("aaa")</code> | <file> |
| | len("aaa") |
| | </file> |
| |
| <code>Odaberite element borca (Ice, Fire, Acid): aaa | Poziva se na ovakav način. |
| Odaberite borca (Zorn, Krev, Milo)len</code> | |
| | <file> |
| | Odaberite element borca (Ice, Fire, Acid): aaa |
| | Odaberite borca (Zorn, Krev, Milo)len |
| | </file> |
| |
| {{ elemental_fighters:slika8.png?nolink&500 | Slika 8. - poziv len("aaa")}} | {{ elemental_fighters:slika8.png?nolink&500 | Slika 8. - poziv len("aaa")}} |
| |
| Vidimo da program vraća 5, što odgovara trima znakovima <code>"a"</code> koje smo poslali i dvama znakovima zagrada. | Vidimo da program vraća 5, što je točno 3 znakova a koje smo poslali plus dva znaka zagrada. |
| |
| No flag nije zapisan niti 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. | Flag nije zapisan niti u jednoj varijabli, nego je samo hardkodiran 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 bio bi mijenjanje varijable <code>s</code> (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 <code>s</code> umjesto na inicijalizaciju borca ne bi bila problem jer postoji default odabir borca (vidi sliku 3). | 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 unos potrošen na mijenjanje 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 izvršava se unutar <code>eval</code> funkcije, koja je namijenjena za evaluiranje <code>expression</code>. <code>Expression</code> je izraz koji nakon izvršavanja vraća neku vrijednost — primjerice: | Cijeli unos igrača se izvršava unutar funkcije **eval**, koja je namijenjena samo za evaluiranje **expression**-a, koji nakon izvršavanja vraća neku vrijednost, na primjer: |
| |
| <code>"2+2"</code> → 4 | <file> |
| <code>len("(aaa)")</code> → 5 | "2+2" |
| | </file> |
| |
| Svaka vrijednost vraćena nakon izvršavanja izraza u <code>eval</code> funkciji bit će pridružena varijabli <code>player</code> (vidi sliku 4). | vraćena vrijednost je 4, |
| |
| Potrebno je pronaći način kako mijenjati druge varijable u globalnom kontekstu programa, kao varijablu <code>s</code>, unutar <code>eval</code> funkcije, čiji se rezultat izvršavanja pridružuje varijabli <code>player</code>. | <file> |
| | len("(aaa)") |
| | </file> |
| |
| Osim <code>expression</code> u Pythonu, koji izvršava izraz i vraća vrijednost, postoji i <code>statement</code>, koji izvršava definiranu akciju, no ne vraća ništa. | 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). |
| |
| <code>statement</code> je, primjerice, <code>a = 0</code>, no pošto <code>statement</code> ne vraća ništa, <code>b = (a = 0)</code> je invalidan Python kod; također <code>eval("a = 0")</code> je invalidan Python kod. | 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**. |
| |
| Slično <code>eval</code>, postoji funkcija <code>exec</code>, koja je namijenjena za izvršavanje <code>statement</code> (ali može izvršavati i <code>expression</code>), i može definirati ili mijenjati varijable u globalnom kontekstu ako se pokreće u globalnom kontekstu. | Osim **expression** 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. |
| |
| Primjer: | Primjer **statement-a** |
| |
| <code>exec("a=0")</code> | <file> |
| | a = 0 |
| | </file> |
| |
| Pregledom funkcije za validaciju unosa (slika 6) vidi se da <code>exec</code> funkcija nije zabranjena. Dakle, <code>exec</code> se može koristiti za mijenjanje vrijednosti varijable <code>s</code>. | , no pošto **statement** ne vraća ništa |
| | |
| | <file> |
| | b = (a = 0) |
| | </file> |
| | |
| | je neispravan Python kod, kao što bi bilo i da se eval koristio. |
| | |
| | <file> |
| | eval("a = 0") |
| | </file> |
| | |
| | (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. |
| | |
| | <file> |
| | exec("a=0") |
| | </file> |
| | |
| | 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: | Iz ovoga se može zaključiti da potencijalni način rješavanja ovog programa bi bio izvršiti: |
| |
| <code>exec(s=0)</code> | <file> |
| | exec(s=0) |
| | </file> |
| |
| Unos bi izgledao ovako: | Čime bi unos izgledao ovako: |
| |
| <code>Odaberite element borca (Ice, Fire, Acid): s=0 | <file> |
| Odaberite borca (Zorn, Krev, Milo)exec</code> | Odaberite element borca (Ice, Fire, Acid): s=0 |
| | Odaberite borca (Zorn, Krev, Milo)exec |
| | </file> |
| |
| Pokretanjem ovog unosa pojavljuje se greška: | Pokretanjem ovog unosa, prikazanog na slici 9, pojavljuje se greška. |
| |
| {{ elemental_fighters:slika9.png?nolink&500 | Slika 9. - greška pri pokušaju}} | {{ elemental_fighters:slika9.png?nolink&500 | Slika 9. - greška pri pokušaju}} |
| |
| Greška je <code>invalid syntax</code>, koju baca parser <code>eval</code> funkcije. | 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 prikazana su na slici 10. | Opisana ponašanja su prikazana na slici 10. |
| |
| {{ elemental_fighters:slika10.png?nolink&500 | Slika 10. - eval parser i exec funkcija}} | {{ elemental_fighters:slika10.png?nolink&500 | Slika 10. - eval parser i exec funkcija}} |
| |
| Na slici je prikazano da <code>eval</code> može izvršiti <code>a==3</code>, jer je to validan <code>expression</code> koji se evaluira u <code>False</code>. | Na slici je prikazano da **eval** može izvršiti: |
| |
| Može se zaključiti da je cilj napisati kod koji će parser <code>eval</code> odobriti, a koji će imati logiku jednaku <code>s=0</code> <code>statement</code>. Ovdje pomaže činjenica da <code>exec</code> može izvršavati <code>expression</code>, pa treba napisati <code>expression</code> koji će <code>eval</code> parser odobriti, a <code>exec</code> izvršiti i promijeniti globalnu varijablu <code>s</code>. | <file> |
| | a==3 |
| | </file> |
| |
| Način da se to postigne je korištenjem Pythonovog "walrus" operatora (<code>:=</code>), koji je <code>expression</code>. On vraća rezultat evaluacije izraza, ali također postavlja evaluiranu vrijednost u varijablu. | jer je to zapravo validan **expression** koji se evaluira u False. |
| | |
| | Način kako se ovo može postići je korištenjem Pythonovog "walrus" operatora, koji je zapravo **expression**, vraća rezultat evaluacije **expressiona**, 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. - walrus operator}} | {{ elemental_fighters:slika11.png?nolink&500 | Slika 11. - walrus operator}} |
| |
| Walrus operator evaluira <code>expression</code> poput <code>1 == 1</code>, što je <code>True</code>, i to postavlja kao vrijednost varijable. Walrus operator se može koristiti za promjenu vrijednosti globalne varijable <code>s</code> unutar <code>eval</code> ili <code>exec</code>. | Na slici 11 može se vidjeti kako funkcionira walrus operator: evaluira **expression** "1 == 1", što je `True`, i to prosljeđuje kao rezultat **if izrazu**, ali također tu istu vrijednost postavlja kao vrijednost varijable **a**. |
| |
| Ako se stave zagrade oko njega, izraz: | Walrus operatorom se može napisati validan **expression**, koji nakon evaluacije također mijenja vrijednost odabrane varijable, ciljano varijable **s**. |
| |
| <code>(a:=3)</code> | Walrus operator se ne može sam izvršavati niti unutar **eval** niti unutar **exec** funkcije, zato što je namijenjen da bude izvršen unutar **expressiona**. |
| |
| postaje validan <code>expression</code>, što je prikazano na slici 13. | {{ elemental_fighters:slika12.png?nolink&500 | Slika 12 - walrus operator}} |
| | |
| | No ako se stave zagrade oko njega, izraz: |
| | |
| | <file> |
| | (a:=3) |
| | </file> |
| | |
| | postaje validan **expression**, što je prikazano na slici ispod. |
| |
| {{ elemental_fighters:slika13.png?nolink&500 | Slika 13 - walrus operator unutar zagrada}} | {{ elemental_fighters:slika13.png?nolink&500 | Slika 13 - walrus operator unutar zagrada}} |
| |
| Ovo je slučaj u kodu zadatka, jer se oko unosa dodaju zagrade (vidi sliku 14). | Ovo je slučaj i u kodu zadatka, zato što se oko unosa dodaju zagrade, što je prikazano na slici ispod. |
| |
| {{ elemental_fighters:slika14.png?nolink&500 | Slika 14 - zagrade oko korisničkog unosa}} | {{ elemental_fighters:slika14.png?nolink&500 | Slika 14 - zagrade oko korisničkog unosa}} |
| |
| Kada su zagrade oko unosa, unutar njih mora biti valid <code>expression</code>, što <code>s=0</code> nije (vidi sliku 15). | Kada su zagrade oko unosa, unutar zagrada mora biti validan **expression**, što `s=0` nije nego je **statement**, kao što se vidi na slici ispod. |
| |
| {{ elemental_fighters:slika15.png?nolink&500 | Slika 15 - nevaljajuća sintaksa}} | {{ elemental_fighters:slika15.png?nolink&500 | Slika 15 - neispravna sintaksa}} |
| |
| Zato rješenje: | To čini ovaj pristup neizvedivim: |
| |
| <code>Odaberite element borca (Ice, Fire, Acid): s=0 | <file> |
| Odaberite borca (Zorn, Krev, Milo)milo</code> | Odaberite element borca (Ice, Fire, Acid): s=0 |
| | Odaberite borca (Zorn, Krev, Milo)milo |
| | </file> |
| |
| nije moguće. | |
| |
| Ali walrus operator je validan <code>expression</code> i može se izvršiti unutar <code>eval</code> i <code>exec</code>, mijenjajući vrijednost globalne varijable. | "alrus operator je validan **expression** i namijenjen je za izvršavanje unutar **expressiona**, što se može napraviti stavljanjem dodatnih zagrada. Ovako se walrus operator može izvršiti i unutar **eval** i unutar **exec** funkcije, gdje u oba slučaja uspješno mijenja vrijednost varijable (ali samo ako se **exec** i **eval** pokreću unutar globalnog konteksta). |
| |
| {{ elemental_fighters:slika16.png?nolink&500 | Slika 16 - walrus i eval / exec}} | {{ elemental_fighters:slika16.png?nolink&500 | Slika 16 - walrus i eval / exec}} |
| | |
| | I zato se također može izvršavati na ovaj način: |
| | |
| | {{ elemental_fighters:slika17.png?nolink&500 | Slika 17 - walrus unutar exec unutar eval}} |
| | |
| | Što je, uz dodavanje zagrada, zapravo ekvivalentno ovom kodu: |
| | |
| | {{ elemental_fighters:slika18.png?nolink&500 | Slika 18 - kod ekvivalentan kodu zadatka}} |
| | |
| | Odnosno ovome, nakon zamjene stringova da se sve vidi u jednoj liniji: |
| | |
| | {{ elemental_fighters:slika19.png?nolink&500 | Slika 19 - pojednostavljeni ekvivalentan kod}} |
| | |
| | I ovo je format inicijalizacije **player** varijable u zadatku, pa time i rješenja ovog zadatka; ove zagrade su iste kao i u liniji koda inicijalizacije **player** varijable **eval** funkcijom na slici 4. |
| | |
| | Nakon toga se mogu raditi daljnje redukcije, zamjenom **eval** funkcije i njenih argumenata s ekvivalentnim kodom, pa primjenom istog postupka za **exec** funkciju s proslijeđenim argumentima. Nakon redukcija ta linija koda postaje ekvivalentna samo walrus operatoru unutar zagrada. |
| | |
| | {{ elemental_fighters:slika20.png?nolink&500 | Slika 20 - pojednostavljeno izvršavanje payloada}} |
| | |
| | Sada je poznato rješenje i način kako rješava zadatak. |
| |
| Rješenje se može unijeti ovako: | Rješenje se može unijeti ovako: |
| |
| <code>Odaberite element borca (Ice, Fire, Acid): s:=0 | <file> |
| Odaberite borca (Zorn, Krev, Milo)exec</code> | Odaberite element borca (Ice, Fire, Acid): s:=0 |
| | Odaberite borca (Zorn, Krev, Milo)exec |
| | </file> |
| |
| Izvršava se prethodno opisani postupak, varijabla <code>s</code> postaje 0, <code>player</code> ostaje None, te se inicijalizira default borac <code>Milo("ice")</code>. | Izvršava se prethodno opisan postupak izvršavanja ovog koda, koji rezultira u postavljanju varijable **s** u 0, a varijable **player** u None, te se **player** onda inicijalizira u default, odnosno **Milo("ice")**. |
| |
| {{ elemental_fighters:slika21.png?nolink&500 | Slika 21. - rješenje zadatka}} | {{ elemental_fighters:slika21.png?nolink&500 | Slika 21. - rješenje zadatka}} |
| |
| Eval / Exec i global scope: | ==== Eval / Exec i global scope ==== |
| |
| Kada se izvršava u global scopeu, <code>exec</code> može promijeniti vrijednost varijable <code>s</code>. U local scope funkcije to ne bi bilo moguće. | Budući da je ovo izvršeno u global scopeu, **exec** može promijeniti vrijednost scale varijable **s**, dok, da se izvršavao unutar funkcije, ne bi mogao promijeniti niti lokalnu varijablu unutar istog scopea kao funkcija. |
| |
| {{ elemental_fighters:slika22.png?nolink&500 | Slika 22. - eval unutar functiona}} | {{ elemental_fighters:slika22.png?nolink&500 | Slika 22. - eval unutar functiona}} |
| |
| Pri izvršavanju u global scopeu, ovo je moguće. | Dok je pri izvršavanju u global scopeu, ovo moguće. |
| |
| {{ elemental_fighters:slika23.png?nolink&500 | Slika 23. - eval unutar global scopea}} | {{ elemental_fighters:slika23.png?nolink&500 | Slika 23. - eval unutar global scopea}} |
| |
| | Ovo pravilo vrijedi i za **eval** i za **exec**, obje funkcije mogu mijenjati globalne varijable samo kada se izvršavaju u globalnom scopeu. |