Kartenspiele (Teil 2)

Der zweite Teil dieses Karten-Tutorials behandelt die Umsetzung eines einfachen Glücksspiels, welches unter dem Namen 17-und-4 bekannt ist.

B. Olaf Rasch 04/2003

 Anregungen oder Tipps an B. Olaf Rasch
 
  Einführung voriges Thema [ Top ] nächstes Thema

Es hat seinen Ursprung in den USA, wo es in der originalen (und auch komplexeren) Variante als Black Jack in Spielhallen und Casinos sehr verbreitet ist.

Während Black Jack mit 208 (4 x 52) Karten gespielt wird und nicht gerade einfach zu erlernen ist, begnügen wir uns bei 17-und-4 mit einem Satz von 32 Karten und beschränken die Regeln auf wenige, leicht zu merkende Punkte.

Außerdem soll demonstriert werden, wie man Karten bewegt und wie man Einsätze in Form von Chips verwaltet.

 
  Projektdateien voriges Thema [ Top ] nächstes Thema

Das Demoprojekt (Standard-EXE) umfasst folgende Module:

eine Form (Name: frmMain),
  Das Hauptfenster, auf welchem der Spieltisch dargestellt wird. In dieser Form befindet sich der Hauptteil des Codes.
eine Dialogbox (Name: frmInfo),
  Diese Form wird gezeigt, wenn der Benutzer über das Menü den Punkt '? | Info' wählt.
eine leere Form (Name: frmCard),
  Wird benutzt, um eine bewegliche Karte darzustellen.
noch eine leere Form (Name: frmChip),
  Wird benutzt, um einen beweglichen Chip darzustellen.
eine Klasse (Name: Chip),
  Diese Klasse dient der Kapselung eines Chip-Objektes und enthält nur eine Long-Variable zur Speicherung des Chip-Wertes.
eine Klasse (Name: Chips),
  Über diese Klasse können Chips bzw. Chip-Stapel angesprochen werden.
ein Bas-Modul (Name: basChips),
  Enthält globale Konstanten, die sich auf die Darstellung von Chips beziehen.
ein Bas-Modul (Name: basShapes),
  Enthält Initialisierungs- und Hilfsroutinen um den Umriss einer Form zu verändern.
das Cards-Modul (Name: CardsDll),
  Deklarationsmodul für die Windows-Bibliothek cards.dll.
 
  Spielregeln voriges Thema [ Top ] nächstes Thema

Die Spielregeln sind wie bereits erwähnt nah an BlackJack angelehnt. Als Kinder haben wir damals 17-und-4 mit einem Satz Skat-Karten gespielt, wobei die 'Bank' von einem virtuellen Spieler übernommen wurde; das ist möglich, weil die Regeln für die Bank (übrigens auch in Spielhallen) so strikt sind, dass keinerlei individuelle Entscheidungen vom Kartengeber getroffen werden müssen.

Man spielt immer gegen den Kartengeber (im folgenden Bank genannt).

Ziel ist es, mit seinen Karten möglichst nahe an eine Punktzahl von 21 heranzukommen bzw. diese genau zu erreichen.

Hierbei haben die Karten folgende Wertigkeit:

Die Zahlen (7, 8, 9 und 10) haben jeweils ihre entsprechende Punktzahl.
Der Bube zählt 2, die Dame 3 und der König 4 Punkte.
Das As kann je nach Bedarf als 11 oder als 1 gezählt werden.

Zu Beginn einer Runde teilt die Bank wie folgt Karten aus:
Eine (offen) für den Spieler, eine (offen) für sich, eine zweite (offen) für den Spieler und eine zweite (verdeckt) für sich. Falls der Spieler jetzt schon 21 Punkte hat (z.B. ein As und eine 10), hat er bereits gewonnen und die Runde ist zu Ende. Hat er weniger als 21 Punkte, kann er sich, wenn er möchte, von der Bank einzeln weitere Karten geben lassen. Erreicht er damit jedoch eine Punktzahl über 21, dann hat automatisch die Bank gewonnen und die Runde ist zu Ende.

Wenn der Spieler denkt, dass er eine gute Punktzahl erreicht hat und keine weitere Karte mehr verlangt, dreht die Bank ihre verdeckte Karte um. Falls die Bank damit mehr Punkte hat als der Spieler, hat sie gewonnen. Wenn nicht, muss sie nun ihrerseits durch Ziehen weiterer Karten dieses Ziel erreichen, ohne über 21 zu kommen, ansonsten gewinnt der Spieler.

Sonderregel: Die Bank muss auf alle Fälle mindestens 17 Punkte erreichen und darf, sobald sie mehr als 16 Punkte hat, keine weitere Karte mehr aufnehmen.

Haben Bank und Spieler die gleiche Punktzahl, steht es unentschieden, der Einsatz bleibt auf dem Tisch liegen und fließt in die nächste Runde mit ein.

Zusatzregel: Sobald man genau 4 Karten besitzt, die alle ein Bild (Bube, Dame oder König) zeigen und eine 5. Karte erhält, die ebenfalls ein Bild zeigt, hat man automatisch gewonnen, unabhängig von der erreichten Punktzahl.


Einsätze:

Während man in einem Casino vor jeder Runde einen Betrag setzen muss (Mindest- und Maximaleinsatz sind vorgeschrieben) und nur in bestimmten Situationen den Einsatz erhöhen kann, wollen wir bei unserem 17-und-4 diese Regelung etwas freier handhaben:

Der Spieler darf zu jeder Zeit beliebige Beträge setzen, die die Bank dann ebenfalls unverzüglich einbringen muss.

Der Spieler kann auch auf das Einsetzen verzichten; es wird dann sozusagen eine Null-Runde gespielt, bei der es nichts zu gewinnen und nichts zu verlieren gibt (außer, es befinden sich noch Chips aus einem vorhergehenden 'Unentschieden' im Pot).

 
  Oberfläche voriges Thema [ Top ] nächstes Thema

Am Spieltisch ist das obere Drittel der Bank (Kartengeber) zugeordnet, das untere Drittel dem Spieler.

In der Mitte befindet sich der Bereich für die Einsätze (Pot) sowie zwei Schaltflächen, mit denen der Spieler eine Runde eröffnen bzw. eine Karte anfordern ('Geben') sowie mitteilen kann, dass er genug Karten hat ('Ok').

Auf dem Screenshot werden die einzelnen Bereiche noch einmal im Detail erläutert.

An Controls werden außer den beiden ImageListen (für die Chip-Grafiken) und dem Timer (für die Kartenanimation) nur Labels benötigt; auch die beiden Schaltflächen Geben und Ok sind lediglich Labels mit einem separaten Mousepointer (zeigende Hand).

MousePointer der Labels 'Geben' und 'Ok':

Position und Größe von versenkten Flächen (z.B. Kartenbereiche oder Pot) werden intern in einer benutzerdefinierten Struktur (Area) gespeichert.

Private Type Area
  Left As Long
  Top As Long
  Width As Long
  Height As Long
End Type

Auf diese Weise kann man die Fläche gezielt löschen oder in einem Mouse_Move-Ereignis abfragen, ob sich die Maus im Inneren befindet.

Private Sub DrawArea(ByRef a As Area)
  Dim re As Long, un As Long
  
  With a
    re = .Left + .Width
    un = .Top + .Height
    Line (.Left - 1, .Top - 1)-(re, .Top - 1), SHADOWCOLOR
    Line (.Left - 1, .Top - 1)-(.Left - 1, un), SHADOWCOLOR
    Line (re, .Top - 1)-(re, un), LIGHTCOLOR
    Line (.Left - 1, un)-(re + 1, un), LIGHTCOLOR
  End With
End Sub
 
Private Sub
EmptyArea(ByRef a As Area)
  With a
    Line (.Left, .Top)- _
         (.Left + .Width - 1, .Top + .Height - 1), _
         TABLECOLOR, BF
  End With
End Sub
 
Private Function
InArea(ByRef a As Area, ByVal x As Long, _
                        ByVal y As Long) As Boolean
  InArea = False
  With
a
    If (x < .Left) Then Exit Function
    If
(y < .Top) Then Exit Function
    If
(x >= .Left + .Width) Then Exit Function
    If
(y >= .Top + .Height) Then Exit Function
  End With

  InArea = True
End Function
 
  Karten voriges Thema [ Top ] nächstes Thema

Intern werden Kartengruppen (Stapel, Bank- und Spieler-Karten) in Long-Arrays gehalten. Die einzelnen Werte im Stapel entsprechen dabei den ID-Nummern, wie sie in cards.dll definiert sind (siehe Konstanten in Kartenspiele Teil 1), während die Werte der Bank- bzw. Spieler-Karten bereits den Punktzahlen der Karten entsprechen.
 
Verwaltung:

Während der Kartenstapel (Card(31)) für 32 Karten dimensioniert sein muss, reichen für die Karten der Bank (BankCard(10)) und die des Spielers (PlayerCard(10)) Arrays der Größe 11 aus. Dies ist die maximale Anzahl Karten, die man bei 17-und-4 auf der Hand halten kann. Die aktuelle Anzahl in den einzelnen Feldern wird in den Zählern Cards, BankCards und PlayerCards vermerkt.

Bevor wir eine Karte austeilen, ziehen wir mittels der Funktion NextCard eine vom Stapel.

Private Function NextCard() As Long
  Cards = Cards - 1
  NextCard = Card(Cards)
End Function

Um die Karte zuzuteilen, benutzen wir die Funktion AddCard, der wir das Ziel-Array, dessen aktuelle Anzahl und natürlich die ID der neuen Karte übergeben. Zurückgeliefert wird dabei die Summe des neuen Blattes, damit das jeweilige Punkte-Label geupdatet werden kann.

Private Function AddCard(ByRef c() As Long, ByRef Count As Long, _
                         ByVal newcard As Long) As Long

'Karte hinzufügen, liefert die Summe des Blattes
  Dim i As Long
 
  c(Count) = CardPoints(newcard)
  Count = Count + 1
  Do
    AddCard = ArraySum(c(), Count)
    If (AddCard <= 21) Then Exit Function
    If Not
(Eleven2One(c(), Count)) Then Exit Function
  Loop
End Function

Da die neue Karte vom Stapel kommt und daher eine ID darstellt, müssen wir diese zunächst in eine Punktzahl umwandeln. Das besorgt die Funktion CardPoints:

Private Function CardPoints(ByVal nr As Long) As Long
'liefert den Wert der Karte nr
  nr = nr \ 4
  Select Case (nr)
    Case 0: CardPoints = 11
    Case 1 To 9: CardPoints = nr + 1
    Case Else: CardPoints = nr - 8
  End Select
End Function

Um die Summe des Blattes zu ermitteln, benutzen wir die Funktion ArraySum, welche eine Anzahl von Elementen eines Long-Arrays addiert.

Private Function ArraySum(ByRef x() As Long, ByVal Count As Long) _
                 As Long
  Dim
i As Long
    ArraySum = 0
  For i = 0 To Count - 1
    ArraySum = ArraySum + x(i)
  Next
End Function

Falls die Summe des Blattes größer als 21 ist, müssen wir versuchen, eventuell enthaltene Asse als 1 statt als 11 zu zählen. Dies wird von der Funktion Eleven2One gemacht, die uns ein Flag zurückliefert, ob eine Umwandlung erfolgen konnte.

Private Function Eleven2One(ByRef x() As Long, _
                            ByVal Count As Long) As Boolean
'versucht ein 11er-As in ein 1er-As zu wandeln
  Dim i As Long
    Eleven2One = False
  For
i = 0 To Count - 1
    If (x(i) = 11) Then
      x(i) = 1: Eleven2One = True: Exit Function
    End If
  Next
End Function

Mischen:

Vor jeder neuen Runde brauchen wir einen Satz von 32 Karten, deren ID's in das Long-Array aufgenommen werden müssen. Da die Karten außerdem immer wieder neu gemischt werden, können wir das in einer einzigen Sub (Shuffle) erledigen.

Private Sub Shuffle()
'alle Karten mischen
  Dim i As Long, j As Long
  Dim
c(31) As Long, b(31) As Boolean

  For i = 0 To 3 'Asse
    c(i) = i: b(i) = False
  Next


  For i = 0 To 27 'Rest
    c(4 + i) = 24 + i: b(4 + i) = False
  Next

  i = 0

  Do
    Randomize
    j = (Rnd() * 32) Mod 32
    While (b(j))
      j = (j + 31) Mod 32
    Wend
    b(j) = True
    Card(i) = c(j): i = i + 1
  Loop Until (i = 32)

  Cards = 32
End Sub

Beim Mischen werden aus einem Quell-Array per Zufallsgenerator Karten gezogen; in einem Boolean-Array werden die gezogenen Karten vermerkt. Trifft man auf eine bereits gezogene Karte, geht man einfach (um eine ungerade Anzahl von Schritten) solange weiter, bis man auf eine noch nicht gezogene Karte stößt.
 
Darstellung:

Für die Ausgabe der Karten-Grafiken wird die Windows-Bibliothek cards.dll benutzt, die sich normalerweise im Systemordner befindet. Falls du nicht im Besitz dieser Datei bist (sie heißt manchmal auch cards32.dll), dann kannst du sie auch auf dieser Seite downloaden, wo übrigens auch ihr Interface ausführlich beschrieben wird.
 
Animation:

Wenn eine Karte ausgeteilt wird, soll sie (mit der Rückseite nach oben) an ihre Zielposition 'schweben'. Am einfachsten nimmt man hierfür eine Form mit dem Umriss einer Karte und zeichnet darauf eine Rückseite. Da diese Form immer wieder gebraucht wird und sich die Grafik auch nicht ändert - es wird ja immer nur die selbe Rückseite gezeigt -, können wir gleich zu Beginn (im Form_Load-Ereignis) das benötigte Objekt erzeugen und vorbereiten. Dies geschieht in der Sub CreateCardForm, der wir die 'zweckzugentfremdende' Form und die benötigten Maße übergeben. Breite und Höhe einer Karte werden uns übrigens beim Initialisieren der cards.dll über cdtInit mitgeteilt.

Public Sub CreateCardForm(ByRef fo As Form, ByVal b As Long, _
                          ByVal h As Long)

'Karte beweglich machen -> Form erzeugen
  Dim rgnCards As Long 'RegionHandle
  '---- Region erstellen
  GetCardShape b, h
  rgnCards = CreatePolygonRgn(cs(0), 8, POLY_WINDING)
  '---- Kartenform erzeugen
  With frmCard
    '---- Größe
    .Width = b * Screen.TwipsPerPixelX
    .Height = h * Screen.TwipsPerPixelY
    SetWindowRgn .hWnd, rgnCards, False
    DeleteObject rgnCards
  End With
End Sub

Um den Umriss der Form genau an eine Karte (die ja leicht abgerundete Ecken besitzt) anzupassen, erzeugen wir über die API-Funktion CreatePolygonRgn zuerst eine Region, also eine Art Polygon, das den exakten Umriss einer Karte beschreibt. Diese Region lässt sich dann mittels SetWindowRgn einem Objekt mit hWnd-Eigenschaft (also auch einer Form) 'aufprägen'. Die einzelnen Punkte (vom Typ POINT) des Karten-Polygons werden von der Sub GetCardShape gesetzt.

Public Sub GetCardShape(ByVal b As Long, ByVal h As Long)

'Füllt das POINT-Feld 'cs()' mit den
'Umriß-Punkten einer SpielKarte (b x h).

  '---- cs initialisieren

  ReDim cs(7)
  cs(0).x = 2: cs(0).y = 0
  cs(1).x = b - 2: cs(1).y = 0
  cs(2).x = b: cs(2).y = 2
  cs(3).x = b: cs(3).y = h - 3
  cs(4).x = b - 3: cs(4).y = h
  cs(5).x = 3: cs(5).y = h
  cs(6).x = 0: cs(6).y = h - 3
  cs(7).x = 0: cs(7).y = 2
End Sub

Nachdem frmCard hergerichtet wurde, können wir jetzt die Rückseite aufmalen und die Form später mit dem Setzen ihrer Visible-Eigenschaft verwenden und wieder verschwinden lassen.

'verdeckte Karte zeichnen
  cdtDraw frmCard.hDC, 0, 0, _
          CARD_BACK_Kariert, CARD_DRAW_BACK, CARDCOLOR

Die Routinen zum Bewegen der frmCard sind komplett in der Sub des Timers (timMain) untergebracht. In der Variablen ActionMode wird die aktuelle Art der Aktion festgehalten, denn es gibt ja drei verschiedene Animationen:

ACTION_START: Austeilen von 4 Karten am Anfang einer Runde.
ACTION_GIVE: Dem Spieler eine einzelne Karte zuteilen.
ACTION_TAKE: Bank nimmt sich selbst eine einzelne Karte.

Bei ACTION_START müssen außerdem 4 Kartenzuteilungen unterschieden werden (ActionStartMode = 0...3). Dies alles wird im Code der Sub timMain_Timer() von SELECT-Anweisungen geregelt.

Sobald der letzte Schritt einer Animationssequenz getan ist, schaltet sich der Timer selbst wieder aus (bzw. er setzt seine Enabled-Eigenschaft nicht wieder auf TRUE). Damit der Timer nun weiß, wann eine Animationsphase vorüber ist, errechnen wir zuvor aus der Start- und der Zielposition der Karte die Anzahl der benötigten Schritte und speichern diese in der Variablen CardSteps ab. Da der Timer die Schritte in der Variablen ActCardStep mitzählt, kann er immer in einem Vergleich herausfinden, ob noch etwas zu tun ist.

Während dem Austeilen der 4 Anfangskarten schaltet der Timer nach Beendigung jeder Einzelsequenz den ActionStartMode um eins weiter in den nächsten Modus (auf die nächste Karte), bis alle Bewegungen erledigt sind.

Nach dem Ablegen einer Karte muss das Label für die Punktzahl geupdatet werden; außerdem muss eine Prüfung erfolgen, ob der Kartenempfänger jetzt ein besonderes Blatt besitzt (z.B. 21 Punkte, mehr als 21 Punkte, usw.). Dieses erfolgt in den Funktionen CheckBankWin bzw. CheckPlayerWin, die auch per MessageBox Erfolg oder Verlust melden und bei Bedarf dem Gewinner den Inhalt des Pots zuteilen (inkl. Update des Kontostandes).

 
  Chips und Jetons voriges Thema [ Top ] nächstes Thema

Chips:

Damit die ganze Sache etwas spannender wird, wollen wir - wie in einem Casino üblich - Chips als Spieleinsatz verwenden. Hierzu habe ich mit Paint ein paar einfache 16-Farben-Bitmaps entworfen, die die Werte von 1 bis 5000 abdecken. Neben der normalen Darstellung (32 x 32 Pixel) brauchen wir jeweils noch eine liegende Variante (32 x 18 Pixel), um auch das Stapeln optisch ansprechend realisieren zu können.

1 2 5 10 20 50
100 200 500 1000 2000 5000

Jetons:

Obwohl wir in 17-und-4 nur Chips verwenden, möchte ich der Vollständigkeit halber hier noch Jetons (48 x 32 Pixel, liegend: 48 x 18 Pixel) vorstellen, die man in ähnlichen Spielen, bei denen es um größere Einsätze geht (Poker, Börse, Roulette oder Monopoly), benutzen kann. Sie decken den Bereich 10000 bis 500 Mio. ab, und du kannst sie gerne in eigenen Projekten verwenden ;)

10000

20000 50000 100000 200000

500000

1 Mio. 2 Mio. 5 Mio. 10 Mio.

20 Mio.

50 Mio. 100 Mio. 200 Mio. 500 Mio.

Verwaltung:

Alle Eigenschaften und Funktionen, die mit den Spielchips zu tun haben, werden in der Klasse Chips gekapselt.

Nach der Deklaration eines Chips-Objektes muss zuerst die Sub Init aufgerufen werden. Sie benötigt als Parameter die Ziel-Form (auf der die Chips dargestellt werden sollen), eine ImageList (welche die Chip-Bitmaps enthält), eine Pixel-Position (Bereich, in dem die Darstellung erfolgen soll), die Anzahl der Spalten und Reihen (der gestapelten Chips).

Nun können mit der Sub Add Chips aufgenommen werden. Als Parameter wird der Wert des neuen Chips verlangt - also eine der Zahlen 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000 und 5000.

Die aktuelle Anzahl der Chips lässt sich mit der Property Count ermitteln,

den Gesamtwert der Chips kann man mit der Property Sum abfragen,

den Wert eines bestimmten Chips liefert die Function Value, der man als Parameter dessen Nummer übergibt, also eine Zahl zwischen 1 und N, wobei N die Anzahl der (in diesem Bereich) vorhandenen Chips ist.

Um Chips zu entfernen, wird die Sub Delete verwendet Ihr übergibt man ebenfalls die Nummer (1...N) des zu löschenden Chips.

Die grafische Ausgabe erfolgt über die Sub Show. Sie baut den kompletten Bereich mit allen Stapeln auf (von links nach rechts). Falls der Bereich zu klein für alle Chips ist, werden nur so viele dargestellt, wie in den Bereich passen.

Will man herausfinden, ob sich der Benutzer mit dem Mauszeiger über einem bestimmten Chip befindet, kann man sich von der Function HitChip, die ein Paar Pixel-Koordinaten verlangt, die Nummer (1...N) des Chips liefern lassen (bzw. 0, falls sich an der angegebenen Position kein Chip befindet).

Möchte man sich einen Chip einwechseln lassen (z.B. einen 200er in zwei 100er), dann kann man die Sub Split dazu bemühen. Sie benötigt als Parameter die Nummer (1...N) des Chips. Durch den Wechselvorgang neu entstehende Chips werden auf den letzten Stapel gelegt. Am Beispiel der 100er wird das Wechseln nach folgendem Prinzip durchgeführt:
  100 -> 50 + 20 + 20 + 10
  200 -> 100 + 100
  500 -> 200 + 200 + 100

Auch der umgekehrte Vorgang, das Zusammenfassen von Chips, ist möglich. Dies erledigt die Sub Merge. Sie wechselt alle Chips in größtmögliche Beträge ein und sortiert den entstehenden Stapel gleich noch, wobei die höherwertigen Chips zuunterst liegen.

Falls man die Nummer eines Chips mit einem bestimmten Wert benötigt, kann man die Function ValueChip verwenden. Ihr übergibt man als Parameter den Wert des gesuchten Chips und bekommt dann seine Nummer (1...N) zurückgeliefert. Steht kein Chip dieses Wertes zur Verfügung, werden automatisch höherwertige Chips solange eingewechselt, bis der verlangte Chip vorhanden ist.

 

Mit der Sub Clear lässt sich schließlich noch der Chip-Bereich (optisch) löschen.

Darstellung:

Da die Bitmaps der Chips in ImageLists untergebracht sind, ist es ein Einfaches, sie mithilfe der Draw-Methode der ImageList auszugeben. Diese Methode benötigt als Parameter einen hDC (DeviceContext-Handle), die Zielkoordinaten (in Pixeln) und ein Flag, mit dem man z.B. die transparente Ausgabe erzwingen kann.

Public Sub Show()
'alle ChipStapel aufbauen
  Dim col As Long, row As Long, i As Long
  Dim x As Long, y As Long

  Clear
  If (MyChips = 0) Then Exit Sub
  i = 1
  col = 0
  x = MyX
  Do
    row = 0
    y = MyY + MyHeight - CHIPHEIGHT
    Do
      MyIml.ListImages("c" & MyChip(i).Value).Draw _
        MyForm.hDC, x, y, imlTransparent
      i = i + 1
      If (i > MyChips) Then Exit Sub
      row = row + 1
      y = y - CHIPOFFSET
    Loop Until (row = MyRows)
    col = col + 1
    x = x + CHIPWIDTH
  Loop Until (col = MyCols)
End Sub

Auch beim In-die-Hand-nehmen eines Chips wird im MouseDown-Event der frmMain diese Methode verwendet, um einen Chip auf die frmChip zu zeichnen.

In der ImageList werden die einzelnen Bitmaps über ihre Key-Eigenschaft angesprochen, die sich wie folgt zusammensetzt: "c" & <Wert des Chips> (z.B. 'c50', um den 50er zu holen).
 

Animation:

Sobald der Spieler mit der Maus auf einen seiner Chips klickt, wird dieser aus dem Stapel entfernt und (in seiner Draufsicht) auf eine vorbereitete Form gezeichnet, die eine kreisrunde Fläche in der Größe eines Chips hat. Auch diese frmChip wird bereits bei Programmstart mithilfe der Sub CreateChipForm vorbereitet. Dazu wird, genau wie bei der Sub CreateCardForm (s.o.) die API verwendet, um mit dem Zuweisen einer Region den Umriss zu beeinflussen.

Der MousePointer wird gleichzeitig in eine offene Hand verwandelt, damit der Eindruck entsteht, man würde den Chip wirklich festhalten bzw. über den Tisch schieben.

MousePointer für das Halten eines Chips:  

Solange der Spieler die Maustaste gedrückt hält, werden alle Bewegungen im MouseDown-Event der frmMain registriert und an die frmChip weitergeleitet. Erst wenn man die Maustaste loslässt, wird im MouseDown-Event die frmChip wieder ausgeblendet und es erfolgt die Abfrage über den 'Landeplatz' des Chips. Hierbei gibt es dann drei Möglichkeiten:

1. Der Chip wurde in den Pot gelegt.  
Dies bedeutet, dass der Spieler einen Einsatz machen, bzw. diesen erhöhen will. Es ist zu prüfen, ob die Bank mithalten kann, denn sie muss jetzt ebenfalls diesen Betrag bringen. Hat die Bank zuwenig Geld auf ihrem Konto, erscheint eine entsprechende MessageBox ("Die Bank kann leider nicht mithalten!") und der Chip wandert zurück auf den Stapel des Spielers. Ansonsten fällt er in den Pot und die Bank legt ihren Chip dazu.

2. Der Chip wurde in den Chip-Bereich der Bank gelegt.
Falls der
Chip einen Wert größer 1 hat, wird er in kleinere Chips gewechselt, die auf dem Stapel des Spielers landen.

3. Der Chip wurde woanders abgelegt.
Das ist nicht sinnvoll, also wandert er zurück auf den Stapel des Spielers.
 

Einwechseln:

Zum 'Kleinmachen' eines Chips gibt es zwei Möglichkeiten: Zum einen kann man ihn in den Chip-Bereich der Bank fallen lassen, zum anderen kann man mittels Rechtsklick auf einen Spielerchip ein kleines PopUp-Menü erscheinen lassen, welches auch den Menüpunkt Wechseln enthält.

Zum Wechseln in große Chips kann man ebenfalls dieses PopUp-Menü aufrufen und den Punkt 'zusammenfassen' anwählen. Dies bewirkt die Umwandlung der Spielerchips in die größtmöglichen Werte; gleichzeitig werden die Chips sortiert (höherwertige zuunterst).

 
  Tipps zum Spiel voriges Thema [ Top ] nächstes Thema

Für den Anfang solltest du auf Einsätze verzichten und erst einmal Gefühl für die Karten entwickeln. So ist es zum Beispiel immer ein Risiko, sich mit 15 Punkten noch eine weitere Karte geben zu lassen. Um hier nicht über 21 zu kommen, braucht man ein Bild oder ein As (als 1 Punkt) - also eine Wahrscheinlichkeit von nur 1:2.

Es liegt in der Natur des unerfahrenen Spielers, manchmal selbst bei 18 Punkten noch eine Karte zu verlangen, in der Hoffnung, mit einer Dame auf 21 zu kommen. Solch eine Strategie ist natürlich höchst verwegen und man wird damit lange brauchen, die Bank zu sprengen.

Falls du ein As und ein Bild erhältst, also 13 bis 15 Punkte hast, stellt eine weitere Karte kein Risiko dar, denn selbst wenn du jetzt eine 10 bekommst, bist du noch nicht aus dem Rennen: Das As zählt dann als 1 Punkt und du hast demzufolge jetzt 14 bis 16 Punkte. Zugegeben, auch nicht das Gelbe vom Ei, aber du hättest ja auch ein weiteres Bild bekommen können, dann würdest du effektiv im Bereich um 8 liegen (wenn man das vorhandene As jetzt schon als 1 zählt), also eine gute Basis um mit einer weiteren hohen Karte knapp unter 20 zu kommen.

Hat man weniger als 20 Punkte, dann sollte man Zurückhaltung üben, wenn die offene Karte der Bank ein As ist. Hier lauert oft eine 9 oder gar eine 10 unter der verdeckten Karte.

Es ist auch keine Schande, einmal auf 14 oder 15 Punkten stehen zu bleiben; selbst wenn die Bank 16 Punkte erreicht, hat sie noch nicht gewonnen, denn sie muss ja mindestens 17 Punkte erreichen, um aufhören zu können. Und wie wir wissen, stehen die Chancen nicht sonderlich gut, wenn man mit 16 Punkten noch eine Karte nehmen muss. Optimal ist es, 20 Punkte zu haben, und die offene Karte der Bank ist eine mittlere Karte, am besten König oder 7. Bei dieser Konstellation kann man sich mit einem hohen Einsatz gut die Kasse füllen.

Ironischerweise ist es kein Vorteil, schon beim Austeilen 21 Punkte auf die Hand zu bekommen, denn zu diesem Zeitpunkte hat man wahrscheinlich einen sehr geringen oder noch gar keinen Einsatz gemacht.

Auf alle Fälle wünsche ich dir eine unterhaltsame Zeit, sowohl beim Studium des SourceCodes als auch beim Spielen selbst und hoffe, das Projekt kann dir für eigene Entwicklungen dienlich sein.
 Wenn dir 17-und-4 gefällt, insbesondere, wenn du es schaffst, die Bank zu sprengen, schick mir doch eine kurze Mail ;)

 
  Download des Projekts voriges Thema [ Top ]   

Zum Abschluss können Sie sich hier das fertige Projekt zu 17-und-4 herunterladen.

  Download
17und4.zip
 (37,4 kB)
Downloadzeit: <1 Min. - 28.8k / <1 Min. - ISDN Downloads bisher: [ 1977 ]

Startseite | VB/VBA-Tipps | Projekte | Tutorials | API-Referenz | Komponenten | Bücherecke | VB-/VBA-Forum | VB.Net-Forum | DirectX | DirectX-Forum | Foren-Archiv | VB.Net | Chat | Links | Suchen | Stichwortverzeichnis | Feedback | Impressum

Seite empfehlen Bug-Report

Letzte Aktualisierung: Montag, 28. April 2003