diff --git a/build/chess b/build/chess new file mode 100755 index 0000000..164a3e4 Binary files /dev/null and b/build/chess differ diff --git a/src/chess.c b/src/chess.c deleted file mode 100644 index 85cc514..0000000 --- a/src/chess.c +++ /dev/null @@ -1,426 +0,0 @@ -#include -#include "chess.h" -#include "util.h" - -void init_board(board_t board) { - for (int i = 0; i < BOARD_VERTICAL; i++) { - for (int j = 0; j < BOARD_HORIZONTAL; j++) { - board[i][j] = BOARD_DEFAULT_CONFIG[i][j]; - } - } -} - -void select_player(player_e *player, unsigned int round_count) { - if (round_count == 1) { - *player = WHITE; - return; - } - - if (*player == WHITE) { - *player = BLACK; - return; - } - - if (*player == BLACK) { - *player = WHITE; - return; - } -} - -void show_help() { - puts( - "movement help:\n\ - there is a prompt asking for where to move a piece every turn\n\ - format: e.g. you want to move a knight from b1 to a3:\n\ - you'd type -> b1 a3 <- (without the arrows)\n" - ); -} - -// returns false if the numbers of letters are out of bounds -bool is_move_in_bounds(move_t move) { - if ( - is_int_in_range(move.from.x, 0, 7) && - is_int_in_range(move.from.y, 0, 7) && - is_int_in_range(move.to.x, 0, 7) && - is_int_in_range(move.to.y, 0, 7) - ) { - return true; - } else { - return false; - } -} - -bool get_move(board_t board, move_t *move) { - char _x_from, _x_to; - int _y_from, _y_to; - printf("move >> "); - scanf(" %c%d %c%d", - &_x_from, - &_y_from, - &_x_to, - &_y_to - ); - - move->from.x = _x_from-96-1; // char to int conversion - move->from.y = _y_from-1; - - move->to.x = _x_to-96-1; - move->to.y = _y_to-1; - - move->piece = board[move->from.y][move->from.x]; - - return is_move_in_bounds(*move); -} - -bool compare_pos(pos_t a, pos_t b) { - return a.x == b.x && a.y == b.y; -} - -pos_t pos_multiply_scalar(pos_t a, int b) { - return (pos_t){a.x*b, a.y*b}; -} - -pos_t pos_add(pos_t a, pos_t b) { - return (pos_t){a.x + b.x, a.y + b.y}; -} - -// check whether a position is out of bounds of the board -bool is_pos_oob(pos_t pos) { - return pos.x < 0 || pos.x > BOARD_HORIZONTAL || - pos.y < 0 || pos.y > BOARD_VERTICAL; -} - -/** - * check the wanted pos_t against all valid move options - * of the current piece - */ - /** TODO: add roundcount check for PAWN */ -bool is_move_legal(move_t move) { - pos_t new_pos = move.to; - - // OOB check - if (is_pos_oob(new_pos)) { - return false; - } - - valid_move_t valid_moves[PIECE_COUNT] = DEFAULT_VALID_MOVES; - switch (move.piece) { - case NONE: - case WPAWN: - case WKNIGHT: - case WKING: - case BPAWN: - case BKNIGHT: - case BKING: - for (int j = 0; j < valid_moves[move.piece].piece_move_count; j++) { - pos_t candidate = pos_add(move.from, valid_moves[move.piece].options[j]); - printf("cadidate {%d,%d}\n", candidate.x, candidate.y); - if ( - compare_pos( - new_pos, - candidate - ) - ) { - return true; - } - } - break; - case WROOK: - case WBISHOP: - case WQUEEN: - case BROOK: - case BBISHOP: - case BQUEEN: - for (int j = 0; j < valid_moves[move.piece].piece_move_count; j++) { - /** NOTE: BOARD_HORIZONTAL assuming HORIZONTAL & VERTICAL are the same */ - for (int k = 0; k < BOARD_HORIZONTAL; k++) { - if ( - compare_pos( - new_pos, - pos_multiply_scalar( - pos_add( - move.from, - valid_moves[move.piece].options[j] - ), k - ) - ) - ) { - return true; - } - } - } - break; - } - - return false; -} - -bool is_dest_none(board_t board, pos_t pos) { - return board[pos.y][pos.x] == NONE; -} - - -bool is_dest_opponent(board_t board, player_e player, move_t move) { - switch (player) { - case WHITE: - return board[move.to.y][move.to.x] < 0; - break; - case BLACK: - return board[move.to.y][move.to.x] > 0; - break; - } -} - -/** the move can be: - * 1. legal x illegal - * 2a. the 'to' pos could be occupied x non-occupied - * 2b. it could be capturable x non-capturable - * 2c. it could be out of bounds -*/ -/** FIXME: does not work -> b1 a3 says 0 */ -bool validate_move(board_t board, player_e player, move_t move) { - bool none = is_dest_none(board, move.to); - bool dest_is_opponent = is_dest_opponent(board, player, move); - bool move_legal = is_move_legal(move); - printf("move.from = {%d,%d}; move.to = {%d,%d}\n", move.from.x, move.from.y, move.to.x, move.to.y); - printf("from.piece = %d; to.piece = %d\n", board[move.from.y][move.from.x], board[move.to.y][move.to.x]); - printf("dest is none? %d; dest is opp? %d; legal? %d\n", none, dest_is_opponent, move_legal); - if (none && move_legal) { - // free ability to move - return true; - } else if (!none && dest_is_opponent && move_legal) { - // capturing - return true; - } else { - // otherwise, invalid move - return false; - } -} - -/** - * checks and sorts out a capture edge case of a move - * sorts out = moves the piece into the 'captured' area and puts a NONE in its' place - * (if the 'captured' piece is not a king, because you cannot capture a king) -*/ -bool check_captures(board_t *board, player_e player, captured_t *captured, move_t move) { - if (is_dest_opponent(*board, player, move)) { - switch (player) { - case WHITE: - captured->white[captured->white_n++] = *board[move.to.y][move.to.x]; - *board[move.to.y][move.to.x] = NONE; - break; - case BLACK: - captured->white[captured->white_n++] = *board[move.to.y][move.to.x]; - *board[move.to.y][move.to.x] = NONE; - break; - } - - return true; - } else { - return false; - } -} - -// checks and sorts out a promotion edge case of a move -bool check_promotions(board_t *board, player_e player, move_t *move) { - pos_t new_pos = move->to; - - // white player achieved a promotion - if (new_pos.y < 0 && player == WHITE && move->piece == WPAWN) { - *board[move->from.y][move->from.x] = WQUEEN; - move->piece = WQUEEN; - return true; - } - - // black player achieved a promotion - if (new_pos.y > BOARD_VERTICAL && player == BLACK && move->piece == BPAWN) { - *board[move->from.y][move->from.x] = BQUEEN; - move->piece = BQUEEN; - return true; - } - - return false; -} - -// negative pos => no such piece is there -pos_t get_pos_of_piece(board_t board, piece_e piece) { - for (int i = 0; i < BOARD_VERTICAL; i++) { - for (int j = 0; j < BOARD_VERTICAL; j++) { - if (board[i][j] == piece) { - return (pos_t){j, i}; - } - } - } - - return (pos_t){-1, -1}; -} - -// if king's pos is in another piece's valid moves, it is endangered -bool is_king_endangered(board_t board, player_e player, move_t move) { - pos_t king_pos; - valid_move_t valid_moves[PIECE_COUNT] = DEFAULT_VALID_MOVES; - - switch (player) { - case WHITE: - king_pos = get_pos_of_piece(board, WKING); - switch (move.piece) { - case NONE: - case BPAWN: - case BKNIGHT: - case BKING: - for (int i = 0; i < PIECE_COUNT; i++) { - for (int j = 0; - j < valid_moves[i].piece_move_count && - move.piece == valid_moves[i].piece; - j++ - ) { - if ( - compare_pos( - king_pos, - valid_moves[i].options[j] - ) - ) { - return true; - } - } - } - break; - case BROOK: - case BBISHOP: - case BQUEEN: - for (int i = 0; i < PIECE_COUNT; i++) { - for (int j = 0; - j < valid_moves[i].piece_move_count && - move.piece == valid_moves[i].piece; - j++ - ) { - /** NOTE: BOARD_HORIZONTAL assuming HORIZONTAL & VERTICAL are the same */ - for (int k = 0; k < BOARD_HORIZONTAL; k++) { - if ( - compare_pos( - king_pos, - pos_multiply_scalar(valid_moves[i].options[j], k) - ) - ) { - return true; - } - } - } - } - break; - default: - break; - } - break; - case BLACK: - king_pos = get_pos_of_piece(board, BKING); - switch (move.piece) { - case NONE: - case WPAWN: - case WKNIGHT: - case WKING: - for (int i = 0; i < PIECE_COUNT; i++) { - for (int j = 0; - j < valid_moves[i].piece_move_count && - move.piece == valid_moves[i].piece; - j++ - ) { - if ( - compare_pos( - king_pos, - valid_moves[i].options[j] - ) - ) { - return true; - } - } - } - break; - case WROOK: - case WBISHOP: - case WQUEEN: - for (int i = 0; i < PIECE_COUNT; i++) { - for (int j = 0; - j < valid_moves[i].piece_move_count && - move.piece == valid_moves[i].piece; - j++ - ) { - /** NOTE: BOARD_HORIZONTAL assuming HORIZONTAL & VERTICAL are the same */ - for (int k = 0; k < BOARD_HORIZONTAL; k++) { - if ( - compare_pos( - king_pos, - pos_multiply_scalar(valid_moves[i].options[j], k) - ) - ) { - return true; - } - } - } - } - break; - default: - break; - } - break; - } - - return false; -} - -// checks for mates -end_condition_e check_end(board_t board, player_e player, move_t move) { - // here 'is_move_legal' is used as a determinator if a piece can move - if (is_king_endangered(board, player, move) && !is_move_legal(move)) { - return CHECKMATE; - } else if (!is_king_endangered(board, player, move) && !is_move_legal(move)) { - return STALEMATE; - } else { - return CONTINUE; - } -} - -end_condition_e perform_move(board_t *board, player_e player, captured_t *captured, move_t move) { - if (validate_move(*board, player, move)) { - if ( - *board[move.to.y][move.to.x] != WKING || // accounting for both players - *board[move.to.y][move.to.x] != BKING // you cannot capture a king - ) { - check_captures(board, player, captured, move); - } - - check_promotions(board, player, &move); - - *board[move.to.y][move.to.x] = move.piece; // move piece to wanted place - *board[move.from.y][move.from.x] = NONE; // erase the remnants - } - - return check_end(*board, player, move); -} - -void announce_results(end_condition_e end_condition, player_e player) { - printf("The game has ended because of a "); - - switch (end_condition) { - case CHECKMATE: - printf("CHECKMATE"); - break; - case STALEMATE: - printf("STALEMATE"); - break; - case CONTINUE: break; // to make the linter happy - } - - printf("\nand the player who has won is"); - - switch (player) { - case WHITE: - printf(" WHITE"); - break; - case BLACK: - printf(" BLACK"); - break; - } - - printf("! Congratulations.\n"); -} \ No newline at end of file diff --git a/src/chess.h b/src/chess.h deleted file mode 100644 index 402267f..0000000 --- a/src/chess.h +++ /dev/null @@ -1,190 +0,0 @@ -#ifndef CHESS_H -#define CHESS_H - -/* the "board" is the playable area on which - * pieces move. pieces are represented by numbers: - * 0 - no piece - * 1 - pawn - * 2 - rook - * 3 - knight - * 4 - bishop - * 5 - queen - * 6 - king -*/ - -#include - -// board size constraints -#define BOARD_HORIZONTAL 8 -#define BOARD_VERTICAL 8 - -// type abstractions -typedef enum { WHITE, BLACK } player_e; - -#define PIECE_COUNT 13 // how many unique pieces are there -#define MAX_PIECE_MOVE_COUNT 27 // the max possible num of options where to move in one moment -typedef enum { - NONE, - - WPAWN, - WROOK, - WKNIGHT, - WBISHOP, - WQUEEN, - WKING, - - BPAWN, - BROOK, - BKNIGHT, - BBISHOP, - BQUEEN, - BKING -} piece_e; - -typedef piece_e board_t[BOARD_VERTICAL][BOARD_HORIZONTAL]; - -typedef struct { - int x; - int y; -} pos_t; - -typedef struct { - // what - // where from - // where to - piece_e piece; - pos_t from; - pos_t to; -} move_t; - -// the default arrangment of pieces on the board -// negative numbers are black, positive ones white -#define BOARD_DEFAULT_CONFIG \ -(board_t){\ - { BROOK, BKNIGHT, BBISHOP, BQUEEN, BKING, BBISHOP, BKNIGHT, BROOK },\ - { BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN, BPAWN },\ - { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ - { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ - { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ - { NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE },\ - { WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN, WPAWN },\ - { WROOK, WKNIGHT, WBISHOP, WQUEEN, WKING, WBISHOP, WKNIGHT, WROOK }\ -} - -typedef struct { - piece_e piece; - pos_t options[MAX_PIECE_MOVE_COUNT]; - unsigned int piece_move_count; -} valid_move_t; - -/** FIXED: ALSO DEPENDS ON THE STATE OF THE GAME - - * e.g. a pawn cannot move two places in the middle - * of the game but can in the first round - * => ASSUME NO OTHER PIECES - then ALWAYS CHECK - * FOR PIECE COLLISIONS and FOR ROUND COUNT - * (pawn 1 || 2 move) - */ - - /** MAYBE: consider the POV from both sides???!!! */ - /** - * the NONE, PAWN, KNIGHT and KING pieces' relative_moves are - * treated like specific coordinates relative to their - * position, - * whereas the BISHOP, ROOK and QUEEN pieces' 'moves' - * are treated as *directions* in which they can move rather than - * absolute x,y values -> e.g. the rook can move - * up, down, left, right * n -> and then bounds & edge cases are checked - */ -#define DEFAULT_VALID_MOVES {\ - {NONE, {}, 0},\ - \ - {WPAWN, {\ - { 0, -1}, {-1, -1}, {+1, -1}, { 0, -2}\ - }, 4},\ - {WROOK, {\ - { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0}\ - }, 4},\ - {WKNIGHT, {\ - {-1, -2}, {+1, -2}, {-2, -1}, {-2, +1},\ - {+2, -1}, {+2, +1}, {-1, -2}, {-1, +2}\ - }, 8},\ - {WBISHOP, {\ - {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ - }, 4},\ - {WQUEEN, {\ - {0, -1}, {0, +1}, {-1, 0}, {+1, 0},\ - {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ - }, 8},\ - {WKING, {\ - { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0},\ - {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ - }, 8},\ - \ - {BPAWN, {\ - { 0, -1}, {-1, -1}, {+1, -1}, { 0, -2}\ - }, 4},\ - {BROOK, {\ - { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0}\ - }, 4},\ - {BKNIGHT, {\ - {-1, -2}, {+1, -2}, {-2, -1}, {-2, +1},\ - {+2, -1}, {+2, +1}, {-1, -2}, {-1, +2}\ - }, 8},\ - {BBISHOP, {\ - {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ - }, 4},\ - {BQUEEN, {\ - {0, -1}, {0, +1}, {-1, 0}, {+1, 0},\ - {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ - }, 8},\ - {BKING, {\ - { 0, -1}, { 0, +1}, {-1, 0}, {+1, 0},\ - {-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\ - }, 8}\ -} - -/** - * states in which the game can end - * CONTINUE => the game does not end, it continues - * CHECKMATE => one side wins - * STALEMATE => it's a draw -*/ -typedef enum { - CONTINUE, - CHECKMATE, - STALEMATE -} end_condition_e; - -/** - * a tracker for which pieces have been captured by who - * white contains opponent pieces WHITE have been captured by them - * black contains opponent pieces BLACK have been captured by them -*/ -#define PIECE_COUNT_CAPTURABLE_PER_PLAYER 15 // 2*8 - 1 -typedef struct { - piece_e white[PIECE_COUNT_CAPTURABLE_PER_PLAYER]; - unsigned int white_n; - piece_e black[PIECE_COUNT_CAPTURABLE_PER_PLAYER]; - unsigned int black_n; -} captured_t; - -// initializes the board with the default configuration of pieces -void init_board(board_t board); - -// selects the player to currently play -// if it is the first round, it selects white -void select_player(player_e *player, unsigned int round_count); - -// listens for a command to make a move by the currently playing player -bool get_move(board_t board, move_t *move); - -// performs the move if is it possible -end_condition_e perform_move(board_t *board, player_e player, captured_t *captured, move_t move); - -// if there is one of the mates, end the game and show who has won -void announce_results(end_condition_e end_condition, player_e player); - -// shows a guide to help players understand the move prompt system -void show_help(); - -#endif // CHESS_H \ No newline at end of file diff --git a/src/main.c b/src/main.c index b108a0b..8321e25 100644 --- a/src/main.c +++ b/src/main.c @@ -1,54 +1,6 @@ -// a chess implementation in pure C :) - -#include "chess.h" -#include "render.h" -#include #include -/** - * steps to victory: - * 1. init the board with the default config - * 2. game loop - * 2a. select the current player (if first round - white starts) - * 2b. wait for the movement of the current player - * 2c. once a move is registered, perform it IF POSSIBLE - * 2d. check for captures and/or promotions and/or mates (check, stale...) - * 2e. if there is an end condition that is met, end the game; announce - * the results -*/ - int main() { - // variable defs - board_t board; - move_t move; - player_e player; - unsigned int round_count = 0; - end_condition_e end_condition; - captured_t captured = {.white_n = 0, .black_n = 0}; - bool running = true; - - // algo begin - - init_board(board); - - show_help(); - - while (running) { - round_count++; - - select_player(&player, round_count); - - show_board(board, player); - - get_move(board, &move); - - end_condition = perform_move(&board, player, &captured, move); - if (end_condition == CHECKMATE || end_condition == STALEMATE) { - running = false; - } - } - - announce_results(end_condition, player); - - return EXIT_SUCCESS; + printf("Hello, World!\n"); + return 0; } \ No newline at end of file diff --git a/src/render.c b/src/render.c deleted file mode 100644 index 6578979..0000000 --- a/src/render.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "render.h" -#include "chess.h" -#include - -void show_board(board_t board, player_e player) { - printf("\n "); - for (int i = 0; i < BOARD_HORIZONTAL; i++) { - printf("%-2c ", i+97); - } - printf("\n\n"); - for (int i = 0; i < BOARD_VERTICAL; i++) { - printf("%-2d [", i+1); - for (int j = 0; j < BOARD_HORIZONTAL-1; j++) { - printf("%-2d ", board[i][j]); - } - printf("%d]\n", board[i][BOARD_HORIZONTAL-1]); - } - - printf("\ncurrently plays: "); - switch (player) { - case WHITE: - printf("WHITE\n"); - break; - case BLACK: - printf("BLACK\n"); - break; - } -} \ No newline at end of file diff --git a/src/render.h b/src/render.h deleted file mode 100644 index 70ed91a..0000000 --- a/src/render.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef RENDER_H -#define RENDER_H - -#include "chess.h" - -void show_board(board_t board, player_e player); - -#endif // RENDER_H \ No newline at end of file diff --git a/src/util.c b/src/util.c deleted file mode 100644 index 64a5c1e..0000000 --- a/src/util.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "util.h" -#include - -void clear_stdin() { - while ((getchar()) != '\n'); -} - -bool is_int_in_range(int x, int start, int end) { - if (start <= x && x <= end) { - return true; - } else { - return false; - } -} \ No newline at end of file diff --git a/src/util.h b/src/util.h deleted file mode 100644 index 8eeb693..0000000 --- a/src/util.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H - -#include - -// misc. defines and methods - -// for input handling consistency -void clear_stdin(); - -// self-explanatory -bool is_int_in_range(int x, int start, int end); - -#endif // UTIL_H \ No newline at end of file