今度は出せるカードがない場合、いつでもジョーカーを代用できるわけではなく、次のカードと一緒に出せる場合でないと出せないルールに適用させます。
ジョーカーといっしょに出せるカードを取得する必要があります。そのために出せるカードとジョーカーがその代わりをするカードをペアにしたPairWithJokerクラスを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class PairWithJoker { public Card Card { private set; get; } = null; public Card Joker // ジョーカーがその代わりをするカード { private set; get; } = null; public PairWithJoker(Card card, Card joker) { Card = card; Joker = joker; } } |
GetPairWithJokerメソッドはジョーカーといっしょに出せるカードのペア(PairWithJoker)のリストを返します。
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 85 |
public partial class Form1 : Form { List<PairWithJoker> GetPairWithJoker(CardMark mark) { List<PairWithJoker> retCards = new List<PairWithJoker>(); List<Card> notExistCards = cards.Where(x => x.Mark == mark && !x.isExists).ToList(); // カードはすべてそろっている if(notExistCards.Count == 0) { return retCards; } // トンネルは成立しているか? if(notExistCards.Count(x => x.Number < 7) == 0) { // 1 ⇒ 13 が成立している // 13以下で欠けているもののうち最大のものよりひとつ小さいものを探す // ただしそのカードがすでに存在する場合は除外する int i = notExistCards.Where(x => x.Number <= 13).Max(x => x.Number); if(GetCard(mark, i-1).isExists) return retCards; PairWithJoker pair = new PairWithJoker(GetCard(mark, i - 1), GetCard(mark, i)); retCards.Add(pair); return retCards; } else if(notExistCards.Count(x => x.Number > 7) == 0) { // 13 ⇒ 1 が成立している // 1以上で欠けているもののうち最小のものよりひとつ大きいものを探す // ただしそのカードがすでに存在する場合は除外する int i = notExistCards.Where(x => x.Number >= 1).Min(x => x.Number); if(GetCard(mark, i +1).isExists) return retCards; PairWithJoker pair = new PairWithJoker(GetCard(mark, i + 1), GetCard(mark, i)); retCards.Add(pair); return retCards; } // トンネルは成立していない // 番号が小さい側を求める // 1~7で欠けているもののうち最大のものよりひとつ小さいものを探す // 該当のカードが存在しないならリストに追加する int left = notExistCards.Where(x => x.Number < 7).Max(x => x.Number); if(left != 1) { if(!GetCard(mark, left - 1).isExists) { PairWithJoker pair = new PairWithJoker(GetCard(mark, left - 1), GetCard(mark, left)); retCards.Add(pair); } } else { if(!GetCard(mark, 13).isExists) { PairWithJoker pair = new PairWithJoker(GetCard(mark, 13), GetCard(mark, left)); retCards.Add(pair); } } // 番号が大きい側を求める // 8~13で欠けているもののうち最小のものよりひとつ大きいものを探す int right = notExistCards.Where(x => x.Number > 7).Min(x => x.Number); if(right != 13) { if(!GetCard(mark, right + 1).isExists) { PairWithJoker pair = new PairWithJoker(GetCard(mark, right + 1), GetCard(mark, right)); retCards.Add(pair); } } else { if(!GetCard(mark, 1).isExists) { PairWithJoker pair = new PairWithJoker(GetCard(mark, 1), GetCard(mark, right)); retCards.Add(pair); } } return retCards; } } |
List
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public partial class Form1 : Form { List<PairWithJoker> GetPairWithJoker(List<Card> cards) { // ジョーカーをもっていないなら問題外 if(!cards.Any(x => x.Mark == CardMark.joker)) return new List<PairWithJoker>(); List<PairWithJoker> retCards = new List<PairWithJoker>(); retCards.AddRange(GetPairWithJoker(CardMark.spade)); retCards.AddRange(GetPairWithJoker(CardMark.hart)); retCards.AddRange(GetPairWithJoker(CardMark.dia)); retCards.AddRange(GetPairWithJoker(CardMark.club)); // ジョーカーで置き換えられるカードをもたず、出せるカードはもっているか? return retCards.Where(x => cards.Any(x1 => x1 == x.Card) && !cards.Any(x1 => x1 == x.Joker) ).ToList(); } } |
それではまずライバルのターンのときの処理を考えてみましょう。
これまで使用してきたRivalTurnメソッドをそのまま改造しようとするので以下のように変更しました。
カードを単独で出せるか、ジョーカーといっしょならどうかについてそれぞれ考えます。そして両方が可能であれば1/5の確率でジョーカーといっしょに出します。片方の方法でしか出せないならその方法で出します。
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 { void RivalTurn(int rivalID) { var rivalCards = playersCards[rivalID]; if(rivalCards.Count == 1 && rivalCards[0].Mark == CardMark.joker) { string str = String.Format("ジョーカーしかもっていない {0} は失格です", rivalID); MessageBox.Show(str, "報告", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // 失格の表示をさせるための処理 passCounts[rivalID] = PASS_MAX + 1; ShowPlayerInfo(rivalID); return; } // 単独で出せるか? List<Card> canCards = rivalCards.Intersect(GetCanTakeout()).ToList(); // ジョーカーと一緒なら出せるか? List<PairWithJoker> pairWithJokers = GetPairWithJoker(rivalCards); // 両方の方法で出せるなら 1/5の確率でジョーカーと出す if(canCards.Count > 0 && pairWithJokers.Count > 0) { int a = random.Next(0, 5); if(a == 3) TakeoutWithJoker(rivalID); else TakeoutSingle(rivalID); } else if(canCards.Count > 0) { // 単独でしか出せないなら単独で出す TakeoutSingle(rivalID); } else if(pairWithJokers.Count > 0) { // ジョーカーと一緒にしか出せないならジョーカーと出す TakeoutWithJoker(rivalID); } else RivalPass(rivalID); // どちらもできないならパス ShowPlayerInfo(rivalID); } } |
TakeoutSingleはカードを一枚だけ出すメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { void TakeoutSingle(int rivalID) { var rivalCards = playersCards[rivalID]; List<Card> canCards = rivalCards.Intersect(GetCanTakeout()).ToList(); int next = random.Next(0, canCards.Count); Card nextCard = canCards[next]; rivalCards.Remove(nextCard); nextCard.isExists = true; Rectangle rectInvalidate = new Rectangle(nextCard.Point, Card.Size); Invalidate(rectInvalidate); } } |
TakeoutWithJokerはジョーカーといっしょに出すためのメソッドです。
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 TakeoutWithJoker(int rivalID) { var rivalCards = playersCards[rivalID]; List<PairWithJoker> pairWithJokers = GetPairWithJoker(rivalCards); Card jokerCard = rivalCards.First(x => x.Mark == CardMark.joker); // どこへ出すか? int next = random.Next(0, pairWithJokers.Count); PairWithJoker nextPair = pairWithJokers[next]; // 出したカードは手元から取り除く rivalCards.Remove(nextPair.Card); rivalCards.Remove(jokerCard); // 出されたカードが見えるようにする jokerCard.UseJoker(nextPair.Joker.Mark, nextPair.Joker.Number); jokerCard.isExists = true; Rectangle rectInvalidate1 = new Rectangle(jokerCard.Point, Card.Size); Invalidate(rectInvalidate1); nextPair.Card.isExists = true; Rectangle rectInvalidate2 = new Rectangle(nextPair.Card.Point, Card.Size); Invalidate(rectInvalidate2); OnUseJoker(jokerCard, nextPair.Joker); } } |
では自分がカードを出すときはどのようにすればいいでしょうか?
ゲーム開始のときとライバルのターンが終わったときに、引数が0でCanPlayerTakeoutCardメソッドが呼ばれます。ここでカードを出すことができるかどうかの判定をしているわけですが、ここにジョーカーと一緒なら出せるかどうかの判定する処理をかけばいいですね。
CanPlayerTakeoutCard(0)の実行結果がfalseの場合、強制的にパスになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form1 : Form { bool CanPlayerTakeoutCard(int player) { List<Card> cards = GetCanTakeout(); if(playersCards[player].Intersect(cards).Count() > 0) return true; List<PairWithJoker> pairWithJokers = GetPairWithJoker(playersCards[player]); if(pairWithJokers.Count > 0) return true; return false; } } |
CanPlayerTakeoutCard(0)の実行結果がfalseの場合、強制的にパスになるため、前回書き換えたMyPassメソッドをは元に戻します。
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 |
public partial class Form1 : Form { void MyPass() { var myCards = playersCards[0]; // ジョーカーしかもっていないのであれば失格 if(myCards.Count == 1 && myCards[0].Mark == CardMark.joker) { string str = String.Format("ジョーカーしかもっていないのであなたは失格です"); MessageBox.Show(str, "報告", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); // 失格の表示をさせるための処理 passCounts[0] = PASS_MAX + 1; ShowPlayerInfo(0); return; } IsMyTurn = false; passCounts[0]++; if(passCounts[0] <= PASS_MAX) { string str = String.Format("出せるカードがないのでパスします。パス{0}回目", passCounts[0]); MessageBox.Show(str, "報告", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); } else { string str = String.Format("出せるカードがありません。{0}回パスをしたので失格です。", passCounts[0]); MessageBox.Show(str, "報告", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); OnRivalLost(0); } ShowPlayerInfo(0); } } |
前回はジョーカーはカードをもっていない部分であればどこにでも置くことができましたが、今回は違います。ジョーカーを使うときはジョーカーではなくいっしょに出したいカードをクリックさせるようにします。
ジョーカーをクリックした場合は「ジョーカーではなく一緒に出したいカードをクリックせよ」という内容のメッセージボックスを出します。
適切なクリックがされた場合は、自動的に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 |
public partial class Form1 : Form { private void Form1_MouseClick(object sender, MouseEventArgs e) { if(!IsMyTurn) return; if(playersCards == null) return; var playerCards = playersCards[0]; int cardsCount = playerCards.Count; if(cardsCount == 0) return; OnMouseClickForTakeCard(e); ShowPlayerInfo(0); if(!IsMyTurn) { OnRivalsTurn(); } } } |
OnMouseClickForTakeCardメソッドはクリックされた場所に自分のカードがあるかどうか調べてTakeoutMyCardIfCanメソッドを呼びます。TakeoutMyCardIfCanメソッドは名前のとおり、可能であれば引数でわたされたカードを出します。
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 |
public partial class Form1 : Form { void OnMouseClickForTakeCard(MouseEventArgs e) { var myCards = playersCards[0]; int cardsCount = myCards.Count; for(int i = 0; i < cardsCount; i++) { Rectangle rect = GetMyCardRectangle(i); if(rect.Top > e.Y) return; if(rect.Bottom < e.Y) return; if(rect.Left < e.X && e.X < rect.Right) { Card card = myCards[i]; TakeoutMyCardIfCan(card); return; } } } } |
TakeoutMyCardIfCanメソッドを示します。
カードがクリックされたらそのまま出せるか、ジョーカーとセットで出せるかの判定をして可能であればカードを出します。適切ではないカードがクリックされたらエラーのメッセージボックスを表示します。
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 TakeoutMyCardIfCan(Card card) { var myCards = playersCards[0]; int num = card.Number; CardMark mark = card.Mark; if(mark == CardMark.joker) { MessageBox.Show("ジョーカーではなくジョーカーといっしょに出したいカードをクリックしてください", "", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if(CanTakeout(mark, num)) { myCards.Remove(card); card.isExists = true; Invalidate(); IsMyTurn = false; return; } // ジョーカーと出せるか? List<PairWithJoker> pairWithJokers = GetPairWithJoker(myCards); if(pairWithJokers.Count != 0) { var pair = pairWithJokers.First(x => x.Card == card); Card joker = myCards.First(x => x.Mark == CardMark.joker); myCards.Remove(pair.Card); pair.Card.isExists = true; myCards.Remove(joker); joker.isExists = true; joker.UseJoker(pair.Joker.Mark, pair.Joker.Number); Invalidate(); OnUseJoker(joker, pair.Joker); IsMyTurn = false; } else { MessageBox.Show("このカードは出せません!", "", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } |