2

二十一点游戏的一般算法是什么?我正在用 C++ 编写一个,结果有太多的 if 语句破坏了整个事情。

该项目是一个win32 GUI应用程序,我发布了消息循环以及检查游戏状态的程序部分发布完整的代码将使它变得巨大,所以这里是所有文件的链接: 完整源代码

消息循环

LRESULT CALLBACK WndProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam ){
  switch(Msg)
  {
  case WM_PAINT:
  {
      PAINTSTRUCT PS;
      HDC hDC = BeginPaint(hWnd, &PS);
      You.Draw(hDC);
      Dealer.Draw(hDC);
      EndPaint(hWnd, &PS);
      if(Bet.Enabled){
          Bet.GetFocus();
          Bet.Select(0,Bet.Length());
      }
  }
  return 0;
  case WM_CTLCOLORSTATIC:
  {
      SetBkMode((HDC)wParam, TRANSPARENT);
  }
  return (INT_PTR)(HBRUSH)GetStockObject(NULL_BRUSH);
  case WM_CREATE:
  {
      //Create edit control
      Bet.Create(hWnd, 10, 550, 100, 25, "0");
      //Create labels
      char Buffer[30];
      sprintf(Buffer, "%d", You.HandValue());
      BetLabel.Create(hWnd, 10, 500, 100, 40, "Enter Bet Amount");
      GameControls.Create(hWnd, 10, 375, 100, 40, "Game Controls");
      PlayerLabels.Create(hWnd, 10, 10, 100, 20, "You:");
      PlayerLabels.Create(hWnd, 10, 150, 100, 20, "Dealer:");
      HandValueLabel.Create(hWnd, (int)You.x[You.cards] + 85, (int)You.y[You.cards] + 25, 100, 20, Buffer);
      YourMoney.Create(hWnd, 125, 500, 100, 40, "Your money: ");
      sprintf(Buffer, "%d", You.money);
      MoneyValue.Create(hWnd, 125, 525, 100, 40, Buffer);
      //Create buttons
      Ok.Create(hWnd, 10, 600, 100, 50, "Ok");
      Hit.Create(hWnd, 10, 425, 100, 50, "Hit");
      Stand.Create(hWnd, 120, 425, 100, 50, "Stand");
      //Select Text
      Bet.Select(0,3);
  }
  return 0;
  case WM_CLOSE:
    exit(0);
    break;
  case WM_COMMAND:
  {
      switch(HIWORD(wParam))
      {
      case BN_CLICKED:
      {
          switch(LOWORD(wParam))
          {
          case ID_OK:
          {
              //Place bet
              int bet = 0;
              bet = StringToNumber(Bet.Text());
              You.money -= bet;
              char Buffer[30];
              sprintf(Buffer, "%d", You.money);
              MoneyValue.SetText(Buffer);
              //Update the window
              InvalidateRect(hWnd, 0, TRUE);
              You.Bet = YES;
              Ok.Disable();
              Bet.Disable();
              You.DealCard();
              You.DealCard();
              Dealer.DealCard();
              Dealer.DealCard();
              You.win = false;
              You.bust = false;
              You.playing = YES;
              Dealer.win = NO;
              Dealer.bust = NO;
              Dealer.playing = YES;
          }
          break;
          case ID_HIT:
          {
             if(You.HandValue() < 21 && You.playing && !You.bust){
                 //Deal a card to the player, if he hasn't won or lost
                 You.DealCard();
             }
             if((Dealer.HandValue() <= 17 || Dealer.HandValue() < You.HandValue()) && Dealer.playing)
                       {
                         Dealer.DealCard();
                       }
            //Update Hand Value
            char Buffer[30];
            sprintf(Buffer, "%d", You.HandValue());
            HandValueLabel.SetText(Buffer);
            InvalidateRect(hWnd,0,TRUE);
            MoveWindow(HandValueLabel.Handle, (int)You.x[You.cards] + 80, (int)You.y[You.cards] + 25, 100, 20, TRUE);
            RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT);
          }
          break;
          case ID_STAND:
              if(Hit.Enabled){
                  //Don't deal more cards
                  Hit.Disable();
                  You.playing = false;
              }
              if((Dealer.HandValue() <= 17 || Dealer.HandValue() < You.HandValue()) && Dealer.playing){
                  //Deal a card to the dealer
                  Dealer.DealCard();
              }
          }

状态检查器

void ProcessStatus(HWND hWnd){
     if(Dealer.HandValue() > 17){
          if(Dealer.HandValue() > You.HandValue() && Dealer.playing){
              Dealer.playing = false;
            }
          else if(!You.playing && Dealer.HandValue() <= You.HandValue() && Dealer.playing){
              Dealer.DealCard();
            }
        }
      else if(Dealer.HandValue() <= 17 && Dealer.HandValue() <= You.HandValue() && !You.playing && Dealer.playing){
          Dealer.DealCard();
        }
      if(EvaluateStatus() != 0){
          status = OVER;
        }
     if(EvaluateStatus() == -1){
         switch(MessageBoxA(hWnd, "You Lost! \n Keep Playing ?", "You Lost!", MB_YESNO | MB_ICONQUESTION))
         {
         case IDYES:
             Reset();
             break;
         case IDNO:
             exit(0);
             break;
         }
     }
     else if(EvaluateStatus() == 1){
         switch(MessageBoxA(hWnd, "You Won! \n Keep Playing ?", "You Won!", MB_YESNO | MB_ICONQUESTION))
         {
         case IDYES:
             Reset();
             break;
         case IDNO:
             exit(0);
             break;
         }
     }
    }
int EvaluateStatus()
{
  if(You.HandValue() > 21 && You.playing)
    {
      You.bust = true;
      Dealer.win = true;
      You.playing = NO;
      return -1;
    }
  if(You.HandValue() == 21)
    {
      You.win = true;
      return 1;
    }
  if(Dealer.HandValue() > 21)
    {
      Dealer.bust = true;
      You.win = true;
      return 1;
    }
  if(Dealer.HandValue() == 21)
    {
      Dealer.win = true;
      return -1;
    }
  if(!You.playing && Dealer.HandValue() > You.HandValue() &&!Dealer.bust)
    {
      Dealer.win = true;
      return -1;
    }
  if(You.HandValue() > Dealer.HandValue() && (!You.playing && (!Dealer.playing || Dealer.bust)))
    {
      You.win = true;
      return 1;
    }
  if(You.HandValue() == Dealer.HandValue() && (!You.playing || !Dealer.playing))
    {
      You.win = true;
      return 1;
    }
4

3 回答 3

2

作为第一步:我可能不会混合游戏逻辑和您的 GUI 逻辑.. 将它们分成不同的模块。

如果你只有两个玩家,你只需要一个布尔值来决定哪一个赢了。您不需要玩家 Bust 和 Won 变量。

于 2011-09-02T09:43:09.310 回答
1

谷歌搜索显示了这篇关于 BlackJack 算法的PDF 论文。我敢肯定,如果您使用 Google 进行更多研究,您也可以找到一些东西。

要弄清楚如何计算出玩游戏所需的实际步骤,在纸上计算出游戏的不同步骤,然后决定如何创建代表游戏部分的类以及这些类中要执行的函数游戏的不同步骤。

这可能是一个迭代的事情,你通过它,然后创建一些类、函数等,现在你可以回去看看你所做的是否有意义,改进设计等。

也许还玩游戏,看看做了什么,如何将其转换为代码。

于 2011-09-02T09:44:13.177 回答
0

我已经修改了程序处理消息的方式,并将其作为单独的答案发布。由于轻微的打嗝,它仍然被打破,但我认为总体思路是可靠的。评论将不胜感激

卡片.h

/*
 * cards.h
 *
 *  Created on: Aug 31, 2011
 *      Author: Viraj
 */
#ifndef CARDS_H_
#define CARDS_H_
#include <iostream>
#include <algorithm>
#define STARTED true
#define OVER false
#define YES true
#define NO false
using namespace std;
HINSTANCE hApplication;
HBITMAP Bitmap;
char Buffer[30];
class Deck{
public:
    int deck[53];
    Deck(){
        for(int suit = 1; suit <= 4; suit++){
            for(int card = 1; card <= 13; card++){
                deck[((suit-1)*13)+card] = card;
            }
        }
        deck[0] = 0;
    }
};
Deck deck;
class Cards{
private:
    int card_seed;
    int suit_seed;
public:
    int card[11];
    int cards;
    int value[11];
    Cards(){
        fill(card, card + 10, 0);
        fill(value, value + 10, 0);
        cards = 0;
    }
    void DealCard()
    {
        cards++;
        while(!deck.deck[card[cards]]){
            card_seed = rand()%13 + 1;
            suit_seed = rand()%4 + 1;
            card[cards] = (suit_seed - 1)*13 + card_seed;
        }
        deck.deck[card[cards]] = 0;
        if(card_seed > 1 && card_seed < 11){
             value[cards] = card_seed;
        }
        else if(card_seed >= 11 && card_seed <= 13){
            value[cards] = 10;
        }
        else if(card_seed == 1){
            value[cards] = 11;
        }
    }
};
class Player : public Cards {
public:
    float x[11];
    float y[11];
    bool playing;
    bool bust;
    bool win;
    int money;
    Player(const char* Type){
        srand(time(NULL));
        playing = true;
        if(Type == "Dealer"){
            for(int card = 1; card <= 10; card++){
                x[card] = 10 + (card-1)*75;
                y[card] = 200;
            }
        }
        else{
            for(int card = 1; card <= 10; card++){
                x[card] = 10 + (card-1)*75;
                y[card] = 35;
                money = 1000;
            }
        }
    }
    void Reset(){
        cards = 0;
        fill(card, card + 10, 0);
        fill(value, value + 10, 0);
        playing = true;
    }
    int HandValue(){
        int sum = 0;
        for(int current_card = 1; current_card <= cards; current_card++){
            sum += value[current_card];
          }
        if(sum > 21){
            for(int current_card = 1; current_card <= cards; current_card++){
                if(value[current_card] == 11 && sum > 21){
                    value[current_card] = 1;
                    sum = HandValue();
                    break;
                  }
              }
          }
        return sum;
    }
    void Draw(HDC hDC){
        HDC MemoryDevice;
        for(int CardToLoad = 1; CardToLoad <= cards; CardToLoad++){
            Bitmap = LoadBitmap(hApplication, MAKEINTRESOURCE(card[CardToLoad]));
            MemoryDevice = CreateCompatibleDC(hDC);
            SelectObject(MemoryDevice, Bitmap);
            BitBlt(hDC, (int)x[CardToLoad], (int)y[CardToLoad], 75, 100, MemoryDevice, 0, 0, SRCCOPY);
        }
        DeleteDC(MemoryDevice);
    }
};
class CONTROL{
public:
    HWND Handle;
    char* ClassName;
    char* Caption;
    DWORD ClassStyle;
    DWORD ExtendedStyle;
    int x;
    int y;
    int Width;
    int Height;
    int ID;
    bool Enabled;
    void Create(HWND ParentHandle, int x, int y, int Width, int Height, char* Caption){
        Handle = CreateWindowExA(ExtendedStyle, ClassName, Caption, ClassStyle, x, y, Width, Height, ParentHandle, (HMENU)ID, NULL, NULL);
        Enabled = true;
        CONTROL::Caption = Caption;
        CONTROL::x = x;
        CONTROL::y = y;
        CONTROL::Width = Width;
        CONTROL::Height = Height;
    }
    void GetFocus(){
        SetFocus(Handle);
    }
    void Disable(){
        EnableWindow(Handle, FALSE);
        Enabled = false;
    }
    void Enable(){
        EnableWindow(Handle, TRUE);
        Enabled = true;
    }
    void SetText(char* String){
        SetWindowTextA(Handle, String);
    }
};
class BUTTON : public CONTROL {
public:
    BUTTON(int Identifier){
        ClassStyle = BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD;
        ID = Identifier;
        ClassName = "BUTTON";
        ExtendedStyle = 0;
    }
};
class EDIT : public CONTROL {
public:
    EDIT(int Identifier){
        ClassStyle = ES_NUMBER | WS_VISIBLE | ES_CENTER | WS_CHILD | ES_NOHIDESEL;
        ExtendedStyle = WS_EX_CLIENTEDGE;
        ID = Identifier;
        ClassName = "EDIT";
    }
    void Select(int first, int last){
        Edit_SetSel(Handle, first, last);
    }
    int Length(){
        return Edit_GetTextLength(Handle);
    }
    const char* Text(){
        char Buffer[Length() + 1];
        Edit_GetText(Handle, (LPTSTR)&Buffer, Length() + 1);
        string String = Buffer;
        return String.c_str();
    }
};
class STATIC : public CONTROL {
public:
    STATIC() {
        ClassStyle = WS_VISIBLE | SS_CENTER | WS_CHILD;
        ID = 0;
        ClassName = "STATIC";
        ExtendedStyle = 0;
    }
};
Player You("You"), Dealer("Dealer");
BUTTON Ok(ID_OK), Hit(ID_HIT), Stand(ID_STAND);
EDIT Bet(ID_BET);
STATIC BetLabel, GameControls, PlayerLabels, HandValueLabel, YourMoney, MoneyValue, Bust;
class Game{
public:
    bool started;
    Game(){
        started = false;
    }
    void Stop(){
        started = false;
    }
    void ResetGame(HWND hWnd){
        //Reset Players
        You.Reset();
        Dealer.Reset();
        //Reset Controls
        Ok.Enable();
        Bet.Enable();
        Hit.Enable();
        //Reset Hand value
        sprintf(Buffer, "%d", You.HandValue());
        HandValueLabel.SetText(Buffer);
        //Reset deck
        deck = Deck();
        //Stop the game
        started = false;
        //Update the Display
        InvalidateRect(hWnd, 0, TRUE);
        RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT);
    }
    int SendMessage(HWND hWnd, int ButtonID, unsigned int MESSAGE, int Information, char* _Information){
        return ProcessMessage(hWnd, ButtonID, MESSAGE, Information, _Information);
    }
    int ProcessMessage(HWND hWnd, int ButtonID, unsigned int MESSAGE, int Information, char* _Information){
        switch(MESSAGE)
        {
        case START_GAME:
            //Place Bet
            You.money -= Information;
            started = YES;
            sprintf(Buffer, "%d", You.money);
            //Update Controls
            MoneyValue.SetText(Buffer);
            Ok.Disable();
            Bet.Disable();
            //Deal two cards to player and dealer
            You.DealCard();
            You.DealCard();
            Dealer.DealCard();
            Dealer.DealCard();
            //Update the Hand Value Label
            sprintf(Buffer, "%d", You.HandValue());
            HandValueLabel.SetText(Buffer);
            //Move the label
            MoveWindow(HandValueLabel.Handle, (int)You.x[You.cards] + 80, (int)You.y[You.cards] + 25, 100, 20, TRUE);
            //Send PAINT message
            InvalidateRect(hWnd,0, TRUE);
            RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT);
            //Check for BlackJacks
            if(You.HandValue() == 21){
                SendMessage(hWnd, 0, INTERNAL, YOU_WIN, "");
            }
            else if(Dealer.HandValue() == 21){
                SendMessage(hWnd, 0, INTERNAL, DEALER_WIN, "");
            }
            break;
        case STAND:
            if(started){
                Hit.Disable();
                You.playing = false;
            }
            break;
        case INTERNAL:
            switch(Information)
            {
            case DEALER_WIN:
                return MessageBoxA(hWnd, "You Loose! \n Play Again ?", "Game Over", MB_YESNO | MB_ICONINFORMATION);
            case YOU_WIN:
                return MessageBoxA(hWnd, "You Win! \n Keep Playing ?", "Game Over", MB_YESNO | MB_ICONINFORMATION);
            }
        break;
        case IDLE:
            if(started){
                //Check for BlackJacks
                if(Dealer.HandValue() == 21){
                    started = false;
                    switch(SendMessage(hWnd, 0, INTERNAL, DEALER_WIN, ""))
                    {
                    case IDYES:
                        ResetGame(hWnd);
                        return 0;
                    case IDNO:
                        exit(0);
                        return 0;
                    }
                }
                if(!You.playing && Dealer.playing && Dealer.HandValue() < You.HandValue())
                    Dealer.DealCard();
                    //Update the Window
                    InvalidateRect(hWnd,0, TRUE);
                    RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT);
                    //Check for a Bust
                    if(Dealer.HandValue() > 21){
                        switch(SendMessage(hWnd, 0, INTERNAL, YOU_WIN, ""))
                        {
                        case IDYES:
                            ResetGame(hWnd);
                            return 0;
                        case IDNO:
                            exit(0);
                            return 0;
                        }
                    }
                    if(Dealer.HandValue() == 21){
                        switch(SendMessage(hWnd, 0, INTERNAL, YOU_WIN, ""))
                        {
                        case IDYES:
                            ResetGame(hWnd);
                            return 0;
                        case IDNO:
                            exit(0);
                            return 0;
                        }
                    }

                }
        break;
        case HIT:
            //Check to see who's still playing and deal a card to them
            if(started){
            if(You.playing){
                You.DealCard();
                //Update the Hand Value Label
                sprintf(Buffer, "%d", You.HandValue());
                HandValueLabel.SetText(Buffer);
                //Move the label and update the screen
                MoveWindow(HandValueLabel.Handle, (int)You.x[You.cards] + 80, (int)You.y[You.cards] + 25, 100, 20, TRUE);
                InvalidateRect(hWnd, 0, TRUE);
                RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT);
                //Check for a BlackJack
                if(You.HandValue() == 21){
                    started = false;
                    switch(SendMessage(hWnd, 0, INTERNAL, YOU_WIN, ""))
                    {
                    case IDYES:
                        ResetGame(hWnd);
                        return 0;
                    case IDNO:
                        exit(0);
                        return 0;
                    }
                }
                //Check for a bust
                if(You.HandValue() > 21){
                    started = false;
                    switch(SendMessage(hWnd, 0, INTERNAL, YOU_WIN, ""))
                    {
                    case IDYES:
                        ResetGame(hWnd);
                        return 0;
                    case IDNO:
                        exit(0);
                        return 0;
                    }
                }
            }
            if(Dealer.playing){
                Dealer.DealCard();
                InvalidateRect(hWnd, 0, TRUE);
                RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT);
                //Check for BlackJack
                if(Dealer.HandValue() == 21){
                    started = false;
                    switch(SendMessage(hWnd, 0, INTERNAL, DEALER_WIN, ""))
                    {
                    case IDYES:
                        ResetGame(hWnd);
                        return 0;
                    case IDNO:
                        exit(0);
                        return 0;
                    }
                }
                //Check for Bust
                if(Dealer.HandValue() > 21){
                    started = false;
                    switch(SendMessage(hWnd, 0, INTERNAL, YOU_WIN, ""))
                    {
                    case IDYES:
                        ResetGame(hWnd);
                        return 0;
                    case IDNO:
                        exit(0);
                        return 0;
                    }

                }
            }
            }
            break;
        }
        return 0;
    }
};
Game game;
#endif

消息循环

 switch(Msg)
  {
  case WM_PAINT:
  {
      PAINTSTRUCT PS;
      HDC hDC = BeginPaint(hWnd, &PS);
      You.Draw(hDC);
      Dealer.Draw(hDC);
      EndPaint(hWnd, &PS);
      if(Bet.Enabled){
          Bet.GetFocus();
          Bet.Select(0,Bet.Length());
      }
  }
  return 0;
  case WM_CTLCOLORSTATIC:
  {
      SetBkMode((HDC)wParam, TRANSPARENT);
  }
  return (INT_PTR)(HBRUSH)GetStockObject(NULL_BRUSH);
  case WM_CREATE:
  {
      //Create edit control
      Bet.Create(hWnd, 10, 550, 100, 25, "0");
      //Create labels
      char Buffer[30];
      sprintf(Buffer, "%d", You.HandValue());
      BetLabel.Create(hWnd, 10, 500, 100, 40, "Enter Bet Amount");
      GameControls.Create(hWnd, 10, 375, 100, 40, "Game Controls");
      PlayerLabels.Create(hWnd, 10, 10, 100, 20, "You:");
      PlayerLabels.Create(hWnd, 10, 175, 100, 20, "Dealer:");
      HandValueLabel.Create(hWnd, (int)You.x[You.cards] + 85, 60, 100, 20, Buffer);
      YourMoney.Create(hWnd, 125, 500, 100, 40, "Your money: ");
      sprintf(Buffer, "%d", You.money);
      MoneyValue.Create(hWnd, 125, 525, 100, 40, Buffer);
      //Create buttons
      Ok.Create(hWnd, 10, 600, 100, 50, "Ok");
      Hit.Create(hWnd, 10, 425, 100, 50, "Hit");
      Stand.Create(hWnd, 120, 425, 100, 50, "Stand");
      //Select Text
      Bet.Select(0,3);
  }
  return 0;
  case WM_CLOSE:
    exit(0);
    break;
  case WM_COMMAND:
  {
      switch(HIWORD(wParam))
      {
      case BN_CLICKED:
      {
          switch(LOWORD(wParam))
          {
          case ID_OK:
          {
              //Place bet
              int bet = 0;
              bet = StringToNumber(Bet.Text());
              game.SendMessage(hWnd, ID_OK, START_GAME, bet, "");
          }
          break;
          case ID_HIT:
          {
              //Tell the game that the player wants another card
              game.SendMessage(hWnd, ID_HIT, HIT, 0, "");
          }
          break;
          case ID_STAND:
              game.SendMessage(hWnd, ID_STAND, STAND, 0, "");
          }
      }
      break;
      }
    }
  break;
    default:
    //Default:
    game.SendMessage(hWnd, 0, IDLE, 0, "");
    return DefWindowProcA(hWnd, Msg, wParam, lParam);
    }
  return 0;
}
于 2011-09-03T16:31:33.420 回答