mirror of
https://gitlab.com/manzerbredes/ochess.git
synced 2025-07-01 09:47:39 +00:00
Improve openings database code
This commit is contained in:
parent
b7667d1d40
commit
3b11b9d4f3
19 changed files with 3709 additions and 28331 deletions
106
src/Openings.cpp
Normal file
106
src/Openings.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "Openings.hpp"
|
||||
#include <sstream>
|
||||
|
||||
/**
|
||||
* @brief Count the number of similar moves in between two lines
|
||||
*
|
||||
* @param m1
|
||||
* @param m2
|
||||
* @return int
|
||||
*/
|
||||
inline int CompareMainlines(const pgnp::HalfMove *m1,const pgnp::HalfMove *m2){
|
||||
int count=0;
|
||||
const pgnp::HalfMove *n1=m1;
|
||||
const pgnp::HalfMove *n2=m2;
|
||||
while(n1 && n2 != nullptr){
|
||||
if(n1->move == n2->move)
|
||||
count++;
|
||||
else
|
||||
break;
|
||||
n1=n1->MainLine;
|
||||
n2=n2->MainLine;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
Openings::Openings() {
|
||||
std::string tsv(a_tsv);
|
||||
LoadVolume(tsv,&A);
|
||||
tsv=std::string(b_tsv);
|
||||
LoadVolume(tsv,&B);
|
||||
tsv=std::string(c_tsv);
|
||||
LoadVolume(tsv,&C);
|
||||
tsv=std::string(d_tsv);
|
||||
LoadVolume(tsv,&D);
|
||||
tsv=std::string(e_tsv);
|
||||
LoadVolume(tsv,&E);
|
||||
}
|
||||
|
||||
void Openings::SearchOpening(const pgnp::HalfMove *moves,std::string &name, std::string &eco){
|
||||
// Now goto the right volume
|
||||
std::vector<Volume*> vols={&A,&B,&C,&D,&E};
|
||||
if(moves->GetLength()>=2){
|
||||
if(moves->move == "e4"){
|
||||
if(moves->MainLine->move == "e6"|| moves->MainLine->move == "e5")
|
||||
vols={&C};
|
||||
else
|
||||
vols={&B};
|
||||
}
|
||||
// TODO: Improve volumes filtering
|
||||
}
|
||||
// Then search for opening name:
|
||||
int matches=0;
|
||||
for(Volume *v: vols){
|
||||
for(auto &entry: *v){
|
||||
std::string &eco_=std::get<0>(entry);
|
||||
std::string &name_=std::get<1>(entry);
|
||||
std::string &pgn_=std::get<2>(entry);
|
||||
pgnp::HalfMove *moves_=new pgnp::HalfMove();
|
||||
pgnp::ParseSANMoves(pgn_,moves_);
|
||||
int nmatches=CompareMainlines(moves,moves_);
|
||||
if(nmatches>matches && nmatches == moves_->GetLength()){
|
||||
name=name_;
|
||||
eco=eco_;
|
||||
matches=nmatches;
|
||||
if(moves->GetLength()==moves_->GetLength()){
|
||||
delete moves_;
|
||||
return;
|
||||
}
|
||||
}
|
||||
delete moves_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Openings::GuessOpening(const std::string &SANMoves, std::string &name, std::string &eco){
|
||||
pgnp::HalfMove *m=new pgnp::HalfMove();
|
||||
pgnp::ParseSANMoves(SANMoves,m);
|
||||
SearchOpening(m,name,eco);
|
||||
delete m;
|
||||
}
|
||||
|
||||
void Openings::GuessOpening(const pgnp::HalfMove *moves, std::string &name, std::string &eco){
|
||||
SearchOpening(moves,name,eco);
|
||||
}
|
||||
|
||||
void Openings::LoadVolume(const std::string &tsv, Volume *vol){
|
||||
std::istringstream f(tsv);
|
||||
std::string line;
|
||||
while (std::getline(f, line)) {
|
||||
int eco=-1,name=-1,pgn=-1;
|
||||
for(int i=0;i<line.size();i++){
|
||||
if(line[i]=='\t'){
|
||||
if(eco == -1)
|
||||
eco=i-1;
|
||||
else if(name==-1)
|
||||
name=i-1;
|
||||
else
|
||||
pgn=i;
|
||||
}
|
||||
}
|
||||
vol->push_back(std::make_tuple(
|
||||
line.substr(0,eco+1),
|
||||
line.substr(eco+2,name-eco-1),
|
||||
line.substr(name+2,pgn-name)));
|
||||
}
|
||||
}
|
21
src/Openings.hpp
Normal file
21
src/Openings.hpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "binres/openings.hpp"
|
||||
#include "pgnp.hpp"
|
||||
|
||||
/**
|
||||
* @brief Guess the opening using the Lichess Opening Database
|
||||
* See: https://github.com/lichess-org/chess-openings
|
||||
*/
|
||||
class Openings {
|
||||
typedef std::vector<std::string> MoveList;
|
||||
typedef std::vector<std::tuple<std::string,std::string,std::string>> Volume;
|
||||
|
||||
Volume A,B,C,D,E;
|
||||
void SearchOpening(const pgnp::HalfMove *moves,std::string &name, std::string &eco);
|
||||
void LoadVolume(const std::string &tsv, Volume *vol);
|
||||
public:
|
||||
void GuessOpening(const std::string &SANMoves, std::string &name, std::string &eco);
|
||||
void GuessOpening(const pgnp::HalfMove *moves, std::string &name, std::string &eco);
|
||||
Openings();
|
||||
};
|
|
@ -23,4 +23,5 @@ wxBitmap LoadPNG(std::string icon) {
|
|||
return (wxBITMAP_PNG(mat));
|
||||
}
|
||||
return (wxNullBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,4 +10,5 @@
|
|||
#include "mat_png.hpp"
|
||||
|
||||
wxBitmap LoadPNG(std::string icon, wxSize size);
|
||||
wxBitmap LoadPNG(std::string icon);
|
||||
wxBitmap LoadPNG(std::string icon);
|
||||
|
||||
|
|
31692
src/binres/openings.hpp
31692
src/binres/openings.hpp
File diff suppressed because it is too large
Load diff
|
@ -131,6 +131,39 @@ bool Game::Play(std::string move,char promotion) {
|
|||
return (false);
|
||||
}
|
||||
|
||||
void Game::GetOpening(std::string &name,std::string &eco){
|
||||
HalfMove *m=current;
|
||||
if(m == nullptr)
|
||||
m=moves;
|
||||
if(m!=nullptr){
|
||||
// First check if opening already set
|
||||
std::string cname,ceco;
|
||||
m->GetOpening(cname,ceco);
|
||||
if(ceco.size()>0){
|
||||
name=cname;
|
||||
eco=ceco;
|
||||
}else {
|
||||
// If not, get the current move line (or first move)
|
||||
// and try to guess opening
|
||||
auto line=m->GetLine(); // Vector of HalfMove
|
||||
std::string pgn;
|
||||
int count=1;
|
||||
for(int i=0;i<line.size();i++){
|
||||
if(i%2==0){
|
||||
pgn+=std::to_string(count)+".";
|
||||
count+=1;
|
||||
}
|
||||
pgn+=line[i]->move +" ";
|
||||
}
|
||||
// If there is a line, try to guess the opening
|
||||
if(pgn.size()>0){
|
||||
wxGetApp().GetBook().GuessOpening(pgn,name,eco);
|
||||
m->SetOpening(name,eco);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Game::Previous() {
|
||||
if (current != nullptr) {
|
||||
current = current->GetParent();
|
||||
|
|
|
@ -35,6 +35,7 @@ public:
|
|||
HalfMove *GetMoves();
|
||||
std::string GetFen();
|
||||
std::string GetResult();
|
||||
void GetOpening(std::string &name,std::string &eco);
|
||||
/// @brief Play the given absolute move
|
||||
bool Play(std::string move,char promotion='q');
|
||||
bool IsBlackToPlay();
|
||||
|
|
|
@ -20,6 +20,38 @@ HalfMove::~HalfMove() {
|
|||
}
|
||||
}
|
||||
|
||||
void HalfMove::SetOpening(const std::string &name, const std::string &eco){
|
||||
HalfMove *m=this;
|
||||
while(m!=nullptr){
|
||||
m->opening=name;
|
||||
m->eco=eco;
|
||||
if(m->parent != nullptr && m->parent->mainline==m)
|
||||
m=m->parent;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HalfMove::GetOpening(std::string &name, std::string &eco){
|
||||
name=this->opening;
|
||||
eco=this->eco;
|
||||
}
|
||||
|
||||
std::vector<HalfMove *> HalfMove::GetLine(){
|
||||
std::vector<HalfMove *> line;
|
||||
HalfMove *m=this;
|
||||
while(m!=nullptr){
|
||||
line.push_back(m);
|
||||
// Check if in a variation:
|
||||
if(m->parent!=nullptr && m->parent->mainline!=m)
|
||||
m=m->parent->parent; // Because we are in a variation
|
||||
else
|
||||
m=m->parent;
|
||||
}
|
||||
std::reverse(line.begin(), line.end());
|
||||
return line;
|
||||
}
|
||||
|
||||
HalfMove::HalfMove(HalfMove *m){
|
||||
src=m->src;
|
||||
dst=m->dst;
|
||||
|
|
|
@ -20,6 +20,8 @@ class HalfMove : public cgeditor::CGEHalfMove {
|
|||
void BuildAndVerify(HalfMove *m, std::string fen);
|
||||
/// @brief Store the source and destination square of the current move (mainly used for pieces animation)
|
||||
std::string src,dst;
|
||||
/// @brief Opening reach by that move while taking into account all the parents
|
||||
std::string opening, eco;
|
||||
|
||||
public:
|
||||
HalfMove(HalfMove *m);
|
||||
|
@ -49,9 +51,13 @@ public:
|
|||
HalfMove *GetParent();
|
||||
HalfMove *GetMainline();
|
||||
std::vector<HalfMove *> GetVariations();
|
||||
|
||||
/// @brief Retrieve the list of moves from the current one to the first one
|
||||
std::vector<HalfMove *> GetLine();
|
||||
std::map<char, std::uint8_t> GetLineCaptures();
|
||||
|
||||
/// @brief The opening name of current line
|
||||
void SetOpening(const std::string &name, const std::string &eco);
|
||||
/// @brief Getters for name and eco
|
||||
void GetOpening(std::string &name, std::string &eco);
|
||||
/// @brief Set parent of the current move
|
||||
void SetParent(HalfMove *m);
|
||||
std::string GetFen();
|
||||
|
|
|
@ -81,7 +81,6 @@ void GameTabLeftPanel::OnPlay(wxCommandEvent &event) {
|
|||
ProcessEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
Notify(true); // Redraw event is move failed! Otherwise piece not resets to it initial position after dragging
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ GameTabRightPanel::GameTabRightPanel(wxFrame *parent, std::shared_ptr<Game> game
|
|||
tags_list->InsertColumn(1, L"Value", wxLIST_FORMAT_LEFT, 500);
|
||||
tagTextCtrl->SetHint("Tag");
|
||||
valueTextCtrl->SetHint("Value");
|
||||
|
||||
opening_label->SetHint("Current opening");
|
||||
RefreshTagsList();
|
||||
|
||||
// Bind events
|
||||
|
@ -42,6 +42,7 @@ GameTabRightPanel::GameTabRightPanel(wxFrame *parent, std::shared_ptr<Game> game
|
|||
Bind(wxEVT_KEY_UP, [p=this](wxKeyEvent &e){e.ResumePropagation(1);e.Skip();});
|
||||
|
||||
ApplyPreferences();
|
||||
analyze_game_button->Disable();
|
||||
}
|
||||
|
||||
void GameTabRightPanel::OnLiveAnalysis(wxCommandEvent &event) {
|
||||
|
@ -156,6 +157,11 @@ void GameTabRightPanel::Notify() {
|
|||
if (live_engine != nullptr) {
|
||||
live_engine->SetFEN(game->GetFen());
|
||||
}
|
||||
// Update opening name
|
||||
std::string opening,eco;
|
||||
game->GetOpening(opening,eco);
|
||||
if(eco.size()>0)
|
||||
opening_label->SetValue(eco+": "+opening);
|
||||
}
|
||||
|
||||
void GameTabRightPanel::ApplyPreferences() {
|
||||
|
|
|
@ -434,6 +434,9 @@ TabGameRightPanel::TabGameRightPanel( wxWindow* parent, wxWindowID id, const wxP
|
|||
editor_page = new wxPanel( notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
editor_page_sizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
opening_label = new wxTextCtrl( editor_page, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY );
|
||||
editor_page_sizer->Add( opening_label, 0, wxALL|wxEXPAND, 5 );
|
||||
|
||||
editor_canvas_sizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
|
||||
|
|
|
@ -284,6 +284,7 @@ class TabGameRightPanel : public wxPanel
|
|||
wxNotebook* notebook;
|
||||
wxPanel* editor_page;
|
||||
wxBoxSizer* editor_page_sizer;
|
||||
wxTextCtrl* opening_label;
|
||||
wxBoxSizer* editor_canvas_sizer;
|
||||
wxStaticLine* m_staticline1;
|
||||
wxStaticText* comment_label;
|
||||
|
|
|
@ -19,6 +19,8 @@ std::vector<TabInfos *> MyApp::ListTabInfos() {
|
|||
return (tinfos);
|
||||
}
|
||||
|
||||
Openings& MyApp::GetBook() { return Book; }
|
||||
|
||||
void MyApp::NewGame(TabInfos *tabsrc,std::shared_ptr<Game> g){
|
||||
MainWindow *w=((MainWindow *)this->GetTopWindow());
|
||||
TabInfos *i=w->NewGame(g);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#endif
|
||||
|
||||
#include "binres/binres.hpp"
|
||||
#include "Openings.hpp"
|
||||
#include "gui.h"
|
||||
#include <memory>
|
||||
#include <wx/app.h>
|
||||
|
@ -72,10 +73,12 @@ public:
|
|||
*/
|
||||
class MyApp : public wxApp {
|
||||
public:
|
||||
Openings Book;
|
||||
virtual bool OnInit();
|
||||
std::vector<TabInfos *> ListTabInfos();
|
||||
void NewGame(TabInfos *tabsrc,std::shared_ptr<Game> g);
|
||||
void NewGame(std::shared_ptr<Game> g);
|
||||
Openings& GetBook();
|
||||
};
|
||||
|
||||
wxDECLARE_APP(MyApp);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue