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