There are several things to consider in what you want.
If you always prefer squares over the largest figure, you can get many single lines in the form of columns, because the rectangle always starts with a square. A square or rectangle can only begin if the upper left is significant.
Consider this array:
[1, 2, 3, 4, 5], [7, 0, 0, 0, 0], [0, 0, 0, 0, 0], [1, 2, 0, 0, 5]
If you select the squares above the largest figure, you get a square and three columns
[1] [2] [3] [4] [5] instead of [1] [2] [3] [4] [5] [ 7 ] [ ] [ ] [ ] [ 7 ] [ ] [ ] [ ] [ ] [ ]
In addition, you can get empty individual cells:
[1, 1, 2, 0], gives [1] [1] [ 2 ] [3, 0, 0, 0], [ ] [?] <--- [0, 0, 0, 5], [ 3 ] [5] [0, 0, 0, 1] [ ] [1]
The biggest square here is 3x3, starting at 3. If you first declare 3x3 and then a line starting at 2, you will get an empty cell [?] Above 5.
Last, if you have zeros in the top row or in the left column, you may also encounter a problem:
V [1, 0, 0, 2], will leave you with [ 1 ] [?] [2] [0, 0, 3, 4], [ ] [3] [4] [0, 5, 6, 7] --->[?] [5] [6] [7]
However, maybe your array is not so complex, and maybe you are not against empty cells. In any case, it was fun to solve this puzzle with your specifications. My decision is subject to the following rules:
1) first the squares.
2) Size matters. The bigger, the better
3) Rows and columns: size matters. The biggest ones go first.
4) Colspan skips rowspan
Fiddle
var maxv = arr.length; var maxh = arr[0].length; var rsv = []; var rsh = []; pop_arr(); var shape; try_sq(); try_rect(); try_loose(); //TRY SQUARES FIRST function try_sq() { var sv, sh; shape = []; for (sv = 0; sv < maxv - 1; sv++) { for (sh = 0; sh < maxh - 1; sh++) { if (arr[sv][sh] === 0 || rsv[sv][sh] > -1 || rsh[sv][sh] > -1) { continue; } check_sq(sv, sh); } } if (shape.length > 0) { occu(); } } //TRY RECTANGLES function try_rect() { var sv, sh; var rect = false; do { shape = []; for (sv = 0; sv < maxv; sv++) { for (sh = 0; sh < maxh; sh++) { if (arr[sv][sh] === 0 || rsv[sv][sh] > -1 || rsh[sv][sh] > -1) continue; check_rec(sv, sh); } } if (shape.length > 0) { occu(); } else { rect = false; } } while (rect === true); } //TRY LOOSE function try_loose() { var sv, sh; //SET THE 1x1 with value for (sv = 0; sv < maxv; sv++) { for (sh = 0; sh < maxh; sh++) { if (arr[sv][sh] !== 0 && (rsv[sv][sh] == -1 || rsh[sv][sh] == -1)) { rsv[sv][sh] = 1; rsh[sv][sh] = 1; } } } //SEARCH FOR rectangles wit no value var rect = true; do { shape = []; for (sv = 0; sv < maxv; sv++) { for (sh = 0; sh < maxh; sh++) { if (arr[sv][sh] !== 0 || (rsv[sv][sh] > -1 || rsh[sv][sh] > -1)) { continue; } rect = check_loose(sv, sh); } } if (shape.length > 0) occu(); else { rect = false; } } while (rect === true); } //check SINGLES function check_loose(start_v, start_h) { var vd, hd, iv, ih, rect; var hor = ver = 1; var horb = 0; var mxv = maxv - 1; var mxh = maxh - 1; rect = true; vd = start_v + ver; hd = start_h + hor; //check horizontal for (sh = start_h + 1; sh <= mxh; sh++) { if (arr[start_v][sh] !== 0 || rsh[start_v][sh] > -1) { break; } hor++; } //check vertical for (iv = start_v + 1; iv <= mxv; iv++) { if (arr[iv][start_h] !== 0 || rsh[iv][start_h] > -1) { break; } ver++; } if (hor > ver || hor == ver) { shape.unshift({ 0: (hor), 1: [start_v, start_h, 1, hor] }); return true; } else if (ver > hor) { shape.push({ 0: (ver), 1: [start_v, start_h, ver, 1] }); return true; } return false; } //check SQUARE function check_sq(start_v, start_h) { if (arr[start_v + 1][start_h] !== 0) { return false; } if (arr[start_v][start_h + 1] !== 0) { return false; } var vd, hd, sv, sh, square; var hor = ver = 1; var mxv = maxv - 1; var mxh = maxh - 1; //CHECK DIAGONAL do { square = true; vd = start_v + ver; hd = start_h + hor; //diagonal OK if (arr[vd][hd] !== 0) { if (hor == 1) { if (ver == 1) { return false; } square = false; break; } } //check horizontal for (sh = start_h; sh <= hd; sh++) { if (arr[vd][sh] !== 0) { square = false; break; } } if (square === false) break; //check vertical for (sv = start_v; sv <= vd; sv++) { if (arr[sv][hd] !== 0) { square = false; break; } } if (square === false) break; hor++; ver++; } while (square === true && vd < mxv && hd < mxh); //SQUARE OK if (hor > 1 && ver > 1 && hor == ver) { shape.push({ 0: (hor * ver), 1: [start_v, start_h, ver, hor] }); } } //check RECTANGLE function check_rec(start_v, start_h) { var vd, hd, iv, ih, rect; var hor = ver = 1; var horb = 0; var mxv = maxv - 1; var mxh = maxh - 1; rect = true; vd = start_v + ver; hd = start_h + hor; //check horizontal if (start_h < maxh) { for (sh = start_h + 1; sh <= mxh; sh++) { if (arr[start_v][sh] !== 0 || rsh[start_v][sh] > -1) break; hor++; } } //check vertical if (start_v < maxv) { for (iv = start_v + 1; iv <= mxv; iv++) { if (arr[iv][start_h] !== 0 || rsh[iv][start_h] > -1) break; ver++; } } if (hor == 1 && ver == 1) return false; if (hor > ver || hor == ver) { shape.unshift({ 0: (hor), 1: [start_v, start_h, 1, hor] }); return true; } else { shape.push({ 0: (ver), 1: [start_v, start_h, ver, 1] }); return true; } return false; } //FIND LARGEST SHAPE function occu() { var le = shape.length; for (var i = 0; i < le; i++) { var b = Math.max.apply(Math, shape.map(function (v) { return v[0]; })); for (var j = 0; j < shape.length; j++) { if (shape[j][0] == b) break; } var c = shape.splice(j, 1); claim(c[0][1]); } } //CLAIM SHAPE function claim(sh) { var iv, ih; for (iv = sh[0]; iv < sh[0] + sh[2]; iv++) { for (ih = sh[1]; ih < sh[1] + sh[3]; ih++) { if (rsv[iv][ih] > -1 || rsh[iv][ih] > -1) return false; } } for (iv = sh[0]; iv < sh[0] + sh[2]; iv++) { for (ih = sh[1]; ih < sh[1] + sh[3]; ih++) { rsv[iv][ih] = 0; rsh[iv][ih] = 0; } } rsv[sh[0]][sh[1]] = sh[2]; rsh[sh[0]][sh[1]] = sh[3]; } function pop_arr() { var em = []; em[0] = arr[0].concat(); for (var i = 0; i < maxh; i++) { em[0][i] = -1; } for (i = 0; i < maxv; i++) { rsv[i] = em[0].concat(); rsh[i] = em[0].concat(); } }