PDA

View Full Version : [Python] Ereditarietà e gioco Old Maid


~FullSyst3m~
10-12-2008, 19:15
Apro un nuovo topic dato che l'altro sul libro in generale ormai e poco controllato. Ecco in breve cosa mi interesserebbe sapere, il libro che sto studiando negli ultimi capitoli ha un sacco di errori e ho passato più tempo a scovarli e correggerli che a concentrarmi sullo studio vero e proprio. Per prima cosa posto il codice, cosi da avere le idee chiare:

class Carta:
ListaSemi = ["Fiori", "Quadri", "Cuori", "Picche"]
ListaRanghi = ["impossibile", "Asso", '2', '3', '4', '5', '6', '7', '8', \
"9", "10", "Jack", "Regina", "Re"]

def __init__(self, Seme=0, Rango=0):
self.Seme = Seme
self.Rango = Rango
def __str__(self):
return (self.ListaRanghi[self.Rango] + " di " + self.ListaSemi[self.Seme])
def __cmp__(self, Altro):
#controlla il seme
if self.Seme > Altro.Seme: return 1
if self.Seme < Altro.Seme: return -1
#controlla il rango
if (self.Rango == 1) and (Altro.Rango != 1): return 1
if (self.Rango != 1) and (Altro.Rango == 1): return -1
if self.Rango > Altro.Rango: return 1
if self.Rango < Altro.Rango: return -1
#se anche il rango ? uguale allora le carte sono uguali
return 0

class Mazzo:
def __init__(self):
self.Carte = []
for Seme in range(4):
for Rango in range(1, 14):
self.Carte.append(Carta(Seme, Rango))
def StampaMazzo(self):
for Carta in self.Carte:
print Carta

def __str__(self):
s = ""
for i in range(len(self.Carte)):
s = s + ""*i + str(self.Carte[i]) + '\n'
return s

def Mescola(self):
import random
NumCarte = len(self.Carte)
for i in range(NumCarte):
j = random.randrange(i, NumCarte)
self.Carte[i], self.Carte[j] = self.Carte[j], self.Carte[i]

def RimuoviCarta(self, Carta):
if Carta in self.Carte:
self.Carte.remove(Carta)
return 1
else:
return 0

def PrimaCarta(self):
return self.Carte.pop()

def EVuoto(self):
return(len(self.Carte) == 0)

def Distribuisci(self, ListaMani, NumCarte=999):
NumMani = len(ListaMani)
for i in range(NumCarte):
if self.EVuoto(): break
Carta = self.PrimaCarta()
Mano = ListaMani[i % NumMani]
Mano.AggiungiCarta(Carta)

class Mano(Mazzo):
def __init__(self, Nome=""):
self.Carte = []
self.Nome = Nome

def AggiungiCarta(self,Carta):
self.Carte.append(Carta)

def __str__(self):
s = "La mano di " + self.Nome
if self.EVuoto():
s = s + " e' vuota\n"
else:
s = s + " contiene queste carte: \n"
return s + Mazzo.__str__(self)

class GiocoDiCarte:
def __init__(self):
self.Mazzo = Mazzo()
self.Mazzo.Mescola()

class ManoOldMaid(Mano):
def RimuoviCoppie(self):
Conteggio = 0
CarteOriginali = self.Carte[:]
for CartaOrig in CarteOriginali:
CartaDaCercare = Carta(3-CartaOrig.Seme, CartaOrig.Rango)
if CartaDaCercare in self.Carte:
self.Carte.remove(CartaOrig)
self.Carte.remove(CartaDaCercare)
print "Mano di %s : %s elimina %s" % (self.Nome, CartaOrig, CartaDaCercare)
Conteggio += 1
return Conteggio

class GiocoOldMaid(GiocoDiCarte):

def StampaMani(self):
for Mano in self.Mani:
print Mano

def RimuoveTutteLeCoppie(self):
Conteggio = 0
for Mano in self.Mani:
Conteggio = Conteggio + Mano.RimuoviCoppie()
return Conteggio

def TrovaVicino(self, Giocatore):
NumMani = len(self.Mani)
for Prossimo in range(1, NumMani):
Vicino = (Giocatore + Prossimo) % NumMani
if not self.Mani[Vicino].EVuoto():
return Vicino

def GiocaUnTurno(self, Giocatore):
if self.Mani[Giocatore].EVuoto():
return 0
Vicino = self.TrovaVicino(Giocatore)
CartaScelta = self.Mani[Vicino].PrimaCarta()
self.Mani[Giocatore].AggiungiCarta(CartaScelta)
print "Mano di ", self.Mani[Giocatore].Nome, ": scelta ", CartaScelta
Conteggio = self.Mani[Giocatore].RimuoviCoppie()
self.Mani[Giocatore].Mescola()
return Conteggio

def Partita(self, Nomi):
self.Mazzo.RimuoviCarta(Carta(0, 12)) # Rimozione della regina di fiori
self.Mani = [] # Creazione di una mano per ogni giocatore
for Nome in Nomi:
self.Mani.append(ManoOldMaid(Nome))
self.Mazzo.Distribuisci(self.Mani) # Distribuzione delle carte
print "----------- Le carte sono state distribuite -----------"
self.StampaMani()
NumCoppie = self.RimuoveTutteLeCoppie() # Toglie le coppie iniziali
print "----------- Coppie scartate, inizia la partita -----------"
self.StampaMani()
Turno = 0 # Gioca finche' non sono state fatte 25 coppie
NumMani = len(self.Mani)
while NumCoppie < 25:
Turno = (Turno + 1) % NumMani
NumCoppie = NumCoppie + self.GiocaUnTurno(Turno)
print "----------- La partita e' finita -----------"
self.StampaMani()

Gioco = GiocoOldMaid()
Gioco.Partita(["Full", "Syst3m", "Io"])

e questo è il capitolo interessato http://www.python.it/doc/Howtothink/Howtothink-html-it/chap16.htm

Adesso a noi:

1) ho provato a "runnare" ripetutamente il programma e alle volte viene eseguito senza problemi fino alla fine, altre dà un errore di indice (rilevato nella riga di codice in grassetto) e altre ancora si ferma al Nooooooooooooooooooooooooooooooo e finisce l'esecuzione.

2)

Mano = ListaMani[i % NumMani]
Mano.AggiungiCarta(Carta)

Apparte che credo che l'approccio di insegnamento del libro in questi ultimi capitoli è degradato e apparte che crea un casino bestiale mettere una variabile con lo stesso nome di una classe, non capisco come mai ad AggiungiCarta viene passato come parametro self Mano che è una variabile? Probabilmente perchè il contenuto di quella variabile è un oggetto Mano, ma allora la domanda è: dove è stato creato questo oggetto Mano? Forse qua dato che ManoOldMaid eredita da Mano?

self.Mani.append(ManoOldMaid(Nome))

3) se si usava Mano(Nome) non era la stessa cosa? la classe ManoOldMaid è stata usata per poter poi sfruttare in un successivo momento il metodo al suo interno (RimuoviCoppie)?


P.S: credo che sia cosi controllando passo per passo, ma vorrei una conferma: l'ereditarietà funziona come una scatola dentro un'altra in pratica. Quindi ManoOldMaid oltre ad ereditare da Mano eredita anche da Mazzo visto che Mano eredita a sua volta da Mazzo?

cdimauro
11-12-2008, 21:36
Apro un nuovo topic dato che l'altro sul libro in generale ormai e poco controllato. Ecco in breve cosa mi interesserebbe sapere, il libro che sto studiando negli ultimi capitoli ha un sacco di errori e ho passato più tempo a scovarli e correggerli che a concentrarmi sullo studio vero e proprio.
Guarda che è molto più difficile trovare un errore, perché ciò richiede necessariamente che tu abbia una buona comprensione dei concetti. ;)
Per prima cosa posto il codice, cosi da avere le idee chiare:
[...]
e questo è il capitolo interessato http://www.python.it/doc/Howtothink/Howtothink-html-it/chap16.htm

Adesso a noi:

1) ho provato a "runnare" ripetutamente il programma e alle volte viene eseguito senza problemi fino alla fine, altre dà un errore di indice (rilevato nella riga di codice in grassetto) e altre ancora si ferma al Nooooooooooooooooooooooooooooooo e finisce l'esecuzione.
Il codice mi sembra a posto. Quando hai un errore, riporta anche il traceback completo: magari c'è qualche informazione in più.
2)

Mano = ListaMani[i % NumMani]
Mano.AggiungiCarta(Carta)

Apparte che credo che l'approccio di insegnamento del libro in questi ultimi capitoli è degradato e apparte che crea un casino bestiale mettere una variabile con lo stesso nome di una classe,
In effetti avrebbe potuto usare un nome diverso.
non capisco come mai ad AggiungiCarta viene passato come parametro self Mano che è una variabile?
Veramente ad AggiungiCarta viene passata sempre una carta, da quel che vedo.
Probabilmente perchè il contenuto di quella variabile è un oggetto Mano, ma allora la domanda è: dove è stato creato questo oggetto Mano?
Riporta il pezzo di codice, perché non è chiaro: io vedo che gli viene passato un parametro di tipo Carta.
Forse qua dato che ManoOldMaid eredita da Mano?

self.Mani.append(ManoOldMaid(Nome))
No, qui vengono semplicemente creati i vari mazzi.
3) se si usava Mano(Nome) non era la stessa cosa?
No, perché è una classe diversa.
la classe ManoOldMaid è stata usata per poter poi sfruttare in un successivo momento il metodo al suo interno (RimuoviCoppie)?
Esattamente.
P.S: credo che sia cosi controllando passo per passo, ma vorrei una conferma: l'ereditarietà funziona come una scatola dentro un'altra in pratica. Quindi ManoOldMaid oltre ad ereditare da Mano eredita anche da Mazzo visto che Mano eredita a sua volta da Mazzo?
Sì.

~FullSyst3m~
11-12-2008, 23:14
Guarda che è molto più difficile trovare un errore, perché ciò richiede necessariamente che tu abbia una buona comprensione dei concetti. ;)

Allora stando a questo dovrei avere una buona comprensione dei concetti visto che dal 13 ad ora ho trovato parecchi errori per essere un libro visto e rivisto, corretto e ricorretto

Il codice mi sembra a posto. Quando hai un errore, riporta anche il traceback completo: magari c'è qualche informazione in più.

Ho trovato il bug nel codice: era sbagliata l'indentazione dell'if nel metodo TrovaVicino, ora è tutto ok

In effetti avrebbe potuto usare un nome diverso.

Infatti

Veramente ad AggiungiCarta viene passata sempre una carta, da quel che vedo.

Come secondo parametro, ma come parametro self il metodo AggiungiCarta vuole un oggetto Mano, infatti viene chiamata con Mano.AggiungiCarta(Carta). Mano sarebbe la variabile Mano = ListaMani[i % NumMani]

Riporta il pezzo di codice, perché non è chiaro: io vedo che gli viene passato un parametro di tipo Carta.

Stessa cosa di sopra

No, qui vengono semplicemente creati i vari mazzi.

ManoOldMaid eredita da Mano, quindi essendo state aggiunte le varie mani con questa classe gli oggetti che sono dentro la lista sono del tipo Mano. E' l'unico posto che vedo dove possono essere creati oggetti Mano, altrimenti non mi spiego come AggiungiCarta sia stato invocato sulla variabile Mano

No, perché è una classe diversa.

Eredita da Mano però, quindi ha tutti i metodi, compreso __init__, di Mano. Credo che se al posto di ManoOldMaid(Nome), si scriveva Mano(Nome) era la stessa cosa dato che ManoOldMaid eredita da Mano, solo che ManoOldMaid è stato usato per poter poi usufruire del metodo al suo interno

cdimauro
14-12-2008, 08:19
Allora stando a questo dovrei avere una buona comprensione dei concetti visto che dal 13 ad ora ho trovato parecchi errori per essere un libro visto e rivisto, corretto e ricorretto
Esattamente.
Come secondo parametro, ma come parametro self il metodo AggiungiCarta vuole un oggetto Mano, infatti viene chiamata con Mano.AggiungiCarta(Carta). Mano sarebbe la variabile Mano = ListaMani[i % NumMani]
Ah, ok, ho capito. E' vero che self è il primo parametro di un metodo, ma per i metodi lo "standard" è di non considerarlo come parametro vero e proprio, visto che in una chiamata del tipo self.NomeMetodo(Parametro1) il suo passaggio è implicito.

Per risponderti, Mano è una variabile, ma contiene appunto un'istanza della classe Mano:

Mano = ListaMani[i % NumMani]

ListaMani è una lista di istanze di mani, per cui con questa istruzione tu ne selezioni una.

Visto che Mano contiene un'istanza di mani, allora può benissimo invocare il metodo AggiungiCarta.
ManoOldMaid eredita da Mano, quindi essendo state aggiunte le varie mani con questa classe gli oggetti che sono dentro la lista sono del tipo Mano. E' l'unico posto che vedo dove possono essere creati oggetti Mano, altrimenti non mi spiego come AggiungiCarta sia stato invocato sulla variabile Mano
Hai centrato il punto. :)
Eredita da Mano però, quindi ha tutti i metodi, compreso __init__, di Mano. Credo che se al posto di ManoOldMaid(Nome), si scriveva Mano(Nome) era la stessa cosa dato che ManoOldMaid eredita da Mano, solo che ManoOldMaid è stato usato per poter poi usufruire del metodo al suo interno
Sì, esatto. Attenzione però: Python chiama l'ULTIMO metodo __init__ definito nella gerarchia di classi.

Quindi per ManoOldMaid, visto che non ha definito alcun metodo __init__, verrà chiamato il metodo __init__ della classe da cui eredita, e SOLO QUELLO.

Se per caso ManoOldMaid ridefinisse il metodo __init__, allora NON verrebbe più chiamato nessun altro metodo __init__ di nessuna altra classe da cui essa deriva.

Questo comporta due cose: o ManoOldMaid si occupa di effettuare tutte le inizializzazioni eseguite nelle classi da cui deriva, oppure a sua volta dentro il suo __init__ provvede a chiamare l'__init__ del classe da cui deriva.

~FullSyst3m~
14-12-2008, 12:16
Esattamente.

Ah, ok, ho capito. E' vero che self è il primo parametro di un metodo, ma per i metodi lo "standard" è di non considerarlo come parametro vero e proprio, visto che in una chiamata del tipo self.NomeMetodo(Parametro1) il suo passaggio è implicito.

Per risponderti, Mano è una variabile, ma contiene appunto un'istanza della classe Mano:

Mano = ListaMani[i % NumMani]

ListaMani è una lista di istanze di mani, per cui con questa istruzione tu ne selezioni una.

Visto che Mano contiene un'istanza di mani, allora può benissimo invocare il metodo AggiungiCarta.

Hai centrato il punto. :)

Sì, esatto. Attenzione però: Python chiama l'ULTIMO metodo __init__ definito nella gerarchia di classi.

Quindi per ManoOldMaid, visto che non ha definito alcun metodo __init__, verrà chiamato il metodo __init__ della classe da cui eredita, e SOLO QUELLO.

Se per caso ManoOldMaid ridefinisse il metodo __init__, allora NON verrebbe più chiamato nessun altro metodo __init__ di nessuna altra classe da cui essa deriva.

Questo comporta due cose: o ManoOldMaid si occupa di effettuare tutte le inizializzazioni eseguite nelle classi da cui deriva, oppure a sua volta dentro il suo __init__ provvede a chiamare l'__init__ del classe da cui deriva.


Scusa se ti ho fatto perdere tempo, ma avevo già trovato risposta a tutto. Ho dimenticato di postare perchè sto capitolo mi ha fatto perdere un sacco di tempo, era scritto proprio con i piedi. Solo una cosa vorrei aggiungere:

Sì, esatto. Attenzione però: Python chiama l'ULTIMO metodo __init__ definito nella gerarchia di classi.

Quindi per ManoOldMaid, visto che non ha definito alcun metodo __init__, verrà chiamato il metodo __init__ della classe da cui eredita, e SOLO QUELLO.

Se per caso ManoOldMaid ridefinisse il metodo __init__, allora NON verrebbe più chiamato nessun altro metodo __init__ di nessuna altra classe da cui essa deriva.

Questo comporta due cose: o ManoOldMaid si occupa di effettuare tutte le inizializzazioni eseguite nelle classi da cui deriva, oppure a sua volta dentro il suo __init__ provvede a chiamare l'__init__ del classe da cui deriva.

Credo che non abbia senso scrivere un __init__ e al suo interno chiamarne un altro. Tanto vale usare l'init della classe genitore. Inoltre, anche se ManoOldMaid avesse il suo metodo di inizializzazione, tutti i metodi presenti nella classe Mano sarebbero ereditati ugualmente. Queste poi sono scelte che fa il programmatore in base alle proprie esigenze e in base a come vuole strutturare il programma

cdimauro
15-12-2008, 08:17
Richiamare l'__init__ (in gergo tecnico nella OOP viene chiamato "costruttore") delle classi da cui si discende è, invece, estremamente utile e pratica molto comune.

Quando avrai affinato le tue conoscenze della programmazione OOP lo capirai meglio. ;)

~FullSyst3m~
15-12-2008, 12:31
Richiamare l'__init__ (in gergo tecnico nella OOP viene chiamato "costruttore") delle classi da cui si discende è, invece, estremamente utile e pratica molto comune.

Quando avrai affinato le tue conoscenze della programmazione OOP lo capirai meglio. ;)

Al momento non ne vedo l'utilità sinceramente. Se si deve invocare il costruttore della classe genitore basta non definire un metodo di inizializzazione per la classe figlia cosi viene invocato in automatico quello della classe genitore. Come ManoOldMaid ad esempio

cdimauro
15-12-2008, 14:09
L'utilità c'è se vuoi cambiare l'inizializzazione dell'oggetto, mantenendo però il setup che è previsto dalla classe genitrice.

Cosa che, ripeto, è abbastanza frequente lavorando con la OOP. Vedrai che ti capiterà. ;)

~FullSyst3m~
15-12-2008, 14:19
L'utilità c'è se vuoi cambiare l'inizializzazione dell'oggetto, mantenendo però il setup che è previsto dalla classe genitrice.

Cosa che, ripeto, è abbastanza frequente lavorando con la OOP. Vedrai che ti capiterà. ;)

In pratica potrebbe servire nel caso voglio scrivere un metodo di inizializzazione di una classe figlia, che però ha bisogno dell'__init__ della classe genitrice oltre a quello proprio? Ad esempio inserire nell'init della classe figlia il codice che serve e in più l'init della classe genitrice?

cdimauro
15-12-2008, 14:30
Esatto. In genere il lavoro di inizializzazione della classe genitrice è utile e andrebbe fatto comunque. Oltre a questo, la classe figlia può voler effettuare ALTRE inizializzazioni.

~FullSyst3m~
15-12-2008, 14:54
Esatto. In genere il lavoro di inizializzazione della classe genitrice è utile e andrebbe fatto comunque. Oltre a questo, la classe figlia può voler effettuare ALTRE inizializzazioni.

Ora inizio a vederci una certa utilità, anche se al momento personalmente non credo che mi serva

~FullSyst3m~
15-12-2008, 22:29
Edit: spostato nel nuovo post http://www.hwupgrade.it/forum/showthread.php?t=1885943

~FullSyst3m~
16-12-2008, 01:55
Edit: spostato nel nuovo post http://www.hwupgrade.it/forum/showthread.php?t=1885943

~FullSyst3m~
16-12-2008, 02:32
Edit: spostato nel nuovo post http://www.hwupgrade.it/forum/showthread.php?t=1885943