井字棋作为一款简单而经典的游戏,一直深受大家喜爱。在这篇文章中,我将为大家分享我用 C++ 实现的井字棋代码,并对其进行详细介绍。
这里电脑会根据对局情况,判断下一步的位置。
一、头文件
我们使用 C++ 来构建这个游戏,主要依赖了以下几个头文件:
<iostream>
:用于输入输出操作。<string>
:处理字符串相关的操作。<windows.h>
:在代码中用于实现一些特定的系统功能,比如控制控制台的显示。
二、整体代码结构
整个代码由多个函数组成,它们相互协作,共同实现了井字棋游戏的完整逻辑。
- menu函数用于打印选择菜单
blankmap
函数负责将棋盘初始化为空白状态,为游戏的开始做好准备。showmap
函数用于清晰地在控制台输出当前棋盘的状态,包括行列的标识和棋子的分布,让玩家能够直观地了解局面。judge
函数是游戏胜负判定的核心,通过巧妙地计算棋子的 ASCII 码之和,来判断是玩家获胜、电脑获胜还是出现平局的情况。player
函数处理玩家的回合,包括接收玩家输入的坐标,并进行合法性校验,然后在合法的位置放置玩家的棋子。ai
函数则实现了电脑的下棋策略,电脑会先尝试寻找自己能够获胜的位置,如果没有,就检查是否能阻止玩家获胜,若都不行,则随机选择一个空位下棋。playa
和playb
分别是玩家先手和电脑先手的游戏循环控制函数,确保游戏按照规则有序进行。
三、关键函数解析
(一)blankmap
函数
这个函数通过简单的两层循环,将棋盘的每个位置初始化为空白,为游戏的开始做好准备。
void blankmap(char map[3][3]){
for(int a=0;a<3;a++){
for(int b=0;b<3;b++){
map[a][b] = ' ';
}
}
}
(二)showmap
函数
它使用系统命令清屏,并以清晰直观的格式输出棋盘的当前状态,方便玩家查看。
void showmap(const char map[3][3]){
system("cls");
cout<<" 0 1 2 "<<endl;
cout<<" -------"<<endl;
for(int a=0;a<3;a++){
cout<<a<<" | ";
for(int b=0;b<3;b++){
cout<<map[a][b]<<" ";
}
cout<<"|"<<endl;
}
cout<<" -------"<<endl;
}
(三)judge
函数
judge
函数是游戏胜负判定的核心。它通过巧妙计算棋子在行列和对角线上的 ASCII 码之和,准确判断出玩家获胜、电脑获胜或平局的情况。
string judge(const char map[3][3]){
int a=0, b=0, c=0,d=0;
// 判断行列
for(int i=0;i<3;i++){
a=map[i][0]+map[i][1]+map[i][2];
b=map[0][i]+map[1][i]+map[2][i];
if(a==126 || b==126){ // 比较 ASCII 码的和来判断输赢情况
return "玩家获胜!" ;
}
else if(a==192 || b==192){// 比较 ASCII 码的和来判断输赢情况
return "电脑获胜!";
}
}
// 判断对角线
c=map[0][0]+map[1][1]+map[2][2];
d=map[0][2]+map[1][1]+map[2][0];
if(c==126 || d==126){
return "玩家获胜!" ;
}
else if(c==192 || d==192){
return "电脑获胜!";
}
// 判断是否下满棋盘
else if(isfull(map)) {
return "平局";
}
return "putin";
}
(四)player
函数
player
函数负责处理玩家的输入,包括对输入的合法性检查和在合法位置放置玩家的棋子。
void player(char map[3][3]){
if(isfull(map)){
return;
}
// 玩家输入坐标
int x=0, y=0;
while(judge(map)=="putin"){
cout <<"请输入横纵坐标: (中间加空格)"<<endl;
cout <<"玩家棋子为'*', 电脑棋子为'@' :"<<endl;
cin >> y >> x;
// 判定输入是否有误并改变相应棋子
if(x<0 || x>2 ||y<0 || y>2){
cout<<"输入有误!请重新输入!!!"<<endl;
continue;
}
else if(map[x][y] == '*' || map[x][y]=='@'){
cout<<"该位置已被棋子占用,请重新选择位置!"<<endl;
continue;
}
else if(map[x][y]==' '){
map[x][y]='*';
break;
}
}
showmap(map);
}
(五)ai
函数
ai
函数实现了电脑的下棋策略。电脑首先尝试自己获胜,然后阻止玩家获胜,若都不行则随机选择空位下棋。
//电脑回合
void ai(char map[3][3]) {
// 如果棋盘已满,直接返回
if (isfull(map)) {
return;
}
cout<<"电脑正在下棋,请稍后"<<endl; //增加游戏体验,模拟电脑的思考
Sleep(1000);
// 检查自己能否获胜
// 检查行
for (int i = 0; i < 3; i++) { // 遍历每一行
int countAt = 0, countEmpty = 0; // 分别记录当前行中 '@' 的数量和空位置的数量
for (int j = 0; j < 3; j++) { // 遍历当前行的每一列
if (map[i][j] == '@') { // 如果位置为 '@'
countAt++; // 增加 '@' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '@' 的数量为 2 且空位置数量为 1
if (countAt == 2 && countEmpty == 1) {
for (int j = 0; j < 3; j++) { // 再次遍历当前行
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查列
for (int j = 0; j < 3; j++) { // 遍历每一列
int countAt = 0, countEmpty = 0; // 分别记录当前列中 '@' 的数量和空位置的数量
for (int i = 0; i < 3; i++) { // 遍历当前列的每一行
if (map[i][j] == '@') { // 如果位置为 '@'
countAt++; // 增加 '@' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '@' 的数量为 2 且空位置数量为 1
if (countAt == 2 && countEmpty == 1) {
for (int i = 0; i < 3; i++) { // 再次遍历当前列
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查对角线
int countAtDiag1 = 0, countEmptyDiag1 = 0; // 初始化主对角线 '@' 和空位置的计数
int countAtDiag2 = 0, countEmptyDiag2 = 0; // 初始化副对角线 '@' 和空位置的计数
for (int i = 0; i < 3; i++) { // 遍历对角线位置
if (map[i][i] == '@') { // 主对角线位置为 '@'
countAtDiag1++; // 增加主对角线 '@' 的计数
} else if (map[i][i] ==' ') { // 主对角线位置为空
countEmptyDiag1++; // 增加主对角线空位置的计数
}
if (map[i][2 - i] == '@') { // 副对角线位置为 '@'
countAtDiag2++; // 增加副对角线 '@' 的计数
} else if (map[i][2 - i] ==' ') { // 副对角线位置为空
countEmptyDiag2++; // 增加副对角线空位置的计数
}
}
// 主对角线如果 '@' 的数量为 2 且空位置数量为 1
if (countAtDiag1 == 2 && countEmptyDiag1 == 1) {
for (int i = 0; i < 3; i++) { // 遍历主对角线
if (map[i][i] ==' ') { // 找到空位置
map[i][i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 副对角线如果 '@' 的数量为 2 且空位置数量为 1
if (countAtDiag2 == 2 && countEmptyDiag2 == 1) {
for (int i = 0; i < 3; i++) { // 遍历副对角线
if (map[i][2 - i] ==' ') { // 找到空位置
map[i][2 - i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 检查能否阻止玩家获胜
// 检查行
for (int i = 0; i < 3; i++) { // 遍历每一行
int countStar = 0, countEmpty = 0; // 分别记录当前行中 '*' 的数量和空位置的数量
for (int j = 0; j < 3; j++) { // 遍历当前行的每一列
if (map[i][j] == '*') { // 如果位置为 '*'
countStar++; // 增加 '*' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '*' 的数量为 2 且空位置数量为 1
if (countStar == 2 && countEmpty == 1) {
for (int j = 0; j < 3; j++) { // 再次遍历当前行
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查列
for (int j = 0; j < 3; j++) { // 遍历每一列
int countStar = 0, countEmpty = 0; // 分别记录当前列中 '*' 的数量和空位置的数量
for (int i = 0; i < 3; i++) { // 遍历当前列的每一行
if (map[i][j] == '*') { // 如果位置为 '*'
countStar++; // 增加 '*' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '*' 的数量为 2 且空位置数量为 1
if (countStar == 2 && countEmpty == 1) {
for (int i = 0; i < 3; i++) { // 再次遍历当前列
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查对角线
int countStarDiag1 = 0;
countEmptyDiag1 = 0; // 初始化主对角线 '*' 和空位置的计数
int countStarDiag2 = 0;
countEmptyDiag2 = 0; // 初始化副对角线 '*' 和空位置的计数
for (int i = 0; i < 3; i++) { // 遍历对角线位置
if (map[i][i] == '*') { // 主对角线位置为 '*'
countStarDiag1++; // 增加主对角线 '*' 的计数
} else if (map[i][i] ==' ') { // 主对角线位置为空
countEmptyDiag1++; // 增加主对角线空位置的计数
}
if (map[i][2 - i] == '*') { // 副对角线位置为 '*'
countStarDiag2++; // 增加副对角线 '*' 的计数
} else if (map[i][2 - i] ==' ') { // 副对角线位置为空
countEmptyDiag2++; // 增加副对角线空位置的计数
}
}
// 主对角线如果 '*' 的数量为 2 且空位置数量为 1
if (countStarDiag1 == 2 && countEmptyDiag1 == 1) {
for (int i = 0; i < 3; i++) { // 遍历主对角线
if (map[i][i] ==' ') { // 找到空位置
map[i][i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 副对角线如果 '*' 的数量为 2 且空位置数量为 1
if (countStarDiag2 == 2 && countEmptyDiag2 == 1) {
for (int i = 0; i < 3; i++) { // 遍历副对角线
if (map[i][2 - i] ==' ') { // 找到空位置
map[i][2 - i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 如果没有上述情况,随机选择
int x = 0, y = 0;
while (judge(map) == "putin") { // 只要游戏未结束
x = rand() % 3; // 生成随机的 x 坐标
y = rand() % 3; // 生成随机的 y 坐标
// 如果位置已被 '*' 或 '@' 占用
if (map[x][y] == '*' || map[x][y] == '@') {
continue; // 重新生成坐标
} else if (map[x][y] ==' ') { // 如果位置为空
map[x][y] = '@'; // 放置 '@'
showmap(map);
break; // 完成操作并退出循环
}
}
showmap(map); // 显示当前棋盘状态
return;
}
(六)playa
和 playb
函数
这两个函数分别控制玩家先手和电脑先手的游戏流程,通过循环交替玩家和电脑的回合,直到游戏结束。
void playa(char map[3][3]){
showmap(map);
do{
player(map);
ai(map);
}
while(judge(map)=="putin");
cout << judge(map) << endl;
}
void playb(char map[3][3]){
showmap(map);
do{
ai(map);
player(map);
}
while(judge(map)=="putin");
cout << judge(map) << endl;
}
四、游戏流程
当我们运行程序时,首先会看到一个菜单选项:
- 选择
1
开始游戏。 - 接着选择
a
表示玩家先手,选择b
表示电脑先手,选择c
退出游戏。
在游戏过程中,玩家和电脑轮流下棋,每一步都会更新并展示棋盘状态,直到游戏结束,判定出胜负或平局。
五、源代码
#include <iostream>
#include <string>
#include <windows.h>
using namespace std;
//将棋盘初始化为空白
void blankmap(char map[3][3]);
//输出当前棋盘状态
void showmap(const char map[3][3]);
//判定游戏结束与否
string judge(const char map[3][3]);
//玩家回合
void player(char map[3][3]);
//电脑回合
void ai(char map[3][3]) ;
//游戏循环(玩家先手)
void playa(char map[3][3]);
//游戏循环(电脑先手)
void playb( char map[3][3]);
//判断棋盘是否有剩余空间
bool isfull(const char map[3][3]);
//菜单
void menu();
int main(){
menu();
system("pause");
return 0;
}
void menu(){
char map[3][3];
char choice;
bool iscontinue=true;
//界面选项
while(choice!='2'){
cout<<"****************"<<endl;
cout<<"* 1. 开始游戏 *"<<endl;
cout<<"****************"<<endl;
cout<<"* 2. 退出游戏 *"<<endl;
cout<<"****************"<<endl;
cout<<"请输入1或2来选择: "<<endl;
cin>>choice;
switch(choice){
case '1':
iscontinue=true;
while(iscontinue){
char s;
cout<<"****************"<<endl;
cout<<"您可继续选择:"<<endl;
cout<<" a. 玩家先手 "<<endl;
cout<<" b. 电脑先手 "<<endl;
cout<<" c. 退出游戏 "<<endl;
cin>>s;
switch(s){
case 'a':
blankmap(map);
playa(map);
continue;
case 'b':
blankmap(map);
playb(map);
continue;
case 'c':
iscontinue=false;
break;
default:
cout<<"输入错误,请重新输入!"<<endl;
break;
}
}
case '2':
cout<<"已退出游戏!";
choice='2';
break;
default:
cout<<"输入错误,请重新输入!"<<endl;
break;
}
}
}
//初始化棋盘
void blankmap(char map[3][3]){
for(int a=0;a<3;a++){
for(int b=0;b<3;b++){
map[a][b] = ' ';
}
}
}
//打印棋盘
void showmap(const char map[3][3]){
system("cls");
cout<<" 0 1 2 "<<endl;
cout<<" -------"<<endl;
for(int a=0;a<3;a++){
cout<<a<<" | ";
for(int b=0;b<3;b++){
cout<<map[a][b]<<" ";
}
cout<<"|"<<endl;
}
cout<<" -------"<<endl;
}
//判断游戏是否结束
string judge(const char map[3][3]){
int a=0, b=0, c=0,d=0;
//判断行列
for(int i=0;i<3;i++){
a=map[i][0]+map[i][1]+map[i][2];
b=map[0][i]+map[1][i]+map[2][i];
if(a==126 || b==126){ //比较ASCII码的和来判断输赢情况
return "玩家获胜!" ;
}
else if(a==192 || b==192){//比较ASCII码的和来判断输赢情况
return "电脑获胜!";
}
}
//判断对角线
c=map[0][0]+map[1][1]+map[2][2];
d=map[0][2]+map[1][1]+map[2][0];
if(c==126 || d==126){
return "玩家获胜!" ;
}
else if(c==192 || d==192){
return "电脑获胜!";
}
//判断是否下满棋盘
else if(isfull(map)) {
return "平局";
}
return "putin";
}
//判断棋盘是否满
bool isfull(const char map[3][3]){
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(map[i][j]==' '){
return false;
}
}
}
return true;
}
//玩家回合
void player(char map[3][3]){
if(isfull(map)){
return;
}
//玩家输入坐标
int x=0, y=0;
while(judge(map)=="putin"){
cout <<"请输入横纵坐标: (中间加空格)"<<endl;
cout <<"玩家棋子为'*' , 电脑棋子为'@' :"<<endl;
cin >> y >> x;
//判定输入是否有误并改变相应棋子
if(x<0 || x>2 ||y<0 || y>2){
cout<<"输入有误!请重新输入!!!"<<endl;
continue;
}
else if(map[x][y] == '*' || map[x][y]=='@'){
cout<<"该位置已被棋子占用,请重新选择位置!"<<endl;
continue;
}
else if(map[x][y]==' '){
map[x][y]='*';
break;
}
}
showmap(map);
}
//电脑回合
void ai(char map[3][3]) {
// 如果棋盘已满,直接返回
if (isfull(map)) {
return;
}
cout<<"电脑正在下棋,请稍后"<<endl; //增加游戏体验,模拟电脑的思考
Sleep(1000);
// 检查自己能否获胜
// 检查行
for (int i = 0; i < 3; i++) { // 遍历每一行
int countAt = 0, countEmpty = 0; // 分别记录当前行中 '@' 的数量和空位置的数量
for (int j = 0; j < 3; j++) { // 遍历当前行的每一列
if (map[i][j] == '@') { // 如果位置为 '@'
countAt++; // 增加 '@' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '@' 的数量为 2 且空位置数量为 1
if (countAt == 2 && countEmpty == 1) {
for (int j = 0; j < 3; j++) { // 再次遍历当前行
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查列
for (int j = 0; j < 3; j++) { // 遍历每一列
int countAt = 0, countEmpty = 0; // 分别记录当前列中 '@' 的数量和空位置的数量
for (int i = 0; i < 3; i++) { // 遍历当前列的每一行
if (map[i][j] == '@') { // 如果位置为 '@'
countAt++; // 增加 '@' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '@' 的数量为 2 且空位置数量为 1
if (countAt == 2 && countEmpty == 1) {
for (int i = 0; i < 3; i++) { // 再次遍历当前列
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查对角线
int countAtDiag1 = 0, countEmptyDiag1 = 0; // 初始化主对角线 '@' 和空位置的计数
int countAtDiag2 = 0, countEmptyDiag2 = 0; // 初始化副对角线 '@' 和空位置的计数
for (int i = 0; i < 3; i++) { // 遍历对角线位置
if (map[i][i] == '@') { // 主对角线位置为 '@'
countAtDiag1++; // 增加主对角线 '@' 的计数
} else if (map[i][i] ==' ') { // 主对角线位置为空
countEmptyDiag1++; // 增加主对角线空位置的计数
}
if (map[i][2 - i] == '@') { // 副对角线位置为 '@'
countAtDiag2++; // 增加副对角线 '@' 的计数
} else if (map[i][2 - i] ==' ') { // 副对角线位置为空
countEmptyDiag2++; // 增加副对角线空位置的计数
}
}
// 主对角线如果 '@' 的数量为 2 且空位置数量为 1
if (countAtDiag1 == 2 && countEmptyDiag1 == 1) {
for (int i = 0; i < 3; i++) { // 遍历主对角线
if (map[i][i] ==' ') { // 找到空位置
map[i][i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 副对角线如果 '@' 的数量为 2 且空位置数量为 1
if (countAtDiag2 == 2 && countEmptyDiag2 == 1) {
for (int i = 0; i < 3; i++) { // 遍历副对角线
if (map[i][2 - i] ==' ') { // 找到空位置
map[i][2 - i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 检查能否阻止玩家获胜
// 检查行
for (int i = 0; i < 3; i++) { // 遍历每一行
int countStar = 0, countEmpty = 0; // 分别记录当前行中 '*' 的数量和空位置的数量
for (int j = 0; j < 3; j++) { // 遍历当前行的每一列
if (map[i][j] == '*') { // 如果位置为 '*'
countStar++; // 增加 '*' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '*' 的数量为 2 且空位置数量为 1
if (countStar == 2 && countEmpty == 1) {
for (int j = 0; j < 3; j++) { // 再次遍历当前行
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查列
for (int j = 0; j < 3; j++) { // 遍历每一列
int countStar = 0, countEmpty = 0; // 分别记录当前列中 '*' 的数量和空位置的数量
for (int i = 0; i < 3; i++) { // 遍历当前列的每一行
if (map[i][j] == '*') { // 如果位置为 '*'
countStar++; // 增加 '*' 的计数
} else if (map[i][j] ==' ') { // 如果位置为空
countEmpty++; // 增加空位置的计数
}
}
// 如果 '*' 的数量为 2 且空位置数量为 1
if (countStar == 2 && countEmpty == 1) {
for (int i = 0; i < 3; i++) { // 再次遍历当前列
if (map[i][j] ==' ') { // 找到空位置
map[i][j] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
}
// 检查对角线
int countStarDiag1 = 0;
countEmptyDiag1 = 0; // 初始化主对角线 '*' 和空位置的计数
int countStarDiag2 = 0;
countEmptyDiag2 = 0; // 初始化副对角线 '*' 和空位置的计数
for (int i = 0; i < 3; i++) { // 遍历对角线位置
if (map[i][i] == '*') { // 主对角线位置为 '*'
countStarDiag1++; // 增加主对角线 '*' 的计数
} else if (map[i][i] ==' ') { // 主对角线位置为空
countEmptyDiag1++; // 增加主对角线空位置的计数
}
if (map[i][2 - i] == '*') { // 副对角线位置为 '*'
countStarDiag2++; // 增加副对角线 '*' 的计数
} else if (map[i][2 - i] ==' ') { // 副对角线位置为空
countEmptyDiag2++; // 增加副对角线空位置的计数
}
}
// 主对角线如果 '*' 的数量为 2 且空位置数量为 1
if (countStarDiag1 == 2 && countEmptyDiag1 == 1) {
for (int i = 0; i < 3; i++) { // 遍历主对角线
if (map[i][i] ==' ') { // 找到空位置
map[i][i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 副对角线如果 '*' 的数量为 2 且空位置数量为 1
if (countStarDiag2 == 2 && countEmptyDiag2 == 1) {
for (int i = 0; i < 3; i++) { // 遍历副对角线
if (map[i][2 - i] ==' ') { // 找到空位置
map[i][2 - i] = '@'; // 放置 '@'
showmap(map);
return; // 完成操作并返回
}
}
}
// 如果没有上述情况,随机选择
int x = 0, y = 0;
while (judge(map) == "putin") { // 只要游戏未结束
x = rand() % 3; // 生成随机的 x 坐标
y = rand() % 3; // 生成随机的 y 坐标
// 如果位置已被 '*' 或 '@' 占用
if (map[x][y] == '*' || map[x][y] == '@') {
continue; // 重新生成坐标
} else if (map[x][y] ==' ') { // 如果位置为空
map[x][y] = '@'; // 放置 '@'
showmap(map);
break; // 完成操作并退出循环
}
}
showmap(map); // 显示当前棋盘状态
return;
}
//玩家先手情况
void playa(char map[3][3]){
showmap(map);
do{
player(map);
ai(map);
}
while(judge(map)=="putin");
cout << judge(map) << endl;
}
//电脑先手情况
void playb(char map[3][3]){
showmap(map);
do{
ai(map);
player(map);
}
while(judge(map)=="putin");
cout << judge(map) << endl;
}
希望这篇博客能符合您的需求,代码可能有很多不足之处,希望大家能包容,给我一些宝贵的建议。