Skillbook Logo
foto profilo

Categoria: Tutorials


Impariamo Python giocando al "Solitario del Ferroviere" - Lezione 3

Gino Visciano | Skill Factory - 27/12/2019 09:24:31 | in Tutorials

Nella prima lezione abbiamo descritto le regole per giocare al "Solitario del ferroviere" ed implementato la  funzionalità per scegliere la carta da pensare, nella seconda lezione abbiamo implementato la funzionalità che permette di creare il mazzo di carte e girare le carte.

In questa nuova lezione implementeremo le seguenti funzionalità:

1) mischiare le carte 

2) visualizzare l'ora ed il tempo trascorso

3)  visualizzare il numero di volte che si mischiano le carte: tentativi

Come mischiare le carte

Per mischiare le carte è necessario invertire diverse volete, in modo casuale, i valori delle carte nella lista mazzo_carte, in questo modo cambiano anche le posizioni delle immagini associate alle carte, come mostra l'esempio seguente:

L'esempio prevede 6 iterazioni (loop), con inversione casuale di due valori nella lista mazzo_carte. Questo lavoro produce  il risultato seguente:

mazzo_carte=[1,2,3,4,5,6,7,8,9,10]  => mazzo_carte=[6,4,5,2,7,1,9,10,3,8]


Associando ai valori della lista mazzo_carte le rispettive immagini si ottiene:

       6           4         5          2           7         1          9       10        3          8

La funzione Python seguente inverte 99 volte, in modo casuale, due valori della lista mazzo_carte:  
 

 

Come visualizzare l'ora e misurare il tempo in secondi 

In Python per ottenre l'ora del sistema occorre importare la classe time ed usare la funzione strftime(...), come mostra l'esempio seguente:

# Che ora è?
import time
orario=time.strftime('%H:%M:%S')
print (orario)

>>>15:10:20

Per ottenere l'ora di sistema in un frame, potete utilizzare l'esempio seguente:

# Che ora è?
import time
import tkinter as tk
orario=time.strftime('%H:%M:%S')
root = tk.Tk()
root.wm_title('Orologio')
root.resizable(False, False)
label_orologio=tk.Label(root, text=orario,font=("Helvetica", 20))
label_orologio.pack()
root.mainloop()

Per trasformare la visualizzazione dell'ora di sistema in un vero orologio, dovete aggiornare ogni secondo l'etichetta label_orologio, con l'ora corrente, utilizzando la funzione after(...) come mostra l'esempio seguente:

# Orologio
import time
import tkinter as tk

# Questa funzione aggiorna ogni secondo l'ora visualizzata nell'etichetta label_orologio, utilizzando la finzione after(ogni_milliscondi, esegui_funzione)
def aggiorna_orario():
    orario=time.strftime('%H:%M:%S')
    label_orologio.config(text=orario)
    label_orologio.pack()
    label_orologio.after(1000,aggiorna_orario)

root = tk.Tk()
root.wm_title('Orologio')
root.resizable(False, False)
label_orologio=tk.Label(root, text="",font=("Helvetica", 20))
aggiorna_orario()
root.mainloop()

Possiamo usare l'esempio dell'orologio per visualizzare anche i secondi trascorsi, utilizzando la funzione time(), che restituisce l'ora di sistema in secondi:

# Orologio con secondi
import time
import tkinter as tk

# Questa funzione aggiorna ogni secondo l'ora visualizzata nell'etichetta label_orologio ed i secondi trascorsi nell'etichetta label_secondi, utilizzando la finzione after(ogni_milliscondi, esegui_funzione)
def aggiorna_orario():
    global time_inizio
    orario=time.strftime('%H:%M:%S')
    time_fine=time.time()
    secondi=int(time_fine-time_inizio)
    label_orologio.config(text=orario)
    label_secondi.config(text=secondi)
    label_orologio.pack()
    label_secondi.pack()
    label_orologio.after(1000,aggiorna_orario)

root = tk.Tk()
root.wm_title('Orologio')
root.resizable(False, False)
label_orologio=tk.Label(root, text="",font=("Helvetica", 20))
label_secondi=tk.Label(root, text="",font=("Helvetica", 20))
time_inizio=time.time()
aggiorna_orario()
root.mainloop()

 

Come visualizzare il numero di volte che si mischiano le carte

Per visualizzare il numero di volte che si mischiano le carte, dovete usare un contatore che s'incrementa ogni volta che le carte vengono mischiate. Il contatore deve essere inserito nella funzione mischia_carte(mazzo),  come mostra l'esempio seguete:

tentativi=tentativi+1,

tentativi è una variabile globale, che s'incrementa di uno ogni volta che vengono mischiate le carte.

 Per visualizzare i tentativi sul pannello di gioco, dovete creare un'etichetta e gestirla come mostra il codice seguente:

# Dichiarazione dell'etichetta per visualizzare il titolo del contatore
label_titolo_tentativi=tk.Label(root, text="TENTATIVI",font=("times", 12,"italic"),fg="black")

# Dichiarazione dell'etichetta per visualizzare il valore del contatore
label_tentativi=tk.Label(root, font=('arial', 14, 'normal'), bg='orange',fg='white')

# Formattazione della stringa da associare all'etichetta che visualizza i tentativi
spazi="          "[:(10-len(str(tentativi)))] # Imposta il numero di spazi da aggiungere a sinistra del valore del contatore, per ottenere sempre una stringa di 10 caratteri
# Assegna la stringa di 10 caratteri all'etichetta per vusializzare i tentativi
label_tentativi.config(text=spazi+str(tentativi)+" ")

 

# Posiziana e visualizza le etichette sul pannello di gioco
label_titolo_tentativi.place(x=153,y=455)
label_tentativi.place(x=160,y=515)

Per vedere dove deve essere posizionato il codice Python descritto in questa sezione, consultate la sezione Analisi del codice Python

L'immagine seguente mostra come si presenta il pannello di gioco, dopo il posizionamento di tutte le etichette create.

  

 

Analisi del codice Python

# Solitario del ferroviere (Lezione 3)
import pygame                                  # Importa classe pygame per gestire i suoni
import tkinter as tk                           
# Importa classe tkinter con alias tk per gestire il pannello di gioco
import time                                        # Importa classe time per gestire l'orologio di sistema 
import random                                  # Importa classe random che permette di ottenere gli indici casuali usati per mischiare le carte
from PIL import Image, ImageTk    # Dalla libreira Pillow importa le classi Image e ImageTk per gestire le immagini del gioco 

mazzo_carte=[1,2,3,4,5,6,7,8,9,10] # Lista con i valori delle carte 
immagini_carte=[]                             
# Lista che contiene le immagini delle carte
indice_carta_girata=0
time1=''                                               
# Questa variabile viene usata per il calcolo dei secondi trascorsi
time_start=''                                       # Questa variabile viene usata per il calcolo dei secondi trascorsi
tentativi=0                                          # Questa variabile viene usata per contare quante volte vengono mischiate le carte 

# Questa funzione viene eseguita ogni 200 millisecondi ed aggiorna il contenuto di tutte le etichette del pannello di gioco
def tick():
    global time1
    global time_start
    global tentativi
    # legge ora locale del pc
    time2 = time.strftime('%H:%M:%S')
    if time2 != time1:
       time1 = time2
       
# Etichetta con orologio in alto a destra del pannello di gioco
       label_clock.config(text=time2)
       secondi=int(time.time()-time_start)
       spazi="          "[:(10-len(str(secondi)))]
       str_secondi=spazi+str(secondi)+" "
       label_secondi.config(text=str_secondi)
       spazi="          "[:(10-len(str(tentativi)))]
       label_tentativi.config(text=spazi+str(tentativi)+" ")

    # La funzione after avvia la funzione tick ogni 200 millisecondi
    label_clock.after(200, tick)


# Questa funzione permette di mischiare le carte, l'argomento mazzo, è un riferimento, contiene l'indirizzo (posizione in memoria) della lista mazzo_carte 

def mischia_carte(mazzo):
    global tentativi
    tentativi=tentativi+1 
# Questa variabile viene ha il ruolo di contatore, conta quante volte le carte vengono mischiate
    temp=0
    max=len(mazzo)-1    
# Questa variabile indica l'indice massimo che punta all'ultimo valore contenuto nella liata mazzo_carte
    for i in range(1,100):
        prima=random.randint(0,max)       
# Questa variabile indica il primo indice casuale che punta al valore da invertire per mischiare le carte
        seconda=random.randint(0,max)  # Questa variabile indica il secondo indice casuale che punta al valore da invertire per mischiare le carte
        if prima!=seconda:
            
# SWAP, inverte la posizione dei valori nella lista mazzo_carte
            temp=mazzo[prima]
            mazzo[prima]=mazzo[seconda]
            mazzo[seconda]=temp

# Questa funzione permette di scegliere il suona da emettere (sound) e la durata (tempo) 
def suono(sound,tempo):
    if sound==1:
       pygame.mixer_music.load("TickSound.mp3")
    elif sound==2:
       pygame.mixer_music.load("ElectronicSound.wav")
    else:
       pygame.mixer_music.load("ComputerSound.wav")
    pygame.mixer_music.play()   
# Emette il suono richiesto
    time.sleep(tempo)                   # Pausa
    
# Questa funzione carica nella lista immagini_carte le immagini delle carte 
def carica_immagini_carte():
    immagini_carte=[]
    immagini_carte.append(ImageTk.PhotoImage(Image.open('Carte_Napoletane_retro.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('1coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('2coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('3coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('4coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('5coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('6coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('7coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('8coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('9coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('10coppe.png')))
    return immagini_carte

# Questa funzione permette di girare le carte
def gira():
    global indice_carta_girata
    global mazzo_carte
    global flag
    label_carta_girata.config(image=immagini_carte[mazzo_carte[indice_carta_girata]])
    label_carta_girata.place(x=240,y=40)
    girate="   "+str(indice_carta_girata+1)+"/10 "
    label_girate.config(text=girate)
    label_girate.place(x=285,y=370)
    if indice_carta_girata<9:
       indice_carta_girata=indice_carta_girata+1
       suono(1,0.1)
    else:
       pulsante_gira.place_forget()
       label_girate.place_forget()
       indice_carta_girata=0
       label_mazzo_di_carte.place_forget()
       pulsante_nuova_partita.place(x=270,y=370)
       suono(2,0.1)


    
# Questa funzione si attiva quando si clicca sul pulsante_nuova_partita, permette di mischiare le carte e riprendere il gioco
def nuova_partita():
   global indice_carta_girata
   label_carta_girata.place_forget()
   pulsante_nuova_partita.place_forget()
   suono(3,2.5)
   label_girate.config(text="   0/10  ")
   start()

# Questa funzione permette di inizializzare il pannello di gioco
def start():
    global indice_carta_girata
    global mazzo_carte
    global time_start
    time_start = time.time()  
# Legge l'ora di sistema espressa in secondi
    label_clock.place(x=360,y=5)
    label_titolo_mazzo_di_carte.place(x=45,y=5)
    label_mazzo_di_carte.place(x=25,y=40)
    pulsante_gira.place(x=70,y=370)
    label_girate.place(x=285,y=370)
    label_titolo_tempo.place(x=35,y=455)
    label_secondi.place(x=30,y=515)
    label_tentativi.place(x=160,y=515)
    label_titolo_tentativi.place(x=153,y=455)
    label_secondi.place(x=30,y=480)
    label_tentativi.place(x=160,y=480)
    tick()    
# Esegue la funzione tick() che aggiorna il contenuto di tutte le etichette del pannello di gioco
    pygame.init() # Inizializza le funzionalità pygame per la gestione dei suoni
    indice_carta_girata=0
    mischia_carte(mazzo_carte)  # Mischia i valori della lista mazzo_carte

# Inizializzazione ed impostazione caratteristiche pannello di gioco
root = tk.Tk()
root.wm_title('Solitario del ferroviere')
root.geometry("460x520")
root.resizable(False, False)
label_titolo_mazzo_di_carte=tk.Label(root, text="MAZZO DI CARTE",font=("Helvetica", 14))
pulsante_gira=tk.Button(root,text="GIRA", width=10, height=2, bd = 3,command=gira, bg="green",font=("Helvetica", 12))
pulsante_nuova_partita=tk.Button(root,text="NUOVA PARTITA", width=15, height=2, bd = 3,command=nuova_partita, bg="orange",font=("Helvetica", 12))
label_girate=tk.Label(root, text="   0/10  ",font=("Helvetica", 25),fg="white",bg="orange")
label_clock = tk.Label(root, font=('arial', 14, 'italic'), bg='gray',fg='black')
label_titolo_tempo=tk.Label(root, text="TEMPO",font=("times", 12,"italic"),fg="black")
label_titolo_tentativi=tk.Label(root, text="TENTATIVI",font=("times", 12,"italic"),fg="black")
label_secondi=tk.Label(root, font=('arial', 14, 'normal'), bg='blue',fg='white')
label_tentativi=tk.Label(root, font=('arial', 14, 'normal'), bg='orange',fg='white')
immagini_carte=carica_immagini_carte()
label_mazzo_di_carte=tk.Label(root, image=immagini_carte[0])
label_carta_girata=tk.Label(root, image=immagini_carte[0])
start() # Inizializza 
il pannello di gioco
root.mainloop()

 

Nella prossima lezione implementeremo la funzionalità  per controllare se la carta pensata e uguale alla carta girata dal mazzo, in questo modo già potrete iniziare a giocare.

Arrivederci ...


<< Lezione precedente           Lezione successiva >>



 Per scaricare le risorse di questa lezione clicca sul link seguente:risorse_lezione_03

 



 

Share Button

Impariamo Python giocando al "Solitario del Ferroviere" - Lezione 2

Gino Visciano | Skill Factory - 21/12/2019 10:02:13 | in Tutorials

Come creare il mazzo di carte

Nella lezione precedente abbiamo descritto le regole del gioco per giocare al "Solitario del ferroviere" ed implementato la  funzionalità per scegliere la carta da pensare.

In questa seconda lezione implementeremo la funzionalità che permette di girare le carte del mazzo.

 

Per iniziare serve il  mazzo di carte, per crearlo abbiamo usato la lista mazzo_carte a cui sono stati aggiunti i valori delle 10 carte da gioco, come mostra il comando seguente:

mazzo_carte=[1,2,3,4,5,6,7,8,9,10]

Successivamente, per associare i valori delle carte alle ripsettive immagini, abbiamo usato come indice della lista immagini_carte, i valori della lista mazzo_carte, come mostra il comando seguente:

immagini_carte[mazzo_carte[indice_carta_girata]]

La variabile indice_carta_girata è l'indice che ci permette di scegliere uno dei 10 valori presenti nella lista mazzo_carte a cui viene associata l'immagine della carta da visualizzare.   

Ad esempio, se indice_carta_girata è uguale a 4, il valore corrispondente alla carta da visualizzare  è 5, come mostra l'esempio seguente:

mazzo_carte=[1,2,3,4,5,6,7,8,9,10] 

Utilizzando il valore ottenuto come indice della lista immagini_carte, che contiene le immagini delle carte da gioco, si ottine la carta da gioco corrispondente:

 

Come abbiamo già visto nella lezione precedente, le immagini nella lista immagini_carte vengono caricate con la funzione carica_immagini_carte(), nell'ordine seguente:
 

Per visualizzare le carte da gioco sul pannello di gioco, abbiamo usato le etichette seguenti:

label_mazzo_di_carte=tk.Label(root, image=immagini_carte[0])
label_carta_girata=tk.Label(root, image=immagini_carte[0])

Per cambiare l'immagine associata all'etichetta label_carta_girata, abbiamo usato la funzione config(...), come mostra il comando seguente :

label_carta_girata.config(image=immagini_carte[mazzo_carte[indice_carta_girata]])

 

Come girare le carte mazzo

Per girare le carte del mazzo, è sufficiente incrementare ogni volta di uno la variabile indice_carta_girata, naturalmente la variabile deve essere azzerata ogni volta che il suo valore va oltre il limite massimo consentito, per evitare che venga generato l'errore:

IndexError: list index out of range  

Per gestire questa funzionalità abbiamo creato il  pulsante gira associato alla funzione gira(), come mostra il comando seguente:

pulsante_gira=tk.Button(root,text="GIRA", width=10, height=2, bd = 3,command=gira, bg="green",font=("Helvetica", 12))

Ogni volta che si clicca sul pulsante gira, la funzione gira() incrementa di 1 la variabile indice_carta_girata, in questo modo, di volta in volta, vengono visualizzate tutte le carte del mazzo, come mostra l'immagine seguente:
 

 

Quando la variabile indice_carta_girata diventa uguale a 8,  la funzione gira(), gestisce le seguenti attività:

1) visualizza il dieci di coppe

2) elimina dal pannello di gioco, l'ultima carta del mazzo 

3) elimina dal pannello di gioco il pulsante gira

4) permette  di iniziare una nuova partitta, visualizzando il pulsante nuova partita.

L'immagine seguente mostra il pannello di gioco quando la variabile indice_carta_girata diventa uguale ad 8.

 Cliccando sul pulsante nuova partita, viene eseguita la funzione nuova_partita(), che svolge le attività necessarie per riprendere il gioco.
 

Analisi del codice Python

# Solitario del ferroviere (Lezione 2)
import pygame 
# Importo la libreria pygame per gestire i suoni
import tkinter as tk  # Importo la libreria tkinter con alias (abbreviazione) tk, per gestire il pannello su cui giocare
import time #Importo la libreria time per gestire il tempo attraverso l'orologio di sistema
from PIL import Image, ImageTk # Importo da PIL le librerie per gestire le immagini 
from tkinter import messagebox 
 Importo da tkinter la libreria per gestire una finestra modale per gestire la fine del programma (game over)

 # Lista per gestire il mazzo di carte
mazzo_carte=[1,2,3,4,5,6,7,8,9,10]
# Lista in cui caricare i riferimenti alle immagini delle carte
immagini_carte=[]
# Indice che punta al valore della carta girata
indice_carta_girata=0

# Questa funzione gestisce i suoni, sound permette di scegliere il suono, tempo indica pa pausa richiesta dopo il suono  
def suono(sound,tempo):
    if sound==1:
       pygame.mixer_music.load("TickSound.mp3")
    elif sound==2:
       pygame.mixer_music.load("ElectronicSound.wav")
    else:
       pygame.mixer_music.load("ComputerSound.wav")
    pygame.mixer_music.play()
    time.sleep(tempo)    
    

# Questa funzione permette di aggiungere le immagini del gioco nella lista immagini_carte 
def carica_immagini_carte():
    immagini_carte=[]
    immagini_carte.append(ImageTk.PhotoImage(Image.open('Carte_Napoletane_retro.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('1coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('2coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('3coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('4coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('5coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('6coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('7coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('8coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('9coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('10coppe.png')))
    return immagini_carte

# Questa funzione permette di girare le carte del mazzo  
def gira():
    global indice_carta_girata
    global mazzo_carte
    global flag
   
# Visualizza la carta girata
     label_carta_girata.config(image=immagini_carte[mazzo_carte[indice_carta_girata]])
    label_carta_girata.place(x=240,y=40)
    girate="   "+str(indice_carta_girata+1)+"/10 "
    label_girate.config(text=girate)
    label_girate.place(x=285,y=370)
    if indice_carta_girata<9:
       indice_carta_girata=indice_carta_girata+1
       suono(1,0.1)
    else:

       # Questo blocco gestisce le attività richieste quando finiscono le carte del mazzo
       pulsante_gira.place_forget()
       label_girate.place_forget()
       indice_carta_girata=0
       label_mazzo_di_carte.place_forget()
       pulsante_nuova_partita.place(x=270,y=370)
       suono(2,0.1)
    

# Questa funzione avvia una nuova partita
def nuova_partita():
   global indice_carta_girata
   label_carta_girata.place_forget()
   pulsante_nuova_partita.place_forget()
   suono(3,2.5)
   label_girate.config(text="   0/10  ")
   start()

# Questa funzione inizializza il pannello di gioco
def start():
    global indice_carta_girata
    label_titolo_mazzo_di_carte.place(x=45,y=5)
    label_mazzo_di_carte.place(x=25,y=40)
    pulsante_gira.place(x=70,y=370)
    label_girate.place(x=285,y=370)
    pygame.init()
    indice_carta_girata=0

# Preparazione dei componenti del pannello di gioco
root = tk.Tk()
root.wm_title('Solitario del ferroviere')
root.geometry("460x450")
root.resizable(False, False)
label_titolo_mazzo_di_carte=tk.Label(root, text="MAZZO DI CARTE",font=("Helvetica", 14))
pulsante_gira=tk.Button(root,text="GIRA", width=10, height=2, bd = 3,command=gira, bg="green",font=("Helvetica", 12))
pulsante_nuova_partita=tk.Button(root,text="NUOVA PARTITA", width=15, height=2, bd = 3,command=nuova_partita, bg="orange",font=("Helvetica", 12))
label_girate=tk.Label(root, text="   0/10  ",font=("Helvetica", 25),fg="white",bg="orange")
immagini_carte=carica_immagini_carte()
label_mazzo_di_carte=tk.Label(root, image=immagini_carte[0])
label_carta_girata=tk.Label(root, image=immagini_carte[0])
start()
root.mainloop()

 

Nella prossima lezione implementeremo la funzione per mischiare le carte del mazzo centrale.

Arrivederci ...


<< Lezione precedente           Lezione successiva >>



 Per scaricare le risorse di questa lezione clicca sul link seguente:risorse_lezione_02

 



 

Share Button

Impariamo a programmare in Python, giocando al "Solitario del Ferroviere" - Lezione 1

Gino Visciano | Skill Factory - 18/12/2019 20:28:16 | in Tutorials

Regole del gioco

Il Solitario del Ferroviere è un passatempo che si può giocare con le carte napoletane, le regole del gioco sono le seguenti:

1) Mischiare le carte e metterle al centro; 

2) Pensare ad una carta del mazzo;

3) Alzare una carta dal mazzo;

4) Se la carta alzata è uguale a quella pensata, mettere la carta a sinistra, altrimenti metterla a destra;

5) Continuare a giocare mentre ci sono carte nel mazzo centrale;

6) Quando le carte del mazzo centrale finiscono, mentre ci sono carte a destra (carte non indovinate), mischiare le carte, metterle al centro e ricominciare a giocare.


Per semplificare la realizzazione del gioco con Python, utilizzeremo solo 10 carte napoletane , come mostra l'immagine seguente:
 



Il Diagramma di Flusso segunte descrive l'Algoritmo per giocare al Solitario del Ferroviere:
 

La pseudo-codifica  seguente descrive meglio le strutture logiche dell'Algoritmo per giocare al Solitario del Ferroviere, soprattutto perché permette di riconoscere i cicli che spesso nei Diagrammi di Flusso vengono confusi con le selezioni

#Algoritmo in pseudo-codifica, per giocare al solitario del ferroviere

Inizio
    Inizio Mentre
         Mischia le carte e mettile al centro
         Inizio Mentre
              Pensa ad una carta
              Alza una carta
              Se carta pensata==carta alzata allora
                   Metti la carta a sinistra
              Altrimenti
                   Metti la carta a destra
              Fine Se
         Mentre carte al centro !=0
    Mentre carte a destra !=0
Fine


In questa prima lezione implementeremo la funzione Python che permette di scegliere la carta pensata, come mostra l'immagine seguente:



 

Cosa serve per iniziare

Per iniziare dovete installare Python (almeno la versione 3.8), per scrivere il codice,  vi suggerisco di usare come ide (integrated development environment ) Visual Studio Code.

Usando la guida seguente potete installare sia Python che Visual Studio Code: guida_installazione.

Inoltre dovete installare  le seguenti librerie Python:

- pygame

- pillow

- time
 

con i comandi seguenti:

pip install pygame

pip install Pillow

pip install time

Se il comando pip non è installato, procedete nel modo seguente:

1) Scaricate il file get-pip.py;

2) Eseguite il comando: python get-pip.py.

 

Cosa dovete sapere

In Python non esistono comandi per dichiarare i tipi di variabili, le  variabili vengono tipizzate quando si assegna il primo valore, come mostra l'esempio seguente:

a=10 # Variabile intera
b=2.5 # Variabile Decimale
nome='Mario' # Variabile Stringa
flag=True # Variabile Booleana 

Le liste sono insiemi dinamici di valori, per dichiarare una lista si usa la sintassi seguente:

valori=[10,20,30] # Lista di valori di tipo intero 
nominativi=['Mario','Clara','Paolo','Roberta'] # Lista di valori di tipo Stringa
elenco=[] 
# Lista di valori vuota non ancora tipizzata

Per aggiungere un elemento ad una lista si usa il metodo append, come mostra l'esempio seguente:

elencoValori=[] # Lista di valori vuota non ancora tipizzata
# Aggiungo tre valori alla lista
elencoValori.append(10)
elencoValori.append(20)
elencoValori.append(30)
# Ciclo usato per visualizzare il contenuto della lista, i assume ad ogni ripetizione i valori seguenti: 0,1 e 2
for i in range(0,3):
      print(elencoValori[i]) 

L'esempio seguente, si differenzia da quello precedente, perchè per visualizzare il contenuto della lista usa un foreach.
Questa struttura di programmazione permette di leggere il contenuto di una lista senza usare l'indice dell'elemento da visualizzare, perché automaticamente ad ogni iterazione (loop) legge il valore seguente e lo assegna alla variabile del ciclo. 

elencoValori=[] # Lista di valori vuota non ancora tipizzata
# Aggiungo tre valori alla lista
elencoValori.append(10)
elencoValori.append(20)
elencoValori.append(30)
# Ciclo foreach, ad ogni ripetizione ad a viene assegnato un valore della lista elencoValori
for a in elencoValori:
      print(a) 

 
In Python l'indent assume un ruolo molto importante, perché si usa per indicare il bloccho d’istruzioni all'interno di una struttura di programmazione o di una funzione, come mostra l’esempio:
 
 
 
 

Analisi del codice Python

Per implemetare la specifica funzionale richiesta in questa prima lezione, dovete prima di tutto caricare le librerie per gestire i suoni, le immagini, il tempo ed il pannello (frame) su cui poggiare le carte da gioco. Successivamente bisogna creare (istanziare) il pannello da gioco, con il comando root = tk.Tk() e tutti i componenti che servono, principalmente le etichette ed il pulsante cambia.
 
La funzione carica_immagini_carte(), crea la lista che contiene le immagini delle carte, mentre la funzione cambia(), associata al pulsante cambia, permette di visialuzzare ad ogni click un carta diversa della lista immagini_carte.
 
Infine la funzione start() serve per visualizzare e posizionare tutti i componeti del pannello di gioco.
 
Studiate il codice seguente, con attenzione, anche facendo uso dei commenti e successivamente scaricate le risorse allegate alla lezione per ottenere tutto quello che vi serve per provare la prima funzionalità che serve per creare il "Solitario del ferroviere".
 
   
# Solitario del ferroviere (Lezione 1)
import pygame # Importo la libreria pygame per gestire i suoni
import tkinter as tk  
# Importo la libreria tkinter con alias (abbreviazione) tk, per gestire il pannello su cui giocare
from PIL import Image, ImageTk # Importo da PIL le librerie per gestire le immagini
from tkinter import messagebox # Importo da tkinter la libreria per gestire una finestra modale per gestire la fine del programma (game over)
import time #Importo la libreria time per gestire il tempo attraverso l'orologio di sistema
indice_carta_pensata=0
flag=True

 
# Questa funzione permette di aggiungere le immagini del gioco nella lista immagini_carte    
def carica_immagini_carte():
    immagini_carte=[]
    immagini_carte.append(ImageTk.PhotoImage(Image.open('Carte_Napoletane_retro.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('1coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('2coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('3coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('4coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('5coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('6coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('7coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('8coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('9coppe.png')))
    immagini_carte.append(ImageTk.PhotoImage(Image.open('10coppe.png')))
    return immagini_carte
 

 

# Questa funzione incrementa la variabile indice_carta_pensata, per cambiare la carta visualizzata, viene eseguite ogni volta che si esegue un click sul pulsante cambia
def cambia():
   
# indica che la variabile indice_carta_pensata è una variabile globale, creata all'esterno della funzione 
    global indice_carta_pensata
    # Imposta l'immagine della carta corrente 

    label_carta_pensata.config(image=immagini_carte[indice_carta_pensata])
    # Visualizza la carta corrente alla posizione indicata 

    label_carta_pensata.place(x=5,y=40)
    if indice_carta_pensata<10:
       indice_carta_pensata=indice_carta_pensata+1
    else:
       indice_carta_pensata=1
    # Emette un suono ed imposta una pausa 0.1 secondi
    pygame.mixer_music.load("TickSound.mp3")
    pygame.mixer_music.play()
    time.sleep(0.1)    

# Questa funzione inizializza il pannello di gioco
def start():
    global indice_carta_pensata
    # Visualizza il pulsante cambia, che ad ogni click esegue la funzione cambia() 

    pulsante_cambia.place(x=60,y=370)
    label_titolo_carta_pensata.place(x=25,y=5)
    label_carta_pensata.place(x=5,y=40)
    # Inizializza la gestione dei suoni
    pygame.init()
    indice_carta_pensata=2

# Crea il pannello di gioco
root = tk.Tk()
root.wm_title('Solitario del ferroviere') # Imposta il titolo del 
 pannello di gioco 
root.geometry("215x450") # Imposta le dimensioni del 
 pannello di gioco 
root.resizable(False, False) # Disabilità il ridimensionamento del 
 pannello di gioco

# Preparazione pulsanti ed etichette, l'attributo commad permette di indicare la funzione eseguita quando si clicca sul pulsante
pulsante_cambia=tk.Button(root,text="CAMBIA", width=10, height=2, bd = 3,command=cambia, bg="green",font=("Helvetica", 12))
label_titolo_carta_pensata=tk.Label(root, text="CARTA PENSATA",font=("Helvetica", 14))

# Prepara il vettore con i riferimenti alle immagini delle carte
immagini_carte=carica_immagini_carte()

# Carica nell'etichetta la prima carta del mazzo
label_carta_pensata=tk.Label(root, image=immagini_carte[1])

# Inizializza l'applicazione e visualizza l'etichetta ed il bottone cambia
start()
root.mainloop() # Rende il pannello di gioco interattivo

 

Nella prossima lezione implementeremo la funzione per girare le carte del mazzo centrale.

Arrivederci ...

 


Lezione successiva >>



 Per scaricare le risorse di questa lezione clicca sul link seguente: risorse_lezione_01

 



Resto a casa a fare formazione ...
Anche durante il lockdown non ci siamo mai fermati, erogando oltre 3000 ore di formazione e laboratorio in Smart-Learning/Working. Grazie al nostro impegno, durante il periodo di emergenza COVID50 giovani programmatori hanno iniziato un tirocinio formativo presso un nostro Job partner IT, con l'opportunità di essere assunti a settembre 2020.  

 


Tutto quello che ti serve per diventare programmatore te lo insegniamo noi!
Vuoi partecipare ad una "Skill Factory"? Invia il tuo CV a recruiting@skillfactory.it, ti contatteremo per verificare se hai i prerequisiti richiesti.
Non cerchiamo talenti o esperti d'Informatica, ma giovani seri volenterosi, predisposti a risolvere problemi logici.   


Sei un programmatore esperto?
Pensi che la tua esperienza possa essere utile agli utenti della nostra community?
Cosa aspetti, conttattaci!  Puoi diventare un nostro "Competence partner" e dare valore alla tua esperienza ...

mail: sid@skillfactory.it 


Per far parte della nostra community registrati su www.skillbook.it.


T U T O R I A L S    S U G G E R I T I


EDUCATIONAL GAMING BOOK (EGB) "H2O"

Nell'era dello SMART LEARNING e di PYTHON i libri non si scrivono, ma si sviluppano, in questo modo chi studia, può sperimentare ed apprendere contemporaneamente; un libro con queste caratteristiche lo possiamo definire un  Educational Gaming Book (EGB).

"H2Oè un EGB che descrive tutte le caratteristiche dell'acqua, la sostanza formata da molecole di H2O, che attraverso il suo ciclo di vita garantisce la sopravvivenza di tutti gli esseri viventi del Pianeta

L'obiettivo dell'EGB è quello di far conoscere ai giovani le proprietà dell'acqua, sotto molti aspetti uniche, per sensibilizzarli a salvaguardare un bene comune raro, indispensabile per la vita


Per il DOWNLOAD di "H2Oclicca qui.

Share Button

Introduzione alla logica degli oggetti - Lezione 6

Gino Visciano | Skill Factory - 09/12/2018 13:36:37 | in Tutorials

In questa lezione parliamo di Contenitori anche detti Collezioni, classi specializzate per la gestione di raccolte di oggetti con modalità di accesso differenti.

Le Collezioni possono essere di 4 tipi:

vediamo in dettaglio le loro caratteristiche.

LE LISTE
Le Liste sono contenitori di oggetti simili ai vettori,  di dimensioni dinamiche e modalità di accesso indicizzata, questo significa che in una lista potete aggiungere tutti i valori oppure gli oggetti che volete ed ognuno di essi può essere identificato da un valore numerico chiamato indice, come per i vettori, il primo indice corrisponde al valore 0.

L'immagine seguente mostra la struttura di una lista, che contiene i riferimenti di tre oggetti di tipo Persona:

 

Attenzione persona1, persona2 e persona3 corrispondono agli indirizzi di memoria (riferimenti) delle posizioni dove sono stati istanziati i tre oggeti di tipo Persona.

In C++ per  inserire valori oppure oggetti all'inizio di una lista, si usa il metodo push_front(), per inserirli alla fine, si usa il metodo push_back(), mentre per puntare al primo oppure all'ultimo elemento si usano i metodi pop_front() e pop_back().  Per leggere ed eliminare il primo oppure l'ultimo elemento di una lista si usano i metodi front() e back().

L'immagine seguente mostra come si gestisce una lista in C++.

Per conoscere quanti elementi sono presenti in una lista, potete usare il metodo size(), altri metodi importatnti sono:

empty() restituisce true se la lista è vuota false se contiene elementi;
clear() elimina tutti gli elementi di una lista;
erase(posizione) oppure erese(da,a) elimina dalla lista uno specifico elemento oppure un intervallo di elementi;
insert(posizione,valore) inserisce il valore prima della posizione indicata.

Per passare alla partica, vediamo un esempio, scritto in linguaggio C++,

Esempio 1

// Calcola il totale dei valori interi inseriti nella lista
#include <iostream>
#include <list>
using namespace std;

int main ()
{
// Dichiarazione lista che contiene valori interi. Valori è il riferimento che contiene l'indirizzo di dove è stata allocata la lista
list<int> valori;
int somma=0;
// Inserimento alla fine della lista di valori interi
valori.push_back (10);
valori.push_back (20);
valori.push_back (30);
// Mentre la lista è piena, viene letto l'ultimo valore ed aggiunto alla variabile somma. Successivamente il valore letto viene rimosso. Alla fine del ciclo la lista sarà vuota
while (!valori.empty())
  {

    // Legge l'ultimo valore della lista e allo stesso tempo lo elimina. Il metodo back() legge ed elimina.
    // Per leggere senza eliminare il valore dovete usare un iteratore

    somma+=valori.back();
   // Punta all'ultimo elemento disponibile nella lista
    valori.pop_back();
  }
// Visualizza il Totale contenuto nella variabile somma e va a capo (endl='\n')
  cout << "Totale:" << somma << endl;
  return 0;
}

 

Per ordinare una lista in C++, si usa il metodo sort(),  al metodo potete anche passare un metodo in cui vengono indicate le modalità di comparazione da usare per l'ordinamento.

Gli esempi seguenti mostrano come ordinare una lista di numeri interi, sia in modo crescente, sia in modo decrescente.

Esempio 2

// Lista ordinata in modo crescente
#include <iostream>
#include <list>

using namespace std;

int main(){
    list<int> valori;
    valori.push_back(50);
    valori.push_back(40);
    valori.push_back(30);
    valori.push_back(10);
    valori.push_back(20);
    valori.push_back(35);
    valori.push_back(60);
    valori.push_back(45);
    valori.push_back(23);
    valori.push_back(15);
    // Ordina la lista in modo crescente
    valori.sort();
    while (!valori.empty())
    {
         // Legge il primo valore della lista e allo stesso tempo lo elimina. Il metodo front() legge ed elimina.
         // Per leggere senza eliminare il valore dovete usare un iteratore

        cout << valori.front() << endl;
       // Punta al primo elemento disponibile nella lista
        valori.pop_front();
    }
      return 0;
}



Esempio 3

// Lista ordinata in modo decrescente
#include <iostream>
#include <list>

using namespace std;

int main(){
    list<int> valori;
    valori.push_back(50);
    valori.push_back(40);
    valori.push_back(30);
    valori.push_back(10);
    valori.push_back(20);
    valori.push_back(35);
    valori.push_back(60);
    valori.push_back(45);
    valori.push_back(23);
    valori.push_back(15);
    // Ordina la lista in modo decrescente, il metodo greater<int>() permette d'indicare il metdo di comparazione decrescente
    valori.sort(greater<int>());
    while (!valori.empty())
    {
         // Legge il primo valore della lista e allo stesso tempo lo elimina. Il metodo front() legge ed elimina.
         // Per leggere senza eliminare il valore dovete usare un iteratore

        cout << valori.front() << endl;
        valori.pop_front();
    }
      return 0;

}

Come avete potuto notare dagli esempi precedenti, in C++, ogni volta che si usano i metodi back() e front(), per leggere un valore dalla lista, il valore viene eliminato, questo significa che se leggete tutti i valori, alla fine la lista sarà vuota.  Per evitare di svuotare le liste dopo la lettura dei valori, usare un iteratore, un riferimento dinamico che può essere incrementato oppure decrementato, che permette di puntare ad un elemento qualsiasi della lista. Dopo aver creato l'iteratore, per ottenere il riferimento del primo elemento di una lista si usa il metodo begin(), mentre il metodo end() restituisce l'indirizzo che corrisponde alla fine della lista.

L'immagine seguente mostra il funzionamento degli iteratori in C++:

 

I due esempi seguenti mostrano come si usano gli iteratori per visualizzare i valori di una lista con C++:

Esempio 4

#include <iostream>
#include <list>

using namespace std;

int main ()
{
  // Vettore con i valori da inserire nella lista
  int vlista[]= {10,30,20,30,5};
  // Creo una lista passando al costruttore i valori del vettore vlista
  // L'argomento vlista è l'indirizzo del primo valore, vlista+4 è l'indirizzo dell'ultimo valore - lista (da,a)

  list<int> lista (vlista,vlista+4);
  list<int>::iterator iterLista;
  // Assegno all'iteratore l'indirizzo del primo valore della lista
  iterLista=lista.begin();
  cout << "Elenco valori nella lista:" << endl;
  // Il ciclo serve per incrementare per 5 volte l'indirizzo memorizzato nell'iteratore e visualizzare il contenuto della lista
  for(int x=0;x<lista.size();x++){
    // *iterLista visualizza il valore corrispondente all'indirizzo memorizzato nell'iteratore
    // successivamente ++ incrementa l'indirizzo corrente di una posizione per puntare la valore seguente

    cout << *iterLista++ << endl;
  }
  // Rimuove tutti i valori 30 dalla lista
  lista.remove(30);
  cout << "Elenco valori nella lista dopo la cancellazione:" << endl;
  // Visualizza tutti i valori della lista usando l'iteratore
  for (iterLista=lista.begin(); iterLista!=lista.end(); ++iterLista) {
    cout << *iterLista << endl;
    }
  return 0;
}

 

 

Esempio 5

// Lista di nomi ordinata in modo crescente
#include <iostream>
#include <list>
#include <string>
#include <cctype>

using namespace std;

// compara i nomi della lista trascurando la differenza tra maiuscolo e minuscolo (not case sensitive)
bool compara_nocase (const string& prima, const string& seconda)
{
  unsigned int i=0;
// Il ciclo continua ad essere eseguito mentre il valore della variabile è minore della lunghezza del primo nome e del secondo nome
  while ( (i<prima.length()) && (i<seconda.length()) )
  {
    // Se il carattere iesimo del primo nome e minore del carattere iesimo del secondo nome viene restituito true altrimenti false
    if (tolower(prima[i])<tolower(seconda[i])) return true;
    else if (tolower(prima[i])>tolower(seconda[i])) return false;
    ++i;
  }
  return ( prima.length() < seconda.length() );
}

int main ()
{
  int i;
  list<string> nomi;
  list<string>::iterator iterNomi;
  nomi.push_back ("Mario");
  nomi.push_back ("Alessandra");
  nomi.push_back ("Carlo");
  nomi.push_back ("mario");
  nomi.push_back ("ALESSANDRA");
  nomi.push_back ("carlo");
  nomi.push_back ("Marianna");
  nomi.push_back ("ROBERTA");
  nomi.push_back ("ROBERTO");
  nomi.sort();
  i=0;
  cout << "Elenco nomi ordinati considerando la differenza tra maiuscolo e minuscolo:"  << endl;
  for (iterNomi=nomi.begin(); iterNomi!=nomi.end(); ++iterNomi)
    cout << ++i << ' ' << *iterNomi << endl;
  cout << endl;
  nomi.sort(compara_nocase);
  i=0;
  cout << "Elenco nomi ordinati senza considerare la differenza tra maiuscolo e minuscolo:" << endl;
  for (iterNomi=nomi.begin(); iterNomi!=nomi.end(); ++iterNomi)
    cout << ++i << ' ' << *iterNomi  << endl;
  cout << endl;

  return 0;
}


 

COME INSERIRE OGGETTI IN UNA LISTA

L'esempio seguente mostra come si gestisce una lista di oggetti di tipo Persona con C++.

Esempio 6

 

LE MAPPE
Le Mappe sono contenitori che permettono di associare un valore oppure un oggetto ad una chiave. In pratica è come indirizzare un vettore con una valore qualsiasi invece che con un indice numerico.
Anche le Mappe sono contenitori di dimensioni dinamiche e modalità di accesso indicizzata.

L'immagine seguente mostra la struttura di una mappa che contiene i riferimenti di oggetti di tipo Persona:

 

In C++ per aggiungere un valore ad una  mappa potete usare i comandi seguenti:

1) mappa["dieci"]=10;

oppure

2) mappa.insert(pair<string,int>("dieci", 10));

In entrambi i casi la chiave scelta, "dieci", è di tipo stringa, mentre il valore associato, 10, è di tipo intero.

Attenzione le chiavi associate ai valori oppure agli oggetti di una mappa possono essere di qualunque tipo, nell'esempio sono chiavi di tipo stringa.

Il metodo insert(...) permette d'inserire in una mappa la struttura pair che corrisponde ad una tupla con due campi first (chiave) e second (valore):

mappa->first corrisponde alla chiave di tipo stringa "dieci";

mappa->second corrisponde al valore di tipo intero 10;

Vediamo tre esempi sviluppati con C++:

Esempio 7

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main ()
{
map<string,int> mappa;
// Inserimento valori interi nella mappa
mappa["primo"] = 1;
mappa["secondo"] = 2;
mappa["terzo"] = 3;
mappa["quarto"] = 4;
mappa["quinto"] = 5;
// Visualizzazione valori attraverso l'uso delle chiavi associate ai valori
cout << "1) Accesso diretto attraverso la chiave:" << endl;
cout << mappa["primo"] << endl;
cout << mappa["secondo"] << endl;
cout << mappa["terzo"] << endl;
cout << mappa["quarto"] << endl;
cout << mappa["quinto"] << endl;
// Dichiarazione iteratore
map<string,int>::iterator iteraMappa;
cout << "2) Accesso attraverso un iteratore:" << endl;
// Visualizzazione valori attraverso l'uso dell'iteratore
for (iteraMappa=mappa.begin();iteraMappa!=mappa.end();++iteraMappa) {
    cout << iteraMappa->first << ": " << iteraMappa->second << endl;
  }

  return 0;
}


Come potete vedere l'iteratore ordina le chiavi in modo crescete.

Esempio 8

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main ()
{
map<string,int> mappa;
map<string,int>::iterator iteraMappa;
// Inserimento valori attraverso l'uso di una tupla di tipo pair
mappa.insert(pair<string,int>("primo", 0));
mappa.insert(pair<string,int>("secondo", 0));
mappa.insert(pair<string,int>("terzo", 0 ) );
mappa.insert(pair<string,int>("quarto",0));
mappa.insert(pair<string,int>("quinto",0));
// Il metodo at(chiave) permette di modificare il valore associato alla chiave
mappa.at("primo") = 1;
mappa.at("secondo") = 2;
mappa.at("terzo") = 3;
mappa.at("quarto") = 4;
mappa.at("quinto") = 5;
// Visualizzazione valori attraverso l'uso dell'iteratore
cout << "Accesso attraverso un iteratore:" << endl;
for (iteraMappa=mappa.begin();iteraMappa!=mappa.end();++iteraMappa) {
    cout << iteraMappa->first << ": " << iteraMappa->second << endl;
  }

  return 0;
}

 

Esempio 9

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main ()
{
map<string,int> mappa;
// Inserimento valori interi nella mappa
mappa["primo"] = 1;
mappa["secondo"] = 2;
mappa["terzo"] = 3;
mappa["quarto"] = 4;
mappa["quinto"] = 5;
// Dichiarazione iteratore
map<string,int>::iterator iteraMappa;
// Visualizzazione valori attraverso l'uso dell'iteratore
cout << "2) Accesso attraverso un iteratore:" << endl;
for (iteraMappa=mappa.begin();iteraMappa!=mappa.end();++iteraMappa) {
    cout << iteraMappa->first << ": " << iteraMappa->second << endl;
  }
// Il metodo find cerca nella mappa la chiave fornita come argomento ed assegna all'iteratore l'indirizzo corrispondente alla posizione della chiave cercata
iteraMappa=mappa.find("secondo");
// Il metodo erase cancella l'elemento all'indirizzo memorizzato nell'iteratore
mappa.erase(iteraMappa);
iteraMappa=mappa.find("quarto");
mappa.erase(iteraMappa);
cout << "2) Elenco valori dopo cancellazione:" << endl;
for (iteraMappa=mappa.begin();iteraMappa!=mappa.end();++iteraMappa) {
    cout << iteraMappa->first << ": " << iteraMappa->second << endl;
  }

  return 0;
}

 

COME INSERIRE OGGETTI IN UNA MAPPA

In una  mappa si possono anche inserire oggetti, l'esempio seguente mostra come si gestisce una mappa di oggetti di tipo Persona con C++:

Esempio 10


GLI INSIEMI
Gli Insiemi sono contenitori di valori oppure di oggetti distinti (detti anche componenti o membri) dello stesso tipo. A differenza delle liste e delle mappe gli elementi di un insieme non possono essere duplicati, cioè un insieme non può contenere valori uguali oppure oggetti con lo stesso contenuto.

Il numero di elementi di un insieme è detto cardinalità.

I due esempi seguenti mostrano come si gestisce un insieme di numeri interi , con C++:

Esempio 11

int main(){
    // Dichiarazione di un insieme di numeri interi
    set<int> valori;
    // Dichiarazione di un iteratore
    set<int>::iterator iterValori;
   // Inserimento valori interi nell'insieme
    valori.insert(50);
    valori.insert(40);
    valori.insert(30);
    valori.insert(10);
    valori.insert(20);
    valori.insert(30);
    valori.insert(50);
    valori.insert(40);
    valori.insert(20);
    valori.insert(10);
   // Visualizzazione cardinalità e contenuto dell'insieme attraverso l'uso di un iteratore
    cout << "Cardinalita':" << valori.size() << endl << endl;
    for (iterValori=valori.begin(); iterValori!=valori.end(); ++iterValori){
         cout << *iterValori << endl;
    }
    return 0;
}

 

Esempio 12

#include <iostream>
#include <set>

using namespace std;

int main(){
    set<int> valori;
    // Dichiarazione di un iteratore
    set<int>::iterator iterValori;
   // Inserimento valori interi nell'insieme
    valori.insert(50);
    valori.insert(40);
    valori.insert(30);
    valori.insert(10);
    valori.insert(20);
    valori.insert(30);
    valori.insert(50);
    valori.insert(40);
    valori.insert(20);
    valori.insert(10);
   // Visualizza la cardinalità ed il contenuto dell'insieme attraverso l'uso di un iteratore
    cout << "Cardinalita':" << valori.size() << endl;
    for (iterValori=valori.begin(); iterValori!=valori.end(); ++iterValori){
         cout << *iterValori << endl;
    }
    // Il mtodo find cerca il valore fornito come argomento ed assegna all'iteratore l'indirizzo della posizione nell'insieme
    iterValori=valori.find(20);
   // Il metodo erase utilizza la posizione memorizzata nell'iteratore per eliminare l'elemento corrispondente
    valori.erase(iterValori);
    iterValori=valori.find(40);
    valori.erase(iterValori);
   // Visualizzaza la cardinalità ed il contenuto dell'insieme attraverso l'uso di un iteratore dopo l'eliminazione dei due elementi
    cout << "Cardinalita' dopo la cancellazione dei valori:" << valori.size() << endl;
    for (iterValori=valori.begin(); iterValori!=valori.end(); ++iterValori){
         cout << *iterValori << endl;
    }
    return 0;
}

COME INSERIRE OGGETTI IN UN INSIEME

Se in un insieme  vengono inseriti oggetti, dovete sovrascrivere l'operatore <, come mostra l'esempio seguente:

bool operator<(const Persona& x) const {
    string strPersona1, strPersona2;
    bool esito=false;
    stringstream seta1;
    stringstream seta2;
    seta1 << eta;
    seta2 << x.eta;
    strPersona1=cognome+nome+seta1.str();
    strPersona2=x.cognome+x.nome+seta2.str();
    return strPersona1<strPersona2;
 }

Il programma seguente mostra come si gestisce un insieme di oggetti di tipo Persona, con C++:

Esempio 13

 

 

LE CODE
Le Code sono pile sequenziali di valori oppure oggetti di tipo FIFO (First In First Out - Il primo ad entrare è il primo ad uscire) oppure di tipo LIFO (Last In First Out - L'ultimo ad entrare è il primo ad uscire).

L'immagine seguente mostra il comportamento di una coda di tipo FIFO (Queue) :

 

Gli elementi di una coda di tipo FIFO (Queue) si comportano come le persone che fanno la fila ad una cassa.

L'immagine seguente mostra il comportamento di una coda di tipo LIFO (Stack):

 

Gli elementi di una coda di tipo LIFO (Stack) si comportano come una pila di piatti.

In C++, per aggiungere un valore oppure un oggetto ad una coda si usa il metodo push(), per puntare al successivo elemento si usa il metodo pop(), per estrarlo da una coda, si usa il metodo front(), per estrarlo da uno stack, si usa il metodo top(),come mostrano gli esempi seguenti:

Esempio 14

#include <iostream>
#include <queue>

using namespace std;

int main()
{
  // Dichiarazione coda di valori interi di tipo FIFO
  queue <int> coda;
 // Inserimento valori nella coda
  coda.push(10);
  coda.push(20);
  coda.push(30);
  coda.push(5);
  coda.push(15);
  coda.push(25);
  coda.push(50);
  coda.push(60);
  coda.push(40);
  coda.push(30);
  cout << "Dimensione coda:" << coda.size() <<endl;
 // Legge i valori dalla coda mentre non è vuota
  while (!coda.empty())
  {
   // Legge ed elimina il valore corrente dalla coda
    cout << coda.front() << endl;
   // Punta al valore successivo da leggere
    coda.pop();
  }
  cout << "Dimensione coda:" << coda.size() <<endl;
  return 0;
}

 

Esempio 15

#include <iostream>
#include <stack>

using namespace std;

int main()
{
  // Dichiarazione coda di valori interi di tipo LIFO
  stack <int> coda;
 // Inserimento valori nella coda
  coda.push(10);
  coda.push(20);
  coda.push(30);
  coda.push(5);
  coda.push(15);
  coda.push(25);
  coda.push(50);
  coda.push(60);
  coda.push(40);
  coda.push(30);
  cout << "Dimensione coda:" << coda.size() <<endl;
  while (!coda.empty())
  {
   // Legge ed elimina il valore corrente dalla coda
    cout << coda.top() << endl;
   // Punta al valore successivo da leggere
    coda.pop();
  }
  cout << "Dimensione coda:" << coda.size() <<endl;
  return 0;
}

 

Esempio 16

 

Esempio 17

Nella prossima lezione faremo un confronto tra C++ e Java.


<< Lezione precedente


  Clicca qui per scaricare i laboratori di questa lezione  (Per eseguire i laboratori installate Code Block sul vostro computer)


Laboratori di Logica di Programmazione in C

Share Button

Introduzione alla logica degli oggetti - Lezione 5

Gino Visciano | Skill Factory - 24/11/2018 14:02:07 | in Tutorials

Nelle lezioni precedenti abbiamo parlato di Polimorfismo dei metodi, definendo l'Overload lezione 3 e l'Override lezione 4,   in questa lezione parleremo di Polimorfismo degli oggetti.
Iniziamo intanto a dire che due oggetti si dicono Polimorfi  se sono simili.  Gli oggetti diventano simili quando si verifica uno dei seguenti casi:

1) una classe eredita un'altra classe;
2) più classi implementano la stessa interfaccia.

Nell'esempio seguente, le classi Persona, Dipendente, Manager e Dirigente, sono simili perché si ereditano tra loro:

La classe Persona è la più  generica, mentre la classe Dirigente è la più specializzata.

Nell'esempio seguente, le classi Rettangolo, Triangolo e Cerchio sono simili perché implementano la stessa interfaccia:

 

In C++ l'ereditarietà è multipla, quindi le interfacce non esistono, al loro posto si usano le classi astratte,  il polimorfismo basto sulle intefacce esiste solo in linguaggi di programmazione  come Java e C#, dove l'ereditarietà è singola e le interfacce si usano per superare questo limite.

Quando gli oggetti sono simili, diventano possibili operazioni di questo tipo:

Persona *dirigente=new Dirigente();

oppure

IFiguraGeometrica *triangolo=new Triangolo();

Nel primo esempio abbiamo istanziato un oggetto di tipo Dirigente, ma la variabile (puntatore) che contiene l'indirizzo dell'oggetto creato è di tipo Persona, un tipo molto più generico.
Nel secondo esempio abbiamo istanziato un oggetto di tipo Triangolo, ma la variabile (puntatore) che contiene l'indirizzo dell'oggetto creato è di tipo IFiguraGeometrica, che corrisponde ad una interfaccia oppure una classe astratta.

Questa proprietà degli oggetti simili  è molto utile quando volete passare allo stesso metodo oggetti simili;

COME PASSARE OGGETTI SIMILI AD UN METODO
Il Polimorfismo degli oggetti permette di passare oggetti simili allo stesso metodo, per evitare di creare un metodo per ogni tipo di oggetto da gestire.
Immaginate di voler stampare gli attributi di più oggetti di tipo diverso: Dipendente, Manager e Dirigente.
Senza il polimorfismo dovremmo creare tre metodi diversi:

stampaDipendente(Dipendente dipendente);
stampaManager(Manager manager);
stampaDirigente(Dirigente dirigente);

Invece, sfruttando il polimorfismo, possiamo creare un unico metodo a cui passiamo tutti e tre i tipi di oggetti simili, basta indicare come argomento il tipo più generico, che in questo caso è il Dipendente:

stampa(Dipendente dipendente);

questa sarebbe una semplificazione importante per chi deve stampare, perché dovrebbe ricordare il nome di un solo metodo anzicché tre nomi diversi.

Naturalmente il metodo stampa dovrà essere implementato in modo che riconosca quali sono i tipi di oggetti passati come argomenti e stamaprli in modo corretto.

L'esempio seguente mostra come creare il metodo getInformazioni con C++:

#include <string>
#include <iostream>
#include <sstream>
#include <typeinfo>

using namespace std;

class Persona {
    private:
        string classe;
        string nome;
        string cognome;
        int eta;
    public:
        void setNome(string nome){
            this->nome=nome;
        };
        void setCognome(string cognome){
            this->cognome=cognome;
        };
        void setEta(int eta){
            this->eta=eta;
        };
        string getNome(){
            return nome;
        }
        string getCognome(){
            return cognome;
        }
        int getEta(){
            return eta;
        }
       // getClasse() restituisce il tipo di classe dell'oggetto corrente
   
        string getClasse(){
            return this->classe;
        }

       /******
           setClasse() imposta l'attributo classe uguale al tipo di classe corrente, usando la funzione typeid(*this).name().
           *this è il puntatore che identifica l'oggetto corrente,
setClasse() deve essere virtuale perchè se viene 
           ereditato ,quando è invocato dalla classe derivata, deve comportarsi come se appartenesse ad essa, altrimenti
           il tipo restituito da setClasse() è sempre quello della classe più generica in cui è stato implementato.
       ******/ 

        virtual void setClasse(){
             this->classe=typeid(*this).name();
        }

        Persona(){
             setClasse();
        };
        Persona(string nome, string cognome, int eta){
            this->nome=nome;
            this->cognome=cognome;
            this->eta=eta;
           setClasse();
        }
};

// La classe Dipendente eredita la classe Persona e diventano simili
class Dipendente : public Persona {
    private:
        string ruolo;
        double stipendio;
    public:
        void setRuolo(string ruolo){
            this->ruolo=ruolo;
        };
        string getRuolo(){
            return ruolo;
        }
        void setStipendio(double stipendio){
            this->stipendio=stipendio;
        };
        double getStipendio(){
            return stipendio;
        }
        Dipendente(){
            setClasse(); // Ereditato dalla classe Persona, assegna all'attributo classe il tipo Dipendente
        };
        Dipendente(string nome, string cognome, int eta, string ruolo, double stipendio):Persona(nome, cognome, eta){
            this->ruolo=ruolo;
            this->stipendio=stipendio;
            setClasse(); // Ereditato dalla classe Persona, assegna all'attributo classe il tipo Dipendente
        }
};

// La classe Manager eredita la classe Dipendente e diventano simili, quindi per la proprietà transitiva Manager e simile anche a Persona
class Manager : public Dipendente {
        private:
        string areaResponsabilita;
    public:
        void setAreaResponsabilita(string areaResponsabilita){
            this->areaResponsabilita=areaResponsabilita;
        };
        string getAreaResponsabilita(){
            return areaResponsabilita;
        }
        Manager(){
            setClasse();
        };
        Manager(string nome, string cognome, int eta, string ruolo, double stipendio, string areaResponsabilita):
            Dipendente(nome, cognome, eta, ruolo, stipendio){
            this->areaResponsabilita=areaResponsabilita;
            setClasse();
        }
};

// La classe Dirigente eredita la classe Manager e diventano simili, quindi per la proprietà transitiva Dirigente e simile anche a Dipendente e Persona
class Dirigente : public Manager {
        private:
        string livelloFunzionale;
    public:
        void setLivelloFunzionale(string livelloFunzionale){
            this->livelloFunzionale=livelloFunzionale;
        };
        string getLivelloFunzionale(){
            return livelloFunzionale;
        }
        Dirigente(){
              setClasse(); // Ereditato dalla classe Persona, assegna all'attributo classe il tipo Manager

        }; 
        Dirigente(string nome, string cognome, int eta, string ruolo, double stipendio, string areaResponsabilita,string livelloFunzionale):
            Manager(nome, cognome, eta, ruolo, stipendio,areaResponsabilita){
            this->livelloFunzionale=livelloFunzionale;
            setClasse(); // Ereditato dalla classe Persona, assegna all'attributo classe il tipo Manager
        }
};
 

class GestisciDipendenti {
      public:
            // Il Metodo getInformazioni può ricevere come argomento oggetti di tipo Dipendente oppure oggetti simili di tipo Manager o Dirigente
            string getInformazioni(Dipendente *dipendente){
                       string infoPersona="";
                       stringstream sEta;
                       stringstream sStipendio;
                       string strEta,strStipendio;
                       sEta << dipendente->getEta();
                       sStipendio << dipendente->getStipendio();
                       strEta=sEta.str();
                       strStipendio=sStipendio.str();
                       // Se il puntatore *dipendente è di tipo Dirigente esegue uno static_cast a Dirigente e assegna all'attributo infoPersona le informazioni del Dirigente
                       if(dipendente->getClasse()==typeid(Dirigente).name()){
                          Dirigente *dirigente;
                          dirigente=static_cast<Dirigente *>(dipendente);
                          infoPersona = dirigente->getNome()+","+dirigente->getCognome()+","+strEta+","+dirigente->getRuolo()+","+strStipendio+","+
                          dirigente->getAreaResponsabilita()+","+dirigente->getLivelloFunzionale();
                          // Se il puntatore *dipendente è di tipo Manager esegue uno static_cast a Manager e assegna all'attributo infoPersona le informazioni del Manager
                          } else if(dipendente->getClasse()==typeid(Manager).name()){
                                     Manager *manager;
                                     manager=static_cast<Manager *>(dipendente);
                                     infoPersona = manager->getNome()+","+manager->getCognome()+","+strEta+","+manager->getRuolo()+","+strStipendio+","+
                                                               manager->getAreaResponsabilita();
                         // Se il puntatore *dipendente è di tipo Dipendente  assegna all'attributo infoPersona le informazioni del Dipendente 
                         } else{
                                   infoPersona = dipendente->getNome()+","+dipendente->getCognome()+","+strEta+","+dipendente->getRuolo()+","+strStipendio;
                        }
                        return infoPersona;
            }
};


int main(){
// Istanzio oggetto di tipo Dipendente
Dipendente *dipendente=new Dipendente("Marco","Rossi",30,"Programmatore",100.58);
// Istanzio oggetto di tipo Manager
Manager *manager=new Manager("Franco","Verdi",50,"Direttore Generale",200.58,"CED");
// Istanzio oggetto di tipo Dirigente
Dirigente *dirigente=new Dirigente("Carlo","Bianchi",50,"Direttore Generale",200.58,"CED","F3");
// Istanzio oggetto di tipo GestisciDipendenti che contiene il metodo getInformazioni
GestisciDipendenti *gd=new GestisciDipendenti();
// Dato che gli oggetti di tipo Dipendente, Manger e Dirigente sono Polimorfi (simili), possono essere passati al metodo getInformazioni impostando come
// argomento del metodo il tipo più generico dei tre,  il Dipendente: getInformazioni(Dipendente *dipendente) 
cout << "1) Dipendente" <<endl;
cout << gd->getInformazioni(dipendente) << endl;
cout << "2) Manager" <<endl;
cout << gd->getInformazioni(manager) << endl;
cout << "3) Dirigente" << endl;
cout << gd->getInformazioni(dirigente) << endl;
}

COME CREARE OGGETTI SIMILI ATTRAVERSO L'UTILIZZO D'INTERFACCE O CLASSI ASTRATTE
Le interfacce e le classi astratte sono strutture di programmazione simili,  perché entrambe contengono metodi astratti (non impelmentati) e non possono istanziare oggetti.

Le differenze principali tra loro sono due:

1)  le classi astratte possono contenere anche metodi già implementati, come le normali classi,  le interfacce no, a meno che non state usando Java 8 che permette d'implementare nelle interfacce anche metodi statici o di default;
2) le classi astratte si ereditano, mentre le interfacce s'implementano.

L'uso delle interfacce in alcuni linguaggi di programmazione ad oggetti come Java e C#, è necessario per superare il limite dell'ereditarietà è singola, che non permette di ereditare più classi contemporaneamente, mentre non è necessario in linguaggi di programmazione ad oggetti  dove l'ereditarietà è multipla, come in C++.

In realtà le classi astratte sono pattern (soluzioni applicative), che permettono di creare metodi che usano il risultato di altri metodi, detti astratti, la cui logica (comportamento) verrà implementata in futuro in una classe derivata.

Oggetti che ereditano la stessa classe astratta  oppure implementano la stessa interfaccia  diventano polimorfi, cioè simili.

Immaginate di voler creare un'applicazione specializzata per calcolare il perimetro e l'area delle seguenti figure geometriche:


Le quattro figure geometriche sono simili perché hanno in comune le seguenti caratteristiche:

1) corrispondono tutte ad un tipo di figura geometrica;
2) hanno tutte due coordinate x ed y che identificano la posizione in un piano bidimensionale;
3) hanno tutte un colore;
4) hanno tutte un perimetro ed un'area, che vengono calcolati diversamente per ogni tipo di figura geometrica, quindi devono essere implementati nelle classi di riferimento.

Tutte queste caratteristiche comuni possono essere raggruppate in una  classe astratta  come mostra il Diagramma di Classe seguente:

Successivamente la classe astratta FiguraGeometrica può essere ereditata dalle classi derivate Quadrato, Rettangolo, Triangolo e Cerchio che diventano simili tra loro.
In queste classi dovranno essere implementati i metodi astratti perimetro ed area, come mostra il Diagramma di Classe seguente: 

L'esempio seguente mostra come creare l'applicazione Geometria con C++, la classe FiguraGeometrica è una classe astratta che utilizza il metodo getInformazioni per restituire il perimetro e l'area delle figure geometriche istanziate.

Il metodo getInformazioni può usare i metodi perimetro ed area anche se non sono stati implementati perché sono astratti. Quando i due metodi verranno implementati nelle classi di riferimento forniranno il perimetro e l'area della figura geometrica corrente.

In C++ i metodi astrattti si dichiarano nel modo seguente:

virtual double perimetro()=0;
virtual double area()=0;

 

/*****************************
      Applicazione Geometria.cpp
****************************/

#include <string>
#include <iostream>
#include <sstream>
#include <math.h>

using namespace std;

// Classe che permette di definire la posizione nel piano delle figure geometriche in base alle coordinate x ed y
class Point {
private:
    int x,y;
public:
    Point(int x, int y){
    this->x=x;
    this->y=y;
    }
    getX(){
       return x;}
    getY(){
       return y;}
};

// Classe astratta che raggruppa la informazioni comuni a tutte le figure geometriche
class FiguraGeometrica {
private:
    string tipoFigura;
    Point *posizione;
    string colore;
public:
    string getTipoFigura(){
           return tipoFigura;
           }
    Point * getPosizione(){
           return posizione;
           }
    void setPosizione(Point *posizione){
           this->posizione=posizione;}
    string getColore(){
           return colore;}
    void setColore(string colore){
           this->colore=colore;
    }
    string getInformazioni(){
        stringstream sperimetro;
        stringstream sarea;
        stringstream sx;
        stringstream sy;
        sperimetro << perimetro();
// Utilizzo metodo astratto che verrà implementato successivamente nelle classi derivate
        sarea << area(); // Utilizzo metodo astratto che verrà implementato successivamente nelle classi derivate
        sx << posizione->getX();
        sy << posizione->getY();
        return tipoFigura+", posizione:"+sx.str()+", "+sy.str()+", "+colore+", perimetro:"+sperimetro.str()+", area:"+sarea.str();
    }
    virtual double perimetro()=0;
// Metodo astratto
    virtual double area()=0; // Metodo astratto
    FiguraGeometrica(string tipoFigura,Point *posizione,string colore){
        this->tipoFigura=tipoFigura;
        this->posizione=posizione;
        this->colore=colore;
    }
};

// La classe Quadrato eredita la classe astratta FiguraGeometrica e diventa simile a tutte le altre classi che erediteranno questa classe.
class Quadrato: public FiguraGeometrica {
private:
    double lato;
public:
    double getLato(){
        return lato;
    }
    void setLato(double lato){
        this->lato=lato;
    }
    // Sovrascrittura del metodo perimetro richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere il preimetro del Quadrato
    double perimetro(){
       return lato*4;}
    // Sovrascrittura del metodo area richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere l'area del Quadrato
    double area(){
       return pow(lato,2); // pow = potenza(base,esponete)
    }
    Quadrato(double lato,Point *posizione,string colore):FiguraGeometrica("Quadrato", posizione, colore){
        this->lato=lato;
    }
};

// La classe Rettangolo eredita la classe astratta FiguraGeometrica e diventa simile a tutte le altre classi che erediteranno questa classe.
class Rettangolo: public FiguraGeometrica {
private:
    double latoMinore;
    double latoMaggiore;
public:
    double getLatoMinore(){
        return latoMinore;
    }
    void setLatoMinire(double latoMinore){
        this->latoMinore=latoMinore;
    }
    double getLatoMaggiore(){
        return latoMaggiore;
    }
    void setLatoMaggiore(double latoMaggiore){
        this->latoMaggiore=latoMaggiore;
    }
    // Sovrascrittura del metodo perimetro richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere il preimetro del Rettangolo
    double perimetro(){
       return (latoMinore+latoMaggiore)*2;}
    // Sovrascrittura del metodo area richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere l'area del Rettangolo
    double area(){
       return latoMinore*latoMaggiore;
    }
    Rettangolo(double latoMinore,double latoMaggiore, Point *posizione,string colore):FiguraGeometrica("Rettangolo", posizione, colore){
        this->latoMinore=latoMinore;
        this->latoMaggiore=latoMaggiore;
    }
};

// La classe Triangolo eredita la classe astratta FiguraGeometrica e diventa simile a tutte le altre classi che erediteranno questa classe.
class Triangolo: public FiguraGeometrica {
private:
    double latoPrimo;
    double latoSecondo;
    double latoTerzo;
public:
    double getLatoPrimo(){
        return latoPrimo;
    }
    void setLatoPrimo(double latoPrimo){
        this->latoPrimo=latoPrimo;
    }
    double getLatoSecondo(){
        return latoSecondo;
    }
    void setLatoSecondo(double latoSecondo){
        this->latoSecondo=latoSecondo;
    }
    double getLatoTerzo(){
        return latoTerzo;
    }
    void setLatoTerzo(double latoTerzo){
        this->latoTerzo=latoTerzo;
    }
    // Sovrascrittura del metodo perimetro richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere il preimetro del Triangolo
    double perimetro(){
       return latoPrimo+latoSecondo+latoTerzo;
       }
    // Sovrascrittura del metodo area richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere l'area del Triangolo
    double area(){
       // Formula di Erone
       double semiPerimetro=perimetro()/2;
       return sqrt(semiPerimetro*(semiPerimetro-latoPrimo)*(semiPerimetro-latoSecondo)*(semiPerimetro-latoTerzo));
    }
    Triangolo(double latoPrimo,double latoSecondo,double latoTerzo, Point *posizione,string colore):FiguraGeometrica("Triangolo", posizione, colore){
        this->latoPrimo=latoPrimo;
        this->latoSecondo=latoSecondo;
        this->latoTerzo=latoTerzo;
    }
};

// La classe Cerchio eredita la classe astratta FiguraGeometrica e diventa simile a tutte le altre classi che erediteranno questa classe.
class Cerchio: public FiguraGeometrica {
private:
    double raggio;
public:
    double getRaggio(){
        return raggio;
    }
    void setRaggio(double raggio){
        this->raggio=raggio;
    }
    // Sovrascrittura del metodo perimetro richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere il preimetro del Cerchio
    double perimetro(){
       return raggio*M_PI*2;} // M_PI = PI GRECO
    // Sovrascrittura del metodo area richiamato dal metodo getInformazioni ereditato dalla classe FiguraGeometrica per ottenere l'area del Cerchio

    double area(){
       return pow(raggio,2)*M_PI; // M_PI = PI GRECO, pow = potenza(base,esponete)
    }
    Cerchio(double raggio,Point *posizione,string colore):FiguraGeometrica("Cerchio", posizione, colore){
        this->raggio=raggio;
    }
};

int main() {
// Posizione Quadrato
Point *pointQuadrato=new Point(10,20);
// Quadrato(lato, posizione, colore)
Quadrato *quadrato=new Quadrato(25,pointQuadrato,"verde");
// Posizione Rettangolo
Point *pointRettangolo=new Point(60,80);
// Rettangolo(latoMinore, latoMaggiore, posizione, colore)
Rettangolo *rettangolo=new Rettangolo(25,40,pointRettangolo,"Blu");
// Posizione Trianangolo
Point *pointTriangolo=new Point(100,150);
// Triangolo(latoPrimo, latoSecondo, latoTerzo, posizione, colore)
Triangolo *triangolo=new Triangolo(40,30,20,pointTriangolo,"Rosso");
// Posizione Cerchio
Point *pointCerchio=new Point(100,150);
// Cerchio(lraggio, posizione, colore)
Cerchio *cerchio=new Cerchio(40,pointCerchio,"Grigio");
cout << quadrato->getInformazioni() << endl;
cout << rettangolo->getInformazioni() << endl;
cout << triangolo->getInformazioni() << endl;
cout << cerchio->getInformazioni() << endl;
}

Nella prossima lezione parleremo di Collection.


<< Lezione precedente           Lezione successiva >>


  Clicca qui per scaricare i laboratori di questa lezione  (Per eseguire i laboratori installate Code Block sul vostro computer)


Laboratori di Logica di Programmazione in C

Share Button
TOP