目录:
在前面的博客中已经对游戏的整体框架做出了分析,在这片博客中将会对整个游戏进行功能的完善的改进。
初始化和打印棋盘的功能已经在前面给出,现在还需要实现的功能便是玩家走、电脑走、和判断输赢的函数。
玩家走:PlayerMove()
在这个函数中通过数组下标确定一个二维数组中的位置,只要该位置没有放入“棋子”,便把该数组空间赋值为'X'
,所以确定该函数的返回值为void
,需要将对数组通过循环遍历的临界值ROW COL
传过去。所以函数的功能实现如下:
void PlayerMove(char arr[ROW][COL], int row, int col)
{
int r = 0;
int c = 0;
while (1)
{
printf("玩家走:>\n");
scanf("%d%d", &r, &c);
if (r>=1 && r<=row && c>=1 && c<=col)
{
if (arr[r-1][c-1] == ' ')
{
arr[r-1][c-1] = 'X';
break;
}
else
{
printf("已选择过!请重新输入\n");
}
}
else
{
printf("输入有误!请重新输入\n");
}
}
}
电脑走:ComputerMove()
与电脑走的函数方法类似,不同的是电脑随机生成一个坐标,如果该座标没有放入“棋子”,便将该数组空间赋值为'0'
,返回值同样为void
同样需要把ROW COL
传过去。
void ComputerMove(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
printf("电脑走:>\n");
while (1)
{
i = rand()%row;
j = rand()%col;
if (arr[i][j] == ' ')
{
arr[i][j] = '0';
break;
}
}
}
rand()函数在调用之前,必须先调用srand()生成随机数起点,因起点生成后不许再变,所以将其放入main()中之调用一次即可。
判断输赢 IsWin()
判断输赢要分四种情况讨论,玩家赢、电脑赢、平局、以上三种情况均不是,比赛继续。
- 玩家赢、电脑赢、都可以通过循环遍历数组,通过if语句判断棋盘的状态来完成。
如果发现棋盘已满,而胜负未定则为平局。
以上情况均不是则继续游戏。
如何实现这个逻辑,可以通过返回值来判断:当玩家赢返回X
,电脑赢返回0
,平局返回空字符,如果继续不做处理。
char IsWin(char arr[ROW][COL], int row, int col)
{
int i = 0;
for (i=0; i<row; i++)
{
//判断是否同行
if (arr[i][0]==arr[i][1] && arr[i][1]==arr[i][2] && arr[i][1] != ' ')
{
return arr[i][1];
}
//列
else if (arr[0][i]==arr[1][i] && arr[1][i]==arr[2][i] && arr[1][i] != ' ')
{
return arr[1][i];
}
//斜
else if ((arr[0][0]==arr[1][1] && arr[1][1]==arr[2][2] && arr[1][1] != ' ')
||(arr[0][2]==arr[1][1] && arr[1][1]==arr[2][0] && arr[1][1] != ' '))
{
return arr[1][1];
}
else
{
;
}
}
if (IsFull(arr,ROW,COL))
{
return ' ';
}
}
//判断棋满
static int IsFull(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i=0; i<row; i++)
{
for (j=0; j<col; j++)
{
if (arr[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
IsFull()
将返回值设置成1/0可以很好的在调用的if语句中能够做出判断,前面加上static
是为这个函数不需要在其他地方调用,只用于本源码,避免命名冲突。
电脑下棋算法优化
标题很高大上,其实实现方法很挫,说白了就是对棋盘当前状态的一种判断,与判断输赢的逻辑方法完全一样,根据判断的结果,电脑给出当下的最优选择。就是将很多种状态提前告知电脑,然后判断符合状态后,接下来的走法也人为规定好了。这就跟现在的电脑下象棋,围棋是一个道理,就是检索内存中的当前棋盘已有最优方案。
整体的逻辑如下:先判断是否抢3,如果有符合当前电脑能赢的方案,则优先抢其。
接着选择是否堵3,如果玩家出现再落一子就赢的情况,要对其进行封堵。
最后一点,在下棋的过程中,由于棋盘限制,中间位置尤为重要,占据它,将占据主动权,如果为空,则选其。
void ComputerMove(char arr[ROW][COL], int row, int col)
{
int m = 0;
int i = 0;
int j = 0;
printf("电脑走:>\n");
//抢3
//抢行
for (m=0; m<ROW; m++)
{
if (arr[m][0]=='0' && arr[m][1]=='0' && arr[m][2]==' ')
{
arr[m][2] = '0';
goto exit;
}
if (arr[m][0]=='0' && arr[m][2]=='0' && arr[m][1]==' ')
{
arr[m][1] = '0';
goto exit;
}
if (arr[m][1]=='0' && arr[m][2]=='0' && arr[m][0]==' ')
{
arr[m][0] = '0';
goto exit;
}
}
//抢列
for (m=0; m<COL; m++)
{
if (arr[0][m]=='0' && arr[1][m]=='0' && arr[2][m]==' ')
{
arr[2][m] = '0';
goto exit;
}
if (arr[0][m]=='0' && arr[2][m]=='0' && arr[1][m]==' ')
{
arr[1][m] = '0';
goto exit;
}
if (arr[1][m]=='0' && arr[2][m]=='0' && arr[0][m]==' ')
{
arr[0][m] = '0';
goto exit;
}
}
//抢斜行
//情况一:斜率为-1的斜行
if (arr[0][0]=='0' && arr[1][1]=='0' && arr[2][2]==' ')
{
arr[2][2] = '0';
goto exit;
}
if (arr[0][0]=='0' && arr[2][2]=='0' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='0' && arr[2][2]=='0' && arr[0][0]==' ')
{
arr[0][0] = '0';
goto exit;
}
//情况二:斜率为1的斜行
if (arr[2][0]=='0' && arr[1][1]=='0' && arr[0][2]==' ')
{
arr[0][2] = '0';
goto exit;
}
if (arr[2][0]=='0' && arr[0][2]=='0' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='0' && arr[0][2]=='0' && arr[2][0]==' ')
{
arr[2][0] = '0';
goto exit;
}
//堵
//堵行
for (m=0; m<ROW; m++)
{
if (arr[m][0]=='X' && arr[m][1]=='X' && arr[m][2]==' ')
{
arr[m][2] = '0';
goto exit;
}
if (arr[m][0]=='X' && arr[m][2]=='X' && arr[m][1]==' ')
{
arr[m][1] = '0';
goto exit;
}
if (arr[m][1]=='X' && arr[m][2]=='X' && arr[m][0]==' ')
{
arr[m][0] = '0';
goto exit;
}
}
//堵列
for (m=0; m<COL; m++)
{
if (arr[0][m]=='X' && arr[1][m]=='X' && arr[2][m]==' ')
{
arr[2][m] = '0';
goto exit;
}
if (arr[0][m]=='X' && arr[2][m]=='X' && arr[1][m]==' ')
{
arr[1][m] = '0';
goto exit;
}
if (arr[1][m]=='X' && arr[2][m]=='X' && arr[0][m]==' ')
{
arr[0][m] = '0';
goto exit;
}
}
//堵斜行
//情况一:斜率为-1的斜行
if (arr[0][0]=='X' && arr[1][1]=='X' && arr[2][2]==' ')
{
arr[2][2] = '0';
goto exit;
}
if (arr[0][0]=='X' && arr[2][2]=='X' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='X' && arr[2][2]=='X' && arr[0][0]==' ')
{
arr[0][0] = '0';
goto exit;
}
//情况二:斜率为1的斜行
if (arr[2][0]=='X' && arr[1][1]=='X' && arr[0][2]==' ')
{
arr[0][2] = '0';
goto exit;
}
if (arr[2][0]=='X' && arr[0][2]=='X' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='X' && arr[0][2]=='X' && arr[2][0]==' ')
{
arr[2][0] = '0';
goto exit;
}
//抢2
if (arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
while (1)
{
i = rand()%row;
j = rand()%col;
if (arr[i][j] == ' ')
{
arr[i][j] = '0';
break;
}
}
exit:;
}
由于前面做出选择后,后面将不再落子,前面未作处理,才来到后面。所以通过goto
语句进行跳转,以实现其逻辑。
这样的不算优化的优化,也仅仅是让电脑不再继续傻下去。由于棋盘的限制,如果有可能,可以将其所有的棋盘状态进行穷举,而电脑每一步落子都是对自己最优,对玩家最不利的,这样人想赢就难了,因为人脑的逻辑分析能力和存储记忆能力是远不及计算机的。
礼让电脑
由于棋盘大小限制,和谁先走就有抢中间位置的决定权,如果感觉自己萌萌哒,可以让电脑先走~。~,变化只是game函数的调用顺序问题以及多一步switch语句的选择,将会在后面完整代码中体现。
完整代码展示
game.h
#ifndef __GAME_H__
#define __GAME_H__
#define ROW 3
#define COL 3
void InitBoard(char arr[ROW][COL], int row, int col);//初始化棋盘
void PrintBoard(char arr[ROW][COL], int row, int col);//打印棋盘
void PlayerMove(char arr[ROW][COL], int row, int col);//玩家走
char IsWin(char arr[ROW][COL], int row, int col);//判断输赢
void ComputerMove(char arr[ROW][COL], int row, int col);//电脑走
#endif //__GAME_H__
game.c
#include <stdio.h>
#include <stdlib.h>
#include "game.h"
void InitBoard(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i=0; i<row; i++)
{
for (j=0; j<col; j++)
{
arr[i][j] = ' ';
}
}
}
void PrintBoard(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i=0; i<row; i++)
{
for (j=0; j<col; j++)
{
printf(" %c ",arr[i][j]);
if (j<col-1)
{
printf("|");
}
}
printf("\n");
if (i<row-1)
{
for (j=0; j<col; j++)
{
printf("---");
if (j<col-1)
{
printf("|");
}
}
printf("\n");
}
}
}
void PlayerMove(char arr[ROW][COL], int row, int col)
{
int r = 0;
int c = 0;
while (1)
{
printf("玩家走:>\n");
scanf("%d%d", &r, &c);
if (r>=1 && r<=row && c>=1 && c<=col)
{
if (arr[r-1][c-1] == ' ')
{
arr[r-1][c-1] = 'X';
break;
}
else
{
printf("已选择过!请重新输入\n");
}
}
else
{
printf("输入有误!请重新输入\n");
}
}
}
static int IsFull(char arr[ROW][COL], int row, int col)
{
int i = 0;
int j = 0;
for (i=0; i<row; i++)
{
for (j=0; j<col; j++)
{
if (arr[i][j] == ' ')
{
return 0;
}
}
}
return 1;
}
char IsWin(char arr[ROW][COL], int row, int col)
{
int i = 0;
for (i=0; i<row; i++)
{
//判断是否同行
if (arr[i][0]==arr[i][1] && arr[i][1]==arr[i][2] && arr[i][1] != ' ')
{
return arr[i][1];
}
//列
else if (arr[0][i]==arr[1][i] && arr[1][i]==arr[2][i] && arr[1][i] != ' ')
{
return arr[1][i];
}
//斜
else if ((arr[0][0]==arr[1][1] && arr[1][1]==arr[2][2] && arr[1][1] != ' ')
||(arr[0][2]==arr[1][1] && arr[1][1]==arr[2][0] && arr[1][1] != ' '))
{
return arr[1][1];
}
else
{
;
}
}
if (IsFull(arr,ROW,COL))
{
return ' ';
}
}
void ComputerMove(char arr[ROW][COL], int row, int col)
{
int m = 0;
int i = 0;
int j = 0;
printf("电脑走:>\n");
//抢3
//抢行
for (m=0; m<ROW; m++)
{
if (arr[m][0]=='0' && arr[m][1]=='0' && arr[m][2]==' ')
{
arr[m][2] = '0';
goto exit;
}
if (arr[m][0]=='0' && arr[m][2]=='0' && arr[m][1]==' ')
{
arr[m][1] = '0';
goto exit;
}
if (arr[m][1]=='0' && arr[m][2]=='0' && arr[m][0]==' ')
{
arr[m][0] = '0';
goto exit;
}
}
//抢列
for (m=0; m<COL; m++)
{
if (arr[0][m]=='0' && arr[1][m]=='0' && arr[2][m]==' ')
{
arr[2][m] = '0';
goto exit;
}
if (arr[0][m]=='0' && arr[2][m]=='0' && arr[1][m]==' ')
{
arr[1][m] = '0';
goto exit;
}
if (arr[1][m]=='0' && arr[2][m]=='0' && arr[0][m]==' ')
{
arr[0][m] = '0';
goto exit;
}
}
//抢斜行
//情况一:斜率为-1的斜行
if (arr[0][0]=='0' && arr[1][1]=='0' && arr[2][2]==' ')
{
arr[2][2] = '0';
goto exit;
}
if (arr[0][0]=='0' && arr[2][2]=='0' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='0' && arr[2][2]=='0' && arr[0][0]==' ')
{
arr[0][0] = '0';
goto exit;
}
//情况二:斜率为1的斜行
if (arr[2][0]=='0' && arr[1][1]=='0' && arr[0][2]==' ')
{
arr[0][2] = '0';
goto exit;
}
if (arr[2][0]=='0' && arr[0][2]=='0' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='0' && arr[0][2]=='0' && arr[2][0]==' ')
{
arr[2][0] = '0';
goto exit;
}
//堵
//堵行
for (m=0; m<ROW; m++)
{
if (arr[m][0]=='X' && arr[m][1]=='X' && arr[m][2]==' ')
{
arr[m][2] = '0';
goto exit;
}
if (arr[m][0]=='X' && arr[m][2]=='X' && arr[m][1]==' ')
{
arr[m][1] = '0';
goto exit;
}
if (arr[m][1]=='X' && arr[m][2]=='X' && arr[m][0]==' ')
{
arr[m][0] = '0';
goto exit;
}
}
//堵列
for (m=0; m<COL; m++)
{
if (arr[0][m]=='X' && arr[1][m]=='X' && arr[2][m]==' ')
{
arr[2][m] = '0';
goto exit;
}
if (arr[0][m]=='X' && arr[2][m]=='X' && arr[1][m]==' ')
{
arr[1][m] = '0';
goto exit;
}
if (arr[1][m]=='X' && arr[2][m]=='X' && arr[0][m]==' ')
{
arr[0][m] = '0';
goto exit;
}
}
//堵斜行
//情况一:斜率为-1的斜行
if (arr[0][0]=='X' && arr[1][1]=='X' && arr[2][2]==' ')
{
arr[2][2] = '0';
goto exit;
}
if (arr[0][0]=='X' && arr[2][2]=='X' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='X' && arr[2][2]=='X' && arr[0][0]==' ')
{
arr[0][0] = '0';
goto exit;
}
//情况二:斜率为1的斜行
if (arr[2][0]=='X' && arr[1][1]=='X' && arr[0][2]==' ')
{
arr[0][2] = '0';
goto exit;
}
if (arr[2][0]=='X' && arr[0][2]=='X' && arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
if (arr[1][1]=='X' && arr[0][2]=='X' && arr[2][0]==' ')
{
arr[2][0] = '0';
goto exit;
}
//抢2
if (arr[1][1]==' ')
{
arr[1][1] = '0';
goto exit;
}
while (1)
{
i = rand()%row;
j = rand()%col;
if (arr[i][j] == ' ')
{
arr[i][j] = '0';
break;
}
}
exit:;
}
test.c
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include "game.h"
void menu1()
{
printf("****************************************\n");
printf("******* 1.play *******\n");
printf("******* 0.exit *******\n");
printf("****************************************\n");
}
void menu2()
{
printf("****************************************\n");
printf("******* 1.player_first *******\n");
printf("******* 2.computer_first *******\n");
printf("****************************************\n");
}
void player_first_game()
{
char arr[ROW][COL];
InitBoard(arr,ROW,COL);
PrintBoard(arr,ROW,COL);
while (1)
{
PlayerMove(arr,ROW,COL);
PrintBoard(arr,ROW,COL);
//判断输赢,分为四种情况,如果不是以下三种情况游戏继续
if (IsWin(arr,ROW,COL)==' ')
{
printf("很遗憾,平局!\n");
break;
}
if (IsWin(arr,ROW,COL)=='X')
{
printf("玩家赢!\n");
break;
}
if (IsWin(arr,ROW,COL)=='0')
{
printf("电脑赢!\n");
break;
}
ComputerMove(arr,ROW,COL);
PrintBoard(arr,ROW,COL);
if (IsWin(arr,ROW,COL)==' ')
{
printf("很遗憾,平局!\n");
break;
}
if (IsWin(arr,ROW,COL)=='X')
{
printf("玩家赢!\n");
break;
}
if (IsWin(arr,ROW,COL)=='0')
{
printf("电脑赢!\n");
break;
}
}
}
void computer_first_game()
{
char arr[ROW][COL];
InitBoard(arr,ROW,COL);
while (1)
{
ComputerMove(arr,ROW,COL);
PrintBoard(arr,ROW,COL);
//判断输赢,分为四种情况,如果不是以下三种情况游戏继续
if (IsWin(arr,ROW,COL)==' ')
{
printf("很遗憾,平局!\n");
break;
}
if (IsWin(arr,ROW,COL)=='X')
{
printf("玩家赢!\n");
break;
}
if (IsWin(arr,ROW,COL)=='0')
{
printf("电脑赢!\n");
break;
}
PlayerMove(arr,ROW,COL);
PrintBoard(arr,ROW,COL);
if (IsWin(arr,ROW,COL)==' ')
{
printf("很遗憾,平局!\n");
break;
}
if (IsWin(arr,ROW,COL)=='X')
{
printf("玩家赢!\n");
break;
}
if (IsWin(arr,ROW,COL)=='0')
{
printf("电脑赢!\n");
break;
}
}
}
int main()
{
int input1 = 0;
int input2 = 0;
srand((unsigned int)time(NULL));
do
{
menu1();
scanf("%d",&input1);
switch(input1)
{
case 1:
// game(此处可以添加更完整的游戏功能实现);
menu2();
printf("请选择谁先走:>\n");
scanf("%d",&input2);
switch(input2)
{
case 1:
player_first_game();
goto flag;
case 2:
computer_first_game();
}
flag:;
break;
case 0:
printf("游戏结束\n");
return 0;
default :
printf("输入错误!请重新输入\n");
break;
}
}while (input1);
return 0;
}