ブロックが落下した際にそのブロックの配列からx--とx++、y--とy++方向に同じ色があるか
もし限界領域内であり、y--方向にあった場合は探索位置をy方向に一つずらし、同じ方法で探索。
繰り返して探索結果が同じ色が4つ以上存在した場合、ブロックを削除し、-y軸にあるブロックを消去分だけ下げる
という方法が私の思いつきの限界なのですが、それでは上と下や上と右、各上下左右に同じ色が存在した場合に正しい結果が得られない気がします。
何か良いアルゴリズムなどございますでしょうか

void CheckBlock( bl_t bl[Y][X], //ブロックの情報 int x, //座標 int y, int *cnt //数えた個数 ){ int col; (*cnt)++; // カウントをインクリメントする col = bl[y][x].col; // 色情報を一時的に保存する bl[y][x].Checked = 1; // 検査済みの箇所に印を付ける // 上下左右方向で隣接している同色ぷよの数をカウントする if (一番上ではない && 未検査 && bl[y-1][x].col == col) CheckBlock(bl, x, y-1, cnt); if (一番下ではない && 未検査 && bl[y+1][x].col == col) CheckBlock(bl, x, y+1, cnt); if (一番左ではない && 未検査 && bl[y][x-1].col == col) CheckBlock(bl, x-1, y, cnt); if (一番右ではない && 未検査 && bl[y][x+1].col == col) CheckBlock(bl, x+1, y, cnt); }
実際にはちょっと違いますが、概ねこんな感じです。 第一引数のfdはフィールドを表す2次元配列です。 FIELD_X、FIELD_Y はそれぞれフィールドの幅と高さです。 6と13ですね。 第2引数x、第3引数yを基点とし上下左右を調べて同じ色なら再帰します。 戻り値は消されるぷよの数です。 addErasePuyo() 関数は一時的な消去バッファへ消す予定のぷよを放り込みます。 途中で現れる5という数値は、おじゃまぷよを表すぷよの番号です。 参考までに。 int eraseLoop(unsigned char fd[FIELD_X][FIELD_Y], int x, int y, int cnt) { unsigned char puyo = fd[x][y]; addErasePuyo(x, y, puyo); cnt++; if(x > 0){ if(fdd[x - 1][y] == puyo) cnt = eraseLoop(fd, x - 1, y, cnt); else if(fd[x - 1][y] == 5) addErasePuyo(x - 1, y, 5); } if(x < FIELD_X - 1){ if(fd[x + 1][y] == puyo) cnt = eraseLoop(fd, x + 1, y, cnt); else if(fd[x + 1][y] == 5) addErasePuyo(x + 1, y, 5); } if(y > 0){ if(fd[x][y - 1] == puyo) cnt = eraseLoop(fd, x, y - 1, cnt); else if(fd[x][y - 1] == 5) addErasePuyo(x, y - 1, 5); } if(y < FIELD_Y - 1){ if(fd[x][y + 1] == puyo) cnt = eraseLoop(fd, x, y + 1, cnt); else if(fd[x][y + 1] == 5) addErasePuyo(x, y + 1, 5); } return cnt; }
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define WIDTH 6 // フィールド幅 #define HEIGHT 16 // フィールド高 #define VANISH 4 // 消去に必要な個数 #define COLOR 3 // 色数 // ブロック管理用構造体 struct BLOCK { int nColor; // 色 int nVanish; // 消去フラグ }; // フィールド管理用構造体 struct FIELD { struct BLOCK stBlock[WIDTH][HEIGHT]; // ブロック構造体配列 }; // 関数のプロトタイプ宣言 void ShowField(struct FIELD* pField); int Check(struct FIELD* pField); void Vanish(struct FIELD* pField); int Slide(struct FIELD* pField); void Count(struct FIELD* pField, int nX, int nY, int* pCount); // メイン関数 int main(void) { int nX = 0; int nY = 0; int nChain = 0; struct FIELD stField; // 乱数の種を仕込む srand((unsigned)time(NULL)); // フィールドへ適当な色のブロックを仕込む for (nY = 0; nY < HEIGHT; ++nY) { for (nX = 0; nX < WIDTH; ++nX) { stField.stBlock[nX][nY].nColor = rand() % COLOR + 1; stField.stBlock[nX][nY].nVanish = 0; } } // 初期状態の描画 printf("初期状態\n"); ShowField(&stField); getchar(); // 消去箇所が存在する間はループを続ける while (Check(&stField) != 0) { // 消去チェック後の状態を描画 printf("%2d連鎖\n", ++nChain); printf("チェック後\n"); ShowField(&stField); getchar(); // 消去処理後の状態を描画 printf("消去後\n"); Vanish(&stField); ShowField(&stField); getchar(); // 空白を詰める為に1段ずつ落下させる while (Slide(&stField) != 0) { printf("1段落下後\n"); ShowField(&stField); getchar(); } } printf("%2d連鎖で終了しました\n", nChain); return 0; } // フィールドを描画 void ShowField(struct FIELD* pField) { int nX = 0; int nY = 0; for (nY = 0; nY < HEIGHT; ++nY) { for (nX = 0; nX < WIDTH; ++nX) { // 色と消去フラグを表示する printf("[%2d,%2d] ", pField->stBlock[nX][nY].nColor, pField->stBlock[nX][nY].nVanish); } putc('\n', stdout); } putc('\n', stdout); } // 全ブロックの同色で隣接している個数をチェックする int Check(struct FIELD* pField) { int nX = 0; int nY = 0; int nReturn = 0; struct FIELD stDummy; for (nX = 0; nX < WIDTH; ++nX) { for (nY = 0; nY < HEIGHT; ++nY) { // 空白=0としているので、0の場合は何もしない if(pField->stBlock[nX][nY].nColor == 0) { continue; } // 消去フラグをクリアしておく pField->stBlock[nX][nY].nVanish = 0; // フィールド構造体をコピーして、検査用の構造体領域を作成する memcpy(&stDummy, pField, sizeof(struct FIELD)); // 同色で隣接している個数をカウントする Count(&stDummy, nX, nY, &pField->stBlock[nX][nY].nVanish); // 同色で隣接している個数がVANISH定数を超えていたら、戻り値を1にする nReturn |=(pField->stBlock[nX][nY].nVanish >= VANISH); } } // 1箇所でも消去される箇所があれば1、なければ0が返る return nReturn; } // VANISH定数よりたくさん同色で隣接していたら消去する void Vanish(struct FIELD* pField) { int nX = 0; int nY = 0; for (nX = 0; nX < WIDTH; ++nX) { for (nY = 0; nY < HEIGHT; ++nY) { // 同色での隣接数がVANISH定数以上か? if(pField->stBlock[nX][nY].nVanish >= VANISH) { // ブロック情報をクリアする memset(&pField->stBlock[nX][nY], 0, sizeof(struct BLOCK)); } } } } // 消去されて空いた隙間を1段だけ詰める int Slide(struct FIELD* pField) { int nX = 0; int nY = 0; int nReturn = 0; for (nY = HEIGHT - 1; nY >= 1; --nY) { for (nX = 0; nX < WIDTH; ++nX) { // 検査箇所が空白で、1つ上が空白ではない場合 if(pField->stBlock[nX][nY].nColor == 0 && pField->stBlock[nX][nY - 1].nColor != 0) { // 検査箇所へ1つ上のブロック情報をコピーする memcpy(&pField->stBlock[nX][nY], &pField->stBlock[nX][nY - 1], sizeof(struct BLOCK)); // 1つ上のブロック情報をクリアする memset(&pField->stBlock[nX][nY - 1], 0, sizeof(struct BLOCK)); // 動いた箇所があったので、戻り値を1にする nReturn = 1; } } } // 動いた箇所があった場合は1、そうでない場合は0が返る return nReturn; } // 同色で隣接している個数を数える(再帰関数) void Count(struct FIELD* pField, int nX, int nY, int* pCount) { // 検査箇所の色を保持する int nColor = pField->stBlock[nX][nY].nColor; // ダブって検査しないように、検査した箇所のブロック情報をクリアしておく memset(&pField->stBlock[nX][nY], 0, sizeof(struct BLOCK)); // カウンタをインクリメントする ++(*pCount); // 1つ上のブロックを検査 if(nY - 1 >= 0 && nColor == pField->stBlock[nX][nY - 1].nColor) { Count(pField, nX, nY - 1, pCount); } // 1つ下のブロックを検査 if(nY + 1 < HEIGHT && nColor == pField->stBlock[nX][nY + 1].nColor) { Count(pField, nX, nY + 1, pCount); } // 1つ左のブロックを検査 if(nX - 1 >= 0 && nColor == pField->stBlock[nX - 1][nY].nColor) { Count(pField, nX - 1, nY, pCount); } // 1つ右のブロックを検査 if(nX + 1 < WIDTH && nColor == pField->stBlock[nX + 1][nY].nColor) { Count(pField, nX + 1, nY, pCount); } }
int Check(struct FIELD* pField) { int nX = 0; int nY = 0; int nCount = 0; int nReturn = 0; // 消去フラグをクリア for (nX = 0; nX < WIDTH; ++nX) { for (nY = 0; nY < HEIGHT; ++nY) { pField->stBlock[nX][nY].nVanish = 0; } } for (nX = 0; nX < WIDTH; ++nX) { for (nY = 0; nY < HEIGHT; ++nY) { // 空白=0としているので、0の場合は何もしない if(pField->stBlock[nX][nY].nColor == 0) { continue; } // 同色で並んでいる個数をカウントする // 上方向 nCount = 0; Count(pField, nX, nY, 0, -1, &nCount); // 右上方向 nCount = 0; Count(pField, nX, nY, 1, -1, &nCount); // 右方向 nCount = 0; Count(pField, nX, nY, 1, 0, &nCount); // 右下方向 nCount = 0; Count(pField, nX, nY, 1, 1, &nCount); // 同色で並んでいる個数がVANISH定数を超えていたら、戻り値を1にする nReturn |= (pField->stBlock[nX][nY].nVanish >= VANISH); } } // 1箇所でも消去される箇所があれば1、なければ0が返る return nReturn; } void Count(struct FIELD* pField, int nPosX, int nPosY, int nMoveX, int nMoveY, int* pCount) { // 次の検査箇所の座標を求める int nNextX = nPosX + nMoveX; int nNextY = nPosY + nMoveY; // カウンタをインクリメントする ++(*pCount); // 次の検査箇所へ if (nNextX >= 0 && nNextX < WIDTH && nNextY >= 0 && nNextY < HEIGHT && pField->stBlock[nPosX][nPosY].nColor == pField->stBlock[nNextX][nNextY].nColor) { Count(pField, nNextX, nNextY, nMoveX, nMoveY, pCount); } // 同色で並んでいる個数がVANISH定数以上ならば消去フラグをVANISHにする if (*pCount >= VANISH) { pField->stBlock[nPosX][nPosY].nVanish = VANISH; } }