/************************************************************************/ /* */ /* パズルプログラミング勉強会 第1課題 3D版 */ /* */ /* 5×4×3の直方体に詰める場合 (3940通り) */ /* */ /* ペントミノ箱詰めパズル */ /* 株式会社テンヨーから『FACOM』として発売されていた */ /* */ /* by パソコン初心者 */ /* */ /************************************************************************/ /* 注意事項 */ /* */ /* Cのプログラムらしくない! */ /* C++の影響を変に受けていて、これならちゃんとC++で書くべき! */ /* */ /* long long 64ビット整数 を利用 */ /* 5x4x3 の場合しか考えていない! */ /* コンパイラ: gcc */ /* */ /************************************************************************/ /* メモ(はずかしいから読むな) */ /* */ /* 開始の動機 */ /* 3Dでも、2Dと同程度の時間で解き終えられるし、 */ /* 技術的にも似たようなもので、ちょっと手を加えてみた */ /* つまり、ペントミノのプログラムに若干手を加えた状態 */ /* */ /* 2001年10月3日 */ /* 一応動くようになり、3940通りが出るようになった。 */ /* めでたし、めでたしということで、一応公開。 */ /* 回転は F の配置方向で制限し、鏡映は鏡映パターンによる確認 */ /* */ /* 勝手な期待 */ /* 多分、だれかが高速化、汎用化をするものと期待する。 */ /* */ /************************************************************************/ #define TRUE 1 #define FALSE 0 #define CN 5 /* セル数 */ #define PN 12 /* ピース数 */ #define WIDTH 5 /* ボード幅 */ #define HEIGHT 4 /* ボード高 */ #define DEPTH 3 /* ボード奥行き */ #define BPOS(x,y,z) ((x)*HEIGHT+(y))*DEPTH+(z) #define X(z) ((z)/(DEPTH*HEIGHT)) #define Y(z) (((z)/DEPTH)%HEIGHT) #define Z(z) ((z)%(DEPTH)) #define MIN(x,y) ((x)<(y)?(x):(y)) #define BLANKCELL '.' #define WALLCELL '#' typedef long long BitsForm; typedef struct { int x; int y; int z; } Pos; typedef struct { Pos form[CN]; char name; } PieceFormDef; typedef struct { int n; BitsForm bits[24]; /* 可能ビットパターン */ int offsets[24][CN]; /* 可能オフセット */ } AllBitsForm; typedef struct { char name; int n; Pos form[24][CN]; /* 基準パターン(Pos) */ BitsForm bits[24]; /* 基準パターン(ビット) */ AllBitsForm allbits[64]; /* 特定基準位置の可能パターン */ } PieceAllFormDef; PieceFormDef OriginalPieceForm[PN] = { {{{ 0, 0, 0},{ 0, 1, 0},{ 1, 1, 0},{ 1, 2, 0},{ 2, 1, 0}}, 'F'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 0, 2, 0},{ 0, 3, 0},{ 0, 4, 0}}, 'I'}, {{{ 0, 1, 0},{ 1, 0, 0},{ 1, 1, 0},{ 1, 2, 0},{ 2, 1, 0}}, 'X'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 1, 1, 0},{ 1, 2, 0},{ 2, 2, 0}}, 'W'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 1, 1, 0},{ 2, 1, 0},{ 2, 2, 0}}, 'Z'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 0, 2, 0},{ 1, 1, 0},{ 2, 1, 0}}, 'T'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 0, 2, 0},{ 1, 0, 0},{ 2, 0, 0}}, 'V'}, {{{ 0, 0, 0},{ 0, 2, 0},{ 1, 0, 0},{ 1, 1, 0},{ 1, 2, 0}}, 'U'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 1, 1, 0},{ 1, 2, 0},{ 1, 3, 0}}, 'N'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 0, 2, 0},{ 0, 3, 0},{ 1, 1, 0}}, 'Y'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 0, 2, 0},{ 0, 3, 0},{ 1, 0, 0}}, 'L'}, {{{ 0, 0, 0},{ 0, 1, 0},{ 1, 0, 0},{ 1, 1, 0},{ 2, 0, 0}}, 'P'} }; PieceAllFormDef AllPieceForm[PN]; /* ピースの全方向データ */ char CharBoard[WIDTH*HEIGHT*DEPTH]; /* 文字型ボード */ BitsForm BitsConv[WIDTH*HEIGHT*DEPTH]; int RestPieces[PN]; /* 未使用ピース TRUE/FALSE */ int totalcount; /* 解の総数 */ int tryalcount; /* 試行錯誤の回数 */ int printflag; /* 解の印刷フラグ */ /* 0 : 印刷しない */ /* 1 : 文字表示 */ /* 2 : 境界線表示 */ int stepflag; /* ステップ毎印刷フラグ */ int debugflag; /* デバッグ用印刷フラグ */ /*----------------------------------------------------------------------*/ /* 一応宣言しておこう */ /*----------------------------------------------------------------------*/ int CmpPos( Pos p1, Pos p2 ); int IsInsidePos( Pos p ); int IsSamePieceForm( Pos cell1[], Pos cell2[] ); char GetChar( char board[], int x, int y, int z ); BitsForm PieceFormToBitsForm( Pos cells[] ); void RotetePiece( Pos *cells, int rot, int plane ); void MakeAllPieceForm( void ); int IsPieceOnBoard( Pos cells[], int x, int y, int z ); void MakeAllPieceBits( void ); void Initialize( void ); void PrintPieceForm( Pos cells[] ); void PrintBitsForm( BitsForm bits ); void PrintAllPieceForm( void ); void PrintAllBitsForm( int x, int y, int z ); void PrintCurrentBoardbyChar( char board[] ); void PrintCurrentBoardbyLine( char board[] ); void PrintCurrentBoard( char board[] ); int MirrorCheck( void ); void Found( void ); int SearchNextBase( BitsForm board, int base ); void SearchOneLevel( int level, BitsForm board, int base ); void SearchAll( void ); int main(int argc, char **argv); /************************************************************************/ /* 道具箱 */ /************************************************************************/ /*----------------------------------------------------------------------*/ /* 位置の大小比較 */ /*----------------------------------------------------------------------*/ int CmpPos( Pos p1, Pos p2 ) { if( p1.x == p2.x ) { if( p1.y == p2.y ) { if( p1.z == p2.z ) return 0; return p1.z > p2.z ? 1 : -1; } return p1.y > p2.y ? 1 : -1; } return p1.x > p2.x ? 1 : -1; } /*----------------------------------------------------------------------*/ /* 位置がボード範囲内かどうかの判定 */ /*----------------------------------------------------------------------*/ int IsInsidePos( Pos p ) { if( p.x < 0 || p.x >= WIDTH ) return FALSE; if( p.y < 0 || p.y >= HEIGHT ) return FALSE; if( p.z < 0 || p.z >= DEPTH ) return FALSE; return TRUE; } /*----------------------------------------------------------------------*/ /* 駒の一致比較 */ /*----------------------------------------------------------------------*/ int IsSamePieceForm( Pos cell1[], Pos cell2[] ) { int i; for( i=0; i= WIDTH ) return WALLCELL; if( y < 0 || y >= HEIGHT ) return WALLCELL; if( z < 0 || z >= DEPTH ) return WALLCELL; return board[BPOS(x,y,z)]; } /************************************************************************/ /* 初期化 */ /************************************************************************/ /*----------------------------------------------------------------------*/ /* 駒のビットデータ化 */ /* 駒の置き方の変換: 配列 → ビットデータ */ /*----------------------------------------------------------------------*/ BitsForm PieceFormToBitsForm( Pos cells[] ) { int i; BitsForm bits = 0; BitsForm one = 1; Pos *p; for( p=cells, i=0; ix, p->y, p->z)]; } return bits; } /*----------------------------------------------------------------------*/ /* ピースの回転 */ /* *cells 位置配列データ */ /* rot ピースの回転反転など 8通り (0..7) */ /* plane 平面の決定 0:XY, 1:YZ, 2:ZX */ /*----------------------------------------------------------------------*/ void RotetePiece( Pos *cells, int rot, int plane ) { int i, j, k, dx, dy, dz, w; Pos pw; /* XY平面での入れ換えステップ */ if( rot & 0x01 ) /* 対角線を軸とする入れ換え */ for( i=0; i 0 ) k = j; } if( k != i ) { pw = cells[i]; cells[i] = cells[k]; cells[k] = pw; } } dx = cells[0].x; dy = cells[0].y; dz = cells[0].z; for( i=0; i=2 ) break; for( k=0; kbits[n] = (AllPieceForm[i].bits[j] << w); for( k=0; koffsets[n][k] = BPOS(pos.x,pos.y,pos.z) + w; } ++n; } abf->n = n; } } } } } /*----------------------------------------------------------------------*/ /* 初期化 */ /*----------------------------------------------------------------------*/ void Initialize( void ) { int i; BitsForm one = 1; /* ビット変換テーブルの初期化 */ for( i=0; i0) ) printf( " " ); if( (i % (HEIGHT*DEPTH) ) == 0 && (i>0) ) printf( " " ); printf( "%c", (bits & mask) ? '1' : '0' ); mask <<= 1; } printf( "\n" ); for( y=0; y> BPOS(x,y,z)) & 1 ? 'X' : '.' ); } printf( " " ); } printf( "\n" ); } printf( "\n" ); } /*-----------------------------------------------------------------Print*/ /* 全ピース方向データの印刷 */ /*----------------------------------------------------------------------*/ void PrintAllPieceForm( void ) { int i, j; for( i=0; in == 0 ) continue; printf( "%c\n", AllPieceForm[i].name ); for( j=0; j < abf->n; ++j ) { PrintBitsForm( abf->bits[j] ); } } } /*-----------------------------------------------------------------Print*/ /* 現在の試行錯誤状況(文字)の印刷 */ /*----------------------------------------------------------------------*/ void PrintCurrentBoardbyChar( char board[] ) { int x, y, z; int c; for( y=0; y mirror[i] ) return FALSE; if( CharBoard[i] < mirror[i] ) break; } return TRUE; } /*----------------------------------------------------------------------*/ /* 解を発見 */ /*----------------------------------------------------------------------*/ void Found( void ) { if( ! MirrorCheck() ) return; ++totalcount; if( printflag ) { printf( "No. %d\n", totalcount ); PrintCurrentBoard( CharBoard ); } } /*----------------------------------------------------------------------*/ /* 次に試すべき場所を探す */ /*----------------------------------------------------------------------*/ int SearchNextBase( BitsForm board, int base ) { for( ; base> base ) & 1 ) == 0 ) break; } return base; } /*----------------------------------------------------------------------*/ /* 試行錯誤 (1レベル再帰的に) */ /*----------------------------------------------------------------------*/ void SearchOneLevel( int level, BitsForm board, int base ) { int i, j; int pn; /* ピース番号 */ char name; /* ピース名 */ int ss, nx; int nth; BitsForm prevboard; BitsForm *bitsp; AllBitsForm *pats; if( level >= PN ) { Found(); return; } base = SearchNextBase( board, base ); /* 次のベース位置 */ /* 速度向上のため 「I」は x軸と並行にしか置けない。 */ /* x=0 の平面を調べ終えたときに、既に使用済み。 */ if( (base >= DEPTH*HEIGHT) && RestPieces[1] ) return; for( pn = 0; pn < PN; ++pn ) { /* 次の可能なピースを探す */ if( ! RestPieces[pn] ) continue; name = AllPieceForm[pn].name; pats = &(AllPieceForm[pn].allbits[base]); bitsp = pats->bits; for( nth=0; nth < pats->n; ++nth, ++bitsp ) { /* 同じピースで、次の置き方を探す */ /* 重なる時は、次のピースを探す */ if( board & *bitsp ) continue; /* 置けることが分かったので、置いて先に進もう */ ++tryalcount; prevboard = board; board |= *bitsp; RestPieces[pn] = FALSE; for( j=0; joffsets[nth][j]] = name; if( stepflag ) /* ステップ印字 */ PrintCurrentBoard( CharBoard ); SearchOneLevel( level+1, board, base+1 ); /* 再帰 */ /* 戻って来たので、取り除いて以前の状態にする */ board = prevboard; RestPieces[pn] = TRUE; for( j=0; joffsets[nth][j]] = BLANKCELL; } } } /*----------------------------------------------------------------------*/ /* 試行錯誤 (全部調べ尽くす) */ /*----------------------------------------------------------------------*/ void SearchAll( void ) { int i; BitsForm board; totalcount = tryalcount = 0; /* 全ピースを未使用にする */ for( i=0; i 0 ) { ++argv; if( strcmp( *argv, "-p" ) == 0 ) { printflag |= 1; } if( strcmp( *argv, "-l" ) == 0 ) { printflag |= 2; } if( strcmp( *argv, "-s" ) == 0 ) { stepflag = TRUE; printflag |= 1; } if( strcmp( *argv, "-h" ) == 0 ) { printf( "\nusage: pentomino [-p][-l][-s][-h]\n" ); printf( "\tp : print by characters\n" ); printf( "\tl : print by lines\n" ); printf( "\ts : print each step\n" ); printf( "\th : help\n" ); exit(0); } } Initialize(); /* 初期化 */ SearchAll(); /* 調べ尽くす */ printf( "Total Count : %d\n", totalcount ); /* 総数 */ printf( "Tryal Count : %d\n", tryalcount ); /* 試行回数 */ } /************************************************************************/ /* End of File */ /************************************************************************/