Created
June 8, 2013 20:53
-
-
Save takehiko/5736543 to your computer and use it in GitHub Desktop.
A tiny combo checker for Puzzle & Dragons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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