C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

在我们学习完数组之后,我们完全可以利用数组相关知识来写一个微小型的游戏,比如说今天所说的——三子棋。

大纲:

   文件组成

   实现

   完整代码展示

   扩展

即:

C语言之三字棋的实现及扩展

一.文件组成:

在我们学习的过程中,我们要逐渐习惯多文件的书写方式,也就是模块化书写。

在本文中,笔者分为了三个文件来写,分别是:

1.game.h——实现游戏函数的声明

2.game.c——游戏函数的实现

3.test.c —— 测试及游戏函数的调用

二.实现

0.文件的初始化

在这里我们分别在我们所创建的 test.c 和 game.c 包含我们的头文件——game.h

C语言之三字棋的实现及扩展

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;

}

运行效果:

C语言之三字棋的实现及扩展

现在,我们的菜单已经做好了,接下来要做的就是来打印我们的棋盘。

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不再表示

运行结果:

C语言之三字棋的实现及扩展

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

C语言之三字棋的实现及扩展

值得注意的是,在这有人会把棋盘打印写成这个样子

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 3

void 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-----和局

int ISFULL(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++)

{

if (board[i][j] == ' ')

return 0;

}

}

return 1;

}

char ISWIN(char board[ROW][COL], int row, int col)

{

int i = 0, j = 0;

for (i = 0; i < row; i++)

{

for (j = 0; j < col - 2; j++)

{

//判断横行

if (board[i][j] == board[i][j + 1] && board[i][j] == board[i][j + 2] && board[i][j] != ' ')

return board[i][j];

//判断主对角线

else if (board[i][j] == board[i + 1][j + 1] && board[i + 2][j + 2] == board[i][j] && board[i][j] != ' ')

return board[i][j];

//判断副对角线

else if (board[col - 1 - i][j] == board[col - 2 - i][j + 1] && board[col - 3 - i][j + 2] == board[col - 1 - i][j] && board[col - 1 - i][j] != ' ')

return board[col - 1 - i][j];

}

}

for (i = 0; i < row - 2; i++)

{

for (j = 0; j < col; j++)

{

//判断竖行

if (board[i][j] == board[i + 1][j] && board[i][j] == board[i + 2][j] && board[i][j] != ' ')

return board[i][j];

}

}

//判断是否满盘---放在最后是因为最后一步的判断

if (1 == ISFULL(board, row, col))

{

return 'F';

}

return 'C';

}

到这,我们的三子棋似乎已经编完了,先看一下运行结果:

C语言之三字棋的实现及扩展 C语言之三字棋的实现及扩展 C语言之三字棋的实现及扩展 C语言之三字棋的实现及扩展 C语言之三字棋的实现及扩展

在这个过程中,我们会发现电脑下的特别快(当然,这跟我们的懒惰有关……)所以我们在电脑下的步骤中加一个Sleep()函数来延长电脑所用时间

已即,我们可以下一次子就清一下屏,这样看起来比较舒服

所以

最后的代码部分:

#pragma once

#include<stdio.h>

#include<stdlib.h>

#include<time.h>

#include<Windows.h>

#define ROW 3//利用宏来实现棋盘的大小

#define COL 3

void 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");

// }

// }

//}

void DisplayBoard(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++)

{

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");

}

}

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

{

int x, y;

printf("玩家走:\n");

while (1)

{

printf("请输入你所要落子的坐标:");

scanf("%d%d", &x, &y);

if (!((x > 0 && x <= row) && (y > 0 && y <= col)))

{

printf("该坐标为非法坐标,请重新输入!\n");//坐标非法

}

else if (board[x - 1][y - 1] != ' ')//坐标被占用

{

printf("该坐标已被占用,请重新下子!\n");

}

else

{

board[x - 1][y - 1] = '*';//玩家落子,暂时用 * 来表示

break;

}

}

system("cls");

}

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

{

int x, y;

printf("电脑走:\n");

Sleep(1000);

while (1)

{

x = rand() % row;

y = rand() % col;

if (board[x][y] == ' ')

{

board[x][y] = '#';//这里我们用 # 来表示电脑下棋

break;

}

}

system("cls");

}

int ISFULL(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++)

{

if (board[i][j] == ' ')

return 0;

}

}

return 1;

}

char ISWIN(char board[ROW][COL], int row, int col)

{

int i = 0, j = 0;

for (i = 0; i < row; i++)

{

for (j = 0; j < col - 2; j++)

{

//判断横行

if (board[i][j] == board[i][j + 1] && board[i][j] == board[i][j + 2] && board[i][j] != ' ')

return board[i][j];

//判断主对角线

else if (board[i][j] == board[i + 1][j + 1] && board[i + 2][j + 2] == board[i][j] && board[i][j] != ' ')

return board[i][j];

//判断副对角线

else if (board[col - 1 - i][j] == board[col - 2 - i][j + 1] && board[col - 3 - i][j + 2] == board[col - 1 - i][j] && board[col - 1 - i][j] != ' ')

return board[col - 1 - i][j];

}

}

for (i = 0; i < row - 2; i++)

{

for (j = 0; j < col; j++)

{

//判断竖行

if (board[i][j] == board[i + 1][j] && board[i][j] == board[i + 2][j] && board[i][j] != ' ')

return board[i][j];

}

}

//判断是否满盘---放在最后是因为最后一步的判断

if (1 == ISFULL(board, row, col))

{

return 'F';

}

return 'C';

}

game.c

#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 ret;

char board[ROW][COL] = { 0 };

InitBoard(board, ROW, COL);

DisplayBoard(board, ROW, COL);

while (1)

{

PlayerMove(board, ROW, COL);

DisplayBoard(board, ROW, COL);

ret = ISWIN(board, ROW, COL);

{

if (ret != 'C')

break;

}

ComputerMove(board, ROW, COL);

DisplayBoard(board, ROW, COL);

ret = ISWIN(board, ROW, COL);

{

if (ret != 'C')

break;

}

}

switch (ret)

{

case 'F':

printf("和局!\n");

system("pause");

system("cls");

break;

case '#':

printf("电脑赢!\n");

system("pause");

system("cls");

break;

case '*':

printf("玩家赢!\n");

system("pause");

system("cls");

break;

default:

break;

}

}

int main()

{

srand((unsigned)time(NULL));//随机种子的初始化

int input;//在这里,我们利用玩家选择的模式来控制循环的终止

do

{

menu();

printf("请输入你的选择:");

scanf("%d", &input);

switch (input)

{

case 1://play

system("cls");

play();

break;

case 0://退出

printf("欢迎下次再来!\n");

break;

default://当玩家输入了非法字符,让其重新选择

system("cls");

printf("输入错误,请重新输入!\n");

}

} while (input);//当input为0时,停止循环

return 0;

}

test.c

三.扩展

1.五子棋的实现

五子棋的实现仅仅只改变了判断规则,其它方式都没变。

判断代码:

char ISWIN(char board[ROW][COL], int row, int col)

{

int i = 0, j = 0;

for (i = 0; i < col; i++)

{

for (j = 0; j < col - 4; j++)

{

//判断横行

if (board[i][j] == board[i][j + 1] && board[i][j] == board[i][j + 2] && board[i][j] == board[i][j + 3] && board[i][j] == board[i][j + 4] && board[i][j] != ' ')

return board[i][j];

//判断对角线

else if (board[i][j] == board[i + 1][j + 1] && board[i + 2][j + 2] == board[i][j] && board[i][j] == board[i + 3][j + 3] && board[i][j] == board[i + 4][j + 4] && board[i][j] != ' ')

return board[i][j];

else if (board[col - 1 - i][j] == board[col - 2 - i][j + 1] && board[col - 3 - i][j + 2] == board[col - 1 - i][j] && board[col - 4 - i][j + 3] == board[col - 2 - i][j + 1] && board[col - 5 - i][j + 4] == board[col - 2 - i][j + 1] && board[col - 1 - i][j] != ' ')

return board[col - 1 - i][j];

}

}

for (i = 0; i < col - 4; i++)

{

for (j = 0; j < col ; j++)

{

//判断竖行

if (board[i][j] == board[i + 1][j] && board[i][j] == board[i + 2][j] && board[i][j] == board[i + 3][j] && board[i][j] == board[i + 4][j] && board[i][j] != ' ')

return board[i][j];

}

}

//判断是否满盘

if (1 == ISFULL(board, row, col))

{

return 'f';

}

return 'c';

}

2.玩家对战玩家

只需将电脑下子的部分,替换成玩家下子即可

完整五子棋-人人对战代码:

#pragma once

#include <stdio.h>

#include<stdlib.h>

#include<time.h>

#include<Windows.h>

#define ROW 10

#define COL 10

//初始化棋盘

void InitBoard(char board[ROW][COL], int row, int col);

//打印棋盘

void DisplayBoard(char board[ROW][COL], int row, int col);

//玩家走

void Player1Move(char board[ROW][COL],int row, int col);

//玩家2走

void Player2Move(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[ROW][COL], int row, int col)

{

int i = 0, j = 0;

for (i = 0; i < col; i++)

printf(" %d", i + 1);

printf("\n");

for (i = 0; i < row; i++)

{

printf("%2d", i + 1);

for (j = 0; j < col; j++)

{

printf(" %c ",board[i][j]);

if (j < col - 1)

printf("|");

}

printf("\n");

//打印分割行

if (i < row - 1)

{

printf(" ");

for (j = 0; j < col; j++)

{

printf("---");

if (j < col - 1)

printf("|");

}

printf("\n");

}

}

}

void Player1Move(char board[ROW][COL], int row, int col)

{

int i = 0, j = 0;

while (1)

{

printf("请输入玩家1要下的棋的坐标:");

scanf("%d%d", &i, &j);

//检查是否越界

if (1 <= i && i <= row && j >= 1 && j <= col)

{

if (board[i-1][j-1] != ' ')

printf("此坐标已被占用!\n");

else

{

board[i-1][j-1] = '*';

break;

}

}

else

printf("坐标非法访问!\n");

}

system("cls");

}

void Player2Move(char board[ROW][COL], int row, int col)

{

int i = 0, j = 0;

while (1)

{

printf("请输入玩家2要下的棋的坐标:");

scanf("%d%d", &i, &j);

//检查是否越界

if (1 <= i && i <= row && j >= 1 && j <= col)

{

if (board[i - 1][j - 1] != ' ')

printf("此坐标已被占用!\n");

else

{

board[i - 1][j - 1] = '#';

break;

}

}

else

printf("坐标非法访问!\n");

}

system("cls");

}

//void ComputerMove(char board[ROW][COL], int row, int col)

//{

// int i = 0, j = 0;

// printf("电脑走:\n");

// while (1)

// {

// i = rand() % row;

// j = rand() % col;

// if (board[i][j] == ' ')

// {

// board[i][j] = '#';

// break;

// }

// }

// Sleep(1000);

// system("cls");

//}

int ISFULL(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++)

{

if (board[i][j] == ' ')

return 0;

}

}

return 1;

}

char ISWIN(char board[ROW][COL], int row, int col)

{

int i = 0, j = 0;

for (i = 0; i < col; i++)

{

for (j = 0; j < col - 4; j++)

{

//判断横行

if (board[i][j] == board[i][j + 1] && board[i][j] == board[i][j + 2] && board[i][j] == board[i][j + 3] && board[i][j] == board[i][j + 4] && board[i][j] != ' ')

return board[i][j];

//判断对角线

else if (board[i][j] == board[i + 1][j + 1] && board[i + 2][j + 2] == board[i][j] && board[i][j] == board[i + 3][j + 3] && board[i][j] == board[i + 4][j + 4] && board[i][j] != ' ')

return board[i][j];

else if (board[col - 1 - i][j] == board[col - 2 - i][j + 1] && board[col - 3 - i][j + 2] == board[col - 1 - i][j] && board[col - 4 - i][j + 3] == board[col - 2 - i][j + 1] && board[col - 5 - i][j + 4] == board[col - 2 - i][j + 1] && board[col - 1 - i][j] != ' ')

return board[col - 1 - i][j];

}

}

for (i = 0; i < col - 4; i++)

{

for (j = 0; j < col ; j++)

{

//判断竖行

if (board[i][j] == board[i + 1][j] && board[i][j] == board[i + 2][j] && board[i][j] == board[i + 3][j] && board[i][j] == board[i + 4][j] && board[i][j] != ' ')

return board[i][j];

}

}

//判断是否满盘

if (1 == ISFULL(board, row, col))

{

return 'f';

}

return 'c';

}

game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

void menu()

{

printf("*********************************************\n");

printf("******* 1.play *********\n");

printf("******* 0.quit *********\n");

printf("*********************************************\n");

}

int choice()

{

int input = 0;

printf("请输入你的选择:");

scanf("%d", &input);

return input;

}

void game()

{

char ret;

char board[ROW][COL] = { 0 };

InitBoard(board, ROW, COL);

DisplayBoard(board, ROW, COL);

while (1)

{

Player1Move(board, ROW, COL);

DisplayBoard(board, ROW, COL);

ret = ISWIN(board, ROW, COL);

{

if (ret != 'c')

break;

}

Player2Move(board, ROW, COL);

DisplayBoard(board, ROW, COL);

ret = ISWIN(board, ROW, COL);

{

if (ret != 'c')

break;

}

}

switch (ret)

{

case 'f':

printf("和局!\n");

break;

case '#':

//printf("电脑赢!\n");

printf("玩家2赢!\n");

break;

case '*':

printf("玩家1赢!\n");

break;

default:

break;

}

}

void play()

{

int input;

do

{

menu();

input = choice();

system("cls");

switch (input)

{

case 1:

game();

break;

case 0:

printf("谢谢使用,欢迎下次再来!\n");

break;

default:

system("cls");

printf("输入错误,请重新输入!\n");

break;

}

} while (input);

}

int main()

{

play();

return 0;

}

test.c

3.电脑下棋的探究

对此我们的算法肯定是需要极大的改进的

建议:

1.不在用随机函数代替机器大脑

2.电脑根据情况堵截(但是这样的话,最终的情况多是平局)

所以:

在这,我们还仍需更多的努力!

4.将game.c转为lib文件

C语言之三字棋的实现及扩展

然后出现:

C语言之三字棋的实现及扩展

此时我们会看到在我们的项目文件夹下的Debug文件夹下产生了:

C语言之三字棋的实现及扩展

接着,我们就可删除我们项目中的game.c了

然后再test.c中加入这样的一句话:

C语言之三字棋的实现及扩展

此时我们再点击运行,发现与之前并无不同!

5.打包为可安装文件

因为在VS2019中,微软似乎删除了这个组件,所以我们先需要去官网下载安装一下 : 点这

直接安装就行,尽管它是英文版,但是我们任可使用(学好英语的重要性!!!)

安装好之后,

下面是具体步骤:

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

C语言之三字棋的实现及扩展

之后我们就可以把我们所编写的游戏分享给我们的小伙伴,让其安装畅玩

关于三子棋的讲解便到此为止。

因笔者水平有限,若有错误之处,还望多多指正。

以上是 C语言之三字棋的实现及扩展 的全部内容, 来源链接: utcz.com/a/120490.html

回到顶部