VisualBasic(脳トレ的アプリ003: 不正解部分のコーディング推敲)

■前回のコーディングを推敲する。
前回はとりあえず動かすようコーディングしただけなので、改善したいポイントの対応を考えてみる。
まずは選択肢の部分について。
・ 不正解の選択をランダムで1~99で選ぶと、マイナスが正解の時、正解が目立つ。
 → マイナスが正解となる問題を避ける。
・ 足し算だった場合、不正解の選択をランダムで1~99で選ぶと、大きな数に偏りがちになる。
 → 不正解のつくり方を考える必要がある。
・ 不正解の選択をランダムで1~99で選ぶと、正解と同じ数値が不正解のボタンに入る場合がある。
 → 9つの選択肢は別の数値となるようにする。

不正解のつくり方で間違えるパターンを考えてみる。
1.+、×、-を見間違えるパターン。
 → 不正解として、正解以外の記号の計算結果も選択肢に含める。
2.単純な計算ミス。
 → 似た数値を選択肢に含める。
    ・計算前の数の1つ大きい、又は1つ小さい数から作ったもの。
    ・正解に対して数が近いもの、など。
この考え方で、不正解を8つ作る(見間違えるパターン2、計算ミス6)。
正解又は不正解にマイナスが入ることを避けるのは、不正解を作る中でロジックに組み込む。
最後に、9つの選択肢が別の数値になっているか確認する。

このロジック部分は少し長くなりそうなので、別のVisual basicファイルにする。引数として、ランダムで選んだ数値1、数値2、×などの記号を入れ、正解を含む数値のリストを返すようにした。
別のファイルの関数を使う場合、Sharedといった修飾子が必要なよう。これがないと、別のVisual Basicファイルで使用するときに、「BC30469: 非共有メンバーを参照するには、オブジェクト参照が必要です。」のエラーがでる。
作成したコードが下のもの(Logic.vb)。

Public Class Logic
    Shared Function createAnswers(ByVal num_1 As Integer, ByVal num_2 As Integer, ByVal code As String)

        Dim candidateList As New List(Of Integer)(New Integer() {0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
        Console.Write(candidateList)

        '1, 10番目に正解、2、3番目に正解以外の記号の結果を入力
        'マイナスにならないようにこの関数の前で処理。
        If code.Equals("0") Then
            candidateList(0) = num_1 * num_2
            candidateList(1) = num_1 + num_2
            candidateList(2) = num_1 - num_2
            candidateList(9) = candidateList(0)
        ElseIf code.Equals("1") Then
            candidateList(0) = num_1 + num_2
            candidateList(1) = num_1 * num_2
            candidateList(2) = num_1 - num_2
            candidateList(9) = candidateList(0)
        ElseIf code.Equals("2") Then
            For i = 0 To 8
                candidateList(i) = i
            Next
            candidateList(9) = num_1 - num_2
        End If

        '4-6番目にnum_1, num_2から±1した数から計算した選択肢。
        '7-9番目に正解の±10以内の選択肢。
        'リストで重複するものがないか確認する(重複がなければwhile文から抜ける)。

        If code.Equals("0") Or code.Equals("1") Then

            While True
                Dim pattern_index = Int((3 * Rnd()) + 1)
                Dim pattern_index2 = Int((2 * Rnd()) + 1)
                If code.Equals("0") Then
                    If pattern_index = 1 Then
                        candidateList(3) = (num_1 + 1) * num_2
                        candidateList(4) = num_1 * (num_2 + 1)
                        candidateList(5) = (num_1 + 1) * (num_2 + 1)
                    ElseIf pattern_index = 2 Then
                        candidateList(3) = (num_1 - 1) * num_2
                        candidateList(4) = num_1 * (num_2 - 1)
                        candidateList(5) = (num_1 - 1) * (num_2 - 1)
                    ElseIf pattern_index = 3 Then
                        candidateList(3) = (num_1 + 1) * (num_2 - 1)
                        candidateList(4) = (num_1 + 1) * (num_2 + 1)
                        candidateList(5) = (num_1 - 1) * num_2
                    End If

                    If pattern_index2 = 1 Then
                        Dim diff = Int((5 * Rnd()) + 1)
                        candidateList(6) = candidateList(0) + diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(7) = candidateList(0) + diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(8) = candidateList(0) + diff
                    ElseIf pattern_index2 = 2 Then
                        Dim diff = Int((5 * Rnd()) + 1)
                        candidateList(6) = candidateList(0) - diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(7) = candidateList(0) - diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(8) = candidateList(0) - diff
                    End If

                ElseIf code.Equals("1") Then
                    If pattern_index = 1 Then
                        candidateList(3) = num_1 + num_2 + 1
                        candidateList(4) = num_1 + num_2 + 2
                        candidateList(5) = num_1 + num_2 - 1
                    ElseIf pattern_index = 2 Then
                        candidateList(3) = num_1 + num_2 + 1
                        candidateList(4) = num_1 + num_2 + 2
                        candidateList(5) = num_1 + num_2 - 2
                    ElseIf pattern_index = 3 Then
                        candidateList(3) = num_1 + num_2 + 1
                        candidateList(4) = num_1 + num_2 - 1
                        candidateList(5) = num_1 + num_2 - 2
                    End If

                    If pattern_index2 = 1 Then
                        Dim diff = Int((5 * Rnd()) + 1)
                        candidateList(6) = candidateList(0) + diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(7) = candidateList(0) + diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(8) = candidateList(0) + diff
                    ElseIf pattern_index2 = 2 Then
                        Dim diff = Int((5 * Rnd()) + 1)
                        candidateList(6) = candidateList(0) - diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(7) = candidateList(0) - diff
                        diff = Int((5 * Rnd()) + 1)
                        candidateList(8) = candidateList(0) - diff
                    End If
                End If

                Dim tempList As New List(Of Integer)

                For Each num As Integer In candidateList
                    If (tempList.Contains(num)) Then
                        Continue While
                    Else
                        tempList.Add(num)
                    End If
                    If (tempList.Count = 9) Then
                        Exit While
                    End If
                Next
            End While
        End If

        '重複がなければリスト内でシャッフルする。
        Return candidateList
    End Function
End Class


実際に動かしてみると、無限ループになる時があるみたい。9つの選択肢が別の値にならないとループを抜けられないようにしているので、おそらくそれが原因。
実際に1から9の数値の×、+、-の結果を見てみると、+や-では選択肢の候補となる数が少ない(下の図)。-は、そもそも正解が0から8までしかないし、+も2から18までしかない。

この辺りの不正解のつくり方をもう少し考えないといけないかもしれない。