Source

python-ai / src / othello / Board.cpp

Full commit
#include "Board.hpp"
#include <sstream>

using std::stringstream;

Board::Board() {
    for(int32_t i = 0; i < 16; i++) {
        positions[i] = 0;
    }
    positions[6] = SND_PLAYER;
    positions[7] = FST_PLAYER << 6;
    positions[8] = FST_PLAYER;
    positions[9] = SND_PLAYER << 6;
}

BoardElem Board::get(const BoardIdx row, const BoardIdx col) const {
    int32_t elemNum = 2*row + col/4;
    int32_t shiftNum = 6 - 2*(col%4);
    uint8_t shiftedElem = (positions[elemNum] >> shiftNum);
    return shiftedElem & 0x03;
}

Board Board::extend(const BoardIdx row, const BoardIdx col,
                    const BoardPlayerNum player) const {
    int32_t elemNum = 2*row + col/4;
    int32_t shiftNum = 6 - 2*(col%4);
    Board next = Board(this);
    next.positions[elemNum] &= ~(0x03 << shiftNum);
    next.positions[elemNum] |= (player << shiftNum);
    return next;
}

Board Board::makeMove(const BoardIdx row, const BoardIdx col,
                      const BoardPlayerNum player) const {
    Board next = extend(row, col, player);
    for(int8_t rowDir = -1; rowDir <= 1; rowDir++) {
        for(int8_t colDir = -1; colDir <= 1; colDir++) {
            if((rowDir == 0) && (colDir == 0)) continue;
            if(sandwich(row, col, player, rowDir, colDir)) {
                BoardLocs changes = getSandwich(row, col, player, rowDir, colDir);
                BoardLocs::iterator it;
                for(it = changes.begin(); it != changes.end(); it++) {
                    BoardLoc loc = *it;
                    next = next.extend(loc, player);
                }
            }
        }
    }
    return next;
}

inline BoardLocs Board::validMoves(const BoardPlayerNum player) const {
    BoardLocs moves;
    for(int32_t i = 0; i < 8; i++) {
        for(int32_t j = 0; j < 8; j++) {
            if(validMove(i, j, player)) {
                moves.push_back(BoardLoc(i, j));
            }
        }
    }
    return moves;
}

inline bool Board::validMove(const BoardIdx row, const BoardIdx col,
                             const BoardPlayerNum player) const {
    return (get(row, col) == UNPICKED) &&
           (sandwich(row, col, player, -1,  0) ||
            sandwich(row, col, player,  1,  0) ||
            sandwich(row, col, player,  0, -1) ||
            sandwich(row, col, player,  0,  1) ||
            sandwich(row, col, player, -1, -1) ||
            sandwich(row, col, player, -1,  1) ||
            sandwich(row, col, player,  1, -1) ||
            sandwich(row, col, player,  1,  1));
}

inline bool Board::sandwich(const BoardIdx row, const BoardIdx col, const BoardPlayerNum player,
                            const int8_t rowDir, const int8_t colDir) const {
    if(valid(row+rowDir, col+colDir) && get(row+rowDir, col+colDir) == otherPlayer(player)) {
        for(int diff = 2; valid(row+rowDir*diff, col+colDir*diff); diff++) {
             BoardPlayerNum val = get(row+rowDir*diff, col+colDir*diff);
             if(val == player) return true;
             if(val == UNPICKED) break;
        }
    }
    return false;
}

inline BoardLocs Board::getSandwich(const BoardIdx row, const BoardIdx col, const BoardPlayerNum player,
                                    const int8_t rowDir, const int8_t colDir) const {
    BoardPlayerNum other = otherPlayer(player);
    BoardLocs locs;
    for(int diff = 1; valid(row+rowDir*diff, col+colDir*diff); diff++) {
         BoardPlayerNum val = get(row+rowDir*diff, col+colDir*diff);
         if(val == other) locs.push_back(BoardLoc(row+rowDir*diff, col+colDir*diff));
         else break;
    }
    return locs;
}

BoardScore Board::score(const BoardPlayerNum player) const {
    BoardScore counter = 0;
    for(int32_t i = 0; i < 8; i++) {
        for(int32_t j = 0; j < 8; j++) {
            if(get(i, j) == player) {
                ++counter;
            }
        }
    }
    return counter;
}

bool Board::complete() const {
    return (score(UNPICKED) == 0) ||
           ((validMoves(FST_PLAYER).size() == 0) &&
            (validMoves(SND_PLAYER).size() == 0));
           
}

BoardPlayerNum Board::winner() const {
    BoardScore counters[4] = {0, 0, 0, 0};
    for(int32_t i = 0; i < 8; i++) {
        for(int32_t j = 0; j < 8; j++) {
            ++(counters[get(i, j)]);
        }
    }
    if(counters[FST_PLAYER] == counters[SND_PLAYER]) {
        return UNPICKED;
    }
    return (counters[FST_PLAYER] > counters[SND_PLAYER]) ? FST_PLAYER : SND_PLAYER;
}

string Board::toString() const {
    char c[4] = {' ', 'B', '-', 'I'};
    string val;
    for(int32_t i = 0; i < 8; i++) {
        val += "+---+---+---+---+---+---+---+---+\n";
        for(int32_t j = 0; j < 8; j++) {
            val += "| ";
            val += c[get(i, j)];
            val += " ";
        }
        val += "|\n";
    }
    val += "+---+---+---+---+---+---+---+---+\n";
    return val;
}

bool Board::moveValid(const BoardIdx row, const BoardIdx col,
                      const BoardLocs validMoves) {
    BoardLocs::const_iterator it;
    for(it = validMoves.begin(); it != validMoves.end(); it++) {
        BoardLoc testMove = *it;
        if((testMove.get<0>() == row) && (testMove.get<1>() == col)) {
            return true;
        }
    }
    return false;
}

string Board::boardLocToString(const BoardLoc loc) {
    const BoardIdx row = loc.get<0>();
    const BoardIdx col = loc.get<1>();
    stringstream s;
    s << "(ROW " << ((int)row) << ", COL " << ((int)col) << ")";
    return s.str();
}

string Board::boardLocsToString(const BoardLocs locs) {
    string result = "[";
    BoardLocs::const_iterator it;
    for(it = locs.begin(); it != locs.end(); it++) {
        if(it != locs.begin()) result += ", ";
        result += boardLocToString(*it);
    }
    result += "]";
    return result;
}

Feature Board::featureCorners(const BoardPlayerNum player) const {
    int32_t corners = 0;
    for(int32_t i = 0; i <= 7; i += 7) {
        for(int32_t j = 0; j <= 7; j += 7) {
            if(get(i, j) == player) ++corners;
        }
    }
    return ((double)corners) / 4.0;
}

Feature Board::featureEdges(const BoardPlayerNum player) const {
    int32_t edges = 0;
    for(int32_t i = 0; i < 8; i++) {
        for(int32_t j = 0; j < 8; j++) {
            if(i == 0 || i == 7 || j == 0 || j == 7) {
                if(get(i, j) == player) ++edges;
            }
        }
    }
    return ((double)edges) / 28.0;
}