Mischfunktion für Kartenspiel

  • VB.NET

Es gibt 10 Antworten in diesem Thema. Der letzte Beitrag () ist von Schocklage.

    Mischfunktion für Kartenspiel

    Hallo Community,
    ich habe eine Routine zum mischen von Karten geschrieben und wollte einfach mal von euch hören, ob der Code besser gestaltet werden kann oder ob er so eigentlich recht gut ist. Er funktioniert, kann der so bleiben? hier der Code:

    VB.NET-Quellcode

    1. Imports System.Text
    2. Public Class Form1
    3. Private Sub Karten_mischen_Click(sender As Object, e As EventArgs) Handles Karten_mischen.Click
    4. Dim lst_Inhalt As New List(Of String) ' Neue Liste, welche im weiteren Code dynamisch befüllt wird
    5. Dim rnd_Zufallszahl As New Random ' Deklaration einer neuen Zufallszahl
    6. Dim i_Zufallsspeicher As Integer = 0 ' Zwischenspeicher für die Zufallszahl
    7. Dim Stringspeicher As New StringBuilder("") ' Verkettet die verschiedenen Möglichkeiten
    8. RichTextBox1.Clear() ' Löscht den Text in der Rich Text Box
    9. For i = 0 To 51 ' Beispiel für dynamische Füllung mittels Schleife
    10. lst_Inhalt.Add((i + 1).ToString & " ") ' Beispiel für dynamische Füllung mittels Schleife
    11. Next ' Beispiel für dynamische Füllung mittels Schleife
    12. For i = 0 To 51 ' Beginn der Zufallsschleife ... Maximum der To Schleife entspricht im Beispiel den gesamten 52 Karten. Wert kann festgelegt werden
    13. i_Zufallsspeicher = rnd_Zufallszahl.Next(0, lst_Inhalt.Count) ' Generierung der Zufallszahl mit Zwischenspeicherung in i_Zufallsspeicher
    14. Stringspeicher.Append((i) + 1 & " - " & lst_Inhalt.Item(i_Zufallsspeicher) & vbCrLf) ' Den Ausgabestring um den ermittelten String erweitern
    15. lst_Inhalt.RemoveAt(i_Zufallsspeicher) ' Um Mehrfachverwendung auszuschliessen wird der selbe String in der ListOf gelöscht
    16. Next ' Nächsten String ermitteln bis Ende
    17. RichTextBox1.Text = Stringspeicher.ToString ' Anzeige des gemischten Ergebnisses
    18. End Sub
    19. End Class


    Für Programmiertechnische Tipps wäre ich dankbar

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

    Willkommen im Forum.

    Zeile#7 muss sollte aus der Prozedur raus, sonst könnten immer die gleiche "Zufallsfolge" kommen.
    Zeile#9: "" kann weg. Der StringBuilder wird eh leer initialisiert.
    Was hat das auf Anhieb mit Karten zu tun? 1-52, ok. Aber sonst? Na wurscht.
    Vereinfachen könnte man das ganze auch, soweit ich mich auf Anhieb erinnere. Da braucht man dann nur eine For-Schleife.

    btw: statt dem allgemeinen CodeTag bitte den VB.Net-CodeTag verwenden.

    ##########

    Hab's gefunden: In einem alten Post von RfG, hiesige Zeile#5:

    VB.NET-Quellcode

    1. Dim rnd_Zufallszahl As New Random
    2. Dim i_Zufallsspeicher = 0
    3. Dim Stringspeicher As New StringBuilder()
    4. RichTextBox1.Clear()
    5. Dim Liste = (From q In Enumerable.Range(0, 52) Order By rnd_Zufallszahl.Next).ToArray
    6. For i = 0 To 51
    7. Stringspeicher.Append(i + 1 & " - " & Liste(i).ToString & Environment.NewLine) ' vbCrLf ist aus dem MS.VB-Namespace, der standardmäßig nicht importiert werden sollte
    8. Next
    9. RichTextBox1.Text = Stringspeicher.ToString

    die Themen Daten-GUI-Trennung und SRP stehen auf einem anderen Blatt, von daher momentan irrelevent.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „VaporiZed“ ()

    Schau dir das mal an. Ich habs in der Konsole geschrieben.
    Sollte aber selbsterkärend sein.

    Freundliche Grüsse

    exc-jdbi

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Option Explicit On
    3. Imports System.Windows.Forms
    4. Public Module Module1
    5. Private rand As New Random()
    6. Private RichTextBox1 As New RichTextBox
    7. Public Sub Main()
    8. Dim _min = 0I, _max = 52I
    9. Dim rngshuff = RandomScale(_min, _max)
    10. Dim res = Enumerable.Range(_min, _max) _
    11. .Select(Function(x) _
    12. String.Format("{0} - {1}",
    13. x.ToString("D2"),
    14. rngshuff(x).ToString("D2"))).ToArray
    15. Array.ForEach(res, Sub(s) Console.WriteLine(s))
    16. Console.ReadLine()
    17. 'Für RichTextBox1
    18. Array.ForEach(res, Sub(s) RichTextBox1.Text &= s & Environment.NewLine)
    19. Stop
    20. End Sub
    21. Private Function RandomScale(_min As Int32, _max As Int32) As Int32()
    22. Return Enumerable.Range(_min, _max).OrderBy(Function(x) rand.Next()).ToArray()
    23. End Function
    24. End Module

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „exc-jdbi“ ()

    Danke für die Antworten,
    eure Lösungsvorschläge sind im Moment noch etwas wie Böhmische Dörfer für mich. @VaporiZed ich weiss, dass es im ersten Moment etwas blöd aussieht, da ich mir die Funktion im Code visualisieren lasse mittels RichTextBox. In der Anwendung selbst, kommt es nur auf die zufällige Reihenfolge des Kartendecks an. Da ist es ne list of Integer.

    Dim Liste = (From q In Enumerable.Range(0, 52) Order By rnd_Zufallszahl.Next).ToArray


    Das versteh ich nicht. Wofür steht das q zum Beispiel.
    @Schocklage Vielleicht was verständlicheres:
    Mach Dir eine List(Of Integer), die Du mit { 0, 1, ... 51 } befüllst.

    Quellcode

    1. DO
    2. Nimm eine Zufallszahl von { 0 .. DIESE_LIST.Count },
    3. Nimm den Wert mit diesem Index aus der List raus und hänge ihn an Deine finale Liste an.
    4. LOOP (DIESE_LIST.Count > 0)

    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!

    VaporiZed schrieb:

    Dim Liste = (From q In Enumerable.Range(0, 52) Order By rnd_Zufallszahl.Next).ToArray


    Das würde doch zulassen, dass der gleiche Kartenindex mehrfach vorkommt, oder hab ich da etwas falsch verstanden?
    Dein Vorschlag ist doch in etwa, was ich umgesetzt habe. Einzig die finale Liste ist hier zur Visualisierung als RichTextBox inklusive voran gestelltem Index dargestellt.

    Quellcode

    1. Ich fülle die Liste zwischen Zeile 13 und 15
    2. Dann Ziehe ich eine Zufällige aus den übrigen und füge sie in der RichTextBox ein (benutze die RTB als Liste)


    Wenn Du das ganze auf die Funktion herunterbrichst ist es doch nicht am Thema vorbei, oder?
    vbCrLf ist aus dem MS.VB-Namespace, der standardmäßig nicht importiert werden sollte

    Danke dafür, habs ins Inventar aufgenommen! :)
    Die Zeile
    (From q In Enumerable.Range(0, 52) Order By rnd_Zufallszahl.Next).ToArray
    oder in meiner bevorzugten Schreibweise
    Enumerable.Range(0, 52).OrderBy(Function(x) rnd_Zufallszahl.Next).ToArray
    macht folgendes: Enumerable.Range(0, 52) Sie generiert eine Auflistung von 52 Ganzzahlen, beginnend bei Null.
    Mit jeder dieser Zahlen macht sie etwas. Und zwar konkret das, was nach dem Punkt kommt. Hier OrderBy, also eine Sortierung.
    Was ist nun das Sortierkriterium? Das erfolgt in den Klammern: Function(x) rnd_Zufallszahl.Next: Für jede dieser Zahlen von 0 bis 51 wird eine Zufallszahl generiert. Beispielsweise:
    0 -> 321
    1 -> 12433
    2 -> 87
    3 -> 7643
    4 -> 666
    5 -> 42
    ...
    Und nach diesen Zufallszahlen wird dann sortiert. Nehmen wir die genannten 6. Die o.g. Liste wird dann zu:
    5 -> 42
    2 -> 87
    0 -> 321
    4 -> 666
    3 -> 7643
    1 -> 12433
    Die Zufallszahlen werden verworfen und übrig bleibt die Zufallsauflistung Deiner 52 Zahlen.
    Der Algorithmus ist an sich schnell geschrieben (aber nicht sofort für den Erstverwender verständlich), wird bei kleineren Listen häufiger mal verwendet, hat aber wohl ein, zwei Schwächen (die hier aber irrelevant sind). Vielleicht will noch jemand Fisher-Yates erwähnen (ups, hab ich ja gerade) und näher bringen.

    ##########

    Schocklage schrieb:

    Wenn Du das ganze auf die Funktion herunterbrichst ist es doch nicht am Thema vorbei, oder?
    Öhm. Den Satz versteh ich inhaltlich nicht. Dein Code funktioniert. Das hast Du selber schon geschrieben. Das von mir beschriebene wäre eine Vereinfachung und Kürzung des Codes, da Du nur noch eine For-Schleife brauchst. Um mehr ging es mir nicht. Ach, oder meinst Du, dass ich mal die Funktion erkläre, wie ich es hier mit diesem Post gemacht habe? Nee, das gehört ja dann mit zum Thema, das passt somit schon hier rein..
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    exc-jdbi schrieb:

    Private Function RandomScale(_min As Int32, _max As Int32) As Int32()
    Return Enumerable.Range(_min, _max).OrderBy(Function(x) rand.Next()).ToArray()
    End Function


    Ja da muss ich @VaporiZed recht geben. Auf den ersten Blick kann das ziemlich ein Fragezeichen auslösen. Schlussendlich entspricht es dem Prinzip was das Fisher-Yates macht. Die Schreibweise von @RodFromGermany enspricht der typischen LINQ-Schreibweise. Das From q ist wie eine Deklaration/Initialisierung.

    Und nein es werden keine doppelte Werte generiert, da Enumerable.Range(_min, _max)dafür sorgt das es eine Aufzählung ist, ohne doppelten Werte. .OrderBy(Function(x) nimmt also aus der entsprechenden Array jede Zahl nur einmal.

    In dein Click-Event kannst du den Inhalt von meinem Main-Code reinkopieren (Zeile 8 - 20, ohne Zeile 16+17). Einfach noch die Funktion RandomScale separat hinschreiben, und schon sollte es funktionieren. Auch RichTextBox.Clear noch hinschreiben, das habe ich oben nicht erwähnt.

    Freundliche Grüsse

    exc-jdbi

    Dieser Beitrag wurde bereits 6 mal editiert, zuletzt von „exc-jdbi“ ()

    Enumerable.Range(0, 52) erzeugt Deine Auflistung von 0-51. Daran ändert sich nix. Die Zufallszahlen, die entstehen, geben quasi nur die Plätze an, auf denen diese Zahlen von 0-51 stehen. Und diese Plätze gehen von 0 bis Integer.MaxValue, also 2^32 - 1. Dadurch erhält die 0 zum Beispiel Platz 12345, die 1 Platz 9898989 usw. und dann wird nur noch nach den Plätzen sortiert. Vielleicht ist folgender Code etwas verständlicher*:

    VB.NET-Quellcode

    1. Dim rnd_Zufallszahl As New Random
    2. Dim AuflistungWelcheZahlWelchePlatznummerBekommt As New List(Of Integer)
    3. Private Sub Karten_mischen_Click(sender As Object, e As EventArgs) Handles Karten_mischen.Click
    4. Dim EineSortierteAuflistungVon0Bis51 = Enumerable.Range(0, 52)
    5. Dim DieZahlenVon0Bis51ImGemischtenZustand = EineSortierteAuflistungVon0Bis51.OrderBy(AddressOf GibMirEineZufälligePlatznummer).ToArray
    6. For i = 0 To 51
    7. ListBox1.Items.Add($"{(i + 1)} erhielt die Platznummer {AuflistungWelcheZahlWelchePlatznummerBekommt(i)}")
    8. Next
    9. End Sub
    10. Private Function GibMirEineZufälligePlatznummer(DieZahlInDerAuflistungDieEinePlatznummerBekommenSoll As Integer) As Integer '**
    11. Dim ZufälligePlatznummer = rnd_Zufallszahl.Next
    12. AuflistungWelcheZahlWelchePlatznummerBekommt.Add(ZufälligePlatznummer)
    13. Return ZufälligePlatznummer
    14. End Function

    *dieser Code mit seinen ausschweifenden Namen ist für VB-Anfänger zur Erläuterung gedacht; ich gehe davon aus, dass die hier Beteiligten es auch ohne diese Ausschweifungen verstanden haben
    **den Parameter DieZahlInDerAuflistungDieEinePlatznummerBekommenSoll kann man ggf. verwenden, muss man aber nicht, weil er für den hiesigen Platznummergenerierungcode irrelevant ist
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

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