Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "testlib.h"
- #include <bits/stdc++.h>
- using namespace std;
- typedef pair<int, int> ii;
- typedef long long ll;
- typedef long double ld;
- #define sz(v) ((int)((v).size()))
- #define all(v) (v).begin(),(v).end()
- namespace Chess {
- const char WHITE_KING = 'K';
- const char WHITE_QUEEN = 'Q';
- const char BLACK_KING = 'X';
- const vector<int> dx8 = {-1, -1, -1, 0, 0, 1, 1, 1};
- const vector<int> dy8 = {-1, 0, 1, -1, 1, -1, 0, 1};
- struct Cell {
- int x, y;
- bool operator == (const Cell& o) const {
- return x == o.x && y == o.y;
- }
- bool operator != (const Cell& o) const {
- return x != o.x || y != o.y;
- }
- bool operator < (const Cell& o) const {
- return x < o.x || (x == o.x && y < o.y);
- }
- };
- struct Move {
- Cell from, to;
- };
- struct Board {
- Cell wkPos, wqPos, bkPos;
- bool operator == (const Board& o) const {
- return wkPos == o.wkPos && wqPos == o.wqPos && bkPos == o.bkPos;
- }
- bool operator < (const Board& o) const {
- return (wkPos < o.wkPos) ||
- (wkPos == o.wkPos && wqPos < o.wqPos) ||
- (wkPos == o.wkPos && wqPos == o.wqPos && bkPos < o.bkPos);
- }
- };
- struct BoardWithMoveNumber {
- int n;
- Board b;
- bool operator == (const BoardWithMoveNumber& o) const {
- return n == o.n && b == o.b;
- }
- bool operator < (const BoardWithMoveNumber& o) const {
- return n < o.n || (n == o.n && b < o.b);
- }
- };
- bool inRange(const int x, const int y) {
- return 0 <= x && x < 8 && 0 <= y && y < 8;
- }
- Cell parseCell(const string& s) {
- assert(sz(s) == 2);
- int x = s[0] - 'a';
- int y = s[1] - '1';
- assert(inRange(x, y));
- return Cell{x, y};
- }
- Move parseMove(const string& s) {
- assert(sz(s) == 4);
- Cell from = parseCell(s.substr(0, 2));
- Cell to = parseCell(s.substr(2, 2));
- return Move{from, to};
- }
- vector<string> writeBoard(const Board& b) {
- vector<string> s(8, string(8, '.'));
- s[b.wkPos.x][b.wkPos.y] = 'K';
- s[b.wqPos.x][b.wqPos.y] = 'Q';
- s[b.bkPos.x][b.bkPos.y] = 'X';
- return s;
- }
- string writeCell(const Cell& c) {
- string res;
- res += char(c.x + 'a');
- res += char(c.y + '1');
- return res;
- }
- vector<BoardWithMoveNumber> restoreGame(BoardWithMoveNumber b, const map<BoardWithMoveNumber, BoardWithMoveNumber>& parentBoards) {
- vector<BoardWithMoveNumber> v;
- while (true) {
- v.push_back(b);
- auto it = parentBoards.find(b);
- assert(it != parentBoards.end());
- const BoardWithMoveNumber& newB = it->second;
- if (newB == b) break;
- b = newB;
- }
- reverse(all(v));
- return v;
- }
- void writeSingleBoard(const Board& b) {
- cerr << "K" << writeCell(b.wkPos) << " Q" << writeCell(b.wqPos) << " / K" << writeCell(b.bkPos);
- }
- string getMove(const Board& b1, const Board& b2) {
- string res;
- int eq = 0;
- if (b1.wkPos == b2.wkPos) eq++; else res = "K" + writeCell(b1.wkPos) + "-" + writeCell(b2.wkPos);
- if (b1.wqPos == b2.wqPos) eq++; else res = "Q" + writeCell(b1.wqPos) + "-" + writeCell(b2.wqPos);
- if (b1.bkPos == b2.bkPos) eq++; else res = "K" + writeCell(b1.bkPos) + "-" + writeCell(b2.bkPos);
- assert(eq == 2);
- assert(!res.empty());
- return res;
- }
- vector<string> convertToMoves(const vector<BoardWithMoveNumber>& v) {
- vector<string> moves;
- for (int i = 1; i < sz(v); i++) {
- assert(v[i - 1].n + 1 == v[i].n);
- const string move = getMove(v[i - 1].b, v[i].b);
- moves.push_back(move);
- }
- return moves;
- }
- void writeSequence(const BoardWithMoveNumber& last, const map<BoardWithMoveNumber, BoardWithMoveNumber>& parentBoards) {
- vector<BoardWithMoveNumber> v = restoreGame(last, parentBoards);
- vector<string> moves = convertToMoves(v);
- cerr << "Number of moves = " << (sz(moves) + 1) / 2 << endl;
- for (int i = 0, moveId = 1; i < sz(moves); i += 2, moveId++) {
- cerr << moveId << ". ";
- cerr << moves[i];
- if (i + 1 < sz(moves)) {
- cerr << " " << moves[i + 1];
- }
- cerr << endl;
- }
- cerr << endl;
- }
- bool isValidBlackKingMove(const int tox, const int toy, const Board& b) {
- if (!inRange(tox, toy)) return false;
- const Cell& wkPos = b.wkPos;
- const Cell& wqPos = b.wqPos;
- const Cell& bkPos = b.bkPos;
- // check self position
- if (Cell{tox, toy} == bkPos) return false;
- if (abs(bkPos.x - tox) > 1) return false;
- if (abs(bkPos.y - toy) > 1) return false;
- // check white king
- for (int i = -1; i <= 1; i++) {
- for (int j = -1; j <= 1; j++) {
- if (Cell{wkPos.x + i, wkPos.y + j} == Cell{tox, toy}) return false;
- }
- }
- // check white queen
- for (int dir = 0; dir < 8; dir++) {
- for (int len = 1; true; len++) {
- int qx = wqPos.x + len * dx8[dir];
- int qy = wqPos.y + len * dy8[dir];
- if (!inRange(qx, qy)) break;
- if (Cell{qx, qy} == wkPos) break;
- if (Cell{qx, qy} == Cell{tox, toy}) return false;
- }
- }
- return true;
- }
- bool isQueenCaptureMove(const int tox, const int toy, const Board& b) {
- return isValidBlackKingMove(tox, toy, b) && Cell{tox, toy} == b.wqPos;
- }
- bool isValidWhiteKingMove(const int tox, const int toy, const Board& b) {
- if (!inRange(tox, toy)) return false;
- const Cell& wkPos = b.wkPos;
- const Cell& wqPos = b.wqPos;
- const Cell& bkPos = b.bkPos;
- // check self position
- if (Cell{tox, toy} == wkPos) return false;
- if (abs(wkPos.x - tox) > 1) return false;
- if (abs(wkPos.y - toy) > 1) return false;
- // check black king
- for (int i = -1; i <= 1; i++) {
- for (int j = -1; j <= 1; j++) {
- if (Cell{bkPos.x + i, bkPos.y + j} == Cell{tox, toy}) return false;
- }
- }
- // check self queen
- if (Cell{tox, toy} == wqPos) return false;
- return true;
- }
- bool isValidWhiteQueenMove(const int tox, const int toy, const Board& b) {
- if (!inRange(tox, toy)) return false;
- const Cell& wkPos = b.wkPos;
- const Cell& wqPos = b.wqPos;
- const Cell& bkPos = b.bkPos;
- // check self position
- if (Cell{tox, toy} == wqPos) return false;
- // check directions
- bool ok = false;
- for (int dir = 0; dir < 8; dir++) {
- for (int len = 1; true; len++) {
- int qx = wqPos.x + len * dx8[dir];
- int qy = wqPos.y + len * dy8[dir];
- if (!inRange(qx, qy)) break;
- if (Cell{qx, qy} == wkPos) break; // self king blocks the path
- if (Cell{qx, qy} == bkPos) assert(false); // can capture black king - can't happen
- if (Cell{qx, qy} == Cell{tox, toy}) ok = true; // ok, but don't return - check assert in all directions
- }
- }
- return ok;
- }
- bool blackKingHasNoMoves(const Board& b) {
- const Cell& bkPos = b.bkPos;
- for (int dir = 0; dir < 8; dir++) {
- int tox = bkPos.x + dx8[dir];
- int toy = bkPos.y + dy8[dir];
- if (isValidBlackKingMove(tox, toy, b)) return false;
- }
- return true;
- }
- bool blackKingUnderCheck(const Board& b) {
- const Cell& wkPos = b.wkPos;
- const Cell& wqPos = b.wqPos;
- const Cell& bkPos = b.bkPos;
- for (int dir = 0; dir < 8; dir++) {
- for (int len = 1; true; len++) {
- int qx = wqPos.x + len * dx8[dir];
- int qy = wqPos.y + len * dy8[dir];
- if (!inRange(qx, qy)) break;
- if (Cell{qx, qy} == wkPos) break;
- if (Cell{qx, qy} == bkPos) return true;
- }
- }
- return false;
- }
- bool isCheckmate(const Board& b) {
- return blackKingHasNoMoves(b) && blackKingUnderCheck(b);
- }
- bool isStalemate(const Board& b) {
- return blackKingHasNoMoves(b) && !blackKingUnderCheck(b);
- }
- bool isValidWhiteMove(const int fromx, const int fromy, const int tox, const int toy, const Board& b) {
- if (Cell{fromx, fromy} == b.wkPos) return isValidWhiteKingMove(tox, toy, b);
- if (Cell{fromx, fromy} == b.wqPos) return isValidWhiteQueenMove(tox, toy, b);
- return false;
- }
- void makeMove(const int fromx, const int fromy, const int tox, const int toy, Board& b) {
- if (Cell{fromx, fromy} == b.wkPos) {
- assert(isValidWhiteKingMove(tox, toy, b));
- b.wkPos = Cell{tox, toy};
- return;
- }
- if (Cell{fromx, fromy} == b.wqPos) {
- assert(isValidWhiteQueenMove(tox, toy, b));
- b.wqPos = Cell{tox, toy};
- return;
- }
- if (Cell{fromx, fromy} == b.bkPos) {
- assert(isValidBlackKingMove(tox, toy, b));
- assert(!isQueenCaptureMove(tox, toy, b));
- b.bkPos = Cell{tox, toy};
- return;
- }
- assert(false);
- }
- // (error, max number of moves)
- pair<string, int> checkSequence(const vector<string>& inputSequence, const string& wkStartPosStr, const string& wqStartPosStr) {
- //cerr << "sz(inputSequence) == " << sz(inputSequence) << endl;
- vector<Move> moves;
- for (const string& moveStr : inputSequence) {
- moves.push_back(parseMove(moveStr));
- }
- map<BoardWithMoveNumber, BoardWithMoveNumber> parentBoards;
- const Cell wkStartPos = parseCell(wkStartPosStr);
- const Cell wqStartPos = parseCell(wqStartPosStr);
- assert(wkStartPos != wqStartPos);
- set<BoardWithMoveNumber> currentBoards;
- // top-right corner
- for (int bkx = wqStartPos.x + 1; bkx <= 7; bkx++) {
- for (int bky = wqStartPos.y + 1; bky <= 7; bky++) {
- Cell bkStartPos{bkx, bky};
- if (max(abs(bkStartPos.x - wkStartPos.x), abs(bkStartPos.y - wkStartPos.y)) <= 1) {
- // kings are too close
- continue;
- }
- Board b;
- b.wkPos = wkStartPos;
- b.wqPos = wqStartPos;
- b.bkPos = bkStartPos;
- if (blackKingUnderCheck(b)) {
- continue;
- }
- //cerr << "Will add start black position: " << writeCell(bkStartPos) << endl;
- currentBoards.insert({1, b});
- parentBoards[{1, b}] = {1, b};
- }
- }
- assert(!currentBoards.empty());
- set<BoardWithMoveNumber> checkmates;
- for (const Move& move : moves) {
- /*cerr << "Before move " << writeCell(move.from) << "-" << writeCell(move.to) << endl;
- cerr << "Number of boards = " << sz(currentBoards) << endl;
- for (const BoardWithMoveNumber& bb : currentBoards) {
- writeSingleBoard(bb.b);
- cerr << endl;
- }
- cerr << "=======================" << endl;*/
- set<BoardWithMoveNumber> newBoards;
- for (const BoardWithMoveNumber& bb : currentBoards) {
- const Board& b = bb.b;
- const int n = bb.n;
- if (isValidWhiteMove(move.from.x, move.from.y, move.to.x, move.to.y, b)) {
- Board b2 = b;
- makeMove(move.from.x, move.from.y, move.to.x, move.to.y, b2);
- parentBoards[{n + 1, b2}] = {n, b};
- if (isCheckmate(b2)) {
- checkmates.insert({n + 1, b2});
- continue;
- }
- if (isStalemate(b2)) {
- writeSequence({n + 1, b2}, parentBoards);
- //cerr << "Stalemate" << endl;
- return {"Stalemate", -1};
- }
- const Cell& bkPos = b2.bkPos;
- for (int dir = 0; dir < 8; dir++) {
- int tox = bkPos.x + dx8[dir];
- int toy = bkPos.y + dy8[dir];
- if (isQueenCaptureMove(tox, toy, b2)) {
- writeSequence({n + 1, b2}, parentBoards);
- //cerr << "Queen is captured" << endl;
- return {"Queen is captured", -1};
- }
- if (isValidBlackKingMove(tox, toy, b2)) {
- Board b3 = b2;
- makeMove(bkPos.x, bkPos.y, tox, toy, b3);
- parentBoards[{n + 2, b3}] = {n + 1, b2};
- newBoards.insert({n + 2, b3});
- }
- }
- } else {
- newBoards.insert(bb);
- }
- }
- currentBoards = newBoards;
- }
- if (checkmates.empty()) {
- //cerr << "Couldn't give even a single checkmate" << endl;
- return {"Couldn't give even a single checkmate", -1};
- }
- if (!currentBoards.empty()) {
- //cerr << "Couldn't give checkmate in some situations" << endl;
- return {"Couldn't give checkmate in some situations", -1};
- }
- vector<int> numbersOfMoves;
- for (const BoardWithMoveNumber& bb : checkmates) {
- numbersOfMoves.push_back(bb.n / 2);
- }
- const int maxNumberOfMoves = *max_element(all(numbersOfMoves));
- if (maxNumberOfMoves > 50) {
- return {"Max number of moves is greater than 50", maxNumberOfMoves};
- }
- /*cerr << "Success!" << endl;
- cerr << "Numbers of moves: ";
- for (const int x : numbersOfMoves) cerr << x << " ";
- cerr << endl;
- cerr << "Gave " << sz(checkmates) << " checkmates" << endl;
- for (const BoardWithMoveNumber& bb : checkmates) {
- writeSequence(bb, parentBoards);
- }*/
- return {"", maxNumberOfMoves}; // ok
- }
- }
- vector<string> readSequence(InStream& stream, TResult waResult) {
- vector<string> inputSequence;
- while (!stream.seekEof()) {
- const string token = stream.readToken();
- if (sz(token) != 4) stream.quitf(waResult, "Wrong move string length: %d", sz(token));
- if (token[0] < 'a' || token[0] > 'h') stream.quitf(waResult, "Wrong 0-th character: '%c'", token[0]);
- if (token[1] < '1' || token[1] > '8') stream.quitf(waResult, "Wrong 1-st character: '%c'", token[1]);
- if (token[2] < 'a' || token[2] > 'h') stream.quitf(waResult, "Wrong 2-nd character: '%c'", token[2]);
- if (token[3] < '1' || token[3] > '8') stream.quitf(waResult, "Wrong 3-rd character: '%c'", token[3]);
- if (token[0] == token[2] && token[1] == token[3]) stream.quitf(waResult, "Wrong move: start and finish cells are equal");
- inputSequence.push_back(token);
- if (sz(inputSequence) > 500) {
- stream.quitf(waResult, "Too many moves in the sequence");
- }
- }
- if (inputSequence.empty()) {
- stream.quitf(waResult, "Sequence is empty");
- }
- return inputSequence;
- }
- vector<string> getAlexInputSequence() {
- return {
- // Kc3, Qd4
- "c3d3", "d3e3", "e3f3", "f3g3",
- // Kg3, Qd4
- "g3g4", "g3h4", "h4g4", "d4c5", "g4g3", "h4g3",
- // Kg3, Qc5
- "g3f3", "f3e3", "e3d3", "d3c4", "c4b5", "b5b6", "b6b7",
- // Kb7, Qc5
- "b7c6", "b7b6", "b6c6",
- // Kc6, Qc5, black is either e+ or {c8 d8}
- "c5d5", "c6c7", "d5a5", "a5c7", "c7h7", "h7b7",
- // Kc7, Qa5 (or already mated)
- "c7c6", "a5d5",
- // Kc6, Qd5
- "c6c5", "c5d4", "d4e4", "e4f4", "f4g4",
- // Kg4, Qd5
- "g4g5", "g4h5", "h5g5", "d5c6", "g5g4", "h5g4",
- // Kg4, Qc6
- "g4f4", "f4e4", "e4d4", "d4c5", "c5b6", "b6b7",
- // Kb7, Qc6
- "b7c7", "b7b8", "b8c7", "c6d6",
- // Kc7, Qd6
- "c7d7", "c7c8", "c8d7", "d6e6",
- // Kd7, Qe6
- "d7e7", "d7d8", "d8e7",
- // Ke7, Qe6
- "e6g4", "e7f7", "g4h4",
- };
- }
- vector<string> getSergeyInputSequence() {
- return {
- // Move king to h4
- "c3d3", "d3e3", "e3f3", "f3g3", "g3h4",
- // If black king is on g5 or h5, place queen to h4, then to e7 and give checkmate
- "d4h4", "h4e7", "g3g4", "g3f3", "f3g4", "g4f5", "g4f4", "f4f5", "e7g5", "f5f6", "g5g7",
- // Our king is on h4, go to f5, then make triangle f6-g6-f5 and try to place queen to f6
- "h4g4", "h4h5", "h5g4", "g4f5", "g4f4", "f4f5", "f5f6", "f6g6", "g6f5", "d4f6",
- // If we succeeded in placing queen is on f6, white king is on f5, and black king is in e7..h8 area
- // Move queen to b6, king to b7 and give easy checkmate
- "f6b6", "f5e5", "e5d5", "d5c5", "c5b5", "b5a6", "a6b7", "a6a7", "a7b7",
- "b6c6", "b7c7", "b7b8", "b8c7",
- "c6d6", "c7d7", "c7c8", "c8d7",
- "d6e6", "d7e7", "d7d8", "d8e7",
- "e6g4", "e7f7", "g4h4",
- // If we couldn't place queen on f6, white king is on f6, and black king is on h5, h6, h7
- // Place queen to g file and give checkmate
- "d4g1", "g1g2", "f6f5", "g2g4", "g4g5", "f5f6", "g5g7"
- };
- }
- int main(int argc, char* argv[]) {
- /*
- const vector<string> inputSequence = getSergeyInputSequence();
- cerr << "sz = " << sz(inputSequence) << endl;
- const string error = Chess::checkSequence(inputSequence, "c3", "d4");
- cerr << "Error: " << error << endl;
- */
- registerTestlibCmd(argc, argv);
- const string wkStartPosStr = inf.readToken();
- const string wqStartPosStr = inf.readToken();
- assert(
- ((wkStartPosStr == "c3") && (wqStartPosStr == "d4"))
- ||
- ((wkStartPosStr == "f6") && (wqStartPosStr == "e5"))
- );
- const vector<string> jInputSequence = readSequence(ans, _fail);
- const pair<string, int> jError = Chess::checkSequence(jInputSequence, wkStartPosStr, wqStartPosStr);
- if (!jError.first.empty()) {
- quitf(_fail, "Jury solution is wrong: %s", jError.first.c_str());
- }
- assert(jError.second > 0);
- const vector<string> pInputSequence = readSequence(ouf, _wa);
- const pair<string, int> pError = Chess::checkSequence(pInputSequence, wkStartPosStr, wqStartPosStr);
- if (!pError.first.empty()) {
- quitf(_wa, "Participant solution is wrong: %s", pError.first.c_str());
- }
- assert(pError.second > 0);
- quitf(_ok, "Correct solution, length %d, max number of moves %d", sz(pInputSequence), pError.second);
- }
Advertisement
Add Comment
Please, Sign In to add comment