Background
Shakashaka is a puzzle on a grid, whose objective is to place some half-squares (right triangles) on the empty cells so that all the remaining contiguous regions form rectangles, either upright or 45 degrees rotated. Here is an example puzzle with a solution:
Ignore the number clues for this challenge.
Challenge
Given a grid with black squares and half-squares placed on some of the cells, determine if it is a valid solution to some Shakashaka puzzle, i.e. all the white regions form rectangles.
The input is a 2D grid (in any valid form) with each cell containing its encoded state. Each cell will be in one of the six states: white square (empty), black square, and four possible orientations of the half-square (NW, NE, SW, SE). You may encode them as six distinct numbers or (possibly multi-dimensional) arrays of numbers, and you can use characters instead of numbers (so strings or string arrays are also acceptable).
Standard code-golf rules apply. The shortest code in bytes wins.
Test cases
Because it is pretty hard to reproduce a Shakashaka board with Unicode chars, I include a Python script to convert the input to any form of your choice. The default configuration renders them using Unicode geometric shapes, so you can roughly see how the actual grid will look like.
def gentest(tc,mapping,charjoin='',linejoin='\n',preamble='',postamble=''):
return preamble+linejoin.join(charjoin.join(mapping[x] for x in line.split())for line in tc.strip().splitlines())+postamble
How to use it
tc - the testcase string, which will be supplied in the footer
mapping - the dict of 6 keys 'B', 'W', 'NE', 'NW', 'SE', 'SW' mapped to
6 distinct strings
'B' is filled (black) square, 'W' is white square,
the rest four represent half-cell filled in that direction
charjoin - string to insert between each cell on a horizontal line
linejoin - string to insert between each line of cells
preamble - string to add in front of the entire grid
postamble - string to add at the end of the entire grid
Alternatively, you can use this Stack Snippet kindly written by @Arnauld to better visualize the test cases (it shows the 10 test cases by default):
function draw2() { let grids = document.getElementById("in").value.split('\n\n').map(g => g.split('\n').map(r => r.split(/ +/))), out = ""; grids.forEach(g => { out += '<div class="wrapper" style="width:' + g[0].length * 17 + 'px;height:' + g.length * 17 + 'px;">'; g.forEach(r => { r.forEach(s => { out += '<div class="cell"><div class="cell ' + s + '"></div></div>'; }); }); out += '</div>'; }); document.getElementById("out").innerHTML = out;}window.onload = () => { document.getElementById("in").value = [ "NW NE W W B W NW NE NW NE", "SW W NE NW NE B SW SE SW SE", "B SW SE SW SE W NW NE B W", "W NW NE NW NE W SW W NE W", "NW W SE SW SE B W SW SE B", "SW SE B B W W NW NE NW NE", "B NW NE W B NW W SE SW SE", "NW W W NE W SW SE B NW NE", "SW W W SE B NW NE NW W SE", "B SW SE W W SW SE SW SE B", "", "W W W", "W W W", "W W W", "", "NW NE W", "SW SE W", "W W B", "", "B B B", "B B B", "B B B", "", "SE", "", "W NW", "NW W", "", "NW W SE", "", "W NW NE W", "NW W W NE", "SW W B SE", "W SW SE W", "", "B W", "W W", "", "W NW NE B", "NW W W NE", "SW SE SW SE" ].join('\n'); draw2();};
textarea { width: 400px; } #wrapper, .wrapper { border-left: 1px solid #555; border-top: 1px solid #555; margin-top: 10px; } .cell { float: left; width: 16px; height: 16px; border-right: 1px solid #555; border-bottom: 1px solid #555; overflow: hidden; } .NW { width: 0; height: 0; border-right: 16px solid #fff; border-top: 16px solid #00b496; } .SW { width: 0; height: 0; border-right: 16px solid #fff; border-bottom: 16px solid #00b496; } .NE { width: 0; height: 0; border-left: 16px solid #fff; border-top: 16px solid #00b496; } .SE { width: 0; height: 0; border-left: 16px solid #fff; border-bottom: 16px solid #00b496; } .W { width: 16px; height: 16px; } .B { width: 16px; height: 16px; background-color: #000; }
<textarea id="in" oninput="draw2()"></textarea><div id="out"></div>
Truthy test cases
# The 10x10 puzzle solution above
NW NE W W B W NW NE NW NE
SW W NE NW NE B SW SE SW SE
B SW SE SW SE W NW NE B W
W NW NE NW NE W SW W NE W
NW W SE SW SE B W SW SE B
SW SE B B W W NW NE NW NE
B NW NE W B NW W SE SW SE
NW W W NE W SW SE B NW NE
SW W W SE B NW NE NW W SE
B SW SE W W SW SE SW SE B
# all white
W W W
W W W
W W W
# a diamond and some rectangles
NW NE W
SW SE W
W W B
# all black
B B B
B B B
B B B
Falsy test cases
# a triangle
SE
# a larger triangle, with a valid white square
W NW
NW W
# a parallelogram
NW W SE
# a slanted square with a black hole in the middle
W NW NE W
NW W W NE
SW W B SE
W SW SE W
# a region that contains two rectangles but is not a rectangle by itself
B W
W W
# same as above, but 45 degrees rotated
W NW NE B
NW W W NE
SW SE SW SE