目录
1.头文件game.h
可取点:在头文件中,使用#define定义行和列类,在后续的程序中行和列没有具体数值,全部通过ROW和COL表示。这样以后想要对棋盘的大小进行修改的时候,直接在头文件中对#define进行修改即可。
#define ROW 3
#define COL 3
#pragma once
#define ROW 3
#define COL 3
#include<stdio.h>
//菜单函数
void menu();
//是否继续游戏菜单
void ContinueMenu();
//游戏函数
void game();
//初始化棋盘
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 Judge(char Board[ROW][COL], int row, int col);
//判断棋盘是否下满了
int Full(char Board[ROW][COL], int row, int col);
2.源文件
2.1主函数
//井字棋游戏
#include"game.h"
int main()
{
int input = 0;
int con = 0;
do
{
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("开始游戏\n");
game();
ContinueMenu();
printf("请选择:");
scanf("%d", &con);
break;
case 2:
printf("退出游戏\n");
break;
default:
printf("输入无效,请重新选择\n");
break;
}
}
while (con == 1 ||(input != 2 && input != 1));
return 0;
}
2.2子函数
要点
1. 子函数【2.2棋盘生成函数】,生成的棋盘样式如下图,不知道这样直观的展示出来,会方便理解该程序吗?
2.子函数【2.3玩家下棋函数】中
因为数组的起始位置为[0][0],但大众一般认为的起始位置为[1][1],所以要进行+1、-1调整,以符合大众的认知。
if ((i > 0 && i < row+1 && j > 0 && j < col+1) && (Board[i-1][j-1] == ' '))
{
Board[i-1][j-1] = 'O';
...
如果输入的坐标无效,就要重新输入,为此设置了一个标志flag。如果flog=0,说明输入的坐标无效(位置被占用或超出棋盘范围),需要重新输入。
3.生成随机数
利用时间戳生成随机数,需要的头文件为#include<stdlib.h>和#include<time.h>,使用rand()函数生成随机数之前,需要先使用srand()函数,生成随机数种子。
#include<stdlib.h>
#include<time.h>
int ran;
srand((unsigned int)time(NULL));
ran = rand();
4.【2.5判断胜负函数】
如果是玩家胜利,就返回玩家使用的符号‘O’
如果是电脑胜利,就返回电脑使用的符号‘X’
如果是棋盘满了且未分出胜负,就返回‘E’
如果以上三种情况都不是,就返回'C',继续进行游戏
#define ROW 3
#define COL 3
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
//1.菜单函数
void menu()
{
printf("************************\n");
printf("*****1.Play 2.Exit*****\n");
printf("************************\n");
}
//2.游戏函数
void game()
{
char result;
//生成棋盘
char Board[ROW][COL] = { 0 };
//初始化棋盘
InitBoard(Board, ROW, COL);
//展示棋盘
DisplayBoard(Board, ROW, COL);
while (1)
{
//玩家下棋
PlayerMove(Board, ROW, COL);
DisplayBoard(Board, ROW, COL);
//判断胜负
result = Judge(Board, ROW, COL);
if (result != 'C')
{
break;
}
//电脑下棋
ComputerMove(Board, ROW, COL);
DisplayBoard(Board, ROW, COL);
//判断胜负
result = Judge(Board, ROW, COL);
if (result != 'C')
{
break;
}
}
if (result == 'O')
printf("玩家获胜\n");
else if (result == 'X')
printf("电脑获胜\n");
else
printf("平局\n");
}
//2.1棋盘初始化函数
void InitBoard(char Board[ROW][COL],int row,int col)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
Board[i][j] = ' ';
}
}
}
//2.2棋盘生成函数
void DisplayBoard(char Board[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
//1.打印一行数据
printf(" %c ", Board[i][j]);
if (j < col - 1)
printf("|");
}
printf("\n");
//2.打印分割线
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
printf("|");
}
printf("\n");
}
}
}
//2.3玩家下棋
void PlayerMove(char Board[ROW][COL], int row, int col)
{
int i, j;
int flag = 1;
printf("玩家回合\n");
do
{
printf("请输入坐标:");
scanf("%d%d", &i, &j);
//因为数组的起始位置为[0][0],但大众一般认为的起始位置为[1][1],所以要进行+1、-1调整
if ((i > 0 && i < row+1 && j > 0 && j < col+1) && (Board[i-1][j-1] == ' '))
{
Board[i-1][j-1] = 'O';
flag = 1;
}
else
{
//如果输入的坐标无效,就要重新输入,为此设置了一个标志flag
printf("坐标无效,请重新输入\n");
flag = 0;
}
}
while (flag == 0);
}
//2.4电脑下棋
void ComputerMove(char Board[ROW][COL], int row, int col)
{
int i, j;
int flag = 1;
printf("电脑回合\n");
do
{
//通过时间生成随机位置
srand((unsigned int)time(NULL));
//通过模row取余数,就能让i在0~row-1之间浮动;j同理
i = rand() % row;
j = rand() % col;
if (Board[i][j] == ' ')
{
Board[i][j] = 'X';
flag = 1;
}
else
flag = 0;
}
while (flag == 0);
}
//2.5判断胜负函数
char Judge(char Board[ROW][COL], int row, int col)
{
int i, j;
//判断行是否连成三个
for (i = 0; i < row; i++)
{
int count = 0;
for (j = 0; j < col-1; j++)
{
if ((Board[i][0] != ' ') && (Board[i][j] == Board[i][j + 1]))
{
count++;
}
}
if (col-1 == count)
return Board[i][0];
}
//判断列是否连成三个
for (j = 0; j < col; j++)
{
int count = 0;
for (i = 0; i < row-1; i++)
{
if ((Board[0][j] != ' ') && (Board[i][j] == Board[i+1][j]))
{
count++;
}
}
//三个元素相等,计数器的数字应该比行数少1。可以用两刀切三段理解。
if (row - 1 == count)
return Board[0][j];
}
//判断斜线是否连成三个
if (Board[0][0] == Board[1][1] && Board[2][2] == Board[1][1] && Board[0][0] != ' ')
return Board[0][0];
if (Board[0][2] == Board[1][1] && Board[2][0] == Board[1][1] && Board[0][2] != ' ')
return Board[0][2];
//判断是否平局
if (1 == Full(Board, ROW, COL))
{
return 'E';
}
return 'C';
}
//2.5.1判断棋盘是否满了函数
int Full(char Board[ROW][COL], int row, int col)
{
int i, j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
if (Board[i][j] == ' ')
return 0;
}
}
return 1;
}
//3.继续游戏函数
void ContinueMenu()
{
printf("************************\n");
printf("游戏已结束,是否继续游戏\n");
printf("***1.Continue 2.Exit***\n");
printf("************************\n");
}
3.程序的不足
程序的不足出现在子函数【2.4电脑下棋】函数中。在实际游玩的发现,在游戏的后期,电脑下棋的速度会变慢。我分析原因如下:
1.电脑下棋的位置完全靠随机数生成,这就导致在游玩的后期,当大多数格子被占用时,电脑往往需要进行多次循环,才能随机出新的位置,耗费计算资源。
2.完全随机的落子,也导致电脑下棋没什么策略性,导致玩家很容易胜利,游戏的趣味性降低。
如果大家有好的解决方案,欢迎留言!
//2.4电脑下棋
void ComputerMove(char Board[ROW][COL], int row, int col)
{
int i, j;
int flag = 1;
printf("电脑回合\n");
do
{
//通过时间生成随机位置
srand((unsigned int)time(NULL));
//通过模row取余数,就能让i在0~row-1之间浮动;j同理
i = rand() % row;
j = rand() % col;
if (Board[i][j] == ' ')
{
Board[i][j] = 'X';
flag = 1;
}
else
flag = 0;
}
while (flag == 0);
}