咳といいます。
> という感じで、どなたかここに送ってみませんか?
さっそく箱入り娘を持ってきました。(でもDivが必要‥)
Divは↓からダウンロードして %ruby install.rb してください。
* http://www.ruby-lang.org/~knu/cgi-bin/cvsweb.cgi/app/div/div.tar.gz?tarball=1
このメールの二つのファイル、hakolet.rb と hako-e.rb を
hakolet ディレクトリに置いて次のように起動します。
% ruby samplet.rb hakolet
hako-e.rbの英語版はgotokenさんの記事[ruby-talk:00503]から拝借しました。
hako-e.rbを単体で起動するとTcl/Tk版の箱入り娘が動きます。
% ruby hako-e.rb
# だれかこれをうぇぶりっけんへ‥
---- hakolet.rb -----
require 'div/div'
require 'div/tofusession'
require 'tofu/tofulet'
require 'hako-e'
class HakoDiv < Div::Div
def initialize(session)
super(session)
@count = nil
start
end
attr_reader :count
def start
@widget = Hako::TableHako.new(self)
@game = Hako::Game.new(@widget)
@count = 0
end
def to_html(context)
<<EOP
<html>
<head><title>hako</title></head>
<body>
#{@widget.view(context)}
<div align="right">
<p><small>
[Step: #{@count}][#{a('retry', {}, context)}Retry</a>]
</small></p>
</div>
</body>
</html>
EOP
end
def finish?
@game.finish?
end
def to_args(params)
chip, = params['chip']
dx, = params['dx']
dy, = params['dy']
chip = chip.to_i if chip
dx = dx.to_i if dx
dy = dy.to_i if dy
return chip, dx, dy
end
def do_move(context, params)
chip, dx, dy = to_args(params) rescue return
@count += 1
@widget.do_move(chip, dx, dy)
end
def do_retry(context, params)
start
end
public :a
end
class HakoSession < Div::TofuSession
def initialize(bartender, hint=nil)
super(bartender, hint)
@hako = HakoDiv.new(self)
end
def do_GET(context)
update_div(context)
context.res_header('content-type', 'text/html; charset=euc-jp')
context.res_body(@hako.to_html(context))
@expires = Time.now + 900
end
end
def _setup_hakolet(srv, mount_point)
bartender = Tofu::Bartender.new(HakoSession)
srv.mount(mount_point, WEBrick::Tofulet, bartender)
end
---- hako-e.rb -----
require 'monitor'
module Hako
class TkHako
def initialize(parent=nil, unit_size=64)
@w = 4
@h = 5
@unit_size = unit_size
@house = TkFrame.new(parent,
'width' => @unit_size * @w,
'height' => @unit_size * @h).pack
@chips = {}
end
def add_chip(chip)
widget = TkLabel.new(@house,
'text' => chip.name,
'relief' => 'raised')
@chips[chip] = widget
widget.bind('B1-Motion', proc{|x, y| do_motion(x, y, chip)}, "%x %y")
end
def do_motion(x, y, chip)
if x >= @unit_size * chip.w
dx = 1
elsif x < 0
dx = -1
else
dx = 0
end
if y >= @unit_size * chip.h
dy = 1
elsif y < 0
dy = -1
else
dy = 0
end
if (dx != 0)
dy = 0
end
return if (dx == 0) and (dy == 0)
chip.try_move(dx, dy)
end
def moveto_chip(x, y, chip)
widget = @chips[chip]
widget.place('x' => @unit_size * x,
'y' => @unit_size * y,
'width' => @unit_size * chip.w,
'height' => @unit_size * chip.h)
end
end
class TableHako
include MonitorMixin
def initialize(div)
super()
@w = 4
@h = 5
@chips = {}
@arrow_icon = {
[0, 1] => 'v',
[0, -1] => '^',
[1, 0] => '>',
[-1, 0] => '<'
}
@color = {
"Father" => "#ccccff",
"Daughter" => "#ffcccc",
"Mother" => "#ffdddd",
"Boy" => "#ffccff",
"Servant" => "#ccffff",
"Manager" => "#ccffcc",
nil => "#ffffff"
}
@div = div
@nil_cell = chip_cell(nil)
end
def add_chip(chip)
@chips[chip.id] = chip
end
def do_move(chip_id, dx, dy)
chip = @chips[chip_id]
return unless chip
chip.try_move(dx, dy)
end
def moveto_chip(x, y, chip)
synchronize { @map = nil }
end
def view(context)
synchronize do
@map = make_map unless @map
s = '<table border="0"><tr><td></td>' + ('<td width="80"></td>' * (@w)) + '<td></td></tr>'
@h.times do |y|
s << "<tr><td height='80'>#{@nil_cell}</td>"
@w.times do |x|
chip = @map[[x, y]]
s << (chip ? chip_cell(chip, context) : @nil_cell)
end
s << "<td>#{@nil_cell}</td></tr>"
end
s << ('<tr>' + ('<td></td>' * (@w + 2)) + '</tr></table>')
end
end
private
def chip_cell(chip, context=nil)
return '' if chip == true
if chip
w = chip.w
h = chip.h
name = chip.name
left = arrow(chip, -1, 0, context)
right = arrow(chip, 1, 0, context)
up = arrow(chip, 0, -1, context)
down = arrow(chip, 0, 1, context)
bg = @color[name]
else
w = h = 1
left = right = up = down = " "
name = " "
bg = @color[nil]
end
"<th align='center' colspan='#{w}' rowspan='#{h}' bgcolor='#{bg}'>" +
"<table border='0' cellpadding='0' cellspacing='0'>" +
"<tr><th>#{up}</th></tr>" +
"<tr><th>#{left} #{name} #{right}</th></tr>"+
"<tr><th>#{down}</th></tr></table>" +
"</th>"
end
def arrow(chip, dx ,dy, context)
if chip && chip.move?(dx, dy)
@div.a('move', {'chip'=>chip.id, 'dx' => dx, 'dy' => dy}, context) +
@arrow_icon[[dx, dy]] + "</a>"
else
"<font color='#ffffff'>#{@arrow_icon[[dx, dy]]}</font>"
end
end
def make_map
map = {}
@chips.values.each do |chip|
area = chip.area
map[area.shift] = chip
area.each do |pos|
map[pos] = true
end
end
map
end
end
class House
def initialize(widget)
@w = 4
@h = 5
@widget = widget
@chips = []
self
end
attr :widget
attr :unit_size
attr :chips
def enter(chip)
@chips.push(chip)
end
def wall
hash = {}
(-1..@w).each do |i|
hash[[i, -1]] = true
hash[[i, @h]] = true
end
(-1..@h).each do |i|
hash[[-1, i]] = true
hash[[@w, i]] = true
end
hash
end
def move?(chip, dx, dy)
field = self.wall
@chips.each do |c|
unless c == chip
c.area.each do |a|
field[a] = chip
end
end
end
chip.area(dx, dy).each do |a|
return false if field[a]
end
return true
end
end
class Chip
def initialize(house, name, x, y, w=1, h=1)
@name = name
@w = w
@h = h
@house = house
house.enter(self)
house.widget.add_chip(self)
moveto(x, y)
end
attr_reader :name, :w, :h, :x, :y
def area(dx=0, dy=0)
v = []
for i in (1..@h)
for j in (1..@w)
v.push([dx + @x + j - 1, dy + @y + i - 1])
end
end
v
end
def moveto(x, y)
@x = x
@y = y
@house.widget.moveto_chip(x, y, self)
end
def move(dx, dy)
x = @x + dx
y = @y + dy
moveto(x, y)
end
def do_motion(x, y)
try_move(dx, dy)
end
def move?(dx, dy)
@house.move?(self, dx, dy)
end
def try_move(dx, dy)
if move?(dx, dy)
move(dx, dy)
end
end
end
class Game
def initialize(widget)
house = House.new(widget)
@she = Chip.new(house, 'Daughter', 1, 0, 2, 2)
father = Chip.new(house, "Father", 0, 0, 1, 2)
mother = Chip.new(house, "Mother", 3, 0, 1, 2)
kozou = []
(0..3).each do |i|
kozou.push Chip.new(house, "Boy", i, 2)
end
genan = []
(0..1).each do |i|
genan.push Chip.new(house, "Servant", i * 3, 3, 1, 2)
end
bantou = Chip.new(house, "Manager", 1, 3, 2, 1)
end
def finish?
@she.x == 1 && @she.y == 3
end
end
end
if __FILE__ == $0
def tk_main
require 'tk'
game = Hako::Game.new(Hako::TkHako.new(nil, 64))
Tk.mainloop
end
tk_main
end