在C++中评估桥牌手

Evaluating Bridge Hands in C++

本文关键字:评估 C++      更新时间:2023-10-16

我正在开发一个程序,该程序应该读取一个文件(文件中的每一行代表一手 13 张牌(并评估每张桥牌。

最后我会问我的具体问题,但由于这个程序有很多内容,我将包括所有说明,以便您了解所需的内容。

以下是将要读入的文本文件:

2C QD TC AD 6C 3D TD 3H 5H 7H AS JH KH
3C 4C 2D AC QC 7S 7C TD 9C 4D KS 8D 6C
2C 3C KC JC 4C 8C 7C QC AC 5C 9C 6C TC
5H 3S 4D KC 9S 3D 4S 8H JC TC 8S 2S 4C
2S 5D 6S 8S 9D 3C 2H TH
2H 6D %S 8S 7S 4D 3H 4S KS QH JH 5C 9S
2C QD TC AD 6C 3D TD 3C 5H 7H AS JH KD QS
2C QD TC AD 6C 3D TD 2C 5D 7H AS JH KD
2H 6D TS 8Z 7S 4D 3H 4S KS QD JH 5C 9S

每对代表一张牌(价值和花色(。

法律价值包括:

2-9
T(10), A(Ace), K(King), Q(Queen), and J(Jack)

和西装:

C(Clubs), S(Spades), D(Diamonds), and H(Hearts)

一旦文件被读入,每手牌必须首先按花色排序,然后按花色内的等级排序(王牌很高(。排序完成后,必须使用以下规则评估每只手牌:

Aces = 4
Kings = 3
Queens = 2
Jacks = 1
voids (no cards in a suit) = 3
singletons (one card in a suit) = 2
doubletons (two cards in a suit) = 1
long suits (more than 5 cards in a suit) = 1 count for each card over 5 in number

评估后,每只手应按以下格式显示:

Example Input:
2C QD TC AD 6C 3D TD 3H 5H 7H AS JH KH
Example Output:
Clubs    10  6  2
Diamonds A   Q  10  3
Hearts   K   J  7   5  3
Spades   A
Points = 16

以下是有关该计划必须包括的内容的一些细节:

1. A data structure to hold cards in an ordered manner.
2. A function to read in the hand.
3. A function to evaluate the hand (with support functions).
4. A function to display the hand.

这是我能够想出的小代码。如果不清楚,注释是我认为需要完成的步骤,以使程序正常工作。现在它所做的只是打开文件,是的,我将删除"文件已打开"消息,我只是想确保文件确实处于打开状态。

//#include <program3.h>
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main() {
    //Create Array
    //char bridgeHands[];
    //Open file, if it doesn't exist, exit the program
    ifstream bridgeFile;
    bridgeFile.open("prog3.dat");
    if(!bridgeFile) {
        cerr << "Open Failure" << endl;
        exit(1);
    }
    else {
        //Read file into array
        //Sort array
        //Evaluate hands
        //Display hands
        cout << "File is open" << endl;
    }
    return 0;
}

我想我现在的具体问题是这个。我需要如何创建和加载数组?我从未使用过从成对的输入加载数组。另外,这如何与数据结构一起使用?

我相信你现在已经知道我在这方面非常陌生,并且正在学习(几乎所有我知道如何在C++中做的事情都是用该代码编写的(,所以任何帮助都非常感谢。谢谢你的时间。

我脑海中有很多悬而未决的问题。 首先和最重要的是:每只手应该在单独的线上,还是只是接下来的 13 张牌,不考虑换行符。 这将更改你读手的方式。 无论哪种情况,您的输入文件都有错误必须检测:在第一种情况下,第五种和第七种行的格式不正确,在第二个行中,数量卡片不是 13 的倍数,因此必须存在错误地方。

无论如何,解决这个问题的正确方法是定义类型(类(为牌和手牌,并定义用户定义 每个operator>>,手operator>>使用一个用于卡片。 例如:

std::istream& operator>>( std::istream& source, Card& object)
{
    char value;
    char suit;
    source >> std::ws;  //  skip leading whitespace.
    source.get(value);  //  these do _not_ skip whitespace
    source.get(suit);
    if ( source ) {     //  no errors on input...
        //  Map the characters to the internal representation and write
        //  them into `object`.  This operator may have to be a friend
        //  to do this.
        //
        //  If one of the characters isn't legal:
        //      source.setstate( std::ios_base::failbit );
    }
    return source;
}

对于手,如果您的输入是面向线的,则最佳解决方案大概是用std::getline来读行,那么 std::istringstream解析它。 确保检查一旦你读完了13卡。 (如果输入忽略行尾,则只需读取 13卡应该足够了。 无论策略如何,都要确保每次读取后检查错误,然后再使用您已阅读的值。 所以你是循环(要么在 std::istringstream或原始来源,取决于(可能看起来像这样:

int i = 0;
while ( i != 13 && source >> dest[i] ) {
    ++ i;
}
if ( i == 13 ) {
    //  Input of 13 cards succeeded...
}

最后:你的输入包含错误(可能是故意的,以确保正确测试它们(。 这意味着最简单的外部循环形式将无法正常工作(因为它将在第一个错误处停止(。 如果我们假设直线定向全局输入,但手的>>运算符忽略行结束,只寻找下一张 13 张牌:

std::string line;
int lineNumber = 0;
while ( std::getline( source, line ) ) {
    ++ lineNumber;
    std::istringstream parser( line );
    if ( parser >> hand >> std::ws && parser.get() == EOF) {
        //  Line is good, hand contains the instance to be evaluated
    } else {
        std::cerr << "Input format error in line " << lineNumber << std::endl;
    }
}

if中确认条件:我们读一手,然后跳过空白;如果成功,我们将验证我们已达到文件末尾(否则,在文件末尾有额外的垃圾行(。 您可以给出更详细的错误消息,如果您将这些不同的操作分开,尽管要表明哪张卡有误,您必须输入 13 张卡直接在此级别,而不是将>>用于Hand

另一个建议:我会选择一个内部代表这使得处理变得简单,具有用于输入的映射功能和输出。 这可能是两个枚举:一个用于值(使用按排名顺序排列的值(,一个用于花色(也按其排名顺序(。 这将使排序并且计数明显更容易,映射功能是非常简单:对于这样的小集合,只不过是一个数组与法律代表:输入,线性搜索,以及在输出上,只是索引。 (请注意,这可能会导致值 2在内部0数值,因为它将是枚举的第一个值。

使用 ifstream::getline() 函数读取和分析文件。在此链接中,您还可以找到一个很好的例子,如何将文件直接读取到std::array中:

#include <iostream>
#include <sstream>
#include <vector>
#include <array>
int main()
{
    std::istringstream input("abc|def|gh");
    std::vector<std::array<char, 4>> v;
    // note: the following loop terminates when std::ios_base::operator bool()
    // on the stream returned from getline() returns false
    for (std::array<char, 4> a; input.getline(&a[0], 4, '|'); ) {
        v.push_back(a);
    }
    for (auto& a : v) {
        std::cout << &a[0] << 'n';
    }
}

但请仔细查看这是否适合您的情况。作为替代方案,您可以省略getline的最后一个参数,以便真正逐行获取。然后,您必须使用 std::string::find()std::string::substr() 解析这些行。