C语言实现三子棋

Suzhou
• 阅读 147

game.h:

#define ROW 3  //行
#define COL 3  //列
//在头文件中定义行和列,日后扩展使用可以直接更改头文件中的ROW和COL的值

#include <stdio.h>  //game.c文件中DisplayBoard函数内使用了printf的函数,需要包含stdio.h的头文件
#include <stdlib.h>  //test.c文件中test函数内使用了srand()函数,需要包含stdlib.h的头文件
#include <time.h>  //test.c文件中test函数内使用了time()函数,需要包含time.h的头文件

void InitBoard(char board[ROW][COL], int row, int col);  //函数InitBoard的声明
void DisplayBoard(char board[ROW][COL], int row, int col);  //函数DisplayBoard的声明
void PlayerMove(char board[ROW][COL], int row, int col);  //函数PlayerMove的声明
void ComputerMove(char board[ROW][COL], int row, int col);  //函数ComputerMove的声明

//IsWin()函数应该返回如下四种游戏状态:
//1.玩家赢   返回“*”
//2.电脑赢   返回“#”
//3.平局     返回“Q”
//4.继续     返回“A”
char IsWin(char board[ROW][COL], int row, int col);  

game.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"  //第4行代码使用了game.h中的符号,需要包含这个头文件

//函数InitBoard的实现(数组元素初始化成空格)
void InitBoard(char board[ROW][COL], int row, int col)  
{
    int i = 0;
    int j = 0;
    //两层for循环遍历数组
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)  
        {
            board[i][j] = ' ';
        }
    }
}

//函数DisplayBoard的实现
void DisplayBoard(char board[ROW][COL], int row, int col)  
//打印如下内容:
//   |   |
//---|---|---
//   |   |   
//---|---|---
//   |   |   
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        //打印3行棋盘时,代码如下:
        //printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);  //打印一行棋盘
        //if(i < row-1)     //打印分割行(不包括最后一行)
        //{
        //    printf("---|---|---\n");
        //}
        //使得棋盘可以按照game.h中规定的行和列打印棋盘时,代码如下:
        int j = 0;
        for (j = 0; j < col; j++)
        {
            //打印一行棋盘
            printf(" %c ", board[i][j]);
            //打印一行棋盘中的分隔线(不包括最后一个)
            if (j < col - 1)
            {
                printf("|");  //在每一列数据后加上“|”(最后一列除外)
            }
        }
        printf("\n");
        //打印分割行(不包括最后一行)
        if (i < row - 1)
        {
            for (j = 0; j < col; j++)
            {
                printf("---");
                if (j < col - 1)
                {
                    printf("|");  //在每一列数据后加上“|”(最后一列除外)
                }
            }
            printf("\n");
        }
    }
}

void PlayerMove(char board[ROW][COL], int row, int col)
{
    int x = 0, y = 0;  //存储玩家输入的坐标
    printf("玩家走>\n");
    while (1)
    {
        printf("请输入坐标:>");
        scanf("%d %d", &x, &y);
        //判断用户输入坐标合法性
        if (x > 0 && x <= row && y > 0 && y <= col)  //按照普通人思路,3*3的方格行列排序是从1到3的,而不是从0开始
        {
            //当输入坐标合法时,继续检查该坐标是否已经存在元素
            if (board[x - 1][y - 1] == ' ')  //该坐标为空格,表示未被占用
            {
                board[x-1][y-1] = '*';  //为该坐标赋值星号
                break;
            }
            else
            {
                printf("该位置已被占用\n");  //执行else后实际上while循环的本次循环已经结束,重新执行while循环
            }
        }
        else
        {
            printf("坐标非法,请重新输入!\n");  //要求重新输入,就需要循环
        }
    }
}

void ComputerMove(char board[ROW][COL], int row, int col)
{
    int x = 0, y = 0;  //存储电脑输入的坐标
    printf("电脑走>\n");
    while (1)
    {
        //rand()生成随机数,范围:0-RAND_MAX
        //使用rand()需要调用srand(),srand只能调用一次,设置在只调用一次的test.c中的test函数中
        x = rand() % row;  //rand()产生随机值模行、列的值后产生0-2的值,即在3*3的棋盘中不存在非法问题
        y = rand() % col;
        if (board[x][y] == ' ')  //该坐标为空格,表示未被占用
        {
            board[x][y] = '#';  //为该坐标赋值井号
            break;
        }
        //else  电脑不需要提示该坐标已被占用,当生成坐标不合法(坐标被占用)时,重新执行while循环直到找到合法坐标后break跳出
        //    ...
    }
}

int IsFull(char board[ROW][COL], int row, int col)  //返回1表示棋盘全满,返回0表示棋盘上有空位
{
    //遍历棋盘寻找是否有空格
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            if (board[i][j] == ' ')
            {
                return 0;  //棋盘未满
            }
        }
    }
    return 1;  //棋盘已满
}


char IsWin(char board[ROW][COL], int row, int col)
{
    int i = 0;
    //判断横三行是否相同
    for (i = 0; i < row; i++)
    {
        if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
        {
            return board[i][1];  //玩家赢和电脑赢分别返回“*”和“#”,和玩家以及电脑下棋的符号相同,即可以返回棋盘上的字符
        }
    }
    //判断竖三列是否相同
    for (i = 0; i < col; i++)
    {
        if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
        {
            return board[1][i];
        }
    }
    //判断两个对角线
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    //判断是否平局(没有人赢且棋盘上没有空位)
    if(1 == IsFull(board, row, col))  //函数设置棋盘全满时返回1
    {
        return 'Q';
    }
    return 'A';
}

test.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"  //第14行使用了game.h中的符号,需要包含这个头文件

void menu()
{
    printf("**********************\n");
    printf("****1.paly  0.exit****\n");
    printf("**********************\n");
}

//游戏算法实现
void game()  
{
    char ret = 0;  //设置变量ret接收IsWin函数的返回值
    char board[ROW][COL] = {0}; //设置一个数组board,记录棋盘中每一步棋的位置
    //没有棋子时棋盘中应该是空白的,有棋子时才会打印一个字符。空白的棋盘实际也会打印内容(在打印棋盘时就已经打印了数组元素)。
    //为了视觉上看到棋盘是空白的,最好每个元素在起始时都是空格,即需要初始化数组。
    InitBoard(board,ROW,COL);  //初始化棋盘.属于游戏的相关代码,在game.h中声明,在game.c中实现
    DisplayBoard(board,ROW,COL);  //打印棋盘(数组)
    while (1)  //开始下棋直到有一方获胜,使用break跳出
    {
        PlayerMove(board,ROW,COL);  //玩家先手下棋,下棋的位置是棋盘坐标,即对数组board进行操作,需要行列数据
        DisplayBoard(board, ROW, COL);
        //判断输赢是判断行、列、对角是否3个符号相同,即对数组board进行判断,需要行列数据
        ret = IsWin(board, ROW, COL);  //在玩家每走一步棋后判断输赢
        if (ret != 'A')  //不要继续(分出输赢或平局)
        {
            break;
        }
        ComputerMove(board, ROW, COL);  //电脑下棋,和玩家一样,下棋的位置是棋盘坐标,即对数组board进行操作,需要行列数据
        DisplayBoard(board, ROW, COL);
        ret = IsWin(board, ROW, COL);  //在电脑每走一步棋后判断输赢
        if (ret != 'A')  //不要继续(分出输赢或平局)
        {
            break;
        }
    }
    if (ret == '*')
    {
        printf("玩家赢\n");
    }
    else if (ret == '#')
    {
        printf("电脑赢\n");
    }
    else
    {
        printf("平局\n");
    }
}

void test()
{
    int input = 0;
    //srand()设置随机数生成器时使用时间戳time(),参数为空指针,返回类型由time_t强制转换为int
    srand((unsigned int)time(NULL));  //使用srand()和time()需要引用头文件
    do
    {
        menu();   //进入游戏后显示菜单
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("三子棋\n");
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新选择!\n");
            break;
        }
    } while (input);
    //走到default时要重新执行do-while循环重新输入,即while要为真。
    //只有当case为0时,break跳出循环到while语句,input=0,判断为假需要退出。
    //综合考虑判断条件应为input,非0时为真,进入循环。
}

int main()
{
    test();  //希望游戏可以重复玩
    return 0;
}
评论区
推荐文章

暂无数据

Suzhou
Suzhou
Lv1
这个网站设计堪称垃圾。CTRL+S过后的东西,重启电脑只能保存5行。多按几次CTRL+S还提示频繁保存不了。吐了。
9
文章
0
粉丝
0
获赞