Arkham Horror: The Cardgame es un juego de cartas por Fantasy Flight Games en el universo de Cthulhu Mythos. En este juego los investigadores intentan detener a cultistas de invocar a sus oscuros amos desde otros planos para destruir la tierra; y siendo un juego de cartas, obviamente se ha convertido en mi nueva obsesión.

En este juego cada jugador es uno de cinco detectives cada uno con una mazo de cartas diferente. Y cada uno con dos cartas que solo ese investigador puede tener, una es “buena” y la otra es “mala”. Al ser únicas generalmente quieres tener esta carta tan rápido como sea posible para poderla jugar. Pero ¿Cual es la probabilidad de que pueda tener esta carta en mi primer turno? … Vamos a modelarlas – es el título de este blogpost…

¿Cuál es la probabilidad de que tenga mi carta core al final de la primera ronda?

Un mazo de Arkham, en el juego base, esta compuesto de 33 cartas:

  • 20 cartas de clase
  • 10 cartas genéricas
  • 1 carta única de detective
  • 1 weakness única
  • 1 weakness básico

Al principio del juego se toman 5 cartas y si se toma una weakness se ignora y se toma otra carta. De modo que en ese momento la probabilidad de que saquemos la carta única es de 5/31 y la probabilidad de no sacarla es de (31-5)/31 = 26/31 Estas probabilidades complementarias son importantes porque en realidad vamos a modelar la probabilidad de que al final de la primera ronda no tengas la carta core de tu detective.

Si a esta altura no tienes tu carta core, puedes hacer un mulligan, es decir, apartar tus cinco cartas y tomar otras cinco. Técnicamente lo puedes hacer con cualquier número de cartas en tu mano, pero como queremos tu core, harías mulligan de las cinco. Pero ya no estas tomando de las 31 cartas originales, si no de las 26 que dejaste. Entonces la probabilidad es 5/26 y su complemento 21/26

Una vez más no tienes tu carta core en la mano. Pero ya no puedes hacer mulligan, esta por empezar tu primer turno. Por lo cual todas las cartas que apartaste, las weaknesses y las cinco de tu mulligan anterior se revuelven de vuelta a tu mazo.

Ahora, en tu primer turno tienes tres acciones, las cuales vas a ocupar tomando cada vez una carta de tu mazo de, ahora, veintiocho cartas. La primera vez tienes una probabilidad de 1/28, la segunda 1/27 y la tercera 1/26 y sus respectivos complementos. ¿Por qué modelo cada una de estas cartas aparte? Porque cada una es una acción separada. En las ocasiones anteriores habíamos tomado las cartas en conjunto y no teníamos oportunidad de detenernos en caso de tomar la carta que necesitabamos.

Finalmente en la fase de upkeep vas a tomar una última carta con probabilidad de éxito de 1/25

En total la probabilidad de que termines la primera ronda sin tu carta core es:

Y la probabilidad máxima de que termines la primera rónda con tu carta core es de 41.9% sin considerar efectos especiales.

Ahora… ¿qué sería la vida y de mi sin un poco de código? Vamos a corroborarlo programáticamente utilizando Python

from random import shuffle

class Card:

    def __init__(self, name='', kind="event", core=False, desc=''):
        self.name = name
        self.kind = kind
        self.core = core
        self.desc = desc

    @property
    def kind(self):
        return self._kind

    @kind.setter
    def kind(self, kind):
        if not kind in ["event", "asset", "weakness", "skill"]:
            raise TypeError('Not an event, assset, weakness or skill')
        else:
           self._kind = kind

    @property
    def redraw_setup(self):
        return True if self.kind == "weakness" else False

    def __str__(self):
        return '{} ({})'.format(self.name, self.kind)

    def __repr__(self):
        return '"{}"'.format(self.name)
class PlayerDeck:

    def __init__(self, deck=None):
        self.deck = deck
        self._shuffle()
        self._w = 0

    @property
    def deck(self):
        return self._deck

    @deck.setter
    def deck(self, deck):
        if deck == None:
            self._deck = [Card(core=True)]*1 + [Card()]*30 + [Card(kind="weakness")]*2
        else:
            self._deck = deck

    def draw(self, initial=False):
        drawed = self._deck.pop()
        if drawed.redraw_setup and initial:
            self._w += 1
            return self.draw(True)
        return drawed

    def mulligan(self, r=0):
        self._deck = self._deck + [Card(kind="weakness")]*self._w + [Card()]*r
        self._shuffle()
        self._w = 0

    def _shuffle(self):
        shuffle(self._deck)
def GotCore():
    ad = PlayerDeck([
            Card(name='Forbidden Knowledge', kind='asset'),
            Card(name='Holy Rosary', kind='asset'),
            Card(name='Shrivelling', kind='asset', core=True),
            Card(name='Scrying', kind='asset'),
            Card(name='Arcane Studies', kind='asset'),
            Card(name='Arcane Initiate', kind='asset'),
            Card(name='Drawn to the Flame', kind='event'),
            Card(name='Ward of Protection', kind='event'),
            Card(name='Blinding Light', kind='event'),
            Card(name='Fearless', kind='skill'),
            Card(name='Leather Coat', kind='asset'),
            Card(name='Scavenging', kind='asset'),
            Card(name='Baseball Bat', kind='asset'),
            Card(name='Rabbit\'s Foot', kind='asset'),
            Card(name='Stray Cat', kind='asset'),
            Card(name='Dig Deep', kind='asset'),
            Card(name='Cunning Distraction', kind='event'),
            Card(name='Look what I found!', kind='event'),
            Card(name='Lucky!', kind='event'),
            Card(name='Survival Instinct', kind='skill'),
            Card(name='Heirloom of Hyperborea: Artifact from Another Life', kind='asset'),
            Card(name='Dark Memory', kind='weakness'),
            Card(name='Haunted', kind='weakness'),
            Card(), Card(), Card(), Card(), Card(), Card(), Card(), Card(), Card(), Card(),
        ])
    for i in range(2):
        j = 0
        while j < 5:
            c = ad.draw(True)
            if c.core:
                return True
            j+=1
    else:
        ad.mulligan(r=5)
        j = 0
        while j < 4:
            if ad.draw().core:
                return True
            j+=1
    return False
Lenguaje del código: PHP (php)
success = 0
tries = 100000
for i in range(tries):
    success += GotCore()

print("Tried", tries)
print("Did not got core:", tries - success, "that is", (tries - success)/tries*100, "%")
print("Did got core:", success, "that is", success/tries*100, "%")
Lenguaje del código: PHP (php)
Tried 100000
Did not got core: 57899 that is 57.899 %
Did got core: 42101 that is 42.101 %
Lenguaje del código: CSS (css)

Que es bastante cercano al resultado que obtuvimos con el modelo probabilístico

¿Qué podemos concluir?

Que si quieres tener lo antes posible tu carta core en Arkham Horror: The Card Game, seguramente deberías hacer mulligan de toda tu mano y tomar tres acciones para tomar tres cartas en tu primer turno. Porque no vas a superar el 42% de probabilidad de conseguirla.

Esta es la primera parte de tres en una mini serie de probabilidad en Arkham Horror. Puedes leer las otras dos partes aquí:

Aprende Sheets: Curso práctico de Hojas de cálculo de Google
Aprende Sheets: Curso práctico de Hojas de cálculo de Google