tags:

views:

38

answers:

1

Note the comment below. It cannot compiled on UVA because of a bug in GCC.

#include <cstdio>
#include <cstring>
#include <cctype>
#include <map>
#include <stdexcept>

class Board {
public:
    bool read(FILE *);
    enum Colour {none, white, black};
    Colour check() const;
private:
    struct Index {
            size_t x;
            size_t y;
            Index &operator+=(const Index &) throw(std::range_error);
            Index operator+(const Index &) const throw(std::range_error);
    };
    const static std::size_t size = 8;
    char data[size][size];
    // Cannot be compiled on GCC 4.1.2 due to GCC bug 29993
    // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29993
    typedef bool CheckFunction(Colour, const Index &) const;
    CheckFunction pawn, knight, bishop, king, rook;

    bool queen(const Colour c, const Index &location) const {
            return rook(c, location) || bishop(c, location);
    }

    static char get_king(Colour c) {
            return c == white ? 'k' : 'K';
    }

    template<std::size_t n>
    bool check_consecutive(Colour c, const Index &location, const Index (&offsets)[n]) const {
            for(const Index *p = offsets; p != (&offsets)[1]; ++p) {
                    try {
                            Index target = location + *p;
                            for(; data[target.x][target.y] == '.'; target += *p) {
                            }
                            if(data[target.x][target.y] == get_king(c)) return true;
                    } catch(std::range_error &) {
                    }
            }
            return false;
    }

    template<std::size_t n>
    bool check_distinct(Colour c, const Index &location, const Index (&offsets)[n]) const {
            for(const Index *p = offsets; p != (&offsets)[1]; ++p) {
                    try {
                            Index target = location + *p;
                            if(data[target.x][target.y] == get_king(c)) return true;
                    } catch(std::range_error &) {
                    }
            }
            return false;
    }
};

int main() {
    Board board;
    for(int d = 1; board.read(stdin); ++d) {
            Board::Colour c = board.check();
            const char *sp;
            switch(c) {
            case Board::black:
                    sp = "white";
                    break;
            case Board::white:
                    sp = "black";
                    break;
            case Board::none:
                    sp = "no";
                    break;
            }
            std::printf("Game #%d: %s king is in check.\n", d, sp);
            std::getchar(); // discard empty line
    }
}

bool Board::read(FILE *f) {
    static const char empty[] =
            "........"
            "........"
            "........"
            "........"
            "........"
            "........"
            "........"
            "........";     // 64 dots
    for(char (*p)[size] = data; p != (&data)[1]; ++p) {
            std::fread(*p, size, 1, f);
            std::fgetc(f);  // discard new-line
    }
    return std::memcmp(empty, data, sizeof data);
}

Board::Colour Board::check() const {
    std::map<char, CheckFunction Board::*> fp;
    fp['P'] = &Board::pawn;
    fp['N'] = &Board::knight;
    fp['B'] = &Board::bishop;
    fp['Q'] = &Board::queen;
    fp['K'] = &Board::king;
    fp['R'] = &Board::rook;
    for(std::size_t i = 0; i != size; ++i) {
            for(std::size_t j = 0; j != size; ++j) {
                    CheckFunction Board::* p = fp[std::toupper(data[i][j])];
                    if(p) {
                            Colour ret;
                            if(std::isupper(data[i][j])) ret = white;
                            else ret = black;
                            if((this->*p)(ret, (Index){i, j}/* C99 extension */)) return ret;
                    }
            }
    }
    return none;
}

bool Board::pawn(const Colour c, const Index &location) const {
    const std::ptrdiff_t sh = c == white ? -1 : 1;
    const Index offsets[] = {
            {sh, 1},
            {sh, -1}
    };
    return check_distinct(c, location, offsets);
}

bool Board::knight(const Colour c, const Index &location) const {
    static const Index offsets[] = {
            {1, 2},
            {2, 1},
            {2, -1},
            {1, -2},
            {-1, -2},
            {-2, -1},
            {-2, 1},
            {-1, 2}
    };
    return check_distinct(c, location, offsets);
}

bool Board::bishop(const Colour c, const Index &location) const {
    static const Index offsets[] = {
            {1, 1},
            {1, -1},
            {-1, -1},
            {-1, 1}
    };
    return check_consecutive(c, location, offsets);
}

bool Board::rook(const Colour c, const Index &location) const {
    static const Index offsets[] = {
            {1, 0},
            {0, -1},
            {0, 1},
            {-1, 0}
    };
    return check_consecutive(c, location, offsets);
}

bool Board::king(const Colour c, const Index &location) const {
    static const Index offsets[] = {
            {-1, -1},
            {-1, 0},
            {-1, 1},
            {0, 1},
            {1, 1},
            {1, 0},
            {1, -1},
            {0, -1}
    };
    return check_distinct(c, location, offsets);
}

Board::Index &Board::Index::operator+=(const Index &rhs) throw(std::range_error) {
    if(x + rhs.x >= size || y + rhs.y >= size) throw std::range_error("result is larger than size");
    x += rhs.x;
    y += rhs.y;
    return *this;
}

Board::Index Board::Index::operator+(const Index &rhs) const throw(std::range_error) {
    Index ret = *this;
    return ret += rhs;
}
+1  A: 
Michael Aaron Safyan
Thanks, but the declarations in Board::check() const using CheckFunction becomes much uglier than before.
Michael Tsang