1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2  * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see       *
3  * http://www.gnu.org/software/gnugo/ for more information.          *
4  *                                                                   *
5  * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,   *
6  * 2008 and 2009 by the Free Software Foundation.                    *
7  *                                                                   *
8  * This program is free software; you can redistribute it and/or     *
9  * modify it under the terms of the GNU General Public License as    *
10  * published by the Free Software Foundation - version 3 or          *
11  * (at your option) any later version.                               *
12  *                                                                   *
13  * This program is distributed in the hope that it will be useful,   *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of    *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     *
16  * GNU General Public License in file COPYING for more details.      *
17  *                                                                   *
18  * You should have received a copy of the GNU General Public         *
19  * License along with this program; if not, write to the Free        *
20  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,       *
21  * Boston, MA 02111, USA.                                            *
22 \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23 
24 #include "gnugo.h"
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 
29 #include "liberty.h"
30 
31 #define INFINITY 1000
32 
33 static void find_moves_to_make_seki(void);
34 static void update_status(int dr, enum dragon_status new_status,
35    			  enum dragon_status new_safety);
36 static int close_enough_for_proper_semeai(int apos, int bpos);
37 
38 /* semeai() searches for pairs of dragons of opposite color which
39  * have safety DEAD. If such a pair is found, owl_analyze_semeai is
40  * called to read out which dragon will prevail in a semeai, and
41  * whether a move now will make a difference in the outcome. The
42  * dragon statuses are revised, and if a move now will make a
43  * difference in the outcome this information is stored in
44  * dragon_data2 and an owl reason is later generated by
45  * semeai_move_reasons().
46  */
47 
48 #define MAX_DRAGONS 50
49 
50 void
semeai()51 semeai()
52 {
53   int semeai_results_first[MAX_DRAGONS][MAX_DRAGONS];
54   int semeai_results_second[MAX_DRAGONS][MAX_DRAGONS];
55   int semeai_move[MAX_DRAGONS][MAX_DRAGONS];
56   signed char semeai_certain[MAX_DRAGONS][MAX_DRAGONS];
57   int d1, d2;
58   int k;
59   int num_dragons = number_of_dragons;
60 
61   if (num_dragons > MAX_DRAGONS) {
62     TRACE("Too many dragons!!! Semeai analysis disabled.");
63     return;
64   }
65 
66   for (d1 = 0; d1 < num_dragons; d1++)
67     for (d2 = 0; d2 < num_dragons; d2++) {
68       semeai_results_first[d1][d2] = -1;
69       semeai_results_second[d1][d2] = -1;
70     }
71 
72   for (d1 = 0; d1 < num_dragons; d1++)
73     for (k = 0; k < dragon2[d1].neighbors; k++) {
74       int apos = DRAGON(d1).origin;
75       int bpos = DRAGON(dragon2[d1].adjacent[k]).origin;
76       int result_certain;
77 
78       d2 = dragon[bpos].id;
79 
80       /* Look for semeais */
81 
82       if (dragon[apos].color == dragon[bpos].color
83 	  || (dragon[apos].status != DEAD
84 	      && dragon[apos].status != CRITICAL)
85 	  || (dragon[bpos].status != DEAD
86 	      && dragon[bpos].status != CRITICAL))
87 	continue;
88 
89 
90       /* Ignore inessential worms or dragons */
91 
92       if (worm[apos].inessential
93 	  || DRAGON2(apos).safety == INESSENTIAL
94 	  || worm[bpos].inessential
95 	  || DRAGON2(bpos).safety == INESSENTIAL)
96 	continue;
97 
98       /* Sometimes the dragons are considered neighbors but are too
99        * distant to constitute a proper semeai, e.g. in nngs4:650, P2
100        * vs. R3. Then the result of semeai reading may be meaningless
101        * and can confuse the analysis. In order to avoid this we check
102        * that the dragons either are directly adjacent or at least
103        * have one common liberty.
104        */
105       if (!close_enough_for_proper_semeai(apos, bpos))
106 	continue;
107 
108       /* The array semeai_results_first[d1][d2] will contain the status
109        * of d1 after the d1 d2 semeai, giving d1 the first move.
110        * The array semeai_results_second[d1][d2] will contain the status
111        * of d1 after the d1 d2 semeai, giving d2 the first move.
112        */
113 
114       DEBUG(DEBUG_SEMEAI, "Considering semeai between %1m and %1m\n",
115 	    apos, bpos);
116       owl_analyze_semeai(apos, bpos,
117 			 &(semeai_results_first[d1][d2]),
118 			 &(semeai_results_second[d1][d2]),
119 			 &(semeai_move[d1][d2]), 1, &result_certain);
120       DEBUG(DEBUG_SEMEAI, "results if %s moves first: %s %s, %1m%s\n",
121 	    board[apos] == BLACK ? "black" : "white",
122 	    result_to_string(semeai_results_first[d1][d2]),
123 	    result_to_string(semeai_results_second[d1][d2]),
124 	    semeai_move[d1][d2], result_certain ? "" : " (uncertain)");
125       semeai_certain[d1][d2] = result_certain;
126     }
127 
128   /* Look for dragons which lose all their semeais outright. The
129    * winners in those semeais are considered safe and further semeais
130    * they are involved in are disregarded. See semeai:81-86 and
131    * nicklas5:1211 for examples of where this is useful.
132    *
133    * Note: To handle multiple simultaneous semeais properly we would
134    * have to make simultaneous semeai reading. Lacking that we can
135    * only get rough guesses of the correct status of the involved
136    * dragons. This code is not guaranteed to be correct in all
137    * situations but should usually be an improvement.
138    */
139   for (d1 = 0; d1 < num_dragons; d1++) {
140     int involved_in_semeai = 0;
141     int all_lost = 1;
142     for (d2 = 0; d2 < num_dragons; d2++) {
143       if (semeai_results_first[d1][d2] != -1) {
144 	involved_in_semeai = 1;
145 	if (semeai_results_first[d1][d2] != 0) {
146 	  all_lost = 0;
147 	  break;
148 	}
149       }
150     }
151 
152     if (involved_in_semeai && all_lost) {
153       /* Leave the status changes to the main loop below. Here we just
154        * remove the presumably irrelevant semeai results.
155        */
156       for (d2 = 0; d2 < num_dragons; d2++) {
157 	if (semeai_results_first[d1][d2] == 0) {
158 	  int d3;
159 	  for (d3 = 0; d3 < num_dragons; d3++) {
160 	    if (semeai_results_second[d3][d2] > 0) {
161 	      semeai_results_first[d3][d2] = -1;
162 	      semeai_results_second[d3][d2] = -1;
163 	      semeai_results_first[d2][d3] = -1;
164 	      semeai_results_second[d2][d3] = -1;
165 	    }
166 	  }
167 	}
168       }
169     }
170   }
171 
172   for (d1 = 0; d1 < num_dragons; d1++) {
173     int semeais_found = 0;
174     int best_defense = 0;
175     int best_attack = 0;
176     int defense_move = PASS_MOVE;
177     int attack_move = PASS_MOVE;
178     int defense_certain = -1;
179     int attack_certain = -1;
180     int semeai_attack_target = NO_MOVE;
181     int semeai_defense_target = NO_MOVE;
182 
183     for (d2 = 0; d2 < num_dragons; d2++) {
184       if (semeai_results_first[d1][d2] == -1)
185 	continue;
186       gg_assert(semeai_results_second[d1][d2] != -1);
187       semeais_found++;
188 
189       if (best_defense < semeai_results_first[d1][d2]
190 	  || (best_defense == semeai_results_first[d1][d2]
191 	      && defense_certain < semeai_certain[d1][d2])) {
192 	best_defense = semeai_results_first[d1][d2];
193 	defense_move = semeai_move[d1][d2];
194 	defense_certain = semeai_certain[d1][d2];
195 	gg_assert(board[dragon2[d2].origin] == OTHER_COLOR(board[dragon2[d1].origin]));
196 	semeai_defense_target = dragon2[d2].origin;
197       }
198       if (best_attack < semeai_results_second[d2][d1]
199 	  || (best_attack == semeai_results_second[d2][d1]
200 	      && attack_certain < semeai_certain[d2][d1])) {
201 	best_attack = semeai_results_second[d2][d1];
202 	attack_move = semeai_move[d2][d1];
203 	attack_certain = semeai_certain[d2][d1];
204 	semeai_attack_target = dragon2[d2].origin;
205       }
206     }
207 
208     if (semeais_found) {
209       dragon2[d1].semeais = semeais_found;
210       if (best_defense != 0 && best_attack != 0)
211 	update_status(DRAGON(d1).origin, CRITICAL, CRITICAL);
212       else if (best_attack == 0 && attack_certain)
213 	update_status(DRAGON(d1).origin, ALIVE, ALIVE);
214       dragon2[d1].semeai_defense_code = best_defense;
215       dragon2[d1].semeai_defense_point = defense_move;
216       dragon2[d1].semeai_defense_certain = defense_certain;
217       ASSERT1(board[semeai_defense_target]
218 	      == OTHER_COLOR(board[dragon2[d1].origin]),
219 	      dragon2[d1].origin);
220       dragon2[d1].semeai_defense_target = semeai_defense_target;
221       dragon2[d1].semeai_attack_code = best_attack;
222       dragon2[d1].semeai_attack_point = attack_move;
223       dragon2[d1].semeai_attack_certain = attack_certain;
224       dragon2[d1].semeai_attack_target = semeai_attack_target;
225     }
226   }
227   find_moves_to_make_seki();
228 }
229 
230 /* Find moves turning supposed territory into seki. This is not
231  * detected above since it either involves an ALIVE dragon adjacent to
232  * a CRITICAL dragon, or an ALIVE dragon whose eyespace can be invaded
233  * and turned into a seki.
234  *
235  * Currently we only search for tactically critical strings with
236  * dragon status dead, which are neighbors of only one opponent
237  * dragon, which is alive. Through semeai analysis we then determine
238  * whether such a string can in fact live in seki. Relevant testcases
239  * include gunnar:42 and gifu03:2.
240  */
241 static void
find_moves_to_make_seki()242 find_moves_to_make_seki()
243 {
244   int str;
245   int defend_move;
246   int resulta, resultb;
247 
248   for (str = BOARDMIN; str < BOARDMAX; str++) {
249     if (IS_STONE(board[str]) && is_worm_origin(str, str)
250 	&& attack_and_defend(str, NULL, NULL, NULL, &defend_move)
251 	&& dragon[str].status == DEAD
252 	&& DRAGON2(str).hostile_neighbors == 1) {
253       int k;
254       int color = board[str];
255       int opponent = NO_MOVE;
256       int certain;
257       struct eyevalue reduced_genus;
258 
259       for (k = 0; k < DRAGON2(str).neighbors; k++) {
260 	opponent = dragon2[DRAGON2(str).adjacent[k]].origin;
261 	if (board[opponent] != color)
262 	  break;
263       }
264 
265       ASSERT1(opponent != NO_MOVE, opponent);
266 
267       if (dragon[opponent].status != ALIVE)
268 	continue;
269 
270       /* FIXME: These heuristics are used for optimization.  We don't
271        *        want to call expensive semeai code if the opponent
272        *        dragon has more than one eye elsewhere.  However, the
273        *        heuristics might still need improvement.
274        */
275       compute_dragon_genus(opponent, &reduced_genus, str);
276 
277       if (min_eyes(&reduced_genus) > 1
278 	  || DRAGON2(opponent).moyo_size > 10
279 	  || DRAGON2(opponent).moyo_territorial_value > 2.999
280 	  || DRAGON2(opponent).escape_route > 0
281 	  || DRAGON2(str).escape_route > 0)
282 	continue;
283 
284       owl_analyze_semeai_after_move(defend_move, color, opponent, str,
285 				    &resulta, &resultb, NULL, 1, &certain, 0);
286 
287       if (resultb == WIN) {
288 	owl_analyze_semeai(str, opponent, &resultb, &resulta,
289 			   &defend_move, 1, &certain);
290 	resulta = REVERSE_RESULT(resulta);
291 	resultb = REVERSE_RESULT(resultb);
292       }
293 
294       /* Do not trust uncertain results. In fact it should only take a
295        * few nodes to determine the semeai result, if it is a proper
296        * potential seki position.
297        */
298       if (resultb != WIN && certain) {
299 	int d = dragon[str].id;
300 	DEBUG(DEBUG_SEMEAI, "Move to make seki at %1m (%1m vs %1m)\n",
301 	      defend_move, str, opponent);
302 	dragon2[d].semeais++;
303 	update_status(str, CRITICAL, CRITICAL);
304 	dragon2[d].semeai_defense_code = REVERSE_RESULT(resultb);
305 	dragon2[d].semeai_defense_point = defend_move;
306 	dragon2[d].semeai_defense_certain = certain;
307 	gg_assert(board[opponent] == OTHER_COLOR(board[dragon2[d].origin]));
308 	dragon2[d].semeai_defense_target = opponent;
309 
310 	/* We need to determine a proper attack move (the one that
311 	 * prevents seki).  Currently we try the defense move first,
312 	 * and if it doesn't work -- all liberties of the string.
313 	 */
314 	owl_analyze_semeai_after_move(defend_move, OTHER_COLOR(color),
315 				      str, opponent, &resulta, NULL,
316 				      NULL, 1, NULL, 0);
317 	if (resulta != WIN) {
318 	  dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
319 	  dragon2[d].semeai_attack_point = defend_move;
320 	}
321 	else {
322 	  int k;
323 	  int libs[MAXLIBS];
324 	  int liberties = findlib(str, MAXLIBS, libs);
325 
326 	  for (k = 0; k < liberties; k++) {
327 	    owl_analyze_semeai_after_move(libs[k], OTHER_COLOR(color),
328 					  str, opponent, &resulta, NULL,
329 					  NULL, 1, NULL, 0);
330 	    if (resulta != WIN) {
331 	      dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
332 	      dragon2[d].semeai_attack_point = libs[k];
333 	      break;
334 	    }
335 	  }
336 
337 	  if (k == liberties) {
338 	    DEBUG(DEBUG_SEMEAI,
339 		  "No move to attack in semeai (%1m vs %1m), seki assumed.\n",
340 		  str, opponent);
341 	    dragon2[d].semeai_attack_code = 0;
342 	    dragon2[d].semeai_attack_point = NO_MOVE;
343 	    update_status(str, ALIVE, ALIVE_IN_SEKI);
344 	  }
345 	}
346 
347 	DEBUG(DEBUG_SEMEAI, "Move to prevent seki at %1m (%1m vs %1m)\n",
348 	      dragon2[d].semeai_attack_point, opponent, str);
349 
350 	dragon2[d].semeai_attack_certain = certain;
351 	dragon2[d].semeai_attack_target = opponent;
352       }
353     }
354   }
355 
356   /* Now look for dead strings inside a single eyespace of a living dragon.
357    *
358    * FIXME: Clearly this loop should share most of its code with the
359    *        one above. It would also be good to reimplement so that
360    *        moves invading a previously empty single eyespace to make
361    *        seki can be found.
362    */
363   for (str = BOARDMIN; str < BOARDMAX; str++) {
364     if (IS_STONE(board[str]) && is_worm_origin(str, str)
365 	&& !find_defense(str, NULL)
366 	&& dragon[str].status == DEAD
367 	&& DRAGON2(str).hostile_neighbors == 1) {
368       int k;
369       int color = board[str];
370       int opponent = NO_MOVE;
371       int certain;
372       struct eyevalue reduced_genus;
373 
374       for (k = 0; k < DRAGON2(str).neighbors; k++) {
375 	opponent = dragon2[DRAGON2(str).adjacent[k]].origin;
376 	if (board[opponent] != color)
377 	  break;
378       }
379 
380       ASSERT1(opponent != NO_MOVE, opponent);
381 
382       if (dragon[opponent].status != ALIVE)
383 	continue;
384 
385       /* FIXME: These heuristics are used for optimization.  We don't
386        *        want to call expensive semeai code if the opponent
387        *        dragon has more than one eye elsewhere.  However, the
388        *        heuristics might still need improvement.
389        */
390       compute_dragon_genus(opponent, &reduced_genus, str);
391       if (DRAGON2(opponent).moyo_size > 10 || min_eyes(&reduced_genus) > 1)
392 	continue;
393 
394       owl_analyze_semeai(str, opponent, &resulta, &resultb,
395 			 &defend_move, 1, &certain);
396 
397       /* Do not trust uncertain results. In fact it should only take a
398        * few nodes to determine the semeai result, if it is a proper
399        * potential seki position.
400        */
401       if (resulta != 0 && certain) {
402 	int d = dragon[str].id;
403 	DEBUG(DEBUG_SEMEAI, "Move to make seki at %1m (%1m vs %1m)\n",
404 	      defend_move, str, opponent);
405 	dragon2[d].semeais++;
406 	update_status(str, CRITICAL, CRITICAL);
407 	dragon2[d].semeai_defense_code = resulta;
408 	dragon2[d].semeai_defense_point = defend_move;
409 	dragon2[d].semeai_defense_certain = certain;
410 	gg_assert(board[opponent] == OTHER_COLOR(board[dragon2[d].origin]));
411 	dragon2[d].semeai_defense_target = opponent;
412 
413 	/* We need to determine a proper attack move (the one that
414 	 * prevents seki).  Currently we try the defense move first,
415 	 * and if it doesn't work -- all liberties of the string.
416 	 */
417 	owl_analyze_semeai_after_move(defend_move, OTHER_COLOR(color),
418 				      str, opponent, &resulta, NULL,
419 				      NULL, 1, NULL, 0);
420 	if (resulta != WIN) {
421 	  dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
422 	  dragon2[d].semeai_attack_point = defend_move;
423 	}
424 	else {
425 	  int k;
426 	  int libs[MAXLIBS];
427 	  int liberties = findlib(str, MAXLIBS, libs);
428 
429 	  for (k = 0; k < liberties; k++) {
430 	    owl_analyze_semeai_after_move(libs[k], OTHER_COLOR(color),
431 					  str, opponent, &resulta, NULL,
432 					  NULL, 1, NULL, 0);
433 	    if (resulta != WIN) {
434 	      dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta);
435 	      dragon2[d].semeai_attack_point = libs[k];
436 	      break;
437 	    }
438 	  }
439 
440 	  if (k == liberties) {
441 	    DEBUG(DEBUG_SEMEAI,
442 		  "No move to attack in semeai (%1m vs %1m), seki assumed.\n",
443 		  str, opponent);
444 	    dragon2[d].semeai_attack_code = 0;
445 	    dragon2[d].semeai_attack_point = NO_MOVE;
446 	    update_status(str, ALIVE, ALIVE_IN_SEKI);
447 	  }
448 	}
449 
450 	DEBUG(DEBUG_SEMEAI, "Move to prevent seki at %1m (%1m vs %1m)\n",
451 	      dragon2[d].semeai_attack_point, opponent, str);
452 
453 	dragon2[d].semeai_attack_certain = certain;
454 	dragon2[d].semeai_attack_target = opponent;
455       }
456     }
457   }
458 }
459 
460 
461 /* neighbor_of_dragon(pos, origin) returns true if the vertex at (pos) is a
462  * neighbor of the dragon with origin at (origin).
463  */
464 static int
neighbor_of_dragon(int pos,int origin)465 neighbor_of_dragon(int pos, int origin)
466 {
467   int k;
468   if (pos == NO_MOVE)
469     return 0;
470 
471   for (k = 0; k < 4; k++)
472     if (ON_BOARD(pos + delta[k]) && dragon[pos + delta[k]].origin == origin)
473       return 1;
474 
475   return 0;
476 }
477 
478 /* Check whether two dragons are directly adjacent or have at least
479  * one common liberty.
480  */
481 static int
close_enough_for_proper_semeai(int apos,int bpos)482 close_enough_for_proper_semeai(int apos, int bpos)
483 {
484   int pos;
485   for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
486     if (board[pos] == EMPTY
487 	&& neighbor_of_dragon(pos, apos)
488 	&& neighbor_of_dragon(pos, bpos))
489       return 1;
490     else if (IS_STONE(board[pos])) {
491       if (is_same_dragon(pos, apos) && neighbor_of_dragon(pos, bpos))
492 	return 1;
493       if (is_same_dragon(pos, bpos) && neighbor_of_dragon(pos, apos))
494 	return 1;
495     }
496   }
497 
498   return 0;
499 }
500 
501 /* This function adds the semeai related move reasons, using the information
502  * stored in the dragon2 array.
503  *
504  * If the semeai had an uncertain result, and there is a owl move with
505  * certain result doing the same, we don't trust the semeai move.
506  */
507 void
semeai_move_reasons(int color)508 semeai_move_reasons(int color)
509 {
510   int other = OTHER_COLOR(color);
511   int d;
512   int liberties;
513   int libs[MAXLIBS];
514   int r;
515 
516   for (d = 0; d < number_of_dragons; d++)
517     if (dragon2[d].semeais && DRAGON(d).status == CRITICAL) {
518       if (DRAGON(d).color == color
519           && dragon2[d].semeai_defense_point
520 	  && (dragon2[d].owl_defense_point == NO_MOVE
521 	      || dragon2[d].semeai_defense_certain >=
522 	         dragon2[d].owl_defense_certain)) {
523 	/* My dragon can be defended. */
524 	add_semeai_move(dragon2[d].semeai_defense_point, dragon2[d].origin);
525 	DEBUG(DEBUG_SEMEAI, "Adding semeai defense move for %1m at %1m\n",
526 	      DRAGON(d).origin, dragon2[d].semeai_defense_point);
527 	if (neighbor_of_dragon(dragon2[d].semeai_defense_point,
528 			       dragon2[d].semeai_defense_target)
529 	    && !neighbor_of_dragon(dragon2[d].semeai_defense_point,
530 				   dragon2[d].origin)
531 	    && !is_self_atari(dragon2[d].semeai_defense_point, color)) {
532 
533 	  /* If this is a move to fill the non-common liberties of the
534 	   * target, and is not a ko or snap-back, then we mark all
535 	   * non-common liberties of the target as potential semeai moves.
536 	   */
537 
538           liberties = findlib(dragon2[d].semeai_defense_target, MAXLIBS, libs);
539 
540           for (r = 0; r < liberties; r++) {
541             if (!neighbor_of_dragon(libs[r], dragon2[d].origin)
542 		&& !is_self_atari(libs[r], color)
543 		&& libs[r] != dragon2[d].semeai_defense_point)
544 	      add_potential_semeai_defense(libs[r], dragon2[d].origin,
545 					   dragon2[d].semeai_defense_target);
546 	  }
547 	}
548       }
549       else if (DRAGON(d).color == other
550 	       && dragon2[d].semeai_attack_point
551 	       && (dragon2[d].owl_attack_point == NO_MOVE
552 		   || dragon2[d].owl_defense_point == NO_MOVE
553 		   || dragon2[d].semeai_attack_certain >=
554 		      dragon2[d].owl_attack_certain)) {
555 	/* Your dragon can be attacked. */
556 	add_semeai_move(dragon2[d].semeai_attack_point, dragon2[d].origin);
557 	DEBUG(DEBUG_SEMEAI, "Adding semeai attack move for %1m at %1m\n",
558 	      DRAGON(d).origin, dragon2[d].semeai_attack_point);
559 	if (neighbor_of_dragon(dragon2[d].semeai_attack_point,
560 			       dragon2[d].origin)
561 	    && !neighbor_of_dragon(dragon2[d].semeai_attack_point,
562 				  dragon2[d].semeai_attack_target)
563 	    && !is_self_atari(dragon2[d].semeai_attack_point, color)) {
564 
565           liberties = findlib(dragon2[d].origin, MAXLIBS, libs);
566 
567           for (r = 0; r < liberties; r++) {
568             if (!neighbor_of_dragon(libs[r], dragon2[d].semeai_attack_target)
569 		&& !is_self_atari(libs[r], color)
570 		&& libs[r] != dragon2[d].semeai_attack_point)
571 	      add_potential_semeai_attack(libs[r], dragon2[d].origin,
572 					  dragon2[d].semeai_attack_target);
573 	  }
574 	}
575       }
576     }
577 }
578 
579 
580 /* Change the status and safety of a dragon.  In addition, if the new
581  * status is not DEAD, make all worms of the dragon essential, so that
582  * results found by semeai code don't get ignored.
583  */
584 static void
update_status(int dr,enum dragon_status new_status,enum dragon_status new_safety)585 update_status(int dr, enum dragon_status new_status,
586     	      enum dragon_status new_safety)
587 {
588   int pos;
589 
590   if (dragon[dr].status != new_status
591       && (dragon[dr].status != CRITICAL || new_status != DEAD)) {
592     DEBUG(DEBUG_SEMEAI, "Changing status of %1m from %s to %s.\n", dr,
593 	  status_to_string(dragon[dr].status),
594 	  status_to_string(new_status));
595     for (pos = BOARDMIN; pos < BOARDMAX; pos++)
596       if (IS_STONE(board[pos]) && is_same_dragon(dr, pos)) {
597 	dragon[pos].status = new_status;
598 	if (new_status != DEAD)
599 	  worm[pos].inessential = 0;
600       }
601   }
602 
603   if (DRAGON2(dr).safety != new_safety
604       && (DRAGON2(dr).safety != CRITICAL || new_safety != DEAD)) {
605     DEBUG(DEBUG_SEMEAI, "Changing safety of %1m from %s to %s.\n", dr,
606 	  status_to_string(DRAGON2(dr).safety), status_to_string(new_safety));
607     DRAGON2(dr).safety = new_safety;
608   }
609 }
610 
611 
612 /*
613  * Local Variables:
614  * tab-width: 8
615  * c-basic-offset: 2
616  * End:
617  */
618