C语言:用数组知识写出三子棋小游戏

本小游戏需要创建三个文件,分别是头文件game.h和源文件game.c、test.c

游戏菜单的创建,框架的构建

和猜数字游戏一样,为了让玩家可以在再开一把和退出中进行选择,我们用一个do while的结构,再用switch分支,就可以完成框架的构建

//test.c


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

void test()
{
    int input = 0;
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("三子棋\n");//三子棋游戏的全部逻辑封装为一个函数后就可把该行替换
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新选择\n");
            break;
        }
    } while (input);
}

int main()
{
    test();
    return 0;
}

游戏主体部分

棋盘的初始化和打印

//game.c

void init_board(char board[ROW][COL], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        int j = 0;
        for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}

//打印棋盘函数
void printf_board(char board[ROW][COL], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
        //把第一行棋盘分成空格 字符 空格 竖杠 空格 字符 空格 竖杠 空格 字符 空格
        if(i<row-1)
            printf("---|---|---\n");
            //把第二行棋盘分成---竖杠---竖杠---
            //并且由于最后一行无需打印,我们设置了i<row-1的条件判断
    }
}

上面的棋盘打印函数存在缺陷,比如若对ROW和COL进行修改,就会出现问题(5*5的棋盘打印效果如图)

不难发现,出现这种情况是因为列的打印被我们固定好了。

如何解决这个问题呢,我们可以看看下面这段改良版。

//game.c
//由于只能3*3,所以推出了改良版的打印函数
//改良版
void printf_board(char board[ROW][COL], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        //printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);//对这部分进行拆分
        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)//行与行之间的分隔也要少打一次
        {
            //printf("---|---|---\n");//进行拆分
            for (j = 0; j < col; j++)
            {
                printf("---");
                if (j < col - 1)//少打印一个|
                {
                    printf("|");
                }
            }
            printf("\n");//记得换行
        }
    }
}

玩家下棋

//game.c
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
    printf("玩家下棋\n");
    while (1)//尚未知道停下的条件
    {
        printf("请输入要下棋的坐标:>");
        int x = 0;
        int y = 0;
        scanf("%d %d", &x, &y);
        if (x >= 1 && x <= row && y >= 1 && y <= col)//玩家不知道数组元素编号规则
        {
            if (board[x - 1][y - 1] == ' ')//看看这个位置有没有被占用
            {
                board[x - 1][y - 1] = '*';
                break;//直到成功下了棋以后,才跳出该循环
            }
            else
            {
                printf("该坐标被占用,请重新输入");
            }
        }
        else
        {
            printf("坐标非法\n");
        }
    }
}

电脑下棋

电脑随机下棋,逻辑比较简单,这里用一个随机数生成加上判断该位置是否为空即可,落棋成功后再跳出循环,否则不断生成随机数并再次判断。

//game.c
//电脑下棋
//随机生成坐标,只要坐标没有被占用,就下棋
void computer_move(char board[ROW][COL], int row, int col)
{
    printf("电脑下棋:\n");
    while (1)
    {
        int x = rand() % row;
        int y = rand() % col;
        if (board[x][y] == ' ')
        {
            board[x][y] = '#';
            break;//电脑成功找到一个空位置并下棋以后跳出循环
        }
    }
}
//test.c
//在main函数的首行
    srand((unsigned int)time(NULL));
//test.c

void game()
{
    char board[ROW][COL];//创建棋盘
    init_board(board, ROW, COL);//初始化棋盘的函数,将其有几行几列都传过去
    printf_board(board, ROW, COL);//打印棋盘
    while (1)//当某一方赢了或者平局 就停止
    {
        player_move(board, ROW, COL);
        printf_board(board, ROW, COL);//下一次棋就显示一次
        //这里缺少了输赢的判断,若玩家下了这一步以后赢了,这个循环就可以停止了
        computer_move(board, ROW, COL);
        printf_board(board, ROW, COL);//电脑下棋以后也要显示一次
        //这里缺少了输赢的判断,若电脑下了这一步以后赢了,这个循环就可以停止了
    }
}

判断输赢

这段代码还存在一定缺陷,同未改良的棋盘打印函数一样,行与列过于固定,如果对棋盘大小进行修改会出现bug,后期本人会进行改良

//test.c

void game()
{
    char ret = '0';
    char board[ROW][COL];//创建棋盘
    init_board(board, ROW, COL);//初始化棋盘的函数,将其有几行几列都传过去
    printf_board(board, ROW, COL);//打印棋盘
    while (1)//当某一方赢了或者平局 就停止
    {
        player_move(board, ROW, COL);
        printf_board(board, ROW, COL);
        //判断输赢
        ret = is_win(board, ROW, COL);
        if (ret != 'C')
        {
            break;//只要不是C,统统跳出循环
        }
        computer_move(board, ROW, COL);
        printf_board(board, ROW, COL);
        //判断输赢
        ret = is_win(board, ROW, COL);
        if (ret != 'C')
        {
            break;//只要不是C,统统跳出循环
        }
    }
    if (ret == '#')
        printf("电脑赢了\n");
    else if (ret == '*')
        printf("玩家赢了\n");
    else if (ret == 'Q')
        printf("平局\n");
}

//判断输赢,判断输赢的代码要告诉我:电脑赢了?玩家赢了?平局了?游戏继续?
//电脑赢了-返回#
//玩家赢了-返回*
//平局了-返回Q
//继续-返回C
//game.c

//判断输赢
//有缺陷,三行三列已经固定死了
char is_win(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][0] != ' ')
        {
            return board[i][0];
        }
    }
    for (i = 0; i < col; i++)
    {
        if(board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
        {
            return board[0][i];
        }
    }
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    //平局?
    //棋盘满了
    if (is_full(board, row, col) == 1)
    {
        return 'Q';
    }
    //否则继续
    //没有人赢,也没有平局,游戏继续
    return 'C';
}
//game.c

//is_full这个函数只是为了支持is_win,只是在is_win的内部使用
//那我们就没有必要在game.h中声明
//判断棋盘有没有满
//在输赢判断函数里会被使用
//这块代码要写在is_win上面
static int is_full(char board[ROW][COL], int row, int col)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            if (board[i][j] == ' ')
                return 0;//只要有一个位置是空下来的,就说明没有满,返回0
        }
    }
    return 1;
}

三子棋代码一览

//game.h

#pragma once

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

#define ROW 3
#define COL 3

//在头文件中声明函数

//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);

//打印棋盘
void printf_board(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col);

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

//判断输赢
char is_win(char board[ROW][COL], int row, int col);
//game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

//初始化棋盘
void init_board(char board[ROW][COL], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        int j = 0;
        for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}

//void printf_board(char board[ROW][COL], int row, int col)
//{
//    int i = 0;
//    for (i = 0; i < row; i++)
//    {
//        printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//        if(i<row-1)
//            printf("---|---|---\n");
//    }
//}
 
//由于只能3*3,所以推出了打印改良版
//改良版
void printf_board(char board[ROW][COL], int row, int col)
{
    int i = 0;
    for (i = 0; i < row; i++)
    {
        //printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
        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)//行与行之间的分隔也要少打一次
        {
            //printf("---|---|---\n");
            for (j = 0; j < col; j++)
            {
                printf("---");
                if (j < col - 1)//少打印一个|
                {
                    printf("|");
                }
            }
            printf("\n");//记得换行
        }
    }
}


//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
    printf("玩家下棋\n");
    while (1)
    {
        printf("请输入要下棋的坐标:>");
        int x = 0;
        int y = 0;
        scanf("%d %d", &x, &y);
        if (x >= 1 && x <= row && y >= 1 && y <= col)//玩家不知道数组元素编号规则
        {
            if (board[x - 1][y - 1] == ' ')
            {
                board[x - 1][y - 1] = '*';
                break;//直到成功下了棋以后,才跳出该循环
            }
            else
            {
                printf("该坐标被占用,请重新输入");
            }
        }
        else
        {
            printf("坐标非法\n");
        }
    }
}


//电脑下棋
//随机生成坐标,只要坐标没有被占用,就下棋
void computer_move(char board[ROW][COL], int row, int col)
{
    printf("电脑下棋:\n");
    while (1)
    {
        int x = rand() % row;
        int y = rand() % col;
        if (board[x][y] == ' ')
        {
            board[x][y] = '#';
            break;//电脑成功找到一个空位置并下棋以后跳出循环
        }
    }
}

//is_full这个函数只是为了支持is_win,只是在is_win的内部使用
//那我们就没有必要在game.h中声明
//判断棋盘有没有满
//在输赢判断函数里会被使用
static int is_full(char board[ROW][COL], int row, int col)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            if (board[i][j] == ' ')
                return 0;//只要有一个位置是空下来的,就说明没有满,返回0
        }
    }
    return 1;
}

//判断输赢
//有缺陷,三行三列已经固定死了
char is_win(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][0] != ' ')
        {
            return board[i][0];
        }
    }
    for (i = 0; i < col; i++)
    {
        if(board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
        {
            return board[0][i];
        }
    }
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
    {
        return board[1][1];
    }
    //平局?
    //棋盘满了
    if (is_full(board, row, col) == 1)
    {
        return 'Q';
    }
    //否则继续
    //没有人赢,也没有平局,游戏继续
    return 'C';
}
//test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void menu()
{
    printf("**********************************\n");
    printf("**********    1.play    **********\n");
    printf("**********    0.exit    **********\n");
    printf("**********************************\n");
}
void game()
{
    char ret = '0';
    char board[ROW][COL];//创建棋盘
    init_board(board, ROW, COL);//初始化棋盘的函数,将其有几行几列都传过去
    printf_board(board, ROW, COL);//打印棋盘
    while (1)//当某一方赢了或者平局 就停止
    {
        player_move(board, ROW, COL);
        printf_board(board, ROW, COL);
        //判断输赢
        ret = is_win(board, ROW, COL);
        if (ret != 'C')
        {
            break;//只要不是C,统统跳出循环
        }
        computer_move(board, ROW, COL);
        printf_board(board, ROW, COL);
        //判断输赢
        ret = is_win(board, ROW, COL);
        if (ret != 'C')
        {
            break;//只要不是C,统统跳出循环
        }
    }
    if (ret == '#')
        printf("电脑赢了\n");
    else if (ret == '*')
        printf("玩家赢了\n");
    else if (ret == 'Q')
        printf("平局\n");
}

//判断输赢,判断输赢的代码要告诉我:电脑赢了?玩家赢了?平局了?游戏继续?
//电脑赢了-#
//玩家赢了-*
//平局了-Q
//继续-C
void test()
{
    srand((unsigned int)time(NULL));
    int input = 0;
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新选择\n");
            break;
        }
    } while (input);
}

int main()
{
    test();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值