Lähdin tuossa työstämään yksikertaista teksiroolipeliä ja kehittelin siihen taistelusysteemiä, jonka luulin osaavani tehdä. Itse peruskoodin väänettyäni ajoin sen ja tulos oli tälläinen:
UnboundLocalError: local variable 'komento' referenced before assignment
Olen yrittänyt etsiä netistä mikä tähän auttaisi, mutta en tunnu löytävän apua mistään ongelmaan.
Ohjelma tulostaa ensimmäiset print-komennot ja kaatuu sen jälkeen ilmoittaen em. lauseen.
Alhaalla lähdekoodi, haluaisin asian mieluiten selitettynä ja korjattuna jos vain mahdollista. Kiitos.
import random vihollinen_hp = 20 pelaaja_hp = 18 voima = 3 vih_voima = 2 sankari = 'sotilas' vihollinen = 'hirviö' kierros = 1 alotvuoro = 0 ottelu() def ottelu(): while vihollinen_hp or pelaaja_hp < 0: print "Valitse komento:" print "[1] Hyökkää" print "[2] Käytä tavaraa" komento = input("Komento: ") if komento == 1: random_isku = random.randint(1,10) plr_iskuvoima = random_isku+voima vihollinen_hp - plr_iskuvoima print sankari, "kohottaa aseensa ja iskee sillä vihollista." print "Isku tuottaa", plr_iskuvoima, "pisteen verran tuhoa ja siten vihollisen elinvoima alenee", vihollinen_hp, "pisteeseen." print random_isku = random.randint(1,10) vih_iskuvoima = random_isku+vih_voima pelaaja_hp - vih_iskuvoima print vihollinen, "valmistuu hyökkäykseen." print vihollinen, "hyökkää kohti ja tuottaa", vih_iskuvoima, "pistettä vahinkoa." print sankari, "kärsii ja hänen elinvoimansa alenee", pelaaja_hp, "pisteeseen." kierros+1 elif komento == 2: print "Jee" kierros+1
Input-funktiota ei pitäisi tuossa kohden käyttää, vaan sen sijaan int(raw_input("Komento: "))
Luetaan sitä manuaalia
Muokkasin hieman koodia ja nyt sain itse taisteluosuuden toimimaan, mutta nyt on sellainen ongelma, että vaikka muuttujat
vihollinen_hp tai pelaaja_hp) < 0
ei while komento lopu.. Mikähän tähän auttaisi?
import random vihollinen_hp = 20 pelaaja_hp = 18 voima = 3 vih_voima = 2 sankari = 'sotilas' vihollinen = 'hirviö' kierros = 1 alotvuoro = 0 while vihollinen_hp or pelaaja_hp) < 0: print "Kierros: ", kierros print print "Valitse komento:" print "[1] Hyökkää" print "[2] Käytä tavaraa" komento = int(raw_input("Komento: ")) if komento == 1: random_isku = random.randint(1,10) plr_iskuvoima = random_isku + voima vihollinen_hp = vihollinen_hp - plr_iskuvoima print sankari, "kohottaa aseensa ja iskee sillä vihollista." print "Isku tuottaa", plr_iskuvoima, "pisteen verran tuhoa ja siten vihollisen elinvoima alenee", vihollinen_hp, "pisteeseen." print random_isku = random.randint(1,10) vih_iskuvoima = random_isku + vih_voima pelaaja_hp = pelaaja_hp-vih_iskuvoima print vihollinen, "valmistuu hyökkäykseen." print vihollinen, "hyökkää kohti ja tuottaa", vih_iskuvoima, "pistettä vahinkoa." print sankari, "kärsii ja hänen elinvoimansa alenee", pelaaja_hp, "pisteeseen." kierros = kierros + 1 elif komento == 2: print "Jee" kierros+1 if vihollinen_hp < 0: print "Voitit" elif pelaaja_hp < 0: print "Hävisit"
Tossa ekassa virityksessä ainakin kutsutaan funktiota ottelu ennenkuin se on esitelty, niinkun näyt itsekkin huomanneesi. Tällöinhän funktion paikalliseen muuttujaan 'komento' on viitattu ennen määrittelyä, minkä voisi englanniksi kääntää vaikkapa: local variable 'komento' referenced before assignment.
Isopaha kirjoitti:
while vihollinen_hp or pelaaja_hp) < 0:
while -silmukkaan ei oikein missään vaiheessa mennä kun sen ehto ei ole epäilemättä tosi.
Jos pelaaja_hp kuvaa sankarimme hitpointseja niin väännä ihmeessä toi pienempikuinsuurempi -operaattori toisin päin. Tai käytä suurempieliyhtäpieni -operaattoria >=. Kai sitä nyt nolla taulussa kuolee.
alotvuoroa ei näy kuuluvan tuohon loppukoodiin.(?)
Edit: tosiaan whilestä puuttuu sulku ( . Ja en tiedä miten oot ton koodin liittänyt selaimeen, mutta ainakaan mulla ei toimi ilman sisennyksiä.
Mitä olet hakenut tuolla while-ehdolla? Milloin silmukan pitäisi loppua? Vailla parempaa tietämystä veikkaisin ainakin, että "while (vihollinen_hp or pelaaja_hp) < 0" ei ole Pythonissa sama asia kuin "while (vihollinen_hp < 0 or pelaaja_hp < 0)", jos tuota olet tarkoittanut. Tuossakin muodossa ehto tosin vaikuttaa kummalliselta.
Joo, tagit tosiaan söi jostain syystä todellisen koodin, syytä en tiedä..
Mutta siis mitä tarkoitat tuolla operaattorin toisinpäin kääntämisellä?
Jostain syystä tuo taistelu ei toimi funktiossa ollenkaan. Funktion sisällä hitpointsit eivät vähene ollenkaan, ainoastaan hyökkäyspisteet muuttuvat.
@hunajavohveli
Olen tarkoittanut sen toimivaksi näin:
"While ohitetaan mikäli pelaajan tai vihollisen hitpointsit (jotka löytyvät muuttujistaan) ovat vähemmän kuin 0."
Sori tuplaposti.
Hioin koodia vähän ja nyt tajusin mitä olinkin tehnyt. Loogisuushan siitä puuttui kun operaattori oli väärinpäin...
Mutta nyt on jäljellä tämä ongelma, että funktion sisällä koodi ei jostain syystä suostu laskemaan muuttujien välisiä laskuja. Mikähän tähän auttaisi?
hunajavohveli kirjoitti:
että "while (vihollinen_hp or pelaaja_hp) < 0" ei ole Pythonissa sama asia kuin "while (vihollinen_hp < 0 or pelaaja_hp < 0)"
Juuri näin. Lauseessa "(x or y) > 0" boolean or -operaattori palauttaa x:n ollessa tosi x:n arvon, eikä tarkista sattuiko y muuten olemaan tosi vai ei. x:n ollessa epätosi palautetaan y:n arvo. Isopahan koodissa or palauttaa joko 20 taikka 18. Näistä ensimmäistä eli vihollinen_hp (eli 20) verrataan sitten nollaan, jolloin ehto on tosi.
Lauseessa "(x > 0 or y > 0)" ensiksi vertaillaan onko muuttuja suurempi kuin vakio. Arvoilla 20 ja 18 saadaan (True or True). or palauttaa ensin mainitun operandin arvon sen ollessa tosi eli palauttaa True.
Noilta osin lauseet ovat saman arvoisia, mutta lauseen "(x or y) > 0" kanssa voi käydä niin että x < 0 ja y > 0. Tällöin or lause palauttaa siis x:n arvon joka on negatiivinen mutta tosi, ja sitten sitä vain verrataan nollaan vaikka y olisi ollut enemmän kuin nolla.
Itse käyttäisin or-operaattorin sijasta and-operaattoria, koska muuten vihollinen aiheuttaa sankarillemme vahinkoa kuoltuaankin, aina niin kauan että kummankin taistelijan hitpointsit ovat kuluneet alle nollan. Korjatkaa jos olen väärässä.
Isopaha kirjoitti:
hitpointsit eivät vähene ollenkaan, ainoastaan hyökkäyspisteet muuttuvat.
Kokeile tämmöstä:
komento = input('Anna komento: ') if komento == 1: vihollinen_hp -= voima + random.randint(1, 10)
ja sama toisin päin.
Isopaha kirjoitti:
print "Jee"
kierros+1
Tossa on kirjotuvirhe (kierros += 1).
Lisäksi raw inputin käyttäminen on turhaa. Tallennat käyttäjän syötteen (1 tai 2) merkkijonona ja muutat sen heti luvuksi.
Isopaha kirjoitti:
taistelu ei toimi funktiossa ollenkaan
Minusta taistelu ton funktion kanssa sujuu mallikkaasti mutta jos et saanut funktion sisäisiä muuttujien välisiä laskuja toimimaan, niin kerrothan miten hioit koodiasi.
Nyt muuttelin koodia ohjeittesi mukaan, jolloin taisteluosuus alkoi toimimaan. Nyt tosin vihollinen pystyy myös hyökkäämään vaikka hitpointsit olisivat jo nollan alapuolella.
Toinen ongelma on edelleen tuo funktio. Kun siirrän itse tämän koodin funktion sisälle, tulee tuttu ilmoitus jälleen:
UnboundLocalError: local variable 'vihollinen_hp' referenced before assignment
Onko ideoita kuinka funktion saisi toimimaan niinkuin pitäisi?
import random vihollinen_hp = 20 pelaaja_hp = 20 voima = 3 vih_voima = 2 sankari = 'sotilas' vihollinen = 'hirviö' kierros = 1 alotvuoro = 0 while (vihollinen_hp or pelaaja_hp) > 0: print "Kierros: ", kierros print print "Valitse komento:" print "[1] Hyökkää" print "[2] Käytä tavaraa" komento = input('Anna komento: ') if komento == 1: vihollinen_hp_nyt = vihollinen_hp vihollinen_hp -= voima + random.randint(1, 10) plr_iskuvoima = vihollinen_hp_nyt - vihollinen_hp print sankari, "kohottaa aseensa ja iskee sillä vihollista." print "Isku tuottaa", plr_iskuvoima, "pisteen verran tuhoa ja siten vihollisen elinvoima alenee", vihollinen_hp, "pisteeseen." print pelaaja_hp_nyt = pelaaja_hp pelaaja_hp -= vih_voima + random.randint(1, 10) vih_iskuvoima = pelaaja_hp_nyt - pelaaja_hp print vihollinen, "valmistuu hyökkäykseen." print vihollinen, "hyökkää kohti ja tuottaa", vih_iskuvoima, "pistettä vahinkoa." print sankari, "kärsii ja hänen elinvoimansa alenee", pelaaja_hp, "pisteeseen." kierros += 1 elif komento == 2: print "Jee" kierros += 1 if vihollinen_hp < 0: print "Voitit" elif pelaaja_hp < 0: print "Hävisit"
Python -virheiden ihmettelemisen peruskurssi 101:
Isopaha kirjoitti:
Toinen ongelma on edelleen tuo funktio. Kun siirrän itse tämän koodin funktion sisälle, tulee tuttu ilmoitus jälleen:
UnboundLocalError: local variable 'vihollinen_hp' referenced before assignment
Python Ohjelmointiopas LTY Liite 4: Tulkin virheilmoitusten tulkinta kirjoitti:
Virheilmoitus: UnboundLocalError
Mitä tarkoittaa: Viittasit muuttujaan, jolla
ei ole arvoa.
Mitä luultavasti tapahtui: Koetit käyttää tunnettua muuttujaa,
jolle ei ole määritelty arvoa
paikassa, jossa muuttujalla on oltava
yksiselitteinen arvo.
"referenced before assignment" == "viitattu ennen määrittelyä"
Määrittely == Annetaan muuttujalle nimi sekä arvo (tjsp. vappuhöpöä)
Virheselvityksessä varmaan kerrotaan millä rivillä virhe ensimmäisen kerran esiintyi? No millä?
Isopaha kirjoitti:
Nyt muuttelin koodia ohjeittesi mukaan ... vihollinen pystyy myös hyökkäämään vaikka hitpointsit olisivat jo nollan alapuolella.
Mites sen andyn kanssa?
while (vihollinen_hp and pelaaja_hp) > 0:
Edid: Tietysti tarkoitit että jos vihollinen sattuu menehtymään kesken funktion, sen ei pitäisi enää iskeä takaisin.. Tarkistetaan onko vihollisen hp yli nollan, sen jälkeen kun vihollista on kuritettu:
if vihollisen hp <= 0: break
Reilua on jos myös sankarin hitpointsit tarkistetaan samalla tavalla, mutta jos sankari oikeasti kuolee ja peli loppuu hänen hp:nsa mennessä nollaan tai alle, ei sankarin hp:n tarkistamiselle ole tarvetta.
Juuri noin tuon hitpoints jutun piti mennä, kiitos siitä.
Ja takaisin siihen funktioon. Ajettaessa konsoli ilmoittaa tämän:
File "**path**", line 15, in ottelu while vihollinen_hp or pelaaja_hp < 0: UnboundLocalError: local variable 'vihollinen_hp' referenced before assignment
Eli ilmeisesti funktio ei jostain syystä löydä arvoa vihollinen_hp muuttujalle vaikka se määritellään aikaisemmin koodissa.
Tuossa on kyse siitä, että ilman lisämäärittelyjä voit funktion sisällä muuttaa vain siellä määriteltyjen muuttujien arvoa.
Tuo vihollinen_hp on määritelty funktion ulkopuolella. Voit kyllä lukea sitä, mutta myöhemmin samassa funktiossa yrität muuttaa sitä. Arvon muuttaminen saa Pythonin ymmärtämään, että kyse on funktion sisäisestä muuttujasta. Tällöin jo aiempi vertailu antaa virheen, koska et ole antanut muuttujalle arvoa (funktion sisällä) ennen kuin yrität lukea sitä.
Ratkaisuna voit määritellä muuttujan globaaliksi global-avainsanalla funktion alussa. Esimerkki:
a = 1 def foo(): global a # ilman tätä vertailu tuottaa virheen if a < 2: a = 2 def bar(): # ei ongelmaa, globaalia vain luetaan if a < 2: print 'Pieni on'
Kiitos Chiman, nyt alkoi pelittämään!
Kiitos myös pipolle, ilman sinua olisin jättänyt tämän tekemättä.
Koska Isopaha taisi saada koodinsa haluttuun pisteeseen asti valmiiksi, laitan tähän oman toteutukseni samalla toiminnallisuudella kuin yllä.
#!/usr/bin/python # -*- coding: latin-1 -*- import random class Kuolema(Exception): def __init__(self, sanoma): self.sanoma = sanoma def __str__(self): return self.sanoma class Hahmo(object): def __init__(self, hp, voima, nimi): self.hp = hp self.voima = voima self.nimi = nimi def elossa(self): return self.hp > 0 def iske(self): return random.randint(1, 10) + self.voima def osuma(self, tuho): self.hp -= tuho def kayta_tavaraa(sankari, vihollinen): """Käytä tavaraa""" print 'Jee' def pelaa_kierros(sankari, vihollinen): """Hyökkää""" iskuvoima = sankari.iske() vihollinen.osuma(iskuvoima) print print sankari.nimi, 'kohottaa aseensa ja iskee sillä vihollista.' print 'Isku tuottaa', iskuvoima, 'pisteen verran tuhoa ja siten' print 'vihollisen elinvoima alenee', vihollinen.hp, 'pisteeseen.' if not vihollinen.elossa(): raise Kuolema('Voitit') iskuvoima = vihollinen.iske() sankari.osuma(iskuvoima) print print vihollinen.nimi, 'valmistuu hyökkäykseen.' print vihollinen.nimi, 'hyökkää kohti ja tuottaa', iskuvoima, \ 'pistettä vahinkoa.' print sankari.nimi, 'kärsii ja hänen elinvoimansa alenee', \ sankari.hp, 'pisteeseen.' if not sankari.elossa(): raise Kuolema('Hävisit') def kysy_valinta(): valinnat = {'1': pelaa_kierros, '2': kayta_tavaraa} print print 'Valitse komento:' for komento, toiminto in valinnat.iteritems(): print '[%s] %s' % (komento, toiminto.__doc__) while True: valinta = raw_input('Komento: ') try: return valinnat[valinta] except KeyError: continue def pelaa(): sankari = Hahmo(18, 3, 'Sotilas') vihollinen = Hahmo(20, 2, 'Hirviö') try: while True: toiminto = kysy_valinta() toiminto(sankari, vihollinen) except Kuolema, sanoma: print sanoma if __name__ == '__main__': pelaa()
Toimiihan se noinkin jos osaa :)
Nyt hajoaa kyllä pää tämän kanssa. Scripti toimi loistavasti. Sitten kun suljin Pythonin ja myöhemmin avasin lähdekoodin, virhettä alkoi taas pukata.
Ei tästä tule kyllä yhtään mitään kun taas UnboundLocalError tulee huolimatta siitä onko siinä Globalia vai ei!
Laita riittävästi koodia näkyviin ja myös virheilmoitus kokonaisena, niin katsotaan mikä siinä mättää.
No saamanne pitää:
Ensimmäinen koodi, josta kutsutaan noille vihollisille arvot. Tässä tapauksessa vihollisia on vain yksi, elikkäs tuo Rotta.
import taistelut def rotta(): vihollinen_hp = 20 #Viholliset hitpointsit vih_voima = 2 #Vihollisen voima vihollinen = 'Rotta' #Vihollisen nimi print vihollinen, "hyökkää kimppuusi" taistelut.ottelu() rotta()
Toinen on sitten tässä, itse taisteluosuus.
#!/usr/bin/python # -*- coding: iso-8859-15 -*- import random def ottelu(): global vihollinen_hp global pelaaja_hp while (vihollinen_hp or pelaaja_hp) > 0: print "Valitse komento:" print "[1] Hyökkää" print "[2] Käytä parannusta" komento = input('Anna komento: ') if komento == 1: vihollinen_hp_nyt = vihollinen_hp vihollinen_hp -= voima + random.randint(1, 10) plr_iskuvoima = vihollinen_hp_nyt - vihollinen_hp print sankari, "kohottaa aseensa ja iskee sillä vihollista." print "Isku tuottaa", plr_iskuvoima, "pisteen verran tuhoa ja siten vihollisen elinvoima alenee", vihollinen_hp, "pisteeseen." if vihollinen_hp <= 0: break print pelaaja_hp_nyt = pelaaja_hp pelaaja_hp -= vih_voima + random.randint(1, 10) vih_iskuvoima = pelaaja_hp_nyt - pelaaja_hp print vihollinen, "valmistuu hyökkäykseen." print vihollinen, "hyökkää kohti ja tuottaa", vih_iskuvoima, "pistettä vahinkoa." print sankari, "kärsii ja hänen elinvoimansa alenee", pelaaja_hp, "pisteeseen." elif komento == 2: print "Jee" if vihollinen_hp < 0: print "Voitit" elif pelaaja_hp < 0: print "Hävisit"
Ja kun sitten ajan tuon ensimmäisen koodin, konsoliin tulee tälläistä viestiä:
Rotta hyökkää kimppuusi Traceback (most recent call last): File "D:\Program Files\ActiveState Python\Lib\site-packages\pythonwin\pywin\framework\scriptutils.py", line 310, in RunScript exec codeObject in __main__.__dict__ File "F:\Projektit\Tekstirope\säätö.py", line 13, in <module> rotta() File "F:\Projektit\Tekstirope\säätö.py", line 10, in rotta taistelut.ottelu() File "F:\Projektit\Tekstirope\taistelut.py", line 10, in ottelu while (vihollinen_hp or pelaaja_hp) > 0: NameError: global name 'vihollinen_hp' is not defined
Viimeinen rivi paljastaa ongelman: muuttujaa 'vihollinen_hp' ei ole määritelty. Eli välitähän tarvittavat muuttujat parametreina ottelu()-funktiolle tai huolehdi muuten, että muuttujat näkyvät ko. funktiossa.
Kuinkas tuo käytännössä sitten toimii? Kun mikäli ymmärsin, että jos pistän ne parametreiksi, muuttujista tulee taas paikallisia.
Niin, jos välität kokonaisluku- ja merkkijonomuuttujat funktiolle parametreina, niihin tehdyt muutokset eivät välity takaisin funktion ulkopuolelle, mutta koodisi nykymuodossa muutosten välittymiselle ei näyttäisi olevan tarvettakaan.
While-ehto sinulla on edelleen väärin. Oikea muoto sille löytyy esim. hunajavohvelin viestistä.
Edit: Korjasin sanamuotoa.
Kun säätö.py -koodia aletaan kääntämään, niin siinähän ensi töiksi esitellään toi taistelut.py, jossa on seuraava rivi:
while (vihollinen_hp or pelaaja_hp) > 0:
Muuttujalle vihollinen_hp annetaan arvo (20) vasta myöhemmin käännettävässä rotta-funktiossa.
pipo kirjoitti:
Mitä luultavasti tapahtui: Koetit käyttää tunnettua muuttujaa,
jolle ei ole määritelty arvoa
paikassa, jossa muuttujalla on oltava
yksiselitteinen arvo.
Korjatkaa joku viisaampi käyttämäni termit jos tarvii, mutta eikös tuossa tosta ole kyse. (?)
(pelaaja_hp:n arvon olet kai määritellyt jossain muualla, ei sattunut silmääni.)
e:En väitä että aikaisemmat viestit olisi väärin, mutta jos tää olisi yksinkertaisemmin kerrottu
Yritin uusintaa Isopahan viimeisintä ohjelmaa niin, että se toimii. Käytän oliota tiedon välittämiseen funktioon. Näin voi Isopahakin tehdä mallin mukaan jatkossa ja joskus opetella, miten olioita voit käyttää Pythonissa tyylikkäämmin.
Yksinkertaisin luokan määrittely Pythonissa ei sisällä mitään (paitsi pass-sanan), koska oliolle voi luoda kenttiä ja laittaa niihin arvoja muualla. (C++-ohjelmoijat kauhistuvat tästä...)
Kenttiin viitataan merkinnällä olio.kentän_nimi . Ne ova tmuuttujia siinä missä muutkin.
# -*- coding: latin-1 -*- import taistelut def rotta(): vihu = taistelut.Vihollinen () # luomme uuden vihollisen vihu.hp = 20 #Viholliset hitpointsit vihu.voima = 2 #Vihollisen voima vihu.nimi = 'Rotta' #Vihollisen nimi print vihu.nimi, "hyökkää kimppuusi" taistelut.ottelu(vihu) rotta()
taistelut.py:
# -*- coding: latin-1 -*- import random class Vihollinen (): pass def ottelu(vihu): pelaaja_hp = 20 pelaaja_voima = 3 sankari = "Ylevä Eemeli" voittaja = '' while 1: # toistetaan kunnes tulee break print "Valitse komento:" print "[1] Hyökkää" print "[2] Käytä parannusta" komento = input('Anna komento: ') if komento == 1: vahinko = pelaaja_voima + random.randint(1, 10) vihu.hp -= vahinko print sankari, "kohottaa aseensa ja iskee sillä vihollista." print "Isku tuottaa", vahinko, "pisteen verran tuhoa ja siten vihollisen elinvoima alenee", vihu.hp, "pisteeseen." if vihu.hp <= 0: voittaja = sankari break print vahinko = vihu.voima + random.randint(1, 10) pelaaja_hp -= vahinko print vihu.nimi, "valmistuu hyökkäykseen." print vihu.nimi, "hyökkää kohti ja tuottaa", vahinko, "pistettä vahinkoa." print sankari, "kärsii ja hänen elinvoimansa alenee", pelaaja_hp, "pisteeseen." if pelaaja_hp <= 0: voittaja = vihu.nimi break elif komento == 2: print "Jee" # while loppuu, eli joku on kuukahtanut print "Taiston voitti", voittaja # tässä voisi palauttaa voittajatiedon tai vihu-olion (tai molemmat)
Nyt Isopaha voi harjoituksena tehdä pelaajastakin olion :-)
Hmm.. Tuo olio-rakenne selvästi tekee koodista helpommin toimivamman, mutta olisiko vielä liikaa pyydetty Pekka, jos selittäisit minulle mitä tuo luokka tekee varsinaisesti tekee tuossa koodissa.
I know, oon pölkky : D
Anteeksi viivästys vastauksessa. Luokka Vihollinen tässä koodissa ei tee juuri mitään itse. Se on vain eräänlainen loota, johon voi laittaa arvoja ja päivittää niitä. Eräänlainen nimiavaruus siis.
Normaalisti olio-ohjelmoinnissa luokka sisältää enemmän määrittelyjä. Se on ikäänkuin resepti, joka kertoo, miten olioita voi luoda ja käsitellä. Lisäksi oikeassa ohjelmassa on useampia luokkia, jotka muodostavat toivottavasti järkevän hierarkian. Esimerkkinä pelissäsi voisi olla kantaluokka Vihollinen, josta olisi peritty Rotta, Hirvi, Ankerias ja Jellona, jotka olisivat erilaisia vihollisia.
Tämä kertoisi sekä ohjelma lukijalle, että ahaa, nämä asiat liittyvät toisiinsa, että auttaisi järjestämään koodia niin, että sitä olisi helppo muokata jatkossakin. Uuden luokan lisääminen on hyvin tehdyssä ohjelmassa helppoa. Koko koodia ei tarvitsisi uusia hyvässä tapauksessa, jos haluaisimmenkin vihollisen nimeltä Mäyrä mukaan kuvioihin.
Oliot ovat taas ohjelman ajon aikana olemassa olevia luokan esiintymiä. Joku tietty rotta, joka tulee luolassa vastaan on Rotta-luokan olio. Se olisi voinut syntyä vaikkapa ns. satunnaisen kohtaamisen seurauksena pelissä, ja olisi luotu siis tarpeen mukaan. Voipa niitä olioita tallentaa tiedostoonkin, mikä Pythonissa on tehty erityisen helpoksi. Esim. moduuli nimeltä pickle sisältää toimintoja tähän.
No, miten tämä auttaa sinua nyt, kun et osaa Python-tyylisen OO:n hienouksia? Kyllä se auttaa. Aina kun haluat ohjelmaasi jonkin monimutkaisen asian, jolle itse osaat antaa nimen, tee siitä luokka. Aluksi määrittely voi todellakin olla pelkkä pass. Pythonissa perintä (eli ne hierarkiat) ei ole niin tärkeässä osassa kuin joissakin kielissä, joten sitä ei tarvitse heti ottaa käyttöön.
Olennaista on muistaa, että jokaisen luokkaa käyttävän koodinpätkän ei tulisi tietää tarkalleen, miten luokka on toteutettu, vaan käyttää sitä selkeän rajapinnan kautta.
Tehdessäsi ohjelmaasi siis tavallaan voit sopia itsesi kanssa, että Vihollinen toimii näin:
sillä on terveys, eli kenttä nimeltä hp
sillä on hyökkäysvoimi, eli kenttä nimeltä voima
sillä on nimi; kentän nimi on myös nimi
Jos myöhemmin lisäät Vihollinen-olioon ominaisuuksia, voit tämä sopimus pitää silti paikkansa, ja vanha koodi käyttää Vihollinen-olioita samalla tavalla. Silloin ei koko ohjelmaa tarvitse tehdä uudestaan, vaikka sitä laajentaa jostakin kohdasta.
Jos haluat miettiä asiaa harjoitusmielessä, voit pohtia tulisiko taistelusta hyvä Taistelu-luokka. Luokissa voi olla metodejakin, eli Taistelu-luokkaan voi laittaa funktion, jonka nimi olisi vaikka suorita_kierros(), ja muitakin.
Sitten koodin voisi rakentaa tällaisen idean ympärille:
matsi = Taistelu () # uusi taistelu # laita taistelijatiedot matsiin tässä välissä while not matsi.ohi(): # matsi.suorita_kierros() matsi.kerro_tilanne() # matsin jälkeiset jutut
Jos tarvitset apua luokan metodien määrittelyssä, se kyllä neuvotaan. Siinä on omat tapansa. Juttuni taitaa venyä, joten en halua antaa nyt esimerkkiä.
Luokkien tarkoitus on siis jäsentää ohjelmasi rakennetta, jotta siitä selviää ihmisille, mitä asioita se käsittelee ja miten. Koodia pitää myös lukea; se ei ole vain koneille. Joudut itsekin tulevaisuudessa lukemaan omaa koodiasi, kun haluat lisätä ohjelmaasi toimintoja.
Kiitos Pekka, tämä selvensi huomattavasti ymmärrystäni olioista. Itse opin Pythonin koulussa, jossa meille ei juurikaan olioita selitetty.
Vielä sen verran kyselisin tuosta taistelu-esimerkistä, että suoritetaanko nuo
matsi.suorita_kierro() ja
matsi.kerro_tilanne()
ihan omissa funktioissaan (suorita_kierros(matsi) & kerro_tilanne(matsi)?
Ja pistetäänkö em. funktiot Taistelu luokan alle?
Aihe on jo aika vanha, joten et voi enää vastata siihen.