//
// Created by WAHAHA
// 

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>

char BoardRecord[4][4];
char drawPieceCoord[9][2] = {
    {1, 0},
    {5, 0},
    {9, 0},
    {1, 2},
    {5, 2},
    {9, 2},
    {1, 4},
    {5, 4},
    {9, 4},
};
int check[8][3][2] = {
    {{1, 1}, {2, 2}, {3, 3}},
    {{1, 3}, {2, 2}, {3, 1}},
    {{1, 1}, {1, 2}, {1, 3}},
    {{2, 1}, {2, 2}, {2, 3}},
    {{3, 1}, {3, 2}, {3, 3}},
    {{1, 1}, {2, 1}, {3, 1}},
    {{1, 2}, {2, 2}, {3, 2}},
    {{1, 3}, {2, 3}, {3, 3}},
};

//menu
void menu();

void initmenu();

void wrongInput();

//game begin
void start();

void printBoard();

void computerPlay(char);

int select_mode();

void userPlay(char);

int checkGameover(char);

void gameover(int, int);

//system
void gotoxy(int, int);

//menu
void menu() {
    char c;
    initmenu();
    while (1) {
        gotoxy(37, 8);//定位到输入栏
        c = _getch();//vs2022要求将getch()更换为_getch()---标准c编译器换回getch()(?)
        if (c == '1') {
            start();
            system("cls");
            initmenu();
        } else if (c == '2') {
            break;
        } else wrongInput();
    }
}

void initmenu() {
    gotoxy(28, 0);
    printf("三子棋\n");
    const char *select[8] = {
        "        _______________________________________________\n",
        "        |                                             |\n",
        "        |        请输入编号:                          |\n",
        "        |       1.开始游戏     2.退出游戏             |\n",
        "        |                                             |\n",
        "        |                                             |\n",
        "        -----------------------------------------------\n",
        "                    请输入编号[1/2]:[ ]"
    };
    for (int i = 0; i < 8; ++i) {//打印菜单
        printf("%s", select[i]);
    }
}

void wrongInput() {
    gotoxy(39, 8);
    printf("输入错误!");
    Sleep(1000);
    gotoxy(39, 8);
    printf("         ");
    return;
}

//game bigin
void start() {
    memset(BoardRecord, '0', sizeof(BoardRecord));
    srand((unsigned int) time(NULL));
    int flag, mode, hand = rand() % 2;

    mode = select_mode();
    system("cls");
    printBoard();

    if (mode == 1) {//先后手的分配要考虑人机对战
        if (hand == 0) {
            printf("\n这局你为先手!\n");
        } else {
            printf("\n这局你为后手!\n");
            computerPlay('x');
        }
    } else {
        if (hand == 0) {
            printf("\n这局玩家'o'为先手!\n");
        } else {
            printf("\n这局玩家'x'为先手!\n");
            userPlay('x');
        }
    }

    while (1) {
        if (mode == 1) {//人机对战
            userPlay('o');
            if ((flag = checkGameover('x')) != 0) {
                gameover(flag, mode);
                return;
            }
            computerPlay('x');
            if ((flag = checkGameover('o')) != 0) {
                gameover(flag, mode);
                return;
            }
        } else {//双人对战
            userPlay('o');
            if ((flag = checkGameover('x')) != 0) {
                gameover(flag, mode);
                return;
            }
            userPlay('x');
            if ((flag = checkGameover('o')) != 0) {
                gameover(flag, mode);
                return;
            }
        }
    }
}

void printBoard() {
    const char *board[5] = {
        "   |   |   \n",
        "---+---+---\n",
        "   |   |   \n",
        "---+---+---\n",
        "   |   |   "
    };
    gotoxy(0, 0);
    for (int i = 0; i < 5; ++i) {
        printf("%s", board[i]);
    }
}

int checkGameover(char next_player) {//0为继续,1为(玩家o)(电脑或玩家2)赢,2为(玩家x)赢,3为平局
    //为了实现提前预测胜负,每次落子判断胜负的函数传入下一个玩家的身份(字符)
    int num = 0, *a, *b, *c;
    //先判断玩家o赢---每次玩家o先下棋(?)---在人机对战时
    for (int i = 0; i < 8; ++i) {
        a = check[i][0];
        b = check[i][1];
        c = check[i][2];
        if (BoardRecord[a[0]][a[1]] == 'o' &&
                BoardRecord[b[0]][b[1]] == 'o' &&
                BoardRecord[c[0]][c[1]] == 'o') {
            return 2;
        } else if (BoardRecord[a[0]][a[1]] == 'x' &&
                   BoardRecord[b[0]][b[1]] == 'x' &&
                   BoardRecord[c[0]][c[1]] == 'x') {
            return 1;
        }
    }
    if (next_player == '#') //递归出口
        return 3;
    int last_i, last_j;
    for (int i = 1; i <= 3; ++i)
        for (int j = 1; j <= 3; ++j)
            if (BoardRecord[i][j] == '0') {
                last_i = i;
                last_j = j;
                ++num;
            }
    if (num == 1) {
        BoardRecord[last_i][last_j] = next_player;
        if (checkGameover('#') == 3)//此处以'#'作为递归出口
            return 3;
        BoardRecord[last_i][last_j] = '0';
    }
    return 0;
}

void gameover(int flag, int mode) {
    const char *gameoverTips_mode1[4] = {
        "\0",
        "You lose!",
        "You win!",
        "Draw!"
    };
    const char *gameoverTips_mode2[4] = {//认为玩家o为优先
        "\0",
        "玩家'x' win!",
        "玩家'o' win!",
        "Draw!"
    };
    gotoxy(0, 7);
    if (mode == 1)
        printf("%s\n", gameoverTips_mode1[flag]);
    else
        printf("%s\n", gameoverTips_mode2[flag]);

    printf("按空格键确认:[ ]");
    gotoxy(14, 8);
    char c;
    while ((c = _getch()) != ' ');
}

void computerPlay(char c) {
    srand((unsigned int) time(NULL));
    int x, y;
    x = (rand() % 3) + 1;
    y = (rand() % 3) + 1;
    while (BoardRecord[x][y] != '0') {
        x = (rand() % 3) + 1;
        y = (rand() % 3) + 1;
    }
    BoardRecord[x][y] = c;
    gotoxy(drawPieceCoord[3 * (x - 1) + y - 1][0], drawPieceCoord[3 * (x - 1) + y - 1][1]);
    putchar(c);
    return;
}

void userPlay(char c) {
    int x, y;
    gotoxy(12, 2);
    printf("轮到玩家'%c'落子", c);
    while (1) {
        gotoxy(0, 6);
        printf("请输入你要落子的坐标(例如:左上为1 1;左下为3 1):");
        scanf("%d%d", &x, &y);
        while (getchar() != '\n');
        if (x >= 1 && x <= 3 && y >= 1 && y <= 3 && BoardRecord[x][y] == '0') {
            BoardRecord[x][y] = c;
            gotoxy(47, 6);
            printf("       ");
            break;
        } else {
            printf("error!");
            gotoxy(47, 6);
            printf("       ");
            Sleep(1000);
            gotoxy(0, 7);
            printf("      ");
        }
    }
    gotoxy(drawPieceCoord[3 * (x - 1) + y - 1][0], drawPieceCoord[3 * (x - 1) + y - 1][1]);
    putchar(c);
    return;
}

int select_mode() {
    system("cls");
    gotoxy(28, 0);
    printf("选择模式\n");
    const char *select[8] = {
        "        _______________________________________________\n",
        "        |                                             |\n",
        "        |        请输入编号:                          |\n",
        "        |       1.人机对战     2.双人对战             |\n",
        "        |                                             |\n",
        "        |                                             |\n",
        "        -----------------------------------------------\n",
        "                    请输入编号[1/2]:[ ]"
    };
    for (int i = 0; i < 8; ++i) {//打印菜单
        printf("%s", select[i]);
    }
    char c;
    while (1) {
        gotoxy(37, 8);//定位到输入栏
        c = _getch();//vs2022要求将getch()更换为_getch()---标准c编译器换回getch()(?)
        if (c == '1') {
            return 1;
        } else if (c == '2') {
            return 2;
        } else wrongInput();
    }
}

//system
void gotoxy(int x, int y) {
    COORD c;
    static HANDLE h;
    h = GetStdHandle(STD_OUTPUT_HANDLE);
    c.X = x;
    c.Y = y;
    SetConsoleCursorPosition(h, c);
}

int main() {
    menu();
    return 0;
}