Build a top-front-side puzzle solver A top-front-side puzzle is a puzzle where you are required to construct a 3-D shape of (usually cubic) blocks given three orthogonal views: a top view, a front view, and a side view.

For example, given a top, front, and side view as follows:

Top:        Front:      Side:
. . . .     . . . .     . . . .
. x x .     . x x .     . x x .
. x x .     . x x .     . x x .
. . . .     . . . .     . . . .

In this problem, the side view is taken from the right.


A 2x2x2 cube (with volume 8) would satisfy this solution, but it's doable in volume 4, if we have the following layer structure:

. . . .     . . . .
. x . .     . . x .
. . x .     . x . .
. . . .     . . . .


There are also some unsolvable arrangements. Take, for example:

Top:        Front:      Side:
. . . .     . . . .     . . . .
. . . .     . . x .     . . . .
. x . .     . . . .     . x . .
. . . .     . . . .     . . . .


If the top view says the block is second from the left, there's no way that can match the front view that says the block must be third from the left. So this arrangement is impossible.



Your task is to build a program that, given an arbitrary 4x4 top-front-side puzzle, attempts to solve it in the fewest number of cubes, or declares it unsolvable.

Your program will take as input a series of 48 bits, representing the top, front, and side views. They may be in any format you want (a 6-byte string, a string of 0's and 1's, a 12-digit hex number, etc.), but the order of the bits must map as such:

Top: 0x00   Front: 0x10 Side: 0x20
0 1 2 3     0 1 2 3     0 1 2 3
4 5 6 7     4 5 6 7     4 5 6 7
8 9 a b     8 9 a b     8 9 a b
c d e f     c d e f     c d e f


In other words, the bits go in a left-to-right, top-to-bottom order, in the top, then front, then side view.

Your program will then output either a series of 64 bits indicating the cubes in the 4x4x4 grid that are filled in, or indicate that the grid is unsolvable.



Your program will be scored by running a battery of 1,000,000 test cases.

The test data will be generated by taking the MD5 hashes of the integers "000000" through "999999" as strings, extracting the first 48 bits (12 hexits) of each of these hashes, and using them as input for the top-front-side puzzle. As an example, here are some of the test inputs and the puzzles they generate:

Puzzle seed: 000000   hash: 670b14728ad9
Top:        Front:      Side:
. x x .     . . . x     x . . .
x x x x     . x . x     x . x .
. . . .     . x x x     x x . x
x . x x     . . x .     x . . x

Puzzle seed: 000001   hash: 04fc711301f3
Top:        Front:      Side:
. . . .     . x x x     . . . .
. x . .     . . . x     . . . x
x x x x     . . . x     x x x x
x x . .     . . x x     . . x x

Puzzle seed: 000157   hash: fe88e8f9b499
Top:        Front:      Side:
x x x x     x x x .     x . x x
x x x .     x . . .     . x . .
x . . .     x x x x     x . . x
x . . .     x . . x     x . . x


The first two are unsolvable, while the last one has a solution with the following layers, front to back:

x . . .   . . . .   x x x .   x x x .
. . . .   x . . .   . . . .   . . . .
x . . .   . . . .   . . . .   x x x x
x . . .   . . . .   . . . .   x . . x

There are a total of 16 blocks here, but it can probably be done in less.


Your program's score will be determined by the following criteria, in descending order of priority:


The highest number of solved cases.
The lowest number of blocks required to solve those cases.
The shortest code in bytes.


You must submit and calculate the score by yourself, which requires your program to be able to run through all 1,000,000 test cases.
Python: 5360 test-cases solved using 69519 blocks, 594 bytes

This should be the optimal values. 

Approach:

First I'll test if the test-case is actual solvable. I do this by initializing a list of length 64 by ones and set all positions to zero, if there the corresponding pixel of the three views is zero. 
Then I simple view at the puzzle from all 3 directions, and look if the views are the same as the input views. If there're equal, the puzzle is solvable (we already found the worst solution), otherwise it's unsolvable. 

Then I do a branch-and-bound approach for finding the optimal solution. 

Branching: I have a recursive function. The recursion depth tells how many values are fixed already. In each call of the function I call itself twice, if the value at the current index is 1 (this value can be 0 or 1 in the optimal solution), or once, if the value is 0 (it has to be zero in the optimal solution). 

Bounding: I use two different strategies here. 


I calculate the views from the 3 sides in each function call and look If they still match the input values. If they don't match, I don't call the function recursively. 
I keep the best solution in memory. It there already more fixed ones in the current branch than in the best solution, I can already close that branch. Additionally I can estimate a lower bound for the number of activated blocks, which are not fixed. And the condition becomes #number of activated fixed blocks + #lower bound of activated blocks (under the not fixed blocks) &lt; #number of activated blocks in the best solution.


Here's the Python code. It defines a function f which expects 3 lists containing 1s and 0s and returns either 0 (not solvable) or a list of 1s and 0s representing the optimal solution.

S=sum;r=range
def f(t,f,s):
 for i in r(4):s[4*i:4*i+4]=s[4*i:4*i+4][::-1]
 c=[min(t[i%16],f[(i//16)*4+i%4],s[i//4])for i in r(64)]
 m=lambda:([int(S(c[i::16])&gt;0)for i in r(16)],[int(S(c[i+j:i+j+16:4])&gt;0)for i in r(0,64,16)for j in r(4)],[int(S(c[i+j:i+j+4])&gt;0)for i in r(0,64,16)for j in r(0,16,4)])==(t,f,s)
 Z=[65,0];p=[]
 def g(k):
  if k&gt;63and S(c)&lt;Z[0]:Z[:]=[S(c),c[:]]
  if k&gt;63or S(c[:k])+p[k]&gt;=Z[0]:return
  if c[k]:c[k]=0;m()and g(k+1);c[k]=1
  m()and g(k+1)
 for i in r(64):h,R=(i//16)*4+4,(i//4)%4;p+=[max(S(f[h:]),S(s[h:]))+max((R&lt;1)*S(f[h+i%4-3:h]),S(s[h+R-3:h]))]
 g(0);return Z[1]


The code length is 596 bytes/chars. And here's the test framework:

from hashlib import md5
from time import time

N = 1000000
start=time();count=blocks=0
for n in range(N):
 bits = list(map(int,"{:048b}".format(int(md5("{:06}".format(n).encode("utf-8")).hexdigest()[:12], 16))))
 result = f(bits[:16], bits[16:32], bits[32:])
 if result:
  count += 1
  blocks += sum(result)
  print("Seed: {:06}, blocks: {}, cube: {}".format(n, sum(result), result))
print()
print("{} out of {} puzzles are solvable using {} blocks.".format(count, N, blocks))
print("Total time: {:.2f} seconds".format(time()-start))


You can find an ungolfed version of the program here. It's also a little bit faster. 

Results:

5360 out of 1000000 puzzles are solvable. In total we need 69519 blocks. The number of blocks varies from 6 blocks to 18 blocks. The hardest puzzle took about 1 second to solve. It's the puzzle with the seed "347177", which looks like 

Top:      Front:    Side:
x x . .   x x x x   x . x .
x x x x   x x x x   x x x x
x x x x   x x x x   x x x x
x x . x   x x x x   x . x x


and has an optimal solution with 18 cubes. The following is a few from top for each of the layers: 

Top 1:    Top 2:    Top 3:    Top 4:
. . . .   . x . .   . x . .   x . . .
. . x x   . . x .   x . . .   . x x .
. . . .   . . . x   x x x .   . . . .
x x . .   x . . .   . . . x   . . . x


The total running time for all test-cases was about 90 seconds. I used PyPy to execute the my program. CPython (the default Python interpreter) is a bit slower, but also solves all puzzles in only 7 minutes.

You can find the complete output here: The output is self-explanatory. E.g. the output for the puzzle above is: 

Seed: 347177, blocks: 18, cube: [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1]