C, 824 803 byte
#define Z return
#define Y char*b
#define N --n
i,j,n,w,h,A,B,C,D,E,F,G,H;char c[9999],*r,*d;x(b)Y;{if(b<c||*b<35)Z;++n;*b^=1;x(b-1);x(b+1);x(b-w);x(b+w);}m(b,p,y)Y,*p;{d=b;if(!y)for(y=-1,--p;1[++p]&31;)d+=w;for(i=0;*p&31?!(*p&16>>i)||b[i]&1:0;++i>4?p+=y,b+=w,i=0:0);Z!(*p&31)?x(d),n:0;}a(b)Y;{for(j=n=0;j<w*h;++j)if(m(c+j,b,1)||m(c+j,b,0))Z n;Z 0;}f(Y){bzero(c,9999);for(h=0,b=strcpy(c,b);r=b,b=strchr(b+1,10);h++,w=b-r);for(A=2,r=1+"@_`^C@|T@^R@XO@XX`|FB@|PP@|DD@PXN@XHX@XPX`PPXL@XHHX@XLDD@XPPX`PPPXH@PXHHH@PPPPP@";*r;r+=A+=r[-1]/96)while(a(r));A=B=C=D=E=F=G=H=0;while(a("PX")||a("XH")) (n-=3)?N?N?N?0:++H:++G:++F:++C;while(a("^")||a("PPPP"))(n-=4)?N?N?0:++H:++G:++E;while(a("P"))N?N?N?N?N?N?0:++H:++G:++F:++D:++B:++A;Z H||(G&&A)||(F&&B+B+A>1)||(E&&A>1)||D>1||C>1||((D||C)*3+B*2+A>5)*(A>1||B>2||A*B);}
Lưu ý: Bao gồm sửa lỗi (mục trước đó đã xác định sai một tromino và hai domino là tạo thành một khối).
Trong mã trình điều khiển TIO, có nhiều trường hợp kiểm tra hơn và giờ đây có trình theo dõi pass / fail; trường hợp kiểm tra hexomino đã được cập nhật với giá trị vượt qua / thất bại dự kiến trong nhãn.
Hãy thử trực tuyến!
... Và trước khi giải thích điều này một cách chi tiết, nó đáng để xem xét tổng quan ở cấp độ cao.
Tổng quan cơ bản
Thuật toán này áp dụng một công cụ đối sánh mẫu để phân loại từng polyomino mà nó tìm thấy với một mẫu nhất định là tập hợp con của nó. Vì đa giác được khớp, chúng "không được đánh dấu", loại trừ chúng khỏi khớp mẫu một lần nữa. Việc phân loại ban đầu được đưa ra bởi công cụ đối sánh chỉ đơn giản là đếm số lượng gạch trong polyomino.
Bộ so khớp được áp dụng trước tiên để loại bỏ tất cả các đa giác không thể gấp lại thành một khối; việc phân loại các polyominos bị loại bỏ. Trận đấu thành công nếu những đa giác này xuất hiện trong những cấp độ cao hơn; do đó, chúng tôi chỉ quan tâm đến tập hợp con nhỏ nhất "không thể bán được" cho mỗi lớp. Thể hiện ở đây cùng với các bảng mã được đệm là tất cả các đa giác như vậy (không bao gồm các phản xạ dọc của chúng). Mã hóa sử dụng các bit 4-0 của mỗi ký tự để biểu thị các ô vuông trên mỗi hàng:
[^C```] [XHX``] [PPPXH] [XHHX`] [PXN``] [|PP``]
####. ##... #.... ##... #.... ###..
...## .#... #.... .#... ##... #....
..... ##... #.... .#... .###. #....
..... ..... ##... ##... ..... .....
..... ..... .#... ..... ..... .....
[|FB``] [XPX``] [PPXL`] [XLDD`] [XPPX`] [|DD``]
###.. ##... #.... ##... ##... ###..
..##. #.... #.... .##.. #.... ..#..
...#. ##... ##... ..#.. #.... ..#..
..... ..... .##.. ..#.. ##... .....
..... ..... ..... ..... ..... .....
[|T```] [^R```] [PXHHH] [XO```] [_````] [PPPPP]
###.. ####. #.... ##... ##### #....
#.#.. #..#. ##... .#### ..... #....
..... ..... .#... ..... ..... #....
..... ..... .#... ..... ..... #....
..... ..... .#... ..... ..... #....
[XX```]
##...
##...
.....
.....
.....
Sau khi các đa giác này được loại bỏ, chúng tôi phân loại các đa giác còn lại thành các loại có liên quan. Điều đáng chú ý là trong hầu hết các trường hợp, người ta chỉ có thể tìm thấy các đa giác còn lại (chúng có thể gập lại trên các hình khối) và chỉ cần tìm kiếm tổng số sáu. Có hai trường hợp ngoại lệ:
- Một tromino góc và một tromino đường thẳng không thể tạo thành một khối
- Một tetromino dòng và domino không thể tạo thành một khối
Để có thể đáp ứng hạn chế này, chúng tôi tạo thành 8 loại, từ AH: A cho monominoes (gạch đơn), B cho domino, C cho trominoes góc, D cho trominoes dòng, E cho tetrominoes dòng, F cho tetrominoes khác, G cho pentominoes, và H cho hexominoes. Bất cứ điều gì không thuộc một trong những loại này chỉ là bỏ qua. Đếm các đa giác rơi vào mỗi loại đủ.
Cuối cùng, chúng tôi chỉ trả lại sự trung thực hoặc giả dối dựa trên một phương trình khổng lồ và các bảng này.
Phản đối với ý kiến
i,j,n,w,h,A,B,C,D,E,F,G,H;char c[9999],*r,*d;
x(b)char*b;{ // recursively unmarks polyomino pointed to by b.
if(b<c||*b<35)return;
++n; *b^=1; // Tabulates tiles in n as it goes.
x(b-1);x(b+1);x(b-w);x(b+w); // left/up/down/right
}
m(b,p,y)char*b,*p;{ // pattern match area b with pattern p, direction y.
// y=1 scans down; y=0 scans up.
d=b; // d tracks a tile in the currently matched pattern for unmarking.
// Note that all patterns are oriented to where "top-left" is a tile.
if(!y) // ...when scanning up, move p to the end, set y to -1 to count backward,
// and advance d to guarantee it points to a tile (now "bottom-left")
for(y=-1,--p;1[++p]&31;)d+=w;
// Match the pattern
for(i=0;*p&31?!(*p&16>>i)||b[i]&1:0;++i>4?p+=y,b+=w,i=0:0);
return !(*p&31) // If it matches...
? x(d),n // ...unmark/count total polyomino tiles and return the count
: 0;
}
a(b)char*b;{ // Scan for an occurrence of the pattern b.
for(j=n=0;j<w*h;++j)
if(m(c+j,b,1)||m(c+j,b,0)) // (short circuit) try down then up
return n;
return 0;
}
// This is our function. The parameter is a string containing the entire area,
// delimited by new lines. The algorithm assumes that this is a rectangular area.
// '#' is used for tiles; ' ' spaces.
f(char*b) {
bzero(c,9999); // Init categories, c buffer
for(h=0,b=strcpy(c,b);r=b,b=strchr(b+1,10);h++,w=b-r); // Find width/height
// Unmark all polyominoes that contain unfoldable subsets. This was
// compacted since the last version as follows. A tracks
// the current pattern's length; r[-1], usually terminator for the
// previous pattern, encodes whether the length increases; and o/c
// the patterns were sorted by length.
for(A=2,r=1+"@_`^C@|T@^R@XO@XX`|FB@|PP@|DD@PXN@XHX@XPX`PPXL@XHHX@XLDD@XPPX`PPPXH@PXHHH@PPPPP@";*r;r+=A+=r[-1]/96)
while(a(r));
A=B=C=D=E=F=G=H=0;
// Match corner trominoes now to ensure they go into C.
while(a("PX")||a("XH"))
(n-=3)
? --n
? --n
? --n
? 0 // More than 6 tiles? Ignore it.
: ++H // 6 tiles? It's an H.
: ++G // 5 tiles? It's a G.
: ++F // 4 tiles? It's an F.
: ++C; // only 3 tiles? It's a C.
// Now match line tetrominoes to ensure they go into E.
while(a("^")||a("PPPP"))
(n-=4)
? --n
? --n
? 0 // More than 6 tiles? Ignore it.
: ++H // 6 tiles? It's an H.
: ++G // 5 tiles? It's a G.
: ++E; // only 4 tiles? It's an E.
// Find all remaining tetrominoes ("P" is a monomino pattern)
while(a("P"))
--n
? --n
? --n
? --n
? --n
? --n
? 0 // More than 6 tiles? Ignore it.
: ++H // 6 tiles? It's an H.
: ++G // 5 tiles? It's a G.
: ++F // 4 tiles? It's an F.
: ++D // 3 tiles? It's a D.
: ++B // 2 tiles? It's a B.
: ++A; // only 1 tile? It's an A.
// Figure out if we can form a cube:
return H // Yes if we have a foldable hexomino
||(G&&A) // Yes if we have a foldable pentomino
// and a monomino
||(F&&B+B+A>1) // Yes if we have a foldable non-line tetromino
// and 2 other tiles (dominoes count twice).
// Either one domino or two monominoes will do.
||(E&&A>1) // Yes if we have a foldable line tetromino (E)
// and two monominoes (A). Note we can't make a
// cube with a line tetromino and a domino (B).
||D>1 // Yes if we have two line trominoes
||C>1 // Yes if we have two corner trominoes
||((D||C)*3+B*2+A>5)
// Any combination of trominoes, dominoes, monominoes>6,
// where trominoes are used at most once
// (via logical or)...
* (A>1||B>2||A*B)
// ...times this includer/excluder fudge factor
// that culls out the one non-working case;
// see table:
//
// Trominos Dominos Monomos Cube A>1 B>2 A*B
// 1 0 3+ yes Y N 0
// 1 1 1+ yes Y N 1
// 1 2 0 no N N 0
// 0+ 3 0+ yes Y Y 1
;
}