五子棋棋盤實現

最近寫了一個對抗搜索 + α\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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章