Merge branch 'master' of git+ssh://repo.or.cz/srv/git/pachi
[pachi/derm.git] / joseki / joseki.c
blobd9a0ce766477b689e7cfd201b4a49e3f3e44db8b
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 * joseki_pats[]. */
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 j->discard = false;
35 for (int i = 0; i < 16; i++) {
36 board_resize(j->b[i], j->size - 2);
37 board_clear(j->b[i]);
41 //printf("%s %d\n", coord2sstr(m->coord, b), coord_quadrant(m->coord, b));
43 assert(!is_resign(m->coord));
44 if (is_pass(m->coord))
45 return NULL;
46 /* Ignore moves in different quadrants. */
47 if (coord_quadrant(m->coord, b) > 0)
48 return NULL;
50 if (coord_x(m->coord, b) == board_size(b) / 2 || coord_y(m->coord, b) == board_size(b) / 2) {
51 /* This is troublesome, since it cannot mirror properly:
52 * it won't be hashed in some quadrants. Better just discard
53 * the rest of the sequence for now. (TODO: Make quadrants
54 * overlap.) */
55 j->discard = true;
57 if (j->discard)
58 return NULL;
60 //printf("%"PRIhash" %"PRIhash"\n", j->b[0]->qhash[0], b->qhash[0]);
61 assert(j->b[0]->qhash[0] == b->qhash[0]);
63 /* Record next move in all rotations and update the hash. */
64 for (int i = 0; i < 16; i++) {
65 #define HASH_VMIRROR 1
66 #define HASH_HMIRROR 2
67 #define HASH_XYFLIP 4
68 #define HASH_OCOLOR 8
69 int quadrant = 0;
70 coord_t coord = m->coord;
71 if (i & HASH_VMIRROR) {
72 coord = coord_xy(b, coord_x(coord, b), board_size(b) - 1 - coord_y(coord, b));
73 quadrant += 2;
75 if (i & HASH_HMIRROR) {
76 coord = coord_xy(b, board_size(b) - 1 - coord_x(coord, b), coord_y(coord, b));
77 quadrant++;
79 if (i & HASH_XYFLIP) {
80 coord = coord_xy(b, coord_y(coord, b), coord_x(coord, b));
81 if (quadrant == 1)
82 quadrant = 2;
83 else if (quadrant == 2)
84 quadrant = 1;
86 enum stone color = m->color;
87 if (i & HASH_OCOLOR)
88 color = stone_other(color);
90 coord_t **ccp = &joseki_pats[j->b[i]->qhash[quadrant] & joseki_hash_mask].moves[color - 1];
92 int count = 1;
93 if (*ccp) {
94 for (coord_t *cc = *ccp; !is_pass(*cc); cc++) {
95 count++;
96 if (*cc == coord) {
97 //printf("%d,%d (%"PRIhash", %d) !+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b));
98 goto already_have;
103 //printf("%d,%d (%"PRIhash", %d) =+ %s\n", i, quadrant, j->b[i]->qhash[quadrant], count, coord2sstr(coord, b));
104 *ccp = realloc(*ccp, (count + 1) * sizeof(coord_t));
105 (*ccp)[count - 1] = coord;
106 (*ccp)[count] = pass;
108 already_have: {
109 struct move m2 = { .coord = coord, .color = color };
110 board_play(j->b[i], &m2);
114 return NULL;
117 static coord_t *
118 joseki_genmove(struct engine *e, struct board *b, struct time_info *ti, enum stone color, bool pass_all_alive)
120 fprintf(stderr, "genmove command not available in joseki scan!\n");
121 exit(EXIT_FAILURE);
124 void
125 joseki_done(struct engine *e)
127 struct joseki_engine *j = e->data;
128 struct board *b = board_init();
129 board_resize(b, j->size - 2);
130 board_clear(b);
132 for (hash_t i = 0; i < 1 << joseki_hash_bits; i++) {
133 for (int j = 0; j < 2; j++) {
134 static const char cs[] = "bw";
135 if (!joseki_pats[i].moves[j])
136 continue;
137 printf("%" PRIhash " %c", i, cs[j]);
138 coord_t *cc = joseki_pats[i].moves[j];
139 int count = 0;
140 while (!is_pass(*cc)) {
141 printf(" %s", coord2sstr(*cc, b));
142 cc++, count++;
144 printf(" %d\n", count);
148 board_done(b);
152 struct joseki_engine *
153 joseki_state_init(char *arg)
155 struct joseki_engine *j = calloc2(1, sizeof(struct joseki_engine));
157 for (int i = 0; i < 16; i++)
158 j->b[i] = board_init();
160 j->debug_level = 1;
162 if (arg) {
163 char *optspec, *next = arg;
164 while (*next) {
165 optspec = next;
166 next += strcspn(next, ",");
167 if (*next) { *next++ = 0; } else { *next = 0; }
169 char *optname = optspec;
170 char *optval = strchr(optspec, '=');
171 if (optval) *optval++ = 0;
173 if (!strcasecmp(optname, "debug")) {
174 if (optval)
175 j->debug_level = atoi(optval);
176 else
177 j->debug_level++;
179 } else {
180 fprintf(stderr, "joseki: Invalid engine argument %s or missing value\n", optname);
181 exit(EXIT_FAILURE);
186 return j;
189 struct engine *
190 engine_joseki_init(char *arg, struct board *b)
192 struct joseki_engine *j = joseki_state_init(arg);
193 struct engine *e = calloc2(1, sizeof(struct engine));
194 e->name = "Joseki Engine";
195 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.";
196 e->genmove = joseki_genmove;
197 e->notify_play = joseki_play;
198 e->done = joseki_done;
199 e->data = j;
200 // clear_board does not concern us, we like to work over many games
201 e->keep_on_clear = true;
203 return e;