Skip to content

Instantly share code, notes, and snippets.

@MaxHalford
Created April 15, 2020 14:33
Show Gist options
  • Save MaxHalford/0bf06078d2dd6ef3609f4e3cca5bc41c to your computer and use it in GitHub Desktop.
Save MaxHalford/0bf06078d2dd6ef3609f4e3cca5bc41c to your computer and use it in GitHub Desktop.
Analysis of the Zixor card from Hearthstone
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Zixor analyis"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class Card:\n",
" \n",
" def __eq__(self, other):\n",
" \"\"\"We'll use to `==` operator to compare cards.\"\"\"\n",
" return self.name == other.name\n",
" \n",
"class BeastMixin:\n",
" pass\n",
"\n",
"class RushMixin:\n",
" pass\n",
" \n",
"class Zixor(Card, BeastMixin, RushMixin):\n",
" \"\"\"Zixor is the first card we want to reach.\"\"\"\n",
" name = 'Zixor, Apex Predator'\n",
" mana = 3\n",
" \n",
"class ZixorPrime(Card, BeastMixin, RushMixin):\n",
" \"\"\"Zixor Prime is the second card we want to reach.\n",
" \n",
" This card is randomly inserted into the deck once Zixor's deathrattle triggers.\n",
" \n",
" \"\"\"\n",
" name = 'Zixor Prime'\n",
" mana = 8\n",
" \n",
"class Wisp(Card):\n",
" \"\"\"This will act as a filler card.\"\"\"\n",
" \n",
" name = 'Wisp'\n",
" mana = 0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Triggering Zixor's deathrattle"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import random\n",
"\n",
"def look_for_zixor(deck, rng=random):\n",
" \"\"\"Simulation with pure draw, no gryphons.\"\"\"\n",
" \n",
" # Generate a first hand, we'll assume that we're going first so we draw 3 cards\n",
" hand, deck = deck[:3], deck[3:]\n",
" \n",
" # Mulligan if Zixor is not in the hand\n",
" if Zixor not in hand:\n",
" deck.extend(hand) # add the hand to the end of the deck\n",
" n_mulligan = random.randint(2, 3) \n",
" hand, deck = deck[:n_mulligan], deck[n_mulligan:]\n",
" rng.shuffle(deck)\n",
" \n",
" # Draw a first card\n",
" hand.append(deck.pop(0))\n",
" \n",
" # Draw until Zixor is in hand and there is enough mana to summon him\n",
" turn = 1\n",
" while Zixor not in hand or turn < Zixor.mana:\n",
" turn += 1\n",
" hand.append(deck.pop(0))\n",
"\n",
" # Let's assume that it will take 1 or 2 turns to trigger Zixor's deathrattle\n",
" for _ in range(random.randint(1, 2)):\n",
" turn += 1\n",
" try:\n",
" hand.append(deck.pop(0))\n",
" except IndexError: # the deck might be empty at this point\n",
" pass\n",
"\n",
" # Now we randomly insert Zixor Prime into the deck\n",
" i = rng.randint(0, len(deck))\n",
" deck.insert(i, ZixorPrime)\n",
"\n",
" return turn, hand, deck"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.31 s, sys: 10.8 ms, total: 2.32 s\n",
"Wall time: 2.33 s\n",
"Turns till Zixor deathrattles: 13.747 ± 8.306, median is 13\n"
]
}
],
"source": [
"import statistics as stats\n",
"\n",
"def simulate(rng=random):\n",
" deck = [Zixor] + [Wisp] * 29\n",
" rng.shuffle(deck)\n",
" turn, _, _ = look_for_zixor(deck, rng=rng)\n",
" return turn\n",
"\n",
"%time sims = [simulate() for _ in range(50_000)]\n",
"print(f'Turns till Zixor deathrattles: {stats.mean(sims):.3f} ± {stats.stdev(sims):.3f}, median is {int(stats.median(sims))}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Drawing Zixor Prime"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"def look_for_zixor_prime(deck, rng=random):\n",
" \n",
" # We first look for Zixor\n",
" turn, hand, deck = look_for_zixor(deck, rng=rng)\n",
" \n",
" # Draw until Zixor Prime is in hand and there is enough mana to summon him\n",
" while ZixorPrime not in hand or turn < ZixorPrime.mana:\n",
" turn += 1\n",
" hand.append(deck.pop(0))\n",
" \n",
" return turn, hand, deck"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.42 s, sys: 11.6 ms, total: 2.43 s\n",
"Wall time: 2.44 s\n",
"Turns till Zixor Prime is summoned: 21.752 ± 6.310, median is 23\n"
]
}
],
"source": [
"def simulate(rng=random):\n",
" deck = [Zixor] + [Wisp] * 29\n",
" rng.shuffle(deck)\n",
" turn, _, _ = look_for_zixor_prime(deck, rng=rng)\n",
" return turn\n",
"\n",
"%time sims = [simulate() for _ in range(50_000)]\n",
"print(f'Turns till Zixor Prime is summoned: {stats.mean(sims):.3f} ± {stats.stdev(sims):.3f}, median is {int(stats.median(sims))}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Card draw helpers"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Diving Gryphon](https://www.hearthpwn.com/cards/151350-diving-gryphon)."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"class DivingGryphon(Card, BeastMixin, RushMixin):\n",
" name = 'Diving Gryphon'\n",
" mana = 3\n",
" \n",
" @classmethod\n",
" def draw(cls, hand, deck, rng=random):\n",
" rush_cards = [i for i, card in enumerate(deck) if issubclass(card, RushMixin)]\n",
" if rush_cards:\n",
" i = rng.choice(rush_cards)\n",
" hand.append(deck.pop(i))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Scavenger's Ingenuity](https://www.hearthpwn.com/cards/210658-scavengers-ingenuity)."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"class ScavengersIngenuity(Card, BeastMixin, RushMixin):\n",
" name = \"Scavenger's Ingenuity\"\n",
" mana = 3\n",
" \n",
" @classmethod\n",
" def draw(cls, hand, deck, rng=random):\n",
" beasts = [i for i, card in enumerate(deck) if issubclass(card, BeastMixin)]\n",
" if beasts:\n",
" i = rng.choice(beasts)\n",
" hand.append(deck.pop(i))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Tracking](https://hearthstone.gamepedia.com/Tracking)."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"class Tracking(Card):\n",
" name = 'Tracking'\n",
" mana = 1\n",
" \n",
" @classmethod\n",
" def draw(cls, hand, deck, rng=random):\n",
" next_3, deck = deck[:3], deck[3:]\n",
" if not next_3:\n",
" return\n",
" for card in wish_list:\n",
" if card in next_3:\n",
" hand.append(card)\n",
" return\n",
" # If no tracked card was in the wish list then we pick one at random\n",
" hand.append(rng.choice(next_3))\n",
" \n",
"wish_list = [ZixorPrime, Zixor, DivingGryphon, ScavengersIngenuity, Tracking]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's our simulation functions to take into account the draw cards."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"def look_for_zixor(deck, rng=random):\n",
" \"\"\"Simulation which takes into account card draw helpers.\"\"\"\n",
" \n",
" turn = 1\n",
" \n",
" # Generate a first hand, we'll assume that we're going first so we draw 3 cards\n",
" hand, deck = deck[:3], deck[3:]\n",
" \n",
" # Mulligan if Zixor is not in the hand\n",
" if Zixor not in hand:\n",
" deck.extend(hand) # add the hand to the end of the deck\n",
" n_mulligan = random.randint(2, 3) \n",
" hand, deck = deck[:n_mulligan], deck[n_mulligan:]\n",
" rng.shuffle(deck)\n",
" \n",
" # Draw a first card\n",
" hand.append(deck.pop(0))\n",
" \n",
" # Check if any draw card can be used\n",
" for draw_card in [Tracking]:\n",
" if draw_card in hand and draw_card.mana <= 1:\n",
" draw_card.draw(hand, deck)\n",
" hand.remove(draw_card)\n",
" \n",
" # Draw until Zixor is in hand \n",
" while Zixor not in hand:\n",
" turn += 1\n",
" hand.append(deck.pop(0))\n",
" \n",
" # Check if any draw card can be used\n",
" for draw_card in [DivingGryphon, ScavengersIngenuity, Tracking]:\n",
" if draw_card in hand and turn >= draw_card.mana:\n",
" draw_card.draw(hand, deck)\n",
" hand.remove(draw_card)\n",
" \n",
" # Draw until there is enough mana to summon Zixor\n",
" while turn < Zixor.mana:\n",
" hand.append(deck.pop(0))\n",
" turn += 1\n",
"\n",
" # Let's assume that it will take 1 or 2 turns to trigger Zixor's deathrattle\n",
" for _ in range(random.randint(1, 2)):\n",
" turn += 1\n",
" try:\n",
" hand.append(deck.pop(0))\n",
" except IndexError: # the deck might be empty at this point\n",
" pass\n",
"\n",
" # Now we randomly insert Zixor Prime into the deck\n",
" i = rng.randint(0, len(deck))\n",
" deck.insert(i, ZixorPrime)\n",
"\n",
" return turn, hand, deck"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"def look_for_zixor_prime(deck, rng=random):\n",
" \n",
" # We first look for Zixor\n",
" turn, hand, deck = look_for_zixor(deck, rng=rng)\n",
" \n",
" # Draw until Zixor Prime is in hand\n",
" while ZixorPrime not in hand:\n",
" hand.append(deck.pop(0))\n",
" turn += 1\n",
" \n",
" for draw_card in [DivingGryphon, ScavengersIngenuity, Tracking]:\n",
" if draw_card in hand and turn >= draw_card.mana:\n",
" draw_card.draw(hand, deck)\n",
" hand.remove(draw_card)\n",
" \n",
" while turn < ZixorPrime.mana:\n",
" turn += 1\n",
" hand.append(deck.pop(0))\n",
" \n",
" \n",
" return turn, hand, deck"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's try out the different possibilities."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"27"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import itertools\n",
"\n",
"def powerset(iterable):\n",
" \"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)\"\n",
" s = list(iterable)\n",
" return itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s)+1))\n",
"\n",
"cards = 2 * [DivingGryphon] + 2 * [Tracking] + 2 * [ScavengersIngenuity]\n",
"combos = list(powerset(cards))\n",
"combos = list(dict.fromkeys(combos)) # removes duplicates while preserving order\n",
"len(combos)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's do a first analysis where there are no beasts in the deck, other than Zixor and potential Diving Gryphons."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pure draw\n",
" Turns till Zixor deathrattles: 13.743 ± 8.327, median is 13\n",
" Turns till Zixor Prime is summoned: 21.802 ± 6.293, median is 23\n",
"\n",
"1x Diving Gryphon\n",
" Turns till Zixor deathrattles: 9.461 ± 6.017, median is 7\n",
" Turns till Zixor Prime is summoned: 17.167 ± 6.338, median is 17\n",
"\n",
"1x Tracking\n",
" Turns till Zixor deathrattles: 13.671 ± 8.268, median is 13\n",
" Turns till Zixor Prime is summoned: 21.517 ± 6.310, median is 23\n",
"\n",
"1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.453 ± 5.988, median is 7\n",
" Turns till Zixor Prime is summoned: 17.057 ± 6.374, median is 17\n",
"\n",
"2x Diving Gryphon\n",
" Turns till Zixor deathrattles: 7.766 ± 4.511, median is 6\n",
" Turns till Zixor Prime is summoned: 14.444 ± 5.840, median is 14\n",
"\n",
"1x Diving Gryphon, 1x Tracking\n",
" Turns till Zixor deathrattles: 9.419 ± 5.990, median is 7\n",
" Turns till Zixor Prime is summoned: 16.722 ± 6.323, median is 17\n",
"\n",
"1x Diving Gryphon, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.640 ± 4.530, median is 5\n",
" Turns till Zixor Prime is summoned: 14.539 ± 5.831, median is 14\n",
"\n",
"2x Tracking\n",
" Turns till Zixor deathrattles: 13.560 ± 8.221, median is 12\n",
" Turns till Zixor Prime is summoned: 21.195 ± 6.379, median is 23\n",
"\n",
"1x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.479 ± 6.034, median is 7\n",
" Turns till Zixor Prime is summoned: 16.769 ± 6.378, median is 17\n",
"\n",
"2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.830 ± 4.552, median is 6\n",
" Turns till Zixor Prime is summoned: 14.441 ± 5.826, median is 14\n",
"\n",
"2x Diving Gryphon, 1x Tracking\n",
" Turns till Zixor deathrattles: 7.721 ± 4.496, median is 6\n",
" Turns till Zixor Prime is summoned: 13.988 ± 5.721, median is 13\n",
"\n",
"2x Diving Gryphon, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.813 ± 3.560, median is 5\n",
" Turns till Zixor Prime is summoned: 12.606 ± 5.125, median is 11\n",
"\n",
"1x Diving Gryphon, 2x Tracking\n",
" Turns till Zixor deathrattles: 9.316 ± 5.994, median is 7\n",
" Turns till Zixor Prime is summoned: 16.299 ± 6.315, median is 16\n",
"\n",
"1x Diving Gryphon, 1x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.623 ± 4.541, median is 5\n",
" Turns till Zixor Prime is summoned: 14.234 ± 5.763, median is 13\n",
"\n",
"1x Diving Gryphon, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.844 ± 3.530, median is 5\n",
" Turns till Zixor Prime is summoned: 12.689 ± 5.158, median is 11\n",
"\n",
"2x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.399 ± 6.023, median is 7\n",
" Turns till Zixor Prime is summoned: 16.338 ± 6.325, median is 16\n",
"\n",
"1x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.783 ± 4.554, median is 6\n",
" Turns till Zixor Prime is summoned: 14.086 ± 5.766, median is 13\n",
"\n",
"2x Diving Gryphon, 2x Tracking\n",
" Turns till Zixor deathrattles: 7.664 ± 4.471, median is 5\n",
" Turns till Zixor Prime is summoned: 13.707 ± 5.681, median is 12\n",
"\n",
"2x Diving Gryphon, 1x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.736 ± 3.468, median is 5\n",
" Turns till Zixor Prime is summoned: 12.237 ± 4.973, median is 10\n",
"\n",
"2x Diving Gryphon, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.308 ± 2.816, median is 5\n",
" Turns till Zixor Prime is summoned: 11.503 ± 4.552, median is 9\n",
"\n",
"1x Diving Gryphon, 2x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.564 ± 4.463, median is 5\n",
" Turns till Zixor Prime is summoned: 13.807 ± 5.629, median is 13\n",
"\n",
"1x Diving Gryphon, 1x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.787 ± 3.503, median is 5\n",
" Turns till Zixor Prime is summoned: 12.432 ± 5.068, median is 11\n",
"\n",
"2x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.682 ± 4.494, median is 5\n",
" Turns till Zixor Prime is summoned: 13.671 ± 5.631, median is 12\n",
"\n",
"2x Diving Gryphon, 2x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.679 ± 3.458, median is 5\n",
" Turns till Zixor Prime is summoned: 11.939 ± 4.860, median is 10\n",
"\n",
"2x Diving Gryphon, 1x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.257 ± 2.756, median is 5\n",
" Turns till Zixor Prime is summoned: 11.234 ± 4.423, median is 9\n",
"\n",
"1x Diving Gryphon, 2x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.699 ± 3.448, median is 5\n",
" Turns till Zixor Prime is summoned: 12.156 ± 4.956, median is 10\n",
"\n",
"2x Diving Gryphon, 2x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 6.239 ± 2.775, median is 5\n",
" Turns till Zixor Prime is summoned: 10.937 ± 4.223, median is 8\n",
"\n"
]
}
],
"source": [
"import collections\n",
"\n",
"results = []\n",
"\n",
"class SnowflipperPenguin(Card, BeastMixin):\n",
" \"\"\"This acts as a beast filler. The point of adding beasts to a deck is\n",
" to dampen the effect of Scavenger's Ingenuity.\n",
" \n",
" \"\"\"\n",
" name = 'Snowflipper Penguin'\n",
" mana = 0\n",
"\n",
"def analyze(n_beasts):\n",
"\n",
" for combo in combos:\n",
"\n",
" counts = collections.Counter(combo)\n",
" print(', '.join([f'{count}x {card.name}' for card, count in counts.items()]) or 'Pure draw')\n",
"\n",
" # Build the deck\n",
" deck = [Zixor] + list(combo) + [SnowflipperPenguin] * n_beasts\n",
" deck += [Wisp] * (30 - len(deck))\n",
"\n",
" # Zixor\n",
"\n",
" def simulate(rng=random):\n",
" deck_copy = deck[:]\n",
" rng.shuffle(deck_copy)\n",
" turn, _, _ = look_for_zixor(deck_copy, rng=rng)\n",
" return turn\n",
"\n",
" sims = [simulate() for _ in range(20_000)]\n",
" avg = stats.mean(sims)\n",
" std = stats.stdev(sims)\n",
" med = stats.median(sims)\n",
" print(f' Turns till Zixor deathrattles: {avg:.3f} ± {std:.3f}, median is {int(med)}')\n",
"\n",
" # Zixor Prime\n",
"\n",
" def simulate(rng=random):\n",
" deck_copy = deck[:]\n",
" rng.shuffle(deck_copy)\n",
" turn, _, _ = look_for_zixor_prime(deck_copy, rng=rng)\n",
" return turn\n",
"\n",
" sims = [simulate() for _ in range(20_000)]\n",
" avg = stats.mean(sims)\n",
" std = stats.stdev(sims)\n",
" med = stats.median(sims)\n",
" print(f' Turns till Zixor Prime is summoned: {avg:.3f} ± {std:.3f}, median is {int(med)}')\n",
"\n",
" print()\n",
" \n",
"analyze(n_beasts=0)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With 2 copies of all 3 draw cards, the median number of turns to reach Zixor Prime is 8, which is exactly it's cost.\n",
"\n",
"Now let's try with 4 additional beasts in the deck."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Pure draw\n",
" Turns till Zixor deathrattles: 13.668 ± 8.286, median is 12\n",
" Turns till Zixor Prime is summoned: 21.627 ± 6.331, median is 23\n",
"\n",
"1x Diving Gryphon\n",
" Turns till Zixor deathrattles: 9.455 ± 6.005, median is 7\n",
" Turns till Zixor Prime is summoned: 17.025 ± 6.384, median is 17\n",
"\n",
"1x Tracking\n",
" Turns till Zixor deathrattles: 13.706 ± 8.245, median is 13\n",
" Turns till Zixor Prime is summoned: 21.526 ± 6.367, median is 23\n",
"\n",
"1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 12.279 ± 7.563, median is 11\n",
" Turns till Zixor Prime is summoned: 19.832 ± 6.273, median is 21\n",
"\n",
"2x Diving Gryphon\n",
" Turns till Zixor deathrattles: 7.820 ± 4.593, median is 6\n",
" Turns till Zixor Prime is summoned: 14.381 ± 5.807, median is 13\n",
"\n",
"1x Diving Gryphon, 1x Tracking\n",
" Turns till Zixor deathrattles: 9.449 ± 6.026, median is 7\n",
" Turns till Zixor Prime is summoned: 16.710 ± 6.369, median is 17\n",
"\n",
"1x Diving Gryphon, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.528 ± 6.085, median is 7\n",
" Turns till Zixor Prime is summoned: 16.705 ± 6.200, median is 17\n",
"\n",
"2x Tracking\n",
" Turns till Zixor deathrattles: 13.524 ± 8.264, median is 12\n",
" Turns till Zixor Prime is summoned: 21.203 ± 6.360, median is 23\n",
"\n",
"1x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 12.129 ± 7.496, median is 10\n",
" Turns till Zixor Prime is summoned: 19.419 ± 6.275, median is 20\n",
"\n",
"2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 11.219 ± 6.856, median is 9\n",
" Turns till Zixor Prime is summoned: 18.268 ± 6.113, median is 19\n",
"\n",
"2x Diving Gryphon, 1x Tracking\n",
" Turns till Zixor deathrattles: 7.739 ± 4.512, median is 6\n",
" Turns till Zixor Prime is summoned: 14.047 ± 5.728, median is 13\n",
"\n",
"2x Diving Gryphon, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 8.119 ± 4.926, median is 6\n",
" Turns till Zixor Prime is summoned: 14.379 ± 5.714, median is 14\n",
"\n",
"1x Diving Gryphon, 2x Tracking\n",
" Turns till Zixor deathrattles: 9.289 ± 5.944, median is 7\n",
" Turns till Zixor Prime is summoned: 16.377 ± 6.309, median is 16\n",
"\n",
"1x Diving Gryphon, 1x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.467 ± 6.039, median is 7\n",
" Turns till Zixor Prime is summoned: 16.231 ± 6.109, median is 16\n",
"\n",
"1x Diving Gryphon, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.291 ± 5.825, median is 7\n",
" Turns till Zixor Prime is summoned: 15.851 ± 5.839, median is 16\n",
"\n",
"2x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 11.926 ± 7.437, median is 10\n",
" Turns till Zixor Prime is summoned: 19.017 ± 6.310, median is 20\n",
"\n",
"1x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 11.022 ± 6.795, median is 9\n",
" Turns till Zixor Prime is summoned: 17.776 ± 6.031, median is 18\n",
"\n",
"2x Diving Gryphon, 2x Tracking\n",
" Turns till Zixor deathrattles: 7.676 ± 4.488, median is 5\n",
" Turns till Zixor Prime is summoned: 13.644 ± 5.656, median is 12\n",
"\n",
"2x Diving Gryphon, 1x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 8.046 ± 4.822, median is 6\n",
" Turns till Zixor Prime is summoned: 14.015 ± 5.585, median is 13\n",
"\n",
"2x Diving Gryphon, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 8.101 ± 4.820, median is 6\n",
" Turns till Zixor Prime is summoned: 13.959 ± 5.394, median is 13\n",
"\n",
"1x Diving Gryphon, 2x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.257 ± 5.863, median is 7\n",
" Turns till Zixor Prime is summoned: 15.764 ± 6.029, median is 16\n",
"\n",
"1x Diving Gryphon, 1x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 9.078 ± 5.626, median is 6\n",
" Turns till Zixor Prime is summoned: 15.392 ± 5.740, median is 15\n",
"\n",
"2x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 10.637 ± 6.572, median is 8\n",
" Turns till Zixor Prime is summoned: 17.260 ± 6.013, median is 18\n",
"\n",
"2x Diving Gryphon, 2x Tracking, 1x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.770 ± 4.609, median is 6\n",
" Turns till Zixor Prime is summoned: 13.557 ± 5.450, median is 12\n",
"\n",
"2x Diving Gryphon, 1x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.871 ± 4.640, median is 6\n",
" Turns till Zixor Prime is summoned: 13.561 ± 5.295, median is 12\n",
"\n",
"1x Diving Gryphon, 2x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 8.839 ± 5.475, median is 6\n",
" Turns till Zixor Prime is summoned: 14.925 ± 5.659, median is 15\n",
"\n",
"2x Diving Gryphon, 2x Tracking, 2x Scavenger's Ingenuity\n",
" Turns till Zixor deathrattles: 7.701 ± 4.469, median is 6\n",
" Turns till Zixor Prime is summoned: 13.116 ± 5.097, median is 12\n",
"\n"
]
}
],
"source": [
"analyze(n_beasts=4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also see how the number of beasts in the deck affects the median of the best combination. "
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0: Turns till Zixor Prime is summoned: 10.907 ± 4.197, median is 8\n",
"1: Turns till Zixor Prime is summoned: 11.687 ± 4.627, median is 9\n",
"2: Turns till Zixor Prime is summoned: 12.307 ± 4.875, median is 10\n",
"3: Turns till Zixor Prime is summoned: 12.779 ± 5.043, median is 11\n",
"4: Turns till Zixor Prime is summoned: 13.094 ± 5.120, median is 12\n",
"5: Turns till Zixor Prime is summoned: 13.350 ± 5.199, median is 12\n",
"6: Turns till Zixor Prime is summoned: 13.513 ± 5.203, median is 12\n",
"7: Turns till Zixor Prime is summoned: 13.721 ± 5.236, median is 13\n",
"8: Turns till Zixor Prime is summoned: 13.869 ± 5.267, median is 13\n",
"9: Turns till Zixor Prime is summoned: 14.065 ± 5.301, median is 13\n",
"10: Turns till Zixor Prime is summoned: 14.024 ± 5.283, median is 13\n",
"11: Turns till Zixor Prime is summoned: 14.221 ± 5.334, median is 14\n",
"12: Turns till Zixor Prime is summoned: 14.262 ± 5.336, median is 14\n",
"13: Turns till Zixor Prime is summoned: 14.264 ± 5.302, median is 14\n",
"14: Turns till Zixor Prime is summoned: 14.423 ± 5.345, median is 14\n"
]
}
],
"source": [
"for n_beasts in range(15):\n",
" \n",
" # Build the deck\n",
" combo = 2 * [DivingGryphon] + 2 * [Tracking] + 2 * [ScavengersIngenuity]\n",
" deck = [Zixor] + list(combo) + [SnowflipperPenguin] * n_beasts\n",
" deck += [Wisp] * (30 - len(deck))\n",
" \n",
" def simulate(rng=random):\n",
" deck_copy = deck[:]\n",
" rng.shuffle(deck_copy)\n",
" turn, _, _ = look_for_zixor_prime(deck_copy, rng=rng)\n",
" return turn\n",
"\n",
" sims = [simulate() for _ in range(20_000)]\n",
" avg = stats.mean(sims)\n",
" std = stats.stdev(sims)\n",
" med = stats.median(sims)\n",
" print(f'{n_beasts}: Turns till Zixor Prime is summoned: {avg:.3f} ± {std:.3f}, median is {int(med)}')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment