forked from pasky/pachi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchat.c
137 lines (123 loc) · 3.99 KB
/
chat.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#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