前言:在我们小时候经常上课开小差,与同桌下“井字棋”---也叫“三子棋”,想不想在vs上重温小时候的快乐,往下看看吧!
目录
今天要完成一个相对于之前学习更有挑战性的小程序——三子棋。
相信我们大部分人都接触过三子棋,这是一款操作简单易上手的小游戏。
先简单介绍一下三子棋的规则,方便我们接下来的编程和理解。规则如下:
在九宫格棋盘上,只要将自己的三个棋子走成一条线(横、竖、对角线),对方就算输了。
规则很简单,但是我们应该从哪里入手完成这个三子棋程序呢?
首先,我们的游戏程序应该先有个菜单页面,这个菜单页面用来让用户操作选择玩游戏,或是退出。
界面可以很简单,只需要几条 printf 语句即可。
这里我们尽可能用函数来完成游戏的功能:
void menu()
{
printf("***************************************\n");
printf("* 1.play 0.exit *\n");
printf("***************************************\n");
}
这就是一个简单的主菜单界面,效果如下:
既然菜单中有选项,我们就必须要设计一个变量用来接收用户的选择。
而且游戏程序本身应该允许用户反复进行玩耍,所以在主函数中,我们需要用一个循环来完成这个功能。
do-while循环在调用时会先运行循环体,而后在进行判断,所以这里我们选择使用do-while循环:
int main()
{
int input;
do
{
menu();
printf("请选择是否(1/0)开始游戏");
scanf("%d", &input);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (input);
return 0;
}
由于用户选择选项1时我们需要为用户启动游戏程序,所以这里我们使用一个switch语句来接受用户的选择。
选择0时退出程序,所以我们只需要跳出循环,即可退出。
而输入0、1之外的值时,我们应该向用户报输入错误,所以在default中输出一条报错信息。
接下来就是我们程序的重点——三子棋游戏本身了。
先创建一个实现三子棋的game()函数,里面的内容我们一步步来填充。
void game()
{
}
在程序开始阶段,我们首先要创建一个3*3的二维字符数组,
由于长和宽我们要经常用到,为了后续修改方便,我们采用宏定义方式:
#define ROWS 3
#define COLS 3
接下来在game()函数中创建数组:
char board[ROWS][COLS];
并将这个数组进行初始化,因为这个数组在后续需要输出展示在用户面前,所以我们将值初始化为空格“ ”。
初始化用函数进行完成:
void init_board(char board[ROWS][COLS], int rows, int cols)
{
//对棋盘初始化
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = ' ';
}
}
}
接下来,我们应该输出这个数组棋盘,来检查一下是否初始化完成,
但我们没有展示棋盘的函数,所以我们需要完成这个显示棋盘函数:
void show_board(char board[ROWS][COLS], int rows, int cols)
{
int i;
for (int i = 0; i < rows; i++)
{
printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);
if (i != rows - 1)
printf(" ---|---|--- \n");
}
}//棋盘的打印有两种方法
//这是简单粗暴的方法
//我会在后面介绍另一种
这里需要利用循环来输出我们棋盘的格式,格式不统一,可以根据自己的喜好进行修改。
下面展示一下我这个代码所输出的棋盘:
上面已经看出我的棋盘初始化已经完成,所以接下来我们需要考虑如何让电脑或用户进行下棋操作了。
电脑走需要让电脑产生随机坐标,然后在数组的这个坐标位置放入一个 ‘#’ 作为电脑的棋子。
所以代码如下:
void ComputerMove(char board[ROWS][COLS], int rows, int cols)
{
int x, y;
printf("电脑走:\n");
while (1)
{
x = rand() % rows;
y = rand() % cols;
if (board[x][y] == ' ')
{
board[x][y] = '#';
break;
}
}
}
rand()是用来生成随机数的,这里我们还需要在主函数main中加上一行代码;
由于我们不希望他多次生成随机数,生成一次就足够用了,所以直接在创建choice变量的下一行加上下面这个代码即可:
srand((unsigned int)time(NULL));
注意生成随机数需要引用time.h这个头文件。
电脑下完棋后,需要轮到我们的用户进行下棋操作,这个函数和电脑下棋大同小异;
玩家下的棋,用 '*' 记录;
但是要注意人的习惯不会输入(0,0)这个坐标,而是输入(1,1);
所以这里我们要给传过去的参数-1。
解决这个问题后的代码如下:
void player_move(char board[ROWS][COLS], int rows, int cols)
{
int x, y;
printf("玩家走:\n");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= rows && y >= 1 && y <= cols)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = 'O';
break;
}
else printf("坐标被占用,请重新输入!\n");
}
else printf(" 坐标非法,请重新输入!\n");
}
}
在双方走完后,我们需要判断是否胜利,也就是判断之前我们提到的规则,
这时我们需要编写一个is_win()函数来检测并返回一个值来判断谁获胜。
代码如下:
char is_win(char board[ROWS][COLS], int rows, int cols)
{
int i;
for (i = 0; i < rows; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
return board[i][1];
}
for (i = 0; i < cols; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
return board[1][i];
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
return board[1][1];
else if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
else if (is_full(board, rows, cols))
return 'q';
return 0;
}
由于可能出现棋盘下满,却没人获胜的情况,
所以我们需要在其中判断一下平局的情况,
这里我们编写了一个is_full()函数,用来判断棋盘是否下满:
int is_full(char board[ROWS][COLS], int rows, int cols)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
这时程序的雏形已经基本完成,我们只需要按我们自己理解的游戏步骤来完成game()函数即可。
所以完成后的game()函数如下所示:
void game()
{
char win = 0;
char arr[ROWS][COLS];
init_board(board, ROWS, COLS);
do
{
computer_move(board, ROWS, COLS);
show_board(board, ROWS, COLS);
win = is_win(board, ROWS, COLS);
if (win != 0)
break;
player_move(board, ROWS, COLS);
show_board(board, ROWS, COLS);
win = is_win(board, ROWS, COLS);
} while (win == 0);
if (win == '#')
printf("恭喜,电脑赢了!\n");
if (win == '*')
printf("恭喜,你赢了!\n");
if (win == 'q')
printf("平局!\n");
}
这样,将上面各个部分合在一起,这个程序就被我们完成了。
完整代码
下面放上我自己写的,比较丑陋的代码:
我的代码分为三部分,
第一部分是game.h头文件:
#pragma once
#include<stdio.h>
#include<time.h>
#include<Windows.h>
#include<stdlib.h>
#define ROWS 3
#define COLS 3
void show_board(char board[ROWS][COLS], int rows, int cols);
void init_board(char board[ROWS][COLS], int rows, int cols);
void computer_move(char board[ROWS][COLS], int rows, int cols);
void player_move(char board[ROWS][COLS], int rows, int cols);
char is_win(char board[ROWS][COLS], int rows, int cols);
为了方便我们随时改变棋盘大小,所以我们在game.h头文件中,定义棋盘行(ROWS)和列(COLS)的大小。
第二部分是game.c源文件的函数部分:
#include"game.h"
//方法1:
void show_board(char board[ROWS][COLS], int rows, int cols)
{
int i;
for (int i = 0; i < rows; i++)
{
printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);
if (i != rows - 1)
printf(" ---|---|--- \n");
}
}
//方法2
//void show_board(char board[ROWS][COLS], int rows, int cols)
//{
// int i = 0;
// int j = 0;
// for (i = 0; i < rows; i++)
// {
// for (j = 0; j < cols; j++)
// {
// printf(" %c ", board[i][j]);
// if (j < cols - 1)
// printf("|");
// }
// printf("\n");
// if (i < rows - 1)
// {
// for (j = 0; j < cols; j++)
// {
// printf("---");
// if (j < cols - 1)
// printf("|");
// }
// }
// printf("\n");
// }
//}
void init_board(char board[ROWS][COLS], int rows, int cols)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = ' ';
}
}
}
void computer_move(char board[ROWS][COLS], int rows, int cols)
{
int x, y, i;
printf("电脑走:\n");
while (1)
{
x = rand() % rows;
y = rand() % cols;
for (i = 0; i < rows; i++)
{
if (board[i][0] == board[i][1] && board[i][0] == '#' && board[i][2] == ' ')
{
board[i][2] = '#';
goto flag1;
}
else if (board[i][1] == board[i][2] && board[i][1] == '#' && board[i][0] == ' ')
{
board[i][0] = '#';
goto flag1;
}
else if (board[i][0] == board[i][2] && board[i][2] == '#' && board[i][1] == ' ')
{
board[i][1] = '#';
goto flag1;
}
else if (board[0][i] == board[1][i] && board[0][i] == '#' && board[2][i] == ' ')
{
board[2][i] = '#';
goto flag1;
}
else if (board[1][i] == board[2][i] && board[1][i] == '#' && board[0][i] == ' ')
{
board[0][i] = '#';
goto flag1;
}
else if (board[0][i] == board[2][i] && board[2][i] == '#' && board[1][i] == ' ')
{
board[1][i] = '#';
goto flag1;
}
else if (board[0][0] == board[1][1] && board[0][0] == '#' && board[2][2] == ' ')
{
board[2][2] = '#';
goto flag1;
}
else if (board[1][1] == board[2][2] && board[1][1] == '#' && board[0][0] == ' ')
{
board[0][0] = '#';
goto flag1;
}
else if (board[0][0] == board[2][2] && board[0][0] == '#' && board[1][1] == ' ')
{
board[1][1] = '#';
goto flag1;
}
else if (board[0][2] == board[1][1] && board[1][1] == '#' && board[2][0] == ' ')
{
board[2][0] = '#';
goto flag1;
}
else if (board[1][1] == board[2][0] && board[1][1] == '#' && board[0][2] == ' ')
{
board[0][2] = '#';
goto flag1;
}
else if (board[2][0] == board[0][2] && board[2][0] == 'X' && board[1][1] == ' ')
{
board[1][1] = '#';
goto flag1;
}
}
for (i = 0; i < rows; i++)
{
if (board[i][0] == board[i][1] && board[i][0] == 'O' && board[i][2] == ' ')
{
board[i][2] = '#';
goto flag1;
}
else if (board[i][1] == board[i][2] && board[i][1] == 'O' && board[i][0] == ' ')
{
board[i][0] = '#';
goto flag1;
}
else if (board[i][0] == board[i][2] && board[i][2] == 'O' && board[i][1] == ' ')
{
board[i][1] = '#';
goto flag1;
}
else if (board[0][i] == board[1][i] && board[0][i] == 'O' && board[2][i] == ' ')
{
board[2][i] = '#';
goto flag1;
}
else if (board[1][i] == board[2][i] && board[1][i] == 'O' && board[0][i] == ' ')
{
board[0][i] = '#';
goto flag1;
}
else if (board[0][i] == board[2][i] && board[2][i] == 'O' && board[1][i] == ' ')
{
board[1][i] = '#';
goto flag1;
}
else if (board[0][0] == board[1][1] && board[0][0] == 'O' && board[2][2] == ' ')
{
board[2][2] = '#';
goto flag1;
}
else if (board[1][1] == board[2][2] && board[1][1] == 'O' && board[0][0] == ' ')
{
board[0][0] = '#';
goto flag1;
}
else if (board[0][0] == board[2][2] && board[0][0] == 'O' && board[1][1] == ' ')
{
board[1][1] = '#';
goto flag1;
}
else if (board[0][2] == board[1][1] && board[1][1] == 'O' && board[2][0] == ' ')
{
board[2][0] = '#';
goto flag1;
}
else if (board[1][1] == board[2][0] && board[1][1] == 'O' && board[0][2] == ' ')
{
board[0][2] = '#';
goto flag1;
}
else if (board[2][0] == board[0][2] && board[2][0] == 'O' && board[1][1] == ' ')
{
board[1][1] = '#';
goto flag1;
}
}
if (board[x][y] == ' ')
{
board[x][y] = '#';
goto flag1;
}
}
flag1:;
}
void player_move(char board[ROWS][COLS], int rows, int cols)
{
int x, y;
printf("玩家走:\n");
while (1)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= rows && y >= 1 && y <= cols)
{
if (board[x - 1][y - 1] == ' ')
{
board[x - 1][y - 1] = 'O';
break;
}
else printf("坐标被占用,请重新输入:>\n");
}
else printf(" 坐标非法,请重新输入:>\n");
}
}
int is_full(char board[ROWS][COLS], int rows, int cols)
{
int i, j;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
if (board[i][j] == ' ')
return 0;
}
}
return 1;
}
char is_win(char board[ROWS][COLS], int rows, int cols)
{
int i;
for (i = 0; i < rows; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
return board[i][1];
}
for (i = 0; i < cols; i++)
{
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
return board[1][i];
}
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
return board[1][1];
else if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
return board[1][1];
else if (is_full(board, rows, cols))
return 'q';
return 0;
}
这一部分是经过发挥的,让电脑更智能,但代码部分我并没有想出简化的写法,只好一步步的用if语句进行完成了。
第三部分是test.c源文件的游戏测试部分:
#include"game.h"
void menu()
{
printf("***************************************\n");
printf("* 1.play 0.exit *\n");
printf("***************************************\n");
}
void first_move()
{
printf("***************************************\n");
printf("* 1.computer first 2.player first *\n");
printf("***************************************\n");
}
void game()
{
int input_1, win;
char board[ROWS][COLS];
init_board(board, ROWS, COLS);
first_move();
again:
printf("请选择谁先走:>");
scanf("%d", &input_1);
switch (input_1)//选择谁先走~
{
case 1:
do
{
computer_move(board, ROWS, COLS);
show_board(board, ROWS, COLS);
win = is_win(board, ROWS, COLS);//每走一步,我们需要判断是否输赢
if (win != 0)
break;
player_move(board, ROWS, COLS);
show_board(board, ROWS, COLS);
win = is_win(board, ROWS, COLS);//每走一步,我们需要判断是否输赢
} while (win == 0);
if (win == '#')
printf("恭喜,电脑赢了!\n");
if (win == '*')
printf("恭喜,你赢了!\n");
if (win == 'q')
printf("平局!\n");
break;
case 2:
do
{
show_board(board, ROWS, COLS);
player_move(board, ROWS, COLS);
show_board(board, ROWS, COLS);
win = is_win(board, ROWS, COLS);
if (win != 0)
break;
computer_move(board, ROWS, COLS);
win = is_win(board, ROWS, COLS);
} while (win == 0);
if (win == '*')
printf("恭喜!电脑赢了!\n");
if (win == '#')
printf("恭喜!你赢了!\n");
if (win == 'q')
printf("平局!\n");
break;
default:
printf(" 输入错误,请重新输入!\n");
goto again;
}
}
int main()
{
int input;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (input);
return 0;
}