From 29e5b3c2bb76ae077275a90aaa915cd934044342 Mon Sep 17 00:00:00 2001 From: Ole Morud Date: Thu, 28 Mar 2024 15:52:48 +0100 Subject: [PATCH] Almots working bot --- Makefile | 30 +- compile_flags.txt | 1 - doxygen-config | 142 ------- include/common.h | 26 -- include/graphics.h | 8 - include/pieces.h | 12 - include/util.h | 11 - src/chess.c | 859 +++++++++++++++++++++++++++++++++++---- src/graphics.c | 95 ----- src/pieces.c | 230 ----------- src/util.c | 130 ------ testing/test_threatmap.c | 60 +++ 12 files changed, 859 insertions(+), 745 deletions(-) delete mode 100644 doxygen-config delete mode 100644 include/common.h delete mode 100644 include/graphics.h delete mode 100644 include/pieces.h delete mode 100644 include/util.h delete mode 100644 src/graphics.c delete mode 100644 src/pieces.c delete mode 100644 src/util.c create mode 100644 testing/test_threatmap.c diff --git a/Makefile b/Makefile index a609b6d..2aa7cbe 100644 --- a/Makefile +++ b/Makefile @@ -1,40 +1,42 @@ CC = clang LD = ld.lld -CFLAGS = -Iinclude +#CFLAGS += -O0 -ggdb3 CFLAGS += -Ofast -ggdb3 -CFLAGS += -Wall -Wextra -Werror -CFLAGS += -fsanitize=address +CFLAGS += -Wall -Wextra -Wno-unused-function +#CFLAGS += -fsanitize=address LDFLAGS = -fuse-ld=lld -LDFLAGS = -fsanitize=address +#LDFLAGS += -fsanitize=address -_OBJ = chess.o common.o graphics.o pieces.o util.o +_OBJ = chess.o OBJ = $(addprefix obj/, $(_OBJ)) +TEST_DIR = testing + all: bin/chess +test: $(TEST_DIR)/bin/test_threatmap + obj: mkdir -p $@ bin: mkdir -p $@ -docs: - doxygen doxygen-config +$(TEST_DIR): + mkdir -p $@/bin clean: rm bin/* obj/*.o -gdb: bin/chess - gdb $< - -run: bin/chess - ./$< - obj/%.o: src/%.c $(CC) -o $@ $(CFLAGS) -c $< bin/chess: $(OBJ) $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ -.PHONY: all run gdb clean docs +$(TEST_DIR)/bin/test_%: testing/test_%.c obj/%.o obj/util.o + echo $^ + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ + +.PHONY: all clean docs test diff --git a/compile_flags.txt b/compile_flags.txt index e6efa87..ededa11 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -1,4 +1,3 @@ -Iinclude -Wall -Wextra --Werror diff --git a/doxygen-config b/doxygen-config deleted file mode 100644 index 7ed4b2e..0000000 --- a/doxygen-config +++ /dev/null @@ -1,142 +0,0 @@ -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = "Terminal Chess" -PROJECT_BRIEF = Chess application for ANSI terminals -OUTPUT_DIRECTORY = docs -CREATE_SUBDIRS = NO -ALLOW_UNICODE_NAMES = NO -OUTPUT_LANGUAGE = English -BRIEF_MEMBER_DESC = YES -REPEAT_BRIEF = YES -ALWAYS_DETAILED_SEC = NO -INLINE_INHERITED_MEMB = NO -FULL_PATH_NAMES = YES -SHORT_NAMES = NO -JAVADOC_AUTOBRIEF = NO -JAVADOC_BANNER = NO -QT_AUTOBRIEF = NO -MULTILINE_CPP_IS_BRIEF = NO -PYTHON_DOCSTRING = YES -INHERIT_DOCS = YES -SEPARATE_MEMBER_PAGES = NO -TAB_SIZE = 4 -OPTIMIZE_OUTPUT_FOR_C = YES -MARKDOWN_SUPPORT = YES -TOC_INCLUDE_HEADINGS = 5 -AUTOLINK_SUPPORT = YES -IDL_PROPERTY_SUPPORT = YES -DISTRIBUTE_GROUP_DOC = NO -GROUP_NESTED_COMPOUNDS = NO -SUBGROUPING = YES -NUM_PROC_THREADS = 0 -EXTRACT_ALL = YES -CASE_SENSE_NAMES = SYSTEM -SHOW_HEADERFILE = YES -SHOW_INCLUDE_FILES = YES -SHOW_GROUPED_MEMB_INC = NO -FORCE_LOCAL_INCLUDES = NO -INLINE_INFO = YES -SORT_MEMBER_DOCS = YES -SORT_BRIEF_DOCS = NO -SORT_MEMBERS_CTORS_1ST = NO -SORT_GROUP_NAMES = NO -SORT_BY_SCOPE_NAME = NO -STRICT_PROTO_MATCHING = NO -GENERATE_TODOLIST = YES -GENERATE_TESTLIST = YES -GENERATE_BUGLIST = YES -GENERATE_DEPRECATEDLIST= YES -MAX_INITIALIZER_LINES = 30 -SHOW_USED_FILES = YES -SHOW_FILES = YES -SHOW_NAMESPACES = YES -QUIET = NO -WARNINGS = YES -WARN_IF_UNDOCUMENTED = YES -WARN_IF_DOC_ERROR = YES -WARN_IF_INCOMPLETE_DOC = YES -WARN_NO_PARAMDOC = NO -WARN_IF_UNDOC_ENUM_VAL = NO -WARN_AS_ERROR = NO -WARN_FORMAT = "$file:$line: $text" -WARN_LINE_FORMAT = "at line $line of file $file" -INPUT = src -INPUT_ENCODING = UTF-8 -FILE_PATTERNS = *.c *.h -RECURSIVE = YES -EXCLUDE_SYMLINKS = NO -EXAMPLE_PATTERNS = * -EXAMPLE_RECURSIVE = NO -FILTER_SOURCE_FILES = NO -USE_MDFILE_AS_MAINPAGE = README.md -SOURCE_BROWSER = NO -INLINE_SOURCES = NO -STRIP_CODE_COMMENTS = YES -REFERENCED_BY_RELATION = NO -REFERENCES_RELATION = NO -REFERENCES_LINK_SOURCE = YES -SOURCE_TOOLTIPS = YES -VERBATIM_HEADERS = YES -ALPHABETICAL_INDEX = YES -GENERATE_HTML = YES -HTML_OUTPUT = html -HTML_FILE_EXTENSION = .html -HTML_COLORSTYLE = AUTO_LIGHT -HTML_COLORSTYLE_HUE = 220 -HTML_COLORSTYLE_SAT = 100 -HTML_COLORSTYLE_GAMMA = 80 -HTML_TIMESTAMP = NO -HTML_DYNAMIC_MENUS = YES -HTML_DYNAMIC_SECTIONS = NO -HTML_INDEX_NUM_ENTRIES = 100 -GENERATE_DOCSET = NO -DOCSET_FEEDNAME = "Doxygen generated docs" -DOCSET_BUNDLE_ID = org.doxygen.Project -DOCSET_PUBLISHER_ID = org.doxygen.Publisher -DOCSET_PUBLISHER_NAME = Publisher -GENERATE_HTMLHELP = NO -GENERATE_CHI = NO -BINARY_TOC = NO -TOC_EXPAND = NO -GENERATE_QHP = NO -QHP_NAMESPACE = org.doxygen.Project -QHP_VIRTUAL_FOLDER = doc -GENERATE_ECLIPSEHELP = NO -ECLIPSE_DOC_ID = org.doxygen.Project -DISABLE_INDEX = NO -GENERATE_TREEVIEW = NO -FULL_SIDEBAR = NO -ENUM_VALUES_PER_LINE = 4 -TREEVIEW_WIDTH = 250 -EXT_LINKS_IN_WINDOW = NO -OBFUSCATE_EMAILS = YES -HTML_FORMULA_FORMAT = png -FORMULA_FONTSIZE = 10 -USE_MATHJAX = NO -MATHJAX_VERSION = MathJax_2 -MATHJAX_FORMAT = HTML-CSS -SEARCHENGINE = YES -SERVER_BASED_SEARCH = NO -EXTERNAL_SEARCH = NO -SEARCHDATA_FILE = searchdata.xml -GENERATE_LATEX = YES -LATEX_OUTPUT = latex -MAKEINDEX_CMD_NAME = makeindex -LATEX_MAKEINDEX_CMD = makeindex -COMPACT_LATEX = NO -PAPER_TYPE = a4 -PDF_HYPERLINKS = YES -USE_PDFLATEX = YES -LATEX_BATCHMODE = NO -LATEX_HIDE_INDICES = NO -LATEX_BIB_STYLE = plain -LATEX_TIMESTAMP = NO -ENABLE_PREPROCESSING = YES -MACRO_EXPANSION = NO -EXPAND_ONLY_PREDEF = NO -SEARCH_INCLUDES = YES -SKIP_FUNCTION_MACROS = YES -TAGFILES = tags -ALLEXTERNALS = NO -EXTERNAL_GROUPS = YES -EXTERNAL_PAGES = YES - diff --git a/include/common.h b/include/common.h deleted file mode 100644 index 54e6d53..0000000 --- a/include/common.h +++ /dev/null @@ -1,26 +0,0 @@ - -#include /* true, false, bool */ -#include /* ptrdiff_t */ -#include /* int32_t */ - -/** Type representing piece/tile on a chessboard */ -typedef int32_t tile_t; - -/** Type representing index of a chessboard tile */ -typedef ptrdiff_t index_t; - -#define BOARD_SIZE ((index_t)(8 * 8)) - -#define WHITE ((tile_t)1) -#define BLACK ((tile_t)-1) - -#define E ((tile_t)0) ///< empty tile -#define K ((tile_t)1) ///< king -#define Q ((tile_t)2) ///< queen -#define R ((tile_t)3) ///< rook -#define B ((tile_t)4) ///< bishop -#define N ((tile_t)5) ///< knight -#define P ((tile_t)6) ///< pawn - -#define ROW ((index_t)8) -#define COL ((index_t)1) diff --git a/include/graphics.h b/include/graphics.h deleted file mode 100644 index f4b2c41..0000000 --- a/include/graphics.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef GRAPHICS_H -#define GRAPHICS_H - -#include "common.h" - -void print_board(const tile_t board[BOARD_SIZE]); - -#endif diff --git a/include/pieces.h b/include/pieces.h deleted file mode 100644 index 7da6d80..0000000 --- a/include/pieces.h +++ /dev/null @@ -1,12 +0,0 @@ - -#ifndef PIECES_H -#define PIECES_H - -#include "common.h" - -bool move_ok(const tile_t board[BOARD_SIZE], - index_t from, - index_t to, - int player); - -#endif diff --git a/include/util.h b/include/util.h deleted file mode 100644 index e0adfd8..0000000 --- a/include/util.h +++ /dev/null @@ -1,11 +0,0 @@ -#include "common.h" - -int64_t get_sign(int64_t n); -bool tile_empty(tile_t t); -index_t abs_index(index_t i); -index_t column(index_t i); -index_t row(index_t i); -tile_t abs_tile(tile_t t); -tile_t get_color(tile_t t); -bool same_color(tile_t a, tile_t b); -bool opposite_color(tile_t a, tile_t b); diff --git a/src/chess.c b/src/chess.c index f4eebd5..eddd34c 100644 --- a/src/chess.c +++ b/src/chess.c @@ -1,77 +1,643 @@ -#include "common.h" -#include "graphics.h" -#include "pieces.h" - +#include #include /* isalpha, isdigit ... */ #include /* setlocale */ -#include /* uint64_t */ +#include +#include /* true, false, bool */ +#include /* ptrdiff_t */ +#include /* int32_t */ #include /* printf, scanf */ #include /* memcpy */ +#include -#define CLEAR_SCREEN() printf("\033[2J") +typedef int32_t piece_t; +typedef ptrdiff_t index_t; +typedef uint64_t bitmap_t; -int do_turn(int turn_no, tile_t board[BOARD_SIZE]); -void init_board(tile_t board[BOARD_SIZE]); -index_t input_to_index(char input[2]); +#define MAX_DEPTH 5 // TODO: don't hardcode this +#define N_ELEMS(x) (sizeof x / sizeof x[0]) +#define BOARD_SIZE ((index_t)64) +#define BIT(x) (1UL << (x)) -int main() +#define WHITE ((piece_t)1) +#define BLACK ((piece_t)-1) + +#define EMPTY ((piece_t)0) +#define KING ((piece_t)1) +#define QUEEN ((piece_t)2) +#define ROOK ((piece_t)3) +#define BISHOP ((piece_t)4) +#define KNIGHT ((piece_t)5) +#define PAWN ((piece_t)6) + +#define ROW ((index_t)8) +#define COL ((index_t)1) + +struct game_state { + index_t king_pos[2]; + bool a_rook_touched[2]; + bool h_rook_touched[2]; + bool king_touched[2]; + + int turns_without_captures; + + piece_t board[BOARD_SIZE]; +}; + +enum castle_type { + CASTLE_KINGSIDE = 1, + CASTLE_QUEENSIDE = 2, +}; + +static const double piece_value[] = { + [EMPTY] = 0, + [PAWN] = 1, + [BISHOP] = 3, + [KNIGHT] = 3, + [ROOK] = 5, + [QUEEN] = 9, + [KING] = 0, +}; + +static const double piece_position_bonus[7][BOARD_SIZE * sizeof(double)] = { + [EMPTY] = {0}, + [PAWN] = { + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.7, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.7, + 1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0, + 1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0, + 1.0, 1.0, 1.4, 1.4, 1.4, 1.4, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + }, + [BISHOP] = { + 1.2, 1.0, 1.0, 1.0, 1.0, 1.0, 1.2, 1.2, + 1.2, 1.2, 1.0, 1.0, 1.0, 1.2, 1.2, 1.0, + 1.0, 1.2, 1.2, 1.0, 1.2, 1.2, 1.0, 1.0, + 1.0, 1.0, 1.2, 1.2, 1.2, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.2, 1.2, 1.2, 1.0, 1.0, 1.0, + 1.0, 1.2, 1.2, 1.0, 1.2, 1.2, 1.0, 1.0, + 1.2, 1.2, 1.0, 1.0, 1.0, 1.2, 1.2, 1.0, + 1.2, 1.0, 1.0, 1.0, 1.0, 1.0, 1.2, 1.2, + }, + [KNIGHT] = { + 0.5, 0.7, 0.8, 0.8, 0.8, 0.8, 0.7, 0.5, + 0.6, 0.7, 0.9, 0.9, 0.9, 0.9, 0.7, 0.6, + 0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6, + 0.6, 1.0, 1.0, 1.1, 1.1, 1.0, 1.0, 0.6, + 0.6, 1.0, 1.0, 1.1, 1.1, 1.0, 1.0, 0.6, + 0.6, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6, + 0.6, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.6, + 0.5, 0.7, 0.8, 0.8, 0.8, 0.8, 0.7, 0.5, + }, + [ROOK] = { + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + }, + [QUEEN] = { + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + }, + [KING] = { + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, + 1.001, 1.001, 1.0, 1.0, 1.0, 1.0, 1.001, 1.001, + }, +}; + +#define TILE(col, row) ((col)-'A'+ROW*((row)-1)) +//static inline index_t tile(char col, int row) +//{ +// assert(row >= 1); +// assert(row <= 8); +// assert(col >= 'A'); +// assert(col <= 'H'); +// return col-'A' + ROW*(row-1); +//} + +static bool friends(piece_t a, piece_t b) { - setlocale(LC_ALL, "C.UTF-8"); - - tile_t board[BOARD_SIZE] = { 0 }; - - init_board(board); - - int turn = 0; - - while (true) { - CLEAR_SCREEN(); - print_board(board); - - // turn increments on valid inputs, - turn += do_turn(turn, board); - } - - return 0; + return a * b > 0; } -/** - * Resets/initializes the board - * - * Sets all the tiles of the board to match the starting position of chess. - * - * \param board Pointer to list of tiles representing board state - */ -void init_board(tile_t board[BOARD_SIZE]) +static inline piece_t piece_abs(piece_t t) +{ + if (t < 0) + return -t; + return t; +} + +static int signum(int t) +{ + if (t == 0) + return 0; + if (t >= 0) + return 1; + return -1; +} + +static index_t row(index_t i) +{ + return i / ROW; +} + +static index_t column(index_t i) +{ + return i % ROW; +} + +static bool enemies(piece_t a, piece_t b) +{ + return a * b < 0; +} + +static piece_t piece_color(piece_t t) +{ + return (piece_t)signum(t); +} + +static void board_paint(struct game_state* g) +{ + /* https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode + The unicode symbols for the pieces are calculated from adding + 0x2653 (#define'd as UNICODE_CHESS_SYMBOL) with the piece value. */ +#define BG_DARKBLUE() printf("\033[48;2;100;100;150m") +#define BG_LIGHTBLUE() printf("\033[48;2;150;150;200m") +#define FG_BLACK() printf("\033[38;2;0;0;0m") +#define FG_WHITE() printf("\033[38;2;255;255;255m") +#define UNICODE_CHESS_SYMBOL 0x2659 + for (size_t i = 0; i < 8; i++) { + printf("\n %zu ", 8 - i); // number coordinates + for (size_t j = 0; j < 8; j++) { + piece_t t = g->board[i * 8 + j]; + if ((i + j) % 2) + BG_DARKBLUE(); + else + BG_LIGHTBLUE(); + if (t == EMPTY) { + printf(" "); + continue; + } + if (t > 0) + FG_WHITE(); + else + FG_BLACK(); + printf("%lc ", UNICODE_CHESS_SYMBOL + piece_abs(t)); + } + //setcolor(2, 0, 0, 0); // reset text attributes + printf("\033[0m"); + } + /* horizontal letter coordinates */ + printf("\n "); + for (int i = 0; i < 8; i++) + printf(" %c", 'a' + i); +} + +static void move(struct game_state* g, index_t from, index_t to) +{ + int p = (piece_color(g->board[from]) == WHITE ? 0 : 1); + + if (piece_abs(g->board[from]) == KING) { + g->king_pos[p] = to; + g->king_touched[p] = true; + } else if (from == TILE('A', 7)) { + g->a_rook_touched[0] = true; + } else if (from == TILE('A', 0)) { + g->a_rook_touched[1] = true; + } else if (from == TILE('H', 0)) { + g->h_rook_touched[0] = true; + } else if (from == TILE('H', 7)) { + g->h_rook_touched[1] = true; + } + + if (g->board[to] == EMPTY) { + g->turns_without_captures += 1; + } else { + g->turns_without_captures = 0; + } + + g->board[to] = g->board[from]; + g->board[from] = EMPTY; +} + +static bitmap_t pawn_threatmap(struct game_state* g, index_t index) +{ + index_t direction = -1 * piece_color(g->board[index]); + index_t left = BIT(index + ROW*direction - 1); + index_t right = BIT(index + ROW*direction + 1); + + if (column(index) == 7) + return left; + + if (column(index) == 0) + return right; + + return left | right; +} + +static bitmap_t diagonal_threatmap(struct game_state* g, index_t index) +{ + bitmap_t threatened = 0; + + //index_t directions[] = { ROW+1, ROW-1, -ROW+1, -ROW-1 }; + + for (index_t i = index+ROW+1; i < BOARD_SIZE && column(i-1) != 7; i += ROW+1) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) { + break; + } + } + for (index_t i = index+ROW-1; i < BOARD_SIZE && column(i+1) != 0; i += ROW-1) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) { + break; + } + } + for (index_t i = index-ROW+1; i >= 0 && column(i-1) != 7; i += -ROW+1) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) { + break; + } + } + for (index_t i = index-ROW-1; i >= 0 && column(i+1) != 0; i += -ROW-1) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) { + break; + } + } + return threatened; +} + +static bitmap_t cardinal_threatmap(struct game_state* g, index_t index) +{ + bitmap_t threatened = 0; + + for (index_t i = index+ROW; i < BOARD_SIZE; i += ROW) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) + break; + } + for (index_t i = index-ROW; i >= 0; i -= ROW) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) + break; + } + for (index_t i = index+1; i < BOARD_SIZE && column(i) != 0; i++) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) + break; + } + for (index_t i = index-1; i > 0 && column(i) != 7; i--) { + threatened |= BIT(i); + if (g->board[i] != EMPTY) + break; + } + + return threatened; +} + +static inline bitmap_t bishop_threatmap(struct game_state* g, index_t index) +{ + return diagonal_threatmap(g, index); +} + +static inline bitmap_t rook_threatmap(struct game_state* g, index_t index) +{ + return cardinal_threatmap(g, index); +} + +static bitmap_t knight_threatmap(index_t index) +{ + bitmap_t threatened = 0L; + + //clang-format off + index_t knight_wheel[8*2] = { + /* x, y */ + 1, 2, + 1, -2, + -1, 2, + -1, -2, + 2, 1, + 2, -1, + -2, 1, + -2, -1 + }; + // clang-format on + + for (size_t i = 0; i < N_ELEMS(knight_wheel); i += 2) { + index_t atks = index + knight_wheel[i] + knight_wheel[i+1] * ROW; + + if (column(index) + knight_wheel[i] < 0 || column(index) + knight_wheel[i] > 7 + || row(index) + knight_wheel[i+1] < 0 || row(index) + knight_wheel[i+1] > 7) + continue; + + threatened |= BIT(atks); + } + + return threatened; +} + +static bitmap_t king_threatmap(index_t index) +{ + if (row(index) == 0 && column(index) == 0) { + return BIT(index+1) | BIT(index+ROW+1) | BIT (index+ROW); + } + else if (row(index) == 0 && column(index) == 7) { + return BIT(index-1) | BIT(index+ROW-1) | BIT (index+ROW); + } + else if (row(index) == 7 && column(index) == 0) { + return BIT(index+1) | BIT(index-ROW+1) | BIT (index-ROW); + } + else if (row(index) == 7 && column(index) == 7) { + return BIT(index-1) | BIT(index-ROW-1) | BIT (index-ROW); + } + else if (row(index) == 0) { + return BIT(index-1) | BIT(index+1) | BIT(index+ROW-1) | BIT(index+ROW) | BIT(index+ROW+1); + } + else if (row(index) == 7) { + return BIT(index-1) | BIT(index+1) | BIT(index-ROW-1) | BIT(index-ROW) | BIT(index-ROW+1); + } + else if (column(index) == 0) { + return BIT(index-ROW) | BIT(index-ROW+1) | BIT(index+1) | BIT(index+ROW+1) | BIT(index+ROW); + } + else if (column(index) == 7) { + return BIT(index-ROW) | BIT(index-ROW-1) | BIT(index-1) | BIT(index+ROW-1) | BIT(index+ROW); + } + return BIT(index-ROW-1) | BIT(index-ROW) | BIT(index-ROW+1) + | BIT(index-1) | BIT(index+1) + | BIT(index+ROW-1) | BIT(index+ROW) | BIT(index+ROW+1); +} + +static inline bitmap_t queen_threatmap(struct game_state* g, index_t index) +{ + return diagonal_threatmap(g, index) | cardinal_threatmap(g, index); +} + +static void print_threatmap(bitmap_t threatmap) +{ + for (size_t i = 0; i < 64; i++) { + putc(' ', stdout); + + if (i % 8 == 0) + fputc('\n', stdout); + if (threatmap & BIT(i)) + fputc('x', stdout); + else + fputc('-', stdout); + } +} + +static bitmap_t piece_threatmap(struct game_state* g, index_t index) +{ + switch (piece_abs(g->board[index])) { + case EMPTY: + return 0L; + case PAWN: + return pawn_threatmap(g, index); + case BISHOP: + return bishop_threatmap(g, index); + case ROOK: + return rook_threatmap(g, index); + case KNIGHT: + return knight_threatmap(index); + case KING: + return king_threatmap(index); + case QUEEN: + return queen_threatmap(g, index); + default: + return 0L; + } +} + +static bitmap_t threatmap(struct game_state* g, int attacker) +{ + bitmap_t threatened = 0; + + for(index_t i = 0; i < BOARD_SIZE; i++) { + if (friends(g->board[i], attacker)) { + threatened |= piece_threatmap(g, i); + } + } + + return threatened; +} + +static bool pawn_move_ok(struct game_state* g, index_t from, index_t to) +{ + const index_t direction = -1 * piece_color(g->board[from]); + const index_t diff = (to - from) * direction; + + switch (diff) { + case ROW: /* single move */ + return g->board[to] == EMPTY; + + case ROW - COL: /* diagonal attack */ + case ROW + COL: + return enemies(g->board[to], g->board[from]); + + case 2 * ROW: /* double move */ + return g->board[to] == EMPTY + && g->board[from + ROW * direction] == EMPTY + && (row(from) == 1 || row(from) == 6); + + default: /* any other move is illegal */ + return false; + } +} + +static bool king_move_ok(struct game_state* g, index_t from, index_t to) +{ + // castling is implemented as a separate function, invoked by O-O or O-O-O + return BIT(to) & king_threatmap(from) + && BIT(to) & ~threatmap(g, -piece_color(g->board[from])); +} +bool is_check(struct game_state* g, int player) +{ + return BIT(g->king_pos[player == WHITE ? 0 : 1]) & threatmap(g, -player); +} + +static bool castle_ok(struct game_state* g, int player, enum castle_type t) +{ + if (is_check(g, player)) { + return false; + } + const int p = player == WHITE ? 0 : 1; + const int row = ROW * (player == WHITE ? 7 : 0); + if (t == CASTLE_KINGSIDE) { + return g->h_rook_touched[p] == false + && g->king_touched[p] == false + && threatmap(g, -player) & (BIT(TILE('F', row)) | BIT(TILE('G', row))); + } else if (t == CASTLE_QUEENSIDE) { + return g->a_rook_touched[p] == false + && g->king_touched[p] == false + && threatmap(g, -player) & (BIT(TILE('C', row)) | BIT(TILE('D', row))); + } + + fprintf(stderr, "invalid castle_type passed to %s", __func__); + exit(EXIT_FAILURE); +} + + +bool move_ok(struct game_state* g, index_t from, index_t to, int player) +{ + /* Player must own piece it moves + and a player can't capture their own pieces. */ + if (enemies(player, g->board[from]) || friends(player, g->board[to])) + return false; + + bool move_ok = false;; + + switch (piece_abs(g->board[from])) { + case EMPTY: + move_ok = false; + break; + case PAWN: + move_ok = pawn_move_ok(g, from, to); + break; + case KING: + if (to == TILE('G', 8) || to == TILE('G', 1)) { + move_ok = castle_ok(g, player, CASTLE_KINGSIDE); + } else if (to == TILE('C', 8) || to == TILE('C', 1)) { + move_ok = castle_ok(g, player, CASTLE_QUEENSIDE); + } else { + move_ok = king_move_ok(g, from, to); + } + break; + default: + move_ok = BIT(to) & piece_threatmap(g, from); + break; + } + + if (!move_ok) { + return false; + } + + // pretend move was done and check if king is checked after + unsigned char restore[sizeof *g]; + memcpy(restore, g, sizeof *g); + move(g, from, to); + bool bad = is_check(g, player); + memcpy(g, restore, sizeof *g); + + if (bad) { + return false; + } + + return true; +} + +static bitmap_t valid_moves(struct game_state* g, index_t i, int player) +{ + bitmap_t output = 0; + for (index_t j=0; jturns_without_captures >= 50; +} + +// TODO: fix this garbage +static bool checkmate(struct game_state* g, int player) +{ + if (!is_check(g, player)) + return false; + + // TODO: avoid doubly nested for loop + for (int i=0; iboard, start, sizeof start); + + g->king_pos[0] = 60; + g->king_pos[1] = 4; +} + +static index_t input_to_index(char input[2]) +{ + const char col = toupper(input[0]); + const char row = toupper(input[1]); + + if (col < 'A' || col > 'H' || row < '1' || row > '8') + return -1; + + const int x = col - 'A'; + const int y = row - '1'; + return 8 * (7 - y) + x; } // TODO: Implement algebaric notation -/** Asks for move, validates move and does move if valid - * - * \param turn_no Turn number - * \param board Pointer to list of tiles representing board state - * - * \return (int)1 if successful, (int)0 if invalid input from player - * */ -int do_turn(int turn_no, tile_t board[BOARD_SIZE]) +static int do_turn(int turn_no, struct game_state* g) { char input[3] = { 0 }; int from = -1, to = -1; - printf("\nPlayer %i, your turn to move", 1 + turn_no % 2); + printf("\n%s's turn to move", turn_no % 2 ? "Black" : "White"); printf("\nMove piece\nfrom: "); - scanf(" %2s", input); from = input_to_index(input); @@ -79,6 +645,7 @@ int do_turn(int turn_no, tile_t board[BOARD_SIZE]) if (from == -1) return 0; + print_threatmap(valid_moves(g, g->king_pos[turn_no % 2], turn_no % 2 ? BLACK : WHITE)); printf("\nto: "); scanf(" %2s", input); @@ -87,41 +654,181 @@ int do_turn(int turn_no, tile_t board[BOARD_SIZE]) if (to == -1) return 0; - if (! move_ok(board, from, to, turn_no % 2 ? BLACK : WHITE)) + if (!move_ok(g, from, to, turn_no % 2 ? BLACK : WHITE)) return 0; - board[to] = board[from]; - board[from] = E; + move(g, from, to); - /* increment to make it next turn */ return 1; } -/** - * Translates A1, 3B etc. to the 1D index of the board - * - * \param input string of length 2 representing tile, e.g. "A3" - * - * \return index value on valid input, -1 on invalid input - * - * */ -index_t input_to_index(char input[2]) +static double heuristic(struct game_state* g, int player) { - int x = -1, y = -1; + if (draw(g, player)) + return 0; - for (int i = 0; i < 2; i++) { - const char c = input[i]; + if (checkmate(g, -player)) + return player * INFINITY; - const char v = isalpha(c) ? toupper(c) : c; + double score = 0; + for (index_t i=0; iboard[i]; + const int player = piece_color(piece); + const piece_t type = piece_abs(piece); + score += player * piece_value[type] * piece_position_bonus[type][i]; + } + return score; +} - if ('A' <= v && v <= 'H') - x = v - 'A'; - else if ('1' <= v && v <= '8') - y = v - '1'; +static void print_debug(struct game_state* g, int player) +{ + //struct game_state { + // piece_t board[BOARD_SIZE]; + // index_t king_pos[2]; + // bool a_rook_touched[2]; + // bool h_rook_touched[2]; + // bool king_touched[2]; + //}; + + printf("White king pos: %c%ld\n", 'A'+((char)column(g->king_pos[0])), 8-row(g->king_pos[0])); + printf("Black king pos: %c%ld\n", 'A'+((char)column(g->king_pos[1])), 8-row(g->king_pos[1])); + printf("White A king touched: %s\n", g->king_touched[0] ? "True" : "False"); + printf("Black A king touched: %s\n", g->king_touched[1] ? "True" : "False"); + printf("White A rook touched: %s\n", g->a_rook_touched[0] ? "True" : "False"); + printf("Black A rook touched: %s\n", g->a_rook_touched[1] ? "True" : "False"); + printf("White H rook touched: %s\n", g->h_rook_touched[0] ? "True" : "False"); + printf("Black H rook touched: %s\n", g->h_rook_touched[1] ? "True" : "False"); + printf("Turns with no capture: %d\n", g->turns_without_captures); + printf("Estimated score: %lf\n", heuristic(g, player)); +} + +static double alpha_beta(struct game_state* g, double alpha, double beta, int player, int depth) +{ + if (depth == 0) + return heuristic(g, player) * player; + + double m = alpha; + unsigned char restore[sizeof *g]; + + // TODO: avoid doubly nested for loop + for (int i=0; iboard[i] == EMPTY || !friends(g->board[i], player)) + continue; + for (int j=0; j m) { + m = x; + } + if (m >= beta) { + return m; + } + } } - if (x != -1 && y != -1) - return 8 * (7 - y) + x; - else - return -1; + return m; } + +static void computer_move(struct game_state* g, int player, int depth, index_t* from, index_t* to) +{ + unsigned char restore[sizeof *g]; + + double max = -INFINITY; + + // TODO: avoid doubly nested for loop + for (int i=0; iboard[i], player)) + continue; + for (int j=0; j max) { + *to = j; + *from = i; + max = x; + } + //printf("beta: %lf, max: %lf\n", beta, max); + } + } + //assert(max != -INFINITY); +} + +int main() +{ + setlocale(LC_ALL, "C.UTF-8"); + + struct game_state state = {}; + + game_init(&state); + + int turn = 0; + int player = WHITE; + + while (true) { + print_debug(&state, player); + //print_threatmap(threatmap(&state, turn % 2 ? BLACK : WHITE)); + board_paint(&state); + + if (is_check(&state, player)) { + printf("%s in check!\n", player == WHITE ? "White" : "Black"); + //print_threatmap(piece_threatmap(&state, state.king_pos[player == WHITE ? 0 : 1])); + } + + bool player_intervention = false; + + if (player_intervention) { +intervene: + while (do_turn(turn, &state) == 0) + /*noop*/; + } else { + printf("thinking...\n"); + index_t from = -1, to = -1; + computer_move(&state, player, MAX_DEPTH, &from, &to); + if (from == -1 || to == -1) { + printf("computer couldn't think, starting player intervention\n"); + printf("Valid moves for %s:\n", player == WHITE ? "white" : "black"); + print_threatmap(valid_moves(&state, state.king_pos[player == WHITE ? 0 : 1], player)); + printf("\nThreatened pieces\n"); + print_threatmap(threatmap(&state, -player)); + player_intervention = true; + goto intervene; + } + move(&state, from, to); + printf("Did %c%ld to %c%ld\n", (char)('A'+column(from)), 8-row(from), (char)('A'+column(to)), 8-row(to)); + } + + turn += 1; + player = -player; + + if (checkmate(&state, player)) { + printf("\nCheckmate. %s won!\n", -player == WHITE ? "White" : "Black"); + print_debug(&state, player); + board_paint(&state); + print_threatmap(valid_moves(&state, state.king_pos[player == WHITE ? 0 : 1], player)); + print_threatmap(threatmap(&state, -player)); + break; + } + if (draw(&state, player)) { + printf("\nDraw!\n"); + print_debug(&state, player); + board_paint(&state); + break; + } + } + + return EXIT_SUCCESS; +} + diff --git a/src/graphics.c b/src/graphics.c deleted file mode 100644 index 8c6842b..0000000 --- a/src/graphics.c +++ /dev/null @@ -1,95 +0,0 @@ - -#include "graphics.h" - -#include "common.h" -#include "util.h" - -#include - -/** Set background to dark blue */ -#define BG_DARKBLUE() setcolor(0, 100, 100, 150) - -/** Set background to light blue */ -#define BG_LIGHTBLUE() setcolor(0, 150, 150, 200) - -/** Set foreground to black */ -#define FG_BLACK() setcolor(1, 0, 0, 0) - -/** Set foreground to white */ -#define FG_WHITE() setcolor(1, 0xff, 0xff, 0xff) - -/** 0x2659 == ♙ */ -#define UNICODE_CHESS_SYMBOL 0x2659 - -static inline void -setcolor(const int mode, const int r, const int g, const int b); - -/** - * Sets the foreground or background color for subsequent writes. - * - * Uses Select Graphic Renditions (SGR) to set the color of the terminal output. - * See https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit for more details. - * - * \param mode 0 - change background, 1 - change foreground, 2 - reset colors - * \param r amount of red (0 to 255) - * \param b amount of blue (0 to 255) - * \param g amount of green (0 to 255) - */ -static inline void -setcolor(const int mode, const int r, const int g, const int b) -{ - if (mode == 2) - printf("\033[0m"); - else - printf("\033[%i;2;%i;%i;%im", mode ? 38 : 48, r, g, b); -}; - -/** - * Prints the board - * - * Uses unicode symbols and ANSI escape features to print a chessboard on the - * display. - * - * \param board A pointer to a list of tiles representing the board state - * - * */ -void print_board(const tile_t board[BOARD_SIZE]) -{ - /* https://en.wikipedia.org/wiki/Chess_symbols_in_Unicode - - The unicode symbols for the pieces are calculated from adding - 0x2653 (#define'd as UNICODE_CHESS_SYMBOL) with the piece value. */ - - for (size_t i = 0; i < 8; i++) { - printf("\n %zu ", 8 - i); // number coordinates - - for (size_t j = 0; j < 8; j++) { - tile_t t = board[i * 8 + j]; - - if ((i + j) % 2) - BG_DARKBLUE(); - else - BG_LIGHTBLUE(); - - if (tile_empty(t)) { - printf(" "); - continue; - } - - if (t > 0) - FG_WHITE(); - else - FG_BLACK(); - - printf("%lc ", UNICODE_CHESS_SYMBOL + abs_tile(t)); - } - - setcolor(2, 0, 0, 0); // reset text attributes - } - - /* horizontal letter coordinates */ - printf("\n "); - - for (int i = 0; i < 8; i++) - printf(" %c", 'a' + i); -} diff --git a/src/pieces.c b/src/pieces.c deleted file mode 100644 index 88611e8..0000000 --- a/src/pieces.c +++ /dev/null @@ -1,230 +0,0 @@ - -#include "common.h" -#include "util.h" - -bool bishop_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to); -bool cardinal_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to); -bool diagonal_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to); -bool king_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to); -bool knight_move_ok(index_t from, index_t to); -bool pawn_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to); -bool queen_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to); -bool rook_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to); - -/** - * Check if a move is valid - * - * \param board Pointer to list of tiles representing board state - * \param from Tile to move piece from - * \param to Tile to move piece to - * \param player The current player to move - * - * \return true if move is valid, false otherwise - * */ -bool move_ok(const tile_t board[BOARD_SIZE], - index_t from, - index_t to, - int player) -{ - /* player must own piece it moves */ - if (board[from] * player < 0) - return false; - - /* empty tiles can't be moved */ - if (tile_empty(board[from])) - return false; - - /* player can't take their own pieces or move a piece onto itself */ - if (same_color(board[from], board[to])) - return false; - - /* check piece specific moves */ - switch (abs_tile(board[from])) { - case P: - return pawn_move_ok(board, from, to); - - case B: - return bishop_move_ok(board, from, to); - - case R: - return rook_move_ok(board, from, to); - - case N: - return knight_move_ok(from, to); - - case K: - return king_move_ok(board, from, to); - - case Q: - return queen_move_ok(board, from, to); - } - - return false; -} - -/** - * Check if move is a valid pawn move - * - * \param board Array of tiles representing chess board state - * \param from Index of board piece starts at - * \param to Index of board piece wants to move to - * - * \return true if move is valid, false otherwise - */ -bool pawn_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to) -{ - const index_t direction = -1 * get_color(board[from]), - diff = (to - from) * direction; - - switch (diff) { - case ROW: /* single move */ - return tile_empty(board[to]); - - case ROW - COL: /* diagonal attack */ - case ROW + COL: - return opposite_color(board[to], board[from]); - - case 2 * ROW: /* double move */ - return tile_empty(board[to]) - && tile_empty(board[from + ROW * direction]) - && (row(from) == 1 || row(from) == 6); - - default: /* any other move is illegal */ - return false; - } -} - -/** - * Check if `to` is on a diagonal line of `from` - * - * \param board Array of tiles representing chess board state - * \param from Index of board piece is at - * \param to Index of board piece tries to move to - * - * \return true if \p to is on a diagonal line of \p from - */ -bool diagonal_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to) -{ - // clang-format off - const index_t col_diff = column(to) - column(from), - row_diff = row(to) - row(from), - x = get_sign(col_diff), - y = get_sign(row_diff) * ROW, - step = x + y; - // clang-format on - - if (abs_index(col_diff) != abs_index(row_diff)) - return false; - - for (index_t p = from + step; p != to; p += step) - if (! tile_empty(board[p])) - return false; - - return true; -} - -/** - * Check if index `to` is on a cardinal line of `from` - * - * \param board Array of tiles representing chess board state - * \param from Index of board piece is at - * \param to Index of board piece tries to move to - * - * \return true if \p to and \p from share a column or row - */ -bool cardinal_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to) -{ - const index_t col_diff = column(to) - column(from), - row_diff = row(to - from); - - if (row_diff > 0 && col_diff > 0) - return false; - - const index_t step = - row_diff ? ROW * get_sign(row_diff) : get_sign(col_diff); - - for (index_t p = from + step; p != to; p += step) - if (! tile_empty(board[p])) - return false; - - return true; -} - -/** - * Check if move is a valid bishop move - * - * \param board Array of tiles representing chess board state - * \param from Index of board piece is at - * \param to Index of board piece tries to move to - * - * \return true if move is valid, false otherwise - */ -bool bishop_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to) -{ - return diagonal_move_ok(board, from, to); -} - -/** - * Check if move is a valid rook move - * - * \param board Array of tiles representing chess board state - * \param from Index of board piece is at - * \param to Index of board piece tries to move to - * - * \return true if move is valid, false otherwise - */ -bool rook_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to) -{ - return cardinal_move_ok(board, from, to); -} - -/** - * Check if move is a valid knight move - * - * \param from Index of board piece is at - * \param to Index of board piece tries to move to - * - * \return true if move is valid, false otherwise - */ -bool knight_move_ok(index_t from, index_t to) -{ - const index_t x = abs_index(column(to) - column(from)), - y = abs_index(row(to - from)); - - return (x == 1 && y == 2) || (x == 2 && y == 1); -} - -/** - * Check if move is a valid king move - * - * \param board Array of tiles representing chess board state - * \param from Index of board piece is at - * \param to Index of board piece tries to move to - * - * \return true if move is valid, false otherwise - */ -bool king_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to) -{ - const index_t abs_x = abs_index(column(to) - column(from)), - abs_y = abs_index(row(to) - row(from)); - - // TODO: check if move causes check - (void)board; - - return abs_x <= 1 && abs_y <= 1; -} - -/** - * Check if move is a valid queen move - * - * \param board Array of tiles representing chess board state - * \param from Index of board piece is at - * \param to Index of board piece tries to move to - * - * \return true if move is valid, false otherwise - */ -bool queen_move_ok(const tile_t board[BOARD_SIZE], index_t from, index_t to) -{ - return diagonal_move_ok(board, from, to) - || cardinal_move_ok(board, from, to); -} diff --git a/src/util.c b/src/util.c deleted file mode 100644 index eb58453..0000000 --- a/src/util.c +++ /dev/null @@ -1,130 +0,0 @@ - -#include "util.h" - -#include "common.h" - -/** - * Get sign of a number - * - * \param n positive or negative integer - * - * \return 1 if number is positive - * -1 if number is negative - * 0 if number is zero - */ -int64_t get_sign(int64_t n) -{ - if (n == 0) - return 0; - - if (n >= 0) - return 1; - - return -1; -} - -/** - * Get the color of a tile - * - * \param t tile to check - * - * \return WHITE (1) if tile is white, - * BLACK (-1) if tile is black, - * 0 if the tile is empty - */ -tile_t get_color(tile_t t) -{ - return (tile_t)get_sign(t); -} - -/** - * Calculate the absolute value of an index_t value - * - * \param p positive or negative index_t - * - * \return the absolute value of p - */ -index_t abs_index(index_t i) -{ - if (i < 0) - return -1 * i; - - return i; -} - -/** - * Calculate the absolute value of a tile_t value - * - * \param t positive or negative tile_t - * - * \return the absolute value of t - * */ -tile_t abs_tile(tile_t t) -{ - if (t < 0) - return -1 * t; - - return t; -} - -/** - * Check if tile is has no piece on it - * - * \param t tile to check if empty - * - * \return true if tile is empty, false otherwise - * */ -bool tile_empty(tile_t t) -{ - return t == E; -} - -/** - * Get row of index - * - * \param i index to get row number of - * - * \return row number of i - * */ -index_t row(index_t i) -{ - return i / ROW; -} - -/** - * Get column of index - * - * \param i index to get column number of - * - * \return column number of i - * */ -index_t column(index_t i) -{ - return i % ROW; -} - -/** - * Check if two tiles have pieces of the opposite color - * - * \param a Tile to compare - * \param b Tile to compare it with - * - * \return true if a and b are opposite colors, false otherwise - * */ -bool opposite_color(tile_t a, tile_t b) -{ - return a * b < 0; -} - -/** - * Check if two tiles have pieces of the same color - * - * \param a Tile to compare - * \param b Tile to compare it with - * - * \return true if a and b are opposite colors, false otherwise - * */ -bool same_color(tile_t a, tile_t b) -{ - return a * b > 0; -} diff --git a/testing/test_threatmap.c b/testing/test_threatmap.c new file mode 100644 index 0000000..87dd47c --- /dev/null +++ b/testing/test_threatmap.c @@ -0,0 +1,60 @@ + +#include "threatmap.h" + +// uint64_t pawn_threatmap(const tile_t board[BOARD_SIZE], index_t index); +// uint64_t bishop_threatmap(const tile_t board[BOARD_SIZE], index_t index); +// uint64_t rook_threatmap(const tile_t board[BOARD_SIZE], index_t index); +// uint64_t knight_threatmap(index_t index); +// uint64_t king_threatmap(const tile_t board[BOARD_SIZE], index_t index); +// uint64_t queen_threatmap(const tile_t board[BOARD_SIZE], index_t index); +// uint64_t threatmap(const tile_t board[BOARD_SIZE], int color_attacking); +// void print_threatmap(uint64_t threatmap); + +void test_threatmap() +{ + const tile_t e4[] = { + -R, -N, -B, -Q, -K, -B, -N, -R, + -P, -P, -P, -P, -P, -P, -P, -P, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, P, E, E, E, E, + E, E, E, E, E, E, E, E, + P, P, P, E, P, P, P, P, + R, N, B, Q, K, B, N, R + }; + + const tile_t lonely_bishop[] = { + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, B, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + }; + + const tile_t lonely_rook[] = { + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, R, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + E, E, E, E, E, E, E, E, + }; + + (void)lonely_rook; (void)lonely_bishop; (void)e4; + + //print_threatmap(threatmap(e4, WHITE)); + print_threatmap(threatmap(lonely_bishop, WHITE)); + //print_threatmap(threatmap(lonely_rook, WHITE)); +} + +int main() +{ + test_threatmap(); + + return 0; +}