用c语言写一个简单的三子棋(井字棋)

猫和老鼠
emmmm…大致就是这个效果

目录

1.所需的知识

c语言基本语法,二维数组。

2.总体思路

将三子棋看作3x3的一个二维数组,由玩家和电脑分别填充这个数组。当行、列、叉能够达到三连时,就判断输赢。
默认玩家先行且棋子为 ‘ X ’,电脑后手棋子为 ‘ 0 ’。
下面是用到的一些函数。

//game.h
#ifndef __GAME_H__
#define __GAME_H__
#define ROW 3
#define CLO 3
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <memory.h>
void menu();                                           //打印菜单
void game(char arr[ROW][CLO], int row, int clo);       //执行菜单   
void play(char arr[ROW][CLO], int row, int clo);       //游戏主体
void InitBoard(char arr[ROW][CLO], int row, int clo);  //打印棋盘
void Pmove(char arr[ROW][CLO], int row, int clo);      //玩家下棋
void Cmove(char arr[ROW][CLO], int row, int clo);      //电脑下棋
int Isfull(char arr[ROW][CLO], int row, int clo);      //检测盘满
int Iswin(char arr[ROW][CLO], int row, int clo);       //判断输赢
#endif

下面是主函数

//test.c
#include"game.h"
int main()
{
    srand((unsigned int)time(NULL)); //产生随机数种子
    char arr[ROW][CLO];
    game(arr,ROW,CLO);
    system("pause");
    return 0;
}

3.详细实现

打印菜单

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

这个函数打印菜单选项。

执行菜单

void game(char arr[ROW][CLO], int row, int clo)
{
    int input = 0;
    do
    {
        menu();
        scanf_s("%d",&input);
        memset(arr, ' ', ROW*CLO * sizeof(arr[0][0]));
        switch (input)
        {
        case 1:
            play(arr, row, clo); break;
        case 2:
            exit(0); break;
        default:printf("你按错键了?\n");
        }
    } while (input);
}

这个函数是菜单执行部分。其中 memset 用来初始化二位字符数组为全‘ 空格 ’。
接着,用switch语句来判断玩家选项。

游戏主体

void play(char arr[ROW][CLO], int row, int clo)
{
    int flag = 0;
    InitBoard(arr, row, clo);
    while (1)
    {
        printf("人类走\n");
        Pmove(arr, row, clo);
        InitBoard(arr, row, clo);
        flag = Iswin(arr, row, clo);
        if (flag == 1)
        {
            printf("玩家赢\n");
            break;
        }
        flag = Isfull(arr, row, clo);
        if (flag == 4)
        {
            printf("棋盘满,游戏结束\n");
            break;
        }
        printf("电脑走\n");
        Cmove(arr, row, clo);
        InitBoard(arr, row, clo);
        flag = Iswin(arr, row, clo);
        if (flag == 1)
        {
            printf("电脑赢\n");
            break;
        }
    }
}

这个函数里面拥有一个 while 死循环,它的跳出条件是 玩家赢 、 电脑赢 、棋盘满。
flag == 1 表示电脑或者玩家获胜,flag == 4 表示棋盘满了。
这里将棋盘满的检测放在玩家行动后,因为只有先手才能使棋盘满。

棋盘打印

void InitBoard(char arr[ROW][CLO], int row, int clo)
{
    int i = 0;
    int j = 0;
    for (j = 0; j < row; j++)
    {
        for (i = 0; i < clo; i++)
        {
            printf(" %c ", arr[j][i]);
            if (i < (clo - 1))
            {
                printf("|");
            }
        }
        printf("\n");
        if (j < row - 1)
        {
            for (i = 0; i < clo; i++)
            {
                printf("---");
                if (i < (clo - 1))
                {
                    printf("|");
                }
            }
        }
        printf("\n");
    }
}

这个棋盘打印函数可以扩展。只要改变ROW 和 CLO 的值就可以改变棋盘大小。
例如: 7x7的棋盘
这里写图片描述

玩家下棋

void Pmove(char arr[ROW][CLO], int row, int clo)
{
    printf("请输入坐标(空格隔开)\n");
    int m = 0, n = 0;
    while (1)
    {
        scanf_s("%d %d", &m, &n);
        if (((m >= 1) && (m <= 3)) && ((n >= 1) && (n <= 3)))
        {
            if (arr[m - 1][n - 1] == ' ')
            {
                arr[m - 1][n - 1] = 'X';
                break;
            }
            else
            {
                printf("这里可能已经有棋子了\n");
            }
        }
        else
        {
            printf("可能你输错了\n");
        }
    }
}

这个是玩家输入函数。这里注意判断输入是否合法,输入范围1~3,并查看该点是否有棋子。考虑玩家因素,将获取的数字-1,使之成为数组变量。

判断输赢

int Iswin(char arr[ROW][CLO], int row, int clo)
{
    //行
    int i = 0;
    for (i = 0; i < row; i++)
    {
        if (((arr[i][0] == arr[i][1]) && (arr[i][1] == arr[i][2])) && ((arr[i][0] != ' ')))
        {
            return 1;
        }
    }
    //列
    for (i = 0; i < clo; i++)
    {
        if ((arr[0][i] == arr[1][i]) && (arr[1][i] == arr[2][i]) && ((arr[0][i] != ' ') ))
        {
            return 1;
        }
    }
    //叉
    if ((arr[0][0] == arr[1][1]) && (arr[1][1] == arr[2][2]) && ((arr[0][0] != ' ') ))
    {
        return 1;
    }
    if ((arr[0][2] == arr[1][1]) && (arr[1][1] == arr[2][0]) && ((arr[0][2] != ' ')))
    {
        return 1;
    }
    return 0;
}

这个函数判断输赢。由于3行3列,所以用for循环扫描3次。当连续三字相同且不为‘ 空格 ’,那么取得胜利。

电脑下棋(核心)

void Cmove(char arr[ROW][CLO], int row, int clo)
{
    int flag = 0;
    int m = 0, n = 0;
    for (m = 0; m < row; m++)
    {
        for (n = 0; n < row; n++)
        {
            if (arr[m][n] == ' ')
            {
                arr[m][n] = '0';
                flag = Iswin(arr, row, clo);
                if (flag == 0)
                {
                    arr[m][n] = ' ';
                }
                if (flag == 1)
                {
                    break;
                }
            }
        }
        if (flag == 1)
        {
            break;
        }
    }
    if (flag == 0)
    {
        for (m = 0; m < row; m++)
        {
            for (n = 0; n < row; n++)
            {
                if (arr[m][n] == ' ')
                {
                    arr[m][n] = 'X';
                    flag = Iswin(arr, row, clo);
                    if (flag == 0)
                    {
                        arr[m][n] = ' ';
                    }
                    if (flag == 1)
                    {
                        arr[m][n] = '0';
                        break;
                    }
                }
            }
            if (flag == 1)
            {
                break;
            }
        }

        if ((m == 3) && (n == 3))
        {
            while (1)
            {
                m = rand() % 3;
                n = rand() % 3;
                if (arr[m][n] == ' ')
                {
                    arr[m][n] = '0';
                    break;
                }
            }
        }
    }
}

这是个电脑下棋函数。这里动了点歪脑筋。
第一个双层for循环的作用是:依次检测空位。如果是空位,那么先让该空位被’ 0 ‘填充,然后判断能不能获胜。能获胜就跳出 且 flag == 1,不能获胜就把改位再次置成空位。

接上述不能获胜。如果扫描完后发现并不能获胜,那么采取防守策略。

第二个双层for循环也是检测空位。如果是空位,那么让该位被 ’ X ‘填充,(相当电脑伪装成玩家先走一步)。填充后判断输赢,如果能够获胜,那么电脑再将这个位置改为‘ 0 ’,达到阻止玩家取胜的目的。

最后如果既不能取得胜利,也不用阻止玩家获胜。那么,就让电脑随机找位置下棋。

判断盘满

int Isfull(char arr[ROW][CLO], int row, int clo)
{
    int i = 0, j = 0;
    int count = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < clo; j++)
        {
            if (arr[i][j] != ' ')
            {
                count ++;
            }
        }
    }
    if (count == 9)
    {
        return 4;
    }
    return 3;
}

这个函数是判断棋盘是否满。使用count变量计数,扫描棋盘,如果不为‘ 空格 ’的位置有九个,那么就表示棋盘满了。

3.效果演示

会堵人的电脑
这里写图片描述
会优先自己胜利的电脑
这里写图片描述
棋盘满效果
这里写图片描述

PS:先手优势很大,不出意外的话是先手赢,或是平局。
依此类推,五子棋在15x15及其以上的棋盘,先手必胜。
但是电脑获胜算法非常复杂,反正我是写不出来。

猜你喜欢

转载自blog.csdn.net/H_Strong/article/details/79850456