Skip to content

Instantly share code, notes, and snippets.

@justvanrossum
Last active July 18, 2022 20:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justvanrossum/b2c5cf79fad2a20fec86d146fd17bc93 to your computer and use it in GitHub Desktop.
Save justvanrossum/b2c5cf79fad2a20fec86d146fd17bc93 to your computer and use it in GitHub Desktop.
Visualization and solution to "seven nuts" puzzle. Requires DrawBot for the visualization.
# https://twitter.com/jenskutilek/status/1548996988892479488
def drawNuts(nuts, matches, nutPositions):
for (bi1, bs1), (bi2, bs2) in matches:
with savedState():
strokeWidth(0.3 * distX)
if nuts[bi1][bs1] == nuts[bi2][bs2]:
stroke(0, 1, 0)
else:
stroke(1, 0, 0)
line(nutPositions[bi1], nutPositions[bi2])
for i, (x, y) in enumerate(nutPositions):
drawNut(x, y, radius * 0.95, nuts[i])
def drawNut(cx, cy, r, numbers):
with savedState():
translate(cx, cy)
points = [(r * cos(tau * i / 6), r * sin(tau * i / 6)) for i in range(6)]
fill(0)
polygon(*points)
fill(1)
drawCircle(0, 0, 0.35 * r)
font("HelveticaNeue-CondensedBold") # I don't have Franklin Gothic Condensed
fontSize(0.35 * r)
for i in range(6):
a = tau * i / 6 - pi / 2
text(str(numbers[i]), (0, -r / 1.3), align="center")
rotate(60)
def drawCircle(cx, cy, r):
d = r * 2
oval(cx - r, cy - r, d, d)
def rotateNut(nut, i):
i = i % 6
if i:
nut = nut[i:] + nut[:i]
return nut
def isSolution(nuts, matches):
for (bi1, bs1), (bi2, bs2) in matches:
try:
if nuts[bi1][bs1] != nuts[bi2][bs2]:
return False
except IndexError:
# Partial check, ignore this combination
pass
return True
def solve(base, remaining, matches):
if not remaining:
yield base
return
for i in range(len(remaining)):
first = remaining[i]
rest = remaining[:i] + remaining[i + 1 :]
for j in range(6):
sol = base + [rotateNut(first, j)]
if isSolution(sol, matches):
yield from solve(sol, rest, matches)
# Nut configuration: start from center, then from bottom, go counter clockwise
#
# nuts[4]
# nuts[5] nuts[3]
# nuts[0]
# nuts[6] nuts[2]
# nuts[1]
#
# Starting configuration as per photo
nuts = [
[3, 4, 1, 2, 5, 6], # counter clockwise from bottom
[1, 5, 3, 2, 6, 4],
[1, 2, 3, 4, 5, 6],
[4, 2, 3, 5, 6, 1],
[6, 1, 3, 5, 4, 2],
[2, 4, 6, 1, 3, 5],
[1, 6, 5, 4, 3, 2],
]
assert all(sorted(b) == [1, 2, 3, 4, 5, 6] for b in nuts)
assert all(len(set(b)) == 6 for b in nuts)
assert len({tuple(b) for b in nuts}) == 7
# Nut side indices 0..5 from BOTTOM side, counter-clockwise
matches = [
# (nut index 1, nut side 1), (nut index 2, nut side 2)
((0, 0), (1, 3)),
((0, 1), (2, 4)),
((0, 2), (3, 5)),
((0, 3), (4, 0)),
((0, 4), (5, 1)),
((0, 5), (6, 2)),
((1, 2), (2, 5)),
((2, 3), (3, 0)),
((3, 4), (4, 1)),
((4, 5), (5, 2)),
((5, 0), (6, 3)),
((6, 1), (1, 4)),
]
assert len(matches) == 12
distY = 300
distX = distY * cos(pi / 6)
radius = distY / (2 * cos(pi / 6))
nutPositions = [
(0, 0),
(0, -distY),
(distX, -distY / 2),
(distX, distY / 2),
(0, distY),
(-distX, distY / 2),
(-distX, -distY / 2),
]
solutions = list(solve([], nuts, matches))
print(f"there are {len(solutions)} solutions")
solution = solutions[0]
fill(1)
rect(0, 0, width(), height())
translate(500, 500)
drawNuts(solution, matches, nutPositions)
saveImage("SevenNutsPuzzle.png")
@justvanrossum
Copy link
Author

JensBoltPuzzle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment