DEFINITIONS
This source file includes following functions.
1 #!/usr/local/bin/ruby
2 #----------------------> pretty simple othello game <-----------------------
3 # othello.rb
4 #
5 # version 0.3
6 # maeda shugo (shuto@po.aianet.ne.jp)
7 #---------------------------------------------------------------------------
8
9 # Sep. 17, 1997 modified by Y. Shigehiro for tcltk library
10 # maeda shugo (shugo@po.aianet.ne.jp) 氏による
11 # (ruby/tk で書かれていた) ruby のサンプルプログラム
12 # http:
13 # を tcltk ライブラリを使うように, 機械的に変更してみました.
14 #
15 # なるべくオリジナルと同じになるようにしてあります.
16
17 require "observer"
18 require "tcltk"
19 $ip = TclTkInterpreter.new()
20 $root = $ip.rootwidget()
21 $button, $canvas, $checkbutton, $frame, $label, $pack, $update, $wm =
22 $ip.commands().indexes(
23 "button", "canvas", "checkbutton", "frame", "label", "pack", "update", "wm")
24
25 class Othello
26
27 EMPTY = 0
28 BLACK = 1
29 WHITE = - BLACK
30
31 attr :in_com_turn
32 attr :game_over
33
34 class Board
35
36 include Observable
37
38 DIRECTIONS = [
39 [-1, -1], [-1, 0], [-1, 1],
40 [ 0, -1], [ 0, 1],
41 [ 1, -1], [ 1, 0], [ 1, 1]
42 ]
43
44 attr :com_disk, TRUE
45
46 def initialize(othello)
47 @othello = othello
48 reset
49 end
50
51 def notify_observers(*arg)
52 if @observer_peers != nil
53 super(*arg)
54 end
55 end
56
57 def reset
58 @data = [
59 [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
60 [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
61 [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
62 [EMPTY, EMPTY, EMPTY, WHITE, BLACK, EMPTY, EMPTY, EMPTY],
63 [EMPTY, EMPTY, EMPTY, BLACK, WHITE, EMPTY, EMPTY, EMPTY],
64 [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
65 [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
66 [EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY]
67 ]
68 changed
69 notify_observers
70 end
71
72 def man_disk
73 return - @com_disk
74 end
75
76 def other_disk(disk)
77 return - disk
78 end
79
80 def get_disk(row, col)
81 return @data[row][col]
82 end
83
84 def reverse_to(row, col, my_disk, dir_y, dir_x)
85 y = row
86 x = col
87 begin
88 y += dir_y
89 x += dir_x
90 if y < 0 || x < 0 || y > 7 || x > 7 ||
91 @data[y][x] == EMPTY
92 return
93 end
94 end until @data[y][x] == my_disk
95 begin
96 @data[y][x] = my_disk
97 changed
98 notify_observers(y, x)
99 y -= dir_y
100 x -= dir_x
101 end until y == row && x == col
102 end
103
104 def put_disk(row, col, disk)
105 @data[row][col] = disk
106 changed
107 notify_observers(row, col)
108 DIRECTIONS.each do |dir|
109 reverse_to(row, col, disk, *dir)
110 end
111 end
112
113 def count_disk(disk)
114 num = 0
115 @data.each do |rows|
116 rows.each do |d|
117 if d == disk
118 num += 1
119 end
120 end
121 end
122 return num
123 end
124
125 def count_point_to(row, col, my_disk, dir_y, dir_x)
126 return 0 if @data[row][col] != EMPTY
127 count = 0
128 loop do
129 row += dir_y
130 col += dir_x
131 break if row < 0 || col < 0 || row > 7 || col > 7
132 case @data[row][col]
133 when my_disk
134 return count
135 when other_disk(my_disk)
136 count += 1
137 when EMPTY
138 break
139 end
140 end
141 return 0
142 end
143
144 def count_point(row, col, my_disk)
145 count = 0
146 DIRECTIONS.each do |dir|
147 count += count_point_to(row, col, my_disk, *dir)
148 end
149 return count
150 end
151
152 def corner?(row, col)
153 return (row == 0 && col == 0) ||
154 (row == 0 && col == 7) ||
155 (row == 7 && col == 0) ||
156 (row == 7 && col == 7)
157 end
158
159 def search(my_disk)
160 max = 0
161 max_row = nil
162 max_col = nil
163 for row in 0 .. 7
164 for col in 0 .. 7
165 buf = count_point(row, col, my_disk)
166 if (corner?(row, col) && buf > 0) || max < buf
167 max = buf
168 max_row = row
169 max_col = col
170 end
171 end
172 end
173 return max_row, max_col
174 end
175 end #--------------------------> class Board ends here
176
177 class BoardView < TclTkWidget
178
179 BACK_GROUND_COLOR = "DarkGreen"
180 HILIT_BG_COLOR = "green"
181 BORDER_COLOR = "black"
182 BLACK_COLOR = "black"
183 WHITE_COLOR = "white"
184 STOP_COLOR = "red"
185
186 attr :left
187 attr :top
188 attr :right
189 attr :bottom
190
191 class Square
192
193 attr :oval, TRUE
194 attr :row
195 attr :col
196
197 def initialize(view, row, col)
198 @view = view
199 @id = @view.e("create rectangle", *view.tk_rect(view.left + col,
200 view.top + row,
201 view.left + col + 1,
202 view.top + row + 1))
203 @row = row
204 @col = col
205 @view.e("itemconfigure", @id,
206 "-width 0.5m -outline #{BORDER_COLOR}")
207 @view.e("bind", @id, "<Any-Enter>", TclTkCallback.new($ip, proc{
208 if @oval == nil
209 view.e("itemconfigure", @id, "-fill #{HILIT_BG_COLOR}")
210 end
211 }))
212 @view.e("bind", @id, "<Any-Leave>", TclTkCallback.new($ip, proc{
213 view.e("itemconfigure", @id, "-fill #{BACK_GROUND_COLOR}")
214 }))
215 @view.e("bind", @id, "<ButtonRelease-1>", TclTkCallback.new($ip,
216 proc{
217 view.click_square(self)
218 }))
219 end
220
221 def blink(color)
222 @view.e("itemconfigure", @id, "-fill #{color}")
223 $update.e()
224 sleep(0.1)
225 @view.e("itemconfigure", @id, "-fill #{BACK_GROUND_COLOR}")
226 end
227 end #-----------------------> class Square ends here
228
229 def initialize(othello, board)
230 super($ip, $root, $canvas)
231 @othello = othello
232 @board = board
233 @board.add_observer(self)
234
235 @squares = Array.new(8)
236 for i in 0 .. 7
237 @squares[i] = Array.new(8)
238 end
239 @left = 1
240 @top = 0.5
241 @right = @left + 8
242 @bottom = @top + 8
243
244 i = self.e("create rectangle", *tk_rect(@left, @top, @right, @bottom))
245 self.e("itemconfigure", i,
246 "-width 1m -outline #{BORDER_COLOR} -fill #{BACK_GROUND_COLOR}")
247
248 for row in 0 .. 7
249 for col in 0 .. 7
250 @squares[row][col] = Square.new(self, row, col)
251 end
252 end
253
254 update
255 end
256
257 def tk_rect(left, top, right, bottom)
258 return left.to_s + "c", top.to_s + "c",
259 right.to_s + "c", bottom.to_s + "c"
260 end
261
262 def clear
263 each_square do |square|
264 if square.oval != nil
265 self.e("delete", square.oval)
266 square.oval = nil
267 end
268 end
269 end
270
271 def draw_disk(row, col, disk)
272 if disk == EMPTY
273 if @squares[row][col].oval != nil
274 self.e("delete", @squares[row][col].oval)
275 @squares[row][col].oval = nil
276 end
277 return
278 end
279
280 $update.e()
281 sleep(0.05)
282 oval = @squares[row][col].oval
283 if oval == nil
284 oval = self.e("create oval", *tk_rect(@left + col + 0.2,
285 @top + row + 0.2,
286 @left + col + 0.8,
287 @top + row + 0.8))
288 @squares[row][col].oval = oval
289 end
290 case disk
291 when BLACK
292 color = BLACK_COLOR
293 when WHITE
294 color = WHITE_COLOR
295 else
296 fail format("Unknown disk type: %d", disk)
297 end
298 self.e("itemconfigure", oval, "-outline #{color} -fill #{color}")
299 end
300
301 def update(row = nil, col = nil)
302 if row && col
303 draw_disk(row, col, @board.get_disk(row, col))
304 else
305 each_square do |square|
306 draw_disk(square.row, square.col,
307 @board.get_disk(square.row, square.col))
308 end
309 end
310 @othello.show_point
311 end
312
313 def each_square
314 @squares.each do |rows|
315 rows.each do |square|
316 yield(square)
317 end
318 end
319 end
320
321 def click_square(square)
322 if @othello.in_com_turn || @othello.game_over ||
323 @board.count_point(square.row,
324 square.col,
325 @board.man_disk) == 0
326 square.blink(STOP_COLOR)
327 return
328 end
329 @board.put_disk(square.row, square.col, @board.man_disk)
330 @othello.com_turn
331 end
332
333 private :draw_disk
334 public :update
335 end #----------------------> class BoardView ends here
336
337 def initialize
338 @msg_label = TclTkWidget.new($ip, $root, $label)
339 $pack.e(@msg_label)
340
341 @board = Board.new(self)
342 @board_view = BoardView.new(self, @board)
343 #### added by Y. Shigehiro
344 ## board_view の大きさを設定する.
345 x1, y1, x2, y2 = @board_view.e("bbox all").split(/ /).collect{|i| i.to_f}
346 @board_view.e("configure -width", x2 - x1)
347 @board_view.e("configure -height", y2 - y1)
348 ## scrollregion を設定する.
349 @board_view.e("configure -scrollregion {", @board_view.e("bbox all"),
350 "}")
351 #### ここまで
352 $pack.e(@board_view, "-fill both -expand true")
353
354 panel = TclTkWidget.new($ip, $root, $frame)
355
356 @play_black = TclTkWidget.new($ip, panel, $checkbutton,
357 "-text {com is black} -command", TclTkCallback.new($ip, proc{
358 switch_side
359 }))
360 $pack.e(@play_black, "-side left")
361
362 quit = TclTkWidget.new($ip, panel, $button, "-text Quit -command",
363 TclTkCallback.new($ip, proc{
364 exit
365 }))
366 $pack.e(quit, "-side right -fill x")
367
368 reset = TclTkWidget.new($ip, panel, $button, "-text Reset -command",
369 TclTkCallback.new($ip, proc{
370 reset_game
371 }))
372 $pack.e(reset, "-side right -fill x")
373
374 $pack.e(panel, "-side bottom -fill x")
375
376 # root = Tk.root
377 $wm.e("title", $root, "Othello")
378 $wm.e("iconname", $root, "Othello")
379
380 @board.com_disk = WHITE
381 @game_over = FALSE
382
383 TclTk.mainloop
384 end
385
386 def switch_side
387 if @in_com_turn
388 @play_black.e("toggle")
389 else
390 @board.com_disk = @board.man_disk
391 com_turn unless @game_over
392 end
393 end
394
395 def reset_game
396 if @board.com_disk == BLACK
397 @board.com_disk = WHITE
398 @play_black.e("toggle")
399 end
400 @board_view.clear
401 @board.reset
402 $wm.e("title", $root, "Othello")
403 @game_over = FALSE
404 end
405
406 def com_turn
407 @in_com_turn = TRUE
408 $update.e()
409 sleep(0.5)
410 begin
411 com_disk = @board.count_disk(@board.com_disk)
412 man_disk = @board.count_disk(@board.man_disk)
413 if @board.count_disk(EMPTY) == 0
414 if man_disk == com_disk
415 $wm.e("title", $root, "{Othello - Draw!}")
416 elsif man_disk > com_disk
417 $wm.e("title", $root, "{Othello - You Win!}")
418 else
419 $wm.e("title", $root, "{Othello - You Loose!}")
420 end
421 @game_over = TRUE
422 break
423 elsif com_disk == 0
424 $wm.e("title", $root, "{Othello - You Win!}")
425 @game_over = TRUE
426 break
427 elsif man_disk == 0
428 $wm.e("title", $root, "{Othello - You Loose!}")
429 @game_over = TRUE
430 break
431 end
432 row, col = @board.search(@board.com_disk)
433 break if row == nil || col == nil
434 @board.put_disk(row, col, @board.com_disk)
435 end while @board.search(@board.man_disk) == [nil, nil]
436 @in_com_turn = FALSE
437 end
438
439 def show_point
440 black = @board.count_disk(BLACK)
441 white = @board.count_disk(WHITE)
442 @msg_label.e("configure -text",
443 %Q/{#{format("BLACK: %.2d WHITE: %.2d", black, white)}}/)
444 end
445 end #----------------------> class Othello ends here
446
447 Othello.new
448
449 #----------------------------------------------> othello.rb ends here