Joseki: Encapsulate dictionary in struct joseki_dict
[pachi/json.git] / joseki / joseki.c
blob103713d80b15eb75dda53c2626c3a0cb7eb8ff18
1 #include <assert.h>
2 #include <stdio.h>
3 #include <stdlib.h>
5 #include "board.h"
6 #include "debug.h"
7 #include "engine.h"
8 #include "move.h"
9 #include "joseki/joseki.h"
10 #include "joseki/base.h"
13 /* Internal engine state. */
14 struct joseki_engine {
15 int debug_level;
16 int size;
17 bool discard;
19 struct board *b[16]; // boards with reversed color, mirrored and rotated
22 /* We will record the joseki positions into incrementally-built
23 * jdict->patterns[]. */
26 static char *
27 joseki_play(struct engine *e, struct board *b, struct move *m)
29 struct joseki_engine *j = e->data;
31 if (!b->moves) {
32 /* New game, reset state. */
33 j->size = board_size(b);
34 assert(j->size == jdict->bsize);
35 j->discard = false;
36 for (int i = 0; i < 16; i++) {
37 board_resize(j->b[i], j->size - 2);
38 board_clear(j->b[i]);
42 //printf("%s %d\n", coord2sstr(m->coord, b), coord_quadrant(m->coord, b));
44 assert(!is_resign(m->coord));
45 if (is_pass(m->coord))
46 return NULL;
47 /* Ignore moves in different quadrants. */
48 if (coord_quadrant(m->coord, b) > 0)
49 return NULL;
51 if (coord_x(m->coord, b) == board_size(b) / 2 || coord_y(m->coord, b) == board_size(b) / 2) {
52 /* This is troublesome, since it cannot mirror properly:
53 * it won't be hashed in some quadrants. Better just discard
54 * the rest of the sequence for now. (TODO: Make quadrants
55 * overlap.) */
56 j->discard = true;
58 if (j->discard)
59 return NULL;
61 //printf("%"PRIhash" %"PRIhash"\n", j->b[0]->qhash[0], b->qhash[0]);
62 assert(j->b[0]->qhash[0] == b->qhash[0]);
64 /* Record next move in all rotations and update the hash. */
65 for (int i = 0; i < 16; i++) {
66 #define HASH_VMIRROR 1
67 #define HASH_HMIRROR 2
68 #define HASH_XYFLIP 4
69 #define HASH_OCOLOR 8
70 int quadrant = 0;
71 coord_t coord = m->coord;
72 if (i & HASH_VMIRROR) {
73 coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b));
74 quadrant += 2;
76 if (i & HASH_HMIRROR) {
77 coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b));
78 quadrant++;
80 if (i & HASH_XYFLIP) {
81 coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b));
82 if (quadrant == 1)
83 quadrant = 2;
84 else if (quadrant == 2)
85 quadrant = 1;
87 enum stone color = m->color;
88 if (i & HASH_OCOLOR)
89 color = stone_other(color);
91 coord_t **ccp = &jdict->patterns[j->b[i]->qhash[quadrant] & joseki_hash_mask].moves[color - 1];
93 int count = 1;
94 if (*ccp) {
95 for (coord_t *cc = *ccp; !is_pass(*cc); cc++) {
96 count++;
97 if (*cc == coord) {
98 //printf("%d,%d (%"PRIhash", %d) !+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b));
99 goto already_have;
104 //printf("%d,%d (%"PRIhash", %d) =+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b));
105 *ccp = realloc(*ccp, (count + 1) * sizeof(coord_t));
106 (*ccp)[count - 1] = coord;
107 (*ccp)[count] = pass;
109 already_have: {
110 struct move m2 = { .coord = coord, .color = color };
111 board_play(j->b[i], &m2);
115 return NULL;
118 static coord_t *
119 joseki_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive)
121 fprintf(stderr, "genmove command not available in joseki scan!\n");
122 exit(EXIT_FAILURE);
125 void
126 engine_joseki_done(struct engine *e)
128 struct joseki_engine *j = e->data;
129 struct board *b = board_init();
130 board_resize(b, j->size - 2);
131 board_clear(b);
133 for (hash_t i = 0; i < 1 << joseki_hash_bits; i++) {
134 for (int j = 0; j < 2; j++) {
135 static const char cs[] = "bw";
136 if (!jdict->patterns[i].moves[j])
137 continue;
138 printf("%" PRIhash " %c", i, cs[j]);
139 coord_t *cc = jdict->patterns[i].moves[j];
140 int count = 0;
141 while (!is_pass(*cc)) {
142 printf(" %s", coord2sstr(*cc, b));
143 cc++, count++;
145 printf(" %d\n", count);
149 board_done(b);
153 struct joseki_engine *
154 joseki_state_init(char *arg)
156 struct joseki_engine *j = calloc2(1, sizeof(struct joseki_engine));
158 for (int i = 0; i < 16; i++)
159 j->b[i] = board_init();
161 j->debug_level = 1;
163 if (arg) {
164 char *optspec, *next = arg;
165 while (*next) {
166 optspec = next;
167 next += strcspn(next, ",");
168 if (*next) { *next++ = 0; } else { *next = 0; }
170 char *optname = optspec;
171 char *optval = strchr(optspec, '=');
172 if (optval) *optval++ = 0;
174 if (!strcasecmp(optname, "debug")) {
175 if (optval)
176 j->debug_level = atoi(optval);
177 else
178 j->debug_level++;
180 } else {
181 fprintf(stderr, "joseki: Invalid engine argument %s or missing value\n", optname);
182 exit(EXIT_FAILURE);
187 if (jdict)
188 joseki_done(jdict);
189 jdict = joseki_init(19 + 2); // XXX
191 return j;
194 struct engine *
195 engine_joseki_init(char *arg, struct board *b)
197 struct joseki_engine *j = joseki_state_init(arg);
198 struct engine *e = calloc2(1, sizeof(struct engine));
199 e->name = "Joseki Engine";
200 e->comment = "You cannot play Pachi with this engine, it is intended for special development use - scanning of joseki sequences fed to it within the GTP stream.";
201 e->genmove = joseki_genmove;
202 e->notify_play = joseki_play;
203 e->done = engine_joseki_done;
204 e->data = j;
205 // clear_board does not concern us, we like to work over many games
206 e->keep_on_clear = true;
208 return e;