c语言初阶(3-数组-实战收尾篇1-三(多)子棋)

        相信大家都知道五子棋的游戏规则和游戏布局,即当某方5个棋子连续(包括斜向)时,获得胜利,三子棋本质和五子棋相同,不过胜利条件变成了某方3个棋子连续(包括斜向)时,获得胜利。下面就让我们来做一个三子棋吧!加油噢!

目录

一:思路

二:具体实现

1.模拟登录

2.游戏菜单

3.棋盘的初始化

4.棋盘的打印

5.接下来进入玩家开始落子

6.电脑下棋

7.判断输赢

8.倒计时函数

三:全部源码

补充 :


一:思路

1.首先我们需要登录游戏,输入账号密码,此功能的实现我们之前以前讲过(判断和循环(实战收尾篇2—猜数字游戏)),不再赘述,直接使用

2.接下来我们需要一个游戏菜单

3.然后我们需要一个棋盘

1.1棋盘的搭建需要一个打印一个井字表格

1.2需要一个二维数组,并将该数组放在打印的井字格中,便于数据的更改和添加。

4.棋盘有了,我们就要开始游戏了

4.1首先是玩家先手下棋

4.2其次是电脑的下棋,此处要用到随机数

5.每一方下完棋后,我们都需要再次打印棋盘,并判断正负,棋盘打印可直接调用第三步函数,我们接下来要实现的是判断输赢的函数。

该游戏我们使用多文件操作,下图是main.c中的主要功能实现思路

#include"main.h"
//main.c
int main()
{
    //设置随机数
    srand((unsigned long)time(NULL));
    Log_In();
    int input;
    do
    {
        Menu();
        printf("请做出你的选择>");
        scanf("%d", &input);
        switch (input)
        {
        case 0:
            return;
            break;
        case 1:
            Game();
            break;
        default:
            printf("选择错误,请重新选择!\n");
            break;
        }
    } while (input);
    return 0;
}

        进入main函数后,首先设置随机数,为后续的使用做铺垫。然后调用登录函数,登录成功后进入do-while语句,首先调用菜单函数,然后就开始进入分支选择,输入1则进入Game函数,0退出,其它打印“选择错误,请重新选择!”并重新进入循环。

void Game()
{
        //倒计时函数
        My_Time();
        int x=0, y=0;
        printf("三子棋游戏开始!\n");
        //设置棋盘
        char my_board[ROW][COL];
        //棋盘初始化
        Init_Board(my_board,ROW,COL);
        //打印棋盘
        Print_Board(my_board,ROW,COL);
//      //玩家下棋
        while (1)
        {
               Player(my_board, ROW, COL,x,y);
               Print_Board(my_board, ROW, COL);
               //判断胜负
               int ret = Is_Win(my_board,ROW,COL,x,y);
               if (1 == ret)
               {
                       printf("恭喜玩家获得胜利!\n");
                       break;
               }
               else if (0 == ret)
               {
               }
               else
               {
                       printf("平局\n");
                       break;
               }
               //计算机下棋
               Computer(my_board, ROW, COL,x,y);
               Print_Board(my_board, ROW, COL);
               //判断胜负
                ret = Is_Win(my_board, ROW, COL, x, y);
                if (1 == ret)
                {
                        printf("很遗憾,玩家失败!\n");
                        break;
                }
                else if (0 == ret)
                {
                }
                else
                {
                        printf("平局\n");
                        break;
                }
        }
        return;
}

     若输入1,进入Game函数后,首先调用倒计时函数,实现游戏开始的倒计时小功能,之后依次设置棋盘,初始化棋盘,打印棋盘,然后进入玩家下棋,下完后进行棋盘打印并通过胜负函数Is—_Win的返回值判断输赢,若玩家胜利或平局,结束游戏,若无事发生,则进入电脑下棋阶段,然后同样进行棋盘打印和输赢判断。

在了解大致思路后让我们来一点点的把它们具体实现吧。

二:具体实现

1.模拟登录

void Log_In()
{
    char account[15]="\0";
    char password[15] = "\0";
    while (1)
    {
        printf("请输入账号和密码\n");
        printf("账号>");
        scanf("%s", account);
        printf("密码>");
        scanf("%s", password);
        //检验账号密码的正确性
        if (((strcmp(account, ACCOUNT)) == 0) && ((strcmp(password, PASSWORD)) == 0))
        {
            printf("登陆成功!\n");
            break;
        }
        else
        {
            printf("账号或密码错误,请重新输入!\n");
        }
    }
    return;
}

        对于ACCOUNT(账号)和PASSWORD(密码)我们在头文件中会进行宏定义,以便后续的修改。
 

2.游戏菜单

void Menu()
{
    printf("**********  欢迎来到三子棋游戏  **********\n");
    printf("****     1.Play        0.Exit      ****\n");
    printf("****************************************\n");
    return;
}

3.棋盘的初始化

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

        将二维数组my_board各个元素均初始化为空格以便于棋盘的打印,同时使得后续对数组的元素的更改可以同步反映到棋盘上,并在打印函数中打印出来

4.棋盘的打印

void Print_Board(char my_board[][COL],int row,int col)
{
        //刷新界面
        system("cls");
        //打印棋盘
        for (int i=0;i<row;i++)
        {
               int j = 0;
               for (j = 0; j < col; j++)
               {
                       printf(" %c ",my_board[i][j]);
                       if (j < col - 1)
                       {
                              printf("|");
                       }
               }
               printf("\n");
               if (i < row - 1)
               {
                       for (j = 0; j < col; j++)
                       {
                              if (j < col - 1)
                              {
                                      printf("---|");
                              }
                              else
                              {
                                      printf("---");
                              }
                              
                       }
               }
               printf("\n");
        }
               return;
}

        每次调用打印函数前,首先进行清屏处理,增加游戏体验感,而"cls"就是清屏命令,通过windows.h头文件来引进命令行的使用,使用system(“cls”)进行清屏操作(即页面刷新)。

        之后则利用for的嵌套和多个if语句来对棋盘进行具体打印,第一个for语句代表着行数的循环,之后进入每行的具体打印                

        我们打印的棋盘如上图所示,第一行为:空格+“|”+空格+“|”+空格

        使用第二个for循环进行第一行各列的打印,循环打印为 空格+“|”,但很显然最后一列会多出一个“|”

        为使得最后一列不打印“|”,所以我们使用第一个if语句,控制 “|” 不打印在最后一行

        打印为第一行后,通过换行进入第二行,第二行为: “---|”+“---|”+“---”,先不看第二个if语句,

       直接进入第三个for循环,通过if语句来实现除最后一列外,打印“---|”,而最后一列打印为“---”

       若如此循环,则最后一行也会打印为:“---|”+“---|”+“---” ,因此我们通过第二个if语句来使得最后一行不打印:“---|”+“---|”+“---”

         打印完第二行后进行换行,然后循环最外侧的for,依次打印,结果如上图所示。

5.接下来进入玩家开始落子

void Player(char my_board[][COL], int row, int col,int x,int y)
{
        while (1)
        {
               printf("请输入坐标(x,y)\n");
               scanf("%d %d", &x, &y);
               x = x - 1;
               y = y - 1;
               //判断坐标合法性
               if (!(x >= 0 && x < row && y < col && y >= 0))
               {
                       printf("坐标不合法!\n");
               }
               else if (my_board[x][y] != ' ')
               {
                       printf("该坐标已被占用!\n");
               }
               else
               {
                       my_board[x][y] = 'O';
                       break;
               }
        }
        return;
}

        因为玩家坐标的输入可能不具有合法性,因此首先设立一个while循环,进入循环后,让玩家输入落子坐标因为不是每一个玩家都是程序员,明白数组首元素下标是0,因此我们为方便玩家进行坐标输入,在玩家输入坐标后,对坐标进行减1操作,使得坐标合法同时方便玩家,如玩家输入坐标为(1,1)其实是想在真实坐标(1,1)处落子,但实际上会落到坐标(2,2)处,我们对玩家输入的坐标减1后就会落到数组(0,0),即真实坐标(1,1)处了。

        当玩家输入坐标后,首先进行坐标合法性判断,若坐标不在棋盘范围内,则输出“坐标不合法”,并重新进入循环再次输入坐标,合法性判断后,再次判断该坐标是否已被占用,即该坐标处元素值是否为初始值“空格”,若并非空格,则已被占用,并重新循环。以上条件街皆满足后,将该坐标处元素值改为玩家棋子“O”并退出循环,结束玩家下棋。

6.电脑下棋

void Computer(char my_board[][COL], int row, int col,int x,int y)
{
        while (1)
        {       //电脑的随机下棋
                x = rand() % row;
                y = rand() % col;
               if (my_board[x][y] == ' ')
               {
                       my_board[x][y] = 'X';
                       break;
               }
        }
        return;
}

        电脑下棋的实现较为简单,对坐标进行随机化并使得随机在合法范围内即可,若随机处已被占用,则重新随机,若未占用,则更改此处元素初始值“空格”为电脑棋子“X”。

7.判断输赢

每次电脑或玩家下完棋后都需要进行输赢的判断。

int Is_Win(char my_board[][COL], int row, int col, int x, int y)
{
    //0—无事发生 2—平局 1—电脑/玩家胜利

    //判断是否平局
    int count = 0;
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            if (my_board[i][j] == ' ') {
                count++;
            }
        }
    }
    if (count == 0)
    {
        return 2;
    }

    //判断横向
    count = 1;
    for (int tmp = 1; y - tmp >= 0; tmp++)
    {
        if (my_board[x][y - tmp] == my_board[x][y])
            count++;
        else break;
    }
    for (int tmp = 1; y + tmp < col; tmp++)
    {
        if (my_board[x][y + tmp] == my_board[x][y])
            count++;
        else break;
    }
    if (count >= 3)
        return 1;
    //纵向
    count = 1;
    for (int tmp = 1; (x - tmp >= 0); tmp++)
    {
        if (my_board[x - tmp][y] == my_board[x][y])
            count++;
        else
            break;
    }
    for (int tmp = 1; (x + tmp < col); tmp++)
    {
        if (my_board[x + tmp][y] == my_board[x][y])
            count++;
        else
            break;
    }
    if (count >= 3)
        return 1;
    //正斜  右上:x-tmp, y+tmp;   左下:x+tmp, y-tmp
    count = 1;
    for (int tmp = 1; (x - tmp >= 0) && (y + tmp < col); tmp++) {
        if (my_board[x - tmp][y + tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    for (int tmp = 1; (x + tmp < row) && (y - tmp >= 0); tmp++) {
        if (my_board[x + tmp][y - tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    if (count >= 3)
        return 1;
    //反斜 左上:x-tmp, y-tmp;  右下:x+tmp,y+tmp
    count = 1;
    for (int tmp = 1; (x - tmp >= 0) && (y - tmp >= 0); tmp++)
    {
        if (my_board[x - tmp][y - tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    for (int tmp = 1; (x + tmp < row) && (y + tmp < col); tmp++)
    {
        if (my_board[x + tmp][y + tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    if (count >= 3)
        return 1;
    return  0;
}

                首先进行平局的判断

        直接对二维数组进行遍历查找即可,当发现其中所有元素值均不为空格时(即count==0),即为平局,返回2;若有值(即count>=0),则进入后面的判断。 

        接下来判断输赢,只要有横向或纵向或斜向任意三个连续位置棋子相同,则说明游戏结束。那现在问题就是怎样来判断连续的三个位置呢,现在我们打印的九宫格的三子棋,每一个方向只有三个连续格子,可以直接对三个格子进行判断,看看是否相等。但若我们改变棋盘为5*5(改变棋盘行列的宏定义), 那么我们怎样判断呢?

        这时候我们就需要一个新的变量tmp作为坐标的偏移量,如(x,y)为一坐标点,那么(x-tmp,y)就是该点向左偏移tmp个单位位置后的元素坐标。由于不同方向的判断思路相同,我们在这里只分析纵向的判断。

         横向中,纵坐标不变,横坐标偏移,我们把tmp初始化为1,为保证坐标的合法性,将第一个for循环的判断条件设置为 x - tmp>=0,判断原坐标(x,y)左边相同的棋子个数,若 my_board[x - tmp][y] == my_board[x][y],则说明左边第一个棋子和原棋子相同,相同则count+1,不同则结束该for循环,此时的count即为(x,y)及左边连续相同的棋子个数(包括判断原点(x,y)),只后进入第2个for循环,判断(x,y)右边的相同的连续棋子个数,结束两个for循环后,若count为3,则说明有三个连续且相同的棋子,则返回1,代表一方胜出。

        纵向与横向相同,不过是横坐标不变,总左边改变,要保证纵坐标的合法性。但要注意每次判断完一个方向后都要重新把count赋值为1。

        正斜思路与横向类似,不过坐标变化为行减列加或行加列减,且坐标合法性要同时检验横纵坐标。 反斜不再赘述。

8.倒计时函数

void My_Time()
{
    int times = 5;
    printf("\n \n \n");
    while (times)
    {
        Sleep(1000);
        printf(" ************* 游戏将会在 %d s后开始 *****************\r", times);
        times--;
    }
}

        很简单的一个小功能,前面说过很多次,此处不赘述。

三:全部源码

#include"main.h"
//main.c
int main()
{
    //设置随机数
    srand((unsigned long)time(NULL));
    Log_In();
    int input;
    do
    {
        Menu();
        printf("请做出你的选择>");
        scanf("%d", &input);
        switch (input)
        {
        case 0:
            return;
            break;
        case 1:
            Game();
            break;
        default:
            printf("选择错误,请重新选择!\n");
            break;
        }
    } while (input);
    return 0;
}



#pragma once
//main.h
#pragma warning(disable:4996)
#include<stdio.h>
#include<windows.h>
#include<time.h>
#include<stdlib.h>
#define ACCOUNT "ranjianmin"
#define PASSWORD "20030512"
#define ROW 4
#define COL 4
extern void Game();
extern void Menu();
extern void Log_In();
extern void Init_Board(char my_board[][COL], int row, int col);
extern void Print_Board(char my_board[][COL], int row, int col);
extern void Player(char my_board[][COL], int row, int col, int x, int y);
extern int Is_Win(char my_board[][COL], int row, int col, int x, int y);
extern void Computer(char my_board[][COL], int row, int col, int x, int y);
#include"main.h"
//text.c
void Log_In()
{
    char account[15]="\0";
    char password[15] = "\0";
    while (1)
    {
        printf("请输入账号和密码\n");
        printf("账号>");
        scanf("%s", account);
        printf("密码>");
        scanf("%s", password);
        //检验账号密码的正确性
        if (((strcmp(account, ACCOUNT)) == 0) && ((strcmp(password, PASSWORD)) == 0))
        {
            printf("登陆成功!\n");
            break;
        }
        else
        {
            printf("账号或密码错误,请重新输入!\n");
        }
    }
    return;
}
void Menu()
{
    printf("**********  欢迎来到三子棋游戏  **********\n");
    printf("****     1.Play        0.Exit      ****\n");
    printf("****************************************\n");
    return;
}
void Init_Board(char my_board[][COL], int row, int col)
{
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            my_board[i][j] = ' ';
        }
    }
}
void Print_Board(char my_board[][COL], int row, int col)
{
    //刷新界面
    system("cls");
    //打印棋盘
    for (int i = 0; i < row; i++)
    {
        int j = 0;
        for (j = 0; j < col; j++)
        {
            printf(" %c ", my_board[i][j]);
            if (j < col - 1)
            {
                printf("|");
            }
        }
        printf("\n");
        if (i < row - 1)
        {
            for (j = 0; j < col; j++)
            {
                if (j < col - 1)
                {
                    printf("---|");
                }
                else
                {
                    printf("---");
                }

            }
        }
        printf("\n");
    }
    return;
}
void Player(char my_board[][COL], int row, int col, int x, int y)
{
    while (1)
    {
        printf("请输入坐标(x,y)\n");
        scanf("%d %d", &x, &y);
        x = x - 1;
        y = y - 1;
        //判断坐标合法性
        if (!(x >= 0 && x < row && y < col && y >= 0))
        {
            printf("坐标不合法!\n");
        }
        else if (my_board[x][y] != ' ')
        {
            printf("该坐标已被占用!\n");
        }
        else
        {
            my_board[x][y] = 'O';
            break;
        }
    }
    return;
}
void Computer(char my_board[][COL], int row, int col, int x, int y)
{
    while (1)
    {       //电脑的随机下棋
        x = rand() % row;
        y = rand() % col;
        if (my_board[x][y] == ' ')
        {
            my_board[x][y] = 'X';
            break;
        }
    }
    return;
}
int Is_Win(char my_board[][COL], int row, int col, int x, int y)
{
    //0—无事发生 2—平局 1—电脑/玩家胜利

    //判断是否平局
    int count = 0;
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            if (my_board[i][j] == ' ') {
                count++;
            }
        }
    }
    if (count == 0)
    {
        return 2;
    }

    //判断横向
    count = 1;
    for (int tmp = 1; y - tmp >= 0; tmp++)
    {
        if (my_board[x][y - tmp] == my_board[x][y])
            count++;
        else break;
    }
    for (int tmp = 1; y + tmp < col; tmp++)
    {
        if (my_board[x][y + tmp] == my_board[x][y])
            count++;
        else break;
    }
    if (count >= 3)
        return 1;
    //纵向
    count = 1;
    for (int tmp = 1; (x - tmp >= 0); tmp++)
    {
        if (my_board[x - tmp][y] == my_board[x][y])
            count++;
        else
            break;
    }
    for (int tmp = 1; (x + tmp < col); tmp++)
    {
        if (my_board[x + tmp][y] == my_board[x][y])
            count++;
        else
            break;
    }
    if (count >= 3)
        return 1;
    //正斜  右上:x-tmp, y+tmp;   左下:x+tmp, y-tmp
    count = 1;
    for (int tmp = 1; (x - tmp >= 0) && (y + tmp < col); tmp++) {
        if (my_board[x - tmp][y + tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    for (int tmp = 1; (x + tmp < row) && (y - tmp >= 0); tmp++) {
        if (my_board[x + tmp][y - tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    if (count >= 3)
        return 1;
    //反斜 左上:x-tmp, y-tmp;  右下:x+tmp,y+tmp
    count = 1;
    for (int tmp = 1; (x - tmp >= 0) && (y - tmp >= 0); tmp++)
    {
        if (my_board[x - tmp][y - tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    for (int tmp = 1; (x + tmp < row) && (y + tmp < col); tmp++)
    {
        if (my_board[x + tmp][y + tmp] == my_board[x][y])
            count++;
        else
            break;
    }
    if (count >= 3)
        return 1;
    return  0;
}
void My_Time()
{
    int times = 5;
    printf("\n \n \n");
    while (times)
    {
        Sleep(1000);
        printf(" ************* 游戏将会在 %d s后开始 *****************\r", times);
        times--;
    }
}
void Game()
{
    //倒计时函数
    My_Time();
    int x = 0, y = 0;
    printf("三子棋游戏开始!\n");
    //设置棋盘
    char my_board[ROW][COL];
    //棋盘初始化
    Init_Board(my_board, ROW, COL);
    //打印棋盘
    Print_Board(my_board, ROW, COL);
    //      //玩家下棋
    while (1)
    {
        Player(my_board, ROW, COL, x, y);
        Print_Board(my_board, ROW, COL);
        //判断胜负
        int ret = Is_Win(my_board, ROW, COL, x, y);
        if (1 == ret)
        {
            printf("恭喜玩家获得胜利!\n");
            break;
        }
        else if (0 == ret)
        {
        }
        else
        {
            printf("平局\n");
            break;
        }
        //计算机下棋
        Computer(my_board, ROW, COL, x, y);
        Print_Board(my_board, ROW, COL);
        //判断胜负
        ret = Is_Win(my_board, ROW, COL, x, y);
        if (1 == ret)
        {
            printf("很遗憾,玩家失败!\n");
            break;
        }
        else if (0 == ret)
        {
        }
        else
        {
            printf("平局\n");
            break;
        }
    }
    return;
}

补充 :

        假如需要将三子棋改为五子棋,只要将判断输赢函数里改为count>=5,并把棋盘大小(ROW和COL)改成适合的大小即可。

        下一篇我们会进行扫雷的讲解,晚安喔!留个赞赞吧(小声)。

最后:        

        愿每一个奋斗者都有着直面苍天的勇气,在孤独的旅途中,自信的踏向茫茫的明天!或许不会有很多人看到我们的努力,那就让他们看到我们的成功!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲸落之·海

哇塞,我将因此动力加倍!冲冲冲

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值