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 << "" @w.times do |x| chip = @map[[x, y]] s << (chip ? chip_cell(chip, context) : @nil_cell) end s << "" end s << ('' + ('' * (@w + 2)) + '
#{@nil_cell}#{@nil_cell}
') 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