gitea push
This commit is contained in:
+219
@@ -0,0 +1,219 @@
|
||||
#include "chess.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
move_t get_move(state_t state) {
|
||||
move_t move;
|
||||
int row_from, row_to;
|
||||
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;
|
||||
}
|
||||
|
||||
int board_get(state_t state, v2_t pos) {
|
||||
return state.board[pos.y][pos.x];
|
||||
}
|
||||
|
||||
void board_set(state_t *state, v2_t pos, int piece) {
|
||||
state->board[pos.y][pos.x] = piece;
|
||||
}
|
||||
|
||||
bool is_move_dest_in_bounds(state_t state, move_t move) {
|
||||
return
|
||||
0 <= move.to.x && move.to.x < state.board_cols &&
|
||||
0 <= move.to.y && move.to.y < state.board_rows;
|
||||
}
|
||||
|
||||
bool is_dest_empty(state_t state, move_t move) {
|
||||
return board_get(state, move.to) == 0;
|
||||
}
|
||||
|
||||
bool is_dest_opposite_color(state_t state, move_t move) {
|
||||
return (board_get(state, move.from) * board_get(state, move.to) < 0); // < 0 means different colors
|
||||
}
|
||||
|
||||
// white pieces are positive ints and black are negative
|
||||
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);
|
||||
}
|
||||
|
||||
v2_t v2(int x, int y) {
|
||||
return (v2_t){x, y};
|
||||
}
|
||||
|
||||
/**
|
||||
* 0 -> a == b
|
||||
* -1 -> a < b
|
||||
* 1 -> a > b
|
||||
*/
|
||||
int v2_cmp(v2_t a, v2_t b) {
|
||||
if (a.x == b.x && a.y == b.y) {
|
||||
return 0;
|
||||
} else if (a.x < b.x && a.y < b.y) {
|
||||
return -1;
|
||||
} else if (a.x > b.x && a.y > b.y) {
|
||||
return 1;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// this only ever must get called AFTER made sure it is legal
|
||||
// with is_move_legal()
|
||||
void perform_move(state_t *state, move_t move) {
|
||||
board_set(state, move.to, board_get(*state, move.from));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
case WHITE:
|
||||
state->captured_by_black[state->by_black_capture_num++] =
|
||||
board_get(*state, move.to);
|
||||
break;
|
||||
case 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;
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#define repeat(var, n) for (int var = 0; var < n; var++)
|
||||
|
||||
// types of players
|
||||
typedef enum {
|
||||
EMPTY,
|
||||
WHITE,
|
||||
BLACK
|
||||
} player_e;
|
||||
|
||||
// 2d vector struct (used e.g. for directions or coords)
|
||||
typedef struct {
|
||||
int x;
|
||||
int y;
|
||||
} v2_t;
|
||||
|
||||
// everything we need to identify and perform a move
|
||||
typedef struct {
|
||||
v2_t from;
|
||||
v2_t to;
|
||||
} move_t;
|
||||
|
||||
typedef struct {
|
||||
v2_t table[7][8];
|
||||
int piece_numbers[7];
|
||||
} valid_moves_t;
|
||||
|
||||
// appstate struct
|
||||
#define CAPTURABLE 16
|
||||
typedef struct {
|
||||
unsigned int board_cols;
|
||||
unsigned int board_rows;
|
||||
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;
|
||||
|
||||
/**
|
||||
* pieces:
|
||||
* 0 = empty
|
||||
* +-1 = pawn
|
||||
* +-2 = rook
|
||||
* +-3 = knight
|
||||
* +-4 = bishop
|
||||
* +-5 = queen
|
||||
* +-6 = king
|
||||
*/
|
||||
|
||||
int **create_board(int rows, int cols);
|
||||
move_t get_move(state_t state);
|
||||
bool is_move_valid(state_t state, move_t move);
|
||||
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);
|
||||
|
||||
v2_t v2(int x, int y);
|
||||
int v2_cmp(v2_t a, v2_t b);
|
||||
+64
-2
@@ -1,5 +1,67 @@
|
||||
#include <stdio.h>
|
||||
/**
|
||||
* algo:
|
||||
* 1. create a chess board
|
||||
* 2. loop
|
||||
* 2a. select the player to play
|
||||
* 2b. they try to make a move
|
||||
* 2c. the move gets validated
|
||||
* 2d. if it can be made and it is legal, go ahead
|
||||
* 2e. check for captures and promotions
|
||||
* 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() {
|
||||
puts("Hello, World!\n");
|
||||
state_t state;
|
||||
state.valid_moves = (valid_moves_t){
|
||||
.table = {
|
||||
{}, // 0
|
||||
|
||||
{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
|
||||
|
||||
{v2( 0,-1), v2( 0, 1), v2(-1, 0), v2( 1, 0)}, // ...
|
||||
|
||||
{v2(-1,-2), v2(-1, 2), v2(-2,-1), v2(-2, 1),
|
||||
v2( 1,-2), v2( 1, 2), v2( 2,-1), v2( 2, 1)},
|
||||
|
||||
{v2(-1,-1), v2(-1, 1), v2( 1,-1), v2( 1, 1)},
|
||||
|
||||
{v2(-1,-1), v2(-1, 1), v2( 1,-1), v2( 1, 1),
|
||||
v2( 0,-1), v2( 0, 1), v2(-1, 0), v2( 1, 0)},
|
||||
|
||||
{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
|
||||
|
||||
state.active_player = WHITE;
|
||||
|
||||
while (true) {
|
||||
move_t move = get_move(state);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
switch_active_player(&state);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user