This sample code demonstrates implementing a Solitaire game using Visual Basic. This code does not necessarily work; it is
meant to highlight how to use the various Solitaire Pack interfaces.
The game presented here is Aces Up. The goal of the game is to get four Aces (which are high) remaining. Cards are first
put in the Stock. Then, four are turned up at a time onto piles. Only the top cards of the pile can be played. A card can
be played to the Foundation when a card of the same suit but higher rank is also on top of some pile. Cards can be moved
into empty spaces. Four more cards are then turned up, and so on.
The excerpts presented are from the implementation of a Visual Basic class. The game class must implement the
ISolpackGame interface.
This class contains member variables that store the Engine, the deck, and piles. Also, GameData objects are used to cache
the top card of each pile, instead of having to manually get the card each time. The final variable is used to keep track
of the pile from which a drag operation started.
Option Explicit
Implements ISolpackGame
Dim m_pApp As ISolpackApp
Dim m_pDeck As ISolpackPile
Dim m_pStock As ISolpackPile
Dim m_aPiles(3) As ISolpackPile
Dim m_pFoundation As ISolpackPile
Dim m_pWastePile As ISolpackPile
Dim m_aTopOfPile(3) As ISolpackGameData
Dim m_pSourcePile As ISolpackPile
This method would return an object that implements the ISolpackGameInfo interface,
from which the game title, rules, and difficulty could be obtained. It is not implemented here.
Private Property Get ISolpackGame_Info() As SolpackLib.ISolpackGameInfo
ISolpackGame_Info = Nothing
End Property
This method caches the engine object, creates the deck, creates several piles, and sets several pile comments. Finally,
it calls RecalcLayout to position the pile objects.
Private Sub ISolpackGame_Initialize(ByVal pApp As SolpackLib.ISolpackApp)
Set m_pApp = pApp
Set m_pDeck = m_pApp.CreatePile(PILE_DECK)
Dim i As Integer
For i = 0 To 3
Set m_aPiles(i) = m_pApp.CreatePile(PILE_TABLEAUDOWN)
Set m_aTopOfPile(i) = m_pApp.CreateGameData()
Next i
Set m_pStock = m_pApp.CreatePile(PILE_STACK)
m_pStock.ShowNumCardsTooltip = True
m_pStock.CommentTooltip = "Stock." & Chr(13) & _
"Deal an additional row of cards."
Set m_pFoundation = pApp.CreatePile(PILE_STACK)
m_pFoundation.BaseImage = BASE_FOUNDATION
m_pFoundation.ShowNumCardsTooltip = True
m_pFoundation.CommentTooltip = "Foundation." & Chr(13) & _
"Get 48 cards here to win."
Set m_pWastePile = pApp.CreatePile(PILE_STACK)
m_pWastePile.CommentTooltip = "Waste Pile." & Chr(13) & _
"You may put any cards here at any time, but may not remove them."
RecalcLayout
End Sub
This method releases all created objects by setting them equal to Nothing.
Private Sub ISolpackGame_Uninitialize()
Dim i As Integer
For i = 0 To 3
Set m_aPiles(i) = Nothing
Set m_aTopOfPile(i) = Nothing
Next i
Set m_pSourcePile = Nothing
Set m_pStock = Nothing
Set m_pWastePile = Nothing
Set m_pFoundation = Nothing
Set m_pDeck = Nothing
Set m_pApp = Nothing
End Sub
This method shuffles the deck, and creates a fragment to transfer the cards from the deck to the Stock pile.
Private Sub ISolpackGame_Deal()
m_pDeck.Shuffle
m_pApp.Score = 4
Dim pFrag As ISolpackFragment
Set pFrag = m_pApp.Fragment
pFrag.AddToFragment pDeck
pFrag.MoveToPile m_pStock, False, False
Set pFrag = Nothing
End Sub
This method goes through the cards at the top of each pile, and autoplays them if possible. Whenever a card is autoplayed,
it will start over at the first pile. When all top-level cards cannot be autoplayed, the method finishes. The return value
is True if any card was played, else False.
Private Function ISolpackGame_AutoPlay() As Boolean
ISolpackGame_AutoPlay = False
Dim i As Integer
i = 0
While i < 4
If InternalAutoPlayCard(m_aTopOfPile(i).Data) Then
i = 0
ISolpackGame_AutoPlay = True
Else
i = i + 1
End If
Wend
End Function
This method tries autoplaying the individual card specified.
Private Function ISolpackGame_AutoPlayCard(ByVal pCard As SolpackLib.ISolpackCard)
As Boolean
ISolpackGame_AutoPlayCard = InternalAutoPlayCard(pCard)
End Function
This method does not do anything. If you do register commands, you should check the lID parameter and handle the
command as appropriate.
Private Sub ISolpackGame_HandleCommand(ByVal lID As Long)
End Sub
This method is called when any pile is clicked. The only pile we care about is the Stock; when it is clicked, we move
four cards from the stock to the four piles. The CanDrag properties of the former "top of pile" cards, if existant,
are set to False, and the new cards are set as the top cards. The cards are animated and flipped over when the animation
finishes.
Private Sub ISolpackGame_PileClicked(ByVal pPile As SolpackLib.ISolpackPile,
ByVal pCard As SolpackLib.ISolpackCard)
If Not pPile Is m_pStock Or m_pStock.NumCards = 0 Then Return
Dim cCards as Integer
cCards = m_pStock.NumCards
Dim i as Integer
For i = 0 To 4
If Not m_aTopOfPile(i).Data Is Nothing Then
m_aTopOfPile(i).Data.CanDrag = False
End If
Dim pCard as SolpackLib.ISolpackCard
pCard = m_pStock.GetCard(cCards - (i + 1))
pCard.CanDrag = True
Dim pFrag As SolpackLib.ISolpackFragment
pFrag = m_pApp.CreateFragment(m_pStock)
pFrag.AddToFragment pCard
m_aTopOfPile(i).Data = pCard
pFrag.MoveToPile m_aPiles(i), True, True
Next i
End Sub
When the user starts to drag a card, this method is called. If the card is at the top of one of the piles, we add it to
the fragment.
Private Sub ISolpackGame_SetupFragment(ByVal pPileSrc As SolpackLib.ISolpackPile,
ByVal pCardSrc As SolpackLib.ISolpackCard,
ByVal pFrag As SolpackLib.ISolpackFragment)
Dim i as Integer
For i = 0 To 4
If pCardSrc Is m_aTopOfPile(i).Data Then
Set m_pSourcePile = pPileSrc
pFrag.AddToFragment pCardSrc
Return
End If
Next i
End Sub
When the user drops a card over a pile, this method is called. First we ensure we have a valid fragment and that the
target pile is not the Stock. Then, we find the index of the source pile and the destination pile (if it is one of the
four tableau piles). If the destination pile is the waste pile, the card is moved there. If it is the foundation, we check
to see if the card is playable, and move it to the foundation if it is. If the destination is another pile, which is
empty, we move the card to it. "Top of pile" objects are updated as necessary.
Private Function ISolpackGame_DropFragment(ByVal pFrag As SolpackLib.ISolpackFragment,
ByVal pDest As SolpackLib.ISolpackPile)
As Boolean
Dim bReturn As Boolean
bReturn = False
If pFrag.NumCards <> 1 Or pDest Is m_pStock Then Return
Dim pCard As SolpackLib.ISolpackCard
Set pCard = pFrag.GetCard(0)
Dim i As Integer
Dim iSrc As Integer
Dim iDest As Integer
iSrc = -1
iDest = -1
For i = 0 To 4
If m_pSourcePile Is m_aPiles(i) Then iSrc = i
If pDest Is m_aPiles(i) Then iDest = i
Next i
If pDest Is m_pWastePile Then
pCard.CanDrag = False
pFrag.MoveToPile m_pWastePile, True, False
bReturn = True
ElseIf pDest Is m_pFoundation Then
If IsCardPlayable(iSrc) Then
pFrag.MoveToPile m_pFoundation, True, False
ScoreCard pCard
bReturn = True
End If
ElseIf iDest <> -1
If m_aPiles(iDest).NumCards = 0 Then
pFrag.MoveToPile pDest, True, False
m_aTopOfPile(iDest).Data = pCard
bReturn = True
End If
End If
If bReturn Then
Set m_pSourcePile = Nothing
If m_aPiles(iSrc).NumCards > 0
Dim pNewTop As SolpackLib.ISolpackCard
Set pNewTop = m_aPiles(iSrc).GetCard(m_aPiles(iSrc).NumCards - 1)
pNewTop.CanDrag = True
m_aTopOfPile(iSrc).Data = pNewTop
End If
End If
ISolpackGame_DropFragment = bReturn
End Function
This method releases the cached source pile object by setting it to Nothing.
Private Sub ISolpackGame_CancelMove()
Set m_pSourcePile = Nothing
End Sub
This method lays out the pile objects, either when the game is initialized or when window or card metrics change.
Private Sub ISolpackGame_RecalcLayout()
Dim i As Integer
For i = 0 To 3
m_aPiles(i).X = (((m_pApp.CardWidth + m_pApp.SpacingX) * (i + 1)) + _
(1.5 * m_pApp.SpacingX))
m_aPiles(i).Y = m_pApp.SpacingY
Next i
m_pStock.X = m_pApp.SpacingX
m_pStock.Y = m_pApp.SpacingY
m_pFoundation.X = (m_pApp.CardWidth * 5) + (m_pApp.SpacingX * 7)
m_pFoundation.Y = m_pApp.SpacingY
m_pWastePile.X = m_pApp.SpacingX
m_pWastePile.Y = m_pApp.CardHeight + (2.5 * m_pApp.SpacingY)
End Sub
This function determines whether a card can be played to the foundation. It looks at all other "top of pile" cards, and if
one of them has the same suit as this card but a higher rank, this card can be played.
Private Function IsCardPlayable(iPileIndex As Integer) As Boolean
Dim bCanMove As Boolean
bCanMove = False
Dim pCard As ISolpackCard
Set pCard = m_aTopOfPile(iPileIndex).Data
Dim pTop As ISolpackCard
Dim j As Integer
For j = 0 To 4
If Not bCanMove Then
If Not (j = iPileIndex) Then
Set pTop = m_aTopOfPile(j).Data
If pTop.Suit = pCard.Suit Then
If ((pCard.Rank < pTop.Rank) Or (pTop.Rank = RANK_ACE))
And (Not (pCard.Rank = RANK_ACE)) Then
IsCardPlayable = True
Return
End If
End If
Set pTop = Nothing
End If
End If
Next j
IsCardPlayable = False
End Function
This function plays the card to a foundation, if possible. It is used by the AutoPlay and AutoPlayCard
methods.
Private Function InternalAutoPlayCard(ByVal pCard As SolpackLib.ISolpackCard)
As Boolean
InternalAutoPlayCard = False
If pCard Is Nothing Then
Return
End If
Dim j As Integer
For j = 0 To 4
If m_aTopOfPile(j).Data Is pCard Then
If IsCardPlayable(j) Then
Dim pFrag As ISolpackFragment
pFrag = m_pApp.CreateFragment(m_aPiles(j))
pFrag.AddToFragment pCard
pFrag.MoveToPile m_pFoundation, True, False
ScoreCard pCard
Dim pNewTop As ISolpackCard
If m_aPiles(j).NumCards = 0 Then
Set m_aTopOfPile(j).Data = Nothing
Else
Set m_aTopOfPile(j) = m_aPiles(j).GetCard(m_aPiles(j).NumCards - 1)
m_aTopOfPile(j).Data.CanDrag = True
End If
InternalAutoPlayCard = True
End If
Return
End If
Next j
End Function
This sub is called after a card has been moved to the foundation (either by autoplay or by drop). It sets the specified
card's CanDrag property to false, increments the score, and checks whether the game is now won.
Private Sub ScoreCard(ByVal pCard As SolpackLib.ISolpackCard)
If pCard Is Nothing Then Return
pCard.CanDrag = False
m_pApp.Score = m_pApp.Score + 1
If m_pApp.Score = 52 Then m_pApp.GameWon
End Sub