Rubyでぷよぷよ19連鎖

http://okajima.air-nifty.com/b/2011/01/2011-ffac.html にある、連鎖するプログラムを書けというやつ。

大きな方針として、最初から以下のようにすべきだった。オブジェクトは各種状態を1つだけ表現するようにして、次々と状態が隣接状態を作るという。やっぱりオブジェクト指向がよく分からない。データと手続きを一緒にまとめられるので、やっぱり自分自身を書き換えたくなるように思うんだけど、それは非常に良くない感じがする。そう言われてみれば、Rubyの標準ライブラリではビックリマークのある破壊的メソッドは少数派だし、あまり使う気がしないのだった。同様に、自分でクラスを作るときもそうすればいいだけなのか。インスタンスメソッドの最後で、ThatSameClass.newを返すというのを何となくあまり見ないような気がするんだけど、なぜだろうか。

途中で機能を足したからコンストラクタが汚い。こうやってファクトリパターンは生まれたのかと思った。

class Board
  attr_accessor :height, :width, :state, :new_state

  def initialize(text_or_state)
    case text_or_state
    when String
      @text =  text_or_state
      @state = []
      @text.each_line do |l|
        next if l =~ /^$/
        @state << l.chomp.split(//)
      end
    when Array
      @state = text_or_state
    end
    @new_state = Marshal.load(Marshal.dump(@state))
    @height = @state.size
    @width = @state.map(&:size).max
    visited_reset
  end

  def visited_reset
    @visited = Array.new(@height) {Array.new(@width, "o")}
  end

  def remove
    @state.each do |y|
      y.map!{|c| c == "#" ? " ":c}
    end
    Board.new(@state)
  end

  def fall
    new_line = []
    @new_state = Array.new(@height) {Array.new(@width, " ")}

    (0..(@width - 1)).each do |x|
      (0..(@height - 1)).to_a.reverse.each do |y|
        next if @state[y][x] == " "
        new_line << @state[y][x]
      end

      (0..(@height - 1)).to_a.reverse.each do |y|
        break if new_line.size == 0
        @new_state[y][x] = new_line.shift
      end
    end
    Board.new(@new_state)
  end

  def all_delete
    (0..(@height - 1)).each do |y|
      (0..(@width - 1)).each do |x|
        delete(y, x)
      end
    end
    if !@new_state.flatten.include?("#")
      nil
    else
      Board.new(@new_state)
    end
  end

private

  def delete(y, x)
    return if @state[y][x] == "#"
    same = find_same(y, x, [])
    visited_reset
    if same.size >= 4
      same.each do |pos|
        @new_state[pos[0]][pos[1]] = "#"
      end
    end
  end

  def find_same(y, x, pos)
    pos << [y, x]
    @visited[y][x] = "."
    col = @state[y][x]
    return pos if col == " "

    if (y > 0) && (@visited[y - 1][x] != ".") && (@state[y - 1][x] == col)
      find_same(y - 1, x, pos)
    end
    if (y < @height - 1) && (@visited[y + 1][x] != ".") && (@state[y + 1][x] == col)
      find_same(y + 1, x, pos)
    end
    if (x > 0) && (@visited[y][x - 1] != ".") && (@state[y][x - 1] == col)
      find_same(y, x - 1, pos)
    end
    if (x < @width - 1) && (@visited[y][x + 1] != ".") && (@state[y][x + 1] == col)
      find_same(y, x + 1, pos)
    end
    pos
  end

  def to_s
    @state.inject(""){|s, l| s += ((l.join) + "\n")} + "-" * @width
  end

end

b = Board.new(File.open(ARGV.shift).read)
rensa = 0
p b; sleep 1

loop do 
  b = b.all_delete
  break if b.nil?
  p b; sleep 0.3
  b = b.remove
  p b; sleep 0.3
  b = b.fall
  p b; sleep 0.3
  rensa += 1
end

print "rensa: #{rensa}\n"

ファクトリパターンで書き換えると、こんな感じか。

class BoardFactory
  def self.create(text_or_state)
    case text_or_state
    when String
      text =  text_or_state
      state = []
      text.each_line do |l|
        next if l =~ /^$/
        state << l.chomp.split(//)
      end
    when Array
      state = text_or_state
    end
    Board.new(state)
  end
end

class Board
  attr_accessor :height, :width, :state, :new_state

  def initialize(state)
    @state = state
    @new_state = Marshal.load(Marshal.dump(@state))
    @height = @state.size
    @width = @state.map(&:size).max
    visited_reset
  end
  :
  :


b = BoardFactory.create(File.open(ARGV.shift).read)
% cat p2.txt
  GYRR
RYYGYG
GYGYRR
RYGYRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
% ruby puyo.rb p2.txt
  GYRR
RYYGYG
GYGYRR
RYGYRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
  GYRR
R##GYG
G#GYRR
R#GYRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
  GYRR
R  GYG
G GYRR
R GYRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
   YRR
R GGYG
G GYRR
R GYRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
   YRR
R ##YG
G #YRR
R #YRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
   YRR
R   YG
G  YRR
R  YRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
    RR
R  YYG
G  YRR
R  YRG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
    RR
R  ##G
G  #RR
R  #RG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
    RR
R    G
G   RR
R   RG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
     R
R   RG
G   RR
R   RG
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
     R
R   #G
G   ##
R   #G
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
     R
R    G
G     
R    G
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R    R
G    G
R    G
YGYRYG
GYRYRG
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R    R
G    #
R    #
YGYRY#
GYRYR#
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R    R
G     
R     
YGYRY 
GYRYR 
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGYRY 
GYRYRR
YGYRYR
YGYRYR
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGYRY 
GYRY##
YGYRY#
YGYRY#
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGYRY 
GYRY  
YGYRY 
YGYRY 
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGYR  
GYRYY 
YGYRY 
YGYRY 
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGYR  
GYR## 
YGYR# 
YGYR# 
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGYR  
GYR   
YGYR  
YGYR  
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGY   
GYRR  
YGYR  
YGYR  
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGY   
GY##  
YGY#  
YGY#  
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YGY   
GY    
YGY   
YGY   
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YG    
GYY   
YGY   
YGY   
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YG    
G##   
YG#   
YG#   
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
YG    
G     
YG    
YG    
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
Y     
GG    
YG    
YG    
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
Y     
##    
Y#    
Y#    
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
R     
G     
R     
Y     
      
Y     
Y     
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
R     
G     
R     
Y     
Y     
Y     
YRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
R     
G     
R     
#     
#     
#     
#RRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
R     
G     
R     
      
      
      
 RRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
      
      
      
      
R     
G     
RRRGRG
RYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
      
      
      
      
R     
G     
###GRG
#YGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
      
      
      
      
R     
G     
   GRG
 YGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
      
      
      
      
      
      
R  GRG
GYGYGG
GRYGYR
GRYGYR
GRYGYR
------
      
      
      
      
      
      
      
      
R  GRG
#YGYGG
#RYGYR
#RYGYR
#RYGYR
------
      
      
      
      
      
      
      
      
R  GRG
 YGYGG
 RYGYR
 RYGYR
 RYGYR
------
      
      
      
      
      
      
      
      
   GRG
 YGYGG
 RYGYR
 RYGYR
RRYGYR
------
      
      
      
      
      
      
      
      
   GRG
 YGYGG
 #YGYR
 #YGYR
##YGYR
------
      
      
      
      
      
      
      
      
   GRG
 YGYGG
  YGYR
  YGYR
  YGYR
------
      
      
      
      
      
      
      
      
   GRG
  GYGG
  YGYR
  YGYR
 YYGYR
------
      
      
      
      
      
      
      
      
   GRG
  GYGG
  #GYR
  #GYR
 ##GYR
------
      
      
      
      
      
      
      
      
   GRG
  GYGG
   GYR
   GYR
   GYR
------
      
      
      
      
      
      
      
      
   GRG
   YGG
   GYR
   GYR
  GGYR
------
      
      
      
      
      
      
      
      
   GRG
   YGG
   #YR
   #YR
  ##YR
------
      
      
      
      
      
      
      
      
   GRG
   YGG
    YR
    YR
    YR
------
      
      
      
      
      
      
      
      
    RG
    GG
    YR
   GYR
   YYR
------
      
      
      
      
      
      
      
      
    RG
    GG
    #R
   G#R
   ##R
------
      
      
      
      
      
      
      
      
    RG
    GG
     R
   G R
     R
------
      
      
      
      
      
      
      
      
     G
     G
     R
    RR
   GGR
------
      
      
      
      
      
      
      
      
     G
     G
     #
    ##
   GG#
------
      
      
      
      
      
      
      
      
     G
     G
      
      
   GG 
------
      
      
      
      
      
      
      
      
      
      
      
     G
   GGG
------
      
      
      
      
      
      
      
      
      
      
      
     #
   ###
------
      
      
      
      
      
      
      
      
      
      
      
      
      
------
      
      
      
      
      
      
      
      
      
      
      
      
      
------
rensa: 19