Index: [Article Count Order] [Thread]

Date:  Wed, 20 Feb 2002 12:42:16 +0900 (JST)
From:  m_seki@mva.biglobe.ne.jp
Subject:  [webrickja:6] Re: samplet
Sender:  Masatoshi SEKI <seki@mr.nasu.toshiba.co.jp>
To:  webrickja@notwork.org
Message-Id:  <20020220124216A.seki@mr.nasu.toshiba.co.jp>
In-Reply-To:  <20020220.053123.125128294.gotoyuzo@kotetsu.does.notwork.org>
References:  <20020220.053123.125128294.gotoyuzo@kotetsu.does.notwork.org>
X-Mail-Count: 00006


咳といいます。

> という感じで、どなたかここに送ってみませんか?

さっそく箱入り娘を持ってきました。(でも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] => '&gt;', 
	[-1, 0] => '&lt;'
      }
      @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}&nbsp;#{name}&nbsp;#{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