前回はプレイヤーの名前と残り枚数、その他の情報を表示させるためにユーザーコントロールを作成しました。
Contents
カードを引いた結果、カードはそろったのか?
今回は「その他の情報」としてカードを引いた結果、カードはそろったのか? 確定した順位などを表示させることを考えています。
Playerクラスでは相手のカードを引くためのメソッドを作成しましたが、これを少し変更します。カードを引いた結果を返すようにつくりかえます。
CardPairクラスを変更
CardPairクラスはカードを捨てたときに空いてから引いたカードと元から持っていたカードの組み合わせを知るためのクラスです。Player.PullCardメソッドがnullを返さなかった場合はペアができたことになります。またどんなペアかもわかるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class CardPair { public CardPair(Card pulledCard, Card myCard) { PulledCard = pulledCard; MyCard = myCard; } public Card PulledCard { get; private set; } public Card MyCard { get; private set; } } |
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 class Player { Random random = new Random(); public CardPair PullCard(Player player, int i) { // 存在しないカードは引けない if(player.Cards.Count <= i) return null; // 引いたカードは相手の手元からなくなる Card card = player.Cards[i]; player.Cards.Remove(card); // 数字があっているならカードを捨てる、ないなら引いたカードを自分のカードに加える Card card1 = Cards.FirstOrDefault(x => x.Number == card.Number); if(card1 == null) { // カードはどこに追加するか? int where = random.Next(-1, Cards.Count); if(where == -1) Cards.Add(card); else Cards.Insert(where, card); return null; } else { Cards.Remove(card1); return new CardPair(card, card1); } } } |
それから引いたカードをどの位置に追加するかも最後に追加するのではなくランダムな位置になるように変更しました。
RivalsTurn1メソッドを一部変更
ライバルたちがカードを引いたときに表示されている情報を更新します。
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 |
public partial class Form1 : Form { int RivalsTurn1() { List<Player> players1 = Players.Where(x => !x.IsFinished).ToList(); int playersCount = players1.Count; for(int i = 1; i < playersCount; i++) { if(players1[i].Cards.Count != 0) { System.Threading.Thread.Sleep(1000); int rand = random.Next(0, players1[i - 1].Cards.Count); CardPair pair = players1[i].PullCard(players1[i - 1], rand); // 自分のカードが引かれたならイメージとして表示されているカードを減らす if(i-1 == 0) panel2.Invalidate(); // カードを引かれた側の情報表示 // カードを引かせることであがってしまうかもしれない if(players1[i - 1].Cards.Count == 0) { players1[i - 1].IsFinished = true; PlayerFinished(players1[i - 1]); } else { players1[i - 1].PlayerInfo.ShowCardsCount(players1[i - 1].Cards.Count); } // カードを引いた側の情報表示の前に少し時間差をつける System.Threading.Thread.Sleep(200); // カードの残数を表示 players1[i].PlayerInfo.ShowCardsCount(players1[i].Cards.Count); // その他の情報を表示 if(pair != null) { string str = String.Format("{0}と{1}のペアができました", GetCardString(pair.PulledCard), GetCardString(pair.MyCard)); players1[i].PlayerInfo.ShowPlayerInfo(str); // ペアができたことであがってしまうかもしれない if(players1[i].Cards.Count == 0) { players1[i].IsFinished = true; PlayerFinished(players1[i]); } } else { players1[i].PlayerInfo.ShowPlayerInfo("ペアはできなかったようです。"); } } } return Players.Count(x => !x.IsFinished); } } |
これはcardを文字列に変換するメソッドです。
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 |
public partial class Form1 : Form { string GetCardString(Card card) { string suit = ""; if(card.Suit == Suit.Spade) suit = "スペード"; if(card.Suit == Suit.Hart) suit = "ハード"; if(card.Suit == Suit.Dia) suit = "ダイヤ"; if(card.Suit == Suit.Club) suit = "クラブ"; string number = ""; if(card.Number == 1) number = "A"; else if(card.Number == 11) number = "J"; else if(card.Number == 12) number = "Q"; else if(card.Number == 13) number = "K"; else number = card.Number.ToString(); return String.Format("{0}の{1}",suit, number); } } |
あがったときの処理を変更
それからあがったときにメッセージボックスを出さずにプレイヤー情報として表示させることにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public partial class Form1 : Form { void PlayerFinished(Player player) { if(!finishedPlayers.Any(x => x == player)) { finishedPlayers.Add(player); string str = String.Format("{0}位", rank++); player.PlayerInfo.ShowPlayerInfo(str); if(Players.Count-1 == finishedPlayers.Count) { string name = Players.Except(finishedPlayers).ToList()[0].Name; string str1 = String.Format("終了!\n{0}が最下位です", name); MessageBox.Show(str1); } } } } |
自分がカードを引いたときも情報を表示させる
また自分がカードを引いたときも情報が表示されるようにする必要があります。
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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
public partial class Form1 : Form { private void panel1_MouseDown(object sender, MouseEventArgs e) { // 自分のターンでないときはなにもしない if(!IsMyTurn) return; int cardIndex = GetIndexClickedCard(e.X); // 不正な場所がクリックされた if(cardIndex == -1) { MessageBox.Show("カードのある位置をクリックしてください", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } List<Card> oldCards = Players[0].Cards.ToList(); // カードを引いたあとの処理 Player pulledPlayer = Players.Last(x => !x.IsFinished); CardPair pair = Players[0].PullCard(pulledPlayer, cardIndex); // カードを引かれた側の情報表示 // カードを引かせることであがってしまうかもしれない if(pulledPlayer.Cards.Count == 0) { pulledPlayer.IsFinished = true; PlayerFinished(pulledPlayer); } else { pulledPlayer.PlayerInfo.ShowCardsCount(pulledPlayer.Cards.Count); } // フォームに表示されているカードのイメージを更新 panel1.Invalidate(); panel2.Invalidate(); // 自分のカードの残数を表示 Players[0].PlayerInfo.ShowCardsCount(Players[0].Cards.Count); // その他の情報を表示 if(pair != null) { // ペアができている場合。 Players[0].PlayerInfo.ShowPlayerInfo(GetStringPair(pair.PulledCard, pair.MyCard)); // ペアができたことであがってしまうかもしれない if(Players[0].Cards.Count == 0) { Players[0].IsFinished = true; PlayerFinished(Players[0]); } } else { // ペアができていない場合。 // 引いたカードが何かわかるようにする Card card = Players[0].Cards.Except(oldCards).First(); Players[0].PlayerInfo.ShowPlayerInfo(GetCardString(card)+" ペアはできなかったようです。"); } // ライバルのターンへ RivalsTurn(); } // 何と何でペアができたのか、結果を文字列として取得する // 出力の例 "{0}の{1}と{2}の{3}のペアができました" string GetStringPair(Card pulledCard, Card myCard) { string pulledCardSuit = ""; if(pulledCard.Suit == Suit.Spade) pulledCardSuit = "スペード"; if(pulledCard.Suit == Suit.Hart) pulledCardSuit = "ハード"; if(pulledCard.Suit == Suit.Dia) pulledCardSuit = "ダイヤ"; if(pulledCard.Suit == Suit.Club) pulledCardSuit = "クラブ"; string pulledCardNumber = ""; if(pulledCard.Number == 1) pulledCardNumber = "A"; else if(pulledCard.Number == 11) pulledCardNumber = "J"; else if(pulledCard.Number == 12) pulledCardNumber = "Q"; else if(pulledCard.Number == 13) pulledCardNumber = "K"; else pulledCardNumber = pulledCard.Number.ToString(); string myCardSuit = ""; if(myCard.Suit == Suit.Spade) myCardSuit = "スペード"; if(myCard.Suit == Suit.Hart) myCardSuit = "ハード"; if(myCard.Suit == Suit.Dia) myCardSuit = "ダイヤ"; if(myCard.Suit == Suit.Club) myCardSuit = "クラブ"; string myCardNumber = ""; if(myCard.Number == 1) myCardNumber = "A"; else if(myCard.Number == 11) myCardNumber = "J"; else if(myCard.Number == 12) myCardNumber = "Q"; else if(myCard.Number == 13) myCardNumber = "K"; else myCardNumber = myCard.Number.ToString(); return String.Format( "{0}の{1}と{2}の{3}のペアができました", pulledCardSuit, pulledCardNumber, myCardSuit, myCardNumber); } } |
これで自分がカードを引いたときも情報が表示されるようになります。
カードを引く相手は誰なのかを表示する
フォーム上部にはカードが表示されているのですが、これが誰のカードなのでしょうか。これまではそれがわかりにくかったので、わかるように改良します。
自分が誰のカードを引くのかはゲームの進行によってかわります。これまでカードを引いていた相手があがってしまうと別のプレイヤーからカードを引くことになります。
フォーム上に誰のカードが表示されているのか表示するラベルを追加しました。
ここの内容が変更されるのは、ゲームが始まったとき、自分に順番が回ってきたときです。
そこでゲーム開始のときとライバルたちのターンが終わったときに、ここに適切な文字列を表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); CreateCards(); CreatePlayer(); this.BackColor = Color.Green; // ラベルのフォント、表示されている文字列の初期化 labelPulledPlayer.ForeColor = Color.White; labelPulledPlayer.Text = ""; Font oldfont = labelPulledPlayer.Font; Font newFont = new Font(oldfont, FontStyle.Bold); labelPulledPlayer.Font = newFont; } } |
ゲーム開始時と自分に順番がまわってきたときにはShowRivalCardsメソッドが実行されるので、ここで処理をおこないます。
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 { void ShowRivalCards(Graphics g) { if(Players[0].IsFinished) { labelPulledPlayer.Text = ""; return; } Player PulledPlayer = Players.Last(x => !x.IsFinished); if(PulledPlayer.Cards.Count != 0 && PulledPlayer != Players[0]) labelPulledPlayer.Text = Players.Last(x => !x.IsFinished).Name + "のカードが表示されています。"; else labelPulledPlayer.Text = ""; // これより下はこれまでと変わりなし // 省略 } } |
失礼いたします。いつも参考にさせていただいています。
GetStringPairメソッドが存在しない為、お手数ですが、記述の方をおねがいいたします。
すみません。記事を修正しました。
ありがとうございます。
また、続けて申し訳ありません。CPUのターン時
ShowCardsCountメソッド内の
labelCount.Text = “残り ” + a.ToString() + “枚”;
にて下記の例外がスローされました。
InvalidOperationException: 有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール ‘labelCount’ がアクセスされました。
解決方法を調査してはおりますが、なかなか見つからずにいます
>InvalidOperationException: 有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール ‘labelCount’ がアクセスされました。
非同期処理をしているのでその対策が必要でした。
ありがとうございます!!
無事動作いたしました!!