Index: apps/plugins/chessbox/chessbox.c
===================================================================
--- apps/plugins/chessbox/chessbox.c	(revision 13222)
+++ apps/plugins/chessbox/chessbox.c	(working copy)
@@ -191,6 +191,26 @@
 /* save files */
 #define SAVE_FILE  PLUGIN_DIR "/chessbox.save"
 
+#define PGN_FILE  PLUGIN_DIR "/chessbox.pgn"
+
+const char initial_pgn_board[12][12] = {{'#','#','#','#','#','#','#','#','#','#','#','#'},
+                                    {'#','#','#','#','#','#','#','#','#','#','#','#'},
+                                    {'#','#','r','n','b','q','k','b','n','r','#','#'},
+                                    {'#','#','p','p','p','p','p','p','p','p','#','#'},
+                                    {'#','#',' ',' ',' ',' ',' ',' ',' ',' ','#','#'},
+                                    {'#','#',' ',' ',' ',' ',' ',' ',' ',' ','#','#'},
+                                    {'#','#',' ',' ',' ',' ',' ',' ',' ',' ','#','#'},
+                                    {'#','#',' ',' ',' ',' ',' ',' ',' ',' ','#','#'},
+                                    {'#','#','P','P','P','P','P','P','P','P','#','#'},
+                                    {'#','#','R','N','B','Q','K','B','N','R','#','#'},
+                                    {'#','#','#','#','#','#','#','#','#','#','#','#'},
+                                    {'#','#','#','#','#','#','#','#','#','#','#','#'}};
+
+/* representation of the current board position */
+/* this is only to express moves in PGN notation, nothing to do with the chess engine */
+char pgn_board[12][12];
+int move_count;
+
 /* commands enum */
 #define COMMAND_NOP        0
 #define COMMAND_MOVE       1
@@ -298,6 +318,433 @@
     rb->lcd_update();
 }
 
+/* ---- Initialize the board used to express moves in PGN notation --- */
+void cb_init_pgn_board (void) {
+    short r,c;
+    for (r=0;r<12;r++){
+        for (c=0;c<12;c++){
+            pgn_board[r][c] = initial_pgn_board[r][c];
+        }
+    }
+    move_count = 1;
+}
+
+/* ---- Restore the position in the board used to express moves in PGN notation --- */
+void cb_restore_pgn_board (void) {
+    short r,c,l,p_type,p_color;
+    for (r = 0; r < 8; r++) {
+        for (c = 0; c < 8; c++) {
+            pgn_board[9-r][c+2] = ' ';
+            l = locn[r][c];
+            p_type = board[l] ;
+            p_color = color[l] ;
+            switch (p_type) {
+                case pawn:
+                    pgn_board[9-r][c+2] = 'p';
+                    break;
+                case rook:
+                    pgn_board[9-r][c+2] = 'r';
+                    break;
+                case knight:
+                    pgn_board[9-r][c+2] = 'n';
+                    break;
+                case bishop:
+                    pgn_board[9-r][c+2] = 'b';
+                    break;
+                case queen:
+                    pgn_board[9-r][c+2] = 'q';
+                    break;
+                case king:
+                    pgn_board[9-r][c+2] = 'k';
+                    break;
+            }
+            if (p_color == white){
+                pgn_board[9-r][c+2] -= 32;
+            }
+        }
+    }
+}
+
+/* ---- Add the header of a PGN game to the file ---- */
+void cb_init_pgn_game(int fhandler) {
+    rb->write(fhandler, "[Event \"Chessbox Chess Game\"]\r\n",31);
+    rb->write(fhandler, "[White \"player\"]\r\n",18);
+    rb->write(fhandler, "[Black \"Chessbox (powered by GnuChess)\"]\r\n\r\n",44);
+}
+
+/* ---- Convert a move from chessbox's notation to PGN --- */
+void cb_move_to_pgn(char *move, char *pgn){
+    short r0, c0, r1, c1, i, j, pos;
+    short kc, kr;
+    short check;
+    c0 = move[0] - 95;
+    r0 = 58 - move[1];
+    c1 = move[2] - 95;
+    r1 = 58 - move[3];
+
+    /* check castling */
+    if (pgn_board[r0][c0] == 'k' || pgn_board[r0][c0] == 'K'){
+        if (c0 == 6 && c1 == 8) {
+            /* castling kingside */
+            pgn[0] = 'O';
+            pgn[1] = '-';
+            pgn[2] = 'O';
+            pgn[3] = '\0';
+            pgn_board[r1][c1] = pgn_board[r0][c0];
+            pgn_board[r1][c1-1] = pgn_board[r1][c1+1];
+            pgn_board[r0][c0] = ' ';
+            pgn_board[r1][c1+1] = ' ';
+            return;
+        }
+        if (c0 == 6 && c1 == 4) {
+            /* castling queenside */
+            pgn[0] = 'O';
+            pgn[1] = '-';
+            pgn[2] = 'O';
+            pgn[3] = '-';
+            pgn[4] = 'O';
+            pgn[5] = '\0';
+            pgn_board[r1][c1] = pgn_board[r0][c0];
+            pgn_board[r1][c1+1] = pgn_board[r1][c1-2];
+            pgn_board[r0][c0] = ' ';
+            pgn_board[r1][c1-2] = ' ';
+            return;
+        }        
+    }
+
+    pos = 0;
+
+    /* add the moving piece (not pawn) to the pgn string */
+    if (pgn_board[r0][c0] != 'p' && pgn_board[r0][c0] != 'P') {
+        pgn[pos] = pgn_board[r0][c0];
+        /* make sure it's upper case */
+        if (pgn[pos] >= 97) {
+            pgn[pos] -= 32;
+        }
+        pos++;
+    } else {
+        /* it's a pawn, add the column */
+        pgn[pos] = move[0];
+        pos++;
+    }
+
+    /* avoid ambiguos moves */
+    /* this would definitely be easier and faster if I kept bit boards instead of a board array :( */
+    do {
+        if (pgn_board[r0][c0] == 'r' || pgn_board[r0][c0] == 'R' || pgn_board[r0][c0] == 'q' || pgn_board[r0][c0] == 'Q'){
+            /* check for other rook or other queen (horizontally or vertically) attacking the final position */
+            for (i=r1+1;pgn_board[i][c1]==' ';i++);
+            if (pgn_board[i][c1] == pgn_board[r0][c0] && i != r0){
+                pgn[pos] = move[1];
+                pos++;
+                break;
+            }
+            for (i=r1-1;pgn_board[i][c1]==' ';i--);
+            if (pgn_board[i][c1] == pgn_board[r0][c0] && i != r0){
+                pgn[pos] = move[1];
+                pos++;
+                break;
+            }
+            for (i=c1+1;pgn_board[r1][i]==' ';i++);
+            if (pgn_board[r1][i] == pgn_board[r0][c0] && i != c0){
+                pgn[pos] = move[0];
+                pos++;
+                break;
+            }
+            for (i=c1-1;pgn_board[r1][i]==' ';i--);
+            if (pgn_board[r1][i] == pgn_board[r0][c0] && i != c0){
+                pgn[pos] = move[0];
+                pos++;
+                break;
+            }
+        }
+
+        if (pgn_board[r0][c0] == 'b' || pgn_board[r0][c0] == 'B' || pgn_board[r0][c0] == 'q' || pgn_board[r0][c0] == 'Q'){
+            /* check for other bishop or other queen (diagonally) attacking the final position */
+            for (i=r1+1,j=c1+1;pgn_board[i][j]==' ';i++,j++);
+            if (pgn_board[i][j] == pgn_board[r0][c0]){
+                if (i != r0) {
+                  pgn[pos] = move[1];
+                  pos++;
+                  break;
+                }
+                if (j != c0) {
+                  pgn[pos] = move[0];
+                  pos++;
+                  break;
+                }
+            }
+            for (i=r1-1,j=c1-1;pgn_board[i][j]==' ';i--,j--);
+            if (pgn_board[i][j] == pgn_board[r0][c0]){
+                if (i != r0) {
+                  pgn[pos] = move[1];
+                  pos++;
+                  break;
+                }
+                if (j != c0) {
+                  pgn[pos] = move[0];
+                  pos++;
+                  break;
+                }
+            }
+            for (i=r1+1,j=c1-1;pgn_board[i][j]==' ';i++,j--);
+            if (pgn_board[i][j] == pgn_board[r0][c0]){
+                if (i != r0) {
+                  pgn[pos] = move[1];
+                  pos++;
+                  break;
+                }
+                if (j != c0) {
+                  pgn[pos] = move[0];
+                  pos++;
+                  break;
+                }
+            }
+            for (i=r1-1,j=c1+1;pgn_board[i][j]==' ';i--,j++);
+            if (pgn_board[i][j] == pgn_board[r0][c0]){
+                if (i != r0) {
+                  pgn[pos] = move[1];
+                  pos++;
+                  break;
+                }
+                if (j != c0) {
+                  pgn[pos] = move[0];
+                  pos++;
+                  break;
+                }
+            }
+        }
+
+        if (pgn_board[r0][c0] == 'n' || pgn_board[r0][c0] == 'N'){
+            /* check for other knight attacking the final position */
+            if (pgn_board[r1-1][c1-2] == pgn_board[r0][c0] && (r1 - 1 != r0 || c1 - 2 != c0)){
+                if (r1 - 1 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+            if (pgn_board[r1+1][c1+2] == pgn_board[r0][c0] && (r1 + 1 != r0 || c1 + 2 != c0)){
+                if (r1 + 1 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+            if (pgn_board[r1-2][c1-1] == pgn_board[r0][c0] && (r1 - 2 != r0 || c1 - 1 != c0)){
+                if (r1 - 2 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+            if (pgn_board[r1+2][c1+1] == pgn_board[r0][c0] && (r1 + 2 != r0 || c1 + 1 != c0)){
+                if (r1 + 2 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+
+            if (pgn_board[r1-1][c1+2] == pgn_board[r0][c0] && (r1 - 1 != r0 || c1 + 2 != c0)){
+                if (r1 - 1 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+            if (pgn_board[r1+1][c1-2] == pgn_board[r0][c0] && (r1 + 1 != r0 || c1 - 2 != c0)){
+                if (r1 + 1 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+            if (pgn_board[r1+2][c1-1] == pgn_board[r0][c0] && (r1 + 2 != r0 || c1 - 1 != c0)){
+                if (r1 - 2 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+            if (pgn_board[r1-2][c1+1] == pgn_board[r0][c0] && (r1 - 2 != r0 || c1 + 1 != c0)){
+                if (r1 + 2 != r0) {
+                    pgn[pos] = move[1];
+                } else {
+                    pgn[pos] = move[0];
+                }
+                pos++;
+                break;
+            }
+
+        }
+    } while (false);
+
+    /* determine if a piece was taken */
+    if (pgn_board[r1][c1] != ' ') {
+        pgn[pos] = 'x';
+        pos++;
+    }
+    /* check "en-passant" */
+    /* it's a pawn, it changed column and no piece or pawn was sitting in the final square */
+    if ((pgn_board[r0][c0] == 'p' || pgn_board[r0][c0] == 'p') && (c0 - c1 == 1 || c0 - c1 == -1) && (pgn_board[r1][c1] == ' ')){
+        pgn[pos] = 'x';
+        pos++;
+        pgn_board[r0][c1] = ' ';
+    }
+
+    /* add the final position */
+    if ((pgn_board[r0][c0] != 'p' && pgn_board[r0][c0] != 'P') || pgn[pos-1] == 'x'){
+      pgn[pos] = move[2];
+      pos++;
+    }
+    pgn[pos] = move[3];
+    pos++;
+
+    if ((pgn_board[r0][c0] == 'p' || pgn_board[r0][c0] == 'P') && (r1 == 2 || r1 == 9)){
+        pgn[pos] = '=';
+        pos++;
+        /* the pawn is replaced by the queen in the original position, it will be moved later */
+        if (pgn_board[r0][c0] == 'P'){
+            pgn[pos] = 'Q';
+            pgn_board[r0][c0] = 'Q';
+        } else {
+            pgn[pos] = 'q';
+            pgn_board[r0][c0] = 'q';
+        }
+        pos++;
+    }
+
+    /* update the board */
+    pgn_board[r1][c1] = pgn_board[r0][c0];
+    pgn_board[r0][c0] = ' ';
+
+    /* add 'check' indicator */
+    /* look for the king's position (perhaps I should keep it in a set of variables instead of looking for it) */
+    kc = kr = 0;
+    check = 0;
+    for (i=2;i<10;i++){
+        for (j=2;j<10;j++){
+            if (pgn_board[r1][c1] < 96 && pgn_board[i][j] == 'k') {
+                kr = i;
+                kc = j;
+                break;
+            }
+            if (pgn_board[r1][c1] > 96 && pgn_board[i][j] == 'K') {
+                kr = i;
+                kc = j;
+                break;
+            }
+        }
+        if (kc != 0){
+            break;
+        }
+    }
+    do {
+        if (mate) {
+            break;
+        }
+        /* look for "attacking" pieces (queen, rook or bishop) */
+        for (i=kr+1;pgn_board[i][kc]==' ';i++);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[i][kc] == 'R' || pgn_board[i][kc] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[i][kc] == 'r' || pgn_board[i][kc] == 'q'))){
+            check = 1;
+            break;
+        }
+        for (i=kr-1;pgn_board[i][kc]==' ';i--);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[i][kc] == 'R' || pgn_board[i][kc] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[i][kc] == 'r' || pgn_board[i][kc] == 'q'))){
+            check = 1;
+            break;
+        }
+
+        for (j=kc+1;pgn_board[kr][j]==' ';j++);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[kr][j] == 'R' || pgn_board[kr][j] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[kr][j] == 'r' || pgn_board[kr][j] == 'q'))){
+            check = 1;
+            break;
+        }
+        for (j=kc-1;pgn_board[kr][j]==' ';j--);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[kr][j] == 'R' || pgn_board[kr][j] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[kr][j] == 'r' || pgn_board[kr][j] == 'q'))){
+            check = 1;
+            break;
+        }
+
+        for (i=kr+1,j=kc+1;pgn_board[i][j]==' ';i++,j++);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[i][j] == 'B' || pgn_board[i][j] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[i][j] == 'b' || pgn_board[i][j] == 'q'))){
+            check = 1;
+            break;
+        }
+        for (i=kr-1,j=kc-1;pgn_board[i][j]==' ';i--,j--);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[i][j] == 'B' || pgn_board[i][j] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[i][j] == 'b' || pgn_board[i][j] == 'q'))){
+            check = 1;
+            break;
+        }
+
+        for (i=kr+1,j=kc-1;pgn_board[i][j]==' ';i++,j--);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[i][j] == 'B' || pgn_board[i][j] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[i][j] == 'b' || pgn_board[i][j] == 'q'))){
+            check = 1;
+            break;
+        }
+        for (i=kr-1,j=kc+1;pgn_board[i][j]==' ';i--,j++);
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[i][j] == 'B' || pgn_board[i][j] == 'Q')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[i][j] == 'b' || pgn_board[i][j] == 'q'))){
+            check = 1;
+            break;
+        }
+
+        /* look for "attacking" pieces (knights) */
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[i+2][j+1] == 'N' || pgn_board[i-2][j-1] == 'N' ||
+                                          pgn_board[i+1][j+2] == 'N' || pgn_board[i-1][j-2] == 'N' || 
+                                          pgn_board[i-2][j+1] == 'N' || pgn_board[i+2][j-1] == 'N' || 
+                                          pgn_board[i+1][j-2] == 'N' || pgn_board[i-1][j+2] == 'N')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[i+2][j+1] == 'n' || pgn_board[i-2][j-1] == 'n' ||
+                                          pgn_board[i+1][j+2] == 'n' || pgn_board[i-1][j-2] == 'n' || 
+                                          pgn_board[i-2][j+1] == 'n' || pgn_board[i+2][j-1] == 'n' || 
+                                          pgn_board[i+1][j-2] == 'n' || pgn_board[i-1][j+2] == 'n'))){
+            check = 1;
+            break;
+        }
+
+        /* look for "attacking" pieces (pawns) */
+        if ((pgn_board[kr][kc] == 'k' && (pgn_board[kr+1][kc+1] == 'P' || pgn_board[kr+1][kc-1] == 'P')) ||
+            (pgn_board[kr][kc] == 'K' && (pgn_board[kr-1][kc+1] == 'p' || pgn_board[kr-1][kc-1] == 'p'))) {
+            check = 1;
+            break;
+        }
+    } while (false);
+    if (check && !mate) {
+        pgn[pos] = '+';
+        pos++;
+    }
+
+    if (mate) {
+        pgn[pos] = '#';
+        pos++;
+    }
+
+    /* finalize the string */
+    pgn[pos] = '\0';
+}
+
 /* ---- Switch mark on board ---- */
 void cb_switch ( short x , short y ) {
     rb->lcd_set_drawmode ( DRMODE_COMPLEMENT );
@@ -308,6 +755,8 @@
     rb->lcd_set_drawmode ( DRMODE_SOLID );
 }
 
+
+
 /* ---- callback for capturing interaction while thinking ---- */
 void cb_wt_callback ( void ) {
     int button = BUTTON_NONE;
@@ -437,6 +886,9 @@
         rb->write(fd, &(GameList[i].piece), sizeof(GameList[i].piece));
         rb->write(fd, &(c), sizeof(c));
     }
+    move_count++;
+    rb->write(fd, &(move_count), sizeof(move_count));
+    move_count--;
     rb->close(fd);
 }
 
@@ -499,6 +951,8 @@
             else
                 --GameList[GameCnt].color;
         }
+        rb->read(fd, &(move_count), sizeof(move_count));
+        move_count--;
         GameCnt--;
         if (TimeControl.clock[white] > 0)
             TCflag = true;
@@ -508,6 +962,7 @@
     cb_setlevel(Level);
     InitializeStats();
     Sdepth = 0;
+
 }
 
 /* ---- main user loop ---- */
@@ -659,6 +1114,11 @@
 #endif
     /* end of plugin init */
 
+    /* string buffer for storing history */
+    int fhandler;
+    char move_buffer[10];
+    char pgn_move[10];
+
     /* load opening book, soon */
 
     /* init board */
@@ -666,16 +1126,30 @@
     
     /* restore saved position, if saved */
     cb_restoreposition();
+
+    cb_restore_pgn_board();
+
+    /* open the history file */
+    fhandler = rb->open(PGN_FILE, O_WRONLY|O_APPEND|O_CREAT);
     
     /* draw the board */
-    /* I don't like configscreens, start game inmediatly */
+    /* I don't like config screens, start game immediately */
     cb_drawboard();
     
     while (!exit) {
         if ( mate ) {
             rb->splash ( 500 , "Checkmate!" );
+
+            /* mark the end of the game in the history file */
+            rb->write(fhandler, "\r\n\r\n",4);
+
             rb->button_get(true);
+
             GNUChess_Initialize();
+
+            cb_init_pgn_board();
+            cb_init_pgn_game(fhandler);
+
             cb_drawboard();
         }
         command = cb_getcommand ();
@@ -686,11 +1160,34 @@
                     cb_drawboard();
                 } else {
                     cb_drawboard();
+
                     rb->splash ( 0 , "Thinking..." );
+
+                    /* save player move */
+                    cb_move_to_pgn(command.mv_s, pgn_move);
+                    if (opponent == white) {
+                        rb->snprintf(move_buffer,4,"%u. ",move_count);
+                        rb->write(fhandler, move_buffer, rb->strlen(move_buffer));
+                        move_count++;
+                    }
+                    rb->write(fhandler, &(pgn_move),rb->strlen(pgn_move));
+                    rb->write(fhandler, " ",1);
+
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
                     rb->cpu_boost ( true );
 #endif
-                    SelectMove ( computer , 0 , cb_wt_callback );
+                    SelectMove ( computer , 0 , cb_wt_callback , move_buffer);
+                    
+                    /* save computer move */
+                    cb_move_to_pgn(move_buffer, pgn_move);
+                    if (computer == white) {
+                        rb->snprintf(move_buffer,4,"%u. ",move_count);
+                        rb->write(fhandler, move_buffer, rb->strlen(move_buffer));
+                        move_count++;
+                    }
+                    rb->write(fhandler, &(pgn_move),rb->strlen(pgn_move));
+                    rb->write(fhandler, " ",1);
+
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
                     rb->cpu_boost ( false );
 #endif
@@ -703,17 +1200,46 @@
                 break;
 #ifdef COMMAND_RESTART
             case COMMAND_RESTART:
+                /* mark the end of the game in the history file */
+                rb->write(fhandler, "\r\n\r\n",4);
+
                 GNUChess_Initialize();
+    
+                cb_init_pgn_board();
+                cb_init_pgn_game(fhandler);
+
                 cb_drawboard();
+
                 break;
 #endif
             case COMMAND_PLAY:
-                opponent = !opponent; computer = !computer;
+                /* I'm not sure this is the right thing to change the values,
+                   negating 0 will result in FF since (opponent) and (computer)
+                   are declared as short. Won't they? */
+                /* opponent = !opponent; computer = !computer; */
+                if (opponent == white) {
+                    opponent = black;
+                    computer = white;
+                } else {
+                    opponent = white;
+                    computer = black;
+                }
                 rb->splash ( 0 , "Thinking..." );
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
                 rb->cpu_boost ( true );
 #endif
-                SelectMove ( computer , 0 , cb_wt_callback );
+                SelectMove ( computer , 0 , cb_wt_callback , move_buffer);
+
+                /* save computer move */
+                cb_move_to_pgn(move_buffer, pgn_move);
+                if (computer == white) {
+                    rb->snprintf(move_buffer,4,"%u. ",move_count);
+                    rb->write(fhandler, move_buffer, rb->strlen(move_buffer));
+                    move_count++;
+                }
+                rb->write(fhandler, &(pgn_move),rb->strlen(pgn_move));
+                rb->write(fhandler, " ",1);
+
 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
                 rb->cpu_boost ( false );
 #endif
@@ -732,9 +1258,13 @@
                 break;
         }
     }
+
+    rb->close(fhandler);
     
     cb_saveposition();
+
     rb->lcd_setfont(FONT_UI);
+
     return PLUGIN_OK;
 }
 
