Moggy: Introduce capcheckall switch for global_atari_check()
[pachi/derm.git] / joseki / joseki.c
blob930c2e4f152d6152c2eba00eaf8e446bcbf07f4e
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 bool discard;
18 int size;
19 struct joseki_dict *jdict;
21 struct board *b[16]; // boards with reversed color, mirrored and rotated
24 /* We will record the joseki positions into incrementally-built
25 * jdict->patterns[]. */
28 static char *
29 joseki_play(struct engine *e, struct board *b, struct move *m)
31 struct joseki_engine *j = e->data;
33 if (!b->moves) {
34 /* New game, reset state. */
35 j->size = board_size(b);
36 if (j->jdict)
37 assert(j->size == j->jdict->bsize);
38 else
39 j->jdict = joseki_init(j->size);
40 j->discard = false;
41 for (int i = 0; i < 16; i++) {
42 board_resize(j->b[i], j->size - 2);
43 board_clear(j->b[i]);
47 //printf("%s %d\n", coord2sstr(m->coord, b), coord_quadrant(m->coord, b));
49 assert(!is_resign(m->coord));
50 if (is_pass(m->coord))
51 return NULL;
52 /* Ignore moves in different quadrants. */
53 if (coord_quadrant(m->coord, b) > 0)
54 return NULL;
56 if (coord_x(m->coord, b) == board_size(b) / 2 || coord_y(m->coord, b) == board_size(b) / 2) {
57 /* This is troublesome, since it cannot mirror properly:
58 * it won't be hashed in some quadrants. Better just discard
59 * the rest of the sequence for now. (TODO: Make quadrants
60 * overlap.) */
61 j->discard = true;
63 if (j->discard)
64 return NULL;
66 //printf("%"PRIhash" %"PRIhash"\n", j->b[0]->qhash[0], b->qhash[0]);
67 assert(j->b[0]->qhash[0] == b->qhash[0]);
69 /* Record next move in all rotations and update the hash. */
70 for (int i = 0; i < 16; i++) {
71 #define HASH_VMIRROR 1
72 #define HASH_HMIRROR 2
73 #define HASH_XYFLIP 4
74 #define HASH_OCOLOR 8
75 int quadrant = 0;
76 coord_t coord = m->coord;
77 if (i & HASH_VMIRROR) {
78 coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b));
79 quadrant += 2;
81 if (i & HASH_HMIRROR) {
82 coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b));
83 quadrant++;
85 if (i & HASH_XYFLIP) {
86 coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b));
87 if (quadrant == 1)
88 quadrant = 2;
89 else if (quadrant == 2)
90 quadrant = 1;
92 enum stone color = m->color;
93 if (i & HASH_OCOLOR)
94 color = stone_other(color);
96 coord_t **ccp = &j->jdict->patterns[j->b[i]->qhash[quadrant] & joseki_hash_mask].moves[color - 1];
98 int count = 1;
99 if (*ccp) {
100 for (coord_t *cc = *ccp; !is_pass(*cc); cc++) {
101 count++;
102 if (*cc == coord) {
103 //printf("%d,%d (%"PRIhash", %d) !+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b));
104 goto already_have;
109 //printf("%d,%d (%"PRIhash", %d) =+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b));
110 *ccp = realloc(*ccp, (count + 1) * sizeof(coord_t));
111 (*ccp)[count - 1] = coord;
112 (*ccp)[count] = pass;
114 already_have: {
115 struct move m2 = { .coord = coord, .color = color };
116 board_play(j->b[i], &m2);
120 return NULL;
123 static coord_t *
124 joseki_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive)
126 fprintf(stderr, "genmove command not available in joseki scan!\n");
127 exit(EXIT_FAILURE);
130 void
131 engine_joseki_done(struct engine *e)
133 struct joseki_engine *j = e->data;
134 struct board *b = board_init();
135 board_resize(b, j->size - 2);
136 board_clear(b);
138 for (hash_t i = 0; i < 1 << joseki_hash_bits; i++) {
139 for (int s = 0; s < 2; s++) {
140 static const char cs[] = "bw";
141 if (!j->jdict->patterns[i].moves[s])
142 continue;
143 printf("%" PRIhash " %c", i, cs[s]);
144 coord_t *cc = j->jdict->patterns[i].moves[s];
145 int count = 0;
146 while (!is_pass(*cc)) {
147 printf(" %s", coord2sstr(*cc, b));
148 cc++, count++;
150 printf(" %d\n", count);
154 board_done(b);
156 joseki_done(j->jdict);
160 struct joseki_engine *
161 joseki_state_init(char *arg)
163 struct joseki_engine *j = calloc2(1, sizeof(struct joseki_engine));
165 for (int i = 0; i < 16; i++)
166 j->b[i] = board_init();
168 j->debug_level = 1;
170 if (arg) {
171 char *optspec, *next = arg;
172 while (*next) {
173 optspec = next;
174 next += strcspn(next, ",");
175 if (*next) { *next++ = 0; } else { *next = 0; }
177 char *optname = optspec;
178 char *optval = strchr(optspec, '=');
179 if (optval) *optval++ = 0;
181 if (!strcasecmp(optname, "debug")) {
182 if (optval)
183 j->debug_level = atoi(optval);
184 else
185 j->debug_level++;
187 } else {
188 fprintf(stderr, "joseki: Invalid engine argument %s or missing value\n", optname);
189 exit(EXIT_FAILURE);
194 return j;
197 struct engine *
198 engine_joseki_init(char *arg, struct board *b)
200 struct joseki_engine *j = joseki_state_init(arg);
201 struct engine *e = calloc2(1, sizeof(struct engine));
202 e->name = "Joseki Engine";
203 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.";
204 e->genmove = joseki_genmove;
205 e->notify_play = joseki_play;
206 e->done = engine_joseki_done;
207 e->data = j;
208 // clear_board does not concern us, we like to work over many games
209 e->keep_on_clear = true;
211 return e;