【C#.NET】大富豪やったことない奴が大富豪作ってみる その1

2022.01.06

CATEGORY : C#.NET プログラミング

あけましておめでとうございます。あんみんどうふです。

トランプを使った「大富豪」というゲームをご存知でしょうか。
私はルールを一切知りません。なのでこれといった説明もできず、多人数でやるトランプゲームということしか知りません。

ということで今回は大富豪のルールを学びつつ、C#でプログラムに起こしてみようと思います。

結構複雑そうなので何回かに分けて投稿します。

開発環境

いつも通りです。

  • C#.NET(.NET Core 3.1)
  • Visual Studio 2019

作る

大富豪を理解するために、Wikipediaを参考にします。

大富豪(だいふごう)、もしくは大貧民(だいひんみん)は、複数人で遊ぶトランプゲームの1つ。カードを全てのプレイヤーに均等に配り、手持ちのカードを順番に場に出して早く手札を無くすことを競うゲームである。

ここだけ見ると「ババ抜き」を戦略的にしたような感じですね。
ルール確認しながら作っていきます。

1組全てのカードをプレイヤー全員に均等に配る。通常はジョーカーを1枚含めてプレイする。カードは特に人数が多い場合などは2組(あるいはそれ以上)を同時に使うこともある。

カードは全種類使うみたいなので、13(A、2~10、J、Q、K)×4(♤♡♢♧)+1(ジョーカー)の計53枚が必要ですね。

というわけで「カード」を作るために、独自クラスCardを宣言します。

これで「new Card({任意のID})」という感じにインスタンス化すると「1枚のカード」として作成されます。
「{Card型変数}.{プロパティ名}」で以下の情報を取得できます。

プロパティ名内容
IdintカードID(1~53)
Numintカードの番号(1~13)
Typestringカードの種類名(スペード、ハート等)
ListNamestringリスト表示用のカード名(♠3、♥6等)
「黒塗り記号+数字」のフォーマット
ジョーカーは「JOKER」
MsgNamestringメッセージ表示用のカード名(3、A(エース)、J(ジャック)等)
数字はそのまま A・J・Q・Kは読みが表示される
Powerintカードの強さ(3~15、ジョーカーなら99)
3~13はそのまま、1と2はキングより強いので14、15
ジョーカーはいかなるカードにも強いので99

 

例:40(クローバーの1)の場合

ちなみにカード作成に必要な情報はインスタンス化時の引数に使うIDだけなので、どのプロパティも読み取り専用にしています。

 

カードの種類はIDから1引いた数を13で割ってカードを判別する形にしてみました。

IDはそれぞれ、1~13は♤14~26は♡27~39は♢40~52は♧53はジョーカーに対応しています。
このIDから1を引き13で割った商種類余りに1を足して数値を判別します。
ちなみに商はそれぞれ0=♤1=♡2=♢3=♧4=ジョーカーです。

例えば、IDが「29」だとすると、(29-1)÷13=2余り2。商が2なら「♢」、余りの2に1足して「3」なので、「♢の3」になります。

商を求める時に1を引いて余りに1を足す理由は、例えばIDが「13」の場合だと本来は「♤の13」になりますが、1の足し引きを行わない場合だと商は1、余りは0になるので「♡の0」という結果になってしまいます。明らかにおかしいですね。
これを防ぐために、商はあえて1引くことで割り切れない状態にし、13で割るとどうしても余りが13になることは無いので、1足すことで13になるようにしています。

 

「カード」を用意したので、次は53枚の「カード」があることを表すカードマスタを作成します。Listを用意して連番で1~53のカードを追加するだけ。

 

初期化

初期化処理を書いていきます。Load時にInitialize関数を呼び出しています。
リトライ機能とか実装する際に融通利くように分けてます。

細かくコメント書いてわかりやすくしました。ブログに書くためにも、備忘録としても・・・。
とは言ってもいきなりドンと出されてもわからないので、分けて解説します。

 

cardsという二次元Listを各プレイヤーの持ち手札としています。
手札は数が増減するため、配列だとちょっと取り回しが面倒なのでListを使っています。

 

カードマスタを直接いじるのは怖いので、cardTmpというListに移してシャッフルします。要するにcardTmpは「山札」です。
最初の手札の枚数ですが、Wikipediaによると

1組全てのカードをプレイヤー全員に均等に配る。

とのことなので、まず均等になるように「枚数(53枚)÷人数」で分けます。余りの処理は後回しでとりあえず今はMath.Floorで切り捨てします(余った分は後で配ります)。

 

カードの配り方は至って単純で、「各プレイヤーの持ち手札に、シャッフルしたcardTmp(山札)の先頭のカードを格納→cardTmp(山札)から削除」を繰り返すことで配っています。

 

また、こういうルールがあるみたいで。

ゲームはダイヤの3から始める。最初の親が手札から最初のカードを出し、以降順番に次のプレイヤーがカードを出し重ねていく。

とのことなので、カードを配る際に♢の3を引いたプレイヤーはそれを場に出し、その人を基準として順番にカードを出していくことになります。
♢の3を引いたプレイヤーのIDはplayerParent(親プレイヤー)、playerNow(現在のプレイヤー)の変数に格納されます。

場に出す処理は一旦後回しで別の記事に書こうと思います。

 

さっきの処理で余ったカードを配ります。
順番に配ると最初のプレイヤーが必ずカードを多く貰うことになり不利になってしまうので、randomIndexというListを作り、プレイヤー数分の連番を格納してシャッフル→先頭から順に対応したIDのプレイヤーに配る方法で公平性を保っています。

ループを回す前にremainという変数でcardTmpの残り枚数を取得していますが、これはfor文の中でListを削る処理(remove)を行っており、条件式にcardTmp.Countを直接入れるとループ回数がおかしくなってしまうのを防ぐためです。ここで一回つまづきました。

また、エラー処理にErrorMessageという関数を使っていますが、これは単にダイアログでエラー出してるだけです。第2引数はtrueだとフォームを閉じます。

 

手札がぐちゃぐちゃなので第1キーをPower(カードの強さ)、第2キーをId(カードID)で昇順ソートしています。これで数字が弱い順に、同じ数字は♠→♥→♦→♣の順に並ぶようになります。

ここまでやったらデバッグとしてコンソールに出力してみます。
手札の内容が出力されます。今回はとりあえず4人に設定しているので4人分出力されればOKです。

(私の環境では何故かConsole.Writeが効かないので、名前空間に「using System.Diagnostics」を追加してDebug.Writeで代用しています・・・。)

プレイヤーID、手札の枚数、手札の内容、番が回ったプレイヤーが一目瞭然です。

 

(ReadCardImageとWriteMsgの部分は今回やらなかったのでコメントアウトしてます。また今度。)


今回はカードを作り、プレイヤーに配った所まで作りました。続きは次回・・・。

あんみんどうふです。 前回は手札を配る所まで作りました。 今回はカードを場に出して次の番へ進められる所まで作ります。 前回の記事はこちら。 https://www.terasol.co.jp/%e3%83%97%e3%8...