複数人に対応したジャンケンプログラムを作成しよう
前回の記事では、1(プレイヤー)対1(コンピューター)のジャンケンプログラムを作成しました。
今回は、複数人に対応したジャンケンプログラムを作成しましょう。
本記事で作成するプログラムの環境は、以下の通りです。
本記事で使用する開発環境
- IDE(統合開発環境)は、Visual Studio Community 2022 を使用します。
- 開発言語は、Visual Basic を使用します。
- フレームワークは、.NET Framework 4.8 を使用します。
Visual Studioのインストール方法は、以下のマイクロソフト公式サイトを参照してください。
ジャンケンのルール
ジャンケンはご存じのように、「グー」「チョキ」「パー」の三すくみになっています。
プレイヤーはグー、チョキ、パーのどれかを画面から入力して、コンピューターはグー、チョキ、パーをランダムに選択するようにします。
このゲームの仕様は、以下のようになります。
ゲームの仕様
- プレイヤーは、グーは「1」、チョキは「2」、パーは「3」を入力する
- プレイヤーが、1、2、3以外を入力した場合、メッセージを表示し、再度入力させるようにする
- コンピューターはランダムにグー、チョキ、パーを選択する
- プレイヤーとコンピューターの出した値を画面に表示する
- 誰が勝ったかを表示する
- ジャンケンを3回繰り返し、合計の勝利数が多い人を勝者とする
ジャンケンをするメンバーのクラス
まず、ジャンケンをするメンバーの情報を格納するクラスを作成します。
コンピューターのジャンケンの手は、Randomクラスを使ってランダムに出します。
クラスをインスタンス化する時に、メンバー名が渡されるようにして、コンストラクタの中でプロパティに設定します。
ジャンケン実行メソッド(Play)では、RandomクラスのNext()メソッドを使って、1~3の値を発生させ、Actプロパティに設定します。
なお、前回からの変更点として、インスタンスを作成するときにRandom()クラスで使うシード値が渡されます。
シード値を指定しないとデフォルトの値が設定され、どのコンピューターのメンバーも同じ手になってしまいます。
詳しくは、以下のマイクロソフトの公式サイトを参照してください。
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 Class Member 'プロパティ定義 Public ReadOnly Property Name As String '名前 Public Property Act As Integer 'ジャンケンの手 Public Property Win As Integer '勝数 Public Property Lose As Integer '負数 '乱数オブジェクト Private rnd As Random 'コンストラクタ Sub New(Name As String, Seed As Integer) Me.Name = Name Me.Act = 0 Me.Win = 0 Me.Lose = 0 rnd = New Random(Seed) End Sub 'ジャンケン実行メソッド Sub Play() Me.Act = rnd.Next(1, 4) End Sub End Class |
ジャンケンプログラムのモジュール
ジャンケンプログラムのモジュールは、以下のようになります。
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 |
Module Janken2 'ジャンケン回数 Private Const CNT As Integer = 3 'ジャンケン定義 Private HAND As New Dictionary(Of Integer, String) From {{1, "グー"}, {2, "チョキ"}, {3, "パー"}} 'メイン処理 Sub Main() Dim MemName() As String = {"Player", "Computer1", "Computer2"} 'メンバー名 Dim Gamer = New List(Of Member) 'Memberクラスのインスタンスを格納するList Dim InpNum As Integer = 0 'プレイヤーの入力値 Dim Seed As Integer = 0 'Randomのシード値 Dim Dt As DateTime = DateTime.Now '現在時間 Dim Action() As Integer 'ジャンケンの実行結果を格納する配列 Dim MaxWin = New List(Of Integer) '最多勝利数 'インスタンスを作成し配列に格納 For Each name As String In MemName Seed += CInt(Dt.Millisecond) 'シード値作成 Gamer.Add(New Member(name, Seed)) Next 'ジャンケン回数分ループ For i = 1 To CNT '配列初期化(予備,グー,チョキ,パー) Action = {0, 0, 0, 0} 'ジャンケンの手を入力 Console.WriteLine("◆ {0:d}回目 ◆", i) Console.WriteLine("### 1:グー 2:チョキ 3:パー ###") Console.Write("> ") '入力チェック(1~3以外はやり直し) If Integer.TryParse(Console.ReadLine(), InpNum) = False Then Gamer(0).Act = 0 Else Gamer(0).Act = InpNum End If If Gamer(0).Act < 1 OrElse Gamer(0).Act > HAND.Count Then Console.WriteLine(">>> 1~3を入力してください! <<<") Console.WriteLine() i -= 1 Continue For End If 'コンピューターがジャンケンを実行 For j = 1 To Gamer.Count - 1 Gamer(j).Play() Next '各メンバーの実行結果を表示 For j = 0 To Gamer.Count - 1 Console.Write("{0,-10}-> ", Gamer(j).Name) Console.WriteLine(HAND(Gamer(j).Act)) 'ジャンケンの手を配列に加算 Select Case Gamer(j).Act Case 1 'グー Action(1) += 1 Case 2 'チョキ Action(2) += 1 Case 3 'パー Action(3) += 1 End Select Next '勝敗判定(全員同じか全員違う場合は引き分け) If (Action(1) = Gamer.Count OrElse Action(2) = Gamer.Count OrElse Action(3) = Gamer.Count) OrElse (Action(1) <> 0 And Action(2) <> 0 And Action(3) <> 0) Then Console.WriteLine("*** 引き分け ***") Else If Action(1) = 0 Then 'グー以外 For Each name In Gamer If name.Act = 2 Then name.Win += 1 Console.WriteLine("*** {0}の勝利 ***", name.Name) ElseIf name.Act = 3 Then name.Lose += 1 End If Next ElseIf Action(2) = 0 Then 'チョキ以外 For Each name In Gamer If name.Act = 3 Then name.Win += 1 Console.WriteLine("*** {0}の勝利 ***", name.Name) ElseIf name.Act = 1 Then name.Lose += 1 End If Next ElseIf Action(3) = 0 Then 'パー以外 For Each name In Gamer If name.Act = 1 Then name.Win += 1 Console.WriteLine("*** {0}の勝利 ***", name.Name) ElseIf name.Act = 2 Then name.Lose += 1 End If Next End If End If Console.WriteLine() Next '結果表示 Console.WriteLine("■■■ 結果 ■■■") For Each name In Gamer Console.WriteLine("{0,-10}:{1:d}勝 {2:d}敗", name.Name, name.Win, name.Lose) MaxWin.Add(name.Win) Next If MaxWin.Max() = MaxWin.Min() Then Console.WriteLine("*** 引き分け ***") Else For Each name In Gamer If MaxWin.Max() = name.Win Then Console.WriteLine("*** {0}の勝利!! ***", name.Name) End If Next End If End Sub End Module |
プログラムの解説
1対1のジャンケンと比べると難しくなっているので、ポイントを解説します。
- 3行目:ジャンケンを実行する回数を設定します。
- 5行目:ディクショナリーを使って、1をグー、2をチョキ、3をパーに定義します。
- 9行目:配列にメンバーの名前を定義します。この配列に名前を定義した人数でジャンケンをします。
- 18~21行目:GamerリストにMemberクラスのインスタンスを追加します。
- 19行目:ミリセカンドを使って、Random()クラスのシード値を作成します。
- 26行目:ジャンケンの手を格納するAction配列を、0で初期化します。
- 47~49行目:コンピューターがPlay()メソッドを使ってジャンケンの手を出します。Gamerリストの0番目はプレイヤーなので、1番目から実行します。
- 57~64行目:メンバーの出した手を、Action配列に1づつ加算します。
- 68、69行目:全員同じ手か手が全部出ている場合は、引き分けとします。
- 72~99行目:出されていない手を探し、その手に対応した勝敗をメンバー毎に判定します。
- 108行目:MaxWinリストに、各メンバーの勝利数を追加します。
- 110行目:MaxWinリストにある最大勝利数と最小勝利数が同じ場合は、引き分けとします。
- 113~117行目:最大勝利数のメンバーを画面に表示します。
プログラムの実行
プログラムの実行結果は、以下のようになります。
実行する時は、Ctrl + F5キーを押下してデバッグなしモードで開始します。
クラスのフィールドにPrivate変数を使ったプログラム
上記の例では、クラスのフィールドにPropertyを使っていますが、これをPrivate変数に置き換えると、以下のようなプログラムになります。
Private変数にすると、外部から直接アクセス出来なくなるので、アクセスするためのメソッドを用意する必要があります。
その分コードが長くなりますが、オブジェクト指向のカプセル化という観点では、こちらの方が良いでしょう
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 |
Public Class Member 'プロパティ定義 Private Name As String '名前 Private Act As Integer 'ジャンケンの手 Private Win As Integer '勝数 Private Lose As Integer '負数 '乱数オブジェクト Private rnd As Random 'コンストラクタ Sub New(Name As String, Seed As Integer) Me.Name = Name Me.Act = 0 Me.Win = 0 Me.Lose = 0 rnd = New Random(Seed) End Sub 'ジャンケン実行メソッド Sub Play() Me.Act = rnd.Next(1, 4) End Sub 'ジャンケン結果設定 Sub SetAct(act As Integer) Me.Act = act End Sub '名前取得 Function GetName() As String Return Me.Name End Function 'ジャンケン結果取得 Function GetAct() As Integer Return Me.Act End Function '勝数加算 Sub AddWin() Me.Win += 1 End Sub '負数加算 Sub AddLose() Me.Lose += 1 End Sub '勝数取得 Function GetWin() As Integer Return Me.Win End Function '負数取得 Function GetLose() As Integer Return Me.Lose End Function End Class |
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 |
Module Janken2 'ジャンケン回数 Private Const CNT As Integer = 3 'ジャンケン定義 Private HAND As New Dictionary(Of Integer, String) From {{1, "グー"}, {2, "チョキ"}, {3, "パー"}} 'メイン処理 Sub Main() Dim MemName() As String = {"Player", "Computer1", "Computer2"} 'メンバー名 Dim Gamer = New List(Of Member) 'Memberクラスのインスタンスを格納するList Dim InpNum As Integer = 0 'プレイヤーの入力値 Dim Seed As Integer = 0 'Randomのシード値 Dim Dt As DateTime = DateTime.Now '現在時間 Dim Action() As Integer 'ジャンケンの実行結果を格納する配列 Dim MaxWin = New List(Of Integer) '最多勝利数 'インスタンスを作成し配列に格納 For Each name As String In MemName Seed += CInt(Dt.Millisecond) 'シード値作成 Gamer.Add(New Member(name, Seed)) Next 'ジャンケン回数分ループ For i = 1 To CNT '配列初期化(予備,グー,チョキ,パー) Action = {0, 0, 0, 0} 'ジャンケンの手を入力 Console.WriteLine("◆ {0:d}回目 ◆", i) Console.WriteLine("### 1:グー 2:チョキ 3:パー ###") Console.Write("> ") '入力チェック(1~3以外はやり直し) If Integer.TryParse(Console.ReadLine(), InpNum) = False Then Gamer(0).SetAct(0) Else Gamer(0).SetAct(InpNum) End If If Gamer(0).GetAct() < 1 OrElse Gamer(0).GetAct() > HAND.Count Then Console.WriteLine(">>> 1~3を入力してください! <<<") Console.WriteLine() i -= 1 Continue For End If 'コンピューターがジャンケンを実行 For j = 1 To Gamer.Count - 1 Gamer(j).Play() Next '各メンバーの実行結果を表示 For j = 0 To Gamer.Count - 1 Console.Write("{0,-10}-> ", Gamer(j).GetName()) Console.WriteLine(HAND(Gamer(j).GetAct())) 'ジャンケンの手を配列に加算 Select Case Gamer(j).GetAct() Case 1 'グー Action(1) += 1 Case 2 'チョキ Action(2) += 1 Case 3 'パー Action(3) += 1 End Select Next '勝敗判定(全員同じか全員違う場合は引き分け) If (Action(1) = Gamer.Count OrElse Action(2) = Gamer.Count OrElse Action(3) = Gamer.Count) OrElse (Action(1) <> 0 And Action(2) <> 0 And Action(3) <> 0) Then Console.WriteLine("*** 引き分け ***") Else If Action(1) = 0 Then 'グー以外 For Each name In Gamer If name.GetAct() = 2 Then name.AddWin() Console.WriteLine("*** {0}の勝利 ***", name.GetName()) ElseIf name.GetAct() = 3 Then name.AddLose() End If Next ElseIf Action(2) = 0 Then 'チョキ以外 For Each name In Gamer If name.GetAct() = 3 Then name.AddWin() Console.WriteLine("*** {0}の勝利 ***", name.GetName()) ElseIf name.GetAct() = 1 Then name.AddLose() End If Next ElseIf Action(3) = 0 Then 'パー以外 For Each name In Gamer If name.GetAct() = 1 Then name.AddWin() Console.WriteLine("*** {0}の勝利 ***", name.GetName()) ElseIf name.GetAct() = 2 Then name.AddLose() End If Next End If End If Console.WriteLine() Next '結果表示 Console.WriteLine("■■■ 結果 ■■■") For Each name In Gamer Console.WriteLine("{0,-10}:{1:d}勝 {2:d}敗", name.GetName(), name.GetWin(), name.GetLose()) MaxWin.Add(name.GetWin()) Next If MaxWin.Max() = MaxWin.Min() Then Console.WriteLine("*** 引き分け ***") Else For Each name In Gamer If MaxWin.Max() = name.GetWin() Then Console.WriteLine("*** {0}の勝利!! ***", name.GetName()) End If Next End If End Sub End Module |