chessbox: Fixes and enhancements

- Unfinished game is now saved along with current position.
- All savings are automatically done on shutdown.
- Implemented facility to view played games.
- Fixed bug that prevented program from the very first move.

Patch by Igor Poretsky

Change-Id: I997b97752e4362ed953309bea985d071f9db229b
This commit is contained in:
Solomon Peachy 2019-07-15 10:49:55 -04:00
parent c5ed45d8c7
commit 5572904887
5 changed files with 117 additions and 83 deletions

View file

@ -15259,3 +15259,20 @@ id: VOICE_BAT_BENCH_KEYS
recording: "Recording Directory" recording: "Recording Directory"
</voice> </voice>
</phrase> </phrase>
<phrase>
id: LANG_CHESSBOX_MENU_VIEW_GAMES
desc: in the chessbox menu
user: core
<source>
*: none
lcd_bitmap: "View Played Games"
</source>
<dest>
*: none
lcd_bitmap: "View Played Games"
</dest>
<voice>
*: none
lcd_bitmap: "View Played Games"
</voice>
</phrase>

View file

@ -74,6 +74,8 @@ extern const fb_data chessbox_pieces[];
#define COMMAND_SELECT 10 #define COMMAND_SELECT 10
#define COMMAND_NEXT 11 #define COMMAND_NEXT 11
#define COMMAND_PREV 12 #define COMMAND_PREV 12
#define COMMAND_VIEW 13
#define COMMAND_RETURN 14
short plugin_mode; short plugin_mode;
@ -360,7 +362,7 @@ static void cb_saveposition_dbg ( void )
sizeof(temp)); sizeof(temp));
rb->write(fd, buf, ch_ct); rb->write(fd, buf, ch_ct);
} }
for (i = 0; i <= GameCnt; i++) { for (i = 0; i < ((GameCnt + 1) & 0xFF); i++) {
ch_ct = rb->snprintf(buf,31,"GameCt %d, %d bytes\n",i, ch_ct = rb->snprintf(buf,31,"GameCt %d, %d bytes\n",i,
sizeof(GameCnt)); sizeof(GameCnt));
rb->write(fd, buf, ch_ct); rb->write(fd, buf, ch_ct);
@ -399,17 +401,21 @@ static void cb_saveposition_dbg ( void )
} }
#endif #endif
/* ---- Save current position ---- */ /* ---- Save current position and game history ---- */
static void cb_saveposition ( void ) { static void cb_saveposition ( struct pgn_game_node* game ) {
int fd; int fd;
short sq,i,c; short sq,i,c;
unsigned short temp; unsigned short temp;
struct pgn_ply_node *ply;
char buf[4];
#ifdef CHESSBOX_SAVE_FILE_DBG #ifdef CHESSBOX_SAVE_FILE_DBG
cb_saveposition_dbg(); cb_saveposition_dbg();
#endif #endif
rb->splash ( 0 , ID2P(LANG_CHESSBOX_SAVING_POSITION) ); rb->splash ( 0 , ID2P(LANG_CHESSBOX_SAVING_POSITION) );
fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666); fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
computer++; rb->write(fd, &(computer), sizeof(computer)); computer--; computer++; rb->write(fd, &(computer), sizeof(computer)); computer--;
opponent++; rb->write(fd, &(opponent), sizeof(opponent)); opponent--; opponent++; rb->write(fd, &(opponent), sizeof(opponent)); opponent--;
@ -438,7 +444,9 @@ static void cb_saveposition ( void ) {
temp = 256*board[sq] + c ; temp = 256*board[sq] + c ;
rb->write(fd, &(temp), sizeof(temp)); rb->write(fd, &(temp), sizeof(temp));
} }
for (i = 0; i <= GameCnt; i++) { c = GameCnt;
rb->write(fd, &(c), sizeof(c));
for (i = 0; i < ((GameCnt + 1) & 0xFF); i++) {
if (GameList[i].color == neutral) if (GameList[i].color == neutral)
c = 0; c = 0;
else else
@ -451,14 +459,24 @@ static void cb_saveposition ( void ) {
rb->write(fd, &(GameList[i].piece), sizeof(GameList[i].piece)); rb->write(fd, &(GameList[i].piece), sizeof(GameList[i].piece));
rb->write(fd, &(c), sizeof(c)); rb->write(fd, &(c), sizeof(c));
} }
for (ply=game->first_ply; ply!=NULL; ply=ply->next_node) {
buf[0] = ply->column_from + 'a';
buf[1] = ply->row_from + '1';
buf[2] = ply->column_to + 'a';
buf[3] = ply->row_to + '1';
rb->write(fd, buf, 4);
}
rb->close(fd); rb->close(fd);
} }
/* ---- Restore saved position ---- */ /* ---- Restore saved position and game history ---- */
static void cb_restoreposition ( void ) { static struct pgn_game_node* cb_restoreposition ( void ) {
int fd; int fd;
short sq; short sq;
unsigned short m; unsigned short m;
short n;
char buf[4];
struct pgn_game_node* game = pgn_init_game();
if ( (fd = rb->open(SAVE_FILE, O_RDONLY)) >= 0 ) { if ( (fd = rb->open(SAVE_FILE, O_RDONLY)) >= 0 ) {
rb->splash ( 0 , ID2P(LANG_CHESSBOX_LOADING_POSITION) ); rb->splash ( 0 , ID2P(LANG_CHESSBOX_LOADING_POSITION) );
@ -492,9 +510,12 @@ static void cb_restoreposition ( void ) {
else else
--color[sq]; --color[sq];
} }
GameCnt = MAX_GAME_CNT - 1; /*uchar rollsover to 0 after 255*/ rb->read(fd, &(n), sizeof(n));
while (rb->read(fd, &(GameList[++GameCnt].gmove), n++;
sizeof(GameList[GameCnt].gmove)) > 0) { n &= 0xFF;
for (GameCnt = 0; GameCnt < n; GameCnt++) {
rb->read(fd, &(GameList[GameCnt].gmove),
sizeof(GameList[GameCnt].gmove));
rb->read(fd, &(GameList[GameCnt].score), rb->read(fd, &(GameList[GameCnt].score),
sizeof(GameList[GameCnt].score)); sizeof(GameList[GameCnt].score));
rb->read(fd, &(GameList[GameCnt].depth), rb->read(fd, &(GameList[GameCnt].depth),
@ -506,7 +527,7 @@ static void cb_restoreposition ( void ) {
rb->read(fd, &(GameList[GameCnt].piece), rb->read(fd, &(GameList[GameCnt].piece),
sizeof(GameList[GameCnt].piece)); sizeof(GameList[GameCnt].piece));
rb->read(fd, &(GameList[GameCnt].color), rb->read(fd, &(GameList[GameCnt].color),
sizeof(GameList[GameCnt].color)); sizeof(GameList[GameCnt].color));
if (GameList[GameCnt].color == 0) if (GameList[GameCnt].color == 0)
GameList[GameCnt].color = neutral; GameList[GameCnt].color = neutral;
else else
@ -516,47 +537,45 @@ static void cb_restoreposition ( void ) {
if (TimeControl.clock[white] > 0) if (TimeControl.clock[white] > 0)
TCflag = true; TCflag = true;
computer--; opponent--; computer--; opponent--;
n = 0;
while (rb->read(fd, buf, 4) > 0)
pgn_append_ply(game, ((n++) & 1) ? black : white, buf, false);
rb->close(fd);
} }
rb->close(fd);
cb_setlevel(Level); cb_setlevel(Level);
InitializeStats(); InitializeStats();
Sdepth = 0; Sdepth = 0;
return game;
} }
/* ---- show menu in viewer mode---- */ /* ---- show menu in viewer mode---- */
static int cb_menu_viewer(void) static int cb_menu_viewer(void)
{ {
int selection; int selection;
int result = 0;
bool menu_quit = false;
MENUITEM_STRINGLIST(menu,"Chessbox Menu",NULL, MENUITEM_STRINGLIST(menu,"Chessbox Menu",NULL,
ID2P(LANG_CHESSBOX_MENU_RESTART_GAME), ID2P(LANG_CHESSBOX_MENU_RESTART_GAME),
ID2P(LANG_CHESSBOX_MENU_SELECT_OTHER_GAME), ID2P(LANG_CHESSBOX_MENU_SELECT_OTHER_GAME),
ID2P(LANG_CHESSBOX_MENU_RESUME_GAME),
ID2P(LANG_RETURN),
ID2P(LANG_MENU_QUIT)); ID2P(LANG_MENU_QUIT));
while(!menu_quit) switch(rb->do_menu(&menu, &selection, NULL, false))
{ {
switch(rb->do_menu(&menu, &selection, NULL, false)) case 0:
{ return COMMAND_RESTART;
case 0: case 1:
menu_quit = true; return COMMAND_SELECT;
result = COMMAND_RESTART; case 3:
break; return COMMAND_RETURN;
case 1: case 4:
result = COMMAND_SELECT; return COMMAND_QUIT;
menu_quit = true;
break;
case 2:
result = COMMAND_QUIT;
menu_quit = true;
break;
}
} }
return result; return COMMAND_RESUME;
} }
/* ---- get a command in game mode ---- */ /* ---- get a command in viewer mode ---- */
static struct cb_command cb_get_viewer_command (void) { static struct cb_command cb_get_viewer_command (void) {
int button; int button;
struct cb_command result = { 0, {0,0,0,0,0}, 0 }; struct cb_command result = { 0, {0,0,0,0,0}, 0 };
@ -592,17 +611,18 @@ static struct cb_command cb_get_viewer_command (void) {
} }
/* ---- viewer main loop ---- */ /* ---- viewer main loop ---- */
static void cb_start_viewer(char* filename){ static bool cb_start_viewer(const char* filename){
struct pgn_game_node *first_game, *selected_game; struct pgn_game_node *first_game, *selected_game;
struct pgn_ply_node *curr_ply; struct pgn_ply_node *curr_ply;
bool exit_game = false; bool exit_game = false;
bool exit_viewer = false; bool exit_viewer = false;
bool exit_app = false;
struct cb_command command; struct cb_command command;
first_game = pgn_list_games(filename); first_game = pgn_list_games(filename);
if (first_game == NULL){ if (first_game == NULL){
rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_NO_GAMES) ); rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_NO_GAMES) );
return; return exit_app;
} }
do { do {
@ -759,6 +779,8 @@ static void cb_start_viewer(char* filename){
exit_game = true; exit_game = true;
break; break;
case COMMAND_QUIT: case COMMAND_QUIT:
exit_app = true;
case COMMAND_RETURN:
exit_viewer = true; exit_viewer = true;
break; break;
} }
@ -767,57 +789,44 @@ static void cb_start_viewer(char* filename){
rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_PGN_PARSE_ERROR)); rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_PGN_PARSE_ERROR));
} }
} while (!exit_viewer); } while (!exit_viewer);
return exit_app;
} }
/* ---- show menu ---- */ /* ---- show menu ---- */
static int cb_menu(void) static int cb_menu(void)
{ {
int selection; int selection;
int result = 0;
bool menu_quit = false;
MENUITEM_STRINGLIST(menu,"Chessbox Menu",NULL, MENUITEM_STRINGLIST(menu,"Chessbox Menu",NULL,
ID2P(LANG_CHESSBOX_MENU_NEW_GAME), ID2P(LANG_CHESSBOX_MENU_NEW_GAME),
ID2P(LANG_CHESSBOX_MENU_RESUME_GAME), ID2P(LANG_CHESSBOX_MENU_RESUME_GAME),
ID2P(LANG_CHESSBOX_MENU_SAVE_GAME), ID2P(LANG_CHESSBOX_MENU_SAVE_GAME),
ID2P(LANG_CHESSBOX_MENU_RESTORE_GAME), ID2P(LANG_CHESSBOX_MENU_RESTORE_GAME),
ID2P(LANG_CHESSBOX_MENU_VIEW_GAMES),
#ifdef HAVE_PLAYBACK_CONTROL #ifdef HAVE_PLAYBACK_CONTROL
ID2P(LANG_PLAYBACK_CONTROL), ID2P(LANG_PLAYBACK_CONTROL),
#endif #endif
ID2P(LANG_MENU_QUIT)); ID2P(LANG_MENU_QUIT));
while(!menu_quit) switch(rb->do_menu(&menu, &selection, NULL, false))
{ {
switch(rb->do_menu(&menu, &selection, NULL, false)) case 0:
{ return COMMAND_RESTART;
case 0: case 2:
menu_quit = true; return COMMAND_SAVE;
result = COMMAND_RESTART; case 3:
break; return COMMAND_RESTORE;
case 1: case 4:
result = COMMAND_RESUME; return COMMAND_VIEW;
menu_quit = true; case 5:
break;
case 2:
result = COMMAND_SAVE;
menu_quit = true;
break;
case 3:
result = COMMAND_RESTORE;
menu_quit = true;
break;
case 4:
#ifdef HAVE_PLAYBACK_CONTROL #ifdef HAVE_PLAYBACK_CONTROL
playback_control(NULL); playback_control(NULL);
break; break;
case 5: case 6:
#endif #endif
result = COMMAND_QUIT; return COMMAND_QUIT;
menu_quit = true;
break;
}
} }
return result; return COMMAND_RESUME;
} }
/* ---- get a command in game mode ---- */ /* ---- get a command in game mode ---- */
@ -1029,12 +1038,8 @@ static void cb_play_game(void) {
/* init board */ /* init board */
GNUChess_Initialize(); GNUChess_Initialize();
/* init PGN history data structures */
game = pgn_init_game();
/* restore saved position, if saved */ /* restore saved position, if saved */
cb_restoreposition(); game = cb_restoreposition();
/* TODO: save/restore the PGN history of unfinished games */
/* draw the board */ /* draw the board */
/* I don't like configscreens, start game inmediatly */ /* I don't like configscreens, start game inmediatly */
@ -1097,7 +1102,7 @@ static void cb_play_game(void) {
cb_drawboard(); cb_drawboard();
break; break;
case COMMAND_SAVE: case COMMAND_SAVE:
cb_saveposition(); cb_saveposition(game);
cb_drawboard(); cb_drawboard();
break; break;
case COMMAND_RESTORE: case COMMAND_RESTORE:
@ -1106,12 +1111,21 @@ static void cb_play_game(void) {
/* init board */ /* init board */
GNUChess_Initialize(); GNUChess_Initialize();
/* init PGN history data structures */
game = pgn_init_game();
/* restore saved position, if saved */ /* restore saved position, if saved */
cb_restoreposition(); game = cb_restoreposition();
cb_drawboard();
break;
case COMMAND_VIEW:
if (rb->file_exists(pgn_file)) {
cb_saveposition(game);
if (cb_start_viewer(pgn_file))
return;
GNUChess_Initialize();
game = cb_restoreposition();
}else{
rb->splash ( HZ*2 , ID2P(LANG_CHESSBOX_NO_GAMES) );
}
cb_drawboard(); cb_drawboard();
break; break;
case COMMAND_PLAY: case COMMAND_PLAY:
@ -1158,9 +1172,7 @@ static void cb_play_game(void) {
} }
} }
cb_saveposition(); cb_saveposition(game);
/* TODO: save/restore the PGN history of unfinished games */
rb->lcd_setfont(FONT_UI);
} }
@ -1187,6 +1199,8 @@ enum plugin_status plugin_start(const void* parameter) {
cb_play_game(); cb_play_game();
} }
rb->lcd_setfont(FONT_UI);
if (cb_sysevent) if (cb_sysevent)
rb->default_event_handler(cb_sysevent); rb->default_event_handler(cb_sysevent);

View file

@ -22,9 +22,9 @@
#include "plugin.h" #include "plugin.h"
#include "chessbox_pgn.h" #include "chessbox_pgn.h"
#define PGN_FILE PLUGIN_GAMES_DATA_DIR "/chessbox.pgn"
#define LOG_FILE PLUGIN_GAMES_DATA_DIR "/chessbox.log" #define LOG_FILE PLUGIN_GAMES_DATA_DIR "/chessbox.log"
int loghandler; int loghandler;
const char *pgn_file = PLUGIN_GAMES_DATA_DIR "/chessbox.pgn";
short kn_offs[8][2] = {{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}}; short kn_offs[8][2] = {{2,1},{2,-1},{-2,1},{-2,-1},{1,2},{1,-2},{-1,2},{-1,-2}};
short rk_offs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}}; short rk_offs[4][2] = {{1,0},{-1,0},{0,1},{0,-1}};
@ -889,7 +889,7 @@ void pgn_store_game(struct pgn_game_node* game){
ply_count++; ply_count++;
} }
fhandler = rb->open(PGN_FILE, O_WRONLY|O_CREAT|O_APPEND, 0666); fhandler = rb->open(pgn_file, O_WRONLY|O_CREAT|O_APPEND, 0666);
/* the first 7 tags are mandatory according to the PGN specification so we /* the first 7 tags are mandatory according to the PGN specification so we

View file

@ -649,7 +649,7 @@
/* structure to represent the plies */ /* structure to represent the plies */
struct pgn_ply_node { struct pgn_ply_node {
unsigned short player; unsigned short player;
char pgn_text[9]; char pgn_text[11];
unsigned short row_from; unsigned short row_from;
unsigned short column_from; unsigned short column_from;
unsigned short row_to; unsigned short row_to;
@ -677,6 +677,9 @@ struct pgn_game_node {
struct pgn_game_node* next_node; struct pgn_game_node* next_node;
}; };
/* File for saving games */
extern const char* pgn_file;
/* Return the list of games in a PGN file. /* Return the list of games in a PGN file.
* Parsing of the games themselves is postponed until * Parsing of the games themselves is postponed until
* the user selects a game, that obviously saves processing * the user selects a game, that obviously saves processing

View file

@ -1167,12 +1167,12 @@ void OpeningBook()
m = 0; m = 0;
while ( o_c < MAX_OPENING ) { while ( o_c < MAX_OPENING ) {
m_c = 0 ; m_c = 0 ;
for (j = 0; j <= GameCnt; j++) { for (j = 0; j < ((GameCnt + 1) & 0xFF); j++) {
if ( GameList[j].gmove != OBook[o_c][m_c] ) break; if ( GameList[j].gmove != OBook[o_c][m_c] ) break;
m_c++; m_c++;
} }
/* I added ( m != OBook[o_c][m_c] ) trying to get more random games */ /* I added ( m != OBook[o_c][m_c] ) trying to get more random games */
if ( ( j > GameCnt ) && ( m != OBook[o_c][m_c] ) ) { if ( ( j >= ((GameCnt + 1) & 0xFF) ) && ( m != OBook[o_c][m_c] ) ) {
r=rb->rand(); r=rb->rand();
if ( r > r0 ) { if ( r > r0 ) {
r0 = r; m = OBook[o_c][m_c]; r0 = r; m = OBook[o_c][m_c];
@ -2066,7 +2066,7 @@ register short i,c,f,t;
short b[64]; short b[64];
unsigned short m; unsigned short m;
*cnt = c = 0; *cnt = c = 0;
if (GameCnt > Game50+3) if (((GameCnt + 1) & 0xFF) > Game50+4)
{ {
for (i = 0; i < 64; b[i++] = 0); for (i = 0; i < 64; b[i++] = 0);
for (i = GameCnt; i > Game50; i--) for (i = GameCnt; i > Game50; i--)