diff --git a/.gitignore b/.gitignore index 9f2cd5e..4704c9e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ move bits eval debug +bodichess *.s /test/ /obj/ diff --git a/Makefile b/Makefile index a41733d..69987f5 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,9 @@ CC=gcc .SECONDEXPANSION: OBJ=$(addprefix $(OBJDIR)/,$(SRC_S:.c=.o)) -BIN=fen pool piece move debug eval bits +BIN=fen pool piece move debug eval bits bodichess +LIBS = -lreadline -lncurses CFLAGS += -std=gnu99 #CFLAGS += -O2 @@ -72,15 +73,21 @@ clean: #$(OBJ): $(OBJDIR)/%.o: $(SRCDIR)/%.c # @mkdir -p $(@D) # $(CC) -c $(CFLAGS) -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR)/%.c +$(OBJDIR)/%.o: @mkdir -p $(@D) @echo compiling $@. @$(CC) -c $(CFLAGS) -o $@ $< #fen: CFLAGS+=-DBIN_$$@ +#$(BIN): $$(subst $(OBJDIR)/$$@.o,,$(OBJ)) $(SRCDIR)/$$@.c +# @echo compiling $@. +# @$(CC) -DBIN_$@ $(CFLAGS) $^ $(LIBS) -o $@ + +# TODO: find a better dependancy graph $(BIN): $$(subst $(OBJDIR)/$$@.o,,$(OBJ)) $(SRCDIR)/$$@.c @echo compiling $@. - @$(CC) -DBIN_$@ $(CFLAGS) $^ -o $@ + @echo NEED_TO_CHANGE_THIS=$^ + @$(CC) -DBIN_$@ $(CFLAGS) $^ $(LIBS) -o $@ #pool: CFLAGS+=-DPOOLBIN #pool: $$(subst $(OBJDIR)/$$@.o,,$(OBJ)) $(SRCDIR)/$$@.c diff --git a/make.deps b/make.deps index 947bf82..caaabb4 100644 --- a/make.deps +++ b/make.deps @@ -1,4 +1,7 @@ ./obj/bits.o:: src/bits.c src/bits.h src/debug.h +./obj/bodichess.o:: src/bodichess.c src/chessdefs.h src/bits.h src/debug.h \ + src/board.h src/piece.h src/list.h src/pool.h src/move.h src/position.h \ + src/fen.h src/eval.h src/bodichess.h ./obj/debug.o:: src/debug.c src/debug.h ./obj/eval.o:: src/eval.c src/eval.h src/position.h src/chessdefs.h src/bits.h \ src/debug.h src/board.h src/piece.h src/list.h src/pool.h diff --git a/src/bodichess.c b/src/bodichess.c new file mode 100644 index 0000000..b0c3ede --- /dev/null +++ b/src/bodichess.c @@ -0,0 +1,423 @@ +/* bodichess.c - main loop. + * + * Copyright (C) 2021 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + + +#include +#include +#include +#include +#include +#include + +#include "chessdefs.h" +#include "board.h" +#include "piece.h" +#include "move.h" +#include "list.h" +#include "debug.h" +#include "fen.h" +#include "eval.h" +#include "bodichess.h" + +typedef struct { + char *name; /* User printable name */ + int (*func)(pos_t *, char *); /* function doing the job */ + char *doc; /* function doc */ +} COMMAND; + +/* readline example inspired by : + * - https://thoughtbot.com/blog/tab-completion-in-gnu-readline + * - http://web.mit.edu/gnu/doc/html/rlman_2.html + */ +char **commands_completion(const char *, int, int); +char *commands_generator(const char *, int); +char *escape(const char *); +int quote_detector(char *, int); +int execute_line (pos_t *, char *line); +COMMAND *find_command (char *); +char *stripwhite (char *string); + +/* The names of functions that actually do the manipulation. */ +int do_help (pos_t *, char*); +int do_fen (pos_t *, char*); +int do_pos (pos_t *, char*); +int do_genmoves(pos_t *, char*); +int do_prmoves(pos_t *, char*); +int do_eval (pos_t *, char*); +int do_quit (pos_t *, char*); + +COMMAND commands[] = { + { "help", do_help, "Display this text" }, + { "?", do_help, "Synonym for 'help'" }, + { "fen", do_fen, "Set position to FEN" }, + { "pos", do_pos, "Print current position" }, + { "quit", do_quit, "Quit" }, + { "genmv", do_genmoves, "Generate next move list" }, + { "prmv", do_prmoves, "Generate next move list" }, + { "eval", do_eval, "Eval current position" }, + { NULL, (int (*)()) NULL, NULL } +}; + +static int done=0; + +int bodichess(pos_t *pos) +{ + char *buffer, *s; + + rl_attempted_completion_function = commands_completion; + rl_completer_quote_characters = "'\""; + rl_completer_word_break_characters = " "; + rl_char_is_quoted_p = "e_detector; + + while (!done) { + buffer = readline("chess> "); + if (!buffer) + break; + /* Remove leading and trailing whitespace from the line. + * Then, if there is anything left, add it to the history list + * and execute it. + */ + s = stripwhite (buffer); + + if (*s) { + add_history (s); + execute_line (pos, s); + } + free (buffer); + } + + return 0; +} + +//char **commands_completion(const char *text, int start, int end) +char **commands_completion(const char *text, + __attribute__((unused)) int start, + __attribute__((unused)) int end) +{ + rl_attempted_completion_over = 1; + return rl_completion_matches(text, commands_generator); +} + +char *commands_generator(const char *text, int state) +{ + static int list_index, len; + char *name; + + if (!state) { + list_index = 0; + len = strlen(text); + } + + while ((name = commands[list_index++].name)) { + if (rl_completion_quote_character) { + name = strdup(name); + } else { + name = escape(name); + } + + if (strncmp(name, text, len) == 0) { + return name; + } else { + free(name); + } + } + + return NULL; +} + +char *escape(const char *original) +{ + size_t original_len; + size_t i, j; + char *escaped, *resized_escaped; + + original_len = strlen(original); + + if (original_len > SIZE_MAX / 2) { + errx(1, "string too long to escape"); + } + + if ((escaped = malloc(2 * original_len + 1)) == NULL) { + err(1, NULL); + } + + for (i = 0, j = 0; i < original_len; ++i, ++j) { + if (original[i] == ' ') { + escaped[j++] = '\\'; + } + escaped[j] = original[i]; + } + escaped[j] = '\0'; + + if ((resized_escaped = realloc(escaped, j)) == NULL) { + free(escaped); + resized_escaped = NULL; + err(1, NULL); + } + + return resized_escaped; +} + +int quote_detector(char *line, int index) +{ + return ( + index > 0 && + line[index - 1] == '\\' && + !quote_detector(line, index - 1) + ); +} + +/* Execute a command line. */ +int execute_line (pos_t *pos, char *line) +{ + register int i; + COMMAND *command; + char *word; + + /* Isolate the command word. */ + i = 0; + while (line[i] && whitespace (line[i])) + i++; + word = line + i; + + while (line[i] && !whitespace (line[i])) + i++; + + if (line[i]) + line[i++] = '\0'; + + command = find_command (word); + + if (!command) { + fprintf (stderr, "%s: Unknown command.\n", word); + return (-1); + } + + /* Get argument to command, if any. */ + while (whitespace (line[i])) + i++; + + word = line + i; + + /* return command number */ + return ((*(command->func)) (pos, word)); +} + +/* Look up NAME as the name of a command, and return a pointer to that + command. Return a NULL pointer if NAME isn't a command name. */ +COMMAND *find_command(char *name) +{ + register int i; + + for (i = 0; commands[i].name; i++) + if (strcmp (name, commands[i].name) == 0) + return (&commands[i]); + + return ((COMMAND *)NULL); +} + +/* Strip whitespace from the start and end of STRING. Return a pointer + into STRING. */ +char *stripwhite(char *string) +{ + register char *s, *t; + + for (s = string; whitespace (*s); s++) + ; + + if (*s == 0) + return (s); + + t = s + strlen (s) - 1; + while (t > s && whitespace (*t)) + t--; + *++t = '\0'; + + return s; +} + +int do_eval(__attribute__((unused)) pos_t *pos, + __attribute__((unused)) char *arg) +{ + eval_t res = eval(pos); + printf("eval=%ld (%.3f pawns)\n", res, (float)res/100); + return 1; +} + +int do_fen(pos_t *pos, char *arg) +{ + log_f(1, "%s\n", arg); + fen2pos(pos, arg); + return 1; +} + +int do_pos (pos_t *pos, + __attribute__((unused)) char *arg) +{ + log_f(1, "%s\n", arg); + pos_print(pos); + return 1; +} + +int do_genmoves(pos_t *pos, + __attribute__((unused)) char *arg) +{ + log_f(1, "%s\n", arg); + moves_gen(pos, OPPONENT(pos->turn), false); + moves_gen(pos, pos->turn, true); + return 1; +} + +int do_prmoves(pos_t *pos, + __attribute__((unused)) char *arg) +{ + log_f(1, "%s\n", arg); + moves_print(pos, M_PR_SEPARATE); + return 1; +} + +int do_quit(__attribute__((unused)) pos_t *pos, + __attribute__((unused)) char *arg) +{ + return done = 1; +} + +/* Print out help for ARG, or for all of the commands if ARG is + not present. */ +int do_help(__attribute__((unused)) pos_t *pos, + __attribute__((unused)) char *arg) +{ + register int i; + int printed = 0; + + for (i = 0; commands[i].name; i++) { + if (!*arg || (strcmp (arg, commands[i].name) == 0)) { + printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc); + printed++; + } + } + + if (!printed) { + printf ("No commands match `%s'. Possibilties are:\n", arg); + + for (i = 0; commands[i].name; i++) { + /* Print in six columns. */ + if (printed == 6) { + printed = 0; + printf ("\n"); + } + + printf ("%s\t", commands[i].name); + printed++; + } + + if (printed) + printf ("\n"); + } + return (0); +} + +/* + static char *read_line(void) + { + char *line = NULL; + ssize_t bufsize = 0; // have getline allocate a buffer for us + + if (getline(&line, &bufsize, stdin) == -1){ + if (feof(stdin)) { + exit(EXIT_SUCCESS); // We recieved an EOF + } else { + perror("readline"); + exit(EXIT_FAILURE); + } + } + + return line; + } +*/ +/* + int bodichess(pos_t *pos) + { + while (1) { + printf("bodichess> "); + if (side == computer_side) // programme joue + { + //recherche du meilleur coup a profondeur max_depth + bestMove = programme_joue(max_depth, 1); + jouer_coup(bestMove); + affiche_echiquier(); + affiche_infos(); + affiche_resultat(); + continue; + } + printf("isa> "); + //printf("Taper help pour voir les commandes dispos\n"); + if (scanf("%s", s) == EOF) //ferme le programme + return 0; + if (!strcmp(s, "d")) //affichage de l'échiquier et des infos + { + affiche_echiquier(); + continue; + } + debug_init(1); + piece_pool_init(); + moves_pool_init(); + pos_pool_init(); + pos = pos_get(); + } +*/ + +#ifdef BIN_bodichess +/** main() + * options: + int bodichess(pos_t *pos) + * + */ +static int usage(char *prg) +{ + fprintf(stderr, "Usage: %s [-ilw] [file...]\n", prg); + return 1; +} + +#include + +int main(int ac, char **av) +{ + pos_t *pos; + int opt; + + debug_init(1); + piece_pool_init(); + moves_pool_init(); + pos_pool_init(); + pos = pos_get(); + + while ((opt = getopt(ac, av, "d:f:")) != -1) { + switch (opt) { + case 'd': + debug_level_set(atoi(optarg)); + break; + case 'f': + fen2pos(pos, optarg); + break; + default: + return usage(*av); + } + } + printf("optind = %d ac = %d\n", optind, ac); + if (optind < ac) + return usage(*av); + + return bodichess(pos); +} +#endif /* BIN_bodichess */ diff --git a/src/bodichess.h b/src/bodichess.h new file mode 100644 index 0000000..f32bddf --- /dev/null +++ b/src/bodichess.h @@ -0,0 +1,21 @@ +/* bodichess.h - main loop. + * + * Copyright (C) 2021 Bruno Raoult ("br") + * Licensed under the GNU General Public License v3.0 or later. + * Some rights reserved. See COPYING. + * + * You should have received a copy of the GNU General Public License along with this + * program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#ifndef BODICHESS_H +#define BODICHESS_H + +#include "position.h" + +int bodichess(pos_t *pos); + +#endif /* BODICHESS_H */ diff --git a/src/fen.h b/src/fen.h index a709816..714137e 100644 --- a/src/fen.h +++ b/src/fen.h @@ -18,4 +18,4 @@ pos_t *fen2pos(pos_t *pos, char *fen); -#endif +#endif /* FEN_H */ diff --git a/src/move.c b/src/move.c index 683dee1..01e7b8a 100644 --- a/src/move.c +++ b/src/move.c @@ -13,6 +13,7 @@ #include #include + #include "chessdefs.h" #include "board.h" #include "piece.h" @@ -379,7 +380,7 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece, bool doit) u64 bb_new; # ifdef DEBUG_MOVE - log_f(1, "pos:%p turn:%s piece:%d [%s %s] at %#04x[%c%c]\n", + log_f(2, "pos:%p turn:%s piece:%d [%s %s] at %#04x[%c%c]\n", pos, IS_WHITE(pos->turn)? "white": "black", piece, @@ -413,7 +414,7 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece, bool doit) /* king: do not move to opponent controlled square */ if (piece == KING && pos->controlled[OPPONENT(vcolor)] & bb_new) { # ifdef DEBUG_MOVE - log_i(1, "%s king cannot move to %c%c\n", + log_i(2, "%s king cannot move to %c%c\n", IS_WHITE(color)? "white": "black", FILE2C(GET_F(new)), RANK2C(GET_R(new))); # endif @@ -424,7 +425,7 @@ int pseudo_moves_gen(pos_t *pos, piece_list_t *ppiece, bool doit) //bitboard_print(pos->occupied[vcolor]); //bitboard_print(pos->occupied[OPPONENT(vcolor)]); # ifdef DEBUG_MOVE - log_i(1, "BB: skipping %#llx [%c%c] (same color piece)\n", + log_i(2, "BB: skipping %#llx [%c%c] (same color piece)\n", new, FILE2C(GET_F(new)), RANK2C(GET_R(new))); # endif break; @@ -455,7 +456,7 @@ int moves_gen(pos_t *pos, bool color, bool doit) int count = 0; # ifdef DEBUG_MOVE - log_f(1, "color:%s doit:%d\n", color? "Black": "White", doit); + log_f(2, "color:%s doit:%d\n", color? "Black": "White", doit); # endif piece_list = &pos->pieces[color]; @@ -477,7 +478,7 @@ int moves_gen(pos_t *pos, bool color, bool doit) int move_doit(pos_t *pos, move_t *move) { # ifdef DEBUG_MOVE_TOTO - log_f(1, "color:%s doit:%d\n", color? "Black": "White", doit); + log_f(2, "color:%s doit:%d\n", color? "Black": "White", doit); # endif if (pos && move) return 1; @@ -501,7 +502,7 @@ int main(int ac, char**av) } else { fen2pos(pos, av[1]); } - printf("turn = %d opponent = %d\n", pos->turn, OPPONENT(pos->turn)); + //printf("turn = %d opponent = %d\n", pos->turn, OPPONENT(pos->turn)); moves_gen(pos, OPPONENT(pos->turn), false); moves_gen(pos, pos->turn, true); pos_print(pos); @@ -511,4 +512,4 @@ int main(int ac, char**av) //bitboard_print2(castle_squares[0].controlled, castle_squares[1].controlled); //bitboard_print2(castle_squares[0].occupied, castle_squares[1].occupied); } -#endif +#endif /* BIN_move */ diff --git a/src/position.h b/src/position.h index 160f01b..8378c7a 100644 --- a/src/position.h +++ b/src/position.h @@ -48,4 +48,4 @@ pool_t *pos_pool_init(); pos_t *pos_get(); pos_t *pos_dup(pos_t *pos); -#endif +#endif /* POSITION_H */