gnugo-devel
[Top][All Lists]
Advanced

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

[gnugo-devel] owl threat combinator


From: Paul Pogonyshev
Subject: [gnugo-devel] owl threat combinator
Date: Mon, 1 Mar 2004 00:08:10 +0200
User-agent: KMail/1.6.50

Here is a first attempt on owl threat combinator.  It is
quite primitive and includes just a few patterns.  On the
other hand, it is ready for inclusion in CVS because it
doesn't break anything and peacefully coexists with other
owl code.

This patch includes "pattern reimplementation" patch I
sent before.

There are some comments in new code.  I plan to add more
later.

Regression breakage:

ld_owl:316      PASS 1 T16 [1 T16]
owl1:326        PASS 3 S18 [3 S18]

Total nodes: 1503494279 2723171 10963294

Paul



Index: engine/owl.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v
retrieving revision 1.199
diff -u -p -r1.199 owl.c
--- engine/owl.c        11 Feb 2004 12:38:24 -0000      1.199
+++ engine/owl.c        29 Feb 2004 21:12:43 -0000
@@ -126,11 +126,15 @@ struct owl_move_data {
 
 struct matched_pattern_data {
   int move;
+  int value;
   int ll;
 #if USE_BDIST
   int bdist;
 #endif
   struct pattern *pattern;
+
+  /* To link combinable patterns in chains. */
+  int next_pattern_index;
 };
   
 struct matched_patterns_list_data {
@@ -139,11 +143,10 @@ struct matched_patterns_list_data {
   int used;            /* How many patterns have already been used?*/
   int list_size;       
   struct matched_pattern_data *pattern_list;
+  int first_pattern_index[BOARDMAX];
 
   int heap_num_patterns;
   struct matched_pattern_data **pattern_heap;
-
-  struct matched_pattern_data *next_pattern;
 };
 
 void dump_pattern_list(struct matched_patterns_list_data *list);
@@ -161,9 +164,13 @@ static void owl_shapes(struct matched_pa
 static void collect_owl_shapes_callbacks(int anchor, int color,
                                         struct pattern *pattern_db,
                                         int ll, void *data);
+
+static void pattern_list_prepare(struct matched_patterns_list_data *list);
 static void pattern_list_build_heap(struct matched_patterns_list_data *list);
-static void pattern_list_get_next_pattern(struct matched_patterns_list_data
-                                         *list);
+static void pattern_list_pop_heap_once(struct matched_patterns_list_data 
*list);
+static void pattern_list_sink_heap_top_element(struct 
matched_patterns_list_data
+                                              *list);
+
 static int get_next_move_from_list(struct matched_patterns_list_data *list,
                                    int color, struct owl_move_data *moves,
                                   int cutoff);
@@ -3711,7 +3718,6 @@ init_pattern_list(struct matched_pattern
   if (0)
     gprintf("List at %x has new array at %x\n", list, list->pattern_list);
 
-  list->next_pattern = NULL;
   list->initialized = 1;
 }
 
@@ -3809,14 +3815,134 @@ collect_owl_shapes_callbacks(int anchor,
   }
 
   next_pattern = &matched_patterns->pattern_list[matched_patterns->counter];
-  next_pattern->move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);
-  next_pattern->ll = ll;
-  next_pattern->pattern = pattern;
+  next_pattern->move   = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);
+  next_pattern->value  = pattern->value;
+  next_pattern->ll     = ll;
+  next_pattern->pattern        = pattern;
+  next_pattern->next_pattern_index = -1;
 
   matched_patterns->counter++;
 }
 
 
+#define MAX_STORED_REASONS     4
+
+static int
+valuate_combinable_pattern_chain(struct matched_patterns_list_data *list,
+                                int pos)
+{
+  /* FIXME: This is just a first attempt at pattern combination.
+   *       Improve it.  The first idea is to differentiate between
+   *       move reason types.  For instance, when there is a secure
+   *       eye already, a threat to create another is more severe.
+   *
+   *       This will certainly involve splitting the function into
+   *       attack and defense versions.
+   */
+
+  int pattern_index = list->first_pattern_index[pos];
+  int num_capture_threats = 0;
+  int capture_threats[MAX_STORED_REASONS];
+  int num_eye_threats = 0;
+  int eye_threats[MAX_STORED_REASONS];
+  int num_reverse_sente = 0;
+  int reverse_sente_against[MAX_STORED_REASONS];
+  int num_move_reasons;
+  float full_value = 0.0;
+
+  ASSERT1(pattern_index != -1, pos);
+
+  do {
+    struct matched_pattern_data *pattern_data = (list->pattern_list
+                                                + pattern_index);
+    struct pattern_attribute *attribute;
+
+    /* Skip patterns that haven't passed constraint validation. */
+    if (pattern_data->pattern) {
+      for (attribute = pattern_data->pattern->attributes;
+          attribute->type != LAST_ATTRIBUTE;
+          attribute++) {
+       int k;
+       int target = AFFINE_TRANSFORM(attribute->offset, pattern_data->ll,
+                                     pattern_data->move);
+
+       switch (attribute->type) {
+       case THREATENS_TO_CAPTURE:
+         if (num_capture_threats < MAX_STORED_REASONS) {
+           ASSERT1(IS_STONE(board[target]), target);
+           target = find_origin(target);
+
+           for (k = 0; k < num_capture_threats; k++) {
+             if (capture_threats[k] == target)
+               break;
+           }
+
+           if (k == num_capture_threats) {
+             capture_threats[num_capture_threats++] = target;
+             full_value += pattern_data->pattern->value;
+           }
+         }
+
+         break;
+
+       case THREATENS_EYE:
+         if (num_eye_threats < MAX_STORED_REASONS) {
+           target = current_owl_data->my_eye[target].origin;
+
+           for (k = 0; k < num_eye_threats; k++) {
+             if (eye_threats[k] == target)
+               break;
+           }
+
+           if (k == num_eye_threats) {
+             eye_threats[num_eye_threats++] = target;
+             full_value += pattern_data->pattern->value;
+           }
+         }
+
+         break;
+
+       case REVERSE_SENTE:
+         if (num_reverse_sente < MAX_STORED_REASONS) {
+           ASSERT1(board[target] == EMPTY, target);
+
+           for (k = 0; k < num_reverse_sente; k++) {
+             if (reverse_sente_against[k] == target)
+               break;
+           }
+
+           if (k == num_reverse_sente) {
+             reverse_sente_against[num_reverse_sente++] = target;
+             full_value += pattern_data->pattern->value;
+           }
+         }
+
+         break;
+
+       default:
+         gg_assert(0);
+       }
+      }
+    }
+
+    pattern_index = pattern_data->next_pattern_index;
+  } while (pattern_index >= 0);
+
+
+  num_move_reasons = num_capture_threats + num_eye_threats + num_reverse_sente;
+  if (num_move_reasons <= 1) {
+    /* Not much to combine, eh? */
+    return 0;
+  }
+
+  if (num_move_reasons == 2)
+    return gg_min((int) full_value, 75);
+  if (num_move_reasons == 3)
+    return gg_min((int) (full_value * 0.85), 90);
+  return gg_min((int) (full_value * 0.75), 99);
+}
+
+
 #if USE_BDIST
 
 /* Compute the squared of the distance of a point on the board to the
@@ -3840,8 +3966,8 @@ bdist(int move)
  */
 
 #define BETTER_PATTERN(a, b)                           \
-  ((a)->pattern->value > (b)->pattern->value           \
-   || ((a)->pattern->value == (b)->pattern->value      \
+  ((a)->value > (b)->value                             \
+   || ((a)->value == (b)->value                                \
        && ((a)->pattern < (b)->pattern                 \
           || ((a)->pattern == (b)->pattern             \
               && ((a)->bdist < (b)->bdist              \
@@ -3851,8 +3977,8 @@ bdist(int move)
 #else  /* not USE_BDIST */
 
 #define BETTER_PATTERN(a, b)                           \
-  ((a)->pattern->value > (b)->pattern->value           \
-   || ((a)->pattern->value == (b)->pattern->value      \
+  ((a)->value > (b)->value                             \
+   || ((a)->value == (b)->value                                \
        && ((a)->pattern < (b)->pattern                 \
           || ((a)->pattern == (b)->pattern             \
               && (a)->move < (b)->move))))
@@ -3860,28 +3986,68 @@ bdist(int move)
 #endif /* not USE_BDIST */
 
 
-/* Fast heap building.  Takes O(n) only. */
 static void
-pattern_list_build_heap(struct matched_patterns_list_data *list)
+pattern_list_prepare(struct matched_patterns_list_data *list)
 {
   int k;
-  int limit;
+  int pos;
 
-  if (list->counter > 0) {
-    list->pattern_heap = malloc(list->counter
-                               * sizeof(struct matched_pattern_data*));
-    gg_assert(list->pattern_heap != NULL);
-  }
+  list->heap_num_patterns = 0;
+
+  /* This is more than needed in case of (combinable) pattern chains,
+   * but it is easier to allocate more than to count real number of
+   * heap elements first.
+   */
+  list->pattern_heap = malloc(list->counter
+                             * sizeof(struct matched_pattern_data *));
+  gg_assert(list->pattern_heap != NULL);
+
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
+    list->first_pattern_index[pos] = -1;
 
   for (k = 0; k < list->counter; k++) {
+    int move = list->pattern_list[k].move;
+
 #if USE_BDIST
-    list->pattern_list[k].bdist = bdist(list->pattern_list[k].move);
+    list->pattern_list[k].bdist = bdist(move);
 #endif
-    list->pattern_heap[k] = &list->pattern_list[k];
+
+    /* Allocate heap elements for normal patterns.  Link combinable
+     * patterns in chains.
+     */
+    if (!(list->pattern_list[k].pattern->class & CLASS_c))
+      list->pattern_heap[list->heap_num_patterns++] = &list->pattern_list[k];
+    else {
+/*       list->pattern_list[k].next_pattern_index = 
list->first_pattern_index[move]; */
+/*       list->first_pattern_index[move] = k; */
+    }
   }
 
-  list->heap_num_patterns = list->counter;
-  limit = list->heap_num_patterns / 2;
+  /* Allocate one heap element for each chain of combinable patterns
+   * and calculate initial chain values (as if all patterns passed
+   * constraint validation).
+   */
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+    if (list->first_pattern_index[pos] != -1) {
+      struct matched_pattern_data *pattern_data
+       = &list->pattern_list[list->first_pattern_index[pos]];
+
+      pattern_data->value = valuate_combinable_pattern_chain(list, pos);
+      list->pattern_heap[list->heap_num_patterns++] = pattern_data;
+    }
+  }
+
+  if (list->heap_num_patterns > 0)
+    pattern_list_build_heap(list);
+}
+
+
+/* Fast heap building.  Takes O(n) only. */
+static void
+pattern_list_build_heap(struct matched_patterns_list_data *list)
+{
+  int k;
+  int limit = list->heap_num_patterns / 2;
 
   for (k = limit; --k >= 0;) {
     int parent;
@@ -3906,17 +4072,13 @@ pattern_list_build_heap(struct matched_p
 }
 
 
-/* Pops patterns list's heap once and makes `list->next_pattern' point
- * to the popped pattern.
- */
+/* Pops patterns list's heap once. */
 static void
-pattern_list_get_next_pattern(struct matched_patterns_list_data *list)
+pattern_list_pop_heap_once(struct matched_patterns_list_data *list)
 {
   int parent;
   int child;
 
-  list->next_pattern = list->pattern_heap[0];
-
   list->heap_num_patterns--;
   for (parent = 0; 2 * parent + 1 < list->heap_num_patterns; parent = child) {
     child = 2 * parent + 1;
@@ -3935,6 +4097,35 @@ pattern_list_get_next_pattern(struct mat
 }
 
 
+/* Sink top element of heap because it got devalued.  This happens
+ * when a combinable pattern doesn't pass check_pattern_hard() -- it
+ * is no longer counted and its whole chain's value is reduced.
+ */
+static void
+pattern_list_sink_heap_top_element(struct matched_patterns_list_data *list)
+{
+  int parent;
+  int child;
+  struct matched_pattern_data *heap_top_element = list->pattern_heap[0];
+
+  for (parent = 0; 2 * parent + 1 < list->heap_num_patterns; parent = child) {
+    child = 2 * parent + 1;
+    if (child + 1 < list->heap_num_patterns
+       && BETTER_PATTERN(list->pattern_heap[child + 1],
+                         list->pattern_heap[child]))
+      child++;
+
+    if (BETTER_PATTERN(heap_top_element,
+                      list->pattern_heap[child]))
+      break;
+
+    list->pattern_heap[parent] = list->pattern_heap[child];
+  }
+
+  list->pattern_heap[parent] = heap_top_element;
+}
+
+
 /* This function searches in the previously stored list of matched
  * patterns for the highest valued unused patterns that have a valid
  * constraint.  It returns the moves at the next empty positions in
@@ -3945,11 +4136,13 @@ pattern_list_get_next_pattern(struct mat
  * If the highest valued pattern found has a value less than cutoff,
  * no move is returned.  Returns 1 if a move is found, 0 otherwise.
  *
- * pattern_list_get_next_pattern() is used to pop the next highest
- * valued pattern from the list's heap.  Then it is checked whether
- * this move has not been tried before, and if the pattern constraint
- * is valid. This is repeated until enough moves are found or the end
- * of the list is reached.
+ * This function also dispatches constraint validation of combinable
+ * pattern chains.  Whenever a pattern from a chain fails constraints,
+ * the chain is reevaluated and most likely drops in value enough to
+ * let other patterns (or chains) climb to the top of pattern heap.
+ *
+ * This function loops until enough moves are found or the end of the
+ * list is reached.
  */
 
 static int
@@ -3963,26 +4156,28 @@ get_next_move_from_list(struct matched_p
   sgf_dumptree = NULL;
   count_variations = 0;
 
+  /* Prepare pattern list if needed. */
   if (!list->pattern_heap)
-    pattern_list_build_heap(list);
+    pattern_list_prepare(list);
 
-  while (list->heap_num_patterns > 0 || list->next_pattern) {
+  while (list->heap_num_patterns > 0) {
     int k;
     struct pattern *pattern;
     int move;
+    int value;
     int ll;
+    int next_pattern_index;
 
-    if (!list->next_pattern)
-      pattern_list_get_next_pattern(list);
-
-    pattern = list->next_pattern->pattern;
-    if (pattern->value < (float) cutoff)
+    /* Peek top element of heap associated with pattern list. */
+    if (list->pattern_heap[0]->value < cutoff)
       break;
 
-    move = list->next_pattern->move;
-    ll = list->next_pattern->ll;
+    pattern = list->pattern_heap[0]->pattern;
+    move    = list->pattern_heap[0]->move;
+    value   = list->pattern_heap[0]->value;
+    ll      = list->pattern_heap[0]->ll;
+    next_pattern_index = list->pattern_heap[0]->next_pattern_index;
 
-    list->next_pattern = NULL;
     list->used++;
 
     ASSERT_ON_BOARD1(move);
@@ -3990,57 +4185,126 @@ get_next_move_from_list(struct matched_p
       if (moves[k].pos == move || moves[k].value <= 0)
        break;
     }
-    if (moves[k].pos == move)
+
+    if (moves[k].pos == move) {
+      /* No point in testing this pattern/chain.  Throw it out. */
+      pattern_list_pop_heap_once(list);
       continue;
+    }
+
+    /* There has to be an empty space. */
+    gg_assert(k < MAX_MOVES);
+
+    /* If a pattern chain was devalued because its last pattern didn't
+     * pass constraint validation, `pattern' is set NULL (i.e. nothing
+     * more to test).  Note that devalued chains might still be
+     * useful, i.e. if 2 of 3 patterns passed check_pattern_hard().
+     */
+    if (pattern == NULL
+       || check_pattern_hard(move, color, pattern, ll)) {
+      if (next_pattern_index == -1) {
+       /* Normal pattern or last one in a chain. */
+       pattern_list_pop_heap_once(list);
+      }
+      else {
+       /* We just validated a non-last pattern in a chain.  Since the
+        * chain remains at the same value, we keep the heap structure
+        * untouched.  However, we need to set heap's top to point to
+        * next pattern of the chain.
+        */
+       list->pattern_heap[0] = list->pattern_list + next_pattern_index;
+       list->pattern_heap[0]->value = value;
+       continue;
+      }
 
-    gg_assert(k < MAX_MOVES); /* There has to be an empty space. */
-    if (check_pattern_hard(move, color, pattern, ll)) {
       moves[k].pos = move;
-      moves[k].value = pattern->value;
-      moves[k].name = pattern->name;
+      moves[k].value = value;
       move_found = 1;
-      TRACE("Pattern %s found at %1m with value %d\n",
-           pattern->name, move, moves[k].value);
 
-      if (pattern->class & CLASS_B)
-       moves[k].same_dragon = 0;
-      else if (pattern->class & CLASS_b) {
-       int i;
-       int same_dragon = 1;
+      if (pattern && !(pattern->class & CLASS_c)) {
+       moves[k].name = pattern->name;
+       TRACE("Pattern %s found at %1m with value %d\n",
+             pattern->name, move, moves[k].value);
+
+       if (pattern->class & CLASS_B)
+         moves[k].same_dragon = 0;
+       else if (pattern->class & CLASS_b) {
+         int i;
+         int same_dragon = 1;
 
-       /* If we do not yet know whether the move belongs to the same dragon,
-        * we see whether another pattern can clarify.
-        */
-       for (i = 0; i < list->heap_num_patterns; i++) {
-         struct matched_pattern_data *pattern_data = list->pattern_heap[i];
+         /* If we do not yet know whether the move belongs to the
+          * same dragon, we see whether another pattern can clarify.
+          */
+         for (i = 0; i < list->heap_num_patterns; i++) {
+           struct matched_pattern_data *pattern_data = list->pattern_heap[i];
 
-         if (pattern_data->move == move
-             && ((pattern_data->pattern->class & CLASS_B)
-                 || !(pattern_data->pattern->class & CLASS_b))) {
-           if (check_pattern_hard(move, color, pattern_data->pattern,
-                                  pattern_data->ll)) {
-             TRACE("Additionally pattern %s found at %1m\n",
-                   pattern_data->pattern->name, move);
-             if (pattern_data->pattern->class & CLASS_B)
-               same_dragon = 0;
-             else
-               same_dragon = 2;
+           if (pattern_data->pattern
+               && pattern_data->move == move
+               && ((pattern_data->pattern->class & CLASS_B)
+                   || !(pattern_data->pattern->class & CLASS_b))) {
+             if (check_pattern_hard(move, color, pattern_data->pattern,
+                                    pattern_data->ll)) {
+               TRACE("Additionally pattern %s found at %1m\n",
+                     pattern_data->pattern->name, move);
+               if (pattern_data->pattern->class & CLASS_B)
+                 same_dragon = 0;
+               else
+                 same_dragon = 2;
 
-             break;
+               break;
+             }
            }
          }
+
+         moves[k].same_dragon = same_dragon;
+       }
+       else
+         moves[k].same_dragon = 2;
+      }
+      else {
+       moves[k].name = "Pattern combination";
+       if (verbose) {
+         /* FIXME: write names of all patterns in chain. */
        }
 
-       moves[k].same_dragon = same_dragon;
+       /* FIXME: Add handling of CLASS_b.
+        *
+        * FIXME: It is silently assumed that all patterns in the
+        *        chain have the same class.  When the last pattern in
+        *        chain didn't match, this will not work at all.
+        */
+       if (pattern && pattern->class & CLASS_B)
+         moves[k].same_dragon = 0;
+       else
+         moves[k].same_dragon = 2;
       }
-      else
-       moves[k].same_dragon = 2;
-      if (pattern->class & CLASS_E)
+
+      if (pattern && pattern->class & CLASS_E)
        moves[k].escape = 1;
       else
        moves[k].escape = 0;
+
       break;
-    } /* if (check_pattern_hard(...)) */
+    }
+    else {                     /* !check_pattern_hard(...) */
+      if (!(pattern->class & CLASS_c)) {
+       /* Just forget about it. */
+       pattern_list_pop_heap_once(list);
+      }
+      else {
+       /* Set this pattern to not matched and advance to next one in
+        * the chain, if any.
+        */
+       list->pattern_heap[0]->pattern = NULL;
+       if (next_pattern_index != -1)
+         list->pattern_heap[0] = list->pattern_list + next_pattern_index;
+
+       /* Reevaluate chain and adjust heap structure accordingly. */
+       list->pattern_heap[0]->value = valuate_combinable_pattern_chain(list,
+                                                                       move);
+       pattern_list_sink_heap_top_element(list);
+      }
+    }
   }
 
   sgf_dumptree = save_sgf_dumptree;
Index: engine/shapes.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/shapes.c,v
retrieving revision 1.50
diff -u -p -r1.50 shapes.c
--- engine/shapes.c     24 Jan 2004 04:04:56 -0000      1.50
+++ engine/shapes.c     29 Feb 2004 21:12:44 -0000
@@ -39,10 +39,161 @@
 #define t_VALUE 16.0
 
 
+/* Take care of joseki patterns. */
+static void
+handle_joseki_patterns(struct pattern_attribute *attributes,
+                      unsigned int class, int move,
+                      int my_dragons[MAX_DRAGONS_PER_PATTERN],
+                      int my_ndragons,
+                      int your_dragons[MAX_DRAGONS_PER_PATTERN],
+                      int your_ndragons)
+{
+  struct pattern_attribute *attribute;
+
+  /* Pattern class J, joseki standard move. Add expand territory and
+   * moyo, and require the value at least J_value.
+   */
+  if (class & CLASS_J) {
+    TRACE("...joseki standard move\n");
+    add_expand_territory_move(move);
+    TRACE("...expands territory\n");
+    add_expand_moyo_move(move);
+    TRACE("...expands moyo\n");
+    set_minimum_move_value(move, J_VALUE);
+    TRACE("... minimum move value %f\n", J_VALUE);
+  }
+
+  /* Class `j' and `t' patterns are treated similarly. */
+  if (class & (CLASS_j | CLASS_t)) {
+    float min_value;
+    float shape_value = 0.0;
+
+    if (class & CLASS_j) {
+      min_value = j_VALUE;
+      TRACE("...less urgent joseki move\n");
+      add_expand_territory_move(move);
+      TRACE("...expands territory\n");
+      add_expand_moyo_move(move);
+      TRACE("...expands moyo\n");
+    }
+    else {
+      min_value = t_VALUE;
+      TRACE("...minor joseki move\n");
+    }
+
+    /* Board size modification. */
+    min_value *= board_size / 19.0;
+
+    for (attribute = attributes; attribute->type != LAST_ATTRIBUTE;
+        attribute++) {
+      if (attribute->type == SHAPE) {
+       shape_value = attribute->value;
+       min_value *= (1 + 0.01 * shape_value);
+       break;
+      }
+    }
+
+    if ((board_size >= 17) && (class & CLASS_F)) {
+      /* Otherwise, `j' and `t' patterns not of CLASS_F would get
+       * preferred in value_move_reasons().
+       */
+      min_value *= 1.005;
+
+      set_maximum_move_value(move, min_value);
+      scale_randomness(move, 5.0);
+      TRACE("...move value %f (shape %f)\n", min_value, shape_value);
+    }
+    else
+      TRACE("...minimum move value %f (shape %f)\n", min_value, shape_value);
+
+    set_minimum_move_value(move, min_value);
+  }
+
+  /* Pattern class U, very urgent joseki move. Add strategical defense
+   * and attack, plus a shape bonus of 15 and a minimum value of 40.
+   */
+  if (class & CLASS_U) {
+    int k;
+
+    TRACE("...joseki urgent move\n");
+    for (k = 0; k < my_ndragons; k++) {
+      add_strategical_defense_move(move, my_dragons[k]);
+      TRACE("...strategical defense of %1m\n", my_dragons[k]);
+    }
+    for (k = 0; k < your_ndragons; k++) {
+      add_strategical_attack_move(move, your_dragons[k]);
+      TRACE("...strategical attack on %1m\n", your_dragons[k]);
+    }
+    add_shape_value(move, 15);
+    TRACE("...shape value 15\n");
+    set_minimum_move_value(move, U_VALUE);
+    TRACE("...(min) move value %f\n", U_VALUE);
+  }
+
+  /* Pattern class T, joseki trick move. For the moment we never play
+   * these.
+   */
+  if (class & CLASS_T) {
+    TRACE("...joseki trick move\n");
+    add_antisuji_move(move);
+    TRACE("...antisuji\n");
+  }
+
+  for (attribute = attributes; attribute->type != LAST_ATTRIBUTE;
+       attribute++) {
+    switch (attribute->type) {
+    case MIN_VALUE:
+      set_minimum_move_value(move, attribute->value);
+      TRACE("...(min) move value %f\n", attribute->value);
+      break;
+
+    case MAX_VALUE:
+      set_maximum_move_value(move, attribute->value);
+      TRACE("...max move value %f\n", attribute->value);
+      break;
+
+    case MIN_TERRITORY:
+      set_minimum_territorial_value(move, attribute->value);
+      TRACE("...(min) territorial value %f\n", attribute->value);
+      break;
+
+    case MAX_TERRITORY:
+      set_maximum_territorial_value(move, attribute->value);
+      TRACE("...max territorial value %f\n", attribute->value);
+      break;
+
+    case SHAPE:
+      /* For class `j' and `t' patterns shape value has been counted
+       * already.
+       */
+      if (!(class & (CLASS_j | CLASS_t))) {
+       add_shape_value(move, attribute->value);
+       TRACE("...shape value %f\n", attribute->value);
+      }
+
+      break;
+
+    case FOLLOWUP:
+      add_followup_value(move, attribute->value);
+      TRACE("...followup value %f\n", attribute->value);
+      break;
+
+    case REVERSE_FOLLOWUP:
+      add_reverse_followup_value(move, attribute->value);
+      TRACE("...reverse followup value %f\n", attribute->value);
+      break;
+
+    default:
+      /* Must not happen. */
+      gg_assert(0);
+    }
+  }
+}
+
+
 /* 
  * This callback is invoked for each matched pattern.
  */
-
 static void
 shapes_callback(int anchor, int color, struct pattern *pattern, int ll,
                void *data)
@@ -202,7 +353,8 @@ shapes_callback(int anchor, int color, s
   if (!pattern->helper
       && !allpats
       && !(pattern->autohelper_flag & HAVE_ACTION)
-      && !(class & (CLASS_MOVE_REASONS | CLASS_MOVE_VALUES)))
+      && !(class & CLASS_MOVE_REASONS)
+      && pattern->attributes->type == LAST_ATTRIBUTE)
     return;
   
   /* For sacrifice patterns, the survival of the stone to be played is
@@ -355,160 +507,21 @@ shapes_callback(int anchor, int color, s
     }
   }
 
-  /* Pattern class J, joseki standard move. Add expand territory and
-   * moyo, and require the value at least J_value.
-   */
-  if (class & CLASS_J) {
-    TRACE("...joseki standard move\n");
-    add_expand_territory_move(move);
-    TRACE("...expands territory\n");
-    add_expand_moyo_move(move);
-    TRACE("...expands moyo\n");
-    set_minimum_move_value(move, J_VALUE);
-    TRACE("... minimum move value %f\n", J_VALUE);
-  }
-
-  /* Pattern class j, less urgent joseki move. Add expand territory and
-   * moyo, set a minimum value of j_VALUE. If it is a fuseki pattern, set also
-   * the maximum value to j_VALUE.
-   */
-  if (class & CLASS_j) {
-    float min_value = j_VALUE;
-    TRACE("...less urgent joseki move\n");
-    add_expand_territory_move(move);
-    TRACE("...expands territory\n");
-    add_expand_moyo_move(move);
-    TRACE("...expands moyo\n");
-
-    /* Board size modification. */
-    min_value *= board_size / 19.0;
-    
-    if (class & VALUE_SHAPE) {
-      min_value *= (1 + 0.01 * pattern->shape);
-      class &= ~VALUE_SHAPE;
-    };
-
-    if ((board_size >= 17) && (class & CLASS_F)) {
-      min_value *= 1.005; /* Otherwise, j patterns not of CLASS_F would */
-                          /* get preferred in value_move_reasons */
-      set_maximum_move_value(move, min_value);
-      scale_randomness(move, 5.);
-      TRACE("...move value %f (shape %f)\n", min_value, pattern->shape);
-    }
-    else 
-      TRACE("...minimum move value %f (shape %f)\n",
-            min_value, pattern->shape);
-    set_minimum_move_value(move, min_value);
-  }
-
-  /* Pattern class t, minor joseki move (tenuki OK).
-   * Set the (min-)value at t_value
-   */
-  if (class & CLASS_t) {
-    float min_value = t_VALUE;
-    TRACE("...minor joseki move\n");
-    
-    /* Board size modification. */
-    min_value *= board_size / 19.0;
-    
-    if (class & VALUE_SHAPE) {
-      min_value *= (1 + 0.01 * pattern->shape);
-      class &= ~VALUE_SHAPE;
-    };
-    
-    if ((board_size >= 17) && (class & CLASS_F)) {
-      min_value *= 1.005; /* Otherwise, j patterns not of CLASS_F would */
-                          /* get preferred in value_move_reasons */
-      set_maximum_move_value(move, min_value);
-      scale_randomness(move, 5.);
-      TRACE("...move value %f (shape %f)\n", min_value, pattern->shape);
-    }
-    else
-      TRACE("...minimum move value %f (shape %f)\n",
-            min_value, pattern->shape);
-    set_minimum_move_value(move, min_value);
-  }
-
-  /* Pattern class U, very urgent joseki move. Add strategical defense
-   * and attack, plus a shape bonus of 15 and a minimum value of 40.
-   */
-  if (class & CLASS_U) {
-    TRACE("...joseki urgent move\n");
-    for (k = 0; k < my_ndragons; k++) {
-      add_strategical_defense_move(move, my_dragons[k]);
-      TRACE("...strategical defense of %1m\n", my_dragons[k]);
-    }
-    for (k = 0; k < your_ndragons; k++) {
-      add_strategical_attack_move(move, your_dragons[k]);
-      TRACE("...strategical attack on %1m\n", your_dragons[k]);
-    }
-    add_shape_value(move, 15);
-    TRACE("...shape value 15\n");
-    set_minimum_move_value(move, U_VALUE);
-    TRACE("...(min) move value %f\n", U_VALUE);
-  }
-
-  /* Pattern class T, joseki trick move. For the moment we never play
-   * these.
-   */
-  if (class & CLASS_T) {
-    TRACE("...joseki trick move\n");
-    add_antisuji_move(move);
-    TRACE("...antisuji\n");
-  }
-
   /* Pattern class W, worthwhile threat move. */
   if (class & CLASS_W) {
     TRACE("...worthwhile threat move\n");
     add_worthwhile_threat_move(move);
   }
 
-  /* Minimum move value specified. */
-  if (class & VALUE_MINVAL) {
-    set_minimum_move_value(move, pattern->value);
-    TRACE("...(min) move value %f\n", pattern->value);
-  }
-
-  /* Maximum move value specified. */
-  if (class & VALUE_MAXVAL) {
-    set_maximum_move_value(move, pattern->maxvalue);
-    TRACE("...max move value %f\n", pattern->maxvalue);
-  }
-
-  /* Minimum territorial value specified. */
-  if (class & VALUE_MINTERRITORY) {
-    set_minimum_territorial_value(move, pattern->minterritory);
-    TRACE("...(min) territorial value %f\n", pattern->minterritory);
-  }
-
-  /* Maximum territorial value specified. */
-  if (class & VALUE_MAXTERRITORY) {
-    set_maximum_territorial_value(move, pattern->maxterritory);
-    TRACE("...max territorial value %f\n", pattern->maxterritory);
-  }
-
-  /* Shape value specified. */
-  if (class & VALUE_SHAPE) {
-    add_shape_value(move, pattern->shape);
-    TRACE("...shape value %f\n", pattern->shape);
-  }
-
-  /* Followup value specified. */
-  if (class & VALUE_FOLLOWUP) {
-    add_followup_value(move, pattern->followup);
-    TRACE("...followup value %f\n", pattern->followup);
-  }
-
-  /* Reverse followup value specified. */
-  if (class & VALUE_REV_FOLLOWUP) {
-    add_reverse_followup_value(move, pattern->reverse_followup);
-    TRACE("...reverse followup value %f\n", pattern->reverse_followup);
-  }
+  handle_joseki_patterns(pattern->attributes, class, move,
+                        my_dragons, my_ndragons, your_dragons, your_ndragons);
 }
 
 
-/* This callback is invoked for each matched pattern from joseki database.
- * This function is just a copy of relevant parts of shapes_callback().
+/* This callback is invoked for each matched pattern from joseki
+ * database.  This function is just a copy of relevant parts of
+ * shapes_callback().  However, most of the common code resides in
+ * handle_joseki_patterns().
  */
 static void
 joseki_callback(int move, int color, struct corner_pattern *pattern,
@@ -591,117 +604,14 @@ joseki_callback(int move, int color, str
   if (pattern->autohelper_flag & HAVE_ACTION)
     pattern->autohelper(trans, move, color, 1);
 
-  /* Pattern class J, joseki standard move. Add expand territory and
-   * moyo, and require the value at least J_value.
-   */
-  if (class & CLASS_J) {
-    TRACE("...joseki standard move\n");
-    add_expand_territory_move(move);
-    TRACE("...expands territory\n");
-    add_expand_moyo_move(move);
-    TRACE("...expands moyo\n");
-    set_minimum_move_value(move, J_VALUE);
-    TRACE("... minimum move value %f\n", J_VALUE);
-  }
-
-  /* Pattern class j, less urgent joseki move. Add expand territory and
-   * moyo, set a minimum value of j_VALUE. If it is a fuseki pattern, set also
-   * the maximum value to j_VALUE.
-   */
-  if (class & CLASS_j) {
-    float min_value = j_VALUE;
-    TRACE("...less urgent joseki move\n");
-    add_expand_territory_move(move);
-    TRACE("...expands territory\n");
-    add_expand_moyo_move(move);
-    TRACE("...expands moyo\n");
-
-    /* Board size modification. */
-    min_value *= board_size / 19.0;
-
-    if (class & VALUE_SHAPE) {
-      min_value *= (1 + 0.01 * pattern->shape);
-      class &= ~VALUE_SHAPE;
-    };
-
-    if (board_size >= 17) {
-      min_value *= 1.005; /* Otherwise, j patterns not of CLASS_F would */
-                          /* get preferred in value_move_reasons(). */
-      set_maximum_move_value(move, min_value);
-      scale_randomness(move, 5.);
-      TRACE("...move value %f (shape %f)\n", min_value, pattern->shape);
-    }
-    else
-      TRACE("...minimum move value %f (shape %f)\n",
-            min_value, pattern->shape);
-    set_minimum_move_value(move, min_value);
-  }
-
-  /* Pattern class t, minor joseki move (tenuki OK).
-   * Set the (min-)value at t_value
-   */
-  if (class & CLASS_t) {
-    float min_value = t_VALUE;
-    TRACE("...minor joseki move\n");
-
-    /* Board size modification. */
-    min_value *= board_size / 19.0;
-
-    if (class & VALUE_SHAPE) {
-      min_value *= (1 + 0.01 * pattern->shape);
-      class &= ~VALUE_SHAPE;
-    };
-
-    if ((board_size >= 17) && (class & CLASS_F)) {
-      min_value *= 1.005; /* Otherwise, j patterns not of CLASS_F would */
-                          /* get preferred in value_move_reasons */
-      set_maximum_move_value(move, min_value);
-      scale_randomness(move, 5.);
-      TRACE("...move value %f (shape %f)\n", min_value, pattern->shape);
-    }
-    else
-      TRACE("...minimum move value %f (shape %f)\n",
-            min_value, pattern->shape);
-    set_minimum_move_value(move, min_value);
-  }
-
-  /* Pattern class U, very urgent joseki move. Add strategical defense
-   * and attack, plus a shape bonus of 15 and a minimum value of 40.
-   */
-  if (class & CLASS_U) {
-    TRACE("...joseki urgent move\n");
-    for (k = 0; k < my_ndragons; k++) {
-      add_strategical_defense_move(move, my_dragons[k]);
-      TRACE("...strategical defense of %1m\n", my_dragons[k]);
-    }
-    for (k = 0; k < your_ndragons; k++) {
-      add_strategical_attack_move(move, your_dragons[k]);
-      TRACE("...strategical attack on %1m\n", your_dragons[k]);
-    }
-    add_shape_value(move, 15);
-    TRACE("...shape value 15\n");
-    set_minimum_move_value(move, U_VALUE);
-    TRACE("...(min) move value %f\n", U_VALUE);
-  }
-
-  /* Pattern class T, joseki trick move. For the moment we never play these. */
-  if (class & CLASS_T) {
-    TRACE("...joseki trick move\n");
-    add_antisuji_move(move);
-    TRACE("...antisuji\n");
-  }
-
   /* Pattern class N, antisuji move. */
   if (class & CLASS_N) {
     TRACE("...antisuji move\n");
     add_antisuji_move(move);
   }
 
-  /* Shape value specified. */
-  if (class & VALUE_SHAPE) {
-    add_shape_value(move, pattern->shape);
-    TRACE("...shape value %f\n", pattern->shape);
-  }
+  handle_joseki_patterns(pattern->attributes, class, move,
+                        my_dragons, my_ndragons, your_dragons, your_ndragons);
 }
 
 
Index: patterns/aa_attackpats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/aa_attackpats.db,v
retrieving revision 1.20
diff -u -p -r1.20 aa_attackpats.db
--- patterns/aa_attackpats.db   24 Jan 2004 04:04:56 -0000      1.20
+++ patterns/aa_attackpats.db   29 Feb 2004 21:12:44 -0000
@@ -62,6 +62,8 @@
 #########################################################
 
 
+attribute_map none
+
 goal_elements Xx
 callback_data X
 
Index: patterns/attack.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/attack.db,v
retrieving revision 1.13
diff -u -p -r1.13 attack.db
--- patterns/attack.db  24 Jan 2004 04:04:56 -0000      1.13
+++ patterns/attack.db  29 Feb 2004 21:12:45 -0000
@@ -44,6 +44,8 @@
 ##################################################################
 
 
+attribute_map none
+
 goal_elements none
 callback_data X
 
Index: patterns/barriers.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/barriers.db,v
retrieving revision 1.62
diff -u -p -r1.62 barriers.db
--- patterns/barriers.db        24 Jan 2004 04:04:56 -0000      1.62
+++ patterns/barriers.db        29 Feb 2004 21:12:54 -0000
@@ -55,6 +55,8 @@
 # documentation for explanation and influence.c for details.
 
 
+attribute_map value_only
+
 goal_elements none
 callback_data XO,!
 
Index: patterns/conn.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/conn.db,v
retrieving revision 1.40
diff -u -p -r1.40 conn.db
--- patterns/conn.db    24 Jan 2004 04:04:56 -0000      1.40
+++ patterns/conn.db    29 Feb 2004 21:12:54 -0000
@@ -65,6 +65,8 @@
 ###################################
 
 
+attribute_map none
+
 goal_elements none
 # callback_data is dependent on pattern class in this database
 
Index: patterns/defense.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/defense.db,v
retrieving revision 1.14
diff -u -p -r1.14 defense.db
--- patterns/defense.db 24 Jan 2004 04:04:56 -0000      1.14
+++ patterns/defense.db 29 Feb 2004 21:12:54 -0000
@@ -44,6 +44,8 @@
 ##################################################################
 
 
+attribute_map none
+
 goal_elements none
 callback_data O
 
Index: patterns/endgame.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/endgame.db,v
retrieving revision 1.59
diff -u -p -r1.59 endgame.db
--- patterns/endgame.db 24 Jan 2004 04:04:56 -0000      1.59
+++ patterns/endgame.db 29 Feb 2004 21:12:54 -0000
@@ -102,6 +102,8 @@
 ######################################################################
 
 
+attribute_map general
+
 goal_elements none
 # Several patterns here have a or d class, so we need these elements.
 callback_data XOxo
Index: patterns/extract_fuseki.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/extract_fuseki.c,v
retrieving revision 1.21
diff -u -p -r1.21 extract_fuseki.c
--- patterns/extract_fuseki.c   25 Jan 2004 21:35:37 -0000      1.21
+++ patterns/extract_fuseki.c   29 Feb 2004 21:12:55 -0000
@@ -1204,7 +1204,9 @@ main(int argc, char *argv[])
      */
     generate_patterns();
     fprintf(stderr, "generate OK.\n");
-    
+
+    printf("attribute_map value_only\n\n\n");
+
     /* Write the patterns to stdout in patterns.db format.
      */
     print_patterns();
Index: patterns/fuseki.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/fuseki.db,v
retrieving revision 1.37
diff -u -p -r1.37 fuseki.db
--- patterns/fuseki.db  24 Jan 2004 04:04:56 -0000      1.37
+++ patterns/fuseki.db  29 Feb 2004 21:12:58 -0000
@@ -94,6 +94,8 @@
 ######################################################################
 
 
+attribute_map general
+
 goal_elements none
 callback_data XOxo
 
Index: patterns/fuseki13.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/fuseki13.db,v
retrieving revision 1.8
diff -u -p -r1.8 fuseki13.db
--- patterns/fuseki13.db        24 Jan 2004 04:04:56 -0000      1.8
+++ patterns/fuseki13.db        29 Feb 2004 21:13:21 -0000
@@ -37,6 +37,10 @@
 #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+
+attribute_map value_only
+
+
 Pattern Fuseki1
 # 799/1591
 
Index: patterns/fuseki19.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/fuseki19.db,v
retrieving revision 1.11
diff -u -p -r1.11 fuseki19.db
--- patterns/fuseki19.db        24 Jan 2004 04:04:57 -0000      1.11
+++ patterns/fuseki19.db        29 Feb 2004 21:13:38 -0000
@@ -41,6 +41,9 @@
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
 
+attribute_map value_only
+
+
 Pattern Fuseki1
 # 4924/6203
 
Index: patterns/fuseki9.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/fuseki9.db,v
retrieving revision 1.8
diff -u -p -r1.8 fuseki9.db
--- patterns/fuseki9.db 24 Jan 2004 04:04:57 -0000      1.8
+++ patterns/fuseki9.db 29 Feb 2004 21:13:47 -0000
@@ -40,6 +40,10 @@
 #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+
+attribute_map value_only
+
+
 Pattern Fuseki1
 # 504/1625
 
Index: patterns/gnugo-db.el
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/gnugo-db.el,v
retrieving revision 1.5
diff -u -p -r1.5 gnugo-db.el
--- patterns/gnugo-db.el        24 Jan 2004 04:04:57 -0000      1.5
+++ patterns/gnugo-db.el        29 Feb 2004 21:13:49 -0000
@@ -51,7 +51,8 @@
 
 (defvar gnugo-db-font-lock-keywords (list "Pattern"
                                          "goal_elements"
-                                         "callback_data"))
+                                         "callback_data"
+                                         "attribute_map"))
 
 
 (defun gnugo-db-mode()
Index: patterns/handicap.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/handicap.db,v
retrieving revision 1.6
diff -u -p -r1.6 handicap.db
--- patterns/handicap.db        24 Jan 2004 04:04:57 -0000      1.6
+++ patterns/handicap.db        29 Feb 2004 21:13:49 -0000
@@ -73,6 +73,8 @@
 #  cutoffs involved, see engine/handicap.c for details.
 
 
+attribute_map value_only
+
 goal_elements none
 callback_data !
 
Index: patterns/influence.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/influence.db,v
retrieving revision 1.12
diff -u -p -r1.12 influence.db
--- patterns/influence.db       24 Jan 2004 04:04:57 -0000      1.12
+++ patterns/influence.db       29 Feb 2004 21:13:50 -0000
@@ -38,6 +38,8 @@
 #  I - Invasion points.
 
 
+attribute_map value_only
+
 goal_elements none
 # callback_data is pattern class dependent for this database
 
Index: patterns/joseki.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/joseki.c,v
retrieving revision 1.21
diff -u -p -r1.21 joseki.c
--- patterns/joseki.c   25 Jan 2004 21:35:37 -0000      1.21
+++ patterns/joseki.c   29 Feb 2004 21:13:50 -0000
@@ -430,6 +430,7 @@ main(int argc, char *argv[])
 "
 
   printf(PREAMBLE);
+  printf("attribute_map general\n\n");
 
   /* Call the engine to setup and clear the board. */
   board_size = MAX_BOARD;
Index: patterns/mkpat.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/mkpat.c,v
retrieving revision 1.131
diff -u -p -r1.131 mkpat.c
--- patterns/mkpat.c    25 Jan 2004 21:35:37 -0000      1.131
+++ patterns/mkpat.c    29 Feb 2004 21:14:01 -0000
@@ -173,6 +173,8 @@ static char constraint_diagram[MAX_BOARD
 static char *prefix;
 static struct pattern pattern[MAXPATNO]; /* accumulate the patterns into here 
*/
 static char pattern_names[MAXPATNO][MAXNAME]; /* with optional names here, */
+static int num_attributes;
+static struct pattern_attribute attributes[MAXPATNO * NUM_ATTRIBUTES];
 static char helper_fn_names[MAXPATNO][MAXNAME]; /* helper fn names here */
 static char autohelper_code[MAXPATNO*300]; /* code for automatically generated 
*/
                                           /* helper functions here */
@@ -196,6 +198,69 @@ static const char *current_file = NULL;
 static int current_line_number = 0;
 
 
+struct attribute_description {
+  const char *input_name;      /* The name used in `.db' files. */
+  enum attribute_type type;
+};
+
+
+static const char *attribute_name[NUM_ATTRIBUTES + 1] = {
+  "MIN_VALUE",
+  "MAX_VALUE",
+  "MIN_TERRITORY",
+  "MAX_TERRITORY",
+  "SHAPE",
+  "FOLLOWUP",
+  "REVERSE_FOLLOWUP",
+  "THREATENS_TO_CAPTURE",
+  "THREATENS_EYE",
+  "REVERSE_SENTE",
+  "LAST_ATTRIBUTE"
+};
+
+
+/* Owl-style value stored in pattern itself. */
+#define IN_PATTERN_VALUE       NUM_ATTRIBUTES
+
+struct attribute_description general_attribute_map[] = {
+  { "value",           MIN_VALUE },
+  { "minvalue",                MIN_VALUE },
+  { "maxvalue",                MAX_VALUE },
+  { "terri",           MIN_TERRITORY },
+  { "minterri",                MIN_TERRITORY },
+  { "maxterri",                MAX_TERRITORY },
+  { "shape",           SHAPE },
+  { "followup",                FOLLOWUP },
+  { "followup_value",  FOLLOWUP },
+  { "reverse_followup",        REVERSE_FOLLOWUP },
+  { NULL,              LAST_ATTRIBUTE }
+};
+
+struct attribute_description value_only_attribute_map[] = {
+  { "value",           IN_PATTERN_VALUE },
+  { NULL,              LAST_ATTRIBUTE }
+};
+
+struct attribute_description owl_attack_attribute_map[] = {
+  { "value",                   IN_PATTERN_VALUE },
+  { "threatens_to_capture",    THREATENS_TO_CAPTURE },
+  { "threatens_eye",           THREATENS_EYE },
+  { "reverse_sente",           REVERSE_SENTE },
+  { NULL,                      LAST_ATTRIBUTE }
+};
+
+struct attribute_description owl_defense_attribute_map[] = {
+  { "value",                   IN_PATTERN_VALUE },
+  { "threatens_to_capture",    THREATENS_TO_CAPTURE },
+  { "threatens_eye",           THREATENS_EYE },
+  { "reverse_sente",           REVERSE_SENTE },
+  { NULL,                      LAST_ATTRIBUTE }
+};
+
+struct attribute_description *attribute_map = NULL;
+static int attributes_needed = 0;
+
+
 /* ================================================================ */
 /*                                                                  */
 /*                Autohelper function definitions                   */
@@ -979,12 +1044,45 @@ check_constraint_diagram_size(void)
            current_file, current_line_number, pattern_names[patno]);
     fatal_errors++;
   }
-}  
+}
+
+
+static void
+convert_attribute_labels_to_offsets(void)
+{
+  struct pattern_attribute *attribute;
+
+  if (patno < 0 || !pattern[patno].attributes)
+    return;
+
+  for (attribute = pattern[patno].attributes;
+       attribute->type != LAST_ATTRIBUTE;
+       attribute++) {
+    if (attribute->type >= FIRST_OFFSET_ATTRIBUTE) {
+      int label = attribute->offset;
+      int x;
+      int y;
+
+      if (label_coords[label][0] == -1) {
+       fprintf(stderr,
+               "%s(%d) : error : Pattern attribute uses label '%c' that wasn't 
specified in the diagram\n", 
+               current_file, current_line_number, label);
+       fatal_errors++;
+       return;
+      }
+
+      TRANSFORM2(label_coords[label][0] - ci - movei,
+                label_coords[label][1] - cj - movej, &x, &y,
+                labels_transformation);
+      attribute->offset = OFFSET(x, y);
+    }
+  }
+}
+
 
 /* On reading a line starting ':', finish up and write
  * out the current pattern 
  */
-
 static void
 finish_pattern(char *line)
 {
@@ -1081,7 +1179,6 @@ finish_pattern(char *line)
     char *p = line;
     char *p2;
     int n;
-    float v = 0.0;
     
     class[0] = 0;  /* in case sscanf doesn't get that far */
     s = sscanf(p, ":%c,%[^,]%n", &symmetry, class, &n);
@@ -1092,59 +1189,64 @@ finish_pattern(char *line)
       fatal_errors++;
     }
 
+    pattern[patno].attributes = NULL;
     while (sscanf(p, "%*[, ]%[^,]%n", entry, &n) > 0) {
+      const char *paren;
       p += n;
 
-      if (sscanf(entry, "%*[^(](%f)", &v) > 0) {
-       const char *corner_unsupported = NULL;
+      paren = strchr(entry, '(');
+      if (paren) {
+       struct attribute_description *description;
+
+       if (attribute_map) {
+         for (description = attribute_map; description->input_name;
+              description++) {
+           if (strncmp(entry, description->input_name, paren - entry) == 0) {
+             if (description->type != IN_PATTERN_VALUE) {
+               if (!pattern[patno].attributes)
+                 pattern[patno].attributes = attributes + num_attributes;
+
+               attributes[num_attributes].type = description->type;
+               if (description->type >= FIRST_OFFSET_ATTRIBUTE) {
+                 /* We store the label for now, since we don't know
+                  * its offset without having seen the constraint
+                  * diagram.
+                  */
+                 if (*(paren + 1) != '*'
+                     && !strchr(VALID_CONSTRAINT_LABELS, *(paren + 1))) {
+                   fprintf(stderr, "%s(%d) : error : '%c' is not a valid 
label.\n",
+                           current_file, current_line_number, *(paren + 1));
+                   fatal_errors++;
+                   continue;
+                 }
+
+                 attributes[num_attributes].offset = *(paren + 1);
+               }
+               else
+                 sscanf(paren + 1, "%f", &attributes[num_attributes].value);
 
-       if (strncmp(entry, "value", 5) == 0
-           || strncmp(entry, "minvalue", 8) == 0) {
-         pattern[patno].value = v;
-         pattern[patno].class |= VALUE_MINVAL;
-         corner_unsupported = "value";
-       }
-       else if (strncmp(entry, "maxvalue", 8) == 0) {
-         pattern[patno].maxvalue = v;
-         pattern[patno].class |= VALUE_MAXVAL;
-         corner_unsupported = "maxvalue";
-       }
-       else if (strncmp(entry, "terri", 5) == 0
-                || strncmp(entry, "minterri", 8) == 0) {
-         pattern[patno].minterritory = v;
-         pattern[patno].class |= VALUE_MINTERRITORY;
-         corner_unsupported = "terri";
-       }
-       else if (strncmp(entry, "maxterri", 8) == 0) {
-         pattern[patno].maxterritory = v;
-         pattern[patno].class |= VALUE_MAXTERRITORY;
-         corner_unsupported = "maxterri";
-       }
-       else if (strncmp(entry, "shape", 5) == 0) {
-         pattern[patno].shape = v;
-         pattern[patno].class |= VALUE_SHAPE;
-       }
-       else if (strncmp(entry, "followup", 8) == 0) {
-         pattern[patno].followup = v;
-         pattern[patno].class |= VALUE_FOLLOWUP;
-         corner_unsupported = "followup";
-       }
-       else if (strncmp(entry, "reverse_followup", 16) == 0) {
-         pattern[patno].reverse_followup = v;
-         pattern[patno].class |= VALUE_REV_FOLLOWUP;
-         corner_unsupported = "reverse_followup";
+               num_attributes++;
+             }
+             else
+               sscanf(paren + 1, "%f", &pattern[patno].value);
+
+             if (!strchr(paren + 1, ')')) {
+               fprintf(stderr, "%s(%d) : error : ')' missed\n",
+                       current_file, current_line_number);
+               fatal_errors++;
+             }
+
+             break;
+           }
+         }
        }
-       else {
+
+       if (attribute_map == NULL || description->input_name == NULL) {
          fprintf(stderr, "%s(%d) : error : Unknown value field: %s\n",
-                  current_file, current_line_number, entry);
-          fatal_errors++;
+                 current_file, current_line_number, entry);
+         fatal_errors++;
          break;
        }
-
-       if (database_type == DB_CORNER && corner_unsupported) {
-         fprintf(stderr, "%s(%d) : warning : `%s' is unsupported in corner 
databases\n",
-                 current_file, current_line_number, corner_unsupported);
-       }
       }
       else {
        strncpy(helper_fn_names[patno], entry, 79);
@@ -1152,6 +1254,12 @@ finish_pattern(char *line)
       }
     }
 
+    if (pattern[patno].attributes != NULL) {
+      attributes[num_attributes].type = LAST_ATTRIBUTE;
+      attributes[num_attributes].value = 0.0;
+      num_attributes++;
+    }
+
     for (p2 = class; *p2; p2++) {
       switch (*p2) {
        case 's': pattern[patno].class |= CLASS_s; break;
@@ -2601,7 +2709,43 @@ corner_write_variations(struct corner_va
 }
 
 
-/* sort and write out the patterns */
+static void
+write_attributes(FILE *outfile)
+{
+  int k;
+
+  fprintf(outfile, "static struct pattern_attribute attributes[] = {\n");
+  fprintf(outfile, "#ifdef __GNUC__\n");
+
+  for (k = 0; k < num_attributes; k++) {
+    fprintf(outfile, "  {%s,", attribute_name[attributes[k].type]);
+    if (attributes[k].type >= FIRST_OFFSET_ATTRIBUTE)
+      fprintf(outfile, "{.offset=%d}}", attributes[k].offset);
+    else
+      fprintf(outfile, "{.value=%f}}", attributes[k].value);
+
+    if (k != num_attributes - 1)
+      fprintf(outfile, ",\n");
+  }
+
+  fprintf(outfile, "\n#else\n");
+
+  for (k = 0; k < num_attributes; k++) {
+    fprintf(outfile, "  {%s,", attribute_name[attributes[k].type]);
+    if (attributes[k].type >= FIRST_OFFSET_ATTRIBUTE)
+      fprintf(outfile, "0.0,%d}", attributes[k].offset);
+    else
+      fprintf(outfile, "%f,0}", attributes[k].value);
+
+    if (k != num_attributes - 1)
+      fprintf(outfile, ",\n");
+  }
+
+  fprintf(outfile,"\n#endif\n};\n\n");
+}
+
+
+/* Sort and write out the patterns. */
 static void
 write_patterns(FILE *outfile)
 {
@@ -2634,10 +2778,19 @@ write_patterns(FILE *outfile)
     }
 
     if (database_type == DB_CORNER) {
-      fprintf(outfile, "  {%d,%d,0x%x,\"%s\",%f,%d,",
+      fprintf(outfile, "  {%d,%d,0x%x,\"%s\",",
              second_corner_offset[j], (p->trfno == 4),
-             p->class, pattern_names[j], p->shape,
-             p->autohelper_flag);
+             p->class, pattern_names[j]);
+
+      if (attributes_needed) {
+       fprintf(outfile, "attributes+%d,",
+               p->attributes ? p->attributes - attributes : 0);
+      }
+      else
+       fprintf(outfile, "NULL,");
+
+      fprintf(outfile, "%d,", p->autohelper_flag);
+
       if (p->autohelper)
        fprintf(outfile, "autohelper%s%d}", prefix, j);
       else
@@ -2684,17 +2837,17 @@ write_patterns(FILE *outfile)
     fprintf(outfile, "}\n   ");
 #endif
 
-    fprintf(outfile, ", 0x%x,%f,%f,%f,%f,%f,%f,%f,%d,%s,",
-           p->class,
-           p->value,
-           p->maxvalue,
-           p->minterritory,
-           p->maxterritory,
-           p->shape,
-           p->followup,
-           p->reverse_followup,
-           p->autohelper_flag,
-           helper_fn_names[j]);
+    fprintf(outfile, ", 0x%x,%f,", p->class, p->value);
+
+    if (attributes_needed) {
+      fprintf(outfile, "attributes+%d,",
+             p->attributes ? p->attributes - attributes : 0);
+    }
+    else
+      fprintf(outfile, "NULL,");
+
+    fprintf(outfile, "%d,%s,", p->autohelper_flag, helper_fn_names[j]);
+
     if (p->autohelper)
       fprintf(outfile, "autohelper%s%d", prefix, j);
     else
@@ -2722,10 +2875,9 @@ write_patterns(FILE *outfile)
 #if GRID_OPT
   fprintf(outfile, ",{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}");
 #endif
-  fprintf(outfile, ",0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0,NULL,NULL,0,0.0");
+  fprintf(outfile, ",0,0.0,NULL,0,NULL,NULL,0,0.0");
 #if PROFILE_PATTERNS
-  fprintf(outfile, ",0,0");
-  fprintf(outfile, ",0");
+  fprintf(outfile, ",0,0,0");
 #endif
   fprintf(outfile, "}\n};\n");
 }
@@ -2936,6 +3088,9 @@ main(int argc, char *argv[])
   autohelper_code[0] = 0;
   code_pos = autohelper_code;
 
+  num_attributes = 1;
+  attributes[0].type = LAST_ATTRIBUTE;
+
   /* Parse the input file, output pattern elements as as we find them,
    * and store pattern entries for later output.
    *
@@ -2996,6 +3151,8 @@ main(int argc, char *argv[])
        command = 2;
       else if (sscanf(line, "callback_data %s", command_data) == 1)
        command = 3;
+      else if (sscanf(line, "attribute_map %s", command_data) == 1)
+       command = 4;
 
       if (command) {
        char *p = strpbrk(command_data, " \t");
@@ -3017,9 +3174,25 @@ main(int argc, char *argv[])
            break;
          case 5:
          case 6:
-           fprintf(stderr,
-                   "%s(%d) : Warning: constraint diagram but no constraint 
line for pattern %s\n",
-                   current_file, current_line_number, pattern_names[patno]);
+           {
+             struct pattern_attribute *attribute = NULL;
+
+             if (pattern[patno].attributes) {
+               for (attribute = pattern[patno].attributes;
+                    attribute->type != LAST_ATTRIBUTE;
+                    attribute++) {
+                 if (attribute->type >= FIRST_OFFSET_ATTRIBUTE)
+                   break;
+               }
+             }
+
+             if (attribute == NULL || attribute->type == LAST_ATTRIBUTE) {
+               fprintf(stderr,
+                       "%s(%d) : Warning: constraint diagram but no constraint 
line or offset attributes for pattern %s\n",
+                       current_file, current_line_number, 
pattern_names[patno]);
+             }
+           }
+
            break;
          case 7:
          case 8:
@@ -3032,8 +3205,10 @@ main(int argc, char *argv[])
        }
 
        if (command == 1) { /* Pattern `name' */
-         if (!discard_pattern)
+         if (!discard_pattern) {
+           convert_attribute_labels_to_offsets();
            patno++;
+         }
          else
            code_pos = save_code_pos;
          reset_pattern();
@@ -3050,7 +3225,7 @@ main(int argc, char *argv[])
          state = 1;
          discard_pattern = 0;
        }
-       else {
+       else if (command == 2 || command == 3) {
          int *sort_out = (command == 2 ? nongoal : callback_unneeded);
          int k;
 
@@ -3091,6 +3266,48 @@ main(int argc, char *argv[])
 
          state = 0;
        }
+       else {
+         struct attribute_description *old_map = attribute_map;
+
+         if (strcmp(command_data, "general") == 0) {
+           attribute_map = general_attribute_map;
+           attributes_needed = 1;
+         }
+         else if (strcmp(command_data, "value_only") == 0) {
+           attribute_map = value_only_attribute_map;
+           attributes_needed = 0;
+         }
+         else if (strcmp(command_data, "owl_attack") == 0) {
+           attribute_map = owl_attack_attribute_map;
+           attributes_needed = 1;
+         }
+         else if (strcmp(command_data, "owl_defense") == 0) {
+           attribute_map = owl_defense_attribute_map;
+           attributes_needed = 1;
+         }
+         else if (strcmp(command_data, "none") == 0) {
+           attribute_map = NULL;
+           attributes_needed = 0;
+         }
+         else {
+           fprintf(stderr, "%s(%d) : Error : unknown attribute map `%s'",
+                   current_file, current_line_number, command_data);
+           fatal_errors++;
+         }
+
+         if (patno != -1 && attribute_map != old_map) {
+           fprintf(stderr, "%s(%d) : Error : attribute map can only be set 
before the first pattern\n",
+                   current_file, current_line_number);
+           fatal_errors++;
+         }
+
+         if (attributes_needed
+             && (database_type == DB_FULLBOARD || database_type == DB_TREE)) {
+           fprintf(stderr, "%s(%d) : Error : attributes other than `value' are 
not allowed in fullboard and tree databases\n",
+                   current_file, current_line_number);
+           fatal_errors++;
+         }
+       }
       }
       else if (line[0] == '\n' || line[0] == '#') { 
        /* nothing */
@@ -3190,6 +3407,8 @@ main(int argc, char *argv[])
       }
     } /* while not EOF */
   } /* for each file */
+
+  convert_attribute_labels_to_offsets();
   
   if (patno >= 0) {
     switch (state) {
@@ -3242,6 +3461,11 @@ main(int argc, char *argv[])
 
     /* Write the autohelper code. */
     fprintf(output_FILE, "%s", autohelper_code);
+
+    if (attributes_needed)
+      write_attributes(output_FILE);
+    else
+      assert(num_attributes == 1);
 
     write_patterns(output_FILE);
 
Index: patterns/oracle.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/oracle.db,v
retrieving revision 1.9
diff -u -p -r1.9 oracle.db
--- patterns/oracle.db  24 Jan 2004 04:04:57 -0000      1.9
+++ patterns/oracle.db  29 Feb 2004 21:14:01 -0000
@@ -28,6 +28,9 @@
 # Nadare joseki variations
 
 
+attribute_map value_only
+
+
 Pattern O1
 
 +-------
Index: patterns/owl_attackpats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/owl_attackpats.db,v
retrieving revision 1.107
diff -u -p -r1.107 owl_attackpats.db
--- patterns/owl_attackpats.db  4 Feb 2004 16:07:37 -0000       1.107
+++ patterns/owl_attackpats.db  29 Feb 2004 21:14:20 -0000
@@ -81,7 +81,13 @@
 # A14xx Invasion patterns
 # A15xx Ko patterns
 # A16xx Specific edge attacks
+#
+# ACxx  Threats to capture
+# AExx  Threats to destroy eyes
+# ARxx  Reverse sente attacking moves
+
 
+attribute_map owl_attack
 
 goal_elements Xx
 callback_data none
@@ -6211,6 +6217,65 @@ OX*X
 ----
 
 :8,s,value(75)
+
+
+#########################################################
+#                                                       #
+#                 Threats to capture                   #
+#                                                       #
+#########################################################
+
+
+Pattern AC01
+# pp New pattern (3.5.5).
+
+xXO        threaten a snapback
+x.Y
+*.X
+---
+
+:8,cs, threatens_to_capture(A), value(25)
+
+xXB
+x.A
+*.A
+---
+
+; lib(A) == 2 && lib(B) > 1 && olib(*) > 1
+
+
+#########################################################
+#                                                       #
+#               Threats to destroy eyes                #
+#                                                       #
+#########################################################
+
+
+Pattern AE01
+# pp New pattern (3.5.5).
+
+|OXx       threaten to destroy the eye in ko
+|X.X
+|.Yx
+|.*.
++---
+
+:8,cs, threatens_eye(a), value(20)
+
+|BXx
+|XaX
+|.Yx
+|.*.
++---
+
+; owl_proper_eye(a) && !attack(B)
+
+
+#########################################################
+#                                                       #
+#            Reverse sente attacking moves             #
+#                                                       #
+#########################################################
 
 
 # END OF FILE
Index: patterns/owl_defendpats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/owl_defendpats.db,v
retrieving revision 1.114
diff -u -p -r1.114 owl_defendpats.db
--- patterns/owl_defendpats.db  4 Feb 2004 16:07:37 -0000       1.114
+++ patterns/owl_defendpats.db  29 Feb 2004 21:14:20 -0000
@@ -81,7 +81,13 @@
 # D12xx Kikashi
 # D13xx Escape
 # D14xx Ko
+#
+# DCxx  Threats to capture
+# DExx  Threats to create eyes
+# DRxx  Reverse sente defending moves
+
 
+attribute_map owl_defense
 
 goal_elements Oo
 callback_data none
@@ -7364,6 +7403,174 @@ Pattern D1424
 +---
 
 :8,-,value(45)
+
+
+#########################################################
+#                                                       #
+#                 Threats to capture                   #
+#                                                       #
+#########################################################
+
+
+Pattern DC01a
+# pp New pattern (3.5.5).
+
+???        threaten to cut off vital chain
+X.X
+O*?
+
+:8,c, threatens_to_capture(A), value(35)
+
+abc
+AdX
+O*?
+
+; o_somewhere(a,b,c) && vital_chain(A) && !oplay_defend(*,?,d,A)
+
+
+Pattern DC01b
+# pp New pattern (3.5.5).
+
+???        threaten to cut off vital chain
+X.X
+O*?
+
+:8,c, threatens_to_capture(B), value(35)
+
+abc
+XdB
+O*?
+
+; o_somewhere(a,b,c) && vital_chain(B) && !oplay_defend(*,?,d,B)
+
+
+Pattern DC02
+# pp New pattern (3.5.5) -- see ld_owl:316.
+
+OX.?
+OX..
+.*..
+----
+
+:8,c, threatens_to_capture(A), value(35)
+
+ABb?
+AB..
+.*..
+----
+
+; lib(A) >= 3 && lib(B) == 3 && olib(b) > 2
+
+
+#########################################################
+#                                                       #
+#               Threats to create eyes                 #
+#                                                       #
+#########################################################
+
+
+Pattern DE01a
+# pp New pattern (3.5.5).
+
+?O*?
+....
+xOOO
+
+:8,c, threatens_eye(a), value(30)
+
+?O*?
+..a.
+xOOO
+
+
+Pattern DE01b
+# pp New pattern (3.5.5).
+
+?O*?
+....
+OOOo
+
+:8,c, threatens_eye(b), value(30)
+
+?O*?
+.b..
+OOOo
+
+
+Pattern DE02
+# pp New pattenr (3.5.5).
+
+?O*
+O..
+---
+
+:8,c, threatens_eye(a), value(30)
+
+bO*
+Oa.
+---
+
+; o_somewhere(b)
+; || (x_somewhere(b) ? !oplay_defend(*,b) : !oplay_defend(*,b,b))
+
+
+Pattern DE03
+# pp New pattern (3.5.5) -- see owl1:326.
+
+OO?
+..*
+---
+
+:8,c, threatens_eye(a), value(30)
+
+OOb
+.a*
+---
+
+; o_somewhere(b)
+; || (x_somewhere(b) ? !oplay_defend(*,b) : !oplay_defend(*,b,b))
+
+
+Pattern DE04
+# pp New pattern (3.5.5) -- see owl1:326.
+
+xXOo?
+XO.O?
+X.OX*
+-----
+
+:8,c, threatens_eye(a), value(30)
+
+xXOo?
+XOaO?
+X.OX*
+-----
+
+
+#########################################################
+#                                                       #
+#            Reverse sente defending moves             #
+#                                                       #
+#########################################################
+
+
+Pattern DR01
+# pp New pattern (3.5.5) -- see ld_owl:316.
+# FIXME: most likely need variations for other first line moves.
+
+?.OX?      prevent X's sente move at 'a'
+..OXx
+...*.
+-----
+
+:8,c, reverse_sente(a), value(30)
+
+?.OB?
+.aOBx
+...*.
+-----
+
+; owl_proper_eye(a) && !attack(B)
 
 
 # END OF FILE
Index: patterns/owl_vital_apats.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/owl_vital_apats.db,v
retrieving revision 1.46
diff -u -p -r1.46 owl_vital_apats.db
--- patterns/owl_vital_apats.db 28 Jan 2004 12:22:50 -0000      1.46
+++ patterns/owl_vital_apats.db 29 Feb 2004 21:14:20 -0000
@@ -62,6 +62,8 @@
 # 
 
 
+attribute_map value_only
+
 goal_elements Xx
 callback_data !
 
Index: patterns/patterns.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/patterns.db,v
retrieving revision 1.124
diff -u -p -r1.124 patterns.db
--- patterns/patterns.db        24 Jan 2004 04:04:57 -0000      1.124
+++ patterns/patterns.db        29 Feb 2004 21:14:35 -0000
@@ -85,6 +85,8 @@
 #
 
 
+attribute_map general
+
 goal_elements none
 # FIXME: try to make callback_data pattern category specific
 callback_data XOxo
Index: patterns/patterns.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/patterns.h,v
retrieving revision 1.61
diff -u -p -r1.61 patterns.h
--- patterns/patterns.h 24 Jan 2004 04:04:57 -0000      1.61
+++ patterns/patterns.h 29 Feb 2004 21:14:37 -0000
@@ -125,8 +125,9 @@ typedef int (*autohelper_fn_ptr)(int rot
 #define CLASS_s     0x0010   /* move is a sacrifice */
 #define CLASS_n     0x0020   /* X could also make this move if we do not */
 #define CLASS_D     0x0040   /* defense pattern */
-#define CLASS_C     0x0080   /* move connects two worms */ 
+#define CLASS_C     0x0080   /* move connects two worms */
 #define CLASS_c     0x0100   /* move weakly connects two worms */ 
+                            /* for owl databases: combinable pattern */
 #define CLASS_B     0x0200   /* move breaks connection between enemy worms */
 #define CLASS_A     0x0400   /* attack pattern */
 #define CLASS_b     0x0800   /* move is intended to block opponent */
@@ -151,20 +152,6 @@ typedef int (*autohelper_fn_ptr)(int rot
                            CLASS_J | CLASS_j | CLASS_U | CLASS_T | CLASS_t | \
                             CLASS_W | CLASS_c | CLASS_F)
 
-/* Values associated with patterns. Stored together with classes. */
-#define VALUE_MINVAL       0x01000000 /* pattern has a minimum value */
-#define VALUE_MAXVAL       0x02000000 /* pattern has a maximum value */
-#define VALUE_MINTERRITORY 0x04000000 /* pattern has a min territorial value */
-#define VALUE_MAXTERRITORY 0x08000000 /* pattern has a max territorial value */
-#define VALUE_SHAPE        0x10000000 /* pattern has a shape value */
-#define VALUE_FOLLOWUP     0x20000000 /* pattern has a followup value */
-#define VALUE_REV_FOLLOWUP 0x40000000 /* pattern has a reverse followup value 
*/
-
-/* Collection of the classes inducing move values. */
-#define CLASS_MOVE_VALUES (VALUE_MINVAL | VALUE_MAXVAL | VALUE_MINTERRITORY \
-                          | VALUE_MAXTERRITORY | VALUE_SHAPE \
-                          | VALUE_FOLLOWUP | VALUE_REV_FOLLOWUP)
-
 /* directions for applying edge-constraints */
 #define NORTH_EDGE 1
 #define SOUTH_EDGE 2
@@ -195,10 +182,53 @@ typedef struct patval_b {
 } Patval_b;
 
 
+enum attribute_type {
+  MIN_VALUE,
+  MAX_VALUE,
+  MIN_TERRITORY,
+  MAX_TERRITORY,
+  SHAPE,
+  FOLLOWUP,
+  REVERSE_FOLLOWUP,
+
+  /* For `mkpat'. */
+  FIRST_OFFSET_ATTRIBUTE,
+
+  THREATENS_TO_CAPTURE = FIRST_OFFSET_ATTRIBUTE,
+  THREATENS_EYE,
+  REVERSE_SENTE,
+
+  NUM_ATTRIBUTES,
+  LAST_ATTRIBUTE = NUM_ATTRIBUTES
+};
+
+
+#ifdef __GNUC__
+
+struct pattern_attribute {
+  enum attribute_type type;
+
+  /* GCC allows unnamed (and transparent) unions. */
+  union {
+    float value;
+    int offset;
+  };
+};
+
+#else
+
+struct pattern_attribute {
+  enum attribute_type type;
+  float value;
+  int offset;
+};
+
+#endif
+
+
 /*
  * Each pattern as a whole is compiled to an instance of this structure.
  */
-
 struct pattern {
   struct patval *patn;  /* array of elements */
   int patlen;           /* number of elements */
@@ -219,13 +249,14 @@ struct pattern {
 #endif
 
   unsigned int class;   /* classification of pattern */
-  float value;          /* value for pattern, if matched */
-  float maxvalue;
-  float minterritory;
-  float maxterritory;
-  float shape;
-  float followup;
-  float reverse_followup;
+
+  /* Value (owl-style, used for pattern sorting) is not stored as an
+   * attribute, because it is very common.
+   */
+  float value;
+
+  /* Pattern attributes like shape, followup etc. */
+  struct pattern_attribute *attributes;
 
   int autohelper_flag;  /* whether autohelper has constraint and/or action */
   pattern_helper_fn_ptr helper;  /* helper function, or NULL */
@@ -406,7 +437,8 @@ struct corner_pattern {
   unsigned int class;  /* Pattern class. */
   const char *name;    /* Pattern name (optional). */
 
-  float shape;         /* Pattern shape value. */
+  /* Pattern attributes like shape (the only one used currently). */
+  struct pattern_attribute *attributes;
 
   int autohelper_flag; /* Whether autohelper has constraint and/or action. */
   autohelper_fn_ptr autohelper; /* Automatically generated helper (or NULL). */
Index: patterns/patterns2.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/patterns2.db,v
retrieving revision 1.70
diff -u -p -r1.70 patterns2.db
--- patterns/patterns2.db       24 Jan 2004 04:04:57 -0000      1.70
+++ patterns/patterns2.db       29 Feb 2004 21:14:40 -0000
@@ -39,6 +39,8 @@
 ##############################
 
 
+attribute_map general
+
 goal_elements none
 # FIXME: try to make callback_data pattern category specific
 callback_data XOxo
Index: patterns/read_attack.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/read_attack.db,v
retrieving revision 1.12
diff -u -p -r1.12 read_attack.db
--- patterns/read_attack.db     24 Jan 2004 04:04:57 -0000      1.12
+++ patterns/read_attack.db     29 Feb 2004 21:14:42 -0000
@@ -25,6 +25,10 @@
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
 
+# FIXME: Pattern-based reading code is broken.  This makes it even more so.
+attribute_map general
+
+
 # thoughts:
 #  Need some way to specify two patterns, one on this end, one on that end.
 #  Need some better backfill & superstring helpers.
Index: patterns/read_defend.db
===================================================================
RCS file: /cvsroot/gnugo/gnugo/patterns/read_defend.db,v
retrieving revision 1.7
diff -u -p -r1.7 read_defend.db
--- patterns/read_defend.db     24 Jan 2004 04:04:57 -0000      1.7
+++ patterns/read_defend.db     29 Feb 2004 21:14:42 -0000
@@ -24,6 +24,11 @@
 #
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
 
+
+# FIXME: Pattern-based reading code is broken.  This makes it even more so.
+attribute_map general
+
+
 ##Pattern RD001
 ##
 ##X*




reply via email to

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