Debug animations, LiveEngine and improve arrow/squares highlights

This commit is contained in:
Loic Guegan 2023-01-10 11:14:25 +01:00
parent 179a173b3b
commit e069a29f99
7 changed files with 95 additions and 94 deletions

View file

@ -2,7 +2,7 @@
## Before releasing v0.1.0 ## Before releasing v0.1.0
- [ ] Implement pawn promotions in BoardCanvas - [ ] Implement pawn promotions in BoardCanvas
- [ ] Debug animations (have a more reliable approach (can segfault when clicking on variations in the editor)) - [x] Debug animations (have a more reliable approach (can segfault when clicking on variations in the editor))
- [x] In BoardCanvas search for a workaround of the dynamic allocation of adata.buffer (on canvas resize) - [x] In BoardCanvas search for a workaround of the dynamic allocation of adata.buffer (on canvas resize)
- [x] Bind the chess game editor settings to EditorPrefs.hpp - [x] Bind the chess game editor settings to EditorPrefs.hpp
- [x] Ask before closing MainWindow/Tabs if anything is not saved - [x] Ask before closing MainWindow/Tabs if anything is not saved

View file

@ -59,8 +59,8 @@ MainWindow::MainWindow()
// Temporary TO REMOVE JUST FOR TESTS: // Temporary TO REMOVE JUST FOR TESTS:
/*BaseTab *bt = new BaseTab((wxFrame *)notebook, "/home/loic/pgn/wijk_2003_annotated.pgn"); //BaseTab *bt = new BaseTab((wxFrame *)notebook, "/home/loic/pgn/wijk_2003_annotated.pgn");
this->AddPage(bt,bt);*/ //this->AddPage(bt,bt);
} }
void MainWindow::OnAuiNotebookPageCheck(wxAuiNotebookEvent& event){ void MainWindow::OnAuiNotebookPageCheck(wxAuiNotebookEvent& event){

View file

@ -72,14 +72,15 @@ void GameTabLeftPanel::OnPlay(wxCommandEvent &event) {
} }
void GameTabLeftPanel::SetEngineArrows(std::vector<std::string> arrows){ void GameTabLeftPanel::SetEngineArrows(std::vector<std::string> arrows){
engine_arrows=arrows; engine_arrows.clear();
int min_size=5, max_size=80; float scale=1;
int steps=(max_size-min_size)/arrows.size(); unsigned char color=0;
int current_size=max_size; for(auto const &arrow:arrows){
for(std::string &arrow:engine_arrows){ std::string src=arrow.substr(0,2);
wxLogDebug("%s",arrow); std::string dst=arrow.substr(2,2);
arrow+="#000000%"+std::to_string(current_size); engine_arrows.push_back({src,dst,wxColour(color,color,color),scale});
current_size-=steps; scale=std::max(0.1,scale-0.25);
color=std::min(255,color+70);
} }
Notify(true); Notify(true);
} }
@ -94,16 +95,14 @@ void GameTabLeftPanel::Notify(bool skip_animation) {
// Update capture and check if we should to animations during moves change: // Update capture and check if we should to animations during moves change:
if (m){ if (m){
captures = m->GetLineCaptures(); captures = m->GetLineCaptures();
if(!m->IsVariation()){ // Animations can lead to segfault when clicking on CGEditor move if(m->HasParent(last_move) && !m->IsVariation()){
if(m->HasParent(last_move)){ m->GetAbsoluteMove(src,dst);
m->GetAbsoluteMove(src,dst); animate=true;
animate=true; }else if(m->HasChild(last_move) && !last_move->IsVariation()){ // call to IsVariation is safe because of HasChild() before! cf below
}else if(m->HasChild(last_move)){ // Accessing last_move here is safe since it is still
// Accessing last_move here is safe since it is still // in the tree of moves (since HasChild found it so not deleted)
// in the tree of moves (since HasChild found it so not deleted) last_move->GetAbsoluteMove(dst,src);
last_move->GetAbsoluteMove(dst,src); animate=true;
animate=true;
}
} }
} else if(game->GetNextMove()){ // First move animation } else if(game->GetNextMove()){ // First move animation
HalfMove *next=game->GetNextMove(); HalfMove *next=game->GetNextMove();
@ -128,8 +127,8 @@ void GameTabLeftPanel::Notify(bool skip_animation) {
std::string src_hl, dst_hl; std::string src_hl, dst_hl;
m->GetAbsoluteMove(src_hl,dst_hl); m->GetAbsoluteMove(src_hl,dst_hl);
if(src_hl.size()>0){ // Just in case if(src_hl.size()>0){ // Just in case
gs.squares_hl.push_back(src_hl+"d"); gs.squares_hl.push_back({src_hl,wxColour(255,190,190)});
gs.squares_hl.push_back(dst_hl+"a"); gs.squares_hl.push_back({dst_hl,wxColour(255,100,100)});
} }
} }
if(skip_animation || !animate){ if(skip_animation || !animate){

View file

@ -13,7 +13,7 @@ class GameTabLeftPanel : public TabGameLeftPanel {
BoardCanvas *board_canvas; BoardCanvas *board_canvas;
bool repeat; bool repeat;
HalfMove *last_move; HalfMove *last_move;
std::vector<std::string> engine_arrows; std::vector<GameState::Arrow> engine_arrows;
public: public:
GameTabLeftPanel(wxFrame *parent, std::shared_ptr<Game> game); GameTabLeftPanel(wxFrame *parent, std::shared_ptr<Game> game);

View file

@ -18,9 +18,10 @@ BoardCanvas::BoardCanvas(wxFrame *parent)
valid_drag = false; valid_drag = false;
arrow_drag = false; arrow_drag = false;
arrows_offset = t->GetSquaresSizes()/2.5; arrows_offset = t->GetSquaresSizes()/2.5;
arrow_thickness = t->GetSquaresSizes()/1.8;
// Init animation data // Init animation data
adata.duration=100; adata.duration=100;
adata.duration_fast=100; adata.duration_fast=50;
adata.fps=60; adata.fps=60;
adata.buffer=new wxBitmap(500,500,32); adata.buffer=new wxBitmap(500,500,32);
adata.animate=false; adata.animate=false;
@ -230,30 +231,19 @@ void BoardCanvas::DrawBoard(wxDC &dc) {
// Draw highlighted squares // Draw highlighted squares
for(int i=0;i<(gs.squares_hl.size()+squares_hl.size());i++){ for(int i=0;i<(gs.squares_hl.size()+squares_hl.size());i++){
const std::string &s=i<gs.squares_hl.size() ? gs.squares_hl[i] : squares_hl[i-gs.squares_hl.size()]; const GameState::Square &s=i<gs.squares_hl.size() ? gs.squares_hl[i] : squares_hl[i-gs.squares_hl.size()];
std::uint8_t sfile = s[0]-'a'; std::uint8_t sfile = s.square[0]-'a';
std::uint8_t srank = s[1]-'1'; std::uint8_t srank = s.square[1]-'1';
if (!black_side) { if (!black_side) {
srank = 7 - srank; srank = 7 - srank;
sfile = 7 - sfile; sfile = 7 - sfile;
} }
if(srank == rank && sfile==file){ if(srank == rank && sfile==file){
char type='a';
if(s.size()>2)
type=s[2];
// Default highlight (type='a' or something else not supported)
dc.SetPen(wxNullPen); dc.SetPen(wxNullPen);
dc.SetBrush(wxColour(255, 102, 122)); if(s.color!=wxNullColour)
if(type=='b') dc.SetBrush(s.color);
dc.SetBrush(wxColour(120, 255, 102)); else
else if(type=='c') dc.SetBrush(wxColour(255, 102, 122));
dc.SetBrush(wxColour(102, 196, 255));
else if(type=='d')
dc.SetBrush(wxColour(255, 204, 213));
else if(type=='e')
dc.SetBrush(wxColour(220, 255, 204));
else if(type=='f')
dc.SetBrush(wxColour(204, 231, 255));
dc.DrawRectangle(wxRect(x,y,square_width,square_width)); dc.DrawRectangle(wxRect(x,y,square_width,square_width));
} }
} }
@ -396,12 +386,12 @@ void BoardCanvas::DrawBoard(wxDC &dc) {
boardY + square_width * 8 + numbers_size.y*2)); boardY + square_width * 8 + numbers_size.y*2));
} }
// Draw arrows // Draw arrows
for(int i=0;i<(gs.arrows.size()+arrows.size());i++){ for(int i=0;i<(gs.arrows.size()+arrows.size());i++){
const std::string &arrow= i<gs.arrows.size() ? gs.arrows[i] : arrows[i-gs.arrows.size()]; const GameState::Arrow &arrow= i<gs.arrows.size() ? gs.arrows[i] : arrows[i-gs.arrows.size()];
std::uint8_t sfile = arrow[0]-'a'; std::uint8_t sfile = arrow.src[0]-'a';
std::uint8_t srank = arrow[1]-'1'; std::uint8_t srank = arrow.src[1]-'1';
std::uint8_t dfile = arrow[2]-'a'; std::uint8_t dfile = arrow.dst[0]-'a';
std::uint8_t drank = arrow[3]-'1'; std::uint8_t drank = arrow.dst[1]-'1';
if (!black_side) { if (!black_side) {
srank = 7 - srank; srank = 7 - srank;
sfile = 7 - sfile; sfile = 7 - sfile;
@ -414,37 +404,20 @@ void BoardCanvas::DrawBoard(wxDC &dc) {
std::uint32_t dy = boardY + drank * square_width + square_width/2; std::uint32_t dy = boardY + drank * square_width + square_width/2;
// Parse arrow for metadata (maybe having a datatype is better) // Parse arrow for metadata (maybe having a datatype is better)
std::uint8_t thickness=50; if(arrow.color!=wxNullColour)
if(arrow.size()>4){ dc.SetBrush(arrow.color);
std::string color="#"; else
std::string new_thickness="";
char key='?';
for(const char &c:arrow){
if(c=='#'||c=='%'){
key=c;
continue;
}else if(key=='#'){
color+=c;
} else if(key=='%'){
new_thickness+=c;
}
}
if(color.size()>1)
dc.SetBrush(wxColour(color));
if(new_thickness.size()>0)
thickness=(std::uint8_t)std::stoi(new_thickness);
} else
dc.SetBrush(color_arrows); dc.SetBrush(color_arrows);
if(((abs(drank-srank) == 2) && (abs(dfile-sfile) == 1))|| if(((abs(drank-srank) == 2) && (abs(dfile-sfile) == 1))||
((abs(drank-srank) == 1) && (abs(dfile-sfile) == 2))){ ((abs(drank-srank) == 1) && (abs(dfile-sfile) == 2))){
if(abs(drank-srank) == 1){ if(abs(drank-srank) == 1){
DrawLArrow(dc,sx,sy,dx,dy,false,thickness); DrawLArrow(dc,sx,sy,dx,dy,false,arrow_thickness*arrow.scale);
} }
else { else {
DrawLArrow(dc,sx,sy,dx,dy,true,thickness); DrawLArrow(dc,sx,sy,dx,dy,true,arrow_thickness*arrow.scale);
} }
} else { } else {
DrawArrow(dc,sx,sy,dx,dy,thickness); DrawArrow(dc,sx,sy,dx,dy,arrow_thickness*arrow.scale);
} }
} }
} }
@ -519,14 +492,22 @@ void BoardCanvas::MouseEvent(wxMouseEvent &event) {
std::to_string(+active_square.y + 1); std::to_string(+active_square.y + 1);
std::string dst=((char)('a' + file)) + std::to_string(rank + 1); std::string dst=((char)('a' + file)) + std::to_string(rank + 1);
if(src!=dst){ if(src!=dst){
arrows.push_back(src+dst); arrows.push_back(DEFAULT_ARROW(src,dst));
wxLogDebug("Draw arrow %s",src+dst); wxLogDebug("Draw arrow %s",src+dst);
} }
else { else {
if(std::count(squares_hl.begin(), squares_hl.end(), src)){ int id=0;
squares_hl.erase(std::remove(squares_hl.begin(), squares_hl.end(), src), squares_hl.end()); bool removed=false;
}else{ for(GameState::Square &s:squares_hl){
squares_hl.push_back(src+"f"); if(s.square==src){
squares_hl.erase(squares_hl.begin()+id);
removed=true;
break;
}
id++;
}
if(!removed){
squares_hl.push_back(DEFAULT_SQUARE(src));
wxLogDebug("Highlight square %s",src); wxLogDebug("Highlight square %s",src);
} }
} }
@ -548,6 +529,7 @@ void BoardCanvas::Zoom(std::int32_t zoom) {
if(!t->Zoom(zoom)) if(!t->Zoom(zoom))
return; return;
t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR); t_captures->ResizePieces(t->GetPiecesSizes() * CAPTURE_FACTOR);
arrow_thickness = t->GetSquaresSizes()/1.8;
Refresh(); Refresh();
} }

View file

@ -35,6 +35,8 @@ wxDECLARE_EVENT(PLAY_MOVE_EVENT, wxCommandEvent);
#define CAPTURE_FACTOR 0.35 #define CAPTURE_FACTOR 0.35
#define SQUARE_NUM_PADDING 5 #define SQUARE_NUM_PADDING 5
#define DEFAULT_ARROW(SRC,DST) {(SRC),(DST),wxNullColour,1}
#define DEFAULT_SQUARE(SQUARE) {(SQUARE),wxNullColour}
typedef std::tuple<short, short, short> ClockTime; typedef std::tuple<short, short, short> ClockTime;
@ -61,11 +63,20 @@ typedef struct AnimState {
} AnimState; } AnimState;
typedef struct GameState { typedef struct GameState {
typedef struct Arrow {
std::string src,dst;
wxColour color=wxNullColour;
float scale=1;
} Arrow;
typedef struct Square {
std::string square;
wxColour color=wxNullColour;
} Square;
std::string white, black; std::string white, black;
std::string board; std::string board;
std::map<char, std::uint8_t> captures; std::map<char, std::uint8_t> captures;
std::vector<std::string> squares_hl; std::vector<Square> squares_hl;
std::vector<std::string> arrows; std::vector<Arrow> arrows;
bool is_black_turn; bool is_black_turn;
bool mat_black; bool mat_black;
bool mat_white; bool mat_white;
@ -78,10 +89,11 @@ class BoardCanvas : public wxPanel {
Theme *t, *t_captures; Theme *t, *t_captures;
wxColour color_arrows; wxColour color_arrows;
int arrows_offset; int arrows_offset;
std::uint8_t arrow_thickness;
std::string white_player,black_player; std::string white_player,black_player;
// Current highlighted squares and arrows: // Current highlighted squares and arrows:
std::vector<std::string> squares_hl; std::vector<GameState::Square> squares_hl;
std::vector<std::string> arrows; std::vector<GameState::Arrow> arrows;
// Various canvas state variables // Various canvas state variables
bool black_side, is_dragging, valid_drag, arrow_drag, is_black_turn; bool black_side, is_dragging, valid_drag, arrow_drag, is_black_turn;

View file

@ -101,23 +101,31 @@ void LiveEngineDialog::StartEngine() {
} }
void LiveEngineDialog::OnTimerTick(wxTimerEvent &event) { void LiveEngineDialog::OnTimerTick(wxTimerEvent &event) {
wxLogDebug("Tick!"); lines_list->DeleteAllItems(); // Clear lines_list
lines_list->DeleteAllItems();
engine->SyncAfter(0); engine->SyncAfter(0);
EngineEvaluation *eval=new EngineEvaluation(); EngineEvaluation *eval=new EngineEvaluation();
auto const &lines=engine->GetLines(); auto lines=engine->GetLines(); // First best lines
eval->best_lines.resize(lines.size());
for (auto const &line : lines) { // First retrieve lines ids from unordered map lines
long index = lines_list->InsertItem(0, std::to_string(line.first)); std::vector<int> ids;
for(auto const &line : lines){
ids.push_back(line.first);
}
std::sort(ids.begin(),ids.end());
// Now fill eval and lines_list
for (int &id:ids) {
auto const &line=lines[id];
long index = lines_list->InsertItem(id, std::to_string(id));
// Update eval that will be deplayed on the board
if(line.pv.size()>0)
eval->best_lines.push_back(line.pv[0]);
// Refresh lines_list
std::string line_moves; std::string line_moves;
for (std::string move : line.second.pv) { for (std::string move : line.pv)
line_moves += move + " "; line_moves += move + " ";
} std::string cp_str = std::to_string(line.score_cp);
if(line.second.pv.size()>0){ if (line.score_cp > 0) {
eval->best_lines.insert(eval->best_lines.begin()+line.first,line.second.pv[0]);
}
std::string cp_str = std::to_string(line.second.score_cp);
if (line.second.score_cp > 0) {
cp_str = "+" + cp_str; cp_str = "+" + cp_str;
} }
lines_list->SetItem(index, 1, cp_str); lines_list->SetItem(index, 1, cp_str);