[VB.net] 再談使圖片捲動的「拉霸遊戲機」(四) - 做成九格水果盤

直覺上做成九格的工程不大,可是實際上並不容易,因為一個實用的「水果盤」除了要能正常的呈現出所要的圖片外,還要能精確的控制轉出的結果,以及對隨機轉出的結果有自動判讀的能力。
例如「控制轉出的結果」:
1.我要轉出任意「一條連線」的結果,但不能每次都是連在同一個位置,也不能總是同一個號碼的三連線。
2.我要轉出任意「三條連線」的結果,但任意三條連線成立的同時,因為連線位置間的相互關係有可能讓結果多於三條連線,這要避免。
3.我要精準的控制把 「135792468」號的圖片分別出現在井字圖的第1到9的空格裡,要自然的「轉出來」而不是突然蹦出來。
4.其他…

前文(連結)已經把單一的「拉霸控制項」準備完成了,接著我們就可以用九個拉霸組成一個井字格的水果盤了。

直覺上做成九格的工程不大,可是實際上並不容易,因為一個實用的「水果盤」除了要能正常的呈現出所要的圖片外,還要能精確的控制轉出的結果,以及對隨機轉出的結果有自動判讀的能力。

例如「控制轉出的結果」:

  1. 我要轉出任意「一條連線」的結果,但不能每次都是連在同一個位置,也不能總是同一個號碼的三連線。
  2. 我要轉出任意「三條連線」的結果,但任意三條連線成立的同時,因為連線位置間的相互關係有可能讓結果多於三條連線,這要避免。
  3. 我要精準的控制把 「135792468」號的圖片分別出現在井字圖的第1到9的空格裡,要自然的「轉出來」而不是突然蹦出來。
  4. 其他…

再如「自動判讀」能力:

  1. 要知道共幾條「三連線」。
  2. 要知道各條三連線各發生在哪個連線位置上(井字圖共有8條連線位置)。
  3. 要知道各條三連線的成員圖號是幾號(這是為了分配獎分的比重)。
  4. 無論連線成功與否,要能統計各張圖片在九格中出現了幾次(這是為了遊戲中有隨機的「幸運圖號」需給予獎分的機制)。
  5. 其他…

再如依據「使用圖片數」和各種連線的「發生機率」和「押分值」之間關係來計算獎分大小的運算原則和演算法:

  1. 例如只用1種圖片時一定會出現九格同圖的「清一色」局面,這時就不能依據連線數量來計分,而要用圖號大小來加權算出「合理」的獎分。
  2. 例如使用2種圖片時「清一色」的機率是 1 / 2^8 = 0.00390625,使用 10 種圖片時「清一色」的機率是 1 /10^8 = 0.00000001,在各種不同條件下的獎分計算會是個重要課題。
  3. 以下是使用不同圖片數的各種連線機率:

    image
  4. 獎分的計算比較複雜留到下次胋文再討論,今天先來玩「控制結果」和「自動判讀」。

 

 

還是和往常一樣在做成九格的「水果盤控制項」之前我習慣先在表單上面做各種測試動作,如果各項測試OK,在確信可以完成每一個部分零件之後才開 DLL 方案重新整理、重構,建立成為 dll 組件。

 

  1. 先把九個單獨的「拉霸控制項」弄到一個容器上,讓它們可以隨容器的大小形狀自動排列。
  2. 下面程式碼是把 panel3 當做容器,把9個控制項的佈局交給「元件就定位」這個函式,並和 Panel3 的 SizeChanged 事件關聯。
  3. 對於邊界的設定這裡先用固定值,待寫成控制項時再把它們做成 Property 讓外部程序可以控制。

    
    Public Class Form2
        Dim 拉霸陣列(8) As ku_ScrollImage
        Private Sub Panel3_SizeChanged(sender As Object, e As System.EventArgs) Handles Panel3.SizeChanged
            元件就定位(sender)
        End Sub
        '---在容器佈局九個「拉霸控制項」---
        Sub 元件就定位(容器 As Panel)
            If 容器.Controls.Count = 0 Then                '---若容器為空的就建立陣列---
                For i = 0 To 8
                    拉霸陣列(i) = New ku_ScrollImage
                    AddHandler 拉霸陣列(i).產生結果了, AddressOf 處理結果
                Next
            End If
            Dim 左邊界寬度 = 容器.ClientSize.Width / 16    '---以容器大小為依據安排每個拉霸的大小位置---
            Dim 右邊界寬度 = 容器.ClientSize.Width / 16
            Dim 上邊界高度 = 容器.ClientSize.Height / 16
            Dim 下邊界高度 = 容器.ClientSize.Height / 16
            Dim 間隔寬度 = 容器.ClientSize.Width / 64
            Dim 圖框寬度 = (容器.ClientSize.Width - 左邊界寬度 - 右邊界寬度 - 間隔寬度 * 2) \ 3
            Dim 圖框高度 = (容器.ClientSize.Height - 上邊界高度 - 下邊界高度 - 間隔寬度 * 2) \ 3
            Dim x, y As Integer
            For i = 0 To 8
                y = i \ 3 : x = i Mod 3
                With 拉霸陣列(i)
                    .Left = 左邊界寬度 + (圖框寬度 + 間隔寬度) * x
                    .Top = 上邊界高度 + (圖框高度 + 間隔寬度) * y
                    .Size = New Size(圖框寬度, 圖框高度)
                End With
                容器.Controls.Add(拉霸陣列(i))
            Next
        End Sub
        Private Sub 處理結果(sender As Object, n As Integer)
            '---備用---
        End Sub
    
    End Class
  4. 執行結果如下圖。

    image

     
  5. 接下來是一次啟動9個拉霸的方法。
  6. 為了配各種不同的使用方式以對結果控制的種種要求,這部分我做多種不同的多載例如:
    1. 不用參數的用法  → 啟動() (完全由亂數決定結果
    2. 傳入陣列的用法  → 啟動({1, 2, 3, 4, 5, 6, 7, 8, 9})  (把圖號 1-9 分配到九格的 1-9 個圖框裡
    3. 傳入字串的用法  → 啟動(“123456789”)   (同上)
    4. 傳入字串的用法  → 啟動(“123???789”)   (第 1-3 格放入圖號 1-3,第 7-9 格放入圖號 7-9,第 456 格由亂數決定)
    5. 傳入一個整數   → 啟動(5)  (結果的九張圖片由最多5種不同的圖片組成
    6. 傳入兩個整數   → 啟動(6,3)  結果的九張圖片由最多6種不同的圖片組成,並且結果會有三條連線)
  7. 程式碼如下:

    
    #Region "---各種啟動拉霸的多載程序---"
        ''' <summary>
        ''' 不帶參數啟動拉霸機(這是圖片數量為 10 的公平亂數)
        ''' </summary>
        ''' <remarks>不帶參數表示全部由亂數產生</remarks>
        Overloads Sub 啟動()
            全部啟動({-1, -1, -1, -1, -1, -1, -1, -1, -1})
        End Sub
        ''' <summary>
        ''' 預設每個圖框的結果,然後啟動拉霸機(用陣列傳入)
        ''' </summary>
        ''' <param name="結果陣列">希望產生的結果數據依序放到陣列(-1 表示由亂數決定)</param>
        ''' <remarks></remarks>
        Overloads Sub 啟動(結果陣列() As Integer)
            全部啟動(結果陣列)
        End Sub
        ''' <summary>
        ''' 預設每個圖框的結果,然後啟動拉霸機(用字串傳入)
        ''' </summary>
        ''' <param name="結果字串">用字串表示希望產生的結果,例如 "012345RRR"(R 表示由亂數決定)</param>
        ''' <remarks></remarks>
        Overloads Sub 啟動(結果字串 As String)
            Dim 修飾字串 As String = Mid(結果字串 & StrDup(9, "?"), 1, 9)
            Dim tmp1(8) As Integer
            Dim id As String
            For i = 1 To Len(結果字串)
                id = Mid(結果字串, i, 1)
                tmp1(i - 1) = If(IsNumeric(id), CInt(id), -1)
            Next
            全部啟動(tmp1)
        End Sub
        ''' <summary>
        ''' 設定結果圖片母數,然後啟動拉霸機(設為1則必定出現清一色)
        ''' </summary>
        ''' <param name="圖片數">圖片數量越多則三連線機率會越小</param>
        ''' <param name="連線數">指定有幾條三連線</param>
        ''' <remarks></remarks>
        Overloads Sub 啟動(圖片數 As Integer, Optional 連線數 As Integer = 0)
            Dim 圖片總數 = ku_ScrollImage.圖片總數
            圖片數 = If(圖片數 > 圖片總數, 圖片總數, If(圖片數 < 1, 1, 圖片數))
            Randomize()
            Dim m = Int(Rnd() * 圖片數)
            Dim n = Int(Rnd() * (圖片總數 - 圖片數))
            For i = 0 To 8
                期望值(i) = Int(Rnd() * 圖片數 + n)
            Next
            If 連線數 = 0 Then
                全部啟動(期望值)
            Else
                啟動(指定連線數(期望值, 連線數))
            End If
        End Sub
        Function 指定連線數(可用數字() As Integer, 連線數 As Integer) As String
            Dim 可用數字集合 = New List(Of Integer)
            For i = 0 To 8      '---剔除「可用數字陣列」重複數字後放到集合中---
                If Not 可用數字集合.Contains(可用數字(i)) Then
                    可用數字集合.Add(可用數字(i))
                End If
            Next
            Dim 已連線數 As Integer = 0
            Dim 結果 As String = ""
            While 已連線數 <> 連線數
                已連線數 = 0
                '---建立籤筒(內放0-7共八個號碼)供隨機抽取連線位置---
                Dim 籤筒 = New List(Of Integer) From {0, 1, 2, 3, 4, 5, 6, 7}
                Dim 想要連線的位置 = New List(Of Integer)
                Dim 連線圖號 = 可用數字集合(亂數產生器.Next(0, 可用數字集合.Count))
                想要連線的位置 = 抽出的籤組(籤筒, 連線數)
                結果 = 產生期望字串(連線圖號, 想要連線的位置, 已連線數)
            End While
            Return 結果
        End Function
        '---從集合中不重複抽出籤支---
        Function 抽出的籤組(集合 As List(Of Integer), 支數 As Integer) As List(Of Integer)
            If 支數 >= 集合.Count Then Return 集合 '---若要求的支數 >= 籤筒的籤數就不用再抽,直接傳回籤筒就可以了---
            Dim 已抽出的號碼 As Integer
            Dim 傳回的集合 = New List(Of Integer)
            For i = 1 To 支數
                已抽出的號碼 = 集合(亂數產生器.Next(0, 集合.Count)) : 集合.Remove(已抽出的號碼)
                傳回的集合.Add(已抽出的號碼)
            Next
            Return 傳回的集合
        End Function
        '---先把所有位置放入「非連線圖號」,再把「連線圖號」放到「連線位置集合」的每個位置---
        Function 產生期望字串(連線圖號 As Integer, 連線位置集合 As List(Of Integer), ByRef 已連線數 As Integer) As String
            Dim 連線位置(,) = {{3, 5, 7}, {7, 8, 9}, {4, 5, 6}, {1, 2, 3}, {1, 5, 9}, {1, 4, 7}, {2, 5, 8}, {3, 6, 9}}
            Dim 輸出陣列(8) As Integer
            Dim 輸出字串 As String = Replace("1234567890", CStr(連線圖號), "")  '---先用「非連線圖號」佈滿全局---
            Dim 弄亂 As Integer = 亂數產生器.Next(2, 6)                         '---弄亂順序---
            輸出字串 = Mid(輸出字串, 弄亂 + 1) & Mid(輸出字串, 1, 弄亂)
            For i = 0 To 連線位置集合.Count - 1
                For j = 0 To 2
                    Mid(輸出字串, 連線位置(連線位置集合(i), j), 1) = CStr(連線圖號)
                Next
            Next
            '---統計已連線數供呼叫比對是合要求---
            已連線數 = 0
            For i = 0 To 7
                Dim 圖號0 = Mid(輸出字串, 連線位置(i, 0), 1)
                Dim 圖號1 = Mid(輸出字串, 連線位置(i, 1), 1)
                Dim 圖號2 = Mid(輸出字串, 連線位置(i, 2), 1)
                If 圖號0 = 圖號1 AndAlso 圖號1 = 圖號2 Then
                    已連線數 += 1
                End If
            Next
            Return 輸出字串
        End Function
        Private Sub 全部啟動(Obj() As Integer)
            Try
                已停止的控制項數量 = 0
                比對字串 = ""
                For i = 0 To 8
                    拉霸陣列(i).啟動(Obj(i))
                    比對字串 &= CStr(Obj(i))
                Next
            Catch ex As Exception
            End Try
        End Sub
    #End Region
    
  8. 再來是看結果的判讀:
  9. 定義「結果資料結構」包含所有要傳出來的資訊,這個結構也就是將來「水果盤控制項」要傳回的事件參數 Args 。
  10. 程式碼如下:

    
        Structure 單線結果陣列結構
            Dim 三連線 As Boolean          '---記錄是否連線---
            Dim 圖片號 As Integer          '---第一張圖的號碼---
            Dim 該線押分 As Integer        '---該條線上使用者的押分---
            Dim 三張圖的位置() As Integer  '---這條連線上有哪三張---
        End Structure
        Structure 結果資料結構
            Dim 圖號字串 As String
            Dim 三連線結果() As 單線結果陣列結構
            Dim 圖片統計() As Integer
            Dim 押分的連線總數 As Integer
            Dim 全盤的連線總數 As Integer
            Dim 清一色 As Boolean
        End Structure
        Private Sub 處理結果(sender As Object, n As Integer)
            Dim 連線情形(7) As 單線結果陣列結構
            已停止的控制項數量 += 1
            If 已停止的控制項數量 = 9 Then        '---當九個 BAR 全部運轉結束就開始統計---
                已停止的控制項數量 = 0
                Dim 押分的連線總數 As Integer = 0
                Dim 全盤的連線總數 As Integer = 0 '---記錄這盤共有幾條三連線---
                Dim 該線押分 As Integer = 80      '---先設固定為 80,日後再修改---
                Dim 清一色 As Boolean = False
                Dim 首圖號, 增量 As Integer
                '---檢查有哪些三連線(定義每條連線的位置0-7)---
                For i = 0 To 7
                    Select Case i                                             '--4  5 6 7----------------------------
                        Case 1, 2, 3 : 首圖號 = (3 - i) * 3 + 1 : 增量 = +1   '-- \│ │ │--(1=789, 2=456, 3=123)---
                        Case 5, 6, 7 : 首圖號 = (i - 4) : 增量 = +3           '--3─1.2.3--(5=147, 6=258, 7=369)---
                        Case 0 : 首圖號 = 3 : 增量 = +2                       '--2─4.5.6--(0=357)-----------------
                        Case 4 : 首圖號 = 1 : 增量 = +4                       '--1─7.8.9--(4=159)-----------------
                    End Select                                                '--  /------------------------------------
                    '                                                         '--O--------------------------------------
                    Dim 該線為三連線 As Boolean = False
                    Dim 三張圖號(2) As Integer
                    ReDim 連線情形(i).三張圖的位置(2)
                    Dim 位置 As Integer
                    For j = 0 To 2
                        位置 = (首圖號 + j * 增量) - 1
                        連線情形(i).三張圖的位置(j) = 位置
                        三張圖號(j) = 拉霸陣列(位置).結果圖號
                    Next
                    連線情形(i).圖片號 = 三張圖號(0)                                                    '---以第一張圖號為代表號---
                    '該線押分 = CInt(_押分框陣列(i).Text)                                               '---該線的押分---
                    連線情形(i).該線押分 = 該線押分
                    該線為三連線 = (三張圖號(0) = 三張圖號(1)) AndAlso (三張圖號(1) = 三張圖號(2))      '---是否為三連線---
                    全盤的連線總數 += If(該線為三連線, 1, 0)                                            '---無論有否押分的連線總數---
                    連線情形(i).三連線 = 該線為三連線 AndAlso 該線押分 > 0                              '---若該線有押分則列入結果報告---
                    押分的連線總數 += If(連線情形(i).三連線, 1, 0)                                      '---有押分的連線總數---
                    If 連線情形(i).三連線 AndAlso 該線押分 > 0 Then
                        For n = 0 To 2
                            拉霸陣列(連線情形(i).三張圖的位置(n)).圖片變色()
                        Next
                    End If
                Next
                '---統計各圖片出現了幾次---
                Dim 圖片統計(ku_ScrollImage.圖片總數 - 1) As Integer
                For i = 0 To 8
                    圖片統計(拉霸陣列(i).結果圖號) += 1
                    If 圖片統計(拉霸陣列(i).結果圖號) = 9 Then 清一色 = True
                Next
                '---將結果包裝到物件中---
                Dim 結果 = New 結果資料結構
                With 結果
                    .三連線結果 = 連線情形
                    .圖片統計 = 圖片統計
                    .圖號字串 = 比對字串
                    .押分的連線總數 = 押分的連線總數
                    .全盤的連線總數 = 全盤的連線總數
                    .清一色 = 清一色
                End With
                '---交給顯示處理程序---
                結果分析(結果)
            End If
        End Sub
    
  11. 結果分析的 UI 部分程式碼下回再貼,以下是執行畫面擷圖。

    指定由四種圖片組成九格:
    image 

    指定出現三條連線
    image image
     
  12. 影片:


     
  13. 程式碼下載:
    1. 圖片捲動4_demo.rar
    2. 圖片捲動(四)水果盤控制項.rar

 

 

 


ku3