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