1/*
2    Sjeng - a chess variants playing program
3    Copyright (C) 2000 Gian-Carlo Pascutto
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19    File: book.c
20    Purpose: book initialization, selection of book moves, etc...
21
22*/
23
24#include "sjeng.h"
25#include "protos.h"
26#include "extvars.h"
27
28char book[4000][161];
29char book_flags[4000][41];
30int num_book_lines;
31int book_ply;
32int use_book;
33char opening_history[STR_BUFF];
34
35#define book_always 1        /* corresponds with ! */
36#define book_never 2         /* corresponds with ? */
37#define book_interesting 3   /* !?  */
38#define book_solid 4         /* =   */
39#define book_murky 5         /* ?!  */
40
41int init_book (void) {
42
43   /* simply read all the book moves into a book array.  The book will be
44      a simple char[5000][81] array (5000 max book lines, since you can't
45      have *too* many when you're doing this slow strncpy method ;)  After
46      all, this is really just a kludge 'till I add hashing, and support
47      transpositions and such ;) Returns true if reading in the book
48      was successful. */
49
50   FILE *f_book;
51   int ch, i = 0, j = 0;
52
53   int tagmode = FALSE; /* recognize multiple tags */
54   int commentmode = FALSE;
55
56   memset(book_flags, 0, sizeof(book_flags));
57   memset(book, 0, sizeof(book));
58
59   num_book_lines = 0;
60
61   /* init our random numbers: */
62   srand((unsigned) time (NULL));
63
64   if (Variant == Normal)
65     {
66       if ((f_book = fopen ("normal.opn", "r")) == NULL)
67	 return FALSE;
68     }
69   else if (Variant == Crazyhouse)
70     {
71       if ((f_book = fopen ("zh.opn", "r")) == NULL)
72	 return FALSE;
73     }
74   else if (Variant == Suicide)
75     {
76       if ((f_book = fopen ("suicide.opn", "r")) == NULL)
77	 return FALSE;
78     }
79   else if (Variant == Losers)
80   {
81       if ((f_book = fopen ("losers.opn", "r")) == NULL)
82	 return FALSE;
83   }
84   else
85     {
86       if ((f_book = fopen ("bug.opn", "r")) == NULL)
87	 return FALSE;
88     }
89
90   while ((ch = getc(f_book)) != EOF) {
91
92     if (commentmode)
93       {
94	 if (ch == '/') /* end comment */
95	   {
96	     commentmode = FALSE;
97	   }
98       }
99     else
100       {
101	 if (ch == '\n') { /* end of book line */
102
103	   /* not ending an empty book line */
104	   if (j > 0)
105	     {
106	       book[i++][j] = '\0';
107	       j = 0;
108	     }
109
110	   tagmode = FALSE;
111	 }
112	 else if (ch == '!')
113	   {
114	     if (!tagmode)
115	       {
116		 book_flags[i][((j + 1) / 4) - 1] = book_always;
117		 tagmode = TRUE;
118	       }
119	     else
120	       {
121		 book_flags[i][((j + 1) / 4) - 1] = book_murky;
122	       }
123	   }
124	 else if (ch == '?')
125	   {
126	     if (!tagmode)
127	       {
128		 book_flags[i][((j + 1) / 4) - 1] = book_never;
129		 tagmode = TRUE;
130	       }
131	     else
132	       {
133		 book_flags[i][((j + 1) / 4) - 1] = book_interesting;
134	       }
135	   }
136	 else if (ch == '=')
137	   {
138	     book_flags[i][((j + 1) / 4) - 1] = book_solid;
139	     tagmode = TRUE;
140	   }
141	 else if ((ch == ' ') || (ch == '\t'))
142	   {
143	     /* skip spaces and tabs */
144	     tagmode = FALSE;
145	   }
146	 else if (ch == '/') /* start comment */
147	   {
148	     commentmode = TRUE;
149	   }
150	 else if (j < 160 && i < 4000) /* middle of book line */
151	   {
152	     book[i][j++] = ch;
153	     tagmode = FALSE;
154	   }
155	 /* If j >= 100, then the book line was too long.  The rest will be
156	    read, but ignored, and will be null-character terminated when a
157	    '\n' is read.  If i >= 2000, then there are too many book lines.
158	    The remaining lines will be read, but ignored. */
159       }
160   }
161
162   num_book_lines = i;
163
164   fclose(f_book);
165
166   return TRUE;
167
168}
169
170move_s choose_book_move (void) {
171
172   /* Since the book is sorted alphabetically, we can use strncpy, and hope
173      to get early exits once we've found the first few moves in the book.
174      Once we choose a book move, we'll make a variable indicate where
175      it was found, so we can start our search for the next move there. */
176
177   static int last_book_move = 0;
178   int book_match = FALSE;
179   int i, j, num_moves, random_number, num_replies;
180   char possible_move[5], coord_move[5];
181   move_s book_replies[4000], moves[400];
182   char force_move = FALSE;
183   int ic;
184
185   srand((unsigned)time(0));
186
187   if (!book_ply)
188     last_book_move = 0;
189
190   num_replies = 0;
191   gen(&moves[0]);
192   num_moves = numb_moves;
193
194   for (i = last_book_move; i < num_book_lines; i++) {
195      /* check to see if opening line matches up to current book_ply: */
196      if (!strncmp(opening_history, book[i], (book_ply * 4)) || (!book_ply)) {
197	/* if book move is legal, add it to possible list of book moves */
198
199	if ((book_flags[i][book_ply] != book_never)
200	    && (book_flags[i][book_ply] != book_murky))
201	  {
202	    if (book_flags[i][book_ply] == book_always)
203	      {
204		if (force_move != TRUE)
205		  {
206		    /* found 1st ! move -> remove normal ones */
207		    force_move = TRUE;
208		    num_replies = 0;
209		  }
210	      }
211
212	    if ((force_move != TRUE) ||
213		((force_move == TRUE) &&
214		 (book_flags[i][book_ply] == book_always)))
215	      {
216		strncpy(possible_move, book[i] + (book_ply * 4), 4);
217		possible_move[4] = '\0';
218
219		for (j = 0; j < num_moves; j++)
220		  {
221		    comp_to_coord(moves[j], coord_move);
222
223		    if (!strcmp(possible_move, coord_move))
224		      {
225			ic = in_check();
226			make(&moves[0], j);
227			if (check_legal(&moves[0], j, ic))
228			  {
229			    book_replies[num_replies++] = moves[j];
230			    book_match = TRUE;
231			  }
232			unmake(&moves[0], j);
233		      }
234		  }
235	      }
236	  }
237      }
238      /* we can exit the search for a book move early, if we've no longer
239         have a match, but we have found at least one match */
240      if (!book_match && num_replies)
241         break;
242   }
243
244   /* now, if we have some book replies, pick our move randomly from
245      book_replies: */
246   if (!num_replies)
247      return dummy;
248
249   printf("Book moves: %d\n", num_replies);
250
251   random_number = rand() % num_replies;
252   return book_replies[random_number];
253
254}
255
256