LoginSignup
13
12

More than 5 years have passed since last update.

Rubyでマインスイーパー

Last updated at Posted at 2014-10-31

はじめに

Swiftと同時にRubyを学び始めてはや2週間
何か作ってみたいと思い浮かんだのがマインスイーパーであった
各関数の解説も書いてみたいと思う。

環境

OS X 10.9.5
ruby 2.0.0p481
ターミナル上で動作
Winの動作は確認していません

ソース

イメージ

minesweeper.gif

解説

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から見ることになるので改善すべき点がよくわかる
今回のコードもかなりの改善点が見つかったので直していきたい

13
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
12