Chess Viewer

GNU Regex

Descrizione

Visualizzatore di partite di scacchi su terminale da file PGN. Include un parser basilare di file PGN tramite regex ed un controllo, sempre basilare, sulla correttezza delle mosse eseguite.


Sorgente

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <sys/types.h>
#include <unistd.h>

#define W_FLAG 0 /* White Player Flag */
#define B_FLAG 1 /* Black Player Flag */

/* A piece contains info on player and type:
 * b8 b7b6b5b4b3b2b1 - b8 - Player Info, b7-1 Piece Name */
#define P_FLAG(p)         ((int)  ((p) & 128) >> 7)
#define P_TYPE(p)         ((char) ((p) & 127))
#define P_SETS(type,flag) ((flag) ? 128 : 0) | ((type) & 127)

/* Coordinate on column and row is put in an unsigned char.
 * b8b7b6b5 b4b3b2b1 - b8-5 = col, b4-1 = row */
#define P_COLROW(col,row) ((((col) << 4) & 0xf0) | ((row) & 0x0f))
#define P_COL(col)        (((col) >> 4) & 0x0f)
#define P_ROW(row)        ((row) & 0x0f)

#define R_ATOI(a)         ('8' - (a))
#define R_ITOA(a)         (R_ATOI(a))
#define C_ATOI(a)         ((a) - 'a')
#define C_ITOA(a)         ((a) + 'a')

#define P_EXIST(p)        (P_TYPE(p) == 'R' || P_TYPE(p) == 'N' || \
                           P_TYPE(p) == 'B' || P_TYPE(p) == 'p' || \
                           P_TYPE(p) == 'K' || P_TYPE(p) == 'Q')

#define REGEX_INFO "^[ ]*\\[([^ ]+) ?[\"'](.+)[\"']\\]"
#define REGEX_MOVE "([0-9]+)\\.[ ]*([a-h1-8RNBKQpx:+#]+|[O-]+)[ ]*([a-h1-8RNBKQpx:+#]+|[O-]+)"
#define REGEX_MOVE_DETAILS "([RNBKQp])?([a-h])?(x)?([a-h])?([1-8])?(=[RNBKQ])?(\\+{1,2}|#)?"

typedef unsigned char Piece;
typedef Piece *       Row;
typedef Row   *       Chess;

struct _global {
	unsigned color       : 3;
	unsigned chess_print : 3;
	unsigned pause       : 2;
} global_opt = { 0, 1, 0 };

typedef struct {
     unsigned wb_flag   : 1;
     unsigned piece     : 7;
     unsigned start_col : 4;
     unsigned x         : 1;
     unsigned end_col   : 3;
     unsigned end_row   : 3;
     unsigned flags     : 2;
     unsigned promotion : 7;
} Move;

typedef struct _allow_move {
      unsigned char coord;
      struct _allow_move *next;
} AllowMoves;

/* Initialize a new 8x8 array:
 * White Pieces in row 6,7 - Black Pieces in row 0,1 */
Chess chess_new (void)
{
 const char base[8]   = "RNBQKBNR";
 Chess init;
 char j;

 init = (Chess) malloc (8 * sizeof (Row));
 for ( j = 0; j < 8; init[j] = (Row) calloc (8, sizeof (Piece)), j++ );

 for ( j = 0; j < 8; j++ ) {
       init[0][j] = P_SETS (base[j], B_FLAG);
       init[1][j] = P_SETS (    'p', B_FLAG);
       init[7][j] = P_SETS (base[j], W_FLAG);
       init[6][j] = P_SETS (    'p', W_FLAG);
 }

 return init;
}

/* Free the array created by chess_new () */
void chess_free (Chess to_free)
{
 char j;

 for ( j = 0; j < 8; free (to_free[j]), j++ );
 free (to_free);
}

/* Prints the chess in the current state. Identify White pieces with +
 * and Black pieces with -. Writes nothing if NO_PRINT is defined */
void chess_print (Chess chess)
{
 char y, x, j;

 if ( !global_opt.chess_print )
      return;

 if ( global_opt.color ) {

/* FG_BG | W: White, B: Black, G: Green, R: Red */
#define B_W(s) ("\e[30;47m" s)
#define W_B(s) ("\e[37;0m" s)
#define W_R(s) ("\e[37;41m" s)
#define B_G(s) ("\e[30;42m" s)

	  for ( y = 0; y < 8; y++ ) {
       printf (W_B("+------+------+------+------+------+------+------+------+\n"));
       for ( x = 0; x < 8; x++ )
             printf (W_B("|%s"), ((x ^ y) & 1) ? W_B("######") : B_W("      "));
       printf (W_B("|\n"));

       for ( x = 0; x < 8; x++ ) {
             if ( P_EXIST(chess[y][x]) )
                  printf (W_B("|%s %s%c%c %s"),
                          ((x ^ y) & 1) ? W_B("#") : B_W(" "),
                          (P_FLAG (chess[y][x]) ? W_R("") : B_G("")),
                          P_TYPE (chess[y][x]),
                          P_FLAG (chess[y][x]) ? '-' : '+',
                          ((x ^ y) & 1) ? W_B("#") : B_W(" "));
             else printf (W_B("|%s"), ((x ^ y) & 1) ? W_B ("#    #") : B_W("      "));
       }
       printf (W_B ("| %d\n"), 8 -  y);

       for ( x = 0; x < 8; x++ )
             printf (W_B("|%s"), ((x ^ y) & 1) ? W_B("######") : B_W("      "));
       printf (W_B("|\n"));
     }
     printf (W_B("+------+------+------+------+------+------+------+------+\n"));

     printf ("\e[0m");
 }
 else {
	  for ( y = 0; y < 8; y++ ) {
       printf ("+------+------+------+------+------+------+------+------+\n");
       for ( x = 0; x < 8; x++ )
             printf ("|%s", ((x ^ y) & 1) ? "######" : "      ");
       printf ("|\n");

       for ( x = 0; x < 8; x++ ) {
             if ( P_EXIST(chess[y][x]) )
                  printf ("|%c %c%c %c", ((x ^ y) & 1) ? '#' : ' ',
                                         P_TYPE (chess[y][x]),
                                         P_FLAG (chess[y][x]) ? '-' : '+',
                                         ((x ^ y) & 1) ? '#' : ' ');
             else printf ("|%s", ((x ^ y) & 1) ? "#    #" : "      ");
       }
       printf ("| %d\n", 8 -  y);

       for ( x = 0; x < 8; x++ )
             printf ("|%s", ((x ^ y) & 1) ? "######" : "      ");
       printf ("|\n");
     }
     printf ("+------+------+------+------+------+------+------+------+\n");
 }
 for ( x = 0; x < 8; x++ )
       printf ("   %c   ", 'A' + x);
 printf ("\n\n");

 if ( global_opt.pause ) {
	  printf ("Press ENTER to continue.. ");
	  getchar ();
 }
}

/* Parse a PGN file to get the info '[name "value"]' */
void PGN_print_info (char *pgn_data, int data_len, int *last_match)
{
 char *info;
 int off_start, off_end, rc;
 regex_t regex;
 regmatch_t match[4];

 if ( rc = regcomp (&regex, REGEX_INFO, REG_EXTENDED) ) {
        info = (char *) malloc (128 * sizeof (char));
      regerror(rc, &regex, info, 128);

      fprintf(stderr, "(%d) regcomp() failed with '%s'\n", __LINE__, info);

      exit(EXIT_FAILURE);
 }

 /* Cuts the PGN file in lines, for each line apply a regex to find an
  * info. If name and value are not empty, it print them. */
 for ( off_start = 0; off_start < data_len; off_start = off_end) {
       if ( pgn_data[off_start] == '\n' || !off_start ) {
            for ( off_end = off_start + 1; off_end < data_len; off_end++ ) {
                  if ( pgn_data[off_end] == '\n' )
                       break;
            }

            if ( (off_end - off_start - 1) < 2 )
                 continue;

            info = strndup (pgn_data + (off_start + 1), off_end - off_start);

            if ( regexec(&regex, info, 3, match, 0) )
                 continue;

            free (info);
            info = strndup (pgn_data + (off_start + 1 + match[1].rm_so),
                            match[1].rm_eo - match[1].rm_so);
            printf ("%s: ", info);
            free (info);

            info = strndup (pgn_data + (off_start + 1 + match[2].rm_so),
                            match[2].rm_eo - match[2].rm_so);
            printf ("%s\n", info);
            free (info);

            *last_match = off_start + match[0].rm_eo;
       }
       else off_end = off_start + 1;
 }

 regfree (&regex);
}

/* Every move is splitted in groups:
 * 1: Name                                    - Optional if pawn
 * 2: Start Col | End Col if 3 doesn't exists - Only if 3 setted, Optional
 * 3: x                                       - Optional
 * 4: End Col if 3 exists                     - Required
 * 5: End Row                                 - Required
 * 6: Promotion                               - Optional
 * 7: Flags ( +, ++, # )                      - Optional
 *
 * Return a Move structure.
 */
Move move_parse (char *move, int flag)
{
 Move move_details = { 0, 0, 0, 0, 0, 0, 0, 0 };
 int rc;
 char *string;
 regex_t regex;
 regmatch_t match[9];

 if ( rc = regcomp (&regex, REGEX_MOVE_DETAILS, REG_EXTENDED) ) {
      string = (char *) malloc (128 * sizeof (char));
      regerror(rc, &regex, string, 128);

      fprintf(stderr, "(%d) regcomp() failed with '%s'\n", __LINE__, string);

      exit(EXIT_FAILURE);
 }

 if ( regexec (&regex, move, 8, match, 0) ) {
      fprintf (stderr, "Move: %s - String Malformed\n", move);
      regfree (&regex);
      return move_details;
 }

 move_details.wb_flag = flag & 1;

 if ( match[1].rm_so == -1 )
      move_details.piece = P_TYPE ('p' - 'A');
 else move_details.piece = P_TYPE (move[match[1].rm_so] - 'A');

 if ( (P_TYPE (move_details.piece) + 'A') == 'p' && match[6].rm_so != -1 )
      move_details.promotion = P_TYPE (move[match[6].rm_so + 1] - 'A');

 move_details.flags = 0;
 if ( match[7].rm_so != -1 ) {
      if ( (match[7].rm_eo - match[7].rm_so) < 2 ) {
           switch (move[match[7].rm_so]) {
               case '+': move_details.flags = 1;
                         break;
               case '#': move_details.flags = 3;
                         break;
           }
      }
      else move_details.flags = 2;
 }

 if ( match[3].rm_so == -1 ) {
      move_details.x       = 0;

      if ( match[4].rm_so == -1 )
           move_details.end_col = C_ATOI (move[match[2].rm_so]);
      else {
           move_details.start_col = move[match[2].rm_so] - ('a' - 1);
           move_details.end_col = C_ATOI (move[match[4].rm_so]);
	  }
 }
 else {
      move_details.x = 1;

      if ( match[2].rm_so != -1 )
           move_details.start_col = move[match[2].rm_so] - ('a' - 1);
      else move_details.start_col = 0;

      move_details.end_col = C_ATOI (move[match[4].rm_so]);
 }
 move_details.end_row = R_ATOI (move[match[5].rm_so]);

 regfree (&regex);

 return move_details;
}

/* Add a coordinate in AllowMoves list */
AllowMoves *allow_moves_add (AllowMoves *list,
                             unsigned char col, unsigned char row)
{
 AllowMoves *p;

 p = (AllowMoves *) malloc (sizeof (AllowMoves));
 p->coord = P_COLROW (col, row);
 p->next = list;

 return p;
}

/* Check if coord passed exist in AllowMoves list */
int allow_moves_check (AllowMoves *list, unsigned char coord)
{
 AllowMoves *p;

 for ( p = list; p; p = p->next )
       if ( p->coord == coord )
            return 1;

 return 0;
}

/* Free AllowMoves list */
void allow_moves_free (AllowMoves *list)
{
 AllowMoves *p;

 if ( !list )
      return;

 do {
     p = list->next;
     free (list);
     list = p;
 } while ( list );
}

/* Check if the move passed is valid. Return 1 if Valid, 0 if not. */
int check_move (Chess chess, int flag, char piece,
                char s_col, char s_row, char e_col, char e_row)
{
 int j;
 unsigned char coord_start, coord_end;
 AllowMoves *allows = NULL;

 coord_start = P_COLROW (C_ATOI (s_col), R_ATOI (s_row));
 coord_end   = P_COLROW (C_ATOI (e_col), R_ATOI (e_row));

 if ( coord_start == coord_end )
      return 0;

#define POS(row,r_add,col,c_add) (chess[R_ATOI (row) + (r_add)][C_ATOI (col) + (c_add)])

#define MOVE_ADD(m_list,row,r_add,col,c_add,extra)                         \
             if ( !P_EXIST (POS ((row), (r_add), (col), (c_add))) ) {      \
                  m_list = allow_moves_add (m_list,                        \
                                            C_ATOI (col) + (c_add),        \
                                            R_ATOI (row) + (r_add));       \
             }                                                             \
        else if ( P_FLAG (POS ((row), (r_add), (col), (c_add))) != flag) { \
                  m_list = allow_moves_add (m_list,                        \
                                            C_ATOI (col) + (c_add),        \
                                            R_ATOI (row) + (r_add));       \
                  extra;                                                   \
        }

 switch (piece) {
     case 'p':
       if ( (flag && s_row > '1') || (!flag && s_row < '8') ) {
            /* Move Forward */
            if ( !P_EXIST (POS (s_row, flag ? 1 : -1, s_col, 0)) ) {
                 allows = allow_moves_add (allows, C_ATOI (s_col),
                                           R_ATOI (s_row) + (flag ? 1 : -1));

                 if ( (flag && s_row == '7') || (!flag && s_row == '2') ) {
                      if ( !P_EXIST (POS (s_row, flag ? 2 : -2, s_col, 0)) )
                           allows = allow_moves_add (allows, C_ATOI (s_col),
                                                     R_ATOI (s_row) + (flag ? 2 : -2));
                 }
            }

            /* Eat */
            if ( s_col > 'a' )
                 if ( P_EXIST (POS (s_row, flag ? 1 : -1, s_col, -1))
                   && P_FLAG (POS (s_row, flag ? 1 : -1, s_col, -1)) != flag )
                      allows = allow_moves_add (allows, C_ATOI (s_col) - 1,
                                                R_ATOI (s_row) + (flag ? 1 : -1));

            if ( s_col < 'h' )
                 if ( P_EXIST (POS (s_row, flag ? 1 : -1, s_col, 1))
                   && P_FLAG (POS (s_row, flag ? 1 : -1, s_col, 1)) != flag )
                      allows = allow_moves_add (allows, C_ATOI (s_col) + 1,
                                                R_ATOI (s_row) + (flag ? 1 : -1));
       }
       break;
     case 'R':
       /* right, left, bottom, top */
       for ( j = C_ATOI (s_col); j < 8; j++ ) {
             MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break);
       }
       for ( j = C_ATOI (s_col); j > -1; j-- ) {
             MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break);
       }
       for ( j = R_ATOI (s_row); j < 8; j++ ) {
             MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break);
       }
       for ( j = R_ATOI (s_row); j > -1; j-- ) {
             MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break);
       }
       break;
     case 'N':
       /*     5   3
        * 7 _|x|_|x|_
        *   x|_|_|_|x 1
        *   _|_|x|_|_
        * 8 x|_|_|_|x 2
        *    |x| |x|
        *     6   4
        */
       if ( s_col < 'g' && s_row < '8' ) {
            MOVE_ADD (allows, s_row, -1, s_col, +2, 0);
       }
       if ( s_col < 'g' && s_row > '1' ) {
            MOVE_ADD (allows, s_row, +1, s_col, +2, 0);
       }
       if ( s_col < 'h' && s_row < '7' ) {
            MOVE_ADD (allows, s_row, -2, s_col, +1, 0);
       }
       if ( s_col < 'h' && s_row > '2' ) {
            MOVE_ADD (allows, s_row, +2, s_col, +1, 0);
       }
       if ( s_col > 'a' && s_row < '7' ) {
            MOVE_ADD (allows, s_row, -2, s_col, -1, 0);
       }
       if ( s_col > 'a' && s_row > '2' ) {
            MOVE_ADD (allows, s_row, +2, s_col, -1, 0);
       }
       if ( s_col > 'b' && s_row < '8' ) {
            MOVE_ADD (allows, s_row, -1, s_col, -2, 0);
       }
       if ( s_col > 'b' && s_row > '1' ) {
            MOVE_ADD (allows, s_row, +1, s_col, -2, 0);
       }
       break;
     case 'B':
       /* bottom-right, bottom-left, top-right, top-left */
       for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) + j) < 8; j++ ) {
		     MOVE_ADD (allows, s_row, j, s_col, j, break);
	   }
	   for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) - j) > -1; j++ ) {
		     MOVE_ADD (allows, s_row, j, s_col, j * -1, break);
	   }
	   for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) + j) < 8; j++ ) {
		     MOVE_ADD (allows, s_row, j * -1, s_col, j, break);
	   }
	   for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) - j) > -1; j++ ) {
		     MOVE_ADD (allows, s_row, j * -1, s_col, j * -1, break);
	   }
       break;
     case 'Q':
       /* right, left, bottom, top */
       for ( j = C_ATOI (s_col); j < 8; j++ ) {
             MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break);
       }
       for ( j = C_ATOI (s_col); j > -1; j-- ) {
             MOVE_ADD (allows, s_row, 0, C_ITOA (j), 0, break);
       }
       for ( j = R_ATOI (s_row); j < 8; j++ ) {
             MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break);
       }
       for ( j = R_ATOI (s_row); j > -1; j-- ) {
             MOVE_ADD (allows, R_ITOA (j), 0, s_col, 0, break);
       }

       /* bottom-right, bottom-left, top-right, top-left */
       for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) + j) < 8; j++ ) {
		     MOVE_ADD (allows, s_row, j, s_col, j, break);
	   }
	   for ( j = 0; (R_ATOI (s_row) + j) < 8 && (C_ATOI (s_col) - j) > -1; j++ ) {
		     MOVE_ADD (allows, s_row, j, s_col, j * -1, break);
	   }
	   for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) + j) < 8; j++ ) {
		     MOVE_ADD (allows, s_row, j * -1, s_col, j, break);
	   }
	   for ( j = 0; (R_ATOI (s_row) - j) > -1 && (C_ATOI (s_col) - j) > -1; j++ ) {
		     MOVE_ADD (allows, s_row, j * -1, s_col, j * -1, break);
	   }

       break;
     case 'K':
       /* right, left, top, bottom */
       if ( s_col < 'h' )
            MOVE_ADD (allows, s_row, +0, s_col, +1, 0);
       if ( s_col > 'a' )
            MOVE_ADD (allows, s_row, +0, s_col, -1, 0);
       if ( s_row < '8' )
            MOVE_ADD (allows, s_row, -1, s_col, +0, 0);
       if ( s_row > '1' )
            MOVE_ADD (allows, s_row, +1, s_col, +0, 0);

       /* top-right, bottom-right, top-left, bottom-left */
       if ( s_col < 'h' && s_row < '8' )
            MOVE_ADD (allows, s_row, -1, s_col, +1, 0);
       if ( s_col < 'h' && s_row > '1' )
            MOVE_ADD (allows, s_row, +1, s_col, +1, 0);
       if ( s_col > 'a' && s_row < '8' )
            MOVE_ADD (allows, s_row, -1, s_col, -1, 0);
       if ( s_col > 'a' && s_row > '1' )
            MOVE_ADD (allows, s_row, +1, s_col, -1, 0);

       break;
 }

 if ( allow_moves_check (allows, coord_end) ) {
      allow_moves_free (allows);
      return 1;
 }

 allow_moves_free (allows);

 return 0;
}

/* Get current coordinate of a piece. If col or row are not setted (
 * equal to 0 ) it search the piece passed in the whole col or row.
 * col and e_col can be: 'a', 'b', ..., 'h'
 * row and e_row can be: '1', '2', ..., '8'
 *
 * If check_flag is 1 call check_move function () to check if the
 * move is valid.
 *
 * Return 1 if Valid, 0 if not.
 */
int get_coord (Chess chess, int flag, char piece, char col, char row,
               unsigned char *result, int check_flag, char e_col, char e_row)
{
 char f_col, f_row;
 char sc = 0, ec = 7, sr = 0, er = 7;

 if ( row ) {
      sr = R_ATOI (row);
      er = sr;
 }

 if ( col ) {
      sc = C_ATOI (col);
      ec = sc;
 }

 for ( f_row = sr; f_row <= er; f_row++ ) {
       for ( f_col = sc; f_col <= ec; f_col++ ) {
             if ( P_TYPE (chess[f_row][f_col]) == piece
               && P_FLAG (chess[f_row][f_col]) == flag ) {
                  *result = P_COLROW (f_col, f_row);
                  if ( check_flag ) {
                       if ( check_move (chess, flag, piece,
                                        C_ITOA (f_col), R_ITOA (f_row), e_col, e_row) )
                            return 1;
                       else continue;
                  }
                  else return 1;
             }

       }
 }

 return 0;
}

/* Do the move passed for the flag ( White or Black ) player. */
void chess_move (Chess *chess, char *move, int flag)
{
 Move move_details;
 unsigned char p_col;
 unsigned char K_coord, T_coord, coord;

 if ( !strcmp (move, "O-O") ) {
      if ( !get_coord (*chess, flag, 'K', 'e', flag ? '8' : '1', &K_coord, 0, 0, 0) ) {
           fprintf (stderr, "Invalid Move: '%s' - %s\n",
                            move, flag ? "Black" : "White");
           exit (1);
      }

      if ( !get_coord (*chess, flag, 'R', 'h', flag ? '8' : '1', &T_coord, 0, 0, 0) ) {
           fprintf (stderr, "Invalid Move: '%s' - %s\n",
                            move, flag ? "Black" : "White");
           exit (1);
      }

      if ( (flag  && (P_EXIST ((*chess)[0][5]) ||  P_EXIST ((*chess)[0][6])))
        || (!flag && (P_EXIST ((*chess)[7][5]) ||  P_EXIST ((*chess)[7][6]))) ) {
           fprintf (stderr, "Invalid Move: '%s' - %s\n",
                            move, flag ? "Black" : "White");
           exit (1);
      }

      (*chess)[P_ROW (K_coord)][P_COL (K_coord)] = 0;
      (*chess)[flag ? 0 : 7][6] = P_SETS ('K', flag);
      (*chess)[P_ROW (T_coord)][P_COL (T_coord)] = 0;
      (*chess)[flag ? 0 : 7][5] = P_SETS ('R', flag);

      return;
 }

 if ( !strcmp (move, "O-O-O") ) {
      if ( !get_coord (*chess, flag, 'K', 'e', flag ? '8' : '1',
                       &K_coord, 0, 0, 0) ) {
           fprintf (stderr, "Invalid Move: '%s' - %s\n",
                            move, flag ? "Black" : "White");
           exit (1);
      }

      if ( !get_coord (*chess, flag, 'R', 'a', flag ? '8' : '1',
                       &T_coord, 0, 0, 0) ) {
           fprintf (stderr, "Invalid Move: '%s' - %s\n",
                            move, flag ? "Black" : "White");
           exit (1);
      }

      if ( (flag  && (P_EXIST ((*chess)[0][1])
                  ||  P_EXIST ((*chess)[0][2])
                  ||  P_EXIST ((*chess)[0][3])))
        || (!flag && (P_EXIST ((*chess)[7][1])
                  ||  P_EXIST ((*chess)[7][2])
                  ||  P_EXIST ((*chess)[7][3]))) ) {
           fprintf (stderr, "Invalid Move: '%s' - %s\n",
                            move, flag ? "Black" : "White");
           exit (1);
      }

      (*chess)[P_ROW (K_coord)][P_COL (K_coord)] = 0;
      (*chess)[flag ? 0 : 7][2] = P_SETS ('K', flag);
      (*chess)[P_ROW (T_coord)][P_COL (T_coord)] = 0;
      (*chess)[flag ? 0 : 7][3] = P_SETS ('R', flag);

      return;
 }

 move_details = move_parse (move, flag);

 if ( move_details.start_col )
      p_col = move_details.start_col + ('a' - 1);
 else p_col = 0;

 if ( !get_coord (*chess, move_details.wb_flag,
                  move_details.piece + 'A', p_col, 0, &coord, 1,
                  C_ITOA (move_details.end_col), R_ITOA (move_details.end_row)) ) {
      fprintf (stderr, "Invalid Move: '%s' - %s\n",
                        move, flag ? "Black" : "White");
      exit (1);
 }

 (*chess)[P_ROW (coord)][P_COL (coord)] = 0;

 if ( move_details.promotion )
      (*chess)[move_details.end_row][move_details.end_col] =
               P_SETS (move_details.promotion + 'A', move_details.wb_flag);
 else (*chess)[move_details.end_row][move_details.end_col] =
               P_SETS (move_details.piece + 'A', move_details.wb_flag);
}

/* Search the moves in the PGN file passed and print the chess status
 * after every move for each player */
void PGN_print_moves (char *pgn_data, int data_len)
{
 Chess chess;
 char *string;
 int rc, n;
 regex_t regex;
 regmatch_t match[5];

 chess = chess_new ();
 printf ("\n0) Start\n\n"),
 chess_print (chess);

 if ( rc = regcomp (&regex, REGEX_MOVE, REG_EXTENDED | REG_NEWLINE) ) {
      string = (char *) malloc (128 * sizeof (char));
      regerror(rc, &regex, string, 128);

      fprintf (stderr, "(%d) regcomp() failed with '%s'\n", __LINE__, string);

      exit(EXIT_FAILURE);
 }

 for ( n = 0;  n < data_len; n += match[0].rm_eo ) {
       if ( regexec(&regex, pgn_data + n, 4, match, 0) )
            break;

       if ( (match[2].rm_eo - match[2].rm_so) < 2
         || (match[3].rm_eo - match[3].rm_so) < 2 )
            continue;

       string = strndup (pgn_data + (n + match[1].rm_so),
                         match[1].rm_eo - match[1].rm_so);

       printf ("------------------------ %6s ------------------------\n", string);
       free (string);

       string = strndup (pgn_data + (n + match[2].rm_so),
                         match[2].rm_eo - match[2].rm_so);
       printf ("White[+]: %s\n", string);
       chess_move (&chess, string, W_FLAG);
       chess_print (chess);
       free (string);

       if ( match[3].rm_so != -1 ) {
            string = strndup (pgn_data + (n + match[3].rm_so),
                              match[3].rm_eo - match[3].rm_so);
            printf ("Black[-]: %s\n", string);
            chess_move (&chess, string, B_FLAG);
            free (string);

            chess_print (chess);
       }
       else {
            chess_print (chess);
            break;
       }
 }

 regfree (&regex);
 chess_free (chess);
}

int main (int argc, char *argv[])
{
 FILE *input;
 char *data;
 int data_size, last_match;
 int opt;

 if ( argc < 2 ) {
      fprintf (stderr, "Usage: %s  [Options] <PGN file>\n"
                       " Options:\n"
                       "  -c  Print with shell color"
                       "  -i  Print only info"
                       "  -p  Pause every move", *argv);
      return 1;
 }

 while ( (opt = getopt (argc, argv, "cip")) != -1 ) {
	     switch (opt) {
			 case 'c': global_opt.color = 1;
			           break;
			 case 'i': global_opt.chess_print = 0;
			           break;
			 case 'p': global_opt.pause = 1;
			           break;
	     }
 }

 if ( !(input = fopen (argv[argc-1], "rb")) ) {
      fprintf (stderr, "Cannot open '%s'\n", argv[argc-1]);
      return 1;
 }
 fseek (input, 0, SEEK_END);
 data_size = ftell (input);
 rewind (input);

 data = (char *) malloc (data_size * sizeof (char));
 fread (data, sizeof (char), data_size, input);
 fclose (input);

 printf ("Legend: - Black, + White\n\n");

 PGN_print_info (data, data_size, &last_match);
 PGN_print_moves (data + last_match, data_size);

 free (data);
}