Preskoči na glavno vsebino
Učilnica FRI 24/25
  • Domov
  • Več
Zapri
Preklopi iskalni vnos
Slovenščina ‎(sl)‎
English ‎(en)‎ Slovenščina ‎(sl)‎ Македонски ‎(mk)‎ Русский ‎(ru)‎ 한국어 ‎(ko)‎
Trenutno uporabljate gostujoči dostop
Prijavite se
Učilnica FRI 24/25
Domov
Razširi vse Skrči vse
  1. p1
  2. Objektno programiranje
  3. Trdoživi kolesarji, eksplozije in duhovi

Trdoživi kolesarji, eksplozije in duhovi

Zahteve zaključka
Rok za oddajo: torek, 21. januar 2025, 23.59

Pri reševanju te naloge se trudi programirati čim manj. Seveda ne tako, da prepustiš del drugim :), pač pa naj bodo razredi smiselno izpeljani eden iz drugega in naj ne ponavljajo kode, ki jo lahko nadomestijo metode starševskih razredov.

Pred reševanjem preberi celotno nalogo, da ne boš pri nalogi za oceno 7 ali 8 spreminjal tega, kar si naredil za 6. Kateri razred bo izpeljan iz katerega, se odloči sam. Vrstni red opisov v nalogi ne implicira nujno hierarhije razredov.

Atribute imenuj, kakor želiš. Po mili volji smeš dodajati tudi svoje metode (tako kot smo na predavanjih dodali želvi metodo update). Pač pa avtor naloge (= jaz) ne vidi potrebe, da bi vsak razred definiral svoje metode prevozi, lokacija, razdalja, pot in obiskana_polja: te definiraj le v osnovnem razredu. Početi torej smeš kar želiš, dokler bodo vse metode počele vse, kar zahteva naloga, in bo tvoj izdelek prestal teste.

Ocena 6

  • Napiši razred Kolesar.

    • Konstruktor __init__(self, vrstica, stolpec, zemljevid) kot argument prejme začetni koordinati kolesarja in množico koordinat ovir, podanih s terkami (vrstica, stolpec).

      Primer na sliki je opisan z množico

      {(2, 4), (2, 11), (2, 12), (2, 13),
      (4, 2), (4, 10),
      (5, 2), (5, 8),
      (6, 7),
      (7, 11),
      (8, 4), (8, 5), (8, 6),
      (9, 2),
      (10, 6), (10, 11),
      (11, 3), (11, 10),
      (12, 4), (12, 11),
      (13, 7), (13, 12)}
      

    Kako dodati konstruktorju argumente, smo videli na predavanju, v zapiskih pa to najdeš takoj na začetku zapiskov naslednjega tedna.

    • premik(self, smer) prejme smer "<", ">", "^" ali "v". Metoda premakne kolesarja za en kvadratek v podano smer, če tam ni ovire. Če se na ciljnem kvadratku nahaja ovira, pa kolesar ne spremeni pozicije, temveč obleži, kjer je in se poslej ne odziva več na nadaljnje ukaze premik ali pojdi.

    • prevozi(self, pot) prejme niz z zaporedjem premikov. V nizu se izmenjujejo števke (= enomestna števila) in smeri. Tako, na primer, "3>1v2^4^" pomeni tri korake desno, enega dol, 2 gor in potem še štiri gor. Funkcija mora izvesti vse podane korake do konca ali do trenutka, ko se kolesar zaleti v oviro.

    Recimo, da kolesar izvaja "3>", tri korake desno. Rezultat mora biti ekvivalenten temu, da trikrat izvede premik >. Če je že takoj desno od njega ovira, obleži še preden se začne premikati. Če pa je desno eno prazno polje in nato ovira, se zapelje na to, prazno polje in ustavi.

    • lokacija() vrne trenutne koordinate kolesarja v obliki terke (vrstica, stolpec).

Ocena 7

Dodaj naslednje metode.

  • razdalja() vrne razdaljo (v številu) kvadratkov, ki jih je doslej (oz. do nesreče) prevozil kolesar.
  • obiskana_polja() vrne množico koordinat polj, ki jih je obiskal kolesar. Obiskana polja vključujejo tudi začetno polje. Pazi: razdalja ni nujno enaka številu obiskanih polj (+1, za začetno polje), saj se lahko polja ponavljajo, v množici pa nastopajo le enkrat!
  • pot vrne prevoženo pot v obliki, v kakršni jo sprejema metoda prevozi.

    Rezultat funkcije pot naj bo zapisana strnjeno. Recimo, da kolesarka ana prevozi takšno pot:

    ana = Kolesar(2, 2, zemljevid)
    ana.premik(">")
    ana.premik(">")
    ana.prevozi("2>")
    ana.prevozi("8v")
    ana.prevozi("4v")
    ana.premik(">")
    

    Po tem zaporedju klicev mora ana.pot() vrniti "4>9v3v1>".

    • Prvi trije klici (oba premik in še en prevozi) so shranjeni v 4>.
    • 12 premikov v smeri navzdol ni zapisanih kot 12v, ker morajo biti vsa števila enomestna, zato se shrani 9v in nato 3v.

    Nasvet: niz s potjo sestavljaj sproti, med premikanjem, tako da "popravljaš" njegov konec in dodajaš, kadar je potrebno.

Ocena 8

  • Napiši razred Duh. Ta je podoben razredu Kolesar, vendar zna voziti skozi ovire, kot da jih ni: če se zapelje na polje z oviro, se v isti potezi znajde na drugi strani. Če se zaleti v serijo ovir, gre v isti potezi skoznje.

    • Duh, ki se na gornji sliki nahaja na (9, 4) bo po premiku v smeri ^ na polju (7, 4). Pri tem se šteje, da je prevozil dve polji - vključno z oviro: obiskana polja so {(9, 4), (8, 4), (7, 4)}, metoda pot() pa vrne 2^ (ne "1^").
    • Duh, ki se nahaja na (8, 2) bo po premiku 4> na polju (8, 9) (in ne (8, 6) ali (8, 7)). Polja z ovirami zanj preprosto "ne štejejo", upoštevajo pa se pri razdalji: s klicem prevozi("4>") ni prevozil 4 polj temveč 7 polj. (To pravilo je postavljeno tako, da bi bo program preprostejši - če se ga lotiš pametno.)
  • Napiši tudi razred Trdozivec, ki je podoben razredu Kolesar, vendar bolj trdoživ. Njegov konstruktor prejme še en dodatni argument, trdozivost. Ta pove, v koliko ovir se mora kolesar zaleteti, da obleži. Če je trdozivost enaka 3, kolesar obleži, ko se zaleti tretjič. Premik v oviro se seveda nikoli ne izvede (trdoživec ni duh!); razlika med trdoživcem in softičem je samo v tem, da trdoživec ne obleži že pred prvo oviro.

    Pozor: če je ovira desno od kolesarja, klic prevozi("3>") povzroči, da se kolesar trikrat zaleti vanjo. Če je njegova trdoživost enaka 3 ali manj, obleži, če je 4 ali več, pa preživi.

  • Napiši, končno, razred Eksplozivec. Temu ovire ne povzročajo prav nobene škode, pač pa jo on povzroči oviram: ovira, v katero se zaleti eksplozivec, izgine in eksplozivec se pomakne na to polje, kot da ovire tam sploh nikoli ne bilo.

    V nekem srečnem scenariju sta Ana (Eksplozivec) in Berta (Kolesar) začeli na polju 10, 2. Ana je šla gor. Za njo je šla gor še Berta -- in uspela, saj je Ana razstrelila oviro pred njo!

    Če nameravaš reševati tudi dodatno naloge, ovire odstranjuj iz zemljevida tako, da kličeš metodo discard.

Ocena 9

Oddelek za gozdarsko dejavnost in motorni promet skliče izredno sejo, na kateri obravnava problem prevelike pretočnosti kolesarskih stez zaradi eksplozivno odstranjenih ovir. Na njem sklene in po hitrem postopku naroči trdnejše ovire. Nove ovire preživijo tri eksplozije. Eksplozivni kolesar vedno preživi trk (eksplozivni kolesar je na nek način neskončno trdoživ), vendar se ne premakne z mesta, dokler ovira ne eksplodira dovoljkrat.

Vzemimo, da je eksplozivni kolesar na polju (10, 2). Premik "1^" ali "2^" ga ne premakneta z mesta, temveč le načenjata oviro. Premik "3^" (oziroma, v splošnem trije trki v to oviro, ne glede na smer trka in čas, ki mine med njimi) bi jo res uničil in premaknil kolesarja na polje z oviro ((9, 2)). Če eksplozivni kolesar na polju (10, 2) izvede premik "5^", se premakne na polje (7, 2): prva dva premika sta zgolj uničevala oviro, v tretjem, ki je uničil oviro, pa se je že prekamnil na (9, 2) (in v naslednjih dveh do (7, 2)).

Da poskrbiš za ta scenarij, pusti pri miru vse razrede s kolesarji (ob predpostavki, da so sprogramirani pravilno).

Pač pa napiši razred Ovire. Izpeljan naj bo iz razreda dict. Testi za dodatno nalogo bodo ponovno izvedli vse teste za obvezno nalogo (razen teh, ki vključujejo posebne efekte, se prvi eksplozije), a kot argument zemljevid namesto množice podali tvoj razred Ovire. Zato poskrbi, da boš v obvezni nalogi uporabljal množice tako, da bo delovala tudi, če bodo Ovire v resnici slovar. (Namig: razmisliti moraš, kaj bodo tvoji ključi.)

Če bo razred Ovire izpeljan iz razreda dict, zadošča, da definiraš

  • konstruktor, ki prejme množico ovir,
  • metodo discard, ki kot argument prejme koordinate ovire, vendar jo dejansko odstrani šele ob tretjem klicu. (Na tem mestu razmisli, kaj so vrednosti v tvojem slovarju.) Če na podanih koordinatah ni ovire, discard ne stori ničesar.

Tvoj slovar se mora torej vesti tako:

>>> ovire = Ovire({(1, 1), (2, 4)})
>>> ovire.discard((1, 1))
>>> (1, 1) in ovire
True
>>> ovire.discard((2, 4))
>>> (2, 4) in ovire
True
>>> ovire.discard((1, 1))
>>> (1, 1) in ovire
True
>>> ovire.discard((1, 1))
>>> (1, 1) in ovire
False
>>> (2, 4) in ovire
True

Pomoč: konstruktor razreda Ovire bo poklical super().__init__, kot vsi zgledno napisani konstruktorji. Kot argument mu podaj primerno sestavljen slovar.

Ocena 9 in 10

Za oceno 9 bo potrebno sprogramirati še nekaj, prav tako seveda za oceno 10. Navodila bodo še objavljena.

Testi

  • testi.py testi.py
    6. januar 2025, 17:53
Trenutno uporabljate gostujoči dostop (Prijavite se)
Pridobi mobilno aplikacijo
Stran poganja Moodle
Obvestilo o avtorskih pravicah