Created
June 10, 2013 16:42
-
-
Save takehiko/5750263 to your computer and use it in GitHub Desktop.
A tiny combo simulator of Puzzle & Dragons
This file contains hidden or 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 simulator of Puzzle & Dragons | |
# by takehikom (http://d.hatena.ne.jp/takehikom/) | |
# Usage: | |
# ruby padsim.rb | |
# ruby padsim.rb 111234 511123 451116 234234 234234 | |
module PadSim | |
class Table | |
SYMBOL = { | |
"火" => :fire, | |
"水" => :water, | |
"木" => :wood, | |
"闇" => :dark, | |
"光" => :light, | |
"回" => :recover, | |
"1" => :fire, | |
"2" => :water, | |
"3" => :wood, | |
"4" => :dark, | |
"5" => :light, | |
"6" => :recover, | |
} | |
OUTPUT_JP = { | |
:fire => "火", | |
:water => "水", | |
:wood => "木", | |
:dark => "闇", | |
:light => "光", | |
:recover => "回", | |
:erase => "*", | |
:other => "?", | |
} | |
OUTPUT_ASCII = { | |
:fire => "1", | |
:water => "2", | |
:wood => "3", | |
:dark => "4", | |
:light => "5", | |
:recover => "6", | |
:erase => "*", | |
:other => "?" | |
} | |
COMBO_JP = "・①②③④⑤⑥⑦⑧⑨⑩".split(//) | |
COMBO_ASCII = " abcdefghij".split(//) | |
# 環境変数をもとに出力文字(日本語/ASCII)を設定 | |
if /ja/ =~ ENV["LANG"] | |
OUTPUT = OUTPUT_JP | |
COMBO = COMBO_JP | |
else | |
OUTPUT = OUTPUT_ASCII | |
COMBO = COMBO_ASCII | |
end | |
end | |
class Manager | |
# コンストラクタ | |
def initialize(param = nil) | |
if param.nil? || (Array === param && param.empty?) | |
param = <<EOS | |
火闇闇水木水 | |
火火火水木水 | |
火光光水木水 | |
木木木木水水 | |
回光光光光光 | |
EOS | |
end | |
init_field(param) | |
@erase_h = Hash.new(0) # 地点 => 消去度合い(0なら消去されない) | |
@combo_h = Hash.new(0) # 地点 => コンボ番号 | |
@combo_ha = [] # :pos, :neighbor, :cell をキーとするハッシュの配列 | |
@erase_pos_a = nil # 消去される地点の配列.未計算時はnil | |
@combo_len = -1 # コンボ数.未計算時は-1 | |
end | |
attr_reader :combo_len | |
# 消去されるドロップの数 | |
def erase_len | |
if Array === @erase_pos_a | |
@erase_pos_a.length | |
else | |
-1 | |
end | |
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 | |
check_erasable | |
calc_combo | |
end | |
alias :analyze :start | |
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) | |
PadSim::Table::SYMBOL[c.to_s] | |
end | |
# フィールドのドロップ(Symbol)を文字に変換 | |
def pdc_char(sym) | |
PadSim::Table::OUTPUT[sym] || PadSim::Table::OUTPUT[:other] | |
end | |
# コンボ番号を文字に変換 | |
def pdc_combo_char(n) | |
PadSim::Table::COMBO[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 | |
@erase_pos_a << p | |
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 | |
@erase_pos_a.uniq! | |
@combo_ha << {: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) | |
@combo_ha = [] | |
@erase_pos_a = [] | |
check_erasable_v | |
check_erasable_h | |
end | |
# コンボ判定 | |
def calc_combo | |
# 3つ揃い領域の結合 | |
combo_ha1 = @combo_ha.dup | |
combo_ha2 = [] | |
until combo_ha1.empty? | |
combo1 = combo_ha1.shift | |
combo_ha2 << combo1 | |
combo_ha1.each_with_index do |combo2, i| | |
if combo1[:cell] == combo2[:cell] && | |
!(combo1[:neighbor] & combo2[:pos]).empty? | |
combo_ha2.pop | |
combo_ha1.delete_at(i) | |
combo_ha1 << {:pos => (combo1[:pos] | combo2[:pos]).sort.uniq, | |
:neighbor => (combo1[:neighbor] | combo2[:neighbor]).uniq, | |
:cell => combo1[:cell]} | |
break | |
end | |
end | |
end | |
@combo_ha = combo_ha2.sort_by {|item| item[:pos].first.to_s} | |
# コンボ番号の割り振り | |
@combo_h = Hash.new(0) | |
i = 1 | |
@combo_ha.each do |p_a| | |
p_a[:pos].each do |pos| | |
@combo_h[pos] = i | |
end | |
i += 1 | |
end | |
@combo_len = i - 1 | |
end | |
end | |
end | |
if __FILE__ == $0 | |
pcc = PadSim::Manager.new(ARGV) | |
puts "[start]" | |
puts pcc | |
pcc.analyze | |
puts "[erase]" | |
puts pcc.to_s(1) | |
puts "[combo]" | |
puts pcc.to_s(2) | |
puts "[info]" | |
puts "#{pcc.erase_len} drop(s) are erased by #{pcc.combo_len} combo(s)" | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment