はじめに
こちらの記事は、雀魂友人戦の成績管理Botを作成した話①の続きになります
こちらの背景や目的については上記の記事をご確認ください
やりたいこと(再掲)
大きく分けて以下の3つです
-
雀魂の対局結果を取得(済)
- できるだけ手動入力は避けたい
→牌譜のダウンロードは手動だが、データ読み込みはプログラムで対応できた
- できるだけ手動入力は避けたい
-
各種スタッツ、ポイントの計算
- 各試合の立直回数、和了回数、放銃回数の集計ができる
- 通算の立直率、和了率、放銃率、副露率、ポイントの集計ができる
- ポイントは、様々なルールに対応できるように
-
アクセスしやすい場所への格納
- 自分ひとりだけでなく、友人も含め簡単に見ることができる
このページでは
2. 各種スタッツ、ポイントの計算
について記述します
- 雀魂の対局結果を取得については、雀魂友人戦の成績管理Botを作成した話①をご覧ください
各種スタッツ、ポイントの計算
各対局の立直回数、和了回数、放銃回数の集計
前回のPythonプログラムを用いることで、
accounts
には、プレイヤー情報
result
には、対局結果
data_section
には、各種データを読み込んだものが入っており
user_stats
に1対局に関する集計情報を格納できました
これらをデータフレームとしましょう
# accountsデータを取得
accounts_df = pd.DataFrame(accounts)
accounts_df = accounts_df[['account_id', 'nickname', 'seat']]
# resultデータを取得
result_df = pd.DataFrame(result)
# user_statsをデータフレームに変換
user_stats_df = pd.DataFrame.from_dict(user_stats, orient='index').reset_index()
user_stats_df.rename(columns={'index': 'seat'}, inplace=True)
さらに、ポイント制を追加しましょう
自分が導入した順位点、Mリーグと同じ
- 1位:50,000点
- 2位:10,000点
- 3位:-10,000点
- 4位:-30,000点
としました(トップ賞20,000点も含めております)
また同着はMリーグ同様、順位点を分け合う形にしております
(雀魂では席順)
# 順位を計算(同率順位、スキップあり)
result_df['rank'] = result_df['part_point_1'].rank(method='min', ascending=False).astype(int)
# 順位ごとの得点テーブル
point_table = {
1: 50000,
2: 10000,
3: -10000,
4: -30000
}
# 各順位に対して、同着順位の平均得点を割り振る関数
def get_adjusted_point(rank_series):
adjusted_points = []
for rank in rank_series:
# 同じ順位の人の数
same_ranks = rank_series[rank_series == rank]
# 同着した順位の範囲(例:2人で2位→rank: 2,3)
tied_ranks = list(range(rank, rank + len(same_ranks)))
# 対応する点数(ない順位は0点)
scores = [point_table.get(r, 0) for r in tied_ranks]
# 平均して加点
avg_score = sum(scores) / len(scores)
adjusted_points.append(avg_score)
return adjusted_points
# 順位点の加点(同率処理込み)
result_df['rank_bonus'] = get_adjusted_point(result_df['rank'])
また、ポイントは、1,000点 = 1pt
として少数第一位まで変換します
# 30000点を引いて、1000点=1ptとして、小数第1位に変換
result_df['converted_point'] = round(((result_df['part_point_1'] + result_df['rank_bonus'] - 30000) / 1000), 1)
最終的にこれらを結合して、ファイルに書き出します
# accounts_df, result_df, user_stats_dfを結合
# 各データは対局開始時の座り位置で管理されているため、seatで結合
merge_df = accounts_df.merge(result_df, on='seat', how='inner')
merge_df = merge_df.merge(user_stats_df, on='seat', how='inner')
# UUIDを追加
merge_df['uuid'] = new_uuid
merge_df['seat'] = merge_df['seat'].apply(seat_to_seat)
# 必要な列を選択
merge_df = merge_df[['uuid', 'account_id', 'nickname', 'seat', 'converted_point', 'rank',
'total_games', 'riichi_count', 'agari_count', 'houjuu_count', 'furo_games']]
# 既存データに新しいデータを追加
if not existing_data.empty:
merge_df = pd.concat([existing_data, merge_df], ignore_index=True)
# CSV形式で保存
merge_df.sort_values('uuid', ascending=False).to_csv(output_file, index=False)
これで、1対局の全プレイヤーの諸情報がcsvファイルになりました
改行が入って不格好ですが、データ形式のイメージになります
uuid | account_id | nickname | seat | converted_point | rank | total_games | riichi_count | agari_count | houjuu_count | furo_games |
---|---|---|---|---|---|---|---|---|---|---|
a | 1 | あ | 西 | -40.6 | 4 | 10 | 0 | 3 | 4 | 5 |
a | 2 | い | 北 | -18.6 | 3 | 10 | 3 | 1 | 0 | 2 |
a | 3 | う | 南 | 50.5 | 1 | 10 | 2 | 3 | 1 | 5 |
a | 4 | え | 東 | 8.7 | 2 | 10 | 2 | 3 | 2 | 4 |
通算の立直率、和了率、放銃率、副露率、ポイントの集計
各対局のデータができれば通算も集計しましょう
これはデータフレームでgroup byするだけなので簡単です
# 元のCSVを読み込む
df = pd.read_csv(input_file)
# 集計処理
grouped = df.groupby('account_id').agg({
'uuid': 'count', # 対局数
'nickname': 'first', # ニックネーム
'total_games': 'sum', # 総対局数
'riichi_count': 'sum', # 立直回数
'agari_count': 'sum', # 上がり回数
'houjuu_count': 'sum', # 放銃回数
'furo_games': 'sum', # 副露した局数
'converted_point': 'sum', # ポイントの合計
'rank': 'mean' # 平均順位
}).reset_index()
# 立直率、上がり率、放銃率、副露率を計算
grouped['riichi_rate'] = (grouped['riichi_count'] / grouped['total_games'] * 100).round(2)
grouped['agari_rate'] = (grouped['agari_count'] / grouped['total_games'] * 100).round(2)
grouped['houjuu_rate'] = (grouped['houjuu_count'] / grouped['total_games'] * 100).round(2)
grouped['furo_rate'] = (grouped['furo_games'] / grouped['total_games'] * 100).round(2)
grouped['converted_point'] = round(grouped['converted_point'], 1)
grouped['rank'] = grouped['rank'].round(2) # 平均順位を小数点以下2桁に丸める
# 総局数を追加
grouped['total_rounds'] = grouped['uuid']
# 必要な列を選択
grouped = grouped[['account_id', 'nickname', 'total_games', 'total_rounds', 'converted_point', 'rank',
'riichi_rate', 'agari_rate', 'houjuu_rate', 'furo_rate']]
grouped.rename(columns={'account_id': 'アカウントID', 'nickname': 'ユーザー名', 'total_rounds': '試合数',
'total_games': '総局数', 'converted_point': 'ポイント', 'rank': '平均着順',
'riichi_rate': '立直率(%)', 'agari_rate': '和了率(%)',
'houjuu_rate': '放銃率(%)', 'furo_rate': '副露率(%)'}, inplace=True)
# CSV形式で保存
grouped.to_csv(output_file, index=False, encoding='utf-8-sig')
こちらのデータ形式は以下になります
数値には仮の値を入れています
アカウントID | ユーザー名 | 総局数 | 試合数 | ポイント | 平均着順 | 立直率(%) | 和了率(%) | 放銃率(%) | 副露率(%) |
---|---|---|---|---|---|---|---|---|---|
ID001 | プレイヤー1 | 72 | 8 | 98.4 | 2.00 | 25.00 | 20.83 | 6.25 | 20.83 |
ID002 | プレイヤー2 | 64 | 7 | 18.7 | 2.43 | 24.61 | 18.75 | 12.50 | 23.44 |
ID003 | プレイヤー3 | 60 | 7 | 42.3 | 2.29 | 17.21 | 21.31 | 10.66 | 50.00 |
ID004 | プレイヤー4 | 68 | 7 | -10.2 | 2.71 | 28.00 | 26.00 | 11.00 | 35.00 |
ID005 | プレイヤー5 | 62 | 7 | -155.0 | 2.95 | 20.00 | 18.00 | 19.00 | 28.00 |
だいたいイメージ通りの結果が出てきています
次回はこれを私一人ではなく、各プレイヤーがアクセスしやすい(編集はできない)ようにすることを目標に動いていきたいと思います!