スコアをつけよう †やっとこさスコア計算のための準備が整った. とりあえずスコアを管理するScoreManager?を作ろう. またマネージャーかよと思うかもしれないが, マネージャーって「先輩っ」てかわいい感じがするのでこれからも多用します.たぶん. class ScoreManager attr_accessor :score def initialize @score = 0 end def add_chain_score() end end スコアを保持する変数を作り,連鎖によるスコアを計算するメソッドを作った.高速落下によるスコアの加算はアクセサがあるのでとりあえず適当に足せば良いだろう. スコア計算 †スコア計算メソッドを作るのはスコア計算の仕組みを理解しなければならない. とりあえずということで「ぷよぷよ!!20th anniversary」のぷよぷよ通ルールの得点計算方式&各種倍率を採用することにする. ちなみにスコア計算やジャマー発生数など各種情報の参照元は以下. まずは連鎖のスコアを求める式を見てみよう. 連鎖のスコア = ベーススコア * 倍率ボーナス なるほどわからん. ではベーススコアはどうやって求めるのか. ベーススコア = ブロックを消した数 * 10 非常にわかりやすくて助かります. では次に倍率ボーナスの式を見てみよう. 倍率ボーナス = (連鎖ボーナス * ハンデ補正).to_i + 多数同時消しボーナス + 多色同時消しボーナス
ただし,最小値は1,最大値は999
ハンデ補正は甘口とか辛口とかそういうやつ.現在ハンデは考慮していないのでそれを省けば謎のボーナス三種の合計値が倍率ボーナスということになる. これらの三種ボーナスは表で与えられる. 連鎖ボーナスは現在の連鎖数によって変動する値で以下の表に基づく.
こんなに書いといてなんだが結局のところ以下の式である. 連鎖ボーナス(c) = c <= 3 ? (c-1)*8 : [c-3*32, 999].min
ただし,cは連鎖数である
多数同時消しボーナスはつながりの数ごとに得られるボーナスである. ボーナスは以下の表で与えられる.
たとえば,5個のかたまりと4個のかたまりを同時に消すと2+0=2のボーナス,7個のかたまりのみを消すと4のボーナスとなる. 多色同時消しボーナスは同時に消した色の数で得られるボーナスである. ボーナスは以下の表で与えられる.
さてこれでスコア計算に必要な要素はわかった. add_chain_scoreに与えるべき引数は,
このうち連鎖数は新たに変数を設ける必要があるが,残りの3つに関してはコネクションテーブルのみから全て得られる情報である. 実装 †ここまでの考察を元にスコアを導入してみよう. 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メソッドにコネクションテーブルと連鎖数を与えるとベーススコア,連鎖ボーナス,同時多数ボーナス,同時色数ボーナスを計算,保持し,その後各種メソッドにて倍率ボーナスや連鎖スコアを求められるようにした. 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を渡す. 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スコアなのだがどうせジャマーの発生数に関わらないので別にいいだろう*1. 二つ目は連鎖時である.コネクションチェックで生成されたコネクションテーブルはfair_connect_tableによって消去されるコネクションのみが残される. あとはスコアマネージャーにこれを引き渡せばスコアが算出される. 実行 †ちゃんとスコアが計算されている.と思う. Loading the player ...
|