76d8a4e93eb562621da03d94a84857d5bb99d01c
[pachi.git] / chat.c
blob76d8a4e93eb562621da03d94a84857d5bb99d01c
1 #include <assert.h>
2 #include <math.h>
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <regex.h>
6 #include <stdint.h>
8 #define DEBUG
10 #include "debug.h"
11 #include "chat.h"
12 #include "random.h"
14 #define MAX_CHAT_PATTERNS 500
16 static struct chat {
17 double minwin;
18 double maxwin;
19 char from[20];
20 char regex[100];
21 char reply[300]; // in printf format with one param (100*winrate)
23 regex_t preg;
24 bool displayed;
25 bool match;
26 } *chat_table;
28 static char default_reply[] = "I know all those words, but that sentence makes no sense to me";
29 static char not_playing[] = "I'm winning big without playing";
31 /* Read the chat file, a sequence of lines of the form:
32 * minwin;maxwin;from;regex;reply
33 * Set minwin, maxwin to -1.0 2.0 for answers to chat other than winrate.
34 * Set from as one space for replies to anyone.
35 * Examples:
36 * -1.0;0.3; ;winrate;%.1f%% I'm losing
37 * -1.0;2.0;pasky;^when ;Today
39 void chat_init(char *chat_file) {
40 if (!chat_file) return;
41 FILE *f = fopen(chat_file, "r");
42 if (!f) {
43 perror(chat_file);
44 return;
46 chat_table = calloc2(MAX_CHAT_PATTERNS, sizeof(*chat_table));
47 struct chat *entry = chat_table;
48 while (fscanf(f, "%lf;%lf;%20[^;];%100[^;];%300[^\n]\n", &entry->minwin, &entry->maxwin,
49 entry->from, entry->regex, entry->reply ) == 5) {
50 if (!strcmp(entry->from, " "))
51 entry->from[0] = '\0';
52 int err = regcomp(&entry->preg, entry->regex, REG_EXTENDED | REG_ICASE);
53 if (err) {
54 char msg[200];
55 regerror(err, &entry->preg, msg, sizeof(msg));
56 fprintf(stderr, "Error compiling %s: %s\n", entry->regex, msg);
57 } else {
58 entry++;
61 if (!feof(f))
62 fprintf(stderr, "syntax error around line %u in %s\n", (uintptr_t)(entry - chat_table), chat_file);
63 fclose(f);
64 if (DEBUGL(1))
65 fprintf(stderr, "Loaded %u chat entries from %s\n", (uintptr_t)(entry - chat_table), chat_file);
68 void chat_done() {
69 if (chat_table) {
70 free(chat_table);
71 chat_table = NULL;
75 /* Reply to a chat. When not playing, color is S_NONE and all remaining parameters are undefined.
76 * If some matching entries have not yet been displayed we pick randomly among them. Otherwise
77 * we pick randomly among all matching entries. */
78 char
79 *generic_chat(struct board *b, bool opponent, char *from, char *cmd, enum stone color, coord_t move,
80 int playouts, int machines, int threads, double winrate, double extra_komi) {
82 static char reply[1024];
83 if (!chat_table) {
84 if (strncasecmp(cmd, "winrate", 7)) return NULL;
85 if (color == S_NONE) return not_playing;
87 snprintf(reply, 512, "In %d playouts at %d threads, %s %s can win with %.1f%% probability",
88 playouts, threads, stone2str(color), coord2sstr(move, b), 100*winrate);
89 if (abs(extra_komi) >= 0.5) {
90 snprintf(reply + strlen(reply), 510, ", while self-imposing extra komi %.1f", extra_komi);
92 strcat(reply, ".");
93 return reply;
95 int matches = 0;
96 int undisplayed = 0;
97 for (struct chat *entry = chat_table; entry->regex[0]; entry++) {
98 entry->match = false;
99 if (color != S_NONE) {
100 if (winrate < entry->minwin) continue;
101 if (winrate > entry->maxwin) continue;
103 if (entry->from[0] && strcmp(entry->from, from)) continue;
104 if (regexec(&entry->preg, cmd, 0, NULL, 0)) continue;
105 entry->match = true;
106 matches++;
107 if (!entry->displayed) undisplayed++;
109 if (matches == 0) return default_reply;
110 int choices = undisplayed > 0 ? undisplayed : matches;
111 int index = fast_random(choices);
112 for (struct chat *entry = chat_table; entry->regex[0]; entry++) {
113 if (!entry->match) continue;
114 if (undisplayed > 0 && entry->displayed) continue;
115 if (--index < 0) {
116 entry->displayed = true;
117 snprintf(reply, sizeof(reply), entry->reply, 100*winrate);
118 return reply;
121 assert(0);
122 return NULL;