Skip to content

Commit

Permalink
Merge branch 'master' of github.com:gimite/mjai
Browse files Browse the repository at this point in the history
  • Loading branch information
gimite committed Dec 26, 2013
2 parents 2f9358d + 6b88cc0 commit f7a6cf2
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 4 deletions.
9 changes: 9 additions & 0 deletions lib/mjai/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,15 @@ def action_in_view(action, player_id, for_response)
else
return action
end
when :reach
if action.actor == player
return action.merge({
:cannot_dahai =>
with_response_hint ? (player.tehais.uniq() - player.possible_dahais) : nil,
})
else
return action
end
else
return action
end
Expand Down
176 changes: 172 additions & 4 deletions lib/mjai/game_stats.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# coding: utf-8

require "mjai/archive"
require "mjai/confidence_interval"

Expand All @@ -6,9 +8,36 @@ module Mjai

class GameStats

YAKU_JA_NAMES = {
:menzenchin_tsumoho => "面前清自摸和", :reach => "立直", :ippatsu => "一発",
:chankan => "槍槓", :rinshankaiho => "嶺上開花", :haiteiraoyue => "海底摸月",
:hoteiraoyui => "河底撈魚", :pinfu => "平和", :tanyaochu => "断么九",
:ipeko => "一盃口", :jikaze => "面風牌", :bakaze => "圏風牌",
:sangenpai => "三元牌", :double_reach => "ダブル立直", :chitoitsu => "七対子",
:honchantaiyao => "混全帯么九", :ikkitsukan => "一気通貫",
:sanshokudojun => "三色同順", :sanshokudoko => "三色同刻", :sankantsu => "三槓子",
:toitoiho => "対々和", :sananko => "三暗刻", :shosangen => "小三元",
:honroto => "混老頭", :ryanpeko => "二盃口", :junchantaiyao => "純全帯么九",
:honiso => "混一色", :chiniso => "清一色", :renho => "人和", :tenho => "天和",
:chiho => "地和", :daisangen => "大三元", :suanko => "四暗刻",
:tsuiso => "字一色", :ryuiso => "緑一色", :chinroto => "清老頭",
:churenpoton => "九蓮宝燈", :kokushimuso => "国士無双",
:daisushi => "大四喜", :shosushi => "小四喜", :sukantsu => "四槓子",
:dora => "ドラ", :uradora => "裏ドラ", :akadora => "赤ドラ",
}

def self.print(mjson_paths)
num_errors = 0
name_to_ranks = {}
name_to_scores = {}
name_to_kyoku_count = {}
name_to_hora_count = {}
name_to_yaku_stats = {}
name_to_dora_stats = {}
name_to_hoju_count = {}
name_to_furo_kyoku_count = {}
name_to_reach_count = {}
name_to_hora_points = {}
for path in mjson_paths
archive = Archive.load(path)
first_action = archive.raw_actions[0]
Expand All @@ -18,20 +47,92 @@ def self.print(mjson_paths)
num_errors += 1
next
end

id_to_name = first_action.names

chicha_id = archive.raw_actions[1].oya.id
ranked_player_ids =
(0...4).sort_by(){ |i| [-last_action.scores[i], (i + 4 - chicha_id) % 4] }
for r in 0...4
name = first_action.names[ranked_player_ids[r]]
name = id_to_name[ranked_player_ids[r]]
name_to_ranks[name] ||= []
name_to_ranks[name].push(r + 1)
end

for p in 0...4
name = id_to_name[p]
name_to_scores[name] ||= []
name_to_scores[name].push(last_action.scores[p])
end

# Kyoku specific fields.
id_to_done_reach = {}
id_to_done_furo = {}
for raw_action in archive.raw_actions
if raw_action.type == :hora
name = id_to_name[raw_action.actor.id]
name_to_hora_count[name] ||= 0
name_to_hora_count[name] += 1
name_to_hora_points[name] ||= []
name_to_hora_points[name].push(raw_action.hora_points)
for yaku, fan in raw_action.yakus
if yaku == :dora || yaku == :akadora || yaku == :uradora
name_to_dora_stats[name] ||= {}
name_to_dora_stats[name][yaku] ||= 0
name_to_dora_stats[name][yaku] += fan
next
end
name_to_yaku_stats[name] ||= {}
name_to_yaku_stats[name][yaku] ||= 0
name_to_yaku_stats[name][yaku] += 1
end
if raw_action.actor.id != raw_action.target.id
target_name = id_to_name[raw_action.target.id]
name_to_hoju_count[target_name] ||= 0
name_to_hoju_count[target_name] += 1
end
end
if raw_action.type == :reach_accepted
id_to_done_reach[raw_action.actor.id] = true
end
if raw_action.type == :pon
id_to_done_furo[raw_action.actor.id] = true
end
if raw_action.type == :chi
id_to_done_furo[raw_action.actor.id] = true
end
if raw_action.type == :daiminkan
id_to_done_furo[raw_action.actor.id] = true
end
if raw_action.type == :end_kyoku
for p in 0...4
name = id_to_name[p]

if id_to_done_furo[p]
name_to_furo_kyoku_count[name] ||= 0
name_to_furo_kyoku_count[name] += 1
end
if id_to_done_reach[p]
name_to_reach_count[name] ||= 0
name_to_reach_count[name] += 1
end

name_to_kyoku_count[name] ||= 0
name_to_kyoku_count[name] += 1
end

# Reset kyoku specific fields.
id_to_done_furo = {}
id_to_done_reach = {}
end
end
end
if num_errors > 0
puts("errors: %d / %d" % [num_errors, mjson_paths.size])
end
puts("ranks:")
for name, ranks in name_to_ranks

puts("Ranks:")
for name, ranks in name_to_ranks.sort
rank_conf_interval = ConfidenceInterval.calculate(ranks, :min => 1.0, :max => 4.0)
puts(" %s: %.3f [%.3f, %.3f]" % [
name,
Expand All @@ -40,8 +141,75 @@ def self.print(mjson_paths)
rank_conf_interval[1],
])
end
end

puts("Scores:")
for name, scores in name_to_scores.sort
puts(" %s: %d" % [
name,
scores.inject(0, :+).to_i() / scores.size,
])
end

puts("Hora rates:")
for name, hora_count in name_to_hora_count.sort
puts(" %s: %.1f%%" % [
name,
100.0 * hora_count / name_to_kyoku_count[name]
])
end

puts("Hoju rates:")
for name, hoju_count in name_to_hoju_count.sort
puts(" %s: %.1f%%" % [
name,
100.0 * hoju_count / name_to_kyoku_count[name]
])
end

puts("Furo rates:")
for name, furo_kyoku_count in name_to_furo_kyoku_count.sort
puts(" %s: %.1f%%" % [
name,
100.0 * furo_kyoku_count / name_to_kyoku_count[name]
])
end

puts("Reach rates:")
for name, reach_count in name_to_reach_count.sort
puts(" %s: %.1f%%" % [
name,
100.0 * reach_count / name_to_kyoku_count[name]
])
end

puts("Average hora points:")
for name, hora_points in name_to_hora_points.sort
puts(" %s: %d" % [
name,
hora_points.inject(0, :+).to_i() / hora_points.size,
])
end

puts("Yaku stats:")
for name, yaku_stats in name_to_yaku_stats.sort
hora_count = name_to_hora_count[name]
puts(" %s (%d horas):" % [name, hora_count])
for yaku, count in yaku_stats.sort_by{|yaku, count| -count}
yaku_name = YAKU_JA_NAMES[yaku]
puts(" %s: %d (%.1f%%)" % [yaku_name, count, 100.0 * count / hora_count])
end
end

puts("Dora stats:")
for name, dora_stats in name_to_dora_stats.sort
hora_count = name_to_hora_count[name]
puts(" %s (%d horas):" % [name, hora_count])
for dora, count in dora_stats.sort_by{|dora, count| -count}
dora_name = YAKU_JA_NAMES[dora]
puts(" %s: %d (%.3f/hora)" % [dora_name, count, count.to_f() / hora_count])
end
end
end
end

end

0 comments on commit f7a6cf2

Please sign in to comment.