Browse Source

feat: notation_move_from_string; feat: actually keep track of who makes the move; bugfix: convert char to int the right way

master
parent
commit
238c66d7a6
  1. 5
      TODO
  2. 76
      src/algebraic_notation.c
  3. 16
      src/algebraic_notation.h
  4. 71
      src/board.c
  5. 26
      src/board.h
  6. 29
      src/chess.c
  7. 2
      src/chess.h

5
TODO

@ -1,2 +1,3 @@
1. Algebraic notation: notation_move_from_string, notation_move_to_string, documentation 1. Algebraic notation: notation_move_to_string, documentation
2. Board: board_is_move_legal, add other piece's moves 2. Board: board_is_move_legal, add other piece's moves
3. Chess: output special error message for each type of illegal move so it's clear what went wrong

76
src/algebraic_notation.c

@ -17,6 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -28,42 +29,43 @@ Position notation_tile_to_position(TileNotation notation) {
Position pos; Position pos;
switch (notation.file) { switch (notation.file) {
case 'a': { case 'a': {
pos.column = 1; pos.column = 0;
break; break;
} }
case 'b': { case 'b': {
pos.column = 2; pos.column = 1;
break; break;
} }
case 'c': { case 'c': {
pos.column = 3; pos.column = 2;
break; break;
} }
case 'd': { case 'd': {
pos.column = 4; pos.column = 3;
break; break;
} }
case 'e': { case 'e': {
pos.column = 5; pos.column = 4;
break; break;
} }
case 'f': { case 'f': {
pos.column = 6; pos.column = 5;
break; break;
} }
case 'g': { case 'g': {
pos.column = 7; pos.column = 6;
break; break;
} }
case 'h': { case 'h': {
pos.column = 8; pos.column = 7;
break; break;
} }
default: { default: {
break; break;
} }
} }
pos.row = notation.rank; // pos.row = notation.rank - 1;
pos.row = (8 - (notation.rank));
return pos; return pos;
} }
@ -101,7 +103,7 @@ TileNotation notation_tile_from_string(const char* tile_string) {
TileNotation tile; TileNotation tile;
tile.file = tile_string[0]; tile.file = tile_string[0];
tile.rank = (unsigned int) tile_string[1]; tile.rank = (unsigned int) (tile_string[1] - '0'); // Convert to proper int
return tile; return tile;
} }
@ -239,6 +241,28 @@ int notation_is_piece_char(char character) {
} }
/*
Returns a piece deduced from character, returns NONE piece if couldn't deduce a piece type.
Big characters are for WHITE pieces, small for BLACK pieces.
*/
Piece notation_piece_from_char(char character) {
if (character == 'K') return piece_new(WHITE, KING);
else if (character == 'k') return piece_new(BLACK, KING);
else if (character == 'R') return piece_new(WHITE, ROOK);
else if (character == 'r') return piece_new(BLACK, ROOK);
else if (character == 'B') return piece_new(WHITE, BISHOP);
else if (character == 'b') return piece_new(BLACK, BISHOP);
else if (character == 'N') return piece_new(WHITE, KNIGHT);
else if (character == 'n') return piece_new(BLACK, KNIGHT);
else if (character == 'P') return piece_new(WHITE, PAWN);
else if (character == 'p') return piece_new(BLACK, PAWN);
else if (character == 'Q') return piece_new(WHITE, QUEEN);
else if (character == 'q') return piece_new(BLACK, QUEEN);
return piece_new(WHITE, NONE);
}
char* notation_move_to_string(MoveNotation move) { char* notation_move_to_string(MoveNotation move) {
char* move_notation = (char*) malloc(sizeof(char) * 10); char* move_notation = (char*) malloc(sizeof(char) * 10);
if (!move_notation) { if (!move_notation) {
@ -302,8 +326,20 @@ char* notation_move_to_string(MoveNotation move) {
} }
/*
Accepts a move string which consists of an origin square, piece name
(upper case for WHITE, lower case for BLACK) and a square to move to
in algebraic notation.
Returns a notated move with origin, target and piece values set. Move type is to be
determined by the engine.
Returns NULL if technical errors were encountered, move_string does not contain enough information
*/
MoveNotation* notation_move_from_string(const char* move_string) { MoveNotation* notation_move_from_string(const char* move_string) {
if (!move_string) { unsigned int len = strlen(move_string);
if (!move_string || len < 5) {
return NULL; return NULL;
} }
@ -312,11 +348,25 @@ MoveNotation* notation_move_from_string(const char* move_string) {
return NULL; return NULL;
} }
// printf();
if (notation_is_piece_char(move_string[0])) {
// Origin tile
TileNotation origin = notation_tile_from_string(move_string);
// Piece
Piece piece = notation_piece_from_char(move_string[2]);
if (piece.type == NONE) {
// No piece information given
return NULL;
} }
// Target tile
TileNotation target = notation_tile_from_string(move_string + 3);
move->origin = origin;
move->piece = piece;
move->target = target;
move->move_type = 0;
return move; return move;
} }

16
src/algebraic_notation.h

@ -59,6 +59,12 @@ char notation_piece_to_char(Piece piece);
// Returns 1 if character is one of the piece characters, 0 otherwise // Returns 1 if character is one of the piece characters, 0 otherwise
int notation_is_piece_char(char character); int notation_is_piece_char(char character);
/*
Returns a piece deduced from character, returns NONE piece if couldn't deduce a piece type.
Big characters are for WHITE pieces, small for BLACK pieces.
*/
Piece notation_piece_from_char(char character);
typedef struct MoveNotation { typedef struct MoveNotation {
Piece piece; Piece piece;
@ -71,6 +77,16 @@ typedef struct MoveNotation {
char* notation_move_to_string(MoveNotation move); char* notation_move_to_string(MoveNotation move);
/*
Accepts a move string which consists of an origin square, piece name
(upper case for WHITE, lower case for BLACK) and a square to move to
in algebraic notation.
Returns a notated move with origin, target and piece values set. Move type is to be
determined by the engine.
Returns NULL if technical errors were encountered, move_string does not contain enough information
*/
MoveNotation* notation_move_from_string(const char* move_string); MoveNotation* notation_move_from_string(const char* move_string);

71
src/board.c

@ -471,26 +471,41 @@ Position* board_get_rook_moves(const Board* board, const Tile* rook_tile, unsign
/* /*
Checks if a move is legal on this board. Checks if a move is legal or not (and reason) on this board.
Returns 1 - if the move is legal, 0 - otherwise Returns MoveLegality
*/ */
int board_is_move_legal(const Board* board, Move move) { MoveLegality board_is_move_legal(const Board* board, Move move, Color color_plays) {
// Check if the tiles are not out of bounds // Check if the tiles are not out of bounds
if (!board_is_tile_in_bounds(board, move.origin) || if (!board_is_tile_in_bounds(board, move.origin) ||
!board_is_tile_in_bounds(board, move.target) !board_is_tile_in_bounds(board, move.target)
) { ) {
return 0; return ILLOUTOFBOUNDS;
} }
// Check if there is a piece at all // Check if there is a piece at all
if (board_get_tile(board, move.origin)->piece.type == NONE) { if (board_get_tile(board, move.origin)->piece.type == NONE) {
return 0; return ILLNOPIECE;
} }
Tile* origin_tile = board_get_tile(board, move.origin); Tile* origin_tile = board_get_tile(board, move.origin);
Tile* target_tile = board_get_tile(board, move.target); Tile* target_tile = board_get_tile(board, move.target);
// Check if the piece is of the player's color
if (origin_tile->piece.color != color_plays) {
return ILLNOTYOURPIECE;
}
// See if the target tile is of the allied color
if (tile_is_occupied(*target_tile)) {
// The tile is occupied
if (target_tile->piece.color == color_plays) {
// The pieces are allied, can't go there!
return ILLSAMECOLOR;
}
}
// Check based on the piece type // Check based on the piece type
Piece origin_piece = origin_tile->piece; Piece origin_piece = origin_tile->piece;
@ -498,23 +513,14 @@ int board_is_move_legal(const Board* board, Move move) {
case KING: { case KING: {
if (position_distance(move.origin, move.target) > 1) { if (position_distance(move.origin, move.target) > 1) {
// Too far away // Too far away
return 0; return ILLINVALIDPIECEMOVEMENT;
} }
// See if the target tile is occupied or not return LEGAL;
if (tile_is_occupied(*target_tile)) {
// The tile is occupied
if (target_tile->piece.color == origin_piece.color) {
// The pieces are allied, can't go there
return 0;
}
}
return 1;
} }
case QUEEN: { case QUEEN: {
return 1; return LEGAL;
} }
case ROOK: { case ROOK: {
@ -532,49 +538,54 @@ int board_is_move_legal(const Board* board, Move move) {
{ {
// This position is allowed to be moved to // This position is allowed to be moved to
free(legal_positions); free(legal_positions);
return 1; return LEGAL;
} }
} }
free(legal_positions); free(legal_positions);
return 0; return LEGAL;
} }
case KNIGHT: { case KNIGHT: {
return 1; return LEGAL;
} }
case BISHOP: { case BISHOP: {
return 1; return LEGAL;
} }
case PAWN: { case PAWN: {
return 1; return LEGAL;
} }
default: { default: {
// Non reachable // Non reachable
return 0; return LEGAL;
} }
} }
} }
/* /*
Moves piece from one place to other, Moves piece from one place to other, does not
checking whether it is a legal move or not. check if move is legal or not.
Returns 1 if a move has been successfully done, Returns 1 if move has been successfully done, 0 - otherwise
0 - if it is an illegal move
*/ */
int board_process_move(Board* board, Move move) { int board_make_move(Board* board, Move move) {
if (!board_is_move_legal(board, move)) { if (!board) {
return 0; return 0;
} }
// The tiles are legal, no need to re-check
Tile* tile_origin = board_get_tile(board, move.origin); Tile* tile_origin = board_get_tile(board, move.origin);
if (!tile_origin) {
return 0;
}
Tile* tile_target = board_get_tile(board, move.target); Tile* tile_target = board_get_tile(board, move.target);
if (!tile_target) {
return 0;
}
// Move the piece from one tile to another // Move the piece from one tile to another
tile_target->piece = tile_origin->piece; tile_target->piece = tile_origin->piece;

26
src/board.h

@ -123,21 +123,31 @@ Position* board_get_king_moves(const Board* board, const Tile* king_tile, unsign
Position* board_get_rook_moves(const Board* board, const Tile* rook_tile, unsigned int* pos_count); Position* board_get_rook_moves(const Board* board, const Tile* rook_tile, unsigned int* pos_count);
// Move legality status
typedef enum MoveLegality {
LEGAL,
ILLSAMECOLOR,
ILLINVALIDPIECEMOVEMENT,
ILLOUTOFBOUNDS,
ILLNOPIECE,
ILLNOTYOURPIECE
} MoveLegality;
/* /*
Checks if a move is legal on this board. Checks if a move is legal or not (and reason) on this board.
Returns 1 - if the move is legal, 0 - otherwise Returns MoveLegality
*/ */
int board_is_move_legal(const Board* board, Move move); MoveLegality board_is_move_legal(const Board* board, Move move, Color color_plays);
/* /*
Moves piece from one place to other, Moves piece from one place to other, does not
checking whether it is a legal move or not. check if move is legal or not.
Returns 1 if a move has been successfully done, Returns 1 if move has been successfully done, 0 - otherwise
0 - if it is an illegal move
*/ */
int board_process_move(Board* board, Move move); int board_make_move(Board* board, Move move);
#endif #endif

29
src/chess.c

@ -37,6 +37,11 @@ Chess* chess_new(Options options) {
game->turn = WHITE; game->turn = WHITE;
game->state = READY; game->state = READY;
game->options = options; game->options = options;
if (options.play_as_white == 1) {
game->plays_as = WHITE;
} else {
game->plays_as = BLACK;
}
return game; return game;
} }
@ -129,23 +134,37 @@ void chess_next_turn(Chess* chess) {
// Keep asking for a legitimate move // Keep asking for a legitimate move
Move* move = NULL; Move* move = NULL;
int result = 0; MoveLegality result = 0;
while (1) { while (1) {
// Get a move // Get a move
move = chess_take_move_input(chess); move = chess_take_move_input(chess);
if (!move) { if (!move) {
printf("Invalid move input!\n"); printf("Invalid move input! (opt, where o - origin tile, p - piece, t - target tile: a2Pa4 moves white pawn at a2 to a4)\n");
continue; continue;
} }
// Make a move printf("(%d %d) --- (%d %d)\n", move->origin.row, move->origin.column, move->target.row, move->target.column);
result = board_process_move(chess->board, *move);
if (!result) {
// Check if it's a valid move
result = board_is_move_legal(chess->board, *move, chess->turn);
if (result != LEGAL) {
// TODO: output special message for each illegal move
printf("Illegal move!\n"); printf("Illegal move!\n");
free(move); free(move);
continue; continue;
} }
// Make a move!
int move_made = board_make_move(chess->board, *move);
if (!move_made) {
printf("Failed to make a move!\n"); // Should not be reacheable
free(move);
continue;
}
break; break;
} }
free(move); free(move);

2
src/chess.h

@ -37,12 +37,14 @@ typedef struct Options {
FILE* input; FILE* input;
FILE* output; FILE* output;
int use_ansi; int use_ansi;
int play_as_white;
} Options; } Options;
typedef struct Chess { typedef struct Chess {
Board* board; Board* board;
Color turn; Color turn;
Color plays_as;
unsigned int moves_made; unsigned int moves_made;
unsigned int timer_white_ms; unsigned int timer_white_ms;
unsigned int timer_black_ms; unsigned int timer_black_ms;

Loading…
Cancel
Save