File: endgame.c

package info (click to toggle)
gnugo 3.8-4
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 17,312 kB
  • ctags: 4,228
  • sloc: ansic: 56,439; perl: 3,771; lisp: 2,789; sh: 730; makefile: 700; python: 682; awk: 113; sed: 22
file content (527 lines) | stat: -rw-r--r-- 16,928 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see       *
 * http://www.gnu.org/software/gnugo/ for more information.          *
 *                                                                   *
 * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,   *
 * 2008 and 2009 by the Free Software Foundation.                    *
 *                                                                   *
 * This program is free software; you can redistribute it and/or     *
 * modify it under the terms of the GNU General Public License as    *
 * published by the Free Software Foundation - version 3 or          *
 * (at your option) any later version.                               *
 *                                                                   *
 * This program is distributed in the hope that it will be useful,   *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of    *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     *
 * GNU General Public License in file COPYING for more details.      *
 *                                                                   *
 * You should have received a copy of the GNU General Public         *
 * License along with this program; if not, write to the Free        *
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,       *
 * Boston, MA 02111, USA.                                            *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


#include "gnugo.h"
#include "liberty.h"


static void endgame_analyze_worm_liberties(int pos, int color);
static void endgame_find_backfilling_dame(int str, int color);
static int endgame_find_liberties(int str, int *essential_liberties,
				  int essential_libs[MAXLIBS],
				  int *inessential_liberties,
				  int inessential_libs[MAXLIBS],
				  int *false_eye_liberties,
				  int false_eye_libs[MAXLIBS]);
  

/* Generate endgame moves. These are typically moves in settled positions,
 * they aren't worth many points. Currently, we generate such moves using
 * patterns in endgames.db and this algorithmic move generator. It is only
 * called when no move of value higher than 6.0 has been found on board.
 */
void
endgame(int color)
{
  int pos;

  TRACE("\nEndgame move generator tries to look for additional moves...\n");

  /* Try to generate some moves using endgame_analyze_worm_liberties(). See
   * the description of that function to find what moves it generates.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    /* We are only interested in alive, but not invincible worms which are
     * parts of alive dragons. That is, the position must be stable.
     */
    if (IS_STONE(board[pos])
	&& worm[pos].origin == pos
	&& dragon[pos].status == ALIVE
	&& !worm[pos].invincible
	&& !worm[pos].inessential
	&& worm[pos].attack_codes[0] == 0) {
      endgame_analyze_worm_liberties(pos, color);
      endgame_find_backfilling_dame(pos, color);
    }
  }
}


/* This function handles two cases of endgame moves. Consider these two
 * positions (from endgame:301,302 and endgame:801,802 respectively):
 *
 *   OOOOOOO		XXXXXO.|
 *   O.XXX.O		X.O.O*.|
 *   OOX.XXO		X.OOOX.|
 *   .O*X.OX		XXXXOX.|
 *   .OXX..X		X..XOOO|
 *   .OOOXX.		XXXXXXX|
 *
 * The two marked with `*' moves are worth one point in gote each (for
 * both colors). The first one is obvious - once black runs short on
 * liberties, he'll have to defend in his own eyespace, wasting one
 * point. In the second position, although black sacrifices one point
 * by playing in white's territory, he forces white to eventually
 * capture the black string, losing three points. However, white has
 * to play at `*' sooner or later if black doesn't take that vertex, so
 * the move is worth 3 - 1 - 1 = 1 point only, not two.
 *
 * This function is able to find such moves. The algorithm is based on
 * finding so called "inessential liberties". These are defined as
 * liberties, which satisfy five conditions:
 *
 *	1) they are not within an eye (not in someone's territory),
 *	2) all their adjacent worms and dragons are alive,
 *	3) they have adjacent worms of both colors,
 *	4) they have no other adjacent worms of the same color as the worm
 *	   under consideration,
 *	5) they are safe to fill with stones of other than the worm's color.
 *
 * Such liberties are supposed to never become territory (they can't become
 * an additional eye for the worm under consideration), the worm cannot
 * connect to something via such a liberty and they will (or at least can)
 * eventually be filled by either of the players.
 *
 * FIXME: This function can probably be improved to handle more cases.
 */
static void
endgame_analyze_worm_liberties(int pos, int color)
{
  int k;
  int worm_color = board[pos];
  int other = OTHER_COLOR(worm_color);
  int essential_liberties;
  int essential_libs[MAXLIBS];
  int inessential_liberties;
  int inessential_libs[MAXLIBS];
  int false_eye_liberties;
  int false_eye_libs[MAXLIBS];
  int num_attacks;
  int num_attacks2;
  int attacks[MAXLIBS];
  int defenses[MAXLIBS];
  int apos;
  int value;

  if (!endgame_find_liberties(pos, &essential_liberties, essential_libs,
			      &inessential_liberties, inessential_libs,
			      &false_eye_liberties, false_eye_libs))
    return;
  
  apos = NO_MOVE;
  num_attacks = 0;

  /* Now, try to predict the final state of the position. We fill all
   * inessential liberties by stones of other than the current worm's
   * color. This is just a guess, we'll have to check the results later.
   */
  for (k = 0; k < inessential_liberties; k++) {
    if (!safe_move(inessential_libs[k], other)
	|| !trymove(inessential_libs[k], other, "endgame", pos))
      break;
  }

  /* If we haven't eaten the worm accidentally, look if any attacks on the
   * worm have appeared.
   */
  if (k == inessential_liberties && board[pos] != EMPTY) {
    /* Try to look for moves as in position 1. If the worm still has
     * more than one liberty, try to play on every essential liberty
     * and see if an attack appears.
     */
    if (countlib(pos) > 1) {
      for (k = 0; k < essential_liberties; k++) {
	int lib = essential_libs[k];

	if (safe_move(lib, worm_color) && safe_move(lib, other)
	    && trymove(lib, other, "endgame", pos)) {
	  if (attack(pos, NULL) != 0) {
	    int dpos;

	    if (find_defense(pos, &dpos) && is_proper_eye_space(dpos)) {
	      int i;

	      /* If the attack cannot be defended against by playing on
	       * another essential liberty, filling a pure false eye (an
	       * eye which can't become territory) or capturing an opponent
	       * string in atari, keep it for now.
	       */
	      for (i = 0; i < essential_liberties; i++) {
		if (i != k && essential_libs[i] != dpos
		    && does_defend(essential_libs[i], pos))
		  break;
	      }

	      if (i == essential_liberties) {
		for (i = 0; i < false_eye_liberties; i++) {
		  if (does_defend(false_eye_libs[i], pos))
		    break;
		}

		if (i == false_eye_liberties) {
		  int adj[MAXCHAIN];
		  int adjs;

		  adjs = chainlinks2(pos, adj, 1);
		  for (i = 0; i < adjs; i++) {
		    int lib2;
		    findlib(adj[i], 1, &lib2);
		    if (lib2 != dpos && !is_proper_eye_space(lib2)
			&& does_defend(lib2, pos))
		      break;
		  }

		  if (i == adjs) {
		    attacks[num_attacks] = lib;
		    defenses[num_attacks] = dpos;
		    num_attacks++;
		  }
		}
	      }
	    }
	  }

	  popgo();
	}
      }
    }
    else if (essential_liberties > 0) {
      /* If the only remaining liberty is essential, it is an attack. */
      attacks[num_attacks] = essential_libs[0];
      defenses[num_attacks] = NO_MOVE;
      num_attacks++;
    }

    /* Try to find moves as in position 2. */
    if (attack(pos, &apos) != 0) {
      if (is_proper_eye_space(apos)) {
	/* The attack point is in someone's eye (must be an eye which the worm
	 * bounds). This looks promising. If this attack cannot be averted by
	 * playing on an essential liberty, keep it for further analyzis.
	 */
	for (k = 0; k < essential_liberties; k++) {
	  if (does_defend(essential_libs[k], pos)) {
	    apos = NO_MOVE;
	    break;
	  }
	}

	if (apos != NO_MOVE && worm_color == color && !does_defend(apos, pos))
	  apos = NO_MOVE;
      }
      else
	apos = NO_MOVE;
    }
  }
  else {
    /* We were unable to fill all the liberties. Modify
     * `inessential_liberties' in order to undo the right number of
     * moves.
     */
    inessential_liberties = k;
  }

  /* Undo all the moves made to fill inessential liberties. */
  for (k = 0; k < inessential_liberties; k++)
    popgo();
  ASSERT1(stackp == 0, pos);

  num_attacks2 = 0;
  for (k = 0; k < num_attacks; k++) {
    /* These moves must be safe for the other color, otherwise they are
     * pointless. Note that checks for safety on previous step were not
     * sufficient since we had additional stones on board then.
     */
    if (safe_move(attacks[k], other)) {
      if (defenses[k] != NO_MOVE) {
	int i;

	/* Consider this position:
	 *
	 *   .X...OO	The move at `*' satisfies the conditions above.
	 *   .X*OO.O	However, it is pointless, since black has a miai
	 *   X.OX..O	move at `a' to force white to play `b'. That is,
	 *   XXObOOO	no matter if white plays `*' or `a', black takes
	 *   .XXaOXO	the other point and white has to fill `b'. So, if
	 *   ...XXXX	there is a point, adjacent to defense point, safe
	 *		for "other" color, we discard the attack.
	 *
	 * Also, in some positions, defense point is adjacent to worm
	 * inessential liberty. In such cases we discard the attack too.
	 */
	for (i = 0; i < 4; i++) {
	  int pos2 = defenses[k] + delta[i];

	  if (board[pos2] == EMPTY) {
	    int m;

	    if (!is_proper_eye_space(pos2) && safe_move(pos2, other))
	      break;

	    for (m = 0; m < inessential_liberties; m++) {
	      if (inessential_libs[m] == pos2)
		break;
	    }

	    if (m < inessential_liberties)
	      break;
	  }
	}

	/* If this is not the case, the attack is kept for the final trial. */
	if (i == 4)
	  attacks[num_attacks2++] = attacks[k];
      }
      else {
	/* This must be the only attack (filling all inessential liberties
	 * gives an atari).
	 */
	ASSERT1(num_attacks == 1, pos);
	attacks[num_attacks2++] = attacks[k];
      }
    }
  }

  value = 0;
  if (apos != NO_MOVE) {
    /* We use the number of string's liberties minus 2 as the value of
     * the move. Minus 2 is explained in the comment before the
     * function. In some rare cases the value may differ, but this
     * should be a good guess.
     */
    value = accuratelib(apos, other, MAXLIBS, NULL) - 2;
  }

  /* If we haven't found anything interesting or have already dropped it,
   * there is no point trying more moves, so we return now.
   */
  if (value <= 0 && num_attacks2 == 0)
    return;

  /* We filled the liberties with stones of "other" color. That could lead to
   * some strange attacks, since inessential liberties are not always really
   * inessential (see trevorb:320 and trevorb:940 for examples where this step
   * is necessary). Now we fill the liberties with stones of the same color as
   * the current worm. If the results remain unchanged, then we can probably
   * trust them.
   */
  for (k = 0; k < inessential_liberties; k++) {
    if (!trymove(inessential_libs[k], worm_color, "endgame", pos))
      break;
  }

  /* GNU Go currently doesn't allow suicide, but let's assume it does. */
  if (k == inessential_liberties && board[pos] != EMPTY) {
    if (countlib(pos) > 1) {
      for (k = 0; k < num_attacks2; k++) {
	if (trymove(attacks[k], other, "endgame", pos)) {
	  if (attack(pos, NULL) != 0) {
	    TRACE("  endgame move with territorial value %d.0 found at %1m\n",
		  1, attacks[k]);
	    add_expand_territory_move(attacks[k]);
	    /* FIXME: We just guess the value here. Find a way to calculate it
	     *	      (more) precisely.
	     */
	    set_minimum_territorial_value(attacks[k], 1.0);
	  }

	  popgo();
	}
      }
    }
    else if (essential_liberties > 0  && essential_libs[0] == attacks[0]) {
      TRACE("  endgame move with territorial value %d.0 found at %1m\n",
	    1, attacks[k]);
      add_expand_territory_move(attacks[0]);
      /* FIXME: We just guess the value here. Find a way to calculate it
       *	(more) precisely.
       */
      set_minimum_territorial_value(attacks[0], 1.0);
    }

    if (value > 0 && does_attack(apos, pos)) {
      TRACE("  endgame move with territorial value %d.0 found at %1m\n",
	    value, apos);
      add_expand_territory_move(apos);
      set_minimum_territorial_value(apos, (float) value);
    }
  }
  else {
    /* Don't undo moves we didn't play. */
    inessential_liberties = k;
  }

  /* Undo all the moves made at the third step. */
  for (k = 0; k < inessential_liberties; k++)
    popgo();
  ASSERT1(stackp == 0, pos);
}

/* A backfilling dame is a defense move, usually within potential own
 * territory, which does not have to be played immediately but after
 * outer liberties of some string have been filled. If those outer
 * liberties are dame points (here inessential liberties), it is
 * usually better to play the backfilling moves before filling the
 * dame points. If nothing else it reduces the risk for making stupid
 * blunders while filling dame.
 */
static void
endgame_find_backfilling_dame(int str, int color_to_move)
{
  int k;
  int color = board[str];
  int other = OTHER_COLOR(color);
  int essential_liberties;
  int essential_libs[MAXLIBS];
  int inessential_liberties;
  int inessential_libs[MAXLIBS];
  int false_eye_liberties;
  int false_eye_libs[MAXLIBS];
  int dpos;
  int loop_again = 1;
  int potential_moves[BOARDMAX];
  int num_potential_moves = 0;
  int move = NO_MOVE;

  while (loop_again) {
    loop_again = 0;
    if (!endgame_find_liberties(str, &essential_liberties, essential_libs,
				&inessential_liberties, inessential_libs,
				&false_eye_liberties, false_eye_libs))
      break;
    for (k = 0; k < inessential_liberties; k++) {
      if (!safe_move(inessential_libs[k], other)
	  || !trymove(inessential_libs[k], other, "endgame", str))
	continue;
      increase_depth_values();
      if (board[str] == EMPTY)
	break;
      if (attack_and_defend(str, NULL, NULL, NULL, &dpos)) {
	if (worm[dpos].color == EMPTY) {
	  potential_moves[num_potential_moves] = dpos;
	  num_potential_moves++;
	}
	forced_backfilling_moves[dpos] = 1;
	if (trymove(dpos, color, "endgame", str))
	  increase_depth_values();
	loop_again = 1;
	break;
      }
    }
  }
  
  while (stackp > 0) {
    popgo();
    decrease_depth_values();
  }

  for (k = num_potential_moves - 1; k >= 0; k--)
    if (safe_move(potential_moves[k], color)) {
      move = potential_moves[k];
      TRACE("  backfilling dame found at %1m for string %1m\n", move, str);
      if (color == color_to_move) {
	add_expand_territory_move(move);
	set_minimum_territorial_value(move, 0.1);
      }
      break;
    }
}

/* Find liberties of the string str with various characteristics. See
 * the comments above endgame_analyze_worm_liberties() for more
 * information.
 */
static int
endgame_find_liberties(int str,
		       int *essential_liberties, int essential_libs[MAXLIBS],
		       int *inessential_liberties,
		       int inessential_libs[MAXLIBS],
		       int *false_eye_liberties, int false_eye_libs[MAXLIBS])
{
  int liberties;
  int libs[MAXLIBS];
  int k;

  ASSERT1(IS_STONE(board[str]), str);

  *essential_liberties = 0;
  *inessential_liberties = 0;
  *false_eye_liberties = 0;
  
  /* Find all string liberties. */
  liberties = findlib(str, MAXLIBS, libs);

  /* Loop over the liberties and find inessential and essential ones. The
   * latter are defined as those, which are not inside an eye space, but
   * don't otherwise qualify as inessential. If we find a non-alive (dead
   * or critical) worm or dragon around, we stop looking for liberties and
   * skip the current worm (position is unstable).
   */
  for (k = 0; k < liberties; k++) {
    int lib = libs[k];

    if (!is_proper_eye_space(lib)) {
      int i;
      int essential = 0;
      int found_other = 0;

      for (i = 0; i < 4; i++) {
	int pos = lib + delta[i];

	if (!IS_STONE(board[pos]) || !IS_STONE(worm[pos].color))
	  continue;

	if (worm[pos].attack_codes[0] != 0 || dragon[pos].status != ALIVE)
	  return 0;

	if (board[pos] == board[str]) {
	  if (find_origin(pos) != find_origin(str))
	    essential = 1;
	}
	else
	  found_other = 1;
      }

      if (i < 4)
	break;

      if (found_other) {
	if (essential)
	  essential_libs[(*essential_liberties)++] = lib;
	else
	  inessential_libs[(*inessential_liberties)++] = lib;
      }
      else if (is_false_eye(half_eye, lib) && !false_eye_territory[lib])
	false_eye_libs[(*false_eye_liberties)++] = lib;
    }
  }
  return 1;
}

/*
 * Local Variables:
 * tab-width: 8
 * c-basic-offset: 2
 * End:
 */