Fundamentals 8 min read

Solving the 24‑Point Game with Python: Two Algorithmic Approaches

This article explains how to determine whether any four playing cards can be combined using addition, subtraction, multiplication, and division (with parentheses) to reach 24, presenting two Python implementations—an exhaustive enumeration method and a more efficient recursive combination technique—along with performance comparisons.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Solving the 24‑Point Game with Python: Two Algorithmic Approaches

The 24‑point game asks whether four given playing cards can be combined with the four basic arithmetic operations and parentheses to produce the value 24, using each card exactly once.

First solution (enumeration): All possible operator sequences (3 positions) and parenthesization patterns are generated, producing 448 formula templates. Each permutation of the four card values (up to 24) is substituted into these templates, evaluated with eval , and any expression that rounds to 24 is recorded. The implementation is shown below:

<code>import itertools
class CardGaming:
    def __init__(self):
        self.formula_list = list()
        for marks in itertools.product(["+", "-", "*", "/"], repeat=3):
            for bracket in ["{0}%s{1}%s{2}%s{3}", "({0}%s{1})%s{2}%s{3}", "({0}%s{1}%s{2})%s{3}",
                            "{0}%s({1}%s{2})%s{3}",
                            "{0}%s({1}%s{2}%s{3})", "({0}%s{1})%s({2}%s{3})", "{0}%s{1}%s({2}%s{3})"]:
                self.formula_list.append(bracket % marks)
    def solve(self, card_probability):
        answer = []
        for card_order in set(itertools.permutations(card_probability, 4)):
            for formula in self.formula_list:
                final_formula = formula.format(*card_order)
                try:
                    if round(eval(final_formula), 3) == 24:
                        answer.append(final_formula)
                except ZeroDivisionError:
                    continue
        return answer
if __name__ == "__main__":
    print(CardGaming().solve((3, 3, 8, 8)))  # Example output: 8/(3-8/3)
</code>

This method must evaluate up to 4³ × 7 = 448 formula templates for each of the 24 permutations, resulting in 10 752 evaluations per card set. Processing all 13⁴ = 28 561 possible card combinations takes about 1906 seconds on an i7‑7700 with 8 GB RAM.

Second solution (recursive combination): Observing that any valid expression can be built by repeatedly selecting two numbers, applying an operation, and inserting the result back into the list, the algorithm enumerates all unordered pairs and their possible results, avoiding duplicate formula generation. This eliminates the need for eval and reduces the search space dramatically. The core implementation is:

<code>def solve(card_probability):
    card_probability = list(card_probability)
    answer = []
    for combine_1 in itertools.combinations(card_probability, 2):
        for answer_1 in all_maybe_count_answer(combine_1[0], combine_1[1]):
            card_list_1 = copy.deepcopy(card_probability)
            card_list_1.remove(combine_1[0])
            card_list_1.remove(combine_1[1])
            card_list_1.append(answer_1)
            for combine_2 in itertools.combinations(card_list_1, 2):
                for answer_2 in all_maybe_count_answer(combine_2[0], combine_2[1]):
                    card_list_2 = copy.deepcopy(card_list_1)
                    card_list_2.remove(combine_2[0])
                    card_list_2.remove(combine_2[1])
                    card_list_2.append(answer_2)
                    for combine_3 in itertools.combinations(card_list_2, 2):
                        for answer_3 in all_maybe_count_answer(combine_3[0], combine_3[1]):
                            if round(answer_3, 3) == 24:
                                answer.append(total_formula(card_probability, combine_1, answer_1, combine_2,
                                                            answer_2, combine_3, answer_3))
    return answer
if __name__ == "__main__":
    start_time = time.time()
    for cards in itertools.product(range(1,14), repeat=4):
        solve(cards)
    print("计算时间:", time.time() - start_time)
</code>

For each card set this approach only needs to consider C(4,2) × 6 × C(3,2) × 6 × C(2,2) × 6 = 3 888 possibilities, reducing total computation time for all combinations to about 136 seconds—over ten times faster than the enumeration method.

Both implementations demonstrate how algorithmic design choices (enumeration vs. recursive reduction) dramatically affect performance when solving combinatorial arithmetic puzzles like the 24‑point game.

performancealgorithm24-point gamerecursivecombinatorial searchenumeration
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.