ドボンにもあったポンとカン
ドボン – Wikipedia によるとこんなルールがあるそうです。
通常1枚ずつしか手札は捨てられないが、他プレイヤーが捨てたカードを複数枚所持していた場合は「ポン!」「カン!」などとコールし、場に複数カードを捨てられる。
例えば場に「13」が捨てられ自分が2枚「13」を持っていたら「ポン」のコールをし2枚一気に場に出せる。
ただリスクも高いです。
他プレイヤーで合計が26になる者がいた場合それはドボンになり、上がられた場合、手札の合計は2倍になる。通称ポン・ドボンである(3枚の場合はカン・ドボンで手札の合計は3倍となる)。
例のごとくコンピュータには乱数で適当にやってもらいます。ユーザーがポンやカンをする場合、そのためのダイアログを出す必要があります。
設定のダイアログ
まず設定のダイアログをつくります。
順番が回ってきた段階でダイアログを表示し、ユーザーにどうするのか(1枚だけ出す、複数枚の場合はカードも指定)を尋ねます。ドボンになると上がられた場合、手札の合計が2倍または3倍になるのでライバルがこれをする確率は低めにします。ただ同じ数字のカードしか持っていない場合はそのまま出させます。
1 2 3 4 5 6 7 |
static public class Config { // ポン・カンはありか? static public bool IsAllowPonKan = true; // その他の部分は前回と同じ } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class ConfigObject { public void CopyToFile() { IsAllowPonKan = Config.IsAllowPonKan; // その他の部分は前回と同じ } public void CopyFromFile() { Config.IsAllowPonKan = IsAllowPonKan; // その他の部分は前回と同じ } // ポン・カンはありか? public bool IsAllowPonKan = true; // その他のフィールド変数は前回と同じ } |
ConfigDialogクラスの変更
いわゆる「ポン・カン」はありかどうかに関する情報を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public partial class ConfigDialog : Form { public ConfigDialog() { } private void ConfigDialog_Load(object sender, EventArgs e) { if(Config.IsAllowPonKan) cbIsAllowPonKan.Checked = true; else cbIsAllowPonKan.Checked = false; // 追加部分以外は変更なし } private void ButtonOK_Click(object sender, EventArgs e) { if(cbIsAllowPonKan.Checked) Config.IsAllowPonKan = true; else Config.IsAllowPonKan = false; // 追加部分以外は変更なし } private void cbIsDrawCardOnlyOne_CheckedChanged(object sender, EventArgs e) { // このイベントハンドラへの変更はなし } } |
カードを複数出せるようにする
カードを出す処理をしているのはPlayerクラスのPutCardメソッドです。そこでここにカードを複数出すための処理を追加します。
カードを2枚出すとドボンできる番号がいつもの2倍または3倍になるのでカードにそれがわかるように記録します。
1 2 3 4 5 6 7 8 9 10 11 12 |
public class CardEx : CardLibrary.Card { public CardEx(Suit suit, int number) { Suit = suit; Number = number; } public Player player = null; public bool IsTriple = false; public bool IsDouble = false; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
public class Player { // 同じ数字のカードを2枚もっているときに2枚とも出すか? // 実行する確率は2分の1 public CardEx DoPon(int targetNumber) { int r = Random.Next(0, 2); if(r == 0) { var removeCards = Cards.Where(x => x.Number == targetNumber).ToList(); Cards = Cards.Except(removeCards).ToList(); PlayerPanel.Invalidate(); CardEx cardEx = (CardEx)removeCards[0]; cardEx.player = this; cardEx.IsDouble = true; return cardEx; } return null; } // 同じ番号のカードが3枚あったら // 3枚同時に出す確率は3分の1 // うち2枚同時に出す確率は3分の1 public CardEx DoPonKan(int targetNumber) { int r = Random.Next(0, 3); if(r == 0) { var removeCards = Cards.Where(x => x.Number == targetNumber).ToList(); Cards = Cards.Except(removeCards).ToList(); PlayerPanel.Invalidate(); CardEx cardEx = (CardEx)removeCards[0]; cardEx.player = this; cardEx.IsTriple = true; return cardEx; } if(r == 1) { var removeCards = Cards.Where(x => x.Number == targetNumber).Take(2).ToList(); Cards = Cards.Except(removeCards).ToList(); PlayerPanel.Invalidate(); CardEx cardEx = (CardEx)removeCards[0]; cardEx.player = this; cardEx.IsDouble = true; return cardEx; } return null; } } |
あとはライバルのターンを処理するメソッドのなかで呼び出します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
public partial class Form1 : Form { bool RivalsTurn(int firstRival) { firstRival = firstRival % Players.Count; if(firstRival == 0) return CheckPrevMyTurn(); // 0 は自分なのでライバルではない List<Player> rivals = Players.Skip(1).ToList(); // もし順番が反時計回りであれば順番をリバースする if(isFromBack) rivals.Reverse(); int count = rivals.Count; int start = firstRival - 1; for(int i = start; i < count; i++) { // カードを出すときにプレイヤーごとに少し止める System.Threading.Thread.Sleep(500); // Aが出されていたらスキップ(省略) // 2が出されていたら2を出すか2のn乗枚のカードをとる(省略) // ライバルがカードを出す Card card = rivals[i].PutCard(CenterCard, ShuffledCards); // そのカードがQueenだった場合の処理(省略) if(CenterCard.Number == 12) { List<Card> queens = rivals[i].GetPutQueens(); if(queens.Count > 0) CenterCard = queens[0]; } // 出されたカードに他のプレイヤーはドボンできるか? // ドボンまたはドボン返し成立の場合はゲーム終了 if(DobonIfCan(rivals[i])) return false; // 上がり判定 if(rivals[i].Cards.Count == 0) { MessageBox.Show(rivals[i].Name + "が あがりました"); return false; } // 今回の追加部分 if(Config.IsAllowPonKan) { // 誰かがポン・カンしたときの次のプレイヤーは? // 誰もポン・カンしなかった場合の戻り値は -1 int nextPlayer = DoPonKanIfCan((CardEx)CenterCard); // ドボンされるかもしれないのでチェック // CenterCardは変更されているので // CenterCardのプロパティから誰が出したカードか調べる if(nextPlayer != -1) { Player ponkanPlayer = ((CardEx)CenterCard).player; if(DobonIfCan(ponkanPlayer)) return false; } if(nextPlayer == 0) return CheckPrevMyTurn(); if(nextPlayer != -1) return RivalsTurn(nextPlayer); } // Jが出された場合の処理(省略) } // ループが終わったらあなたのターンになるのだが・・・ return CheckPrevMyTurn(); } } |
実際に呼び出されるメソッドを以下に示します。戻り値は次のプレイヤーです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
public partial class Form1 : Form { int DoPonKanIfCan(CardEx card) { List<Player> players = Players.Where(x => x != card.player).ToList(); foreach(Player player in players) { int count = player.Cards.Count(x => x.Number == CenterCard.Number); if(count < 2) continue; if(player == Players[0]) { int ret = OnCanPonKan(); // 内容は後述 if(ret != -1) return ret; else continue; } else { // 同じ番号のカードが2枚あったら1/2の確率で同時に出す if(count == 2) { Card card1 = player.DoPon(card.Number); if(card1 != null) { MessageBox.Show(player.Name + "がポンをしました"); CenterCard = card1; int index = Players.IndexOf(player); if(!isFromBack) { if(index + 1 == Players.Count) return 0; else return index + 1; } else { return index - 1; } } else continue; } // 同じ番号のカードが3枚あったら // 3枚同時に出す確率は3分の1 // うち2枚同時に出す確率は3分の1 if(count == 3) { CardEx card1 = player.DoPonKan(card.Number); if(card1 != null) { if(card1.IsDouble) MessageBox.Show(player.Name + "がポンをしました"); if(card1.IsTriple) MessageBox.Show(player.Name + "がカンをしました"); CenterCard = card1; int index = Players.IndexOf(player); if(!isFromBack) { if(index + 1 == Players.Count) return 0; else return index + 1; } else { return index - 1; } } else continue; } } } return -1; } } |
自分がカードを出す場合
自分自身がポンまたはカンできる場合があります。ライバルのターンがおわるとCheckPrevMyTurnメソッドが実行されるので、このときにポンまたはカンできるかどうか調べて、できるときはダイアログを表示して選べるようにします。
ダイアログは
で使用したものを使います。
Queen以外でも使えるように表示内容を変更します。ポンまたはカンをした場合は次のプレイヤーを、[辞退]した場合は-1を返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public partial class Form1 : Form { int OnCanPonKan() { if(Players[0].Cards.Count(x => x.Number == CenterCard.Number) < 2) return -1; int num = CenterCard.Number; // ダイアログの初期化 FormQueen form = InitQueenForm(num); if(form.ShowDialog() != DialogResult.OK) return -1; CardEx topCard = null; List<Card> removeCards = new List<Card>(); // ダイアログから情報を取得する GetInfoQueenForm(form, num, ref topCard, ref removeCards); form.Dispose(); if(removeCards.Count == 2) ((CardEx)topCard).IsDouble = true; if(removeCards.Count == 3) ((CardEx)topCard).IsTriple = true; // カードを出す CenterCard = topCard; foreach(Card removeCard in removeCards) Players[0].Cards.Remove(removeCard); Players[0].PlayerPanel.Invalidate(); System.Threading.Thread.Sleep(500); // 次のプレイヤーは? if(!isFromBack) return 1; else return Players.Count - 1; } } |
FormQueenはQueenを出すときのために作られたものなので、他の番号の場合は表示を変える必要があります。それをするのがInitQueenFormメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
public partial class Form1 : Form { FormQueen InitQueenForm(int number) { string numString; if(number == 1) numString = "A"; else if(number == 11) numString = "J"; else if(number == 12) numString = "Q"; else if(number == 13) numString = "K"; else numString = number.ToString(); FormQueen form = new FormQueen(); form.checkBoxSpade.Text = "スペードの" + numString; if(Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Spade && x.Number == number) == null) { form.radioButtonSpade.Enabled = false; form.checkBoxSpade.Enabled = false; } form.checkBoxHart.Text = "ハートの" + numString; if(Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Hart && x.Number == number) == null) { form.radioButtonHart.Enabled = false; form.checkBoxHart.Enabled = false; } form.checkBoxDia.Text = "ダイアの" + numString; if(Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Dia && x.Number == number) == null) { form.radioButtonDia.Enabled = false; form.checkBoxDia.Enabled = false; } form.checkBoxClub.Text = "クラブの" + numString; if(Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Club && x.Number == number) == null) { form.radioButtonClub.Enabled = false; form.checkBoxClub.Enabled = false; } form.label1.Text = numString + " は複数枚出せます"; form.Text = numString + " は複数枚出せます"; return form; } } |
ダイアログが閉じられたときに情報を取得してカードを出す出さないを決めなければなりません。GetInfoQueenFormメソッドは、ポンまたはカンをするのか、そのときに一番上に置かれるカードは何かを取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
public partial class Form1 : Form { void GetInfoQueenForm(FormQueen form, int number, ref CardEx topCard, ref List<Card> removeCards) { if(form.ShowDialog() == DialogResult.OK) { if(form.checkBoxSpade.Checked) { Card card = Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Spade && x.Number == number); removeCards.Add(card); if(form.radioButtonSpade.Checked) topCard = (CardEx)card; } if(form.checkBoxHart.Checked) { Card card = Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Hart && x.Number == number); removeCards.Add(card); if(form.radioButtonHart.Checked) topCard = (CardEx)card; } if(form.checkBoxDia.Checked) { Card card = Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Dia && x.Number == number); removeCards.Add(card); if(form.radioButtonDia.Checked) topCard = (CardEx)card; } if(form.checkBoxClub.Checked) { Card card = Players[0].Cards.FirstOrDefault(x => x.Suit == Suit.Club && x.Number == number); removeCards.Add(card); if(form.radioButtonClub.Checked) topCard = (CardEx)card; } } } } |