X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=bindings%2Fsamples%2Ftic-tac-toe.c;h=82e868729750cc29254242e4f336c468f2e13bde;hb=65353dce81a629e042800bb7b86fcd869a76727e;hp=0100fc058fa4b5c178d8561b4b3241a51c7ca2bb;hpb=7059e59cddc1c81321639875636e88895bc14309;p=src%2Fapp-framework-binder.git diff --git a/bindings/samples/tic-tac-toe.c b/bindings/samples/tic-tac-toe.c index 0100fc05..82e86872 100644 --- a/bindings/samples/tic-tac-toe.c +++ b/bindings/samples/tic-tac-toe.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 "IoT.bzh" + * Copyright (C) 2015-2020 "IoT.bzh" * Author José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,21 +20,14 @@ #include #include +#define AFB_BINDING_VERSION 3 #include -/* - * the interface to afb-daemon - */ -const struct afb_binding_interface *afbitf; - -/* - * 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 @@ -48,7 +41,7 @@ struct board int id; int level; char board[9]; - struct waiter *waiters; + afb_event_t event; }; /* @@ -77,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; @@ -99,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) @@ -110,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 @@ -117,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; } @@ -149,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 */ @@ -164,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; } @@ -178,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]; @@ -226,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); } /* @@ -283,46 +301,44 @@ 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(afbitf->daemon, 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; /* retrieves the context for the session */ board = board_of_req(req); - INFO(afbitf, "method 'new' called for boardid %d", board->id); + 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 */ @@ -335,14 +351,14 @@ 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; /* retrieves the context for the session */ board = board_of_req(req); - INFO(afbitf, "method 'board' called for boardid %d", board->id); + AFB_INFO("method 'board' called for boardid %d", board->id); /* describe the board */ description = describe(board); @@ -354,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; @@ -362,7 +378,7 @@ static void move(struct afb_req req) /* retrieves the context for the session */ board = board_of_req(req); - INFO(afbitf, "method 'move' called for boardid %d", board->id); + AFB_INFO("method 'move' called for boardid %d", board->id); /* retrieves the arguments of the move */ index = afb_req_value(req, "index"); @@ -370,27 +386,27 @@ static void move(struct afb_req req) /* checks validity of arguments */ if (i < 0 || i > 8) { - WARNING(afbitf, "can't move to %s: %s", index?:"?", index?"wrong value":"not set"); + AFB_WARNING("can't move to %s: %s", index?:"?", index?"wrong value":"not set"); afb_req_fail(req, "error", "bad request"); return; } /* checks validity of the state */ if (winner(board->board) != 0) { - WARNING(afbitf, "can't move to %s: game is terminated", index); + AFB_WARNING("can't move to %s: game is terminated", index); afb_req_fail(req, "error", "game terminated"); return; } /* checks validity of the move */ - if (board->board[i] != ' ') { - WARNING(afbitf, "can't move to %s: room occupied", index); + if (board->board[i] != SPACE) { + AFB_WARNING("can't move to %s: room occupied", index); afb_req_fail(req, "error", "occupied"); return; } /* applies the move */ - INFO(afbitf, "method 'move' for boardid %d, index=%s", board->id, index); + AFB_INFO("method 'move' for boardid %d, index=%s", board->id, index); add_move(board, i); /* replies */ @@ -403,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; @@ -411,7 +427,7 @@ static void level(struct afb_req req) /* retrieves the context for the session */ board = board_of_req(req); - INFO(afbitf, "method 'level' called for boardid %d", board->id); + AFB_INFO("method 'level' called for boardid %d", board->id); /* retrieves the arguments */ level = afb_req_value(req, "level"); @@ -419,13 +435,13 @@ static void level(struct afb_req req) /* check validity of arguments */ if (l < 1 || l > 10) { - WARNING(afbitf, "can't set level to %s: %s", level?:"?", level?"wrong value":"not set"); + AFB_WARNING("can't set level to %s: %s", level?:"?", level?"wrong value":"not set"); afb_req_fail(req, "error", "bad request"); return; } /* set the level */ - INFO(afbitf, "method 'level' for boardid %d, level=%d", board->id, l); + AFB_INFO("method 'level' for boardid %d, level=%d", board->id, l); board->level = l; /* replies */ @@ -438,14 +454,14 @@ 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; /* retrieves the context for the session */ board = board_of_req(req); - INFO(afbitf, "method 'join' called for boardid %d", board->id); + AFB_INFO("method 'join' called for boardid %d", board->id); /* retrieves the arguments */ id = afb_req_value(req, "boardid"); @@ -455,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 */ @@ -470,19 +486,23 @@ 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; bad_request: - WARNING(afbitf, "can't join boardid %s: %s", id ? : "?", !id ? "no boardid" : atoi(id) ? "not found" : "bad boardid"); + AFB_WARNING("can't join boardid %s: %s", id ? : "?", !id ? "no boardid" : atoi(id) ? "not found" : "bad boardid"); afb_req_fail(req, "error", "bad request"); return; } @@ -490,25 +510,25 @@ 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; /* retrieves the context for the session */ board = board_of_req(req); - INFO(afbitf, "method 'undo' called for boardid %d", board->id); + AFB_INFO("method 'undo' called for boardid %d", board->id); /* checks the state */ if (board->moves == 0) { - WARNING(afbitf, "can't undo"); + AFB_WARNING("can't undo"); afb_req_fail(req, "error", "bad request"); return; } /* undo the last move */ i = board->history[--board->moves]; - board->board[i] = ' '; + board->board[i] = SPACE; /* replies */ afb_req_success(req, NULL, NULL); @@ -520,18 +540,18 @@ 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; /* retrieves the context for the session */ board = board_of_req(req); - INFO(afbitf, "method 'play' called for boardid %d", board->id); + AFB_INFO("method 'play' called for boardid %d", board->id); /* checks validity of the state */ if (winner(board->board) != 0 || board->moves == 9) { - WARNING(afbitf, "can't play: game terminated (%s)", winner(board->board) ? "has winner" : "no room left"); + AFB_WARNING("can't play: game terminated (%s)", winner(board->board) ? "has winner" : "no room left"); afb_req_fail(req, "error", "game terminated"); return; } @@ -547,59 +567,28 @@ 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); - INFO(afbitf, "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_desc_v1 binding_verbs[] = { - /* VERB'S NAME SESSION MANAGEMENT FUNCTION TO CALL SHORT DESCRIPTION */ - { .name= "new", .session= AFB_SESSION_NONE, .callback= new, .info= "Starts a new game" }, - { .name= "play", .session= AFB_SESSION_NONE, .callback= play, .info= "Asks the server to play" }, - { .name= "move", .session= AFB_SESSION_NONE, .callback= move, .info= "Tells the client move" }, - { .name= "board", .session= AFB_SESSION_NONE, .callback= board, .info= "Get the current board" }, - { .name= "level", .session= AFB_SESSION_NONE, .callback= level, .info= "Set the server level" }, - { .name= "join", .session= AFB_SESSION_CHECK,.callback= join, .info= "Join a board" }, - { .name= "undo", .session= AFB_SESSION_NONE, .callback= undo, .info= "Undo the last move" }, - { .name= "wait", .session= AFB_SESSION_NONE, .callback= wait, .info= "Wait for a change" }, - { .name= NULL } /* marker for end of the array */ +static const afb_verb_t verbs[] = { + { .verb="new", .callback=new }, + { .verb="play", .callback=play }, + { .verb="move", .callback=move }, + { .verb="board", .callback=board }, + { .verb="level", .callback=level }, + { .verb="join", .callback=join }, + { .verb="undo", .callback=undo }, + { .verb=NULL } }; /* * description of the binding for afb-daemon */ -static const struct afb_binding binding_description = -{ - /* description conforms to VERSION 1 */ - .type= AFB_BINDING_VERSION_1, - .v1= { /* fills the v1 field of the union when AFB_BINDING_VERSION_1 */ - .prefix= "tictactoe", /* the API name (or binding name or prefix) */ - .info= "Sample tac-tac-toe game", /* short description of of the binding */ - .verbs = binding_verbs /* the array describing the verbs of the API */ - } +const afb_binding_t afbBindingV3 = { + .api = "tictactoe", + .specification = NULL, + .verbs = verbs, + .noconcurrency = 1 }; -/* - * activation function for registering the binding called by afb-daemon - */ -const struct afb_binding *afbBindingV1Register(const struct afb_binding_interface *itf) -{ - afbitf = itf; // records the interface for accessing afb-daemon - return &binding_description; // returns the description of the binding -}