五子棋棋盘实现

30 篇文章 0 订阅
30 篇文章 1 订阅

最近写了一个对抗搜索 + α \alpha α- β \beta β剪枝的下五子棋的“人工智障”,贴个代码,头文件在下面:

Gobang.cpp at 2019.6.8

效果大概就是这样,此图是AI对战AI时的效果图。
在这里插入图片描述

#include "winshow.cpp"
#include "cursorope.cpp"
//#include <algorithm>
//using namespace std;

/// 五子棋游戏 Day74 论对抗搜索 

#define INF (0x3f3f3f3f)

#define BlackPiece (1)
#define WhitePiece (2)
#define SpaceLand  (0)
#define Outside   (-1)

#include <ctime>

namespace AIsolve { /// 用人工智障分析局势 

    int scorebd[11][11] = { /// 描述暴搜的搜索顺序 
        {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 0
        {-1, 65, 64, 63, 62, 61, 60, 59, 58, 57, -1}, /// line 1
        {-1, 66, 37, 36, 35, 34, 33, 32, 31, 56, -1}, /// line 2 
        {-1, 67, 38, 17, 16, 15, 14, 13, 30, 55, -1}, /// line 3
        {-1, 68, 39, 18,  5,  4,  3, 12, 29, 54, -1}, /// line 4
        {-1, 69, 40, 19,  6,  1,  2, 11, 28, 53, -1}, /// line 5
        {-1, 70, 41, 20,  7,  8,  9, 10, 27, 52, -1}, /// line 6
        {-1, 71, 42, 21, 22, 23, 24, 25, 26, 51, -1}, /// line 7
        {-1, 72, 43, 44, 45, 46, 47, 48, 49, 50, -1}, /// line 8
        {-1, 73, 74, 75, 76, 77, 78, 79, 80, 81, -1}, /// line 9
        {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 10
    };
    
    int PoX[82], PoY[82];
    void InitPoXY() { /// 初始化搜索顺序 
        for(int i = 1; i <= 9; i ++) {
            for(int j = 1; j <= 9; j ++) {
                PoX[scorebd[i][j]] = i;
                PoY[scorebd[i][j]] = j;
            }
        }
    }

    int tmpline[20], tcnt; /// 用于直线扫描分析的临时储存 
    void clear() { /// 清空临时储存 
        tcnt = 0;
    }
    void push_back(int item) { /// 向临时储存中添加元素 
        tmpline[++ tcnt] = item;
    } /// tmpline 的可用下标从 1开始 
    
    void GetLine(int chsbd[11][11], int x, int y, int dirx, int diry) {
        /// 从 -1 开始到 -1 结束, 得到一条直线上的所有棋子 
        clear(); /// 清空临时储存 
        int boardcnt = 0; /// 统计 -1出现的次数 
        while(1) {
            if(chsbd[x][y] == -1) boardcnt ++; /// 统计-1出现的次数 
            push_back(chsbd[x][y]);
            if(boardcnt == 2) break; /// 获取完成 
            x += dirx; /// 挪到下一个位置 
            y += diry;
        }
    }
    
    bool CheckSame(int A, int B, int player) { /// 判断两个元素是否同属一段 
        if(B==player || A==player) {
            return A == B;
        }else {
            if(A==SpaceLand || B==SpaceLand) {
                return A == B;
            }else {
                return true;
            }
        }
    }
    
    /// 连续性分析 
    int mode[20], length[20], mcnt; /// 对储存的直线信息进行连续性分析 
    void ContinuousAnalysis(int player) { /// 连续性分析 
        mcnt = 1; mode[1] = -1; length[1] = 1;
        for(int i = 2; i <= tcnt; i ++) {
            if(CheckSame(tmpline[i-1], tmpline[i], player)) { /// 延伸上一段 
                length[mcnt]++;
            }else { /// 开辟新一段 
                mode[++ mcnt] = tmpline[i];
                length[mcnt] = 1;
            }
        }
    }
    
    /// 统计局面(注意对于两个玩家要重新分析) 
    int CntAry[6][2]; /// 用于统计临时数据 0活 1冲  
    void Count(int player) {
        int baseNow = 1, LenSum = 0;
        for(int i = 2; i <= mcnt; i ++) {
            if(mode[i]==3-player || mode[i]==-1) { /// 寻找下一个界标 
                if(LenSum >= 5) {
                    for(int j = baseNow+1; j<=i-1; j ++) { /// 枚举两个界标之间的每一段内容 
                        if(mode[j]==player && length[j]>1) {
                            int border = j==baseNow+1 || j==i-1; /// 是否为冲 
                            CntAry[length[j]][border] ++;
                        }
                    }
                }
                /// 如果总长度小于5 直接忽略这一段 
                baseNow = i; LenSum = 0; /// 重新统计长度 
            }else {
                LenSum += length[i]; /// 累加除界标外部分的长度 
            }
        }
    }
    
    int GetScore(int chsbd[11][11], int player) { /// 得到某个玩家的估价 
        memset(CntAry, 0x00, sizeof(CntAry)); /// 清除原数据 
        /// 统计每一行 
        for(int i = 1; i <= 9; i ++) {
            GetLine(chsbd, i, 0, 0, 1);
            ContinuousAnalysis(player);
            Count(player);
        }
        /// 统计每一列 
        for(int i = 1; i <= 9; i ++) {
            GetLine(chsbd, 0, i, 1, 0);
            ContinuousAnalysis(player);
            Count(player);
        }
        /// 左上右下斜线 
        GetLine(chsbd, 0, 0, 1, 1); /// 对角中轴线 
        ContinuousAnalysis(player);
        Count(player);
        for(int i = 1; i <= 4; i ++) {
            GetLine(chsbd, 0, i, 1, 1); /// 对角中轴线上方 
            ContinuousAnalysis(player);
            Count(player);
            GetLine(chsbd, i, 0, 1, 1); /// 对角中轴线下方 
            ContinuousAnalysis(player);
            Count(player);
        }
        /// 右上左下斜线 
        GetLine(chsbd, 0, 10, 1, -1); /// 对角中轴线 
        ContinuousAnalysis(player);
        Count(player);
        for(int i = 1; i <= 4; i ++) {
            GetLine(chsbd, 0, 10-i, 1, -1); /// 上方 
            ContinuousAnalysis(player);
            Count(player);
            GetLine(chsbd, i, 10, 1, -1); /// 下方 
            ContinuousAnalysis(player);
            Count(player);
        }
        /// 统计完成 
        if(CntAry[5][0] || CntAry[5][1]) return 10000;
        else if(CntAry[4][0] || CntAry[4][1]>=2 || (CntAry[4][1] && CntAry[3][0])) return 9900;
        else if(CntAry[3][0]>=2 || CntAry[4][1]>=2) return 9800;
        else if(CntAry[3][0] && CntAry[4][1]) return 9700;
        else {
            return (CntAry[3][0]+CntAry[4][1])*2000+CntAry[3][1]*300 + CntAry[2][0]*200 + CntAry[2][1]*50;
        }
    }
    
    int FinalScore(int chsbd[11][11]) { /// 最终得分 
        int s1 = GetScore(chsbd, 1), s2 = GetScore(chsbd, 2);
        if(s2 >= 9000) return -s2;
        else if(s1 >= 9000) return s1;
        else return s1 - s2;
    }
    
    int StartTime;
    int Search(int chsbd[11][11], int player, int& bestpos, int depth, int alpha = -INF, int beta = INF) {
        int score = FinalScore(chsbd);
        if(depth >= 4 || clock() - StartTime > 10000) { /// 停止搜索 
            if(player == 1) return alpha>score ? alpha : score;
            else            return  beta<score ?  beta : score;
        }
        bool first = 1;
        for(int i = 1; i <= 81; i ++) {
            int Xn = PoX[i], Yn = PoY[i];
            if(beta <= alpha) break;
            if(chsbd[Xn][Yn] == 0) { /// 可以放置 
                if(first) {
                    bestpos = i;
                    first = 0;
                }
                chsbd[Xn][Yn] = player;
                int tmp = 0;
                score = Search(chsbd, 3-player, tmp, depth+1, alpha, beta);
                if(player == 1) {
                    //alpha = max(alpha, score);
                    if(score > alpha) {
                        alpha = score;
                        bestpos = i;
                    }
                    if(score >= 9000) {
                        chsbd[Xn][Yn] = 0;
                        break;
                    }
                }else {
                    //beta  = min(beta, score);
                    if(score < beta) {
                        beta = score;
                        bestpos = i;
                    }
                    if(score <= -9000) {
                        chsbd[Xn][Yn] = 0;
                        break;
                    }
                }
                chsbd[Xn][Yn] = 0;
            }
        }
        if(player == 1) return alpha;
        else            return beta;
    }
    
    void FindWay(int chsbd[11][11], int player, int& X, int& Y) {
        /// 分析棋盘局势并且落子于 X行 Y列 
        StartTime = clock();
        int tmp = 0;
        Search(chsbd, player, tmp, 0);
        X = PoX[tmp];
        Y = PoY[tmp];
    }
}

namespace Gobang { /// 五子棋游戏 
    int chessboard[11][11]; /// 描述棋盘状态 
    
    wshow::dot GetPiece(int item) { /// 找到棋子对应的形状 
        switch(item) {
            case BlackPiece: return wshow::dot("●", makecol(clr::YELLOW, clr::BLACK));
            case WhitePiece: return wshow::dot("●", makecol(clr::YELLOW, clr::WHITE|clr::LIGHT));
            case  SpaceLand: return wshow::dot("┼", makecol(clr::YELLOW, clr::WHITE));
            case          3: return wshow::dot("●", makecol(clr::YELLOW, clr::RED|clr::LIGHT));
                    default: return wshow::dot("□", makecol(clr::BLACK, clr::YELLOW));
        }
    }
    
    void Disp(int y, int x) { /// 显示棋盘到屏幕 
        for(int i = 0; i <= 10; i ++) {
            for(int j = 0; j <= 10; j ++) {
                wshow::output(GetPiece(chessboard[i][j]), y+i, x+j);
            }
        }
    }
    
    void Init() { /// 初始化棋盘 
        memset(chessboard, 0x00, sizeof(chessboard));
        for(int i = 0; i <= 10; i ++) {
            chessboard[i][0] = chessboard[i][10] = -1;
            chessboard[0][i] = chessboard[10][i] = -1;
        }
    }
    
    void RndTest() { /// 生成随机局面(用于输出测试) 
        for(int i = 1; i <= 9; i ++) {
            for(int j = 1; j <= 9; j ++) {
                chessboard[i][j] = RND(0, 2);
            }
        }
    }
    
    int tmpx, tmpy, tmpdx, tmpdy;
    bool CheckWinInDir(int x, int y, int dirx, int diry) { /// 检测某一点开始某一方向上是否有五连珠 
        tmpx = x; tmpy =y; tmpdx = dirx; tmpdy = diry;
        int tmp = chessboard[x][y];
        if(tmp <= 0) return false; /// 空地不可能成为五连珠的起始 
        for(int i = 1; i <= 5; i ++) {
            if(chessboard[x][y] != tmp) return false; /// 发现不同, 不可能出现五连珠 
            x += dirx;
            y += diry;
        }
        return true; /// 未发现不同 说明存在五连珠 
    }
    
    void MakeSuc() { /// 将五连珠染红 
        for(int i = 1; i <= 5; i ++) {
            chessboard[tmpx][tmpy] = 3;
            tmpx += tmpdx;
            tmpy += tmpdy;
        }
    }
    
    bool CheckWinOnPos(int x, int y) { /// 检测某一点开始是否有五连珠 
        /// 我们只需要检查 左下, 右下, 右, 下 这四个方向 
        return CheckWinInDir(x, y, 0,  1) || /// 右 
               CheckWinInDir(x, y, 1,  0) || /// 下 
               CheckWinInDir(x, y, 1, -1) || /// 左下 
               CheckWinInDir(x, y, 1,  1);   /// 右下 
    }
    
    int CheckWin() { /// 检测是否有人胜利 
        for(int i = 1; i <= 9; i ++) {
            for(int j = 1; j <= 9; j ++) {
                if(CheckWinOnPos(i, j)) {
                    MakeSuc();
                    Disp(0, 0);
                    return chessboard[i][j]; /// 返回胜利玩家 
                }
            }
        }
        return 0; /// 返回游戏未结束 
    }
    
    POINT GetClickOnChessboard(int y, int x) { /// 等待棋盘上的单击左键 
        POINT p;
        while(1) {
            p = cursorope::WaitCursorLeftClick();
            p.x /= 2; /// 注意横向放缩的问题 
            #define Rin(A, B, C) (((A)<=(B))&&((B)<=(C))) /// 判断 A<=B<=C
            if(Rin(x+1, p.x, x+9) && Rin(y+1, p.y, y+9)) {
                p.x -= x; p.y -= y; /// 获得相对位置 
                if(chessboard[p.y][p.x] == 0) break;
            }
        }
        return p;
    }
    
    int MsgCnt = 0;
    void OutputMsg(const char* str, int pos = 47, int col = clr::DEFAULT) { /// 用于输出游戏信息 
        wnd::gotoxy(MsgCnt%20+1, pos);
        for(int i = pos; i <= 78; i ++) putchar(' ');
        wnd::gotoxy(MsgCnt%20, pos);
        for(int i = pos; i <= 78; i ++) putchar(' ');
        wnd::gotoxy(MsgCnt%20, pos);
        wnd::color(col);
        printf("%3d: %s", MsgCnt+1, str);
        wnd::color(clr::DEFAULT);
        MsgCnt ++;
    }
    
    const int WARN_COLOR = makecol(clr::BLACK,    clr::RED|clr::LIGHT);
    const int SUCC_COLOR = makecol(clr::BLACK,  clr::GREEN|clr::LIGHT);
    const int TIPS_COLOR = makecol(clr::BLACK, clr::YELLOW|clr::LIGHT); /// 定义几种颜色 
    const int BLUE_COLOR = makecol(clr::BLACK, clr::PURPLE|clr::LIGHT);

    void Game(int y, int x, bool ifperson1 = false, bool ifperson2 = true) {
        Init();
        chessboard[5][5] = BlackPiece;
        Disp(y, x); /// 显示空棋盘(中央一黑子) 
        POINT p;
        int i;
        for(i = 1; i <= 40; i ++) {
            /// 白棋落子 
            OutputMsg("等待白棋落子 ...", 47, TIPS_COLOR);
            if(ifperson2) { /// 人执白子 
                p = GetClickOnChessboard(y, x);
                chessboard[p.y][p.x] = WhitePiece;
            }else { /// 电脑执白子 
                int X = 0, Y = 0;
                AIsolve::FindWay(chessboard, WhitePiece, X, Y);
                chessboard[X][Y] = WhitePiece;
            }
            Disp(y, x);
            if(CheckWin()) {
                OutputMsg("白棋胜利!", 47, SUCC_COLOR);
                break;
            }
            /// 黑棋落子 
            OutputMsg("等待黑棋落子 ...", 47, BLUE_COLOR);
            if(ifperson1) {
                p = GetClickOnChessboard(y, x); /// 人执黑棋 
                chessboard[p.y][p.x] = BlackPiece;
            }else { /// 电脑执黑棋 
                int X = 0, Y = 0;
                AIsolve::FindWay(chessboard, BlackPiece, X, Y);
                chessboard[X][Y] = BlackPiece;
            }
            Disp(y, x);
            if(CheckWin()) {
                OutputMsg("黑棋胜利!", 47, SUCC_COLOR);
                break;
            }
        }
        if(i == 41) {
            OutputMsg("平局!", 47, SUCC_COLOR);
        }
    }
}

#include <ctime>

int testbd[11][11] = { /// 测试专用棋盘 
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 0
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 1
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 2 
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 3
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 4
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 5
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 6
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 7
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 8
    {-1,  0,  0,  0,  0,  0,  0,  0,  0,  0, -1}, /// line 9
    {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /// line 10
};

int main() {
    srand(time(NULL));
    AIsolve::InitPoXY();
    //Gobang::Init(); Gobang::RndTest(); Gobang::Disp(0, 0); /// 输出测试 
    Gobang::Game(0, 0, false, false);
    while(1);
    //int tmp = AIsolve::GetScore(testbd, BlackPiece);
    //printf("%d\n", tmp);
    return 0;
}

Chessboard.cpp at 2019.6.15

15*15的人人对战平台(后期可以把人工智障嵌入,由于我现在的人工智障太慢了,以后再嵌入)

效果大概这样:

在这里插入图片描述

/// 用于五子棋对战平台 
/// 2019.6.14

#ifndef __CHESSBOARD_CPP__
#define __CHESSBOARD_CPP__

#include "cursorope.cpp" /// 鼠标控制头文件 
#include "winshow.cpp"   /// 控制台显示头文件 

#include <cstring>
namespace ChessBoard { /// 对战平台 
    
    const int _MaxLength = 15; /// 棋盘的大小为 _MaxLength * _MaxLength 
    const int _BlackPiece = 1, _WhitePiece = 2, _RedPiece = 3, _Empty = 0, _Outside = -1; /// 棋子类型 
    
    int statue[_MaxLength + 2][_MaxLength + 2]; /// 描述棋盘上的信息 
    
    void InitStatue() { /// 初始化棋盘 
        memset(statue, 0x00, sizeof(statue)); /// 清空棋盘 
        
        for(int i = 0; i <= _MaxLength + 1; i ++) { /// 设置边界 
            statue[i][0] = statue[0][i] = _Outside;
            statue[i][_MaxLength + 1] = statue[_MaxLength + 1][i] = _Outside;
        }
        
        /// 在中间位置放一粒黑子 
        int middle = (_MaxLength + 1)/2;
        statue[middle][middle] = _BlackPiece;
    }
    
    wshow::dot GetPiece(int item) { /// 找到棋子对应的字符 
        switch(item) {
            case _BlackPiece: return wshow::dot("●", makecol(clr::YELLOW, clr::BLACK));
            case _WhitePiece: return wshow::dot("●", makecol(clr::YELLOW, clr::WHITE|clr::LIGHT));
            case      _Empty: return wshow::dot("┼", makecol(clr::YELLOW, clr::WHITE));
            case   _RedPiece: return wshow::dot("●", makecol(clr::YELLOW, clr::RED|clr::LIGHT));
                     default: return wshow::dot("□", makecol(clr::BLACK, clr::YELLOW));
        }
    }
    
    void Display() { /// 显示棋盘至控制台 
        for(int i = 0; i <= _MaxLength + 1; i ++) {
            for(int j = 0; j <= _MaxLength + 1; j ++) {
                wshow::output(GetPiece(statue[i][j]), i, j);
            }
        }
    }
    
    POINT GetClick(int y = 0, int x = 0) { /// 等待棋盘上的单击左键 
        POINT p;
        while(1) {
            p = cursorope::WaitCursorLeftClick();
            p.x /= 2; /// 注意横向放缩的问题 
            #define Rin(A, B, C) (((A)<=(B))&&((B)<=(C))) /// 判断 A<=B<=C
            if(Rin(x+1, p.x, x+_MaxLength) && Rin(y+1, p.y, y+_MaxLength)) {
                p.x -= x; p.y -= y; /// 获得相对位置 
                if(statue[p.y][p.x] == 0) break;
            }
        }
        return p;
    }
    
    
    int tmpx, tmpy, tmpdx, tmpdy;
    bool CheckWinInDir(int x, int y, int dirx, int diry) { /// 检测某一点开始某一方向上是否有五连珠 
        tmpx = x; tmpy =y; tmpdx = dirx; tmpdy = diry;
        int tmp = statue[x][y];
        if(tmp <= 0) return false; /// 空地不可能成为五连珠的起始 
        for(int i = 1; i <= 5; i ++) {
            if(statue[x][y] != tmp) return false; /// 发现不同, 不可能出现五连珠 
            x += dirx;
            y += diry;
        }
        return true; /// 未发现不同 说明存在五连珠 
    }
    
    void MakeSuc() { /// 将五连珠染红 
        for(int i = 1; i <= 5; i ++) {
            statue[tmpx][tmpy] = 3;
            tmpx += tmpdx;
            tmpy += tmpdy;
        }
    }
    
    bool CheckWinOnPos(int x, int y) { /// 检测某一点开始是否有五连珠 
        /// 我们只需要检查 左下, 右下, 右, 下 这四个方向 
        return CheckWinInDir(x, y, 0,  1) || /// 右 
               CheckWinInDir(x, y, 1,  0) || /// 下 
               CheckWinInDir(x, y, 1, -1) || /// 左下 
               CheckWinInDir(x, y, 1,  1);   /// 右下 
    }
    
    int CheckWin() { /// 检测是否有人胜利 
        for(int i = 1; i <= _MaxLength; i ++) {
            for(int j = 1; j <= _MaxLength; j ++) {
                if(CheckWinOnPos(i, j)) {
                    int tmp = statue[i][j];
                    MakeSuc();
                    Display();
                    return tmp; /// 返回胜利玩家 
                }
            }
        }
        return 0; /// 返回游戏未结束 
    }
    
    
    int MsgCnt = 0;
    void OutputMsg(const char* str, int pos = 47, int col = clr::DEFAULT) { /// 用于输出游戏信息 
        wnd::gotoxy(MsgCnt%20+1, pos);
        for(int i = pos; i <= 78; i ++) putchar(' ');
        wnd::gotoxy(MsgCnt%20, pos);
        for(int i = pos; i <= 78; i ++) putchar(' ');
        wnd::gotoxy(MsgCnt%20, pos);
        wnd::color(col);
        printf("%3d: %s", MsgCnt+1, str);
        wnd::color(clr::DEFAULT);
        MsgCnt ++;
    }
    
    const int WARN_COLOR = makecol(clr::BLACK,    clr::RED|clr::LIGHT);
    const int SUCC_COLOR = makecol(clr::BLACK,  clr::GREEN|clr::LIGHT);
    const int TIPS_COLOR = makecol(clr::BLACK, clr::YELLOW|clr::LIGHT); /// 定义几种颜色 
    const int BLUE_COLOR = makecol(clr::BLACK, clr::PURPLE|clr::LIGHT);
    
    int Game() { /// 进行一轮游戏 
        InitStatue(); /// 初始化棋盘 
        Display();
        for(int i = 1; i <= _MaxLength*_MaxLength-1; i ++) {
            int piece = (i%2==0) ? _BlackPiece : _WhitePiece;
            
            OutputMsg((piece==_BlackPiece) ? "等待黑棋落子 ..." : "等待白棋落子 ...", 47, 
                (piece==_BlackPiece) ? TIPS_COLOR : BLUE_COLOR);
            POINT p = GetClick();
            statue[p.y][p.x] = piece; /// 从鼠标获取落子 
            
            Display();
            int tmp;
            if(tmp=CheckWin()) { /// 判断是否有人胜利 
                OutputMsg((tmp==_BlackPiece) ? "黑棋胜利!" : "白棋胜利!", 47, WARN_COLOR);
                return tmp;
            }
        }
        OutputMsg("平局!", 47, SUCC_COLOR);
        return _Empty;
    }
}

#include <ctime>
int main() {
    srand(time(NULL)); /// 重置随机种子 
    ChessBoard::Game();
    while(1);
    return 0;
}

#else
    /// 文件已经被引用过 
#endif

cursorope.cpp 鼠标操作头文件

/// 2019.6.8 鼠标操作头文件 

#ifndef __CURSOROPE_CPP__
#define __CURSOROPE_CPP__

#include <windows.h>
#include <cstdio>

namespace hwndforcursor { /// hfc
    HWND hwnd=GetForegroundWindow(); /// 当前窗口句柄 
    bool checkupon() {               /// 判断当前窗口是否在最顶层 
    	HWND hwndn=GetForegroundWindow(); return hwndn == hwnd;
    }
    #define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0)
}

#define hfc hwndforcursor
namespace cursorope { /// 支持鼠标操作 
    POINT GetCursorPos() { /// 获取鼠标坐标 
        POINT p;
        GetCursorPos(&p); /// windows API 获取鼠标坐标 
        return p; /// 横x 纵y 左上角是(0, 0) 
    }
    
    void OutputPoint(POINT p) { /// 输出点坐标 
        printf("(%5d, %5d)", p.x, p.y);
    }
    
    RECT GetConsolePos() { /// 获取当前窗口的像素坐标 
        RECT rect;
        GetWindowRect(hfc::hwnd,&rect); /// windows API 获取控制台窗口的坐标 
        return rect;
    }
    
    POINT GetCursorPosInConsole() { /// 获得鼠标相对于控制台的相对位置 
        POINT p   =  GetCursorPos();
        RECT rect = GetConsolePos();
        p.x -= rect.left;
        p.y -= rect.top;
        return p;
    }
    
    POINT GetCursorCharPosInConsole() { /// 找到鼠标在控制台上对应字符位置的坐标 
        POINT p = GetCursorPosInConsole();
        p.x -= 5;  p.x /= 8;
        p.y -= 31; p.y /= 16;
        return p;
    }
    #define Rin(A, B, C) (((A)<=(B))&&((B)<=(C))) /// 判断 A<=B<=C
    
    POINT WaitCursorLeftClick() { /// 等待鼠标点击控制台然后返回点击的字符位置 
        POINT p;
        while(1) {
            if(KEY_DOWN(VK_LBUTTON) && hfc::checkupon()) {
                p = GetCursorCharPosInConsole();
                if(Rin(0, p.x, 79) && Rin(0, p.y, 24)) {
                    break;
                }
            }
            Sleep(20);
        }
        Sleep(100);
        return p;
    }
}

/*
#include "winshow.cpp"
int main() {
    while(1) {
        POINT p = cursorope::WaitCursorLeftClick();
        wshow::output(wshow::dot("●"), p.y, p.x/2);
    }
    return 0;
}*/
#undef hfc
#undef Rin

#else
    /// 头文件已经被定义过了 
#endif

winshow.cpp 控制台输出头文件

#ifndef __WINSHOW_CPP__
#define __WINSHOW_CPP__

/// 2019.6.6 控制台输出 
/// 粘的 Tetris 原来的板子 

#include <windows.h>
#include <cstdlib> 
#include <cstdio>


namespace hwndset {
    HWND hwnd=GetForegroundWindow(); //当前窗口句柄 
    bool checkupon() { /// 判断当前窗口是否在最顶层 
    	HWND hwndn=GetForegroundWindow(); return hwndn == hwnd;
    }

    #define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME) & 0x8000) ? 1:0) /// 检测字符是否按下
    
    ///222: #define VK_LEFT  0x25
    ///223: #define VK_UP    0x26
    ///224: #define VK_RIGHT 0x27
    ///225: #define VK_DOWN  0x28    -- from <windows.h>
    
    #define SHAKE (5)
    void shake(){ /// 屏幕晃动, 懒得写了, 粘的板子 
        RECT rect; 
        /// HWND hwnd=GetForegroundWindow();
        GetWindowRect(hwnd,&rect); 
        MoveWindow(hwnd,rect.left+SHAKE,rect.top,rect.right-rect.left,rect.bottom-rect.top,TRUE); 
        Sleep(28); 
        MoveWindow(hwnd,rect.left+SHAKE,rect.top-SHAKE,rect.right-rect.left,rect.bottom-rect.top,TRUE); 
        Sleep(28); 
        MoveWindow(hwnd,rect.left,rect.top-SHAKE,rect.right-rect.left,rect.bottom-rect.top,TRUE); 
        Sleep(28); 
        MoveWindow(hwnd,rect.left,rect.top,rect.right-rect.left,rect.bottom-rect.top,TRUE); 
    }
    #undef SHAKE

}

int RND(int L, int R) {
	#define RND ((rand()<<15)+rand())
	return RND%(R - L + 1) + L;
	#undef RND
}

namespace clr { /// 关于颜色的一些常量 
	const int BLACK=0, BLUE=1, GREEN=2, CYAN=3, RED=4, PURPLE=5, YELLOW=6, WHITE=7, LIGHT=8;
	const int DEFAULT = (clr::BLACK<<4)|clr::WHITE;
	int makecol(int background, int letter) { /// 装载一种颜色 
		return (background << 4) | letter;
	}
}
using clr::makecol; /// 开放一个对外函数 makecol 

namespace wnd { /// 关于 Windows API 的使用 
	void gotoxy(int y, int x) { /// 更改屏幕输出位置 
		COORD pos; pos.X = x; pos.Y = y;
		SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
	}
	void color(int a) { /// 更改输出指针颜色 
		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),a);
	}
}

namespace wshow {
    struct dot { /// 屏幕上的基本单位 
    	char ch[2], col; /// 两个字节内容,以及其颜色
    	dot() {
    		col = makecol(clr::BLACK, clr::WHITE); // 默认"黑底白字"
    		ch[0] = ch[1] = 0; /// 默认没有任何字符 
    	}
    	dot(const char* s, int c = clr::DEFAULT) { /// 通过字符串构造 dot 
    		ch[0] = s[0], ch[1] = s[1]; col = c; /// 初始化一个字符
    		if(ch[0]<' ' && ch[0]>=-2) ch[0] = 0;
    		if(ch[1]<' ' && ch[1]>=-2) ch[1] = 0; /// 避免 tab '\n' 之类的不必要的控制字符的出现
    	}
    } Screen[24][39]; /// 用这个数组去描述屏幕中"受控制(被记录)"的位置
    //int store[24][39]; /// 这是俄罗斯方块数组的"固化版" 
    
    bool operator == (dot A, dot B) { /// 判断两个 dot 是否相同 
    	return A.ch[0]==B.ch[0] && A.ch[1]==B.ch[1] && A.col==B.col; /// 考虑颜色 
    }
    
    //dot transcol(dot A,int ncol) {
    //	return dot(A.ch, ncol); /// 返回一个内容相同颜色不同的 dot 
    //}
    void print(dot A) { /// 输出一个 dot 
    	wnd::color(A.col); /// 先把输入指针变到对应的颜色
    	printf("%c%c", A.ch[0], A.ch[1]); /// 输出这个字符
    	wnd::color(clr::DEFAULT); /// 及时恢复到默认颜色 
    }
    
    void output(dot A, int y, int x) { /// 唯一的一个新写的函数 
        if(!(Screen[y][x] == A)) { /// 简单粗暴地指定一个位置, 并输出字符 
            wnd::gotoxy(y, x*2);
            print(A);
        }
    }
}

/*
int main() {
    
    return 0;
}
*/
#else
    /// 说明头文件已经被引用过了 
#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值