酒本です。

漢字、カラー対応の端末でないと遊べませんが、
マインスイーパのキャラクタ版です。
モデルとビューも使っていませんし、クラスにする
必要もないかもしれません。
でも、作るの楽しかったです。
息抜きにどうぞ。
おきて破りのエスケープシーケンスを使っています。

移動      :h j k l
マーク    :m
newゲーム :n
終了      :q
開ける     : スペース

#!/usr/local/bin/ruby
def clr
    "\e[2J"
end

def pos(x,y)
  "\e["+(y+1)+";"+(x*2+1)+"H"
end

def clrstr(id,s)
  "\e["+id+"m"+s+"\e[0m"
end

class Board
  def initialize(h,w,m)
    @num=["・","1","2","3","4","5","6","7","8"]
    @hi=h; @wi=w; @m=m
    reset
  end

  def reset
    srand()
    @cx=0; @cy=0;  @mc=@m
    @data=Array.new(@hi*@wi)
    @state=Array.new(@hi*@wi)
    @total=@hi*@wi
    0.upto(@hi*@wi-1){|i| @data[i]=0; @state[i]="NULL"; }
    @m.times{
       while true
         j=rand(@hi*@wi-1)
         if @data[j] == 0 then
           @data[j]=1
           break
         end
       end
    }
    print clr , pos(0,0)
    0.upto(@hi-1){|y| print pos(0,y),clrstr(46,"・"*@wi)}
    print pos(@cx,@cy)
  end
  def mark
    if @state[@wi*@cy+@cx] !="NULL" then return end
    print pos(@cx,@cy),clrstr(43,"★")
    @state[@wi*@cy+@cx] = "MARK"
    @mc=@mc-1;
    @total=@total-1;
    move
  end
  def open(x=@cx,y=@cy)
    if @state[@wi*y+x] =="OPEN"  then return 0 end
    if @state[@wi*y+x]=="NULL" then @total=@total-1 end
    if @state[@wi*y+x]=="MARK" then @mc=@mc+1 end
    @state[@wi*y+x]="OPEN"
    if fetch(x,y) == 1 then return 1 end
    c= count(x,y)
    print pos(x,y),clrstr(43,@num[c])
    if c != 0 then move; return 0 end
    if x > 0 && y > 0 then open(x-1,y-1) end
    if y > 0          then open(x,  y-1) end
    if x < @wi-1 && y > 0   then open(x+1,y-1) end
    if x > 0                then open(x-1,y) end
    if x < @wi-1            then open(x+1,y) end
    if x > 0 && y < @hi-1   then open(x-1,y+1) end
    if y < @hi -1           then open(x,y+1) end
    if x < @wi -1 && y < @hi -1 then open(x+1,y+1) end
    move
  end
  def count(x,y)
    fetch(x-1,y-1)+fetch(x,y-1)+fetch(x+1,y-1)+
    fetch(x-1,  y)+fetch(x+1,y)+
    fetch(x-1,y+1)+fetch(x,y+1)+fetch(x+1,y+1)
  end
  def fetch(x,y)
    if x < 0 then 0
    elsif x >= @wi then 0
    elsif y < 0 then 0
    elsif y >= @hi then 0
    else
      @data[x*@wi+y]
    end
  end
  def over(e)
    0.upto(@hi-1){ |y|
      print pos(0,y)
      0.upto(@wi-1){ |x|
        st=@state[y*@wi+x]
        if (@cx==x) &&( @cy==y) && e==1 then
          print "@@"
        elsif st != "MARK" then
          if fetch(x,y)==1 then
            m= "●"
          else
            m= @num[count(x,y)]
          end
          print clrstr(45,m)
        else
          if fetch(x,y)==1 then
            m= "●"
          else
            m= @num[count(x,y)]
          end
          print clrstr(43,m)
        end
      }
    }
    if e==1 then print pos(0,@hi),"*** OVER ***"
    else print pos(0,@hi),"*** YOU WIN !! ***"
    end
  end
  def down
    if @cy < @hi-1 then @cy=@cy+1; move end
  end
  def up
    if @cy > 0 then @cy=@cy-1; move end
  end
  def left
    if @cx > 0 then @cx=@cx-1; move end
  end
  def right
    if @cx < @wi-1 then @cx=@cx+1; move end
  end
  def move
     print pos(0,@hi),"残り:",@mc,"/",@total,"   "
     print pos(@cx,@cy)
  end
  def quit
    print pos(0,@hi)
  end
  def remain
    @mc+@total
  end
end

bd=Board.new(10,10,10)

system("stty raw -echo")
while(c=getc).chr != "q"
  case c.chr
  when "n"  # new game
    bd.reset
  when "m"  # mark
    bd.mark
  when "j"
    bd.down
  when "k"
    bd.up
  when "h"
    bd.left
  when "l"
    bd.right
  when " "
    if bd.open == 1 then bd.over(1)
      if (c=getc).chr == "q" then break end
      bd.reset
    end
  end
  if bd.remain==0 then
    bd.over(0)
    if (c=getc).chr == "q" then break end
    bd.reset
  end
end
bd.quit
system("stty -raw echo")
print "\n"
--
酒本 定幸