コンテンツにスキップ

周囲のマスをチェックする小技

※for文のカウンタ di や dj を -1から始めて、-1、0、1 の値を作り出すところがミソです。

※実際に使用するには、チェックするマスが盤面外([ i + di ] や [ j + dj ] が範囲オーバー)だった場合などの対策を追加する必要があります。

1.利用シーン

将棋やオセロ、またはマインスイーパーなど、マスで区切った盤面を使ったゲームの場合は、あるマスの周囲のマスの状況をチェックしなければならないことがあります。その場合、左上、真上、右上・・と行番号・列番号を個別に指定してチェックしていくのでもできますが、上記の小技を使えばまとまったすっきりとしたプログラムにすることができます。

2.チェックの動き

下記の図のように自分は⑤の位置(field[ i ][ j ] )にいるとして、上記の小技で周囲をチェックする場合、①~⑨の順番にチェックしていきます。

Untitled

コード例:

自分がfield[ 1 ][ 1 ]にいる場合の周囲チェック

(上下左右の4方向のみのチェックの場合は、if ((di+dj)%2 != 0) { とします。)

int[][] field =
{{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int i = 1;
int j = 1;
println("周囲チェック");
for (int di = -1; di < 2; di++) {
for (int dj = -1; dj < 2; dj++) {
println (i+di, j+dj, "->", field[i+di][j+dj] );
}
}
println();
println("4方向のみチェック");
for (int di = -1; di < 2; di++) {
for (int dj = -1; dj < 2; dj++) {
if ((di+dj)%2 != 0) {
println (i+di, j+dj, "->", field[i+di][j+dj] );
}
}
}

3.小技を使った応用例

マインスイーパーで、各セルにおいて隣接する爆弾の数をカウントする部分のプログラムです。下記の例では、チェックする隣接マスが盤面内だった場合のみチェックするように対策を追加しています。

int h=3; //縦のマスの数。行数。(★ここを変えていろいろ試してみてください)
int w=3; //横のマスの数。列数。(★ここを変えていろいろ試してみてください)
int[][] cellValue; // -1:爆弾 その他:隣接する爆弾の数
void setup() {
size(500, 500);
textAlign(CENTER, CENTER);
textSize(40);
cellValue = new int[h][w];
//爆弾を設置(★ここを変えていろろ試してみてください)
cellValue[1][2]= -1;
cellValue[2][0]= -1;
//隣接爆弾数のカウント
for (int i = 0; i<h; i++) {
for (int j = 0; j<w; j++) {
if (cellValue[i][j] != -1) { //もしそのマスが爆弾(-1)ではなかったら、
//そのマスに隣接するマスの中身を順次チェック
for (int di = -1; di < 2; di++) {
for (int dj = -1; dj < 2; dj++) {
if ((i+ di >= 0) && (i+ di < h) && (j + dj >= 0) && (j + dj < w)) { //もしチェックしようとしている隣接セルが盤面内なら
if (cellValue[i+di][j+dj] == -1) { //もしその隣接マスが爆弾だったら、
cellValue[i][j]++; //cellValue[i][j]の値をカウントアップ
}
}
}
}
}
}
}
}
void draw() {
for (int i = 0; i<h; i++) {
for (int j = 0; j<w; j++) {
fill(255);
rect(j*50, i*50, 50, 50);
fill(0);
text(cellValue[i][j], j*50+25, i*50+25);
}
}
}

Untitled

※爆弾(-1)を[1][2]、[2][0] に設置した後、他のマスの値(隣接する爆弾の数)を計算しています。