chess

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

anki.c (7087B)


      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 
      5 #include <cii/assert.h>
      6 #include <cii/except.h>
      7 #include <cii/mem.h>
      8 #include <curl/curl.h>
      9 #include <json-c/json.h>
     10 
     11 #include "anki.h"
     12 #include "display.h"
     13 
     14 #define Move_T card_T
     15 
     16 struct card_T {
     17   char *id;
     18   char *name;
     19   char *game;
     20   char *fen;
     21   char *pgn;
     22   char *player;
     23   char *start;
     24 };
     25 
     26 Move_T card;
     27 
     28 #define ANKI_URL          "127.0.0.1:8765"
     29 #define ANKI_CURRENT_CARD "{\"version\": 6, \"action\": \"guiCurrentCard\"}"
     30 #define ANKI_EXIT         "{\"version\": 6, \"action\": \"guiExitAnki\"}"
     31 #define ANKI_INIT         "{\"version\": 6, \"action\": \"requestPermission\"}"
     32 #define ANKI_SYNC         "{\"version\": 6, \"action\": \"sync\"}"
     33 
     34 #define ANKI_PROFILES "{\"version\": 6, \"action\": \"getProfiles\"}"
     35 #define ANKI_DECKS    "{\"version\": 6, \"action\": \"deckNames\"}"
     36 
     37 #define ANKI_GRADE_FAIL                                                        \
     38   "{\"version\": 6, \"action\": \"multi\", \"params\": { \"actions\": [ "      \
     39   "{\"action\": \"guiShowAnswer\"}, { \"action\": \"guiAnswerCard\", "         \
     40   "\"params\": {\"ease\": 1}}]}}"
     41 #define ANKI_GRADE_PASS                                                        \
     42   "{\"version\": 6, \"action\": \"multi\", \"params\": { \"actions\": [ "      \
     43   "{\"action\": \"guiShowAnswer\"}, { \"action\": \"guiAnswerCard\", "         \
     44   "\"params\": {\"ease\": 3}}]}}"
     45 
     46 #define JSON_PRINT_FLAGS JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY
     47 #define JSON_PRINT(x)                                                          \
     48   fprintf(stderr, "%s\n---\n",                                                 \
     49           json_object_to_json_string_ext((x), JSON_PRINT_FLAGS));
     50 #define JSON_STRING_ASSERT(x, value)                                           \
     51   strstr(json_object_get_string((x)), (value)) != NULL
     52 
     53 CURL    *curl;
     54 CURLcode res;
     55 
     56 struct json_object *result;
     57 size_t got_data(void *buffer, size_t itemsize, size_t nitems, void *ignore);
     58 
     59 const Except_T ANKIE_CONNECT = {"Anki: Couldn't connect to the server"},
     60                ANKIE_CURL = {"Anki: Couldn't initialize libcurl"},
     61                ANKIE_DECK = {"Anki: Invalid deck"},
     62                ANKIE_DECK_SELECT = {"Anki: Invalid deck selection"},
     63                ANKIE_GRADE = {"Anki: Unable to grade current card"},
     64                ANKIE_OK = {"everything is ok"},
     65                ANKIE_PROFILE = {"Anki: Invalid profile"},
     66                ANKIE_PROFILE_SELECT = {"Anki: Invalid profile selection"},
     67                ANKIE_SUSPEND = {"Anki: Unable to suspend current card"};
     68 
     69 void curl_do(char *action) {
     70   curl_easy_setopt(curl, CURLOPT_URL, ANKI_URL);
     71   curl_easy_setopt(curl, CURLOPT_POSTFIELDS, action);
     72   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, got_data);
     73 
     74   res = curl_easy_perform(curl);
     75 
     76   switch (res) {
     77   case CURLE_OK:
     78     break;
     79   case CURLE_COULDNT_CONNECT:
     80     RAISE(ANKIE_CONNECT);
     81   default:
     82     RAISE(ANKIE_CONNECT);
     83   }
     84 
     85   /* JSON_PRINT(result); */
     86 }
     87 
     88 size_t got_data(void *buffer, size_t itemsize, size_t nitems, void *ignore) {
     89   (void)ignore;
     90 
     91   size_t bytes = itemsize * nitems;
     92   free(result);
     93   struct json_object *parsed_json = json_tokener_parse(buffer);
     94   json_object_object_get_ex(parsed_json, "result", &result);
     95   return bytes;
     96 }
     97 
     98 void anki_connect(void) {
     99   struct json_object *permission;
    100   curl_do(ANKI_INIT);
    101   json_object_object_get_ex(result, "permission", &permission);
    102 
    103   if (!(JSON_STRING_ASSERT(permission, "granted")))
    104     RAISE(ANKIE_CONNECT);
    105 }
    106 
    107 void card_dtor(Move_T self) {
    108   FREE(self->fen);
    109   FREE(self->game);
    110   FREE(self->id);
    111   FREE(self->name);
    112   FREE(self->pgn);
    113   FREE(self->player);
    114   FREE(self->start);
    115 }
    116 
    117 void card_set_field(Move_T self, char *key, const char *name) {
    118   char **field;
    119 
    120   if (strcmp(key, "id") == 0)
    121     field = &self->id;
    122   else if (strcmp(key, "name") == 0)
    123     field = &self->name;
    124   else if (strcmp(key, "FEN") == 0)
    125     field = &self->fen;
    126   else if (strcmp(key, "PGN") == 0)
    127     field = &self->pgn;
    128   else if (strcmp(key, "player") == 0)
    129     field = &self->player;
    130   else if (strcmp(key, "game") == 0)
    131     field = &self->game;
    132   else if (strcmp(key, "start") == 0)
    133     field = &self->start;
    134   else
    135     return;
    136 
    137   *field = ALLOC(strlen(name) * sizeof(char) + 1);
    138   strcpy(*field, name);
    139 }
    140 
    141 Move_T anki_current_card(void) {
    142   struct json_object *fields, *card_id;
    143   /* FREE(card); */
    144   NEW(card);
    145 
    146   curl_do(ANKI_CURRENT_CARD);
    147   if (result == NULL)
    148     return NULL;
    149 
    150   json_object_object_get_ex(result, "fields", &fields);
    151   json_object_object_get_ex(result, "cardId", &card_id);
    152 
    153   json_object_object_foreach(fields, key, val_t) {
    154     struct json_object *value;
    155     json_object_object_get_ex(val_t, "value", &value);
    156     card_set_field(card, key, json_object_get_string(value));
    157   }
    158 
    159   card_set_field(card, "id", json_object_get_string(card_id));
    160 
    161   if (!(card->name && card->pgn)) {
    162     printf("Card info incomplete. Suspending it...\n");
    163     anki_suspend(card);
    164     return 0;
    165   }
    166 
    167   return card;
    168 }
    169 
    170 void anki_start(void) {
    171   if (!(curl = curl_easy_init()))
    172     RAISE(ANKIE_CURL);
    173   anki_connect();
    174 }
    175 
    176 const char **anki_list(size_t *size) {
    177   size_t       len = json_object_array_length(result), i;
    178   const char **list;
    179 
    180   list = ALLOC(len * sizeof(const char *));
    181   for (i = 0; i < len; i++)
    182     list[i] = json_object_get_string(json_object_array_get_idx(result, i));
    183 
    184   *size = len;
    185   return list;
    186 }
    187 
    188 const char **anki_get_profiles(size_t *size) {
    189   curl_do(ANKI_PROFILES);
    190   return anki_list(size);
    191 }
    192 
    193 void anki_load_profile(char *name) {
    194   static char request[100];
    195   sprintf(request,
    196           "{\"version\": 6, \"action\": \"loadProfile\", \"params\": { "
    197           "\"name\": \"%s\" }}",
    198           name);
    199   curl_do(request);
    200   if (!(JSON_STRING_ASSERT(result, "true")))
    201     RAISE(ANKIE_PROFILE);
    202 }
    203 
    204 const char **anki_get_decks(size_t *size) {
    205   curl_do(ANKI_DECKS);
    206   return anki_list(size);
    207 }
    208 
    209 void anki_load_deck(char *name) {
    210   static char request[1000];
    211 
    212   if (!name)
    213     RAISE(ANKIE_DECK_SELECT);
    214   sprintf(request,
    215           "{\"version\": 6, \"action\": \"guiDeckReview\", \"params\": { "
    216           "\"name\": \"%s\" }}",
    217           name);
    218   curl_do(request);
    219 
    220   if (!(JSON_STRING_ASSERT(result, "true")))
    221     RAISE(ANKIE_DECK);
    222 }
    223 
    224 void anki_grade(int pass) {
    225   curl_do((pass) ? ANKI_GRADE_PASS : ANKI_GRADE_FAIL);
    226   if (!(JSON_STRING_ASSERT(result, "true")))
    227     RAISE(ANKIE_GRADE);
    228 }
    229 
    230 void anki_suspend(Move_T self) {
    231   static char request[100];
    232   sprintf(request,
    233           "{\"action\": \"suspend\", \"version\": 6, \"params\": { \"cards\": "
    234           "[%s] }}",
    235           self->id);
    236   curl_do(request);
    237   if (!(JSON_STRING_ASSERT(result, "true")))
    238     RAISE(ANKIE_SUSPEND);
    239 }
    240 
    241 void  anki_stop(void) { curl_easy_cleanup(curl); }
    242 char *card_name(Move_T self) { return self->name; }
    243 char *card_pgn(Move_T self) { return self->pgn; }
    244 char *card_start(Move_T self) { return self->start; }
    245 char *card_game(Move_T self) { return self->game; }
    246 char *card_player(Move_T self) { return self->player; }
    247 char *card_fen(Move_T self) { return self->fen; }
    248 char *card_id(Move_T self) { return self->id; }