sample/mine.rb


DEFINITIONS

This source file includes following functions.


   1  #! /usr/bin/ruby -Ke
   2  
   3  class Board
   4    def clr
   5      print "\e[2J"
   6    end
   7    def pos(x,y)
   8      printf "\e[%d;%dH", y+1, x*2+1
   9    end
  10    def colorstr(id,s)
  11      printf "\e[%dm%s\e[0m", id, s
  12    end
  13    def put(x, y, col, str)
  14      pos(x,y); colorstr(43,str)
  15      pos(0,@hi); print "残り:",@mc,"/",@total,"   "
  16      pos(x,y)
  17    end
  18    private :clr, :pos, :colorstr, :put
  19    CHR=["・","1","2","3","4","5","6","7","8","★","●","@@"]
  20    COL=[46,43,45] # default,opened,over
  21    def initialize(h,w,m)
  22      # ゲーム盤の生成(h:縦,w:横,m:爆弾の数)
  23      @hi=h; @wi=w; @m=m
  24      reset
  25    end
  26    def reset
  27      # ゲーム盤を(再)初期化する
  28      srand()
  29      @cx=0; @cy=0; @mc=@m
  30      @over=false
  31      @data=Array.new(@hi*@wi)
  32      @state=Array.new(@hi*@wi)
  33      @total=@hi*@wi
  34      @total.times {|i| @data[i]=0}
  35      @m.times do
  36         loop do
  37           j=rand(@total-1)
  38           if @data[j] == 0 then
  39             @data[j]=1
  40             break
  41           end
  42         end
  43      end
  44      clr; pos(0,0)
  45      @hi.times{|y| pos(0,y); colorstr(COL[0],CHR[0]*@wi)}
  46      pos(@cx,@cy)
  47    end
  48    def mark
  49      # 現在のカーソル位置にマークをつける
  50      if @state[@wi*@cy+@cx] != nil then return end
  51      @state[@wi*@cy+@cx] = "MARK"
  52      @mc=@mc-1;
  53      @total=@total-1;
  54      put(@cx, @cy, COL[1], CHR[9])
  55    end
  56    def open(x=@cx,y=@cy)
  57      # 現在のカーソル位置をオープンにする
  58      # 爆弾があればゲームオーバー
  59      if @state[@wi*y+x] =="OPEN"  then return 0 end
  60      if @state[@wi*y+x] == nil then @total=@total-1 end
  61      if @state[@wi*y+x] =="MARK" then @mc=@mc+1 end
  62      @state[@wi*y+x]="OPEN"
  63      if fetch(x,y) == 1 then @over = 1; return end
  64      c = count(x,y)
  65      put(x, y, COL[1], CHR[c])
  66      return 0 if c != 0
  67      if x > 0 && y > 0         then open(x-1,y-1) end
  68      if y > 0                  then open(x,  y-1) end
  69      if x < @wi-1 && y > 0     then open(x+1,y-1) end
  70      if x > 0                  then open(x-1,y) end
  71      if x < @wi-1              then open(x+1,y) end
  72      if x > 0 && y < @hi-1     then open(x-1,y+1) end
  73      if y < @hi -1             then open(x,y+1) end
  74      if x < @wi-1 && y < @hi-1 then open(x+1,y+1) end
  75      pos(@cx,@cy)
  76    end
  77    def fetch(x,y)
  78      # (x,y)の位置の爆弾の数(0 or 1)を返す
  79      if x < 0 then 0
  80      elsif x >= @wi then 0
  81      elsif y < 0 then 0
  82      elsif y >= @hi then 0
  83      else
  84        @data[y*@wi+x]
  85      end
  86    end
  87    def count(x,y)
  88      # (x,y)に隣接する爆弾の数を返す
  89      fetch(x-1,y-1)+fetch(x,y-1)+fetch(x+1,y-1)+
  90      fetch(x-1,y)  +             fetch(x+1,y)+
  91      fetch(x-1,y+1)+fetch(x,y+1)+fetch(x+1,y+1)
  92    end
  93    def over(win)
  94      # ゲームの終了
  95      quit
  96      unless win
  97        pos(@cx,@cy); print CHR[11]
  98      end
  99      pos(0,@hi)
 100      if win then print "*** YOU WIN !! ***"
 101      else print "*** GAME OVER ***"
 102      end
 103    end
 104    def over?
 105      # ゲームの終了チェック
 106      # 終了処理も呼び出す
 107      remain = (@mc+@total == 0)
 108      if @over || remain
 109        over(remain)
 110        true
 111      else
 112        false
 113      end
 114    end
 115    def quit
 116      # ゲームの中断(または終了)
 117      # 盤面を全て見せる
 118      @hi.times do|y|
 119        pos(0,y)
 120        @wi.times do|x|
 121          colorstr(if @state[y*@wi+x] == "MARK" then COL[1] else COL[2] end,
 122                   if fetch(x,y)==1 then CHR[10] else CHR[count(x,y)] end)
 123        end
 124      end
 125    end
 126    def down
 127      # カーソルを下に
 128      if @cy < @hi-1 then @cy=@cy+1; pos(@cx, @cy) end
 129    end
 130    def up
 131      # カーソルを上に
 132      if @cy > 0 then @cy=@cy-1; pos(@cx, @cy) end
 133    end
 134    def left
 135      # カーソルを左に
 136      if @cx > 0 then @cx=@cx-1; pos(@cx, @cy) end
 137    end
 138    def right
 139      # カーソルを右に
 140      if @cx < @wi-1 then @cx=@cx+1; pos(@cx, @cy) end
 141    end
 142  end
 143  
 144  bd=Board.new(10,10,10)
 145  system("stty raw -echo")
 146  begin
 147    loop do
 148      case STDIN.getc
 149      when ?n  # new game
 150        bd.reset
 151      when ?m  # mark
 152        bd.mark
 153      when ?j
 154        bd.down
 155      when ?k
 156        bd.up
 157      when ?h
 158        bd.left
 159      when ?l
 160        bd.right
 161      when ?\s
 162        bd.open
 163      when ?q,?\C-c  # quit game
 164        bd.quit
 165        break
 166      end
 167      if bd.over?
 168        if STDIN.getc == ?q then break end
 169        bd.reset
 170      end
 171    end
 172  ensure
 173    system("stty -raw echo")
 174  end
 175  print "\n"