X11 五子棋

#include <X11/Xlib.h>
#include <stdlib.h>
#include <X11/keysym.h>
#include <cstring>
#include <iostream>
using namespace std;
enum CELL_STATE { CELL_EMPTY, CELL_O, CELL_X };
enum GAME_STATE { X_TURN, O_TURN, X_WON, O_WON, DRAW };

class Game {
public:
    Game();

    CELL_STATE getCellState(int x, int y);
    GAME_STATE getGameState();

    bool makeMove(int x, int y);
    bool makeRandomMove();
    void restart();
private:
    CELL_STATE _boardState[3][3];
    GAME_STATE _gameState;
    int _turnsPassed;

    void _checkForEnd();
};

inline CELL_STATE Game::getCellState(int x, int y) {
    return _boardState[x][y];
}

inline GAME_STATE Game::getGameState() {
    return _gameState;
}

Game::Game() {
    restart();
}

void Game::restart() {
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
            _boardState[i][j] = CELL_EMPTY;
        }
    }
    _gameState = X_TURN;
    _turnsPassed = 0;
}

bool Game::makeMove(int x, int y) {
    GAME_STATE nextState;
    CELL_STATE curPlayerCell;

    if(_gameState == O_TURN) {
        curPlayerCell = CELL_O;
        nextState = X_TURN;
    } else if(_gameState == X_TURN) {
        curPlayerCell = CELL_X;
        nextState = O_TURN;
    } else {
        return false;
    }

    if(_boardState[x][y] != CELL_EMPTY) {
        return false;
    }

    _boardState[x][y] = curPlayerCell;
    _turnsPassed++;
    _gameState = nextState;

    _checkForEnd();
    return true;
}

bool Game::makeRandomMove() {
    GAME_STATE nextState;
    CELL_STATE curPlayerCell;

    if(_gameState == O_TURN) {
        curPlayerCell = CELL_O;
        nextState = X_TURN;
    } else if(_gameState == X_TURN) {
        curPlayerCell = CELL_X;
        nextState = O_TURN;
    } else {
        return false;
    }

    int nFreeCells = 0;
    int freeCells[9][2];
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
            if(_boardState[i][j] == CELL_EMPTY) {
                freeCells[nFreeCells][0] = i;
                freeCells[nFreeCells][1] = j;
                nFreeCells++;
            }
        }
    }

    if(nFreeCells == 0) {
        return false;
    }

    int choice = rand() % nFreeCells;
    int x = freeCells[choice][0];
    int y = freeCells[choice][1];

    _boardState[x][y] = curPlayerCell;
    _turnsPassed++;
    _gameState = nextState;

    _checkForEnd();
    return true;
}

void Game::_checkForEnd() {
    if(_turnsPassed == 9) {
        _gameState = DRAW;
    }

    CELL_STATE winningCell = CELL_EMPTY;

    for(int i = 0; i < 3; i++) {
        if((_boardState[i][0] == _boardState[i][1]) &&
                (_boardState[i][1] == _boardState[i][2]) &&
                (_boardState[i][0] != CELL_EMPTY)) {
            winningCell = _boardState[i][0];
        }
    }
    for(int i = 0; i < 3; i++) {
        if((_boardState[0][i] == _boardState[1][i]) &&
                (_boardState[1][i] == _boardState[2][i]) &&
                (_boardState[0][1] != CELL_EMPTY)) {
            winningCell = _boardState[0][i];
        }
    }
    if((_boardState[0][0] == _boardState[1][1]) &&
            (_boardState[1][1] == _boardState[2][2]) &&
            (_boardState[0][0] != CELL_EMPTY)) {
        winningCell = _boardState[0][0];
    }
    if((_boardState[2][0] == _boardState[1][1]) &&
            (_boardState[1][1] == _boardState[0][2]) &&
            (_boardState[2][0] != CELL_EMPTY)) {
        winningCell = _boardState[2][0];
    }

    if(winningCell == CELL_X) {
        _gameState = X_WON;
    } else if(winningCell == CELL_O) {
        _gameState = O_WON;
    }
}

class HelloWorld {
public:
    HelloWorld(Display* display);

    Window getWindow();

    void draw();
    void map();

    void handleKeyPress(const XKeyEvent& event);
    void handleMousePress(const XButtonEvent& event);
    void restartGame();
private:
    const static int MIN_CELL_SIZE;
    const static int STRING_HEIGHT;
    const static char WINDOW_FONT[];

    Display* _display;
    Screen* _screen;
    Window _window;

    Font _font;
    unsigned long _blackColor;
    unsigned long _whiteColor;

    Game _game;

    void _drawO(const GC& gc, int x, int y, int w, int h);
    void _drawX(const GC& gc, int x, int y, int w, int h);

    void _drawString(const GC& gc, const char* str, int x, int y);
    void _drawStringCentered(const GC& gc, const char* str, int x, int y, int w, int h);
};

inline Window HelloWorld::getWindow() {
    return _window;
}




const int HelloWorld::MIN_CELL_SIZE = 50;
const int HelloWorld::STRING_HEIGHT = 20;
const char HelloWorld::WINDOW_FONT[] = "-*-*-*-*-*-*-12-*-*-*-*-*-*-*";


HelloWorld::HelloWorld(Display* display) :
    _game() {

    _display = display;
    _screen = XDefaultScreenOfDisplay(display);

    _blackColor = BlackPixelOfScreen(_screen);
    _whiteColor = WhitePixelOfScreen(_screen);
    _font = XLoadFont(_display, WINDOW_FONT);

    int screenWidth = XWidthOfScreen(_screen);
    int screenHeight = XHeightOfScreen(_screen);

    int windowWidth = MIN_CELL_SIZE * 3;
    int windowHeight = MIN_CELL_SIZE * 3 + STRING_HEIGHT * 3;
    int windowX = (screenWidth + windowWidth) / 2;
    int windowY = (screenHeight + windowHeight) / 2;
    _window = XCreateSimpleWindow(_display, XRootWindowOfScreen(_screen),
            windowX, windowY, windowWidth, windowHeight, 1, _blackColor,
            _whiteColor);

    long eventMask = ButtonPressMask | ExposureMask | KeyPressMask;
    XSelectInput(_display, _window, eventMask);

    draw();
}


void HelloWorld::draw() {
    // Getting window dimensions.
    Window rootWindow;
    int x, y;
    unsigned int width, height, borderWidth, bitDepth;
    XGetGeometry(_display, _window, &rootWindow, &x, &y, &width, &height,
            &borderWidth, &bitDepth);

    // Setting up the GC.
    GC gc = XDefaultGCOfScreen(_screen);
    XSetBackground(_display, gc, _whiteColor);
    XSetFont(_display, gc, _font);
    XSetForeground(_display, gc, _blackColor);

    // Clearing the window.
    XClearArea(_display, _window, 0, 0, width, height, false);

    // Is the window large enough for us?
    if((width < MIN_CELL_SIZE * 3) ||
            (height < (MIN_CELL_SIZE * 3 + STRING_HEIGHT * 3))) {
        _drawStringCentered(gc, "Window too small.", 0, 0, width, height);
        return;
    }

    // Calculating grid cell sizes.
    int xStepSize = width / 3;
    int yStepSize = (height - STRING_HEIGHT * 3) / 3;

    // Drawing glyphs.
    for(int i = 0; i < 3; i++) {
        for(int j = 0; j < 3; j++) {
            switch(_game.getCellState(i, j)) {
            case CELL_O:
                _drawO(gc, xStepSize * i, STRING_HEIGHT * 2 + yStepSize * j,
                        xStepSize, yStepSize);
                break;
            case CELL_X:
                _drawX(gc, xStepSize * i, STRING_HEIGHT * 2 + yStepSize * j,
                        xStepSize, yStepSize);
                break;
            default:
                break;
            }
        }
    }
    XSetBackground(_display, gc, _whiteColor);
    XSetForeground(_display, gc, _blackColor);

    // Drawing the grid lines.
    for(int i = 0; i <= 3; i++) {
        int yValue = STRING_HEIGHT * 2 + yStepSize * i;
        XDrawLine(_display, _window, gc, 0, yValue, width, yValue);
    }
    for(int i = 1; i <= 2; i++) {
        int xValue = xStepSize * i;
        XDrawLine(_display, _window, gc, xValue, STRING_HEIGHT * 2, xValue,
                height - STRING_HEIGHT);
    }

    // Drawing the strings.
    _drawStringCentered(gc, "Hello, World!", 0, 0, width, STRING_HEIGHT);
    _drawStringCentered(gc, "[R]estart", 0, height - STRING_HEIGHT,
            width, STRING_HEIGHT);

    switch(_game.getGameState()) {
    case X_TURN:
        _drawStringCentered(gc, "It is your turn to play.", 0, STRING_HEIGHT,
                width, STRING_HEIGHT);
        break;
    case O_TURN:
        _drawStringCentered(gc, "Please wait, thinking...", 0, STRING_HEIGHT,
                width, STRING_HEIGHT);
        break;
    case X_WON:
        _drawStringCentered(gc, "You have won.", 0, STRING_HEIGHT, width,
                STRING_HEIGHT);
        break;
    case O_WON:
        _drawStringCentered(gc, "You have lost.", 0, STRING_HEIGHT, width,
                STRING_HEIGHT);
        break;
    case DRAW:
        _drawStringCentered(gc, "It is a draw.", 0, STRING_HEIGHT, width,
                STRING_HEIGHT);
        break;
    default:
        break;
    }
}

void HelloWorld::map() {
    XMapWindow(_display, _window);
}

void HelloWorld::handleKeyPress(const XKeyEvent& event) {
    unsigned int keyCode_r = XKeysymToKeycode(_display, XK_r);
    unsigned int keyCode_q = XKeysymToKeycode(_display, XK_q);

    if(event.keycode == keyCode_r) {
        _game.restart();
        draw();
    } else if(event.keycode == keyCode_q) {
        // Quit, somehow
    }
}

void HelloWorld::handleMousePress(const XButtonEvent& event) {
    if(event.button == 1) {
        Window rootWindow;
        int winX, winY;
        unsigned int width, height, borderWidth, bitDepth;
        XGetGeometry(_display, _window, &rootWindow, &winX, &winY, &width,
                &height, &borderWidth, &bitDepth);

        if((event.y >= 2 * STRING_HEIGHT) &&
                (event.y < (height - STRING_HEIGHT))) {
            int xStepSize = width / 3;
            int yStepSize = (height - STRING_HEIGHT * 3) / 3;

            int cellX = event.x / xStepSize;
            int cellY = (event.y - STRING_HEIGHT * 2) / yStepSize;

            if(_game.makeMove(cellX, cellY)) {
                if(_game.getGameState() == O_TURN) {
                    _game.makeRandomMove();
                }
                draw();
            }
        }
    }
}

void HelloWorld::restartGame() {
    _game.restart();
    draw();
}


void HelloWorld::_drawO(const GC& gc, int x, int y, int w, int h) {
    XSetBackground(_display, gc, _whiteColor);
    XSetForeground(_display, gc, _blackColor);
    XFillArc(_display, _window, gc, x + w/10, y + h/10, (w*4)/5, (h*4)/5, 0,
            360*64);

    XSetForeground(_display, gc, _whiteColor);
    XFillArc(_display, _window, gc, x + w/5, y + h/5, (w*3)/5, (h*3)/5, 0,
            360*64);
}

void HelloWorld::_drawX(const GC& gc, int x, int y, int w, int h) {
    static const int POINT_COUNT = 12;
    static const XPoint RAW_CROSS[] = { {1, 2}, {2, 1}, {5, 4}, {8, 1}, {9, 2},
            {6, 5}, {9, 8}, {8, 9}, {5, 6}, {2, 9}, {1, 8}, {4, 5} };

    XPoint scaledCross[POINT_COUNT];
    for(int i = 0; i < POINT_COUNT; i++) {
        scaledCross[i].x = (RAW_CROSS[i].x * w) / 10 + x;
        scaledCross[i].y = (RAW_CROSS[i].y * h) / 10 + y;
    }

    XSetBackground(_display, gc, _whiteColor);
    XSetForeground(_display, gc, _blackColor);
    XFillPolygon(_display, _window, gc, scaledCross, POINT_COUNT, Nonconvex,
            CoordModeOrigin);
}


void HelloWorld::_drawString(const GC& gc, const char* str, int x, int y) {
    XDrawString(_display, _window, gc, x, y, str, strlen(str));
}

void HelloWorld::_drawStringCentered(const GC& gc, const char* str, int x, int y,
                                     int w, int h) {
    int direction, ascent, descent;
    XCharStruct strDimensions;
    XTextExtents(XQueryFont(_display, XGContextFromGC(gc)), str, strlen(str),
            &direction, &ascent, &descent, &strDimensions);

    int newX = x + (w - strDimensions.width) / 2;
    int newY = y + (h + strDimensions.ascent - strDimensions.descent) / 2;
    _drawString(gc, str, newX, newY);
}

int main() {
    Display* display = XOpenDisplay(NULL);
    if(!display) {
        cerr << "Unable to connect to X server." << endl;
        exit(1);
    }

    HelloWorld mainWindow(display);
    mainWindow.map();

    XFlush(display);

    XEvent event;
    while(true) {
        XNextEvent(display, &event);

        switch(event.type) {
        case ButtonPress:
            mainWindow.handleMousePress((XButtonEvent)event.xbutton);
        case Expose:
            if(event.xexpose.count == 0) {
                mainWindow.draw();
            }
            break;
        case KeyPress:
            mainWindow.handleKeyPress((XKeyEvent)event.xkey);
        default:
            break;
        }
    }

    XCloseDisplay(display);
    return 0;
}

 

posted @ 2016-07-26 15:51  20118281131  阅读(289)  评论(0编辑  收藏  举报