1 /* epd.cpp
2 
3    GNU Chess protocol adapter
4 
5    Copyright (C) 2001-2011 Free Software Foundation, Inc.
6 
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 
22 // epd.cpp
23 
24 // includes
25 
26 #include <cerrno>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <cstring>
30 
31 #include "board.h"
32 #include "engine.h"
33 #include "epd.h"
34 #include "fen.h"
35 #include "line.h"
36 #include "move.h"
37 #include "move_legal.h"
38 #include "option.h"
39 #include "parse.h"
40 #include "san.h"
41 #include "uci.h"
42 #include "util.h"
43 
44 namespace adapter {
45 
46 // constants
47 
48 static const bool UseDebug = false;
49 static const bool UseTrace = false;
50 
51 static const int StringSize = 4096;
52 
53 // variables
54 
55 static int MinDepth;
56 static int MaxDepth;
57 
58 static double MaxTime;
59 static double MinTime;
60 
61 static int DepthDelta;
62 
63 static int FirstMove;
64 static int FirstDepth;
65 static int FirstSelDepth;
66 static int FirstScore;
67 static double FirstTime;
68 static sint64 FirstNodeNb;
69 static move_t FirstPV[LineSize];
70 
71 static int LastMove;
72 static int LastDepth;
73 static int LastSelDepth;
74 static int LastScore;
75 static double LastTime;
76 static sint64 LastNodeNb;
77 static move_t LastPV[LineSize];
78 
79 // prototypes
80 
81 static void epd_test_file  (const char file_name[]);
82 
83 static bool is_solution    (int move, const board_t * board, const char bm[], const char am[]);
84 static bool string_contain (const char string[], const char substring[]);
85 
86 static bool engine_step    ();
87 
88 // functions
89 
90 // epd_test()
91 
epd_test(int argc,char * argv[])92 void epd_test(int argc, char * argv[]) {
93 
94    int i;
95    const char * epd_file;
96 
97    epd_file = NULL;
98    my_string_set(&epd_file,"wac.epd");
99 
100    MinDepth = 8;
101    MaxDepth = 63;
102 
103    MinTime = 1.0;
104    MaxTime = 5.0;
105 
106    DepthDelta = 3;
107 
108    for (i = 1; i < argc; i++) {
109 
110       if (false) {
111 
112       } else if (my_string_equal(argv[i],"epd-test")) {
113 
114          // skip
115 
116       } else if (my_string_equal(argv[i],"-epd")) {
117 
118          i++;
119          if (argv[i] == NULL) my_fatal("epd_test(): missing argument\n");
120 
121          my_string_set(&epd_file,argv[i]);
122 
123       } else if (my_string_equal(argv[i],"-min-depth")) {
124 
125          i++;
126          if (argv[i] == NULL) my_fatal("epd_test(): missing argument\n");
127 
128          MinDepth = atoi(argv[i]);
129 
130       } else if (my_string_equal(argv[i],"-max-depth")) {
131 
132          i++;
133          if (argv[i] == NULL) my_fatal("epd_test(): missing argument\n");
134 
135          MaxDepth = atoi(argv[i]);
136 
137       } else if (my_string_equal(argv[i],"-min-time")) {
138 
139          i++;
140          if (argv[i] == NULL) my_fatal("epd_test(): missing argument\n");
141 
142          MinTime = atof(argv[i]);
143 
144       } else if (my_string_equal(argv[i],"-max-time")) {
145 
146          i++;
147          if (argv[i] == NULL) my_fatal("epd_test(): missing argument\n");
148 
149          MaxTime = atof(argv[i]);
150 
151       } else if (my_string_equal(argv[i],"-depth-delta")) {
152 
153          i++;
154          if (argv[i] == NULL) my_fatal("epd_test(): missing argument\n");
155 
156          DepthDelta = atoi(argv[i]);
157 
158       } else {
159 
160          my_fatal("epd_test(): unknown option \"%s\"\n",argv[i]);
161       }
162    }
163 
164    epd_test_file(epd_file);
165 }
166 
167 // epd_test_file()
168 
epd_test_file(const char file_name[])169 static void epd_test_file(const char file_name[]) {
170 
171    FILE * file;
172    int hit, tot;
173    char epd[StringSize];
174    char am[StringSize], bm[StringSize], id[StringSize];
175    board_t board[1];
176    char string[StringSize];
177    int move;
178    char pv_string[StringSize];
179    bool correct;
180    double depth_tot, time_tot, node_tot;
181 
182    ASSERT(file_name!=NULL);
183 
184    // init
185 
186    file = fopen(file_name,"r");
187    if (file == NULL) my_fatal("epd_test_file(): can't open file \"%s\": %s\n",file_name,strerror(errno));
188 
189    hit = 0;
190    tot = 0;
191 
192    depth_tot = 0.0;
193    time_tot = 0.0;
194    node_tot = 0.0;
195 
196    // loop
197 
198    while (my_file_read_line(file,epd,StringSize)) {
199 
200       if (UseTrace) printf("%s\n",epd);
201 
202       if (!epd_get_op(epd,"am",am,StringSize)) strcpy(am,"");
203       if (!epd_get_op(epd,"bm",bm,StringSize)) strcpy(bm,"");
204       if (!epd_get_op(epd,"id",id,StringSize)) strcpy(id,"");
205 
206       if (my_string_empty(am) && my_string_empty(bm)) {
207          my_fatal("epd_test(): no am or bm field in EPD\n");
208       }
209 
210       // init
211 
212       uci_send_ucinewgame(Uci);
213       uci_send_isready_sync(Uci);
214 
215       ASSERT(!Uci->searching);
216 
217       // position
218 
219       if (!board_from_fen(board,epd)) ASSERT(false);
220       if (!board_to_fen(board,string,StringSize)) ASSERT(false);
221 
222       engine_send(Engine,"position fen %s",string);
223 
224       // search
225 
226       engine_send(Engine,"go movetime %.0f depth %d",MaxTime*1000.0,MaxDepth);
227       // engine_send(Engine,"go infinite");
228 
229       // engine data
230 
231       board_copy(Uci->board,board);
232 
233       uci_clear(Uci);
234       Uci->searching = true;
235       Uci->pending_nb++;
236 
237       FirstMove = MoveNone;
238       FirstDepth = 0;
239       FirstSelDepth = 0;
240       FirstScore = 0;
241       FirstTime = 0.0;
242       FirstNodeNb = 0;
243       line_clear(FirstPV);
244 
245       LastMove = MoveNone;
246       LastDepth = 0;
247       LastSelDepth = 0;
248       LastScore = 0;
249       LastTime = 0.0;
250       LastNodeNb = 0;
251       line_clear(LastPV);
252 
253       // parse engine output
254 
255       while (engine_step()) {
256 
257          // stop search?
258 
259          if (Uci->depth > MaxDepth
260           || Uci->time >= MaxTime
261           || (Uci->depth - FirstDepth >= DepthDelta
262            && Uci->depth > MinDepth
263            && Uci->time >= MinTime
264            && is_solution(FirstMove,board,bm,am))) {
265             engine_send(Engine,"stop");
266          }
267       }
268 
269       move = FirstMove;
270       correct = is_solution(move,board,bm,am);
271 
272       if (correct) hit++;
273       tot++;
274 
275       if (correct) {
276          depth_tot += double(FirstDepth);
277          time_tot += FirstTime;
278          node_tot += double(FirstNodeNb);
279       }
280 
281       printf("%s %d %4d %4d",id,correct,hit,tot);
282 
283       if (!line_to_san(LastPV,Uci->board,pv_string,StringSize)) ASSERT(false);
284       printf(" - %2d %6.2f %9lld %+6.2f %s\n",FirstDepth,FirstTime,FirstNodeNb,double(LastScore)/100.0,pv_string);
285    }
286 
287    printf("%d/%d",hit,tot);
288 
289    if (hit != 0) {
290 
291       depth_tot /= double(hit);
292       time_tot /= double(hit);
293       node_tot /= double(hit);
294 
295       printf(" - %.1f %.2f %.0f",depth_tot,time_tot,node_tot);
296    }
297 
298    printf("\n");
299 
300    fclose(file);
301 }
302 
303 // is_solution()
304 
is_solution(int move,const board_t * board,const char bm[],const char am[])305 static bool is_solution(int move, const board_t * board, const char bm[], const char am[]) {
306 
307    char move_string[256];
308    bool correct;
309 
310    ASSERT(move!=MoveNone);
311    ASSERT(bm!=NULL);
312    ASSERT(am!=NULL);
313 
314    if (!move_is_legal(move,board)) {
315       board_disp(board);
316       move_disp(move,board);
317       printf("\n\n");
318    }
319 
320    ASSERT(move_is_legal(move,board));
321 
322    if (!move_to_san(move,board,move_string,256)) ASSERT(false);
323 
324    correct = false;
325    if (!my_string_empty(bm)) {
326       correct = string_contain(bm,move_string);
327    } else if (!my_string_empty(am)) {
328       correct = !string_contain(am,move_string);
329    } else {
330       ASSERT(false);
331    }
332 
333    return correct;
334 }
335 
336 // epd_get_op()
337 
epd_get_op(const char record[],const char opcode[],char string[],int size)338 bool epd_get_op(const char record[], const char opcode[], char string[], int size) {
339 
340    char op[256];
341    int len;
342    const char *p_start, *p_end;
343 
344    ASSERT(record!=NULL);
345    ASSERT(opcode!=NULL);
346    ASSERT(string!=NULL);
347    ASSERT(size>0);
348 
349    // find the opcode
350 
351    sprintf(op," %s ",opcode);
352 
353    p_start = strstr(record,op);
354    if (p_start == NULL) return false;
355 
356    // skip the opcode
357 
358    p_start += strlen(op);
359 
360    // find the end
361 
362    p_end = strchr(p_start,';');
363    if (p_end == NULL) return false;
364 
365    // calculate the length
366 
367    len = p_end - p_start;
368    if (size < len+1) my_fatal("epd_get_op(): size < len+1\n");
369 
370    strncpy(string,p_start,len);
371    string[len] = '\0';
372 
373    return true;
374 }
375 
376 // string_contain()
377 
string_contain(const char string[],const char substring[])378 static bool string_contain(const char string[], const char substring[]) {
379 
380    char new_string[StringSize], *p;
381 
382    strcpy(new_string,string); // HACK
383 
384    for (p = strtok(new_string," "); p != NULL; p = strtok(NULL," ")) {
385       if (my_string_equal(p,substring)) return true;
386    }
387 
388    return false;
389 }
390 
391 // engine_step()
392 
engine_step()393 static bool engine_step() {
394 
395    char string[StringSize];
396    int event;
397 
398    engine_get(Engine,string,StringSize);
399    event = uci_parse(Uci,string);
400 
401    if ((event & EVENT_MOVE) != 0) {
402 
403       return false;
404    }
405 
406    if ((event & EVENT_PV) != 0) {
407 
408       LastMove = Uci->best_pv[0];
409       LastDepth = Uci->best_depth;
410       LastSelDepth = Uci->best_sel_depth;
411       LastScore = Uci->best_score;
412       LastTime = Uci->time;
413       LastNodeNb = Uci->node_nb;
414       line_copy(LastPV,Uci->best_pv);
415 
416       if (LastMove != FirstMove) {
417          FirstMove = LastMove;
418          FirstDepth = LastDepth;
419          FirstSelDepth = LastSelDepth;
420          FirstScore = LastScore;
421          FirstTime = LastTime;
422          FirstNodeNb = LastNodeNb;
423          line_copy(FirstPV,LastPV);
424       }
425    }
426 
427    return true;
428 }
429 
430 }  // namespace adapter
431 
432 // end of epd.cpp
433 
434