Dyota's blog

PowerShell: How many booster packs to complete a whole set?

How many booster packs of Magic: The Gathering would I have to open so that I would have every card in the set?

The current set is Edge of Eternities - this exercise is based on that set.

Results

Let's jump straight to the results.

On average, for Edge of Eternities, you'll need to open 231 booster packs to complete the whole set.

Distribution

At the time of writing, booster boxes come with 30 booster packs. To be almost sure to have all of the commons, you need at least 2 booster boxes.

To be almost sure to have the entire set, you'll need at least 8 booster boxes. 8 booster boxes is 3,360 cards, of which you only need 261 (spells) to complete the set.

Variation - starting off with some cards

In real life, I already have a stack of commons from the set that I purchased individually at the card shop. I wanted to find out if what brought set completion closer. It turns out that it doesn't, especially as all I had were commons, and it doesn't bring me closer to filling out e.g. all of the mythic rares.

Method

To arrive at these numbers, I ran 100 simulations. In each simulation, I open booster packs until I complete the set, and then I tally up how many packs I had to open. After 100 lifetimes, I average out how many cards I get at pack 1, pack 2, and so on.

Data

The raw data for the set is downloaded from the Scryfall API.

Assumptions

Booster Packs

At the time of writing, a booster pack ("Play Booster") is composed of 14 cards.

Real booster packs are probably collated in a certain way, so that the colour and card type distribution are a certain way (e.g. to make sure there are enough creatures, to make sure colours don't clump together, etc).

When composing a booster pack, my algorithm only picks out random cards from the set list, without considering what colours there are or what types they are.

Lands, and special treatments

Magic sets usually have four different pictures for each basic land (i.e. there are five land types, so then there are 20 different basic land "cards"). When assessing set completion, I'm not considering the lands - I'm only considering "spells".

These days, Magic cards always have alternate treatments like borderless, alternate art, and so on. I am not collecting for these.

Full code

I used PowerShell to run these simulations. Full code below.

using namespace System.Collections.Generic; # for List

# $rarities = @(
#     # 'common'
#     # 'uncommon'
#     # 'rare'
#     'mythic'
# )

# $rarities |
#     % {
#         $out = "eoe $_.json"
        
#         # black-border cards only
#         ((iwr "https://api.scryfall.com/cards/search?q=s%3Aeoe+r%3A$_+border%3Ablack&order=set").Content | 
#             convertfrom-json).Data | 
#             # select -first 1 |
#             select collector_number, name, color_identity, rarity  |
#             convertto-json |
#             out-File $out
        
#         s $out
#     }


$c = cat ".\eoe common.json" | convertfrom-json
$u = cat ".\eoe uncommon.json" | convertfrom-json
$r = cat ".\eoe rare.json" | convertfrom-json
$m = cat ".\eoe mythic.json" | convertfrom-json
# $l = cat ".\eoe land.json" | convertfrom-json # lands need to be treated , different art 4x

$all = $c + $u + $r + $m

$setCount = $all.count

$setCount

$collection = [List[PSCustomObject]]::new()

function makeBooster {
    $booster = [List[PSCustomObject]]::new()
    
    $c | get-random -count 7 | % { $booster.add($_) }
    $u | get-random -count 3 | % { $booster.add($_) }
    
    # one of out seven chance to get a mythic
    $mythicChance = get-random -max 7 -min 0
    
    if ($mythicChance -eq 0) {
        $booster.add(($m | get-random -count 1))
    } else {
        $booster.add(($r | get-random -count 1))
    }
    
    # $booster.add(($l | get-random -count 1))
    
    $booster.add(($all | get-random -count 1))
    $booster.add(($all | get-random -count 1))
    
    return $booster
}

function crackapack {
    $booster = makeBooster
    
    $newCards = [list[pscustomobject]]::new()

    $booster | % {
        # $_

        # read-host

        $collectorNumber = $_.collector_number
    
        if (-not ($collectorNumber -in $collection.collector_number)) {
            $collection.add($_)
            $newcards.add($_)
        }
    }

    return $newcards
}

function simulate {
    $data = [list[pscustomobject]]::new()
    
    $packsOpened = 0
    
    function collectionCount {
        ( $collection | ? { $_.rarity -eq 'common' -or  $_.rarity -eq 'uncommon' } ).count
    } 

    $collectionCount
    
    $subsetCount = ($all | ? { $_.rarity -eq 'common' -or  $_.rarity -eq 'uncommon' } ).count

    $subsetCount

    while (( $collection | ? { $_.rarity -eq 'common' -or  $_.rarity -eq 'uncommon' } ).count -lt $subsetCount) {
        
        $packsOpened++
    
        $record = [ordered] @{
            pack = $packsOpened
            newCards = ,(crackapack).collector_number
            collectionCount = $collection.count
            profile = $collection | group rarity | select count, name
        }

        # $record
        collectionCount
    
        $data.add($record)
    }

    
    return $data
}


simulate # | convertto-json -depth 4 > out.json

#montecarlo #powershell