Simulazione di finali di un campionato di calcio

Il procedimento di questo programmino, creato in occasione degli ultimi Mondiali sudafricani,  ha origini molto lontane. Inizialmente l’ho sviluppato in un modello Excel dotato di macro VBA, più avanti ho sfruttato un controllo DataGridView inserito in una Window Form. In entrambi i casi ho ripreso il ben noto schema piramidale inserito nelle celle dell’uno o dell’altro.

Esaminando infine la griglia (Grid) di una finestra WPF mi sono infine detto che la si può direttamente utilizzare, mutatis mutandis, in modo più diretto e ottenendo una creatura meno pesante.

L’idea base si può grossomodo schematizzare con la tabella seguente:

 

 

Valori

 

 

 

 

 

 

Prima squadra

 

6

 

 

 

 

 

 

 

 

 

 

Semifinalista 1

 

 

 

 

Seconda squadra

 

6

 

 

 

 

 

 

 

 

 

 

 

 

Finalista 1

 

 

Terza squadra

 

6

 

 

 

 

 

 

 

 

 

 

Semifinalista 2

 

 

 

 

Quarta squadra

 

6

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Campione

Quinta squadra

 

6

 

 

 

 

 

 

 

 

 

 

Semifinalista 3

 

 

 

 

Sesta squadra

 

6

 

 

 

 

 

 

 

 

 

 

 

 

Finalista 2

 

 

Settima squadra

 

6

 

 

 

 

 

 

 

 

 

 

Semifinalista 4

 

 

 

 

Ottava squadra

 

6

 

 

 

 

 

 

 

Nota. Per semplicità e... pigrizia mi sono limitato ai quarti di finale (cosa tra l’altro tipica di altre finali, per esempio di atletica), ma chi ne ha voglia può ampliare il discorso agli ottavi, aggiungendo sulla sinistra una colonna con 16 finaliste.

Dalla tabella precedente non è arduo evincere di tradurre l’idea in una Window WPF al centro della quale si abbia un container Grid (ma come avete fatto a indovinare?), nelle cui celle si trovano delle TextBox, con aggiunta di uno o più pulsanti di comando.

Chi fosse interessato al programmino per vedere da vicino l'effetto che fa può chiedermelo direttamente tramite email: giannigiac@tin.it .

Al lancio si presenta sostanzialmente conforme alla tabella precedente, con aggiunta di un pulsante in alto.

Si noterà subito che i nomi delle 8 squadre in ballo sono generici, mentre i valori, che esprimono valutazioni (si suggeriscono voti da 1 a 10) su ciascuna di esse, sono allineati alla sufficienza per tutti.

Nota Non ho previsto, per brevità, nessun controllo sui valori che debbono essere rigorosamente numerici (ma utenti così idioti ce ne sono ancora in giro?).

Va da sé che prima di avviare la Simulazione col pulsante così etichettato,  occorre sostituire “Prima squadra”, “Seconda squadra” ecc. coi nomi reali, idem per i voti uniformati sul 6. Dopo di che l’algoritmo introduce un fattore di (pseudo)casualità per tener conto della aleatorietà tipica delle competizioni calcistiche (la palla è tonda, come si sa). Tale procedimento verrà descritto insieme al codice, anticipo solo che esso nelle TextBox da “Semifinalista1” a “Semifinalista4” inserisce i rispettivi vincitori fra Prima e seconda squadra, Terza e quarta squadra ecc. Cosa accade nel resto della piramide degradante segue la stessa logica, ergo la faccenda si commenta da sola.

Il codice XAML

La struttura dei contenitori facenti parte della nostra Window1 (o, in WPF 4.0 , MainWindow) prevede un DockPanel che al suo interno incapsula due Grid, la prima di una sola riga e tre colonne, potendo così ospitare fino a tre pulsanti, di cui uno soltanto viene qui utilizzato (chi vuole ne può aggiungere altri) mentre la seconda corrisponde alla tabella su esposta.

<Window x:Class="Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Finali Campionato mondiale" Height="520" Width="500">

    <DockPanel LastChildFill="True" Background="LightGreen">

        <Grid DockPanel.Dock="Top">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="95.743*" />

                <ColumnDefinition Width="95.743*" />

                <ColumnDefinition Width="95.743*" />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="20*" />

            </Grid.RowDefinitions>

            <Button Click="AvviaSimulaz" Grid.Column="1" Margin="10" Height="30">

                Simulazione

            </Button>

        </Grid>

        <Grid Name="GrigliaSquadre" Width="420.084" Height="400">

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="120*" />

            <ColumnDefinition Width="6*" />

            <ColumnDefinition Width="78.179*" />

            <ColumnDefinition Width="2.858*" />

            <ColumnDefinition Width="95.743*" />

            <ColumnDefinition Width="2.858*" />

            <ColumnDefinition Width="95.743*" />

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

            <RowDefinition Height="25" />

                <RowDefinition Height="30*" />

                <RowDefinition Height="62*" />

                <RowDefinition Height="145.5*" />

            </Grid.RowDefinitions>

        <Label Content ="Valori:" Foreground ="Yellow" Background="Black" VerticalContentAlignment="Center" FontSize="14" Width="49.976" HorizontalAlignment="Right" />

        <TextBox Name="Squad1" Grid.Row="1" Grid.Column="0" Background="LightBlue">

            Prima squadra

        </TextBox>

        <Label Grid.Row="1" Grid.Column="1" Foreground ="DarkRed" FontWeight="Bold" Grid.ColumnSpan="4" Margin="32.657,0,2.648,0">SEMIFINALI</Label>

        <Label Grid.Row="3" Grid.Column="4" Foreground ="DarkRed" FontWeight="Bold" Grid.ColumnSpan="2" Margin="2.848,0,0,0">FINALI</Label>

        <Label Grid.Row="6" Grid.Column="5" Foreground ="DarkRed" FontWeight="Bold" Grid.ColumnSpan="2" Margin="0,25.5,2.848,2.003" Grid.RowSpan="2">CAMPIONE</Label>

        <TextBox Name="Va11" Text="6" FontSize="9" Grid.Row="1" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center" />

        <TextBox Name="Squad2" Grid.Row="3" Grid.Column="0" Background="LightBlue">

            Seconda squadra

        </TextBox>

        <TextBox Name="Va12" Text="6" FontSize="9" Grid.Row="3" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center" />

        <TextBox Name="Squad3" Grid.Row="5" Grid.Column="0" Background="LightBlue">

            Terza squadra

        </TextBox>

        <TextBox Name="Va13" Text="6" FontSize="9" Grid.Row="5" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center" />

        <TextBox Name="Squad4" Grid.Row="7" Grid.Column="0" Background="LightBlue">

            Quarta squadra

        </TextBox>

        <TextBox Name="Va14" Text="6" FontSize="9" Grid.Row="7" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center" />

        <TextBox Name="Squad5" Grid.Row="9" Grid.Column="0" Background="LightBlue">

            Quinta squadra

        </TextBox>

        <TextBox Name="Va15" Text="6" FontSize="9" Grid.Row="9" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center"/>

        <TextBox Name="Squad6" Grid.Row="11" Background="LightBlue">

            Sesta squadra

        </TextBox>

        <TextBox Name="Val6" Text="6" FontSize="9" Grid.Row="11" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center" />

        <TextBox Name="Squad7" Grid.Row="13" Grid.Column="0" Background="LightBlue">

            Settima squadra

        </TextBox>

        <TextBox Name="Va17" Text="6" FontSize="9" Grid.Row="13" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center" />

        <TextBox Name="Squad8" Grid.Row="15" Grid.Column="0" Background="LightBlue">

            Ottava squadra

        </TextBox>

        <TextBox Name="Va18" Text="6" FontSize="9" Grid.Row="15" BorderThickness="4"

                 HorizontalAlignment="Right" Width="32.756" TextAlignment="Center"/>

        <TextBox Name="Semfin1" Grid.Row="2" Grid.Column="2" Background="Aquamarine" Grid.ColumnSpan="2" Margin="0,0,2.648,0">

            Semifinalista 1

        </TextBox>

        <TextBox Name="Semfin2" Grid.Row="6" Grid.Column="2" Background="Aquamarine" Grid.ColumnSpan="2" Margin="0,0,2.648,0">

            Semifinalista 2

        </TextBox>

        <TextBox Name="Semfin3" Grid.Row="10" Grid.Column="2" Background="Aquamarine" Grid.ColumnSpan="2" Margin="0,0,2.648,0">

            Semifinalista 3

        </TextBox>

        <TextBox Name="Semfin4" Grid.Row="14" Grid.Column="2" Background="Aquamarine" Grid.ColumnSpan="2" Margin="0,0,2.648,0">

            Semifinalista 4

        </TextBox>

        <TextBox Name="Fin1" Grid.Row="4" Grid.Column="4" Background="LightBlue">

            Finalista 1

        </TextBox>

        <TextBox Name="Fin2" Grid.Row="12" Grid.Column="4" Background="LightBlue">

            Finalista 2

        </TextBox>

        <TextBox Name="Campione" Grid.Row="8" Grid.Column="6" Foreground="White" Background="DarkBlue" >

            Campione

        </TextBox>

    </Grid>

  </DockPanel>

</Window>

 

L’analisi dello schema XAML è affidato al commento autonomo del lettore, faccio solo notare i nomi parlanti affibbiati alle varie TextBox: Squad1, Squad2, ..., Squad8 (le 8 squadre ovviamente), Val1, Val2, ..., Val8 (le rispettive valutazioni) e via di seguito con Semfin1, ..., Semfin4 poi Fin1 e Fin2 fino al Campione.

Tutto il codice VB, routine per routine

Premesso che al Button etichettato Simulazione, anonimo perché oltretutto è solo soletto, viene associata con Click="AvviaSimulaz" l’omonima routine, secondo una sintassi più consona all’ambiente WPF, qui sotto riporto l’intero listato.

Class Window1

    Dim SquadreValori As New Dictionary(Of String, Double)

    Dim SquadValCaricate = False

    Sub CaricaSquadreValori()

        SquadreValori.Add(Squad1.Text, CType(Va11.Text, Double))

        SquadreValori.Add(Squad2.Text, CType(Va12.Text, Double))

        SquadreValori.Add(Squad3.Text, CType(Va13.Text, Double))

        SquadreValori.Add(Squad4.Text, CType(Va14.Text, Double))

        SquadreValori.Add(Squad5.Text, CType(Va15.Text, Double))

        SquadreValori.Add(Squad6.Text, CType(Val6.Text, Double))

        SquadreValori.Add(Squad7.Text, CType(Va17.Text, Double))

        SquadreValori.Add(Squad8.Text, CType(Va18.Text, Double))

        SquadValCaricate = True

    End Sub

 

    Function Vincitore(ByVal Rivale1 As String, ByVal Rivale2 As String) As String

        If SquadreValori(Rivale1) * (1 + 1.5 * Rnd()) > _

            SquadreValori(Rivale2) * (1 + 1.5 * Rnd()) Then

            Return Rivale1

        Else

            Return Rivale2

        End If

    End Function

 

    Sub AggiornaValori()

        SquadValCaricate = False

        SquadreValori.Clear()

        CaricaSquadreValori()

    End Sub

 

    Private Sub AvviaSimulaz(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

        If Not SquadValCaricate Then

            CaricaSquadreValori()

        End If

        AggiornaValori()

 

        ' Ottavi (o come diavolo si chiamano)

        Randomize()

        Semfin1.Text = Vincitore(Squad1.Text, Squad2.Text)

        Semfin2.Text = Vincitore(Squad3.Text, Squad4.Text)

        Semfin3.Text = Vincitore(Squad5.Text, Squad6.Text)

        Semfin4.Text = Vincitore(Squad7.Text, Squad8.Text)

 

        ' Semifinali

        Fin1.Text = Vincitore(Semfin1.Text, Semfin2.Text)

        Fin2.Text = Vincitore(Semfin3.Text, Semfin4.Text)

 

        ' Finali

        Campione.Text = Vincitore(Fin1.Text, Fin2.Text)

    End Sub

End Class

 

Commenti tacitiani.
Una serie di SquadreValori.Add(Squad1.Text, CType(Va11.Text, Double)) e seguenti carica nella Dictionary SquadreValori, definita a livello Dichiarazioni, i testi delle squadre reali – “Brasile”, “Germania”... - che l’utente avrà la bontà d’inserire al posto dei generici “Prima squadra”, “Seconda squadra, ... in veste di chiave, mentre i corrispondenti valori sono attinti dai vari Val1.Text, Val2.Text, ... previo cast nel formato Double anche a beneficio dei pignoli che volessero assegnare voti tipo 8,45.

Il clou si ha nella funzione Vincitore, di cui riporto nuovamente le istruzioni qui sotto per comodità di chi legge:

  If SquadreValori(Rivale1) * (1 + 1.5 * Rnd()) > _

      SquadreValori(Rivale2) * (1 + 1.5 * Rnd()) Then

      Return Rivale1

  Else

      Return Rivale2

  End If

 

Nota. Chi vuole può sostituire la pur vetusta ma sintetica Rnd con la più moderna System.Random.Next...

L’algoritmo pesca dalla dictionary SquadreValori la valutazione pertinente alle chiavi Rivale1 e Rivale2 cui si aggiunge un pizzico di aleatorietà variabile da 0 a 1,5. Dal confronto emerge come Vincitore una delle due rivali.

Compreso questo meccanismo, il mestiere della routine d’evento AvviaSimulaz appare chiaro, pertanto non voglio tediare la gente con ulteriori chiose.

Una variante più stringata (ma cervellotica)
Potrebbe piovere qualche critica da chi ama la compattezza, per il fatto che tutte le caselle di testo vengono chiamate in gioco per nome, ad una ad una. Ma tale scelta a mio parere era inevitabile, dato il carattere sparso delle caselle di testo nella nostra griglia. Comunque a scopo di dibattito ecco una routine  avente lo scopo di esplorare e segnalare con delle MessageBox tutte e sole le TextBox delle prime 8 squadre affiancandole ai rispettivi valori:

Sub EsploraSquadEtValori

    Static swPrimogiro As Boolean = False

    Dim DepCtrl

    Dim DepNomeCtrl = ""

    For Each Ctrl In GrigliaSquadre.Children

        If TypeOf Ctrl Is TextBox Then

            Dim NomeCtrl = Ctrl.Name.ToString

            If NomeCtrl <> "" And Ctrl.Getvalue(Grid.ColumnProperty) = 0 Then

                If Not swPrimogiro Then

                    DepCtrl = Ctrl

                    DepNomeCtrl = NomeCtrl

                Else

                    MessageBox.Show(DepNomeCtrl & " - " & NomeCtrl)

                End If

                swPrimogiro = Not swPrimogiro

            End If

        End If

    Next

End Sub

 

Il punto è che GrigliaSquadre.Children fornisce tutti i controlli che tale griglia incorpora, di qui la necessità di discriminare quelli di tipo TextBox (TipeOf Ctrl Is TextBox) nonché quelli che giacciono in colonna 0.

Il resto del procedimento si basa sul fatto che, si badi bene, pure i valori sono in colonna 0 (definizione implicita: rivedere tali tag, notando in esse l’assenza dell’attributo Grid.Column) e, conseguentemente, l’esplorazione alterna Squad1 con Val1, Squad2 con Val2, ..., Squad8 con Val8. Infine il gioco dello switch di primo giro swPrimogiro in combutta coi depositi DepCtrl e DepNomeCtrl è un classico su cui non mi dilungo.

Altre possibilità
Ai più esperti affido il compitino di sostituire MessageBox.Show(...) con l’aggiornamento della Dictionary SquadreValori. Ma ne vale la pena?

Piuttosto, chi ne avesse voglia e tempo potrebbe cimentarsi con modifiche e/o ampliamenti come il controllo dei valori (con rigetto dei non numerici) e la finale per il terzo e quarto posto.

Un’ulteriore possibilità (che ho implementato temporibus illis in Excel) potrebbe consistere nell’iterazione in un numero N sufficientemente elevato di cicli For i = 1 To N della Sub AvviaSimulaz aggiornando ad ogni giro un array Vett(7, 1) recante le varie squadre nella prima colonna e, nella seconda, il conteggio delle vincite. In tal modo, al termine, la squadra che avrà vinto più campionati simulati potrebbe considerarsi come il vincitore più probabile.

Gianni Giaccaglini