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 = '
| ' + (' | ' * (@w)) + ' |
'
@h.times do |y|
s << "| #{@nil_cell} | "
@w.times do |x|
chip = @map[[x, y]]
s << (chip ? chip_cell(chip, context) : @nil_cell)
end
s << "#{@nil_cell} |
"
end
s << ('' + (' | ' * (@w + 2)) + '
')
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
"" +
"" +
"| #{up} | " +
"| #{left} #{name} #{right} | "+
"| #{down} |
|---|
" +
" | "
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]] + ""
else
"#{@arrow_icon[[dx, dy]]}"
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