在C++算法中检查四子连珠游戏的获胜者

4

我已经看了几个小时这段代码,卡在了算法的实现上。虽然不想把整个代码都贴出来,但为了让你更好地理解,我觉得有必要这样做。我完全不知道该如何检查胜者。我曾尝试用递归的方法来实现,但和一些人交流后得知这并不是最好的方式。我完全被难住了,不知道该怎么解决这个问题。

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include <iostream>

/**
 * @brief Check if a winner exists
 *
 * @param pBoard The board
 * @param colSize The column size
 * @param rowSize The row size
 *
 * @return The character of the winner, 0 for no winner, and 't' for a tie
 */
char checkWinner(char** pBoard, int colSize, int rowSize, int winSize) {
    // TODO

    **This is where the algorithm needs to go.**

    return 0;
}
/**
 * @brief Place a piece onto the board
 *
 * @param pBoard The game board
 * @param colSize The column size
 * @param rowSize the row size
 * @param columnSelection The column selection
 * @param player The players characterS
 *
 * @return True if the piece was placed, else false
 */
bool placePiece(char** pBoard, int colSize, int rowSize, int columnSelection, char player)
{
    // TODO
    int row = rowSize-1;

    while (pBoard[row][columnSelection]!= ' ' && row > 0){
        row--;
    }

    std::cout << row << std::endl;

    if (pBoard[row][columnSelection] == ' ') {
        pBoard[row][columnSelection] = player;
        return true;
    }   else{
        std::cout << "The space is full." << std::endl;
        return false;
    }

}

/**
 * @brief Print out the game board
 *
 * @param pBoard The game board
 * @param colSize The column size
 * @param rowSize The row size
 */
void printBoard(char** pBoard, int colSize, int rowSize) {

    for (int i = 0; i <= rowSize; ++i){
    std::cout << "|" << i;
    }

    std:: cout << "|" << std::endl;

    for (int i = 0; i < rowSize; ++i){
        std::cout << "|";
        for (int j = 0; j < colSize; ++j){
            std::cout << pBoard[i][j] << "|";
        }
        std::cout << std::endl;
    }
    std::cout << std::endl;
}

int main()
{
    bool running = true;
    printf("Welcome to connect four!\n");
    srand (time(NULL));

    int32_t connectedPiecesToWin = 0;
    int32_t rowSize = 0;
    int32_t colSize = 0;

    // setup game
    std::cout << "How many connected pieces does it take to win?" << std::endl;
    std::cin >> connectedPiecesToWin;

    rowSize = connectedPiecesToWin + 2;
    colSize = connectedPiecesToWin + 3;

    std::cout << "You have selected " << connectedPiecesToWin << " in a row with a game board of " << colSize
<< " x " << rowSize << std::endl;

    // setup board
    char** pBoard = NULL; // TODO create the game board


    // initialize board

    pBoard = new char*[rowSize];

    for (int i = 0; i < rowSize; ++i){
        pBoard[i] = new char[colSize];
    }

    for(int i = 0; i < rowSize; ++i){
        for(int j = 0; j < colSize; ++j){
            pBoard[i][j]= ' ';
        }
    }


    // play
    char winner = 0;
    char player = 'p';
    do
    {
        int columnChoice = 0;

        do
        {
            if (player == 'p')
            {
                printBoard(pBoard, colSize, rowSize);
                std::cout << "Player's column: ";
                std::cin >> columnChoice;
            }
            else
            {
                // computers turn
                columnChoice = rand() % colSize;
            }
        } while (!placePiece(pBoard, colSize, rowSize, columnChoice, player));

        winner = checkWinner(pBoard, colSize, rowSize, connectedPiecesToWin);
        player = (player == 'c') ? 'p' : 'c';
    } while (running && winner == 0);

    printBoard(pBoard, colSize, rowSize);

    if (winner == 't')
    {
        std::cout << "Too bad, the game was a tie!" << std::endl;
    }
    else if (winner == 'c')
    {
        std::cout << "Oh man, you lost to a computer that randomly places pieces!" << std::endl;
    }
    else
    {
        std::cout << "Congrats! You won!" << std::endl;
    }

    // cleanup
    // TODO cleanup the board

    for (int i = 0; i < rowSize; ++i){
        delete[] pBoard[i];
    }
    delete[] pBoard;

    return 0;
}

new char*[rowSize] - 避免使用 new,不要使用数组 new。改用 vector。话虽如此,你肯定可以缩小示例,从而停止成为“那个人”,例如根本不需要任何输入或输出。只需设置一个硬编码的棋盘,然后是检查获胜者的代码。 - Ulrich Eckhardt
@UlrichEckhardt 我本来很想这样做的,我应该提到,是的,这是一项家庭作业,不,我没有写75%的代码。如果有选择的话,我会以布尔函数的形式编写整个程序,并将checkWinner作为一个布尔函数而不是字符函数。 - Sailanarmo
那么,为了满足你的作业要求,你故意违反了这里的网站规则吗?还是因为懒惰没有提取最小化的示例?实际上,这些规则有其原因,尤其是它们帮助你专注于问题,而不会让其他杂七杂八的东西分散你和其他人的注意力。 - Ulrich Eckhardt
@UlrichEckhardt,如果我冒犯了您,请接受我的道歉。但是我并不想让任何人替我完成作业,我只需要一些关于如何最好地实现我整晚都在思考的算法的想法。我已经花了大约8个小时来研究这个算法。当时已经很晚了,在求助这里之前,我已经使用了其他可用的资源。而且所有的回复都比我实际的导师更有帮助。所以我为寻求帮助而道歉。我不会放弃这个任务,因为它已经提交了,我想成为一个更好的程序员,因此寻求帮助。我仍然想完成这个任务。 - Sailanarmo
2个回答

3

暴力的方法是测试每个单元格,看它是否与任何有效方向中的connectedPiecesToWin个单元格相连,因此首先编写一个程序,在以下情况下返回true,即0,0处的单元格为获胜单元格:

  • 选择要搜索的偏移量,例如-1,-1以对角线搜索
  • 对于该偏移量:
    • 检查目标单元格偏移量处的单元格是否具有相同的颜色。
    • 如果是,则增加计数器并在偏移单元格的位置再次执行相同的测试(这里涉及一点递归)
    • 当您撞墙或相同颜色单元格的数量大于或等于connectedPiecesToWin时停止。\\编辑
  • 选择下一个偏移量(-1,0)、(0,-1)、(1,1)等
  • 如果您搜索所有八个方向并且没有获胜,则返回false。

然后该程序可以用于搜索整个棋盘寻找获胜单元格,或仅检查刚刚进行的移动是否创建了获胜状态。

还是我漏了什么?


不,你没有错过任何东西,那是完全正确的。我只是不知道如何执行它而已。我提交了作业,因为我已经完成了90%的工作。只是这个关键部分我还缺少了。 - Sailanarmo

2

既然这显然是作业,那么我将给你一个天真的策略,而不提供实际代码。

对于任何位置(x,y),如果有四个相同颜色的棋子水平、垂直或对角线排列,则存在获胜者。因此,让我们看一下红色在水平方向上获胜的情况:

(x,y)    (x+1,y)  (x+2,y)  (x+3,y)
RED      RED      RED      RED

类似的坐标计算适用于其他排列方式。因此,您只需要从数组中的每个位置开始查找,从 (0,0)(width-4,height-4)(除了对角线情况之一,该情况使用不同的范围),并在该位置开始执行“胜利测试”。
更智能的“胜利测试”将只检查最近下的棋子周围的区域。

好的,如果我理解正确的话。如果我要垂直移动,那么我会按照 (x,y) (x, y+1) (x, y+2) (x, y+3) 的顺序进行移动,对吗? - Sailanarmo
我显然必须找到另一种对角线检查的方法,但我想我的问题是,如果我没有被给予选择权,我该如何传递玩家? 我觉得我必须做两次同样的事情,一次为玩家,一次为计算机。 - Sailanarmo
我认为你的函数应该返回哪个玩家获胜(如果有)。在doxygen注释中,返回值的含义已经写得很清楚了。然而,我不太明白在一个有效的游戏中怎么可能会出现平局。 - paddy
@paddy,我理解了你告诉我的内容。然而,我找不到执行它的方法。既然作业要在午夜前完成,我只好按原样提交了。你能解释一下如何根据最近播放的棋子来确定位置吗?当我在学校的辅导实验室时,他们告诉我要从(0,0)开始,也就是左上角,但在我的想法中,从底部任意空间开始更有意义。 - Sailanarmo
无论你从哪里开始都不重要。你仍然需要检查整个棋盘。根据其函数签名,该函数显然并不打算进行每一步的局部胜利测试。从底部开始的唯一优势是在赢得比赛的情况下更有可能提前退出函数。当然,如果特定元素未被填充,你可以避免进行测试,因此这种“优化”并不特别相关。对于像这样的程序,你应该更加注重代码的清晰而不是速度。 - paddy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接