Designed By Rossi
仅供记录和参考,转载需经本人同意
有段时间没写blog了,感觉自己快生锈了。2021年是时候该整点狠活了,我决定搬出我用c++写了三个月的小游戏
学了这么久编程,总觉得自己除了算法还应该弄点别的东西,用c++写个软件吧似乎很难实现,而且c++的GUI设计还是比较难的。
既然搞不了软件,那要不写个小游戏吧,毕竟自己也喜欢游戏。于是我想了想,贪吃蛇和雷电我都写不出,推箱子做过了感觉太低级,要不整个三子棋吧。
期初我看了网上很多三子棋的例子,发现很多开源例子都只是停留在简单层面的设计,所以我决定写点花里胡哨的
是时候进入正题了
写游戏之前当然得先了解游戏规则和背景吧
三子棋是黑白棋的一种。三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉、一条龙、井字棋等。将正方形对角线连起来,相对两边依次摆上三个双方棋子,只要将自己的三个棋子走成一条线,对方就算输了。但是,有很多时候会出现和棋的情况。
首先进游戏之前得整个花里胡哨的东西——登录
第一步得先设计好登录的界面UI,然后设计好登录时用户名和密码的输入和后台校验。考虑到登录失败的情况,需要逼真地模拟登录失败后的重新登录和延时登录,因此需要编写一个Time()函数套用到LogIn()函数中。
然后这个功能里我最喜欢的部分就是登录输入密码时用*隐藏原始密码,这一步足足花了我两个星期,此前虽然想了三种实现方法都没有成功,但是最后成功之后出来的效果让我比较满意。
//倒计时
void Time()
{
char content[]="9876543210";
char lable[]="Please wait #S!";
int len=strlen(content);
int start=0;
cout<<lable<<'\r';
for(start=0;start<len;start++)
{
lable[12]=content[start];//给数组第十三个元素传值
int t=1000 ;
Sleep(t);
cout<<lable<<'\r';
if(start==len-1)//数传完毕
cout<<'\b'<<"Please log in again! "<<3;// \b 退格符
}
cout<<endl;
}
//登录界面
void LogIn()
{
char username[64];
char password[64];
char code[64];
int flag=0;
cout<<"注意:你只有三次登录机会"<<endl;
for(int times=0;times<3;times++)
{
the: cout<<"请输入账号:";
cin>>username;//数组在输入时降维成指针;所以不带&
cout<<"请输入密码:";
int i=0;
if(strcmp(USERNAME,username)==0)
{
//接下来这一步就是实现模拟密码输入时有*隐藏的效果
do
{
password[i]=getch();
if(password[i]=='\r')
break;
if(password[i]=='\b')
{
if(i==0)
{
cout<<'\a';
continue;
}
i=i-1;
cout<<'\b';//实现删字不影响显示
}
else
{
i=i+1;
cout<<"*";
}
}while(password[i]!='\n' && i<20);
password[i]='\0';
cout<<endl;
if(strcmp(PASSWORD,password)==0)
{
cout<<"登录成功!"<<endl;
flag=1;
break;
}
else
{
cout<<"登录失败!"<<endl;
times==2;
}
if(flag=1)
break;
}
if(times==2)
{
cout<<"请等待 "<<WaitTime<<"秒!"<<'\r';
Time();
goto the;//跳转到登录,登录失败1次就延时!
}
}
}
登录进去了,那得先有棋盘才能下棋吧
这里我使用了一个二维数组来存放棋盘,所以我需要先进行初始化操作然后打印棋盘(row是列,line是行)
void init(char chessBoard[MAXROW][MAXLINE])
{
for(int row=0;row<MAXROW;row++)
for(int line=0;col<MAXLINE;line++)
chessBoard[row][line]=' ';
}
void print_chessBoard(char chessBoard[MAXROW][MAXLINE])
{
cout<<"+---+---+---+"<<endl;
for(int row=0;row<MAXROW;row++)
{
cout<<"| "<<chessBoard[row][0]<<" | "<<chessBoard[row][1]<<" | "<<chessBoard[row][2]<<" |"<<endl;
cout<<"+---+---+---+"<<endl;
}
}
棋盘有了,那可以开始下棋了
因为我用c++写这个游戏没有UI,不像人家那些真游戏那样可以动动鼠标就可以下棋,所以这里只能采用坐标法落子。利用坐标法落子需要注意两个事情:一是落子位置是否已经有棋,二是落子位置是否在棋盘之外。下完棋之后还要做一件事就是“裁判”:设置一个变量来记录比赛结果方便最后根据判定输出结果。还要一个细节就是胜负判定时会出现平局的结果,这时候只需要判断棋盘是否满棋而没有产生胜负就行了。
void playerMove(char chessBoard[MAXROW][MAXLINE])
{
while(1)
{
int row=0;
int line=0;
cout<<"请输入坐标(范围0~2):";
cin>>row>>line;
if(row<0 || row>=MAXROW || line<0 || line>=MAXLINE)
{
cout<<"您的坐标不在合法范围内 [0, 2],请重新输入:"<<endl;
continue;
}
if(chessBoard[row][line]!=' ')
{
cout<<"您下的位置已经有子了!"<<endl;
continue;
}
chessBoard[row][line]='x';
break;
}
}
void computerMove(char chessBoard[MAXROW][MAXLINE])
{
while(1)
{
int row=rand()%MAXROW;
int line=rand()%MAXLINE;
if(chessBoard[row][line]!=' ')
continue;
chessBoard[row][line]='o';
break;
}
}
int isFull(char chessBoard[MAXROW][MAXLINE])
{
for(int row=0;row<MAXROW;row++)
for(int line=0;line<MAXLINE;line++)
if(chessBoard[row][line]==' ')
return 0;
return 1;
}
char isWin(char chessBoard[MAXROW][MAXLINE])
{
for(int row=0;row<MAXROW;row++)
if(chessBoard[row][0] != ' ' && chessBoard[row][0]==chessBoard[row][1] && chessBoard[row][0]==chessBoard[row][2])
return chessBoard[row][0];
for(int line=0;line<MAXLINE;line++)
if(chessBoard[0][line]!=' ' && chessBoard[0][line]==chessBoard[1][line] && chessBoard[0][line]==chessBoard[2][line])
return chessBoard[0][line];
if(chessBoard[0][0] != ' ' && chessBoard[0][0]==chessBoard[1][1] && chessBoard[0][0]==chessBoard[2][2])
return chessBoard[0][0];
if(chessBoard[2][0]!=' ' && chessBoard[2][0]==chessBoard[1][1] && chessBoard[2][0]==chessBoard[0][2])
return chessBoard[2][0];
if(isFull(chessBoard))
return 'q';
return ' ';
}
有棋了,怎么走是个问题
下棋总有先手和后手对吧,所以我写了两个函数,game()是玩家先走的开局,fungame()是电脑先走的开局
void game()
{
char chessBoard[MAXROW][MAXLINE]={
0};
init(chessBoard);
char winner=' ';
while(1)
{
print_chessBoard(chessBoard);
playerMove(chessBoard);
winner=isWin(chessBoard);
if(winner!=' ')
break;
computerMove(chessBoard);
winner=isWin(chessBoard);
if(winner!=' ')
break;
}
print_chessBoard(chessBoard);
if(winner=='x')
cout<<"恭喜您, 您赢了!"<<endl;
else if(winner=='o')
{
score-=50;
cout<<"对不起,你输了!"<<endl;
}
else
{
score-=50;
cout<<"很遗憾,打成了平手!!"<<endl;
}
}
void fungame()
{
char chessBoard[MAXROW][MAXLINE]={
0};
init(chessBoard);
char winner=' ';
while(1)
{
print_chessBoard(chessBoard);
computerMove(chessBoard);
winner=isWin(chessBoard);
if(winner!=' ')
break;
playerMove(chessBoard);
winner=isWin(chessBoard);
if(winner!=' ')
break;
}
print_chessBoard(chessBoard);
if(winner=='x')
cout<<"恭喜您, 您赢了!"<<endl;
else if(winner=='o')
{
score-=50;
cout<<"对不起,你输了!"<<endl;
}
else
{
score-=50;
cout<<"很遗憾,打成了平手!!"<<endl;
}
}
很多人看到这里应该看到了里面的score了吧,别急,下面就是用到它的地方了
玩游戏没有分数哪有动力啊对吧
光玩游戏多没意思啊,而且这样写出来也展现不出我的水平。秉持着花里胡哨的理念,我决定写个成就系统
这里因为传值过程会出现一些问题,最终结果会出现一些与预期效果不符的结果,所以这里结合主函数弄了一个算是比较巧的方法(+50,-50)
void honor(int score)
{
if(score>=0 && score<100)
cout<<"当前称号:小菜鸡"<<" 当前积分:"<<score<<endl;
if(score>=100 && score<250)
cout<<"当前称号:初出茅庐"<<" 当前积分:"<<score<<endl;
if(score>=250 && score<500)
cout<<"当前称号:牛刀小试"<<" 当前积分:"<<score<<endl;
if(score>=500 && score<800)
cout<<"当前称号:棋艺精湛"<<" 当前积分:"<<score<<endl;
if(score>=800)
cout<<"当前称号:三子棋大师"<<" 当前积分:"<<score<<endl;
}
前面用到的棋盘、分数、等待时间等等,都在函数编写前已经定义好了
int WaitTime=10;
int score=0;
包括宏定义
#define _CRT_SECURE_NO_WARNINGS
#define MAXROW 3
#define MAXLINE 3
有了函数自然得有调用它们的办法,这些方法就塞在主函数里面
srand((unsigned int)time(0));
notice();//游戏公告
LogIn();//登录程序
while(1)
{
int choice=menu();//调用菜单
if(choice==1)
{
game();
score+=50;
}
else if(choice==2)
{
fungame();
score+=50;
}
else if(choice==0)
{
cout<<"退出游戏,再见!!!!!"<<endl;
break;
}
else
{
cout<<"输入错误!请重新输入!"<<endl;
continue;
}
honor(score);
}
system("pause");
说实话当写出来、调试成功,能玩的时候,心里的成就感真的满满的。
不过这个游戏不完善的地方还是有的,例如电脑判断玩家棋路这方面,这一步涉及到算法,因为忙于学习 还没有时间去完善这个方面。另外还有一个就是登录方面,还没想到怎么去实现注册账号(注册之后还要实现账号的长久保存)、找回密码等等。时间还长,这些得等到以后才能去实现了(PS:如果有大佬有兴趣搞一下这个不妨和小弟一起探讨一下,或者帮小弟写个补全,蒟蒻期待大佬们临幸我)
部分编译截图
(这里可以看到电脑的棋路和判断还是挺傻的,以后再来改善)
ps:防止被生搬硬造,这里我就不把完整代码放出来了,想要完整源代码的话到时我会放链接的
(exe文件:点击这里)