Ultimate Tik Tak Toe

· · 个人记录

Ultimate Tik Tak Toe游戏。

支持局域网联机和单人与MINMAX对站(很难打)

坐标的表示非常简单:

   |   |
 1 | 2 | 3
   |   |
---+---+---
   |   |
 4 | 5 |  6
   |   |
---+---+---
   |   |
 7 | 8 |  9
   |   |

表示为 (大格子编号,小格子编号)

不会规则的网上搜,你不一定是先手,你是蓝方。

注意,对于MINMAX,程序卡了5秒以内是正常现象,因为MINMAX的搜索层数达到了7层!如果想要减轻难度/提高运行速度,修改第二行的 DIFFICULTY! :::::info[源码] :::info[大号UI版本]

#include<bits/stdc++.h>
#define DIFFICULTY 7
#include<windows.h>
using namespace std;
const char EMPTY='.';
const char PLAYER_X='X';
const char PLAYER_O='O';
const char TIE='T';
mt19937 rng(time(0));
void color(int r,int g,int b)
    {wprintf(L"\x1b[38;2;%d;%d;%dm",r,g,b);}
void HideCursor(){
    CONSOLE_CURSOR_INFO cursor_info={1,0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
using namespace std;
void gotoxy(int x, int y){
    HANDLE hout;
    COORD pos;
    pos.X=x;
    pos.Y=y;
    hout=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hout, pos);
}
class SmallBoard{
public:
    char board[3][3];
    char winner;
    SmallBoard(){
        for(int i=0;i<3;i++)for(int j=0;j<3;j++)
        board[i][j]=EMPTY;winner=EMPTY;
    }
    bool makeMove(int row,int col,char player){
        if(row<0||row>=3||col<0||col>=3||board[row][col]
        !=EMPTY||winner!=EMPTY)return false;
        board[row][col]=player;checkWinner();
        return true;
    }
    void checkWinner(){
        for(int i=0;i<3;i++)
            if(board[i][0]!=EMPTY&&board[i][0]==
            board[i][1]&&board[i][0]==board[i][2]){
                winner=board[i][0];return;}
        for(int j=0;j<3;j++)
            if(board[0][j]!=EMPTY&&board[0][j]==
            board[1][j]&&board[0][j]==board[2][j]){
                winner=board[0][j];return;}
        if(board[0][0]!=EMPTY&&board[0][0]==
        board[1][1]&&board[0][0]==board[2][2]){
            winner=board[0][0];return;}
        if(board[0][2]!=EMPTY&&board[0][2]==
        board[1][1]&&board[0][2]==board[2][0]){
            winner=board[0][2];return;}
        bool isFull=true;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++)
                if(board[i][j]==EMPTY){
                    isFull=false;break;}
            if (!isFull)break;
        }
        if(isFull)winner=TIE;
    }
};
class UltimateBoard{
public:
    SmallBoard boards[3][3];
    char currentPlayer;
    int nextBoardRow;
    int nextBoardCol;
    bool gameOver;
    char ultimateWinner;
    UltimateBoard():currentPlayer(PLAYER_X),nextBoardRow(-1)
    ,nextBoardCol(-1),gameOver(false),ultimateWinner(EMPTY){}
    bool makeMove(int bigRow,int bigCol,int smallRow,int smallCol){
        if(gameOver)return false;
        if(nextBoardRow!=-1&&nextBoardCol!=-1&&(bigRow!=
        nextBoardRow||bigCol!=nextBoardCol))return false;
        if(boards[bigRow][bigCol].winner!=EMPTY)return false;
        if(!boards[bigRow][bigCol].makeMove
        (smallRow,smallCol,currentPlayer))return false;
        checkUltimateWinner();
        nextBoardRow=smallRow;nextBoardCol=smallCol;
        if(boards[nextBoardRow][nextBoardCol].winner!=EMPTY)
            nextBoardRow=-1,nextBoardCol=-1;
        currentPlayer=(currentPlayer==PLAYER_X)?PLAYER_O:PLAYER_X;
        return true;
    }
    void checkUltimateWinner(){
        for(int i=0;i<3;i++){
            if(boards[i][0].winner==PLAYER_X&& boards[i][1].
            winner==PLAYER_X&&boards[i][2].winner==PLAYER_X){
                gameOver=true;ultimateWinner=PLAYER_X;return;}
            if(boards[i][0].winner==PLAYER_O&& boards[i][1].
            winner==PLAYER_O&&boards[i][2].winner==PLAYER_O){
                gameOver=true;ultimateWinner=PLAYER_O;return;}
        }
        for(int j=0;j<3;j++){
            if(boards[0][j].winner==PLAYER_X&&boards[1][j].
            winner==PLAYER_X&&boards[2][j].winner==PLAYER_X){
                gameOver=true;ultimateWinner=PLAYER_X;return;}
            if(boards[0][j].winner==PLAYER_O&&boards[1][j].
            winner==PLAYER_O&&boards[2][j].winner==PLAYER_O){
                gameOver=true;ultimateWinner=PLAYER_O;return;}
        }
        if(boards[0][0].winner==PLAYER_X&&boards[1][1].
        winner==PLAYER_X&&boards[2][2].winner==PLAYER_X){
            gameOver=true;ultimateWinner=PLAYER_X;return;}
        if(boards[0][2].winner==PLAYER_X&&boards[1][1].
        winner==PLAYER_X&&boards[2][0].winner==PLAYER_X){
            gameOver=true;ultimateWinner=PLAYER_X;return;}
        if(boards[0][0].winner==PLAYER_O&&boards[1][1].
        winner==PLAYER_O&&boards[2][2].winner==PLAYER_O){
            gameOver=true;ultimateWinner=PLAYER_O;return;}
        if(boards[0][2].winner==PLAYER_O&&boards[1][1].
        winner==PLAYER_O&&boards[2][0].winner==PLAYER_O){
            gameOver=true;ultimateWinner=PLAYER_O;return;}
        bool isFull=true;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++)
                if(boards[i][j].winner==EMPTY){
                    isFull=false;
                    break;
                }
            if(!isFull)break;
        }
        if(isFull){
            gameOver=true;
            ultimateWinner=TIE;
        }
    }
    tuple<int,int,int> down(char w){
        if(w==PLAYER_O)return make_tuple(200,10,10);
        else if(w==PLAYER_X)return make_tuple(10,150,150);
        else if(w==TIE)return make_tuple(120,120,120);
        else return make_tuple(50,50,50);
    }
    void print_square(int x,int y){
        int rol=(y-3)/8,col=(x-4)/16;
        tuple<int,int,int>c;
        gotoxy(x,y);
        c=down(boards[rol][col].board[0][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        color(120,120,120);cout<<"■";
        c=down(boards[rol][col].board[0][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        color(120,120,120);cout<<"■";
        c=down(boards[rol][col].board[0][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        gotoxy(x,y+1);
        color(120,120,120);cout<<"■■■■■";
        gotoxy(x,y+2);
        c=down(boards[rol][col].board[1][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        color(120,120,120);cout<<"■";
        c=down(boards[rol][col].board[1][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        color(120,120,120);cout<<"■";
        c=down(boards[rol][col].board[1][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        gotoxy(x,y+3);
        color(120,120,120);cout<<"■■■■■";
        gotoxy(x,y+4);
        c=down(boards[rol][col].board[2][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        color(120,120,120);cout<<"■";
        c=down(boards[rol][col].board[2][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        color(120,120,120);cout<<"■";
        c=down(boards[rol][col].board[2][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
    }
    void print(){
        color(200,200,200);
        puts("\
                        \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
 ■■■■■■■■■■■■■■■■■■■■■■■\n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
 ■■■■■■■■■■■■■■■■■■■■■■■\n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \n\
        ■       ■       \
");
        color(120,120,120);
        for(int y=3;y<=19;y+=8)
            for(int x=4;x<=36;x+=16)
                print_square(x,y);
    }   
    vector<pair<pair<int,int>,pair<int,int>>>getPossibleMoves()const{
        vector<pair<pair<int,int>,pair<int,int>>>moves;
        if(gameOver)return moves;
        vector<pair<int,int>>bigBoardsToCheck;
        if(nextBoardRow==-1||nextBoardCol==-1){
            for(int i=0;i<3;i++)for(int j=0;j<3;j++)
                if(boards[i][j].winner==EMPTY)bigBoardsToCheck.emplace_back(i,j);
        }else bigBoardsToCheck.emplace_back(nextBoardRow,nextBoardCol);
        for(const auto& bigPos:bigBoardsToCheck){
            int bigRow=bigPos.first;
            int bigCol=bigPos.second;
            for(int smallRow=0;smallRow<3;smallRow++)
                for(int smallCol=0;smallCol<3;smallCol++)
                    if(boards[bigRow][bigCol].board[smallRow][smallCol]==EMPTY)
                        moves.emplace_back(make_pair(bigRow,bigCol),make_pair(smallRow,smallCol));
        }
        return moves;
    }
    int evaluate()const{
        if(gameOver)
            if(ultimateWinner==PLAYER_X)return 1000;
            else if(ultimateWinner==PLAYER_O)return -1000;
            else return 0;
        int score=0;
        for(int i=0;i<3;i++)for(int j=0;j<3;j++)
            if(boards[i][j].winner==PLAYER_X)score+=10;
            else if(boards[i][j].winner==PLAYER_O)score-=10;
            else for(int x=0;x<3;x++)for(int y=0;y<3;y++)
                if(boards[i][j].board[x][y]==PLAYER_X)score+=1;
                else if(boards[i][j].board[x][y]==PLAYER_O)score-=1;
        return score;
    }
};
class AI{
public:
    pair<pair<int,int>,pair<int,int>>findBestMove(UltimateBoard& board,int depth,bool isMaximizing){
        vector<pair<pair<int,int>,pair<int,int>>>bestMoves;
        int bestValue=isMaximizing?-numeric_limits<int>::max():numeric_limits<int>::max();
        vector<pair<pair<int,int>,pair<int,int>>>possibleMoves=board.getPossibleMoves();
        for(const auto& move:possibleMoves){
            UltimateBoard newBoard=board;
            newBoard.makeMove(move.first.first,move.first.second,move.second.first,move.second.second);
            int moveValue=minimax(newBoard,depth-1,
            -numeric_limits<int>::max(),numeric_limits<int>::max(),!isMaximizing);
            if((isMaximizing&&moveValue>bestValue)||(!isMaximizing&&moveValue<bestValue))
                bestValue=moveValue,bestMoves.clear(),bestMoves.push_back(move);
            else if(moveValue==bestValue)bestMoves.push_back(move);
        }
        if(!bestMoves.empty()){
            uniform_int_distribution<int>dist(0,bestMoves.size()-1);
            return bestMoves[dist(rng)];
        }
        return make_pair(make_pair(-1,-1),make_pair(-1,-1));
    }
private:
    int minimax(UltimateBoard& board,int depth,int alpha,int beta,bool isMaximizing){
        if(depth==0||board.gameOver)
            return board.evaluate();
        if(isMaximizing){
            int maxEval=-numeric_limits<int>::max();
            vector<pair<pair<int,int>,pair<int,int>>>possibleMoves=board.getPossibleMoves();
            for (const auto& move:possibleMoves){
                UltimateBoard newBoard=board;
                newBoard.makeMove(move.first.first,move.first.second,move.second.first,move.second.second);
                int eval=minimax(newBoard,depth-1,alpha,beta,false);
                maxEval=max(maxEval,eval);
                alpha=max(alpha, eval);
                if(beta<=alpha)break;
            }
            return maxEval;
        }
        else{
            int minEval=numeric_limits<int>::max();
            vector<pair<pair<int,int>,pair<int,int>>>possibleMoves=board.getPossibleMoves();
            for(const auto& move:possibleMoves){
                UltimateBoard newBoard=board;
                newBoard.makeMove(move.first.first,move.first.second,move.second.first,move.second.second);
                int eval=minimax(newBoard,depth-1,alpha,beta,true);
                minEval=min(minEval, eval);
                beta=min(beta,eval);
                if(beta<=alpha)break;
            }
            return minEval;
        }
    }
};
void setsize(int col, int row){
    char cmd[64];
    sprintf(cmd,"mode con cols=%d lines=%d",col,row);
    system(cmd);
}
int main() {
    HideCursor();
    setsize(50,29);
    SetConsoleTitle("Tik Tak Toe - PLUS");
    SetWindowLongPtrA(GetConsoleWindow(),GWL_STYLE,GetWindowLongPtrA(
    GetConsoleWindow(),GWL_STYLE)&~WS_SIZEBOX&~WS_MAXIMIZEBOX&~WS_MINIMIZEBOX);
    DWORD mode;GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode);
    HANDLE hOut=GetStdHandle(STD_OUTPUT_HANDLE);
    if(hOut==INVALID_HANDLE_VALUE)return GetLastError();DWORD dwMode=0;
    if(!GetConsoleMode(hOut,&dwMode))return GetLastError();dwMode|=0x0004;
    if(!SetConsoleMode(hOut,dwMode))return GetLastError();
    UltimateBoard board;AI ai;
    pair<pair<int,int>,pair<int,int>>bestMove;
    board.currentPlayer=(rng()&1)?PLAYER_X:PLAYER_O;
    while(!board.gameOver){
        if(board.currentPlayer==PLAYER_X){
            system("cls");
            int z[3][3]={{1,2,3},{4,5,6},{7,8,9},};
            int a,b,zl[10][2]={{0,0},{0,0},{0,1},
            {0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
            color(230,230,230);
            cout<<"AI在 ("<<z[bestMove.first.first][bestMove.first.second]<<" "
                 <<z[bestMove.second.first][bestMove.second.second]<<") 下棋"<<endl;
            board.print();
            gotoxy(0,25);color(230,230,230);
            cout<<"你的回合: ";cin>>a>>b;
            if(!board.makeMove(zl[a][0],zl[a][1],zl[b][0],zl[b][1])){
                cout<<"无效的走法,请重试!"<<endl;
                Sleep(1000);continue;
            }
        }
        else bestMove=ai.findBestMove(board,DIFFICULTY,false),
            board.makeMove(bestMove.first.first,bestMove.first.second,
                        bestMove.second.first,bestMove.second.second);
    }
    system("cls");
    board.print();
    gotoxy(0,25);color(230,230,230);
    if(board.ultimateWinner==PLAYER_X)
        cout<<"恭喜你赢了!"<<endl;
    else if(board.ultimateWinner==PLAYER_O) 
        cout<<"连人机都赢不了,太逊了!"<<endl;
    else cout<<"平局,还行!"<<endl;
    Sleep(10000);
    return 0;
}

::: :::info[小型UI版本]

#include<bits/stdc++.h>
#define DIFFICULTY 7
#include<windows.h>
using namespace std;
const char EMPTY='.';
const char PLAYER_X='X';
const char PLAYER_O='O';
const char TIE='T';
mt19937 rng(time(0));
void color(int r,int g,int b)
    {wprintf(L"\x1b[38;2;%d;%d;%dm",r,g,b);}
void HideCursor(){
    CONSOLE_CURSOR_INFO cursor_info={1,0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
using namespace std;
void gotoxy(int x, int y){
    HANDLE hout;
    COORD pos;
    pos.X=x;
    pos.Y=y;
    hout=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hout, pos);
}
class SmallBoard{
public:
    char board[3][3];
    char winner;
    SmallBoard(){
        for(int i=0;i<3;i++)for(int j=0;j<3;j++)
        board[i][j]=EMPTY;winner=EMPTY;
    }
    bool makeMove(int row,int col,char player){
        if(row<0||row>=3||col<0||col>=3||board[row][col]
        !=EMPTY||winner!=EMPTY)return false;
        board[row][col]=player;checkWinner();
        return true;
    }
    void checkWinner(){
        for(int i=0;i<3;i++)
            if(board[i][0]!=EMPTY&&board[i][0]==
            board[i][1]&&board[i][0]==board[i][2]){
                winner=board[i][0];return;}
        for(int j=0;j<3;j++)
            if(board[0][j]!=EMPTY&&board[0][j]==
            board[1][j]&&board[0][j]==board[2][j]){
                winner=board[0][j];return;}
        if(board[0][0]!=EMPTY&&board[0][0]==
        board[1][1]&&board[0][0]==board[2][2]){
            winner=board[0][0];return;}
        if(board[0][2]!=EMPTY&&board[0][2]==
        board[1][1]&&board[0][2]==board[2][0]){
            winner=board[0][2];return;}
        bool isFull=true;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++)
                if(board[i][j]==EMPTY){
                    isFull=false;break;}
            if (!isFull)break;
        }
        if(isFull)winner=TIE;
    }
};
class UltimateBoard{
public:
    SmallBoard boards[3][3];
    char currentPlayer;
    int nextBoardRow;
    int nextBoardCol;
    bool gameOver;
    char ultimateWinner;
    UltimateBoard():currentPlayer(PLAYER_X),nextBoardRow(-1)
    ,nextBoardCol(-1),gameOver(false),ultimateWinner(EMPTY){}
    bool makeMove(int bigRow,int bigCol,int smallRow,int smallCol){
        if(gameOver)return false;
        if(nextBoardRow!=-1&&nextBoardCol!=-1&&(bigRow!=
        nextBoardRow||bigCol!=nextBoardCol))return false;
        if(boards[bigRow][bigCol].winner!=EMPTY)return false;
        if(!boards[bigRow][bigCol].makeMove
        (smallRow,smallCol,currentPlayer))return false;
        checkUltimateWinner();
        nextBoardRow=smallRow;nextBoardCol=smallCol;
        if(boards[nextBoardRow][nextBoardCol].winner!=EMPTY)
            nextBoardRow=-1,nextBoardCol=-1;
        currentPlayer=(currentPlayer==PLAYER_X)?PLAYER_O:PLAYER_X;
        return true;
    }
    void checkUltimateWinner(){
        for(int i=0;i<3;i++){
            if(boards[i][0].winner==PLAYER_X&& boards[i][1].
            winner==PLAYER_X&&boards[i][2].winner==PLAYER_X){
                gameOver=true;ultimateWinner=PLAYER_X;return;}
            if(boards[i][0].winner==PLAYER_O&& boards[i][1].
            winner==PLAYER_O&&boards[i][2].winner==PLAYER_O){
                gameOver=true;ultimateWinner=PLAYER_O;return;}
        }
        for(int j=0;j<3;j++){
            if(boards[0][j].winner==PLAYER_X&&boards[1][j].
            winner==PLAYER_X&&boards[2][j].winner==PLAYER_X){
                gameOver=true;ultimateWinner=PLAYER_X;return;}
            if(boards[0][j].winner==PLAYER_O&&boards[1][j].
            winner==PLAYER_O&&boards[2][j].winner==PLAYER_O){
                gameOver=true;ultimateWinner=PLAYER_O;return;}
        }
        if(boards[0][0].winner==PLAYER_X&&boards[1][1].
        winner==PLAYER_X&&boards[2][2].winner==PLAYER_X){
            gameOver=true;ultimateWinner=PLAYER_X;return;}
        if(boards[0][2].winner==PLAYER_X&&boards[1][1].
        winner==PLAYER_X&&boards[2][0].winner==PLAYER_X){
            gameOver=true;ultimateWinner=PLAYER_X;return;}
        if(boards[0][0].winner==PLAYER_O&&boards[1][1].
        winner==PLAYER_O&&boards[2][2].winner==PLAYER_O){
            gameOver=true;ultimateWinner=PLAYER_O;return;}
        if(boards[0][2].winner==PLAYER_O&&boards[1][1].
        winner==PLAYER_O&&boards[2][0].winner==PLAYER_O){
            gameOver=true;ultimateWinner=PLAYER_O;return;}
        bool isFull=true;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++)
                if(boards[i][j].winner==EMPTY){
                    isFull=false;
                    break;
                }
            if(!isFull)break;
        }
        if(isFull){
            gameOver=true;
            ultimateWinner=TIE;
        }
    }
    tuple<int,int,int> down(char w){
        if(w==PLAYER_O)return make_tuple(200,10,10);
        else if(w==PLAYER_X)return make_tuple(10,150,150);
        else if(w==TIE)return make_tuple(120,120,120);
        else return make_tuple(50,50,50);
    }
    void print_square(int x,int y){
        int rol=(y-3)/6,col=(x-4)/12;
        tuple<int,int,int>c;
        gotoxy(x,y);
        c=down(boards[rol][col].board[0][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[0][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[0][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        gotoxy(x,y+1);
        c=down(boards[rol][col].board[1][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[1][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[1][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        gotoxy(x,y+2);
        c=down(boards[rol][col].board[2][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[2][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[2][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
    }
    void print(){
        color(200,200,200);
        puts("\
                  \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
 ■■■■■■■■■■■■■■■■■\n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
 ■■■■■■■■■■■■■■■■■\n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \
");
        color(120,120,120);
        for(int y=3;y<=15;y+=6)
            for(int x=4;x<=28;x+=12)
                print_square(x,y);
    }   
    vector<pair<pair<int,int>,pair<int,int>>>getPossibleMoves()const{
        vector<pair<pair<int,int>,pair<int,int>>>moves;
        if(gameOver)return moves;
        vector<pair<int,int>>bigBoardsToCheck;
        if(nextBoardRow==-1||nextBoardCol==-1){
            for(int i=0;i<3;i++)for(int j=0;j<3;j++)
                if(boards[i][j].winner==EMPTY)bigBoardsToCheck.emplace_back(i,j);
        }else bigBoardsToCheck.emplace_back(nextBoardRow,nextBoardCol);
        for(const auto& bigPos:bigBoardsToCheck){
            int bigRow=bigPos.first;
            int bigCol=bigPos.second;
            for(int smallRow=0;smallRow<3;smallRow++)
                for(int smallCol=0;smallCol<3;smallCol++)
                    if(boards[bigRow][bigCol].board[smallRow][smallCol]==EMPTY)
                        moves.emplace_back(make_pair(bigRow,bigCol),make_pair(smallRow,smallCol));
        }
        return moves;
    }
    int evaluate()const{
        if(gameOver)
            if(ultimateWinner==PLAYER_X)return 1000;
            else if(ultimateWinner==PLAYER_O)return -1000;
            else return 0;
        int score=0;
        for(int i=0;i<3;i++)for(int j=0;j<3;j++)
            if(boards[i][j].winner==PLAYER_X)score+=10;
            else if(boards[i][j].winner==PLAYER_O)score-=10;
            else for(int x=0;x<3;x++)for(int y=0;y<3;y++)
                if(boards[i][j].board[x][y]==PLAYER_X)score+=1;
                else if(boards[i][j].board[x][y]==PLAYER_O)score-=1;
        return score;
    }
};
class AI{
public:
    pair<pair<int,int>,pair<int,int>>findBestMove(UltimateBoard& board,int depth,bool isMaximizing){
        vector<pair<pair<int,int>,pair<int,int>>>bestMoves;
        int bestValue=isMaximizing?-numeric_limits<int>::max():numeric_limits<int>::max();
        vector<pair<pair<int,int>,pair<int,int>>>possibleMoves=board.getPossibleMoves();
        for(const auto& move:possibleMoves){
            UltimateBoard newBoard=board;
            newBoard.makeMove(move.first.first,move.first.second,move.second.first,move.second.second);
            int moveValue=minimax(newBoard,depth-1,
            -numeric_limits<int>::max(),numeric_limits<int>::max(),!isMaximizing);
            if((isMaximizing&&moveValue>bestValue)||(!isMaximizing&&moveValue<bestValue))
                bestValue=moveValue,bestMoves.clear(),bestMoves.push_back(move);
            else if(moveValue==bestValue)bestMoves.push_back(move);
        }
        if(!bestMoves.empty()){
            uniform_int_distribution<int>dist(0,bestMoves.size()-1);
            return bestMoves[dist(rng)];
        }
        return make_pair(make_pair(-1,-1),make_pair(-1,-1));
    }
private:
    int minimax(UltimateBoard& board,int depth,int alpha,int beta,bool isMaximizing){
        if(depth==0||board.gameOver)
            return board.evaluate();
        if(isMaximizing){
            int maxEval=-numeric_limits<int>::max();
            vector<pair<pair<int,int>,pair<int,int>>>possibleMoves=board.getPossibleMoves();
            for (const auto& move:possibleMoves){
                UltimateBoard newBoard=board;
                newBoard.makeMove(move.first.first,move.first.second,move.second.first,move.second.second);
                int eval=minimax(newBoard,depth-1,alpha,beta,false);
                maxEval=max(maxEval,eval);
                alpha=max(alpha, eval);
                if(beta<=alpha)break;
            }
            return maxEval;
        }
        else{
            int minEval=numeric_limits<int>::max();
            vector<pair<pair<int,int>,pair<int,int>>>possibleMoves=board.getPossibleMoves();
            for(const auto& move:possibleMoves){
                UltimateBoard newBoard=board;
                newBoard.makeMove(move.first.first,move.first.second,move.second.first,move.second.second);
                int eval=minimax(newBoard,depth-1,alpha,beta,true);
                minEval=min(minEval, eval);
                beta=min(beta,eval);
                if(beta<=alpha)break;
            }
            return minEval;
        }
    }
};
void setsize(int col, int row){
    char cmd[64];
    sprintf(cmd,"mode con cols=%d lines=%d",col,row);
    system(cmd);
}
int main() {
    HideCursor();
    setsize(38,24);
    SetConsoleTitle("Tik Tak Toe - PLUS");
    SetWindowLongPtrA(GetConsoleWindow(),GWL_STYLE,GetWindowLongPtrA(
    GetConsoleWindow(),GWL_STYLE)&~WS_SIZEBOX&~WS_MAXIMIZEBOX&~WS_MINIMIZEBOX);
    DWORD mode;GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode);
    HANDLE hOut=GetStdHandle(STD_OUTPUT_HANDLE);
    if(hOut==INVALID_HANDLE_VALUE)return GetLastError();DWORD dwMode=0;
    if(!GetConsoleMode(hOut,&dwMode))return GetLastError();dwMode|=0x0004;
    if(!SetConsoleMode(hOut,dwMode))return GetLastError();
    UltimateBoard board;AI ai;
    pair<pair<int,int>,pair<int,int>>bestMove;
    board.currentPlayer=(rng()&1)?PLAYER_X:PLAYER_O;
    while(!board.gameOver){
        if(board.currentPlayer==PLAYER_X){
            system("cls");
            int z[3][3]={{1,2,3},{4,5,6},{7,8,9},};
            int a,b,zl[10][2]={{0,0},{0,0},{0,1},
            {0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
            color(230,230,230);
            cout<<"AI在 ("<<z[bestMove.first.first][bestMove.first.second]<<" "
                 <<z[bestMove.second.first][bestMove.second.second]<<") 下棋"<<endl;
            board.print();
            gotoxy(0,20);color(230,230,230);
            cout<<"你的回合: ";cin>>a>>b;
            if(!board.makeMove(zl[a][0],zl[a][1],zl[b][0],zl[b][1])){
                cout<<"无效的走法,请重试!"<<endl;
                Sleep(1000);continue;
            }
        }
        else bestMove=ai.findBestMove(board,DIFFICULTY,false),
            board.makeMove(bestMove.first.first,bestMove.first.second,
                        bestMove.second.first,bestMove.second.second);
    }
    system("cls");
    board.print();
    gotoxy(0,20);color(230,230,230);
    if(board.ultimateWinner==PLAYER_X)
        cout<<"恭喜你赢了!"<<endl;
    else if(board.ultimateWinner==PLAYER_O) 
        cout<<"连人机都赢不了,太逊了!"<<endl;
    else cout<<"平局,还行!"<<endl;
    Sleep(10000);
    return 0;
}

::: :::info[局域网联机版本(支持randmin等虚拟局域网)]

#include<bits/stdc++.h>
#include<winsock2.h>
#include<ws2tcpip.h>
#include<windows.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
const char EMPTY='.';
const char PLAYER_X='X';
const char PLAYER_O='O';
const char TIE='T';
const int PORT = 55555;
const int BUFFER_SIZE = 1024;
mt19937 rng(time(0));
void color(int r,int g,int b)
    {wprintf(L"\x1b[38;2;%d;%d;%dm",r,g,b);}
void HideCursor(){
    CONSOLE_CURSOR_INFO cursor_info={1,0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}
bool InitializeWinsock(){
    WSADATA wsaData;
    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0){
        cerr<<"WSAStartup failed."<<endl;
        return false;
    }
    return true;
}
void setsize(int col, int row){
    char cmd[64];
    sprintf(cmd,"mode con cols=%d lines=%d",col,row);
    system(cmd);
}
int InitializeUI(){
    setsize(38,24);
    SetConsoleTitle("Ultimate Tik Tak Toe - Online");
    SetWindowLongPtrA(GetConsoleWindow(),GWL_STYLE,GetWindowLongPtrA(
    GetConsoleWindow(),GWL_STYLE)&~WS_SIZEBOX&~WS_MAXIMIZEBOX&~WS_MINIMIZEBOX);
    DWORD mode;GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &mode);
    HANDLE hOut=GetStdHandle(STD_OUTPUT_HANDLE);
    if(hOut==INVALID_HANDLE_VALUE)return GetLastError();DWORD dwMode=0;
    if(!GetConsoleMode(hOut,&dwMode))return GetLastError();dwMode|=0x0004;
    if(!SetConsoleMode(hOut,dwMode))return GetLastError();return 0;
}
void gotoxy(int x, int y){
    HANDLE hout;
    COORD pos;
    pos.X=x;
    pos.Y=y;
    hout=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hout, pos);
}
class SmallBoard{
public:
    char board[3][3];
    char winner;
    SmallBoard(){
        for(int i=0;i<3;i++)for(int j=0;j<3;j++)
        board[i][j]=EMPTY;winner=EMPTY;
    }
    bool makeMove(int row,int col,char player){
        if(row<0||row>=3||col<0||col>=3||board[row][col]
        !=EMPTY||winner!=EMPTY)return false;
        board[row][col]=player;checkWinner();
        return true;
    }
    void checkWinner(){
        for(int i=0;i<3;i++)
            if(board[i][0]!=EMPTY&&board[i][0]==
            board[i][1]&&board[i][0]==board[i][2]){
                winner=board[i][0];return;}
        for(int j=0;j<3;j++)
            if(board[0][j]!=EMPTY&&board[0][j]==
            board[1][j]&&board[0][j]==board[2][j]){
                winner=board[0][j];return;}
        if(board[0][0]!=EMPTY&&board[0][0]==
        board[1][1]&&board[0][0]==board[2][2]){
            winner=board[0][0];return;}
        if(board[0][2]!=EMPTY&&board[0][2]==
        board[1][1]&&board[0][2]==board[2][0]){
            winner=board[0][2];return;}
        bool isFull=true;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++)
                if(board[i][j]==EMPTY){
                    isFull=false;break;}
            if (!isFull)break;
        }
        if(isFull)winner=TIE;
    }
};
class UltimateBoard{
public:
    SmallBoard boards[3][3];
    char currentPlayer;
    int nextBoardRow;
    int nextBoardCol;
    bool gameOver;
    char ultimateWinner;
    UltimateBoard():currentPlayer(PLAYER_X),nextBoardRow(-1)
    ,nextBoardCol(-1),gameOver(false),ultimateWinner(EMPTY){}
    bool makeMove(int bigRow,int bigCol,int smallRow,int smallCol){
        if(gameOver)return false;
        if(nextBoardRow!=-1&&nextBoardCol!=-1&&(bigRow!=
        nextBoardRow||bigCol!=nextBoardCol))return false;
        if(boards[bigRow][bigCol].winner!=EMPTY)return false;
        if(!boards[bigRow][bigCol].makeMove
        (smallRow,smallCol,currentPlayer))return false;
        checkUltimateWinner();
        nextBoardRow=smallRow;nextBoardCol=smallCol;
        if(boards[nextBoardRow][nextBoardCol].winner!=EMPTY)
            nextBoardRow=-1,nextBoardCol=-1;
        currentPlayer=(currentPlayer==PLAYER_X)?PLAYER_O:PLAYER_X;
        return true;
    }
    void checkUltimateWinner(){
        for(int i=0;i<3;i++){
            if(boards[i][0].winner==PLAYER_X&& boards[i][1].
            winner==PLAYER_X&&boards[i][2].winner==PLAYER_X){
                gameOver=true;ultimateWinner=PLAYER_X;return;}
            if(boards[i][0].winner==PLAYER_O&& boards[i][1].
            winner==PLAYER_O&&boards[i][2].winner==PLAYER_O){
                gameOver=true;ultimateWinner=PLAYER_O;return;}
        }
        for(int j=0;j<3;j++){
            if(boards[0][j].winner==PLAYER_X&&boards[1][j].
            winner==PLAYER_X&&boards[2][j].winner==PLAYER_X){
                gameOver=true;ultimateWinner=PLAYER_X;return;}
            if(boards[0][j].winner==PLAYER_O&&boards[1][j].
            winner==PLAYER_O&&boards[2][j].winner==PLAYER_O){
                gameOver=true;ultimateWinner=PLAYER_O;return;}
        }
        if(boards[0][0].winner==PLAYER_X&&boards[1][1].
        winner==PLAYER_X&&boards[2][2].winner==PLAYER_X){
            gameOver=true;ultimateWinner=PLAYER_X;return;}
        if(boards[0][2].winner==PLAYER_X&&boards[1][1].
        winner==PLAYER_X&&boards[2][0].winner==PLAYER_X){
            gameOver=true;ultimateWinner=PLAYER_X;return;}
        if(boards[0][0].winner==PLAYER_O&&boards[1][1].
        winner==PLAYER_O&&boards[2][2].winner==PLAYER_O){
            gameOver=true;ultimateWinner=PLAYER_O;return;}
        if(boards[0][2].winner==PLAYER_O&&boards[1][1].
        winner==PLAYER_O&&boards[2][0].winner==PLAYER_O){
            gameOver=true;ultimateWinner=PLAYER_O;return;}
        bool isFull=true;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++)
                if(boards[i][j].winner==EMPTY){
                    isFull=false;
                    break;
                }
            if(!isFull)break;
        }
        if(isFull){
            gameOver=true;
            ultimateWinner=TIE;
        }
    }
    tuple<int,int,int> down(char w){
        if(w==PLAYER_O)return make_tuple(200,10,10);
        else if(w==PLAYER_X)return make_tuple(10,150,150);
        else if(w==TIE)return make_tuple(120,120,120);
        else return make_tuple(50,50,50);
    }
    void print_square(int x,int y){
        int rol=(y-3)/6,col=(x-4)/12;
        tuple<int,int,int>c;
        gotoxy(x,y);
        c=down(boards[rol][col].board[0][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[0][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[0][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        gotoxy(x,y+1);
        c=down(boards[rol][col].board[1][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[1][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[1][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        gotoxy(x,y+2);
        c=down(boards[rol][col].board[2][0]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[2][1]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
        c=down(boards[rol][col].board[2][2]);
        if(boards[rol][col].winner!=EMPTY)
            c=down(boards[rol][col].winner);
        color(get<0>(c),get<1>(c),get<2>(c));
        cout<<"■";
    }
    void print(){
        color(200,200,200);
        puts("\
                  \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
 ■■■■■■■■■■■■■■■■■\n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
 ■■■■■■■■■■■■■■■■■\n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \n\
      ■     ■     \
");
        color(120,120,120);
        for(int y=3;y<=15;y+=6)
            for(int x=4;x<=28;x+=12)
                print_square(x,y);
    }   
    vector<pair<pair<int,int>,pair<int,int>>>getPossibleMoves()const{
        vector<pair<pair<int,int>,pair<int,int>>>moves;
        if(gameOver)return moves;
        vector<pair<int,int>>bigBoardsToCheck;
        if(nextBoardRow==-1||nextBoardCol==-1){
            for(int i=0;i<3;i++)for(int j=0;j<3;j++)
                if(boards[i][j].winner==EMPTY)bigBoardsToCheck.emplace_back(i,j);
        }else bigBoardsToCheck.emplace_back(nextBoardRow,nextBoardCol);
        for(const auto& bigPos:bigBoardsToCheck){
            int bigRow=bigPos.first;
            int bigCol=bigPos.second;
            for(int smallRow=0;smallRow<3;smallRow++)
                for(int smallCol=0;smallCol<3;smallCol++)
                    if(boards[bigRow][bigCol].board[smallRow][smallCol]==EMPTY)
                        moves.emplace_back(make_pair(bigRow,bigCol),make_pair(smallRow,smallCol));
        }
        return moves;
    }
    int evaluate()const{
        if(gameOver)
            if(ultimateWinner==PLAYER_X)return 1000;
            else if(ultimateWinner==PLAYER_O)return -1000;
            else return 0;
        int score=0;
        for(int i=0;i<3;i++)for(int j=0;j<3;j++)
            if(boards[i][j].winner==PLAYER_X)score+=10;
            else if(boards[i][j].winner==PLAYER_O)score-=10;
            else for(int x=0;x<3;x++)for(int y=0;y<3;y++)
                if(boards[i][j].board[x][y]==PLAYER_X)score+=1;
                else if(boards[i][j].board[x][y]==PLAYER_O)score-=1;
        return score;
    }
};
class LAN{
public:
void RunServer(){
    SOCKET listenSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(listenSocket==INVALID_SOCKET){
        cerr<<"Error creating socket: "<<WSAGetLastError()<<endl;
        return;
    }
    sockaddr_in serverAddr;
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_addr.s_addr=INADDR_ANY;
    serverAddr.sin_port=htons(PORT);
    if(bind(listenSocket,(sockaddr*)&serverAddr,sizeof(serverAddr))==SOCKET_ERROR){
        cerr<<"Bind failed: "<<WSAGetLastError()<<endl;
        closesocket(listenSocket);return;
    }
    if(listen(listenSocket,1)==SOCKET_ERROR){
        cerr<<"Listen failed: "<<WSAGetLastError()<<endl;
        closesocket(listenSocket);return;
    }
    cout<<"Server is waiting for client connection..."<<endl;
    SOCKET clientSocket=accept(listenSocket,NULL,NULL);
    if(clientSocket==INVALID_SOCKET) {
        cerr<<"Accept failed: "<<WSAGetLastError()<<endl;
        closesocket(listenSocket);return;
    }
    closesocket(listenSocket);
    cout<<"Client connected."<<endl;
    UltimateBoard board;
    pair<int,int>lastMove;
    board.currentPlayer=(rng()&1)?PLAYER_X:PLAYER_O;
    if(board.currentPlayer==PLAYER_X){
        cout<<"You first."<<endl;string message="second";
        send(clientSocket,message.c_str(),message.size()+1,0);
    }
    else{
        cout<<"You second."<<endl;string message="first";
        send(clientSocket,message.c_str(),message.size()+1,0);
    }
    Sleep(1500);
    char buffer[BUFFER_SIZE];
    while(!board.gameOver){
        if(board.currentPlayer==PLAYER_X){
            system("cls");
            int a,b,zl[10][2]={{0,0},{0,0},{0,1},
            {0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
            color(230,230,230);
            cout<<"对方在 ("<<lastMove.first<<" "<<lastMove.second<<") 下棋"<<endl;
            board.print();
            gotoxy(0,20);color(230,230,230);
            cout<<"你的回合: ";cin>>a>>b;
            if(!board.makeMove(zl[a][0],zl[a][1],zl[b][0],zl[b][1])){
                cout<<"无效的走法,请重试!"<<endl;
                Sleep(1000);continue;
            }
            string message=to_string(a)+" "+to_string(b);lastMove=make_pair(a,b);
            send(clientSocket,message.c_str(),message.size()+1,0);
        }
        else{
            system("cls");
            cout<<"你在 ("<<lastMove.first<<" "<<lastMove.second<<") 下棋"<<endl;
            board.print();gotoxy(0,20);color(230,230,230);cout<<"等待对方走棋...... ";
            int bytesReceived=recv(clientSocket,buffer,BUFFER_SIZE,0);
            if(bytesReceived<=0){
                cout<<"Client disconnected."<<endl;
                board.ultimateWinner=PLAYER_X;
                Sleep(1000);
                break;
            }
            string receivedStr(buffer,bytesReceived);
            size_t spacePos=receivedStr.find(' ');
            if(spacePos!=std::string::npos){
                int num1=stoi(receivedStr.substr(0,spacePos));
                int num2=stoi(receivedStr.substr(spacePos+1));
                int zl[10][2]={{0,0},{0,0},{0,1},{0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
                board.makeMove(zl[num1][0],zl[num1][1],zl[num2][0],zl[num2][1]);
                lastMove=make_pair(num1,num2);
            }
        }
    }
    system("cls");board.print();closesocket(clientSocket);
    gotoxy(0,20);color(230,230,230);
    if(board.ultimateWinner==PLAYER_X)
        cout<<"恭喜你赢了!"<<endl;
    else if(board.ultimateWinner==PLAYER_O) 
        cout<<"竟然输了!"<<endl;
    else cout<<"平局,还行!"<<endl;
    Sleep(10000);
}
void RunClient(){
    SOCKET clientSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (clientSocket==INVALID_SOCKET){
        cerr<<"Error creating socket: "<<WSAGetLastError()<<endl;
        return;
    }
    string serverIP;
    cout<<"Enter server IP address: ";
    cin>>serverIP;
    sockaddr_in serverAddr;
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_port=htons(PORT);
    inet_pton(AF_INET,serverIP.c_str(),&serverAddr.sin_addr);
    if(connect(clientSocket,(sockaddr*)&serverAddr,sizeof(serverAddr))==SOCKET_ERROR){
        cerr<<"Connect failed: "<<WSAGetLastError()<<endl;
        closesocket(clientSocket);return;
    }
    cout<<"Connected to server."<<endl;
    char buffer[BUFFER_SIZE];UltimateBoard board;
    int byteReceived=recv(clientSocket,buffer,BUFFER_SIZE,0);
    string firstMsg(buffer,byteReceived);
    if(firstMsg.find("first")!=string::npos){
        board.currentPlayer=PLAYER_X;
        cout<<firstMsg<<endl;
        cout<<"You first."<<endl;
    }
    else{
        board.currentPlayer=PLAYER_O;
        cout<<"You second."<<endl;
    }
    pair<int,int>lastMove;
    while(!board.gameOver){
        if(board.currentPlayer==PLAYER_X){
            system("cls");
            int a,b,zl[10][2]={{0,0},{0,0},{0,1},
            {0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
            color(230,230,230);
            cout<<"对方在 ("<<lastMove.first<<" "<<lastMove.second<<") 下棋"<<endl;
            board.print();
            gotoxy(0,20);color(230,230,230);
            cout<<"你的回合: ";cin>>a>>b;
            if(!board.makeMove(zl[a][0],zl[a][1],zl[b][0],zl[b][1])){
                cout<<"无效的走法,请重试!"<<endl;
                Sleep(1000);continue;
            }
            string message=to_string(a)+" "+to_string(b);lastMove=make_pair(a,b);
            send(clientSocket,message.c_str(),message.size()+1,0);
        }
        else{
            system("cls");
            cout<<"你在 ("<<lastMove.first<<" "<<lastMove.second<<") 下棋"<<endl;
            board.print();gotoxy(0,20);color(230,230,230);cout<<"等待对方走棋...... ";
            int bytesReceived=recv(clientSocket,buffer,BUFFER_SIZE,0);
            if(bytesReceived<=0){
                cout<<"Client disconnected."<<endl;
                board.ultimateWinner=PLAYER_X;
                Sleep(1000);
                break;
            }
            string receivedStr(buffer,bytesReceived);
            size_t spacePos=receivedStr.find(' ');
            if(spacePos!=std::string::npos){
                int num1=stoi(receivedStr.substr(0,spacePos));
                int num2=stoi(receivedStr.substr(spacePos+1));
                int zl[10][2]={{0,0},{0,0},{0,1},{0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
                board.makeMove(zl[num1][0],zl[num1][1],zl[num2][0],zl[num2][1]);
                lastMove=make_pair(num1,num2);
            }
        }
    }
    system("cls");board.print();closesocket(clientSocket);
    gotoxy(0,20);color(230,230,230);
    if(board.ultimateWinner==PLAYER_X)
        cout<<"恭喜你赢了!"<<endl;
    else if(board.ultimateWinner==PLAYER_O) 
        cout<<"竟然输了!"<<endl;
    else cout<<"平局,还行!"<<endl;
    Sleep(10000);
}
};
int main() {
    InitializeUI();
    if(!InitializeWinsock()){return 1;}int choice;
    while(true){
        cout<<"Choose mode:\n1. Server\n2. Client\nEnter choice :";
        cin>>choice;
        if(choice==1||choice==2)break;
        cout<<"Invalid choice."<<endl;
        Sleep(1000);system("cls");
    }
    LAN lan;
    if(choice==1)lan.RunServer();
    else lan.RunClient();
    return 0;
}
\textcolor{red}{要开}$ `-lws2_32` $\textcolor{red}{的编译命令}

只有小UI。。。因为作者喜欢。

::: :::::