diff options
Diffstat (limited to 'plugins/samples/tic-tac-toe.c')
-rw-r--r-- | plugins/samples/tic-tac-toe.c | 605 |
1 files changed, 0 insertions, 605 deletions
diff --git a/plugins/samples/tic-tac-toe.c b/plugins/samples/tic-tac-toe.c deleted file mode 100644 index 31682d94..00000000 --- a/plugins/samples/tic-tac-toe.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * Copyright (C) 2016 "IoT.bzh" - * Author José Bollo <jose.bollo@iot.bzh> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE -#include <stdio.h> -#include <string.h> -#include <json-c/json.h> - -#include <afb/afb-plugin.h> - -/* - * the interface to afb-daemon - */ -const struct AFB_interface *afbitf; - -/* - * definition of waiters - */ -struct waiter -{ - struct waiter *next; - struct afb_req req; -}; - -/* - * definition of a board - */ -struct board -{ - struct board *next; - int use_count; - int moves; - int history[9]; - int id; - int level; - char board[9]; - struct waiter *waiters; -}; - -/* - * list of boards - */ -static struct board *all_boards; - -/* - * Searchs a board having the 'id'. - * Returns it if found or NULL otherwise. - */ -static struct board *search_board(int id) -{ - struct board *board = all_boards; - while (board != NULL && board->id != id) - board = board->next; - return board; -} - -/* - * Creates a new board and returns it. - */ -static struct board *get_new_board() -{ - /* allocation */ - struct board *board = calloc(1, sizeof *board); - - /* initialisation */ - memset(board->board, ' ', sizeof board->board); - board->use_count = 1; - board->level = 1; - board->moves = 0; - do { - board->id = (rand() >> 2) % 1000; - } while(board->id == 0 || search_board(board->id) != NULL); - - /* link */ - board->next = all_boards; - all_boards = board; - return board; -} - -/* - * Release a board - */ -static void release_board(struct board *board) -{ - /* decrease the reference count ... */ - if (--board->use_count == 0) { - /* ... no more use */ - /* unlink from the list of boards */ - struct board **prv = &all_boards; - while (*prv != NULL && *prv != board) - prv = &(*prv)->next; - if (*prv != NULL) - *prv = board->next; - /* release the used memory */ - free(board); - } -} - -/* - * Checks who wins - * Returns zero if there is no winner - * Returns the char of the winner if a player won - */ -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; - } - - /* check lines */ - for (i = 0 ; i <= 6 ; i += 3) { - c = b[i]; - if (c != ' ' && b[i+1] == c && b[i+2] == 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; - } - - return 0; -} - -/* get the color (X or 0) of the move of index 'move' */ -static char color(int move) -{ - return (move & 1) == 0 ? 'X' : '0'; -} - -/* adds the move to the board */ -static void add_move(struct board *board, int index) -{ - int imove = board->moves++; - board->history[imove] = index; - board->board[index] = color(imove); -} - -/* get a random possible move index from the board described by 'b' */ -static int get_random_move(char b[9]) -{ - int index = rand() % 9; - while (b[index] != ' ') - index = (index + 1) % 9; - return index; -} - -/* - * Scores the position described by 'b' - * for the player of color 'c' using an analysis of 'depth'. - * Returns 1 if player 'c' will win. - * Returns -1 if opponent of player 'c' will win. - * returns 0 otherwise. - */ -static int score_position(char b[9], char c, int depth) -{ - int i, t, r; - - /* check if winner */ - if (winner(b) == c) - return 1; - - /* when depth of analysis is reached return unknown case */ - if (--depth == 0) - return 0; - - /* switch to the opponent */ - c = (char)('O' + 'X' - c); - - /* inspect opponent moves */ - r = 1; - for (i = 0 ; i < 9 ; i++) { - if (b[i] == ' ') { - 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 */ - } - } - return r; -} - -/* get one move: return the computed index of the move */ -static int get_move(struct board *board) -{ - int index, depth, t, f; - char c; - char b[9]; - - /* compute the depth */ - depth = board->level - 1; - if (board->moves + depth > 9) - depth = 9 - board->moves; - - /* case of null depth */ - if (depth == 0) - return get_random_move(board->board); - - /* depth and more */ - memcpy(b, board->board, 9); - c = color(board->moves); - f = 0; - for (index = 0 ; index < 9 ; index++) { - if (board->board[index] == ' ') { - 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 - f = 1; - } - } - return get_random_move(f ? b : board->board); -} - -/* - * get the board description - */ -static struct json_object *describe(struct board *board) -{ - int i; - char w; - struct json_object *resu, *arr; - - resu = json_object_new_object(); - - json_object_object_add(resu, "boardid", json_object_new_int(board->id)); - json_object_object_add(resu, "level", json_object_new_int(board->level)); - - arr = json_object_new_array(); - json_object_object_add(resu, "board", arr); - for (i = 0 ; i < 9 ; i++) - json_object_array_add(arr, - json_object_new_string_len(&board->board[i], 1)); - - arr = json_object_new_array(); - json_object_object_add(resu, "history", arr); - for (i = 0 ; i < board->moves ; i++) - json_object_array_add(arr, json_object_new_int(board->history[i])); - - w = winner(board->board); - if (w) - json_object_object_add(resu, "winner", json_object_new_string_len(&w, 1)); - else if (board->moves == 9) - json_object_object_add(resu, "winner", json_object_new_string("none")); - - return resu; -} - -/* - * signals a change of the board - */ -static void changed(struct board *board, const char *reason) -{ - struct waiter *waiter, *next; - struct json_object *description; - - /* get the description */ - description = describe(board); - - 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; - } - - afb_daemon_broadcast_event(afbitf->daemon, reason, description); -} - -/* - * retrieves the board of the request - */ -static inline struct board *board_of_req(struct afb_req req) -{ - return afb_req_context(req, (void*)get_new_board, (void*)release_board); -} - -/* - * start a new game - */ -static void new(struct afb_req 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); - - /* reset the game */ - memset(board->board, ' ', sizeof board->board); - board->moves = 0; - - /* replies */ - afb_req_success(req, NULL, NULL); - - /* signal change */ - changed(board, "new"); -} - -/* - * get the board - */ -static void board(struct afb_req 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); - - /* describe the board */ - description = describe(board); - - /* send the board's description */ - afb_req_success(req, description, NULL); -} - -/* - * move a piece - */ -static void move(struct afb_req req) -{ - struct board *board; - int i; - const char *index; - - /* retrieves the context for the session */ - board = board_of_req(req); - INFO(afbitf, "method 'move' called for boardid %d", board->id); - - /* retrieves the arguments of the move */ - index = afb_req_value(req, "index"); - i = index == NULL ? -1 : atoi(index); - - /* checks validity of arguments */ - if (i < 0 || i > 8) { - WARNING(afbitf, "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_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); - afb_req_fail(req, "error", "occupied"); - return; - } - - /* applies the move */ - INFO(afbitf, "method 'move' for boardid %d, index=%s", board->id, index); - add_move(board, i); - - /* replies */ - afb_req_success(req, NULL, NULL); - - /* signals change */ - changed(board, "move"); -} - -/* - * set the level - */ -static void level(struct afb_req req) -{ - struct board *board; - int l; - const char *level; - - /* retrieves the context for the session */ - board = board_of_req(req); - INFO(afbitf, "method 'level' called for boardid %d", board->id); - - /* retrieves the arguments */ - level = afb_req_value(req, "level"); - l = level == NULL ? -1 : atoi(level); - - /* 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_req_fail(req, "error", "bad request"); - return; - } - - /* set the level */ - INFO(afbitf, "method 'level' for boardid %d, level=%d", board->id, l); - board->level = l; - - /* replies */ - afb_req_success(req, NULL, NULL); - - /* signals change */ - changed(board, "level"); -} - -/* - * Join a board - */ -static void join(struct afb_req 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); - - /* retrieves the arguments */ - id = afb_req_value(req, "boardid"); - if (id == NULL) - goto bad_request; - - /* none is a special id for joining a new session */ - if (strcmp(id, "none") == 0) { - new_board = get_new_board(); - goto success; - } - - /* searchs the board to join */ - new_board = search_board(atoi(id)); - if (new_board == NULL) - goto bad_request; - - /* - * joining its board is stupid but possible - * however when called with the same stored pointer - * afb_req_context_set will not call the release - * function 'release_board'. So the use_count MUST not - * be incremented. - */ - if (new_board != board) - new_board->use_count++; - -success: - /* set the new board (and leaves the previous one) */ - afb_req_context_set(req, new_board, (void*)release_board); - - /* 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_req_fail(req, "error", "bad request"); - return; -} - -/* - * Undo the last move - */ -static void undo(struct afb_req 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); - - /* checks the state */ - if (board->moves == 0) { - WARNING(afbitf, "can't undo"); - afb_req_fail(req, "error", "bad request"); - return; - } - - /* undo the last move */ - i = board->history[--board->moves]; - board->board[i] = ' '; - - /* replies */ - afb_req_success(req, NULL, NULL); - - /* signals change */ - changed(board, "undo"); -} - -/* - * computer plays - */ -static void play(struct afb_req 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); - - /* 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_req_fail(req, "error", "game terminated"); - return; - } - - /* gets the move and plays it */ - index = get_move(board); - add_move(board, index); - - /* replies */ - afb_req_success(req, describe(board), NULL); - - /* signals change */ - 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 plugin_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 */ -}; - -/* - * description of the plugin for afb-daemon - */ -static const struct AFB_plugin plugin_description = -{ - /* description conforms to VERSION 1 */ - .type= AFB_PLUGIN_VERSION_1, - .v1= { /* fills the v1 field of the union when AFB_PLUGIN_VERSION_1 */ - .prefix= "tictactoe", /* the API name (or plugin name or prefix) */ - .info= "Sample tac-tac-toe game", /* short description of of the plugin */ - .verbs = plugin_verbs /* the array describing the verbs of the API */ - } -}; - -/* - * activation function for registering the plugin called by afb-daemon - */ -const struct AFB_plugin *pluginAfbV1Register(const struct AFB_interface *itf) -{ - afbitf = itf; // records the interface for accessing afb-daemon - return &plugin_description; // returns the description of the plugin -} - |