設計 - Recursion-Group-C/card-game GitHub Wiki

はじめに

Card Game App の設計を行います。このページは現在作成中です。

ワイヤーフレーム

figma参照

システム構成図

image

アクティビティ図

prefix に Sub がついているノードはサブアクティビティ。

flowchart TB
  style mp fill:#0f0,stroke:#333,stroke-width:4px, color:#000
  s((Start)) --> lr{Is login required ?}
  lr -->|Yes| login[SubLogin]
  lr -->|No| mp(MainPage)
  mp --> sl[Select CPU level]
  sl --> sg{Select game}
  login --> mp
  sg --> w[SubWAR]
  w --> res[Results]
  sg --> r[SubRummy]
  r --> res
  sg --> bj[SubBlackjack]
  bj --> res
  sg --> sp[SubSpeed]
  sp --> res
  sg --> p[SubPoker]
  p --> res
  sg --> th[SubTexas Hold'em ]
  th --> res
  res --> cg{Continue game?}
  cg -->|Yes| sg
  cg -->|No| E((End))
Loading

Login

flowchart TB
  style lp fill:#0f0,stroke:#333,stroke-width:4px, color:#000
  style sp fill:#0f0,stroke:#333,stroke-width:4px, color:#000
  style mp fill:#0f0,stroke:#333,stroke-width:4px, color:#000
  style prs fill:#0f0,stroke:#333,stroke-width:4px, color:#000

  s((Start)) --> lp[LoginPage]
  lp --> ha{Has Account?}
  ha --> |Yes| rp{Remember password?}
  ha --> |No| sp[SignInPage]
  sp --> r[Account registration]
  r --> lp
  rp --> |Yes| l[Access account]
  rp --> |No| prs[PasswordResetPage]
  prs --> rpc[Reset password]
  rpc --> lp
  l --> mp[MainPage]
Loading

War

ルールはカジノウォーを採用。

graph TB

St((Start)) --> A
A[Place bet] --> B[Deal 1 card each]
B --> C{Compare cards}
C -->|Player wins| D[Double payout]
C -->|Player loses| E[Lose bet]
C -->|Tie| F[Surrender or War]
F -->|Surrender| G[Lose half, half returned]
F -->|War| H[Bet additional amount]
H --> I[Deal new cards]
I --> J{Compare cards}
J -->|Player wins| K[Return bet, double payout for additional]
J -->|Player loses| L[Lose all bets]
J -->|Tie| M[Double payout for both bets]
D --> N{Continue?}
E --> N
G --> N
K --> N
L --> N
M --> N
N -->|Yes| A
N -->|No| O((End))
Loading

Blackjack

ルールはトランプスタジアム参照。

検討事項

  • CREDIT (ゲーム内通貨)はログイン時はゲーム選択時に任意の額持ち込み、ログインしていない時は一定額自動配布される?
  • CREDIT は途中追加できるようにするか?
  • 参加プレイヤーの人数
flowchart TB
  s((Start)) --> A[Initialize player CREDIT]
  A --> C[Bet minimum bid or more]
  C --> D[Deal cards]
  D --> node1{Player's turn}
  node1 -->|Hit| F[Deal one more card]
  node1 -->|Stand| node2{Dealer's turn}
  F --> G{Total exceeds 21?}
  G -->|Yes| L
  G -->|No| node1
  node2 -->|Hit| H[Deal one more card]
  node2 -->|Stand| node3{Determine winner}
  H --> I{Total exceeds 21?}
  I -->|Yes| J[Player wins]
  I -->|No| node2
  node3 -->|Player's total > Dealer's total| J
  node3 -->|Player's total < Dealer's total| L[Player loses]
  node3 -->|Player's total = Dealer's total| K[Draw]
  J --> node4[Calculate player CREDIT]
  L --> node4
  K --> C
  node5 -->|Yes| C
  node5 -->|No| E((End))
  node4 --> node6{Meets the minimum bid?}
  node6 --> |Yes| node5{Continue the game?}
  node6 --> |No| E

Loading

Rummy

ルールはトランプスタジアム参照。

検討事項

  • 加点方式 or 減点方式?
  • 勝利条件で設定するマッチ数は固定にするか、設定で変更できるようにする?
  • CREDIT の増減の計算方法は?(Speed,War も同様)
flowchart TB
  start((Start)) --> A[プレイヤー人数設定]
  A --> C[ラウンド開始]
  C --> D[初期手札を配る]
  D --> node1{プレイヤーのターン}
  node1 -->|引く| F[山札または捨て札から1枚引く]
  node1 -->|メルド| G[可能ならメルドする]
  node1 -->|捨てる| H[カードを1枚捨てる]
  F --> G
  G --> H
  H --> node2{次のプレイヤーのターン}
  node2 -->|引く| I[山札または捨て札から1枚引く]
  node2 -->|メルド| J[可能ならメルドする]
  node2 -->|捨てる| K[カードを1枚捨てる]
  I --> J
  J --> K
  K --> node3{手札がなくなったか?}
  node3 -->|Yes| node4{ラウンド終了}
  node3 -->|No| node5{山札が尽きたか?}
  node5 -->|Yes| node6{山札を2回目に切りなおすか?}
  node6 -->|Yes| node7[引き分け]
  node6 -->|No| node8[捨て札を切りなおして山札にする]
  node8 --> node1
  node5 -->|No| node1
  node4 --> L[各プレイヤーの得点計算]
  L --> M{誰かが100点以上になったか?}
  M -->|Yes| N{設定された勝利条件を満たしたか?}
  M -->|No| C
  N -->|Yes| O[CREDIT計算]
  N -->|No| C
  node7 --> L
  O --> e((End))
Loading

Speed

ルールはトランプスタジアム参照。

flowchart TB
  s((Start)) --> A[ラウンド数を設定]
  A --> B[カードを赤と黒のマークに分ける]
  B --> C[場札を4枚並べる]
  C --> D[Game start]
  D --> E[ゲーム進行]
  E --> K[ラウンド終了]
  K --> L{勝利条件を\n満たしたか?}
  L --> |Yes| N[CREDIT計算]
  L --> |No| B
  N --> End((End))
Loading

ゲームの進行は、下記のシーケンス図で示す。

sequenceDiagram
    participant Player
    participant CPU
    participant 手札
    participant 台札

    loop Until Game Ends
    alt 場札がなくなる
      Player->>手札: カードを引く
      手札->>Player: カードを渡す
    end
    alt 場札に隣り合ったカードがある
      Player->>台札: 台札にカードを置く
      alt Playerが先にカードを配置
        台札->>Player: 配置確認
        Player->>Player: 勝利条件を確認
      end
    end


    alt 場札がなくなる
      CPU->>手札: カードを引く
      手札->>CPU: カードを渡す
    end
    alt 場札に隣り合ったカードがある
      CPU->>台札: 台札にカードを置く
      alt CPUが先にカードを配置
        台札->>CPU: 配置確認
        CPU->>CPU: 勝利条件を確認
      end
    end


    alt 2人とも出せるカードがない
      手札->>台札: 台札にカードを置く
    end
end

Loading

Poker

ルールはトランプスタジアム参照。

flowchart TB
  start((Start)) -->  A[CREDIT初期化]
  A --> SetPlayers[プレイヤー人数設定]
  SetPlayers --> Ante[各プレイヤーがアンティを支払う]
  Ante --> DealInitialCards[手札を配る]

  DealInitialCards --> FirstBettingRound[1巡目のベッティング]
  FirstBettingRound --> BetOrCallOrFold[アクションを選択]
  BetOrCallOrFold --> turnend{全員のアクション\nが終了したか?}
  turnend --> |No| FirstBettingRound
  turnend --> |Yes|judgeNocontest{残りのプレイヤーが\n2人以上存在するか}

  judgeNocontest --> |No| NoContest1[ノーコンテスト]
  judgeNocontest --> |Yes| CardExchange[カード交換]
  NoContest1 --> DetermineWinner

  CardExchange --> SecondBettingRound[2巡目のベッティング]
  SecondBettingRound --> BetOrCallOrFold2[アクションを選択]
  BetOrCallOrFold2 --> turnend2{全員のアクション\nが終了したか?}
  turnend2 --> |No| SecondBettingRound
  turnend2 --> |Yes| Showdown[役の強さを競う]


  Showdown --> DetermineWinner[勝者を決定]
  DetermineWinner --> PotAndSidePot3[チップを獲得]
  PotAndSidePot3 --> judge{CREDITが最低入札要件\nを満たすか?}
  judge --> |Yes| Continue{ゲームを続けるか?}
  Continue --> |Yes| Ante
  Continue --> |No| en((End))
  judge -->|No| en


Loading

Texas hold'em Poker

ルールはトランプスタジアム参照。
とりあえずリングゲームのアクティビティ図を下記に示す。

検討事項

  • ルールはリングゲーム or トーナメントゲームどちらにするか
flowchart TB
  start((Start)) -->  A[CREDIT初期化]
  A --> SetPlayers[プレイヤー人数設定]
  SetPlayers --> PostBlinds[ブラインド投稿]
  PostBlinds --> DealInitialCards[手札を配る]

  DealInitialCards --> PreFlopBetting[プリフロップベッティング]
  PreFlopBetting --> BetOrCallOrFold[アクションを選択]
  BetOrCallOrFold --> turnend{全員のアクション\nが終了したか?}
  turnend --> |No| PreFlopBetting
  turnend --> |Yes| judgeNocontest1{残りのプレイヤーが\n2人以上存在するか}

  judgeNocontest1 --> |No| NoContest1[ノーコンテスト]
  judgeNocontest1 --> |Yes| Flop[フロップ配布]
  NoContest1 --> DetermineWinner

  Flop --> FlopBetting[フロップベッティング]
  FlopBetting --> BetOrCallOrFold2[アクションを選択]
  BetOrCallOrFold2 --> turnend2{全員のアクション\nが終了したか?}
  turnend2 --> |No| FlopBetting
  turnend2 --> |Yes| judgeNocontest2{残りのプレイヤーが\n2人以上存在するか}

  judgeNocontest2 --> |No| NoContest2[ノーコンテスト]
  judgeNocontest2 --> |Yes| Turn[ターン配布]
  NoContest2 --> DetermineWinner

  Turn --> TurnBetting[ターンベッティング]
  TurnBetting --> BetOrCallOrFold3[アクションを選択]
  BetOrCallOrFold3 --> turnend3{全員のアクション\nが終了したか?}
  turnend3 --> |No| TurnBetting
  turnend3 --> |Yes| judgeNocontest3{残りのプレイヤーが\n2人以上存在するか}

  judgeNocontest3 --> |No| NoContest3[ノーコンテスト]
  judgeNocontest3 --> |Yes| River[リバー配布]
  NoContest3 --> DetermineWinner

  River --> RiverBetting[リバーベッティング]
  RiverBetting --> BetOrCallOrFold4[アクションを選択]
  BetOrCallOrFold4 --> turnend4{全員のアクション\nが終了したか?}
  turnend4 --> |No| RiverBetting
  turnend4 --> |Yes| Showdown[ショーダウン]

  Showdown --> DetermineWinner[勝者を決定]
  DetermineWinner --> PotAndSidePot3[チップを獲得]
  PotAndSidePot3 --> judge{CREDITが最低入札要件\nを満たすか?}
  judge --> |Yes| Continue{ゲームを続けるか?}
  Continue --> |Yes| PostBlinds
  Continue --> |No| en((End))
  judge -->|No| en

Loading

クラス図

---
title: Card Game 
---
classDiagram
Table <|-- BlackjackTable
Table <|-- PorkerTable
Table <|-- RummyTable
Table <|-- SpeedTable
Table <|-- TexHoldemPorkerTable
Table <|-- WarTable

Player <|-- BlackjackPlayer
Player <|-- PorkerPlayer
Player <|-- RummyPlayer
Player <|-- SpeedPlayer
Player <|-- TexHoldemPorkerPlayer
Player <|-- WarPlayer

Table *-- "1.." Player: players
BlackjackTable *-- "2-7" Player: players
BlackjackTable *-- "1" Player: house
PorkerTable *-- "2-10" Player: players
RummyTable *-- "2-4" Player: players
SpeedTable *-- "1" Player: player
TexHoldemPorkerTable *-- "2-10" Player: players
WarTable *-- "1" Player: player
WarTable *-- "1" Player: house

Table *-- "1" Deck: #deck

Deck *-- "0..53" Card: cards

Player *-- "0..*" Card: -hand
BlackjackPlayer *-- "2.." Card: -hand
PorkerPlayer *-- "5" Card: -hand
RummyPlayer *-- "7..10" Card: -hand
SpeedPlayer *-- "0..25" Card: -hand
TexHoldemPorkerPlayer *-- "2" Card: -hand
WarPlayer *-- "1" Card: -hand
Player ..> GameDecision
BlackjackPlayer ..> GameDecision
PorkerPlayer ..> GameDecision
RummyPlayer ..> GameDecision
SpeedPlayer ..> GameDecision
TexHoldemPorkerPlayer ..> GameDecision
WarPlayer ..> GameDecision


    class Table {
        <<Abstract>>
        -List~int~ betDenominations
        #int turnCounter
        -string gamePhase
        -List~string~ resultsLog

        +assignPlayerHands() void
        +clearPlayerHandsAndBets() void
        +Abstract evaluateAndGetRoundResults() string
        +Abstract evaluateMove(Player player) void
        +Abstract getTurnPlayer() Player
        +Abstract haveTurn() void
        +isFirstPlayer() bool
        +isLastPlayer() bool
    }

    class BlackjackTable {
        -isBlackjack(Player) bool
        -isAllPlayerActionsResolved() bool
        -getHandScore(Player) int
    }

    class PorkerTable {
        -getHandScore(Player) int
    }

    class RummyTable {
        -getHandScore(Player) int
    }

    class TexHoldemPorkerTable {
        -getHandScore(Player) int
    }

    class SpeedTable {

    }

    class WarTable {

    }

    class Player {
        <<Abstract>>
        -string name
        -string playerType
        -int chips
        -int bet
        -int winAmount
        -string gameStatus

        + Abstract promptPlayer(int nullable userData) GameDecision
        + Abstract getHandScore() int
    }

    class BlackjackPlayer{
        -getAIBetGameDecision() GameDecision
        -getAIActionGameDecision() GameDecision
        -getUserActionGameDecision() GameDecision
        -getHouseActionGameDecision() GameDecision
    }

    class PorkerPlayer{
        -getAIBetGameDecision() GameDecision
        -getAIActionGameDecision() GameDecision
        -getUserActionGameDecision() GameDecision
    }

    class RummyPlayer {
        -getAIActionGameDecision() GameDecision
        -getUserActionGameDecision() GameDecision
    }
    
    class SpeedPlayer {
        -getUserActionGameDecision() GameDecision
        -getHouseActionGameDecision() GameDecision
    }

    class TexHoldemPorkerPlayer{
        -getAIBetGameDecision() GameDecision
        -getAIActionGameDecision() GameDecision
        -getUserActionGameDecision() GameDecision
    }

    class WarPlayer {
        -getUserActionGameDecision() GameDecision
        -getHouseActionGameDecision() GameDecision
    }

    class Card {
        -string suit
        -string rank

        +getRankNumber(string gameType) int
    }

    class Deck {
        -string gameType

        +shuffle() void
        +drawOne() Card
        +resetDeck() void
    }

    %% どのようなベットやアクションを取るべきかというプレイヤーの決定を表すクラス
    class GameDecision {
        - string action
        - int amount
    }
Loading

クラスの説明

Table

関数名・変数名 説明
betDenominations テーブルで可能なベット金額の単位を表す整数の配列。例えば、[5, 20, 50, 100]など。
resultLog 各ラウンド終了時のハウス以外の全プレイヤーの状態を、文字列の配列の形で記録する。
turnCounter ラウンドロビン形式のゲームで現在ターンのプレイヤーを識別するためのカウンター。
evaluateMove() Table.haveTurn()内で呼ぶ関数。Player.promptPlayer()から現在のプレーヤーのgameDecision(ベット方法やアクションなど)を受け取り, それにしたがって、そのプレイヤーのベット、ハンド、GameStatus、チップの状態などを更新する。
haveTurn() ラウンドロビン形式のゲームで、ゲームフローの制御とテーブルの状態を更新する。

BlackjackTable

関数名・変数名 説明
gamePhase ゲームのフェーズを表す。{'betting', 'acting', 'roundOver', 'endOfGame'}のどれか。
assignPlayerHands() 各プレイヤーにカードを2枚ずつ配布する。
evaluateAndGetRoundResults() すべてのプレイヤーのアクションが終わり、現在のプレイヤーがプレイヤーの配列の最後のプレイヤーである場合に呼び出される。このメソッドは、ブラックジャックの勝敗判定ルールに従ってプレイヤーを更新し、ラウンドが終了してテーブルがクリアされる前の各プレイヤーの状態を表す文字列を返す。この返された文字列は、Table.resultsLogに追加される。gameStatusが'bust', 'broken', 'surrender'となっているプレイヤーなど、既にラウンドが決定しているプレイヤーは一切更新されない。
isAllPlayerActionsResolved() 全てのプレイヤーがセット{'broken', 'bust', 'stand', 'surrender'}のPlayer.gameStatusを持っていればtrueを返し、持っていなければfalseを返す。ハウスを含むプレイヤーは何度も'hit'し続ける可能性があるので、'acting'フェーズがいつ終わるか把握する必要がある。

Player

関数名・変数名 説明
promptPlayer() TableのgamePhaseとPlayerのplayerTypeに応じて、各Playerが取る行動をGameDecisionクラスのオブジェクトとして返す。

BlackjackPlayer

関数名・変数名 説明
gameStatus プレイヤーの状態を表す。{'ready', 'broke (no chip)', 'bet', 'surrender', 'stand', 'hit', 'double', 'bust', 'doubleBust', 'push' }のどれか。
getAIBetGameDecision() Table.promptPlayer()内で呼ぶ関数。AIのベット金額を決める。所持金からベット可能な金額をランダムに決定する。
getAIActionGameDecision() Table.promptPlayer()内で呼ぶ関数。AIのアクションを決める。所持金を考慮し、{'hit', 'stand', 'double', 'surrender'}の候補からアクションをランダムに決定する。
getUserActionGameDecision() Table.promptPlayer()内で呼ぶ関数。Userのアクションを決める。所持金を考慮し、{'hit', 'stand', 'double', 'surrender'}の候補からアクションを決定する。bustしない限り、hitは何度でもできる。
getHouseActionGameDecision() Table.promptPlayer()内で呼ぶ関数。Houseのアクションを決める。持ち札のスコアが17未満であれば、17以上になるまで、Hitを続ける

GameDecision

関数名・変数名 説明
action 各ゲームで取り得るアクション
- Blackjack: {'broke', 'bust', 'bet', 'surrender', 'stand', 'hit', 'double', 'blackjack'}
- Porker: {'broke', 'bet', 'check', 'call', 'raise', 'drop', 'draw'}
- Rummy: {'meld', 'rummy'}
- Speed: {}
- TexHoldemPorker: {'broke', 'bet', 'check', 'call', 'raise', 'drop', 'draw'}
- War: {'broke', 'bet', 'surrender', 'war'}
userData betのアクションがあるゲームでは、bet金額。その他のゲームでは要検討。
⚠️ **GitHub.com Fallback** ⚠️