
順位をつける
殺しの七並べの場合、先にあがれば上位なのかというと違います。殺されたカードが少ない方が上位です。カードが同数のときは先にあがったほうが上位です。またパスを4回してしまって脱落した場合は先に脱落した人が下位になります。
もっとはやくつくるべきだったPlayerクラス
ゲームが終わったら順位をつけます。
そのためにPlayerというクラスをつくります。本当はこのクラスを先につくっておけばよかったのですが、順序が逆になってしまいました。普通の七並べではプレーヤーの情報はそんなに多くなかったのでクラスをつくって管理する必要がなかったからです。
遅ればせながらクラスをつくってデータを格納します。
|
class Player { public Player(int playerId, int deadCardCount, long agariTime, long lostTime) { PlayerId = playerId; DeadCardCount = deadCardCount; AgariTime = agariTime; LostTime = lostTime; } public int PlayerId = 0; public int DeadCardCount = 0; public long AgariTime = 0; public long LostTime = 0; } |
あがったときの処理と脱落したときの処理に少し付け足します。あがったときの時刻、リタイアしたときの時刻を取得すれば先にあがったのは誰か、先にリタイアしたのは誰かがわかります。
あがったり脱落したプレーヤーをPlayersのなかに格納し、全員がゲームから抜けたときにLINQのメソッドを使えば簡単に順位をつけることができます。
あがったときやリタイアしたときに成績を登録する
あがったときはプレーヤー番号とあがった時刻と殺されたカードの枚数をPlayerクラスのコンストラクタに渡して成績を登録します。
まずはあがったときのメソッドを示します。
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 { List<Player> Players = new List<Player>(); void OnAgari(int player) { if(player == 0) MessageBox.Show("あなたはあがりました"); else MessageBox.Show(player.ToString() + " はあがりました"); long agariTime = DateTime.Now.Ticks; // これを追加した Players.Add(new Player(player, deadCount[player], agariTime, -1)); // 4人ともあがるかリタイアしていればゲームセット if(Players.Count == 4) OnGameSet(); } void OnGameSet() { // 後述 } } |
以下はパスを4回してしまってリタイアしたときのメソッドです。
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 OnRivalLost(int rivalID) { // これを追加した Players.Add(new Player(rivalID, deadCount[rivalID], -1, DateTime.Now.Ticks)); var playerCards = playersCards[rivalID]; foreach(Card card in playerCards) { card.isExists = true; } Invalidate(); // 4人ともあがるかリタイアしていればゲームセット if(Players.Count == 4) OnGameSet(); } } |
OnRivalsTurnメソッドを一部変更
それからライバルターンに関するメソッドも変更する必要があります。
カードがないので強制パス?
カードが殺されて手持ちのカードがなくなった場合、出せるカードは持っていないので強制的にパスになってしまいます。これではいけないので順番が回ってきたときにカードがない場合はアガリにします。実際のルールでもカードが殺されて手持ちのカードがゼロになったときも、その状態ではあがれず、つぎに順番が回ってきたときにアガリになるそうです。(これもローカルルールかも)
ところがいまのままではライバルのターンが終わると出せるカードがあるかどうか判断して、ないのであれば自動的にパスにしています。「カードを1枚も持っていないので出せるカードがない、だからパス」では困ります。
またライバルも自分のカードが殺されることで手持ちがゼロになったとき、Playersリストに自分の成績を登録する機会がありません。これではゲームが終わっても結果が表示されないという問題がおきます。
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
|
public partial class Form1 : Form { async void OnRivalsTurn() { // このif文はかわらず if(playersCards[0].Count == 0 && !isAgari) { OnAgari(0); isAgari = true; } // あがってもいないしリタイアもしていないときはカードを出すのだが・・・ for(int i = 1; i< playersCards.Length; i++) { if(playersCards[i].Count != 0 && passCounts[i] <= PASS_MAX) { await System.Threading.Tasks.Task.Delay(1000); RivalTurn(i); if(playersCards[i].Count == 0) { OnAgari(i); } } else { // これまでこの部分がなかった!! // カードが殺されて手持ちのカードがなくなっていることがあるので注意すること // Playersに情報が格納されていないなら格納する if(!Players.Any(x => x.PlayerId == i)) { OnAgari(i); } } } // このif文はかわらず前のものと変わらず if(isAgari || passCounts[0] > PASS_MAX) { if( (playersCards[1].Count != 0 && passCounts[1] <= PASS_MAX) || (playersCards[2].Count != 0 && passCounts[2] <= PASS_MAX) || (playersCards[3].Count != 0 && passCounts[3] <= PASS_MAX) ) OnRivalsTurn(); return; } // この部分も追加!! // カードが殺されて手持ちのカードがなくなった場合、永久にパスすることになってしまう if(playersCards[0].Count == 0 && !isAgari) { OnAgari(0); isAgari = true; } else if(!CanPlayerTakeoutCard(0)) { MyPass(); OnRivalsTurn(); } else IsMyTurn = true; } } |
ゲームセットになったら順位をつける
ゲームセットになったらOnGameSetメソッドが呼ばれます。これまでに集めた情報を利用して順位をつけます。途中でリタイアした人はあがることができた人より順位は下です。また殺されたカードが少ない順に並べますが、同じ場合はあがった時刻で順位を決めます。OrderBy~ThenByというのは便利です。
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 partial class Form1 : Form { void OnGameSet() { // 脱落しないであがったプレイヤーの順位をつける List<Player> players1 = Players.Where(x => x.AgariTime > 0). OrderBy(x => x.DeadCardCount).ThenBy(x => x.AgariTime).ToList(); // 脱落してしまったプレイヤーの順位をつける List<Player> players2 = Players.Where(x => x.AgariTime < 0). OrderByDescending(x => x.AgariTime).ToList(); players1.AddRange(players2); int i = 1; string str = ""; foreach(Player player in players1) { string name = ""; if(player.PlayerId == 0) name = "あなた"; else name = player.PlayerId.ToString(); if(i == 1) str += "一位:" + name + "\n"; if(i == 2) str += "二位:" + name + "\n"; if(i == 3) str += "三位:" + name + "\n"; if(i == 4) str += "最下位:" + name + "\n"; i++; } // 順位がわかれば必要ない Players.Clear(); MessageBox.Show(str, "ゲームの結果", MessageBoxButtons.OK, MessageBoxIcon.Information); } } |
実際に遊んでみてわかったことは12枚殺しや8枚殺しはけっこうな確率で起きやすいということ。終盤になってカードが大量死することもかなりの割合でおきます。

鳩でも分かるC#管理人からのお願い
できる仕事であれば請け負います。鳩でもわかるC#管理人はクラウドワークスに在宅ワーカーとして登録しています。お仕事の依頼もお待ちしております。
⇒ 仕事を依頼する
コメントについて
コメントで英語などの外国語でコメントをされる方がいますが、管理人は日本語以外はわからないので基本的に内容が理解できず、承認することもありません。それからへんな薬を売っているサイトやリンク先のサイトが存在しないというスパムコメントも多々あります。
Some people make comments in foreign languages such as English, but since the manager does not understand anything other than Japanese, he basically cannot understand the content and does not approve it. Please use Japanese when making comments.
そんななか日本語のコメントもいただけるようになりました。「○○という変数はどこで宣言されているのか?」「××というメソッドはどこにあるのか」「例外が発生する」「いっそのことソース丸ごとくれ」という質問ですが、管理人としては嬉しく思います。「自分が書いた記事は読まれているんだな」と。疑問点には可能な限り答えます。記事に問題があれば修正いたします。
そのうえでお願いがあります。「匿名」という味も素っ気もない名前ではなく、捨てハンでいいのでなにかハンドルネームをつくってほしいと思います。
管理人のモチベーションアップのために
よろしければご支援お願いします。
⇒ 管理人の物乞いリスト