di Francesco BalenaQuasi non ci speravo più, ma alla fine qualcuno è riuscito a risolvere il Quizzettone #9, ovvero come determinare una mano di poker in solo otto istruzioni. Anzi, il bravissimo Matteo Conta è riuscito a farlo in solo sette istruzioni. Graaaaande! Trovate la sua soluzione in coda ai vari commenti al post.
Come promesso, passo invece a illustrare la mia soluzione in otto istruzioni. La soluzione è in VB2005 (tratta dal nuovo libro Programming Microsoft Visual Basic 2005: The Language), ma potrebbe essere facilmente resa anche in C#:
Public Shared Function EvalPokerScore2(ByVal ParamArray cards() As String) As String
' Sort the array and create the sequence of values and of suits.
Array.Sort(cards)
Dim values As String = cards(0)(0) + cards(1)(0) + cards(2)(0) + cards(3)(0) + cards(4)(0)
Dim suits As String = cards(0)(1) + cards(1)(1) + cards(2)(1) + cards(3)(1) + cards(4)(1)
Dim scores(,) As String = { _
{"12345|23456|34567|45678|56789|6789T|789JT|89JQT|9JKQT|1JKQT", "(.)\1\1\1\1", "StraightFlush"}, _
{"(.)\1\1\1", ".", "FourOfAKind"}, _
{"(.)\1\1(.)\2|(.)\3(.)\4\4", ".", "FullHouse"}, _
{".", "(.)\1\1\1\1", "Flush"}, _
{"12345|23456|34567|45678|56789|6789T|789JT|89JQT|9JKQT|1JKQT", ".", "Straight"}, _
{"(.)\1\1", ".", "ThreeOfAKind"}, _
{"(.)\1.?(.)\2", ".", "TwoPairs"}, _
{"(.)\1", ".", "OnePair"}}
For i As Integer = 0 To scores.GetUpperBound(0)
If Regex.IsMatch(values, scores(i, 0)) AndAlso Regex.IsMatch(suits, scores(i, 1)) Then
Return scores(i, 2)
End If
Next
Return "HighCard"
End Function
(Nel computo delle istruzioni, non si conta nè End If nè Next, secondo le regole enunciate nel quiz...)
Per prima cosa il programma crea due stringhe, values contentente la sequenza dei valori delle carte, suits contenente la sequenza dei semi, poi definisce il vettore bidimensionale scores con le informazioni per cercare i vari punteggi. Ogni riga del vettore scores contiene due regular expression - una che viene applicata alla stringa values, l'altra alla stringa suits. Se entrambe le regex sono soddisfatte, il programma restituisce il terzo elemento della riga, ovvero il nome della combinazione. Notate che la seconda regex (quella che testa i colori) viene usata solo in due casi, per StraightFlush (scala reale) e Flush (colore), quindi in tutti gli altri casi basta usare la regex "." (qualsiasi carattere).
La maggior parte delle regex che testano i valori usano le cosiddette "backreference", ad es. "(.)\1\1" significa "cerca un carattere qualsiasi (il punto) poi ricercalo altre due volte (\1\1): il risultato della ricerca è positivo se vi sono tre carte uguali una dopo l'altra. Notare che l'array cards è ordinato rispetto al valore delle carte, quindi abbiamo la sicurezza che in values le carte con lo stesso valore sono adiacenti.
Proprio perche la stringa values è ordinata per valori, le regex da usare per testare le scale (StraightFlush e Straight) contengono dei caratteri apparentemente fuori ordine. Ad esempio, la scala massima si testa con la regex "1JKQT" e non con sequenza più intuitiva (ma errata), "TJQK1".
Concludendo, anche se la soluzione di Matteo è a dir poco sbalorditiva e assolutamente geniale, direi che quella basata su regex è decisamente più leggibile e manutenibile.
NOTA: Questa soluzione è in 8 istruzioni, ma ho spiegato che esiste anche la possibilità di scriverla in sole sette istruzioni. Questo si ottiene evitando di dividere le carte in due stringhe distinte (values e suits), ed usando invece una sola stringa data dalla concatenazione delle coppie (valore,seme). Anche in questo caso è possibile scrivere delle regex che trovano correttamaente tutte le combinazioni, ma le regex stesse sono molto più complesse, quindi ho deciso che nel libro avrei pubblicato questa versione più lunga ma più leggibile.
In realtà è persino possibile modificare il precedente codice per ottenere una soluzione in sole sei istruzioni: infatti, invece di creare le stringhe values e suits fuori dal ciclo, potrei crearle al volo ad ogni iterazione del ciclo For..Next. Non solo, ma se aggiungo una ulteriore riga alla matrice scores, contenente i sequenti valori:
{".", ".", "HighCard"}}
Allora posso evitare anche l'istruzione Return finale, portando il totale a sole cinque istruzioni, anche se in quest'ultimo caso devo tenermi il warning che VB emette perchè non tutti i possibili percorsi della funzione restituiscono un valore. Insomma, se davvero volete scrivere codice illeggibile, non c'è che l'imbarazzo della scelta 
Ultimissima nota: un pubblico ringraziamento e una pubblica lode anche a Paolo Possanzini, per essere stato il primo ad accanirsi e a risolvere, qualche mese fa mentre scrivevo il libro, questo quiz. Onore al merito!