Last active
July 18, 2022 20:47
-
-
Save justvanrossum/b2c5cf79fad2a20fec86d146fd17bc93 to your computer and use it in GitHub Desktop.
Visualization and solution to "seven nuts" puzzle. Requires DrawBot for the visualization.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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") |
Author
justvanrossum
commented
Jul 18, 2022
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment