Skip to content

Instantly share code, notes, and snippets.

@takehiko
Created June 8, 2013 20:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takehiko/5736543 to your computer and use it in GitHub Desktop.
Save takehiko/5736543 to your computer and use it in GitHub Desktop.
A tiny combo checker for Puzzle & Dragons
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# A tiny combo checker for Puzzle & Dragons
# by takehikom (http://d.hatena.ne.jp/takehikom/)
# You can find the updated version:
# https://gist.github.com/takehiko/5750263
# Usage:
# ruby pazdracombo.rb
# ruby pazdracombo.rb 111234 511123 451116 234234 234234
class PazdraComboChecker
PDC_SYM_TABLE = {
"火" => :fire,
"水" => :water,
"木" => :wood,
"闇" => :dark,
"光" => :light,
"回" => :recover,
"1" => :fire,
"2" => :water,
"3" => :wood,
"4" => :dark,
"5" => :light,
"6" => :recover,
}
PDC_OUTPUT_JP_TABLE = {
:fire => "火",
:water => "水",
:wood => "木",
:dark => "闇",
:light => "光",
:recover => "回",
:erase => "*",
:other => "?",
}
PDC_OUTPUT_ASCII_TABLE = {
:fire => "1",
:water => "2",
:wood => "3",
:dark => "4",
:light => "5",
:recover => "6",
:erase => "*",
:other => "?"
}
PDC_COMBO_JP_TABLE = "・①②③④⑤⑥⑦⑧⑨⑩".split(//)
PDC_COMBO_ASCII_TABLE = " abcdefghij".split(//)
# 環境変数をもとに出力文字(日本語/ASCII)を設定
if /ja/ =~ ENV["LANG"]
PDC_OUTPUT_TABLE = PDC_OUTPUT_JP_TABLE
PDC_COMBO_TABLE = PDC_COMBO_JP_TABLE
else
PDC_OUTPUT_TABLE = PDC_OUTPUT_ASCII_TABLE
PDC_COMBO_TABLE = PDC_COMBO_ASCII_TABLE
end
# コンストラクタ
def initialize(param = nil)
if param.nil? || (Array === param && param.empty?)
param = <<EOS
火闇闇水木水
火火火水木水
火光光水木水
木木木木水水
回光光光光光
EOS
end
init_field(param)
@erase_h = Hash.new(0)
@combo_h = Hash.new(0)
@combo_aa = []
end
# フィールドの初期化
def init_field(field_str)
field_a = [field_str].flatten.join.gsub(/\s/m, "").split(//)
@field_h = Hash.new
5.times do |y|
6.times do |x|
@field_h[pdc_pos(x, y)] = pdc_sym(field_a.shift || nil)
end
end
end
# 文字列変換
# to_s または to_s(0) : フィールドをそのまま出力
# to_s(1) : 消去処理を加える
# to_s(2) : コンボ処理を加える
def to_s(mode_disp = 0)
s = ""
5.times do |y|
6.times do |x|
if mode_disp == 1 && @erase_h[pdc_pos(x, y)] > 0
c = pdc_char(:erase)
elsif mode_disp == 2 && @combo_h[pdc_pos(x, y)] > 0
c = pdc_combo_char(@combo_h[pdc_pos(x, y)])
else
c = pdc_char(@field_h[pdc_pos(x, y)])
end
s += c
end
s += "\n"
end
s
end
# 処理の開始
def start
puts "[start]"
puts to_s
check_erasable
puts "[erase]"
puts to_s(1)
calc_combo
puts "[combo]"
puts to_s(2)
end
private
# フィールド座標(x, y)を文字列に変換
def pdc_pos(x, y)
[y, x].pack("c*")
end
# 文字列のフィールド座標を配列に戻す(逆変換)
def pdc_depos(s)
s.unpack("c*").reverse
end
# フィールドのドロップをシンボルに変換
def pdc_sym(c)
PDC_SYM_TABLE[c.to_s]
end
# フィールドのドロップ(Symbol)を文字に変換
def pdc_char(sym)
PDC_OUTPUT_TABLE[sym] || PDC_OUTPUT_TABLE[:other]
end
# コンボ番号を文字に変換
def pdc_combo_char(n)
PDC_COMBO_TABLE[n]
end
# 妥当なブロックか判定
def pdc_valid?(sym)
case sym
when :fire, :water, :wood, :dark, :light, :recover
true
else
false
end
end
# 消去可能な3つ揃いと関連情報を記録
def mark_erasable(x, y, delta_x, delta_y)
pos_a = []
neighbor_a = []
3.times do |i|
p = pdc_pos(x + delta_x * i, y + delta_y * i)
@erase_h[p] += 1
pos_a << p
neighbor_a << p <<
pdc_pos(x + delta_x * i - 1, y + delta_y * i) <<
pdc_pos(x + delta_x * i + 1, y + delta_y * i) <<
pdc_pos(x + delta_x * i, y + delta_y * i - 1) <<
pdc_pos(x + delta_x * i, y + delta_y * i + 1)
end
@combo_aa << {:pos => pos_a, :neighbor => neighbor_a.uniq,
:cell => @field_h[pdc_pos(x + delta_x, y + delta_y)]}
end
# 消去可能な縦方向(_v)・横方向(_h)の3つ揃いと関連情報を記録
def mark_erasable_v(x, y); mark_erasable(x, y, 0, 1); end
def mark_erasable_h(x, y); mark_erasable(x, y, 1, 0); end
# 縦方向の消去判定
def check_erasable_v
(5 - 2).times do |y|
6.times do |x|
c = @field_h[pdc_pos(x, y)]
next if !pdc_valid?(c)
next if @field_h[pdc_pos(x, y + 1)] != c
next if @field_h[pdc_pos(x, y + 2)] != c
mark_erasable_v(x, y)
end
end
end
# 横方向の消去判定
def check_erasable_h
5.times do |y|
(6 - 2).times do |x|
c = @field_h[pdc_pos(x, y)]
next if !pdc_valid?(c)
next if @field_h[pdc_pos(x + 1, y)] != c
next if @field_h[pdc_pos(x + 2, y)] != c
mark_erasable_h(x, y)
end
end
end
# 消去判定
def check_erasable
@erase_h = Hash.new(0)
check_erasable_v
check_erasable_h
end
# コンボ判定
def calc_combo
# 3つ揃い領域の結合
combo_aa1 = @combo_aa.dup
combo_aa2 = []
until combo_aa1.empty?
combo1 = combo_aa1.shift
combo_aa2 << combo1
combo_aa1.each_with_index do |combo2, i|
if combo1[:cell] == combo2[:cell] &&
!(combo1[:neighbor] & combo2[:pos]).empty?
combo_aa2.pop
combo_aa1.delete_at(i)
combo_aa1 << {:pos => (combo1[:pos] | combo2[:pos]).sort.uniq,
:neighbor => (combo1[:neighbor] | combo2[:neighbor]).uniq,
:cell => combo1[:cell]}
break
end
end
end
@combo_aa = combo_aa2.sort_by {|item| item[:pos].first.to_s}
# コンボ番号の割り振り
@combo_h = Hash.new(0)
i = 1
@combo_aa.each do |p_a|
p_a[:pos].each do |pos|
@combo_h[pos] = i
end
i += 1
end
end
end
if __FILE__ == $0
PazdraComboChecker.new(ARGV).start
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment