PDA

Click to See Complete Forum and Search --> : [VB.NET] Sorting Array of Custom Class objects


wossname
Oct 27th, 2004, 06:49 AM
Lets say you are writing a card game and you need to be able to sort a player's cards into numerical and suit order.

You have written a PlayingCard class, and you have to find a way of sorting by these 2 criteria.

The .Net (OOP) way is to make that class implement the IComparable interface. This simply adds a function that decides if one class instance is 'greater then, less than or equal to' another instance of the same class. Write your sorting rules into this function body and that is. You don't have to worry about moving items into the right places in the array, or type conversion problems, or anything like that. The Array.Sort() function calls your implementation of the IComparable interface and communicates directly with it without your intervention.

Here is a nice example I just cooked up.
(The important parts are highlighted in red).



Module Module1

Sub Main()

Dim Deck() As PlayingCard = New PlayingCard(99) {}
Dim i As Integer
Dim r As Random = New Random

For i = 0 To 99 'randomize the array
Deck(i) = New PlayingCard(r.Next(PlayingCard.CARDVALUE.Ace, PlayingCard.CARDVALUE.King + 1), r.Next(PlayingCard.CARDSUIT.Spades, PlayingCard.CARDSUIT.Diamonds + 1))
Next i

PlayingCard.AcesHigh = True
Array.Sort(Deck)

For i = 0 To 99
Console.WriteLine(Deck(i))
Next i

Console.ReadLine()

End Sub

End Module

Public Class PlayingCard
Implements IComparable 'IMPORTANT

#Region "Game Specific"
#Region "Enums"
Public Enum CARDVALUE
Ace = 1
Two = 2
Three = 3
Four = 4
Five = 5
Six = 6
Seven = 7
Eight = 8
Nine = 9
Ten = 10
Jack = 11
Queen = 12
King = 13
HighAce = 14
End Enum
Public Enum CARDSUIT
Spades = 0
Hearts = 1
Clubs = 2
Diamonds = 3
End Enum
#End Region
#Region "Shared"
Public Shared AcesHigh As Boolean = False
#End Region
#Region "Member Data"
Private _Value As CARDVALUE = CARDVALUE.Ace
Private _Suit As CARDSUIT = CARDSUIT.Spades
#End Region

Public Property Value() As CARDVALUE
Get
Return _Value
End Get
Set(ByVal RHS As CARDVALUE)
If RHS = CARDVALUE.HighAce Then Throw New ArgumentException("Cannot set Value to HighAce directly, set Value to Ace and set AcesHigh variable to True")
_Value = RHS
End Set
End Property
Public Property Suit() As CARDSUIT
Get
Return _Suit
End Get
Set(ByVal RHS As CARDSUIT)
_Suit = RHS
End Set
End Property
Public ReadOnly Property IsHighAce() As Boolean
Get
Return (_Value = CARDVALUE.Ace) And AcesHigh
End Get
End Property
Public ReadOnly Property IsRoyalty() As Boolean
Get
Return (_Value >= CARDVALUE.Jack) And (_Value <= CARDVALUE.King)
End Get
End Property
Public Overrides Function ToString() As String

Dim s As String

Select Case _Value
Case CARDVALUE.Ace
s = "Ace of "
Case CARDVALUE.Two
s = "Two of "
Case CARDVALUE.Three
s = "Three of "
Case CARDVALUE.Four
s = "Four of "
Case CARDVALUE.Five
s = "Five of "
Case CARDVALUE.Six
s = "Six of "
Case CARDVALUE.Seven
s = "Seven of "
Case CARDVALUE.Eight
s = "Eight of "
Case CARDVALUE.Nine
s = "Nine of "
Case CARDVALUE.Ten
s = "Ten of "
Case CARDVALUE.Jack
s = "Jack of "
Case CARDVALUE.Queen
s = "Queen of "
Case CARDVALUE.King
s = "King of "
End Select

Select Case _Suit
Case CARDSUIT.Spades
s &= "Spades"
Case CARDSUIT.Hearts
s &= "Hearts"
Case CARDSUIT.Clubs
s &= "Clubs"
Case CARDSUIT.Diamonds
s &= "Diamonds"
End Select

Return s

End Function

#End Region

Public Sub New(ByVal itsValue As CARDVALUE, ByVal itsSuit As CARDSUIT)
Value = itsValue
Suit = itsSuit
End Sub

Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo

'where the Sorting logic is run

'Basically says: "if ME greater than the obj card then return 1, if equal return 0 and if less than, return -1"

If Not TypeOf obj Is PlayingCard Then Throw New ArgumentException 'make sure obj is a card before continuing

Dim temp As PlayingCard = CType(obj, PlayingCard)
Dim vObj As CARDVALUE = temp.Value
Dim vMe As CARDVALUE = _Value
Dim s As CARDSUIT = temp.Suit

If _Suit < s Then 'suits take precedence
Return -1
ElseIf _Suit > s Then
Return 1
Else
If AcesHigh Then 'some games require aces to have a value of 14 rather than 1, hence "Aces High"
If vObj = CARDVALUE.Ace Then vObj = CARDVALUE.HighAce
If vMe = CARDVALUE.Ace Then vMe = CARDVALUE.HighAce
End If
'if suite are the same, check the card numbers...
If vMe < vObj Then
Return -1
ElseIf vMe > vObj Then
Return 1
Else
Return 0 'cards are identical (if you are using more than 1 deck deck of cards in a program)
End If
End If

End Function

End Class



Copy this code into a console app and run it. It also demonstrates the overriding of the Tostring() function.

The output looks like this...

WonkoTheSane
Nov 4th, 2004, 03:31 PM
That works fine!

Nice work wossname.

plenderj
Nov 17th, 2004, 06:25 AM
Or how about this :)
http://www.irishdev.com/blogs/kieranlynam/archive/2004/11/16/285.aspx

Hack
Feb 10th, 2006, 12:22 PM
Moved to Games CodeBank