Contents
殺しの七並べと簡単なルール説明
「殺しの七並べ」。物騒な名前ですが、普通の七並べとちがって面白いゲームです。簡単にルールだけ説明します。
斜めでもつながっていればOK
普通の七並べではカードは横につながっている場合でないと出すことはできませんでした。しかし「殺しの七並べ」は違います。横だけでなく、縦や斜めにつながっていてもカードを出すことができます。
このような場合、通常は5のカードを出すのであればダイヤの5しか出せなかったのですが、殺しの七並べではハートの5、クローバーの5も出すことができるのです。
囲まれたカードは殺される
もうひとつの特徴は「カードを殺す」ことができることです。この部分はローカルルールで多少違っている部分もありますが、上下左右を他のカードで囲まれてしまうとそのカードは殺されてしまうという点です。下の図ではクローバーの6が死んでいます。
また複数のカードを殺すこともできます。ただし四角く囲む必要があります。
下の図の下部ではクローバーの5と6、上部ではスペードの5と6、ハードの5と6が死んでいます。
殺すときは四角く囲わないといけません。下の図のような囲み方では殺すことはできません。
順位のつけかたも通常の七並べとは違います。通常の七並べでは先にあがった人が上位ですが、殺しの七並べでは殺されたカードの枚数がもっとも少ない人が優勝です。殺されたカードの枚数が同じ場合は先に上がった人が上位です。
パスについてはできる回数には制限があるルールとないルールがあります。制限がある場合、制限を超えてしまうと脱落になります。脱落者は最下位、複数いる場合は先に脱落した人が下位となります。
またジョーカーを使ったりトンネルのルールは基本的にありません。
作ってみる
ではさっそくこのようなアプリをつくってみましょう。
まず前回まででつくった七並べゲームで利用できるものはすべて利用することにします。カードやカードをおくテーブルはそのまま使えます。新たに必要になるのはカードを置くことができるかどうかを判定するメソッド、カードの死を判定するメソッド、最後に各プレーヤーの順位を判定するメソッドなどです。
カードが出せるかどうか判定するメソッド
横だけでなく縦や斜めでもつながっているのであればカードを出すことができるので、出せるカードを求めるメソッドを作り直す必要があります。
GetCanPutCardsメソッドはそのためのメソッドです。まずテーブルのうえに存在しないカードを探して、それらが縦横斜めでつながっているカードをたどることで、7に到達できるかどうかを調べます。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { List<Card> GetCanPutCards() { List<Card> noExistsCards = cards.Where(x => !x.isExists).ToList(); return noExistsCards.Where(x => CanThisPutCard(x)).ToList(); } } |
CanThisPutCardメソッドは引数の異なるCanThisPutCardを再帰呼び出しをすることで7にたどり着けるかどうかを調べています。また再帰処理では一度調べたカードは調べる必要はないので、一度調べたカードはList
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 |
public partial class Form1 : Form { bool CanThisPutCard(Card card) { List<Card> noNeedCards = new List<Card>(); bool ret = CanThisPutCard(card, noNeedCards); return ret; } bool CanThisPutCard(Card card, List<Card> noNeedCards) { List<Card> cards1 = null; if(card.Number == 1) cards1 = cards.Where(x => x.Number == card.Number || x.Number == card.Number + 1).ToList(); else if(card.Number == 13) cards1 = cards.Where(x => x.Number == card.Number || x.Number == card.Number - 1).ToList(); else cards1 = cards.Where(x => x.Number == card.Number || x.Number == card.Number + 1 || x.Number == card.Number - 1).ToList(); List<Card> cards2 = new List<Card>(); if(card.Mark == CardMark.spade) { cards2.AddRange(cards1.Where(x => x.Mark == CardMark.spade).ToList()); cards2.AddRange(cards1.Where(x => x.Mark == CardMark.hart).ToList()); } if(card.Mark == CardMark.hart) { cards2.AddRange(cards1.Where(x => x.Mark == CardMark.spade).ToList()); cards2.AddRange(cards1.Where(x => x.Mark == CardMark.hart).ToList()); cards2.AddRange(cards1.Where(x => x.Mark == CardMark.dia).ToList()); } if(card.Mark == CardMark.dia) { cards2.AddRange(cards1.Where(x => x.Mark == CardMark.hart).ToList()); cards2.AddRange(cards1.Where(x => x.Mark == CardMark.dia).ToList()); cards2.AddRange(cards1.Where(x => x.Mark == CardMark.club).ToList()); } if(card.Mark == CardMark.club) { cards2.AddRange(cards1.Where(x => x.Mark == CardMark.dia).ToList()); cards2.AddRange(cards1.Where(x => x.Mark == CardMark.club).ToList()); } if(cards2.Any(x => x.Number == 7)) return true; noNeedCards.Add(card); List<Card> cards3 = cards2.Except(noNeedCards).Where(x => x.isExists).ToList(); cards3 = cards3.Where(x => x != card).ToList(); bool ret = false; foreach(var card1 in cards3) { noNeedCards.Add(card1); ret = CanThisPutCard(card1, noNeedCards); if(ret) return true; } return ret; } } |
クリックするとカードが出せるようにする
実際にカードを出すメソッドを作成します。手元のカードをクリックするとどのカードがクリックされたかを調べて適切なカードであればテーブルのうえに出されます。
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 |
public partial class Form1 : Form { private void Form1_MouseClick(object sender, MouseEventArgs e) { if(!IsMyTurn) return; if(playersCards == null) return; Card card = GetClickedCard(e); if(card == null) return; if(!CanThisPutCard(card)) { MessageBox.Show("このカードは出せません!", "", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } IsMyTurn = false; var myCards = playersCards[0]; myCards.Remove(card); card.isExists = true; Invalidate(); ShowPlayerInfo(0); if(!IsMyTurn) { OnRivalsTurn(); } } } |
これはどのカードがクリックされたのかを求めるメソッドです。
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 { Card GetClickedCard(MouseEventArgs e) { var myCards = playersCards[0]; int cardsCount = myCards.Count; if(cardsCount == 0) return null; for(int i = 0; i < cardsCount; i++) { Rectangle rect = GetMyCardRectangle(i); if(rect.Top > e.Y) return null; if(rect.Bottom < e.Y) return null; if(rect.Left < e.X && e.X < rect.Right) return myCards[i]; } return null; } } |
ライバルにカードを出させる
これはライバルにカードを出させるメソッドです。出すことができるカードを取得してライバルのカードとの積集合を求めます。0のときはパス、それ以外の時はランダムにカードが出されます。
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 { void RivalTurn(int rivalID) { var rivalCards = playersCards[rivalID]; List<Card> canCards = rivalCards.Intersect(GetCanPutCards()).ToList(); if(canCards.Count != 0) { 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); } else { RivalPass(rivalID); } ShowPlayerInfo(rivalID); } } |
このように斜めでもつながっているのであればカードが出されます。次に必要なことは死亡判定ですが、次回に続きます。