Objektni minoboti
Testi
Testi: testi-objektni-minoboti.py
Naloga
Napiši razred Minobot
. Ta sicer ne bo imel več nobene zveze z minami, imel pa bo zvezo z nalogo Minobot, ki smo jo reševali pred časom.
Minobot se v začetku nahaja na koordinatah (0, 0) in je obrnjen na desno. Koordinatni sistem je takšen kot pri matematiki: koordinata y narašča navzgor.
Razred Minobot
ima naslednje metode.
naprej(d)
gre zad
naprej v podani smeri;desno()
se obrne za 90 stopinj v desno;levo()
se obrne za 90 stopinj levo;koordinate()
vrne trenutne koordinate (x in y)razdalja()
vrne pravokotno razdaljo do koordinat (0, 0): če se robot nahaja na (5, -3), je razdalja do (0, 0) enaka 8.
Če, recimo izvedemo
a = Minobot()
a.levo()
a.naprej(4)
a.desno()
a.naprej(3)
print(a.koordinate())
se izpiše (3, 4)
.
Rešitev
Robot bo imel tri atribute self.x
in self.y
bosta koordinati, self.smer
pa smer. Kako bomo shranili koordinate, naloga bolj ali manj določa: pravi, naj y narašča navzgor, zato bo najbolj praktično, če ga tudi v razredu shranjujemo tako.
Glede tega, kako naj shranimo smer, naloga ne zahteva ničesar. Lahko shranimo kot v stopinjah, kot v radianih, smer neba s črko ali besedo v angleščini ali slovenščini, lahko shranjujemo z znaki v, ^, < in >... Kakor želimo. Storili bomo takle: 0 bo sever, 1 vzhod, 2 jug in 3 zahod. To nam zelo poenostavi metodi desno
in levo
.
class Minobot:
def __init__(self):
self.x = self.y = 0
self.smer = 1
def desno(self):
self.smer = (self.smer + 1) % 4
def levo(self):
self.smer = (self.smer - 1) % 4
def naprej(self, d):
if self.smer == 0:
self.y += d
elif self.smer == 1:
self.x += d
elif self.smer == 2:
self.y -= d
else:
self.x -= d
def koordinate(self):
return self.x, self.y
def razdalja(self):
return abs(self.x) + abs(self.y)
Za primer krajše rešitve povejmo, da pozna Python tudi kompleksna števila. Po inženirski navadi za i je uporabljamo i temveč j. Tako bi 1 + 2i zapisali z 1 + 2j
. Če želimo le i, moramo napisati 1j
, saj bi sam j
pomenil spremenljivko j
.
class Minobot:
def __init__(self):
self.pozicija = 0
self.smer = 1
def desno(self):
self.smer *= -1j
def levo(self):
self.smer *= 1j
def naprej(self, d):
self.pozicija += d * self.smer
def koordinate(self):
return int(self.pozicija.real), int(self.pozicija.imag)
def razdalja(self):
x, y = self.koordinate()
return abs(x) + abs(y)
Zdaj je self.smer
kar "vektor" v kompleksnem - enaka bo 1, -i, -1 in 1 (desno, dol, levo in gor). Obe koordinati bosta shranjeni v enem samem kompleksnem številu self.pozicija
; metoda naprej
ga spremeni tako, da se prestavi za podano razdaljo (d
) v trenutni smeri.
Zapleteta (pa ne preveč) se le koordinate
in razdalja
, ki morata iz pozicije pobrati realno in imaginarno komponento.
Toliko, da veste, zakaj se splača poslušati pri matematiki.
Dodatna naloga
Dodaj metodo razveljavi()
, ki razveljavi zadnji ukaz naprej
, levo
ali desno
. Lahko jo pokličemo tudi večkrat. (Če jo pokličemo tolikokrat, da ni več česa razveljavljati, ne naredi ničesar.)
Če gornji program nadaljujemo z
a.razveljavi()
a.razveljavi()
a.naprej(2)
print(a.koordinate())
se izpiše (0, 6)
, saj smo razveljavili zadnja ukaza naprej
in desno
.
Če nadaljujemo z
a.razveljavi()
print(a.koordinate())
se izpiše (0, 4)
, saj smo razveljavili naprej(2)
.
Rešitev
Undo najlažje naredimo tako, da si zapomnimo vsa pretekla stanja. (Pravi undo pa je pogosto narejen tako, da si ob izvedbi akcije zapomnimo nasprotno akcijo. Mnogi ste dejansko reševali tako - to je sicer pohvalno, vendar je pri tej nalogi težje.)
Pretekla stanja bomo shranjevali v self.stanja
: to bo seznam trojk (self.x, self.y, self.smer)
. Metoda, ki jo zahteva naloga, razveljavi
, bo le nastavila self.x
, self.y
in self.smer
na zadnje shranjeno stanje. Poleg tega pa bomo napisali še metodo shrani_stanje
, ki bo dodala trojko v seznam. Metodo bomo poklicali pred izvedbo vsake akcije, torej na začetku metod desno
, levo
in naprej
.
class Minobot:
def __init__(self):
self.x = self.y = 0
self.smer = 1
self.stanja = []
def shrani_stanje(self):
self.stanja.append((self.x, self.y, self.smer))
def razveljavi(self):
if self.stanja:
self.x, self.y, self.smer = self.stanja.pop()
def desno(self):
self.shrani_stanje()
self.smer = (self.smer + 1) % 4
def levo(self):
self.shrani_stanje()
self.smer = (self.smer - 1) % 4
def naprej(self, d):
self.shrani_stanje()
if self.smer == 0:
self.y += d
elif self.smer == 1:
self.x += d
elif self.smer == 2:
self.y -= d
else:
self.x -= d
def koordinate(self):
return self.x, self.y
def razdalja(self):
return abs(self.x) + abs(self.y)
Najpomembnejši nauk te zgodbe je, da mora imeti vsak robot svoj undo, zato ga je potrebno shraniti v self
in ne v globalno spremenljivko, kot ste počeli nekateri. Globalne spremenljivke so slaba ideja.