[[目次へもどる>PuyoPuyo]] * スコアをつけよう [#x78f1c22] #contents やっとこさスコア計算のための準備が整った. とりあえずスコアを管理するScoreManagerを作ろう. またマネージャーかよと思うかもしれないが, マネージャーって「先輩っ」てかわいい感じがするのでこれからも多用します.たぶん. #sh(ruby){{ class ScoreManager attr_accessor :score def initialize @score = 0 end def add_chain_score() end end }} スコアを保持する変数を作り,連鎖によるスコアを計算するメソッドを作った.高速落下によるスコアの加算はアクセサがあるのでとりあえず適当に足せば良いだろう. ** スコア計算 [#m670806a] スコア計算メソッドを作るのはスコア計算の仕組みを理解しなければならない. とりあえずということで「ぷよぷよ!!20th anniversary」のぷよぷよ通ルールの得点計算方式&各種倍率を採用することにする. ちなみにスコア計算やジャマー発生数など各種情報の参照元は以下. - http://www45.atwiki.jp/puyo20th/ まずは連鎖のスコアを求める式を見てみよう. 連鎖のスコア = ベーススコア * 倍率ボーナス なるほどわからん. ではベーススコアはどうやって求めるのか. ベーススコア = ブロックを消した数 * 10 非常にわかりやすくて助かります. では次に倍率ボーナスの式を見てみよう. 倍率ボーナス = (連鎖ボーナス * ハンデ補正).to_i + 多数同時消しボーナス + 多色同時消しボーナス ただし,最小値は1,最大値は999 ハンデ補正は甘口とか辛口とかそういうやつ.現在ハンデは考慮していないのでそれを省けば謎のボーナス三種の合計値が倍率ボーナスということになる. これらの三種ボーナスは表で与えられる. 連鎖ボーナスは現在の連鎖数によって変動する値で以下の表に基づく. |連鎖数|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|h |ボーナス|0|8|16|32|64|96|128|160|192|224|256|288|320|352|384|416|448|480| |連鎖数|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35以上|h |ボーナス|512|544|576|608|640|672|704|736|768|800|832|864|896|928|960|992|999| こんなに書いといてなんだが結局のところ以下の式である. 連鎖ボーナス(c) = c <= 3 ? (c-1)*8 : [c-3*32, 999].min ただし,cは連鎖数である 多数同時消しボーナスはつながりの数ごとに得られるボーナスである. ボーナスは以下の表で与えられる. |つながり数|4以下|5|6|7|8|9|10|11以上|h |ボーナス|0|2|3|4|5|6|7|10| たとえば,5個のかたまりと4個のかたまりを同時に消すと2+0=2のボーナス,7個のかたまりのみを消すと4のボーナスとなる. 多色同時消しボーナスは同時に消した色の数で得られるボーナスである. ボーナスは以下の表で与えられる. |色数|1|2|3|4|5|h |ボーナス|0|3|6|12|24| さてこれでスコア計算に必要な要素はわかった. add_chain_scoreに与えるべき引数は, + 連鎖数 + コネクションの配列 + ブロックを消した数 + 消した色の数 このうち連鎖数は新たに変数を設ける必要があるが,残りの3つに関してはコネクションテーブルのみから全て得られる情報である. ** 実装 [#n4c73e33] ここまでの考察を元にスコアを導入してみよう. #sh(ruby){{ class ScoreManager attr_accessor :score attr_reader :base_score, :chain_bonus, :amount_bonus, :color_bonus def initialize @score = 0 clear_chain_score end def clear_chain_score @base_score = 0 @chain_bonus = 0 @amount_bonus = 0 @color_bonus = 0 end def chain_bonus_table(c) if c <= 3 (c - 1) * 8 elsif c < 35 (c - 3) * 32 else 999 end end def amount_bonus_table(n) if n < 5 0 elsif n > 10 10 else n - 3 end end def color_bonus_table(c) c < 2 ? 0 : 3 * 2 ** (c - 2) end def calc_chain_score(connect_table, chain) return 0 if chain == 0 || connect_table.empty? clear_chain_score elimination = 0 jammers = [] colors = [] connect_table.each do |connection| size = connection[:blocks].size @amount_bonus += amount_bonus_table(size) elimination += size jammers.concat connection[:jammers] colors.push connection[:blocks].first.color end @base_score = (elimination + jammers.uniq.size) * 10 @color_bonus = color_bonus_table(colors.uniq.size) @chain_bonus = chain_bonus_table(chain) chain_score end def mag_bonus [[@chain_bonus + @amount_bonus + @color_bonus, 1].max, 999].min end def chain_score @base_score * self.mag_bonus end def debug_sprintf sprintf("%d [%d = %d * (%d + %d + %d)]", @score, chain_score, @base_score, @chain_bonus, @amount_bonus, @color_bonus) end end }} 利便性を考えて,ScoreManagerではcalc_chain_scoreメソッドにコネクションテーブルと連鎖数を与えるとベーススコア,連鎖ボーナス,同時多数ボーナス,同時色数ボーナスを計算,保持し,その後各種メソッドにて倍率ボーナスや連鎖スコアを求められるようにした. #sh(ruby){{ class FieldController def initialize(x,y,row_s, line_s, block_s) @x = x; @y = y @colors = [:r, :g, :b, :y] @row_s = row_s; @line_s = line_s; @block_s = block_s init_control_block_manager init_score_manager init_field init_phase end def init_score_manager @sm = ScoreManager.new end def init_field @field = Field.new(@row_s, @line_s, @block_s, @cbm, @sm) end end }} スコアはプレイヤー一人につき一つなので,FieldControllerクラスにScoreManagerを追加する.その後,Fieldからも参照できるようにFieldのインスタンスに@smを渡す. #sh(ruby)){{ class Field def initialize(row_s, line_s, block_s, cbm, sm) @row_s = row_s @line_s = line_s @block_s = block_s @fallen = false @eliminated = false @cbm = cbm @sm = sm @chain = 0 init_table init_jammer_manager init_blocklist init_connect_table end def update_control_block_move_y(iff,imf) fall_y = @cbm.ctrl_block.can_falldown?(@table, @block_s) if fall_y > 0 # fall # add score @sm.score += 1 if iff && !imf && !@cbm.ctrl_block.momentfall speed = iff ? 6 : 0.8 speed = imf || @cbm.ctrl_block.momentfall ? 64 : speed @cbm.ctrl_block.momentfall = true if imf @cbm.ctrl_block.falldown(fall_y > speed ? speed : fall_y) elsif fall_y < 0 # dent (略) end def eliminate_connection @eliminated = !@connect_table.empty? # check flag @connect_table.each do |connection| #### test jammer #### @jm.jammers += connection[:blocks].size # delete blocks connection[:blocks].each do |block| block.set_collapse(40) @table[block.row][block.line] = nil @active_blocks.delete block @collapse_blocks.push block end # delete jammers [naive] connection[:jammers].each do |jammer| next unless @jamming_blocks.include?(jammer) # already be deleted jammer.set_collapse(40) @table[jammer.row][jammer.line] = nil @jamming_blocks.delete jammer @collapse_blocks.push jammer end end end def fair_connect_table @connect_table.select!{|connection| connection[:blocks].size >= 4} end def eliminate @eliminated = false make_connect_table fair_connect_table @chain += 1 unless @connect_table.empty? @sm.score += @sm.calc_chain_score(@connect_table, @chain) eliminate_connection @eliminated end def start_fall_jammer # reset chain @chain = 0 (略) end end }} まず連鎖数を記録するインスタンス変数@chainを追加した.連鎖数はブロックの消去時に,一つでも消去されるコネクションがあれば1加算される. 連鎖数はジャマーの落下フェーズに入るときに0に初期化される. また,スコアが加算されるの箇所は二つある. 一つはコントロールブロックを高速落下させたときである.1フレームに付き1のスコアが加算される.実際は1マスに付き1スコアなのだがどうせジャマーの発生数に関わらないので別にいいだろう((正確にはぷよぷよ!ではこの落下ボーナスとして加算されるが,その他ではは加算されない)). 二つ目は連鎖時である.コネクションチェックで生成されたコネクションテーブルはfair_connect_tableによって消去されるコネクションのみが残される. あとはスコアマネージャーにこれを引き渡せばスコアが算出される. ** 実行 [#fa074d2e] ちゃんとスコアが計算されている.と思う. #media(PuyoPuyoChap27/PuyoPuyoChap27.flv);