ぷよぷよ19連鎖
ちょうど1年前 Ruby を覚えたての頃書いたコードを発見した。
Ruby をインストールした直後で、何か書いてみたくてネットで例題を探していた記憶がある。
問題はこれ。
http://okajima.air-nifty.com/b/2011/01/2011-ffac.html
ゲーム「ぷよぷよ」で、フィールドの状態がテキストで与えられたとき、消える「ぷよ」を消して次のフィールドの状態を出力するプログラムを書け。
1行目からいきなり腰が抜けそうな書きかたで、他にも今ならこうは書かないだろうというところがあるけど、面白いからそのままにしておく。
19連鎖するだけなら、1時間弱で完成したと思う。
何が自分をそこまで駆り立てたのか分からないけど、何故かスコア計算までするようになっている。
今にしてみれば、もっと面白い問題がいくらでもあると思うんだけど。
DATA = " GYRR|RYYGYG|GYGYRR|RYGYRG|YGYRYG|GYRYRG|YGYRYR|YGYRYR|YRRGRG|RYGYGG|GRYGYR|GRYGYR|GRYGYR" class Scoring attr_reader :output def initialize() @point = @sum_group_bonus = 0 @colors = [] end def read(n_puyo, color) @point += 10 * n_puyo @colors << color @sum_group_bonus += group_bonus(n_puyo) end def calculate(chain) mul = multiplier(chain, @colors.uniq.size, @sum_group_bonus) @output = "chain = #{chain}\nscore = #{@point} * #{mul}" @point * mul end private def multiplier(chain, n_color, sum_group_bonus) mul = chain_bonus(chain) + color_bonus(n_color) + sum_group_bonus mul == 0 ? 1 : mul end def chain_bonus(chain) if chain <= 3 then 8 * (chain - 1) else 32 * (chain - 3) end end def color_bonus(n_color) if n_color <= 1 then 0 else 3 * 2**(n_color - 2) end end def group_bonus(n_puyo) case n_puyo when 4 then 0 when 5..10 then n_puyo - 3 else 10 end end end class Puyo def initialize @board = DATA.split("|").map {|s| s.split(//)} @width = @board[0].size @height = @board.size @chain = @total = 0 put end def disappear @chain += 1 disappeared = false @done = Array.new(@height) {Array.new(@width, false)} scoring = Scoring.new @width.times do |x| @height.times do |y| n_puyo = connect(x, y) if n_puyo >= 4 then disappeared = true scoring.read(n_puyo, @board[y][x]) @queue.each {|pos| @board[pos[:y]][pos[:x]] = "*"} end end end return false unless disappeared @total += scoring.calculate(@chain) puts scoring.output put true end def fall @width.times do |x| yy = @height - 1 (@height - 1).downto(0) do |y| color = @board[y][x] @board[y][x] = " " if color != "*" then @board[yy][x] = color yy -= 1 end end end put end private def put puts "total = #{@total}\n" @height.times do |y| print "|", @board[y].join, "|" puts end puts " ------" puts sleep 1 end def connect(x, y) color = @board[y][x] return 0 if color == " " @queue = [] enqueue(x, y, color) n = 0 while @queue[n] do xx, yy = @queue[n].values_at(:x, :y) enqueue(xx - 1, yy, color) enqueue(xx + 1, yy, color) enqueue(xx, yy - 1, color) enqueue(xx, yy + 1, color) n += 1 end n end def enqueue(x, y, color) if (0...@width).member?(x) and (0...@height).member?(y) and @board[y][x] == color and !@done[y][x] then @queue <<= {:x => x, :y => y} @done[y][x] = true end end end puyo = Puyo.new puyo.fall while puyo.disappear