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
2. Board: board_is_move_legal, add other piece's moves
1. Algebraic notation: notation_move_to_string, documentation
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 <stdlib.h>
#include <string.h>
@ -28,42 +29,43 @@ Position notation_tile_to_position(TileNotation notation) {
Position pos;
switch (notation.file) {
case 'a': {
pos.column = 1;
pos.column = 0;
break;
}
case 'b': {
pos.column = 2;
pos.column = 1;
break;
}
case 'c': {
pos.column = 3;
pos.column = 2;
break;
}
case 'd': {
pos.column = 4;
pos.column = 3;
break;
}
case 'e': {
pos.column = 5;
pos.column = 4;
break;
}
case 'f': {
pos.column = 6;
pos.column = 5;
break;
}
case 'g': {
pos.column = 7;
pos.column = 6;
break;
}
case 'h': {
pos.column = 8;
pos.column = 7;
break;
}
default: {
break;
}
}
pos.row = notation.rank;
// pos.row = notation.rank - 1;
pos.row = (8 - (notation.rank));
return pos;
}
@ -101,7 +103,7 @@ TileNotation notation_tile_from_string(const char* tile_string) {
TileNotation tile;
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;
}
@ -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* move_notation = (char*) malloc(sizeof(char) * 10);
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) {
if (!move_string) {
unsigned int len = strlen(move_string);
if (!move_string || len < 5) {
return NULL;
}
@ -312,11 +348,25 @@ MoveNotation* notation_move_from_string(const char* move_string) {
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;
}

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
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 {
Piece piece;
@ -71,6 +77,16 @@ typedef struct MoveNotation {
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);

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
if (!board_is_tile_in_bounds(board, move.origin) ||
!board_is_tile_in_bounds(board, move.target)
) {
return 0;
return ILLOUTOFBOUNDS;
}
// Check if there is a piece at all
if (board_get_tile(board, move.origin)->piece.type == NONE) {
return 0;
return ILLNOPIECE;
}
Tile* origin_tile = board_get_tile(board, move.origin);
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
Piece origin_piece = origin_tile->piece;
@ -498,23 +513,14 @@ int board_is_move_legal(const Board* board, Move move) {
case KING: {
if (position_distance(move.origin, move.target) > 1) {
// Too far away
return 0;
return ILLINVALIDPIECEMOVEMENT;
}
// See if the target tile is occupied or not
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;
return LEGAL;
}
case QUEEN: {
return 1;
return LEGAL;
}
case ROOK: {
@ -532,49 +538,54 @@ int board_is_move_legal(const Board* board, Move move) {
{
// This position is allowed to be moved to
free(legal_positions);
return 1;
return LEGAL;
}
}
free(legal_positions);
return 0;
return LEGAL;
}
case KNIGHT: {
return 1;
return LEGAL;
}
case BISHOP: {
return 1;
return LEGAL;
}
case PAWN: {
return 1;
return LEGAL;
}
default: {
// Non reachable
return 0;
return LEGAL;
}
}
}
/*
Moves piece from one place to other,
checking whether it is a legal move or not.
Moves piece from one place to other, does not
check if move is legal or not.
Returns 1 if a move has been successfully done,
0 - if it is an illegal move
Returns 1 if move has been successfully done, 0 - otherwise
*/
int board_process_move(Board* board, Move move) {
if (!board_is_move_legal(board, move)) {
int board_make_move(Board* board, Move move) {
if (!board) {
return 0;
}
// The tiles are legal, no need to re-check
Tile* tile_origin = board_get_tile(board, move.origin);
if (!tile_origin) {
return 0;
}
Tile* tile_target = board_get_tile(board, move.target);
if (!tile_target) {
return 0;
}
// Move the piece from one tile to another
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);
// 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,
checking whether it is a legal move or not.
Moves piece from one place to other, does not
check if move is legal or not.
Returns 1 if a move has been successfully done,
0 - if it is an illegal move
Returns 1 if move has been successfully done, 0 - otherwise
*/
int board_process_move(Board* board, Move move);
int board_make_move(Board* board, Move move);
#endif

29
src/chess.c

@ -37,6 +37,11 @@ Chess* chess_new(Options options) {
game->turn = WHITE;
game->state = READY;
game->options = options;
if (options.play_as_white == 1) {
game->plays_as = WHITE;
} else {
game->plays_as = BLACK;
}
return game;
}
@ -129,23 +134,37 @@ void chess_next_turn(Chess* chess) {
// Keep asking for a legitimate move
Move* move = NULL;
int result = 0;
MoveLegality result = 0;
while (1) {
// Get a move
move = chess_take_move_input(chess);
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;
}
// Make a move
result = board_process_move(chess->board, *move);
if (!result) {
printf("(%d %d) --- (%d %d)\n", move->origin.row, move->origin.column, move->target.row, move->target.column);
// 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");
free(move);
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;
}
free(move);

2
src/chess.h

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

Loading…
Cancel
Save