libs.domain.score

libs/domain/score.py

  1"""
  2libs/domain/score.py
  3"""
  4
  5import re
  6from dataclasses import dataclass, field
  7from typing import TYPE_CHECKING, Any, Literal, Optional, cast
  8
  9import pandas as pd
 10
 11if TYPE_CHECKING:
 12    from libs.types import ScoreDict
 13
 14
 15@dataclass
 16class Score:
 17    """
 18    プレイヤー成績
 19
 20    Note:
 21        フィールド名の 'r_' プレフィックス (r_str, rpoint など) は、
 22        GameResult.to_dict() によって 'p1_', 'p2_', 'p3_', 'p4_' に置換され、
 23        DBテーブルのカラム名 (p1_str, p2_str など) として使用する。
 24
 25    """
 26
 27    name: str = field(default="")
 28    """プレイヤー名"""
 29    r_str: str = field(default="")
 30    """素点(ユーザー入力文字列/未計算)"""
 31    rpoint: int = field(default=0)
 32    """素点(ユーザー入力文字列評価後)"""
 33    point: float = field(default=0.0)
 34    """獲得ポイント"""
 35    rank: int = field(default=0)
 36    """獲得順位"""
 37
 38    def has_valid_data(self) -> bool:
 39        """有効なデータを持っているかチェック"""
 40        return self != Score()
 41
 42    def to_dict(self, prefix: str) -> "ScoreDict":
 43        """
 44        データを辞書で返す
 45
 46        Args:
 47            prefix (str): キーに付与する接頭辞 (p1, p2, p3, p4)
 48
 49        Returns:
 50            ScoreDict: 返却する辞書
 51
 52        Note:
 53            フィールド名の 'r_' プレフィックスは、指定された prefix に置換される。
 54            例: r_str -> p1_str (prefix='p1' の場合)
 55
 56        """
 57        return cast(
 58            "ScoreDict",
 59            {
 60                f"{prefix}_name": self.name,
 61                f"{prefix}_str": self.r_str,
 62                f"{prefix}_rpoint": self.rpoint,
 63                f"{prefix}_point": self.point,
 64                f"{prefix}_rank": self.rank,
 65            },
 66        )
 67
 68
 69class GameResult:
 70    """スコアデータ"""
 71
 72    def __init__(self, **kwargs: Any) -> None:
 73        # ゲーム結果
 74        self.ts: str = ""
 75        """タイムスタンプ"""
 76        self.p1: Score = Score()
 77        """東家成績"""
 78        self.p2: Score = Score()
 79        """南家成績"""
 80        self.p3: Score = Score()
 81        """西家成績"""
 82        self.p4: Score = Score()
 83        """北家成績"""
 84        self.comment: Optional[str] = None
 85        """ゲームコメント"""
 86        self.deposit: int = 0
 87        """供託"""
 88
 89        # 付属情報
 90        self.mode: Literal[3, 4] = 4
 91        """集計モード(三人打ち/四人打ち)"""
 92        self.rule_version: str = ""
 93        """ルール識別子"""
 94        self.origin_point: int = 250
 95        """配給原点"""
 96        self.return_point: int = 300
 97        """返し点"""
 98        self.rank_point: list[int] = [30, 10, -10, -30]
 99        """順位点"""
100        self.draw_split: bool = False
101        """同着時に順位点を山分けにするか"""
102        self.source: Optional[str] = None
103        """データ入力元識別子"""
104
105        self.calc(**kwargs)
106
107    def __bool__(self) -> bool:
108        return all(self.to_list("name") + self.to_list("str"))
109
110    def __eq__(self, other: Any) -> bool:
111        if not isinstance(other, GameResult):
112            return NotImplemented
113
114        return all(
115            [
116                self.mode == other.mode,
117                self.ts == other.ts,
118                self.p1.name == other.p1.name,
119                self.p1.rpoint == other.p1.rpoint,
120                self.p2.name == other.p2.name,
121                self.p2.rpoint == other.p2.rpoint,
122                self.p3.name == other.p3.name,
123                self.p3.rpoint == other.p3.rpoint,
124                self.p4.name == other.p4.name,
125                self.p4.rpoint == other.p4.rpoint,
126                self.rule_version == other.rule_version,
127                self.comment == other.comment,
128                self.source == other.source,
129            ]
130        )
131
132    def __lt__(self, other: Any) -> bool:
133        if not isinstance(other, GameResult):
134            return NotImplemented
135        return self.ts < other.ts
136
137    def has_valid_data(self) -> bool:
138        """DB更新に必要なデータを持っているかチェック"""
139        # スコアデータ
140        match self.mode:
141            case 3:
142                score_data = all(
143                    [
144                        self.p1.has_valid_data(),
145                        self.p2.has_valid_data(),
146                        self.p3.has_valid_data(),
147                    ]
148                )
149            case 4:
150                score_data = all(
151                    [
152                        self.p1.has_valid_data(),
153                        self.p2.has_valid_data(),
154                        self.p3.has_valid_data(),
155                        self.p4.has_valid_data(),
156                    ]
157                )
158            case _:
159                score_data = False
160
161        return all([self.ts, isinstance(self.ts, str), score_data, all(self.to_list("rank"))])
162
163    def set(self, **kwargs: Any) -> None:
164        """データ取り込み"""
165
166        def _normalize_score_string(s: str) -> str:
167            """
168            素点文字列の正規化
169
170            Args:
171                s (str): 入力文字列
172
173            Returns:
174                str: 正規化後の文字列
175
176            """
177            s = s.strip()
178            s = re.sub(r"(-)+|(\+)+", r"\1\2", s)  # 連続した符号を集約
179            s = re.sub(r"(-|\+)0+", r"\1", s)  # 符号の直後のゼロを削除
180            if s != "0":  # 先頭のゼロとプラス記号を削除
181                s = re.sub(r"^[0+]+", "", s)
182            return s
183
184        def _set_score_attr(score: Score, prefix: str, key: str, value: object) -> None:
185            """
186            Scoreオブジェクトに属性を設定
187
188            Args:
189                score (Score): 対象スコアオブジェクト
190                prefix (str): プレイヤーポジション (p1-p4)
191                key (str): 属性名
192                value (object): 設定値
193
194            """
195            match key:
196                case "name":
197                    score.name = str(value)
198                case "str":
199                    score.r_str = _normalize_score_string(str(value))
200                case "r_str":
201                    score.r_str = str(value)
202                case "rpoint" if isinstance(value, int):
203                    score.rpoint = value
204                case "point" if isinstance(value, (float, int)):
205                    score.point = float(value)
206                case "rank" if isinstance(value, int):
207                    score.rank = value
208
209        # プレイヤースコア設定
210        for prefix in ("p1", "p2", "p3", "p4"):
211            score_obj = cast(Score, getattr(self, prefix))
212            for attr in ("name", "str", "r_str", "rpoint", "point", "rank"):
213                key = f"{prefix}_{attr}"
214                if key in kwargs:
215                    _set_score_attr(score_obj, prefix, attr, kwargs[key])
216
217        # ゲーム設定
218        if "mode" in kwargs and isinstance(kwargs["mode"], int):
219            if kwargs["mode"] in (3, 4):
220                self.mode = kwargs["mode"]  # type: ignore[assignment]
221        if "ts" in kwargs and isinstance(kwargs["ts"], str):
222            self.ts = kwargs["ts"]
223        if "rule_version" in kwargs and isinstance(kwargs["rule_version"], str):
224            self.rule_version = str(kwargs["rule_version"])
225        if "deposit" in kwargs and isinstance(kwargs["deposit"], int):
226            self.deposit = int(kwargs["deposit"])
227        if "origin_point" in kwargs and isinstance(kwargs["origin_point"], int):
228            self.origin_point = int(kwargs["origin_point"])
229        if "return_point" in kwargs and isinstance(kwargs["return_point"], int):
230            self.return_point = int(kwargs["return_point"])
231        if "rank_point" in kwargs and isinstance(kwargs["rank_point"], list):
232            self.rank_point = kwargs["rank_point"]
233        if "draw_split" in kwargs and isinstance(kwargs["draw_split"], bool):
234            self.draw_split = kwargs["draw_split"]
235        if "comment" in kwargs:
236            self.comment = kwargs["comment"]
237        if "source" in kwargs:
238            self.source = kwargs["source"]
239
240    def to_dict(self) -> "ScoreDict":
241        """
242        データを辞書で返す
243
244        Returns:
245            ScoreDict: スコアデータ
246
247        """
248        return {
249            "ts": self.ts,
250            **self.p1.to_dict("p1"),
251            **self.p2.to_dict("p2"),
252            **self.p3.to_dict("p3"),
253            **self.p4.to_dict("p4"),
254            "deposit": self.deposit,
255            "comment": self.comment,
256            "rule_version": self.rule_version,
257            "source": self.source,
258            "mode": self.mode,
259        }
260
261    def to_text(self, kind: Literal["simple", "detail", "logging"] = "simple") -> str:
262        """
263        データをテキストで返す
264
265        Args:
266            kind (Literal, optional): 表示形式
267                - *simple*: 簡易情報 (Default)
268                - *detail*: 詳細情報
269                - *logging*: ロギング用
270
271        Returns:
272            str: スコアデータ
273
274        """
275        ret_text: str = ""
276        match kind:
277            case "simple":
278                ret_text += f"[{self.p1.name} {self.p1.r_str}] "
279                ret_text += f"[{self.p2.name} {self.p2.r_str}] "
280                ret_text += f"[{self.p3.name} {self.p3.r_str}] "
281                ret_text += f"[{self.p4.name} {self.p4.r_str}] " if self.mode == 4 else ""
282                ret_text += f"[供託 {self.deposit}] [{self.comment if self.comment else None}]"
283            case "detail":
284                ret_text += f"[{self.p1.rank}{self.p1.name} {self.p1.rpoint * 100}点 ({self.p1.point}pt)] ".replace("-", "▲")
285                ret_text += f"[{self.p2.rank}{self.p2.name} {self.p2.rpoint * 100}点 ({self.p2.point}pt)] ".replace("-", "▲")
286                ret_text += f"[{self.p3.rank}{self.p3.name} {self.p3.rpoint * 100}点 ({self.p3.point}pt)] ".replace("-", "▲")
287                ret_text += f"[{self.p4.rank}{self.p4.name} {self.p4.rpoint * 100}点 ({self.p4.point}pt)] ".replace("-", "▲") if self.mode == 4 else ""
288                ret_text += f"[供託 {self.deposit * 100}点] "
289                ret_text += f"[{self.comment if self.comment else None}]"
290            case "logging":
291                ret_text += f"ts={self.ts}, deposit={self.deposit}, rule_version={self.rule_version}, "
292                ret_text += f"p1={self.p1.to_dict('p1')}, p2={self.p2.to_dict('p2')}, p3={self.p3.to_dict('p3')}, "
293                ret_text += f"p4={self.p4.to_dict('p4')}, " if self.mode == 4 else ""
294                ret_text += f"comment={self.comment if self.comment else None}, source={self.source}"
295
296        return ret_text
297
298    def to_list(self, kind: Literal["name", "str", "rpoint", "point", "rank"] = "name") -> list[str | int | float]:
299        """
300        指定データをリストで返す
301
302        Args:
303            kind (Literal, optional): 取得内容
304                - *name*: プレイヤー名 (Default)
305                - *str*: 入力された素点情報
306                - *rpoint*: 素点
307                - *point*: ポイント
308                - *rank*: 順位
309
310        Returns:
311            list[str | int | float]: リスト
312
313        """
314        ret_list: list[str | int | float] = []
315        match kind:
316            case "name":
317                ret_list = [self.p1.name, self.p2.name, self.p3.name, self.p4.name]
318            case "str":
319                ret_list = [self.p1.r_str, self.p2.r_str, self.p3.r_str, self.p4.r_str]
320            case "rpoint":
321                ret_list = [self.p1.rpoint, self.p2.rpoint, self.p3.rpoint, self.p4.rpoint]
322            case "point":
323                ret_list = [self.p1.point, self.p2.point, self.p3.point, self.p4.point]
324            case "point":
325                ret_list = [self.p1.point, self.p2.point, self.p3.point, self.p4.point]
326            case "rank":
327                ret_list = [self.p1.rank, self.p2.rank, self.p3.rank, self.p4.rank]
328
329        return ret_list[: self.mode]
330
331    def calc(self, **kwargs: Any) -> None:
332        """獲得ポイント計算"""
333        if kwargs:
334            self.set(**kwargs)
335
336        match self.mode:
337            case 3:
338                if all([self.p1.has_valid_data(), self.p2.has_valid_data(), self.p3.has_valid_data()]):
339                    self.set(**self._calculation_point3())
340                    self.p4 = Score()
341            case 4:
342                if all([self.p1.has_valid_data(), self.p2.has_valid_data(), self.p3.has_valid_data(), self.p4.has_valid_data()]):
343                    self.set(**self._calculation_point4())
344            case _:
345                raise RuntimeError
346
347    def _normalized_expression(self, expr: str) -> int:
348        """
349        入力文字列を式として評価し、計算結果を返す
350
351        Args:
352            expr (str): 入力式
353
354        Returns:
355            int: 計算結果
356
357        """
358        normalized: list[str] = []
359
360        for token in re.findall(r"\d+|[+\-*/]", expr):
361            if isinstance(token, str):
362                if token.isnumeric():
363                    normalized.append(str(int(token)))
364                else:
365                    normalized.append(token)
366
367        return int(eval("".join(normalized)))
368
369    def _calculation_point3(self) -> dict[str, Any]:
370        """
371        獲得ポイントと順位を計算する(三人打ち)
372
373        Returns:
374            dict[str, Any]: 更新用辞書(順位と獲得ポイントのデータ)
375
376        """
377        # 計算用データフレーム
378        score_df = pd.DataFrame({"rpoint": [self._normalized_expression(str(x)) for x in self.to_list("str")]}, index=["p1", "p2", "p3"])
379
380        work_rank_point = self.rank_point.copy()  # ウマ
381        work_rank_point[0] += int((self.return_point - self.origin_point) / 10 * 3)  # オカ
382
383        # 席順
384        score_df["rank"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")
385
386        # 獲得ポイントの計算 (素点-配給原点)/10+順位点
387        score_df["position"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")  # 加算する順位点リストの位置
388        score_df["point"] = (score_df["rpoint"] - self.return_point) / 10 + score_df["position"].apply(lambda p: work_rank_point[p - 1])
389        score_df["point"] = score_df["point"].apply(lambda p: float(f"{p:.1f}"))  # 桁ブレ修正
390
391        # 返却値用辞書
392        ret_dict = {f"{k}_{x}": v for x in score_df.columns for k, v in score_df[x].to_dict().items()}
393        ret_dict.update(deposit=int(self.origin_point * 3 - score_df["rpoint"].sum()))
394
395        return ret_dict
396
397    def _calculation_point4(self) -> dict[str, Any]:
398        """
399        獲得ポイントと順位を計算する(四人打ち)
400
401        Returns:
402            dict[str, Any]: 更新用辞書(順位と獲得ポイントのデータ)
403
404        """
405
406        def point_split(point: list[int]) -> list[int]:
407            """
408            順位点を山分けする
409
410            Args:
411                point (list[int]): 山分けするポイントのリスト
412
413            Returns:
414                list[int]: 山分けした結果
415
416            """
417            new_point = [int(sum(point) / len(point))] * len(point)
418            if sum(point) % len(point):
419                new_point[0] += sum(point) % len(point)
420                if sum(point) < 0:
421                    new_point = list(map(lambda x: x - 1, new_point))
422
423            return new_point
424
425        # 計算用データフレーム
426        score_df = pd.DataFrame({"rpoint": [self._normalized_expression(str(x)) for x in self.to_list("str")]}, index=["p1", "p2", "p3", "p4"])
427
428        work_rank_point = self.rank_point.copy()  # ウマ
429        work_rank_point[0] += int((self.return_point - self.origin_point) / 10 * 4)  # オカ
430
431        if self.draw_split:  # 山分け
432            score_df["rank"] = score_df["rpoint"].rank(ascending=False, method="min").astype("int")
433
434            # 順位点リストの更新
435            match "".join(score_df["rank"].sort_values().to_string(index=False).split()):
436                case "1111":  # 2.5/2.5/2.5/2.5
437                    work_rank_point = point_split(work_rank_point)
438                case "1114":  # 2/2/2/4
439                    new_point = point_split(work_rank_point[0:3])
440                    work_rank_point[0] = new_point[0]
441                    work_rank_point[1] = new_point[1]
442                    work_rank_point[2] = new_point[2]
443                case "1134":  # 1.5/1.5/3/4
444                    new_point = point_split(work_rank_point[0:2])
445                    work_rank_point[0] = new_point[0]
446                    work_rank_point[1] = new_point[1]
447                case "1133":  # 1.5/1.5/3.5/3.5
448                    new_point = point_split(work_rank_point[0:2])
449                    work_rank_point[0] = new_point[0]
450                    work_rank_point[1] = new_point[1]
451                    new_point = point_split(work_rank_point[2:4])
452                    work_rank_point[2] = new_point[0]
453                    work_rank_point[3] = new_point[1]
454                case "1222":  # 1/3/3/3
455                    new_point = point_split(work_rank_point[1:4])
456                    work_rank_point[1] = new_point[0]
457                    work_rank_point[2] = new_point[1]
458                    work_rank_point[3] = new_point[2]
459                case "1224":  # 1/2.5/2.5/4
460                    new_point = point_split(work_rank_point[1:3])
461                    work_rank_point[1] = new_point[0]
462                    work_rank_point[2] = new_point[1]
463                case "1233":  # 1/2/3.5/3.5
464                    new_point = point_split(work_rank_point[2:4])
465                    work_rank_point[2] = new_point[0]
466                    work_rank_point[3] = new_point[1]
467                case _:
468                    pass
469
470        else:  # 席順
471            score_df["rank"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")
472
473        # 獲得ポイントの計算 (素点-配給原点)/10+順位点
474        score_df["position"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")  # 加算する順位点リストの位置
475        score_df["point"] = (score_df["rpoint"] - self.return_point) / 10 + score_df["position"].apply(lambda p: work_rank_point[p - 1])
476        score_df["point"] = score_df["point"].apply(lambda p: float(f"{p:.1f}"))  # 桁ブレ修正
477
478        # 返却値用辞書
479        ret_dict = {f"{k}_{x}": v for x in score_df.columns for k, v in score_df[x].to_dict().items()}
480        ret_dict.update(deposit=int(self.origin_point * 4 - score_df["rpoint"].sum()))
481
482        return ret_dict
483
484    @property
485    def rpoint_sum(self) -> int:
486        """
487        素点合計
488
489        Returns:
490            int: 素点合計
491
492        """
493        if not all(self.to_list("rank")):  # 順位が確定していない場合は先に計算
494            self.calc()
495
496        return sum(cast(list[int], self.to_list("rpoint")))
@dataclass
class Score:
16@dataclass
17class Score:
18    """
19    プレイヤー成績
20
21    Note:
22        フィールド名の 'r_' プレフィックス (r_str, rpoint など) は、
23        GameResult.to_dict() によって 'p1_', 'p2_', 'p3_', 'p4_' に置換され、
24        DBテーブルのカラム名 (p1_str, p2_str など) として使用する。
25
26    """
27
28    name: str = field(default="")
29    """プレイヤー名"""
30    r_str: str = field(default="")
31    """素点(ユーザー入力文字列/未計算)"""
32    rpoint: int = field(default=0)
33    """素点(ユーザー入力文字列評価後)"""
34    point: float = field(default=0.0)
35    """獲得ポイント"""
36    rank: int = field(default=0)
37    """獲得順位"""
38
39    def has_valid_data(self) -> bool:
40        """有効なデータを持っているかチェック"""
41        return self != Score()
42
43    def to_dict(self, prefix: str) -> "ScoreDict":
44        """
45        データを辞書で返す
46
47        Args:
48            prefix (str): キーに付与する接頭辞 (p1, p2, p3, p4)
49
50        Returns:
51            ScoreDict: 返却する辞書
52
53        Note:
54            フィールド名の 'r_' プレフィックスは、指定された prefix に置換される。
55            例: r_str -> p1_str (prefix='p1' の場合)
56
57        """
58        return cast(
59            "ScoreDict",
60            {
61                f"{prefix}_name": self.name,
62                f"{prefix}_str": self.r_str,
63                f"{prefix}_rpoint": self.rpoint,
64                f"{prefix}_point": self.point,
65                f"{prefix}_rank": self.rank,
66            },
67        )

プレイヤー成績

Note:

フィールド名の 'r_' プレフィックス (r_str, rpoint など) は、 GameResult.to_dict() によって 'p1_', 'p2_', 'p3_', 'p4_' に置換され、 DBテーブルのカラム名 (p1_str, p2_str など) として使用する。

Score( name: str = '', r_str: str = '', rpoint: int = 0, point: float = 0.0, rank: int = 0)
name: str = ''

プレイヤー名

r_str: str = ''

素点(ユーザー入力文字列/未計算)

rpoint: int = 0

素点(ユーザー入力文字列評価後)

point: float = 0.0

獲得ポイント

rank: int = 0

獲得順位

def has_valid_data(self) -> bool:
39    def has_valid_data(self) -> bool:
40        """有効なデータを持っているかチェック"""
41        return self != Score()

有効なデータを持っているかチェック

def to_dict(self, prefix: str) -> libs.types.ScoreDict:
43    def to_dict(self, prefix: str) -> "ScoreDict":
44        """
45        データを辞書で返す
46
47        Args:
48            prefix (str): キーに付与する接頭辞 (p1, p2, p3, p4)
49
50        Returns:
51            ScoreDict: 返却する辞書
52
53        Note:
54            フィールド名の 'r_' プレフィックスは、指定された prefix に置換される。
55            例: r_str -> p1_str (prefix='p1' の場合)
56
57        """
58        return cast(
59            "ScoreDict",
60            {
61                f"{prefix}_name": self.name,
62                f"{prefix}_str": self.r_str,
63                f"{prefix}_rpoint": self.rpoint,
64                f"{prefix}_point": self.point,
65                f"{prefix}_rank": self.rank,
66            },
67        )

データを辞書で返す

Arguments:
  • prefix (str): キーに付与する接頭辞 (p1, p2, p3, p4)
Returns:

ScoreDict: 返却する辞書

Note:

フィールド名の 'r_' プレフィックスは、指定された prefix に置換される。 例: r_str -> p1_str (prefix='p1' の場合)

class GameResult:
 70class GameResult:
 71    """スコアデータ"""
 72
 73    def __init__(self, **kwargs: Any) -> None:
 74        # ゲーム結果
 75        self.ts: str = ""
 76        """タイムスタンプ"""
 77        self.p1: Score = Score()
 78        """東家成績"""
 79        self.p2: Score = Score()
 80        """南家成績"""
 81        self.p3: Score = Score()
 82        """西家成績"""
 83        self.p4: Score = Score()
 84        """北家成績"""
 85        self.comment: Optional[str] = None
 86        """ゲームコメント"""
 87        self.deposit: int = 0
 88        """供託"""
 89
 90        # 付属情報
 91        self.mode: Literal[3, 4] = 4
 92        """集計モード(三人打ち/四人打ち)"""
 93        self.rule_version: str = ""
 94        """ルール識別子"""
 95        self.origin_point: int = 250
 96        """配給原点"""
 97        self.return_point: int = 300
 98        """返し点"""
 99        self.rank_point: list[int] = [30, 10, -10, -30]
100        """順位点"""
101        self.draw_split: bool = False
102        """同着時に順位点を山分けにするか"""
103        self.source: Optional[str] = None
104        """データ入力元識別子"""
105
106        self.calc(**kwargs)
107
108    def __bool__(self) -> bool:
109        return all(self.to_list("name") + self.to_list("str"))
110
111    def __eq__(self, other: Any) -> bool:
112        if not isinstance(other, GameResult):
113            return NotImplemented
114
115        return all(
116            [
117                self.mode == other.mode,
118                self.ts == other.ts,
119                self.p1.name == other.p1.name,
120                self.p1.rpoint == other.p1.rpoint,
121                self.p2.name == other.p2.name,
122                self.p2.rpoint == other.p2.rpoint,
123                self.p3.name == other.p3.name,
124                self.p3.rpoint == other.p3.rpoint,
125                self.p4.name == other.p4.name,
126                self.p4.rpoint == other.p4.rpoint,
127                self.rule_version == other.rule_version,
128                self.comment == other.comment,
129                self.source == other.source,
130            ]
131        )
132
133    def __lt__(self, other: Any) -> bool:
134        if not isinstance(other, GameResult):
135            return NotImplemented
136        return self.ts < other.ts
137
138    def has_valid_data(self) -> bool:
139        """DB更新に必要なデータを持っているかチェック"""
140        # スコアデータ
141        match self.mode:
142            case 3:
143                score_data = all(
144                    [
145                        self.p1.has_valid_data(),
146                        self.p2.has_valid_data(),
147                        self.p3.has_valid_data(),
148                    ]
149                )
150            case 4:
151                score_data = all(
152                    [
153                        self.p1.has_valid_data(),
154                        self.p2.has_valid_data(),
155                        self.p3.has_valid_data(),
156                        self.p4.has_valid_data(),
157                    ]
158                )
159            case _:
160                score_data = False
161
162        return all([self.ts, isinstance(self.ts, str), score_data, all(self.to_list("rank"))])
163
164    def set(self, **kwargs: Any) -> None:
165        """データ取り込み"""
166
167        def _normalize_score_string(s: str) -> str:
168            """
169            素点文字列の正規化
170
171            Args:
172                s (str): 入力文字列
173
174            Returns:
175                str: 正規化後の文字列
176
177            """
178            s = s.strip()
179            s = re.sub(r"(-)+|(\+)+", r"\1\2", s)  # 連続した符号を集約
180            s = re.sub(r"(-|\+)0+", r"\1", s)  # 符号の直後のゼロを削除
181            if s != "0":  # 先頭のゼロとプラス記号を削除
182                s = re.sub(r"^[0+]+", "", s)
183            return s
184
185        def _set_score_attr(score: Score, prefix: str, key: str, value: object) -> None:
186            """
187            Scoreオブジェクトに属性を設定
188
189            Args:
190                score (Score): 対象スコアオブジェクト
191                prefix (str): プレイヤーポジション (p1-p4)
192                key (str): 属性名
193                value (object): 設定値
194
195            """
196            match key:
197                case "name":
198                    score.name = str(value)
199                case "str":
200                    score.r_str = _normalize_score_string(str(value))
201                case "r_str":
202                    score.r_str = str(value)
203                case "rpoint" if isinstance(value, int):
204                    score.rpoint = value
205                case "point" if isinstance(value, (float, int)):
206                    score.point = float(value)
207                case "rank" if isinstance(value, int):
208                    score.rank = value
209
210        # プレイヤースコア設定
211        for prefix in ("p1", "p2", "p3", "p4"):
212            score_obj = cast(Score, getattr(self, prefix))
213            for attr in ("name", "str", "r_str", "rpoint", "point", "rank"):
214                key = f"{prefix}_{attr}"
215                if key in kwargs:
216                    _set_score_attr(score_obj, prefix, attr, kwargs[key])
217
218        # ゲーム設定
219        if "mode" in kwargs and isinstance(kwargs["mode"], int):
220            if kwargs["mode"] in (3, 4):
221                self.mode = kwargs["mode"]  # type: ignore[assignment]
222        if "ts" in kwargs and isinstance(kwargs["ts"], str):
223            self.ts = kwargs["ts"]
224        if "rule_version" in kwargs and isinstance(kwargs["rule_version"], str):
225            self.rule_version = str(kwargs["rule_version"])
226        if "deposit" in kwargs and isinstance(kwargs["deposit"], int):
227            self.deposit = int(kwargs["deposit"])
228        if "origin_point" in kwargs and isinstance(kwargs["origin_point"], int):
229            self.origin_point = int(kwargs["origin_point"])
230        if "return_point" in kwargs and isinstance(kwargs["return_point"], int):
231            self.return_point = int(kwargs["return_point"])
232        if "rank_point" in kwargs and isinstance(kwargs["rank_point"], list):
233            self.rank_point = kwargs["rank_point"]
234        if "draw_split" in kwargs and isinstance(kwargs["draw_split"], bool):
235            self.draw_split = kwargs["draw_split"]
236        if "comment" in kwargs:
237            self.comment = kwargs["comment"]
238        if "source" in kwargs:
239            self.source = kwargs["source"]
240
241    def to_dict(self) -> "ScoreDict":
242        """
243        データを辞書で返す
244
245        Returns:
246            ScoreDict: スコアデータ
247
248        """
249        return {
250            "ts": self.ts,
251            **self.p1.to_dict("p1"),
252            **self.p2.to_dict("p2"),
253            **self.p3.to_dict("p3"),
254            **self.p4.to_dict("p4"),
255            "deposit": self.deposit,
256            "comment": self.comment,
257            "rule_version": self.rule_version,
258            "source": self.source,
259            "mode": self.mode,
260        }
261
262    def to_text(self, kind: Literal["simple", "detail", "logging"] = "simple") -> str:
263        """
264        データをテキストで返す
265
266        Args:
267            kind (Literal, optional): 表示形式
268                - *simple*: 簡易情報 (Default)
269                - *detail*: 詳細情報
270                - *logging*: ロギング用
271
272        Returns:
273            str: スコアデータ
274
275        """
276        ret_text: str = ""
277        match kind:
278            case "simple":
279                ret_text += f"[{self.p1.name} {self.p1.r_str}] "
280                ret_text += f"[{self.p2.name} {self.p2.r_str}] "
281                ret_text += f"[{self.p3.name} {self.p3.r_str}] "
282                ret_text += f"[{self.p4.name} {self.p4.r_str}] " if self.mode == 4 else ""
283                ret_text += f"[供託 {self.deposit}] [{self.comment if self.comment else None}]"
284            case "detail":
285                ret_text += f"[{self.p1.rank}{self.p1.name} {self.p1.rpoint * 100}点 ({self.p1.point}pt)] ".replace("-", "▲")
286                ret_text += f"[{self.p2.rank}{self.p2.name} {self.p2.rpoint * 100}点 ({self.p2.point}pt)] ".replace("-", "▲")
287                ret_text += f"[{self.p3.rank}{self.p3.name} {self.p3.rpoint * 100}点 ({self.p3.point}pt)] ".replace("-", "▲")
288                ret_text += f"[{self.p4.rank}{self.p4.name} {self.p4.rpoint * 100}点 ({self.p4.point}pt)] ".replace("-", "▲") if self.mode == 4 else ""
289                ret_text += f"[供託 {self.deposit * 100}点] "
290                ret_text += f"[{self.comment if self.comment else None}]"
291            case "logging":
292                ret_text += f"ts={self.ts}, deposit={self.deposit}, rule_version={self.rule_version}, "
293                ret_text += f"p1={self.p1.to_dict('p1')}, p2={self.p2.to_dict('p2')}, p3={self.p3.to_dict('p3')}, "
294                ret_text += f"p4={self.p4.to_dict('p4')}, " if self.mode == 4 else ""
295                ret_text += f"comment={self.comment if self.comment else None}, source={self.source}"
296
297        return ret_text
298
299    def to_list(self, kind: Literal["name", "str", "rpoint", "point", "rank"] = "name") -> list[str | int | float]:
300        """
301        指定データをリストで返す
302
303        Args:
304            kind (Literal, optional): 取得内容
305                - *name*: プレイヤー名 (Default)
306                - *str*: 入力された素点情報
307                - *rpoint*: 素点
308                - *point*: ポイント
309                - *rank*: 順位
310
311        Returns:
312            list[str | int | float]: リスト
313
314        """
315        ret_list: list[str | int | float] = []
316        match kind:
317            case "name":
318                ret_list = [self.p1.name, self.p2.name, self.p3.name, self.p4.name]
319            case "str":
320                ret_list = [self.p1.r_str, self.p2.r_str, self.p3.r_str, self.p4.r_str]
321            case "rpoint":
322                ret_list = [self.p1.rpoint, self.p2.rpoint, self.p3.rpoint, self.p4.rpoint]
323            case "point":
324                ret_list = [self.p1.point, self.p2.point, self.p3.point, self.p4.point]
325            case "point":
326                ret_list = [self.p1.point, self.p2.point, self.p3.point, self.p4.point]
327            case "rank":
328                ret_list = [self.p1.rank, self.p2.rank, self.p3.rank, self.p4.rank]
329
330        return ret_list[: self.mode]
331
332    def calc(self, **kwargs: Any) -> None:
333        """獲得ポイント計算"""
334        if kwargs:
335            self.set(**kwargs)
336
337        match self.mode:
338            case 3:
339                if all([self.p1.has_valid_data(), self.p2.has_valid_data(), self.p3.has_valid_data()]):
340                    self.set(**self._calculation_point3())
341                    self.p4 = Score()
342            case 4:
343                if all([self.p1.has_valid_data(), self.p2.has_valid_data(), self.p3.has_valid_data(), self.p4.has_valid_data()]):
344                    self.set(**self._calculation_point4())
345            case _:
346                raise RuntimeError
347
348    def _normalized_expression(self, expr: str) -> int:
349        """
350        入力文字列を式として評価し、計算結果を返す
351
352        Args:
353            expr (str): 入力式
354
355        Returns:
356            int: 計算結果
357
358        """
359        normalized: list[str] = []
360
361        for token in re.findall(r"\d+|[+\-*/]", expr):
362            if isinstance(token, str):
363                if token.isnumeric():
364                    normalized.append(str(int(token)))
365                else:
366                    normalized.append(token)
367
368        return int(eval("".join(normalized)))
369
370    def _calculation_point3(self) -> dict[str, Any]:
371        """
372        獲得ポイントと順位を計算する(三人打ち)
373
374        Returns:
375            dict[str, Any]: 更新用辞書(順位と獲得ポイントのデータ)
376
377        """
378        # 計算用データフレーム
379        score_df = pd.DataFrame({"rpoint": [self._normalized_expression(str(x)) for x in self.to_list("str")]}, index=["p1", "p2", "p3"])
380
381        work_rank_point = self.rank_point.copy()  # ウマ
382        work_rank_point[0] += int((self.return_point - self.origin_point) / 10 * 3)  # オカ
383
384        # 席順
385        score_df["rank"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")
386
387        # 獲得ポイントの計算 (素点-配給原点)/10+順位点
388        score_df["position"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")  # 加算する順位点リストの位置
389        score_df["point"] = (score_df["rpoint"] - self.return_point) / 10 + score_df["position"].apply(lambda p: work_rank_point[p - 1])
390        score_df["point"] = score_df["point"].apply(lambda p: float(f"{p:.1f}"))  # 桁ブレ修正
391
392        # 返却値用辞書
393        ret_dict = {f"{k}_{x}": v for x in score_df.columns for k, v in score_df[x].to_dict().items()}
394        ret_dict.update(deposit=int(self.origin_point * 3 - score_df["rpoint"].sum()))
395
396        return ret_dict
397
398    def _calculation_point4(self) -> dict[str, Any]:
399        """
400        獲得ポイントと順位を計算する(四人打ち)
401
402        Returns:
403            dict[str, Any]: 更新用辞書(順位と獲得ポイントのデータ)
404
405        """
406
407        def point_split(point: list[int]) -> list[int]:
408            """
409            順位点を山分けする
410
411            Args:
412                point (list[int]): 山分けするポイントのリスト
413
414            Returns:
415                list[int]: 山分けした結果
416
417            """
418            new_point = [int(sum(point) / len(point))] * len(point)
419            if sum(point) % len(point):
420                new_point[0] += sum(point) % len(point)
421                if sum(point) < 0:
422                    new_point = list(map(lambda x: x - 1, new_point))
423
424            return new_point
425
426        # 計算用データフレーム
427        score_df = pd.DataFrame({"rpoint": [self._normalized_expression(str(x)) for x in self.to_list("str")]}, index=["p1", "p2", "p3", "p4"])
428
429        work_rank_point = self.rank_point.copy()  # ウマ
430        work_rank_point[0] += int((self.return_point - self.origin_point) / 10 * 4)  # オカ
431
432        if self.draw_split:  # 山分け
433            score_df["rank"] = score_df["rpoint"].rank(ascending=False, method="min").astype("int")
434
435            # 順位点リストの更新
436            match "".join(score_df["rank"].sort_values().to_string(index=False).split()):
437                case "1111":  # 2.5/2.5/2.5/2.5
438                    work_rank_point = point_split(work_rank_point)
439                case "1114":  # 2/2/2/4
440                    new_point = point_split(work_rank_point[0:3])
441                    work_rank_point[0] = new_point[0]
442                    work_rank_point[1] = new_point[1]
443                    work_rank_point[2] = new_point[2]
444                case "1134":  # 1.5/1.5/3/4
445                    new_point = point_split(work_rank_point[0:2])
446                    work_rank_point[0] = new_point[0]
447                    work_rank_point[1] = new_point[1]
448                case "1133":  # 1.5/1.5/3.5/3.5
449                    new_point = point_split(work_rank_point[0:2])
450                    work_rank_point[0] = new_point[0]
451                    work_rank_point[1] = new_point[1]
452                    new_point = point_split(work_rank_point[2:4])
453                    work_rank_point[2] = new_point[0]
454                    work_rank_point[3] = new_point[1]
455                case "1222":  # 1/3/3/3
456                    new_point = point_split(work_rank_point[1:4])
457                    work_rank_point[1] = new_point[0]
458                    work_rank_point[2] = new_point[1]
459                    work_rank_point[3] = new_point[2]
460                case "1224":  # 1/2.5/2.5/4
461                    new_point = point_split(work_rank_point[1:3])
462                    work_rank_point[1] = new_point[0]
463                    work_rank_point[2] = new_point[1]
464                case "1233":  # 1/2/3.5/3.5
465                    new_point = point_split(work_rank_point[2:4])
466                    work_rank_point[2] = new_point[0]
467                    work_rank_point[3] = new_point[1]
468                case _:
469                    pass
470
471        else:  # 席順
472            score_df["rank"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")
473
474        # 獲得ポイントの計算 (素点-配給原点)/10+順位点
475        score_df["position"] = score_df["rpoint"].rank(ascending=False, method="first").astype("int")  # 加算する順位点リストの位置
476        score_df["point"] = (score_df["rpoint"] - self.return_point) / 10 + score_df["position"].apply(lambda p: work_rank_point[p - 1])
477        score_df["point"] = score_df["point"].apply(lambda p: float(f"{p:.1f}"))  # 桁ブレ修正
478
479        # 返却値用辞書
480        ret_dict = {f"{k}_{x}": v for x in score_df.columns for k, v in score_df[x].to_dict().items()}
481        ret_dict.update(deposit=int(self.origin_point * 4 - score_df["rpoint"].sum()))
482
483        return ret_dict
484
485    @property
486    def rpoint_sum(self) -> int:
487        """
488        素点合計
489
490        Returns:
491            int: 素点合計
492
493        """
494        if not all(self.to_list("rank")):  # 順位が確定していない場合は先に計算
495            self.calc()
496
497        return sum(cast(list[int], self.to_list("rpoint")))

スコアデータ

GameResult(**kwargs: Any)
 73    def __init__(self, **kwargs: Any) -> None:
 74        # ゲーム結果
 75        self.ts: str = ""
 76        """タイムスタンプ"""
 77        self.p1: Score = Score()
 78        """東家成績"""
 79        self.p2: Score = Score()
 80        """南家成績"""
 81        self.p3: Score = Score()
 82        """西家成績"""
 83        self.p4: Score = Score()
 84        """北家成績"""
 85        self.comment: Optional[str] = None
 86        """ゲームコメント"""
 87        self.deposit: int = 0
 88        """供託"""
 89
 90        # 付属情報
 91        self.mode: Literal[3, 4] = 4
 92        """集計モード(三人打ち/四人打ち)"""
 93        self.rule_version: str = ""
 94        """ルール識別子"""
 95        self.origin_point: int = 250
 96        """配給原点"""
 97        self.return_point: int = 300
 98        """返し点"""
 99        self.rank_point: list[int] = [30, 10, -10, -30]
100        """順位点"""
101        self.draw_split: bool = False
102        """同着時に順位点を山分けにするか"""
103        self.source: Optional[str] = None
104        """データ入力元識別子"""
105
106        self.calc(**kwargs)
ts: str

タイムスタンプ

p1: Score

東家成績

p2: Score

南家成績

p3: Score

西家成績

p4: Score

北家成績

comment: str | None

ゲームコメント

deposit: int

供託

mode: Literal[3, 4]

集計モード(三人打ち/四人打ち)

rule_version: str

ルール識別子

origin_point: int

配給原点

return_point: int

返し点

rank_point: list[int]

順位点

draw_split: bool

同着時に順位点を山分けにするか

source: str | None

データ入力元識別子

def has_valid_data(self) -> bool:
138    def has_valid_data(self) -> bool:
139        """DB更新に必要なデータを持っているかチェック"""
140        # スコアデータ
141        match self.mode:
142            case 3:
143                score_data = all(
144                    [
145                        self.p1.has_valid_data(),
146                        self.p2.has_valid_data(),
147                        self.p3.has_valid_data(),
148                    ]
149                )
150            case 4:
151                score_data = all(
152                    [
153                        self.p1.has_valid_data(),
154                        self.p2.has_valid_data(),
155                        self.p3.has_valid_data(),
156                        self.p4.has_valid_data(),
157                    ]
158                )
159            case _:
160                score_data = False
161
162        return all([self.ts, isinstance(self.ts, str), score_data, all(self.to_list("rank"))])

DB更新に必要なデータを持っているかチェック

def set(self, **kwargs: Any) -> None:
164    def set(self, **kwargs: Any) -> None:
165        """データ取り込み"""
166
167        def _normalize_score_string(s: str) -> str:
168            """
169            素点文字列の正規化
170
171            Args:
172                s (str): 入力文字列
173
174            Returns:
175                str: 正規化後の文字列
176
177            """
178            s = s.strip()
179            s = re.sub(r"(-)+|(\+)+", r"\1\2", s)  # 連続した符号を集約
180            s = re.sub(r"(-|\+)0+", r"\1", s)  # 符号の直後のゼロを削除
181            if s != "0":  # 先頭のゼロとプラス記号を削除
182                s = re.sub(r"^[0+]+", "", s)
183            return s
184
185        def _set_score_attr(score: Score, prefix: str, key: str, value: object) -> None:
186            """
187            Scoreオブジェクトに属性を設定
188
189            Args:
190                score (Score): 対象スコアオブジェクト
191                prefix (str): プレイヤーポジション (p1-p4)
192                key (str): 属性名
193                value (object): 設定値
194
195            """
196            match key:
197                case "name":
198                    score.name = str(value)
199                case "str":
200                    score.r_str = _normalize_score_string(str(value))
201                case "r_str":
202                    score.r_str = str(value)
203                case "rpoint" if isinstance(value, int):
204                    score.rpoint = value
205                case "point" if isinstance(value, (float, int)):
206                    score.point = float(value)
207                case "rank" if isinstance(value, int):
208                    score.rank = value
209
210        # プレイヤースコア設定
211        for prefix in ("p1", "p2", "p3", "p4"):
212            score_obj = cast(Score, getattr(self, prefix))
213            for attr in ("name", "str", "r_str", "rpoint", "point", "rank"):
214                key = f"{prefix}_{attr}"
215                if key in kwargs:
216                    _set_score_attr(score_obj, prefix, attr, kwargs[key])
217
218        # ゲーム設定
219        if "mode" in kwargs and isinstance(kwargs["mode"], int):
220            if kwargs["mode"] in (3, 4):
221                self.mode = kwargs["mode"]  # type: ignore[assignment]
222        if "ts" in kwargs and isinstance(kwargs["ts"], str):
223            self.ts = kwargs["ts"]
224        if "rule_version" in kwargs and isinstance(kwargs["rule_version"], str):
225            self.rule_version = str(kwargs["rule_version"])
226        if "deposit" in kwargs and isinstance(kwargs["deposit"], int):
227            self.deposit = int(kwargs["deposit"])
228        if "origin_point" in kwargs and isinstance(kwargs["origin_point"], int):
229            self.origin_point = int(kwargs["origin_point"])
230        if "return_point" in kwargs and isinstance(kwargs["return_point"], int):
231            self.return_point = int(kwargs["return_point"])
232        if "rank_point" in kwargs and isinstance(kwargs["rank_point"], list):
233            self.rank_point = kwargs["rank_point"]
234        if "draw_split" in kwargs and isinstance(kwargs["draw_split"], bool):
235            self.draw_split = kwargs["draw_split"]
236        if "comment" in kwargs:
237            self.comment = kwargs["comment"]
238        if "source" in kwargs:
239            self.source = kwargs["source"]

データ取り込み

def to_dict(self) -> libs.types.ScoreDict:
241    def to_dict(self) -> "ScoreDict":
242        """
243        データを辞書で返す
244
245        Returns:
246            ScoreDict: スコアデータ
247
248        """
249        return {
250            "ts": self.ts,
251            **self.p1.to_dict("p1"),
252            **self.p2.to_dict("p2"),
253            **self.p3.to_dict("p3"),
254            **self.p4.to_dict("p4"),
255            "deposit": self.deposit,
256            "comment": self.comment,
257            "rule_version": self.rule_version,
258            "source": self.source,
259            "mode": self.mode,
260        }

データを辞書で返す

Returns:

ScoreDict: スコアデータ

def to_text(self, kind: Literal['simple', 'detail', 'logging'] = 'simple') -> str:
262    def to_text(self, kind: Literal["simple", "detail", "logging"] = "simple") -> str:
263        """
264        データをテキストで返す
265
266        Args:
267            kind (Literal, optional): 表示形式
268                - *simple*: 簡易情報 (Default)
269                - *detail*: 詳細情報
270                - *logging*: ロギング用
271
272        Returns:
273            str: スコアデータ
274
275        """
276        ret_text: str = ""
277        match kind:
278            case "simple":
279                ret_text += f"[{self.p1.name} {self.p1.r_str}] "
280                ret_text += f"[{self.p2.name} {self.p2.r_str}] "
281                ret_text += f"[{self.p3.name} {self.p3.r_str}] "
282                ret_text += f"[{self.p4.name} {self.p4.r_str}] " if self.mode == 4 else ""
283                ret_text += f"[供託 {self.deposit}] [{self.comment if self.comment else None}]"
284            case "detail":
285                ret_text += f"[{self.p1.rank}{self.p1.name} {self.p1.rpoint * 100}点 ({self.p1.point}pt)] ".replace("-", "▲")
286                ret_text += f"[{self.p2.rank}{self.p2.name} {self.p2.rpoint * 100}点 ({self.p2.point}pt)] ".replace("-", "▲")
287                ret_text += f"[{self.p3.rank}{self.p3.name} {self.p3.rpoint * 100}点 ({self.p3.point}pt)] ".replace("-", "▲")
288                ret_text += f"[{self.p4.rank}{self.p4.name} {self.p4.rpoint * 100}点 ({self.p4.point}pt)] ".replace("-", "▲") if self.mode == 4 else ""
289                ret_text += f"[供託 {self.deposit * 100}点] "
290                ret_text += f"[{self.comment if self.comment else None}]"
291            case "logging":
292                ret_text += f"ts={self.ts}, deposit={self.deposit}, rule_version={self.rule_version}, "
293                ret_text += f"p1={self.p1.to_dict('p1')}, p2={self.p2.to_dict('p2')}, p3={self.p3.to_dict('p3')}, "
294                ret_text += f"p4={self.p4.to_dict('p4')}, " if self.mode == 4 else ""
295                ret_text += f"comment={self.comment if self.comment else None}, source={self.source}"
296
297        return ret_text

データをテキストで返す

Arguments:
  • kind (Literal, optional): 表示形式
    • simple: 簡易情報 (Default)
    • detail: 詳細情報
    • logging: ロギング用
Returns:

str: スコアデータ

def to_list( self, kind: Literal['name', 'str', 'rpoint', 'point', 'rank'] = 'name') -> list[str | int | float]:
299    def to_list(self, kind: Literal["name", "str", "rpoint", "point", "rank"] = "name") -> list[str | int | float]:
300        """
301        指定データをリストで返す
302
303        Args:
304            kind (Literal, optional): 取得内容
305                - *name*: プレイヤー名 (Default)
306                - *str*: 入力された素点情報
307                - *rpoint*: 素点
308                - *point*: ポイント
309                - *rank*: 順位
310
311        Returns:
312            list[str | int | float]: リスト
313
314        """
315        ret_list: list[str | int | float] = []
316        match kind:
317            case "name":
318                ret_list = [self.p1.name, self.p2.name, self.p3.name, self.p4.name]
319            case "str":
320                ret_list = [self.p1.r_str, self.p2.r_str, self.p3.r_str, self.p4.r_str]
321            case "rpoint":
322                ret_list = [self.p1.rpoint, self.p2.rpoint, self.p3.rpoint, self.p4.rpoint]
323            case "point":
324                ret_list = [self.p1.point, self.p2.point, self.p3.point, self.p4.point]
325            case "point":
326                ret_list = [self.p1.point, self.p2.point, self.p3.point, self.p4.point]
327            case "rank":
328                ret_list = [self.p1.rank, self.p2.rank, self.p3.rank, self.p4.rank]
329
330        return ret_list[: self.mode]

指定データをリストで返す

Arguments:
  • kind (Literal, optional): 取得内容
    • name: プレイヤー名 (Default)
    • str: 入力された素点情報
    • rpoint: 素点
    • point: ポイント
    • rank: 順位
Returns:

list[str | int | float]: リスト

def calc(self, **kwargs: Any) -> None:
332    def calc(self, **kwargs: Any) -> None:
333        """獲得ポイント計算"""
334        if kwargs:
335            self.set(**kwargs)
336
337        match self.mode:
338            case 3:
339                if all([self.p1.has_valid_data(), self.p2.has_valid_data(), self.p3.has_valid_data()]):
340                    self.set(**self._calculation_point3())
341                    self.p4 = Score()
342            case 4:
343                if all([self.p1.has_valid_data(), self.p2.has_valid_data(), self.p3.has_valid_data(), self.p4.has_valid_data()]):
344                    self.set(**self._calculation_point4())
345            case _:
346                raise RuntimeError

獲得ポイント計算

rpoint_sum: int
485    @property
486    def rpoint_sum(self) -> int:
487        """
488        素点合計
489
490        Returns:
491            int: 素点合計
492
493        """
494        if not all(self.to_list("rank")):  # 順位が確定していない場合は先に計算
495            self.calc()
496
497        return sum(cast(list[int], self.to_list("rpoint")))

素点合計

Returns:

int: 素点合計