Chess Viewer

GNU Regex

Description

Chess games viewer from PGN files on terminal. It includes a basic PGN files parser using regex and a, even basic, check todeterminate the correctness of the moves performed.


Source

#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);
}