| |||
| © Perl Mongers Italia. Tutti i diritti riservati. | |||
Premessa: come funziona il SuperenalottoIl Superenalotto è legato strettamente alle estrazioni del Lotto nazionale, che da tempo immemorabile avvengono in diverse città d'Italia. La combinazione vincente del Superenalotto si forma prendendo in considerazione i numeri primi estratti al Lotto sulle Ruote di Bari, Firenze, Milano, Napoli, Palermo, Roma (in quest'ordine). Ovviamente, nel caso in cui lo stesso numero venisse estratto per primo su due ruote, si tiene il primo numero della prima ruota e si prende il secondo estratto della seconda. Se ci fosse ancora una sovrapposizione si procederebbe allo stesso modo fino ad ottenere sei numeri diversi. C'è un settimo numero, detto "numero jolly", che è il primo estratto della ruota di Venezia. Questo numero serve per la composizione del premio "5+1", che si ottiene quando si indovinano cinque numeri fra i primi estratti, più il numero Jolly. Primo problema: la scelta delle colonneIl primo problema da risolvere era la scelta casuale delle colonne. Ok, questo era facile e bastava un one-liner scritto in Perl. Eccolo:
Una linea abbastanza lunga, è vero, ma tutto sommato comprensibile. Si parte con un array @n che contiene tutti i numeri da 1 a 90, e un array @colonne inizialmente vuoto. Poi, in un ciclo while che procede finché in @n rimangono elementi, estraiamo (letteralmente!) a caso sei numeri da @n (usando splice per pescare dall'array in posizione arbitraria) e li mettiamo dentro un array temporaneo @colonna, che una volta completato viene aggiunto a @colonne usando push. Estratti tutti i numeri bisognava stamparli, e questo è ciò che fa il semplice ciclo foreach alla fine della linea. Secondo problema: la verifica dei risultatiIl problema della scelta delle colonne era risolto. Ora bisognava risolverne un altro: quello della verifica dei risultati con le colonne estratte. Anche in questo caso Perl viene in aiuto.
#!/usr/bin/perl
use strict ;
use warnings ;
die "Uso: $0 n1 n2 n3 n4 n5 n6 jolly\n\n" unless @ARGV == 7 ;
#######################################################################
my $colonne = 'colonne.txt' ;
#######################################################################
my @estratti = @ARGV[0..5] ;
my $jolly = $ARGV[6] ;
print "-"x72,"\n","Colonna vincente: @estratti, jolly $jolly\n","-"x72,"\n" ;
open COL,$colonne or die "Cannot read $colonne: $!" ;
while (local $_ = <COL>) {
chomp ;
my $col = $_ ;
my @numbers = split ;
my $count = 0 ;
foreach my $number (@numbers) {
$count++ if grep $number == $_ ,@estratti
}
my $match_points = $count == 1? 'punto': 'punti' ;
$match_points .= '!'x$count if $count >= 3 ;
if ($count == 5) {
$count = '5 + 1' if grep $jolly == $_,@numbers ;
}
printf "Colonna %2u %2u %2u %2u %2u %2u: %s %s\n",@numbers,$count,$match_points ;
}
close COL ;
Anche questo è un programma estremamente semplice, privo di qualsiasi generalizzazione che lascio per esercizio al lettore.
Il programma sa ovviamente discernere fra il 6 e il 5+1:
Era già un buon risultato ma mancava di automazione; infatti è necessario inserire a mano i numeri estratti per poter ottenere la valutazione del punteggio conseguito. Perché non approfittare della presenza su Internet di diversi siti che pubblicano i risultati del Superenalotto? Perché non fare in modo di ricevere nella propria casella di posta elettronica i risultati dell'estrazione? Terzo problema: la verifica automaticaUna volta scelta la pagina web da cui prelevare i risultati e individuato il modo migliore per estrarne i dati, le modifiche da fare allo script erano davvero poche:
#!/usr/bin/perl
use strict ;
use warnings ;
use LWP::UserAgent ;
#######################################################################
my $url = 'http://www.corriere.it/giochiepronostici/Corriere_superenalotto.shtml' ;
my $colonne = '/etc/local/enalotto/colonne.txt' ;
my @proxy_conf = ('http','http://proxy:3128/') ;
my $minutes_before_retrying_connection = 5 ;
my $max_attempts = 5 ;
#######################################################################
my $retry = 60 * $minutes_before_retrying_connection ;
my $ua = LWP::UserAgent->new(timeout => 5) ;
$ua->proxy(@proxy_conf) ;
my $page ;
my $attempts = 0 ;
until ($page = $ua->get($url)->as_string) {
if ($attempts++ > $max_attempts) {
die "Tried to get results $max_attempts times, giving up\n" ;
}
print STDERR "Cannot get $url, sleeping $retry seconds...\n" ;
sleep $retry ;
}
my @estratti = ( $page =~ m|<td class="number" >(\d+)</td>|g ) ;
my $jolly = pop @estratti ;
print "-"x72,"\n","Colonna vincente: @estratti, jolly $jolly\n","-"x72,"\n" ;
open COL,$colonne or die "Cannot read $colonne: $!" ;
while (local $_ = <COL>) {
chomp ;
my $col = $_ ;
my @numbers = split ;
my $count = 0 ;
foreach my $number (@numbers) {
$count++ if grep $number == $_ ,@estratti
}
my $match_points = $count == 1? 'punto': 'punti' ;
$match_points .= '!'x$count if $count >= 3 ;
if ($count == 5) {
$count = '5 + 1' if grep $jolly == $_,@numbers ;
}
printf "Colonna %2u %2u %2u %2u %2u %2u: %s %s\n",@numbers,$count,$match_points ;
}
close COL ;
Se notate, questo programma differisce dal precedente solo per dettagli, e precisamente:
Lo script comincia così con un ciclo nel quale si tenta di scaricare i dati; tutta la pagina web viene inserita nella variabile $page e, con un'espressione regolare, si ricavano i numeri estratti (per meglio comprendere l'espressione regolare potete fare riferimento al sorgente della pagina web). Il resto del programma è identico al precedente. Questo programma può essere eseguito automaticamente la mattina dopo l'estrazione dei numeri, e i risultati spediti alle persone interessate. In ufficio lo abbiamo messo in crontab su una macchina Linux in questo modo:
Lo script viene eseguito il mercoledì, il venerdì e la domenica alle 8:30 del mattino. L'opzione -s di mailx indica l'oggetto (subject) del messaggio (in questo caso: Estrazione), mentre "enalotto" è l'indirizzo a cui spedire il messaggio. Ovviamente "enalotto" è un alias che corrisponde a tutti gli indirizzi delle persone che vogliono ricevere le notifiche via e-mail. E ora, i fuochi artificialiMancava ancora una cosa per i palati più difficili: una pagina web per quelli che non volevano ricevere le notifiche per posta. Nessun problema: ancora una volta ho usato Perl e un tool che mi piace molto: AxKit. Cos'è AxKit? In due parole, è un server XML per Apache v1.3, basato su mod_perl. AxKit inserisce in Apache un motore di generazione e trasformazione di XML. Tramite AxKit, ad esempio, è possibile estrarre dati da un database, comporli in un documento XML e trasformarli mediante fogli di stile (scritti nello standard XSLT o con XPathScript, un linguaggio di trasformazione basato su Perl) in qualunque altro formato: HTML, PDF, DocBook, testo semplice... Maggiori informazioni su AxKit e sulle sue potenzialità si trovano sul sito http://www.axkit.org/ e sul bellissimo libro "XML Publishing with AxKit" edito da O'Reilly & Associates (cfr.: http://www.oreilly.com/catalog/xmlaxkit/index.html) e scritto da Kip Hampton, uno degli sviluppatori del progetto. Avevo già un sito interno dell'azienda basato su AxKit, quindi avevo già pronti i fogli di stile per trasformare un certo formato XML in una pagina web. Quello che rimaneva da fare era produrre dinamicamente un documento XML che poi AxKit avrebbe trasformato in HTML e "trasmesso" ai browser. Per la generazione dinamica di XML AxKit mette a disposizione le XSP (eXtensible Server Pages), una specifica originariamente introdotta nell'ambito del progetto Cocoon (http://cocoon.apache.org/). La differenza fra Cocoon e AxKit è che il linguaggio "embedded" nei file XSP è Java nel primo caso e Perl nell'altro. Per ottenere una pagina con i risultati delle estrazioni dovevo solo adattare e "immergere" il codice già esistente in una pagina XSP. Anche questo non è stato troppo difficile e il codice risultante è il seguente:
Tralascio una descrizione approfondita di questo codice perché richiederebbe una lunga digressione su XML in generale e XSP in particolare. Mi limiterò a dire che questa pagina produce codice XML al cui interno si trova una tabella HTML; successivamente questo codice viene processato con un foglio di stile in XPathScript che lo trasforma in HTML, al quale viene applicato un comune foglio di stile CSS. Il risultato finale è quello illustrato in figura (opportunamente offuscata per non dare troppe indicazioni né ai giocatori scaramantici né al cracker di turno). Si noti inoltre che i numeri estratti vengono evidenziati in giallo, il numero jolly viene cerchiato in rosso e il punteggio totale della colonna viene evidenziato in un colore diverso a seconda del valore. Repetita non juvantCome sempre accade in questi casi, qualcuno si è posto il dubbio se, per caso, le colonne potessero essere scelte in base a qualche legge statistica per aumentare le probabilità di successo. La risposta ovviamente è stata: "NO, tutte le colonne sono equiprobabili". Se non siete convinti, tentiamo una spiegazione semplice. Giocate a tombola con gli amici, e avete un sacco con 90 numeri ben mescolati. Chiedetevi: che probabilità c'è che alla prima estrazione venga fuori il numero 1? Ovviamente: 1/90. Finita la prima partita ne seguirà un'altra, con il sacco di nuovo pieno e un con i numeri opportunamente mescolati. Fatevi nuovamente la stessa domanda, e la risposta sarà chiaramente la stessa: 1/90, sia che il numero 1 sia effettivamente stato estratto per primo la volta precedente, sia che sia stato estratto un numero diverso. Questo accade perché ogni partita, ogni estrazione, ha una storia a sé che non è influenzata dalle estrazioni precedenti e l'evento che state osservando è completamente casuale. Potreste essere ancora scettici: in questo caso vi raccomando la lettura dell'interessante saggio-articolo di Adam Atkinson (http://www.ghira.mistral.co.uk/ritardi.ps) dal titolo "FAQ sull'inutilità dei ritardi nel lotto (e altrove)". Atkinson spiega con un linguaggio informale e con rigore matematico insieme perché tutte quelle sedicenti teorie sui ritardi siano in realtà delle baggianate senza senso. Buona lettura e in bocca al lupo! | |||