C++实现黑白棋小游戏
由于备考的缘由,需要暂时退役,以后是否重学OI也是个未知数,特此用C++写了一个黑白棋的小游戏,祭奠一下。
了解黑白棋的基本规则
-
棋盘:黑白棋的棋盘是一个8x8的方格,共有64个格子。棋盘分为两半,每半各有32个格子,一方使用黑子,另一方使用白子。
-
棋子:黑白棋的棋子呈圆形,两面颜色不同,一面为黑色,一面为白色。每方有16枚棋子。
-
下棋规则:双方轮流在棋盘上放置自己的棋子,每次只能放一个棋子。棋子必须放在空白的格子上,不能放在已经有棋子的地方。当一方的棋子占据了棋盘上连续四个相同颜色的格子时,这些棋子会被对方翻转,即变成对方的颜色。
-
胜负判断:当某一方无法再下棋时,游戏结束。此时,如果某一方的棋子占据了棋盘上连续四个相同颜色的格子,那么该方获胜;如果棋盘上没有连续四个相同颜色的格子,那么占据格子数量多的那一方获胜。
程序源码
#include <iostream>
#include <string>
#include <Windows.h>
#include <conio.h>
#include <fstream>
#include <ctime>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bits/stdc++.h>
using namespace std;
int rx,ry;
template<typename T>
inline void read(T &x)
{
x=0;char c = getchar();int s = 1;
while(c < '0' || c > '9') {if(c == '-') s = -1;c = getchar();}//是符号
while(c >= '0' && c <= '9') {x = x*10 + c -'0';c = getchar();}//是数字
x*=s;
}
/*
SetConsoleTextAttribute(hStdOutHandle, wOldColorAttrs);
printf("恢复控制台文字默认颜色\n");
SetConsoleTextAttribute(hStdOutHandle, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
printf("设置控制台文字为绿色\n");
SetConsoleTextAttribute(hStdOutHandle, FOREGROUND_RED | FOREGROUND_INTENSITY);
printf("设置控制台文字为红色\n");
*/
int in,ma[10][10];
void SCAB(int ForgC, int BackC) {
WORD wColor = ((BackC & 0x0F) << 4) + (ForgC & 0x0F);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), wColor);
}
//前一个数据是字体颜色,后一个数据是背景颜色
void print_map(){
printf(" ");
for(int i=1;i<=8;i++)printf(" %d",i);
puts("");
printf("\n");
for(int i=1;i<=8;i++){
SCAB(7,0);
printf("%d",i);
for(int j=1;j<=8;j++){
if(ma[i][j]==0)SCAB(7,0);
else if(ma[i][j]==1)SCAB(4,0);
else SCAB(10,0);
printf(" ●");
//●
}
printf("\n\n");
}
}
int Reverse(int x,int y,int xx,int yy){
rx=x,ry=y;
if(xx==0&&yy==0)return 0;
int X=x,Y=y;
int p=ma[x][y];
int sum=0;
for(int i=1;i>=-1;i--)
for(int j=1;j>=-1;j--)
sum+=ma[i+x][j+y];
sum-=p;
if(sum==0){return sum;}
sum=0;
while(x>=1&&x<=8&&y>=1&&y<=8){
//cout<<x<<y<<endl;
x+=xx;
y+=yy;
if(ma[x][y]==0)break;
if(ma[x][y]==p)break;
ma[x][y]=p;
//print_map();
}
if(ma[x][y]==p){
//cout<<x<<" "<<y<<endl;
for(int i=X,j=Y;i>=1&&i<=8&&j>=1&&j<=8&&(i!=x||j!=y);i+=xx,j+=yy){sum++;}
sum=max(0,sum-1);
//cout<<sum<<endl;
}
rx=x;
ry=y;
return sum;
}
void put_back(int x,int y,int xx,int yy){
//printf("rx:%d ry:%d\n",rx,ry);
if(xx==0&&yy==0)return ;
int p=ma[x][y];
if(x+xx==rx&&y+yy==ry)return ;
if(x==rx&&y==ry)return ;
for(int i=x+xx,j=y+yy;i>=1&&i<=8&&j>=1&&j<=8&&(i!=rx||j!=ry);i+=xx,j+=yy)ma[i][j]=3-p;
return;
}
void robot_put_key(){
int ans=0,X=0,Y=0,sum;
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
if(ma[i][j]==0){
ma[i][j]=1;
sum=0;
for(int x=1;x>=-1;x--)
for(int y=1;y>=-1;y--){
sum+=Reverse(i,j,x,y);
put_back(i,j,x,y);
}
ma[i][j]=0;
if(sum>ans)X=i,Y=j;
}
if(X==0&&Y==0){
SCAB(7,0);
puts("无路可走");
return ;
}
ma[X][Y]=1;
//int rx=0,ry=0;
SCAB(7,0);
printf("机器人落子(%d,%d)\n",X,Y);
for(int x=1;x>=-1;x--)
for(int y=1;y>=-1;y--){
sum=Reverse(X,Y,x,y);
if(sum==0)put_back(X,Y,x,y);
}
return ;
}
void people_put_key(){
int ans=0,X=0,Y=0,sum;
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
if(ma[i][j]==0){
ma[i][j]=1;
sum=0;
for(int x=1;x>=-1;x--)
for(int y=1;y>=-1;y--){
sum+=Reverse(i,j,x,y);
put_back(i,j,x,y);
}
ma[i][j]=0;
if(sum>ans)X=i,Y=j;
}
if(X==0&&Y==0){
SCAB(7,0);
puts("无路可走");
return ;
}
SCAB(7,0);
puts("请输入落子点");
ans=0,X=0,Y=0,sum;
EXIT:
read(X),read(Y);
if(ma[X][Y]==1){
puts("不符合规则,请重新输入");
goto EXIT;
}
ma[X][Y]=2;
sum=0;
for(int x=1;x>=-1;x--)
for(int y=1;y>=-1;y--){
sum=Reverse(X,Y,x,y);
ans+=sum;
if(sum==0)put_back(X,Y,x,y);
}
if(ans==0){
puts("不符合规则,请重新输入");
ma[X][Y]=0;
goto EXIT;
}
return ;
}
void chess(){
EXIT:
SCAB(7,0);
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
ma[i][j]=0;
ma[5][5]=2;
ma[4][4]=2;
ma[5][4]=1;
ma[4][5]=1;
puts("请问您想要先落子(1)还是后落子(2)");
read(in);
if(in>2||in<=0){
puts("不符合规则,请重新输入");
goto EXIT;
}
if(in==2)robot_put_key();
print_map();
for(int lunci=1;lunci<=31;lunci++){
people_put_key();
print_map();
robot_put_key();
print_map();
}
if(in==2){
people_put_key();
print_map();
}
else {
people_put_key();
print_map();
robot_put_key();
print_map();
}
int green=0,red=0;
for(int i=1;i<=8;i++){
for(int j=1;j<=8;j++){
if(ma[i][j]==1)green++;
else red++;
}
}
if(red>green)puts("红色赢了");
else if(red==green)puts("红色赢了");
else puts("绿色赢了");
}
int main(){
if (MessageBox(0, TEXT("请不要输入任何字符,否则可能导致游戏崩溃!"), TEXT("重要提示"), MB_OKCANCEL | MB_ICONINFORMATION) != IDOK)
{
MessageBox(0, TEXT("既然你不同意,那就别玩了"), TEXT("重要提示"), MB_OK);
return 0;
};
cout << "游戏规则:把自己颜色的棋子放在棋盘的空格上,而当自己放下的棋子在横、竖、斜八个方向内有一个自己的棋子,则被夹在中间的对方棋子全部翻转会成为自己的棋子。夹住的位置上必须全部是对手的棋子,不能有空格。并且,只有在可以翻转棋子的地方才可以下子。一步棋可以在数个方向上翻棋,任何被夹住的棋子都必须被翻转过来,棋手无权选择不去翻某个棋子必须是刚下的子夹对方才能够翻对方的子,因翻转对方的棋子而夹住的子是不能被翻的。" << endl<<endl;
Sleep(100);
chess();
int a;
cin>>a;
while(a!=0)cin>>a;
return 0;
}
(部分调试没删,见谅)
接下来,我想简单谈谈各个函数的作用
函数设计
read
read是典型的传引用快读,这里的作用便是去除字符,正如我主函数的提示中所说,读入字符可能会导致程序崩溃,而快读在每次读入时会自动排除掉无关的字符,所以更加安全一些
SCAB
这个函数用来改变输出字体颜色,区分黑白棋
print_map
此函数用来绘制棋盘,为方便,我采用的是数组来保存棋局,尽量避免使用竖线和横线,因为~处于未知的原因,程序转移到别人的电脑上就会窜版,所以就没画网格线,见谅
Reverse
这个函数的功能主要是沿着指定的方向探索,知道找到和当前棋子相同颜色的,存在一个全局变量中,顺便将沿途的棋子翻过来,并返回翻转的棋子数量。
put_back
在人机落子时,会扫描整个棋盘,对于每个位置,如果可以翻的棋子并非最多,还可以将Reverse函数的作用逆转
robot_put_key
扫描棋盘,找到可以翻转的棋子的最大值的位置,翻转,储存新棋盘
people_put_key
同样扫描棋盘,如果无路可走跳到下一回合,纯模拟。
这是我的第二十九篇文章,如有纰漏也请各位大佬指正
辛苦创作不易,还望看官点赞收藏打赏,但是后续可能不会更新新的内容。
见谅,感谢支持。