mirror of
https://gitlab.com/manzerbredes/pgnp.git
synced 2025-04-05 17:46:25 +02:00
Create project
This commit is contained in:
commit
fd78f92863
10 changed files with 21790 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
build
|
||||||
|
test
|
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
project(pgnp)
|
||||||
|
|
||||||
|
# Shared library
|
||||||
|
add_library(pgnp SHARED src/pgnp.cpp)
|
||||||
|
|
||||||
|
# Includes
|
||||||
|
set(PGNP_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/includes) # For conveniance
|
||||||
|
set(PGNP_INCLUDE_DIR ${PGNP_INCLUDE_DIR} PARENT_SCOPE) # To be used by other projects with add_subdirectory()
|
||||||
|
file(MAKE_DIRECTORY ${PGNP_INCLUDE_DIR})
|
||||||
|
configure_file(src/pgnp.hpp ${PGNP_INCLUDE_DIR} COPYONLY)
|
||||||
|
|
||||||
|
# Unit tests
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(./tests)
|
31
README.md
Normal file
31
README.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
## PGNP: PGN Parser
|
||||||
|
|
||||||
|
PGNP is a Portable Game Notation (PGN) parser. More details about the
|
||||||
|
PGN specification can be found [here](https://www.chessclub.com/help/PGN-spec).
|
||||||
|
|
||||||
|
# How to use it ?
|
||||||
|
PGNP can be used as a shared library in your project.
|
||||||
|
You only need to include the header file and linking the .so file to your
|
||||||
|
executable.
|
||||||
|
|
||||||
|
# Example
|
||||||
|
Load PGN from file:
|
||||||
|
|
||||||
|
pgnp::PGN pgn;
|
||||||
|
try {
|
||||||
|
pgn.FromFile("pgn.txt");
|
||||||
|
}
|
||||||
|
catch(...){
|
||||||
|
// Handle exceptions
|
||||||
|
}
|
||||||
|
Load PGN from string:
|
||||||
|
|
||||||
|
pgnp::PGN pgn;
|
||||||
|
try {
|
||||||
|
pgn.FromString("YOUR PGN CONTENT HERE");
|
||||||
|
}
|
||||||
|
catch(...){
|
||||||
|
// Handle exceptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: How to use the PGN object with the parsed data
|
81
src/pgnp.cpp
Normal file
81
src/pgnp.cpp
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
#include "pgnp.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#define IS_BLANK(c) (c==' ' || c=='\n' || c=='\t')
|
||||||
|
#define IS_EOF(loc) (loc>=pgn_content.size())
|
||||||
|
#define EOF_CHECK(loc) {if(IS_EOF(loc)) throw UnexpectedEOF();}
|
||||||
|
|
||||||
|
namespace pgnp {
|
||||||
|
|
||||||
|
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;
|
||||||
|
int loc=0;
|
||||||
|
while(!IS_EOF(loc)) {
|
||||||
|
char c=pgn_content[loc];
|
||||||
|
if(!IS_BLANK(c)){
|
||||||
|
switch (c) {
|
||||||
|
case '[':
|
||||||
|
loc=ParseNextTag(loc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*for (auto const& [key, val] : tags){
|
||||||
|
std::cout << key <<"="<<val<<std::endl;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
return(valueloc+1); // +1 For the last char of the tag which is ']'
|
||||||
|
}
|
||||||
|
|
||||||
|
int PGN::NextNonBlank(int loc){
|
||||||
|
char c=pgn_content[loc];
|
||||||
|
while(IS_BLANK(c)){
|
||||||
|
loc++;
|
||||||
|
c=pgn_content[loc];
|
||||||
|
}
|
||||||
|
return(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
src/pgnp.hpp
Normal file
46
src/pgnp.hpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <streambuf>
|
||||||
|
#include <iostream>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
namespace pgnp {
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct HalfMove {
|
||||||
|
|
||||||
|
} HalfMove;
|
||||||
|
|
||||||
|
|
||||||
|
class PGN {
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string,std::string> tags;
|
||||||
|
HalfMove moves;
|
||||||
|
std::string pgn_content;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void FromFile(std::string);
|
||||||
|
void FromString(std::string);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// @brief Populate @a tags with by parsing the one starting at location in argument
|
||||||
|
int ParseNextTag(int);
|
||||||
|
|
||||||
|
/// @brief Get the next non-blank char location starting from location in argument
|
||||||
|
int NextNonBlank(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct UnexpectedEOF : public std::exception
|
||||||
|
{
|
||||||
|
const char * what () const throw ()
|
||||||
|
{
|
||||||
|
return "Unexpected end of pgn file";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
8
tests/CMakeLists.txt
Normal file
8
tests/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
include_directories(./catch3/)
|
||||||
|
|
||||||
|
# Copy asset files
|
||||||
|
file(COPY pgn_files DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/)
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
add_executable(pgnp_tests tests.cpp ./catch3/catch_amalgamated.cpp)
|
||||||
|
add_test(PGNP_Tests pgnp_tests)
|
9929
tests/catch3/catch_amalgamated.cpp
Normal file
9929
tests/catch3/catch_amalgamated.cpp
Normal file
File diff suppressed because it is too large
Load diff
11652
tests/catch3/catch_amalgamated.hpp
Normal file
11652
tests/catch3/catch_amalgamated.hpp
Normal file
File diff suppressed because it is too large
Load diff
20
tests/pgn_files/valid/pgn1.pgn
Normal file
20
tests/pgn_files/valid/pgn1.pgn
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
[Event "Rated Rapid game"]
|
||||||
|
[Site "https://lichess.org/cMjillK3"]
|
||||||
|
[Date "2021.11.08"]
|
||||||
|
[White "UpSideGravity"]
|
||||||
|
[Black "manzerbredes"]
|
||||||
|
[Result "0-1"]
|
||||||
|
[UTCDate "2021.11.08"]
|
||||||
|
[UTCTime "11:06:47"]
|
||||||
|
[WhiteElo "1830"]
|
||||||
|
[BlackElo "1818"]
|
||||||
|
[WhiteRatingDiff "-45"]
|
||||||
|
[BlackRatingDiff "+13"]
|
||||||
|
[Variant "Standard"]
|
||||||
|
[TimeControl "600+5"]
|
||||||
|
[ECO "A00"]
|
||||||
|
[Opening "Hungarian Opening"]
|
||||||
|
[Termination "Normal"]
|
||||||
|
[Annotator "lichess.org"]
|
||||||
|
|
||||||
|
1. g3 { A00 Hungarian Opening } d5 2. Bg2 Nf6 3. c4 c6 4. Nf3 h6?! { (0.22 → 0.74) Inaccuracy. dxc4 was best. } (4... dxc4 5. O-O Nbd7 6. Qc2 Nb6 7. a4 a5 8. Na3 Be6 9. Ne5) 5. e3?! { (0.74 → -0.15) Inaccuracy. O-O was best. } (5. O-O Bf5 6. d3 e6 7. cxd5 cxd5 8. Qb3 Qb6 9. Nd4 Bg6) 5... Bf5 6. Nc3?! { (-0.15 → -0.75) Inaccuracy. d4 was best. } (6. d4 Nbd7 7. b3 e6 8. O-O Be7 9. Nc3 O-O 10. Bb2 a5 11. Qe2 Ne4 12. Nxe4 Bxe4) 6... e6 7. d4 Be7 8. Qe2 O-O 9. a3 a5 10. Bd2?! { (-0.33 → -0.84) Inaccuracy. b3 was best. } (10. b3 Re8) 10... Bc2 11. O-O Bb3 12. c5?? { (-0.22 → -3.09) Blunder. cxd5 was best. } (12. cxd5) 12... Bc4 13. Qd1 Bxf1 14. Bxf1 b6 15. cxb6 Qxb6 16. Na4 Qa7 17. Rc1 Rc8 18. Ne5 c5 19. dxc5 Bxc5 20. Bb5? { (-2.12 → -4.00) Mistake. Rc2 was best. } (20. Rc2 Nbd7) 20... Ne4?? { (-4.00 → -1.02) Blunder. Qb7 was best. } (20... Qb7 21. Be8) 21. Kg2?! { (-1.02 → -2.00) Inaccuracy. b4 was best. } (21. b4) 21... Qb7 22. f3?? { (-2.13 → -7.56) Blunder. Bd3 was best. } (22. Bd3 Ba7) 22... Nxd2 23. Bc6 Nxc6 24. Nxc5 Qxb2 25. Ned3 Qxa3 26. Qxd2 Nb4 27. Nb2 Rc7 28. Kh3 Rac8 29. Nba4 Na6 30. Rc3 Qa1 31. Rc1 Qf6 32. Qxa5 Nxc5 33. Nb6 Nb3 { White resigns. } 0-1
|
6
tests/tests.cpp
Normal file
6
tests/tests.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include <catch_amalgamated.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE( "DUMMY TEST", "[test]" ) {
|
||||||
|
REQUIRE( 1 == 1 );
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue