Binary Puzzle Solver

Brachylog, 34 bytes

{ℕ<2}ᵐ²&≜{d?ọᵐctᵐ=&{ḅlᵐ⌉<3}ᵐ}&\↰₂&

Try it online!

This is pretty damn slow, so the test case on TIO is 4x4. I am currently running the 6x6 test case on my computer to see how much time it takes.

This takes a list of lists as input. The unknown values should be indicated with variables, that is with all-uppercase strings (and they should all be different, as otherwise you would be indicating that some cells must have the same value)

Explanation

We constrain the values to be in {0,1}, then we try instantiations of the variables until one respects all 3 rules. This is why this is so slow (because it will try all of them until finding one; and because in that case Brachylog is not implemented well enough so that constraints can be imposed before trying a possible matrix).

                                 &  Output = Input
{   }ᵐ²                             Map two levels on the Input (i.e. each cell):
 ℕ<2                                  The cell is either 0 or 1
       &≜                           Assign values to all cells
         {                  }       Define predicate 2:
          d?                          The Input with no duplicates is still the Input
                                        (i.e. all rows are different)
           ?ọᵐctᵐ=                    All occurences of 1s and 0s for each rows are equal
                  &{      }ᵐ          Map on rows:
                    ḅlᵐ                 Get the lengths of runs of equal values
                       ⌉<3              The largest one is strictly less than 3
                             &\↰₂   Apply predicate 2 on the transpose of the Input
                                      (i.e. do the same checks but on columns)

JavaScript (ES6), 274 270 bytes

Takes input as a 2D array, where empty cells are marked with 2. Prints all possible solutions to the console.

f=(a,x=0,y=0,w=a.length,p,R=a[y])=>(M=z=>!a.some((r,y)=>/(0|1),\1,\1/.exec(s=r.map((v,x)=>(v=z?v:a[x][y],b-=v&1,c-=!v,m|=v&2,v),b=c=w/2))||b*c<0|o[b*c||s]&(o[s]=1),o={}))(m=0)&M(1)&&(m?R&&[0,1].map(n=>(p=R[x])==n|p>1&&(R[x]=n,f(a,z=(x+1)%w,y+!z),R[x]=p)):console.log(a))

How it works

The first part of the code uses the M() function to check the validity of the current board, both horizontally and vertically.

M = z =>
  !a.some((r, y) =>
    /(0|1),\1,\1/.exec(
      s = r.map((v, x) =>
        (
          v = z ? v : a[x][y],
          b -= v & 1,
          c -= !v,
          m |= v & 2,
          v
        ),
        b = c = w / 2
      )
    ) ||
    b * c < 0 |
    o[b * c || s] &
    (o[s] = 1),
    o = {}
  )

It maps a full row or column to the string s. This is actually an array coerced to a string, so it looks like "1,2,2,0,2,2".

It uses:

  • The regular expression /(0|1),\1,\1/ to detect 3 or more consecutive identical digits.
  • The counters b and c to keep track of the number of ones and zeros. Both counters are initialized to w / 2 and decremented each time a one or a zero (respectively) is encountered. This leads to either:
    • b = c = 0b * c = 0 → the line is complete and correct (as many zeros as ones)
    • b > 0 AND c > 0b * c > 0 → the line is not complete but correct so far (we don't have more than w / 2 zeros or more than w / 2 ones)
    • b < 0 OR c < 0b * c < 0 → the line is invalid
  • The flag m (for 'missing') which is non-zero if there's at least one remaining two on the board.
  • The object o to keep track of all line patterns encountered so far.

If the board is invalid, we stop immediately. If the board is valid and complete, we print it to the console. Otherwise, the second part of the code attempts to replace each 2 with either a zero or a one with recursive calls:

[0, 1].map(n =>
  (p = a[y][x]) == n |
  p > 1 && (
    a[y][x] = n,
    f(a, z = (x + 1) % w, y + !z),
    a[y][x] = p
  )
)

Demo

f=(a,x=0,y=0,w=a.length,p,R=a[y])=>(M=z=>!a.some((r,y)=>/(0|1),\1,\1/.exec(s=r.map((v,x)=>(v=z?v:a[x][y],b-=v&1,c-=!v,m|=v&2,v),b=c=w/2))||b*c<0|o[b*c||s]&(o[s]=1),o={}))(m=0)&M(1)&&(m?R&&[0,1].map(n=>(p=R[x])==n|p>1&&(R[x]=n,f(a,z=(x+1)%w,y+!z),R[x]=p)):console.log(a))

console.log('Example #1')
f([
  [1,2,2,0,2,2],
  [2,2,0,0,2,1],
  [2,0,0,2,2,1],
  [2,2,2,2,2,2],
  [0,0,2,1,2,2],
  [2,1,2,2,0,0]
])

console.log('Example #2')
f([
  [2,2,2,2,2,2,2,1,2,2],
  [2,0,0,2,2,0,2,2,1,2],
  [2,0,2,2,1,2,2,0,2,0],
  [2,2,1,2,2,2,1,2,2,2],
  [1,2,1,2,2,2,2,2,2,1],
  [2,2,2,2,2,2,2,1,2,2],
  [2,0,2,2,1,2,2,2,0,2],
  [2,2,2,2,1,1,2,2,2,0],
  [2,0,2,0,2,2,1,2,2,0],
  [0,2,2,2,0,2,2,2,1,2]
])