home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / gnushogi-1.1 / src / pattern.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-22  |  17.0 KB  |  731 lines

  1. /*
  2.  * pattern.c - C source for GNU SHOGI
  3.  *
  4.  * Copyright (c) 1993 Matthias Mutz
  5.  *
  6.  * GNU SHOGI is based on GNU CHESS
  7.  *
  8.  * Copyright (c) 1988,1989,1990 John Stanback
  9.  * Copyright (c) 1992 Free Software Foundation
  10.  *
  11.  * This file is part of GNU SHOGI.
  12.  *
  13.  * GNU Shogi is free software; you can redistribute it and/or modify
  14.  * it under the terms of the GNU General Public License as published by
  15.  * the Free Software Foundation.
  16.  *
  17.  * GNU Shogi is distributed in the hope that it will be useful,
  18.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20.  * GNU General Public License for more details.
  21.  *
  22.  * You should have received a copy of the GNU General Public License
  23.  * along with GNU Shogi; see the file COPYING.  If not, write to
  24.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  25.  */
  26.  
  27. #include "gnushogi.h"
  28.  
  29. #include "pattern.h"
  30.  
  31.  
  32. OpeningPattern *Patterns;
  33.  
  34. #ifdef NONDSP
  35. static long allocated_bytes;
  36. #endif
  37.  
  38.  
  39.  
  40. #define is_digit(c) ((c) >= '0' && (c) <= '9')
  41. #define is_alfa(c) ((c) >= 'a' && (c) <= 'z' || (c) >= 'A' && (c) <= 'Z')
  42.  
  43. #define eos(s) (*s == '\0' || *s == '\n')
  44.  
  45.  
  46. /* skip blanks and comments in brackets */
  47.  
  48. static void skipbb(char **s) 
  49.  { while (**s == ' ' || **s == '|' || **s == '[') { 
  50.      if ( **s == '[' ) 
  51.        while (**s != ']') (*s)++; 
  52.      (*s)++; 
  53.    } \
  54.  }  
  55.                         
  56. /* skip unsigned numbers */
  57.  
  58. static void skipi(char **s)
  59.  { 
  60.    while ( is_digit(**s) ) 
  61.      (*s)++;
  62.    skipbb(s);
  63.  }
  64.  
  65.  
  66. inline
  67. static
  68. short
  69. ScanPiece (char **s, small_short *side, small_short *piece, small_short *square)
  70. {
  71.   short isp, isw, c, r;
  72.   
  73.   /* determine promotion status */
  74.   if ( **s == '+' )
  75.     isp = true, (*s)++;
  76.   else
  77.     isp = false;
  78.   /* determine side and piece */
  79.   for (c = 0; c < NO_PIECES; c++)
  80.     if ((isw = (**s == pxx[c])) || **s == qxx[c])
  81.       {
  82.     *piece = isp ? promoted[c] : unpromoted[c];
  83.     *side  = isw;
  84.     (*s)++; 
  85.     break;
  86.       } 
  87.   if (c == NO_PIECES) 
  88.     return(1);
  89.   if ( **s == '*' )
  90.     {
  91.       /* piece is captured */ 
  92.       (*s)++;
  93.       *square = NO_SQUARES + *piece;
  94.     }
  95.   else
  96.     {        
  97.       /* determine column */
  98.       for (c = 0; c < NO_COLS; c++)
  99.         if (**s == cxx[c])
  100.           {
  101.         (*s)++; 
  102.         break;
  103.           }
  104.       if (c == NO_COLS) 
  105.         return(1);
  106.       /* determine row */
  107.       for (r = 0; r < NO_ROWS; r++)
  108.         if (**s == rxx[r])
  109.           {
  110.         (*s)++;
  111.         break;
  112.           }
  113.       if (r == NO_ROWS) return(1);
  114.       /* determine square */
  115.       *square = r*NO_COLS + c;
  116.     }
  117.   skipbb(s); 
  118.   return(0);
  119. }
  120.  
  121.  
  122. short
  123. string_to_board_color
  124.  (char *s)
  125.  
  126. {
  127.   skipbb(&s); 
  128.   while ( !eos(s))
  129.     { small_short side, piece, square;
  130.       if ( ScanPiece(&s, &side, &piece, &square) )
  131.     return(1);
  132.       else
  133.     {             
  134.       if ( square >= NO_SQUARES )
  135.         {
  136.           Captured[side][piece]++;
  137.         }
  138.       else
  139.         {
  140.           Pindex[square] = ++PieceCnt[side];                 
  141.           PieceList[side][Pindex[square]] = square;
  142.           color[square] = side;
  143.           board[square] = piece;
  144.         }
  145.     }
  146.     }
  147.   return(0); 
  148. }
  149.  
  150.  
  151. short
  152. string_to_patternfields
  153.  (char *s, PatternFields *patternfields)
  154.  
  155. {
  156.   patternfields->n = 0;
  157.   skipbb(&s); 
  158.   while ( !eos(s) )
  159.     { short c, r, isp, isw;
  160.       PatternField *field;
  161.       field = &patternfields->field[patternfields->n];
  162.       if ( ScanPiece(&s, &field->side, &field->piece, &field->square) )
  163.     return(1);
  164.       else
  165.     if (patternfields->n++ == MAX_PATTERN) 
  166.       return(1);
  167.     }
  168.   return(0); 
  169. }
  170.                            
  171.  
  172. char *patternfile = PATTERNFILE;
  173.                
  174.  
  175.  
  176. static
  177. short
  178. ScanSequencePattern (char *s, PatternSequence **sequence)
  179. {         
  180.     PatternSequence *pattern;
  181.     if ( *sequence = pattern = (PatternSequence *) malloc (sizeof(PatternSequence)) ) {
  182. #ifdef NONDSP
  183.       allocated_bytes += sizeof(PatternSequence);
  184. #endif
  185.       pattern->next_pattern = NULL;
  186.       pattern->reachedGameCnt[black] = MAXMOVES;  
  187.       pattern->reachedGameCnt[white] = MAXMOVES;  
  188.       pattern->n = 0;
  189.       skipbb(&s); /* skip blanks and comments */
  190.       while ( is_digit(*s) )
  191.         {                             
  192.       pattern->next[pattern->n] = atoi(s);
  193.           pattern->n++;
  194.       skipi(&s);
  195.         }
  196.       if ( string_to_patternfields(s,&pattern->patternfields) )
  197.         return(1);
  198.       else
  199.         return(0);
  200.     } else {
  201.       ShowMessage("cannot allocate pattern space...");
  202.       return(1);
  203.     }
  204. }
  205.                                        
  206.  
  207.  
  208. void
  209. GetOpeningPatterns ()
  210.  
  211.     FILE *fd;
  212.     char s[256];
  213.     OpeningPattern *p, *q;
  214.     short count = 0;
  215.  
  216. #ifdef NONDSP                                      
  217.     allocated_bytes = 0;
  218. #endif
  219.  
  220.     if ( (fd = fopen (patternfile, "r")) == NULL )
  221.     fd = fopen ("gnushogi.pattern", "r");       
  222.  
  223.     if ( fd != NULL )
  224.       {
  225.     PatternSequence **current_pattern = NULL; 
  226.     Patterns = p = q = NULL;
  227.     while ( fgets (s, 256, fd) != NULL )
  228.       {
  229.         if ( *s == '#' )
  230.           { /* comment, skip line */
  231.           } 
  232.         else if ( is_alfa(*s) )
  233.           {
  234.         if ( p = (OpeningPattern *) malloc (sizeof(OpeningPattern)) )
  235.           { char *pn, *ps;
  236. #ifdef NONDSP
  237.                   allocated_bytes += sizeof(OpeningPattern);
  238. #endif
  239.             if ( Patterns == NULL )
  240.               Patterns = q = p;
  241.             else
  242.               q = q->next = p;
  243.             p->next = NULL;
  244.             p->n = 0;
  245.             p->sequence = NULL;
  246.             current_pattern = &p->sequence;
  247.                     for (pn=p->name,ps=s; !eos(ps); pn++,ps++)
  248.             *pn = *ps;
  249.                     *pn = '\0';
  250.           }
  251.         else
  252.           {
  253.             ShowMessage("cannot allocate opening sequence space...");
  254.             exit(1);
  255.           }
  256.           }
  257.         else
  258.           {
  259.         if ( ScanSequencePattern(s,current_pattern) )
  260.           {
  261.             ShowMessage("error in pattern sequence...");
  262.             exit(1);
  263.           }
  264.         else
  265.           {  
  266.             current_pattern = &(*current_pattern)->next_pattern;
  267.             p->n++;
  268.             count++;
  269.           }
  270.           }
  271.       }
  272. #ifdef NONDSP
  273.     printf("Pattern used %d entries, %ld bytes allocated.\n",
  274.             count, allocated_bytes);
  275. #endif  
  276.     fclose(fd);
  277.       }
  278. #ifdef NONDSP
  279.     else
  280.       {     
  281.         printf("no pattern file '%s'",patternfile);
  282.       }      
  283. #endif
  284.  
  285. }
  286.                             
  287.  
  288.  
  289.  
  290. void
  291. ShowOpeningPatterns ()
  292.  
  293. { OpeningPattern *root = Patterns;
  294.   while (root != NULL)
  295.     { short i;
  296.       PatternSequence *sequence;
  297.       printf("Opening line: %s number of patterns %d\n",root->name,root->n);
  298.       for (i=0,sequence = root->sequence; sequence; i++,sequence = sequence->next_pattern)
  299.     { short j;
  300.       printf("%d successor patterns:",sequence->n);
  301.       for (j = 0; j < sequence->n; j++) {
  302.         printf(" %d",sequence->next[j]);
  303.       }
  304.       printf(" pattern %d:\n",i);
  305.           DisplayPattern(&sequence->patternfields);
  306.     }
  307.       root = root->next;
  308.     }
  309. }
  310.  
  311.  
  312.  
  313. short
  314. piece_to_pattern_distance 
  315.   (short side, short piece, short pside, PatternFields *pattern)
  316.  
  317. /*
  318.  * Determine the minimum number of moves from the current position
  319.  * to a specific pattern for a specific piece.
  320.  * Consider the "side" piece of the pattern.
  321.  * The pattern should match for "pside".
  322.  */
  323. {
  324.   short nP, P[4], nB, B[4]; /* at most 4 pieces of same kind */
  325.   short i, j, r, dd, occupied, mindd, c[4], d[4];
  326.   
  327.   /*
  328.    * If pside == white, a black piece in the pattern should match
  329.    * a white piece on board, and vice versa. Furthermore, if
  330.    * pside == white, reversed pattern should match board.
  331.    */
  332.  
  333.   /* special pawn handling */     
  334.  
  335.   if ( piece == pawn ) {
  336.     mindd = occupied = 0; 
  337.     for ( i = 0; i < pattern->n; i++ ) {
  338.       PatternField field = pattern->field[i];
  339.       if ( (field.side == side) && (field.piece == pawn) ) {
  340.     short t = field.square;
  341.         short pcol = column(t);                    
  342.     dd = CANNOT_REACH;
  343.         for ( j = 0; j <= PieceCnt[side ^ pside]; j++) {
  344.       short sq = (short)PieceList[side ^ pside][j];
  345.       if ( board[sq] == pawn ) {
  346.         if ( pside == white ) sq = NO_SQUARES - 1 - sq;
  347.             if ( column(sq) == pcol ) {
  348.           dd = piece_distance (side, pawn, sq, t);
  349. #ifdef TEST_PATTERN
  350.               printf("update %d pawn from %d to %d is %d\n", side, sq, t, dd);
  351. #endif                                       
  352.           if ( dd != CANNOT_REACH ) {
  353.         /* Increment distance if pattern field is occupied */
  354.         short psq, pc;
  355.               if ( pside == black ) {
  356.               psq = t;
  357.           pc = field.side;
  358.         } else {
  359.           psq = (NO_SQUARES - 1 - t);
  360.           pc = ~field.side;
  361.         }
  362.         if ( (color[psq] == pc) && (board[psq] != pawn) ) {
  363. #ifdef TEST_PATTERN
  364.                   printf("square %d is occupied\n", psq);
  365. #endif                                       
  366.           ++occupied;            
  367.         }
  368.           }
  369.               break;
  370.         } 
  371.       }
  372.         }
  373.         if ( dd == CANNOT_REACH )
  374.       return (CANNOT_REACH);
  375.     else
  376.       mindd += dd;
  377.       }
  378.     }
  379.     return (mindd + occupied);
  380.   }
  381.  
  382.   /* 
  383.    * Determine list of "side" "piece"s in pattern. 
  384.    */
  385.  
  386.   for ( occupied = nP = i = 0; i < pattern->n; i++ ) {
  387.         PatternField field = pattern->field[i];
  388.     if ( (field.side == side) && (field.piece == piece) ) {
  389.       short psq, pc;
  390.       P[nP] = field.square;
  391. #ifdef TEST_PATTERN
  392.           printf("pattern %d piece %d on square %d\n",side,piece,P[nP]);
  393. #endif
  394.       nP++;
  395.       /* Increment distance if pattern field is occupied */
  396.           if ( pside == black ) {
  397.         psq = field.square;
  398.             pc = field.side;
  399.           } else {
  400.         psq = (NO_SQUARES - 1 - field.square);
  401.         pc = field.side ^ 1;
  402.           }
  403.       if ( (color[psq] == pc) && (board[psq] != field.piece) ) {
  404. #ifdef TEST_PATTERN
  405.             printf("square %d is occupied\n", psq);
  406. #endif                                       
  407.         ++occupied;            
  408.       }
  409.     }
  410.   }
  411.  
  412.   if ( nP == 0 )
  413.     return (0);
  414.  
  415. #ifdef TEST_PATTERN
  416.   printf("finding in pattern %d pieces %d of side %d\n", nP, piece, side);
  417. #endif
  418.  
  419.   /* 
  420.    * Determine list of "side ^ pside" "piece"s on board. 
  421.    */
  422.  
  423.   for ( nB = 0, i = 0; i <= PieceCnt[side ^ pside]; i++ ) {
  424.     short sq = PieceList[side ^ pside][i];
  425.     if ( board[sq] == piece ) {
  426.       B[nB] = (pside == black) ? sq : (NO_SQUARES - 1 - sq);
  427. #ifdef TEST_PATTERN
  428.       printf("%d piece %d on square %d\n",side,piece,B[nB]);
  429. #endif
  430.       nB++;
  431.     }
  432.   }
  433.  
  434. #ifdef TEST_PATTERN
  435.   printf("found on board %d pieces %d of side %d\n", nB, piece, side);
  436. #endif
  437.  
  438.   if ( nP > nB ) {
  439.       return (CANNOT_REACH);
  440.   }
  441.  
  442.   /* Determine best assignment from board piece to pattern piece */
  443.  
  444.   r = 0; c[0] = -1; mindd = CANNOT_REACH;
  445.  
  446.   while ( (r >= 0) && (mindd != 0) ) {
  447.  
  448.     if ( ++c[r] == nB ) {
  449.     r--;
  450.     } else {
  451.     for ( i = 0; i < r; i++ )
  452.       if ( c[i] == c[r] )
  453.         break;
  454.     if ( i == r ) {
  455.       d[r] =  piece_distance (side, piece, B[c[r]], P[r]);
  456. #ifdef TEST_PATTERN
  457.           printf("update d[%d] from  %d to %d is %d\n", r, B[c[r]], P[r], d[r]);
  458. #endif
  459.       if ( d[r] == CANNOT_REACH ) {
  460.         /* r--; */
  461.       } else {
  462.         if ( ++r == nP ) {
  463.         for (dd = i = 0; i < nP; i++)
  464.           dd += d[i];
  465.         if ( (dd < mindd) || (mindd == CANNOT_REACH) ) {
  466.           mindd = dd;        
  467. #ifdef TEST_PATTERN
  468.                   printf("update min %d\n", mindd);
  469. #endif
  470.         }
  471.         r--;
  472.         } else
  473.         c[r] = -1;
  474.       }
  475.     }
  476.     }
  477.  
  478.   }
  479.                
  480.   if ( mindd == CANNOT_REACH )
  481.     return (CANNOT_REACH);
  482.   else
  483.     return (mindd + occupied);
  484.  
  485. }
  486.  
  487.  
  488. short
  489. pattern_distance (short pside, PatternFields *pattern)
  490.  
  491. /*
  492.  * Determine the minimum number of moves for the pieces from
  493.  * the current position to reach a pattern.
  494.  * The result is CANNOT_REACH, if there is no possible sequence
  495.  * of moves.
  496.  */
  497.  
  498. {
  499.    short side, piece, d, n;
  500.  
  501. #ifdef TEST_PATTERN
  502.    printf("\nchecking pattern for pside=%d\n\n",pside);
  503. #endif
  504.  
  505.    for ( n = side = 0; side <= 1 && n != CANNOT_REACH; side++ )
  506.      for ( piece = pawn; piece <= king; piece++ ) {
  507.     d = piece_to_pattern_distance (side, piece, pside, pattern);
  508.     if ( d == CANNOT_REACH) {
  509.       n = CANNOT_REACH; break;
  510.     } else
  511.       n += d;
  512.      }
  513.  
  514. #ifdef TEST_PATTERN
  515.    printf("\ndistance to pattern is %d\n\n",n);
  516. #endif
  517.  
  518.    return (n);
  519.     
  520. }
  521.  
  522.  
  523. short
  524. board_to_pattern_distance 
  525.   (short pside, OpeningPattern *opattern, short pmplty, short GameCnt)
  526.  
  527. /*
  528.  * Determine the maximal difference of the number of moves from the pattern 
  529.  * to the initial position and to the current position.
  530.  * Differences are weighted, i.e. the more closer a position is to a pattern
  531.  * the more valuable is a move towards the pattern.
  532.  * Patterns, which are at least "pmplty" halfmoves away, are not counted.
  533.  */
  534.  
  535. {
  536.    short d, dist, diff;
  537.    short maxdiff = 0;
  538.    PatternSequence *sequence;
  539.  
  540.    for ( sequence = opattern->sequence; sequence; sequence = sequence->next_pattern )
  541.      if ( ((d = sequence->distance[pside]) != CANNOT_REACH) && (pmplty > d) )
  542.        { 
  543.          if ( (dist = pattern_distance (pside, &sequence->patternfields)) != CANNOT_REACH )
  544.            { 
  545.          /* "dist" is the distance of the current board position to the pattern.
  546.           * "d - dist" is the difference between the current distance and the 
  547.           * initial distance. Compute "diff" as the weighted difference.
  548.           */
  549.          if ( (diff = (d - dist) * (pmplty - d)) > maxdiff )
  550.         maxdiff = diff;
  551.          /* A reached pattern should not be considered in the future (if GameCnt >= 0) */
  552.          if ( dist == 0 && GameCnt >= 0)
  553.         sequence->reachedGameCnt[pside] = GameCnt;
  554.            }
  555.        }
  556.       
  557.    return (maxdiff);
  558.     
  559. }
  560.  
  561.  
  562.  
  563. void
  564. DisplayPattern (PatternFields *pattern)
  565.  
  566. {
  567.   small_short pboard[NO_SQUARES], pcolor[NO_SQUARES];
  568.   short sq, i, r, c;
  569.  
  570.   for (sq = 0; sq < NO_SQUARES; sq++)
  571.     {
  572.       pboard[sq] = no_piece;
  573.       pcolor[sq] = neutral;
  574.     }
  575.  
  576.   for (i = 0; i < pattern->n; i++)
  577.     {
  578.       PatternField *field = &pattern->field[i];
  579.       pboard[field->square] = field->piece;
  580.       pcolor[field->square] = field->side; 
  581.     }
  582.  
  583.   for (r = NO_ROWS-1; r >= 0; r--)
  584.     {
  585.       for (c = 0; c < NO_COLS; c++)
  586.         {
  587.       sq = r*NO_COLS + c;
  588.       i = pboard[sq];
  589.       if ( i == no_piece )
  590.         printf(" -");
  591.       else
  592.         printf("%c%c",is_promoted[i]?'+':' ',pcolor[sq]?pxx[i]:qxx[i]);
  593.         }
  594.       printf("\n");
  595.     }
  596.  
  597.   printf("\n");
  598.  
  599. }                 
  600.  
  601.  
  602. static
  603. void
  604. RemoveReachable (short pside, OpeningPattern *p, short n)
  605. {
  606.   short i,j;
  607.   PatternSequence *sequence;
  608.  
  609.   /* Adjust to sequence pattern j */
  610.   for (i=0,sequence=p->sequence; i<n; i++)
  611.     sequence=sequence->next_pattern;
  612.  
  613.   /* do not perform visited link twice */
  614.   if ( sequence->visited )
  615.       return;
  616.   else
  617.       sequence->visited = true;
  618.  
  619.   /* Declare links unreachable */
  620.   for (j=0; j<sequence->n; j++)
  621.     RemoveReachable(pside,p,sequence->next[j]);
  622.  
  623.   /* Declare unreachable */
  624.   sequence->distance[pside] = CANNOT_REACH;
  625.  
  626. #ifdef DEBUG_PATTERN
  627.   printf("removing %d\n",n);
  628. #endif
  629.  
  630. }      
  631.  
  632.  
  633. OpeningPattern 
  634. *locate_opening_pattern(short pside, char *s, short GameCnt)
  635.  
  636.    OpeningPattern *p;
  637.    PatternSequence *sequence;
  638.    short i, j, removed;
  639. #ifdef DEBUG_PATTERN
  640.    short n = 0, m = 0;
  641. #endif    
  642.  
  643.   /* 
  644.    * Look for opening pattern name in the list of opening patterns.
  645.    */
  646.  
  647.    for ( p = Patterns; p != NULL; p = p->next )
  648.      {                    
  649.        if ( strcmp(s,p->name) == 0 )
  650.          break;
  651.      }
  652.  
  653.    if ( p == NULL )
  654.      return(NULL);     
  655.  
  656. #ifdef DEBUG_PATTERN                             
  657.    printf("%s uses opening line %s\n",ColorStr[pside],p->name);
  658. #endif
  659.  
  660.   /*
  661.    * Determine patterns which can be reached from the current
  662.    * board position. Only patterns which can be reached will be
  663.    * checked in the following search.
  664.    */
  665.  
  666.    for ( i=0,sequence=p->sequence; sequence; i++,sequence=sequence->next_pattern )
  667.      {
  668.        sequence->distance[pside] = pattern_distance(pside,&sequence->patternfields);
  669.        sequence->visited = false;
  670.        /* Actually reached patterns need not to be observed. */
  671.        if ( sequence->distance[pside] == 0 ) {
  672.      sequence->distance[pside] = CANNOT_REACH;
  673. #ifdef DEBUG_PATTERN
  674.          printf("pattern %d removed because reached\n",i);
  675. #endif
  676.        } else if ( GameCnt >= 0 && GameCnt >= sequence->reachedGameCnt[pside] ) {
  677.      sequence->distance[pside] = CANNOT_REACH; 
  678. #ifdef DEBUG_PATTERN
  679.          printf("pattern %d removed because reached at GameCnt %d below current %d\n",
  680.             i,sequence->reachedGameCnt[pside],GameCnt);
  681. #endif
  682.        }
  683.        if ( sequence->reachedGameCnt[pside] >= GameCnt )
  684.          sequence->reachedGameCnt[pside] = MAXMOVES;
  685.      }            
  686.   /*
  687.    * Remove reachable patterns from search, which are successors of
  688.    * reachable patterns. So, only the next pattern of a pattern sequence
  689.    * is observed. 
  690.    */                    
  691.  
  692.    for (sequence=p->sequence; sequence; sequence=sequence->next_pattern)
  693.      if ( sequence->distance[pside] != CANNOT_REACH ) {
  694.        for (j=0; j<sequence->n; j++) {
  695. #ifdef DEBUG_PATTERN
  696.          printf("removing successors for link %d\n",sequence->next[j]);
  697. #endif
  698.  
  699.          RemoveReachable(pside,p,sequence->next[j]);  
  700.        }
  701.      }
  702.  
  703.   /*
  704.    * Look, whether there is still a reachable pattern.
  705.    */
  706.  
  707.    for ( sequence=p->sequence; sequence; sequence=sequence->next_pattern )
  708.      if ( sequence->distance[pside] != CANNOT_REACH )
  709.        {
  710. #ifdef DEBUG_PATTERN
  711.          for (n=0,m=0,sequence=p->sequence; sequence; sequence=sequence->next_pattern,n++)
  712.             if ( sequence->distance[pside] != CANNOT_REACH )
  713.           m++;
  714.      printf("%d reachable %s patterns out of %d patterns\n",
  715.             m,ColorStr[pside],n);
  716. #endif
  717.          return(p);
  718.        }
  719.  
  720. #ifdef DEBUG_PATTERN
  721.    printf("all %s patterns out of %d patterns (%d reachable) removed\n",
  722.         ColorStr[pside],n,m);
  723. #endif
  724.  
  725.    return (NULL);
  726. }
  727.  
  728.  
  729.