我写的五子棋的所有版本

· · 个人记录

前言

图形化版需要 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"(wzq v1.8
by Autboity
+------------提示-------------+
|1.本程序默认 o 先手。        |
|2.按空格或回车落子。         |
|3.别的还没写好。             |
+-----------------------------+

                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              
                              

)";
    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的版本是在最后一节信息课写好的。

真是一段不错的回忆,不是吗