33

Well we've done sudoku on keyboards, so the next step is obviously putting a Sudoku on a Mobius strip... of course...

I present, the world's very first MOBIUS SUDOKU:

enter image description here


Now this sudoku is going to be a bit different, because how you solve it, is up to you... You can build it, create an excel diagram, make a 3D model, solve on paper etc. Whatever works for you, feel free to go for it! As long as the answer shows an understandable solution path, then the method of solving is up to you.

Here is a link to a printable PDF version via google drive, assembly instructions included - PRINTABLE VERSION

(I highly recommend printing and building one to help with your solve)


enter image description here

Google Sheets version

(The left most square backs onto the right most square, and the middle squares back onto each other. The ends are then attached as indicated.)

RULES:

  • Normal sudoku rules apply for rows and boxes - boxes are split into groups of 3 indicated by the thicker lines.
  • Columns ‘wrap around’ over the edge of the mobius strip – 6 cells per column as a result, normal sudoku rules apply on these 6 cells.
  • No number will ever be orthogonally adjacent to itself, including across the thicker borders separating the sets of 3 boxes.
  • There are 18 total boxes, and as a result 18 total middle cells. There are 2 of each number, 1-9, in the middle cells.

Please provide a solution path in your answer! How you portray the solution is up to you, feel free to be as creative as you want, just please add some explanation of key points etc. I won’t necessarily accept the first answer, but the answer that best does this :)

Enjoy!!!


This puzzle has been translated into a CodeGolf challenge by @Taco タコス.

Hazel へいぜる
  • 10,581
  • 15
  • 74
Beastly Gerbil
  • 58,036
  • 8
  • 166
  • 314
  • 1
    To clarify, this is solvable without printing, it will just probably be very difficult to wrap your head around without seeing it in front of you :) – Beastly Gerbil Sep 20 '21 at 17:23
  • Does [tag:no-computers] apply here? I realize it can't be added, but this feels like a great challenge for code. – Hazel へいぜる Sep 20 '21 at 17:55
  • 1
    @Tacoタコス hmm for once I’m torn on that one. It would be quite interesting to see how someone would solve this with code, considering a lot of the puzzle is figuring out the mechanics. I’m going to say I’d be happy for someone to solve this with code, but I’d request that they don’t post the solution until after someone’s posted a ‘normal’ answer. Feel free to go for it if you think you could solve that way! – Beastly Gerbil Sep 20 '21 at 18:02
  • 1
    @Tacoタコス if you do somehow manage to code this one out, feel free to post now :) – Beastly Gerbil Sep 21 '21 at 00:17
  • I'll have to work on it tomorrow, but I'll see what I can do! :) It's an interesting one – Hazel へいぜる Sep 21 '21 at 00:37
  • Thanks for the nice puzzle! Do you have the source document so I can more easily translate it? I think my father-in-law would enjoy it, too. – Oliphaunt Sep 21 '21 at 11:17
  • 1
    @Oliphaunt-reinstateMonica I can provide you with a link to a copy of it in google sheets if you want? The printable link had to be a pdf as I created it in word and when copying it into both google docs and word online the formatting completely messed up, but now solved I could def add a google sheets version – Beastly Gerbil Sep 21 '21 at 13:13
  • 1
    Can I make a code golf challenge out of this, with proper attribution of course. I’m sure other developers would also be interested in solving it, and I’m interested in seeing the many solutions available I'm in chat if you prefer to talk there. – Hazel へいぜる Sep 21 '21 at 15:18
  • @BeastlyGerbil that would be great, thanks! – Oliphaunt Sep 21 '21 at 16:22
  • 1
    @Oliphaunt-reinstateMonica added the link in the Q just under the picture :) – Beastly Gerbil Sep 21 '21 at 16:28
  • 1
    Awesome! I'm thinking about making a web version where people can play and create their own versions. Would that be okay? I'm still trying to...wrap my head around the rules but it should be simple to make once I understand it! – Muuski Sep 22 '21 at 03:32
  • 1
    @Muuski sure! Just provide attribution and I’d be happy to see that made :) if you do manage please link it here too I’d love to see that! – Beastly Gerbil Sep 22 '21 at 11:54
  • 1
    Didn't have a printer, so I used pencil and paper. It felt like having it in physical form in front of me made it easier than any other method (hardest part was not accidentally folding it when I wanted to enter a number), but also perhaps more enjoyable. Very clever. :3 – AlbeyAmakiir Sep 25 '21 at 16:30
  • @AlbeyAmakiir glad you enjoyed!! :) – Beastly Gerbil Sep 25 '21 at 17:00
  • Since the columns span both sides, the edges don't really count, and this more of a twisted torus rather than a Möbius strip. – Bass Sep 26 '21 at 07:37
  • @Bass can see what you mean, probably depends how you look at it, it’s a literal Möbius strip but the mechanics change that a bit. Then again this most definitely wouldn’t have been possible without the mechanics the way they are :) – Beastly Gerbil Sep 26 '21 at 07:53

2 Answers2

18

First, build a spreadsheet. This has links surrounding it so that I can see where things will be relative to each other.

Then

we start with 9s (since that's the commonest number in the grid)
step 1
Then 3s and 4s:
step 2
Then more hunting in promising areas (2 missing) and their derivatives:
step 3
Another area came out pretty well:
step 4
Tricksy around the paper step. U6:U7 must contain a 4, so I11 can't be a 4:
step 5
I'm sure you all caught my earlier deliberate error with the 57:
step 6
Then we use the fact that there are already 2 central 7s to disambiguate the central 67:
step 7
Which helps get the whole row out:
step 8
Now 6s are relatively abundant on the grid, so we search and find a 6 that resolves. Near that are a few more answers (using the fact that there were already 2 middle 5s to get the highlighted 8:
step 9
We're able to continue on this, using the fact that there are already 2 middle 7s. This gives us a middle 1, which allows us to resolve another 3:
step 10
More sudoku tricks. Here that the 2s in the right block resolve the highlighted 12:
step 11
Only 4 and 9 can be in the middle (they're the only ones with 1 middle square), so:
step 12
More sudoko-ing:
step 13
And with a few more steps (including the no adjacent sameness over the border) and we're done!:
step 14

Dr Xorile
  • 23,406
  • 3
  • 47
  • 125
  • Very nice puzzle, by the way. Amazing idea. Felt like I was going in loops... – Dr Xorile Sep 20 '21 at 23:09
  • Correct of course! You also used the same method I used while test solving which I thinks probably the easiest way of solving. Did you need to build it out of interest? Tick coming soon! – Beastly Gerbil Sep 21 '21 at 00:16
  • I sat and thought for a few minutes about the easiest way to solve it and decided that a spreadsheet was going to be the easiest way to see all the related components. I don't know if that answers your question? I didn't need to build it in the hunter-gatherer sense of the word. No rush on the tick. I know the deal :-) – Dr Xorile Sep 21 '21 at 02:32
  • Impressed that you managed to put the spreadsheet together without having a strip in front of you! Even building this puzzle using a spreadsheet with about 3 prototypes in front of me I still got it wrong :P Glad you enjoyed! – Beastly Gerbil Sep 21 '21 at 02:43
  • 3
    Oh, sorry. I see what you mean. Yeah, I didn't build it. I let the spreadsheet handle the inverting with some judicious OFFSET, ROW, and COLUMN formulae (I think visible in the link). I also had it do the counting for me, which was especially useful for keeping track of the number of middle cells, which would otherwise make me go squint. – Dr Xorile Sep 21 '21 at 06:07
13

I thought I'd try my hand at writing a program for Mobius sudoku. I used the Z3 SMT solver (Python wrapper) for this. There isn't much of a "solution path" to describe: most of the constraints translate fairly directly, even the wrapping around of the columns. The main difficulty was

constraining the middle cells such that each number 1-9 appeared exactly twice. The z3.AtMost and z3.AtLeast methods don't work on symbolic expressions, so you can't say, e.g., exactly ("at most and at least") 2 of the middle cells should equal 1. I resorted to enforcing terms like sum(middle_cells) == sum(one_through_nine_twice), sum(middle_cells_squared) == sum(one_through_nine_twice_squared), and so on for increasing exponents. I think Z3 had some issues with the values getting too large, as it would return Unsatisfiable if I included terms with powers greater than 11. This worked to get the solution to this instance, and 11 terms is likely enough for how constrained the numbers here are, but I imagine there's a cleverer way to do this.

EDIT - See @FliegendeWurst's better constraint for this in the comments

The other constraint which required some care was:

"No number will ever be orthogonally adjacent to itself, including across the thicker borders separating the sets of 3 boxes."

I.e. if you go left/right/up/down 1 cell from a number, you won't find the same number. The column constraint already takes care of up/down; we really only need to check left/right at the borders between groups of blocks (since the row constraints already take care of this within block groups), but it doesn't hurt to check left/right distinctness everywhere. Checking right-distinctness suffices (left-distinctness would just duplicate the constraints), but we have to make sure to include the connections between the two strips (which are flipped vertically).

Here's the Python code:

 import numpy as np
 from z3 import And, Distinct, Int, Solver

strip1_vals = [ [4, 0, 1, 0, 5, 3, 7, 0, 2, 0, 8, 1, 0, 9, 7, 0, 0, 2, 6, 0, 2, 8, 0, 9, 0, 3, 0], [0, 7, 0, 4, 0, 0, 6, 0, 0, 6, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 9, 0, 2, 0, 6, 0, 4], [9, 0, 0, 0, 2, 0, 0, 1, 8, 2, 0, 3, 0, 0, 6, 0, 1, 4, 0, 7, 0, 0, 0, 3, 0, 0, 2], ] strip2_vals = [ [1, 0, 4, 0, 0, 5, 0, 3, 7, 1, 9, 0, 0, 0, 4, 0, 0, 8, 5, 0, 0, 9, 0, 0, 4, 3, 0], [0, 6, 0, 0, 0, 9, 8, 0, 0, 0, 0, 8, 3, 0, 0, 4, 5, 0, 0, 8, 0, 0, 0, 3, 0, 0, 7], [3, 7, 0, 1, 4, 0, 0, 9, 0, 3, 0, 5, 1, 0, 8, 9, 0, 0, 4, 0, 9, 6, 0, 2, 0, 0, 1], ]

strip1 = np.array( [[Int(f"s0r{row}c{col}") for col in range(27)] for row in range(3)] ) strip2 = np.array( [[Int(f"s1r{row}c{col}") for col in range(27)] for row in range(3)] )

3x3 squares

blocks = [np.split(strip1, 9, axis=1), np.split(strip2, 9, axis=1)] block_groups = [np.split(strip1, 3, axis=1), np.split(strip2, 3, axis=1)] cols = np.concatenate([np.fliplr(strip1), strip2]).T

connect the two strips together, add the start of strip1 at the end

so the constraint wraps around

rows = np.concatenate( (strip1, np.flipud(np.fliplr(strip2)), strip1[:, :1]), axis=1 ).T centers = [block[1, 1] for block in blocks] mid_vals = [range(1, 10), range(1, 10)]

constraints = { "one_through_nine": [ And((1 <= c), (c <= 9)) for c in [strip1.flat, strip2.flat] ], "boxes_distinct": [Distinct(*block.flat) for block in blocks], "rows_distinct": [ Distinct(*row) for block_group in block_groups for row in block_group ], "cols_distinct": [Distinct(*col) for col in cols], "right_distinct" : [ Distinct(left_right) for row in rows for left_right in zip(row[:-1], row[1:]) ], "middle_cells": [ sum(c * i for c in centers) == sum(c ** i for c in mid_vals) for i in range(1, 11) ], "filled_entries": [ [ strip1[i, j] == strip1_vals[i][j] for i in range(len(strip1)) for j in range(len(strip1[0])) if strip1_vals[i][j] != 0 ], [ strip2[i, j] == strip2_vals[i][j] for i in range(len(strip2)) for j in range(len(strip2[0])) if strip2_vals[i][j] != 0 ], ], }

solver = Solver() for cs in constraints.values(): solver.add(*cs) solver.check() assignment = solver.model() strip1_sol = np.array( [ [assignment.evaluate(entry).as_long() for entry in row] for row in strip1 ] ) strip2_sol = np.array( [ [assignment.evaluate(entry).as_long() for entry in row] for row in strip2 ] )

for row in strip1_sol: print("||", end=" ") for i, c in enumerate(row): print(c, end=" ") if i % 9 == 8: print("||", end=" ") elif i % 3 == 2: print("|", end=" ") print()

print() for row in strip2_sol: print("||", end=" ") for i, c in enumerate(row): print(c, end=" ") if i % 9 == 8: print("||", end=" ") elif i % 3 == 2: print("|", end=" ") print()

And the final solution:

Sudoku solution

Nathan
  • 241
  • 1
  • 5
  • 1
    Oh wow someone did manage with code! Good job, didn’t think it would be possible :) – Beastly Gerbil Sep 21 '21 at 13:09
  • 2
    I already had a Sudoku solver written similar to this from some time ago, so it was fun to revisit that with a twist =) thanks for the puzzle! – Nathan Sep 21 '21 at 13:26
  • This is the route I was going to take but with C# +1 – Hazel へいぜる Sep 21 '21 at 14:30
  • @Tacoタコス Ah, I was wondering what your approach would have been. Sorry if I sniped using z3 / an SMT solver from you. Did you have any better solutions for the middle cells? – Nathan Sep 21 '21 at 15:12
  • @Nathan I haven't made it that far yet, but I'm not giving up just have some powerline work nearby so I have to wait to continue my work – Hazel へいぜる Sep 21 '21 at 15:14
  • 1
    My solution for the middle cells was: for num in range(1, 10): s.add(sum(If(cells[y][x] == num, 1, 0) for y in (1, 4) for x in range(1, 26, 3)) == 2) (essentially calculating how often each number appears and constraining that sum to 2) – FliegendeWurst Sep 22 '21 at 08:58
  • @FliegendeWurst that seems much better =) I thought AtMost for symbolic expressions (c == num) would be implemented like this (well, with <=), so when that failed, I didn't try just using If myself. Clearly I need to do some more puzzles now to improve my Z3 – Nathan Sep 22 '21 at 13:52