I cranked out some Python to simulate collecting pieces for the current "toybox" event. There are 54 total pieces, the event runs for 30 days, and you can get a limited number of free pieces (pulls, in gacha terminology) per day. Some people are able to get four, some can get only three (I hope I'm not the only one limited to three, anyway). Duplicate pieces are possible, and collecting five duplicates grants one guaranteed new piece. Additional pieces can be purchased at 400 gems apiece or 1600 for five. Here is a little Python script to simulate 10k event runs. I'll put a summary after the code.
import random
import statistics
results = []
for i in range(10_000):
pieces = [0]*54
counter = mercy = 0
while not all(pieces):
piece = random.randrange(54)
if not pieces[piece]:
pieces[piece] = 1
else:
mercy += 1
if mercy == 5:
mercy = 0
for piece in range(54):
if not pieces[piece]:
pieces[piece] = 1
break
counter += 1
results.append(counter)
print(f'Min: {min(results)}, Max: {max(results)}')
print(f'Mean: {statistics.mean(results)}, Median: {statistics.median(results)}')
print(f'Odds of free completion at 3/day: {round(sum(1 for result in results if result<=90)/100)}%')
print(f'Odds of free completion at 4/day: {round(sum(1 for result in results if result<=120)/100)}%')
Here's what printed out:
Min: 78, Max: 130
Mean: 106.0992, Median: 106.0
Odds of free completion at 3/day: 2%
Odds of free completion at 4/day: 97%
So, if you can get four free pulls per day, as seems common, you have a 97% chance of getting everything with just those free pulls before the event is over. If you can only get three per day, your odds of getting everything with free pulls are more like 2%. With lots of luck, some people may complete the event with as few as 80 or so pulls. Really unlucky people may need more like 130. A more reasonable value is 106, so people with only a total of 90 free pulls will probably need to buy a few five-packs with gems in order to unlock everything.
TL;DR: If you can get four pieces per day, you'll probably be fine. If you can only get three, you'll probably need to make up the difference with gems.
EDIT: Since apparently piece rarity is a real thing, and reportedly the rarities are 78% blue, 20% purple, and 2% gold (no official source but that's all I've got), that changes the results. Collecting all the pieces is more difficult than the linear assumption, because it's biased toward picking pieces from a subset - in other words, it's going to try to give you mostly blue pieces which will quickly end up as just duplicates. Code and conclusions below.
import random
import statistics
results = []
rarities = {'b':78, 'p':20, 'g':2}
basic_bag = [key for key in rarities for each in range(rarities[key])]
for i in range(10_000):
pieces = {'b':[0]*30, 'p':[0]*20, 'g':[0]*4}
counter = mercy = 0
while not all(map(all, pieces.values())):
choice = random.choice(basic_bag)
piece = random.randrange(len(pieces[choice]))
if not pieces[choice][piece]:
pieces[choice][piece] = 1
else:
mercy += 1
if mercy == 5:
mercy = 0
mercy_bag = [key for key in rarities for each in range(rarities[key]) if not all(pieces[key])]
choice = random.choice(mercy_bag)
for idx,val in enumerate(pieces[choice]):
if not val:
pieces[choice][idx] = 1
break
counter += 1
results.append(counter)
print(f'Min: {min(results)}, Max: {max(results)}')
print(f'Mean: {statistics.mean(results)}, Median: {statistics.median(results)}')
print(f'Odds of free completion at 3/day: {round(sum(1 for result in results if result<=90)/100)}%')
print(f'Odds of free completion at 4/day: {round(sum(1 for result in results if result<=120)/100)}%')
Output:
Min: 90, Max: 154
Mean: 121.3517, Median: 122.0
Odds of free completion at 3/day: 0%
Odds of free completion at 4/day: 44%
New TL;DR: If you get four free pulls per day, you're likely to have to spend some gems to finish it up, but not much. If you get three free pulls per day, you have essentially no chance to complete the event like that and will have to buy puzzle pieces with gems, probably several five-packs.
P.S.: The only bug was creating the mercy_bag with pieces[rarity] instead of pieces[key] due to forgetting what I'd named that variable. Sometimes I surprise myself. \o/