pachi_py/pachi/chat.c (112 lines of code) (raw):

#include <assert.h> #include <math.h> #include <stdio.h> #include <sys/types.h> #include <stdint.h> #include "chat.h" #ifndef HAVE_NO_REGEX_SUPPORT #include <regex.h> #define DEBUG #include "debug.h" #include "random.h" #define MAX_CHAT_PATTERNS 500 static struct chat { double minwin; double maxwin; char from[20]; char regex[100]; char reply[300]; // in printf format with one param (100*winrate) regex_t preg; bool displayed; bool match; } *chat_table; static char default_reply[] = "I know all those words, but that sentence makes no sense to me"; static char not_playing[] = "I'm winning big without playing"; /* Read the chat file, a sequence of lines of the form: * minwin;maxwin;from;regex;reply * Set minwin, maxwin to -1.0 2.0 for answers to chat other than winrate. * Set from as one space for replies to anyone. * Examples: * -1.0;0.3; ;winrate;%.1f%% I'm losing * -1.0;2.0;pasky;^when ;Today */ void chat_init(char *chat_file) { if (!chat_file) return; FILE *f = fopen(chat_file, "r"); if (!f) { perror(chat_file); return; } chat_table = calloc2(MAX_CHAT_PATTERNS, sizeof(*chat_table)); struct chat *entry = chat_table; while (fscanf(f, "%lf;%lf;%20[^;];%100[^;];%300[^\n]\n", &entry->minwin, &entry->maxwin, entry->from, entry->regex, entry->reply ) == 5) { if (!strcmp(entry->from, " ")) entry->from[0] = '\0'; int err = regcomp(&entry->preg, entry->regex, REG_EXTENDED | REG_ICASE); if (err) { char msg[200]; regerror(err, &entry->preg, msg, sizeof(msg)); fprintf(stderr, "Error compiling %s: %s\n", entry->regex, msg); } else { entry++; } } if (!feof(f)) fprintf(stderr, "syntax error around line %u in %s\n", (unsigned)(entry - chat_table), chat_file); fclose(f); if (DEBUGL(1)) fprintf(stderr, "Loaded %u chat entries from %s\n", (unsigned)(entry - chat_table), chat_file); } void chat_done() { if (chat_table) { free(chat_table); chat_table = NULL; } } /* Reply to a chat. When not playing, color is S_NONE and all remaining parameters are undefined. * If some matching entries have not yet been displayed we pick randomly among them. Otherwise * we pick randomly among all matching entries. */ char *generic_chat(struct board *b, bool opponent, char *from, char *cmd, enum stone color, coord_t move, int playouts, int machines, int threads, double winrate, double extra_komi) { static char reply[1024]; if (!chat_table) { if (strncasecmp(cmd, "winrate", 7)) return NULL; if (color == S_NONE) return not_playing; snprintf(reply, 512, "In %d playouts at %d threads, %s %s can win with %.1f%% probability", playouts, threads, stone2str(color), coord2sstr(move, b), 100*winrate); if (fabs(extra_komi) >= 0.5) { snprintf(reply + strlen(reply), 510, ", while self-imposing extra komi %.1f", extra_komi); } strcat(reply, "."); return reply; } int matches = 0; int undisplayed = 0; for (struct chat *entry = chat_table; entry->regex[0]; entry++) { entry->match = false; if (color != S_NONE) { if (winrate < entry->minwin) continue; if (winrate > entry->maxwin) continue; } if (entry->from[0] && strcmp(entry->from, from)) continue; if (regexec(&entry->preg, cmd, 0, NULL, 0)) continue; entry->match = true; matches++; if (!entry->displayed) undisplayed++; } if (matches == 0) return default_reply; int choices = undisplayed > 0 ? undisplayed : matches; int index = fast_random(choices); for (struct chat *entry = chat_table; entry->regex[0]; entry++) { if (!entry->match) continue; if (undisplayed > 0 && entry->displayed) continue; if (--index < 0) { entry->displayed = true; snprintf(reply, sizeof(reply), entry->reply, 100*winrate); return reply; } } assert(0); return NULL; } #else void chat_init(char *chat_file) {} void chat_done() {} char *generic_chat(struct board *b, bool opponent, char *from, char *cmd, enum stone color, coord_t move, int playouts, int machines, int threads, double winrate, double extra_komi) { static char reply[1024] = { '.', '\0' }; return reply; } #endif