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