我写的五子棋的所有版本
前言
图形化版需要 raylib 这个库来运行。不知道怎么弄的可以用小熊猫C++,里面自带了 raylib 库。
本蒟蒻第一次尝试写图形化界面,所以写的很朴实无华(简陋),欢迎各位试玩并提出各种建议(找 bug)。
命令行最新版
图形化最新版
图形化版
v1.8
说明:只是想下两个版本跳到2.0,所以这次该叫1.8
突然发现上次发的那个版本开头简介里的版本号没改。
//wzq v1.8 by Autboity
#include<windows.h>
#include<iostream>
#include<conio.h>
#include<time.h>
#include<string>
#include<thread>
#include<stack>
using namespace std;
#define Sleep(t) this_thread::sleep_for(chrono::milliseconds(t))
#define gotoxy(a,b) cout<<"\033["<<b<<";"<<a<<"H"
#define clf() cout<<"\033[J"
#define ilv inline void
string ch;//吃!
int a[20][20],turn,x,y,lx=8,ly=8;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
ilv cls(){gotoxy(1, 1);clf();}
ilv cll(){gotoxy(1,35);clf();}
void spark(string s){
for(int i=0;i<=6;i++){
cout<<"\033[27m";
cll();
if(i%2)cout<<"\033[7m"<<s;
else cout<<"\033[27m"<<s;
Sleep(100);
}
cout<<"\n";
}
void show(){//展示棋盘
cls();
cout<<"当前的棋盘。\n ";
for(int i=1;i<=15;i++)cout<<" "<<i / 10<<i%10<<" ";
cout<<" \n";
for(int i=1;i<=15;i++){
cout<<" ";
for(int j=1;j<=15;j++)cout<<"----";
cout<<"\n"<<i / 10<<i%10<<" ";
for(int j=1;j<=15;j++){
cout<<"|";
if(a[j][i]==0)cout<<" ";
else if(a[j][i]==1)cout<<" x ";
else if(a[j][i]==2)cout<<" o ";
}
cout<<"|\n";
}
cout<<" ";
for(int j=1;j<=15;j++)cout<<"----";
cout<<"\n(棋盘展示结束)\n";
gotoxy(66,11);cout<<" ----";
gotoxy(66,12);cout<<"|撤销|";
gotoxy(66,13);cout<<" ----";
gotoxy(66,21);cout<<" ----";
gotoxy(66,22);cout<<"|重做|";
gotoxy(66,23);cout<<" ----";
}
void help(bool b){
cls();
cout<<R"([38;2;0;0;0m[48;2;238;232;170mwzq v1.8
by Autboity
[48;2;255;217;102m+------------提示-------------+
|1.本程序默认 o 先手。 |
|2.按空格或回车落子。 |
|3.别的还没写好。 |
+-----------------------------+
[48;2;32;55;100m
[48;2;32;55;100m
[48;2;32;55;100m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;169;208;142m [48;2;255;217;102m [48;2;91;155;213m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;32;55;100m [48;2;255;217;102m [48;2;32;55;100m [48;2;169;208;142m [48;2;91;155;213m [48;2;255;217;102m [48;2;32;55;100m [48;2;169;208;142m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;32;55;100m [48;2;255;217;102m [48;2;91;155;213m [48;2;169;208;142m [48;2;91;155;213m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;169;208;142m [48;2;91;155;213m [48;2;169;208;142m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;169;208;142m [48;2;91;155;213m [48;2;169;208;142m [48;2;91;155;213m [48;2;169;208;142m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;169;208;142m [48;2;91;155;213m [48;2;169;208;142m [48;2;91;155;213m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;169;208;142m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m [48;2;169;208;142m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m
[48;2;32;55;100m [48;2;255;217;102m
)";
if(b)cout<<"(按任意键继续)\n";
else cout<<"(按任意键开始)\n";
cout<<"\033[48;2;238;232;170m";
getch();
show();
}
void put_chess(bool mode){
if(mode)cout<<"\033["<<2*ly+2<<";"<<4*lx+2<<"H"<<" ";
else cout<<"\033["<<2*y+2<<";"<<4*x+2<<"H"<<((turn%2)?"x":"o");
cll();
}
struct point{
int x,y;
};
stack<point>hist;//用于悔棋
void undo(){
if(hist.empty()){
cll();
spark("没有可撤回的棋子!");
return;
}
lx=hist.top().x,ly=hist.top().y;
a[lx][ly]=0;
put_chess(1);
hist.pop();
turn--;
cout<<"已撤回一步!\n";//死掉了
}
void init(){//初始化
for(int i=1;i<=15;i++){
for(int j=1;j<=15;j++){
a[i][j]=0;
}
}
while(!hist.empty())hist.pop();
turn=0;
show();
}
string op;
void move(){//落子模块
int px=lx,py=ly,outside=0;
while(true){
gotoxy(1,35);cll();
cout<<"当前为 "<<((turn%2)?"x":"o")<<" 方落子";
if(outside==1)gotoxy(67,12);
else if(outside==2)gotoxy(67,22);
else gotoxy(4*px+2,2*py+2);
int ch=getch();
if(ch>128)ch=getch();
if(ch==72){//↑
if(outside==1)continue;
if(outside==2)outside=1;
else py=max(1,py-1);
}else if(ch==80){//↓
if(outside==2)continue;
if(outside==1)outside=2;
else py=min(15,py+1);
}else if(ch==75){//←
if(outside){
px=15;
outside=0;
}else px=max(1,px-1);
}else if(ch==77){//→
if(outside)continue;
if(px==15){
if(py<8)outside=1;
else outside=2;
continue;
}else px++;
}else if(ch==13||ch==32){
if(outside==1){
undo();
continue;
}else if(outside==2){
spark("这个还没做");
continue;
}else if(a[px][py]){
spark("坐标处已有子,请重新落子!");
continue;
}
x=px;y=py;break;
}
}
put_chess(0);
hist.push(point{x,y});
if(turn%2)a[x][y]=1;
else a[x][y]=2;
turn++; lx=x;ly=y;
}
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>15||yy<1||yy>15)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
if(turn==226){
spark("平局!");
Sleep(1000);
spark("( 按任意键开始下一局 )");
getch();
init();
return;
}
for(dir=0;dir<4;dir++){
int aaa=dfs(x,y);
if(aaa>=5){
if(a[x][y]==1)spark("恭喜 x 方获胜");
else spark("恭喜 o 方获胜");
Sleep(1000);
spark("( 按任意键开始下一局 )");
getch();
init();
return;
}
for(int i=1;i<=15;i++){
for(int j=1;j<=15;j++){
vis[i][j]=0;
}
}
}
}
void SetConsoleSize(int col, int row){
char cmd[64];
sprintf(cmd,"mode con cols=%d lines=%d",col,row);
system(cmd);
}
int main(){
SetConsoleSize(15*5+5,15*2+8);
SetWindowLongPtrA(GetConsoleWindow(),GWL_STYLE,GetWindowLongPtrA(GetConsoleWindow(),GWL_STYLE)&~WS_SIZEBOX&~WS_MAXIMIZEBOX&~WS_MINIMIZEBOX);//仅Windows支持这行,想办法干掉
cin.tie(0);
cout.tie(0);
cout<<"\033[48;2;238;232;170m";cls();
help(0);
while(1){
move();
check();
}
return 0;
}
v1.2.1
说明:修了一个Bug(获胜之后undo,然后再redo不显示并不存在的胜利动画)
//图形化的 wzq v1.2.1 by Autboity
#include<raylib.h>
#include<rdrawing.h>
#include<time.h>
#define RAYGUI_IMPLEMENTATION
#include <raygui.h>
#define WINDOW_WIDTH 1000
#define WINDOW_HEIGHT 600
bool win;
int a[20][20],turn,x,y;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
int seq_real[400][2],seq_chess[400][2];
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>19||yy<1||yy>19)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
for(dir=0;dir<4;dir++){
if(dfs(x,y)>=5)win=1;
for(int i=0;i<turn;i++)vis[seq_chess[i][0]][seq_chess[i][1]]=0;
}
}
int main(){
InitWindow(WINDOW_WIDTH,WINDOW_HEIGHT,"wzq");
SetTraceLogLevel(LOG_WARNING);
SetTargetFPS(60);
SetRandomSeed(time(NULL));//用当前时间初始化随机数种子。
//读取字体文件
char guitests[]="!?撤回该黑棋白棋下赢了重开~!@#$%^&*()_+`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./{}|:\"<>?";
//将字符串中的字符逐一转换成Unicode码点,得到码点表
int codepointsCount;
int *codepoints=LoadCodepoints(guitests,&codepointsCount);
//读取仅含码点表中各字符的字体
Font font=LoadFontEx("c:\\windows\\fonts\\simhei.ttf",64,codepoints,codepointsCount);
//释放码点表
UnloadCodepoints(codepoints);
//设置GUI控件的字体和大小
GuiSetFont(font);
GuiSetStyle(DEFAULT,TEXT_SIZE,20);
//创建背景
Image bgimg=GenImageColor(WINDOW_WIDTH,WINDOW_HEIGHT,GetColor(0xEEE8AAFF));
for(int i=0;i<=19;i++){
ImageDrawRectangle(&bgimg,110,10+30 *i,571,1,BLACK);
ImageDrawRectangle(&bgimg,110+30 *i,10,1,571,BLACK);
}
Texture background=LoadTextureFromImage(bgimg);
UnloadImage(bgimg);//不再需要使用img,释放掉
//创建棋子
Image ches_1=GenImageColor(30,30,BLANK),ches_2=GenImageColor(30,30,BLANK);
ImageDrawCircle(&ches_1,15,15,13,WHITE);ImageDrawCircle(&ches_2,15,15,13,BLACK);
Texture chess_1=LoadTextureFromImage(ches_1);Texture chess_2=LoadTextureFromImage(ches_2);
UnloadImage(ches_1);UnloadImage(ches_2);
//创建叉叉 ( × )
Image error_img=GenImageColor(30,30,BLANK);
ImageDrawLineEx(&error_img,5,5,25,25,3,RED);
ImageDrawLineEx(&error_img,25,5,5,25,3,RED);
Texture error_sign=LoadTextureFromImage(error_img);
UnloadImage(error_img);
while(!WindowShouldClose()){
int Mouse_x=GetMouseX(),Mouse_y=GetMouseY();
x=(Mouse_x-110)/30+1;y=(Mouse_y-10)/30+1;
int real_x=(x-1)*30+110,real_y=(y-1)*30+10;
BeginDrawing();
ClearBackground(WHITE);
DrawTexture(background,0,0,WHITE);
for(int i=0;i<turn;i++)
DrawTexture(i%2?chess_1:chess_2,seq_real[i][0],seq_real[i][1],WHITE);
if(x>=1&&x<=19&&y>=1&&y<=19){
if(!a[x][y]){
DrawTexture(turn%2?chess_1:chess_2,real_x,real_y,GetColor(0xFFFFFF88));
if(IsMouseButtonPressed(0)){
a[x][y]=turn%2+1;
seq_chess[turn][0]=x;seq_chess[turn][1]=y;
seq_real[turn][0]=real_x;seq_real[turn][1]=real_y;
turn++;
seq_chess[turn][0]=0;seq_chess[turn][1]=0;//为了撤销多次然后再下一字然后仍能重做这个Bug而打的补丁
check();
}
} else {
DrawTexture(error_sign,real_x,real_y,WHITE);
}
}
//GUI
if((GuiButton((Rectangle){760,150,60,30},"undo")||(IsKeyDown(KEY_LEFT_CONTROL)&&IsKeyPressedRepeat(KEY_Z)))&&turn>0){
turn--;
a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
}
if((GuiButton((Rectangle){760,200,60,30},"redo")||(IsKeyDown(KEY_LEFT_CONTROL)&&IsKeyPressedRepeat(KEY_Y)))&&seq_chess[turn][0]){
a[seq_chess[turn][0]][seq_chess[turn][1]]=turn%2+1;
x=seq_chess[turn][0];y=seq_chess[turn][1];//同下
turn++;
check();//undo之后接redo要判一下胜负
}
if((GuiButton((Rectangle){750,450,100,30},"restart")||IsKeyPressed(KEY_R))&&turn>0){
while(turn--)seq_chess[turn][0]=a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
turn=0;//很奇怪,上面的语句不就是执行到turn为0才结束吗,但是不加这句就不对,很奇怪
}
if(win){ //获胜
while(1){
EndDrawing();
BeginDrawing();
for(int i=0;i<turn;i++){
DrawTexture(i%2?chess_1:chess_2,seq_real[i][0],seq_real[i][1],WHITE);
}
DrawTexture(background,0,0,GetColor(0xFEDCBAAA));
if((GuiButton((Rectangle){745,135,90,60},"undo")||(IsKeyDown(KEY_LEFT_CONTROL)&&IsKeyPressedRepeat(KEY_Z)))&&turn>0){
turn--;
a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
win=0;
break;
}
if(GuiButton((Rectangle){735,435,130,60},"restart")&&turn>0){
while(turn--)seq_chess[turn][0]=a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
win=0;turn=0;//很奇怪,同上,很奇怪
break;
}
}
}
EndDrawing();
}
UnloadTexture(background);//释放texture对象
UnloadTexture(chess_1);
UnloadTexture(chess_2);
UnloadTexture(error_sign);
UnloadFont(font);//释放字体
CloseWindow();
return 0;
}
v1.2
说明:修了一个Bug(撤销多次然后再下一字然后再仍能重做),并不再让叉叉在棋盘外显示了
现在你可以用快捷键来撤销(Ctrl+Z)、重做(Ctrl+Y)和重开(R)了!
//图形化的 wzq v1.2 by Autboity
#include<raylib.h>
#include<rdrawing.h>
#include<time.h>
#define RAYGUI_IMPLEMENTATION
#include <raygui.h>
#define WINDOW_WIDTH 1000
#define WINDOW_HEIGHT 600
bool win;
int a[20][20],turn,x,y;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
int seq_real[400][2],seq_chess[400][2];
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>19||yy<1||yy>19)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
for(dir=0;dir<4;dir++){
if(dfs(x,y)>=5)win=1;
for(int i=0;i<turn;i++)vis[seq_chess[i][0]][seq_chess[i][1]]=0;
}
}
int main(){
InitWindow(WINDOW_WIDTH,WINDOW_HEIGHT,"wzq");
SetTraceLogLevel(LOG_WARNING);
SetTargetFPS(60);
SetRandomSeed(time(NULL));//用当前时间初始化随机数种子。
//读取字体文件
char guitests[]="!?撤回该黑棋白棋下赢了重开~!@#$%^&*()_+`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./{}|:\"<>?";
//将字符串中的字符逐一转换成Unicode码点,得到码点表
int codepointsCount;
int *codepoints=LoadCodepoints(guitests,&codepointsCount);
//读取仅含码点表中各字符的字体
Font font=LoadFontEx("c:\\windows\\fonts\\simhei.ttf",64,codepoints,codepointsCount);
//释放码点表
UnloadCodepoints(codepoints);
//设置GUI控件的字体和大小
GuiSetFont(font);
GuiSetStyle(DEFAULT,TEXT_SIZE,20);
//创建背景
Image bgimg=GenImageColor(WINDOW_WIDTH,WINDOW_HEIGHT,GetColor(0xEEE8AAFF));
for(int i=0;i<=19;i++){
ImageDrawRectangle(&bgimg,110,10+30 *i,571,1,BLACK);
ImageDrawRectangle(&bgimg,110+30 *i,10,1,571,BLACK);
}
Texture background=LoadTextureFromImage(bgimg);
UnloadImage(bgimg);//不再需要使用img,释放掉
//创建棋子
Image ches_1=GenImageColor(30,30,BLANK),ches_2=GenImageColor(30,30,BLANK);
ImageDrawCircle(&ches_1,15,15,13,WHITE);ImageDrawCircle(&ches_2,15,15,13,BLACK);
Texture chess_1=LoadTextureFromImage(ches_1);Texture chess_2=LoadTextureFromImage(ches_2);
UnloadImage(ches_1);UnloadImage(ches_2);
//创建叉叉 ( × )
Image error_img=GenImageColor(30,30,BLANK);
ImageDrawLineEx(&error_img,5,5,25,25,3,RED);
ImageDrawLineEx(&error_img,25,5,5,25,3,RED);
Texture error_sign=LoadTextureFromImage(error_img);
UnloadImage(error_img);
while(!WindowShouldClose()){
int Mouse_x=GetMouseX(),Mouse_y=GetMouseY();
x=(Mouse_x-110)/30+1;y=(Mouse_y-10)/30+1;
int real_x=(x-1)*30+110,real_y=(y-1)*30+10;
BeginDrawing();
ClearBackground(WHITE);
DrawTexture(background,0,0,WHITE);
for(int i=0;i<turn;i++)
DrawTexture(i%2?chess_1:chess_2,seq_real[i][0],seq_real[i][1],WHITE);
if(x>=1&&x<=19&&y>=1&&y<=19){
if(!a[x][y]){
DrawTexture(turn%2?chess_1:chess_2,real_x,real_y,GetColor(0xFFFFFF88));
if(IsMouseButtonPressed(0)){
a[x][y]=turn%2+1;
seq_chess[turn][0]=x;seq_chess[turn][1]=y;
seq_real[turn][0]=real_x;seq_real[turn][1]=real_y;
turn++;
seq_chess[turn][0]=0;seq_chess[turn][1]=0;//为了撤销多次然后再下一字然后仍能重做这个Bug而打的补丁
check();
}
} else {
DrawTexture(error_sign,real_x,real_y,WHITE);
}
}
//GUI
if((GuiButton((Rectangle){760,150,60,30},"undo")||(IsKeyDown(KEY_LEFT_CONTROL)&&IsKeyPressedRepeat(KEY_Z)))&&turn>0){
turn--;
a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
}
if((GuiButton((Rectangle){760,200,60,30},"redo")||(IsKeyDown(KEY_LEFT_CONTROL)&&IsKeyPressedRepeat(KEY_Y)))&&seq_chess[turn][0]){
a[seq_chess[turn][0]][seq_chess[turn][1]]=turn%2+1;
turn++;
}
if((GuiButton((Rectangle){750,450,100,30},"restart")||IsKeyPressed(KEY_R))&&turn>0){
while(turn--)seq_chess[turn][0]=a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
turn=0;//很奇怪,上面的语句不就是执行到turn为0才结束吗,但是不加这句就不对,很奇怪
}
if(win){ //获胜
while(1){
EndDrawing();
BeginDrawing();
for(int i=0;i<turn;i++){
DrawTexture(i%2?chess_1:chess_2,seq_real[i][0],seq_real[i][1],WHITE);
}
DrawTexture(background,0,0,GetColor(0xFEDCBAAA));
if((GuiButton((Rectangle){745,135,90,60},"undo")||(IsKeyDown(KEY_LEFT_CONTROL)&&IsKeyPressedRepeat(KEY_Z)))&&turn>0){
turn--;
a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
win=0;
break;
}
if(GuiButton((Rectangle){735,435,130,60},"restart")&&turn>0){
while(turn--)seq_chess[turn][0]=a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
win=0;turn=0;//很奇怪,同上,很奇怪
break;
}
}
}
EndDrawing();
}
UnloadTexture(background);//释放texture对象
UnloadTexture(chess_1);
UnloadTexture(chess_2);
UnloadTexture(error_sign);
UnloadFont(font);//释放字体
CloseWindow();
return 0;
}
v1.1
说明:抄袭实现了命令行 v1.3.1 版里的所有功能,并出现了一件百思不得其解的事。。。
//图形化的 wzq v1.1 by Autboity
#include<raylib.h>
#include<rdrawing.h>
#include<time.h>
#define RAYGUI_IMPLEMENTATION
#include <raygui.h>
#define WINDOW_WIDTH 1000
#define WINDOW_HEIGHT 600
bool win;
int a[20][20], turn, x, y;
int vis[20][20], d[4][2] = {{0, 1}, {1, 0}, {1, 1}, {1, -1}}, dir;
int seq_real[400][2], seq_chess[400][2];
int dfs(int xx, int yy) {
if (a[xx][yy] != a[x][y] || vis[xx][yy] || xx < 1 || xx > 19 || yy < 1 || yy > 19)return 0;
vis[xx][yy] = 1;
return 1 + dfs(xx + d[dir][0], yy + d[dir][1]) + dfs(xx - d[dir][0], yy - d[dir][1]);
}
void check() {
for (dir = 0; dir < 4; dir++) {
if (dfs(x, y) >= 5)win = 1;
for (int i = 0; i < turn; i++)vis[seq_chess[i][0]][seq_chess[i][1]] = 0;
}
}
int main() {
//启用反锯齿
SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "wzq");
SetTraceLogLevel(LOG_WARNING);
SetTargetFPS(60);
SetRandomSeed(time(NULL)); //用当前时间初始化随机数种子。
//读取字体文件
char guitests[] = "!?撤回该黑棋白棋下赢了重开~!@#$%^&*()_+`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./{}|:\"<>?";
// 将字符串中的字符逐一转换成Unicode码点,得到码点表
int codepointsCount;
int *codepoints = LoadCodepoints(guitests, &codepointsCount);
// 读取仅含码点表中各字符的字体
Font font = LoadFontEx("c:\\windows\\fonts\\simhei.ttf", 64, codepoints, codepointsCount);
// 释放码点表
UnloadCodepoints(codepoints);
//设置GUI控件的字体和大小
GuiSetFont(font);
GuiSetStyle(DEFAULT, TEXT_SIZE, 20);
//创建背景
Image bgimg = GenImageColor(WINDOW_WIDTH, WINDOW_HEIGHT, GetColor(0xEEE8AAFF));
for (int i = 0; i <= 19; i++) {
ImageDrawRectangle(&bgimg, 110, 10 + 30 * i, 571, 1, BLACK);
ImageDrawRectangle(&bgimg, 110 + 30 * i, 10, 1, 571, BLACK);
}
Texture background = LoadTextureFromImage(bgimg);
UnloadImage(bgimg); //不再需要使用img,释放掉
//创建棋子
Image ches_1 = GenImageColor(30, 30, BLANK), ches_2 = GenImageColor(30, 30, BLANK);
ImageDrawCircle(&ches_1, 15, 15, 13, WHITE);
ImageDrawCircle(&ches_2, 15, 15, 13, BLACK);
Texture chess_1 = LoadTextureFromImage(ches_1);
Texture chess_2 = LoadTextureFromImage(ches_2);
UnloadImage(ches_1);
UnloadImage(ches_2);
//创建叉叉 ( × )
Image error_img = GenImageColor(30, 30, BLANK);
ImageDrawLineEx(&error_img, 5, 5, 25, 25, 3, RED);
ImageDrawLineEx(&error_img, 25, 5, 5, 25, 3, RED);
Texture error_sign = LoadTextureFromImage(error_img);
UnloadImage(error_img);
while (!WindowShouldClose()) {
int Mouse_x = GetMouseX(), Mouse_y = GetMouseY();
x = (Mouse_x - 110) / 30 + 1;
y = (Mouse_y - 10) / 30 + 1;
int real_x = (x - 1) * 30 + 110, real_y = (y - 1) * 30 + 10;
BeginDrawing();
ClearBackground(WHITE);
DrawTexture(background, 0, 0, WHITE);
for (int i = 0; i < turn; i++) {
DrawTexture(i % 2 ? chess_1 : chess_2, seq_real[i][0], seq_real[i][1], WHITE);
}
if (x >= 1 && x <= 19 && y >= 1 && y <= 19) {
if (!a[x][y]) {
DrawTexture(turn % 2 ? chess_1 : chess_2, real_x, real_y, GetColor(0xFFFFFF88));
if (IsMouseButtonPressed(0)) {
a[x][y] = turn % 2 + 1;
seq_chess[turn][0] = x;
seq_chess[turn][1] = y;
seq_real[turn][0] = real_x;
seq_real[turn][1] = real_y;
turn++;
check();
}
} else {
DrawTexture(error_sign, real_x, real_y, WHITE);
}
} else {
DrawTexture(error_sign, Mouse_x - 15, Mouse_y - 15, WHITE);
}
//GUI
if (GuiButton((Rectangle) { 760, 150, 60, 30 }, "undo") && turn > 0) {
turn--;
a[seq_chess[turn][0]][seq_chess[turn][1]] = 0;
}
if (GuiButton((Rectangle) { 760, 200, 60, 30 }, "redo") && seq_chess[turn][0]) {
a[seq_chess[turn][0]][seq_chess[turn][1]] = turn % 2 + 1;
turn++;
}
if (GuiButton((Rectangle) { 750, 450, 100, 30 }, "restart")&&turn>0) {
while(turn--)seq_chess[turn][0]=a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
turn=0;//很奇怪,上面的语句不就是执行到turn为0才结束吗,但是不加这句就不对,很奇怪
}
if (win) { //获胜
EndDrawing();
while (1) {
BeginDrawing();
for (int i = 0; i < turn; i++) {
DrawTexture(i % 2 ? chess_1 : chess_2, seq_real[i][0], seq_real[i][1], WHITE);
}
DrawTexture(background, 0, 0, GetColor(0xFEDCBAAA));
if (GuiButton((Rectangle) { 735, 235, 130, 60 }, "restart")&&turn>0) {
while(turn--)seq_chess[turn][0]=a[seq_chess[turn][0]][seq_chess[turn][1]]=0;
win=0;turn=0;//很奇怪,上面的语句不就是执行到turn为0才结束吗,但是不加这句就不对,很奇怪
break;
}
EndDrawing();
}
}
EndDrawing();
}
UnloadTexture(background); //释放texture对象
UnloadTexture(chess_1);
UnloadTexture(chess_2);
UnloadTexture(error_sign);
UnloadFont(font);//释放字体
CloseWindow();
return 0;
}
v1.0
说明:图形化之梦的开始
//图形化的 wzq v1.0 by Autboity
#include<raylib.h>
#include<rdrawing.h>
#include<time.h>
#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
int a[20][20],turn,x,y;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
int seq_real[400][2],seq_chess[400][2];
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>19||yy<1||yy>19)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
bool check(){
for(dir=0;dir<4;dir++){
if(dfs(x,y)>=5){
return 1;
}
for(int i=0;i<turn;i++)vis[seq_chess[i][0]][seq_chess[i][1]]=0;
}
return 0;
}
int main(){
//启用反锯齿
SetConfigFlags(FLAG_MSAA_4X_HINT);
InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT,"wzq");
SetTraceLogLevel(LOG_WARNING);
SetTargetFPS(60);
SetRandomSeed(time(NULL)); //用当前时间初始化随机数种子。
//创建背景
Image bgimg=GenImageColor(WINDOW_WIDTH,WINDOW_HEIGHT,GetColor(0xEEE8AAFF));
for(int i=0;i<=19;i++){
ImageDrawRectangle(&bgimg,110,10+30*i,571,1,BLACK);
ImageDrawRectangle(&bgimg,110+30*i,10,1,571,BLACK);
}
Texture background = LoadTextureFromImage(bgimg);
//创建棋子
Image ches_1=GenImageColor(30,30,BLANK),ches_2=GenImageColor(30,30,BLANK);
ImageDrawCircle(&ches_1,15,15,13,WHITE);ImageDrawCircle(&ches_2,15,15,13,BLACK);
Texture chess_1=LoadTextureFromImage(ches_1);
Texture chess_2=LoadTextureFromImage(ches_2);
//创建叉叉 ( × )
Image error_img=GenImageColor(30,30,BLANK);
ImageDrawLineEx(&error_img,5,5,25,25,3,RED);
ImageDrawLineEx(&error_img,25,5,5,25,3,RED);
Texture error_sign=LoadTextureFromImage(error_img);
bool ok=0;
while (!WindowShouldClose()) {
int Mouse_x=GetMouseX(),Mouse_y=GetMouseY();
x=(Mouse_x-110)/30+1;y=(Mouse_y-10)/30+1;
int real_x=(x-1)*30+110,real_y=(y-1)*30+10;
BeginDrawing();
ClearBackground(WHITE);
DrawTexture(background,0,0,WHITE);
for(int i=0;i<turn;i++){
DrawTexture(i%2?chess_1:chess_2,seq_real[i][0],seq_real[i][1],WHITE);
}
if(x>=1&&x<=19&&y>=1&&y<=19){
if(!a[x][y]){
DrawTexture(turn%2?chess_1:chess_2,real_x,real_y,GetColor(0xFFFFFF44));
if(IsMouseButtonPressed(0)){
a[x][y]=turn%2+1;
seq_chess[turn][0]=x;seq_chess[turn][1]=y;
seq_real[turn][0]=real_x;seq_real[turn][1]=real_y;
turn++;
if(check())ok=1;
}
}else{
DrawTexture(error_sign,real_x,real_y,WHITE);
}
}else{
DrawTexture(error_sign,Mouse_x-15,Mouse_y-15,WHITE);
}
if(ok){
EndDrawing();
while(!WindowShouldClose()){
BeginDrawing();
for(int i=0;i<turn;i++){
DrawTexture(i%2?chess_1:chess_2,seq_real[i][0],seq_real[i][1],WHITE);
}
DrawTexture(background,0,0,GetColor(0xFEDCBAAA));
EndDrawing();
}
break;
}
EndDrawing();
}
UnloadImage(bgimg); //不再需要使用img,释放掉
UnloadTexture(background); //释放texture对象
UnloadTexture(chess_1);
UnloadTexture(chess_2);
UnloadTexture(error_sign);
UnloadImage(ches_1);
UnloadImage(ches_2);
UnloadImage(error_img);
CloseWindow();
return 0;
}
命令行版
1.5.5
说明:原来五子棋应该是15*15的棋盘啊,我一直都以为是19*19的,CTRL+F然后把19改成15然后提交新版本······
当然不是这样的。改完就出 bug 了,屏幕下方预留的空间不够,导致一回车就会把上面的字挤掉(可以自行一试),然后,就错位了。。。
为此我“复习”了半个小时的ANSI转义序列。。。(鉴定为不写注释导致的)
为什么只是 1.5.5 呢?因为原计划是加入对是否支持ANSI序列的判断,但我还没写好。。。
如果你的设备支持ANSI转义序列,这一版应该体验是最好的了。
//wzq v1.5.5 by Autboity
#include<iostream>
#include<stack>
#include<string>
#include<thread>
#include<windows.h>
using namespace std;
string ch;//吃!
int a[20][20],turn,x,y,lx,ly;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
void cls(){ cout<<"\033[H\033[J";}//清屏
void cll(){ cout<<"\033[35H\033[J";}//清除一些行...不好解释,具体请搜索:ANSI转义序列
void Sleep(int t){this_thread::sleep_for(chrono::microseconds(t));}
void spark(string s){
for(int i=0;i<=6;i++){
cout<<"\033[27m";
cll();
if(i%2)cout<<"\033[7m"<<s;
else cout<<"\033[27m"<<s;
Sleep(100);
}
cout<<"\n";
}
void show(){//展示棋盘
cls();
cout<<"当前的棋盘。\n ";
for(int i=1;i<=15;i++)cout<<" "<<i / 10<<i%10<<" ";
cout<<" \n";
for(int i=1;i<=15;i++){
cout<<" ";
for(int j=1;j<=15;j++)cout<<"----";
cout<<"\n"<<i / 10<<i%10<<" ";
for(int j=1;j<=15;j++){
cout<<"|";
if(a[j][i]==0)cout<<" ";
else if(a[j][i]==1)cout<<" x ";
else if(a[j][i]==2)cout<<" o ";
}
cout<<"|\n";
}
cout<<" ";
for(int j=1;j<=15;j++)cout<<"----";
cout<<"\n(棋盘展示结束)\n";
}
void help(bool b){
cls();
cout<<R"(wzq v1.5
by Autboity
***提示***
1.本程序默认 o 先手。
2.每方轮流按 “x y ”的形式输入落子坐标。
3.请输入阿拉伯数字!!!
4.支持的指令:
undo 撤回上一步落子
change 交换棋权(让对方一子)
reset 重开
help 查看帮助信息
)";
if(b)cout<<"(回车以继续)\n";
else cout<<"(回车以开始)\n";
getline(cin,ch);
show();
}
void put_chess(bool mode){
if(mode)cout<<"\033["<<2*ly+2<<";"<<4*lx+2<<"H"<<" ";
else cout<<"\033["<<2*y+2<<";"<<4*x+2<<"H"<<((turn%2)?"x":"o");
cll();
}
struct point{
int x,y;
};
stack<point>hist;//用于悔棋
void undo(){
if(hist.empty()){
cll();
spark("没有可撤回的棋子!");
return;
}
lx=hist.top().x,ly=hist.top().y;
a[lx][ly]=0;
put_chess(1);
hist.pop();
turn--;
cout<<"已撤回一步!\n";
}
void init(){//初始化
for(int i=1;i<=15;i++){
for(int j=1;j<=15;j++){
a[i][j]=0;
}
}
while(!hist.empty())hist.pop();
turn=0;
show();
}
string op;
void move(){//落子模块
while(1){
cout<<"请 "<<((turn%2)?"x":"o")<<" 方输入落子坐标:\n";
getline(cin,op);
x=y=0;
op.erase(0,op.find_first_not_of(" "));
op.erase(op.find_last_not_of(" ")+1);
if(op.size()>18){
spark("输入的东西太长了,请重新输入!");
continue;
}
if(op[0]>='0'&&op[0]<='9'){
bool have_jump=0;
for(int i=0;i<int(op.size());i++){
if(op[i]==' '){
if(have_jump){
x=y=0;break;
}
while(i<int(op.size())&&op[i]==' ')i++;
have_jump=1;
}
if(op[i]<'0'||op[i]>'9'){
x=y=0;
break;
}
if(!have_jump)x=x*10+op[i]-'0';
else y=y*10+op[i]-'0';
}
}else{
if(op=="undo")undo();
else if(op=="reset")init();
else if(op=="help")help(1);
else if(op=="change"){
turn++;
cll();
}else if(op=="")cll();
else spark("输入了错误的指令,请重新输入!");
continue;
}
if(!(x<1||x>15||y<1||y>15||a[x][y])){
hist.push(point{x,y});
break;
}else{
cll();
if(x<1||x>15||y<1||y>15)spark("输入了错误的坐标,请按“x y”的形式重新输入落子坐标!(x,y ∈[1,15]且为整数)");
else if(a[x][y])spark("坐标处已有子,请重新输入落子坐标!");
continue;
}
}
put_chess(0);
if(turn%2)a[x][y]=1;
else a[x][y]=2;
turn++;
}
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>15||yy<1||yy>15)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
for(dir=0;dir<4;dir++){
int aaa=dfs(x,y);
if(aaa >= 5){
if(a[x][y]==1)spark("恭喜 x 方获胜");
else spark("恭喜 o 方获胜");
Sleep(1000);
spark("( 按回车开始下一局 )");
getline(cin,ch);
init();
return;
}
for(int i=1;i<=15;i++){
for(int j=1;j<=15;j++){
vis[i][j]=0;
}
}
}
}
void SetConsoleSize(int col, int row){
char cmd[64];
sprintf(cmd,"mode con cols=%d lines=%d",col,row);
system(cmd);
}
int main(){
cout<<"\033[38;2;0;0;0m\033[48;2;238;232;170m";
SetConsoleSize(15*5,15*2+8);
SetWindowLongPtrA(GetConsoleWindow(),GWL_STYLE,GetWindowLongPtrA(GetConsoleWindow(),GWL_STYLE)&~WS_SIZEBOX&~WS_MAXIMIZEBOX&~WS_MINIMIZEBOX);//仅Windows支持这行,想办法干掉
cin.tie(0);
cout.tie(0);
help(0);
while(1){
move();
check();
}
return 0;
}
1.5
说明:把其它 getchar() 也改成了 getline() ,然后把 ANSI转义序列 看了一下是什么,然后又改了“一小会儿”。 现在输入输出变少了(因为直接用转义序列跳到对应位置打一个 x/o 就好了),所以理论上更流畅了。
还有就是加了提示的动画(闪烁)
//wzq v1.5 by Autboity
#include<windows.h>
#include<iostream>
#include<stack>
#include<string>
using namespace std;
string ch;//吃
int a[20][20],turn,x,y,lx,ly;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
void cls(){cout<<"\033[H\033[J";}//清屏
void cll(){cout<<"\033[43H\033[J";}//清除一些行...不好解释,具体请搜索:ANSI转义序列
void spark(string s){
for(int i=0;i<5;i++){
cout<<"\033[27m";
cll();
if(i%2)cout<<"\033[7m"<<s;
else cout<<"\033[27m"<<s;
Sleep(100);
}
cout<<"\n";
}
void show(){//展示棋盘
cls();
cout<<"当前的棋盘。\n";
for(int i=1;i<=19;i++)cout<<" "<<i/10<<i%10<<" ";
cout<<" \n";
for(int i=1;i<=19;i++){
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n"<<i/10<<i%10<<" ";
for(int j=1;j<=19;j++){
cout<<"|";
if(a[j][i]==0)cout<<" ";
else if(a[j][i]==1)cout<<" x ";
else if(a[j][i]==2)cout<<" o ";
}
cout<<"|\n";
}
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n(棋盘展示结束)\n";
}
void help(bool b){
cls();
cout<<R"(wzq v1.5
by Autboity
***提示***
1.本程序默认 o 先手。
2.每方轮流按 “x y ”的形式输入落子坐标。
3.请输入阿拉伯数字!!!
4.支持的指令:
undo 撤回上一步落子
change 交换棋权(让对方一子)
reset 重开
help 查看帮助信息
)";
if(b)cout<<"(回车以继续)\n";
else cout<<"(回车以开始)\n";
getline(cin,ch);
show();
}
void put_chess(bool mode){
if(!mode)cout<<"\033["<<2*y+2<<";"<<4*x+2<<"H"<<((turn%2)?"x":"o");
else cout<<"\033["<<2*ly+2<<";"<<4*lx+2<<"H"<<" ";
cll();
}
struct point{
int x,y;
};
stack<point>hist;//用于悔棋
void undo(){
if(hist.empty()){
cll();
spark("没有可撤回的棋子!");
return;
}
lx=hist.top().x,ly=hist.top().y;
a[lx][ly]=0;
put_chess(1);
hist.pop();
turn--;
cout<<"已撤回一步!\n";
}
void init(){//初始化
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
a[i][j]=0;
}
}
while(!hist.empty())hist.pop();
turn=0;
show();
}
string op;
void move(){//落子模块
while(1){
cout<<"请 "<<((turn%2)?"x":"o")<<" 方输入落子坐标:\n";
getline(cin,op);
x=y=0;
op.erase(0,op.find_first_not_of(" "));
op.erase(op.find_last_not_of(" ") + 1);
if(op.size()>18){
spark("输入的东西太长了,请重新输入!");
continue;
}
if(op[0]>='0'&&op[0]<='9'){
bool have_jump=0;
for(int i=0;i<int(op.size());i++){
if(op[i]==' '){
if(have_jump){
x=y=0;break;
}
while(i<int(op.size())&&op[i]==' ')i++;
have_jump=1;
}
if(op[i]<'0'||op[i]>'9'){
x=y=0;
break;
}
if(!have_jump)x=x*10+op[i]-'0';
else y=y*10+op[i]-'0';
}
}else{
if(op=="undo")undo();
else if(op=="reset")init();
else if(op=="help")help(1);
else if(op=="change"){
turn++;
cll();
}else if(op=="")cll();
else spark("输入了错误的指令,请重新输入!");
continue;
}
if(!(x<1||x>19||y<1||y>19||a[x][y])){
hist.push(point{x,y});
break;
}else{
cll();
if(x<1||x>19||y<1||y>19)spark("输入了错误的坐标,请按“x y”的形式重新输入落子坐标!(x,y ∈[1,19]且为整数)");
else if(a[x][y])spark("坐标处已有子,请重新输入落子坐标!");
continue;
}
}
put_chess(0);
if(turn%2)a[x][y]=1;
else a[x][y]=2;
turn++;
}
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>19||yy<1||yy>19)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
for(dir=0;dir<4;dir++){
int aaa=dfs(x,y);
if(aaa>=5){
if(a[x][y]==1)spark("恭喜 x 方获胜");
else spark("恭喜 o 方获胜");
Sleep(1000);
spark("( 按回车开始下一局 )");
getline(cin,ch);
init();
return;
}
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
vis[i][j]=0;
}
}
}
}
void SetConsoleSize(int x, int y, int cols, int lines){//感谢夏树大佬的https://tree.moe/c-set-console-size/
HANDLE hOut;
CONSOLE_FONT_INFO consoleCurrentFont;
COORD bufferSize,fontSize;
TCHAR title[256];
HWND hWnd;
//Set console buffer size
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetCurrentConsoleFont(hOut, false, &consoleCurrentFont);
fontSize = GetConsoleFontSize(hOut,consoleCurrentFont.nFont);
bufferSize.X = cols;
bufferSize.Y = lines;
SetConsoleScreenBufferSize(hOut, bufferSize);
//Set console window size
GetConsoleTitle(title, 256);
hWnd = FindWindow(0, title);
MoveWindow(hWnd,x,y,(cols+4)*fontSize.X,(lines+2)*fontSize.Y,true);
}
int main(){
cout<<"\033[38;2;0;0;0m\033[48;2;238;232;170m";
SetConsoleSize(250,60,19*5,19*3);
cin.tie(0);
cout.tie(0);
help(0);
while(1){
move();
check();
}
return 0;
}
1.4
说明:网上看到一个 C++ 清屏的指令,忍不住又“稍微”动了一下代码,加了一个 help 指令,然后把报错改的更明确了,又翻到一个能修改控制台窗口大小的函数,又“小改”了一下,然后把一部分的(一个) getchar() 改成了 getline()
//wzq v1.4 by Autboity
#include<windows.h>
#include<iostream>
#include<stack>
#include<string>
using namespace std;
string ch;//吃
void cls(){cout<<"\033[2J\033[H";}
void help(bool b){
cls();
cout<<R"(
wzq v1.3.1 by Autboity 洛谷:https://www.luogu.com.cn/user/1086798
***提示***
1.本程序默认 o 先手。
2.每方轮流按 “x y ”的形式输入落子坐标。
3.请输入阿拉伯数字!!!
4.支持的指令:
undo 撤回上一步落子
change 交换棋权(让对方一子)
restart 重开
help 查看帮助信息
)";
if(b)cout<<"(回车以继续)\n";
else cout<<"(回车以开始)\n";
getline(cin,ch);
}
int a[20][20],turn,x,y;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
void SetConsoleSize(int x, int y, int cols, int lines){//感谢夏树大佬的https://tree.moe/c-set-console-size/
HANDLE hOut;
CONSOLE_FONT_INFO consoleCurrentFont;
COORD bufferSize,fontSize;
TCHAR title[256];
HWND hWnd;
//Set console buffer size
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetCurrentConsoleFont(hOut, false, &consoleCurrentFont);
fontSize = GetConsoleFontSize(hOut,consoleCurrentFont.nFont);
bufferSize.X = cols;
bufferSize.Y = lines;
SetConsoleScreenBufferSize(hOut, bufferSize);
//Set console window size
GetConsoleTitle(title, 256);
hWnd = FindWindow(0, title);
MoveWindow(hWnd,x,y,(cols+4)*fontSize.X,(lines+2)*fontSize.Y,true);
}
void show(){//展示棋盘
cls();
cout<<"当前的棋盘。\n ";
for(int i=1;i<=19;i++)cout<<" "<<i/10<<i%10<<" ";
cout<<"\n";
for(int i=1;i<=19;i++){
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n"<<i/10<<i%10<<" ";
for(int j=1;j<=19;j++){
cout<<"|";
if(a[j][i]==0)cout<<" ";
else if(a[j][i]==1)cout<<" x ";
else if(a[j][i]==2)cout<<" o ";
}
cout<<"|\n";
}
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n(棋盘展示结束)\n";
}
struct point{
int x,y;
};
stack<point>hist;//用于悔棋
void undo(){
if(hist.empty()){
cout<<"没有可撤回的棋子!";
return;
}
int lx=hist.top().x,ly=hist.top().y;
a[lx][ly]=0;
hist.pop();
turn--;
cout<<"已撤回一步!\n";
show();
}
void init(){//初始化
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
a[i][j]=0;
}
}
while(!hist.empty())hist.pop();
turn=0;
}
string op1,op2;//用输入其它指令,同时避免user输入奇奇怪怪的东西
int re_int(string a){//把string 转 int
int ans=0,i=0;
for(;i<int(a.size());i++){
if(a[i]>='0'&&a[i]<='9')ans=ans*10+a[i]-'0';
else return -1;//不是阿拉伯数字
}
return ans;
}
void move(){//落子模块
while(1){
cout<<"请 "<<((turn%2)?"x":"o")<<" 方输入落子坐标:\n";
cin>>op1;
if(op1=="undo"){
undo();
continue;
}else if(op1=="show"){
show();
continue;
}else if(op1=="restart"){
init();
show();
continue;
}else if(op1=="change"){
turn++;
show();
continue;
}else if(op1=="help"){
help(1);
continue;
}
cin>>op2;
x=re_int(op1),y=re_int(op2);
if(!(x<1||x>19||y<1||y>19||a[x][y])){
hist.push(point{x,y});
break;
}else{
cls();
show();
if(a[x][y])cout<<"坐标处已有子,请重新输入落子坐标!\n";
if(x<1||x>19||y<1||y>19)cout<<"坐标格式错误,请按“x y”的形式重新输入落子坐标!(x,y ∈[1,19]且为整数)\n";
continue;
}
}
if(turn%2)a[x][y]=1;
else a[x][y]=2;
turn++;
}
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>19||yy<1||yy>19)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
for(dir=0;dir<4;dir++){
int aaa=dfs(x,y);
if(aaa>=5){
cout<<"恭喜 "<<(a[x][y]==1?"x":"o")<<" 方获胜!\n( 按回车开始下一局 )";
getchar();getchar();
init();
show();
return;
}
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
vis[i][j]=0;
}
}
}
}
int main(){
SetConsoleSize(250,60,19*5,19*3);
cin.tie(0);
cout.tie(0);
cout<<"来下五子棋!\n";
help(0);
show();
while(1){
move();
show();
check();
}
return 0;
}
1.3.1
说明:不改了,再也不改了😭😭😭
//wzq v1.3.1 by Autboity
#include<iostream>
#include<stack>
#include<string>
using namespace std;
void help(){
cout<<R"(***提示***
1.本程序默认 o 先手。
2.每方轮流按 “x y ”的形式输入落子坐标。
3.请输入阿拉伯数字!!!)";
}
int a[20][20],turn,x,y;
int vis[20][20],d[4][2]={{0,1},{1,0},{1,1},{1,-1}},dir;
void show(){//展示棋盘
cout<<"当前的棋盘。\n ";
for(int i=1;i<=19;i++)cout<<" "<<i/10<<i%10<<" ";
cout<<"\n";
for(int i=1;i<=19;i++){
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n"<<i/10<<i%10<<" ";
for(int j=1;j<=19;j++){
cout<<"|";
if(a[j][i]==0)cout<<" ";
else if(a[j][i]==1)cout<<" x ";
else if(a[j][i]==2)cout<<" o ";
}
cout<<"|\n";
}
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n(棋盘展示结束)\n";
}
struct point{
int x,y;
};
stack<point>hist;//用于悔棋
void undo(){
if(hist.empty()){
cout<<"没有可撤回的棋子!";
return;
}
int lx=hist.top().x,ly=hist.top().y;
a[lx][ly]=0;
hist.pop();
turn--;
cout<<"已撤回一步!\n";
show();
}
void init(){//初始化
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
a[i][j]=0;
}
}
while(!hist.empty())hist.pop();
turn=0;
}
string op1,op2;//用输入其它指令,同时避免user输入奇奇怪怪的东西
int re_int(string a){//把string 转 int
int ans=0,i=0;
for(;i<int(a.size());i++){
if(a[i]>='0'&&a[i]<='9')ans=ans*10+a[i]-'0';
else return -1;//不是阿拉伯数字
}
return ans;
}
void move(){//落子模块
while(1){
cout<<"请 "<<((turn%2)?"x":"o")<<" 方输入落子坐标:\n";
cin>>op1;
if(op1=="undo"){
undo();
continue;
}else if(op1=="show"){
show();
continue;
}else if(op1=="clean"){
init();
show();
continue;
}else if(op1=="change"){
turn++;
continue;
}
cin>>op2;
x=re_int(op1),y=re_int(op2);
if(x<1||x>19||y<1||y>19||a[x][y]){
cout<<"请按“x y”的形式重新输入落子坐标!(x,y ∈[1,19]且为整数)\n";
continue;
}else{
hist.push(point{x,y});
break;
}
}
if(turn%2)a[x][y]=1;
else a[x][y]=2;
turn++;
}
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>19||yy<1||yy>19)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
for(dir=0;dir<4;dir++){
int aaa=dfs(x,y);
if(aaa>=5){
cout<<"恭喜 "<<(a[x][y]==1?"x":"o")<<" 方获胜!\n( 按回车开始下一局 )";
getchar();getchar();
init();
show();
return;
}
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
vis[i][j]=0;
}
}
}
}
int main(){
cout<<"来下五子棋!\n";
help();
cout<<"(回车以开始)\n";
getchar();
show();
while(1){
move();
show();
check();
}
return 0;
}
1.3
说明:删去了不必要的报错和 ChatGPT 写的判赢的代码,并埋下了一个Bug
//wzq v1.3 by Autboity
#include<iostream>
#include<stack>
#include<string>
using namespace std;
void help(){
cout<<R"(***提示***
1.本程序默认 o 先手。
2.每方轮流按 “x y ”的形式输入落子坐标。
3.请输入阿拉伯数字!!!)";
}
int a[20][20],turn,x,y;
int vis[20][20],d[3][2]={{0,1},{1,0},{1,1}},dir;
void show(){//展示棋盘
cout<<"当前的棋盘。\n ";
for(int i=1;i<=19;i++)cout<<" "<<i/10<<i%10<<" ";
cout<<"\n";
for(int i=1;i<=19;i++){
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n"<<i/10<<i%10<<" ";
for(int j=1;j<=19;j++){
cout<<"|";
if(a[j][i]==0)cout<<" ";
else if(a[j][i]==1)cout<<" x ";
else if(a[j][i]==2)cout<<" o ";
}
cout<<"|\n";
}
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n(棋盘展示结束)\n";
}
struct point{
int x,y;
};
stack<point>hist;//用于悔棋
void undo(){
if(hist.empty()){
cout<<"没有可撤回的棋子!";
return;
}
int lx=hist.top().x,ly=hist.top().y;
a[lx][ly]=0;
hist.pop();
turn--;
cout<<"已撤回一步!\n";
show();
}
void init(){//初始化
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
a[i][j]=0;
}
}
while(!hist.empty())hist.pop();
turn=0;
}
string op1,op2;//用输入其它指令,同时避免user输入奇奇怪怪的东西
int re_int(string a){//把string 转 int
int ans=0,i=0,fix=1;
if(a[0]=='-'){//棋盘上哪有负的...
i++;
fix=-1;
}
for(;i<int(a.size());i++){
if(a[i]>='0'&&a[i]<='9')ans=ans*10+a[i]-'0';
else return INT16_MIN;//不是阿拉伯数字
}
return ans*fix;
}
void move(){//落子模块
while(1){
cout<<"请 "<<((turn%2)?"x":"o")<<" 方输入落子坐标:\n";
cin>>op1;
if(op1=="undo"){
undo();
continue;
}else if(op1=="show"){
show();
continue;
}else if(op1=="clean"){
init();
show();
continue;
}else if(op1=="change"){
turn++;
continue;
}
cin>>op2;
x=re_int(op1),y=re_int(op2);
if(re_int(op1)==INT16_MIN||min(x,y)==INT16_MIN||x<1||x>19||y<1||y>19||a[x][y]){
cout<<"请按“x y”的形式重新输入落子坐标!(x,y ∈[1,19]且为整数)\n";
continue;
}else{
hist.push(point{x,y});
break;
}
}
if(turn%2)a[x][y]=1;
else a[x][y]=2;
turn++;
}
int dfs(int xx,int yy){
if(a[xx][yy]!=a[x][y]||vis[xx][yy]||xx<1||xx>19||yy<1||yy>19)return 0;
vis[xx][yy]=1;
return 1+dfs(xx+d[dir][0],yy+d[dir][1])+dfs(xx-d[dir][0],yy-d[dir][1]);
}
void check(){
for(dir=0;dir<3;dir++){
int aaa=dfs(x,y);
if(aaa>=5){
cout<<"恭喜 "<<(a[x][y]==1?"x":"o")<<" 方获胜!\n( 按回车开始下一局 )";
getchar();getchar();
init();
show();
return;
}
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
vis[i][j]=0;
}
}
}
}
int main(){
cout<<"来下五子棋!\n";
help();
cout<<"(回车以开始)\n";
getchar();
show();
while(1){
move();
show();
check();
}
return 0;
}
1.2
说明:我不知道。。。我没有保存再上一版代码。。。不过有时回来翻翻以前写的代码的感觉也挺不错,不是吗?
//wzq v1.2 by Autboity
#include<iostream>
#include<stack>
#include<string>
using namespace std;
void help(){
cout<<R"(***提示***
1.本程序默认 o 先手。
2.每方轮流按 “x y ”的形式输入落子坐标。
3.请输入阿拉伯数字!!!)";
}
int a[20][20],turn,x,y,error_number_time;
/*
错误列表:
1:输入非数字
2:数字超限
3:已有棋子
*/
void error(int e){//报错模块
switch (e) {
case 1:
cout<<"请输入*阿拉伯数字*";
for(int i=0;i<=error_number_time;i++)cout<<"!";
break;
case 2:
cout<<"请输入* 1~19 *之内的数字!";
break;
case 3:
cout<<"此处已有棋子,";
break;
default:
cout<<"请按“ x y ”的形式重新输入落子坐标!\n";
break;
}
}
void show(){//展示棋盘
cout<<"当前的棋盘。\n ";
for(int i=1;i<=19;i++)cout<<" "<<i/10<<i%10<<" ";
cout<<"\n";
for(int i=1;i<=19;i++){
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n"<<i/10<<i%10<<" ";
for(int j=1;j<=19;j++){
cout<<"|";
if(a[j][i]==0)cout<<" ";
else if(a[j][i]==1)cout<<" x ";
else if(a[j][i]==2)cout<<" o ";
}
cout<<"|\n";
}
cout<<" ";
for(int j=1;j<=19;j++)cout<<"----";
cout<<"\n(棋盘展示结束)\n";
}
struct point{
int x,y;
};
stack<point>hist;//用于悔棋
void undo(){
if(hist.empty()){
cout<<"没有可撤回的棋子!";
return;
}
int lx=hist.top().x,ly=hist.top().y;
a[lx][ly]=0;
hist.pop();
turn--;
cout<<"已撤回一步!\n";
show();
}
void init(){//初始化
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
a[i][j]=0;
}
}
while(!hist.empty())hist.pop();
turn=0;
}
string op1,op2;//用输入其它指令,同时避免user输入奇奇怪怪的东西
int re_int(string a){//把string 转 int
int ans=0,i=0,fix=1;
if(a[0]=='-'){//棋盘上哪有负的...
i++;
fix=-1;
}
for(;i<a.size();i++){
if(a[i]>='0'&&a[i]<='9')ans=ans*10+a[i]-'0';
else return INT16_MIN;//不是阿拉伯数字
}
return ans*fix;
}
void move(){//落子模块
while(1){
cout<<"请 "<<((turn%2)?"x":"o")<<" 方输入落子坐标:\n";
cin>>op1;
if(op1=="undo"){
undo();
continue;
}else if(op1=="show"){
show();
continue;
}else if(op1=="clean"){
init();
show();
continue;
}else if(op1=="change"){
turn++;
continue;
}
else if(re_int(op1)==INT16_MIN){
error(1);
continue;
}
cin>>op2;
x=re_int(op1),y=re_int(op2);
if(min(x,y)==INT16_MIN)error(1);
else if(x<1||x>19||y<1||y>19)error(2);
else if(a[x][y])error(3);
else{
hist.push(point{x,y});
break;
}
}
if(turn%2)a[x][y]=1;
else a[x][y]=2;
turn++;
}
int vis[20][20],d[9][2]={{0,0},{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}},dir,prewin,win;
void dfs(int x,int y,int step){
if(step==5){
win=a[x][y];
return;
}
int nx=x+d[dir][0],ny=y+d[dir][1];
if(nx<1||nx>19||ny<1||ny>19||a[nx][ny]!=a[x][y])return;
dfs(nx,ny,step+1);
}
void check(){
for(int i=1;i<=19;i++){
for(int j=1;j<=19;j++){
for(dir=1;dir<=8;dir++){
if(a[x][y]){
win=0;
dfs(i,j,1);
if(win){
cout<<"恭喜 "<<(win==1?"x":"o")<<" 方获胜!\n(按回车开始下一局)";
getchar();
init();
show();
return;
}
}
}
}
}
}
int main(){
cout<<"来下五子棋!\n";
help();
cout<<"(回车以开始)\n";
getchar();
show();
while(1){
move();
show();
check();
}
return 0;
}
/*
样例一(o赢):
1 1
1 2
2 2
1 3
3 3
1 4
4 4
1 5
5 5
样例二(x赢)
1 1
1 2
2 2
1 3
3 3
1 4
4 4
1 5
6 5
1 6
*/
后记
最早的版本是在学校机房上信息课时写的,功能很简单,只是玩一局五子棋,没有撤回,没有判赢,没有重开,也没有Bug,也没有同桌( @bailianyi ,膜拜数学大佬)陪我玩,因为没有Bug的版本是在最后一节信息课写好的。
真是一段不错的回忆,不是吗