Countdown Numbers Game Solver

Version 4B, notes updated 2025-06-21

Overview

Contestants on the UK Channel 4 TV game show Countdown have thirty seconds in the Numbers Round to work out how to calculate a three-digit random target number (or get as close as possible). They may apply addition, subtraction, multiplication and division to some or all of the six numbers (each used at most once) from a partially random selection. This document describes a computer program that solves any given Numbers Round game. It is the author’s personal endeavour and is in no way affiliated.

The program reads a list of numbers and a target. It finds and displays in human-friendly fashion all distinct ways of calculating the best solution.

It is one of many similar programs to be found online today. It started as a 1988 Christmas break project under Xenix on a 12 MHz Intel 80286 CPU. It was a challenge then to write code that would find any solution within half a minute if there was one. Version 1 did this but it took some minutes to conclude without a result when there was no exact solution. Now updated for Linux, the same core solver runs much more quickly on a modern processor. The updates have improved completeness and, from a much more interesting angle, improved human-friendliness in ways described below.

The program’s essential elements, covered later in more detail, are embodied in a search procedure and a display procedure. They are:

       find all sufficiently close candidate solutions by brute force search;

       discard those containing ineffectiveness;

       discard the least human-friendly alternative when duplication is found;

       keep but mark those containing over-complexity; and

       present those that remain in human-friendly order.

The baseline code reads a problem from its command line and prints the solutions. There is also a text-graphic interface version (not provided) which additionally provides suggested solutions for the Letters Round.

Brute Force Search

The program will find single number solutions from the list without arithmetic. This accommodates a target of 100 when 100 is also present in the list.

However, at the program’s core is a brute force search procedure. There are more elegant methods but they may be no better in practice. The procedure takes a list of numbers, selects all unordered pairs and applies all allowed arithmetic operations to each pair (the correct way round). For each result it updates a record of how the calculation reached that result.

Whenever an arithmetic operation produces an interesting result – one close enough to the target – a display procedure gets called. This procedure checks the calculation. If it is a good candidate solution it is stored in displayable form. “Interesting” starts at “10 away” from the target. This distance reduces whenever a better result is found. Less interesting candidate solutions found thus far get discarded.

If a result is equal to the target, onward search is terminated and the search procedure returns. Otherwise the search procedure calls itself recursively, with a shorter numbers list where the selected pair of numbers is replaced by the result, returning instead when this is no longer possible.

This search covers all possible calculations and will always find every solution. With six numbers and four arithmetic operators, the search procedure could in principle check up to 3,516,060 calculated results (in addition to six checks on the numbers in the list).

The final list of candidate solutions stored during the search is printed when the search is complete.

Keeping Track of the Calculation

The search procedure records its current calculation using a binary expression tree. The tree is maintained on a stack that grows and shrinks with the recursive search. Each tree node on the stack contains two operands, pointers to where each originated (either the list or another tree node) and an operator. The result currently reached by the search procedure is the one that may be calculated from the top entry of the stack.

Distinct Solutions

To keep just distinct solutions the search procedure discards calculations containing ineffectiveness. The display procedure removes any remaining ineffectiveness; detects duplication, keeping just the most human-friendly version of a candidate solution; and keeps but marks candidate solutions containing over-complexity so the user can decide if these are distinct or not.

For more detail of the categorizations above:

       Ineffectiveness: This arises when an operation makes no progress towards the target. The first type is multiplying or dividing a number by one; the second is subtracting two identical numbers; the third is a division that does not produce an integer result. Whenever one of these happens, onward search is terminated, which significantly trims the number of results examined. A fourth type, handled by the display procedure, is an unconnected sub-calculation – some arithmetic that does not get used in obtaining the final result. Such sub-calculations get ignored when processing and storing a candidate solution.

       Duplication: The brute force search generates arithmetically indistinguishable candidate solutions more than once. A candidate solution is a duplicate if its key is the same as that of a stored one. Details of how the key is generated are given separately below. When a duplicate is found just the one that scores best for human-friendliness gets kept.

       Over-complexity: This arises when an operation, or a connected sequence of operations, generates a result that duplicates an input into its calculation. Simple examples include 6–3=3 and 9/3=3. These, in effect, simply discard a number. More convoluted, the example sequence 8–6=2 then 2x3=6 in effect just discards 8 and 3; or 4x4=16 then 16/8=2 effectively discards a 4. The sequence 3+3=6 then 6+3=9 (i.e. 3+3+3=9 vs 3x3=9) effectively discards a 3 but is not marked because it does not duplicate one of its inputs.

There is at least one further question about whether the availability of choice in a calculation can lead to duplication or over-complexity not detected as described above. For example, choice (i) when there are two identical numbers in the list; (ii) when a partial calculation generates a result that is also available as a number from the list; or (iii) when two partial calculations give the same result. The current key generation code answers “duplicate” in cases (i) and (iii) but “distinct” in case (ii).

Exactly what puts a candidate solution into one or more of these categories, whether the program gets it right or not, and whether there are missing categories may need further study. The small differences between good online solvers likely arise from subtly different answers to the elements of the distinct solutions question.

Key Generation

When the search procedure calls the display procedure with an interesting result, the display procedure generates the candidate solution’s key to check if it is a duplicate.

To generate the key, the binary expression tree on the stack gets printed to a string as though collapsed into an n-way expression tree. This results in a sequence of sub-calculations. This is done by (i) merging connected add and subtract operations; (ii) merging connected multiply and divide operations; and (iii) printing each resulting sub-calculation with its operations arranged so that its operands are numerically ordered. Finally the set of sub-calculations in the string get alphabetically re-ordered.

This string – the key – is a canonical way of presenting the solution that is not human-friendly but seems to be sufficient to identify each set of arithmetically indistinguishable solutions all sharing the same key.

The principle (with slightly different implementation) is illustrated nicely here by Ben North. It also seems to be very similar to the one explained here by Graeme Cole as used in his solver.

Human-Friendly Solution Presentation

The key generation code is also used to generate the displayable solution. It operates as described above except for some differences described below to make the output human-friendly.

Connected sequences of calculations involving just addition and subtraction are merged by default into a single sub-calculation for display. This is exactly as for key generation. Contestants seem to do this. The ordering of operations and operands is chosen to approximate how contestants typically explain a solution. For the key consistency is all that is required. Human-friendly ordering is sufficient.

Merging can also be done for sequences containing just multiplication and division. This is much less commonly done by contestants and its human-friendliness may be questioned. By default this particular merge is not done for display. When a sequence of multiplications and/or divisions arises, whether they get merged or not, there is a question needing further study about the order in which contestants typically explain it.

Human-friendliness is enhanced by selecting from each set of arithmetically indistinguishable solutions the one whose displayed sub-calculation order has the lowest heuristic “distance score”. The lowest distance score means that as many sub-calculations as possible are displayed immediately after the sub-calculations that make the results they use. This typically preserves a good “narrative” order – such as a contestant might use to explain a solution. The distance score code also has an element that encourages a sub-calculation display order that approaches the target as quickly as possible without going too far above, in a way contestants often seem to do. This is a difference from key generation’s ordering.

Finally, distinct solutions are scored for complexity according to a heuristic “user score”. Solutions with the lowest complexity are displayed first. Contestants often find one of the simpler solutions but there is also an observable tendency to favour particular forms in the final sub-calculation. This needs further study.

Compiling and Running

Install a Linux operating system with gcc. Compile from a command line with gcc -O3 -o numbers-tty numbers-tty.c.

To solve example 1 below run “./numbers-tty 10 4 1 7 7 75 =778” from a command line. A problem’s numbers and target may appear in any order with the target identified by a preceding “=”.

Customization

There are game policy switches in the code for turning on or off validity checks for the numbers list and for setting its acceptable length. The range of acceptable target values can be specified. The minimum must be 1 or more. A maximum of 0 is interpreted as unlimited. The maximum distance between result and target that makes a solution interesting can be set.

There are constants for enabling or disabling solution sub-calculation merging (both types) and heuristic scoring. The user score heuristic can be tuned (but the distance score heuristic needs work in this respect). Over-complex solutions may be shown or excluded. Quite a few solution display options can be changed if desired.

Speed

To test speed, run “time numbers-tty <problem X> >/dev/null” and record user time.

       <problem A>: 1 1 2 2 3 4 =959 (tests searching only – there are no close enough solutions to display)

       <problem B>: 2 4 6 8 10 75 =750 (tests searching plus processing to display 225 distinct solutions)

       <problem C>: 1 2 3 4 5 6 =7 (tests searching plus processing to display 1015 distinct solutions – needs the default minimum target reduced)

On 2024-07-07 with an Intel Celeron processor N5015: problem A: 8-14 ms; problem B: 35-47 ms; problem C: 132-142 ms.

Problem and Solution Examples

Reproduced below is a selection from a bigger suite of examples used for testing (hence there may be discontinuous numbering). If “*” is tagged onto to the solution number it contains over-complexity. Note that the numbering and presentation of solutions depends on the “tuning” of the heuristic scoring. This might have changed since the examples were created.

Graeme Cole’s numbers game solver (https://graemecole.uk/countdown/) when asked to find all solutions (2025-06) shows a few differences compared to my solver. The differences seem to be related to showing some solutions with what I consider over-complexity but not showing others (e.g. example 16 below). This has not yet been investigated to see if there’s something in my baseline code to be corrected.

Ben North’s solver (https://bennorth.github.io/countdown-numbers-solver/) seems to reproduce my examples (2025-06). However, it seems to give duplicate solutions whenever a number is duplicated in the list as in several examples below (this is mentioned on his web site).

Example 1

Target: 778 from 10 4 1 7 7 75
[1]   75x10=750  7x4=28  750+28=778
[2]   75+1=76  76x10=760  760+7+7+4=778
[3]   75x10=750  4+1=5  7x5=35  750+35-7=778
[4]   75x10=750  4-1=3  7x3=21  750+21+7=778
[5]*  75x10=750  7+1-4=4  7x4=28  750+28=778

Example 2

Target: 849 from 100 3 2 7 10 10
[1]   10-2=8  100x8=800  10-3=7  7x7=49  800+49=849
[2]   100+10-3=107  10-2=8  107x8=856  856-7=849
[3]   100+7=107  10-2=8  107x8=856  856+3-10=849

Example 3

Target: 669 from 50 100 75 25 7 9
[1]   75x9=675  100+50=150  150/25=6  675-6=669
[2]   100x7=700  50x9=450  450/75=6  700-25-6=669

Example 4

Target: 643 from 25 100 9 9 1 1
[1]   25x9=225  225+100=325  1+1=2  325x2=650  650-9=641
[2]   100-1=99  99x9=891  9+1=10  25x10=250  891-250=641

Example 6

Target: 477 from 8 4 7 4 3 50
[1]   50x8=400  4+4+3=11  11x7=77  400+77=477
[2]   50+3=53  8+4+4-7=9  53x9=477
[3]   50+7-4=53  8+4-3=9  53x9=477
[4]   50+3=53  8/4=2  7+2=9  53x9=477
[5]   50+3=53  4/4=1  8+1=9  53x9=477
[6]   50-7=43  8+3=11  43x11=473  473+4=477
[7]   50+3=53  4x4=16  16-7=9  53x9=477
[8]   50x8=400  7+4=11  4+3=7  11x7=77  400+77=477
[9]   4+4=8  50x8=400  8+3=11  11x7=77  400+77=477
[10]* 50+3=53  8/4=2  7+4-2=9  53x9=477
[11]  50-8-4=38  38x4=152  152+7=159  159x3=477
[12]* 50+3=53  8/4=2  4/2=2  7+2=9  53x9=477
[13]  4+4=8  8x8=64  64-3=61  61x7=427  427+50=477
[14]* 50+3=53  8-4=4  4x4=16  16-7=9  53x9=477
[15]  50+3=53  7x4=28  28+8=36  36/4=9  53x9=477

Example 7

Target: 701 from 25 9 10 2 3 8
[1]   10x3=30  30-2=28  28x25=700  700+9-8=701
[2]   25x9=225  225x3=675  8x2=16  675+16+10=701
[3]   9x8=72  72x10=720  3x2=6  720+6-25=701
[4]   25x3=75  75+2=77  77x9=693  693+8=701
[5]   10x9=90  90-2=88  88x8=704  704-3=701
[6]   25x9=225  225+8=233  233x3=699  699+2=701
[7]   25+10+9=44  44x8=352  352x2=704  704-3=701
[8]   10x2=20  20+9=29  29x25=725  8x3=24  725-24=701
[9]   25x10=250  250-9-8=233  233x3=699  699+2=701
[10]  8/2=4  25x3=75  75-4=71  71x10=710  710-9=701
[11]  25x3=75  8/2=4  75+4=79  79x9=711  711-10=701
[12]  25-2=23  23x9=207  207x3=621  10x8=80  621+80=701
[13]  25x9=225  225+10=235  235x3=705  8/2=4  705-4=701

Example 8

Target: 718 from 3 5 3 7 10 75
[1]   75x10=750  7x5=35  750+3-35=718
[2]   75-3=72  72x10=720  720+5-7=718
[3]   75-3=72  72x10=720  720+3-5=718
[4]   75+3-7=71  71x10=710  710+5+3=718
[5]   75-5=70  70x10=700  7x3=21  700+21-3=718
[6]   75x3=225  225x3=675  10x5=50  675+50-7=718
[7]   75x10=750  5+3=8  7-3=4  8x4=32  750-32=718
[8]   75-3=72  72x10=720  7+3=10  10/5=2  720-2=718
[9]   75-3=72  7+3=10  72x10=720  10/5=2  720-2=718

Example 9

Target: 556 from 10 4 1 7 7 75
[1]   75+4-1=78  78x7=546  546+10=556
[2]   75+4=79  79x7=553  553+10-7=556
[3]   75+7-4=78  78x7=546  546+10=556
[4]   7+1=8  10x8=80  80x7=560  560-4=556
[5]   75x7=525  4-1=3  7x3=21  525+21+10=556
[6]   75x7=525  7-4=3  10x3=30  525+30+1=556
[7]   10x7=70  75+70+1-7=139  139x4=556
[8]   75+10+7=92  7-1=6  92x6=552  552+4=556
[9]   75-1=74  74x7=518  7x4=28  518+28+10=556
[10]  7+7=14  14x10=140  140-1=139  139x4=556

Example 11

Target: 794 from 25 100 2 3 2 6
[1]   3x2=6  6+2=8  100x8=800  800-6=794
[2]   6+2=8  100x8=800  3x2=6  800-6=794
[3]   100-2-2=96  96/3=32  32x25=800  800-6=794
[4]   100+2=102  6+2=8  102x8=816  816+3-25=794
[5]   100+2=102  102/3=34  34-2=32  32x25=800  800-6=794
[6]   2x2=4  100-4=96  96/3=32  32x25=800  800-6=794
[7]   2/2=1  25-1=24  24/3=8  100x8=800  800-6=794
[8]   6-2=4  100x4=400  400-3=397  397x2=794
[9]   6x2=12  25x12=300  300+100-3=397  397x2=794
[10]  6x2=12  25x12=300  300-2=298  298x3=894  894-100=794

Example 16

Target: 732 from 4 2 9 5 7 7
[1]   9x7=63  63-2=61  7+5=12  61x12=732
[2]*  9x7=63  63+2-4=61  7+5=12  61x12=732
[3]   9x5=45  45+7=52  52x2=104  104x7=728  728+4=732
[4]   4+2=6  9x6=54  54+7=61  7+5=12  61x12=732
[5]*  9x7=63  4/2=2  63-2=61  7+5=12  61x12=732

 

John A. Phillips