[[目次へもどる>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);


トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS