mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
c09cea5e1b
attr_reader. [RCR#331] * ruby.h: export classes/modules to implement sandbox. [ruby-core:08283] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@10577 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
451 lines
13 KiB
Ruby
451 lines
13 KiB
Ruby
#!/usr/local/bin/ruby
|
|
#----------------------> pretty simple othello game <-----------------------
|
|
# othello.rb
|
|
#
|
|
# version 0.3
|
|
# maeda shugo (shuto@po.aianet.ne.jp)
|
|
#---------------------------------------------------------------------------
|
|
|
|
# Sep. 17, 1997 modified by Y. Shigehiro for tcltk library
|
|
# maeda shugo (shugo@po.aianet.ne.jp) 氏による
|
|
# (ruby/tk で書かれていた) ruby のサンプルプログラム
|
|
# http://www.aianet.or.jp/~shugo/ruby/othello.rb.gz
|
|
# を tcltk ライブラリを使うように, 機械的に変更してみました.
|
|
#
|
|
# なるべくオリジナルと同じになるようにしてあります.
|
|
|
|
require "observer"
|
|
require "tcltk"
|
|
$ip = TclTkInterpreter.new()
|
|
$root = $ip.rootwidget()
|
|
$button, $canvas, $checkbutton, $frame, $label, $pack, $update, $wm =
|
|
$ip.commands().values_at(
|
|
"button", "canvas", "checkbutton", "frame", "label", "pack", "update", "wm")
|
|
|
|
class Othello
|
|
|
|
EMPTY = 0
|
|
BLACK = 1
|
|
WHITE = - BLACK
|
|
|
|
attr :in_com_turn
|
|
attr :game_over
|
|
|
|
class Board
|
|
|
|
include Observable
|
|
|
|
DIRECTIONS = [
|
|
[-1, -1], [-1, 0], [-1, 1],
|
|
[ 0, -1], [ 0, 1],
|
|
[ 1, -1], [ 1, 0], [ 1, 1]
|
|
]
|
|
|
|
attr_accessor :com_disk
|
|
|
|
def initialize(othello)
|
|
@othello = othello
|
|
reset
|
|
end
|
|
|
|
def notify_observers(*arg)
|
|
if @observer_peers != nil
|
|
super(*arg)
|
|
end
|
|
end
|
|
|
|
def reset
|
|
@data = [
|
|
[EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
|
|
[EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
|
|
[EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
|
|
[EMPTY, EMPTY, EMPTY, WHITE, BLACK, EMPTY, EMPTY, EMPTY],
|
|
[EMPTY, EMPTY, EMPTY, BLACK, WHITE, EMPTY, EMPTY, EMPTY],
|
|
[EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
|
|
[EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY],
|
|
[EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY]
|
|
]
|
|
changed
|
|
notify_observers
|
|
end
|
|
|
|
def man_disk
|
|
return - @com_disk
|
|
end
|
|
|
|
def other_disk(disk)
|
|
return - disk
|
|
end
|
|
|
|
def get_disk(row, col)
|
|
return @data[row][col]
|
|
end
|
|
|
|
def reverse_to(row, col, my_disk, dir_y, dir_x)
|
|
y = row
|
|
x = col
|
|
begin
|
|
y += dir_y
|
|
x += dir_x
|
|
if y < 0 || x < 0 || y > 7 || x > 7 ||
|
|
@data[y][x] == EMPTY
|
|
return
|
|
end
|
|
end until @data[y][x] == my_disk
|
|
begin
|
|
@data[y][x] = my_disk
|
|
changed
|
|
notify_observers(y, x)
|
|
y -= dir_y
|
|
x -= dir_x
|
|
end until y == row && x == col
|
|
end
|
|
|
|
def put_disk(row, col, disk)
|
|
@data[row][col] = disk
|
|
changed
|
|
notify_observers(row, col)
|
|
DIRECTIONS.each do |dir|
|
|
reverse_to(row, col, disk, *dir)
|
|
end
|
|
end
|
|
|
|
def count_disk(disk)
|
|
num = 0
|
|
@data.each do |rows|
|
|
rows.each do |d|
|
|
if d == disk
|
|
num += 1
|
|
end
|
|
end
|
|
end
|
|
return num
|
|
end
|
|
|
|
def count_point_to(row, col, my_disk, dir_y, dir_x)
|
|
return 0 if @data[row][col] != EMPTY
|
|
count = 0
|
|
loop do
|
|
row += dir_y
|
|
col += dir_x
|
|
break if row < 0 || col < 0 || row > 7 || col > 7
|
|
case @data[row][col]
|
|
when my_disk
|
|
return count
|
|
when other_disk(my_disk)
|
|
count += 1
|
|
when EMPTY
|
|
break
|
|
end
|
|
end
|
|
return 0
|
|
end
|
|
|
|
def count_point(row, col, my_disk)
|
|
count = 0
|
|
DIRECTIONS.each do |dir|
|
|
count += count_point_to(row, col, my_disk, *dir)
|
|
end
|
|
return count
|
|
end
|
|
|
|
def corner?(row, col)
|
|
return (row == 0 && col == 0) ||
|
|
(row == 0 && col == 7) ||
|
|
(row == 7 && col == 0) ||
|
|
(row == 7 && col == 7)
|
|
end
|
|
|
|
def search(my_disk)
|
|
max = 0
|
|
max_row = nil
|
|
max_col = nil
|
|
for row in 0 .. 7
|
|
for col in 0 .. 7
|
|
buf = count_point(row, col, my_disk)
|
|
if (corner?(row, col) && buf > 0) || max < buf
|
|
max = buf
|
|
max_row = row
|
|
max_col = col
|
|
end
|
|
end
|
|
end
|
|
return max_row, max_col
|
|
end
|
|
end #--------------------------> class Board ends here
|
|
|
|
class BoardView < TclTkWidget
|
|
|
|
BACK_GROUND_COLOR = "DarkGreen"
|
|
HILIT_BG_COLOR = "green"
|
|
BORDER_COLOR = "black"
|
|
BLACK_COLOR = "black"
|
|
WHITE_COLOR = "white"
|
|
STOP_COLOR = "red"
|
|
|
|
attr :left
|
|
attr :top
|
|
attr :right
|
|
attr :bottom
|
|
|
|
class Square
|
|
|
|
attr :oval, TRUE
|
|
attr :row
|
|
attr :col
|
|
|
|
def initialize(view, row, col)
|
|
@view = view
|
|
@id = @view.e("create rectangle",
|
|
*(view.tk_rect(view.left + col,
|
|
view.top + row,
|
|
view.left + col + 1,
|
|
view.top + row + 1) \
|
|
<< "-fill #{BACK_GROUND_COLOR}") )
|
|
@row = row
|
|
@col = col
|
|
@view.e("itemconfigure", @id,
|
|
"-width 0.5m -outline #{BORDER_COLOR}")
|
|
@view.e("bind", @id, "<Any-Enter>", TclTkCallback.new($ip, proc{
|
|
if @oval == nil
|
|
view.e("itemconfigure", @id, "-fill #{HILIT_BG_COLOR}")
|
|
end
|
|
}))
|
|
@view.e("bind", @id, "<Any-Leave>", TclTkCallback.new($ip, proc{
|
|
view.e("itemconfigure", @id, "-fill #{BACK_GROUND_COLOR}")
|
|
}))
|
|
@view.e("bind", @id, "<ButtonRelease-1>", TclTkCallback.new($ip,
|
|
proc{
|
|
view.click_square(self)
|
|
}))
|
|
end
|
|
|
|
def blink(color)
|
|
@view.e("itemconfigure", @id, "-fill #{color}")
|
|
$update.e()
|
|
sleep(0.1)
|
|
@view.e("itemconfigure", @id, "-fill #{BACK_GROUND_COLOR}")
|
|
end
|
|
end #-----------------------> class Square ends here
|
|
|
|
def initialize(othello, board)
|
|
super($ip, $root, $canvas)
|
|
@othello = othello
|
|
@board = board
|
|
@board.add_observer(self)
|
|
|
|
@squares = Array.new(8)
|
|
for i in 0 .. 7
|
|
@squares[i] = Array.new(8)
|
|
end
|
|
@left = 1
|
|
@top = 0.5
|
|
@right = @left + 8
|
|
@bottom = @top + 8
|
|
|
|
i = self.e("create rectangle", *tk_rect(@left, @top, @right, @bottom))
|
|
self.e("itemconfigure", i,
|
|
"-width 1m -outline #{BORDER_COLOR} -fill #{BACK_GROUND_COLOR}")
|
|
|
|
for row in 0 .. 7
|
|
for col in 0 .. 7
|
|
@squares[row][col] = Square.new(self, row, col)
|
|
end
|
|
end
|
|
|
|
update
|
|
end
|
|
|
|
def tk_rect(left, top, right, bottom)
|
|
return left.to_s + "c", top.to_s + "c",
|
|
right.to_s + "c", bottom.to_s + "c"
|
|
end
|
|
|
|
def clear
|
|
each_square do |square|
|
|
if square.oval != nil
|
|
self.e("delete", square.oval)
|
|
square.oval = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
def draw_disk(row, col, disk)
|
|
if disk == EMPTY
|
|
if @squares[row][col].oval != nil
|
|
self.e("delete", @squares[row][col].oval)
|
|
@squares[row][col].oval = nil
|
|
end
|
|
return
|
|
end
|
|
|
|
$update.e()
|
|
sleep(0.05)
|
|
oval = @squares[row][col].oval
|
|
if oval == nil
|
|
oval = self.e("create oval", *tk_rect(@left + col + 0.2,
|
|
@top + row + 0.2,
|
|
@left + col + 0.8,
|
|
@top + row + 0.8))
|
|
@squares[row][col].oval = oval
|
|
end
|
|
case disk
|
|
when BLACK
|
|
color = BLACK_COLOR
|
|
when WHITE
|
|
color = WHITE_COLOR
|
|
else
|
|
fail format("Unknown disk type: %d", disk)
|
|
end
|
|
self.e("itemconfigure", oval, "-outline #{color} -fill #{color}")
|
|
end
|
|
|
|
def update(row = nil, col = nil)
|
|
if row && col
|
|
draw_disk(row, col, @board.get_disk(row, col))
|
|
else
|
|
each_square do |square|
|
|
draw_disk(square.row, square.col,
|
|
@board.get_disk(square.row, square.col))
|
|
end
|
|
end
|
|
@othello.show_point
|
|
end
|
|
|
|
def each_square
|
|
@squares.each do |rows|
|
|
rows.each do |square|
|
|
yield(square)
|
|
end
|
|
end
|
|
end
|
|
|
|
def click_square(square)
|
|
if @othello.in_com_turn || @othello.game_over ||
|
|
@board.count_point(square.row,
|
|
square.col,
|
|
@board.man_disk) == 0
|
|
square.blink(STOP_COLOR)
|
|
return
|
|
end
|
|
@board.put_disk(square.row, square.col, @board.man_disk)
|
|
@othello.com_turn
|
|
end
|
|
|
|
private :draw_disk
|
|
public :update
|
|
end #----------------------> class BoardView ends here
|
|
|
|
def initialize
|
|
@msg_label = TclTkWidget.new($ip, $root, $label)
|
|
$pack.e(@msg_label)
|
|
|
|
@board = Board.new(self)
|
|
@board_view = BoardView.new(self, @board)
|
|
#### added by Y. Shigehiro
|
|
## board_view の大きさを設定する.
|
|
x1, y1, x2, y2 = @board_view.e("bbox all").split(/ /).collect{|i| i.to_f}
|
|
@board_view.e("configure -width", x2 - x1)
|
|
@board_view.e("configure -height", y2 - y1)
|
|
## scrollregion を設定する.
|
|
@board_view.e("configure -scrollregion {", @board_view.e("bbox all"),
|
|
"}")
|
|
#### ここまで
|
|
$pack.e(@board_view, "-fill both -expand true")
|
|
|
|
panel = TclTkWidget.new($ip, $root, $frame)
|
|
|
|
@play_black = TclTkWidget.new($ip, panel, $checkbutton,
|
|
"-text {com is black} -command", TclTkCallback.new($ip, proc{
|
|
switch_side
|
|
}))
|
|
$pack.e(@play_black, "-side left")
|
|
|
|
quit = TclTkWidget.new($ip, panel, $button, "-text Quit -command",
|
|
TclTkCallback.new($ip, proc{
|
|
exit
|
|
}))
|
|
$pack.e(quit, "-side right -fill x")
|
|
|
|
reset = TclTkWidget.new($ip, panel, $button, "-text Reset -command",
|
|
TclTkCallback.new($ip, proc{
|
|
reset_game
|
|
}))
|
|
$pack.e(reset, "-side right -fill x")
|
|
|
|
$pack.e(panel, "-side bottom -fill x")
|
|
|
|
# root = Tk.root
|
|
$wm.e("title", $root, "Othello")
|
|
$wm.e("iconname", $root, "Othello")
|
|
|
|
@board.com_disk = WHITE
|
|
@game_over = FALSE
|
|
|
|
TclTk.mainloop
|
|
end
|
|
|
|
def switch_side
|
|
if @in_com_turn
|
|
@play_black.e("toggle")
|
|
else
|
|
@board.com_disk = @board.man_disk
|
|
com_turn unless @game_over
|
|
end
|
|
end
|
|
|
|
def reset_game
|
|
if @board.com_disk == BLACK
|
|
@board.com_disk = WHITE
|
|
@play_black.e("toggle")
|
|
end
|
|
@board_view.clear
|
|
@board.reset
|
|
$wm.e("title", $root, "Othello")
|
|
@game_over = FALSE
|
|
end
|
|
|
|
def com_turn
|
|
@in_com_turn = TRUE
|
|
$update.e()
|
|
sleep(0.5)
|
|
begin
|
|
com_disk = @board.count_disk(@board.com_disk)
|
|
man_disk = @board.count_disk(@board.man_disk)
|
|
if @board.count_disk(EMPTY) == 0
|
|
if man_disk == com_disk
|
|
$wm.e("title", $root, "{Othello - Draw!}")
|
|
elsif man_disk > com_disk
|
|
$wm.e("title", $root, "{Othello - You Win!}")
|
|
else
|
|
$wm.e("title", $root, "{Othello - You Loose!}")
|
|
end
|
|
@game_over = TRUE
|
|
break
|
|
elsif com_disk == 0
|
|
$wm.e("title", $root, "{Othello - You Win!}")
|
|
@game_over = TRUE
|
|
break
|
|
elsif man_disk == 0
|
|
$wm.e("title", $root, "{Othello - You Loose!}")
|
|
@game_over = TRUE
|
|
break
|
|
end
|
|
row, col = @board.search(@board.com_disk)
|
|
break if row == nil || col == nil
|
|
@board.put_disk(row, col, @board.com_disk)
|
|
end while @board.search(@board.man_disk) == [nil, nil]
|
|
@in_com_turn = FALSE
|
|
end
|
|
|
|
def show_point
|
|
black = @board.count_disk(BLACK)
|
|
white = @board.count_disk(WHITE)
|
|
@msg_label.e("configure -text",
|
|
%Q/{#{format("BLACK: %.2d WHITE: %.2d", black, white)}}/)
|
|
end
|
|
end #----------------------> class Othello ends here
|
|
|
|
Othello.new
|
|
|
|
#----------------------------------------------> othello.rb ends here
|