Gara di salti in Python

Un esercizio dalla complessità più interessante per esplorare ed applicare le nozioni base di Python dall’uso dei file, le strutture dati come liste e dizionari (puoi ripassarli qui), cicli, funzioni, numeri casuali.

Vogliamo simulare una gara di salto in lungo. I nomi degli atleti vengono caricati in una lista leggendo un file di testo, in cui i nomi sono posti uno sotto l’altro. Successivamente il software deve simulare tre salti per ogni atleta lanciando un numero casuale compreso tra 6.00 e 9.99. Il salto può inoltre essere nullo con una probabilità del 50% in stile testa o croce e quindi in questo caso risulta una misura pari a zero. Le misure dei salti devono quindi essere archiviate per ogni atleta in un dizionario opportuno. La seconda fase della gara prevede che a passare siano solo i primi 8 ad aver totalizzato i salti più lunghi tra i tre a disposizione. Degli 8 vincono e vengono stampati i primi 3.

Lettura del file

Come ogni esercizio più complesso che si rispetti, c’è una interazione/caricamento di dati da file. Per abitudine su questo sito web, creiamo sempre una funzione su misura che ci restituisce una lista con i contenuti cercati e caricati. La chiamiamo leggiFile e usiamo il decoratore non obbligatorio ->list per tenere a mente il tipo di ritorno.

Usiamo il costrutto with per evitare le operazioni di try/catch e la necessità di chiudere esplicitamente il file con il comando close().

nomefile = "atleti.txt"



def leggiFile() ->list:

    lista = list()
    with open(nomefile, "r") as file:
        righe = file.readlines()
        for riga in righe:
            lista.append(riga.rstrip("\n")) #rstrip ripulisce andata a capo \n

    return lista

Appendere, ovvero aggiungere i nomi letti ad una lista è semplice ma c’è un dettaglio che spesso rende ardua la vita dello studente alle prime armi. Il file di testo ha un carattere jolly per indicare l’andata a capo che inevitabilmente verrebbe letto assieme al nome. Così lo rimuoviamo con la funzione rstrip in fase di lettura. Esistono anche altri modi per liberarsi del carattere \n.

La funzione per simulare il salto

Prepariamo una funzione per simulare il salto del singolo atleta. Anche qui uso il decoratore ->float Abbiamo bisogno di utilizzare il modulo random da cui possiamo importare solo le funzioni randint e uniform: la prima stacca numeri interi, la seconda numeri decimali in un determinato intervallo assegnato.

Per trovare se il salto è nullo o valido lanciamo una sorta di testa/croce con due numeri 0 e 1. Se esce zero assumo che il salto sia nullo, se 1 vado a trovare la misura del salto. Per il salto lanciamo un secondo numero casuale questa volta decimale invece che intero. Una tragedia del python è gestire i numeri decimali che vengono presentati con molte cifre. A noi occorrono spesso solo le prime due, così effettuiamo un preventivo arrotondamento con round.

from random import uniform, randint

def simulaSalto() -> float:
    
    valido = randint(0,1)
     
    #se esce 0 salto nullo
    if valido == 0:
        return 0
    
    #esce 1, scelgo un numero tra 6 e 9
    salto = round(uniform(6.00, 9.99),2)
    return salto

Il corpo principale

Siamo pronti per creare il nostro programma principale che consigli sempre di differenziare con il if __name__ == “__main__”: per tenere ben separati funzioni, variabili globali, import vari dal corpo centrale del programma.

Qui dobbiamo procedere a chiamare la funzione di caricamento del file. Quindi procediamo a far saltare tre volte ogni atleta della lista e salviamo tutto in un dizionario. Qui il vantaggio di usare python forse è più visibile se usiamo la proprietà di assegnazione di una intera lista con tanto di valori random richiamati in fase di inizializzazione della stessa. Ci sono modi anche più semplici e tradizionali ovviamente per fare la stessa operazione. Stampiamo il risultato finalmente.

    gara = dict()
    for atleta in listaatleti:
        gara[atleta]= [simulaSalto(), simulaSalto(), simulaSalto()] 

Alla seconda fase però partecipano soltanto i primi 8, tra quelli che hanno saltato ovviamente più lontano in modo assoluto e su salto singolo. Ora sta nel trovare per ogni atleta il suo salto massimo tra i tre che ha effettuato. Conviene usare una nuova struttura dizionario dove salvare come chiave ancora il nome e il valore del salto massimo. Ennesimo vantaggio di python è che fare il max di un vettore lista di 3 valori è semplicissimo con la chiamata max() con il vettore associato all’indice del nome atleta come parametro.

    massimi = dict()
    for atleta in gara:
        massimi[atleta]=max(gara[atleta])

Per controllo possiamo stampare quindi il massimo di ogni atleta appena trovato.

Siamo alla parte più complessa dell’esercizio: trovare i primi 8 atleti ad aver saltato più lontano tra i massimi appena individuati. Non c’è una sola soluzione ovviamente ma ne proponiamo una dove scorriamo tutti i valori possibili dei salti da 9,99 a 0,00 spostandoci di decimale in decimale con passo 0,01. Ad ogni possibile misura controlliamo se gli atleti hanno tra i loro salti massimi la misura in considerazione. Se ci sono atleti corrispondenti li inserisco in un nuovo dizionario nome/misura salto e mi decremento un contatore di 8 posi disponibili. Quando ho analizzato tutte le possibile misure o ho già piazzato 8 atleti, esco dal ciclo. Avremo gli 8 atleti con i salti tra l’altro ordinati!

#scorro tutte le misure possibile da 9.99 a 0.00
    #controllo se ci sono atleti corrispondenti a quella misura/salto
    #se trovo un atleta lo inserisco in nuovo dizionario
    #scalo 1 posto disponibile degli 8 ammessi
    secondafase = dict()
    numeroatleti = 8
    misura = 9.99
    while misura >= 0.00:
        for atleta in massimi:
            if massimi[atleta] == misura:
                secondafase[atleta] =massimi[atleta]
                numeroatleti -= 1
        if numeroatleti == 0:
            break
        #occhio alla sottrazione float che ha sempre cifre decimali sporche
        #9.99 - 0.01 non fa 9.98 
        misura = round(misura - 0.01,2) 

Listato completo

from random import uniform, randint


nomefile = "atleti.txt"


'''
FUNZIONI
'''

def leggiFile() ->list:

    lista = list()
    with open(nomefile, "r") as file:
        righe = file.readlines()
        for riga in righe:
            lista.append(riga.rstrip("\n")) #rstrip ripulisce andata a capo \n

    return lista




def simulaSalto() -> float:
    
    valido = randint(0,1)
     
    #se esce 0 salto nullo
    if valido == 0:
        return 0
    
    #esce 1, scelgo un numero tra 6 e 9
    salto = round(uniform(6.00, 9.99),2)
    return salto



'''
MAIN
'''
if __name__ == "__main__":
    listaatleti = leggiFile()
    
    
    '''
    PRIMA FASE: faccio saltare 3 volte tutti gli atleti e li immagazino in un dizionario gara
    '''
    gara = dict()
    for atleta in listaatleti:
        gara[atleta]= [simulaSalto(), simulaSalto(), simulaSalto()] 

    print("|-----------------------------------------------------------|")
    print("|                     RISULTATO SALTI                       |")
    print("|-----------------------------------------------------------|")
    for atleta in gara:
        print(f" ATLETA: {atleta} - SALTO 1: m{gara[atleta][0]}, SALTO 2: m{gara[atleta][0]}, SALTO 3: m{gara[atleta][0]}")
    
    print("")

    '''
    SECONDA FASE: prendo i primi 8
    '''
    
    #Scorro i risultati di ogni atleta e dei 3 prendo il massimo. 
    #Metto i massimi in un dizionario atleta/salto quindi    
    massimi = dict()
    for atleta in gara:
        massimi[atleta]=max(gara[atleta])

    print("|-----------------------------------------------------------|")
    print("|                  SALTO MAX PER ATLETA                     |")
    print("|-----------------------------------------------------------|")        
    for atleta in massimi:
        print(f" ATLETA: {atleta} - SALTO MAX: m{massimi[atleta]}")
    print("")

    #scorro tutte le misure possibile da 9.99 a 0.00
    #controllo se ci sono atleti corrispondenti a quella misura/salto
    #se trovo un atleta lo inserisco in nuovo dizionario
    #scalo 1 posto disponibile degli 8 ammessi
    secondafase = dict()
    numeroatleti = 8
    misura = 9.99
    while misura >= 0.00:
        for atleta in massimi:
            if massimi[atleta] == misura:
                secondafase[atleta] =massimi[atleta]
                numeroatleti -= 1
        if numeroatleti == 0:
            break
        #occhio alla sottrazione float che ha sempre cifre decimali sporche
        #9.99 - 0.01 non fa 9.98 
        misura = round(misura - 0.01,2) 

    print("|-----------------------------------------------------------|")
    print("|                   PODIO ATLETI SALTO                      |")
    print("|-----------------------------------------------------------|")        
    for indice in range(0,3):
        atleta, salto = list(secondafase.items())[indice]
        print(f" ATLETA: {atleta}, m{salto}")
    
    pass

Peri l file abbiamo semplicemente creato il file txt ed inserito nomi di personaggi di topolino, uno sotto l’altro.

Ultima modifica 29 Dicembre 2024