首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++实践项目: Blackjack

C++实践项目: Blackjack
EN

Code Review用户
提问于 2019-06-07 20:18:17
回答 1查看 3.1K关注 0票数 5

在很长一段时间里,我总是觉得设计一个健壮的面向对象程序(尤其是在C++中)是错误的。我觉得我不懂足够的理论或算法,所以我不会学习,也不会写代码,更不用说我总是害怕批评或者看起来很业余。最近,我意识到,通过这样做和超越我的不安全感,我一直在克制自己。我通过编写普通的C++程序来建立我对语言和架构的知识,取得了更大的进步。每个人都告诉我,写一款Blackjack游戏将帮助我为更有雄心的项目建立纯粹的知识,并为我难以理解的开源项目做出贡献。

博士:我一直在创建一个Blackjack程序,以帮助我在更高级的水平上学习编程和设计。

我尝试使用以前从未使用过的东西,例如名称空间、枚举和带有操作符重载的映射。现在这是一项正在进行的工作:它只处理两个玩家,直到我让游戏正常工作,才开发出经销商逻辑,考虑为Blackjack规则创建一个派生类,以防我想要制定扑克规则。我已经知道这是个烂摊子,但我想这是学习过程的一部分,嗯?

main.cpp

代码语言:javascript
复制
#include <iostream>
#include <string>
#include "BlackJack.h"
#include "Player.h"

int main() {
    BlackJack game(2); // only two players for now, requires user for both
    game.play();
    return 0;
}

CardInfo.h

代码语言:javascript
复制
#pragma once
#include <string>
#include <unordered_map>

namespace Cards {
    enum class Rank {
        ONE = 1, TWO, THREE,
        FOUR, FIVE, SIX,
        SEVEN, EIGHT, NINE,
        TEN, JACK, QUEEN, KING, ACE
    }; 
    enum class Suit { 
        CLUBS, DIAMONDS, HEARTS, SPADES 
    };
};

namespace CardData {
    // TODO make a derived card class specifically for Blackjack rules
    struct Card {
        Cards::Rank rank;
        Cards::Suit suit;

        // TODO define this so the map works
        bool operator<(const CardData::Card &card) const {
            return this->rank < card.rank;
        }

    };

}

Player.h

代码语言:javascript
复制
#pragma once
#include <vector>
#include <string>

#include "CardInfo.h"

class Player {
    int points{ 0 };
    int numAces{ 0 }; // count aces in order to deduce 1 or 11 logic in updateScore
    int id;
    std::vector<CardData::Card> hand;

public:
    Player(int ID);
    void dealtCard(CardData::Card &drawnCard);
    void updateScore(int score); // call after every dealt card
    bool requestHit(); // request to exchange cards
    bool hasBust();
    int getScore();
    int getId();
};

Player.cpp

代码语言:javascript
复制
#include "Player.h"
#include <string>
#include <iostream>

Player::Player(int ID) : id(ID) {}

void Player::dealtCard(CardData::Card &drawnCard) {
    //Card drawnCard{ Cards::Rank::ACE, Cards::Suit::SPADES }; remove possibly
    hand.push_back(drawnCard); // test insert
}

void Player::updateScore(const int score) {
    if (score == 11)
        numAces++;

    points += score;

    if (points > 21)
        while (numAces > 0) {
            numAces--;
            points--;
        }
}

bool Player::requestHit() {
    char answer;
    std::cout << "Player " << id << ": Hit or stick? [y|n]" << std::endl;
    std::cin >> answer;
    return tolower(answer) == 'y';
}

bool Player::hasBust() {
    return getScore() > 21;
}

int Player::getScore() { return points; }
int Player::getId() { return id; }

BlackJack.h

代码语言:javascript
复制
#pragma once
#include <set>
#include <random>
#include "Player.h"
#include <string>

class BlackJack {
    int numPlayers;
    static std::unordered_map<Cards::Rank, int> score; 

    std::vector<Player> players;
    std::set<CardData::Card> inPlay; // drawn cards inserted here to prevent duplication 

    CardData::Card drawCard();
    void hitOrStick();
    void playHands();
    int enumerateCard(const CardData::Card &card) const; // converts cards to score, deals with Aces and Royals
    void deal(Player &player); 

public:
    BlackJack(int players);
    void play();
};

BlackJack.cpp

代码语言:javascript
复制
#include "BlackJack.h"
#include <iostream>
#include <string>

std::unordered_map<Cards::Rank, int> BlackJack::score{
    {Cards::Rank::ONE, 1},{Cards::Rank::TWO, 2},{Cards::Rank::THREE, 3},
    {Cards::Rank::FOUR, 4},{Cards::Rank::FIVE, 5},{Cards::Rank::SIX, 6},
    {Cards::Rank::SEVEN, 7},{Cards::Rank::EIGHT, 8},{Cards::Rank::NINE, 9},
    {Cards::Rank::TEN, 10},{Cards::Rank::JACK, 10},{Cards::Rank::QUEEN, 10},
    {Cards::Rank::KING, 10},{Cards::Rank::ACE, 11}
};

BlackJack::BlackJack(int players = 2) : numPlayers(players) {
    for (int i = 0; i < numPlayers; i++) {
        this->players.push_back(Player(i)); 
    }
}

void BlackJack::play() {
    // Shuffle workflow and distribute cards
    for (Player player : players) {
        std::cout << "Player \'" << player.getId() << "\' These are your cards: " << std::endl;
        for (int i = 0; i < 2; i++) {
            deal(player);
        }
    }

    hitOrStick();
    playHands();
}

void BlackJack::deal(Player &player) {
    CardData::Card drawn;
    drawn = drawCard();
    while (inPlay.find(drawn) != inPlay.end())
        drawn = drawCard();

    std::cout << score[drawn.rank] << std::endl;
    player.dealtCard(drawn);
    player.updateScore(enumerateCard(drawn));
    inPlay.insert(drawn);
}

CardData::Card BlackJack::drawCard() {

    std::uniform_int_distribution<> suitDistrib(0, 3);
    std::uniform_int_distribution<> rankDistrib(1, 14);
    std::random_device rd;
    std::mt19937 generator(rd());
    int newRank = rankDistrib(generator);
    int newSuit = suitDistrib(generator);

    return CardData::Card{ static_cast<Cards::Rank>(newRank), static_cast<Cards::Suit>(newSuit) };
}

int BlackJack::enumerateCard(const CardData::Card &card) const {
    return score[card.rank];
}

void BlackJack::hitOrStick() {
    for (Player player : players) 
        while (!player.hasBust() && player.requestHit()) 
            deal(player);
}

void BlackJack::playHands() {
    int winner = -1;
    int maxScore = -1;
    bool push = false;

    for (Player player : players) {
        std::cout << "Player " << player.getId() << ": " << player.getScore() << std::endl;
        if (player.getScore() > maxScore) {
            push = false;
            winner = player.getId();
            maxScore = player.getScore();
        } else if (maxScore == player.getScore()) {
            push = true;
        }
    }

    if (push)
        std::cout << "Push!" << std::endl;
    else if (winner == 0)
        std::cout << "House wins!" << std::endl;
    else
        std::cout << "Player " << winner << " wins!" << std::endl;
}

对于我需要改进什么或者设计/清理代码有什么建议吗?如果你也有任何建议,我应该如何关注我的经销商AI或operator<过载,我真的很感激。祝你每天都好起来!

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-06-10 08:28:28

最近,我意识到,通过这样做和超越我的不安全感,我一直在克制自己。我通过编写普通的C++程序来建立我对语言和架构的知识,取得了更大的进步。

边做边学是一种很好的方法。希望你能改进你的游戏,并再次提交审查!

主要需要Player.h吗?

2转换为一个命名常量:

代码语言:javascript
复制
int constexpr players{2};
BlackJack game{players}; 

这使得传递给你游戏的内容更加清晰。

虽然有些人真的不喜欢这样做,但如果不依赖return 0,则可以从main中省略它。编译器将为您生成它。

cardinfo

就我个人而言,我不介意#pragma once,只要记住它是不标准

主观性不要缩进namespace的后面。

主观性不要在枚举中每行写一个以上的语句。

如果要使用带有自定义键的无序容器,则需要提供散列和比较函数。看看这个问题和这个从偏好链接

播放器

类接口应该从最小限制转到最严格限制(即publicprotectedprivate)。原因是,当有人读取您的界面时,他很可能对可以使用的公共功能感兴趣。

主观性不要漏掉大括号,因为这样会导致错误。例如:

代码语言:javascript
复制
// bad
if (foo) 
    // code here

// good
if (foo)
{
    // code here
}

++foo(优先前缀)而不是后缀(foo++)运算符

更喜欢使用\n结束std::endl

我对21点并不熟悉,但是如果你问“击中还是坚持”,答案应该是“是/否”,而不是“打/棒”吗?

blackjack

看看这个问题,看看它是如何实现的。

您可以并且应该在可能的情况下使用大括号初始化。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/221881

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档