ポン・ドボンとカン・ドボン
ポンまたはカンがされた場合もドボンされる場合があります。3が2枚だされたときは6でドボンすることになります。
ドボンできるのか?
ドボンかどうか判定するのはPlayerクラスのCanDobonメソッドです。長くなったので書き直しています。
設定とカードのフィールド変数を調べてドボンできるかどうかを判断しています。それから出されたカードがポンの場合は2倍、カンの場合は3倍になるのでそれに対応させています。
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 |
public class Player { public bool CanDobon(Card card, ref string reason) { // 自分で出したカードにはドボンできない if(((CardEx)card).player == this) { reason = "自分のカード"; return false; } string dobonedCardString = GetDobonedCardString((CardEx)card); int targetNumber = card.Number; if(Cards.Sum(x => x.Number) == targetNumber) { reason = String.Format("{0}に対して{1}: {2}", dobonedCardString, "通常のドボン", GetStringPlayerCards()); return true; } // 手札が2枚なら足し算だけでなく四則演算すべてを適用する if(Config.IsSpecialDobonTwo && Cards.Count == 2 && CanDobonTwoCard((CardEx)card, ref reason)) return true; // すべてのカードの積 if(Config.IsAllowDobonMultiplication && CanDobonMultiplication((CardEx)card, ref reason)) return true; // すべてのカードの相加平均 if(Config.IsAllowDobonAverage && CanDobonAverage((CardEx)card, ref reason)) return true; // すべてのカードの相乗平均 if(Config.IsAllowDobonGeometricalAverage && CanDobonGeometricalAverage((CardEx)card, ref reason)) return true; reason = "不成立"; return false; } } |
GetDobonedCardStringメソッドは、ドボンの対象になったカードを文字列で取得するメソッドです。ポンドボンやカンドボンであることがわかるような文字列をカード名といっしょに取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Player { string GetDobonedCardString(CardEx card) { string str = card.GetCardString(); if(Config.IsAllowPonKan && ((CardEx)card).IsDouble) str += "(ポン)"; if(Config.IsAllowPonKan && ((CardEx)card).IsTriple) str += "(カン)"; return str; } } |
GetDobonedTargetNumberメソッドは、ドボンの対象になる数字を取得します。カードが同時に2枚だされたときはカードの2倍の数、3枚の場合は3倍の数を返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class Player { int GetDobonedTargetNumber(CardEx card) { int targetNumber = card.Number; if(Config.IsAllowPonKan && ((CardEx)card).IsDouble) targetNumber *= 2; if(Config.IsAllowPonKan && ((CardEx)card).IsTriple) targetNumber *= 3; return targetNumber; } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Player { string GetStringPlayerCards() { StringBuilder sb = new StringBuilder(); foreach(Card card in Cards) { sb.Append(card.GetCardString() + ","); } return sb.ToString(); } } |
GetStringPlayerCardsメソッドはそのプレイヤーのもつカードすべてを文字列にして返します。
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Player { string GetStringPlayerCards() { StringBuilder sb = new StringBuilder(); foreach(Card card in Cards) { sb.Append(card.GetCardString() + ","); } return sb.ToString(); } } |
CanDobonTwoCardメソッドは残りのカードが2枚だけのときに四則演算すべてをドボンの対象にしているときに、ドボンの判定をするメソッドです。
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 |
public class Player { bool CanDobonTwoCard(CardEx card, ref string reason) { int targetNumber = GetDobonedTargetNumber(card); int big = 0; int small = 0; if(Cards[0].Number > Cards[1].Number) { big = Cards[0].Number; small = Cards[1].Number; } else { big = Cards[1].Number; small = Cards[0].Number; } if(targetNumber == big - small) { reason = String.Format("{0}に対して「{1}」: {2}", GetDobonedCardString(card), "2枚の差", GetStringPlayerCards()); return true; } if(targetNumber == Cards[0].Number * Cards[1].Number) { reason = String.Format("{0}に対して「{1}」: {2}", GetDobonedCardString(card), "2枚の積", GetStringPlayerCards()); return true; } if(targetNumber == big / small && big % small == 0) { reason = String.Format("{0}に対して「{1}」: {2}", GetDobonedCardString(card), "2枚の商", GetStringPlayerCards()); return true; } return false; } } |
CanDobonMultiplicationメソッドはすべてのカードの積をドボンの対象にしているときに、ドボンの判定をするメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Player { bool CanDobonMultiplication(CardEx card, ref string reason) { int targetNumber = GetDobonedTargetNumber(card); int i = 1; foreach(Card card1 in Cards) { i *= card1.Number; } if(targetNumber == i) { reason = String.Format("{0}に対して「{1}」: {2}", GetDobonedCardString(card), "手札の積", GetStringPlayerCards()); return true; } return false; } } |
CanDobonAverageメソッドはすべてのカードの番号の平均をドボンの対象にしているときに、ドボンの判定をするメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class Player { bool CanDobonAverage(CardEx card, ref string reason) { int targetNumber = GetDobonedTargetNumber(card); int sum = 0; foreach(Card card1 in Cards) sum += card1.Number; if(sum % Cards.Count == 0 && sum / Cards.Count == targetNumber) { reason = String.Format("{0}に対して「{1}」: {2}", GetDobonedCardString(card), "手札の相加平均", GetStringPlayerCards()); return true; } return false; } } |
CanDobonGeometricalAverageメソッドはすべてのカードの番号の相乗平均をドボンの対象にしているときに、ドボンの判定をするメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class Player { bool CanDobonGeometricalAverage(CardEx card, ref string reason) { int targetNumber = GetDobonedTargetNumber(card); int i = 1; foreach(Card card1 in Cards) { i *= card1.Number; } double d = Math.Pow(i, 1.0 / Cards.Count); if(d == (int)d && d == targetNumber) { reason = String.Format("{0}に対して「{1}」: {2}", GetDobonedCardString(card), "手札の相乗平均", GetStringPlayerCards()); return true; } return false; } } |
じっさいにドボンする
ドボンの条件を満たしていればドボンするかどうかの選択をすることができます。
DobonIfCanメソッドはドボンされる可能性があるプレイヤーを引数にして、ドボンするかどうかを決めさせるメソッドです。コンピュータがドボンするかどうかは乱数で決めます。
ドボンが成立したとき、ポンドボンとカンドボンは点数計算の方法がかわってくるので、これらを区別できるようにしておく必要があります。
誰かがドボンをするとドボン返しがあるかどうかを判定するメソッドがありますが、そのなかで最後に実行されたカードを記憶しておく必要があります。
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 |
public partial class Form1 : Form { bool DobonIfCan(Player player) { int dobonNumber = CenterCard.Number; string reason = ""; foreach(var dobon in Players) { if(dobon.CanDobon(CenterCard, ref reason)) { // 自分が出したカードにはドボンできない if(((CardEx)CenterCard).player == dobon) continue; if(dobon == Players[0]) { DialogResult dr = MessageBox.Show("ドボンできます。ドボンしますか?\n" + reason, "", MessageBoxButtons.YesNo); if(dr == DialogResult.Yes) { DoDobon(player); return true; } continue; } else { // コンピュータがドボンを辞退する確率は5分の1 int a = random.Next(5); if(a != 0) { dobon.CanDobon(CenterCard, ref reason); MessageBox.Show(dobon.Name + "が ドボンしました\n" + reason); if(Config.IsAntiDobonTopCard) { dobonNumber = dobon.Cards.Min(x => x.Number); // 最後にドボンされたカードを記憶しておく LastDobonedCard = (CardEx)CenterCard; // カードを出す CardEx topCard = (CardEx)dobon.Cards.First(x => x.Number == dobonNumber); topCard.player = dobon; CenterCard = topCard; System.Threading.Thread.Sleep(500); } // ドボン返しはあるのか? CheckAntiDobon((CardEx)CenterCard, dobon, player); return true; } else continue; // ドボンは見送る } } } return 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 |
public partial class Form1 : Form { void DoDobon(Player dobonedPlayer) { if(Config.IsAntiDobonTopCard) { DobonTopNumberDialog dlg = new DobonTopNumberDialog(); dlg.vs = Players[0].Cards.Select(x => x.Number).Distinct().ToList(); if(dlg.ShowDialog() == DialogResult.OK) { int dobonNumber = dlg.TopNumber; // 最後にドボンされたカードを記憶しておく LastDobonedCard = (CardEx)CenterCard; // カードを出す CardEx topCard = (CardEx)Players[0].Cards.First(x => x.Number == dobonNumber); topCard.player = Players[0]; CenterCard = topCard; System.Threading.Thread.Sleep(500); } } // ドボン返しはあるのか? CheckAntiDobon((CardEx)CenterCard, Players[0], dobonedPlayer); } } |
ドボン返しはあるのか?
CheckAntiDobonメソッドはドボン返しされるかもしれないカード、現段階でドボンしているプレイヤー、現段階でドボンされているプレイヤーを引数としてドボン返しはあるのか、じっさいにドボン返しされるのかを判定するメソッドです。
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 |
public partial class Form1 : Form { Player LastDobon = null; Player LastDoboned = null; List<Player> DobonPlayers = new List<Player>(); CardEx LastDobonedCard = null; //Card card, ドボン返しされるかもしれないカード //Player dobon, いまのところドボンしたプレイヤー //Player doboned いまのところドボンされたプレイヤー void CheckAntiDobon(CardEx card, Player dobon, Player doboned) { if(Config.IsAntiDobonEveryone) { LastDobon = dobon; LastDoboned = doboned; DobonPlayers.Clear(); DobonPlayers.Add(dobon); // すでにドボンしたプレイヤー以外なら誰でもドボン返しができる // ドボン返しは複数回おこなわれるかもしれないので再帰呼び出しとする CheckAntiDobon1(card, dobon, doboned); LastDobon.Dobon(LastDoboned); MessageBox.Show(LastDobon.Name + " の勝ち、\n" + LastDoboned.Name + " の負けです"); } else { // ドボン返しはドボンされたプレイヤーでないとできない string reason = ""; if(doboned.CanDobon(card, ref reason)) { doboned.AntiDobon(dobon); MessageBox.Show("ドボン返しで" + doboned.Name + " の勝ち、\n" + dobon.Name + " の負けです"); LastDobon = doboned; LastDoboned = dobon; } else { dobon.Dobon(doboned); MessageBox.Show(dobon.Name + " の勝ち、" + doboned.Name + " の負けです"); LastDobon = dobon; LastDoboned = doboned; } } } } |
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 |
public partial class Form1 : Form { void CheckAntiDobon1(CardEx card, Player dobon, Player doboned) { // すでにドボンしたプレイヤーはドボンできない Player[] players = Players.Where(x => x != dobon).Except(DobonPlayers).ToArray(); foreach(Player player in players) { string reason = ""; if(player.CanDobon(card, ref reason)) { if(player == Players[0]) { DialogResult dr = MessageBox.Show("ドボン返しできます。ドボン返ししますか?\n" + reason, "", MessageBoxButtons.YesNo); if(dr != DialogResult.Yes) continue; DoAntiDobon(dobon, card); } LastDobon = player; LastDoboned = dobon; if(Config.IsAntiDobonTopCard) { int dobonNumber1 = player.Cards.Min(x => x.Number); CardEx topCard = (CardEx)player.Cards.First(x => x.Number == dobonNumber1); topCard.player = player; CenterCard = topCard; LastDobonedCard = (CardEx)card; System.Threading.Thread.Sleep(500); MessageBox.Show(doboned.Name + "がドボン返しをしました。"); DobonPlayers.Add(player); CheckAntiDobon1(topCard, player, dobon); } else { DobonPlayers.Add(player); CheckAntiDobon1(card, player, dobon); } return; } } } } |
これは自分がドボン返しするときのメソッドです。
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 |
public partial class Form1 : Form { void DoAntiDobon(Player antiDoboned, CardEx antiDobonedCard) { if(Config.IsAntiDobonTopCard) { int antiDobonNumber = 0; DobonTopNumberDialog dlg = new DobonTopNumberDialog(); dlg.vs = Players[0].Cards.Select(x => x.Number).Distinct().ToList(); if(dlg.ShowDialog() == DialogResult.OK) { antiDobonNumber = dlg.TopNumber; } else return; DobonPlayers.Add(Players[0]); CardEx topCard = (CardEx)Players[0].Cards.First(x => x.Number == antiDobonNumber); topCard.player = Players[0]; CenterCard = topCard; System.Threading.Thread.Sleep(500); LastDobon = Players[0]; LastDoboned = antiDoboned; LastDobonedCard = (CardEx)antiDobonedCard; CheckAntiDobon1(topCard, Players[0], antiDoboned); } else { LastDobon = Players[0]; LastDoboned = antiDoboned; DobonPlayers.Add(Players[0]); CheckAntiDobon1(antiDobonedCard, Players[0], antiDoboned); } return; } } |
CheckAntiDobonメソッドが処理を終えたときに以下のフィールド変数を調べれば、最終的にドボンしたプレイヤーとされたプレイヤー、そのカードがわかります。
1 2 3 |
Player LastDobon Player LastDoboned CardEx LastDobonedCard |
点数計算
最後に点数計算をしておわりです。
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 |
public partial class Form1 : Form { void CalcScore() { Player winner = Players.First(x => x.Cards.Count == 0); Player[] others = Players.Where(x => x.Cards.Count > 0).ToArray(); Player lastDobon = LastDobon; Player lastDoboned = LastDoboned; foreach(Player other in others) { // カードが残ったプレイヤーの負け点を計算する // 1ゲームにおける変動をとりあえず確定する other.Score = -minusScore; } // ドボンで上がられた、ドボン返しで上がられたかどうかで点数を変更する // ポンドボンとカンドボンではないのか? if(Config.IsAllowPonKan && LastDobonedCard.IsDouble) { LastDoboned.Score *= 2; // 負け点2倍 } if(Config.IsAllowPonKan && LastDobonedCard.IsTriple) { LastDoboned.Score *= 3; // 負け点3倍 } // 勝者以外の負け点の合計が勝者の得点となる winner.Score = - others.Sum(x => x.Score); // ゲームごとのスコアからトータルスコアを計算する foreach(Player player in Players) player.TotalScore += player.Score; // フィールド変数をクリア LastDobon = null; LastDoboned = null; DobonPlayers.Clear(); LastDobonedCard = 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 |
public partial class Form1 : Form { void GameStart() { isFromBack = false; CenterCard = null; foreach(CardEx card in Cards) { card.IsDouble = false; card.IsTriple = false; card.player = null; } ShuffleCard(); HandoutCards(); panelSouth.Invalidate(); panelWest.Invalidate(); panelNorth.Invalidate(); panelEast.Invalidate(); PutFirstCard(); isNotFirstCard = false; RivalsTurnAsync(GameCount); } } |