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