#はじめに
Swiftと同時にRubyを学び始めてはや2週間
何か作ってみたいと思い浮かんだのがマインスイーパーであった
各関数の解説も書いてみたいと思う。
##環境
OS X 10.9.5
ruby 2.0.0p481
ターミナル上で動作
Winの動作は確認していません
#ソース
#解説
def initialize
@board = Array.new(SIZE+1){Array.new(SIZE+1,0)}
@board_flag = Array.new(SIZE+1){Array.new(SIZE+1,0)}
地雷とその個数を示す数字を表示する@boardと
■で覆い隠すようにする@board_flagを作成する
それぞれ0を初期値とするSIZE分の長さを持つ2次元配列となる
@board_flag = 0 そのマスは開けられていない
@board_flag = 1 そのマスは開けられた
def showBoard
print " "
for i in 1..SIZE - 1
print " #{i}"
end
print ("\n")
for i in 1..SIZE - 1
print "#{i} "
for j in 1..SIZE - 1
if @board_flag[j][i] == 1
case @board[j][i]
when 0
print(" □")
when -1
print(" *")
when 1..8
print(" #{@board[j][i]}")
end
else
print(" ■")
end
end
print ("\n")
end
番兵を除くすべてのボードを探索する
@board_flagが1のとき(開けられている)その位置の@boardがどんな状態であるか表示する
0 :開いている
-1 :地雷である
1~8:そのマスの周りの地雷の数
checkAround
for i in 1..SIZE - 1
for j in 1..SIZE - 1
count = 0
if @board[j][i] == -1
next
else
for k in -1..1
for l in -1..1
if @board[l+j][k+i] == -1
count += 1
end
end
end
@board[j][i] = count
end
end
end
@boardを全探索をしマスの周囲に-1(地雷)がどれだけ存在しているかを調べる関数
また自身が地雷のときは次のマスに移動する
putMine(x,y)
@board.each{|b_num|
b_num.each{|b_n|
if b_n == -1
return
end
}
}
n = 0
while(n < MINE)
i = rand(SIZE-1) + 1
j = rand(SIZE-1) + 1
flag = 0
for k in -1..1
for l in -1..1
if (i == x+k) && (j == y+l)
flag = 1
end
end
end
if flag == 1
redo
end
if @board[j][i] == -1
redo
end
@board[j][i] = -1
n += 1
end
checkAround
引数x,yはユーザが指定した(xy)座標である
なぜか全探索をするときここだけeachメソッドを使っている、自分が書いたにもかかわらず謎である
これは地雷がおかれていたらこの関数処理をしないという理由で書かれたが今となって思えばinitializeにこの関数とcheckAround関数ぶち込んでおけば必要ないのではと思っている。
このボード全体にランダムに地雷を指定された個数だけ設置するというのだが
1:地雷をおくときに既に地雷があったら避ける
2:最初にあけた場所の周りに地雷がないようにする
の1,2を満たすように書いている
openBoard(x,y)
if x < 1 || x > SIZE - 1 || y < 1 || y > SIZE - 1
return
end
putMine(x,y)
if @board[x][y] == 0
for i in -1..1
for j in -1..1
if @board_flag[j+x][i+y] == 1
next
end
case @board[j+x][i+y]
when 0
@board_flag[j+x][i+y] = 1
openBoard(j+x,i+y)
when 1..8
@board_flag[j+x][i+y] = 1
end
end
end
else
@board_flag[x][y] = 1
return
end
return
引数x,yはユーザが指定した(xy)座標である
ここではユーザが指定した場所を実際に開けるところである
再帰関数として利用
実際に開けた場所が0(空白)である場合その周りの状態も調べ0(空白)であった場合その場所を引数として再帰する、しかしそうでない場合1~8(周りに地雷があるマス)そこを開け再帰をやめる
こうすることによって隣接する0(空白)はすべて開けられることとなる
また0(空白)意外の場合はその場所をあけreturnする
choseNumber
while 1
p "Choose number (xy)[1 to 9]"
# number check
num = gets.chop
if num[1] == nil
p "Entry (xy)"
redo
end
nx = num[0].to_i
ny = num[1].to_i
if !(nx > 0 && ny > 0 && nx < SIZE && ny < SIZE)
redo
end
self.openBoard(nx,ny)
if self.gameOver(nx,ny)
return true
end
break
end
return false
ユーザが実際に数値(xy)を入力する関数
numにユーザが入力した値が代入されそのリストの一番目と二番目の値をnx,nyに代入しIntengerに変換
その値がゲームのマスより小さかったり大きかったり変な値であったら再入力させる
そのx,y情報を引数としopenBoard関数を呼ぶ
またgameOver関数もよび出す
returnはtrueとfalseを返すが
trueであればゲーム終了
falseであればゲーム続行である
gameOver(x,y)
count = 0
for i in 1..SIZE - 1
for j in 1..SIZE - 1
if @board_flag[j][i] == 0
count += 1
end
end
end
if count == MINE
self.showBoard
p "Clear!"
return true
end
if @board[x][y] == -1
@board_flag[x][y] = 1
self.showBoard
p "Failed.."
return true
end
return false
引数x,yはユーザが指定した(xy)座標である
ゲームオーバか否か調べる関数である
とりあえず全体の開かれていない場所の個数を探索しcountに代入
もしcountが地雷の個数と一緒(地雷以外はすべて開かれた)ならクリア
また開かれた場所が地雷だったらそれは失敗となりゲームオーバとなる
#まとめ#
自分で作ったものを解説しようとするとうまい言葉がなかなか見つからず結構難しい、なかなかいい練習になったと思う
また再びコードを1から見ることになるので改善すべき点がよくわかる
今回のコードもかなりの改善点が見つかったので直していきたい