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