Penney’s Game con carte rosse e nere

Immaginate un gioco per due giocatori basato su un mazzo standard di 52 carte, in cui ci interessa esclusivamente il colore delle carte: rosso (R) o nero (N).

Problema

All’inizio del gioco, ogni giocatore sceglie una sequenza di tre colori che osserverà per tutto il resto della partita.

Ad esempio:

  • Giocatore 1: R-N-N
  • Giocatore 2: R-R-N

Il gioco procede pescando le carte una alla volta dal mazzo.
Ogni volta che appare una sequenza corrispondente a quella di un giocatore:

  • Quel giocatore segna un punto.
  • Le carte della sequenza vengono scartate.

Si continua fino all’esaurimento del mazzo.

A prima vista, il gioco sembra equo, ma esiste una strategia vincente per il secondo giocatore.


La domanda naturale è:

Qual è la probabilità che il Giocatore 2 vinca applicando questa strategia rispetto al Giocatore 1?

Clicca qui per la soluzione

Strategia vincente del secondo giocatore

La regola è semplice:

  • Il Giocatore 2 osserva la sequenza scelta dal Giocatore 1, ad esempio R-N-R.
  • Costruisce la propria sequenza seguendo questo schema:
  1. Il primo colore sarà l’inverso del colore centrale della sequenza avversaria (qui, R → N).
  2. Il secondo colore sarà uguale al primo della sequenza avversaria (qui, R).
  3. Il terzo colore sarà uguale al secondo della sequenza avversaria (qui, N).

Esempio:

  • Giocatore 1: R-N-R
  • Giocatore 2: N-R-N

Applicando questa strategia, il Giocatore 2 aumenta le proprie possibilità di vincere.


Probabilità di vittoria

Il gioco descritto è un esempio classico noto come Penney’s Game.

Si può modellare come una catena di Markov assorbente non omogenea, in cui:

  • Gli stati rappresentano le sequenze parziali già osservate.
  • Gli stati assorbenti corrispondono al completamento di una sequenza vincente.

Un approccio intuitivo si può osservare con l’esempio della scelta:

  • Giocatore 1: N-N-N
  • Giocatore 2: R-N-N

Limitando l’analisi alle prime quattro estrazioni:

Immagine Catena Estrazioni

Si nota che il Giocatore 1 ha possibilità di vittoria solo entro le prime 3 estrazioni (collegamento in verde).
In tutti gli altri casi, il gioco evolve verso stati favorevoli al Giocatore 2 (collegamenti in rosso).


Probabilità di vittoria per ciascuna combinazione

Scelta del 1° giocatore Scelta del 2° giocatore Probabilità vittoria 1° giocatore Probabilità vittoria 2° giocatore Probabilità pareggio
NNN NNN 0,11% 99,49% 0,40%
NNR RNN 2,62% 93,54% 3,84%
NRN NNR 11,61% 80,11% 8,28%
NRR NNR 5,18% 88,29% 6,53%
RNN RRN 5,18% 88,29% 6,53%
RNR RRN 11,61% 80,11% 8,28%
RRN NRR 2,62% 93,54% 3,84%
RRR NRR 0,11% 99,49% 0,40%

Nota: N = nero, R = rosso


Risultati Monte Carlo (1 milione di simulazioni)

Se il Giocatore 1 seleziona casualmente la sequenza e il Giocatore 2 applica la strategia:

Probabilità vittoria 1° giocatore Media Punti 1° giocatore Probabilità vittoria 2° giocatore Media Punti 2° giocatore Probabilità pareggio
4,88% 2,23 ± 1,56 90,38% 6,09 ± 1,39 0,40%

Codice Python per la simulazione

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import random
from random import randint as rng
import math

simulazioni = 1_000_000   
l_meta_mazzo = 26         # metà della lunghezza del mazzo
player1_scores = []
player2_scores = []

for _ in range(simulazioni):
    
    player1 = [rng(0,1), rng(0,1), rng(0,1)]
    player2 = [0 if player1[1] == 1 else 1, player1[0], player1[1]]

    mazzo = [1] * l_meta_mazzo + [0] * l_meta_mazzo
    random.shuffle(mazzo)

    player1_points = 0
    player2_points = 0

    i = 0
    while i <= len(mazzo)-3:
        window = mazzo[i:i+3]
        if window == player1:
            player1_points += 1
            i += 3
        elif window == player2:
            player2_points += 1
            i += 3
        else:
            i += 1  

    player1_scores.append(player1_points)
    player2_scores.append(player2_points)

# Calcolo percentuale di vittoria e media punti
player1_wins = sum(1 for p1, p2 in zip(player1_scores, player2_scores) if p1 > p2)
player2_wins = sum(1 for p1, p2 in zip(player1_scores, player2_scores) if p2 > p1)
pareggi = simulazioni - player1_wins - player2_wins

media_player1 = sum(player1_scores) / simulazioni
media_player2 = sum(player2_scores) / simulazioni
dev_std_player1 = math.sqrt(sum((x - media_player1) ** 2 for x in player1_scores) / simulazioni)
dev_std_player2 = math.sqrt(sum((x - media_player2) ** 2 for x in player2_scores) / simulazioni)

print(f"Player1 vince: {player1_wins / simulazioni * 100:.2f}%")
print(f"Player2 vince: {player2_wins / simulazioni * 100:.2f}%")
print(f"Pareggi: {pareggi / simulazioni * 100:.2f}%")
print(f"Media punti Player1: {media_player1:.2f}, Dev Std: {dev_std_player1:.2f}")
print(f"Media punti Player2: {media_player2:.2f}, Dev Std: {dev_std_player2:.2f}")
print(f"mazzo di {len(mazzo)} carte")