Overview

This is an original strategy game, written in VB2008 (for wider platform compatibility).

The aim of the game is to beat your opponent (your computer) in a race to claim the top vertex, through a progression of legal moves. 

How to play...

Click the above How to play header or this animated gif (in a different window) to see a demo.

Youtube video - (If the embedded video doesn't appear, use this external link: https://youtu.be/wDmSv-rQcoE) -

Each player is randomly allocated 1-4 chips each time it's their turn. Every one of your 1-4 chips will be red and your opponents will be yellow. Along the way to the top vertex, there are two randomly positioned power vertexes which will give you (for one power) a 1 in 8 chance for each chip allocated to you will be a 'wild' chip. For two powers, you'll have a 1 in 5 chance of your chips being a 'wild' chip. 

 An icon drawn on the vertex denotes a power vertex...

Chips and moves

The chips and their legal moves are...

Red

 Can be played either as a free turn on the bottom left vertex at the start of a new game, or in any other case, only beside or above another Red or a RedWild chip, progressing chip by chip towards the top vertex.

RedWild

 Can be played either as a Red chip, but also may be played beside or above any other chip, for blocking your opponents progress.

Yellow

 This is your opponents chip and follows the same rules as a Red chip with the bottom right vertex being the free move.

YellowWild

 This is your opponents 'wild' chip and follows the same rules as a RedWild chip.




↑ Back to top

The core.Game Class

The Game Class is the singleton Class used for the duration of gameplay. It contains several Public Methods - the Public Function drawChips, the Public Function 

isValidMove, and the Public Function play. These are used both internally within the Game Class, and they're also called from the main form (frmGame). It also contains six Private methods which are exclusively used within the Game Class where they contribute to the opponent's (computer's) gameplay algorithm.

Public Class game
 
    Private r As New Random
    Private canWin As Boolean = False
    Private canWin2 As Boolean = False
    Private canWin3 As Boolean = False
 
    ''' <summary>
    ''' drawChips Function
    ''' </summary>
    ''' <param name="chips"></param>
    ''' <param name="img1"></param>
    ''' <param name="img2"></param>
    ''' <param name="player"></param>
    ''' <param name="hasTargets"></param>
    ''' <returns></returns>
    ''' <remarks>Allocates the player a random number (1 to 4) of chips
    ''' which each have a 1 in 8 chance of being a wild chip (with one power)
    ''' or 1 in 5 (with two powers)</remarks>
    Public Function drawChips(ByVal chips() As chipPictureBox, ByVal img1 As Bitmap, ByVal img2 As Bitmap, ByVal player As String, ByVal hasTargets() As Integer) As Integer
        Dim max As Integer
        If player.StartsWith("r") Then
            max = If(hasTargets(0) = 1, 8, If(hasTargets(0) = 2, 5, -1))
        ElseIf player.StartsWith("y") Then
            max = If(hasTargets(1) = 1, 8, If(hasTargets(1) = 2, 5, -1))
        End If
         
        Dim n As Integer = r.Next(1, Math.Max(4, r.Next(1, 6)))
 
        For x As Integer = 0 To 3
            Dim t As Integer = 0
            If max > -1 Then
                t = r.Next(0, max)
            End If
            chips(x).Image = If(x < n, If(t = 3, img2, img1), Nothing)
            chips(x).Visible = If(x < n, True, False)
            chips(x).canDrag = player = "r" And x < n
            chips(x).chipType = If(x < n, If(t = 3, player & "w", player), "")
        Next
 
        Return n
 
    End Function
 
    ''' <summary>
    ''' isValidMove Function
    ''' </summary>
    ''' <param name="v"></param>
    ''' <param name="x"></param>
    ''' <param name="y"></param>
    ''' <param name="t"></param>
    ''' <returns></returns>
    ''' <remarks>Used to identify a valid vertex, i.e. unoccupied and a legal move</remarks>
    Public Function isValidMove(ByVal v()() As vertexInfo, ByVal x As Integer, ByVal y As Integer, ByVal t As String) As Boolean
        Dim isValid As Boolean = If(t = "r" Or t = "rw", x = 0 And y = 9, x = 9 And y = 9)
 
        If x > 0 Then
            If t.EndsWith("w") Then
                isValid = isValid Or v(y)(x - 1).chipType <> ""
            Else
                isValid = isValid Or v(y)(x - 1).chipType.StartsWith(t)
            End If
        End If
        If x < v(y).GetUpperBound(0) Then
            If t.EndsWith("w") Then
                isValid = isValid Or v(y)(x + 1).chipType <> ""
            Else
                isValid = isValid Or v(y)(x + 1).chipType.StartsWith(t)
            End If
        End If
        If y < 9 Then
            If t.EndsWith("w") Then
                isValid = isValid Or v(y + 1)(x).chipType <> "" Or v(y + 1)(x + 1).chipType <> ""
            Else
                isValid = isValid Or v(y + 1)(x).chipType.StartsWith(t) Or v(y + 1)(x + 1).chipType.StartsWith(t)
            End If
        End If
 
        If isValid And Not String.IsNullOrEmpty(v(y)(x).chipType) Then
            Return False
        End If
 
        Return isValid
    End Function
 
    ''' <summary>
    ''' checkPath Function
    ''' </summary>
    ''' <param name="v"></param>
    ''' <param name="d"></param>
    ''' <param name="x"></param>
    ''' <param name="y"></param>
    ''' <param name="t"></param>
    ''' <param name="targetLocation"></param>
    ''' <returns></returns>
    ''' <remarks>Used to find the shortest valid path</remarks>
    Private Function checkPath(ByVal v()() As vertexInfo, ByVal d()() As vertexDetails, _
                               ByVal x As Integer, ByVal y As Integer, ByVal t As String, ByVal targetLocation As Point, ByVal block As Boolean) As List(Of pathDetails)
        Dim details(9)() As vertexDetails
        If d Is Nothing Then
            For r As Integer = 9 To 0 Step -1
                Dim row As New List(Of vertexDetails)
                For i As Integer = 0 To r
                    row.Add(New vertexDetails() With {.player = If(Not String.IsNullOrEmpty(v(r)(i).chipType), v(r)(i).chipType.Substring(0, 1), "")})
                Next
                details(r) = row.ToArray
            Next
        Else
            details = d
        End If
 
        'Stop
        canWin = False
        Dim path As New List(Of pathDetails)
        checkPath2(details, x, y, t, path, targetLocation)
 
        canWin2 = False
        Dim path2 As New List(Of pathDetails)
        checkPath4(details, x, y, t, path2, targetLocation)
 
        path = path.Where(Function(vi) String.IsNullOrEmpty(vi.occupied)).ToList
        path2 = path2.Where(Function(vi) String.IsNullOrEmpty(vi.occupied)).ToList
 
        If path.Count < path2.Count Then
            If canWin Then
                Return path
            Else
                Return path2
            End If
        Else
            If canWin2 And Not block Then
                Return path2
            Else
                Return path
            End If
        End If
 
    End Function
 
    ''' <summary>
    ''' checkPath2 Method
    ''' </summary>
    ''' <param name="v"></param>
    ''' <param name="x"></param>
    ''' <param name="y"></param>
    ''' <param name="t"></param>
    ''' <param name="path"></param>
    ''' <param name="targetLocation"></param>
    ''' <remarks>Called recursively to find the shortest valid path</remarks>
    Private Sub checkPath2(ByVal v()() As vertexDetails, ByVal x As Integer, ByVal y As Integer, _
                           ByVal t As String, ByVal path As List(Of pathDetails), ByVal targetLocation As Point)
        If canWin Then Return
 
        'If x > y Then Return
 
        v(y)(x).visited = True
        If t = "y" Then
            If Not v(y)(x).player.StartsWith("y") AndAlso Not String.IsNullOrEmpty(v(y)(x).player) Then Return
        End If
 
        If y < targetLocation.Y Then Return
 
        path.Add(New pathDetails With {.i = x, .r = y, .occupied = v(y)(x).player})
 
        If y = targetLocation.Y And x = targetLocation.X Then
            canWin = True
            Return
        End If
 
        Dim distance As Integer = Math.Abs(targetLocation.Y - y) + Math.Abs(targetLocation.X - x)
        Dim newDistance(3) As Integer
        newDistance(0) = Math.Abs(targetLocation.Y - (y - 1)) + Math.Abs(targetLocation.X - (x - 1))
        newDistance(1) = Math.Abs(targetLocation.Y - (y - 1)) + Math.Abs(targetLocation.X - x)
        newDistance(2) = Math.Abs(targetLocation.Y - y) + Math.Abs(targetLocation.X - (x + 1))
        newDistance(3) = Math.Abs(targetLocation.Y - y) + Math.Abs(targetLocation.X - (x - 1))
        If Not newDistance.Any(Function(i) i < distance) Then
            newDistance(0) = -1
            newDistance(1) = -1
            newDistance(2) = -1
            newDistance(3) = -1
        End If
 
        If newDistance(0) < distance AndAlso y > 0 AndAlso x > 0 AndAlso x - 1 <= y - 1 AndAlso Not v(y - 1)(x - 1).visited Then
            checkPath2(v, x - 1, y - 1, t, path, targetLocation) 'check vertex above left
        End If
 
        If newDistance(1) < distance AndAlso y > 0 AndAlso x <= y - 1 AndAlso Not v(y - 1)(x).visited Then
            checkPath2(v, x, y - 1, t, path, targetLocation) 'check vertex above right
        End If
 
        If newDistance(2) < distance AndAlso x <= y - 1 AndAlso Not v(y)(x + 1).visited Then
            checkPath2(v, x + 1, y, t, path, targetLocation) 'check vertex to right
        End If
 
        If newDistance(3) < distance AndAlso x > 0 AndAlso Not v(y)(x - 1).visited Then
            checkPath2(v, x - 1, y, t, path, targetLocation) 'check vertex to left
        End If
 
    End Sub
 
    ''' <summary>
    ''' checkPath3 Function
    ''' </summary>
    ''' <param name="v"></param>
    ''' <param name="t"></param>
    ''' <param name="targetLocation"></param>
    ''' <returns></returns>
    ''' <remarks>Last Method used to find the shortest valid path</remarks>
    Private Function checkPath3(ByVal v()() As vertexInfo, ByVal t As String, ByVal targetLocation As Point, ByVal block As Boolean) As List(Of pathDetails)
        For y As Integer = 0 To 9
            For x As Integer = 0 To y
                If t = "y" AndAlso v(y)(x).chipType.StartsWith(t) Then
                    Return checkPath(v, Nothing, x, y, t, targetLocation, block)
                ElseIf t = "yw" AndAlso Not String.IsNullOrEmpty(v(y)(x).chipType) Then
                    If x > 0 Then
                        Return checkPath(v, Nothing, x - 1, y - 1, t, targetLocation, block)
                    Else
                        Return checkPath(v, Nothing, x, y - 1, t, targetLocation, block)
                    End If
                End If
            Next
        Next
        Return Nothing
    End Function
 
    ''' <summary>
    ''' checkPath4 Method
    ''' </summary>
    ''' <param name="v"></param>
    ''' <param name="x"></param>
    ''' <param name="y"></param>
    ''' <param name="t"></param>
    ''' <param name="path"></param>
    ''' <param name="targetLocation"></param>
    ''' <remarks>Called recursively to find the shortest valid path</remarks>
    Private Sub checkPath4(ByVal v()() As vertexDetails, ByVal x As Integer, ByVal y As Integer, _
                           ByVal t As String, ByVal path As List(Of pathDetails), ByVal targetLocation As Point)
        If canWin Then Return
 
        'If x > y Then Return
 
        v(y)(x).visited = True
        If t = "y" Then
            If Not v(y)(x).player.StartsWith("y") AndAlso Not String.IsNullOrEmpty(v(y)(x).player) Then Return
        End If
 
        If y < targetLocation.Y Then Return
 
        path.Add(New pathDetails With {.i = x, .r = y, .occupied = v(y)(x).player})
 
        If y = targetLocation.Y And x = targetLocation.X Then
            canWin2 = True
            Return
        End If
 
        Dim dx As Integer = If(targetLocation.X - x < 0, -1, If(targetLocation.X - x > 0, 1, 0))
        Dim dy As Integer = If(targetLocation.Y - y < 0, -1, If(targetLocation.Y - y > 0, 1, 0))
 
        If x + dx >= 0 AndAlso x + dx <= v(y).GetUpperBound(0) AndAlso y + dy >= 0 Then
            checkPath2(v, x + dx, y + dy, t, path, targetLocation) 'check next closest vertex
        End If
 
    End Sub
 
    ''' <summary>
    ''' findClosestOccupiedVertex Function
    ''' </summary>
    ''' <param name="targetLocation"></param>
    ''' <param name="v"></param>
    ''' <returns></returns>
    ''' <remarks>Used in calculating the shortest path to a target</remarks>
    Private Function findClosestOccupiedVertex(ByVal targetLocation As Point, ByVal v()() As vertexInfo, ByVal t As String) As Point
        Dim closest As Integer = Integer.MaxValue
        Dim closestPoint As Point
        For y As Integer = 9 To 0 Step -1
            For x As Integer = 0 To y
                If v(y)(x).chipType.StartsWith("y") OrElse (t = "yw" AndAlso v(y)(x).chipType <> "") Then
                    Dim distance As Integer = Math.Abs(targetLocation.Y - y) + Math.Abs(targetLocation.X - x)
                    If distance < closest Then
                        closest = distance
                        closestPoint = New Point(x, y)
                    End If
                End If
            Next
        Next
        Return closestPoint
    End Function
 
    ''' <summary>
    ''' play Function
    ''' </summary>
    ''' <param name="cChips"></param>
    ''' <param name="img1"></param>
    ''' <param name="img2"></param>
    ''' <param name="v"></param>
    ''' <returns></returns>
    ''' <remarks>Coordinates the computer's gameplay</remarks>
    Public Function play(ByVal cChips() As chipPictureBox, ByVal img1 As Bitmap, _
                    ByVal img2 As Bitmap, ByVal v()() As vertexInfo, ByVal hasTargets() As Integer, _
                    ByVal ri1 As randomIcon, ByVal ri2 As randomIcon, ByVal targets() As Target) As Boolean
 
        Dim numberOfMoves As Integer = drawChips(cChips, img1, img2, "y", hasTargets)
        cChips = cChips.OrderByDescending(Function(p) p.chipType.Length).ToArray
 
        Application.DoEvents()
        Threading.Thread.Sleep(500)
 
        Dim counter As Integer
        Dim chipCounter As Integer = 0
 
        Dim xV As Integer = 9
        Dim yV As Integer = 9
        Do
            'If cChips(chipCounter).chipType = "yw" Then Stop
            Dim target As Target = targets.Last(Function(t) t.acquired = False)
 
            Dim p As Point = findClosestOccupiedVertex(target.location, v, cChips(chipCounter).chipType)
            If p <> Point.Empty Then
                xV = p.X
                yV = p.Y
            End If
 
            Dim cMoves As List(Of pathDetails) = checkPath(v, Nothing, xV, yV, cChips(chipCounter).chipType, target.location, cChips(chipCounter).chipType = "yw")
            If cMoves Is Nothing OrElse cMoves.Count = 0 Then
                If target.location <> Point.Empty Then
                    target.acquired = True
                    Continue Do
                ElseIf Not (xV = 9 And yV = 9) Then
                    cMoves = checkPath(v, Nothing, 9, 9, cChips(chipCounter).chipType, Point.Empty, cChips(chipCounter).chipType = "yw")
                End If
            End If
            Dim cMoves2 As List(Of pathDetails) = checkPath3(v, cChips(chipCounter).chipType, Point.Empty, cChips(chipCounter).chipType = "yw")
            If cMoves2 IsNot Nothing AndAlso cMoves2.Count > 0 AndAlso cMoves IsNot Nothing _
                                                AndAlso cMoves2.Count < cMoves.Count AndAlso cChips(chipCounter).chipType <> "yw" Then
                If cMoves2(0).r < cMoves(0).r Then
                    cMoves = cMoves2
                End If
            End If
 
            If cMoves.Count = 0 Then
                If target.location <> Point.Empty Then
                    target.acquired = True
                    Continue Do
                Else
                    'pass
                    My.Computer.Audio.Play(My.Resources.sigh, AudioPlayMode.Background)
                    Exit Do
                End If
            End If
 
 
            If Not cMoves Is Nothing AndAlso Not cMoves.Count = 0 Then
                'play cMoves
                counter = 0
 
                For x As Integer = 0 To If(cMoves.Count <= numberOfMoves, cMoves.Count - 1, numberOfMoves - 1)
                    Dim m As New moveDetails With {.cpb = cChips(chipCounter), .vertex = v(cMoves(x).r)(cMoves(x).i), .hasTargets = hasTargets, .ri1 = ri1, .ri2 = ri2, .targets = targets}
                    moveChip(m)
                    counter += 1
                    chipCounter += 1
                Next
 
                If v(0)(0).chipType.StartsWith("y") Then counter = numberOfMoves : Return True
                numberOfMoves -= counter
 
            End If
 
        Loop While numberOfMoves > 0
 
        For x As Integer = 0 To 3
            cChips(x).chipType = ""
            cChips(x).Image = Nothing
            cChips(x).Visible = False
        Next
 
        Return False
 
    End Function
 
    ''' <summary>
    ''' moveChip Method
    ''' </summary>
    ''' <param name="m"></param>
    ''' <remarks>Programmatically moves the computer's chips in an animated fashion
    ''' Assigns the 'dropped' chip to the highlighted vertex</remarks>
    Private Sub moveChip(ByVal m As moveDetails)
 
        Dim origin As Point = m.cpb.Location
        Dim endPoint As Point = m.vertex.location
        Dim midPoint As New Point
 
        For x As Integer = 0 To 10
 
            midPoint.X = CInt(Math.Min(origin.X, endPoint.X) + (((Math.Max(origin.X, endPoint.X) - Math.Min(origin.X, endPoint.X)) / 10) * (10 - x)))
            midPoint.Y = CInt(Math.Min(origin.Y, endPoint.Y) + (((Math.Max(origin.Y, endPoint.Y) - Math.Min(origin.Y, endPoint.Y)) / 10) * (10 - x)))
 
            If midPoint.X < m.cpb.Location.X - 25 Then
                midPoint.Offset(-25, -25)
            Else
                midPoint.Offset(0, -25)
            End If
            m.cpb.Location = midPoint
            Application.DoEvents()
            Threading.Thread.Sleep(50)
        Next
 
        m.vertex.chipType = m.cpb.chipType
        If m.vertex.target > 0 Then
            m.hasTargets(1) += 1
            If m.ri1.used Then
                m.ri2.Image = My.Resources.shuffle_51x51
                m.ri2.used = True
            Else
                m.ri1.Image = My.Resources.shuffle_51x51
                m.ri1.used = True
            End If
            m.targets(m.vertex.target).acquired = True
            My.Computer.Audio.Play(My.Resources.floop, AudioPlayMode.Background)
        ElseIf m.vertex.target = 0 Then
            My.Computer.Audio.Play(My.Resources.floop, AudioPlayMode.Background)
        Else
            My.Computer.Audio.Play(My.Resources.Jump, AudioPlayMode.Background)
        End If
        m.cpb.Location = m.cpb.baseLocation
        m.cpb.Image = Nothing
        m.cpb.chipType = ""
        m.cpb.Visible = False
        Application.DoEvents()
 
    End Sub
 
 
End Class

↑ Back to top


Conclusion

VB.Net is a much underrated programming language these days, which is a shame as it is equally as versatile as C#.

This example shows how to write a fully functioning PC game. The only limit when designing a game in VB.Net is your imagination. If you can dream it, you can code it...


Other Resources

I've updated the zip file.
New version has the reduced screen size, to be usable in laptops with smaller screens, and minor changes to the gameplay algorithm.

Download here

Alternative download from MSDN Samples Gallery

This and other games can be downloaded in executable form at: http://www.scproject.biz/puzzlegames.php



See Also


VB.Net - Perspective

VB.Net - WordSearch

VB2017 - Numbers Game