0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

雀魂友人戦の成績管理Botを作成した話➁(コードあり)

Last updated at Posted at 2025-07-27

はじめに

こちらの記事は、雀魂友人戦の成績管理Botを作成した話①の続きになります

こちらの背景や目的については上記の記事をご確認ください

やりたいこと(再掲)

大きく分けて以下の3つです

  1. 雀魂の対局結果を取得(済)
    • できるだけ手動入力は避けたい
      牌譜のダウンロードは手動だが、データ読み込みはプログラムで対応できた
  2. 各種スタッツ、ポイントの計算
    • 各試合の立直回数、和了回数、放銃回数の集計ができる
    • 通算の立直率、和了率、放銃率、副露率、ポイントの集計ができる
    • ポイントは、様々なルールに対応できるように
  3. アクセスしやすい場所への格納
    • 自分ひとりだけでなく、友人も含め簡単に見ることができる

このページでは
2. 各種スタッツ、ポイントの計算
について記述します

  1. 雀魂の対局結果を取得については、雀魂友人戦の成績管理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

だいたいイメージ通りの結果が出てきています

次回はこれを私一人ではなく、各プレイヤーがアクセスしやすい(編集はできない)ようにすることを目標に動いていきたいと思います!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?