Bool transition: do not scanf directly into would-be bools.
[gnushogi.git] / gnushogi / pattern.c
blob349cd9448d11a98d1913318e52965bd1025972e0
1 /*
2 * FILE: pattern.c
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
9 * GNU SHOGI is based on GNU CHESS
11 * Copyright (c) 1988, 1989, 1990 John Stanback
12 * Copyright (c) 1992 Free Software Foundation
14 * This file is part of GNU SHOGI.
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.
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.
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 * ----------------------------------------------------------------------
33 #include "gnushogi.h"
34 #include "pattern.h"
36 /* "pat2inc" generates constants and pattern_data */
37 #ifndef MINISHOGI
38 # include "gnushogi-pattern.inc"
39 #else
40 # include "gnuminishogi-pattern.inc"
41 #endif
43 struct Pattern_rec Pattern[MAX_PATTERN];
44 struct OpeningSequence_rec OpeningSequence[MAX_OPENING_SEQUENCE];
46 small_short pattern_data[MAX_PATTERN_DATA];
49 static void
50 NameOfOpeningValue (short i, char *name)
52 if (i < 100)
54 strcpy(name, "CASTLE_?_?");
56 else
58 strcpy(name, "ATTACK_?_?");
59 i -= 100;
62 switch (i / 10)
64 case 1:
65 name[7] = 'S';
66 break;
68 case 2:
69 name[7] = 'R';
70 break;
72 case 3:
73 name[7] = 'U';
74 break;
76 default:
77 name[7] = '*';
78 break;
81 switch (i % 10)
83 case 1:
84 name[9] = 'S';
85 break;
87 case 2:
88 name[9] = 'R';
89 break;
91 case 3:
92 name[9] = 'U';
93 break;
95 default:
96 name[9] = '*';
97 break;
102 void
103 GetOpeningPatterns (short *max_opening_sequence)
105 short pindex = 0;
106 short os = 0;
107 short p = 0;
108 short i;
112 OpeningSequence[os].opening_type = pattern_data[pindex++];
113 OpeningSequence[os].first_pattern[0] = p;
115 for (i = 1; i < MAX_SEQUENCE; i++)
116 OpeningSequence[os].first_pattern[i] = END_OF_PATTERNS;
120 Pattern[p].reachedGameCnt[black] = MAXMOVES;
121 Pattern[p].reachedGameCnt[white] = MAXMOVES;
122 Pattern[p].first_link = pindex;
124 while (pattern_data[pindex] != END_OF_LINKS)
125 pindex++;
126 pindex++;
128 Pattern[p].first_field = pindex;
130 while (pattern_data[pindex] != END_OF_FIELDS)
131 pindex += 2;
132 pindex++;
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;
139 p++;
141 while (pattern_data[pindex] != END_OF_PATTERNS);
143 pindex++;
144 os++;
146 while (pattern_data[pindex] != END_OF_SEQUENCES);
148 *max_opening_sequence = os;
153 void
154 ShowOpeningPatterns (short max_opening_sequence)
156 short os, p, n, i;
158 for (os = 0; os < max_opening_sequence; os++)
160 char name[16];
161 NameOfOpeningValue(OpeningSequence[os].opening_type, name);
162 printf("Opening Type: %s\n", name);
164 for (p = OpeningSequence[os].first_pattern[0], n = 0;
165 p != END_OF_PATTERNS;
166 p = Pattern[p].next_pattern, n++)
168 printf("Pattern %d (%d) with links ", p, n);
170 for (i = Pattern[p].first_link;
171 pattern_data[i] != END_OF_LINKS;
172 i++)
174 printf("%d ", pattern_data[i]);
177 printf("\n");
178 DisplayPattern(stdout, Pattern[p].first_field);
185 void
186 set_field (short i, struct PatternField *field)
188 field->piece = pattern_data[i];
189 field->square = pattern_data[i+1];
191 if (field->square < 0)
193 field->square = -(field->square);
194 field->side = white;
196 else
198 field->side = black;
205 * piece_to_pattern_distance (side, piece, pside, pattern)
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".
212 short
213 piece_to_pattern_distance(short side, short piece,
214 short pside, short pattern)
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;
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.
227 /* special pawn handling */
229 if (piece == pawn)
231 mindd = occupied = 0;
233 for (i = Pattern[pattern].first_field;
234 pattern_data[i] != END_OF_FIELDS;
235 i += 2)
237 struct PatternField field;
238 set_field(i, &field);
240 if ((field.side == side) && (field.piece == pawn))
242 short t = field.square;
243 short pcol = column(t);
244 dd = CANNOT_REACH;
246 if (PawnCnt[c1][(side == c1) ? pcol : (8 - pcol)])
248 /* there is a pawn on the column */
249 for (j = 0; j <= PieceCnt[c1]; j++)
251 short sq = (short)PieceList[c1][j];
253 if (board[sq] == pawn)
255 if (pside == white)
256 sq = NO_SQUARES - 1 - sq;
258 if (column(sq) == pcol)
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;
271 else
273 /* there is no pawn on the column; drop possible? */
274 if (Captured[c1][pawn])
276 dd = 1;
277 #ifdef TEST_PATTERN
278 printf("update %d pawn drop to %d is %d\n",
279 side, t, dd);
280 #endif
284 if (dd >= 0)
286 /* Increment distance if pattern field is occupied */
287 short psq, pc;
289 if (pside == black)
291 psq = t;
292 pc = field.side;
294 else
296 psq = (NO_SQUARES - 1 - t);
297 pc = ~field.side;
300 if ((color[psq] == pc) && (board[psq] != pawn))
302 #ifdef TEST_PATTERN
303 printf("square %d is occupied\n", psq);
304 #endif
305 ++occupied;
308 mindd += dd;
310 else
312 return CANNOT_REACH;
317 return mindd + occupied;
321 * Determine list of "side" "piece"s in pattern.
324 for (occupied = nP = 0, i = Pattern[pattern].first_field;
325 pattern_data[i] != END_OF_FIELDS;
326 i += 2)
328 struct PatternField field;
329 set_field(i, &field);
331 if ((field.side == side) && (field.piece == piece))
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++;
340 /* Increment distance if pattern field is occupied */
341 if (pside == black)
343 psq = field.square;
344 pc = field.side;
346 else
348 psq = NO_SQUARES - 1 - field.square;
349 pc = field.side ^ 1;
352 if ((color[psq] == pc) && (board[psq] != field.piece))
354 #ifdef TEST_PATTERN
355 printf("square %d is occupied\n", psq);
356 #endif
357 ++occupied;
362 if (nP == 0)
363 return 0;
365 #ifdef TEST_PATTERN
366 printf("finding in pattern %d pieces %d of side %d\n", nP, piece, side);
367 #endif
370 * Determine list of "side ^ pside" "piece"s captured or on board.
373 for (nB = 0; nB < Captured[c1][piece]; nB++)
374 B[nB] = NO_SQUARES + piece;
376 for (i = 0; i <= PieceCnt[c1]; i++)
378 short sq = PieceList[c1][i];
380 if (board[sq] == piece)
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++;
390 #ifdef TEST_PATTERN
391 printf("found on board %d pieces %d of side %d\n", nB, piece, side);
392 #endif
394 if (nP > nB)
396 return CANNOT_REACH;
399 /* Determine best assignment from board piece to pattern piece */
401 r = 0;
402 c[0] = -1;
403 mindd = CANNOT_REACH;
405 while ((r >= 0) && (mindd != 0))
408 if (++c[r] == nB)
410 r--;
412 else
414 for (i = 0; i < r; i++)
416 if (c[i] == c[r])
417 break;
420 if (i == r)
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)
429 /* r--; */
431 else
433 if (++r == nP)
435 for (dd = i = 0; i < nP; i++)
436 dd += d[i];
438 if ((dd < mindd) || (mindd < 0))
440 mindd = dd;
441 #ifdef TEST_PATTERN
442 printf("update min %d\n", mindd);
443 #endif
446 r--;
448 else
450 c[r] = -1;
457 if (mindd < 0)
458 return CANNOT_REACH;
459 else
460 return (mindd + occupied);
467 * pattern_distance (pside, pattern)
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.
476 short
477 pattern_distance (short pside, short pattern)
479 short side, piece, d, n;
481 #ifdef TEST_PATTERN
482 printf("\nchecking pattern %d for pside=%d\n\n", pattern, pside);
483 #endif
485 for (n = side = 0; side <= 1 && n >= 0; side++)
487 for (piece = pawn; piece <= king; piece++)
489 d = piece_to_pattern_distance (side, piece, pside, pattern);
491 if (d < 0)
493 n = CANNOT_REACH;
494 break;
496 else
498 n += d;
503 #ifdef TEST_PATTERN
504 printf("\ndistance to pattern is %d\n\n", n);
505 #endif
507 return n;
513 * board_to_pattern_distance(pside, osequence, pmplty, GameCnt)
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.
522 short
523 board_to_pattern_distance
524 (short pside, short osequence, short pmplty, short GameCnt)
526 short i, d, dist, diff, weighted_diff;
527 short maxdiff = 0, max_weighted_diff = 0;
528 short pattern;
530 for (i = 0; i < MAX_SEQUENCE; i++)
532 for (pattern = OpeningSequence[osequence].first_pattern[i];
533 pattern != END_OF_PATTERNS;
534 pattern = Pattern[pattern].next_pattern)
536 if ((d = Pattern[pattern].distance[pside]) >= 0)
538 if (pmplty > d)
540 dist = pattern_distance (pside, pattern);
541 if (dist >= 0)
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.
551 /* try to reach the nearest pattern */
552 weighted_diff = (diff = (d - dist)) * (pmplty - d);
554 if (weighted_diff > max_weighted_diff)
556 #ifdef COUNT_DIFF
557 maxdiff = diff;
558 #else
559 maxdiff = weighted_diff;
560 #endif
561 max_weighted_diff = weighted_diff;
565 * A reached pattern should not be considered in
566 * the future (if GameCnt >= 0)
569 if (dist == 0 && GameCnt >= 0)
570 Pattern[pattern].reachedGameCnt[pside] = GameCnt;
577 return maxdiff;
583 void
584 DisplayPattern (FILE *fd, short n)
586 small_short pboard[NO_SQUARES], pcolor[NO_SQUARES];
587 short sq, i, r, c;
589 for (sq = 0; sq < NO_SQUARES; sq++)
591 pboard[sq] = no_piece;
592 pcolor[sq] = neutral;
595 for (i = n; pattern_data[i] != END_OF_FIELDS; i += 2)
597 struct PatternField field;
598 set_field(i, &field);
599 pboard[field.square] = field.piece;
600 pcolor[field.square] = field.side;
603 for (r = NO_ROWS - 1; r >= 0; r--)
605 for (c = 0; c < NO_COLS; c++)
607 sq = r*NO_COLS + c;
608 i = pboard[sq];
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]);
617 fprintf(fd, "\n");
620 fprintf(fd, "\n");
626 static void
627 VisitReachable (int pside, short osequence, int k, int n, int remove)
629 short i, j;
630 short pattern;
632 /* Adjust to sequence pattern n */
633 for (i = 0, pattern = OpeningSequence[osequence].first_pattern[k];
634 i < n; i++)
636 pattern = Pattern[pattern].next_pattern;
639 /* do not perform visited link twice */
640 if (Pattern[pattern].visited)
642 return;
644 else
646 Pattern[pattern].visited = true;
649 /* Declare links unreachable */
650 for (j = Pattern[pattern].first_link;
651 pattern_data[j] != END_OF_LINKS; j++)
653 VisitReachable(pside, osequence, k, pattern_data[j], remove);
656 /* Declare unreachable */
657 if (remove && Pattern[pattern].distance[pside] >= 0)
659 Pattern[pattern].distance[pside] = IS_SUCCESSOR;
664 /* simplified matching for opening type names */
666 #define match_char(a, b) \
667 (a == b || (a == '*' && b != 'U') || (b == '*' && a != 'U'))
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]))
674 short
675 locate_opening_sequence(short pside, char *s, short GameCnt)
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];
683 * Look for opening pattern name in the list of opening patterns.
686 name[0] = '\0';
688 for (i = 1, os = 0; os < MAX_OPENING_SEQUENCE; os++)
690 /* locate matching opening type name */
691 NameOfOpeningValue(OpeningSequence[os].opening_type, name);
693 if (match_name(s, name, l))
695 /* locate successor matching names */
696 for (k = os + 1; k < MAX_OPENING_SEQUENCE; k++)
698 NameOfOpeningValue(OpeningSequence[k].opening_type, name2);
700 if (match_name(s, name2, l))
702 OpeningSequence[os].first_pattern[i++]
703 = OpeningSequence[k].first_pattern[0];
707 break;
711 if (os >= MAX_OPENING_SEQUENCE)
713 return END_OF_SEQUENCES;
715 else
717 for (; i < MAX_SEQUENCE;
718 OpeningSequence[os].first_pattern[i++] = END_OF_PATTERNS);
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.
727 for (i = 0; i < MAX_SEQUENCE; i++)
729 check_visited[i] = false;
731 for (k = OpeningSequence[os].first_pattern[i];
732 k != END_OF_PATTERNS;
733 k = Pattern[k].next_pattern)
735 Pattern[k].visited = false;
739 for (i = 0; i < MAX_SEQUENCE; i++)
741 for (k = OpeningSequence[os].first_pattern[i];
742 k != END_OF_PATTERNS;
743 k = Pattern[k].next_pattern)
745 Pattern[k].distance[pside] = pattern_distance(pside, k);
747 /* Actually reached patterns need not to be observed. */
748 if (Pattern[k].distance[pside] == 0)
750 Pattern[k].distance[pside] = CANNOT_REACH;
751 check_visited[i] = Pattern[k].visited = true;
753 for (j = Pattern[k].first_link;
754 pattern_data[j] != END_OF_LINKS; j++)
756 VisitReachable(pside, os, i, pattern_data[j], false);
759 else if ((GameCnt >= 0)
760 && (GameCnt >= Pattern[k].reachedGameCnt[pside]))
762 Pattern[k].distance[pside] = IS_REACHED;
765 if (Pattern[k].reachedGameCnt[pside] >= GameCnt)
766 Pattern[k].reachedGameCnt[pside] = MAXMOVES;
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.
776 for (i = 0; i < MAX_SEQUENCE; i++)
778 for (k = OpeningSequence[os].first_pattern[i];
779 k != END_OF_PATTERNS;
780 k = Pattern[k].next_pattern)
782 if (check_visited[i] && !Pattern[k].visited)
783 Pattern[k].distance[pside] = NOT_TO_REACH;
784 else
785 Pattern[k].visited = false;
789 for (i = 0; i < MAX_SEQUENCE; i++)
791 for (k = OpeningSequence[os].first_pattern[i];
792 k != END_OF_PATTERNS;
793 k = Pattern[k].next_pattern)
795 if ((d = Pattern[k].distance[pside]) >= 0)
797 for (j = Pattern[k].first_link;
798 pattern_data[j] != END_OF_LINKS; j++)
800 VisitReachable(pside, os, i, pattern_data[j], true);
807 * Look to see whether there is still a reachable pattern.
810 for (i = 0; i < MAX_SEQUENCE; i++)
812 for (k = OpeningSequence[os].first_pattern[i];
813 k != END_OF_PATTERNS;
814 k = Pattern[k].next_pattern)
816 if ((d = Pattern[k].distance[pside]) >= 0)
817 return os;
821 return END_OF_SEQUENCES;
827 void
828 update_advance_bonus (short pside, short os)
830 struct PatternField field;
831 short i, j, k, d;
833 for (j = 0; j < MAX_SEQUENCE; j++)
835 for (k = OpeningSequence[os].first_pattern[j];
836 k != END_OF_PATTERNS;
837 k = Pattern[k].next_pattern)
839 if ((d = Pattern[k].distance[pside]) >= 0)
841 for (i = Pattern[k].first_field;
842 pattern_data[i] != END_OF_FIELDS; i += 2)
844 set_field(i, &field);
845 if (field.side == black)
847 short square = (pside == black)
848 ? field.square
849 : NO_SQUARES - 1 - field.square;
851 (*Mpiece[field.piece])[pside][square]
852 += ADVNCM[field.piece];