Kartenspielklasse

  • Allgemein

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    Kartenspielklasse

    Hallo,

    ich bin zur Zeit daran, ein einfaches Kartenspiel zu erstellen ( Mau-Mau ) und wollte euch Kritiker mal Fragen, was ihr an der Klasse generell verbessern würdet.

    VB.NET-Quellcode

    1. Public Class Karte
    2. Public Sub New(ByVal FarbeDerKarte As Farbe, ByVal WertDerKarte As Wert)
    3. _KartenFarbe = FarbeDerKarte
    4. _KartenWert = WertDerKarte
    5. End Sub
    6. Private _KartenFarbe As Farbe
    7. Public Property KartenFarbe As Farbe
    8. Get
    9. Return _KartenFarbe
    10. End Get
    11. Set(value As Farbe)
    12. _KartenFarbe = value
    13. End Set
    14. End Property
    15. Private _KartenWert As Wert
    16. Public Property KartenWert As Wert
    17. Get
    18. Return _KartenWert
    19. End Get
    20. Set(value As Wert)
    21. _KartenWert = value
    22. End Set
    23. End Property
    24. Enum Farbe
    25. Pik
    26. Herz
    27. Kreuz
    28. Karo
    29. End Enum
    30. Enum Wert
    31. Sieben
    32. Acht
    33. Neun
    34. Zehn
    35. Bube
    36. Dame
    37. König
    38. Ass
    39. End Enum
    40. End Class


    Und um einen Kartenstapel zu erstellen ( noch keine Mischfunktion integriert)

    VB.NET-Quellcode

    1. Public Sub NeuenKartenStapelErstellen()
    2. Dim KartenStapel As New List(Of Karten)
    3. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.Sieben))
    4. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.Acht))
    5. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.Neun))
    6. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.Zehn))
    7. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.Bube))
    8. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.Dame))
    9. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.König))
    10. KartenStapel.Add(New Karte(Karte.Farbe.Herz, Karte.Wert.Ass))
    11. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.Sieben))
    12. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.Acht))
    13. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.Neun))
    14. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.Zehn))
    15. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.Bube))
    16. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.Dame))
    17. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.König))
    18. KartenStapel.Add(New Karte(Karte.Farbe.Karo, Karte.Wert.Ass))
    19. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.Sieben))
    20. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.Acht))
    21. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.Neun))
    22. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.Zehn))
    23. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.Bube))
    24. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.Dame))
    25. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.König))
    26. KartenStapel.Add(New Karte(Karte.Farbe.Kreuz, Karte.Wert.Ass))
    27. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.Sieben))
    28. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.Acht))
    29. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.Neun))
    30. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.Zehn))
    31. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.Bube))
    32. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.Dame))
    33. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.König))
    34. KartenStapel.Add(New Karte(Karte.Farbe.Pik, Karte.Wert.Ass))
    35. End Sub
    36. End Class

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Julius“ ()

    die Klasse Karten repräsentiert genau eine Karte. Also benenne sie auch Singular.

    VB.NET-Quellcode

    1. Public Class Karte
    2. Public Sub New(ByVal FarbeDerKarte As Farbe, ByVal WertDerKarte As Wert)
    3. _KartenFarbe = FarbeDerKarte
    4. _KartenWert = WertDerKarte
    5. End Sub
    6. Private _KartenFarbe As Farbe
    7. Public Property KartenFarbe As Farbe
    8. Get
    9. Return _KartenFarbe
    10. End Get
    11. Set(value As Farbe)
    12. _KartenFarbe = value
    13. End Set
    14. End Property
    15. Private _KartenWert As Wert
    16. Public Property KartenWert As Wert
    17. Get
    18. Return _KartenWert
    19. End Get
    20. Set(value As Wert)
    21. _KartenWert = value
    22. End Set
    23. End Property
    24. Public Enum Farbe
    25. Karo
    26. Herz
    27. Pik
    28. Kreuz
    29. End Enum
    30. Public Enum Wert
    31. Sieben
    32. Acht
    33. Neun
    34. Zehn
    35. Bube
    36. Dame
    37. König
    38. Ass
    39. End Enum
    40. End Class
    41. '...
    42. Public Sub NeuenKartenStapelErstellen()
    43. Dim KartenStapel As New List(Of Karte)
    44. For farbe = Karte.Farbe.Karo To Karte.Farbe.Kreuz
    45. For wert = Karte.Wert.Sieben To Karte.Wert.Ass
    46. KartenStapel.Add(New Karte(farbe, wert))
    47. Next
    48. Next
    49. End Sub
    Da Farbe und Wert einer Karte im Konstruktor vorgegeben werden, mach die entsprechenden Properties ReadOnly.

    VB.NET-Quellcode

    1. Public ReadOnly Property KartenFarbe As Farbe
    2. Get
    3. Return _KartenFarbe
    4. End Get
    5. End Property
    6. Public ReadOnly Property KartenWert As Wert
    7. Get
    8. Return _KartenWert
    9. End Get
    10. End Property
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Eine Frage hätte ich am Rande noch. Ich bin gerade dabei, mein Projekt mehrspielerfähig zu machen und habe das Grundgerüst soweit fertig. Da den Spielern jedoch auch das Kartenblatt/einzelne Karten vom Server zugesendet werden sollen und der Befehl zum senden ...StreamWriter.WriteLine(Object) auf das Objekt nur die .ToString Methode anwendet, muss das irgendwie beim Client wieder zusammensetzen. Dachte mir, dass es durch die Implementierung der KartenKlasse in den Client und die Typenkonvertierung beim Client Ctype(StreamReader.ReadLine, Karte) ginge. Bevor ich einen auf den Deckel bekomme: Ja ich habe Option Strict on und habe alleine nur für den Versuch oben schon einen vom Compiler aufn Deckel bekommen ^^
    tja, eine Karte ist eben kein String, und kann man auch nicht draus machen.
    Doch! kann man doch draus machen, nämlich mitte .ToString - Funktion.

    Aber ein String ist keine Karte und kann man auch nicht draus machen, auch nicht mit CType(). Deshalb die Exception.

    Du befindest dich hier im Bereich von Parsen und Formatieren - da habich für die Nachwelt philosophisches zu hinterlassen: Hex-Zahlen?

    Nun hast du viele Möglichkeiten - zB Serialisierung ist ein Standard-Verfahren - informier dich über XmlSerializer - das wird meistens genommen - oder BinaryFormatter oder JsonSerializer.
    Nun dann mach es so:
    Gib den Karten einen Namen: "Kreuz7", "HerzBube", "KreuzPik". Ein aus Farbe und Wert zusammengesetzter String, den Du anders rum wieder zu seinem richtigen Wert zerlegen und zuordnen kannst.
    Wenn alle Spieler dann dieselbe Karten-DLL haben, ist das kein Problem.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    zurückgezogen - zuviele Köche... ;)

    naja - ich kanns doch nicht lassen.
    Also du hast jetzt 2 grundlegende Möglichkeiten: Sowas wie Serialisierung, gewissermassen Parsen/Formatieren in/aus einem Stream.

    Oder klassisches Parsen/Formatieren in/aus einem String.
    Hier würde ich empfehlen, einfach dem Muster zu folgen, wassich in Hex-Zahlen?-post#2 herausarbeite, und also ebenso deiner Klasse eine statische Parse-Function angedeien lassen.

    Abraten täte ich, die klasse durch weitere Felder redundant aufzublähen: Wenn eine Name-Property, dann solls eine berechnete Property sein, etwa

    VB.NET-Quellcode

    1. Return Me.Farbe.Tostring & me.Wert
    Aber findich auch nicht nötig, weil das könnte auch die ToString-Überschreibung abhandeln.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Ich hab mal deine Klasse genommen und etwas dran rumgespielt. Rausgekommen ist das:

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    4. Dim AlleKarten As List(Of Karte) = Karte.GenerateAllCards
    5. MessageBox.Show(String.Format("ToString: {0}\nToInt: {1}\nFromString: {2}\nFromInt: {3}".Replace("\n", Environment.NewLine), _
    6. AlleKarten(10).ToString, AlleKarten(10).ToInt, _
    7. Karte.FromString(AlleKarten(10).ToString).ToString, _
    8. Karte.FromInt(AlleKarten(10).ToInt).ToString))
    9. End Sub
    10. End Class
    11. Public Class Karte
    12. Public Sub New(ByVal farbe As KartenFarbe, ByVal wert As KartenWert) 'Argumente werden klein geschrieben
    13. _Farbe = farbe
    14. _Wert = wert
    15. End Sub
    16. Private _Farbe As KartenFarbe
    17. Public ReadOnly Property Farbe As KartenFarbe 'ReadOnly, andere Benennung
    18. Get
    19. Return _Farbe
    20. End Get
    21. End Property
    22. Private _Wert As KartenWert
    23. Public ReadOnly Property Wert As KartenWert 'ReadOnly, andere Benennung
    24. Get
    25. Return _Wert
    26. End Get
    27. End Property
    28. Public Shared Function GenerateAllCards() As List(Of Karte) 'Generiert alle Kartenkombinationen
    29. Dim Cards As New List(Of Karte)
    30. For Farbe As Integer = 1 To 4
    31. For Karte As Integer = 1 To 8
    32. Cards.Add(New Karte(CType(Farbe, KartenFarbe), CType(Karte, KartenWert)))
    33. Next
    34. Next
    35. Return Cards
    36. End Function
    37. Public Overrides Function ToString() As String 'Die Eigenschaften einer Karte in einen String reinschreiben
    38. Return String.Format("{{Farbe: ""{0}""; Wert: ""{1}""}}", _
    39. [Enum].GetName(GetType(KartenFarbe), Me.Farbe), [Enum].GetName(GetType(KartenWert), Me.Wert))
    40. End Function
    41. Public Shared Function FromString(input As String) As Karte 'Die in den String reingeschriebenen Daten für die Karte extrahieren
    42. Return New Karte(CType([Enum].Parse(GetType(KartenFarbe), input.Split(""""c)(2 - 1)), KartenFarbe), _
    43. CType([Enum].Parse(GetType(KartenWert), input.Split(""""c)(4 - 1)), KartenWert))
    44. End Function
    45. Public Function ToInt() As Integer 'Die Eigenschaften einer Karte in einen Integer reinschreiben
    46. Return 10 * CInt(Me.Farbe) + CInt(Me.Wert) 'Gibt eine Zahl zwischen 11 und 48 aus, entspricht dem Kartenwert und der Kartenfarbe
    47. End Function
    48. Public Shared Function FromInt(input As Integer) As Karte 'Die in den Integer reingeschriebenen Daten für die Karte extrahieren
    49. Return New Karte(CType(CInt(Math.Floor(input / 10)), KartenFarbe), _
    50. CType(CInt(input - Math.Floor(input / 10) * 10), KartenWert)) 'Neue Karte generieren und zurückgeben
    51. End Function
    52. End Class
    53. Public Enum KartenFarbe 'Jeder Kartenfarbe eine Zahl zuweisen (von 1 - 4)
    54. Pik = 1
    55. Herz = 2
    56. Kreuz = 3
    57. Karo = 4
    58. End Enum
    59. Public Enum KartenWert 'Jedem Kartenwert eine Zahl zuweisen (von 1 - 8)
    60. Sieben = 1
    61. Acht = 2
    62. Neun = 3
    63. Zehn = 4
    64. Bube = 5
    65. Dame = 6
    66. König = 7
    67. Ass = 8
    68. End Enum

    Was habe ich gemacht?:
    -Bei den Enums hat jeder Punkt einen eigenen Integer zugewiesen bekommen
    -Ein paar Namen wurden geändert, bsp. Karte.Kartenwert -> Karte.Wert
    -Die Funktion GenerateAllCards wurde hinzugefügt, sie generiert alle verfügbaren Karten
    -Die Funktionen ToString und FromString wurden hinzugefügt, sie wandeln eine Karte in einen String und wieder zurück
    -Die Funktionen ToInt und FromInt wurden hinzugefügt, sie wandeln eine Karte in einen Integer und wieder zurück

    Verwendungsbeispiel:

    VB.NET-Quellcode

    1. Dim AlleKarten As List(Of Karte) = Karte.GenerateAllCards
    2. MessageBox.Show(String.Format("ToString: {0}\nToInt: {1}\nFromString: {2}\nFromInt: {3}".Replace("\n", Environment.NewLine), _
    3. AlleKarten(10).ToString, AlleKarten(10).ToInt, _
    4. Karte.FromString(AlleKarten(10).ToString).ToString, _
    5. Karte.FromInt(AlleKarten(10).ToInt).ToString))
    So habe mal mit euren Ideen mal eben selber 2 Methoden geschrieben, wollte mal fragen, was ihr davon haltet ?

    Methode 1:

    VB.NET-Quellcode

    1. Private Function _Serialize(ByVal _Karte As Karte) As String
    2. Return _Karte.KartenSymbol & _Karte.KartenWert
    3. End Function
    4. Private Function _DeSerialize(ByVal s As String) As Karte
    5. Return New Karte(CType(s.First.ToString, Karte.Symbol), CType(s.Last.ToString, Karte.Wert))
    6. End Function


    Methode 2:

    BinaryFormatter, wie vorgeschlagen. Tüftel gerade noch bissl daran, dass der Server das serializedObject auch mal zu den Clients absendet^^
    Wenn man sich den Längenunterschied des Strings von den Binaryformatter und der anderen Methode anguckst, dann überträgt man ja mit der .ToInt Methode nur einen String von 2 Zahlen, was ja wahrlich kürzer ist. Außerdem wundert es mich, wieso du in den Enumerationen den Werten extra Zahlen zuweist, wenn sie schon von Haus aus "enumeriert" werden.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Julius“ ()

    Enums sind Zahlen, du kannst z.B. festlegen welcher Datentyp für das Enum verwendet werden soll (Byte, Integer, Long,... alle Ganzzahl-Typen eben):

    VB.NET-Quellcode

    1. Public Enum KartenFarbe as Byte
    2. ...
    3. End Enum


    Byte würde für deine Zwecke schon ausreichen, damit kannst du 2^8 verschiedene Zustände speichern (bzw wenn du sie kombinieren willst nur 8, tust du aber nicht).
    Im Endeffekt heißt das, dass du lediglich je 1 Byte für Farbe und Wert versenden müsstest um eine Karte eindeutig zu identifizieren. Natürlich nur, wenn du auf Byte-Ebene arbeitest und keine Strings hin und her schickst.

    lg
    @FreakJNS
    Wenn man bei meiner .ToInt-Funktion den Ausgabetyp zu Byte ändert, passt beides sogar zusammen in einen Byte :P.

    @Julius
    Erstmal sind Enumerationen von Haus aus von 0 - n nummeriert, ich habe die Nummerierung einfach zu 1 - n geändert, da dann die Methoden .ToInt und .FromInt einfacher umzusetzen waren.

    Julius schrieb:

    Außerdem wundert es mich, wieso du in den Enumerationen den Werten extra Zahlen zuweist, wenn sie schon von Haus aus "enumeriert" werden.
    richtig - die Enumerationen waren vorher besser:
    Die Werte fingen bei 0 an und waren dem Kartenwert entsprechend geordnet, also Pik war höher als Karo - dassis hier ein wichtiges Detail des Datenmodells.


    @Julius:: Wenn du eine Methode "Serialize" oder so ähnlich benennst, dann sollte die auch serialisieren. Deine Methoden serialisieren nicht, sondern Formatieren und Parsen.
    wirklich empfehlung: Halte dich an übliche Pattern: Formatieren geht mit einer Tostring-Überschreibung und Parsen sei eine Public Shared Function, die einen String nimmt, und eine Karte zurückgibt.
    Wie bei Integer, DateTime, Boolean und vielen anneren Klassen auch.


    Das ganz zugrunde liegende Design, also dass Karte eine Klasse mit 2 Enumerationen ist, würde ich jetzt erstmal nicht weiter diskutieren.
    Da sind zig weitere Varianten denkbar, alle mit Vor- und Nach-Teilen - etwa, ob eine Karte nicht besser eine Structure sei?
    Oder wie wärs mit einer geflaggten Enumeration - da könnteman die Klasse Karte auch ganz weglassen?

    wie gesagt: sowas jetzt erstma nicht ausdiskutieren - da kommste von Höcksken auf Stöcksken.
    Das derzeitige Design ist so auch i.O. und brauchbar.
    prinzipiell nix dagegen - beinem geflaggten Enum wäre das sogar notwendig.

    Das Prob habichdoch bereits gesagt: Bei deine Enums hat Karo einen höheren Wert als Pik - und dassis ein nicht-stimmiges Datenmodell eines Kartenspiels.
    Enums dienen ja auch < / > - Vergleichen, und Karo muß das niedrigste sein.
    Ich würde nur die .toString-Methode nicht dafür 'Missbrauchen' - sie wird ja von etlichen Controls benutzt um ein Objekt sinnvoll anzeigen zu können. Das macht man sich kaputt, weil in einer Listbox würde ich gerne eine schön formatierte Ausgabe wie "Bauer (rot)" lesen und nicht das, was zum speichern erzeugt wird (ist ja vorzugsweise praktisch, nicht schön).

    Ich würde also dafür separate Methoden schreiben: Public Function ExportToString() as String und Public Shared ImportFromString(input as String) as Karte
    (oder eben toBytes und fromBytes xD)
    Wenn man wie FreakJNS bei der ToString-Methode sowas wie Bauer (Kreuz) haben möchte, geht das doch auch, man muss nur den Parser etwas anders schreiben :D. Man könnte aber auch bei der .ToString-Methode die Möglichkeit hinzufügen, den String nach Belieben zu formatieren, bei der .FromString-Methode ebenfalls. Die Formatierungen könnte man ja so wie bei String.Format() machen. Die Funktion würde dann so aussehen:

    VB.NET-Quellcode

    1. Public Overloads Function ToString(format As String) As String
    2. Dim FormatString As String = System.Text.RegularExpressions.Regex.Replace(format, "\{(?!Wert}|Farbe})", "{{")
    3. FormatString = System.Text.RegularExpressions.Regex.Replace(FormatString, "(?<!{Wert|{Farbe)\}", "}}")
    4. FormatString = FormatString.Replace("{Farbe}", "{0}").Replace("{Wert}", "{1}")
    5. Return String.Format(FormatString, [Enum].GetName(GetType(KartenFarbe), Me.Farbe), [Enum].GetName(GetType(KartenWert), Me.Wert))
    6. End Function
    7. Public Shared Function FromString(input As String, format As String) As Karte
    8. Dim RegexPattern As String = System.Text.RegularExpressions.Regex.Escape(format)
    9. RegexPattern = RegexPattern.Replace("\{Wert}", "(?<Wert>(.*?))").Replace("\{Farbe}", "(?<Farbe>(.*?))")
    10. Dim Match = System.Text.RegularExpressions.Regex.Match(input, RegexPattern)
    11. Return New Karte(CType([Enum].Parse(GetType(KartenFarbe), Match.Groups("Farbe").Value), KartenFarbe), _
    12. CType([Enum].Parse(GetType(KartenWert), Match.Groups("Wert").Value), KartenWert))
    13. End Function

    Dafür kannst du dann verschiedene Formate selber erstellen, hier ein paar Beispiele:
    {Wert} ({Farbe}) -> König (Herz)
    {Farbe} {Wert} -> Herz König
    {Farbe: "{Farbe}"; Wert: "{Wert}"} -> {Farbe: "Herz"; Wert: "König"}
    Dadurch sollte dir auch das System klar geworden sein, {Wert} und {Farbe} stellen die beiden Eigenschaften der Karte dar.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „nafets3646“ ()

    nafets3646 schrieb:

    Die Funktion würde dann so aussehen:
    Wozu brauchst Du bei ToString RegEx?
    Mach es immer so einfach wie möglich, z.B.:

    VB.NET-Quellcode

    1. Public Overrides Function ToString() As String
    2. Return Farbe.ToString & "_" & Wert.ToString
    3. End Function
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!