Introduce Joseki Scanner aux engine
[pachi/peepo.git] / joseki / joseki.c
blob5137c5a796e600de2aef599595fcd6659a1be478
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;
18 struct board *b[16]; // boards with reversed color, mirrored and rotated
21 /* We will record the joseki positions into incrementally-built
22 * joseki_pats[]. */
25 static char *
26 joseki_play(struct engine *e, struct board *b, struct move *m)
28 struct joseki_engine *j = e->data;
30 if (!b->moves) {
31 /* Reset boards. */
32 j->size = board_size(b);
33 for (int i = 0; i < 16; i++) {
34 board_resize(j->b[i], j->size - 2);
35 board_clear(j->b[i]);
39 assert(!is_resign(m->coord));
40 if (is_pass(m->coord))
41 return NULL;
42 //printf("%s %d\n", coord2sstr(m->coord, b), coord_quadrant(m->coord, b));
43 /* Ignore moves in different quadrants. */
44 if (coord_quadrant(m->coord, b) > 0)
45 return NULL;
47 //printf("%"PRIhash" %"PRIhash"\n", j->b[0]->qhash[0], b->qhash[0]);
48 assert(j->b[0]->qhash[0] == b->qhash[0]);
50 /* Record next move in all rotations and update the hash. */
51 for (int i = 0; i < 16; i++) {
52 #define HASH_VMIRROR 1
53 #define HASH_HMIRROR 2
54 #define HASH_XYFLIP 4
55 #define HASH_OCOLOR 8
56 int quadrant = 0;
57 coord_t coord = m->coord;
58 if (i & HASH_VMIRROR) {
59 coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b));
60 quadrant += 2;
62 if (i & HASH_HMIRROR) {
63 coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b));
64 quadrant++;
66 if (i & HASH_XYFLIP) {
67 coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b));
68 if (quadrant == 1)
69 quadrant = 2;
70 else if (quadrant == 2)
71 quadrant = 1;
73 enum stone color = m->color;
74 if (i & HASH_OCOLOR)
75 color = stone_other(color);
77 coord_t **ccp = &joseki_pats[j->b[i]->qhash[quadrant] & joseki_hash_mask].moves[color - 1];
79 int count = 1;
80 if (*ccp) {
81 for (coord_t *cc = *ccp; !is_pass(*cc); cc++) {
82 count++;
83 if (*cc == coord) {
84 //printf("(%"PRIhash", %d) !+ %s\n", j->b[i]->qhash[0], count, coord2sstr(coord, b));
85 goto already_have;
90 *ccp = realloc(*ccp, (count + 1) * sizeof(coord_t));
91 //printf("(%"PRIhash", %d) + %s\n", j->b[i]->qhash[quadrant], count, coord2sstr(coord, b));
92 (*ccp)[count - 1] = coord;
93 (*ccp)[count] = pass;
95 already_have: {
96 struct move m2 = { .coord = coord, .color = color };
97 board_play(j->b[i], &m2);
101 return NULL;
104 static coord_t *
105 joseki_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive)
107 fprintf(stderr, "genmove command not available in joseki scan!\n");
108 exit(EXIT_FAILURE);
111 void
112 joseki_done(struct engine *e)
114 struct joseki_engine *j = e->data;
115 struct board *b = board_init();
116 board_resize(b, j->size - 2);
117 board_clear(b);
119 for (hash_t i = 0; i < 1 << joseki_hash_bits; i++) {
120 for (int j = 0; j < 2; j++) {
121 static const char cs[] = "bw";
122 if (!joseki_pats[i].moves[j])
123 continue;
124 printf("%" PRIhash " %c", i, cs[j]);
125 coord_t *cc = joseki_pats[i].moves[j];
126 int count = 0;
127 while (!is_pass(*cc)) {
128 printf(" %s", coord2sstr(*cc, b));
129 cc++, count++;
131 printf(" %d\n", count);
135 board_done(b);
139 struct joseki_engine *
140 joseki_state_init(char *arg)
142 struct joseki_engine *j = calloc2(1, sizeof(struct joseki_engine));
144 for (int i = 0; i < 16; i++)
145 j->b[i] = board_init();
147 j->debug_level = 1;
149 if (arg) {
150 char *optspec, *next = arg;
151 while (*next) {
152 optspec = next;
153 next += strcspn(next, ",");
154 if (*next) { *next++ = 0; } else { *next = 0; }
156 char *optname = optspec;
157 char *optval = strchr(optspec, '=');
158 if (optval) *optval++ = 0;
160 if (!strcasecmp(optname, "debug")) {
161 if (optval)
162 j->debug_level = atoi(optval);
163 else
164 j->debug_level++;
166 } else {
167 fprintf(stderr, "joseki: Invalid engine argument %s or missing value\n", optname);
168 exit(EXIT_FAILURE);
173 return j;
176 struct engine *
177 engine_joseki_init(char *arg, struct board *b)
179 struct joseki_engine *j = joseki_state_init(arg);
180 struct engine *e = calloc2(1, sizeof(struct engine));
181 e->name = "Joseki Engine";
182 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.";
183 e->genmove = joseki_genmove;
184 e->notify_play = joseki_play;
185 e->done = joseki_done;
186 e->data = j;
187 // clear_board does not concern us, we like to work over many games
188 e->keep_on_clear = true;
190 return e;