Teaser 2579: Box clever
From The Sunday Times, 26th February 2012 [link] [link]
I have placed the digits 1 to 9 in a three-by-three grid made up of nine boxes. For each two-by-two array within the grid, the sum of its four entries is the same. Furthermore, if you reverse the order of the digits in that common total, then you get the sum of the four corner entries from the original grid.
Which digits are adjacent to the 6? (i.e. digits whose boxes have a common side with 6’s box?)
[teaser2579]
Jim Randell 5:25 pm on 9 July 2024 Permalink |
Here is a run-file that uses the [[
SubstitutedExpression]] solver from the enigma.py library to find possible grid layouts.It runs in 94ms. (Internal runtime of the generated program is 8.6ms).
Run: [ @replit ]
Line 22 ensures only one version of symmetrically equivalent layouts (rotations and reflections) is produced.
For a complete solution to the puzzle we can further process the results to find the required digits for the answer.
Run: [ @replit ]
from enigma import (SubstitutedExpression, grid_adjacency, chunk, printf) adj = grid_adjacency(3, 3) p = SubstitutedExpression.from_file("teaser2579.run") for s in p.solve(verbose=''): grid = list(s[x] for x in 'ABCDEFGHI') ans = sorted(grid[i] for i in adj[grid.index(6)]) printf("{grid} -> {ans}", grid=list(chunk(grid, 3)))Solution: The digits adjacent to 6 are: 3, 4, 5.
Here is a viable layout:
The 2×2 grids give:
And the corners:
There are 8 potential layouts, but they can all be rotated/reflected to give the one above.
LikeLike
Ruud 12:40 pm on 10 July 2024 Permalink |
""" label boxes as 012 345 678 """ import itertools def sum4(*indexes): return sum(digits[index] for index in indexes) adjacent = {0: (1, 3), 1: (0, 2, 4), 2: (1, 5), 3: (0, 4, 6), 4: (1, 3, 5, 7), 5: (2, 4, 8), 6: {3, 7}, 7: (6, 4, 8), 8: (5, 7)} for digits in itertools.permutations(range(1, 10)): if (s := sum4(0, 1, 3, 4)) == sum4(1, 2, 4, 5) == sum4(3, 4, 6, 7) == sum4(4, 5, 7, 8) and int(str(s)[::-1]) == sum4(0, 2, 6, 8): print(digits[0], digits[1], digits[2], "\n", digits[3], digits[4], digits[5], "\n", digits[6], digits[7], digits[8], sep="") print(" ", *sorted(digits[index] for index in adjacent[digits.index(6)]))LikeLike
Frits 9:19 pm on 10 July 2024 Permalink |
@Jim, “verbose” only seems to work in the run member.
LikeLike
Jim Randell 7:17 am on 11 July 2024 Permalink |
@Frits: Care to elaborate?
LikeLike
Frits 8:09 am on 11 July 2024 Permalink |
@Jim,
“for s in p.solve(verbose=256):” or “for s in p.solve(verbose=’3′):” is not working in my environment. ‘–verbose=”3″‘ works well in the run member.
LikeLike
Jim Randell 3:06 pm on 11 July 2024 Permalink |
@Frits:
You can add an appropriate [[
--verbose]] directive to the run file, or pass it as an argument to the [[from_file()]] call, so that it is active when the code is being generated.p = SubstitutedExpression.from_file("teaser/teaser2579.run", ["--verbose=C"])But I’ve changed the [[
solve()]] function so that theverboseparameter takes effect before anything else, which means if the code has not already been generated (which will usually be the case the first timesolve()is called), then it will be displayed before it is compiled.LikeLike
GeoffR 7:31 pm on 9 July 2024 Permalink |
LikeLike
GeoffR 3:34 pm on 10 July 2024 Permalink |
from enigma import Timer timer = Timer('ST2579', timer='E') timer.start() from itertools import permutations # a b c # d e f # g h i for a, b, c, d, e, f, g, h, i in permutations (range(1, 10)): # check four square totals tot = a + b + d + e if b + c + e + f == tot: if d + e + g + h == tot: if e + f + h + i == tot: # check corner digits sum for a reverse total x, y = tot // 10, tot % 10 rev_tot = 10 * y + x if a + c + g + i == rev_tot: if a < c and a < g and c < g: # digits whose boxes have a common side with 6’s box if a == 6:print(f"Adjacent digits = {b} and {d}.") if b == 6:print(f"Adjacent digits = {a},{e} and {c}.") if c == 6:print(f"Adjacent digits = {b} and {f}.") if d == 6:print(f"Adjacent digits = {a},{e} and {g}.") if e == 6:print(f"Adjacent digits = {b},{d},{f} and {h}.") if f == 6:print(f"Adjacent digits = {c},{e} and {i}.") if g == 6:print(f"Adjacent digits = {d} and {h}.") if h == 6:print(f"Adjacent digits = {g},{e} and {i}.") if i == 6:print(f"Adjacent digits = {h} and {f}.") # Adjacent digits = 4,3 and 5. timer.stop() timer.report() # [ST2579] total time: 0.0290888s (29.09ms)LikeLike
GeoffR 11:51 am on 11 July 2024 Permalink |
I recoded this teaser in a 3-stage permutation for 4, 2 and 3 digits – my previous posting was a brute-force 9-digit permutation. It was much faster, albeit that this 2nd posting was on an I9 CPU as opposed to an I7 CPU for the original posting
from enigma import Timer timer = Timer('ST2579', timer='E') timer.start() from itertools import permutations # a b c # d e f # g h i digits = set(range(1, 10)) for p1 in permutations (digits, 4): a, b, d, e = p1 # check four square totals tot = a + b + d + e q1 = digits.difference( p1) for p2 in permutations(q1, 2): c, f = p2 if b + c + e + f == tot: q3 = q1.difference(p2) for p3 in permutations(q3): g, h, i = p3 if d + e + g + h == tot: if e + f + h + i == tot: # check corner digits sum for a reverse total x, y = tot // 10, tot % 10 rev_tot = 10 * y + x if a + c + g + i == rev_tot: if a < c and a < g and c < g: # digits whose boxes have a common side with 6’s box if a == 6:print(f"Adjacent digits = {b} and {d}.") if b == 6:print(f"Adjacent digits = {a},{e} and {c}.") if c == 6:print(f"Adjacent digits = {b} and {f}.") if d == 6:print(f"Adjacent digits = {a},{e} and {g}.") if e == 6:print(f"Adjacent digits = {b},{d},{f} and {h}.") if f == 6:print(f"Adjacent digits = {c},{e} and {i}.") if g == 6:print(f"Adjacent digits = {d} and {h}.") if h == 6:print(f"Adjacent digits = {g},{e} and {i}.") if i == 6:print(f"Adjacent digits = {h} and {f}.") # Adjacent digits = 4,3 and 5. timer.stop() timer.report() # [ST2579] total time: 0.0055232s (5.52ms)LikeLike
Frits 9:08 pm on 10 July 2024 Permalink |
Looping over 4 variables.
from itertools import permutations row, col = lambda i: i // 3, lambda i: i % 3 # adjacent fields to field <i> adj = {i: tuple(j for j in range(9) if sum(abs(tp(j) - tp(i)) for tp in (row, col)) == 1) for i in range(9)} # A B C # D E F # G H I ''' # each box of 4 digits sums to XY "A + B + D + E = XY" "B + C + E + F = XY" "D + E + G + H = XY" "E + F + H + I = XY" # the four corners sum to YX "A + C + G + I = YX" ''' sols, set9 = set(), set(range(1, 10)) # Y is 1 or 2 as YX < 30 and X = 2 (H formula) so XY = 21 XY, YX = 21, 12 # get first three numbers for B, D, E in permutations(range(1, 10), 3): # remaining numbers after permutation r = sorted(set9 - {B, D, E}) A = XY - (B + D + E) if A in {B, D, E} or not (0 < A <= 12 - sum(r[:2])): continue for F in set(r).difference([A]): C = XY - B - E - F H = (4 * XY - YX) // 2 - (B + D + 2 * E + F) G = XY - D - E - H I = YX - A - C - G if E + F + H + I != XY: continue vars = [A, B, C, D, E, F, G, H, I] if set(vars) != set9: continue # store digits adjacent to the 6 sols.add(tuple(sorted(vars[j] for j in adj[vars.index(6)]))) print("answer:", ' or '.join(str(s) for s in sols))LikeLike