#include "web_svg.h" #include "web_ui.h" #include "checkouts.h" #include "comp.h" #include #include #include #include #include #include #include "web_match.h" int delay_ms = 1000; enum prompt_mode { PM_DARTBOARD, PM_VISIT, PM_NUM_DARTS, PM_END_MATCH, PM_SELECT_MODE }; enum prompt_mode pm; void set_prompt_mode(enum prompt_mode mode) { pm = mode; if (pm != PM_DARTBOARD) EM_ASM(setPromptActive()); else EM_ASM(setPromptInactive()); EM_ASM({setKeypad($0)}, pm == PM_DARTBOARD ? "dartboard" : pm == PM_SELECT_MODE ? "select_mode" : "default"); } void set_active_player(int pn) { state->active_player = pn; state->active_leg = state->legs[pn-1]; EM_ASM({setPlayerActive($0)}, pn); if (state->mode == M_PVC && pn == 2) { EM_ASM({promptMsgL($0)}, "Bot is throwing…"); set_prompt_mode(PM_DARTBOARD); } else { EM_ASM({promptMsgL($0)}, "Enter points:"); EM_ASM({setKeyLabel($0, $1)}, "submit", "OK"); EM_ASM({setKeyLabel($0, $1)}, "rem", "REMAINING"); set_prompt_mode(PM_VISIT); } EM_ASM({promptMsgR($0)}, ""); } void toggle_active_player() { set_active_player(3 - state->active_player); } EMSCRIPTEN_KEEPALIVE void update_user_rem_from_pts(int pts) { update_player_rem(state->active_player, state->active_leg->rem - pts); } void handle_next(); EMSCRIPTEN_KEEPALIVE void end_boat_visit(int rem, double avg) { EM_ASM(svgClearPoints()); update_player_rem(2, rem); EM_ASM({updatePlayerAvg($0, $1)}, 2, avg); EM_ASM({setPromptInput($0)}, ""); EM_ASM({promptMsgR($0)}, ""); draw_visits(); handle_next(); } EMSCRIPTEN_KEEPALIVE void boat_visit() { struct leg *l = state->legs[1]; if (state->boat_undone) { --state->boat_undone; l->rem = l->visits[l->n_visits++].rem; } else { comp_visit(l); } struct visit *v = l->visits + l->n_visits - 1; double avg = v->rem > 0 ? (double)(l->start - l->rem) / l->n_visits : (double)l->start / (((l->n_visits - 1) * 3) + v->n_darts) * 3; if (!delay_ms) { end_boat_visit(l->rem, avg); return; } int pts = 0; char str[15] = {0}; size_t len_str = 0; for (int i = 0; i < v->n_darts; ++i) { pts += segment_points(v->darts[i]); char *seg_name = segment_name(v->darts[i]); len_str += sprintf(str + len_str, i ? "-%s" : "%s", seg_name); free(seg_name); struct ccoords c = v->ccoords[i]; char *tmp = malloc(len_str + 1); // free in draw_boat_throwing memcpy(tmp, str, len_str + 1); EM_ASM({scheduleCCall($0, $1, $2, $3, $4, $5)}, "draw_boat_throwing", delay_ms * (i+1), pts, tmp, c.x, c.y); } EM_ASM({scheduleCCall($0, $1, $2, $3)}, "end_boat_visit", delay_ms * (v->n_darts + 1), l->rem, avg); } void prompt_num_darts() { set_prompt_mode(PM_NUM_DARTS); EM_ASM({promptMsgL($0)}, "Darts needed?"); EM_ASM({promptMsgR($0)}, ""); EM_ASM({setKeyLabel($0, $1)}, "submit", "OK"); EM_ASM({setKeyLabel($0, $1)}, "rem", "REMAINING"); } void prompt_end_match() { EM_ASM(setPlayerActive()); // sets all inactive set_prompt_mode(PM_END_MATCH); EM_ASM({promptMsgL($0)}, state->mode == M_PVC && state->legs[1]->rem <= 0 ? "Bot wins. :(" : "You win! :)"); EM_ASM({promptMsgR($0)}, ""); EM_ASM({setKeyLabel($0, $1)}, "submit", "END MATCH"); EM_ASM({setKeyLabel($0, $1)}, "rem", "REMATCH"); } void prompt_select_mode() { for (int pn = 1; pn < 3; ++pn) { EM_ASM({hidePlayerInfo($0)}, pn); clear_player_info(pn); } EM_ASM(clearVisits()); set_prompt_mode(PM_SELECT_MODE); EM_ASM({promptMsgL($0)}, "Select match mode:"); EM_ASM({promptMsgR($0)}, ""); } void handle_next() { if (!state) { prompt_select_mode(); } else if (is_match_over()) { if (state->num_darts || (state->mode == M_PVC && state->legs[1]->rem <= 0)) prompt_end_match(); else prompt_num_darts(); } else { if (state->mode == M_P) { set_active_player(1); } else { if (state->active_player) toggle_active_player(); else set_active_player(1); if (state->mode == M_PVC && state->active_player == 2) boat_visit(); } } } EMSCRIPTEN_KEEPALIVE void user_visit(int points) { if (!is_points_valid(points, state->active_leg->rem)) { EM_ASM(oi()); return; } struct leg *l = state->active_leg; if (l->n_visits == l->size_visits) leg_grow_visits(l); struct visit *v = l->visits + l->n_visits++; v->points = points; l->rem -= points; v->rem = l->rem; update_player_rem(state->active_player, l->rem); if (v->rem > 0) update_player_avg(state->active_player, 0); draw_visits(); handle_next(); } EMSCRIPTEN_KEEPALIVE void user_visit_to_rem(int rem) { user_visit(state->legs[0]->rem - rem); } EMSCRIPTEN_KEEPALIVE void user_undo() { if (!state->legs[0]->n_visits) { EM_ASM(oi()); return; } if (state->num_darts) { state->num_darts = 0; EM_ASM({updatePlayerAvg($0, $1)}, state->active_player, ((double)(state->active_leg->start - state->active_leg->visits[ state->active_leg->n_visits-2].rem) / (state->active_leg->n_visits-1))); handle_next(); return; } if (state->mode == M_PVC) { set_active_player(1); } else if (state->mode == M_PVP) { if (is_match_over()) set_active_player(state->active_player); else toggle_active_player(); } struct leg *l = state->active_leg; struct visit *v = l->visits + --l->n_visits; l->rem += v->points; memcpy(v, 0, sizeof(*v)); if (state->mode == M_PVC && state->legs[1]->n_visits > l->n_visits) { state->legs[1]->rem += state->legs[1]->visits[--state->legs[1]->n_visits].points; ++state->boat_undone; } update_player_avg(state->active_player, 0); update_player_rem(state->active_player, l->rem); if (state->mode == M_PVC) { update_player_avg(2, 0); update_player_rem(2, state->legs[1]->rem); } draw_visits(); if (state->mode != M_P) // FIXME avoid double toggle? toggle_active_player(); handle_next(); } EMSCRIPTEN_KEEPALIVE void user_num_darts(int n) { if (n < 1 || n > 3) { EM_ASM(oi()); return; } state->num_darts = n; update_player_avg(state->active_player, n); handle_next(); } EMSCRIPTEN_KEEPALIVE void start_match(int mode) { if (mode < M_FIRST || mode > M_LAST) { EM_ASM(oi()); return; } if (state) free_state(); // rematch gets us here state = calloc(1, sizeof(*state)); state->mode = mode; state->legs[0] = leg_init(501, mode == M_PVP ? "Player 1" : "User"); state->legs[1] = leg_init(501, mode == M_PVC ? "Bot" : "Player 2"); EM_ASM({showPlayerInfo($0)}, 1); EM_ASM({updatePlayerName($0, $1)}, 1, state->legs[0]->name); update_player_rem(1, state->legs[0]->rem); EM_ASM({updatePlayerAvg($0, $1)}, 1, 0); if (mode != M_P) { EM_ASM({showPlayerInfo($0)}, 2); EM_ASM({updatePlayerName($0, $1)}, 2, state->legs[1]->name); update_player_rem(2, state->legs[1]->rem); EM_ASM({updatePlayerAvg($0, $1)}, 2, 0); } draw_visits(); handle_next(); } EMSCRIPTEN_KEEPALIVE void end_match() { if (state) free_state(); handle_next(); } EMSCRIPTEN_KEEPALIVE void set_delay(int delay) { delay_ms = delay; } EMSCRIPTEN_KEEPALIVE void set_stdev(float stdev) { if (!isnan(stdev)) horizontal_stdev = vertical_stdev = stdev; } void init_boat() { srand(time(NULL)); init_board(); svg_draw_board(); EM_ASM(readOpts()); EM_ASM({updateDelay($0)}, delay_ms); EM_ASM({updateStdev($0)}, horizontal_stdev); } char *prompt_get() { return (char *)EM_ASM_INT({return promptGet()}); } void prompt_handle_pre(char *command) { if (pm == PM_DARTBOARD) return; EM_ASM(clearOi()); if ((pm == PM_VISIT || pm == PM_NUM_DARTS || pm == PM_END_MATCH) && strcmp(command, "undo")) deactivate_key("undo"); if (pm == PM_END_MATCH && strcmp(command, "rem")) deactivate_key("rem"); if (pm == PM_END_MATCH && strcmp(command, "submit")) deactivate_key("submit"); } void prompt_handle_on_change() { if (pm != PM_VISIT) return; char *str = prompt_get(); update_user_rem_from_pts(atoi(str)); free(str); } void prompt_handle_append(char *data) { if (pm != PM_SELECT_MODE && pm != PM_VISIT && pm != PM_NUM_DARTS) return; char *str = prompt_get(); size_t len_str = strlen(str); size_t len_data = strlen(data); if (len_str < 3) { str = realloc(str, len_str + len_data + 1); memcpy(str + len_str, data, len_data + 1); EM_ASM({setPromptInput($0)}, str); prompt_handle_on_change(); } free(str); } void prompt_handle_backspace() { if (pm == PM_DARTBOARD) return; char *str = prompt_get(); size_t len_str = strlen(str); if (len_str > 0) { str[len_str-1] = 0; EM_ASM({setPromptInput($0)}, str); prompt_handle_on_change(); } free(str); } void prompt_handle_clear() { if (pm == PM_DARTBOARD) return; EM_ASM({setPromptInput($0)}, ""); prompt_handle_on_change(); } void prompt_handle_submit() { if (pm == PM_END_MATCH) { if (is_key_active("submit")) end_match(); toggle_key("submit"); return; } if (pm != PM_VISIT && pm != PM_NUM_DARTS && pm != PM_SELECT_MODE) return; char *str = prompt_get(); prompt_handle_clear(); if (*str) { if (pm == PM_VISIT) user_visit(atoi(str)); else if (pm == PM_NUM_DARTS) user_num_darts(atoi(str)); else if (pm == PM_SELECT_MODE) start_match(atoi(str)); } free(str); } void prompt_handle_rem() { if (pm == PM_END_MATCH) { if (is_key_active("rem")) start_match(state->mode); toggle_key("rem"); return; } if (pm != PM_VISIT) return; char *str = prompt_get(); prompt_handle_clear(); if (*str) user_visit_to_rem(atoi(str)); free(str); } void prompt_handle_undo() { if (pm != PM_VISIT && pm != PM_NUM_DARTS && pm != PM_END_MATCH) return; prompt_handle_clear(); if (is_key_active("undo")) user_undo(); toggle_key("undo"); } EMSCRIPTEN_KEEPALIVE void prompt_handle(char *command, char *data) { prompt_handle_pre(command); if (!strcmp(command, "append")) prompt_handle_append(data); else if (!strcmp(command, "backspace")) prompt_handle_backspace(); else if (!strcmp(command, "clear")) prompt_handle_clear(); else if (!strcmp(command, "submit")) prompt_handle_submit(); else if (!strcmp(command, "rem")) prompt_handle_rem(); else if (!strcmp(command, "undo")) prompt_handle_undo(); } EMSCRIPTEN_KEEPALIVE void init() { init_boat(); set_prompt_mode(PM_VISIT); handle_next(); } int main() { EM_ASM(boatAfloat()); }