Ticket #150: Monte_Carlo_board_code.c

File Monte_Carlo_board_code.c, 11.4 kB (added by gunnar, 21 months ago)

First attempt on Monte Carlo specific board code.

Line 
1/* Special board code for Monte Carlo simulations.
2 *
3 * A liberty edge is the combination of the position of the liberty
4 * and the direction to a string given by the index into the delta[]
5 * array. A liberty edge at lib with corresponding string in direction
6 * delta[dir] is encoded as (lib << 2 | dir).
7 *
8 * The stones of a string are linked in a cyclic list through the
9 * next_stone field, just like the global board does.
10 *
11 * Likewise the liberty edges corresponding to each string are
12 * connected in a doubly linked cyclic list through the
13 * previous_liberty_edge and next_liberty_edge fields.
14 *
15 * The reference stone has to be the same for every stone in a string
16 * but it doesn't have to be a predictable one, in contrast to the
17 * origin in the global board. The reference stone is the only one
18 * which is guaranteed to have a valid pointer to the "first" liberty
19 * edge (just an arbitrary element in the circular list).
20 */
21
22
23struct mc_board {
24  Intersection board[BOARDSIZE];
25  int board_ko_pos;
26  int reference_stone[BOARDMAX];
27  int next_stone[BOARDMAX];
28  int first_liberty_edge[BOARDMAX];
29  int previous_liberty_edge[4 * BOARDMAX];
30  int next_liberty_edge[4 * BOARDMAX];
31};
32
33
34/* Add a liberty edge for a string at pos with liberty at lib and
35 * direction dir.
36 */
37static void
38mc_add_liberty_edge(struct mc_board *mc, int pos, int lib, int dir)
39{
40  int this_liberty_edge = (lib << 2) | dir;
41  int reference = mc->reference_stone[pos];
42  int first_liberty_edge = mc->first_liberty_edge[reference];
43
44  gg_assert(lib + delta[dir] == pos);
45 
46  if (first_liberty_edge) {
47    int second_liberty_edge = mc->next_liberty_edge[first_liberty_edge];
48    mc->previous_liberty_edge[this_liberty_edge] = first_liberty_edge;
49    mc->next_liberty_edge[this_liberty_edge] = second_liberty_edge;
50    mc->next_liberty_edge[first_liberty_edge] = this_liberty_edge;
51    mc->previous_liberty_edge[second_liberty_edge] = this_liberty_edge;
52  }
53  else {
54    mc->first_liberty_edge[reference] = this_liberty_edge;
55    mc->next_liberty_edge[this_liberty_edge] = this_liberty_edge;
56    mc->previous_liberty_edge[this_liberty_edge] = this_liberty_edge;
57  }
58}
59
60
61/* Remove a liberty edge for a string at pos with liberty at lib and
62 * direction dir.
63 */
64static int
65mc_remove_liberty_edge(struct mc_board *mc, int pos, int lib, int dir)
66{
67  int reference = mc->reference_stone[pos];
68  int this_liberty_edge = (lib << 2) | dir;
69  int next = mc->next_liberty_edge[this_liberty_edge];
70  int previous = mc->previous_liberty_edge[this_liberty_edge];
71 
72  gg_assert(lib + delta[dir] == pos);
73 
74  if (next == this_liberty_edge) {
75    mc->first_liberty_edge[reference] = 0;
76    return 0;
77  }
78
79  mc->next_liberty_edge[previous] = next;
80  mc->previous_liberty_edge[next] = previous;
81  if (mc->first_liberty_edge[reference] == this_liberty_edge)
82    mc->first_liberty_edge[reference] = next;
83
84  return next;
85}
86
87
88/* Join the strings at str1 and str2. It is assumed that str1 is a
89 * newly placed stone (possibly already joined with other strings) and
90 * that the liberty edge corresponding to the liberty at the newly
91 * placed stone has not yet been removed.
92 */
93static void
94mc_join_strings(struct mc_board *mc, int str1, int str2)
95{
96  int reference = mc->reference_stone[str2];
97  int liberty_edge2 = mc->first_liberty_edge[reference];
98  int liberty_edge1 = mc->first_liberty_edge[mc->reference_stone[str1]];
99  int next1;
100  int next2;
101  int pos = str1;
102
103  /* Update the reference stone for str1. */
104  do {
105    mc->reference_stone[pos] = reference;
106    pos = mc->next_stone[pos];
107  } while (pos != str1);
108
109  /* Switch next_stone pointers to join the strings. */
110  next1 = mc->next_stone[str1];
111  mc->next_stone[str1] = mc->next_stone[str2];
112  mc->next_stone[str2] = next1;
113
114  /* Join the circular liberty_edge structures. We know that str2
115   * still has a liberty listed at the newly added stone so
116   * liberty_edge2 is guaranteed to be non-zero.
117   */
118  if (liberty_edge1 != 0) {
119    next1 = mc->next_liberty_edge[liberty_edge1];
120    next2 = mc->next_liberty_edge[liberty_edge2];
121    mc->next_liberty_edge[liberty_edge1] = next2;
122    mc->next_liberty_edge[liberty_edge2] = next1;
123    mc->previous_liberty_edge[next1] = liberty_edge2;
124    mc->previous_liberty_edge[next2] = liberty_edge1;
125  }
126}
127
128
129/* Remove the string at str from the board. */
130static int
131mc_remove_string(struct mc_board *mc, int str)
132{
133  int other = OTHER_COLOR(mc->board[str]);
134  int pos = str;
135  int num_removed_stones = 0;
136  int k;
137 
138  do {
139    for (k = 0; k < 4; k++)
140      if (mc->board[pos + delta[k]] == other)
141        mc_add_liberty_edge(mc, pos + delta[k], pos, k);
142    mc->board[pos] = EMPTY;
143    num_removed_stones++;
144    pos = mc->next_stone[pos];
145  } while (pos != str);
146
147  return num_removed_stones;
148}
149
150
151/* Initialize a Monte Carlo board struct from the global board.
152 */
153static void
154mc_init_board_from_global_board(struct mc_board *mc)
155{
156  int stones[BOARDMAX];
157  int num_stones;
158  int pos;
159  int k;
160  int r;
161 
162  memcpy(mc->board, board, sizeof(mc->board));
163  mc->board_ko_pos = board_ko_pos;
164
165  /* FIXME: next_stone[] can be copied directly from the global board
166   * data structure.
167   */
168  memset(mc->next_stone, 0, sizeof(mc->next_stone));
169  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
170    if (IS_STONE(board[pos]) && mc->next_stone[pos] == 0) {
171      num_stones = findstones(pos, BOARDMAX, stones);
172      mc->first_liberty_edge[pos] = 0;
173      for (r = 0; r < num_stones; r++) {
174        mc->next_stone[stones[r]] = stones[(r + 1) % num_stones];
175        mc->reference_stone[stones[r]] = pos;
176        for (k = 0; k < 4; k++) {
177          if (board[stones[r] + delta[k]] == EMPTY)
178            mc_add_liberty_edge(mc, stones[r], stones[r] + delta[k],
179                                (k + 2) % 4);
180        }
181      }
182    }
183  }
184}
185
186
187/* Count the number of stones in the string at str. Stop counting if
188 * maxstones is reached.
189 */
190static int
191mc_countstones(struct mc_board *mc, int str, int maxstones)
192{
193  int stone = str;
194  int num_stones = 0;
195  do {
196    num_stones++;
197    stone = mc->next_stone[stone];
198  } while (stone != str && num_stones < maxstones);
199
200  return num_stones;
201}
202
203/* Is a move at pos by color suicide? */
204static int
205mc_is_suicide(struct mc_board *mc, int pos, int color)
206{
207  int k;
208 
209  if (mc->board[SOUTH(pos)] == EMPTY
210      || mc->board[WEST(pos)] == EMPTY
211      || mc->board[NORTH(pos)] == EMPTY
212      || mc->board[EAST(pos)] == EMPTY)
213    return 0;
214
215  for (k = 0; k < 4; k++) {
216    int first_liberty_edge;
217    int liberty_edge;
218    int additional_liberty = 0;
219    if (!ON_BOARD(pos + delta[k]))
220      continue;
221
222    first_liberty_edge = (pos << 2) | k;
223    liberty_edge = mc->next_liberty_edge[first_liberty_edge];
224    while (liberty_edge != first_liberty_edge) {
225      if ((liberty_edge >> 2) != pos) {
226        additional_liberty = 1;
227        break;
228      }
229      liberty_edge = mc->next_liberty_edge[liberty_edge];
230    }
231
232    if ((mc->board[pos + delta[k]] != color) ^ additional_liberty)
233      return 0;
234  }
235
236  return 1;
237}
238
239
240/* Is a move at pos by color legal? */
241static int
242mc_is_legal(struct mc_board *mc, int pos, int color)
243{
244  if (pos == PASS_MOVE)
245    return 1;
246
247  if (mc->board[pos] != EMPTY)
248    return 0;
249
250  if (pos == mc->board_ko_pos) {
251    if (mc->board[WEST(pos)] == OTHER_COLOR(color)
252        || mc->board[EAST(pos)] == OTHER_COLOR(color)) {
253      return 0;
254    }
255  }
256
257  return !mc_is_suicide(mc, pos, color);
258}
259
260
261/* Is the string at str in atari? Always place one liberty of the
262 * string in lib, unless it's a null pointer.
263 */
264static int
265mc_is_in_atari(struct mc_board *mc, int str, int *lib)
266{
267  int reference = mc->reference_stone[str];
268  int first_liberty_edge = mc->first_liberty_edge[reference];
269  int liberty = first_liberty_edge >> 2;
270  int liberty_edge = mc->next_liberty_edge[first_liberty_edge];
271  if (lib)
272    *lib = liberty;
273  while (liberty_edge != first_liberty_edge) {
274    if ((liberty_edge >> 2) != liberty)
275      return 0;
276    liberty_edge = mc->next_liberty_edge[liberty_edge];
277  }
278
279  return 1;
280}
281
282
283/* Is a move at pos by color a self atari? */
284static int
285mc_is_self_atari(struct mc_board *mc, int pos, int color)
286{
287  int k;
288  int captured = NO_MOVE;
289  int liberty = NO_MOVE;
290  int reference;
291  int other;
292
293  /* Quick test which is often effective. */
294  if (((mc->board[SOUTH(pos)] == EMPTY)
295       + (mc->board[WEST(pos)] == EMPTY)
296       + (mc->board[NORTH(pos)] == EMPTY)
297       + (mc->board[EAST(pos)] == EMPTY)) > 1)
298    return 0;
299
300  /* Otherwise look closer. */
301  for (k = 0; k < 4; k++) {
302    int first_liberty_edge;
303    int liberty_edge;
304    int additional_liberty = 0;
305    int pos2 = pos + delta[k];
306    if (mc->board[pos2] == EMPTY) {
307      if (pos2 != liberty) {
308        if (liberty != NO_MOVE)
309          return 0;
310        else
311          liberty = pos2;
312      }
313    }
314    else if (IS_STONE(mc->board[pos2])) {
315      first_liberty_edge = (pos << 2) | k;
316      liberty_edge = mc->next_liberty_edge[first_liberty_edge];
317      while (liberty_edge != first_liberty_edge) {
318        int lib = liberty_edge >> 2;
319        if (lib != pos) {
320          additional_liberty = 1;
321          if (mc->board[pos2] == color) {
322            if (lib != liberty) {
323              if (liberty != NO_MOVE)
324                return 0;
325              else
326                liberty = lib;
327            }
328          }
329          else
330            break;
331        }
332        liberty_edge = mc->next_liberty_edge[liberty_edge];
333      }
334
335      if (mc->board[pos2] != color && additional_liberty == 0) {
336        captured = pos2;
337        if (pos2 != liberty) {
338          if (liberty != NO_MOVE)
339            return 0;
340          else
341            liberty = pos2;
342        }
343      }
344    }
345  }
346
347  if (liberty == NO_MOVE || captured == NO_MOVE)
348    return 1;
349
350  /* Now only the difficult case remains where there was no adjacent
351   * empty stone, no adjacent friendly stone with an extra liberty,
352   * and exactly one neighbor was captured. Then the question is
353   * whether the capture produced a second liberty elsewhere.
354   */
355  reference = mc->reference_stone[captured];
356  other = OTHER_COLOR(color);
357  for (k = 0; k < 4; k++) {
358    if (board[pos + delta[k]] == color) {
359      int stone = pos + delta[k];
360      do {
361        int m;
362        for (m = 0; m < 4; m++) {
363          int pos2 = stone + delta[m];
364          if (board[pos2] == other
365              && pos2 != captured
366              && mc->reference_stone[pos2] == reference)
367            return 0;
368        }
369        stone = mc->next_stone[stone];
370      } while (stone != pos + delta[k]);
371    }
372  }
373
374  return 1;
375}
376
377
378/* Play the move at pos by color. */
379static int
380mc_play_move(struct mc_board *mc, int pos, int color)
381{
382  int k;
383  int captured_stones = 0;
384  int num_direct_liberties = 0;
385 
386  if (pos == PASS_MOVE) {
387    mc->board_ko_pos = NO_MOVE;
388    return 1;
389  }
390 
391  /* The move must not be the ko point. */
392  if (pos == mc->board_ko_pos) {
393    if (mc->board[WEST(pos)] == OTHER_COLOR(color)
394        || mc->board[EAST(pos)] == OTHER_COLOR(color)) {
395      return 0;
396    }
397  }
398 
399  /* Test for suicide. */
400  if (mc_is_suicide(mc, pos, color))
401    return 0;
402
403  mc->board_ko_pos = NO_MOVE;
404 
405  mc->board[pos] = color;
406  mc->next_stone[pos] = pos;
407  mc->reference_stone[pos] = pos;
408  mc->first_liberty_edge[pos] = 0;
409
410  for (k = 0; k < 4; k++) {
411    int pos2 = pos + delta[k];
412    if (mc->board[pos2] == EMPTY) {
413      mc_add_liberty_edge(mc, pos, pos2, (k + 2) % 4);
414      num_direct_liberties++;
415    }
416  }
417 
418  for (k = 0; k < 4; k++) {
419    int pos2 = pos + delta[k];
420    if (mc->board[pos2] == OTHER_COLOR(color)) {
421      if (mc_remove_liberty_edge(mc, pos2, pos, k) == 0)
422        captured_stones += mc_remove_string(mc, pos2);
423    }
424    else if (mc->board[pos2] == color) {
425      if (mc->reference_stone[pos] != mc->reference_stone[pos2])
426        mc_join_strings(mc, pos, pos2);
427      mc_remove_liberty_edge(mc, pos2, pos, k);
428    }
429  }
430
431  if (captured_stones == 1
432      && mc->next_stone[pos] == pos
433      && num_direct_liberties == 0)
434    mc->board_ko_pos = mc->first_liberty_edge[pos] >> 2;
435 
436  return 1;
437}