C语言之三字棋的实现及扩展
                
C语言之三字棋的实现及扩展
在我们学习完数组之后,我们完全可以利用数组相关知识来写一个微小型的游戏,比如说今天所说的——三子棋。
大纲:
文件组成
实现
完整代码展示
扩展
即:

一.文件组成:
在我们学习的过程中,我们要逐渐习惯多文件的书写方式,也就是模块化书写。
在本文中,笔者分为了三个文件来写,分别是:
1.game.h——实现游戏函数的声明
2.game.c——游戏函数的实现
3.test.c —— 测试及游戏函数的调用
二.实现
0.文件的初始化
在这里我们分别在我们所创建的 test.c 和 game.c 包含我们的头文件——game.h

1.菜单的实现
在菜单中,我们设置玩家可以选择的模式,play and quit
以及,菜单怎么样多次循环选择,菜单的容错处理。这里,我们利用 do-while 来实现。
#define _CRT_SECURE_NO_WARNINGS 1//加这一句话是因为笔者采用的是 VS 编译器,为了防止一些不必要的错误出现#include "game.h"void menu()//列出可供玩家选择的模式{
    printf("**************************************************************\n");
    printf("*****************         1.play              ****************\n");
    printf("*****************         0.exit              ****************\n");
    printf("**************************************************************\n");
}void play()
{
}int main()
{int input;//在这里,我们利用玩家选择的模式来控制循环的终止do{
        menu();
        printf("请输入你的选择:");
        scanf("%d", &input);switch (input)
        {case 1://play            play();break;case 0://退出printf("欢迎下次再来!\n");break;default://当玩家输入了非法字符,让其重新选择printf("输入错误,请重新输入!\n");
        }
    } while (input);//当input为0时,停止循环return 0;
}运行效果:

现在,我们的菜单已经做好了,接下来要做的就是来打印我们的棋盘。
2.棋盘的打印
这里我们把打印函数的声明放在 game.h 文件里,把实现放在game.c 文件中。
在写代码之前,我们先来想一想在棋盘打印中,我们能不能直接打印空格——这肯定是不能的,因为这样,我们在屏幕上什么都看不见 (≧?≦)?
game.c:
#define _CRT_SECURE_NO_WARNINGS 1#include"game.h"void InitBoard(char board[3][3], int row, int col)//棋盘初始化{int i = 0, j = 0;for (i = 0; i < row; i++)
    {for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}void DisplayBoard(char board[3][3], int row, int col)//棋盘打印函数{int i = 0, j = 0;for (i = 0; i < row; i++)
    {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 < 3; j++)
            {
                printf("---");//分割行if (j < col - 1)
                {
                    printf("|");
                }
            }
        }    
        printf("\n");
    }
}注:
为了避免文章赘余,test.c 以及 game.h不再表示
运行结果:

但是,我们这么写,会不会有问题?

值得注意的是,在这有人会把棋盘打印写成这个样子
void DisplayBoard(char board[3][3], int row, int col)//棋盘打印函数{for (int i = 0; i < 3; i++)
    {
        printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);if (i < 2)
        {
            printf("---|---|---\n");//分割行        }    
    }
}这样,无非还是上面那个问题,代码写死,无法扩展
所以,我们在这利用宏来实现,棋盘的大小随我们的宏来改变
因此在这我们给出头文件的部分
#pragma once#include<stdio.h>#define ROW 3//利用宏来实现棋盘的大小#define COL 3void InitBoard(char board[ROW][COL], int row, int col);//棋盘初始化void DisplayBoard(char board[ROW][COL],int row, int col);//棋盘打印函数
3.棋盘下子
1.玩家下子
在这里我们一共要注意几点:
1.在下子之前,我们要判断玩家所要下的位置是否在棋盘内
2.检测玩家要下的位置是否已有了棋子
3.下子之后,检查棋盘的输赢状况 (这个我们后面再说)
void PlayerMove(char board[ROW][COL], int row, int col)//玩家下棋{int x, y;
    printf("玩家走:\n");
    printf("请输入你所要落子的坐标:");
    scanf("%d%d", &x, &y);if (board[x - 1][y - 1]!=' ')//坐标被占用    {
        printf("该坐标已被占用,请重新下子!\n");
    }else if (!((x > 0 && x <= row) && (y > 0 && y <= col)))
    {
        printf("该坐标为非法坐标,请重新输入!\n");//坐标非法    }else{
        board[x - 1][y - 1] = '*';//玩家落子,暂时用 * 来表示    }
}2.电脑下子
再这里我们暂不深究,使用随机函数来生成一个坐标来下子
void ComputerMove(char board[ROW][COL], int row, int col)//电脑下棋{int x, y;
    printf("电脑走:");while (1)
    {
         x = rand() % row;
         y = rand() % col;         if (board[x][y] == ' ')
         {
             board[x][y] = '#';//这里我们用 # 来表示电脑下棋 break;
         }
    }
}4.胜负的判定
在这里,我们用一个函数的返回值来表示输赢的各个情况。
#-----电脑赢
*-----玩家赢
C-----继续下子
F-----和局
ISFULL( board[ROW][COL], row, i = , j = (i = ; i < row; i++ (j = ; j < col; j++ (board[i][j] == ISWIN( board[ROW][COL], row, i = , j = (i = ; i < row; i++ (j = ; j < col - ; j++ (board[i][j] == board[i][j + ] && board[i][j] == board[i][j + ] && board[i][j] != (board[i][j] == board[i + ][j + ] && board[i + ][j + ] == board[i][j] && board[i][j] != (board[col - - i][j] == board[col - - i][j + ] && board[col - - i][j + ] == board[col - - i][j] && board[col - - i][j] != board[col - - (i = ; i < row - ; i++ (j = ; j < col; j++ (board[i][j] == board[i + ][j] && board[i][j] == board[i + ][j] && board[i][j] !=
    //判断是否满盘---放在最后是因为最后一步的判断
	    if (1 == ISFULL(board, row, col))
	    {
		      return 'F';
	    }
return 'C'; }
到这,我们的三子棋似乎已经编完了,先看一下运行结果:
 
 
 
 
在这个过程中,我们会发现电脑下的特别快(当然,这跟我们的懒惰有关……)所以我们在电脑下的步骤中加一个Sleep()函数来延长电脑所用时间
已即,我们可以下一次子就清一下屏,这样看起来比较舒服
所以
最后的代码部分:


#pragma once#include<stdio.h>#include<stdlib.h>#include<time.h>#include<Windows.h>#define ROW 3//利用宏来实现棋盘的大小#define COL 3void InitBoard(char board[ROW][COL], int row, int col);//棋盘初始化void DisplayBoard(char board[ROW][COL], int row, int col);//棋盘打印函数void PlayerMove(char board[ROW][COL], int row, int col);//玩家下棋void ComputerMove(char board[ROW][COL], int row, int col);//电脑下棋char ISWIN(char board[ROW][COL], int row, int col);//判断输赢int ISFULL(char board[ROW][COL], int row, int col);//判断棋盘是否已满
game.h


#define _CRT_SECURE_NO_WARNINGS 1#include"game.h"void InitBoard(char board[ROW][COL], int row, int col)//棋盘初始化{int i = 0, j = 0;for (i = 0; i < row; i++)
    {for (j = 0; j < col; j++)
        {
            board[i][j] = ' ';
        }
    }
}//void DisplayBoard(char board[3][3], int row, int col)//棋盘打印函数//{//    for (int i = 0; i < 3; i++)//    {//        printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);//        if (i < 2)//        {//            printf("---|---|---\n");//        }    
//    }/
                    