C语言实现井字棋游戏(含算法优化)
目录
序言
代码1
小结
算法优化
代码2
总结
序言
井字棋
英文名叫Tic-Tac-Toe
想必大家对这个小游戏都不陌生了
无论是小时候课堂上被画的全是”井“字的草稿纸
还是长大后在”炼狱小镇“
那么,如何通过C语言来实现呢?
我们需要大量的自定义函数来满足游戏功能
需要函数来展示井字棋的棋盘
需要函数来实现玩家和机器下棋
那么
首先,我们先构思一个框架
头文件 #include<stdio.h> …… 主函数中自定义函数 int menu() 初始界面展示 int Game() 游戏运行算法其他自定义函数 (首先想到的是用3*3的数组来表达棋盘 并通过放入字符来表达落子) void Board(char board[ROW][COL], int row, int col) 棋盘 void player_move(char board[ROW][COL], int row, int col) 玩家下棋 void computer_move(char board[ROW][COL], int row, int col) 电脑下棋 char win_lose(char board[ROW][COL], int row, int col) 判断胜负
由于这次代码有点小多 所以分类建立了三个文件来存储代码
代码1
根据咱们设想的框架来逐步完善程序
game.c
游戏功能函数的实现
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){board[i][j] = ' ';}}
}
//展示棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){//打印数据int j = 0;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 < col; j++){printf("---");if (j < col - 1)printf("|");}printf("\n");}}
}
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("玩家下棋\n");while (1){printf("请输入坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){//下棋if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else{printf("该坐标被占用,请重新输入\n");}}else{printf("坐标非法,请重新输入\n");}}
}
//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("电脑下棋:>\n");while (1){x = rand() % row;//0~2y = rand() % col;//0~2if (board[x][y] == ' '){board[x][y] = '#';break;}}
}
//判断棋盘是否被下满
static int if_full(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){if (board[i][j] == ' '){return 0;//没满}}}return 1;//满了
}
//判断胜负
char is_win(char board[ROW][COL], int row, int col)
{int i = 0;//判断行for (i = 0; i < row; 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 < col; 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];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' '){return board[1][1];}//判断平局if (if_full(board, row, col) == 1){return 'Q';}//继续return 'C';
}
game.h
函数的声明
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.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 player_move(char board[ROW][COL], int row, int col);//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);//判断输赢
char is_win(char board[ROW][COL], int row, int col);//1. 玩家赢了 - hr
//2. 电脑赢了 - cw
//3. 平局 - d
//4. 游戏继续 - c
test.c
测试游戏
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{system("cls");printf("Tic-Tac-Toe");printf("**************************************\n");printf("*********** 1.PLAY ***********\n");printf("**************************************\n");printf("*********** 0.QUET ***********\n");printf("**************************************\n");printf("\n");
}
void Game()
{char ret = 0;char board[ROW][COL] = { 0 };InitBoard(board,ROW,COL);DisplayBoard(board, ROW, COL);while (1){player_move(board, ROW, COL);DisplayBoard(board, ROW, COL);ret = win_lose(board, ROW, COL);if (ret != 'c'){break;}computer_move(board, ROW, COL);DisplayBoard(board, ROW, COL);ret = win_lose(board, ROW, COL);if (ret != 'c'){break;}}if (ret == '*'){printf("\n\n");printf("\t\t\t You Win!\n");}else if (ret == '#'){printf("\n\n");printf("\t\t\t You Lose!\n");}else if (ret =='d'){printf("\n\n");printf("\t\t\t 和局\n");}
}
int main()
{int input = 0;srand((unsigned int)time(NULL));do {menu();printf("\t\t\t 请选择-->");scanf("%d", &input);switch (input){case 1:Game();system("pause");break;case 0:printf("退出游戏\n");system("pause");break;default:printf("输入错误,请重新选择");system("pause");break;}} while (input);return 0;
}
小结
整个“井字棋”程序算是初步完成了
但是有亿点点小问题——电脑宛如人工智障
因为我们是通过<time.h>头文件获得的时间(秒数)数字,调用srand()来初始化它的随机值,最后通过rand() % row rand() % col 随机生成行、列范围在1到3的坐标,从而达成电脑下棋。
BUT!问题在于这样完全没有人机交互性 人工智能?人工智障!
电脑只会在3*3的棋盘中剩余的格子内落子,不会追三字连线去赢,也不会阻碍玩家三子连线
所以我们要对其算法进行优化 让电脑变得更聪明一些
算法优化
我想到的方法是通过设一个变量 当作电脑落子优先级变量
例如int k=0; 当k=0的时候 电脑暂时没有优先级更高的事要做 所以这时候还是随机落子
但是当遇到特别的情况 例如电脑自己将要三子连线 又或是 玩家将要三子连线,将变量k赋值为1,优先执行连子获胜或者围堵玩家妨碍玩家获胜的指令
具体操作如下:
void computer_move(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;int k = 0;k = priority_board_com(board, row, col);while (0 == k){i = rand() % row;j = rand() % col;if (board[i][j] == ' '){Sleep(400);board[i][j] = '#';break;}}
}
在执行指令前另定义一个函数,用于判断当前棋盘局势是否满足特殊情况。若是满足则赋值k=1 执行针对性落子操作;若是不满足,则返回k=0;继续进行computer_move(board, row, col) 中剩余指令,当k==0 随机落子
对于判断优先级函数priority_board_com(board, row, col)没有什么难度高的编写,单纯是列举出所有特殊情况,包括电脑自己将要赢或者玩家将要赢,进行针对性落子,并返回k=1,不执行computer_move()中后续代码
例:当电脑在同一列上有两个己方的子 且剩余的那个格为空
if ((board[i][0] == board[i][1]) && (board[i][0] == '#' || board[i][1] == '#') && board[i][2] == ' '){board[i][2] = '#';k = 1;break;}
同一行从两个子连成三个子的情况有三种
那么对于行 有以下的代码
for (i = 0; i < row; i++){if ((board[i][0] == board[i][1]) && (board[i][0] == '#' || board[i][1] == '#') && board[i][2] == ' '){board[i][2] = '#';k = 1;break;}if ((board[i][0] == board[i][2]) && (board[i][0] == '#' || board[i][2] == '#') && board[i][1] == ' '){board[i][1] = '#';k = 1;break;}if ((board[i][1] == board[i][2]) && (board[i][1] == '#' || board[i][2] == '#') && board[i][0] == ' '){board[i][0] = '#';k = 1;break;}}if (k != 0)break;
举一反三 我们又可以对列 对角线(需注意 对角线分左右两条)条件继续编写 方法一致
编写完电脑判断自己能否获胜的程序,咱们再举再反,编写电脑判断玩家能否获胜的程序
代码与上诉相类似
代码2
通过算法优化后,我们对原代码进行更改,再附上亿点点优化游戏背景的代码
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"//棋盘初始化
void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){board[i][j] = ' ';}}
}
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{system("cls");printf("\n\n\n\n\n\n");printf("\t\t\t\tTic-Tac-Toe");printf("\n\n\n");int i = 0;for (i = 0; i < row; i++){int j = 0;printf("\t\t\t\t");for (j = 0; j < col; j++){printf(" %c ", board[i][j]);if (j < col - 1) printf("|");}printf("\n");if (i < row - 1){printf("\t\t\t\t");for (j = 0; j < col; j++){printf("---");if (j < col - 1)printf("|");}printf("\n");}}
}
//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{int x = 0;int y = 0;printf("\n");printf("\t\t\t(玩家)轮到你了!:\n");while (1){printf("\t\t\t请输入坐标:");fflush(stdin);scanf("%d %d", &x, &y); if (x >= 1 && x <= ROW && y >= 1 && y <= COL){if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else{printf("\t\t\t棋位已占!\n");}}else{printf("\t\t\t非法坐标!\n");}}
}
//电脑下棋
int priority_board_com(char board[ROW][COL], int row, int col);
int priority_board_hum(char board[ROW][COL], int row, int col, int k);void computer_move(char board[ROW][COL], int row, int col)
{printf("\t\t\t(电脑)电脑在思考中~:\n");int i = 0;int j = 0;int k = 0;k = priority_board_com(board, row, col);while (0 == k){i = rand() % row;j = rand() % col;if (board[i][j] == ' '){Sleep(400);board[i][j] = '#';break;}}
}
//电脑算法优化 判断优先级
//电脑判断自己能否赢 并去连线
int priority_board_com(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;//k用于返回值int k = 0;while (k == 0){//电脑判断自己在三行上是否会赢for (i = 0; i < row; i++){if ((board[i][0] == board[i][1]) && (board[i][0] == '#' || board[i][1] == '#') && board[i][2] == ' '){board[i][2] = '#';k = 1;break;}if ((board[i][0] == board[i][2]) && (board[i][0] == '#' || board[i][2] == '#') && board[i][1] == ' '){board[i][1] = '#';k = 1;break;}if ((board[i][1] == board[i][2]) && (board[i][1] == '#' || board[i][2] == '#') && board[i][0] == ' '){board[i][0] = '#';k = 1;break;}}if (k != 0)break;//电脑判断自己在三列上是否会赢for (j = 0; j < col; j++){if ((board[0][j] == board[1][j]) && (board[0][j] == '#' || board[1][j] == '#') && board[2][j] == ' '){board[2][j] = '#';k = 1;break;}if ((board[0][j] == board[2][j]) && (board[0][j] == '#' || board[2][j] == '#') && board[1][j] == ' '){board[1][j] = '#';k = 1;break;}if ((board[1][j] == board[2][j]) && (board[1][j] == '#' || board[2][j] == '#') && board[0][j] == ' '){board[0][j] = '#';k = 1;break;}}if (k != 0)break;//对角线是否会赢while (0 == k){//左边对角线if ((board[0][0] == board[1][1]) && (board[0][0] == '#' || board[1][1] == '#') && board[2][2] == ' '){board[2][2] = '#';k = 1;break;}if ((board[0][0] == board[2][2]) && (board[0][0] == '#' || board[2][2] == '#') && board[1][1] == ' '){board[1][1] = '#';k = 1;break;}if ((board[1][1] == board[2][2]) && (board[1][1] == '#' || board[2][2] == '#') && board[0][0] == ' '){board[0][0] = '#';k = 1;break;}//右边对角线if ((board[0][2] == board[1][1]) && (board[0][2] == '#' || board[1][1] == '#') && board[2][0] == ' '){board[2][0] = '#';k = 1;break;}if ((board[0][2] == board[2][0]) && (board[0][2] == '#' || board[2][0] == '#') && board[1][1] == ' '){board[1][1] = '#';k = 1;break;}if ((board[1][1] == board[2][0]) && (board[1][1] == '#' || board[2][0] == '#') && board[0][2] == ' '){board[0][2] = '#';k = 1;break;}break;}k = priority_board_hum(board, row, col, k);return k;}
}
//电脑判断玩家能否赢 并去堵截
int priority_board_hum(char board[ROW][COL],int row,int col,int k)
{int i = 0;int j = 0;while (k == 0){//判断玩家在三行上是否会赢for (i = 0; i < row; i++){if ((board[i][0] == board[i][1]) && (board[i][0] == '*' || board[i][1] == '*') && board[i][2] == ' '){board[i][2] = '#';k = 1;break;}if ((board[i][0] == board[i][2]) && (board[i][0] == '*' || board[i][2] == '*') && board[i][1] == ' '){board[i][1] = '#';k = 1;break;}if ((board[i][2] == board[i][1]) && (board[i][2] == '*' || board[i][1] == '*') && board[i][0] == ' '){board[i][0] = '#';k = 1;break;}}if (k != 0)break;//判断玩家在三列上是否会赢for (j = 0; j < col; j++){if ((board[0][j] == board[1][j]) && (board[1][j] == '*' || board[0][j] == '*') && board[2][j] == ' '){board[2][j] = '#';k = 1;break;}if ((board[0][j] == board[2][j]) && (board[2][j] == '*' || board[0][j] == '*') && board[1][j] == ' '){board[1][j] = '#';k = 1;break;}if ((board[1][j] == board[2][j]) && (board[2][j] == '*' || board[1][j] == '*') && board[0][j] == ' '){board[0][j] = '#';k = 1;break;}}break;}//判断玩家在对角线上是否会赢while (k == 0){//左边对角线if ((board[0][0] == board[1][1]) && (board[1][1] == '*' || board[0][0] == '*') && board[2][2] == ' '){board[2][2] = '#';k = 1;break;}if ((board[0][0] == board[2][2]) && (board[2][2] == '*' || board[0][0] == '*') && board[1][1] == ' '){board[1][1] = '#';k = 1;break;}if ((board[1][1] == board[2][2]) && (board[1][1] == '*' || board[2][2] == '*') && board[0][0] == ' '){board[0][0] = '#';k = 1;break;}//右边对角线if ((board[0][2] == board[1][1]) && (board[0][2] == '*' || board[1][1] == '*') && board[2][0] == ' '){board[2][0] = '#';k = 1;break;}if ((board[0][2] == board[2][0]) && (board[2][0] == '*' || board[0][2] == '*') && board[1][1] == ' '){board[1][1] = '#';k = 1;break;}if ((board[1][1] == board[2][0]) && (board[2][0] == '*' || board[1][1] == '*') && board[0][2] == ' '){board[0][2] = '#';k = 1;break;}break;}return k;
}
//判断胜负
char win_lose(char board[ROW][COL], int row, int col)
{int i = 0;//判断行for (i = 0; i < row; 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 < col; 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];}if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' '){return board[1][1];}//判断平局if (if_full(board, row, col) == 1){return 'd';}//继续return 'c';
}
//判断是否和局
static int if_full(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){if (board[i][j] == ' '){return 0;//没满}}}return 1;//满了
}
game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<Windows.h>
#define MIN(x,y) ((x)<(y)?(x):(y))#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 player_move(char board[ROW][COL], int row, int col);//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);//1. 玩家赢了 - hr
//2. 电脑赢了 - cw
//3. 平局 - d
//4. 游戏继续 - c//判断游戏是否有输赢
char win_lose(char board[ROW][COL], int row, int col);
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{system("cls");printf("\n\n\n\n\n\n");printf("\t\t\t\t Tic-Tac-Toe");printf("\n\n\n");printf("\t\t\t **************************************\n");printf("\t\t\t *********** 1.PLAY ***********\n");printf("\t\t\t **************************************\n");printf("\t\t\t *********** 0.QUET ***********\n");printf("\t\t\t **************************************\n");printf("\n");
}
void Game();
void menu_intro()
{system("cls");printf("\n\n\n\n\n\n");printf("\t\t\t\t Tic-Tac-Toe");printf("\n\n\n");printf("\t\t\t ****************************************\n");printf("\t\t\t *玩法介绍: *\n");printf("\t\t\t * 玩家的棋子为<*> 电脑的棋子为<#> *\n");printf("\t\t\t * 通过输入棋盘中的坐标落子 *\n");printf("\t\t\t * 例:输入1 3 为第一行第三列 *\n");printf("\t\t\t ****************************************\n");printf("\t\t\t *********** 1.continue **********\n");printf("\t\t\t *********** 0.back **********\n");printf("\t\t\t ****************************************\n");printf("\n");int input2 = 0;printf("\t\t\t 请选择-->");scanf("%d", &input2);switch (input2){case 1:Game();break;case 0:printf("\t\t\t 返回菜单\n");system("pause");break;default:printf("\t\t\t 输入错误,请重新选择");system("pause");break;}
}
void Game()
{char ret = 0;char board[ROW][COL] = { 0 };InitBoard(board,ROW,COL);DisplayBoard(board, ROW, COL);while (1){player_move(board, ROW, COL);DisplayBoard(board, ROW, COL);ret = win_lose(board, ROW, COL);if (ret != 'c'){break;}computer_move(board, ROW, COL);DisplayBoard(board, ROW, COL);ret = win_lose(board, ROW, COL);if (ret != 'c'){break;}}if (ret == '*'){printf("\n\n");printf("\t\t\t You Win!\n");}else if (ret == '#'){printf("\n\n");printf("\t\t\t You Lose!\n");}else if (ret =='d'){printf("\n\n");printf("\t\t\t 和局\n");}
}
int main()
{int input1 = 0;do {menu();printf("\t\t\t 请选择-->");scanf("%d", &input1);switch (input1){case 1:menu_intro();system("pause");break;case 0:printf("\t\t\t退出游戏\n");system("pause");break;default:printf("\t\t\t输入错误,请重新选择");system("pause");break;}} while (input1);return 0;
}
总结
最后代码呈现出来是这样的
通过这次对井字棋的程序编写,学到了更多新知识,较上次的“猜数字游戏”,这次的代码更多,工作量更大,程序更复杂。学会了针对算法的进一步优化,能让程序运行起来看起来“更聪明”。(by th way 升级后的电脑可没那么好赢哦 快去玩玩吧)
C语言实现井字棋游戏(含算法优化)相关推荐
- C语言实现“井字棋”游戏(三子棋)人机对弈
井字棋游戏:即三子棋,英文名叫Tic-Tac-Tic,是一种在3*3格子上进行的连珠游戏,和五子棋比较类似,由于棋盘一般不画边线框,格线排成井字故得名. 题目分析 : 要完成该游戏的编写,我们需要先分 ...
- 【C语言实现井字棋及电脑落子优化】
朋友们,我们还记得以前上课经常和同桌玩起井字棋,那么我们就当我们回忆童年,现在也用C语言来实现井字棋,本次代码相对于初阶的井字棋,在电脑下棋代码部分做了优化,使得电脑更加具有威胁,但是由于博主水平有限 ...
- 井字棋小游戏c语言简单编码,C语言实现井字棋小游戏
C语言实现简单的"井字棋游戏",供大家参考,具体内容如下 总体构造: 1.游戏菜单的逻辑实现 2.游戏本体的代码实现 part 1:游戏菜单的整体逻辑 ①简单的通过一个输入0和1的 ...
- c语言井字棋程序设计报告,井字棋游戏(课程设计)总结报告.doc
井字棋游戏(课程设计)总结报告 C语言贪吃蛇游戏设计总结报告 PAGE PAGE 5 丽水学院 计算机信息学院 <C语言课程设计(短一)> 指 导 书 二 ○ 一一 年 三 附件二 封面格 ...
- C语言第十课:编写井字棋游戏(综合练习1)
目录 前言: 一.文件建立: 1.头文件game.h: 2.函数定义文件game.c: 3.工程测试文件test.c: 二.编写井字棋游戏: 1.程序整体执行思路: 2.menu菜单函数实现: 3.g ...
- C语言初学——井字棋小游戏
hello呀!小伙伴们,今天小刘同学要带着大家学习的内容是--井字棋小游戏!!! 在本次学习中,我们将会运用到的主要内容是数组,函数,循环和判断. 当然,本次学习的内容并不困难,希望本次教学结束大家都 ...
- java——博弈算法实现井字棋游戏
通过java语言开发了一个简单的井字棋游戏.主要有6个类,其中有一个是主类(Main.java),一个是抽象类(PiecesMove.java)组成. 下面对各个类简单介绍一下: TicTicToe. ...
- php井字游戏,python实现井字棋游戏
#本游戏python3.4.0下编写调试,只能在windows下运行. import random import subprocess import time #定义函数 def draw_board ...
- Minimax 和 Alpha-beta 剪枝算法简介,及以此实现的井字棋游戏(Tic-tac-toe)
前段时间用 React 写了个2048 游戏来练练手,准备用来回顾下 React 相关的各种技术,以及试验一下新技术.在写这个2048的过程中,我考虑是否可以在其中加入一个 AI 算法来自动进行游戏, ...
最新文章
- DNS迭代式和递归式域名查询对比
- spring访问oracle函数,spring调用带参数的oracle函数应注意的问题
- 证明并推导汉诺塔(河内之塔)问题公式
- C++学习手记五:C++流操作
- Important table for SEGW
- libnss mysql_Ubuntu通过LDAP集成AD域账号登录(libnss-ldap方式)
- JUnit 5中的测试执行顺序
- lnmp mysql 自动关闭_mysql总是自动停止 日志提示Plugin ‘FEDERATED’ is disabled的解决办法...
- java ajax简单实例_JAVA编写的AJAX例子,很简单,但是很容易理解详解
- 威马汽车创始人沈晖:10万以下的不能叫智能电动汽车
- Zabbix运维监控工具
- netty实现gmssl_gmssl java api 编译
- 市场需求分析(MRD)模板
- matlab 冒泡排序函数,Matlab排序算法-遍历排序、冒泡排序
- 时空数据生成对抗网络研究综述(下)
- Android 状态栏常规操作(状态栏显示,状态栏颜色,沉浸式状态栏)
- win10发送到桌面快捷方式没了
- 学计算机必须要掌握的单词,学习计算机技术必须掌握的53个计算机专业英文单词...
- 深圳市海平线科技有限公司
- PostgreSQL高可用中间件—Pgpool-Ⅱ