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.
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