from enigma import tuples, join, sprintf, SubstitutedExpression, printf # letters (indexed from 1) letters = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ" # M(n, k) = like n mod k, except returns k instead of 0 def M(n, k): r = n % k return (k if r == 0 else r) # rows for diagram A # A1 has the actual values A1 = "PUSSICATO" # A2 uses alphametic symbols A2 = "abcdefghi" # rows for diagram B, with extra 0's added on either end # B1 has the actual values B1 = [0] + list(M(letters.index(x), 5) for x in A1) + [0] # B2 has alphametic symbols B2 = [0] + list(A2) + [0] # symbols for the colours in diagram C colours = "BGRWY" C1 = "WRYRWGBYR" C2 = "WBGRBYYYG" # make an expression for terms t, excluding term i, to give value v def expr(t, i, v): t = list(x for x in t[:i] + t[i + 1:] if x != 0) return sprintf("M({t}, 5) = {v}", t=join(t, sep=' + ')) # collect the expressions exprs = [] for (t1, t2, c1, c2) in zip(tuples(B1, 3), tuples(B2, 3), C1, C2): t = t1 + t2 exprs.extend((expr(t, 1, c1), expr(t, 4, c2))) # make an alphametic puzzle from the expressions p = SubstitutedExpression( exprs, symbols=A2 + colours, distinct=colours, digits=(1, 2, 3, 4, 5), env={ 'M': M } ) # and solve it for s in p.solve(verbose=0): # reconstruct the name from the residues r = join(letters[s[x]] for x in A2) printf("A2 = {r}")

**Solution:** The signature consists of the letters: ABEDABBEC.

So, we might interpret the name as “Abe Dabbec”.

]]>from enigma import Football, printf # we don't care about points, so use the default scoring system football = Football() # labels for the teams (A, B, C, D) = (0, 1, 2, 3) # numbers in the table stand for themselves d = { '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '6': 6, '9': 9 } # determine the match outcomes for (ms, _) in football.substituted_table({ 'played': '?2??', 'w': '?1??', 'l': '0?0?', 'd': '11??' }, d=d): # determine the scores in matches (using teams A, C, D) for ss in football.substituted_table_goals('3?90', '3?64', ms, d=d, teams=[A, C, D]): # ensure not more than 7 goals were scored in any match if not all(s is None or sum(s) < 8 for s in ss.values()): continue # output the matches and scores football.output_matches(ms, ss, teams='ABCD')

**Solution:** The scores in the played matches are: A v C = 3-3, B v C = 3-3, B v D = 1-0, C v D = 3-0. The A v B and A v D matches are not yet played.

I get an execution time of 195ms for the `mzn-gecode -a` solver.

include "globals.mzn"; % current ages (all teenagers) set of int: Ages = { 13, 14, 15, 16, 17, 18, 19 }; var Ages: Mar; var Ages: Apr; var Ages: May; var Ages: Jun; var Ages: Jul; var Ages: Aug; % primes below 120 set of int: Primes = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113 }; % there are five different ages between them constraint nvalue([ Mar, Apr, May, Jun, Jul, Aug ]) = 5; % today only Mar and Apr have prime ages constraint forall (x in { Mar, Apr }) (x in Primes); constraint forall (x in { May, Jun, Jul, Aug }) (not(x in Primes)); % and the sum of the six ages is prime constraint sum([ Mar, Apr, May, Jun, Jul, Aug ]) in Primes; % last year, only May and Jun had prime ages constraint forall (x in { May, Jun }) (x - 1 in Primes); constraint forall (x in { Mar, Apr, Jul, Aug }) (not(x - 1 in Primes)); % and the sum of the six ages was prime constraint sum (x in [ Mar, Apr, May, Jun, Jul, Aug ]) (x - 1) in Primes; % three years ago, only May and Jul had prime ages constraint forall (x in { May, Jul }) (x - 3 in Primes); constraint forall (x in { Mar, Apr, Jun, Aug }) (not(x - 3 in Primes)); % and the sum of the six ages was prime constraint sum (x in [ Mar, Apr, May, Jun, Jul, Aug ]) (x - 3) in Primes; solve satisfy;]]>

The Python 3.6 program below, starts by looking for sequences of ages that sum to a prime number, and would also give a prime sum for the ages a year ago and three years ago. It runs in 98ms.

from itertools import combinations, permutations from enigma import irange, is_prime, uniq, printf # indices for the girls girls = (Mar, Apr, May, Jun, Jul, Aug) = irange(0, 5) # possible ages (they are all teenagers) ages = (13, 14, 15, 16, 17, 18, 19) # generate possible ages def generate(): # choose five of the ages for s in combinations(ages, 5): # and sum them t0 = sum(s) # then add in one of them again (for the twins) for x in s: t = t0 + x # check the sum of the ages is prime, and would be prime last year, and three years ago if not all(is_prime(t + d) for d in (0, -6, -18)): continue # return the ages yield s + (x,) # find girls with prime ages def check(ages, ago=0): return tuple(x for x in girls if is_prime(age[x] - ago)) # choose possible ages for s in generate(): # and assign the ages to the girls for age in uniq(permutations(s)): # in the current year only Mar and Apr are prime if check(ages) != (Mar, Apr): continue # last year only May and Jun were prime if check(ages, 1) != (May, Jun): continue # three years ago only May and Jul were prime if check(ages, 3) != (May, Jul): continue # output solutions printf("Mar={age[Mar]} Apr={age[Apr]} May={age[May]} Jun={age[Jun]} Jul={age[Jul]} Aug={age[Aug]}")

**Solution:** Augusta is 15.

There is only one assignment of ages that satisfies the conditions of the problems:

Marge = 13, April = 13, May = 14, June = 18, Julia = 16, Augusta = 15

In fact, there are only two sequences of ages that will allow the sums to be prime this year, last year and three years ago:

(13, 14, 14, 15, 16, 17), sum = 89

(13, 13, 14, 15, 16, 18), sum = 89

For both sequences the sum last year would be 83, and three years ago it would be 71. But the first of these sequences would give three prime ages three years ago.

]]>We could have had DREAD / OR = SOB, which is what I do every time I see one by Emmet. ]]>

Here is a run file that executes in 95ms.

#!/usr/bin/env python -m enigma -r SubstitutedDivision "txbmt / gx = kpy" "txb - tkp = gt" "gtm - abm = ad" "adt - tpy = tm"

**Solution:** The correct sum is: 17961 ÷ 37 = 485 (remainder 16).

This model executes in 122ms with the `mzn-g12fd` solver.

include "globals.mzn"; % the house numbers var 1..100: H; var 1..100: S; var 1..100: J; % all house numbers are different constraint all_different([H, S, J]); % H's statements var bool: h1; var bool: h2; var bool: h3; constraint h1 = (H mod 7 = 0); constraint h3 = (J = 2 * H); % S's statements var bool: s1; var bool: s2; var bool: s3; constraint s1 = (H = 28); constraint s2 = (J = 3 * S); constraint s3 = not(J mod 2 = 0 /\ S mod 2 = 0); % J's statements var bool: j1; var bool: j2; var bool: j3; constraint j1 = (H = 91); constraint j2 = (S = 81); constraint j3 = (J mod 4 = 0); % assign statement order % 1 = T, T, T % 2 = F, F, F % 3 = T, F, T or F, T, F var 1..3: h; var 1..3: s; var 1..3: j; constraint all_different([h, s, j]); % check s1, s2, s3 correspond to one of the three values predicate check(var int: v, var bool: s1, var bool: s2, var bool: s3) = ((v = 1) -> (s1 /\ s2 /\ s3)) /\ ((v = 2) -> (not(s1) /\ not(s2) /\ not(s3))) /\ ((v = 3) -> ((s1 /\ not(s2) /\ s3) \/ (not(s1) /\ s2 /\ not(s3)))); constraint check(h, h1, h2, h3); constraint check(s, s1, s2, s3); constraint check(j, j1, j2, j3); solve satisfy;

But the `mzn-g12lazy` solver produces multiple solutions, only one of which is correct.

Here is a Python program that executes in 408ms.

from itertools import permutations from enigma import irange, printf # find the third set of statements given two sets of statements and # the first (or last) statement from the third set def third(ss1, ss2, s3): ss = (ss1, ss2) test = ((True, True, True), (False, False, False)) # if TTT or FFF are not already used that that's the result for t in test: if t not in (ss1, ss2): return (t if s3 == t[0] else None) # otherwise the result is alternating return (s3, not(s3), s3) # consider possible house numbers for H and J for (H, J) in permutations(irange(1, 100), 2): # H's statements 1 & 3 # H1: "My number is divisible by 7" H1 = (H % 7 == 0) # H3: "J's number is twice mine" H3 = (J == 2 * H) # they must have the same truth value if H1 != H3: continue # J's statements 1 & 3 # J1: "H lives at 91" J1 = (H == 91) # J3: "J is divisible by 4" J3 = (J % 4 == 0) # they must have the same truth value if J1 != J3: continue # consider possible remaining house numbers for S for S in irange(1, 100): if S == H or S == J: continue # S's statements 1 & 3 # S1: "H lives at 28" S1 = (H == 28) # S3: "J and S are not both even" S3 = not(J % 2 == 0 and S % 2 == 0) # they must have the same truth values if S1 != S3: continue # but they can't all be the same truth value if H1 == J1 == S1: continue # S2: "S is one third of J" S2 = (divmod(J, 3) == (S, 0)) sS = (S1, S2, S3) # J2: "S lives at 91" J2 = (S == 91) sJ = (J1, J2, J3) # S and J's statements can't have the same truth values if sS == sJ: continue # determine the truth values of H's statements sH = third(sS, sJ, H1) printf("H={H} {sH}, S={S} {sS}, J={J} {sJ}")

**Solution:** Hop lives at number 21. Skip lives at number 14. Jump lives at number 42. Skip *is* much too fat.

All Hop’s statements are true.

All Jump’s statements are false.

Skip’s statements are false, true, false.

For the originally published problem (which missed out the “**not**” in Skip’s third statement), there are 4 solutions. These can be seen by removing the `not` from line 42 in the program.

1523 (11); 1237, 1453, 1723, 3019, 3109 (13); 1283, 1409, 3407 (14); 1069, 1249, 1429, 1753, 3049, 3067 (16); 1097, 1259, 1439, 1583, 3257, 3527 (17); 1279, 1657, 3169, 7219 (19); 1487, 1847, 3089, 3467, 3719, 3917 (20); 1597, 1867, 3469 (22); 7349, 7529 (23); 1789, 1879, 3697, 7459 (25); 7649 (26); 7589 (29).

But only two have a difference that is a square; as luck would have it, it’s the same square and they belong to the same digit-sum group.

My Basic program ran very quickly, but as it’s neither a real-time application nor something that has to be carried out again and again, I find execution time quite unimportant.

]]>Neither the Chuffed or Geocode solvers ran in a reasonable time ( < 2 min) and the COIN-OR CBC solver took 19.8sec. I am using a laptop with a 2.80 GHz I7 processor and would have hoped for a faster run-time.

% A Solution in MiniZinc include "globals.mzn"; predicate is_prime(var int: x) = x > 1 /\ forall(i in 2..1 + ceil(sqrt(int2float(ub(x))))) ((i < x) -> (x mod i > 0)); predicate is_sq(var int: y) = let { var 1..ceil(sqrt(int2float(ub(y)))): z} in z*z = y; % First and third number digits var 0..9:a; var 0..9:b; var 0..9:c; var 0..9:d; % Second and fourth number digits var 0..9:e; var 0..9:f; var 0..9:g; var 0..9:h; % num1, num2, num3 and num4 are the four 4-digit card numbers % num3 = num1 (reversed) and num4 = num2 (reversed) var 1000..9999: num1 = 1000*a + 100*b + 10*c + d; var 1000..9999: num2 = 1000*e + 100*f + 10*g + h; var 1000..9999: num3 = 1000*d + 100*c + 10*b + a; var 1000..9999: num4 = 1000*h + 100*g + 10*f + e; var 1000..9999:PIN; % The four 4-digit numbers are in ascending order of size constraint num4 > num3 /\ num3 > num2 /\ num2 > num1; % The sum of the digits of each of the 4-digit numbers is the same constraint a + b + c + d = e + f + g + h; % The first and second numbers are all different digits constraint alldifferent([a, b, c, d]) /\ all_different([e, f, g, h]); % The last 4 digits of all 4-digit numbers are all different digits constraint all_different([d, h, a, e]); % All 4-digit numbers are prime constraint is_prime(num1) /\ is_prime(num2) /\is_prime(num3) /\ is_prime(num4); constraint abs(num1 - num3) == abs(num2 - num4) /\ is_sq(abs(num1 - num3)) /\ PIN = abs(num1 - num3) ; solve satisfy; output[ "Fourth number = " ++ show(num4) ++ ", PIN = " ++ show(PIN) ] ++ [ "\nCredit Card Number is " ++ show(num1) ++ "-" ++ show(num2) ++ "-" ++ show(num3) ++ "-" ++ show(num4)]; % Fourth number = 9103, PIN = 6084 % Credit Card Number is 1237-3019-7321-9103 % ---------- % Finished in 1s 323msec]]>

from collections import defaultdict from itertools import permutations from enigma import Primes, nsplit, nconcat, is_square, printf # 4-digit primes primes = Primes(9999) # record results by their sum ps = defaultdict(list) # find 4-digit primes ABCD, where all digits are different, # DCBA is also a prime and A < D for p in primes.range(1000, 9999): (A, B, C, D) = nsplit(p) if not(A < D and len(set((A, B, C, D))) == 4): continue r = nconcat(D, C, B, A) if r not in primes: continue # record the digit sum, and difference between the numbers ps[(A + B + C + D, r - p)].append((p, r)) # now look for pairs with the same sum and difference for ((s, pin), vs) in ps.items(): if len(vs) < 2: continue printf("[pin={pin} (is_square={r}) vs={vs}]", r=is_square(pin)) # choose two pairs (a, c), (b, d) for ((a, c), (b, d)) in permutations(vs, 2): # the numbers appear in order if not(a < b < c < d): continue # the final digits are all different if len(set(x % 10 for x in (a, b, c, d))) != 4: continue printf("{a} {b} {c} {d}")

**Solution:** The fourth number is 9103.

The full credit card number is 1237 3019 7321 9103. The PIN is 6084 (which is 78²).

The solution is an anagram of the puzzle number.

]]>But we can get viable solutions for a set with 7, 9, 10, 11 or 12 red balls. So perhaps the number of red balls has to remain a triangular number to permit them to be set up in a triangular formation at the start of play. (Note that the title of this puzzle is “Snooker triangle”). So that means only solutions with 1, 3, 6 or 10 red balls are allowed. So with this restriction we do get a unique solution.

This Python program runs in 64ms.

from collections import defaultdict from enigma import irange, is_triangular, printf # the scores for the colours colours = (2, 3, 4, 5, 6, 7) # and their sum (clearing the table) t = sum(colours) # consider the number of reds in the reduced set for n in irange(1, 15): # but only consider numbers that can be formed into a triangle if not is_triangular(n): continue # record <score> -> (<my number of reds>, <my colour score>, <my number of colours>, <daughters colour score>) ss = defaultdict(list) # choose a colour for me to pot for c1 in colours: # consider possible numbers of reds for n1 in irange(1, n - 1): # I either pot n1 colours or (n1 - 1) colours for n2 in (n1, n1 - 1): s = n1 + n2 * c1 # before clearing the table the daughters score would be... d = s - t if not(d > 0): continue # so the colour she would have to pot would be... (c2, r) = divmod(d, n - n1) if not(r == 0): continue c2 -= 1 if not(c2 != c1 and c2 in colours): continue ss[s].append((n1, c1, n2, c2)) # find scores that differ by 1 point for (k1, vs1) in ss.items(): k2 = k1 + 1 if k2 in ss: vs2 = ss[k2] printf("n={n}: {k1} -> {vs1}, {k2} -> {vs2}") printf("{n} red balls, game 1 = {k1} points, game 2 = {k2} points") for (n1, c1, n2, c2) in vs1: printf(" game 1: player 1 potted {n1} reds and {n2} colour {c1}, player 2 potted {m} reds and {m} colour {c2} then cleared", m=n - n1) for (n1, c1, n2, c2) in vs2: printf(" game 2: player 1 potted {n1} reds and {n2} colour {c1}, player 2 potted {m} reds and {m} colour {c2} then cleared", m=n - n1) printf()

**Solution:** (1) Player 1 potted the blue 14 times in the two games. (2) There are 17 balls (including the white) on the small table.

There are 10 red balls (which can be formed into a triangle), the 6 coloured balls, and 1 white ball, making 17 balls in total.

]]>

Game 1:Each player scores 42 points.

Player 1:Pots 7 red balls, each followed by a blue (5) for 7×1 + 7×5 = 42 points.

Player 2:Pots the remaining 3 red balls, each followed by a brown (4) for 3×1 + 3×4 + (2+3+4+5+6+7) = 42 points.

Game 2:Each player scores 43 points.

Player 1:Pots 8 red balls, each followed by a blue (5) except for the last red, for 8×1 + 7×5 = 43 points.

Player 2:Pots the remaining 2 red balls, each followed by a black (7) for 2×1 + 2×7 + (2+3+4+5+6+7) = 43 points.

#!/usr/bin/env python -m enigma -r SubstitutedSum # no leading zeros, no number stands for itself --invalid="0,049" --invalid="1,1" --invalid="2,2" --invalid="3,3" --invalid="4,4" --invalid="5,5" --invalid="6,6" --invalid="7,7" --invalid="8,8" --invalid="9,9" "4751 + 9731 = 46082"

**Solution:** The correct sum is 1637 + 8657 = 10294.