/* * Copyright (C) 2016, 2017 "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-binding.h> /* * the interface to afb-daemon */ const struct afb_binding_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 '<style>.highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span># Set wam executable file path HOOK_SEGV=NO # Set wam name for user-agent WAM_NAME="WebAppManager" WAM_IS_CEF=false # Only allow UTF8 encoding for luna-service messages. LS_ENABLE_UTF8=1 # suspending javascript execution delay for page visibility WAM_SUSPEND_DELAY_IN_MS=250 WAM_DATA_PATH="/home/agl-driver/wamdata" # setup 50 Mb maximum for ApplicationCache WAM_APPCACHE_MAXSIZE=52428800 # setup 10 Mb maximum for ApplicationCache per domain WAM_APPCACHE_DOMAINLIMIT=10485760 # setup 50 Mb maximum for DiskCache WAM_DISKCACHE_MAXSIZE=52428800 # setup 256 Kb maximum for resource buffer allocation WAM_RESOURCE_BUFFER_MAX_ALLOC_SIZE=262144 # setup 1 Mb for resource buffer WAM_RESOURCE_BUFFER_SIZE=1048576 # setup 200 seconds for watchdog timeout of render process WATCHDOG_RENDER_TIMEOUT=200 # setup nubmer of raster threads to 1 BLINK_NUM_RASTER_THREADS=2 # use default tile width if not sed by recipe BLINK_NUM_RASTER_THREADS=1 # setup 6 Mb maximum for the program GPU cache GPU_PROGRAM_CACHE_SIZE=6144 # Set location of all NPAPI plugins NPAPI_PLUGIN_PATH=${HBBTV_PLUGIN_PATH}":"${NETCAST_PLUGIN_PATH}":"${PRIVILEGED_PLUGIN_PATH} # setup 8 Mb minimum codecache capacity JSC_minGlobalCodeCacheCapacity=8388608 # Enable more explicit logging of timing with regards to rendering # export WAM2_ENABLE_DEBUG_RENDER_TIMING=1 # enable Web Inspector and Tellurium if in developer mode TELLURIUM_NUB_PATH=/usr/palm/tellurium/telluriumnub.js ENABLE_INSPECTOR=1 # Enable cursor by default ENABLE_CURSOR_BY_DEFAULT=1 # Enable launch optimization ENABLE_LAUNCH_OPTIMIZATION=1 # Set the duration(seconds) passed from last network activity (e.g. FMP Detector) # If set to a positive value, adjust a c