二次元配列でマップ問題を解く

二次元配列でマップ問題を解く

次のような問題を考えます。

[問題]

長方形状のマップデータ(文字列)が与えられます。

1マスのデータは'#'が爆弾を表し、'.'は何もない空間(空きマス)を表します。

空きマスの上下左右および斜めの8方向に隣接しているマスの中に

爆弾のあるマスが何個あるかを調べて、その数を空きマス'.'に置き換えて

長方形状のマップを出力してください。

[条件]

・マップの縦幅と横幅は1以上かつ50以下です。

・1マスのデータは'#'(爆弾)または'.'(何もない空間)のみです。

解法・ソースコード

長方形のマップを表すために、str型の変数からなるlistを活用します。

長方形状のマップを上からy番目、左からx番目のマスを(x, y)と表します。

入力の文字列データを1マスずつの2次元配列(2次元リスト)に設定します。(14行目)

この問題では、各空きマス(x, y)に対して、その周囲8マスにある爆弾の個数を数えます。

マス(x, y)の周囲8マスは (x+1, y)、(x-1, y)、(x, y+1)、(x, y-1)、(x+1, y+1)、(x+1, y-1)、(x-1, y+1)、(x-1, y-1) と表すことができます。

これらのマスを調べるために、周囲8方向に隣接するマスとの座標の差分値を表す配列を用意しておくと便利です。(17行目)

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#---------- 入力例 ----------
# .が空間、#が爆弾
mp = '''........
..#...#.
........
....#...
.....#..'''
#----------------------------
print('【 元のマップ 】 ')
print(mp)
print()

# 入力データを二次元配列に設定
res = [list(line) for line in mp.split('\n')]

# 隣接8方向への相対位置を定義
dxy = [(1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (1, -1), (-1, 1), (-1, -1)]

for y, row in enumerate(res): # 縦ループ
for x, s in enumerate(row): # 横ループ
if s == '.': # 空きマスの場合
res[y][x] = 0 # 初期化(周囲の爆弾数)
for dx, dy in dxy: # 8方向をループ
x1, y1 = x + dx, y + dy # マスの周囲をx1,y1とする
# マップ外の場合はパス
if x1 < 0 or x1 > len(row) - 1 or y1 < 0 or y1 > len(res) - 1:
pass
elif res[y1][x1] == '#': # 周囲が爆弾'#'の場合
res[y][x] += 1 # 爆弾数カウントアップ

print('【 隣接する爆弾の個数を表示したマップ 】 ')
for row in res:
print(*row, sep='')

[実行結果]

【 元のマップ 】 
........
..#...#.
........
....#...
.....#..

【 隣接する爆弾の個数を表示したマップ 】 
01110111
01#101#1
01121211
0001#210
00012#10

空きマスの周辺にある爆弾数をカウントしてマップを表示することができました。