chess

Terminal based Chess trainer using Anki
git clone git://git.dimitrijedobrota.com/chess.git
Log | Files | Refs

main.c (6168B)


      1 #include <ctype.h>
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 
      6 #define TB_IMPL
      7 #define TB_OPT_TRUECOLOR
      8 
      9 #include <cii/except.h>
     10 #include <cii/mem.h>
     11 #include <pane/menu.h>
     12 #include <pane/pane.h>
     13 #include <pane/utils.h>
     14 #include <pane/widgets.h>
     15 
     16 #include "anki.h"
     17 #include "board.h"
     18 #include "display.h"
     19 
     20 /* static char *profile = NULL; */
     21 
     22 void set_selectDeck(void);
     23 void set_selectProfile(void);
     24 void profile_selected(char *name, int ignore);
     25 void deck_selected(char *name, int ignore);
     26 
     27 void start(void);
     28 void stop(void);
     29 
     30 struct widget_T error_widget = {
     31     .pane = NULL,
     32     .callback = widgetCenter_print,
     33     .title = "Error",
     34 };
     35 
     36 struct menuInfo_T  mainMenuInfo;
     37 struct menuStyle_T mainMenuStyle = {
     38     .separator = "<------------->",
     39     .padding = 2,
     40     .spacing = 1,
     41 };
     42 struct widget_T mainMenu_widget = {
     43     .pane = NULL,
     44     .callback = pane_menu,
     45     .title = "Main Menu",
     46     .style = &mainMenuStyle,
     47     .info = &mainMenuInfo,
     48 };
     49 
     50 menu_T anki_to_menu(const char **(*getter)(size_t *), menuItem_select_f select,
     51                     menu_back_f back) {
     52   size_t       i, size;
     53   const char **profiles = getter(&size);
     54 
     55   menu_T menu = menu_new(size, back);
     56   for (i = 0; i < size; i++) {
     57     menu->items[i].select_f = select;
     58     menu->items[i].name = profiles[i];
     59   }
     60 
     61   return menu;
     62 }
     63 
     64 struct boardInfo_T  boardInfo;
     65 struct boardStyle_T boardStyle = {
     66     .square_dark = TB_GREEN,
     67     .square_light = TB_WHITE,
     68     .piece = TB_BLACK,
     69     .pass = TB_BLUE,
     70     .fail = TB_RED,
     71     .border = 0,
     72     .annotation = 3,
     73 };
     74 struct widget_T board_widget = {
     75     .pane = NULL,
     76     .callback = board_display,
     77     .title = "Board",
     78     .style = &boardStyle,
     79     .info = &boardInfo,
     80 };
     81 
     82 #define MOVE_PADDING 8
     83 
     84 struct movesInfo_T  movesInfo;
     85 struct movesStyle_T movesStyle = {
     86     .move_padding = MOVE_PADDING,
     87     .num_padding = 4,
     88     .foreground = TB_WHITE,
     89     .background = 0,
     90     .active = TB_BLUE,
     91 };
     92 struct widget_T moves_widget = {
     93     .pane = NULL,
     94     .callback = moves_display,
     95     .title = "Moves",
     96     .style = &movesStyle,
     97     .info = &movesInfo,
     98 };
     99 
    100 void title_callback(widget_T widget) {
    101   game_T       game = (game_T)widget->data->payload;
    102   titleInfo_T  info = (titleInfo_T)widget->info;
    103   titleStyle_T style = (titleStyle_T)widget->style;
    104 
    105   data_T data = data_new(card_name(game->card), NULL);
    106   widget_setData(info->subwidget, data);
    107   info->subwidget->pane = widget->pane;
    108   info->subwidget->callback(info->subwidget);
    109 
    110   UNUSED(style);
    111 }
    112 
    113 struct widget_T center_widget = {
    114     .pane = NULL,
    115     .callback = widgetCenter_print,
    116     .title = "Title",
    117 };
    118 
    119 struct titleInfo_T titleInfo = {
    120     .subwidget = &center_widget,
    121 };
    122 
    123 struct titleStyle_T titleStyle;
    124 
    125 struct widget_T title_widget = {
    126     .pane = NULL,
    127     .callback = title_callback,
    128     .title = "Title",
    129     .style = &titleStyle,
    130     .info = &titleInfo,
    131 };
    132 
    133 void done_review(void) {
    134   char message[] =
    135       "You have finished all of the reviews for this deck.  Please "
    136       "check back later!";
    137   struct tb_event ev;
    138   data_T          data = data_new(message, NULL);
    139   widget_setData(&error_widget, data);
    140   widget_activate(&error_widget, MAIN);
    141   error_widget.callback(&error_widget);
    142   tb_poll_event(&ev);
    143   if (ev.ch == 'q' || ev.key == TB_KEY_ESC)
    144     stop();
    145 }
    146 
    147 void review_finished(void) {
    148 
    149   pane_unsplit(MAIN);
    150   pane_clear(MAIN, 0);
    151 
    152   done_review();
    153   set_selectDeck();
    154   widget_activate(&mainMenu_widget, MAIN);
    155 }
    156 
    157 int review_card(void) {
    158   char  *fen;
    159   size_t moves_num, i;
    160 
    161   card_T card = anki_current_card();
    162 
    163   if (card == NULL) {
    164     review_finished();
    165     return 0;
    166   }
    167 
    168   char **moves = Move_list(card_pgn(card), &moves_num);
    169   game_T game = game_new(moves_num);
    170 
    171   int white = 1;
    172   game->boards[0] = (fen = card_fen(card)) ? Board_from_FEN(fen) : Board_new();
    173   for (i = 1; i <= moves_num; i++, white = !white)
    174     game->boards[i] = Board_play(game->boards[i - 1], moves[i], white);
    175 
    176   game->card = card;
    177   game->review_next = review_card;
    178   game->move_start = game->move_current = game->display_current =
    179       atoi(card_start(card));
    180   game->moves = moves;
    181 
    182   data_T data = data_new(game, game_handleInput);
    183   widget_setData(&board_widget, data);
    184   widget_setData(&moves_widget, data);
    185   widget_setData(&title_widget, data);
    186 
    187   return 1;
    188 }
    189 
    190 void deck_selected(char *name, int ignore) {
    191   UNUSED(ignore);
    192   Pane_T *children1, *children2;
    193 
    194   anki_load_deck(name);
    195 
    196   if (anki_current_card()) {
    197     pane_clear(MAIN, 1);
    198     children1 = pane_vsplit(MAIN, 2, -5, 1);
    199     children2 = pane_split(children1[1], 2, -(MOVE_PADDING * 3 + 4), 2);
    200 
    201     review_card();
    202 
    203     widget_activate(&title_widget, children1[0]);
    204     widget_activate(&moves_widget, children2[0]);
    205     widget_activate(&board_widget, children2[1]);
    206   } else {
    207     done_review();
    208     set_selectDeck();
    209     widget_activate(&mainMenu_widget, MAIN);
    210   }
    211 }
    212 
    213 void profile_selected(char *name, int ignore) {
    214   UNUSED(ignore);
    215   /* if (!profile) */
    216   /*   profile = malloc(100 * sizeof(char)); */
    217   /* strcpy(profile, name); */
    218   anki_load_profile(name);
    219   set_selectDeck();
    220 }
    221 
    222 void set_selectProfile() {
    223   menu_T menu = anki_to_menu(anki_get_profiles, profile_selected, stop);
    224   data_T data = data_new(menu, menu_hadleInput);
    225   widget_setData(&mainMenu_widget, data);
    226 }
    227 
    228 void set_selectDeck() {
    229   menu_T menu = anki_to_menu(anki_get_decks, deck_selected, set_selectProfile);
    230   data_T data = data_new(menu, menu_hadleInput);
    231   widget_setData(&mainMenu_widget, data);
    232 }
    233 
    234 int main(void) {
    235   start();
    236   {
    237     set_selectProfile();
    238     widget_activate(&mainMenu_widget, MAIN);
    239     pane_event_loop();
    240   }
    241   stop();
    242 
    243   return 0;
    244 }
    245 
    246 void stop(void) {
    247   anki_stop();
    248   pane_stop();
    249   exit(1);
    250 }
    251 
    252 void start(void) {
    253   struct tb_event ev;
    254   char message[] = "Can't start anki, make sure it's running and try again";
    255 
    256   pane_start(0);
    257   data_T data = data_new(message, NULL);
    258   widget_setData(&error_widget, data);
    259   widget_activate(&error_widget, MAIN);
    260 
    261   TRY { anki_start(); }
    262   EXCEPT(ANKIE_CONNECT) {
    263     error_widget.callback(&error_widget);
    264     tb_poll_event(&ev);
    265     if (ev.ch == 'q' || ev.key == TB_KEY_ESC)
    266       stop();
    267     start();
    268   }
    269   ELSE {
    270     stop();
    271     RERAISE;
    272   }
    273   END_TRY;
    274 }