
Binary Puzzle - mabynogy
http://binarypuzzle.com/
======
grumph
It's "Unruly" in Simon Tatham's puzzle collection[1]. There is also an android
version [2].

In the manual: "This puzzle type was invented by Adolfo Zanellati, under the
name ‘Tohu wa Vohu’."

[1]
[https://www.chiark.greenend.org.uk/~sgtatham/puzzles/](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/)

[2] [https://chris.boyle.name/projects/android-
puzzles/](https://chris.boyle.name/projects/android-puzzles/) or
[https://f-droid.org/repository/browse/?fdfilter=puzzles&fdid...](https://f-droid.org/repository/browse/?fdfilter=puzzles&fdid=name.boyle.chris.sgtpuzzles)

~~~
schoen
Nice! For people who don't get the reference in the name Zanellati gave,

[https://en.wikipedia.org/wiki/Tohu_wa-
bohu](https://en.wikipedia.org/wiki/Tohu_wa-bohu)

Edit: I didn't realize that, as Wikipedia says, this Hebrew phrase is used as
a loanword in several European languages to refer to disorder, which in turn
explains why Tatham called it "Unruly".

------
GrantSolar
I feel that the rules should contain some examples. I spent a while battling a
puzzle to realise that I had been interpretting "No more than two similar
numbers next to or below each other are allowed." as "A box must have no more
than 2 neighbours of each number" i.e. Exactly 2 0s and exactly 2 1s in the
non-diagonal neighbours.

I think that what's being referred to is runs of numbers, so if you see 0,0,_
you know the last one must be a 1

~~~
panic
[http://0hh1.com](http://0hh1.com) is a much better place to learn the rules.

~~~
BatFastard
Indeed it is, it explains the rule, gives you feedback, and it better looking
in general.

~~~
HiroshiSan
I find it's easier than the puzzle linked since my brain can't unsee '1' and
'0' as one and zero, whereas with the blocks, they're just abstract shapes
with no meaning aside from colour.

------
tome
I wonder how one creates puzzles like this (or indeed creates Sudoku puzzles).
I can think of a few strategies

1\. Lovingly hand craft them. Extremely labour intensive!

2\. Generate random grids and repeatedly remove elements. If that leads to a
puzzle with more than one solution, backtrack.

3\. Add random elements to an empty grid. Eventually the grid will have one or
zero solutions. If zero, backtrack. If one, stop.

Finally, classify the difficulty using some heuristic.

Does anyone have a better strategy? Is there some "analytic" property that
guarantees uniqueness of solutions without resorting to some black box solver?

~~~
fenomas
> Is there some "analytic" property that guarantees uniqueness of solutions
> without resorting to some black box solver?

Check out Knuth's Algorithm X. It's an elegant way of analyzing constraint-
satisfaction sorts of problems (of which both Sudoku and the linked puzzle are
examples).

[https://en.wikipedia.org/wiki/Knuth%27s_Algorithm_X](https://en.wikipedia.org/wiki/Knuth%27s_Algorithm_X)

edit: at least I _think_ the linked puzzle is a constraint problem that could
be modeled this way. But now that I consider it it's not so obvious...

~~~
kybernetikos
Algorithm X can 'solve' puzzles with multiple solutions, so it doesn't
guarantee a unique solution.

Here's my implementation: [http://dancing-
links.herokuapp.com/](http://dancing-links.herokuapp.com/) It's happy enough
solving an empty sudoku grid (you can try it near the bottom of that page).

And yes, I think I could model this game as an exact cover problem (and
therefore suitable for algorithm X) - you have a choice for each square to put
a 1 in it or a 0 in it, you have the constraints that the rules tell you that
you have. (e.g. col 1 must not be equal to col 2, and so on for all possible
column pairings, and the same for rows, and col 1 has the same number of 1s as
0s for all columns, and col 1 does not contain more than 2 1s next to each
other, etc...) It shouldn't be all that hard to modify my code above to solve
it.

~~~
fenomas
> Algorithm X can 'solve' puzzles with multiple solutions, so it doesn't
> guarantee a unique solution.

It can be used in a straightforward way to count how many solutions are
possible for a given layout.

So for generating games, you could add numbers to an empty board until there
are less than n solutions, then pick one of the solutions and add numbers from
it until the solution is unique, then add a few more until it's overspecified
to be a bit easier, etc.

------
tedmiston
An example of the easy 6x6 puzzle instead of starting with a very hard 10x10:

[http://binarypuzzle.com/puzzles.php?size=6](http://binarypuzzle.com/puzzles.php?size=6)

------
eridius
If you like puzzles like this, you should check out Nikoli
([http://www.nikoli.com/en/](http://www.nikoli.com/en/)). They're a Japanese
gaming company with a bunch of puzzles online. They don't have this particular
puzzle, but they have similar ones, like Hitori
([http://www.nikoli.com/en/puzzles/hitori/](http://www.nikoli.com/en/puzzles/hitori/)).

------
zmonx
The declarative programming language Prolog is a natural choice for solving
such combinatorial tasks.

Here is a Prolog formulation of the puzzle, using constraint logic programming
over _integers_ that ships with typical Prolog systems:

    
    
        binary_puzzle(Rows) :-
                length(Rows, L),
                maplist(same_length(Rows), Rows),
                maplist(only_two_next_to_each_other, Rows),
                transpose(Rows, Cols),
                maplist(only_two_next_to_each_other, Cols),
                maplist(booleans_integer, Rows, RIs),
                all_different(RIs),
                maplist(booleans_integer, Cols, CIs),
                all_different(CIs),
                Half #= L // 2,
                maplist(equally_distributed(Half), Rows),
                maplist(equally_distributed(Half), Cols).
    
        equally_distributed(L, Bs) :-
                global_cardinality(Bs, [0-L,1-L]).
    
        booleans_integer(Bs, I) :-
                foldl(pow, Bs, 0-0, I-_).
    
        pow(B, N0-I0, N-I) :-
                B in 0..1,
                N #= N0 + B*2^I0,
                I #= I0 + 1.
    
        only_two_next_to_each_other([]).
        only_two_next_to_each_other([_,_]).
        only_two_next_to_each_other([A,B,C|Rest]) :-
                ( A #= B ) #==> ( C #\= B ),
                ( B #= C ) #==> ( B #\= A ),
                only_two_next_to_each_other([B,C|Rest]).
    

For example, let us consider the concrete 10x10 puzzle of today:

    
    
        puzzle([[_,_,_,_,_,1,_,_,_,1],
                [1,_,_,_,_,_,_,0,_,_],
                [_,_,0,_,_,_,_,0,_,_],
                [_,0,0,_,_,_,0,_,_,1],
                [1,_,_,_,_,_,_,_,_,1],
                [_,_,_,0,_,_,1,_,_,_],
                [0,_,_,_,_,1,_,_,_,_],
                [_,_,_,_,_,_,_,0,_,0],
                [0,_,_,_,_,_,_,_,_,0],
                [_,0,_,0,_,1,_,_,_,_]]).
    

You can solve it with the above formulation, using for example SICStus Prolog
and its CLP(FD) library, with the following query:

    
    
        ?- puzzle(Rows),
           binary_puzzle(Rows),
           maplist(label, Rows),
           maplist(portray_clause, Rows).
    

The unique solution is:

    
    
        [0, 1, 0, 1, 0, 1, 0, 1, 0, 1].
        [1, 0, 1, 0, 0, 1, 1, 0, 1, 0].
        [0, 1, 0, 1, 1, 0, 1, 0, 1, 0].
        [1, 0, 0, 1, 1, 0, 0, 1, 0, 1].
        [1, 0, 1, 0, 0, 1, 0, 0, 1, 1].
        [0, 1, 1, 0, 1, 0, 1, 1, 0, 0].
        [0, 1, 0, 1, 0, 1, 0, 0, 1, 1].
        [1, 0, 1, 0, 1, 0, 1, 0, 1, 0].
        [0, 1, 0, 1, 1, 0, 1, 1, 0, 0].
        [1, 0, 1, 0, 0, 1, 0, 1, 0, 1].
    

It is found within a second on current machines.

The above formulation is also quite general. For example, you can use it to
_complete_ , _test_ and _generate_ solutions:

    
    
        ?- binary_puzzle(Rows),
           maplist(label, Rows),
           maplist(portray_clause, Rows).
    

This _generates_ valid solutions of the puzzle for all board sizes:

    
    
        Rows = [] ;
        [0, 1].
        [1, 0].
        Rows = [[0, 1], [1, 0]] ;
        [1, 0].
        [0, 1].
        Rows = [[1, 0], [0, 1]] ;
        [0, 0, 1, 1].
        [0, 1, 0, 1].
        [1, 0, 1, 0].
        [1, 1, 0, 0].
        Rows = [[0, 0, 1, 1], [0, 1, 0, 1], [1, 0, 1, 0], [1, 1, 0, 0]] ;
        [0, 0, 1, 1].
        [0, 1, 0, 1].
        [1, 1, 0, 0].
        [1, 0, 1, 0].
        Rows = [[0, 0, 1, 1], [0, 1, 0, 1], [1, 1, 0, 0], [1, 0, 1, 0]] ;
        etc.
    
    

Thank you for sharing!

~~~
ZephyrP
I like your Prolog solution! I thought I'd take a crack at a Python SAT-solver
solution, also done in less than 1 second.

    
    
      from z3 import *
      
      # we use '-1' for empty
      instance  = ((-1,-1,-1,-1,-1,1,-1,-1,-1,1),
                 (1,-1,-1,-1,-1,-1,-1,0,-1,-1),
                 (-1,-1,0,-1,-1,-1,-1,0,-1,-1),
                 (-1,0,0,-1,-1,-1,0,-1,-1,1),
                 (1,-1,-1,-1,-1,-1,-1,-1,-1,1),
                 (-1,-1,-1,0,-1,-1,1,-1,-1,-1),
                 (0,-1,-1,-1,-1,1,-1,-1,-1,-1),
                 (-1,-1,-1,-1,-1,-1,-1,0,-1,0),
                 (0,-1,-1,-1,-1,-1,-1,-1,-1,0),
                 (-1,0,-1,0,-1,1,-1,-1,-1,-1))
    
      
      size = len(instance)
      
      # we could use bitvecs here too
      X = [ [ Int("x_%s_%s" % (i+1, j+1)) for j in range(size) ]
            for i in range(size) ]
      
      # each cell is a 1 or a 0
      cells_c  = [ And(0 <= X[i][j], X[i][j] <= 1) for i in range(size) for j in range(size) ]
      
      # each row contains as many 1s as 0s
      rows_c   = [ Sum(X[i]) == size / 2 for i in range(size) ]
      
      # each column contains as many 1s as 0s
      cols_c   = [ (Sum([ X[i][j] for i in range(size) ]) == size / 2)
                   for j in range(size) ]
      
      # each cell can only have 2 neighbors sharing it's number.
      rows_not_alike = [ If(X[i][j] == X[i][j-1], X[i][j+1] != X[i][j-1], True) for i in range(size)
                         for j in range(size-1) ]
      
      cols_not_alike = [ If(X[i][j] == X[i-1][j], X[i-1][j] != X[i+1][j], True) for i in range(size-1)
                         for j in range(size) ]
      
      puzzle_c = cells_c + rows_c + cols_c + rows_not_alike + cols_not_alike
      
      instance_c = [ If(instance[i][j] == -1, True, X[i][j] == instance[i][j]) for i in range(size) for j in range(size) ]
      
      s = Solver()
      s.add(puzzle_c + instance_c)
      print s.to_smt2()
      if s.check() == sat:
          m = s.model()
          r = [ [ m.evaluate(X[i][j]) for j in range(size) ] for i in range(size) ]
          print_matrix(r)
      else:
          print "failed to solve"
    
    

Generates:

    
    
      [[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
       [1, 0, 1, 0, 1, 0, 1, 0, 1, 0],
       [0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
       [1, 0, 0, 1, 0, 1, 0, 1, 0, 1],
       [1, 0, 1, 0, 1, 0, 0, 1, 0, 1],
       [0, 1, 1, 0, 1, 0, 1, 0, 1, 0],
       [0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
       [1, 0, 1, 0, 0, 1, 1, 0, 1, 0],
       [0, 1, 0, 1, 1, 0, 0, 1, 1, 0],
       [1, 0, 1, 0, 0, 1, 1, 0, 0, 1]]

~~~
KGIII
This may be frowned upon, but the above post and the parent post are why I
visit, even if I don't comment much.

------
ralfd
I am stuck. What is the next step here? (Aside from programming it in Prolog.)

[https://i.imgur.com/ezUHHBp.png](https://i.imgur.com/ezUHHBp.png)

~~~
Jtsummers
Last year my desk calendar had a bunch of these sorts of puzzles, so some
things I learned:

Any time you see "0..1" you _have_ to have a 0 and 1 between them. This is
useful since each row/column must have the same number of 0s and 1s (5 each in
your puzzle). So counting from (1,1) as the top left, what does (6,3) have to
be? You may not know the position of all the 0s and 1s in row 6, but you have
most of them accounted for because of this specific relationship, and one free
space. Figure it out and you get at least 5 positions resolved.

Keeping in mind that the number of 0s and 1s have to be the same, find rows
where the number of 0s or 1s is near the max but the other is far from it.
Your row 3 is one such location. There has to be one more 0 and three more 1s.
Since the 1s can't all be together, you get some constraints on the outside of
that four space gap "010...010". If you put the 0 on the outside of the gap,
the 1s must be all in a row which is invalid.

Your column 2 offers a similar situation. 4x0s and 2x1s. (from the top)
".01001...0". Where can the one remaining 0 go, or where _can 't_ it go?

Repeating this I can spot about 15-20 spaces that'll be filled in very
quickly, and the rest probably falls into place after that.

------
scscsc
I have implemented a while ago an Android version
([https://play.google.com/store/apps/details?hl=en-
gb&id=com.o...](https://play.google.com/store/apps/details?hl=en-
gb&id=com.ovaludi.nothree.android)).

By far, the most interesting part is automatically generating the levels by
difficulty. In my implementation, I first generate a full puzzle and then
start removing pieces one by one, as long as the part just removed can be
logically deduced. The only difference between easy/medium/hard is the amount
of work performed in the logical deduction: one step for easy, two steps for
medium and three for hard.

------
alangpierce
If anyone else was confused by the rule "No more than two similar numbers
below or next to each other are allowed", I think it means that you aren't
allowed to have three in a row of the same number, horizontally or vertically.

~~~
bsurmanski
Why? Is this a property to guarantee there are no duplicate binary numbers, or
an arbitrary rule to make the puzzle tractable?

------
Aardwolf
It's actually a little bit like doing deductions in minesweeper... without the
random probabilities and timer

------
EvanKelly
Oh Hi
([https://play.google.com/store/apps/details?id=com.presto.ohh...](https://play.google.com/store/apps/details?id=com.presto.ohhi&hl=en_GB))
is an app I have that has these puzzles.

Simple rules and can get surprisingly difficult.

~~~
eldavojohn
What's the difference between your app (that has 100-500 downloads) and this
app that has 100,000-500,000 downloads?

[https://play.google.com/store/apps/details?id=com.q42.ohhi&h...](https://play.google.com/store/apps/details?id=com.q42.ohhi&hl=en_GB)

~~~
EvanKelly
Ha, oops, my wording was bad...I meant I had it on my phone...not my app and I
just searched the app store. I'll change my link.

EDIT: No way to edit old posts, but the link I posted is obviously a rip-off
of the Q42 app linked elsewhere.

------
sushimako
Very cool! It keeps saying that it's not solved correctly, but i cannot find
the mistake: [http://i.imgur.com/qPJLafH.png](http://i.imgur.com/qPJLafH.png)

Already ironed out identical columns but can't seem to find anything else. Any
idea?

~~~
cerebrum
1\. and 6. row identical.

~~~
sushimako
1 & 7, thanks!

------
sarah2079
Nooooooo! I clicked "The rules in more detail..." and it cleared my half-
finished puzzle.

~~~
pavel_lishin
Yeah, I really wish it remembered your current puzzle state.

------
rgerganov
This is copy paste of [http://0hh1.com](http://0hh1.com)

~~~
mhei
Or possibly the other way round? 0hh1.com was registered in 2014 according to
whois info, the puzzles on this site go back to 2011. (I'm not claiming this
is proof, of course)

~~~
kmm
They're older than that, this kind of puzzle is called a Takuzu or Binairo and
was invented in 2009 by a pair of Belgians:
[https://fr.wikipedia.org/wiki/Takuzu](https://fr.wikipedia.org/wiki/Takuzu)

