Switch build to automake, building gnuminishogi officially.
[gnushogi.git] / gnushogi / pattern.c
1 /*
2  * FILE: pattern.c
3  *
4  * ----------------------------------------------------------------------
5  * Copyright (c) 1993, 1994, 1995 Matthias Mutz
6  * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
7  *
8  * GNU SHOGI is based on GNU CHESS
9  *
10  * Copyright (c) 1988, 1989, 1990 John Stanback
11  * Copyright (c) 1992 Free Software Foundation
12  *
13  * This file is part of GNU SHOGI.
14  *
15  * GNU Shogi is free software; you can redistribute it and/or modify it
16  * under the terms of the GNU General Public License as published by the
17  * Free Software Foundation; either version 3 of the License,
18  * or (at your option) any later version.
19  *
20  * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
21  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23  * for more details.
24  *
25  * You should have received a copy of the GNU General Public License along
26  * with GNU Shogi; see the file COPYING. If not, see
27  * <http://www.gnu.org/licenses/>.
28  * ----------------------------------------------------------------------
29  *
30  */
31
32 #include "gnushogi.h"
33 #include "pattern.h"
34
35 /* constants and pattern_data are generated by "pat2inc" */
36 #ifndef MINISHOGI
37 # include "gnushogi-pattern.inc"
38 #else
39 # include "gnuminishogi-pattern.inc"
40 #endif
41
42 struct Pattern_rec Pattern[MAX_PATTERN];
43 struct OpeningSequence_rec OpeningSequence[MAX_OPENING_SEQUENCE];
44
45 small_short pattern_data[MAX_PATTERN_DATA];
46
47
48 static void
49 NameOfOpeningValue (short i, char *name)
50 {
51     if (i < 100)
52     {
53         strcpy(name, "CASTLE_?_?");
54     }
55     else
56     {
57         strcpy(name, "ATTACK_?_?");
58         i -= 100;
59     }
60
61     switch (i / 10)
62     {
63     case 1:
64         name[7] = 'S';
65         break;
66
67     case 2:
68         name[7] = 'R';
69         break;
70
71     case 3:
72         name[7] = 'U';
73         break;
74
75     default:
76         name[7] = '*';
77         break;
78     }
79
80     switch (i % 10)
81     {
82     case 1:
83         name[9] = 'S';
84         break;
85
86     case 2:
87         name[9] = 'R';
88         break;
89
90     case 3:
91         name[9] = 'U';
92         break;
93
94     default:
95         name[9] = '*';
96         break;
97     }
98 }
99
100
101 void
102 GetOpeningPatterns (short *max_opening_sequence)
103 {
104     short pindex = 0;
105     short os = 0;
106     short p = 0;
107     short i;
108
109     do
110     {
111         OpeningSequence[os].opening_type = pattern_data[pindex++];
112         OpeningSequence[os].first_pattern[0] = p;
113
114         for (i = 1; i < MAX_SEQUENCE; i++)
115             OpeningSequence[os].first_pattern[i] = END_OF_PATTERNS;
116
117         do
118         {
119             Pattern[p].reachedGameCnt[black] = MAXMOVES;
120             Pattern[p].reachedGameCnt[white] = MAXMOVES;
121             Pattern[p].first_link = pindex;
122
123             while (pattern_data[pindex] != END_OF_LINKS)
124                 pindex++;
125             pindex++;
126
127             Pattern[p].first_field = pindex;
128
129             while (pattern_data[pindex] != END_OF_FIELDS)
130                 pindex += 2;
131             pindex++;
132
133             if (pattern_data[pindex] != END_OF_PATTERNS)
134                 Pattern[p].next_pattern = p + 1;
135             else
136                 Pattern[p].next_pattern = END_OF_PATTERNS;
137
138             p++;
139         }
140         while (pattern_data[pindex] != END_OF_PATTERNS);
141
142         pindex++;
143         os++;
144     }
145     while (pattern_data[pindex] != END_OF_SEQUENCES);
146
147     *max_opening_sequence = os;
148 }
149
150
151
152 void
153 ShowOpeningPatterns (short max_opening_sequence)
154 {
155     short os, p, n, i;
156
157     for (os = 0; os < max_opening_sequence; os++)
158     {
159         char name[16];
160         NameOfOpeningValue(OpeningSequence[os].opening_type, name);
161         printf("Opening Type: %s\n", name);
162
163         for (p = OpeningSequence[os].first_pattern[0], n = 0;
164              p != END_OF_PATTERNS;
165              p = Pattern[p].next_pattern, n++)
166         {
167             printf("Pattern %d (%d) with links ", p, n);
168
169             for (i = Pattern[p].first_link;
170                  pattern_data[i] != END_OF_LINKS;
171                  i++)
172             {
173                 printf("%d ", pattern_data[i]);
174             }
175
176             printf("\n");
177             DisplayPattern(stdout, Pattern[p].first_field);
178         }
179     }
180 }
181
182
183
184 void
185 set_field (short i, struct PatternField *field)
186 {
187     field->piece  = pattern_data[i];
188     field->square = pattern_data[i+1];
189
190     if (field->square < 0)
191     {
192         field->square = -(field->square);
193         field->side   = white;
194     }
195     else
196     {
197         field->side  = black;
198     }
199 }
200
201
202
203 /*
204  * piece_to_pattern_distance (side, piece, pside, pattern)
205  *
206  * Determine the minimum number of moves from the current position to a
207  * specific pattern for a specific piece.  Consider the "side" piece of the
208  * pattern.  The pattern should match for "pside".
209  */
210
211 short
212 piece_to_pattern_distance(short side, short piece,
213                           short pside, short pattern)
214 {
215     short nP, P[4], nB, B[4]; /* at most 4 pieces of same kind */
216     short i, j, r, dd, occupied, mindd, c[4], d[4];
217     /* a "side" patternfield must match a "c1" piece on board: */
218     short c1 = side ^ pside;
219
220     /*
221      * If pside == white, a black piece in the pattern should match a white
222      * piece on board, and vice versa. Furthermore, if pside == white,
223      * reversed pattern should match board.
224      */
225
226     /* special pawn handling */
227
228     if (piece == pawn)
229     {
230         mindd = occupied = 0;
231
232         for (i = Pattern[pattern].first_field;
233              pattern_data[i] != END_OF_FIELDS;
234              i += 2)
235         {
236             struct PatternField field;
237             set_field(i, &field);
238
239             if ((field.side == side) && (field.piece == pawn))
240             {
241                 short t = field.square;
242                 short pcol = column(t);
243                 dd = CANNOT_REACH;
244
245                 if (PawnCnt[c1][(side == c1) ? pcol : (8 - pcol)])
246                 {
247                     /* there is a pawn on the column */
248                     for (j = 0; j <= PieceCnt[c1]; j++)
249                     {
250                         short sq = (short)PieceList[c1][j];
251
252                         if (board[sq] == pawn)
253                         {
254                             if (pside == white)
255                                 sq = NO_SQUARES - 1 - sq;
256
257                             if (column(sq) == pcol)
258                             {
259                                 dd = piece_distance (side, pawn, sq, t);
260 #ifdef TEST_PATTERN
261                                 printf("update %d pawn "
262                                        "from %d to %d is %d\n",
263                                        side, sq, t, dd);
264 #endif
265                                 break;
266                             }
267                         }
268                     }
269                 }
270                 else
271                 {
272                     /* there is no pawn on the column; drop possible? */
273                     if (Captured[c1][pawn])
274                     {
275                         dd = 1;
276 #ifdef TEST_PATTERN
277                         printf("update %d pawn drop to %d is %d\n",
278                                side, t, dd);
279 #endif
280                     }
281                 }
282
283                 if (dd >= 0)
284                 {
285                     /* Increment distance if pattern field is occupied */
286                     short psq, pc;
287
288                     if (pside == black)
289                     {
290                         psq = t;
291                         pc = field.side;
292                     }
293                     else
294                     {
295                         psq = (NO_SQUARES - 1 - t);
296                         pc = ~field.side;
297                     }
298
299                     if ((color[psq] == pc) && (board[psq] != pawn))
300                     {
301 #ifdef TEST_PATTERN
302                         printf("square %d is occupied\n", psq);
303 #endif
304                         ++occupied;
305                     }
306
307                     mindd += dd;
308                 }
309                 else
310                 {
311                     return CANNOT_REACH;
312                 }
313             }
314         }
315
316         return mindd + occupied;
317     }
318
319     /*
320      * Determine list of "side" "piece"s in pattern.
321      */
322
323     for (occupied = nP = 0, i = Pattern[pattern].first_field;
324          pattern_data[i] != END_OF_FIELDS;
325          i += 2)
326     {
327         struct PatternField field;
328         set_field(i, &field);
329
330         if ((field.side == side) && (field.piece == piece))
331         {
332             short psq, pc;
333             P[nP] = field.square;
334 #ifdef TEST_PATTERN
335             printf("pattern %d piece %d on square %d\n", side, piece, P[nP]);
336 #endif
337             nP++;
338
339             /* Increment distance if pattern field is occupied */
340             if (pside == black)
341             {
342                 psq = field.square;
343                 pc = field.side;
344             }
345             else
346             {
347                 psq = NO_SQUARES - 1 - field.square;
348                 pc  = field.side ^ 1;
349             }
350
351             if ((color[psq] == pc) && (board[psq] != field.piece))
352             {
353 #ifdef TEST_PATTERN
354                 printf("square %d is occupied\n", psq);
355 #endif
356                 ++occupied;
357             }
358         }
359     }
360
361     if (nP == 0)
362         return 0;
363
364 #ifdef TEST_PATTERN
365     printf("finding in pattern %d pieces %d of side %d\n", nP, piece, side);
366 #endif
367
368     /*
369      * Determine list of "side ^ pside" "piece"s captured or on board.
370      */
371
372     for (nB = 0; nB < Captured[c1][piece]; nB++)
373         B[nB] = NO_SQUARES + piece;
374
375     for (i = 0; i <= PieceCnt[c1]; i++)
376     {
377         short sq = PieceList[c1][i];
378
379         if (board[sq] == piece)
380         {
381             B[nB] = (pside == black) ? sq : (NO_SQUARES - 1 - sq);
382 #ifdef TEST_PATTERN
383             printf("%d piece %d on square %d\n", side, piece, B[nB]);
384 #endif
385             nB++;
386         }
387     }
388
389 #ifdef TEST_PATTERN
390     printf("found on board %d pieces %d of side %d\n", nB, piece, side);
391 #endif
392
393     if (nP > nB)
394     {
395         return CANNOT_REACH;
396     }
397
398     /* Determine best assignment from board piece to pattern piece */
399
400     r = 0;
401     c[0] = -1;
402     mindd = CANNOT_REACH;
403
404     while ((r >= 0) && (mindd != 0))
405     {
406
407         if (++c[r] == nB)
408         {
409             r--;
410         }
411         else
412         {
413             for (i = 0; i < r; i++)
414             {
415                 if (c[i] == c[r])
416                     break;
417             }
418
419             if (i == r)
420             {
421                 d[r] =  piece_distance (side, piece, B[c[r]], P[r]);
422 #ifdef TEST_PATTERN
423                 printf("update d[%d] from  %d to %d is %d\n",
424                        r, B[c[r]], P[r], d[r]);
425 #endif
426                 if (d[r] < 0)
427                 {
428                     /* r--; */
429                 }
430                 else
431                 {
432                     if (++r == nP)
433                     {
434                         for (dd = i = 0; i < nP; i++)
435                             dd += d[i];
436
437                         if ((dd < mindd) || (mindd < 0))
438                         {
439                             mindd = dd;
440 #ifdef TEST_PATTERN
441                             printf("update min %d\n", mindd);
442 #endif
443                         }
444
445                         r--;
446                     }
447                     else
448                     {
449                         c[r] = -1;
450                     }
451                 }
452             }
453         }
454     }
455
456     if (mindd < 0)
457         return CANNOT_REACH;
458     else
459         return (mindd + occupied);
460
461 }
462
463
464
465 /*
466  * pattern_distance (pside, pattern)
467  *
468  * Determine the minimum number of moves for the pieces from
469  * the current position to reach a pattern.
470  * The result is CANNOT_REACH, if there is no possible sequence
471  * of moves.
472  *
473  */
474
475 short
476 pattern_distance (short pside, short pattern)
477 {
478     short side, piece, d, n;
479
480 #ifdef TEST_PATTERN
481     printf("\nchecking pattern %d for pside=%d\n\n", pattern, pside);
482 #endif
483
484     for (n = side = 0; side <= 1 && n >= 0; side++)
485     {
486         for (piece = pawn; piece <= king; piece++)
487         {
488             d = piece_to_pattern_distance (side, piece, pside, pattern);
489
490             if (d < 0)
491             {
492                 n = CANNOT_REACH;
493                 break;
494             }
495             else
496             {
497                 n += d;
498             }
499         }
500     }
501
502 #ifdef TEST_PATTERN
503     printf("\ndistance to pattern is %d\n\n", n);
504 #endif
505
506     return n;
507 }
508
509
510
511 /*
512  * board_to_pattern_distance(pside, osequence, pmplty, GameCnt)
513  *
514  * Determine the maximal difference of the number of moves from the pattern
515  * to the initial position and to the current position.
516  * Differences are weighted, i.e. the more closer a position is to a pattern
517  * the more valuable is a move towards the pattern.
518  * Patterns, which are at least "pmplty" halfmoves away, are not counted.
519  */
520
521 short
522 board_to_pattern_distance
523 (short pside, short osequence, short pmplty, short GameCnt)
524 {
525     short i, d, dist, diff, weighted_diff;
526     short maxdiff = 0, max_weighted_diff = 0;
527     short pattern;
528
529     for (i = 0; i < MAX_SEQUENCE; i++)
530     {
531         for (pattern = OpeningSequence[osequence].first_pattern[i];
532              pattern != END_OF_PATTERNS;
533              pattern = Pattern[pattern].next_pattern)
534         {
535             if ((d = Pattern[pattern].distance[pside]) >= 0)
536             {
537                 if (pmplty > d)
538                 {
539                     dist = pattern_distance (pside, pattern);
540                     if (dist >= 0)
541                     {
542                         /*
543                          * "dist" is the distance of the current board
544                          * position to the pattern.  "d - dist" is the
545                          * difference between the current distance and the
546                          * initial distance. Compute "diff" as the weighted
547                          * difference.
548                          */
549
550                         /* try to reach the nearest pattern */
551                         weighted_diff = (diff = (d - dist)) * (pmplty - d);
552
553                         if (weighted_diff > max_weighted_diff)
554                         {
555 #ifdef COUNT_DIFF
556                             maxdiff = diff;
557 #else
558                             maxdiff = weighted_diff;
559 #endif
560                             max_weighted_diff = weighted_diff;
561                         }
562
563                         /*
564                          * A reached pattern should not be considered in
565                          * the future (if GameCnt >= 0)
566                          */
567
568                         if (dist == 0 && GameCnt >= 0)
569                             Pattern[pattern].reachedGameCnt[pside] = GameCnt;
570                     }
571                 }
572             }
573         }
574     }
575
576     return maxdiff;
577 }
578
579
580
581
582 void
583 DisplayPattern (FILE *fd, short n)
584 {
585     small_short pboard[NO_SQUARES], pcolor[NO_SQUARES];
586     short sq, i,  r, c;
587
588     for (sq = 0; sq < NO_SQUARES; sq++)
589     {
590         pboard[sq] = no_piece;
591         pcolor[sq] = neutral;
592     }
593
594     for (i = n; pattern_data[i] != END_OF_FIELDS; i += 2)
595     {
596         struct PatternField field;
597         set_field(i, &field);
598         pboard[field.square] = field.piece;
599         pcolor[field.square] = field.side;
600     }
601
602     for (r = NO_ROWS - 1; r >= 0; r--)
603     {
604         for (c = 0; c < NO_COLS; c++)
605         {
606             sq = r*NO_COLS + c;
607             i = pboard[sq];
608
609             if (i == no_piece)
610                 fprintf(fd, " -");
611             else
612                 fprintf(fd, "%c%c", is_promoted[i] ? '+' : ' ',
613                         pcolor[sq] ? pxx[i] : qxx[i]);
614         }
615
616         fprintf(fd, "\n");
617     }
618
619     fprintf(fd, "\n");
620 }
621
622
623
624
625 static void
626 VisitReachable (int pside, short osequence, int k, int n, int remove)
627 {
628     short i, j;
629     short pattern;
630
631     /* Adjust to sequence pattern n */
632     for (i = 0, pattern = OpeningSequence[osequence].first_pattern[k];
633          i < n; i++)
634     {
635         pattern = Pattern[pattern].next_pattern;
636     }
637
638     /* do not perform visited link twice */
639     if (Pattern[pattern].visited)
640     {
641         return;
642     }
643     else
644     {
645         Pattern[pattern].visited = true;
646     }
647
648     /* Declare links unreachable */
649     for (j = Pattern[pattern].first_link;
650          pattern_data[j] != END_OF_LINKS; j++)
651     {
652         VisitReachable(pside, osequence, k, pattern_data[j], remove);
653     }
654
655     /* Declare unreachable */
656     if (remove && Pattern[pattern].distance[pside] >= 0)
657     {
658         Pattern[pattern].distance[pside] = IS_SUCCESSOR;
659     }
660 }
661
662
663 /* simplified matching for opening type names */
664
665 #define match_char(a, b) \
666 (a == b || (a == '*' && b != 'U') || (b == '*' && a != 'U'))
667
668 #define match_name(a, b, l) \
669 (l > 8 && match_char(a[0], b[0]) && match_char(a[7], b[7]) \
670 && match_char(a[9], b[9]))
671
672
673 short
674 locate_opening_sequence(short pside, char *s, short GameCnt)
675 {
676     short i, j, k, os, d;
677     short l = strlen(s);
678     short check_visited[MAX_SEQUENCE];
679     char name[MAX_NAME], name2[MAX_NAME];
680
681     /*
682      * Look for opening pattern name in the list of opening patterns.
683      */
684
685     name[0] = '\0';
686
687     for (i = 1, os = 0; os < MAX_OPENING_SEQUENCE; os++)
688     {
689         /* locate matching opening type name */
690         NameOfOpeningValue(OpeningSequence[os].opening_type, name);
691
692         if (match_name(s, name, l))
693         {
694             /* locate successor matching names */
695             for (k = os + 1; k < MAX_OPENING_SEQUENCE; k++)
696             {
697                 NameOfOpeningValue(OpeningSequence[k].opening_type, name2);
698
699                 if (match_name(s, name2, l))
700                 {
701                     OpeningSequence[os].first_pattern[i++]
702                         = OpeningSequence[k].first_pattern[0];
703                 }
704             }
705
706             break;
707         }
708     }
709
710     if (os >= MAX_OPENING_SEQUENCE)
711     {
712         return END_OF_SEQUENCES;
713     }
714     else
715     {
716         for (; i < MAX_SEQUENCE;
717              OpeningSequence[os].first_pattern[i++] = END_OF_PATTERNS);
718     }
719
720     /*
721      * Determine patterns which can be reached from the current
722      * board position. Only patterns which can be reached will be
723      * checked in the following search.
724      */
725
726     for (i = 0; i < MAX_SEQUENCE; i++)
727     {
728         check_visited[i] = false;
729
730         for (k = OpeningSequence[os].first_pattern[i];
731              k != END_OF_PATTERNS;
732              k = Pattern[k].next_pattern)
733         {
734             Pattern[k].visited = false;
735         }
736     }
737
738     for (i = 0; i < MAX_SEQUENCE; i++)
739     {
740         for (k = OpeningSequence[os].first_pattern[i];
741              k != END_OF_PATTERNS;
742              k = Pattern[k].next_pattern)
743         {
744             Pattern[k].distance[pside] = pattern_distance(pside, k);
745
746             /* Actually reached patterns need not to be observed. */
747             if (Pattern[k].distance[pside] == 0)
748             {
749                 Pattern[k].distance[pside] = CANNOT_REACH;
750                 check_visited[i] = Pattern[k].visited = true;
751
752                 for (j = Pattern[k].first_link;
753                      pattern_data[j] != END_OF_LINKS; j++)
754                 {
755                     VisitReachable(pside, os, i, pattern_data[j], false);
756                 }
757             }
758             else if ((GameCnt >= 0)
759                      && (GameCnt >= Pattern[k].reachedGameCnt[pside]))
760             {
761                 Pattern[k].distance[pside] = IS_REACHED;
762             }
763
764             if (Pattern[k].reachedGameCnt[pside] >= GameCnt)
765                 Pattern[k].reachedGameCnt[pside] = MAXMOVES;
766         }
767     }
768
769     /*
770      * Remove reachable patterns from search, which are successors of
771      * reachable patterns. So, only the next pattern of a pattern sequence
772      * is observed.
773      */
774
775     for (i = 0; i < MAX_SEQUENCE; i++)
776     {
777         for (k = OpeningSequence[os].first_pattern[i];
778              k != END_OF_PATTERNS;
779              k = Pattern[k].next_pattern)
780         {
781             if (check_visited[i] && !Pattern[k].visited)
782                 Pattern[k].distance[pside] = NOT_TO_REACH;
783             else
784                 Pattern[k].visited = false;
785         }
786     }
787
788     for (i = 0; i < MAX_SEQUENCE; i++)
789     {
790         for (k = OpeningSequence[os].first_pattern[i];
791              k != END_OF_PATTERNS;
792              k = Pattern[k].next_pattern)
793         {
794             if ((d = Pattern[k].distance[pside]) >= 0)
795             {
796                 for (j = Pattern[k].first_link;
797                      pattern_data[j] != END_OF_LINKS; j++)
798                 {
799                     VisitReachable(pside, os, i, pattern_data[j], true);
800                 }
801             }
802         }
803     }
804
805     /*
806      * Look to see whether there is still a reachable pattern.
807      */
808
809     for (i = 0; i < MAX_SEQUENCE; i++)
810     {
811         for (k = OpeningSequence[os].first_pattern[i];
812              k != END_OF_PATTERNS;
813              k = Pattern[k].next_pattern)
814         {
815             if ((d = Pattern[k].distance[pside]) >= 0)
816                 return os;
817         }
818     }
819
820     return END_OF_SEQUENCES;
821 }
822
823
824
825
826 void
827 update_advance_bonus (short pside, short os)
828 {
829     struct PatternField field;
830     short i, j, k, d;
831
832     for (j = 0; j < MAX_SEQUENCE; j++)
833     {
834         for (k = OpeningSequence[os].first_pattern[j];
835              k != END_OF_PATTERNS;
836              k = Pattern[k].next_pattern)
837         {
838             if ((d = Pattern[k].distance[pside]) >= 0)
839             {
840                 for (i = Pattern[k].first_field;
841                      pattern_data[i] != END_OF_FIELDS; i += 2)
842                 {
843                     set_field(i, &field);
844                     if (field.side == black)
845                     {
846                         short square = (pside == black)
847                             ? field.square
848                             : NO_SQUARES - 1 - field.square;
849
850                         (*Mpiece[field.piece])[pside][square]
851                             += ADVNCM[field.piece];
852                     }
853                 }
854             }
855         }
856     }
857 }