Almots working bot
This commit is contained in:
30
Makefile
30
Makefile
@@ -1,40 +1,42 @@
|
|||||||
|
|
||||||
CC = clang
|
CC = clang
|
||||||
LD = ld.lld
|
LD = ld.lld
|
||||||
CFLAGS = -Iinclude
|
#CFLAGS += -O0 -ggdb3
|
||||||
CFLAGS += -Ofast -ggdb3
|
CFLAGS += -Ofast -ggdb3
|
||||||
CFLAGS += -Wall -Wextra -Werror
|
CFLAGS += -Wall -Wextra -Wno-unused-function
|
||||||
CFLAGS += -fsanitize=address
|
#CFLAGS += -fsanitize=address
|
||||||
LDFLAGS = -fuse-ld=lld
|
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))
|
OBJ = $(addprefix obj/, $(_OBJ))
|
||||||
|
|
||||||
|
TEST_DIR = testing
|
||||||
|
|
||||||
all: bin/chess
|
all: bin/chess
|
||||||
|
|
||||||
|
test: $(TEST_DIR)/bin/test_threatmap
|
||||||
|
|
||||||
obj:
|
obj:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
bin:
|
bin:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
docs:
|
$(TEST_DIR):
|
||||||
doxygen doxygen-config
|
mkdir -p $@/bin
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm bin/* obj/*.o
|
rm bin/* obj/*.o
|
||||||
|
|
||||||
gdb: bin/chess
|
|
||||||
gdb $<
|
|
||||||
|
|
||||||
run: bin/chess
|
|
||||||
./$<
|
|
||||||
|
|
||||||
obj/%.o: src/%.c
|
obj/%.o: src/%.c
|
||||||
$(CC) -o $@ $(CFLAGS) -c $<
|
$(CC) -o $@ $(CFLAGS) -c $<
|
||||||
|
|
||||||
bin/chess: $(OBJ)
|
bin/chess: $(OBJ)
|
||||||
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^
|
$(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
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
-Iinclude
|
-Iinclude
|
||||||
-Wall
|
-Wall
|
||||||
-Wextra
|
-Wextra
|
||||||
-Werror
|
|
||||||
|
|||||||
142
doxygen-config
142
doxygen-config
@@ -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
|
|
||||||
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
#include <stdbool.h> /* true, false, bool */
|
|
||||||
#include <stddef.h> /* ptrdiff_t */
|
|
||||||
#include <stdint.h> /* 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)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#ifndef GRAPHICS_H
|
|
||||||
#define GRAPHICS_H
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
void print_board(const tile_t board[BOARD_SIZE]);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -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
|
|
||||||
@@ -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);
|
|
||||||
859
src/chess.c
859
src/chess.c
@@ -1,77 +1,643 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include <assert.h>
|
||||||
#include "graphics.h"
|
|
||||||
#include "pieces.h"
|
|
||||||
|
|
||||||
#include <ctype.h> /* isalpha, isdigit ... */
|
#include <ctype.h> /* isalpha, isdigit ... */
|
||||||
#include <locale.h> /* setlocale */
|
#include <locale.h> /* setlocale */
|
||||||
#include <stdint.h> /* uint64_t */
|
#include <math.h>
|
||||||
|
#include <stdbool.h> /* true, false, bool */
|
||||||
|
#include <stddef.h> /* ptrdiff_t */
|
||||||
|
#include <stdint.h> /* int32_t */
|
||||||
#include <stdio.h> /* printf, scanf */
|
#include <stdio.h> /* printf, scanf */
|
||||||
#include <string.h> /* memcpy */
|
#include <string.h> /* memcpy */
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#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]);
|
#define MAX_DEPTH 5 // TODO: don't hardcode this
|
||||||
void init_board(tile_t board[BOARD_SIZE]);
|
#define N_ELEMS(x) (sizeof x / sizeof x[0])
|
||||||
index_t input_to_index(char input[2]);
|
#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");
|
return a * b > 0;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static inline piece_t piece_abs(piece_t t)
|
||||||
* Resets/initializes the board
|
{
|
||||||
*
|
if (t < 0)
|
||||||
* Sets all the tiles of the board to match the starting position of chess.
|
return -t;
|
||||||
*
|
return t;
|
||||||
* \param board Pointer to list of tiles representing board state
|
}
|
||||||
*/
|
|
||||||
void init_board(tile_t board[BOARD_SIZE])
|
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; j<BOARD_SIZE; j++) {
|
||||||
|
if (move_ok(g, i, j, player)) {
|
||||||
|
output |= BIT(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool draw(struct game_state* g, int player)
|
||||||
|
{
|
||||||
|
(void)player;
|
||||||
|
// TODO: implement stalemate
|
||||||
|
return g->turns_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; i<BOARD_SIZE; i++) {
|
||||||
|
for (int j=0; j<BOARD_SIZE; j++) {
|
||||||
|
if (move_ok(g, i, j, player)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void game_init(struct game_state* g)
|
||||||
{
|
{
|
||||||
// black pieces are prefixed by a minus (-)
|
// black pieces are prefixed by a minus (-)
|
||||||
const tile_t start[]
|
// clang-format off
|
||||||
= { -R, -N, -B, -Q, -K, -B, -N, -R, -P, -P, -P, -P, -P, -P, -P, -P,
|
#if 1
|
||||||
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
|
static const piece_t start[BOARD_SIZE] = {
|
||||||
E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E,
|
-ROOK, -KNIGHT,-BISHOP,-QUEEN, -KING, -BISHOP,-KNIGHT,-ROOK,
|
||||||
P, P, P, P, P, P, P, P, R, N, B, Q, K, B, N, R };
|
-PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN, -PAWN,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN,
|
||||||
|
ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
#else
|
||||||
|
static const piece_t start[BOARD_SIZE] = {
|
||||||
|
-ROOK, -KNIGHT,-BISHOP,-QUEEN, -KING, -BISHOP,-KNIGHT,-ROOK,
|
||||||
|
-PAWN, -PAWN, -PAWN, -PAWN, -PAWN, QUEEN, -PAWN, -PAWN,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, ROOK, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
|
||||||
|
PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN, PAWN,
|
||||||
|
ROOK, KNIGHT, BISHOP, QUEEN, KING, BISHOP, KNIGHT, ROOK
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
memcpy(board, start, sizeof(start));
|
memset(g, 0, sizeof *g);
|
||||||
|
memcpy(g->board, 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
|
// TODO: Implement algebaric notation
|
||||||
/** Asks for move, validates move and does move if valid
|
static int do_turn(int turn_no, struct game_state* g)
|
||||||
*
|
|
||||||
* \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])
|
|
||||||
{
|
{
|
||||||
char input[3] = { 0 };
|
char input[3] = { 0 };
|
||||||
|
|
||||||
int from = -1, to = -1;
|
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: ");
|
printf("\nMove piece\nfrom: ");
|
||||||
|
|
||||||
scanf(" %2s", input);
|
scanf(" %2s", input);
|
||||||
|
|
||||||
from = input_to_index(input);
|
from = input_to_index(input);
|
||||||
@@ -79,6 +645,7 @@ int do_turn(int turn_no, tile_t board[BOARD_SIZE])
|
|||||||
if (from == -1)
|
if (from == -1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
print_threatmap(valid_moves(g, g->king_pos[turn_no % 2], turn_no % 2 ? BLACK : WHITE));
|
||||||
printf("\nto: ");
|
printf("\nto: ");
|
||||||
scanf(" %2s", input);
|
scanf(" %2s", input);
|
||||||
|
|
||||||
@@ -87,41 +654,181 @@ int do_turn(int turn_no, tile_t board[BOARD_SIZE])
|
|||||||
if (to == -1)
|
if (to == -1)
|
||||||
return 0;
|
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;
|
return 0;
|
||||||
|
|
||||||
board[to] = board[from];
|
move(g, from, to);
|
||||||
board[from] = E;
|
|
||||||
|
|
||||||
/* increment to make it next turn */
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static double heuristic(struct game_state* g, int player)
|
||||||
* 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])
|
|
||||||
{
|
{
|
||||||
int x = -1, y = -1;
|
if (draw(g, player))
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
if (checkmate(g, -player))
|
||||||
const char c = input[i];
|
return player * INFINITY;
|
||||||
|
|
||||||
const char v = isalpha(c) ? toupper(c) : c;
|
double score = 0;
|
||||||
|
for (index_t i=0; i<BOARD_SIZE; i++) {
|
||||||
|
const piece_t piece = g->board[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')
|
static void print_debug(struct game_state* g, int player)
|
||||||
x = v - 'A';
|
{
|
||||||
else if ('1' <= v && v <= '8')
|
//struct game_state {
|
||||||
y = v - '1';
|
// 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; i<BOARD_SIZE; i++) {
|
||||||
|
if (g->board[i] == EMPTY || !friends(g->board[i], player))
|
||||||
|
continue;
|
||||||
|
for (int j=0; j<BOARD_SIZE; j++) {
|
||||||
|
if (!move_ok(g, i, j, player))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
memcpy(restore, g, sizeof *g);
|
||||||
|
move(g, i, j);
|
||||||
|
double x = -alpha_beta(g, -beta, -m, -player, depth-1);
|
||||||
|
memcpy(g, restore, sizeof *g);
|
||||||
|
|
||||||
|
// alpha-beta pruning
|
||||||
|
if (x > m) {
|
||||||
|
m = x;
|
||||||
|
}
|
||||||
|
if (m >= beta) {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x != -1 && y != -1)
|
return m;
|
||||||
return 8 * (7 - y) + x;
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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; i<BOARD_SIZE; i++) {
|
||||||
|
if (!friends(g->board[i], player))
|
||||||
|
continue;
|
||||||
|
for (int j=0; j<BOARD_SIZE; j++) {
|
||||||
|
if (!move_ok(g, i, j, player))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
memcpy(restore, g, sizeof *g);
|
||||||
|
move(g, i, j);
|
||||||
|
double x = -alpha_beta(g, -INFINITY, -max, -player, depth-1);
|
||||||
|
memcpy(g, restore, sizeof *g);
|
||||||
|
|
||||||
|
if (x > 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
|
|
||||||
#include "graphics.h"
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/** 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);
|
|
||||||
}
|
|
||||||
230
src/pieces.c
230
src/pieces.c
@@ -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);
|
|
||||||
}
|
|
||||||
130
src/util.c
130
src/util.c
@@ -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;
|
|
||||||
}
|
|
||||||
60
testing/test_threatmap.c
Normal file
60
testing/test_threatmap.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user