This command line runs in 151ms.

**Run:** [ @repl.it ]

% python enigma.py SubstitutedSum "COMPUTER + TAPRURCR = COAXOXPRO" (COMPUTER + TAPRURCR = COAXOXPRO) (10248935 + 96458515 = 106707450) / A=6 C=1 E=3 M=2 O=0 P=4 R=5 T=9 U=8 X=7

**Solution:** The sum is: 10248935 + 96458515 = 106707450.

This Python program runs in 270ms.

**Run:** [ @repl.it ]

from enigma import subsets, join, printf films = { 'Cosiblanket': 'JC, JG, BS', 'Top Hit': 'BD, JG, KH', 'Stagecrouch': 'HB, CG, KH', 'A Star is Bone': 'HB, JC, JS', 'Mildred Purse': 'CG, ST, JW', 'High None': 'HB, JG, ST', 'King Koala': 'FA, JG, CG', 'Random Harpist': 'BD, KH, JS', 'Now Forager': 'JC, BD, CG', 'Mrs Minimum': 'CG, JS, JW', 'The Adventures of Robin Hoop': 'KH, BS, JW', 'The Maltese Foghorn': 'FA, JC, ST', 'Mr Deeds goes to Tune': 'BS, JS, ST', 'Meet me in St Lucy': 'BD, CG, BS', 'Gone with the Wine': 'FA, HB, BD', 'Singing in the Rind': 'FA, JC, KH', 'Mutiny on the Bunting': 'BD, ST, JW', 'The Best Years of our Lifts': 'FA, HB, BS', 'Double Identity': 'FA, JG, JS', } # map the films to stars and pairs of stars stars = dict() pairs = dict() for (k, v) in films.items(): ss = sorted(v.split(", ")) stars[k] = set(ss) pairs[k] = set(subsets(ss, size=2)) # union of a collection of sets union = lambda ss: set().union(*ss) # choose 7 films for fs in subsets(films.keys(), size=7): # count the total number of stars in all the films: ss = union(stars[f] for f in fs) # there should be 7 if len(ss) != 7: continue # count the total number of pairs in all the films: ps = union(pairs[f] for f in fs) # there should be 21 if len(ps) != 21: continue # output the solution fmt = lambda ss: join(ss, sep="; ") printf("{fs} -> {ss}", fs=fmt(fs), ss=fmt(sorted(ss)))

**Solution:** The seven films selected are: High None; The Best Years of our Lifts; Double Identity; A Star is Bone; Cosiblanket; Mr Deeds goes to Tune; The Maltese Foghorn.

These seven films feature all possible pairings of: Barbara Standup; Fred Astride; Humphrey Bigheart; Joan Crowbar; Judy Garage; James Student; Spencer Treacle.

]]>I’m sure the matelots call the rope a painter or something equally strange to us landlubbers.

And if they wanted to prevent the yacht being damaged in the storm, they’d tie it up at the stern as well. ]]>

In the first diagram the length of rope (hypotenuse) is *1 + x* metres. In the second diagram the length of the rope is *x* metres.

Applying the triangle inequality to the first diagram we get:

(1 + x) < (1) + (h)

x < h

But in the second diagram the longest side is *x*, so:

x > h

These two statements are contradictory, so the situation cannot arise.

]]>**Run:** [ @repl.it ]

from enigma import enigma, irange, icount, unpack, seq_all_same, subsets, printf # the predicates operating on (<pos>, <prev pos>) p1 = lambda a, b: enigma.is_prime(a * b) p2 = lambda a, b: enigma.is_square(a * b) p3 = lambda a, b: enigma.is_cube(a * b) p4 = lambda a, b: (a * b) % 2 p5 = lambda a, b: a < b # the list of predicates ps = list(unpack(p) for p in (p1, p2, p3, p4, p5)) # check all predicates have the same number of values for sequence <s> check = lambda s: seq_all_same(icount(s, p) for p in ps) # consider possible "last week" positions for s in subsets(irange(1, 6), size=6, select='P'): # make ("this week", "last week") pairs s = list(enumerate(s, start=1)) # find a soloution if check(s): printf("{s}")

**Solution:**The positions are:

1st: 6th last week (p5: higher position)

2nd: 1st last week (p1: 2×1 = 2 is prime)

3rd: 3rd last week (p2, p4: 3×3 = 9 is square, is odd)

4th: 2nd last week (p3: 4×2 = 8 is cube)

5th: 4th last week

6th: 5th last week

Each predicate is satisfied by exactly one of the pairs, as indicated.

]]>It runs in 92ms.

**Run:** [ @repl.it ]

from enigma import subsets, join, printf # possible moves delta = dict(N=(0, 1), S=(0, -1), E=(1, 0), W=(-1, 0)) # find where path p starts, if it finishes at (x, y) # return ((x, y), <visited nodes>) def solve(x, y, p, s=[]): # are we done? if not p: yield ((x, y), s) else: # retrace the last step (dx, dy) = delta[p[-1]] x -= dx y -= dy if not(x < 0 or x > 4 or y < 0 or y > 3): yield from solve(x, y, p[:-1], [(x, y)] + s) # peter's path path = "PQRQPQPSSPSPQRQ" # assign the directions for vs in subsets('NEWS', size=4, select='P'): # translate the path d = dict(zip('PQRS', vs)) p = join(d[x] for x in path) # find a possible starting point for ((x, y), s) in solve(3, 1, p): # we only visit the end once if (3, 1) in s: continue # output solution printf("start = ({x}, {y}), path = {p}")

**Solution:** The Swan is shown as the green dot on the diagram below:

The path Peter takes is indicated with the blue arrows. P = East, Q = South, R = West, S = North.

The pub immediately north of The Bull is visited twice.

]]>**Run:** [ @repl.it ]

from enigma import Football, digit_map, irange, update # scoring system football = Football(points=dict(w=2, d=1)) # label the teams teams = (A, B, C, D, E) = (0, 1, 2, 3, 4) # columns from the table (with "obvious" values filled out; x, y, z for the missing goals values) (table, gf, ga) = (dict(played='?2?21', w='?0?2?', l='?1?0?', d='11?0?', points='?1?4?'), '4xC93', '56y5z') # find possible match outcomes for (ms, d) in football.substituted_table(table, d=digit_map(0, 12)): # no team played more than 2 matches if any(football.table(*(football.extract(ms, t))).played > 2 for t in teams): continue # choose a value for B's goals for = x, C's goals against = y for x in irange(0, 5): for y in irange(0, 12 + x): d2 = update(d, 'xyz', [x, y, 12 + x - y]) # find possible scorelines (using the goals for/against columns) for ss in football.substituted_table_goals(gf, ga, ms, teams=[A, B, D, E, C], d=d2): # output solution football.output_matches(ms, ss, teams="ABCDE")]]>

#!/usr/bin/env python -m enigma -r # E E E x y z # * E E E * x y z # ----------- ----------- # O O E E E E a b c d = xyz * x # O E E E E e f g h = xyz * y # E O O E i j k m = xyz * z # ----------- ----------- # E O E O O E n p q r s t = xyz * xyz # =========== =========== SubstitutedExpression --symbols="abcdefghijkmnpqrstxyz" --distinct="" --invalid="0,abeijknprsx" --invalid="1|3|5|7|9,cdfghimnqtxyz" --invalid="2|4|6|8,abejkprs" "xyz * xyz = npqrst" "xyz * x = abcd" "xyz * y = efgh" "xyz * z = ijkm" --answer="xyz"]]>

]]>(a = 103 + 60(n – 2), a + 3, a + 6, a + 9, a + 10, a + 12, a + 13, a + 15, a + 16)

# One dart possible scores scores1 = set() for n in range(21): scores1.add(n) # add single, double and treble numbers scores1.add(n*2) scores1.add(n*3) scores1.add(25) # add inner scores1.add(50) # add bull all_singles = set(range(61)) print("Lowest number not available with a single dart is", \ min(sorted((all_singles.difference(scores1))))) # Two dart possible scores scores2 = scores1 all_singles2 = set(range(121)) scores_2d = set() for a in scores1: for b in scores2: scores_2d.add(a + b) print("Lowest number not available with a two darts is", \ (min(sorted((all_singles2.difference(scores_2d)))))) # Three dart possible scores scores3 = scores1 all_singles3 = set(range(181)) scores_3d = set() for a in scores1: for b in scores2: for c in scores3: scores_3d.add(a + b + c) print("Lowest number not available with three darts is", \ min(sorted((all_singles3.difference(scores_3d))))) # Now print all numbers not available with one, two and three darts print() print("Lowest numbers not available with a single dart are", \ (sorted((all_singles.difference(scores1))))) print() print("Lowest numbers not available with a two darts are", \ (sorted((all_singles2.difference(scores_2d))))) print() print("Lowest numbers not available with three darts are", \ sorted((all_singles3.difference(scores_3d)))) # Lowest number not available with a single dart is 23 # Lowest number not available with a two darts is 103 # Lowest number not available with three darts is 163 # Lowest numbers not available with a single dart are [23, 29, 31, 35, 37, # 41, 43, 44, 46, 47, 49, 52, 53, 55, 56, 58, 59] # Lowest numbers not available with a two darts are [103, 106, 109, 112, # 113, 115, 116, 118, 119] # Lowest numbers not available with three darts are [163, 166, 169, 172, # 173, 175, 176, 178, 179]]]>

And if B’s “goals for” is *x*, then we have:

x < 6

And if C’s “goals against” is *y* and E’s “goals against” is *z*, then we have:

28 + x = 16 + y + z

12 + x = y + z

We can use the [[ `Football()` ]] helper class from the **enigma.py** library to complete the table (although the program is not very elegant).

This Python program runs in 129ms.

**Run:** [ @repl.it ]

from enigma import Football, irange, printf # scoring system football = Football(points=dict(w=2, d=1)) # choose outcomes for D's games for (AD, BD, CD, DE) in football.games(repeat=4): D = football.table([AD, BD, CD, DE], [1, 1, 1, 0]) if not(D.played < 3 and D.points == 4): continue # choose outcomes for B's remaining games for (AB, BC, BE) in football.games(repeat=3): B = football.table([AB, BC, BD, BE], [1, 0, 0, 0]) if not(B.played < 3 and B.l == 1 and B.points == 1): continue # choose outcomes for E's remaining games for (AE, CE) in football.games(repeat=2): E = football.table([AE, BE, CE, DE], [1, 1, 1, 1]) if not(E.played < 3 and E.played == 1): continue # the remaining game for AC in football.games(): A = football.table([AB, AC, AD, AE], [0, 0, 0, 0]) if not(A.played < 3 and A.d == 1): continue C = football.table([AC, BC, CD, CE], [1, 1, 0, 0]) if not(C.played < 3): continue # scores in A's games for (sAB, sAC, sAD, sAE) in football.scores([AB, AC, AD, AE], [0, 0, 0, 0], 4, 5): # scores in D's remaining games for (sBD, sCD, sDE) in football.scores([BD, CD, DE], [1, 1, 0], 9, 5, [sAD], [1]): # choose a value for B's "goals for" = x for x in irange(0, 5): # scores in B's remaining matches for (sBC, sBE) in football.scores([BC, BE], [0, 0], x, 6, [sAB, sBD], [1, 0]): # choose values for C and E's "goals against" = y, z for y in irange(0, 12 + x): # score in C's remaining match for (sCE,) in football.scores([CE], [0], 12, y, [sAC, sBC, sCD], [1, 1, 0]): # verify goals for/against E (fE, aE) = football.goals([sAE, sBE, sCE, sDE], [1, 1, 1, 1]) if not(fE == 3 and aE == 12 + x - y): continue printf("AB={AB}:{sAB} AC={AC}:{sAC} AD={AD}:{sAD} AE={AE}:{sAE} BC={BC}:{sBC} BD={BD}:{sBD} BE={BE}:{sBE} CD={CD}:{sCD} CE={CE}:{sCE} DE={DE}:{sDE}") printf("A={A}") printf("B={B}") printf("C={C}") printf("D={D}") printf("E={E}") printf()

**Solution:** The goals in the played matches are: A vs B = 1-1; A vs D = 3-4; B vs D = 2-5; C vs E = 12-3.

The other matches are not yet played.

]]>With 2 darts 101 = 50 + 3× 17.

]]>This Python program finds the answers for 1, 2 and 3 darts.

**Run:** [ @repl.it ]

from enigma import irange, subsets, printf # possible scores with a single dart scores = set([0, 25, 50]).union(*([x, 2 * x, 3 * x] for x in irange(1, 20))) M = max(scores) # find the scores not achievable with k darts def solve(k): ss = set(sum(s) for s in subsets(scores, size=k, select='R')) for n in irange(0, k * M): if n not in ss: yield n # find lowest scores for k=1, 2, 3 darts for k in (1, 2, 3): for n in solve(k): printf("{k} darts -> {n}") break

**Solution:** For 1 dart the lowest unachievable score is 23. For 2 darts it is 103. For 3 darts it is 163.

The sequence can be characterised:

a(1) = 23

a(n) = 103 + 60(n – 2)

See: OEIS A241746.

]]>We’ve done puzzles similar to this one before.

I reused some code from **Enigma 1068** in this Python program.

**Run:** [ @repl.it ]

from itertools import product from enigma import is_duplicate, irange, tri, fib, subsets, printf # select three digit numbers with non-repeating digits # from iterator <i> of increasing non-negative integers def numbers(i): s = set() for n in i: if n < 100: continue if n > 999: break if is_duplicate(n): continue s.add(n) return s # candidate triangular numbers tris = numbers(tri(i) for i in irange(13, 44)) # candidate fibonacci numbers fibs = numbers(fib(1, 1)) # candidate cube numbers cubes = numbers(i ** 3 for i in irange(5, 9)) # find sets that contain 9 different digits ss = sorted(t for t in product(tris, fibs, cubes) if not is_duplicate(*t)) # choose the setters set for D in ss: sD = set(D) # Harry's and Tom's sets have exactly 1 number in common HTs = list(s for s in ss if len(sD.intersection(s)) == 1) # choose sets for Harry and Tom for (H, T) in subsets(HTs, size=2): # there should be 7 different numbers in total # and no numbers common to all three if len(sD.union(H, T)) != 7 or sD.intersection(H, T): continue printf("D={D} H/T={H}/{T}")

**Solution:** The numbers the setter chose (in numerical order) are: 216, 435, 987.

435 = T(29)

987 = F(16)

216 = 6³

One of the others chose: 435, 610, 729.

435 = T(29)

610 = F(15)

729 = 9³

which shares 435 with the original set.

The other chose: (406 or 630), 987, (125 or 512)

406 = T(28), 630 = T(35)

987 = F(16)

125 = 5³, 512 = 8³

which shares 987 with the original set.

So we get 4 possible sets of triples.

There are only two possible Fibonacci numbers that can be used: 610 and 987.

]]>This Python program runs in 173ms.

**Run:** [ @repl.it ]

from itertools import product from enigma import printf # possible scores scores = ( (10, 0), (6, 2), (5, 5), (2, 6), (0, 10), (0, 0) ) # possible scores in the matches for s in product(scores, repeat=6): # only one match was won outright (i.e. (10, 0) or (0, 10)) if sum((10 in x) for x in s) > 1: continue # calculate the overall totals (AB, AC, AD, BC, BD, CD) = s A = AB[0] + AC[0] + AD[0] B = AB[1] + BC[0] + BD[0] C = AC[1] + BC[1] + CD[0] D = AD[1] + BD[1] + CD[1] if not((A, B, C, D) == (10, 11, 17, 14)): continue printf("AB={AB} AC={AC} AD={AD} BC={BC} BD={BD} CD={CD}")

**Solution:** The results are:

]]>A vs B = (2 – 6) B won on the first innings

A vs C = (2 – 6) C won on the first innings

A vs D = (6 – 2) A won on the first innings

B vs C = (5 – 5) tie

B vs D = (0 – 10) D won

C vs D = (6 – 2) C won on the first innings