chess-move-interface/src/PGN.cpp

286 lines
6.4 KiB
C++
Raw Normal View History

2022-01-23 20:57:28 +01:00
#include "pgnp.hpp"
#include <iostream>
2022-01-24 18:32:05 +01:00
#include <string>
2022-01-23 20:57:28 +01:00
2022-01-24 15:29:22 +01:00
#define IS_BLANK(c) (c == ' ' || c == '\n' || c == '\t')
#define IS_DIGIT(c) \
(c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || \
c == '6' || c == '7' || c == '8' || c == '9')
#define IS_EOF(loc) (loc >= pgn_content.size())
#define EOF_CHECK(loc) \
{ \
if (IS_EOF(loc)) \
throw UnexpectedEOF(); \
}
2022-01-23 20:57:28 +01:00
namespace pgnp {
2022-01-24 15:29:22 +01:00
PGN::~PGN() {
if (moves != NULL)
delete moves;
}
2022-01-24 18:32:05 +01:00
std::string PGN::GetResult() { return (result); }
2022-01-24 15:29:22 +01:00
void PGN::FromFile(std::string filepath) {
std::ifstream file(filepath);
std::string content((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
FromString(content);
}
void PGN::FromString(std::string pgn_content) {
this->pgn_content = pgn_content;
2022-01-25 14:53:34 +01:00
moves = new HalfMove();
2022-01-24 15:29:22 +01:00
int loc = 0;
while (!IS_EOF(loc)) {
char c = pgn_content[loc];
if (!IS_BLANK(c)) {
if (c == '[') {
loc = ParseNextTag(loc);
} else if (IS_DIGIT(c)) {
2022-01-25 10:53:10 +01:00
loc = ParseHalfMove(loc, moves);
2022-01-24 15:29:22 +01:00
break;
2022-01-25 14:53:34 +01:00
} else if (c=='{') {
loc = ParseComment(loc,moves);
continue; // No need loc++
2022-01-23 20:57:28 +01:00
}
}
2022-01-24 15:29:22 +01:00
loc++;
}
2022-01-24 18:32:05 +01:00
if (result.size() <= 0) {
throw InvalidGameResult();
}
2022-01-24 15:29:22 +01:00
}
2022-01-23 20:57:28 +01:00
2022-01-24 15:29:22 +01:00
void PGN::STRCheck() {
int i = 0;
// Locate Event tag
while (i < tagkeys.size()) {
if (tagkeys[i] == "Event") {
break;
}
i++;
2022-01-23 20:57:28 +01:00
}
2022-01-24 15:29:22 +01:00
// Check tags
if (i + 6 < tagkeys.size()) {
bool valid = (tagkeys[i] == "Event") && (tagkeys[i + 1] == "Site") &&
(tagkeys[i + 2] == "Date") && (tagkeys[i + 3] == "Round") &&
(tagkeys[i + 4] == "White") && (tagkeys[i + 5] == "Black") &&
(tagkeys[i + 6] == "Result");
if (!valid) {
throw STRCheckFailed();
2022-01-23 20:57:28 +01:00
}
2022-01-24 15:29:22 +01:00
} else {
throw STRCheckFailed();
}
}
2022-01-23 20:57:28 +01:00
2022-01-24 15:29:22 +01:00
bool PGN::HasTag(std::string key) {
auto tags = GetTagList();
return (std::find(tags.begin(), tags.end(), key) != tags.end());
}
2022-01-25 14:53:34 +01:00
int PGN::ParseComment(int loc, HalfMove *hm) {
// Goto next char
loc = NextNonBlank(loc);
EOF_CHECK(loc);
char c = pgn_content[loc];
if(c=='{'){
loc++;
EOF_CHECK(loc);
c = pgn_content[loc];
while(c!='}'){
hm->comment+=c;
loc++;
EOF_CHECK(loc);
c = pgn_content[loc];
}
loc++; // Skip '}'
}
return(loc);
}
2022-01-25 10:53:10 +01:00
int PGN::ParseHalfMove(int loc, HalfMove *hm) {
2022-01-24 15:29:22 +01:00
// Goto next char
loc = NextNonBlank(loc);
EOF_CHECK(loc);
char c = pgn_content[loc];
// Check if we reach score entry (* or 1-0 or 0-1 or 1/2-1/2)
if (!IS_EOF(loc + 1)) {
char nc = pgn_content[loc + 1]; // Next c
2022-01-24 18:32:05 +01:00
if ((IS_DIGIT(c) && nc == '-') or (IS_DIGIT(c) && nc == '/') or c == '*') {
if (c == '*') {
result = "*";
} else if (nc == '-') {
if (c == '1') {
result = "1-0";
} else {
result = "0-1";
}
} else {
result = "1/2-1/2";
}
2022-01-24 15:29:22 +01:00
return (loc);
2022-01-23 20:57:28 +01:00
}
2022-01-24 15:29:22 +01:00
}
2022-01-23 20:57:28 +01:00
2022-01-24 15:29:22 +01:00
// Parse (move number
if (IS_DIGIT(c)) {
std::string move_nb;
while (IS_DIGIT(c)) {
move_nb += c;
loc++;
c = pgn_content[loc];
EOF_CHECK(loc);
}
hm->count = std::stoi(move_nb);
loc++;
EOF_CHECK(loc);
if (pgn_content[loc] == '.') {
hm->isBlack = true;
loc += 2; // Skip two dots
EOF_CHECK(loc);
}
} else {
hm->isBlack = true;
}
2022-01-25 15:23:54 +01:00
// Parse comment entries (various comment could appear during HalfMove parsing)
loc=ParseComment(loc,hm);
2022-01-24 15:29:22 +01:00
// Parse the HalfMove
loc = NextNonBlank(loc);
EOF_CHECK(loc);
c = pgn_content[loc];
std::string move;
while (!IS_BLANK(c) && c != ')') {
move += c;
loc++;
c = pgn_content[loc];
EOF_CHECK(loc);
}
hm->move = move;
2022-01-23 20:57:28 +01:00
2022-01-25 15:23:54 +01:00
// Parse comment
loc=ParseComment(loc,hm);
2022-01-24 15:29:22 +01:00
// Skip end of variation
if (c == ')') {
loc++;
return (loc);
2022-01-23 20:57:28 +01:00
}
2022-01-25 14:53:34 +01:00
// Parse comment
loc=ParseComment(loc,hm);
2022-01-24 15:29:22 +01:00
// Check for variations
loc = NextNonBlank(loc);
while (!IS_EOF(loc) && pgn_content[loc] == '(') {
loc++; // Skip '('
HalfMove *var = new HalfMove;
2022-01-25 10:53:10 +01:00
loc = ParseHalfMove(loc, var);
2022-01-24 15:29:22 +01:00
hm->variations.push_back(var);
loc++; // Skip ')'
}
2022-01-25 15:23:54 +01:00
// Parse comment
loc=ParseComment(loc,hm);
2022-01-24 15:29:22 +01:00
// Parse next HalfMove
loc = NextNonBlank(loc);
if (!IS_EOF(loc)) {
HalfMove *next_hm = new HalfMove;
next_hm->count = hm->count;
2022-01-25 10:53:10 +01:00
loc = ParseHalfMove(loc, next_hm);
2022-01-24 15:29:22 +01:00
// Check if move parsed successfuly
if (next_hm->move.size() > 0) {
hm->MainLine = next_hm;
} else {
delete next_hm;
}
}
return (loc);
}
int PGN::ParseNextTag(int start_loc) {
// Parse key
std::string key;
int keyloc = start_loc + 1;
EOF_CHECK(keyloc);
char c = pgn_content[keyloc];
while (!IS_BLANK(c)) {
key += c;
keyloc++;
EOF_CHECK(keyloc);
c = pgn_content[keyloc];
}
// Parse value
std::string value;
int valueloc = NextNonBlank(keyloc) + 1;
EOF_CHECK(keyloc);
c = pgn_content[valueloc];
while (c != '"' or IS_EOF(valueloc)) {
value += c;
valueloc++;
EOF_CHECK(keyloc);
c = pgn_content[valueloc];
}
// Add tag
tags[key] = value;
tagkeys.push_back(key);
2022-01-24 16:41:02 +01:00
EOF_CHECK(valueloc + 1);
c = pgn_content[valueloc + 1];
if (c != ']') {
throw UnexpectedCharacter(c, ']', valueloc + 1);
}
2022-01-24 15:29:22 +01:00
return (valueloc + 1); // +1 For the last char of the tag which is ']'
}
2022-01-25 10:53:10 +01:00
void PGN::GetMoves(HalfMove *copy) { moves->Copy(copy); }
2022-01-24 15:29:22 +01:00
std::vector<std::string> PGN::GetTagList() { return tagkeys; }
2022-01-24 16:41:02 +01:00
std::string PGN::GetTagValue(std::string key) {
if (tags.find(key) == tags.end()) {
throw InvalidTagName();
}
return tags[key];
}
2022-01-24 15:29:22 +01:00
2022-01-25 10:53:10 +01:00
std::string PGN::Dump() {
std::stringstream ss;
ss << "---------- PGN DUMP ----------" << std::endl;
ss << "Tags:" << std::endl;
2022-01-24 15:29:22 +01:00
for (auto &tag : GetTagList()) {
2022-01-25 10:53:10 +01:00
ss << " " << tag << "=" << GetTagValue(tag) << std::endl;
2022-01-24 15:29:22 +01:00
}
2022-01-25 10:53:10 +01:00
ss << "Moves:" << std::endl;
2022-01-24 15:29:22 +01:00
if (moves != NULL)
2022-01-25 10:53:10 +01:00
ss << moves->Dump();
return (ss.str());
2022-01-24 15:29:22 +01:00
}
int PGN::NextNonBlank(int loc) {
char c = pgn_content[loc];
while (IS_BLANK(c)) {
loc++;
if (IS_EOF(loc))
return (loc);
c = pgn_content[loc];
2022-01-23 20:57:28 +01:00
}
2022-01-24 15:29:22 +01:00
return (loc);
}
2022-01-23 20:57:28 +01:00
2022-01-24 15:29:22 +01:00
} // namespace pgnp