Update copyright dates
[src/app-framework-binder.git] / bindings / samples / tic-tac-toe.c
index d600e19..82e8687 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016, 2017 "IoT.bzh"
+ * Copyright (C) 2015-2020 "IoT.bzh"
  * Author José Bollo <jose.bollo@iot.bzh>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
 #include <string.h>
 #include <json-c/json.h>
 
-#define AFB_BINDING_VERSION 2
+#define AFB_BINDING_VERSION 3
 #include <afb/afb-binding.h>
 
-/*
- * definition of waiters
- */
-struct waiter
-{
-       struct waiter *next;
-       struct afb_req req;
-};
+static const char SPACE = ' ';
+static const char CROSS = 'X';
+static const char ROUND = 'O';
+static const char NHERE = '+';
+static const int DEFLVL = 8;
 
 /*
  * definition of a board
@@ -44,7 +41,7 @@ struct board
        int id;
        int level;
        char board[9];
-       struct waiter *waiters;
+       afb_event_t event;
 };
 
 /*
@@ -73,13 +70,14 @@ static struct board *get_new_board()
        struct board *board = calloc(1, sizeof *board);
 
        /* initialisation */
-       memset(board->board, ' ', sizeof board->board);
+       memset(board->board, SPACE, sizeof board->board);
        board->use_count = 1;
-       board->level = 1;
+       board->level = DEFLVL;
        board->moves = 0;
        do {
                board->id = (rand() >> 2) % 1000;
        } while(board->id == 0 || search_board(board->id) != NULL);
+       board->event = afb_daemon_make_event("board");
 
        /* link */
        board->next = all_boards;
@@ -95,6 +93,7 @@ static void release_board(struct board *board)
        /* decrease the reference count ... */
        if (--board->use_count == 0) {
                /* ... no more use */
+               afb_event_unref(board->event);
                /* unlink from the list of boards */
                struct board **prv = &all_boards;
                while (*prv != NULL && *prv != board)
@@ -106,6 +105,18 @@ static void release_board(struct board *board)
        }
 }
 
+/*
+ * Checks who wins
+ * Returns zero if there is no winner
+ * Returns the char of the winner if a player won
+ */
+static char wins(const char b[9], int first, int incr)
+{
+       char c = b[first];
+
+       return c != SPACE && b[first + incr] == c && b[first + incr + incr] == c ? c : 0;
+}
+
 /*
  * Checks who wins
  * Returns zero if there is no winner
@@ -113,31 +124,39 @@ static void release_board(struct board *board)
  */
 static char winner(const char b[9])
 {
-       int i;
        char c;
 
-       /* check diagonals */
-       c = b[4];
-       if (c != ' ') {
-               if (b[0] == c && b[8] == c)
-                       return c;
-               if (b[2] == c && b[6] == c)
-                       return c;
-       }
+       c = wins(b, 0, 1);
+       if (c)
+               return c;
 
-       /* check lines */
-       for (i = 0 ; i <= 6 ; i += 3) {
-               c = b[i];
-               if (c != ' ' && b[i+1] == c && b[i+2] == c)
-                       return c;
-       }
+       c = wins(b, 3, 1);
+       if (c)
+               return c;
 
-       /* check columns */
-       for (i = 0 ; i <= 2 ; i++) {
-               c = b[i];
-               if (c != ' ' && b[i+3] == c && b[i+6] == c)
-                       return c;
-       }
+       c = wins(b, 6, 1);
+       if (c)
+               return c;
+
+       c = wins(b, 0, 3);
+       if (c)
+               return c;
+
+       c = wins(b, 1, 3);
+       if (c)
+               return c;
+
+       c = wins(b, 2, 3);
+       if (c)
+               return c;
+
+       c = wins(b, 0, 4);
+       if (c)
+               return c;
+
+       c = wins(b, 2, 2);
+       if (c)
+               return c;
 
        return 0;
 }
@@ -145,7 +164,7 @@ static char winner(const char b[9])
 /* get the color (X or 0) of the move of index 'move' */
 static char color(int move)
 {
-       return (move & 1) == 0 ? 'X' : '0';
+       return (move & 1) == 0 ? CROSS : ROUND;
 }
 
 /* adds the move to the board */
@@ -160,7 +179,7 @@ static void add_move(struct board *board, int index)
 static int get_random_move(char b[9])
 {
        int index = rand() % 9;
-       while (b[index] != ' ')
+       while (b[index] != SPACE)
                index = (index + 1) % 9;
        return index;
 }
@@ -174,40 +193,41 @@ static int get_random_move(char b[9])
  */
 static int score_position(char b[9], char c, int depth)
 {
-       int i, t, r;
+       int i, s, nc, wc, lc;
 
        /* check if winner */
        if (winner(b) == c)
                return 1;
 
        /* when depth of analysis is reached return unknown case */
-       if (--depth == 0)
+       if (--depth <= 0)
                return 0;
 
        /* switch to the opponent */
-       c = (char)('O' + 'X' - c);
+       c = (char)(ROUND + CROSS - c);
 
        /* inspect opponent moves */
-       r = 1;
+       nc = wc = lc = 0;
        for (i = 0 ; i < 9 ; i++) {
-               if (b[i] == ' ') {
+               if (b[i] == SPACE) {
                        b[i] = c;
-                       t = score_position(b, c, depth);
-                       b[i] = ' ';
-                       if (t > 0)
-                               return -1; /* opponent will win */
-
-                       if (t == 0)
-                               r = 0; /* something not clear */
+                       s = score_position(b, c, depth);
+                       b[i] = SPACE;
+                       if (s > 0)
+                               lc++; /* opponent's victory, inc loose count */
+                       else if (s < 0)
+                               wc++; /* current's victory, inc win count */
+                       else
+                               nc++; /* none wins, increment null count */
                }
        }
-       return r;
+       return lc ? -lc : wc;
 }
 
 /* get one move: return the computed index of the move */
 static int get_move(struct board *board)
 {
-       int index, depth, t, f;
+       int index, depth, f, s, smax, imax;
        char c;
        char b[9];
 
@@ -222,22 +242,24 @@ static int get_move(struct board *board)
 
        /* depth and more */
        memcpy(b, board->board, 9);
+       f = smax = 0;
        c = color(board->moves);
-       f = 0;
        for (index = 0 ; index < 9 ; index++) {
-               if (board->board[index] == ' ') {
+               if (board->board[index] == SPACE) {
                        board->board[index] = c;
-                       t = score_position(board->board, c, depth);
-                       board->board[index] = ' ';
-                       if (t > 0)
-                               return index;
-                       if (t < 0)
-                               b[index] = '+';
-                       else
+                       s = score_position(board->board, c, depth);
+                       board->board[index] = SPACE;
+                       if (s < 0)
+                               b[index] = NHERE;
+                       else if (s <= smax)
                                f = 1;
+                       else {
+                               smax = s;
+                               imax = index;
+                       }
                }
        }
-       return get_random_move(f ? b : board->board);
+       return smax ? imax : get_random_move(f ? b : board->board);
 }
 
 /*
@@ -279,37 +301,35 @@ static struct json_object *describe(struct board *board)
  */
 static void changed(struct board *board, const char *reason)
 {
-       struct waiter *waiter, *next;
-       struct json_object *description;
-
-       /* get the description */
-       description = describe(board);
+       afb_event_push(board->event, json_object_new_string(reason));
+}
 
-       waiter = board->waiters;
-       board->waiters = NULL;
-       while (waiter != NULL) {
-               next = waiter->next;
-               afb_req_success(waiter->req, json_object_get(description), reason);
-               afb_req_unref(waiter->req);
-               free(waiter);
-               waiter = next;
-       }
+static void *get_new_board_cb(void *closure)
+{
+       afb_req_t req = closure;
+       struct board *board = get_new_board();
+       afb_req_subscribe(req, board->event);
+       return board;
+}
 
-       afb_daemon_broadcast_event(reason, description);
+static void release_board_cb(void *closure)
+{
+       struct board *board = closure;
+       return release_board(board);
 }
 
 /*
  * retrieves the board of the request
  */
-static inline struct board *board_of_req(struct afb_req req)
+static inline struct board *board_of_req(afb_req_t req)
 {
-       return afb_req_context(req, (void*)get_new_board, (void*)release_board);
+       return afb_req_context(req, 0, get_new_board_cb, release_board_cb, req);
 }
 
 /*
  * start a new game
  */
-static void new(struct afb_req req)
+static void new(afb_req_t req)
 {
        struct board *board;
 
@@ -318,7 +338,7 @@ static void new(struct afb_req req)
        AFB_INFO("method 'new' called for boardid %d", board->id);
 
        /* reset the game */
-       memset(board->board, ' ', sizeof board->board);
+       memset(board->board, SPACE, sizeof board->board);
        board->moves = 0;
 
        /* replies */
@@ -331,7 +351,7 @@ static void new(struct afb_req req)
 /*
  * get the board
  */
-static void board(struct afb_req req)
+static void board(afb_req_t req)
 {
        struct board *board;
        struct json_object *description;
@@ -350,7 +370,7 @@ static void board(struct afb_req req)
 /*
  * move a piece
  */
-static void move(struct afb_req req)
+static void move(afb_req_t req)
 {
        struct board *board;
        int i;
@@ -379,7 +399,7 @@ static void move(struct afb_req req)
        }
 
        /* checks validity of the move */
-       if (board->board[i] != ' ') {
+       if (board->board[i] != SPACE) {
                AFB_WARNING("can't move to %s: room occupied", index);
                afb_req_fail(req, "error", "occupied");
                return;
@@ -399,7 +419,7 @@ static void move(struct afb_req req)
 /*
  * set the level
  */
-static void level(struct afb_req req)
+static void level(afb_req_t req)
 {
        struct board *board;
        int l;
@@ -434,7 +454,7 @@ static void level(struct afb_req req)
 /*
  * Join a board
  */
-static void join(struct afb_req req)
+static void join(afb_req_t req)
 {
        struct board *board, *new_board;
        const char *id;
@@ -451,7 +471,7 @@ static void join(struct afb_req req)
        /* none is a special id for joining a new session */
        if (strcmp(id, "none") == 0) {
                new_board = get_new_board();
-               goto success;
+               goto setctx;
        }
 
        /* searchs the board to join */
@@ -466,13 +486,17 @@ static void join(struct afb_req req)
         * function 'release_board'. So the use_count MUST not
         * be incremented.
         */
-       if (new_board != board)
-               new_board->use_count++;
+       if (new_board == board)
+               goto success;
 
-success:
+       new_board->use_count++;
+setctx:
        /* set the new board (and leaves the previous one) */
-       afb_req_context_set(req, new_board, (void*)release_board);
+       afb_req_unsubscribe(req, board->event);
+       afb_req_context(req, 1, NULL, release_board_cb, new_board);
+       afb_req_subscribe(req, new_board->event);
 
+success:
        /* replies */
        afb_req_success(req, NULL, NULL);
        return;
@@ -486,7 +510,7 @@ bad_request:
 /*
  * Undo the last move
  */
-static void undo(struct afb_req req)
+static void undo(afb_req_t req)
 {
        struct board *board;
        int i;
@@ -504,7 +528,7 @@ static void undo(struct afb_req req)
 
        /* undo the last move */
        i = board->history[--board->moves];
-       board->board[i] = ' ';
+       board->board[i] = SPACE;
 
        /* replies */
        afb_req_success(req, NULL, NULL);
@@ -516,7 +540,7 @@ static void undo(struct afb_req req)
 /*
  * computer plays
  */
-static void play(struct afb_req req)
+static void play(afb_req_t req)
 {
        struct board *board;
        int index;
@@ -543,27 +567,10 @@ static void play(struct afb_req req)
        changed(board, "play");
 }
 
-static void wait(struct afb_req req)
-{
-       struct board *board;
-       struct waiter *waiter;
-
-       /* retrieves the context for the session */
-       board = board_of_req(req);
-       AFB_INFO("method 'wait' called for boardid %d", board->id);
-
-       /* creates the waiter and enqueues it */
-       waiter = calloc(1, sizeof *waiter);
-       waiter->req = req;
-       waiter->next = board->waiters;
-       afb_req_addref(req);
-       board->waiters = waiter;
-}
-
 /*
  * array of the verbs exported to afb-daemon
  */
-static const struct afb_verb_v2 verbs[] = {
+static const afb_verb_t verbs[] = {
    { .verb="new",   .callback=new },
    { .verb="play",  .callback=play },
    { .verb="move",  .callback=move },
@@ -571,17 +578,17 @@ static const struct afb_verb_v2 verbs[] = {
    { .verb="level", .callback=level },
    { .verb="join",  .callback=join },
    { .verb="undo",  .callback=undo },
-   { .verb="wait",  .callback=wait },
    { .verb=NULL }
 };
 
 /*
  * description of the binding for afb-daemon
  */
-const afb_binding_v2 afbBindingV2 = {
+const afb_binding_t afbBindingV3 = {
        .api = "tictactoe",
        .specification = NULL,
-       .verbs = verbs
+       .verbs = verbs,
+       .noconcurrency = 1
 };