Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Python: Kummallisia ongelmia

Sivun loppuun

Isopaha [28.04.2008 18:02:41]

#

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

ZcMander [28.04.2008 20:21:17]

#

Input-funktiota ei pitäisi tuossa kohden käyttää, vaan sen sijaan int(raw_input("Komento: "))

Luetaan sitä manuaalia

Isopaha [28.04.2008 22:11:26]

#

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"

pipo [29.04.2008 01:11:33]

#

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ä.

hunajavohveli [29.04.2008 09:50:26]

#

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.

Isopaha [29.04.2008 09:54:42]

#

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."

Isopaha [29.04.2008 11:50:59]

#

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?

pipo [30.04.2008 00:15:25]

#

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.

Isopaha [30.04.2008 12:01:38]

#

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"

pipo [30.04.2008 15:28:25]

#

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.

Isopaha [30.04.2008 16:15:02]

#

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.

Chiman [01.05.2008 08:32:49]

#

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'

Isopaha [01.05.2008 20:16:29]

#

Kiitos Chiman, nyt alkoi pelittämään!

Kiitos myös pipolle, ilman sinua olisin jättänyt tämän tekemättä.

Chiman [02.05.2008 14:28:29]

#

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()

Isopaha [02.05.2008 15:57:56]

#

Toimiihan se noinkin jos osaa :)

Isopaha [02.05.2008 23:37:53]

#

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!

Chiman [03.05.2008 09:04:40]

#

Laita riittävästi koodia näkyviin ja myös virheilmoitus kokonaisena, niin katsotaan mikä siinä mättää.

Isopaha [03.05.2008 10:48:59]

#

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

Gaxx [03.05.2008 11:15:49]

#

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.

Isopaha [03.05.2008 11:53:22]

#

Kuinkas tuo käytännössä sitten toimii? Kun mikäli ymmärsin, että jos pistän ne parametreiksi, muuttujista tulee taas paikallisia.

Chiman [03.05.2008 13:27:42]

#

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.

pipo [03.05.2008 16:05:40]

#

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

Pekka Karjalainen [03.05.2008 18:05:57]

#

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 :-)

Isopaha [03.05.2008 18:50:18]

#

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

Pekka Karjalainen [05.05.2008 09:48:35]

#

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.

Isopaha [05.05.2008 14:30:40]

#

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?


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta