ドボンのルールはここではここに書かれているものを採用します。
まずカードを配る必要があります。
「ドボンのルールと遊び方」にはカードの配布について、このように書かれています。
ジョーカーは使わない。
「親」は左となりの人から時計回りに1枚ずつ「5枚」配る。
残ったカードは中央の場に重ねて積み札とする。
プレイヤーは4人
ゲームをするためにはプレイヤーが必要です。今回はあなたとライバルをあわせて4人にします。
1 2 3 4 5 6 7 8 9 10 11 |
public partial class Form1 : Form { List<Player> Players = new List<Player>(); void CreatePlayer() { Players.Add(new Player("あなた", panelSouth, this)); Players.Add(new Player("佐藤さん", panelWest, this)); Players.Add(new Player("鈴木さん", panelNorth, this)); Players.Add(new Player("高橋さん", panelEast, this)); } } |
プレイヤークラスは以下のとおりです。コンストラクタの引数Panelはカードが表示される部分(後述)です。実はこのままでは不十分で、カードを出すメソッドをつくる必要があります。
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 |
public class Player { Form1 _form1 = null; public Player(string name, Panel playerPanel, Form1 form1) { Name = name; PlayerPanel = playerPanel; _form1 = form1; } public string Name { set; get; } = "名無し"; public Panel PlayerPanel { private set; get; } = null; // 持っているカード public List<Card> Cards = new List<Card>(); } |
カードをつくる
次にカードをつくります。ジョーカーは使わないので作りません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form1 : Form { List<Card> Cards = new List<Card>(); void CreateCards() { for(int i=1; i<=13; i++) Cards.Add(new Card(Suit.Spade, i)); for(int i = 1; i <= 13; i++) Cards.Add(new Card(Suit.Hart, i)); for(int i = 1; i <= 13; i++) Cards.Add(new Card(Suit.Dia, i)); for(int i = 1; i <= 13; i++) Cards.Add(new Card(Suit.Club, i)); } } |
CreatePlayerメソッドとCreateCardsメソッドはアプリが起動して最初に1回だけ実行すればよいのでコンストラクタに入れてしまいましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); CreateCards(); CreatePlayer(); this.BackColor = Color.Green; // それっぽくするために背景を緑にする } } |
ゲーム開始のときはカードをシャッフルして、各プレイヤーに配ります。
乱数を発生させてカードのなかから適当に抜き取ったカードをShuffledCardsに格納しています。
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 { List<Card> ShuffledCards = new List<Card>(); Random random = new Random(); void ShuffleCard() { ShuffledCards.Clear(); // 1回目は空だが2回目以降はカードが残っているのでクリアする List<Card> tempCards = Cards.ToList(); // カードは52枚 for(int i = 0; i < 52; i++) { int index = random.Next(0, tempCards.Count); Card card = tempCards[index]; tempCards.RemoveAt(index); ShuffledCards.Add(card); } } } |
シャッフルしたらカードを配ります。カードを持っているのであればクリアします。またルールでは5枚ずつなのでShuffledCardsの先頭から5枚取ります。そしてShuffledCardsからは5枚取り除きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public partial class Form1 : Form { void HandoutCards() { // カードを持っているのであればクリアする foreach(Player player in Players) player.Cards.Clear(); // カードは5枚 foreach(Player player in Players) { List<Card> cards = ShuffledCards.Take(5).ToList(); player.Cards.AddRange(cards); ShuffledCards.RemoveRange(0, 5); } } } |
カードを表示する
カードを配りおわったらライバルがもっているカードの枚数と自分のカードを表示させる必要があります。
フォーム上にコントロールを配置します。
4つのPanelを使用しています。下のパネルが自分のカードが表示される部分です。4つのパネルに東西南北の名前をつけます。
まず自分のカードを表示させる部分からやっていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public partial class Form1 : Form { int MARGIN = 10; void ShowMyCards(Graphics g) { List<Card> myCards = Players[0].Cards; int startX = 0; int cnt = 0; foreach(Card card in myCards) { int x = startX + cnt * (card.Size.Width + MARGIN); Rectangle rect = new Rectangle(new Point(x, 0), card.Size); g.DrawImage(card.Bitmap, rect); cnt++; } } private void panel1_Paint(object sender, PaintEventArgs e) { ShowMyCards(e.Graphics); } } |
あとは表示させたいタイミングで
1 |
panelSouth.Invalidate(); |
とやれば自分のカードを表示させることができます。
ライバルのカードは裏向きで表示させます。
順番は時計回りなので、自分が親なら 西、北、東の順番になります。ということはpanelWestにはPlayers[1]、panelNorthにはPlayers[2]、panelEastにはPlayers[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 |
public partial class Form1 : Form { void ShowRivalCards(Graphics g, int rival) { List<Card> rivalCards = Players[rival].Cards; int startX = 0; int cnt = 0; foreach(Card card in rivalCards) { int x = startX + cnt * (card.Size.Width + MARGIN); Rectangle rect = new Rectangle(new Point(x, 0), card.Size); g.DrawImage(card.BackBitmap, rect); cnt++; } } private void panelWest_Paint(object sender, PaintEventArgs e) { ShowRivalCards(e.Graphics, 1); } private void panelNorth_Paint(object sender, PaintEventArgs e) { ShowRivalCards(e.Graphics, 2); } private void panelEast_Paint(object sender, PaintEventArgs e) { ShowRivalCards(e.Graphics, 3); } } |
このようにしておけば、ゲームが開始されたときにGameStartメソッドを呼べば図のように表示されるはずです。GameStartメソッドは以下のような内容です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form1 : Form { void GameStart() { ShuffleCard(); HandoutCards(); panelSouth.Invalidate(); panelWest.Invalidate(); panelNorth.Invalidate(); panelEast.Invalidate(); } } |
ちょっとデザイン的には最悪なので改善します。
デザイン的な部分を修正
プレイヤーを円形に並べるのであれば、自分のカードは左右均等の位置に表示させたい。
ババ抜きのときのようにライバルのカードを選択することはなく、持っている枚数がわかればよいのでカードは重ねて表示させてよいのではないか。
などです。
まずライバルのカードは重ねて表示するのであれば、これでよさそうです。カードの裏はどのカードも同じなのでいったん取得したものをそのままコピーして使っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { void ShowRivalCards(Graphics g, int rival) { List<Card> rivalCards = Players[rival].Cards; Bitmap cardBackBMP = Cards[0].BackBitmap; int x = 0; // (panel2.Width - cardsWidth) / 2; foreach(Card card in rivalCards) { Rectangle rect = new Rectangle(new Point(x, 0), card.Size); g.DrawImage(cardBackBMP, rect); x += 15; } } } |
しかしカードをフォーム上にバランスよく配置するためには工夫が必要です。西側なら左づめ、東なら右づめ、北側なら中央によせるとか・・・。
そこで引数をちょっと変えて、持っているカードと1枚目のカードを表示するX座標を引数にするメソッドに変更してみました。
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 |
public partial class Form1 : Form { void ShowRivalCards(Graphics g, List<Card> rivalCards, int baseX) { Card card0 = Cards[0]; Bitmap cardBackBMP = card0.BackBitmap; foreach(Card card in rivalCards) { Rectangle rect = new Rectangle(new Point(baseX, 0), card.Size); g.DrawImage(cardBackBMP, rect); baseX += 15; // 2枚目以降は15ピクセルだけずらす } } // 左づめならそのまま private void panelWest_Paint(object sender, PaintEventArgs e) { ShowRivalCards(e.Graphics, Players[1].Cards, 0); } // 中央なら、表示に必要な幅を計算して // パネルの幅からそれを引いたものを半分にしたものが一枚目のX座標 private void panelNorth_Paint(object sender, PaintEventArgs e) { int cardCount = Players[2].Cards.Count; int cardWidth = Cards[0].Size.Width; int width = (cardCount-1) * 15 + cardWidth; int baseX = (panelNorth.Width - width) /2; ShowRivalCards(e.Graphics, Players[2].Cards, baseX); } // 右左づめなら表示に必要な幅を計算して // パネルの幅からそれを引いたものが一枚目のX座標 private void panelEast_Paint(object sender, PaintEventArgs e) { int cardCount = Players[3].Cards.Count; int cardWidth = Cards[0].Size.Width; int width = (cardCount - 1) * 15 + cardWidth; int baseX = panelEast.Width - width; ShowRivalCards(e.Graphics, Players[3].Cards, baseX); } } |
自分のカードを表示させる
それから自分のカードを表示するメソッドですが、クリックしたときにどれかわかるようにするため、
カードが表示される矩形を求める
その矩形にカードを表示する
という方式に変えました。
GetRectanleMyCardはカードが表示される矩形を求めるメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class Form1 : Form { List<Rectangle> GetRectanleMyCard() { int cardCount = Players[0].Cards.Count; int cardWidth = Cards[0].Size.Width; int width = (cardCount - 1) * MARGIN + cardCount * cardWidth; int baseX = (panelSouth.Width - width) / 2; List<Card> myCards = Players[0].Cards; int cnt = 0; List<Rectangle> rects = new List<Rectangle>(); foreach(Card card in myCards) { int x = baseX + cnt * (card.Size.Width + MARGIN); rects.Add(new Rectangle(new Point(x, 0), card.Size)); cnt++; } return rects; } } |
ShowMyCardsメソッドはGetRectanleMyCardによって求められた矩形にカードを描画するためのものです。
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 ShowMyCards(Graphics g, List<Rectangle> rects) { List<Card> myCards = Players[0].Cards; int cnt = 0; foreach(Card card in myCards) { g.DrawImage(card.Bitmap, rects[cnt]); cnt++; } } private void panel1_Paint(object sender, PaintEventArgs e) { List<Rectangle> rects = GetRectanleMyCard(); ShowMyCards(e.Graphics, rects); } } |
これで
1 |
panelSouth.Invalidate(); |
が実行されると自分のカードが表示されるようになります。
失礼いたします。
ShuffleCardメソッド内の
int index = random.Next(0, tempCards.Count);
のrandomですが、
random.Next→Random.Next(Random先頭を大文字)が正しいのでしょうか?
また、修正したその際
CS0120 静的でないフィールド、メソッド、またはプロパティ ‘Random.Next(int, int)’ で、オブジェクト参照が必要です
というエラーになります。
度々失礼いたします。
変数(定数?)のMARGINが定義されていませんが、ババ抜きと同じ
int MARGIN = 10;
でよろしいのでしょうか?
失礼いたします。
先ほど質問しました。randomですが、ババ抜き、7並べ同様
Random random = new Random();
を作成するであっていますでしょうか?
失礼いたします。
>ShuffleCardメソッド内の
>int index = random.Next(0, tempCards.Count);
>のrandomですが、
>random.Next→Random.Next(Random先頭を大文字)が正しいのでしょうか?
すみません。フィールド変数 randomを書くのを忘れていました。該当部分は修正しました。
>変数(定数?)のMARGINが定義されていませんが、ババ抜きと同じ
>int MARGIN = 10;
>でよろしいのでしょうか?
はい、そのとおりです。
>先ほど質問しました。randomですが、ババ抜き、7並べ同様
>Random random = new Random();
>を作成するであっていますでしょうか?
はい。それで合っています。