Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebec0f013e | ||
|
|
5d4e71388b | ||
|
|
84bc6660a7 | ||
|
|
71e1f16fac | ||
|
|
465398f210 | ||
|
|
51ff3b37d8 | ||
|
|
b40d016e67 | ||
|
|
de55c98be3 | ||
|
|
44c38667a4 | ||
|
|
0e415250e6 |
Executable
BIN
Binary file not shown.
+182
-158
@@ -1,195 +1,219 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include "chess.h"
|
#include "chess.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
void init_board(board_t *board) {
|
#include <stdio.h>
|
||||||
for (int i = 0; i < BOARD_VERTICAL; i++) {
|
#include <stdlib.h>
|
||||||
for (int j = 0; j < BOARD_HORIZONTAL; j++) {
|
#include <math.h>
|
||||||
*board[i][j] = BOARD_DEFAULT_CONFIG[i][j];
|
|
||||||
}
|
int **create_board(int rows, int cols) {
|
||||||
|
int **board = (int **)malloc(sizeof(int*) * rows);
|
||||||
|
if (!board) return NULL;
|
||||||
|
for (int i = 0; i < cols; i++) {
|
||||||
|
board[i] = (int *)malloc(sizeof(int));
|
||||||
|
if (!board[i]) return NULL; // should probably free all the previously allocated ones as well as the ** pointer
|
||||||
}
|
}
|
||||||
|
return board;
|
||||||
}
|
}
|
||||||
|
|
||||||
void select_player(player_e *player, unsigned int round_count) {
|
move_t get_move(state_t state) {
|
||||||
if (round_count == 1) {
|
move_t move;
|
||||||
*player = WHITE;
|
int row_from, row_to;
|
||||||
return;
|
char col_from, col_to;
|
||||||
|
printf("move (e.g. b1 a3) > ");
|
||||||
|
scanf("%c%d %c%d", &col_from, &row_from, &col_to, &row_to);
|
||||||
|
|
||||||
|
move.from = (v2_t){
|
||||||
|
.x = 97-col_from,
|
||||||
|
.y = state.board_rows-row_from};
|
||||||
|
move.to = (v2_t){
|
||||||
|
.x = 97-col_to,
|
||||||
|
.y = state.board_rows-row_to};
|
||||||
|
|
||||||
|
return move;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*player == WHITE) {
|
int board_get(state_t state, v2_t pos) {
|
||||||
*player = BLACK;
|
return state.board[pos.y][pos.x];
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*player == BLACK) {
|
void board_set(state_t *state, v2_t pos, int piece) {
|
||||||
*player = WHITE;
|
state->board[pos.y][pos.x] = piece;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_help() {
|
bool is_move_dest_in_bounds(state_t state, move_t move) {
|
||||||
puts(
|
return
|
||||||
"movement help:\n\
|
0 <= move.to.x && move.to.x < state.board_cols &&
|
||||||
there is a prompt asking for where to move a piece every turn\n\
|
0 <= move.to.y && move.to.y < state.board_rows;
|
||||||
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_dest_empty(state_t state, move_t move) {
|
||||||
bool is_move_in_bounds(move_t move) {
|
return board_get(state, move.to) == 0;
|
||||||
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(move_t *move) {
|
bool is_dest_opposite_color(state_t state, move_t move) {
|
||||||
clear_stdin();
|
return (board_get(state, move.from) * board_get(state, move.to) < 0); // < 0 means different colors
|
||||||
|
|
||||||
char _x_from, _x_to;
|
|
||||||
int _y_from, _y_to;
|
|
||||||
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;
|
|
||||||
|
|
||||||
return is_move_in_bounds(*move);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool compare_pos(pos_t a, pos_t b) {
|
// white pieces are positive ints and black are negative
|
||||||
return a.x == b.x && a.y == b.y;
|
bool is_dest_empty_or_opposite_color(state_t state, move_t move) {
|
||||||
|
return
|
||||||
|
is_dest_empty(state, move) ||
|
||||||
|
is_dest_opposite_color(state, move);
|
||||||
}
|
}
|
||||||
|
|
||||||
pos_t pos_multiply_scalar(pos_t a, int b) {
|
v2_t v2(int x, int y) {
|
||||||
return (pos_t){a.x*b, a.y*b};
|
return (v2_t){x, y};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check the wanted pos_t against all valid move options
|
* 0 -> a == b
|
||||||
* of the current piece
|
* -1 -> a < b
|
||||||
|
* 1 -> a > b
|
||||||
*/
|
*/
|
||||||
bool is_move_legal(move_t move) {
|
int v2_cmp(v2_t a, v2_t b) {
|
||||||
valid_move_t valid_moves[PIECE_COUNT] = DEFAULT_VALID_MOVES;
|
if (a.x == b.x && a.y == b.y) {
|
||||||
switch (move.piece) {
|
return 0;
|
||||||
case NONE:
|
} else if (a.x < b.x && a.y < b.y) {
|
||||||
case PAWN:
|
return -1;
|
||||||
case KNIGHT:
|
} else if (a.x > b.x && a.y > b.y) {
|
||||||
case KING:
|
return 1;
|
||||||
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++
|
|
||||||
) {
|
|
||||||
pos_t new_pos = {move.from.x+move.to.x, move.from.y+move.to.y};
|
|
||||||
if (compare_pos(new_pos, valid_moves[i].options[j]) == true) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ROOK:
|
|
||||||
case BISHOP:
|
|
||||||
case QUEEN:
|
|
||||||
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++) {
|
|
||||||
pos_t new_pos = {
|
|
||||||
move.from.x+move.to.x,
|
|
||||||
move.from.y+move.to.y
|
|
||||||
};
|
|
||||||
if (compare_pos(
|
|
||||||
new_pos,
|
|
||||||
pos_multiply_scalar(valid_moves[i].options[j], k)
|
|
||||||
) == true) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_none(board_t board, pos_t pos) {
|
|
||||||
return board[pos.y][pos.x] == NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the move can be:
|
|
||||||
// 1. legal x illegal
|
|
||||||
// 2. the 'to' pos could be occupied x non-occupied
|
|
||||||
// 2a. it could be capturable x non-capturable
|
|
||||||
bool validate_move(board_t board, move_t move) {
|
|
||||||
bool none = is_none(board, move.to);
|
|
||||||
bool move_legal = is_move_legal(move);
|
|
||||||
if (none == true && move_legal == true) {
|
|
||||||
// free ability to move
|
|
||||||
return true;
|
|
||||||
} else if (none == false && move_legal == true) {
|
|
||||||
// capturing
|
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
// otherwise, invalid move
|
return 99; // error code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_piece_at_origin_able_to_make_move(state_t state, move_t move) {
|
||||||
|
// need to check all legal moves for the piece at move.from
|
||||||
|
|
||||||
|
unsigned int idx = abs(board_get(state, move.from));
|
||||||
|
for (int i = 0; i < state.valid_moves.piece_numbers[idx]; i++) {
|
||||||
|
if (v2_cmp(move.to, state,valid_moves.table[idx][i]) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_move_valid(state_t state, move_t move) {
|
||||||
|
return is_move_dest_in_bounds(state, move) &&
|
||||||
|
is_dest_empty_or_opposite_color(state, move) &&
|
||||||
|
is_piece_at_origin_able_to_make_move(state, move);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** TODO: capturing and promotions */
|
// this only ever must get called AFTER made sure it is legal
|
||||||
bool perform_move(board_t *board, player_e player, move_t move) {
|
// with is_move_legal()
|
||||||
if (validate_move(*board, move) == true) {
|
void perform_move(state_t *state, move_t move) {
|
||||||
*board[move.to.y][move.to.x] = move.piece; // move piece to wanted place
|
board_set(state, move.to, board_get(*state, move.from));
|
||||||
*board[move.from.y][move.from.x] = NONE; // erase the remnants
|
board_set(state, move.from, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
player_e get_dest_color(state_t state, move_t move) {
|
||||||
|
if (board_get(state, move.to) < 0) {
|
||||||
|
return BLACK;
|
||||||
|
} else if (board_get(state, move.to) > 0) {
|
||||||
|
return WHITE;
|
||||||
|
} else {
|
||||||
|
return EMPTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end_condition_e check_edge_cases(board_t board) {
|
void check_captures(state_t *state, move_t move) {
|
||||||
|
// king cannot be captured
|
||||||
}
|
if (is_dest_opposite_color(*state, move) && abs(board_get(*state, move.to)) != 6) {
|
||||||
|
switch (get_dest_color(*state, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("\nand the player who has won is");
|
|
||||||
|
|
||||||
switch (player) {
|
|
||||||
case WHITE:
|
case WHITE:
|
||||||
printf(" WHITE");
|
state->captured_by_black[state->by_black_capture_num++] =
|
||||||
|
board_get(*state, move.to);
|
||||||
break;
|
break;
|
||||||
case BLACK:
|
case BLACK:
|
||||||
printf(" BLACK");
|
state->captured_by_white[state->by_white_capture_num++] =
|
||||||
|
board_get(*state, move.to);
|
||||||
|
break;
|
||||||
|
case EMPTY:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a pawn has reached the opposite side, change it into a queen
|
||||||
|
// TODO: let the player choose the next piece
|
||||||
|
void check_promotions(state_t *state, move_t move) {
|
||||||
|
if (board_get(*state, move.to) == 1 && move.to.y == 0) {
|
||||||
|
// 1, y = 0 => white pawn has reached the end
|
||||||
|
board_set(state, move.to, 5); // 5 -> Wqueen
|
||||||
|
} else if (board_get(*state, move.to) == -1 && move.to.y == state->board_cols-1) {
|
||||||
|
// -1, y = board_cols-1 => black pawn has reached the end
|
||||||
|
board_set(state, move.to, -5); // -5 -> Bqueen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// go through all the valid moves for all the pieces
|
||||||
|
// of the opposite player and see if 'pos' is in there
|
||||||
|
bool is_position_endangered(state_t state, v2_t pos) {
|
||||||
|
/**
|
||||||
|
* for every valid move of KING:
|
||||||
|
* for every OPPOSITE COLOR PIECE:
|
||||||
|
* check if the valid moves contain the KING's pos
|
||||||
|
* ->
|
||||||
|
* 1. iterate through the board
|
||||||
|
* 2. if found opposite color piece
|
||||||
|
* 3. -> for every valid move of this piece, check if it matches any of the kings positions
|
||||||
|
*/
|
||||||
|
for (int col = 0; col < state.board_cols; col++) {
|
||||||
|
for (int row = 0; row < state.board_rows; row++) {
|
||||||
|
v2_t pos = (v2_t){row, col};
|
||||||
|
int piece = board_get(state, pos);
|
||||||
|
switch (state.active_player) {
|
||||||
|
case WHITE:
|
||||||
|
if (piece < 0) {
|
||||||
|
for (int i = 0; i < state.valid_moves.piece_numbers[piece]; i++) {
|
||||||
|
for (int j = 0; j < state.valid_moves.piece_numbers[6]; j++) {
|
||||||
|
if (v2_cmp(
|
||||||
|
state.valid_moves.table[piece][i],
|
||||||
|
state.valid_moves.table[6][j]) == 0
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case BLACK:
|
||||||
|
if (piece > 0) {
|
||||||
|
for (int i = 0; i < state.valid_moves.piece_numbers[piece]; i++) {
|
||||||
|
for (int j = 0; j < state.valid_moves.piece_numbers[6]; j++) {
|
||||||
|
if (v2_cmp(
|
||||||
|
state.valid_moves.table[piece][i],
|
||||||
|
state.valid_moves.table[6][j]) == 0
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EMPTY:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TODO: unimplemented */
|
||||||
|
bool check_end_conditions(state_t state, move_t move) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void switch_active_player(state_t *state) {
|
||||||
|
switch (state->active_player) {
|
||||||
|
case WHITE:
|
||||||
|
state->active_player = BLACK;
|
||||||
|
break;
|
||||||
|
case BLACK:
|
||||||
|
state->active_player = WHITE;
|
||||||
|
break;
|
||||||
|
case EMPTY:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("! Congratulations.\n");
|
|
||||||
}
|
}
|
||||||
+46
-120
@@ -1,140 +1,66 @@
|
|||||||
#ifndef CHESS_H
|
#pragma once
|
||||||
#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 <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// board size constraints
|
#define repeat(var, n) for (int var = 0; var < n; var++)
|
||||||
#define BOARD_HORIZONTAL 8
|
|
||||||
#define BOARD_VERTICAL 8
|
|
||||||
|
|
||||||
// type abstractions
|
// types of players
|
||||||
typedef int board_t[BOARD_HORIZONTAL][BOARD_VERTICAL];
|
|
||||||
|
|
||||||
#define PIECE_COUNT 7 // 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 {
|
typedef enum {
|
||||||
NONE,
|
EMPTY,
|
||||||
PAWN,
|
WHITE,
|
||||||
ROOK,
|
BLACK
|
||||||
KNIGHT,
|
} player_e;
|
||||||
BISHOP,
|
|
||||||
QUEEN,
|
|
||||||
KING
|
|
||||||
} piece_e;
|
|
||||||
|
|
||||||
|
// 2d vector struct (used e.g. for directions or coords)
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
} pos_t;
|
} v2_t;
|
||||||
|
|
||||||
typedef enum { WHITE, BLACK } player_e;
|
|
||||||
|
|
||||||
|
// everything we need to identify and perform a move
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// what
|
v2_t from;
|
||||||
// where from
|
v2_t to;
|
||||||
// where to
|
|
||||||
piece_e piece;
|
|
||||||
pos_t from;
|
|
||||||
pos_t to;
|
|
||||||
} move_t;
|
} move_t;
|
||||||
|
|
||||||
// the default arrangment of pieces on the board
|
|
||||||
#define BOARD_DEFAULT_CONFIG \
|
|
||||||
(board_t){\
|
|
||||||
{2,3,4,5,6,4,3,2},\
|
|
||||||
{1,1,1,1,1,1,1,1},\
|
|
||||||
{0,0,0,0,0,0,0,0},\
|
|
||||||
{0,0,0,0,0,0,0,0},\
|
|
||||||
{0,0,0,0,0,0,0,0},\
|
|
||||||
{1,1,1,1,1,1,1,1},\
|
|
||||||
{2,3,4,5,6,4,3,2}\
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
piece_e piece;
|
v2_t table[7][8];
|
||||||
pos_t options[MAX_PIECE_MOVE_COUNT];
|
int piece_numbers[7];
|
||||||
unsigned int piece_move_count;
|
} valid_moves_t;
|
||||||
} valid_move_t;
|
|
||||||
|
|
||||||
/** FIXED: ALSO DEPENDS ON THE STATE OF THE GAME -
|
// appstate struct
|
||||||
* e.g. a pawn cannot move two places in the middle
|
#define CAPTURABLE 16
|
||||||
* of the game but can in the first round
|
typedef struct {
|
||||||
* => ASSUME NO OTHER PIECES - then ALWAYS CHECK
|
unsigned int board_cols;
|
||||||
* FOR PIECE COLLISIONS and FOR ROUND COUNT
|
unsigned int board_rows;
|
||||||
* (pawn 1 || 2 move)
|
int captured_by_white[CAPTURABLE];
|
||||||
*/
|
unsigned int by_white_capture_num;
|
||||||
|
int captured_by_black[CAPTURABLE];
|
||||||
|
unsigned int by_black_capture_num;
|
||||||
|
player_e active_player;
|
||||||
|
valid_moves_t valid_moves;
|
||||||
|
int **board;
|
||||||
|
} state_t;
|
||||||
|
|
||||||
/** MAYBE: consider the POV from both sides???!!! */
|
|
||||||
/**
|
/**
|
||||||
* the NONE, PAWN, KNIGHT and KING pieces' relative_moves are
|
* pieces:
|
||||||
* treated like specific coordinates relative to their
|
* 0 = empty
|
||||||
* position,
|
* +-1 = pawn
|
||||||
* whereas the BISHOP, ROOK and QUEEN pieces' 'moves'
|
* +-2 = rook
|
||||||
* are treated as *directions* in which they can move rather than
|
* +-3 = knight
|
||||||
* absolute x,y values -> e.g. the rook can move
|
* +-4 = bishop
|
||||||
* up, down, left, right * n -> and then bounds & edge cases are checked
|
* +-5 = queen
|
||||||
|
* +-6 = king
|
||||||
*/
|
*/
|
||||||
#define DEFAULT_VALID_MOVES (valid_move_t[PIECE_COUNT]){\
|
|
||||||
{NONE, {}, 0},\
|
|
||||||
{PAWN, {\
|
|
||||||
{ 0, -1}, {-1, -1}, {+1, -1}, { 0, -2}\
|
|
||||||
}, 4},\
|
|
||||||
{ROOK, {\
|
|
||||||
{ 0, -1}, { 0, +1}, {-1, 0}, {+1, 0}\
|
|
||||||
}, 4},\
|
|
||||||
{KNIGHT, {\
|
|
||||||
{-1, -2}, {+1, -2}, {-2, -1}, {-2, +1},\
|
|
||||||
{+2, -1}, {+2, +1}, {-1, -2}, {-1, +2}\
|
|
||||||
}, 8},\
|
|
||||||
{BISHOP, {\
|
|
||||||
{-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\
|
|
||||||
}, 4},\
|
|
||||||
{QUEEN, {\
|
|
||||||
{0, -1}, {0, +1}, {-1, 0}, {+1, 0},\
|
|
||||||
{-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\
|
|
||||||
}, 8},\
|
|
||||||
{KING, {\
|
|
||||||
{ 0, -1}, { 0, +1}, {-1, 0}, {+1, 0},\
|
|
||||||
{-1, -1}, {-1, +1}, {+1, -1}, {+1, +1}\
|
|
||||||
}, 8}\
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum {
|
int **create_board(int rows, int cols);
|
||||||
CHECKMATE,
|
move_t get_move(state_t state);
|
||||||
STALEMATE
|
bool is_move_valid(state_t state, move_t move);
|
||||||
} end_condition_e;
|
void perform_move(state_t *state, move_t move);
|
||||||
|
void check_captures(state_t *state, move_t move);
|
||||||
|
void check_promotions(state_t *state, move_t move);
|
||||||
|
bool check_end_conditions(state_t state, move_t move);
|
||||||
|
void switch_active_player(state_t *state);
|
||||||
|
|
||||||
// initializes the board with the default configuration of pieces
|
v2_t v2(int x, int y);
|
||||||
void init_board(board_t *board);
|
int v2_cmp(v2_t a, v2_t b);
|
||||||
|
|
||||||
// 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(move_t *move);
|
|
||||||
|
|
||||||
// performs the move if is it possible
|
|
||||||
bool perform_move(board_t *board, player_e player, move_t move);
|
|
||||||
|
|
||||||
// checks for captures, prommotions and mates
|
|
||||||
end_condition_e check_edge_cases(board_t board);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|||||||
+51
-35
@@ -1,51 +1,67 @@
|
|||||||
// a chess implementation in pure C :)
|
|
||||||
|
|
||||||
#include "util.h"
|
|
||||||
#include "chess.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* steps to victory:
|
* algo:
|
||||||
* 1. init the board with the default config
|
* 1. create a chess board
|
||||||
* 2. game loop
|
* 2. loop
|
||||||
* 2a. select the current player (if first round - white starts)
|
* 2a. select the player to play
|
||||||
* 2b. wait for the movement of the current player
|
* 2b. they try to make a move
|
||||||
* 2c. once a move is registered, perform it IF POSSIBLE
|
* 2c. the move gets validated
|
||||||
* 2d. check for captures and/or promotions and/or mates (check, stale...)
|
* 2d. if it can be made and it is legal, go ahead
|
||||||
* 2e. if there is an end condition that is met, end the game; announce
|
* 2e. check for captures and promotions
|
||||||
* the results
|
* 2f. after each move, check for a stalemate or a checkmate condition => if there is -> end of the game
|
||||||
|
* 2g. if there is no end condition => it's the other player's turn
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "chess.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
// variable defs
|
state_t state;
|
||||||
board_t board;
|
state.valid_moves = (valid_moves_t){
|
||||||
move_t move;
|
.table = {
|
||||||
player_e player;
|
{}, // 0
|
||||||
unsigned int round_count = 0;
|
|
||||||
end_condition_e end_condition;
|
|
||||||
bool running = true;
|
|
||||||
|
|
||||||
// algo begin
|
{v2( 0,-1), v2( 0,-2), v2(-1,-1), v2( 1,-1),
|
||||||
|
v2( 0, 1), v2( 0, 2), v2(-1, 1), v2( 1, 1)}, // 1
|
||||||
|
|
||||||
init_board(&board);
|
{v2( 0,-1), v2( 0, 1), v2(-1, 0), v2( 1, 0)}, // ...
|
||||||
|
|
||||||
show_help();
|
{v2(-1,-2), v2(-1, 2), v2(-2,-1), v2(-2, 1),
|
||||||
|
v2( 1,-2), v2( 1, 2), v2( 2,-1), v2( 2, 1)},
|
||||||
|
|
||||||
while (running) {
|
{v2(-1,-1), v2(-1, 1), v2( 1,-1), v2( 1, 1)},
|
||||||
round_count++;
|
|
||||||
|
|
||||||
select_player(&player, round_count);
|
{v2(-1,-1), v2(-1, 1), v2( 1,-1), v2( 1, 1),
|
||||||
|
v2( 0,-1), v2( 0, 1), v2(-1, 0), v2( 1, 0)},
|
||||||
|
|
||||||
get_move(&move);
|
{v2(-1,-1), v2(-1, 1), v2( 1,-1), v2( 1, 1),
|
||||||
|
v2( 0,-1), v2( 0, 1), v2(-1, 0), v2( 1, 0)}
|
||||||
|
},
|
||||||
|
.piece_numbers = {0, 8, 4, 8, 4, 8, 8}
|
||||||
|
};
|
||||||
|
memset(state.captured_by_white, 0, CAPTURABLE);
|
||||||
|
memset(state.captured_by_black, 0, CAPTURABLE);
|
||||||
|
state.by_white_capture_num = 0;
|
||||||
|
state.by_black_capture_num = 0;
|
||||||
|
state.board_rows = 8;
|
||||||
|
state.board_cols = 8;
|
||||||
|
state.board = create_board(state.board_rows, state.board_cols); // creates a constant struct => no need to check for memory issues
|
||||||
|
|
||||||
perform_move(&board, player, move);
|
state.active_player = WHITE;
|
||||||
|
|
||||||
end_condition = check_edge_cases(board);
|
while (true) {
|
||||||
if (end_condition == CHECKMATE || end_condition == STALEMATE) {
|
move_t move = get_move(state);
|
||||||
running = false;
|
|
||||||
}
|
if (is_move_valid(state, move)) {
|
||||||
|
check_captures(&state, move); // checks for captures and modifies the state to keep track of the captures pieces
|
||||||
|
|
||||||
|
check_promotions(&state, move); // same thing as above but for promotions: pawn -> queen when at the end of the board, etc.
|
||||||
|
|
||||||
|
if (check_end_conditions(state, move)) break; // returns false if the game is supposed to end and true if it is supposed tu continue
|
||||||
|
perform_move(&state, move);
|
||||||
}
|
}
|
||||||
|
|
||||||
announce_results(end_condition, player);
|
switch_active_player(&state);
|
||||||
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return 0;
|
||||||
}
|
}
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
#include "util.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
#ifndef UTIL_H
|
|
||||||
#define UTIL_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
// misc. defines and methods
|
|
||||||
|
|
||||||
#define EXIT_SUCCESS 0
|
|
||||||
#define EXIT_FAILURE 1
|
|
||||||
|
|
||||||
// for input handling consistency
|
|
||||||
void clear_stdin();
|
|
||||||
|
|
||||||
// self-explanatory
|
|
||||||
bool is_int_in_range(int x, int start, int end);
|
|
||||||
|
|
||||||
#endif // UTIL_H
|
|
||||||
Reference in New Issue
Block a user