#include "checkouts.h" #include #include #include #include #include #include #include #define HORIZONTAL_STDEV 24 #define VERTICAL_STDEV 24 // board spec from WDF rules #define WIRE_WIDTH 1.56 #define INNER_DIAMETER_BULL 12.7 #define INNER_DIAMETER_25 31.8 #define DOUBLE_OUTER_EDGE 170.0 #define TREBLE_OUTER_EDGE 107.4 #define DOUBLE_INSIDE_WIDTH 8.0 #define TREBLE_INSIDE_WIDTH 8.0 #define SEGMENT_MAX_LEN 5 // distance from centre to apex of outer wire // must be ordered outwards from centre #define FOREACH_RING(M) \ M(BULL, INNER_DIAMETER_BULL/2 + WIRE_WIDTH/2) \ M(25, INNER_DIAMETER_25/2 + WIRE_WIDTH/2) \ M(SMALL, TREBLE_OUTER_EDGE - WIRE_WIDTH - TREBLE_INSIDE_WIDTH - \ WIRE_WIDTH/2) \ M(TREBLE, TREBLE_OUTER_EDGE - WIRE_WIDTH/2) \ M(BIG, DOUBLE_OUTER_EDGE - WIRE_WIDTH - DOUBLE_INSIDE_WIDTH - \ WIRE_WIDTH/2) \ M(DOUBLE, DOUBLE_OUTER_EDGE - WIRE_WIDTH/2) #define GEN_RING_ENUM(X, D) R_ ## X, #define GEN_RING_STRING(X, D) #X, #define GEN_RING_OUTER_DIST(X, D) D, enum ring { FOREACH_RING(GEN_RING_ENUM) R_OUT }; char *RING_NAMES[] = { FOREACH_RING(GEN_RING_STRING) "OUT" }; double OUTER_DISTS[] = { FOREACH_RING(GEN_RING_OUTER_DIST) }; double CENTRE_DISTS[sizeof(OUTER_DISTS)/sizeof(*OUTER_DISTS)]; void init_centre_dists() { CENTRE_DISTS[0] = 0; for (int i = R_25; i < R_OUT; ++i) CENTRE_DISTS[i] = (OUTER_DISTS[i] - ((OUTER_DISTS[i] - OUTER_DISTS[i-1]) / 2)); } int SECTORS[] = { 20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5 }; #define NUM_SECTORS (sizeof(SECTORS) / sizeof(*SECTORS)) #define SECTOR_WIDTH (360.0 / NUM_SECTORS) int SECTOR_INDS[NUM_SECTORS]; double SECTOR_ANGLES[NUM_SECTORS]; void init_sectors() { for (size_t i = 0; i < NUM_SECTORS; ++i) { SECTOR_INDS[SECTORS[i]-1] = i; double angle = 90 - (i * SECTOR_WIDTH); if (angle < 0) angle += 360; SECTOR_ANGLES[i] = angle; } } #define FOREACH_COLOUR(M) \ M(140, 82, 0) \ M(100, 154, 0) \ M(60, 226, 0) \ M(40, 214, 0) \ M(20, 202, 0) \ M(0, 196, 0) \ M(DARTS, 235, 0) \ M(VISIT, 244, 0) \ M(STATUS, 7, 4) #define GEN_COLOUR_ENUM(X, FG, BG) C_ ## X, #define GEN_COLOUR_INIT_PAIR(X, FG, BG) init_pair(C_ ## X, FG, BG); enum colour { C_DEFAULT, // index 0 is reserved for default colours FOREACH_COLOUR(GEN_COLOUR_ENUM) }; void init_colours() { FOREACH_COLOUR(GEN_COLOUR_INIT_PAIR) } WINDOW *w, *titlew, *statw, *promptw; struct ccoords { double x, y; }; struct pcoords { double a, r; }; struct segment { enum ring ring; int sector; }; int get_sector(double angle) { double shifted = angle - 90 - (SECTOR_WIDTH/2); return SECTORS[NUM_SECTORS - 1 - (int)((shifted - (360 * floor(shifted/360))) / SECTOR_WIDTH)]; } enum ring get_ring(double radius) { for (int i = R_BULL; i < R_OUT; ++i) if (radius < OUTER_DISTS[i]) return i; return R_OUT; } double drand() { return (double)rand() / RAND_MAX; } double gauss(double mean, double stdev) { static bool have_next; static double next; double curr; if (have_next) { curr = next; } else { double theta = 2 * M_PI * drand(); double r = sqrt(-2 * log(1 - drand())); curr = r * cos(theta); next = r * sin(theta); } have_next = !have_next; return mean + (curr * stdev); } struct ccoords get_offset() { return (struct ccoords){ .x = gauss(0, HORIZONTAL_STDEV), .y = gauss(0, VERTICAL_STDEV) }; } struct ccoords pol_to_cart(struct pcoords c) { double t = c.a * (M_PI / 180); double x = c.r * cos(t); double y = c.r * sin(t); return (struct ccoords){ .x = x, .y = y }; } struct pcoords cart_to_pol(struct ccoords c) { double a = atan2(c.y, c.x) * (180 / M_PI); double r = sqrt(pow(c.x, 2) + pow(c.y, 2)); return (struct pcoords){ .a = a, .r = r }; } struct pcoords throw_dart(struct pcoords target) { struct ccoords cc = pol_to_cart(target); struct ccoords offset = get_offset(); return cart_to_pol((struct ccoords){ .x = cc.x + offset.x, .y = cc.y + offset.y }); } struct segment get_segment(struct pcoords c) { return (struct segment){ .sector = get_sector(c.a), .ring = get_ring(c.r) }; } char *segment_name(struct segment seg) { char *str = malloc(SEGMENT_MAX_LEN); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-truncation" if (seg.ring == R_BULL || seg.ring == R_25 || seg.ring == R_OUT) strncpy(str, RING_NAMES[seg.ring], SEGMENT_MAX_LEN); else snprintf(str, SEGMENT_MAX_LEN, (seg.ring == R_TREBLE ? "T%d" : (seg.ring == R_DOUBLE ? "D%d" : "%d")), seg.sector); #pragma GCC diagnostic pop return str; } int segment_points(struct segment seg) { if (seg.ring == R_BULL) return 50; else if (seg.ring == R_25) return 25; else if (seg.ring == R_TREBLE) return 3 * seg.sector; else if (seg.ring == R_DOUBLE) return 2 * seg.sector; else if (seg.ring == R_SMALL || seg.ring == R_BIG) return seg.sector; return 0; } bool segment_is_double(struct segment seg) { if (seg.ring == R_DOUBLE || seg.ring == R_BULL) return true; return false; } struct pcoords segment_centre(struct segment seg) { return (struct pcoords){ .a = SECTOR_ANGLES[SECTOR_INDS[seg.sector-1]], .r = CENTRE_DISTS[seg.ring] }; } struct segment segment_from_name(char *name) { int s = 20; enum ring r; if (!strcmp(name, "BULL")) { r = R_BULL; } else if (!strcmp(name, "25")) { r = R_25; } else if (name[0] == 'T') { r = R_TREBLE; s = atoi(name+1); } else if (name[0] == 'D') { r = R_DOUBLE; s = atoi(name+1); } else { r = R_BIG; s = atoi(name); } return (struct segment){ .sector = s, .ring = r }; } struct segment next_dart(int rem, int darts_in_hand) { char *c = NULL; if (rem <= 170) c = CHECKOUTS[darts_in_hand-1][rem-1]; if (!c) c = "T20"; return segment_from_name(c); } struct visit { int points; int rem; int n_darts; struct segment *darts; }; struct leg { char *name; int start; int rem; int n_visits; struct visit *visits; }; struct leg *leg_init(int points, char *name) { struct leg *l = calloc(1, sizeof(*l)); l->name = name; l->start = l->rem = points; l->visits = calloc(1000, sizeof(*(l->visits))); // FIXME return l; } void leg_free(struct leg *l) { for (int i = 0; i < l->n_visits; ++i) if (l->visits[i].darts) free(l->visits[i].darts); free(l->visits); free(l); } void comp_visit(struct leg *l) { struct visit *v = l->visits + l->n_visits++; v->darts = calloc(3, sizeof(*(v->darts))); for (int i = 0; i < 3; ++i) { struct segment ts = next_dart(l->rem - v->points, 3 - i); struct pcoords tc = segment_centre(ts); struct pcoords dc = throw_dart(tc); struct segment ds = get_segment(dc); v->darts[v->n_darts++] = ds; v->points += segment_points(ds); if (l->rem - v->points == 0 && segment_is_double(ds)) break; if (l->rem - v->points <= 1) { v->points = 0; break; } } l->rem -= v->points; v->rem = l->rem; } void test_match(int start_points) { struct leg *l = leg_init(start_points, NULL); while(l->rem > 0) comp_visit(l); printf("(%2d) %3d\n", 0, l->start); for(int i = 0; i < l->n_visits; ++i) { struct visit *v = l->visits + i; printf("(%2d) %3d %3d", i+1, v->points, v->rem); for (int j = 0; j < v->n_darts; ++j) { char *n = segment_name(v->darts[j]); printf(j == 0 ? " %4s" :" %4s", n); free(n); } printf("\n"); } leg_free(l); } void curses_status(char *status) { wmove(statw, 0, 0); wclrtoeol(statw); waddstr(statw, status); wrefresh(statw); } void curses_prompt(char *prompt) { wmove(promptw, 0, 0); wclrtoeol(promptw); waddstr(promptw, prompt); wrefresh(promptw); } void user_visit(struct leg *l) { struct visit *v = l->visits + l->n_visits++; char status[100]; int len = sprintf(status, " %s has %d remaining", l->name, l->rem); if (l->rem <= 170) { char *target = CHECKOUTS[2][l->rem-1]; if (target) { int trem = l->rem - segment_points(segment_from_name(target)); len += sprintf(status + len, " (%s", target); if (trem) { target = CHECKOUTS[1][trem-1]; len += sprintf(status + len, "-%s", target); trem = trem - segment_points(segment_from_name(target)); if (trem) { target = CHECKOUTS[0][trem-1]; len += sprintf(status + len, "-%s", target); } } len += sprintf(status + len, ")"); } } char prompt[] = " enter points> "; curses_status(status); curses_prompt(prompt); char buf[100] = { 0 }; int buflen = 0; int c; while((c = wgetch(promptw)) != 10) { if (c == 127) { if (buflen > 0) { buf[--buflen] = 0; wmove(promptw, 0, sizeof(prompt) - 1 + buflen); wclrtoeol(promptw); wrefresh(promptw); } } else if (c >= '0' && c <= '9') { buf[buflen++] = c; wechochar(promptw, c); } } curses_prompt(""); v->points = atoi(buf); l->rem -= v->points; v->rem = l->rem; } int points_colour(int points) { if (points >= 180) return COLOR_PAIR(C_140) | A_BOLD; else if (points >= 140) return COLOR_PAIR(C_140); else if (points >= 100) return COLOR_PAIR(C_100); else if (points >= 60) return COLOR_PAIR(C_60); else if (points >= 40) return COLOR_PAIR(C_40); else if (points >= 20) return COLOR_PAIR(C_20); else if (points > 0) return COLOR_PAIR(C_0); else return COLOR_PAIR(C_0) | A_BOLD; } void flushbuf(char *buf, int *buflen, int col) { if (!(*buflen)) return; if (col) wattron(w, col); waddstr(w, buf); if (col) wattroff(w, col); *buflen = 0; } void curses_draw(struct leg *l1, struct leg *l2) { werase(w); char buf[100]; int buflen = 0; int wlines = LINES - 3; int n_visits = l1->n_visits > l2->n_visits ? l1->n_visits : l2->n_visits; int start_visit = (wlines > n_visits) ? 0 : (n_visits - wlines); int offset = 0; if (start_visit == 0 && wlines > n_visits) { wmove(w, (offset++)-start_visit, 1); buflen = sprintf(buf, "(%2d)", 0); flushbuf(buf, &buflen, COLOR_PAIR(C_VISIT)); buflen = sprintf(buf, " %3d %3d", l1->start, l2->start); flushbuf(buf, &buflen, 0); } for (int i = start_visit; i < n_visits; ++i) { wmove(w, offset+i-start_visit, 1); buflen += sprintf(buf + buflen, "(%2d)", i+1); flushbuf(buf, &buflen, COLOR_PAIR(C_VISIT)); buflen += sprintf(buf + buflen, " "); flushbuf(buf, &buflen, 0); struct visit *v = l1->visits + i; flushbuf(buf, &buflen, 0); buflen += sprintf(buf + buflen, "%3d", v->points); flushbuf(buf, &buflen, points_colour(v->points)); buflen += sprintf(buf + buflen, " %3d", v->rem); if (i < l2->n_visits) { v = l2->visits + i; buflen += sprintf(buf + buflen, " %3d ", v->rem); flushbuf(buf, &buflen, 0); buflen += sprintf(buf + buflen, "%3d", v->points); flushbuf(buf, &buflen, points_colour(v->points)); for (int j = 0; j < v->n_darts; ++j) { char *n = segment_name(v->darts[j]); buflen += sprintf(buf + buflen, j == 0 ? " %4s" :" %4s", n); free(n); } flushbuf(buf, &buflen, COLOR_PAIR(C_DARTS)); } flushbuf(buf, &buflen, 0); } wrefresh(w); } void curses_match(int start_points, char *n1, void (*f1)(struct leg *), char *n2, void (*f2)(struct leg *)) { initscr(); refresh(); start_color(); init_colours(); curs_set(0); noecho(); w = newwin(LINES-3, COLS, 1, 0); titlew = newwin(1, COLS, 0, 0); statw = newwin(1, COLS, LINES-2, 0); promptw = newwin(1, COLS, LINES-1, 0); wbkgd(titlew, COLOR_PAIR(C_STATUS)); wbkgd(statw, COLOR_PAIR(C_STATUS)); waddstr(titlew, " dartbot"); wrefresh(titlew); struct leg *l1 = leg_init(start_points, n1); struct leg *l2 = leg_init(start_points, n2); curses_draw(l1, l2); while (l1->rem > 0 && l2->rem > 0) { (*f1)(l1); curses_draw(l1, l2); if (l1->rem > 0) (*f2)(l2); curses_draw(l1, l2); } char status[100]; sprintf(status, " %s wins", l1->rem <= 0 ? l1->name : l2->name); curses_status(status); leg_free(l1); leg_free(l2); wgetch(w); delwin(promptw); delwin(statw); delwin(titlew); delwin(w); endwin(); } void cvc_curses_match(int start_points) { curses_match(start_points, "Dartbot 1", comp_visit, "Dartbot 2", comp_visit); } void pvc_curses_match(int start_points) { curses_match(start_points, "David", user_visit, "Dartbot", comp_visit); } void pvp_curses_match(int start_points) { curses_match(start_points, "David", user_visit, "Davidn't", user_visit); } int main() { srand(time(NULL)); init_sectors(); init_centre_dists(); //cvc_curses_match(501); pvc_curses_match(501); //pvp_curses_match(501); test_match(501); return 0; }