gnugo-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnugo-devel] arend_1_18.1: next attempt to bring order to move_reasons


From: Arend Bayer
Subject: [gnugo-devel] arend_1_18.1: next attempt to bring order to move_reasons (supercedes arend_1_17.1)
Date: Mon, 17 Dec 2001 00:50:10 +0100 (CET)

- new scheme to filter out redundant or inessential move reasons

This is an extended and corrected version of arend_1_17.1. There is
actually more new added code than deleted old one, but I still believe
it makes the main functions estimate_territorial_value and
estimate_strategical_value more readable. I think what my patch does is
most quickly summarized by (discard_rules) reproduced below.

This patch should not at all affect GNU Go's behaviour. Of course there
are more rules that can be added to discard_rules, both some more to
carry over from the current code, and of course some new ones.

-Arend

TERRITORIALLY_REDUNDANT means the reasons is skipped in
estimate_territorial_value, similar STRATEGICALLY_REDUNDANT (unused
at the moment), and REDUNDANT sets both.

/* This array lists rules according to which we set the status
 * flags of a move reasons.
 * The format is:
 * { List of reasons to which the rule applies, condition of the rule,
 * flags to be set, trace message }
 */

static struct discard_rule discard_rules[] =
{
  { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO,
      ATTACK_MOVE_BAD_KO, ATTACK_THREAT_MOVE,
      DEFEND_MOVE, DEFEND_MOVE_GOOD_KO,
      DEFEND_MOVE_BAD_KO, DEFEND_THREAT_MOVE, -1 },
    owl_move_vs_worm_known, TERRITORY_REDUNDANT,
    "  %1m: 0.0 - (threat of) attack/defense of %1m (owl attack/defense as 
well)\n" },
  { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
    owl_move_reason_known, REDUNDANT,
    "  %1m: 0.0 - (threat to) win semai involving %1m (owl move as well)\n"},
  { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
    tactical_move_vs_whole_dragon_known, REDUNDANT,
    "  %1m: 0.0 - (threat to) win semai involving %1m (tactical move as 
well)\n"},
  { { ATTACK_EITHER_MOVE, DEFEND_BOTH_MOVE, -1 },
    tactical_move_vs_either_worm_known, REDUNDANT,
    "  %1m: 0.0 - att. either/def. both involving %1m (direct att./def. as 
well)\n"},
  { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO,
      ATTACK_MOVE_BAD_KO, ATTACK_THREAT_MOVE,
      DEFEND_MOVE, DEFEND_MOVE_GOOD_KO,
      DEFEND_MOVE_BAD_KO, DEFEND_THREAT_MOVE, -1 },
    concerns_inessential_worm, TERRITORY_REDUNDANT,
    "  %1m: 0.0 - attack/defense of %1m (inessential)\n"},
  { { OWL_ATTACK_MOVE, OWL_ATTACK_MOVE_GOOD_KO,
      OWL_ATTACK_MOVE_BAD_KO, OWL_ATTACK_THREAT,
      OWL_DEFEND_MOVE, OWL_DEFEND_MOVE_GOOD_KO,
      OWL_DEFEND_MOVE_BAD_KO, UNCERTAIN_OWL_DEFENSE, -1 },
    concerns_inessential_dragon, REDUNDANT,
    "  %1m: 0.0 - (uncertain) owl attack/defense of %1m (inessential)\n"},
  { { -1 }, NULL, 0, ""}  /* Keep this entry at end of the list. */
};

Index: engine/move_reasons.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/move_reasons.c,v
retrieving revision 1.48
diff -u -r1.48 move_reasons.c
--- engine/move_reasons.c       15 Dec 2001 14:35:26 -0000      1.48
+++ engine/move_reasons.c       16 Dec 2001 23:47:37 -0000
@@ -80,6 +80,16 @@
 /* Point redistribution */
 static int replacement_map[BOARDMAX];
 
+/* Helper functions to check conditions in discard rules. */
+typedef int (*discard_condition_fn_ptr)(int pos, int what);
+
+struct discard_rule {
+  int reason_type[40];
+  discard_condition_fn_ptr condition;
+  int flags;
+  char trace_message[160];
+};
+
 /* Initialize move reason data structures. */
 void
 clear_move_reasons(void)
@@ -135,7 +145,7 @@
 
 /*
  * Find the index of a worm in the list of worms. If necessary,
- * add a new entry. (ai, aj) must point to the origin of the worm.
+ * add a new entry. (str) must point to the origin of the worm.
  */
 static int
 find_worm(int str)
@@ -156,7 +166,7 @@
 
 /*
  * Find the index of a dragon in the list of dragons. If necessary,
- * add a new entry. (ai, aj) must point to the origin of the dragon.
+ * add a new entry. (str) must point to the origin of the dragon.
  */
 static int
 find_dragon(int str)
@@ -253,6 +263,60 @@
   return next_eye - 1;
 }
 
+/* Interprets the object of a reason and returns its position.
+ * If the object is a pair (of worms or dragons), the position of the first
+ * object is returned. (This is only used for trace outputs.) Returns
+ * -1 if move does not point to a location.
+ */
+static int
+get_pos(int reason, int what)
+{
+  switch (reason) {
+  case ATTACK_MOVE:
+  case DEFEND_MOVE:
+  case ATTACK_THREAT_MOVE:
+  case DEFEND_THREAT_MOVE:
+  case ATTACK_MOVE_GOOD_KO:
+  case ATTACK_MOVE_BAD_KO:
+  case DEFEND_MOVE_GOOD_KO:
+  case DEFEND_MOVE_BAD_KO:
+    return worms[what];
+  case SEMEAI_MOVE:
+  case SEMEAI_THREAT:
+  case VITAL_EYE_MOVE:
+  case STRATEGIC_ATTACK_MOVE:
+  case STRATEGIC_DEFEND_MOVE:
+  case OWL_ATTACK_MOVE:
+  case OWL_DEFEND_MOVE:
+  case OWL_ATTACK_THREAT:
+  case OWL_DEFENSE_THREAT:
+  case OWL_PREVENT_THREAT:
+  case UNCERTAIN_OWL_ATTACK:
+  case UNCERTAIN_OWL_DEFENSE:
+  case OWL_ATTACK_MOVE_GOOD_KO:
+  case OWL_ATTACK_MOVE_BAD_KO:
+  case OWL_DEFEND_MOVE_GOOD_KO:
+  case OWL_DEFEND_MOVE_BAD_KO:
+    return dragons[what];
+  case ATTACK_EITHER_MOVE:
+  case DEFEND_BOTH_MOVE:
+    return worms[worm_pair1[what]];
+  case CONNECT_MOVE:
+  case CUT_MOVE:
+    return dragons[conn_dragon1[what]];
+  case ANTISUJI_MOVE:
+  case BLOCK_TERRITORY_MOVE:
+  case EXPAND_TERRITORY_MOVE:
+  case EXPAND_MOYO_MOVE:
+  case MY_ATARI_ATARI_MOVE:
+  case YOUR_ATARI_ATARI_MOVE:
+    return -1;
+  }
+  /* We shoud never get here: */
+  gg_assert(1>2);
+  return 0; /* To keep gcc happy. */
+}
+
 /*
  * See if a lunch is already in the list of lunches, otherwise add a new
  * entry. A lunch is in this context a pair of eater (a dragon) and food
@@ -310,25 +374,6 @@
 
 
 /*
- * Find a reason in the list of reasons. If necessary, add a new entry.
- */
-static int
-find_reason(int type, int what)
-{
-  int k;
-  for (k = 0; k < next_reason; k++)
-    if ((move_reasons[k].type == type) && (move_reasons[k].what == what))
-      return k;
-  
-  /* Add a new entry. */
-  gg_assert(next_reason < MAX_MOVE_REASONS);
-  move_reasons[next_reason].type = type;
-  move_reasons[next_reason].what = what;
-  next_reason++;
-  return next_reason - 1;
-}
-
-/*
  * Add a move reason for (pos) if it's not already there or the
  * table is full.
  */ 
@@ -350,9 +395,16 @@
        && move_reasons[r].what == what)
       return;  /* Reason already listed. */
   }
-  /* Reason not found, add it if there is place left. */
-  if (k<MAX_REASONS)
-    move[pos].reason[k] = find_reason(type, what);
+
+  /* Reason not found, add it if there is place left in both lists. */
+  gg_assert(k<MAX_REASONS);
+  gg_assert(next_reason < MAX_MOVE_REASONS);
+  /* Add a new entry. */
+  move[pos].reason[k] = next_reason;
+  move_reasons[next_reason].type = type;
+  move_reasons[next_reason].what = what;
+  move_reasons[next_reason].status = ACTIVE;
+  next_reason++;
 }
 
 /*
@@ -435,6 +487,31 @@
 }
 
 /*
+ * Check whether a tactical attack/defense is already known for
+ * at least one of two worms in a worm pair.
+ */
+static int
+tactical_move_vs_either_worm_known(int pos, int what)
+{
+  return attack_move_reason_known(pos, worm_pair1[what])
+      || attack_move_reason_known(pos, worm_pair2[what])
+      || defense_move_reason_known(pos, worm_pair1[what])
+      || defense_move_reason_known(pos, worm_pair2[what]);
+}
+
+/* Check whether a dragon consists of only one worm. If so, check
+ * whether we know of a tactical attack or defense move.
+ */
+static int
+tactical_move_vs_whole_dragon_known(int pos, int what)
+{
+  int aa = dragons[what];
+  return ((worm[aa].size == dragon[aa].size)
+         && (attack_move_reason_known(pos, find_worm(aa))
+             || defense_move_reason_known(pos, find_worm(aa))));
+}
+
+/*
  * Check whether an owl attack move reason already is recorded for a move.
  * A negative value for 'what' means only match 'type'.
  */
@@ -447,7 +524,7 @@
 }
 
 /*
- * Check whether am owl defense move reason already is recorded for a move.
+ * Check whether an owl defense move reason already is recorded for a move.
  * A negative value for 'what' means only match 'type'.
  */
 static int
@@ -459,6 +536,44 @@
 }
 
 /*
+ * Check whether an owl attack/defense move reason is recorded for a move.
+ * A negative value for 'what' means only match 'type'.
+ */
+static int
+owl_move_reason_known(int pos, int what)
+{
+  return (owl_attack_move_reason_known(pos, what)
+          || owl_defense_move_reason_known(pos, what));
+}
+
+/*
+ * Check whether we have an owl attack/defense reason for a move that
+ * involves a specific worm.
+ */
+static int
+owl_move_vs_worm_known(int pos, int what)
+{
+  return owl_move_reason_known(pos, find_dragon(dragon[worms[what]].origin));
+}
+
+/* Check whether a worm listed in worms[] is inessential */
+static int
+concerns_inessential_worm(int pos, int what)
+{
+  UNUSED(pos);
+  return DRAGON2(worms[what]).safety == INESSENTIAL
+        || worm[worms[what]].inessential;
+}
+
+/* Check whether a dragon listed in dragons[] is inessential */
+static int
+concerns_inessential_dragon(int pos, int what)
+{
+  UNUSED(pos);
+  return DRAGON2(dragons[what]).safety == INESSENTIAL; 
+}
+
+/*
  * Add to the reasons for the move at (pos) that it attacks the worm
  * at (ww).
  */
@@ -2391,6 +2506,71 @@
 }
 
 
+/* This array lists rules according to which we set the status
+ * flags of a move reasons.
+ * The format is:
+ * { List of reasons to which the rule applies, condition of the rule,
+ * flags to be set, trace message }
+ */
+
+static struct discard_rule discard_rules[] =
+{
+  { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO,
+      ATTACK_MOVE_BAD_KO, ATTACK_THREAT_MOVE,
+      DEFEND_MOVE, DEFEND_MOVE_GOOD_KO,
+      DEFEND_MOVE_BAD_KO, DEFEND_THREAT_MOVE, -1 },
+    owl_move_vs_worm_known, TERRITORY_REDUNDANT,
+    "  %1m: 0.0 - (threat of) attack/defense of %1m (owl attack/defense as 
well)\n" },
+  { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
+    owl_move_reason_known, REDUNDANT,
+    "  %1m: 0.0 - (threat to) win semai involving %1m (owl move as well)\n"},
+  { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
+    tactical_move_vs_whole_dragon_known, REDUNDANT,
+    "  %1m: 0.0 - (threat to) win semai involving %1m (tactical move as 
well)\n"},
+  { { ATTACK_EITHER_MOVE, DEFEND_BOTH_MOVE, -1 },
+    tactical_move_vs_either_worm_known, REDUNDANT,
+    "  %1m: 0.0 - att. either/def. both involving %1m (direct att./def. as 
well)\n"},
+  { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO,
+      ATTACK_MOVE_BAD_KO, ATTACK_THREAT_MOVE,
+      DEFEND_MOVE, DEFEND_MOVE_GOOD_KO,
+      DEFEND_MOVE_BAD_KO, DEFEND_THREAT_MOVE, -1 },
+    concerns_inessential_worm, TERRITORY_REDUNDANT,
+    "  %1m: 0.0 - attack/defense of %1m (inessential)\n"},
+  { { OWL_ATTACK_MOVE, OWL_ATTACK_MOVE_GOOD_KO,
+      OWL_ATTACK_MOVE_BAD_KO, OWL_ATTACK_THREAT,
+      OWL_DEFEND_MOVE, OWL_DEFEND_MOVE_GOOD_KO,
+      OWL_DEFEND_MOVE_BAD_KO, UNCERTAIN_OWL_DEFENSE, -1 },
+    concerns_inessential_dragon, REDUNDANT,
+    "  %1m: 0.0 - (uncertain) owl attack/defense of %1m (inessential)\n"},
+  { { -1 }, NULL, 0, ""}  /* Keep this entry at end of the list. */
+};
+
+/* This function checks the list of move reasons for redundant move
+ * reasons and marks them accordingly in their status field.
+ */
+static void
+discard_redundant_move_reasons(int pos)
+{
+  int k1, k2;
+  int l;
+  for (k1 = 0; !(discard_rules[k1].reason_type[0] == -1); k1++) {
+    for (k2 = 0; !(discard_rules[k1].reason_type[k2] == -1); k2++) {
+      for (l = 0; l < MAX_REASONS; l++) {
+
+        int r = move[pos].reason[l];
+        if (r < 0)
+          break;
+        if ((move_reasons[r].type == discard_rules[k1].reason_type[k2])
+            && (discard_rules[k1].condition(pos, move_reasons[r].what))) {
+          DEBUG(DEBUG_MOVE_REASONS, discard_rules[k1].trace_message,
+                pos, get_pos(move_reasons[r].type, move_reasons[r].what)); 
+          move_reasons[r].status |= discard_rules[k1].flags;
+        }
+      } 
+    }
+  }
+}
+
 /*
  * Estimate the direct territorial value of a move at (pos).
  */
@@ -2413,6 +2593,8 @@
     int r = move[pos].reason[k];
     if (r < 0)
       break;
+    if (move_reasons[r].status & TERRITORY_REDUNDANT)
+      continue;
 
     this_value = 0.0;
     switch (move_reasons[r].type) {
@@ -2439,12 +2621,6 @@
        break;
       }
 
-      if (DRAGON2(aa).safety == INESSENTIAL || worm[aa].inessential) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - attack on %1m (inessential)\n", pos, aa);
-       break;
-      }
-
       this_value = 2 * worm[aa].effective_size;
 
       /* If the stones are dead, there is only a secondary value in
@@ -2457,15 +2633,6 @@
        secondary_value += 0.2 * this_value;
        break;
       }
-      
-      /* If the move also owl attacks the same stones, count points
-       * for that move reason instead.
-       */
-      if (owl_attack_move_reason_known(pos, find_dragon(aa))) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - attack on %1m (owl attacked as well)\n", pos, aa);
-       break;
-      }
 
       /* Mark the string as captured, for evaluation in the influence code. */
       mark_string(aa, saved_stones, INFLUENCE_CAPTURED_STONE);
@@ -2521,22 +2688,6 @@
        break;
       }
 
-      /* If the stones are inessential, there is no value in saving them. */
-      if (worm[aa].inessential || DRAGON2(aa).safety == INESSENTIAL) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - defense of %1m (inessential)\n", pos, aa);
-       break;
-      }
-      
-      /* If the move also owl defends the same stones, count points
-       * for that move reason instead.
-       */
-      if (owl_defense_move_reason_known(pos, find_dragon(aa))) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - defense of %1m (owl defended as well)\n", pos, aa);
-       break;
-      }
-
       /* Mark the string as saved, for evaluation in the influence code. */
       mark_string(aa, saved_stones, INFLUENCE_SAVED_STONE);
       TRACE("  %1m: defense of worm %1m\n", pos, aa);
@@ -2572,23 +2723,6 @@
        break;
       }
 
-      if (DRAGON2(aa).safety == INESSENTIAL || worm[aa].inessential) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - threatens to capture %1m (inessential)\n",
-             pos, aa);
-       break;
-      }
-
-      /* If the move also owl attacks the same stones, there is 
-       * no use to threaten tactically.
-       */
-      if (owl_attack_move_reason_known(pos, find_dragon(aa))) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - threaten to capture %1m (owl attacked as well)\n",
-             pos, aa);
-       break;
-      }
-      
       /* The followup value of a move threatening to attack (aa)
        * is twice its effective size, with adjustments. If the
        * worm has an adjacent (friendly) dead dragon we add its
@@ -2668,23 +2802,6 @@
        break;
       }
 
-      /* If the stones are inessential, there is no value in saving them. */
-      if (worm[aa].inessential || DRAGON2(aa).safety == INESSENTIAL) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - threaten to defend %1m (inessential)\n", pos, aa);
-       break;
-      }
-      
-      /* If the move also owl defends the same stones, there is no
-       * use in saving them
-       */
-      if (owl_defense_move_reason_known(pos, find_dragon(aa))) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - threaten to defend of %1m (owl defended as well)\n",
-             pos, aa);
-       break;
-      }
-      
       add_followup_value(pos, 2 * worm[aa].effective_size);
 
       TRACE("  %1m: %f (followup) - threatens to defend %1m\n",
@@ -2717,53 +2834,20 @@
       break;
       
     case SEMEAI_MOVE:
-      /* If this move also owl attacks or defends the same dragon, we
-       * count the points for that move reason instead.
-       */
-      if (owl_attack_move_reason_known(pos, move_reasons[r].what)
-         || owl_defense_move_reason_known(pos, move_reasons[r].what))
-       break;
-
       aa = dragons[move_reasons[r].what];
       
-      /* If the dragon consists of a single worm, and this move
-       * tactically attacks or defends that worm, count the points for
-       * that move reason instead.
-       */
-      if (worm[aa].size == dragon[aa].size
-         && (attack_move_reason_known(pos, find_worm(aa))
-             || defense_move_reason_known(pos, find_worm(aa))))
-       break;
-      
       this_value = 2 * dragon[aa].effective_size;
       TRACE("  %1m: %f - semeai involving %1m\n", pos, this_value, aa);
       tot_value += this_value;
       break;
       
     case SEMEAI_THREAT:
-      /* If this move also owl attacks or defends the same dragon, we
-       * count the points for that move reason instead.
-       */
-      if (owl_attack_move_reason_known(pos, move_reasons[r].what)
-         || owl_defense_move_reason_known(pos, move_reasons[r].what))
-       break;
-      
       aa = dragons[move_reasons[r].what];
 
-      /* If the dragon consists of a single worm, and this move
-       * tactically attacks or defends that worm, count the points for
-       * that move reason instead.
-       */
-      if (worm[aa].size == dragon[aa].size
-         && (attack_move_reason_known(pos, find_worm(aa))
-             || defense_move_reason_known(pos, find_worm(aa))))
-       break;
-      
       /* threaten to win the semeai as a ko threat */
       add_followup_value(pos, 2 * dragon[aa].effective_size);
       TRACE("  %1m: %f (followup) - threatens to win semeai for %1m\n",
            pos, 2 * dragon[aa].effective_size, aa);
-
       break;
       
     case VITAL_EYE_MOVE:
@@ -2780,13 +2864,6 @@
     case OWL_DEFEND_MOVE_GOOD_KO:
     case OWL_DEFEND_MOVE_BAD_KO:
       aa = dragons[move_reasons[r].what];
-      
-      if (DRAGON2(aa).safety == INESSENTIAL) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0 - owl attack/defend for inessential dragon %1m\n",
-             pos, aa);
-       break;
-      }
 
       /* If the dragon is a single ko stone, the owl code currently
        * won't detect that the owl attack is conditional. As a
@@ -2846,13 +2923,6 @@
        break;
       }
 
-      if (DRAGON2(aa).safety == INESSENTIAL) {
-       DEBUG(DEBUG_MOVE_REASONS,
-             "  %1m: 0.0 - threatens to capture %1m (inessential)\n",
-             pos, aa);
-       break;
-      }
-
       /* The followup value of a move threatening to attack (aa) is
        * twice its effective size, unless it has an adjacent
        * (friendly) critical dragon. In that case it's probably a
@@ -3038,6 +3108,8 @@
     int r = move[pos].reason[k];
     if (r < 0)
       break;
+    if (move_reasons[r].status & STRATEGICALLY_REDUNDANT)
+      continue;
     
     this_value = 0.0;
     switch (move_reasons[r].type) {
@@ -3147,18 +3219,6 @@
            && dragon[bb].matcher_status == DEAD)
          break;
 
-       /* If we already have unconditional attack or defense of
-         * either worm, this move reason has no additional value.
-        */
-       if (move_reasons[r].type == ATTACK_EITHER_MOVE
-           && (attack_move_reason_known(pos, worm1)
-               || attack_move_reason_known(pos, worm2)))
-         break;
-       if (move_reasons[r].type == DEFEND_BOTH_MOVE
-           && (defense_move_reason_known(pos, worm1)
-               || defense_move_reason_known(pos, worm2)))
-         break;
-
        /* Also if there is a combination attack, we assume it covers
          * the same thing.
         */
@@ -3338,13 +3398,6 @@
        d1 = move_reasons[r].what;
        aa = dragons[d1];
        
-       if (DRAGON2(aa).safety == INESSENTIAL) {
-         DEBUG(DEBUG_MOVE_REASONS,
-               "  %1m: 0 - uncertain owl attack/defend for inessential dragon 
%1m\n",
-               pos, aa);
-         break;
-       }
-       
        /* If there is an adjacent dragon which is critical we should
         * skip this type of move reason, since attacking or defending
         * the critical dragon is more urgent.
@@ -3563,6 +3616,8 @@
     qsort(&(move[pos].reason[0]), num_reasons, 
          sizeof(move[pos].reason[0]),
          compare_move_reasons);
+    /* Discard move reasons that only duplicate another. */
+    discard_redundant_move_reasons(pos);
 
     /* Estimate the value of various aspects of the move. The order
      * is significant. Territorial value must be computed before
Index: engine/move_reasons.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/move_reasons.h,v
retrieving revision 1.8
diff -u -r1.8 move_reasons.h
--- engine/move_reasons.h       10 Nov 2001 19:19:24 -0000      1.8
+++ engine/move_reasons.h       16 Dec 2001 23:47:37 -0000
@@ -57,6 +57,13 @@
 #define OWL_DEFEND_MOVE_GOOD_KO 35
 #define OWL_DEFEND_MOVE_BAD_KO  36
 
+/* Bitmap values for move_reason.status */
+#define ACTIVE 0
+#define TERRITORY_REDUNDANT 1
+#define STRATEGICALLY_REDUNDANT 2
+#define REDUNDANT 3
+#define SECONDARY 4
+
 #define MAX_REASONS 40
 
 #define HUGE_MOVE_VALUE 10.0*MAX_BOARD*MAX_BOARD
@@ -65,6 +72,8 @@
   int type;   /* e.g. attack, defend, or connect */
   int what;   /* pointer into list of strings, list of pair of dragons,
                 or similar */
+  int status; /* This is a bitmap to mark redundant or secondary
+                 move reasons. */
 };
 
 struct move_data {




reply via email to

[Prev in Thread] Current Thread [Next in Thread]