Skillbook Logo
foto profilo

Categoria: Tutorials


Excel delle Meraviglie Lezione 7 - Come lavorare con un elenco di dati

Gino Visciano | Skill Factory - 13/05/2020 00:18:01 | in Tutorials

In questa lezione imparerete a lavorare con un elenco di dati utilizzando i comandi del menu Dati

Per creare un elenco d'informazioni, è importante rispettare le seguenti regole:

1) l'elenco deve avere sempre una riga d'intestazione con i nomi delle colonne;
2) l'elenco dev'essere compatto, senza righe e colonne vuote;
3) se è possibile, iniziare  l'elenco partendo dalla cella A1;
4) se 
è possibile, dedicare l'intero foglio di lavoro per la gestione dei dati dell'elenco, soprattutto se sono tanti;
5) a parte la riga d'intestazione, non inserire bordi al contenuto dell'elenco.

L'immagine seguente mostra un esempio di elenco di dati.

Come ordinare i dati di un elenco

Prima di procedere con l'ordinamento dei dati dell'elenco, dovete decidere quali sono le chiavi da utilizzare per l'ordinamento.

In questo esempio le chiavi potrebbero essere le seguenti:

A) Cognome, Nome ed Età;
B) Titolo di Studio, Cognome, Nome ed Età;
C) Città, Cognome, Nome ed Età;
D) Sesso, Cognome, Nome ed Età;
E) Ruolo Aziendale, Cognome, Nome ed Età;
F) Matricola.

La prima chiave si chiama primaria, perché l'elenco viene prima ordinato utilizzando questa chiave.
Se dopo l'ordinamento, in alcune righe  la chiave primaria si ripete, queste righe vengono ordinate utilizzando la chiave seguente e così via fin quando l'elenco non risulta ordinato in base a tutte le chiavi indicate.

Attenzione se la chiave primaria di ordinamento non si ripete mai, come accade nel nostro esempio per la matricola, è inutile indicare ulteriori chiavi di ordinamento.

Una volta individuate la chiavi da utilizzare per l'ordinamento potete procedere nel modo seguente:

1) Posizionate il cursore sulla riga d'intestazione dell'elenco, nella prima colonna;
2) Selezionate il menu dati e cliccate sull'icona ordina, indicata di seguito:

3)  Nella casella di selezione "Ordina per", scegliete la colonna cognome;

4) Successivamente per aggiungere anche le altre chiavi di ordinamento, usate il pulsante "+Aggiungi livello, come mostra l'immagine seguente:

   
 

5) Per ogni chiave impostate l'ordine a destra e cliccate sul pulsante "OK".

Per ordinare l'elenco con chiavi di ordinamento diverse, procedete nel modo seguente:

1) Posizionate il cursore sulla riga d'intestazione dell'elenco, nella prima colonna;
2) Selezionate il menu dati e cliccate sull'icona ordina, indicata di seguito:

3) Utilizzare il pulsate:



per eliminare le chiavi precedenti ed aggiungete le nuove chiavi di ordinamento.

Come filtrare i dati di un elenco

Per filtrare i dati di un elenco dovete prima decidere quali sono le condizioni da impostare nelle colonne.
 
Ad esempio immaginate di voler selezionare solo i dipendenti con le seguenti caratteristiche:
 
A) Sesso: Femmina;
B) Città: Potenza;
C) Titolo di studio: Laurea;
 
Per ottenere questo risultato procedete nel modo seguente:
 
1) Posizionate il cursore sulla riga d'intestazione dell'elenco, nella prima colonna;
2) Selezionate il menu dati e cliccate sull'icona filtro, indicata di seguito:
 
 
3) Sulla destra di ogni colonna apparirà una freccia, utilizzare  le frecce per impostare le condizioni filtro, come mostrano le immagini seguenti:
 
 
Per selezionare una sola opzione, cliccate su Seleziona tutto, per togliere tutte le spunte e successivamente cliccate sulla condizione che serve.
Se è necessario, nello stesso elenco potete anche scegliere più condizioni, in questo caso verranno visualizzate solo le righe che nella colonna filtro contengono una delle voci spuntate.   
Più condizioni impostate sulla stessa colonna vengono associate tra loro con un operatore logico di tipo OR,  ad esempio:
 
Città="Potenza" OR Città="Napoli;
 
Quando le condizioni sono associate con un operatore logico di tipo OR, la riga viene visualizzata se almeno una delle condizioni indicate è vera.
 
 
Man mano che aggiungete nuovi filtri ad altre colonne, le condizioni vendono aggiunte alle precedenti attraverso un operatore logico di tipo AND, filtrando  un maggior numero righe dell'elenco.
 
Quando le condizioni sono associate con un operatore logico di tipo AND, la riga viene visualizzata solo se tutte le condizioni indicate sono vere.
 
 
In questo esempio è come se avessimo impostato la condizione logica seguente:
 
Titolo di studio="Laurea" AND sesso="Femmina" AND Città="Potenza".
 
Per eliminare i filtri applicati, cliccate sull'icona filtro, indicata di seguito:
 
 
Nell'esempio seguente imparerete a selezionare le righe di un elenco in base ad un intervallo di valori.
 
Immaginate di voler selezionare tutti i dipendenti che hanno uno stipendio maggiore di €1200.00 e minore di €3000.00, in base alla condizione seguente:
 
Stipendio>1200.00 AND Stipendio<3000.00.
 
Per ottenere questo risultato procedete nel modo seguente:
 
1) Posizionate il cursore sulla riga d'intestazione dell'elenco, nella prima colonna;
2) Selezionate il menu dati e cliccate sull'icona filtro, indicata di seguito:
 

3) Sulla destra di ogni colonna apparirà una freccia, cliccate sulla freccia corrispondente alla colonna Stipendio e scegliete le opzioni seguenti:
 
 
4)  Successivamente completate la scheda seguente e cliccate sul pulsante OK.
 
 
Come impostare un subtotale in un elenco di dati
 
Per impostare un subtotale in un elenco di dati, dovete prima rispondere alle seguenti domande:
 
1) Qual è la chiave di raggruppamento;
2) Che funzione applicare al subtotale, ad esempio: somma, conteggio, media, min, max, prodotto, etc..
3) Qual è la colonna a cui applicare la funzione per il calcolo del subtotale.
 
Ad esempio, immaginate di voler ottenere il totale degli stipendi, raggruppando i dipendenti per ruolo aziendale.
 
1) Prima di tutto dovete ordinare l'elenco impostando come chiave di ordinamento la colonna Ruolo Aziendale, come mostra l'immagine seguente:
 
 
2) Successivamente selezionate l'icona subtotale:
 
 
3) Impostate la chiave di raggruppamento , la funzione somma e la colonna a cui applicare il subtotale, come mostra l'immagine seguente:
 
 
L'immagine seguente mostra l'elenco dati con i subtotali richiesti:
 
 
Per eliminare dall'elenco dati i subtotali cliccate sul pulsante "Rimuovi tutti", in basso a sinistra della finestra dei Subtotali.
 
Nella prossima lezione utilizzeremo delle macro per applicare con un semplice click gli ordinamenti, i filtri ed i subtotali visti in questa lezione.

Per il download del file excel GestioneDipendenti.xlsx clicca qui.


<< Lezione precedente           Lezione successiva >>


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


Share Button

Impariamo Python creando il "Calendario Perpetuo" - Lezione 7

Gino Visciano | Skill Factory - 30/01/2020 23:29:56 | in Tutorials

In questa lezione creiamo un Calendario perpetuo,  per ricavare il giorno della settimana di qualsiasi data

L'algoritmo per creare un calendario perpetuo richiede le seguenti informazioni:

1) Codice dell'anno
2) Codice del mese
3) Numero del giorno della settimana
4) Se l'anno è Bisestile oppure no

Come calcolare il Codice dell'anno

Per calcolare Il codice dell'anno bisogna prima ottenere le due cifre a destra dell'anno scelto, ad esempio per l'anno 2019 serve il 19, che per comodità indichiamo con aa.

In Python per ottenere aa possiamo usare il comando seguente:

aa=int(str(anno)[2:])

La funzione str(...) converte la variabile intera anno  in stringa, mentre con [2:] indichiamo a Python di eliminare dalla stringa i due caratteri a sinistra. Infine i due caratteri che restano vengono di nuovo convertiti in intero ed assgnati alla variabile aa.

Successivamente dobbiamo calcolare il codice_anno_intermedio che è dato dalla formula:

codice_anno_intermedio=aa+int(aa/4)

Infine per ottenere il codice_anno occorre sottrarre al codice_anno_intermedio il suo multiplo di 7 più piccolo, come mostra la formula seguente:

codice_anno=codice_anno_intermedio-int(codice_anno_intermedio/7)*7.

Facciamo un esempio, calcoliamo il codice anno del 2019:

1) aa=19

2) codice_anno_intermendio=aa+int(aa/4)=19+4=23

3) codice_anno=codice_anno_intermedio-int(codice_anno_intermedio/7)*7=23-21=2

 

Come ottenere il Codice del mese

Il codice del mese è un valore intero difficile da calcolare, fortunatmente c'è già chi l'ha fatto per noi, quindi basta guardare semplicemente i risultati ottenuti: 

1)   gennaio = 6 se l'anno è bisestile 5
2)   febbraio =  2 se l'anno è bisestile 1
3)   marzo = 2
4)   aprile = 5
5)   maggio = 0
6)   giuugno = 3
7)   luglio = 5
8)   agosto = 1
9)   settembre = 4
10) ottobre = 6
11) novembre = 2
12) dicembre = 4

Come potete vedere per ottenere il codice del mese è importante sapere se l'anno di riferimento è bisestile.

Come verificare se un anno è bisestile

Nel calendario Gregoriano è bisestile un anno ogni 4. Questa correzione è necessaria perché astronomicamente un anno dura circa 365,25 giorni.
Un anno è bisestile nei casi seguenti:
 
1) se l'anno è divisibile contemporaneamente per 100 e per 400;
 
2) se l'anno è divisibile per 4
 
In Python per verificare se un anno è bisestile possiamo usare la funzione seguente:
 

def verificaBisestile(anno):
    bisestile=False
    if anno%100==0:
        if anno%400==0:
            bisestile=True
    elif anno%4==0:
            bisestile=True
    return bisestile

In questa funzione l'opertore % (modulo) permette di capire se l'anno è divisibile prima per 100 e poi per 400 oppure se è divisibile direttamente per 4.

Come calcolare il numero del giorno
Il numero del giorno corrisponde al giorno della settimana di una data qualunque del calendario, come mostra la tabella seguente:

1) Lunedì = 1
2) Martedì = 2
3) Meercoledì = 3
4) Giovedì = 4
5) Venerdì = 5
6) Sabato = 6
7) Domenica = 7 oppure 0

La formula per calcolare il numero del giorno è la seguente:

Numero giorno = (Codice anno + Codice Mese + Progressivo giorno del mese)%7

Facciamo un esempio, con un anno non bisestile ed uno bisestile:

Esempio 1

Il 18 marzo 2019, corrisponde ad un  lunedì, per verificare calcolare il numero del giorno:

1) aa=19

2) codice_anno_intermendio=aa+int(aa/4)=19+4=23

3) codice_anno=codice_anno_intermedio-int(codice_anno_intermedio/7)*7=23-21=2

4) codice_mese=2

5) Numero giorno = (Codice anno + Codice Mese + Progressivo giorno del mese)%7 = (2+2+18)%7=1

6) Il numero giorno 1 corrisponde a lunedì

Esempio 2

Il 10 gennaio 2020, corrisponde ad un venerdì, per verificare calcolare il numero del giorno:

1) aa=20

2) codice_anno_intermendio=aa+int(aa/4)=20+5=25

3) codice_anno=codice_anno_intermedio-int(codice_anno_intermedio/7)*7=25-21=4

4) codice_mese=5 perché l'anno 2020 è bisestile

5) Numero giorno = (Codice anno + Codice Mese + Progressivo giorno del mese)%7 = (4+5+10)%7=5

6) Il numero giorno 5 corrisponde a venerdi


In Python per ottenere il numero del giorno della settimana di una data qualunque del calendario, possiamo usare la funzione seguente:

def giornoSettimana(gg,mm,aaaa):
    aa=int(str(aaaa)[2:])
    codici_mesi=[6,2,2,5,0,3,5,1,4,6,2,4]
    if verificaBisestile(aaaa)==True:
       codici_mesi=[5,1,2,5,0,3,5,1,4,6,2,4]
    codice_anno_intermedio=(aa+int(aa/4))
    codice_anno=codice_anno_intermedio-int(codice_anno_intermedio/7)*7
    giorno=(codice_anno+codici_mesi[mm-1]+gg)%7
    return giorno

Analisi del codice Python

# Calendario perpetuo

import tkinter as tk # Classe che permette di creare GUI
from PIL import Image, ImageTk # Classi per gestire immagini
import time # Classe che permette di gestire il tempo
  
# Funzione che calcola il numero del giorno della settimana
def giornoSettimana(gg,mm,aaaa):
    aa=int(str(aaaa)[2:])
    codici_mesi=[6,2,2,5,0,3,5,1,4,6,2,4]
    if verificaBisestile(aaaa)==True:
       codici_mesi=[5,1,2,5,0,3,5,1,4,6,2,4]
    codice_anno_intermedio=(aa+int(aa/4))
    codice_anno=codice_anno_intermedio-int(codice_anno_intermedio/7)*7
    giorno=(codice_anno+codici_mesi[mm-1]+gg)%7
    return giorno  
  
# Funzione che stampa i mesi dell'anno scelto
def stampaGiorni(mm,aaaa):
    mesi=["Gennaio", "Febbraio", "Marzo", "Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"]
    giorni=[31, 28, 31, 30,31,30,31,31,30,31,30,31]
    if mm==2:
       if verificaBisestile(aaaa)==True:
          giorni[1]=29 # Imposta il mese di febbraio a 29 giorni se l'anno è bisestile
    conta=0
    titolo=mesi[mm-1]+" "+str(aaaa)
    centra="          "[:10-int(len(titolo)/2)]
    T2.insert(tk.END, '\n'+centra+titolo+'\n')
    T2.insert(tk.END, " Lu Ma Me Gi Ve Sa Do\n")
    gg_settimana=giornoSettimana(1,mm,aaaa)
    if gg_settimana==0:
       gg_settimana=7
    for i in range(1,gg_settimana):
        T2.insert(tk.END, "   ")
    conta=gg_settimana-1
    for gg in range(1,giorni[mm-1]+1):
        giorno="   "[:(3-len(str(gg)))]+str(gg)
        T2.insert(tk.END, giorno)
        conta=conta+1
        if conta==7:
           T2.insert(tk.END,'\n')
           conta=0
    T2.insert(tk.END,'\n')
    T2.insert(tk.END,"---------------------\n")
  
# Funzione che verifica se l'anno è bisestile
def verificaBisestile(aaaa):
    bisestile=False
    if aaaa%100==0:
        if aaaa%400==0:
            bisestile=True
    elif aaaa%4==0:
            bisestile=True
    return bisestile
  
# Funzione che imposta l'anno precedente e lo visualizza
def anno_precedente():
    global anno
    anno=anno-1
    label_anno.config(text=anno)
    main()
  
# Funzione che imposta l'anno successivo e lo visualizza
def anno_successivo():
    global anno
    anno=anno+1
    label_anno.config(text=anno)
    main()
  
# Funzione che crea i Frame in cui visualizzare i mesi dell'anno
def main():
    global T2
    F=[]
    offset=0 # Permette di stampare 3 mesi in ogni Frame
    for f in range(0,4):
        F.append(tk.Frame(Frame_body)) # Il Frame_body contiene 4 Frame ciascuno con 3 mesi 
        F[f].grid(column = f, row = 0)
    for f in range(0,4):
        T2=tk.Text(F[f], height=29, width=25,font=("Courier", 12)) # Imposta lo stile del testo per stampare i mesi dell'anno
        T2.pack(side=tk.LEFT, fill=tk.Y)
        for mm in range(1+offset,4+offset):
            stampaGiorni(mm,anno)
        offset=offset+3 # Inizio programma  
root = tk.Tk()
root.wm_title('CALENDARIO PERPETUO')
root.resizable(False, False)
icona = ImageTk.PhotoImage(Image.open("icona_cal.ico")) # Carica l'immagine dell'icona visualizzata in alto a sinistra della finestra del calendario
root.tk.call("wm", "iconphoto", root._w, icona)

# Frame in cui vengono visualizzati i pulsanti freccia e l'anno corrente
Frame_top=tk.Frame(root)
Frame_top.pack(side="top", fill="both", expand = True) # Associa il Frame_top alla finestra root e lo espande in tutto lo spazio disponibile
# Pulsante freccia a sinistra usato per visualizzare l'anno precedente
freccia_sx=tk.Button(Frame_top,text="<",  bd=1,command=anno_precedente, bg="blue",fg="white",font=("courier", 28))
freccia_sx.pack(side="left")
t = time.localtime()
anno=t.tm_year
label_anno = tk.Label(Frame_top, text=anno, bg="blue", fg="white",font=("Courier", 38))
label_anno.pack(side="left",fill="both",expand=True)
# Pulsante freccia a destra usato per visualizzare l'anno successivo
freccia_dx=tk.Button(Frame_top,text=">", bd=1, command=anno_successivo, bg="blue",fg="white",font=("courier", 28))
freccia_dx.pack(side="left")
# Frame in cui vengono visualizzati i 4 Frame ciascuno con tre mesi dell'anno
Frame_body=tk.Frame(root)
Frame_body.pack()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
# Frame in cui viene visualizzato il logo Skill Factory ed il messaggio "Powered by Skill Factory"

Frame_bottom=tk.Frame(root)
Frame_bottom.pack()
logo=ImageTk.PhotoImage(Image.open('logo_sf.png'))
label_logo=tk.Label(Frame_bottom, image=logo)
label_powered_sf=tk.Label(Frame_bottom, text="2020 - Powered by Skill Factory",font=("times", 14,'italic'))
label_logo.pack(side="left")
label_powered_sf.pack(side="left",fill="both",expand=True)
main()
tk.mainloop()

Conclusioni

Questa lezione è sicuramente molto interessante perché oltre a permettere di acquisire nuove conoscenze ed abilità sull'utilizzo del linguaggio Pythonpermette anche di conoscere le caratteristiche fondamentali del calendario Gregoriano, come ad esempio la durata effettiva di un anno astronomico ed il meccanismo dell'anno bisestile per correggere il ritardo che si accumula ogni quattro anni.

L'applicazione può essere compilata per ottenere un eseguibile che vi permetterà di usare il calendario perpetuo senza dover usare l'interprete Python.   

Per compilare lo script Python dovete installare il tool pyinstaller, con il comando:

pip install pyinstaller

Per compilare lo script dovete usare il comando:

pyinstaller calendario.py --noconsole

Dopo la compilazione travate tutto quello che vi serve nella cartella \dist\calendario.

Prima di lanciare il file eseguibile calendario.exe, dovete caricare nella cartella \dist\calendario tutte le immagini.

Arrivederci alla prossima lezione!

 


<< Lezione precedente



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

 



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

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

Gino Visciano | Skill Factory - 19/01/2020 23:16:45 | in Tutorials

In questa lezione completiamo il "Solitario del Ferroviere" implementando i tre livelli previsti: Apprendisti, Principianti ed Esperti.

  

Come abbiamo già visto nelle lezioni precedenti, i primi due livelli del gioco offrono aiuti differenti, quello per Esperti non prevede nessun tipo di aiuto, dipende tutto dalle capacità del giocatore.

Per scegliere uno dei tre livelli previsti, bisogna cliccare su una delle immagini seguenti presenti sul Frame di avvio:

Le immagini cliccabili, sono associate alle funzioni  apprendisti, principainti ed esperti che attivano i Frame con i componenti del livello scelto.

Di seguito i blocchi di codice python delle tre funzioni che permettono di gestire i livelli del solitario:

def apprendista(event):
    global livello
    frame_avvio.pack_forget()
    frame_avvio.quit()
    livello=1
    root.wm_title('Solitario del ferroviere:[Apprendista]')
    crea_primo_livello_solitario(root)

def principiante(event):
    global livello
    frame_avvio.pack_forget()
    frame_avvio.quit()
    livello=2
    root.wm_title('Solitario del ferroviere:[Principiante]')
    crea_altri_livelli_solitario(root)

def esperto(event):
    global livello
    frame_avvio.pack_forget()
    frame_avvio.quit()
    livello=3
    root.wm_title('Solitario del ferroviere:[Esperto]')
    crea_altri_livelli_solitario(root)

 

Come creare il pannello di gioco ed avviare il livello di avvio

Il blocco di codice Python seguente permette di avviare il solitario, vengono prima caricate le librerie richieste e successivamente viene prima creato il pannello di gioco e poi avviato il Frame di avvio. 

import pygame # Libreria usata nel gioco per gestire i suoni
import random # Libreria che serve a gestire valori casuale
import tkinter as tk # Libreria usata per creare, pannelli e finestre
import time # Libreria per gestire il tempo
from PIL import Image, ImageTk  # Libreria per gestire le immagini
from tkinter import messagebox # Libreria per gestire le finestre modali di scelta
 
root = tk.Tk() # Crea il pannello di gioco che contiene i Frame con i componenti di gioco 
root.wm_title('Solitario del ferroviere')
root.geometry("1400x600")
root.resizable(False, False)
root.protocol("WM_DELETE_WINDOW", on_closing)
flag=False 
# flag=True aggiorna tempo e punteggio, flag=False aggiorna solo orologio
crea_avvio_solitario(root) # Questa funzione crea ed avvia il livello di avvio del solitario
root.mainloop()
 

Come creare il livello di avvio del gioco e rendere le immagini cliccabili

Il codice seguente mostra come caricare le immagini dei livelli di gioco ed associarle ad un evento di tipo click per avviare le funzioni: apprendistiprincipainti ed esperti.  

def crea_avvio_solitario(root):
    global frame_avvio
    global livello
    livello=0

    # Creazione Frame del livello di avvio (frame_avvio) ed associazione al pannello di gioco root
    frame_avvio=tk.Frame(root)
    frame_avvio.pack(side="top", fill="both", expand = True)
    immagine_titolo=ImageTk.PhotoImage(Image.open('titolo_solitario_del_ferroviere.png'))
    
# Caricamento immagini
    immagine_apprendisti=ImageTk.PhotoImage(Image.open('apprendisti.png'))
    immagine_principianti=ImageTk.PhotoImage(Image.open('principianti.png'))
    immagine_esperti=ImageTk.PhotoImage(Image.open('esperti.png'))

    immagine_asso=ImageTk.PhotoImage(Image.open('1coppe.png'))
    immagine_retro=ImageTk.PhotoImage(Image.open('Carte_Napoletane_retro.png'))
    label_titolo=tk.Label(frame_avvio, image=immagine_titolo)
    label_titolo.place(x=180,y=5)

   # Posizionamento delle immagini sul Frame del livello di avvio ed attivazione (bind(...)) dell'evento click alle funzioni apprendiata, principiante ed esperto  
    label_apprendisti=tk.Label(frame_avvio, image=immagine_apprendisti)
    label_apprendisti.place(x=100,y=280)
    label_apprendisti.bind("<Button>",apprendista)
    label_principianti=tk.Label(frame_avvio, image=immagine_principianti)
    label_principianti.place(x=355,y=280)
    label_principianti.bind("<Button>",principiante)
    label_esperti=tk.Label(frame_avvio, image=immagine_esperti)
    label_esperti.place(x=600,y=280)
    label_esperti.bind("<Button>",esperto)
    label_asso=tk.Label(frame_avvio, image=immagine_asso)
    label_asso.place(x=870,y=180)
    label_retro=tk.Label(frame_avvio, image=immagine_retro)
    label_retro.place(x=1100,y=180)
    label_messaggio=tk.Label(frame_avvio, text="Scegli il livello per iniziare a giocare ...",font=("times", 18,'italic'))
    label_messaggio.place(x=240,y=210)
    logo=ImageTk.PhotoImage(Image.open('logo_sf.png'))
    label_logo=tk.Label(frame_avvio, image=logo)
    label_powered_sf=tk.Label(frame_avvio, text="2020 - Powered by Skill Factory",font=("times", 14,'italic'))
    label_powered_sf.place(x=550,y=557)
    label_logo.place(x=805,y=545)
    frame_avvio.mainloop()

Come creare il primo livello di gioco

Il codice seguente mostra come creare il primo livello di gioco:

def crea_primo_livello_solitario(root):
    global frame_primo_livello_solitario
    global label_carte_small_si
    global label_carte_small_no
    global immagini_carte
    global immagini_carte_small
    global label_mazzo_di_carte
    global label_carta_girata
    global label_non_indovinate
    global label_indovinate
    global label_girate
    global label_carta_pensata
    global label_clock
    global label_titolo_tempo
    global label_titolo_tentativi
    global label_secondi
    global label_tentativi
    global label_titolo_score
    global label_score
    global pulsante_gira
    global pulsante_mischia
    global pulsante_nuova_partita

    # Creazione Frame del primo livello di gioco (frame_primo) ed associazione al pannello di gioco root
    frame_primo_livello_solitario=tk.Frame(root)
    frame_primo_livello_solitario.pack(side="top", fill="both", expand = True)
    #Orologio
    label_clock = tk.Label(frame_primo_livello_solitario, font=('arial', 14, 'italic'), bg='darkgray',fg='black')
    label_titolo_tempo=tk.Label(frame_primo_livello_solitario, text="TEMPO",font=("times", 12,"italic"),fg="black")
    label_titolo_tentativi=tk.Label(frame_primo_livello_solitario, text="TENTATIVI",font=("times", 12,"italic"),fg="black")
    label_secondi=tk.Label(frame_primo_livello_solitario, font=('arial', 14, 'normal'), bg='blue',fg='white')
    label_tentativi=tk.Label(frame_primo_livello_solitario, font=('arial', 14, 'normal'), bg='orange',fg='white')
    label_titolo_score=tk.Label(frame_primo_livello_solitario, text="SCORE",font=("times", 12,"italic"),fg="black")
    label_score=tk.Label(frame_primo_livello_solitario, font=('arial', 16, 'normal'), bg='palevioletred',fg='white')
    immagine_indovinate=ImageTk.PhotoImage(Image.open('verde.png'))
    immagine_non_indovinate=ImageTk.PhotoImage(Image.open('rosso.png'))
    label_titolo_carta_pensata=tk.Label(frame_primo_livello_solitario, text="CARTA PENSATA",font=("Helvetica", 14))
    label_titolo_mazzo_di_carte=tk.Label(frame_primo_livello_solitario, text="MAZZO DI CARTE",font=("Helvetica", 14))
    #Pulsanti per gestire il  gioco
    pulsante_cambia=tk.Button(frame_primo_livello_solitario,text="CAMBIA", width=10, height=2, bd = 3,command=cambia, bg="seagreen",font=("Helvetica", 12))
    pulsante_gira=tk.Button(frame_primo_livello_solitario,text="GIRA", width=10, height=2, bd = 3,command=gira_primo, bg="teal",font=("Helvetica", 12))
    pulsante_mischia=tk.Button(frame_primo_livello_solitario,text="MISCHIA", width=10, height=2, bd = 3,command=mischia_primo, bg="mediumpurple",font=("Helvetica", 12))
    pulsante_nuova_partita=tk.Button(frame_primo_livello_solitario,text="NUOVA PARTITA", width=15, height=2, bd = 3,command=nuova_partita_primo, bg="darkorange",font=("Helvetica", 12))

    label_girate=tk.Label(frame_primo_livello_solitario, text="   0/10  ",font=("Helvetica", 25),fg="white",bg="tomato")
    label_img_indovinate=tk.Label(frame_primo_livello_solitario, image=immagine_indovinate)
    label_indovinate=tk.Label(frame_primo_livello_solitario, text=str(indovinate),font=("Helvetica", 24))
    label_non_indovinate=tk.Label(frame_primo_livello_solitario, text=str(non_indovinate),font=("Helvetica", 24))
    label_img_non_indovinate=tk.Label(frame_primo_livello_solitario, image=immagine_non_indovinate)
    immagini_carte=carica_immagini_carte()
    label_carta_pensata=tk.Label(frame_primo_livello_solitario, image=immagini_carte[1])
    label_mazzo_di_carte=tk.Label(frame_primo_livello_solitario, image=immagini_carte[0])
    label_carta_girata=tk.Label(frame_primo_livello_solitario, image=immagini_carte[0])
    immagini_carte_small=carica_immagini_carte_small()
    logo=ImageTk.PhotoImage(Image.open('logo_sf.png'))
    label_logo=tk.Label(frame_primo_livello_solitario, image=logo)
    label_powered_sf=tk.Label(frame_primo_livello_solitario, text="2020 - Powered by Skill Factory",font=("times", 14,'italic'))
    label_clock.place(x=1300,y=5)
    pulsante_cambia.place(x=60,y=370)
    label_titolo_carta_pensata.place(x=25,y=5)
    label_carta_pensata.config(image=immagini_carte[1])
    label_carta_pensata.place(x=5,y=40)
    label_titolo_mazzo_di_carte.place(x=260,y=5)
    label_mazzo_di_carte.place(x=240,y=40)
    pulsante_gira.place(x=285,y=370)
    label_girate.config(text="   0/"+str(len(mazzo_carte))+"  ")
    label_girate.place(x=500,y=370)
    label_img_indovinate.place(x=750,y=60)
    label_indovinate.config(text=str(indovinate))
    label_indovinate.place(x=820,y=65)
    label_img_non_indovinate.place(x=750,y=150)
    label_non_indovinate.config(text=str(non_indovinate))
    label_non_indovinate.place(x=820,y=158)
    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)
    label_titolo_score.place(x=280,y=455)
    label_score.place(x=280,y=480)
    label_powered_sf.place(x=550,y=557)
    label_logo.place(x=805,y=545)
    start_primo()
   # Se il flag avviaTick e True viene eseguita la funzione Tick(). Questa funzione ogni 200 millisecondi aggiorna l'orologio, il tempo trascorso ed il punteggio
    if avviaTick:
        tick()
    frame_primo_livello_solitario.mainloop()

Come creare gli altri  livello di gioco

Il codice seguente mostra come creare gli  altri livelli di gioco:

def crea_altri_livelli_solitario(root):
    global frame_altri_livelli_solitario
    global label_mazzo_di_carte
    global label_carta_girata
    global label_girate
    global label_carta_pensata
    global label_clock
    global label_titolo_tempo
    global label_titolo_tentativi
    global label_secondi
    global label_tentativi
    global label_titolo_score
    global label_score
    global pulsante_gira
    global pulsante_mischia
    global pulsante_nuova_partita
    global immagini_carte
    global label_titolo_carta_da_indovinare
    global label_titolo_carte_indovinate
    global label_titolo_carte_non_indovinate
    global label_indovinate
    global label_non_indovinate
    global label_carta_indovinata
    global label_carta_non_indovinata
    global label_carte_da_indovinare
    global label_carta_non_indovinata_retro
    global livello

    # Creazione Frame per il secondo e terzo livello di gioco (frame_altri_livelli) ed associazione al pannello di gioco root
    frame_altri_livelli_solitario=tk.Frame(root)
    frame_altri_livelli_solitario.pack(side="top", fill="both", expand = True)
    label_titolo_carte_indovinate=tk.Label(frame_altri_livelli_solitario, text="CARTE INDOVINATE",font=("Helvetica", 14),fg="green")
    label_titolo_carte_non_indovinate=tk.Label(frame_altri_livelli_solitario, text="CARTE NON INDOVINATE",font=("Helvetica", 14),fg="red")
    label_indovinate=tk.Label(frame_altri_livelli_solitario, text="    0    ",font=("Helvetica", 25),fg="white",bg="green")
    label_non_indovinate=tk.Label(frame_altri_livelli_solitario, text="    0    ",font=("Helvetica", 25),fg="white",bg="red")
    #Orologio
    label_clock = tk.Label(frame_altri_livelli_solitario, font=('arial', 14, 'italic'), bg='darkgray',fg='black')
    label_titolo_tempo=tk.Label(frame_altri_livelli_solitario, text="TEMPO",font=("times", 12,"italic"),fg="black")
    label_titolo_tentativi=tk.Label(frame_altri_livelli_solitario, text="TENTATIVI",font=("times", 12,"italic"),fg="black")
    label_secondi=tk.Label(frame_altri_livelli_solitario, font=('arial', 14, 'normal'), bg='blue',fg='white')
    label_tentativi=tk.Label(frame_altri_livelli_solitario, font=('arial', 14, 'normal'), bg='orange',fg='white')
    label_titolo_score=tk.Label(frame_altri_livelli_solitario, text="SCORE",font=("times", 12,"italic"),fg="black")
    label_score=tk.Label(frame_altri_livelli_solitario, font=('arial', 16, 'normal'), bg='palevioletred',fg='white')
    label_titolo_carta_pensata=tk.Label(frame_altri_livelli_solitario, text="CARTA PENSATA",font=("Helvetica", 14))
    label_titolo_mazzo_di_carte=tk.Label(frame_altri_livelli_solitario, text="MAZZO DI CARTE",font=("Helvetica", 14))
    
#Pulsanti per gestire il gioco
    pulsante_cambia=tk.Button(frame_altri_livelli_solitario,text="CAMBIA", width=10, height=2, bd = 3,command=cambia, bg="seagreen",font=("Helvetica", 12))
    pulsante_gira=tk.Button(frame_altri_livelli_solitario,text="GIRA", width=10, height=2, bd = 3,command=gira_altri, bg="teal",font=("Helvetica", 12))
    pulsante_mischia=tk.Button(frame_altri_livelli_solitario,text="MISCHIA", width=10, height=2, bd = 3,command=mischia_altri, bg="mediumpurple",font=("Helvetica", 12))
    pulsante_nuova_partita=tk.Button(frame_altri_livelli_solitario,text="NUOVA PARTITA", width=15, height=2, bd = 3,command=nuova_partita_altri, bg="darkorange",font=("Helvetica", 12))

    label_girate=tk.Label(frame_altri_livelli_solitario, text="   0/10  ",font=("Helvetica", 25),fg="white",bg="tomato")
    immagini_carte=carica_immagini_carte()
    label_carta_pensata=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[1])
    label_mazzo_di_carte=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
    label_carta_girata=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
    label_carte_da_indovinare=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
    label_carta_girata=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
    logo=ImageTk.PhotoImage(Image.open('logo_sf.png'))
    label_logo=tk.Label(frame_altri_livelli_solitario, image=logo)
    label_powered_sf=tk.Label(frame_altri_livelli_solitario, text="2020 - Powered by Skill Factory",font=("times", 14,'italic'))
    label_clock.place(x=1300,y=5)
    pulsante_cambia.place(x=60,y=370)
    label_titolo_carta_pensata.place(x=25,y=5)
    label_carta_pensata.config(image=immagini_carte[1])
    label_carta_pensata.place(x=5,y=40)
    label_titolo_mazzo_di_carte.place(x=260,y=5)
    label_mazzo_di_carte.place(x=240,y=40)
    pulsante_gira.place(x=285,y=370)
    label_girate.config(text="   0/"+str(len(mazzo_carte))+"  ")
    label_girate.place(x=500,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)
    label_titolo_score.place(x=280,y=455)
    label_score.place(x=280,y=480)
    label_powered_sf.place(x=550,y=557)
    label_logo.place(x=805,y=545)
    label_carta_indovinata=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
    label_carta_non_indovinata=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
    if livello==2:
        label_carta_non_indovinata=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
        label_carta_non_indovinata.bind("<Button>",gira_carta_non_indovinata)
        immagine_help=ImageTk.PhotoImage(Image.open('help.png'))
        label_carta_non_indovinata_help=tk.Label(frame_altri_livelli_solitario, image=immagine_help)
        label_carta_non_indovinata_help.place(x=1230,y=150)
        label_carta_non_indovinata_help.bind("<Button>",help_frame_show)
        label_carta_non_indovinata_retro=tk.Label(frame_altri_livelli_solitario, image=immagini_carte[0])
        label_carta_non_indovinata_retro.bind("<Button>",gira_carta_non_indovinata_back)
        label_carta_non_indovinata_help.place(x=1230,y=150)
        label_carta_non_indovinata_help.bind("<Button>",help_frame_show)
    start_altri()
   # Se il flag avviaTick e True viene eseguita la funzione Tick(). Questa funzione ogni 200 millisecondi aggiorna l'orologio, il tempo trascorso ed il punteggio
    if avviaTick:
        tick()
    frame_altri_livelli_solitario.mainloop()

 

Come abbandonare il Solitario oppure un livello di gioco

Per abbandonare il livello di gioco scelto oppure uscire dal Solitario, basta cliccare sull'icona close in alto a destra del Pannello di gioco.  

La finestra modele seguente permette di abbandonare un livello di gioco.

La finestra modele seguente permette di uscire dal Solitario.

Per associare all'icona close del Pannello di gioco la funzione per abbandonare il livello di gioco oppure il Solitario  abbiamo usato il codice seguete:

root.protocol("WM_DELETE_WINDOW", on_closing)

def on_closing():
    if livello==0:
        if messagebox.askokcancel("EXIT", "Vuoi uscire dal solitario?"):
            root.destroy()
    else:
        if messagebox.askokcancel("EXIT", "Vuoi abbandonare il gioco?"):
            torna_avvio_solitario()

La funzione torna_avvio_solitario() permette di abbandonare il livello di gioco corrente e tornare al livello di avvio. 

   def torna_avvio_solitario():
    global mazzo_carte
    global mazzo_carte_non_indovinate
    global indovinate
    global non_indovinate
    global offset_indovinate
    global offset_non_indovinate
    global tentativi
    global score
    global conta_score
    global time_start
    global livello
    global flag
    if livello==1:
        frame_primo_livello_solitario.pack_forget()
        frame_primo_livello_solitario.quit()
    elif livello>1:
        frame_altri_livelli_solitario.pack_forget()
        frame_altri_livelli_solitario.quit()
    mazzo_carte=[1,2,3,4,5,6,7,8,9,10]
    mazzo_carte_non_indovinate.clear()
    livello=0
    flag=False
    indovinate=0
    offset_indovinate=50
    non_indovinate=0
    offset_non_indovinate=50
    tentativi=1
    score=1000
    conta_score=0
    time_start=0
    crea_avvio_solitario(root)

Descrizione delle Funzioni usate nel gioco 

def tick(): Aggiorna l'orologio, il tempo ed il punteggio ogni 200 millisecondi
def suono(sound,tempo): Gestisce i suoni del gioco
def on_closing(): Associa all'icona close del Pannello di gioco la funzione per abbandonare il livello di gioco oppure il Solitario
def mischia_carte(mazzo): Mischia le carte del mazzo centrale
def carica_immagini_carte(): Carica le immagini del gioco
def carica_immagini_carte_small(): Carica le immagini del primo livello di gioco
def cambia(): Permette di scegliere la carta pensata
def gira_primo(): Gira le carte del mazzo centrale del primo livello di gioco
def gira_altri(): Gira le carte del mazzo centrale del secondo e terzo livello di gioco
def nuova_partita_primo(): Avvia una nuova partita nel primo livello di gioco
def nuova_partita_altri(): Avvia una nuova partita nel secondo e terzo livello di gioco
def mischia_primo(): Mischia le carte nel primo livello di gioco
def mischia_altri(): Mischia le carte nel secondo e terzo livello di gioco
def torna_avvio_solitario(): Abbandona il livello di gioco corrente e torna al livello di avvio
def gira_carta_non_indovinata(event): Gira le carte del mazzo di carte non indovinate
def gira_carta_non_indovinata_back(event): Gira le carte del mazzo di carte non indovinate
def ripristina_carte_non_indovinate(): Ripristina il mazzo di carte non indovinate se si gira una carta del mazzo centrale
def help_frame_hidden(): Nasconde l'help del secondo livello di gioco
def help_frame_show(event): Mostra l'help del secondo livello di gioco
def apprendista(event): Avvia il primo livello di gioco
def principiante(event): Avvia il secondo livello di gioco
def esperto(event): Avvia il terzo livello di gioco
def crea_avvio_solitario(root): Avvia il livello di avvio del solitario
def crea_primo_livello_solitario(root): Crea il frame con tutti i componenti del primo livello di gioco
def crea_altri_livelli_solitario(root): Crea il frame con tutti i componenti del secondo e terzo livello di gioco
def start_primo(): Inizializza il primo livello di gioco
def start_altri(): Inizializza il secondo e terzo livello di gioco

 

Conclusioni

Con questa lezione il gioco del "Solitario del Ferroviere" è finito, sicuramente può essere migliorato e possono essere aggiunte ultriori funzionalità, come ad esempio la registrazione dei punteggi migliori raggiunti, per creare una classifica dei giocatori più bravi.

Il gioco può essere compilato per ottenere un eseguibile che vi permetterà di giocare senza dover usare l'interprete Python.   

Per compilare uno script Python potete installare il tool pyinstaller, con il comando:

pip install pyinstaller

Per compilare lo script dovete usare il comando:

pyinstaller solitario_del_ferroviere.py --noconsole

Dopo la compilazione travate tutto quello che vi serve nella cartella \dist\solitario_del_ferroviere.

Prima di lanciare il file eseguibile solitario_del_ferroviere.exe, dovete caricare nella cartella \dist\solitario_del_ferroviere tutte le immagini ed i file audio.

Buon divertimento ed arrivederci alla prossima lezione!

 


<< Lezione precedente           Lezione successiva >>



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

 



 

Share Button

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

Gino Visciano | Skill Factory - 06/01/2020 22:33:30 | in Tutorials

Nella lezione precedente abbiamo completato il primo livello del "Solitario del Ferroviere", questo livello è stato creato per gli apprendisti che intendono imparare a giocare, senza grosse difficoltà. La caratteristica principale è quella che sia le carte indovinate, sia quelle non indovinate sono scoperte, in modo da rendere più facile la scelta della carta da pensare,  come mostra l'immagine seguente.

In questa lezione completiamo il secondo livello del "Solitario del Ferroviere", creato per i principianti. A differenza del livello precedente, qui sia le carte indovinate, sia quelle non indovinate sono coperte, come mostra l'immagine seguente:

 

A cosa serve l'icona Help me!

L'icona Help me! segnala al giocatore come ottenere un aiuto per ricordare quali sono le le carte non indovinate.

Cliccando sull'icona Help me! appare la finestra seguente:

Il messaggio di aiuto indica al giocatore come vedere le carte non indovinate:

L'immagine Help me! sparisce automaticamente dopo 5 secondi. Per ottenere questo effetto, sono state utilizzate le funzioni seguenti:

# Disattiva la finestra Help me!
def help_frame_hidden():
   global help_show
   help_show.place_forget()

# Attiva la finestra Help me!
def help_frame_show(event):
    global help_show
    help_show=tk.Frame(root)
    label_carta_non_indovinata_help_show=tk.Label(help_show, image=immagine_help_show)
    label_carta_non_indovinata_help_show.pack()
    help_show.place(x=1000,y=60)
    suono(11,0.1)

    # Dopo 5000 millisecondi (5 secondi) esegue la funzione help_frame_hidden
    help_show.after(5000,help_frame_hidden)

 

Come rendere cliccabile l'icona Help me

L'icona Help me, corrisponde all'immagine seguente:

 

Per renderla cliccabile è stato utilizzato il codice seguente:

immagine_help=ImageTk.PhotoImage(Image.open('help.png'))                     # Carica l'immagine dell'icona
label_carta_non_indovinata_help=tk.Label(root, image=immagine_help)     
# Associa l'immagine dell'icona all'etichetta
label_carta_non_indovinata_help.place(x=1230,y=150)                                    # Visualizza l'etichetta con l'immagine dell'icona sul piano di gioco
# La funzione bind permette di associare all'etichetta l'evento click (<button> ) per eseguire la funzione help_frame_show
label_carta_non_indovinata_help.bind("<Button>",help_frame_show)          

 

Come consultare le carte sbagliate cliccando sul mazzo delle carte non indovinate

Per poter gestire questa funzionalità è stato applicato l'algoritmo seguente:

Inizio (Azione 1)
  Mentre clicchi su una carta del mazzo
     Se carte del mazzo non indivinate>1 allora
          Se non è l'ultima carta del mazzo allora
               Se è la prima carta allora
                    visualizza carta precedente
                    visualizza retro carta a desta
              Altrimenti
                   visualizza carta precedente
              Fine se
          Fine se
     Fine se
  Fine Mentre
Fine

 

Inizio (Azione 2)
  Mentre clicchi sul retro della carta a destra
     Se la carte del mazzo visualizzata non è la penultima allora
          Visualizza la carta precedente a quella visualizzata
     Altrimenti
          visualizza la prima carta del mazzo
          nascondi il retro della carta a destra
     Fine se
  Fine Mentre
Fine

 
Per poter implemantare questo algoritmo, prima di tutto bisogna associare l'evento click alle etichette seguenti: 

label_carta_non_indovinata.bind("<Button>",gira_carta_non_indovinata)
label_carta_non_indovinata_retro.bind("<Button>",gira_carta_non_indovinata_back)

In questo modo, quando si clicca su una carta del mazzo delle carte non indovinate, si attiva la funzione gira_carta_non_indovinata, quando si clicca  sul retro, si attiva la funzione gira_carta_non_indovinata_back. Le due funzioni permettono di vedere tutte le carte del mazzo delle carte non indovinate.   

# Ad ogni click gira le carte del mazzo e mostra la carta successiva
def gira_carta_non_indovinata(event):
    global indice_gira_carta_non_indovinata
    if len(mazzo_dx)>1 and indice_gira_carta_non_indovinata>0:
       label_carta_non_indovinata_retro.place(x=1175,y=40)
       indice_gira_carta_non_indovinata-=1 
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)
       suono(2,0.1)
    else:
       suono(6,0.1)


# Ad ogni click gira le carte del mazzo e mostra la carta precedente
def gira_carta_non_indovinata_back(event):
    global mazzo_dx
    global indice_gira_carta_non_indovinata
    if indice_gira_carta_non_indovinata<len(mazzo_dx)-2:
       indice_gira_carta_non_indovinata+=1
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)
    else:
       label_carta_non_indovinata_retro.place_forget()
       indice_gira_carta_non_indovinata+=1 
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)
    suono(1,0.1) 

 

 

Come ripristinare le carte del mazzo carte non indovinate quando si gira una nuova carta dal mazzo centrale

Quando viene girata una nuova carta dal mazzo centrale, il mazzo delle carte non indovinate deve essere ripristinato, per ottenere questo risultato è stata implementata la funzione seguente: 

def ripristina_carte_non_indovinate():
    if len(mazzo_dx)>1:
       label_carta_non_indovinata_retro.place_forget()
       indice_gira_carta_non_indovinata=len(mazzo_dx)-1 
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)

 

Analisi del codice Python

# Solitario del ferroviere (Lezione 4)
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 
from tkinter import messagebox   # Dalla libreira tkinter importa le classe messagebox usata per creare la finestra modale "GAME OVER"

mazzo_centrale=[1,2,3,4,5,6,7,8,9,10]        # Lista con i valori delle carte (mazzo di carte centrale)
mazzo_sx=[]                                                   # Lista con i valori delle carte (mazzo di carte indovinate) 
mazzo_dx=[]                                                   
# Lista con i valori delle carte (mazzo di carte non indovinate) 
immagini_carte=[]                                         
# Lista con i riferimenti delle immagini delle carte 
indice_carta_pensata=0
indice_carta_girata=0
indice_gira_carta_non_indovinata=0
time1=0
time_start=0
tentativi=1
score=1000
conta_score=0
flag=True                                                      
# Abilita (True) e disabilita (False) l'aggiornamento dei secondi e del punteggio
help_show=None                                        # Riferimento della finestra Help me!

# Termina l'applicazione 
def esci():
   root.quit()

# Ogni 200 millisecondi aggiorna secondi, tentativi, punteggio ed orologio
def tick():
    global time1
    global time_start
    global tentativi
    global score
    global conta_score
    global flag
    # legge ora locale del pc
    if flag: # Abilita/Disabilita aggiornamento secondi e punteggio
         time2 = time.strftime('%H:%M:%S')
         if time2 != time1:
            time1 = time2
            label_clock.config(text=time2)
            secondi=int(round(time.time()-time_start,0))
            conta_score+=1
            if conta_score>10:
               score=score-1
               conta_score=0
            if score>0:
               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)+" ")
               spazi="          "[:(10-len(str(score)))]
               label_score.config(text=spazi+str(score)+" ")
            else:
               label_score.config(text="          0 ")
               info=messagebox.showinfo('Fine Partita', 'GAME OVER')
               esci()

    # chiama se stessa (ricorsiva) ogni 200 milliseconds
    label_clock.after(200, tick)

 

# Mischia i valori delle carte del mazzo centrale
def mischia_carte(mazzo):
    temp=0
    max=len(mazzo)-1
    for i in range(1,100):
        prima=random.randint(0,max)
        seconda=random.randint(0,max)
        if prima!=seconda:
            temp=mazzo[prima]
            mazzo[prima]=mazzo[seconda]
            mazzo[seconda]=temp

# Gestisce i suoni e le durate
def suono(sound,tempo):
    if sound==1:
       pygame.mixer_music.load("Click.wav")
    elif sound==2:
       pygame.mixer_music.load("PenSound.wav")
    elif sound==3:
       pygame.mixer_music.load("ToggleSound.wav")
    elif sound==4:
       pygame.mixer_music.load("TickSound.mp3")
    elif sound==5:
       pygame.mixer_music.load("ToneSound.wav")
    elif sound==6:
       pygame.mixer_music.load("PlingSound.wav")
    elif sound==7:
       pygame.mixer_music.load("OvationSound.wav")
    elif sound==8:
       pygame.mixer_music.load("ElectronicSound.wav")
    elif sound==9:
       pygame.mixer_music.load("CardSound.wav")
    elif sound==10:
       pygame.mixer_music.load("BranchSound.wav")
    elif sound==11:
       pygame.mixer_music.load("smsalertSound.wav")
    elif sound==12:
       pygame.mixer_music.load("ComputerSound.wav")
    pygame.mixer_music.play()
    time.sleep(tempo)    
    

 

# Carica le immagini delle carte ed aggiunge i riferimenti alla 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

Si attiva quando si clicca sul pulsante gira, alza le carte del mazzo centrale e controlla se la carta alzata è uguale alla carta pensata
def gira():
    global indice_carta_girata
    global indice_carta_pensata
    global indice_gira_carta_non_indovinata
    global mazzo_centrale
    global flag
    ripristina_carte_non_indovinate()
    max=len(mazzo_centrale)-1
    if indice_carta_pensata==1:
       carta_pensata=10
    else:
       carta_pensata=indice_carta_pensata-1
    label_carta_girata.config(image=immagini_carte[mazzo_centrale[indice_carta_girata]])
    label_carta_girata.place(x=475,y=40)
    girate="   "+str(indice_carta_girata+1)+"/"+str(max+1)+"  "
    label_girate.config(text=girate)
    label_girate.place(x=520,y=370)
    if carta_pensata==mazzo_centrale[indice_carta_girata]:
       mazzo_sx.append(mazzo_centrale[indice_carta_girata])
       indovinate= "    "+str(len(mazzo_sx))+"    "
       if len(mazzo_sx)==10:
          indovinate=indovinate[:9]
       label_indovinate.config(text=indovinate)
       label_indovinate.place(x=765,y=370)
       label_carta_indovinata.config(image=immagini_carte[mazzo_centrale[indice_carta_girata]])
       label_carta_indovinata.place(x=710,y=40)
       suono(5,0.1)
    else:
       mazzo_dx.append(mazzo_centrale[indice_carta_girata])
       indice_gira_carta_non_indovinata=len(mazzo_dx)-1
       non_indovinate= "    "+str(len(mazzo_dx))+"    "
       if len(mazzo_dx)==10:
          non_indovinate=non_indovinate[:9]
       label_non_indovinate.config(text=non_indovinate)
       label_non_indovinate.place(x=1000,y=370)
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_centrale[indice_carta_girata]])
       label_carta_non_indovinata.place(x=945,y=40)
       suono(3,0.1)
    if indice_carta_girata<max:
       indice_carta_girata=indice_carta_girata+1
    else:
       label_carte_da_indovinare.place_forget()
       label_carta_girata.place_forget()
       pulsante_gira.place_forget()
       label_girate.place_forget()
       indice_carta_girata=0
       if(len(mazzo_dx)>0):
          suono(8,0.5)
          pulsante_mischia.place(x=290,y=370)
       else:
          suono(7,0.3)
          pulsante_nuova_partita.place(x=290,y=370)
          flag=False

Si attiva quando si clicca sul pulsante cambia, permette di scegliere la carta pensata
def cambia():
    global indice_carta_pensata
    label_carta_pensata.config(image=immagini_carte[indice_carta_pensata])
    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
    suono(4,0.1)

 

# Si attiva quando si clicca sul pulsante mischia, avvia tutta la procedura per mischiare le carte
def mischia():
    global mazzo_centrale
    global indice_carta_pensata
    global tentativi
    global score
    suono(9,4)
    tentativi=tentativi+1
    score=int(score/2)
    mazzo_centrale.clear()
    mazzo_centrale=mazzo_dx[:]
    ripristina_carte_non_indovinate()
    mazzo_dx.clear()
    mischia_carte(mazzo_centrale)
    label_carta_girata.place_forget()
    label_carta_non_indovinata.place_forget()
    label_carta_pensata.config(image=immagini_carte[1])
    indice_carta_pensata=2
    label_carta_girata.config(image=immagini_carte[0])
    label_carte_da_indovinare.place(x=245,y=40)
    label_non_indovinate.config(text="    0    ")
    label_non_indovinate.place(x=1000,y=370)
    girate="   0/"+str(len(mazzo_centrale))+"  "
    label_girate.config(text=girate)
    label_girate.place(x=520,y=370)
    pulsante_gira.place(x=290,y=370)
    pulsante_mischia.place_forget()
    

# Si attiva quando si clicca sul pulsante nuova_partita, avvia tutta la procedura per iniziare una nuova partita
def nuova_partita():
   global mazzo_dx
   global mazzo_sx
   global mazzo_centrale
   global indice_carta_pensata
   global indice_carta_girata
   global indice_gira_carta_non_indovinata
   global tentativi
   global score
   global conta_score
   global time_start
   global flag
   mazzo_centrale=[1,2,3,4,5,6,7,8,9,10]
   mazzo_sx=[]
   ripristina_carte_non_indovinate()
   mazzo_dx=[]
   indice_carta_pensata=0
   indice_carta_girata=0
   indice_gira_carta_non_indovinata=0
   tentativi=1
   score=1000
   conta_score=0
   flag=True
   time_start=time.time()
   label_carta_indovinata.place_forget()
   label_carta_non_indovinata.place_forget()
   label_carta_girata.place_forget()
   label_carta_pensata.config(image=immagini_carte[1])
   indice_carta_pensata=2
   label_carta_girata.config(image=immagini_carte[0])
   label_carte_da_indovinare.place(x=245,y=40)
   label_indovinate.config(text="    0    ")
   label_indovinate.place(x=765,y=370)
   label_non_indovinate.config(text="    0    ")
   label_non_indovinate.place(x=1000,y=370)
   girate="   0/"+str(len(mazzo_centrale))+"  "
   label_girate.config(text=girate)
   label_girate.place(x=520,y=370)
   pulsante_gira.place(x=290,y=370)
   pulsante_nuova_partita.place_forget()
   mischia_carte(mazzo_centrale)
   suono(12,2.5)

# Si attiva quando si clicca sulla carta del mazzo carte non indovinate, mostra la carta precedente se disponibile
def gira_carta_non_indovinata(event):
    global indice_gira_carta_non_indovinata
    if len(mazzo_dx)>1 and indice_gira_carta_non_indovinata>0:
       label_carta_non_indovinata_retro.place(x=1175,y=40)
       indice_gira_carta_non_indovinata-=1 
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)
       suono(2,0.1)
    else:
       suono(6,0.1)


# Si attiva quando si clicca sul retro di una carta del mazzo carte non indovinate, riscopre le carte girate
def gira_carta_non_indovinata_back(event):
    global mazzo_dx
    global indice_gira_carta_non_indovinata
    if indice_gira_carta_non_indovinata<len(mazzo_dx)-2:
       indice_gira_carta_non_indovinata+=1
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)
    else:
       label_carta_non_indovinata_retro.place_forget()
       indice_gira_carta_non_indovinata+=1 
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)
    suono(1,0.1) 

def ripristina_carte_non_indovinate():
    if len(mazzo_dx)>1:
       label_carta_non_indovinata_retro.place_forget()
       indice_gira_carta_non_indovinata=len(mazzo_dx)-1 
       label_carta_non_indovinata.config(image=immagini_carte[mazzo_dx[indice_gira_carta_non_indovinata]])
       label_carta_non_indovinata.place(x=945,y=40)
       

# Disattiva la finestra Help me!
def help_frame_hidden():
   global help_show
   help_show.place_forget()

# Attiva la finestra Help me!
def help_frame_show(event):
    global help_show
    help_show=tk.Frame(root)
    label_carta_non_indovinata_help_show=tk.Label(help_show, image=immagine_help_show)
    label_carta_non_indovinata_help_show.pack()
    help_show.place(x=1000,y=60)
    suono(11,0.1)
    help_show.after(5000,help_frame_hidden)

# Inizia una nuova partita
def start():
    global indice_carta_pensata
    global indice_carta_girata
    global time_start
    pulsante_gira.place(x=290,y=370)
    pulsante_cambia.place(x=60,y=370)
    label_titolo_carta_pensata.place(x=25,y=5)
    label_carta_pensata.place(x=5,y=40)
    label_carte_da_indovinare.place(x=245,y=40)
    label_titolo_carta_da_indovinare.place(x=230,y=5)
    label_titolo_carte_indovinate.place(x=710,y=5)
    label_titolo_carte_non_indovinate.place(x=920,y=5)
    label_girate.place(x=520,y=370)
    label_indovinate.place(x=765, y=370)
    label_non_indovinate.place(x=1000, y=370)
    label_clock.place(x=1300,y=5)
    if time_start==0:
       time_start = time.time()
    label_titolo_tempo.place(x=30,y=490)
    label_secondi.place(x=30,y=515)
    label_tentativi.place(x=160,y=515)
    label_titolo_tentativi.place(x=160,y=490)
    label_titolo_score.place(x=280,y=490)
    label_score.place(x=280,y=515)
    pygame.init()
    indice_carta_pensata=2
    indice_carta_girata=0

# Preparazione pannello di gioco
root = tk.Tk()
root.wm_title('Solitario del ferroviere')
root.geometry("1400x600")
root.resizable(False, False)
immagine_help=ImageTk.PhotoImage(Image.open('help.png'))
label_carta_non_indovinata_help=tk.Label(root, image=immagine_help)
immagine_help_show=ImageTk.PhotoImage(Image.open('help_show.png'))
pulsante_gira=tk.Button(root,text="GIRA", width=10, height=2, bd = 3,command=gira, bg="teal",font=("Helvetica", 12))
pulsante_cambia=tk.Button(root,text="CAMBIA", width=10, height=2, bd = 3,command=cambia, bg="seagreen",font=("Helvetica", 12))
pulsante_mischia=tk.Button(root,text="MISCHIA", width=10, height=2, bd = 3,command=mischia, bg="mediumpurple",font=("Helvetica", 12))
pulsante_nuova_partita=tk.Button(root,text="NUOVA PARTITA", width=15, height=2, bd = 3,command=nuova_partita, bg="darkorange",font=("Helvetica", 12))
label_titolo_carta_pensata=tk.Label(root, text="CARTA PENSATA",font=("Helvetica", 14))
label_titolo_carta_da_indovinare=tk.Label(root, text="CARTE DA INDOVINARE",font=("Helvetica", 14))
label_titolo_carte_indovinate=tk.Label(root, text="CARTE INDOVINATE",font=("Helvetica", 14),fg="green")
label_titolo_carte_non_indovinate=tk.Label(root, text="CARTE NON INDOVINATE",font=("Helvetica", 14),fg="red")
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_titolo_score=tk.Label(root, text="SCORE",font=("times", 12,"italic"),fg="black")
label_indovinate=tk.Label(root, text="    0    ",font=("Helvetica", 25),fg="white",bg="green")
label_powered_sf=tk.Label(root, text="2020 - Powered by Skill Factory",font=("times", 14,'italic'))
label_non_indovinate=tk.Label(root, text="    0    ",font=("Helvetica", 25),fg="white",bg="red")
label_girate=tk.Label(root, text="   0/10  ",font=("Helvetica", 25),fg="white",bg="tomato")
label_clock = tk.Label(root, font=('arial', 16, 'italic'), bg='darkgray',fg='black')
label_secondi=tk.Label(root, font=('arial', 16, 'normal'), bg='blue',fg='white')
label_tentativi=tk.Label(root, font=('arial', 16, 'normal'), bg='orange',fg='white')
label_score=tk.Label(root, font=('arial', 16, 'normal'), bg='palevioletred',fg='white')
immagini_carte=carica_immagini_carte()
logo=ImageTk.PhotoImage(Image.open('logo_sf.png'))
label_logo=tk.Label(root, image=logo)
label_carta_pensata=tk.Label(root, image=immagini_carte[1])
label_carte_da_indovinare=tk.Label(root, image=immagini_carte[0])
label_carta_girata=tk.Label(root, image=immagini_carte[0])
label_carta_indovinata=tk.Label(root, image=immagini_carte[0])
label_carta_non_indovinata=tk.Label(root, image=immagini_carte[0])
label_carta_non_indovinata.bind("<Button>",gira_carta_non_indovinata)
label_carta_non_indovinata_retro=tk.Label(root, image=immagini_carte[0])
label_carta_non_indovinata_retro.bind("<Button>",gira_carta_non_indovinata_back)
label_powered_sf.place(x=550,y=557)
label_logo.place(x=805,y=545)
label_carta_non_indovinata_help.place(x=1230,y=150)
label_carta_non_indovinata_help.bind("<Button>",help_frame_show)
tick()
start()
mischia_carte(mazzo_centrale)
root.mainloop()

Nella prossima lezione implementeremo le funzionalità  per creare il terzo livello di difficoltà del "Solitario del Ferroviere", creato per gli esperti. Per questo livello di gioco non è previsto nessun aiuto, dipende tutto dalla capacità del giocatore che deve ricordare tutte le carte indovinate e non indovinate.

Arrivederci ...


<< Lezione precedente           Lezione successiva >>



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

 



 

Share Button

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

Gino Visciano | Skill Factory - 01/01/2020 21:44:25 | in Tutorials

Nella quarta lezione creiamo il primo livello del "Solitario del Ferroviere" con cui realmente potete iniziare a giocare, con questo livello di difficoltà è più semplice giocare perché  le carte indovinate e quelle non indovinate sono scoperte, in modo da rendere più facile la scelta della carta da pensare,  come mostra l'immagine seguente.

Il pulsante cambia, come abbiamo visto nella prima lezione, permette di scegliere la carta da pensare, mentre il  pulsante gira, come abbiamo visto nella seconda lezione e nella terza lezione permette di girare le carte del mazzo. Quando la carta pensata è uguale alla carta girata, l'immagine small della carta indovinata viene aggiunta in corrispondenza del cerchio verde, altrimenti viene aggiunta in corrispondenza del cerchio rosso.  

Quando le carte da girare finiscono, se ci sono carte non indovinate, è possibie mischiare e riprendere il gioco, come mostra l'immagine seguente.

Il gioco finisce quando s'indovinano tutte le carte, in quel caso è possibile iniziare una nuova partita, come mostra l'immagine seguente.

Col passare del tempo i punti diminuiscono ed ogni volta che le carte vengono mischiate il punteggio si dimezza. Se il punteggio arriva a 0 senza aver completato il solitario, appare la finestra modale (modale=impedisce qualunque operazione e permette solo di uscire), "GAME OVER" che indica la fine del gioco, come mostra l'immagine seguente.

Come aggiornare il punteggio

Il punteggio (score) all'inizio del gioco vale 1000, questo valore si decrementa di uno ogni 10 secondi e viene dimezzato ogni volta che si mischiano le carte.

Per gestire l'aggiornamento del punteggio è stata utilizzata la funzione tick() che viene eseguita ogni 200 millisecondi:

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

Per decrementare il punteggio ogni 10 secondi si usa la variabile conta_score. Questa variabile  s'incrementa di uno, ogni volta che viene eseguita la funzione tick(), quando diventa uguale a 50, significa che sono trascorsi 10 secondi (1 secondo=5 x 200 millisecondi => 10 secondi/200 millisecondi = 50) e viene decrementato il punteggio di uno, come mostra il blocco di codice segunte, inserito nella funzione tick():

conta_score+=1
if conta_score>50:
   score=score-1
   conta_score=0

Per dimezzare il punteggio ogni volta che le carte vengono mischiate, è stata inserita l'istruzione score=int(score/2) nella funzione mischia(), come mostra il blocco di codice seguente:

def mischia():
    ...
    global tentativi
    global score
    tentativi=tentativi+1
    score=int(score/2)
    ...

 

Come visualizzzare le carte small indovinate e non indovinate

La visualizzazione delle carte small indovinate oppure non indovinate serve per rendere il livello di difficolta del solitario più semplice, perché è più facile scegliere la carta da pensare

Per caricare le carte small abbiamo usato la funzione carica_immagini_carte_small(), che aggiunge i riferimenti delle immagini nella lista immagini_carte_small e restituisce il riferimento della lista creata. 

def carica_immagini_carte_small():
    immagini_carte_small=[]
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('1coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('2coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('3coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('4coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('5coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('6coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('7coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('8coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('9coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('10coppe_small.png')))
    return immagini_carte_small

Per visualizzare le immagini small, ogni volta che si gira una carta, viene creata un'etichetta label_img_carta_indovinata se la carta è stata indovinata, altrimenti   label_img_carta_non_indovinata, se la carta non è stata indovinata.
Le etichette usate per visualizzare le carte small sul piano di gioco, si trovano ad una distanza di 50 Pixel, l'una dall'altra. La posizione corrente viene indicata dalle variabili offset_indovinate ed offset_non_indovinate, come mostral'immagine seguente:

Il blocco di codice seguente, incluso nella funzione gira(), mostra come sono gestite le carte small:

    if carta_pensata==mazzo_carte[indice_carta_girata]:
        label_img_carta_indovinata=tk.Label(root, image=immagini_carte_small[mazzo_carte[indice_carta_girata]-1])
        label_img_carta_indovinata.place(x=820+offset_indovinate,y=60)
        offset_indovinate=offset_indovinate+50
        indovinate=indovinate+1
        label_indovinate.config(text=str(indovinate))
        label_indovinate.place(x=820,y=65)
        label_carte_small_si.append(label_img_carta_indovinata)
        suono(5,0.1)
    else:
        label_img_carta_non_indovinata=tk.Label(root, image=immagini_carte_small[mazzo_carte[indice_carta_girata]-1])
        label_img_carta_non_indovinata.place(x=820+offset_non_indovinate,y=153)
        offset_non_indovinate=offset_non_indovinate+50
        non_indovinate=non_indovinate+1
        mazzo_carte_non_indovinate.append(mazzo_carte[indice_carta_girata])
        label_non_indovinate.config(text=str(non_indovinate))
        label_non_indovinate.place(x=820,y=158)
        label_carte_small_no.append(label_img_carta_non_indovinata)

 

Le liste label_carte_small_silabel_carte_small_no, servono per memorizzare i riferimenti di tutte le etichette che contengono carte smll, in modo da poter essere cancellate quando è necessario, utilizzando i blocchi di codice seguenti: 

# Elimina dal piano di gioco tutte le etichete che contengono carte small indovinate
for i in range(0,10):
    label_carte_small_si[i].place_forget() # Cancella l'etichetta corrispondente al riferimento corrente della lista
label_carte_small_si.clear() # Cancella dalla lista tutti i riferimenti alle etchette che contengono carte small indovinate

 

# Elimina dal piano di gioco tutte le etichete che contengono carte small non indovinate
for i in range(0,len(mazzo_carte)):
    label_carte_small_no[i].place_forget() # Cancella l'etichetta corrispondente al riferimento corrente della lista
label_carte_small_no.clear() # Cancella dalla lista tutti i riferimenti alle etchette che contengono carte small non indovinate

 

Come rimescolare le carte non indovinate e riprendere il gioco

Quando le carte del mazzo centrale vengono girate tutte, se ci sono ancora carte non indovinate occore rimescolarle per riprendere il gioco, questa operazione viene svolta dalla funzione mischia().

Prima di tutto bisogna svuotare la lista mazzo_carte, che contiene i valori delle 10 carte da gioco iniziali, il comando seguente esegue questa operazione:

mazzo_carte.clear()

Successivamente bisogna caricare i valori delle carte non indovinate nella lista mazzo_carte, e svuotare la lista che contiene i valori delle carte non indovinate, come mostrano le istruzioni seguenti:

mazzo_carte=mazzo_carte_non_indovinate[:]
mazzo_carte_non_indovinate.clear()

Il blocco di codice seguente mostra le istruzioni fondamentali della funzione mischia() per mescolare le carte non indovinate e riprendere il gioco:

def mischia():
    global mazzo_carte
    global mazzo_carte_non_indovinate
    global non_indovinate
    global offset_non_indovinate
    global label_carte_small_no
    ...
    mazzo_carte.clear()
    mazzo_carte=mazzo_carte_non_indovinate[:]
    mazzo_carte_non_indovinate.clear()
    non_indovinate=0
    offset_non_indovinate=50

     # Elimina dal piano di gioco il pulsante mischia
    pulsante_mischia.place_forget()
    suono(9,4)
    # Elimina dal piano di gioco tutte le etichete che contengono carte small non indovinate
    for i in range(0,len(mazzo_carte)):
        label_carte_small_no[i].place_forget()
    label_carte_small_no.clear()
    start() # Riavvia il gioco

 

Come iniziare una nuova partita oppure visualizzare la finestra modale "GAME OVER" per interrompere il gioco

Quando le carte vengono indovinate tutte, se il punteggio è maggiore di zero è possibile iniziare una nuova partita,

In qualunque momento il punteggio diventa zero, appare la finestra modale "GAME OVER" e bisogna interrompere il gioco.

Il primo caso (nuova partita) si controlla attraverso il codice seguente, inserito nella funzione gira():

    # Se l'indice della carta girata e minore del del massimo consentito, incrementa l'indice di uno, altrimenti mischia oppure inizia una nuova partita
    if indice_carta_girata<len(mazzo_carte)-1:
       indice_carta_girata=indice_carta_girata+1
       suono(3,0.1)
    else:
       pulsante_gira.place_forget()
       label_girate.place_forget()
       label_mazzo_di_carte.place_forget()
       label_carta_girata.place_forget()
       # Se ci sono ancora carte da indovinare mischia
       if indovinate<10:
          pulsante_mischia.place(x=295,y=370)
          suono(8,0.1)
       # Se non ci sono carte da indovinare inizia una nuova partita
       else:
          pulsante_nuova_partita.place(x=270,y=370)
          suono(7,0.1)
          flag=False

La variabile flag a false blocca l'aggiornamento dei secondi e del punteggio.

Per gestire il secondo caso (GAME OVER) occorre verificare se il punteggio diventa uguale a zero, questo è possibile solo quando viene aggiornato il punteggio all'interno della funzione tick(), come mostra blocco di codice seguente:

        conta_score+=1
        # Se conta_score>50 aggiorna il punteggio
        if conta_score>50:
            score=score-1
            conta_score=0
        
# Se score>0 aggiorna le etichette dei secondi, dei tentativi e del punteggio
        if score>0:
            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)+" ")
            spazi="          "[:(10-len(str(score)))]
            label_score.config(text=spazi+str(score)+" ")
        
# Se score==0 visualizza la finestra modale GAME OVER ed interrompe il gioco
       else:
            label_score.config(text="          0 ")
            flag=False
            messagebox.showinfo('Fine Partita', 'GAME OVER')
            esci() # Esegue funzione esci()

# Interrompe il gioco
def esci():
   root.quit()

 

Analisi del codice Python

# Solitario del ferroviere (Lezione 4)
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 
from tkinter import messagebox    # Dalla libreira tkinter importa le classe messagebox usata per creare la finestra modale "GAME OVER"

mazzo_carte=[1,2,3,4,5,6,7,8,9,10]  # Contiene i valori della 10 carte da gioco
mazzo_carte_non_indovinate=[]     
# Contiene i valori delle carte non indovinate
immagini_carte=[]                             # Contiene i riferimenti delle immagini  della 10 carte da gioco
immagini_carte_small=[]                  # Contiene i riferimenti delle immagini  della 10 carte da gioco small
label_carte_small_no=[]
label_carte_small_si=[]
indice_carta_pensata=0
indice_carta_girata=2
indovinate=0
non_indovinate=0
carta_pensata=1
time1=0                                               
# Questa variabile viene usata per il calcolo dei secondi trascorsi
time_start=0                                       # Questa variabile viene usata per il calcolo dei secondi trascorsi
tentativi=1                                           # Questa variabile viene usata per contare quante volte vengono mischiate le carte
offset_indovinate=50                        # Questa variabile viene usata per posizionare le carte small indovinate sul pannello di gioco
offset_non_indovinate=50               # Questa variabile viene usata per posizionare le carte small non indovinate sul pannello di gioco
score=1000                                         # Questa variabile contiene il punteggio
conta_score=0                                    # Questa variabile viene usata per decrementare di uno il punteggio ogni 10 secondi
flag=True                                             # Questa variabile viene usata per interrompere l'aggiornamento dei secondi e del punteggio (nuova partita/game over)

# Questa funzione interrompe il gioco
def esci():
   root.quit()

# 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
    global score
    global conta_score
    global flag
    # legge ora locale del pc
    time2 = time.strftime('%H:%M:%S')
    if time2 != time1:
        time1 = time2
        label_clock.config(text=time2)   
# Aggiorna l'orologio in alto a destra del pannello di gioco
    if flag:     # Se flag==true vengono aggiornati i secondi ed il punteggio, altrimenti viene aggiornato solo l'orologio
        secondi=int(time.time()-time_start) # Calcola i secondi trascorsi
        spazi="          "[:(10-len(str(secondi)))]  # Qunatità di spazi usati per impaginare i secondi ed i tentativi
        str_secondi=spazi+str(secondi)+" "
        label_secondi.config(text=str_secondi)
        spazi="          "[:(10-len(str(tentativi)))]
        label_tentativi.config(text=spazi+str(tentativi)+" ")
        conta_score+=1
        if conta_score>50:   
# Quando conta_score è ugluale a 50 sono passati 10 secondi e bisogna decrementare di uno il punteggio
            score=score-1
            conta_score=0
        if score>0:
            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)+" ")
            spazi="          "[:(10-len(str(score)))]
            label_score.config(text=spazi+str(score)+" ")
        else:
            label_score.config(text="          0 ")
            flag=False  
# Disattiva l'aggiornamento dei secondi e del punteggio
            messagebox.showinfo('Fine Partita', 'GAME OVER')  # Visualizza la finestra modale "GAME OVER"
            esci()  # Esce dal gioco
    # chiama la funzione tick ogni 200 millisecondi
    label_clock.after(200, tick)


# Questa funzione viene eseguita per mischiare le carte
def mischia_carte(mazzo):
    temp=0
    max=len(mazzo)-1
    for i in range(1,100):
        prima=random.randint(0,max)
        seconda=random.randint(0,max)
        if prima!=seconda:
            temp=mazzo[prima]
            mazzo[prima]=mazzo[seconda]
            mazzo[seconda]=temp

def suono(sound,tempo):
    if sound==1:
       pygame.mixer_music.load("TickSound.mp3")
    elif sound==2:
       pygame.mixer_music.load("ElectronicSound.wav")
    elif sound==3:
       pygame.mixer_music.load("ToggleSound.wav")
    elif sound==4:
       pygame.mixer_music.load("ToneSound.wav")
    elif sound==5:
       pygame.mixer_music.load("OvationSound.wav")
    elif sound==6:
       pygame.mixer_music.load("ElectronicSound.wav")
    elif sound==7:
       pygame.mixer_music.load("CardSound.wav")
    elif sound==8:
       pygame.mixer_music.load("ComputerSound.wav")
    pygame.mixer_music.play()
    time.sleep(tempo)    
    

# Questa funzione viene eseguita per caricare le immagini delle 10 carte da gioco
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 viene eseguita per caricare le immagini small delle 10 carte da gioco
def carica_immagini_carte_small():
    immagini_carte_small=[]
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('1coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('2coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('3coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('4coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('5coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('6coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('7coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('8coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('9coppe_small.png')))
    immagini_carte_small.append(ImageTk.PhotoImage(Image.open('10coppe_small.png')))
    return immagini_carte_small


# Questa funzione permette di scegliere la carta pensata
def cambia():
    global indice_carta_pensata
    global carta_pensata
    carta_pensata=indice_carta_pensata
    label_carta_pensata.config(image=immagini_carte[indice_carta_pensata])
    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
    suono(1,0.1)
    

# Questa funzione viene eseguita per girare le carte e verificare se la carta pensata è uguale alla carta girata
def gira():
    global indice_carta_girata
    global flag
    global mazzo_carte
    global carta_pensata
    global indovinate
    global non_indovinate
    global offset_indovinate
    global offset_non_indovinate
    global label_carte_small_no
    global label_carte_small_si
    label_carta_girata.config(image=immagini_carte[mazzo_carte[indice_carta_girata]])
    label_carta_girata.place(x=455,y=40)
    girate="   "+str(indice_carta_girata+1)+"/"+str(len(mazzo_carte))+"  "
    label_girate.config(text=girate)
    label_girate.place(x=500,y=370)
    if carta_pensata==mazzo_carte[indice_carta_girata]:
        label_img_carta_indovinata=tk.Label(root, image=immagini_carte_small[mazzo_carte[indice_carta_girata]-1])
        label_img_carta_indovinata.place(x=820+offset_indovinate,y=60)
        offset_indovinate=offset_indovinate+50
        indovinate=indovinate+1
        label_indovinate.config(text=str(indovinate))
        label_indovinate.place(x=820,y=65)
        label_carte_small_si.append(label_img_carta_indovinata)
        suono(4,0.1)
    else:
        label_img_carta_non_indovinata=tk.Label(root, image=immagini_carte_small[mazzo_carte[indice_carta_girata]-1])
        label_img_carta_non_indovinata.place(x=820+offset_non_indovinate,y=153)
        offset_non_indovinate=offset_non_indovinate+50
        non_indovinate=non_indovinate+1
        mazzo_carte_non_indovinate.append(mazzo_carte[indice_carta_girata])
        label_non_indovinate.config(text=str(non_indovinate))
        label_non_indovinate.place(x=820,y=158)
        label_carte_small_no.append(label_img_carta_non_indovinata)
    if indice_carta_girata<len(mazzo_carte)-1:
       indice_carta_girata=indice_carta_girata+1
       suono(3,0.1)
    else:
       pulsante_gira.place_forget()
       label_girate.place_forget()
       label_mazzo_di_carte.place_forget()
       label_carta_girata.place_forget()
       if indovinate<10:
          pulsante_mischia.place(x=295,y=370)
          suono(6,0.1)
       else:
          pulsante_nuova_partita.place(x=270,y=370)
          suono(5,0.1)
          flag=False

# Questa funzione viene eseguita quando si clicca sul pulsante mischia, gestisce tutte le operazioni richieste quando si mischiano le carte non indovinate
def mischia():
    global mazzo_carte
    global mazzo_carte_non_indovinate
    global non_indovinate
    global offset_non_indovinate
    global label_carte_small_no
    global tentativi
    global score
    tentativi=tentativi+1
    score=int(score/2)
    mazzo_carte.clear()
    mazzo_carte=mazzo_carte_non_indovinate[:]
    mazzo_carte_non_indovinate.clear()
    non_indovinate=0
    offset_non_indovinate=50
    pulsante_mischia.place_forget()
    suono(7,4)
    for i in range(0,len(mazzo_carte)):
        label_carte_small_no[i].place_forget()
    label_carte_small_no.clear()
    start()

# Questa funzione viene eseguita quando si clicca sul pulsante nuova partita, gestisce tutte le operazioni necessarie per iniziare una nuova partita
def nuova_partita():
   global mazzo_carte
   global mazzo_carte_non_indovinate
   global indovinate
   global non_indovinate
   global offset_indovinate
   global offset_non_indovinate
   global label_carte_small_si
   global tentativi
   global score
   global conta_score
   global time_start
   global flag
   mazzo_carte=[1,2,3,4,5,6,7,8,9,10]
   mazzo_carte_non_indovinate.clear()
   flag=True
   indovinate=0
   offset_indovinate=50
   non_indovinate=0
   offset_non_indovinate=50
   tentativi=1
   score=1000
   conta_score=0
   time_start=0
   label_carta_girata.place_forget()
   pulsante_nuova_partita.place_forget()
   suono(8,2.5)
   for i in range(0,10):
       label_carte_small_si[i].place_forget()
   label_carte_small_si.clear()
   start()

# Questa funzione viene eseguita per inizializzare il pannello di gioco e per gestire tutte le oprazioni richieste quando inizia una nuova partita
def start():
    global indice_carta_pensata
    global carta_pensata
    global indice_carta_girata
    global non_indovinate
    global mazzo_carte
    global time_start
    if time_start==0:
       time_start = time.time()
    label_clock.place(x=1300,y=5)
    pulsante_cambia.place(x=60,y=370)
    label_titolo_carta_pensata.place(x=25,y=5)
    label_carta_pensata.config(image=immagini_carte[1])
    label_carta_pensata.place(x=5,y=40)
    label_titolo_mazzo_di_carte.place(x=260,y=5)
    label_mazzo_di_carte.place(x=240,y=40)
    pulsante_gira.place(x=285,y=370)
    label_girate.config(text="   0/"+str(len(mazzo_carte))+"  ")
    label_girate.place(x=500,y=370)
    label_img_indovinate.place(x=750,y=60)
    label_indovinate.config(text=str(indovinate))
    label_indovinate.place(x=820,y=65)
    label_img_non_indovinate.place(x=750,y=150)
    label_non_indovinate.config(text=str(non_indovinate))
    label_non_indovinate.place(x=820,y=158)
    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)
    label_titolo_score.place(x=280,y=455)
    label_score.place(x=280,y=480)
    pygame.init()
    indice_carta_pensata=2
    carta_pensata=1
    indice_carta_girata=0
    mischia_carte(mazzo_carte)

# Preparazione pannello di gioco
root = tk.Tk()
root.wm_title('Solitario del ferroviere')
root.geometry("1400x600")
root.resizable(False, False)
immagine_indovinate=ImageTk.PhotoImage(Image.open('verde.png'))
immagine_non_indovinate=ImageTk.PhotoImage(Image.open('rosso.png'))
label_titolo_carta_pensata=tk.Label(root, text="CARTA PENSATA",font=("Helvetica", 14))
label_titolo_mazzo_di_carte=tk.Label(root, text="MAZZO DI CARTE",font=("Helvetica", 14))
pulsante_cambia=tk.Button(root,text="CAMBIA", width=10, height=2, bd = 3,command=cambia, bg="seagreen",font=("Helvetica", 12))
pulsante_gira=tk.Button(root,text="GIRA", width=10, height=2, bd = 3,command=gira, bg="teal",font=("Helvetica", 12))
pulsante_mischia=tk.Button(root,text="MISCHIA", width=10, height=2, bd = 3,command=mischia, bg="mediumpurple",font=("Helvetica", 12))
pulsante_nuova_partita=tk.Button(root,text="NUOVA PARTITA", width=15, height=2, bd = 3,command=nuova_partita, bg="darkorange",font=("Helvetica", 12))
label_girate=tk.Label(root, text="   0/10  ",font=("Helvetica", 25),fg="white",bg="tomato")
label_img_indovinate=tk.Label(root, image=immagine_indovinate)
label_indovinate=tk.Label(root, text=str(indovinate),font=("Helvetica", 24))
label_non_indovinate=tk.Label(root, text=str(non_indovinate),font=("Helvetica", 24))
label_img_non_indovinate=tk.Label(root, image=immagine_non_indovinate)
label_clock = tk.Label(root, font=('arial', 14, 'italic'), bg='darkgray',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')
label_titolo_score=tk.Label(root, text="SCORE",font=("times", 12,"italic"),fg="black")
label_score=tk.Label(root, font=('arial', 16, 'normal'), bg='palevioletred',fg='white')
logo=ImageTk.PhotoImage(Image.open('logo_sf.png'))
label_logo=tk.Label(root, image=logo)
label_powered_sf=tk.Label(root, text="2020 - Powered by Skill Factory",font=("times", 14,'italic'))
label_powered_sf.place(x=550,y=557)
label_logo.place(x=805,y=545)
immagini_carte=carica_immagini_carte()
label_carta_pensata=tk.Label(root, image=immagini_carte[1])
label_mazzo_di_carte=tk.Label(root, image=immagini_carte[0])
label_carta_girata=tk.Label(root, image=immagini_carte[0])
immagini_carte_small=carica_immagini_carte_small()
label_sinistra=tk.Label(root, image=immagini_carte_small[0])
label_destra=tk.Label(root, image=immagini_carte_small[0])
start()
tick()
root.mainloop()

 

Nella prossima lezione implementeremo le funzionalità  per creare il secondo livello di difficoltà del "Solitario del Ferroviere". 

Arrivederci ...


<< Lezione precedente           Lezione successiva >>



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

 



 

Share Button
TOP