Mathy Card Game

A group a cards that show the math expression 5 + 3 = 8.

Published 2024 May 21st
Edited 2025 February 13th

ESL
Game
Math

Oral arithmetic is an important skill for learning more advanced mathematical topics and communicating factual information to others. However, for years I had difficulty finding good ways to practice it in my ESL classes that wasn’t boring or slow. That’s why I developed Mathy Card Game. It’s fun, easy to learn, and takes no time to set up. Players compete to quickly think of a way to calculate a target number using a set of other numbers and common mathematical operations. It’s free for personal use by individual teachers.

How to play

Mathy Card Game can be played by one or many players. In my classes I am the dealer and my students (usually 8 to 12 of them) play with occasional assistance and encouragement from me.

Mathy Card Game uses three kinds of cards: operator cards, target cards, and play cards. Operator cards contain common mathematical operators (+ - × ÷ aba^b =). Target cards contain one of the digits 0 to 9 on each side. Play cards contain one of the digits 1 to 9 on one side. The target cards and play cards are separate decks and should be shuffled before each game.

Plus, minus, times, and equals cards are laid out on the table.

⤷ The dealer lays out all of the operator cards than can be used during the current game. In this picture not all of the operator cards are present, which is better for younger players.

A 4 tens card and a 2 ones card are concatenated to make 42.

⤷ The dealer lays down two target cards, one with the tens side face up and the other with the ones side face up, and makes a two digit number. This is the target number that the players must calculate.

Three plays cards, 5, 8, and 9 are on the table. The players are thinking.

⤷ The dealer lays down a few play cards and the players begin trying to think of way to calculate the target number using the play cards and the operators.

The dealer asks, 'Would you like another card?'.  One player says, 'Yes' and another says, 'OK'.

⤷ After letting the players think for a moment, the dealer asks the players if they would like another card. Players can insist on more time to think. In this image the players agree that they want another card.

The play cards 5, 8, 9, and now 3 are on the table.

⤷ The dealer, with some dramatic flare, places another play card on the table.

A player raises their hand and shouts, 'I know!'

⤷ One player thinks that they have a solution and signals to the dealer.

The player says, 'Nine times five is forty-five. Forty-five minus three is forty-two.'

⤷ In very simple terms the player explains how to calculate the target number using any of the play cards and operators. Not all of the play cards need to be used. There are no restrictions on how often a player may use operators or use figurative parentheses. A number card, however, can only be used once in a calculation. For example, if a player wanted to use 2+22 + 2 there must be two 2s on the table.

The play cards 5, 9, and 3 have been removed from the table. The play card 8 remains.

⤷ The player gets to keep the cards they used in their calculation. Every card is worth one point. The dealer returns the target cards and unused play cards to the bottoms of their respective decks. The dealer then starts another round. The game continues until it’s impossible to calculate the target number with all of the remaining play cards. The player with the most points is the winner.

Make your own set

You can easily make your own copy of the game with just 81 blank cards and something to write with. Of the 81 cards there are 6 operator cards, 40 target cards, and 45 play cards.

Operator cards

Each of the six operator cards has one arithmetic operator depicted in the middle. The cards that are laid on the table at the beginning of the game define what operations are allowed to be performed on the play to read the target number. My personal cards that I use with my students also include notes about how to use them in a spoken expression, often in two different ways.

operatornotesalt notes
++… plus …… and …
-… minus …… subtract …
×\times… times …… multiplied by …
÷\div… divided by …
aba^baa to the power of bb
==… is …… equals …

Yes, I know that == is not an operator, but rather a relational symbol of equality. I keep it with the operator cards for the sake of simplicity and so that my students can be reminded how to speak it.

Play cards

There are 45 play cards. Each one has a digit from 1 to 9 on one side. The other side is blank. The digits are uniformly distributed among the cards, so five 1s, five 2s, etc. Make more if you want your games to last longer.

Target cards

There are 40 target cards. Each side of a target card has a digit from 0 to 9 on both sides. One side is the tens side and the other is the ones side. The digits on the ones side are uniformly distributed, so four 3s, four 4s, etc. The tens side, however, follows are very particular distribution that makes targets in the 20s the most common, with other targets becoming less and less likely in proportion to their distance from the 20s.

digitnumber of cards
05
16
27
36
45
54
63
72
81
91

The tens and ones distributions are independent, so whatever digit is on the tens side of a target card has no bearing on what’s on the ones side. That said, after you finished making all of the ones sides I recommend shuffling the cards before making the tens sides, or vice versa. This will make guessing what’s on the other side of a card more difficult.

My personal set

The set that I use to play with my students is made from a simple pack of blank cards that cost less the $1 USD. Here are what the various cards look like:

The five types of cards. Top-left: An optional title card with 'Mathy Card Game' written on it. Top-middle: The plus operator card with the words 'plus' and 'and' written on it. Top-right: A number 7 play card. Bottom-left: A tens place card with 2 on it. Bottom-right: A ones place card with 3 on it.

I found that cutting the corners differently for each type of card helps tremendously for keeping them organized and separating the different sub-decks when it’s time to play. The target cards are cut such that they form a nice rounded rectangle when placed next to each other. The play cards are cut such that the shape enforces only one obvious way to stack them, so I can trust my students to keep them orderly as they collect them during the game. The operator and title cards are cut differently just to differentiate them when all of the cards are together.

Left: My personal deck of cards. The differently-cut corners make the different groups of cards easy to distinguish. Right: A 10 millimeter corner cutter.

Variations

Considerations

Designing the game

⚠️ By this point you have everything you need to play the game and make your own copy. Everything from here on onward is technical background information about how I developed the game.

Target cards distribution

Because in my ESL classes I mostly teach elementary school aged children, I wanted to keep target numbers rather low, like in the 20s or 30s, but still with a small chance for targets like 00, 99, and everything in between. I thought that it would be nice if the distribution of target numbers across many games could resemble a normal distribution. Of course since there are bounds at 0 and 99, it would have to be a truncated normal1, which has a pretty gnarly PDF:

f(x;μ,σ,a,b)=1σφ(xμσ)Φ(bμσ)Φ(aμσ)f (x;\mu,\sigma,a,b) = \frac{1}{\sigma}\frac{\varphi(\frac{x - \mu}{\sigma})}{\Phi(\frac{b - \mu}{\sigma}) - \Phi(\frac{a - \mu}{\sigma})}

for a<X<ba < X < b, with f=0f = 0 otherwise.

Here φ(ξ)\varphi(\xi) is the PDF of the standard normal distribution and Φ()\Phi(\cdot) is it’s CDF.

I messed around with a plot I found on Desmos, added a few parameters of my own thought that this looked pretty good.

Now my plan was to integrate over portions of the PDF to get the distribution of target cards. For example the frequency of 24 should be 2425f(x)dx0.0152\int_{24}^{25} f(x) \, dx \approx 0.0152

But there’s already a problem. Given the above distribution, the frequency of 99 should be about 0.0017 which is one-ninth the frequency of 24. In other words, to achieve the distribution using real cards, if there were just one 99 target card, there would have to be nine 24s! Along with all of the other target cards there would have to be at least… I’m not sure, but clearly way too many. I had to think of a way to get a good enough approximation of the distribution with a manageable amount of cards.

The solution I arrived at after a lot of tinkering was to split the target numbers into two parts, the tens and the ones, and create independent distributions for each. It’s like answering the question, “Given the original distribution, what’s the probability of drawing a card with a 3 in the tens position?”

I used the Python package SciPy2 to write a little script that crunched to numbers for me. There’s a link to a Google Colab notebook in the footnotes if you want to play around with it3. The TENS_DECK_SIZE and ONES_DECK_SIZE constants exists because I originally wanted them to be separate decks.

arithmetic-card-game.ipynb
from scipy.stats import truncnorm
from scipy.integrate import quad
LOC = 24 # The center of the non-truncated distribution.
SCALE = 36 # The standard deviation of the non-truncated distribution.
LOWER = 0 # The lower bound.
UPPER = 100 # The upper bound.
TENS_DECK_SIZE = 40 # How many cards are in the tens deck.
ONES_DECK_SIZE = 40 # How many cards are in the ones deck.
AUTO_ADJUST = True # Ensure deck has an even number of cards.
# Generate probabilities of each target number
# by integrating over a truncated normal distribution.
# See scipy.stats.truncnorm documentation for more info.
a = (LOWER - LOC) / SCALE
b = (UPPER - LOC) / SCALE
pdf = truncnorm(a, b, loc=LOC, scale=SCALE).pdf
target_probs = [quad(pdf, i, i+1)[0] for i in range(0, 100)]
# Make two arrays that hold the accumulated frequencies of every
# tens and ones digit from the ideal targets distribution.
tens = [0] * 10
ones = [0] * 10
for target, prob in enumerate(target_probs):
tens_digit = target // 10 % 10
ones_digit = target % 10
tens[tens_digit] += prob
ones[ones_digit] += prob
tens_deck = list(map(lambda x: round(x * TENS_DECK_SIZE), tens))
ones_deck = list(map(lambda x: round(x * ONES_DECK_SIZE), ones))
# The tens deck might be off by one. This corrects the error.
# Add to the first moment if there's a deficit and remove a 9 if
# there's a surplus.
error = sum(tens_deck) - TENS_DECK_SIZE
if (error != 0 and AUTO_ADJUST):
print(f'Error: {error}. Adjusting')
if error < 0:
max_count = max(tens_deck)
max_cards = [i for i, j in enumerate(tens_deck) if j == max_count]
mean_card = round(sum(max_cards) / len(max_cards))
tens_deck[mean_card] -= error
print(f'Added {-1 * error} more {mean_card}(s).')
else:
tens_deck[9] -= error
if tens_deck[9] < 0:
raise ValueError('There is a negative amount of 9s!')
print('Tens:')
print({str(i): j for i, j in enumerate(tens_deck)})
print('Ones:')
print({str(i): j for i, j in enumerate(ones_deck)})

Which outputs this.

Tens:
{'0': 5, '1': 6, '2': 7, '3': 6, '4': 5, '5': 4, '6': 3, '7': 2, '8': 1, '9': 1}
Ones:
{'0': 4, '1': 4, '2': 4, '3': 4, '4': 4, '5': 4, '6': 4, '7': 4, '8': 4, '9': 4}

At this deck size only the tens digits follow something like the ideal distribution from the beginning, while the ones digits appear uniformly distributed. This is going to produce something like a stepwise distribution over the course of many samples. Let’s take a look.

arithmetic-card-game.ipynb
import random
import matplotlib.pyplot as plt
DRAWS = 1_000_000
possible_targets = [f'{i:02}' for i in range(LOWER, UPPER)]
drawn_targets = {t: 0 for t in possible_targets}
rand_tens = random.choices(range(len(tens_deck)), weights=tens_deck, k=DRAWS)
rand_ones = random.choices(range(len(ones_deck)), weights=ones_deck, k=DRAWS)
for i in range(DRAWS):
target = str(rand_tens[i]) + str(rand_ones[i])
drawn_targets[target] += 1
labels = drawn_targets.keys()
values = [i / sum(drawn_targets.values()) for i in drawn_targets.values()]
fig, ax = plt.subplots(figsize=(16, 4))
bar_plot = ax.bar(labels, values)
plt.xlim(-0.5, len(drawn_targets.keys())-0.5)
for i, label in enumerate(ax.get_xticklabels()):
if i % 10 != 0:
label.set_visible(False)
plt.show()
A roughly normal distribution, truncated at 0 and 99. It's stepwise, with each step comprising 10 numbers. The highest step is the one that covers 20 to 29.

Looks good to me 🥳

Footnotes

  1. https://en.wikipedia.org/wiki/Truncated_normal_distribution

  2. https://scipy.org

  3. https://colab.research.google.com/drive/11tw68fMM21SAOhcFEP_dWIy7r6UO8x0Q?usp=sharing