Tree2
[siwg.git] / espy / src / tree / EngineJacek.h
blob7c19178aca6b2c1cbb4a9e5938d8b08aa2524ba6
1 #include<pthread.h>
2 #include<iostream>
3 #include<string>
4 #include<stdlib.h>
6 #include<errno.h>
8 #include "CPEventListener.h"
9 #include "CPCommandParser.h"
10 #include "ConnectSix.h"
12 int ec_prep = 0;
14 #include<vector>
15 using namespace std;
17 #include "Log.h"
18 #include "sys/time.h"
20 template <class G>
21 class Engine : public CPEventListener {
24 * Dostęp do danych i synchronizacja między wątkami.
26 class Mutex {
27 pthread_mutex_t mutex;
28 pthread_cond_t cond;
29 public:
30 Mutex() {
31 if (pthread_mutex_init(&mutex,NULL) != 0)
32 Log::error("pthread_mutex_init failed in Mutex class constructor");
33 if (pthread_cond_init(&cond,NULL) != 0)
34 Log::error("pthread_cond_init failed in Mutex class constructor");
37 ~Mutex() {
38 if (pthread_mutex_destroy(&mutex) != 0)
39 Log::error("pthread_mutex_destroy failed in Mutex class destructor");
40 if (pthread_cond_destroy(&cond) != 0)
41 Log::error("pthread_cond_destroy failed in Mutex class destructor");
44 void grab() {
45 if (pthread_mutex_lock(&mutex) != 0)
46 Log::error("pthread_mutex_lock failed in Mutex.grab()");
49 void release() {
50 if (pthread_mutex_unlock(&mutex) != 0)
51 Log::error("pthread_mutex_unlock failed in Mutex.release()");
54 void sleep() {
55 if (pthread_cond_wait(&cond,&mutex) != 0)
56 Log::error("pthread_cond_wait failed in Mutex.sleep()");
59 void sleep(timespec &ts) {
60 int x;
61 if ((x = pthread_cond_timedwait(&cond,&mutex, &ts)) != 0) {
62 if (x != ETIMEDOUT) {
63 Log::error("pthread_cond_wait failed in method Mutex.sleep(timespec)");
68 void wake() {
69 if (pthread_cond_signal(&cond) != 0)
70 Log::error("pthread_cond_signal failed in Mutex.wake()");
76 protected:
78 class SearchResult {
79 public:
80 Mutex lock;
82 bool in_progress;
83 int nominal_depth;
84 int score_lower_bound;
85 int score_upper_bound;
87 bool resign;
88 vector<typename G::move> pv;
90 int elapsed_time;
91 int nodes_used;
93 void init() {
94 in_progress = true;
95 resign = false;
96 nominal_depth = -1;
97 pv.clear();
100 void update(SearchResult &sr) {
101 lock.grab();
102 in_progress = sr.in_progress;
103 nominal_depth = sr.nominal_depth;
104 pv = sr.pv;
105 // .....
106 lock.release();
110 SearchResult search_result;
112 bool pause_search;
114 SearchResult current_search;
116 private:
117 G official_board;
118 Mutex official_board_mutex;
119 int _time;
121 virtual void think(G&, SearchResult&, int) = 0;
123 Mutex engine_mutex;
125 typedef enum { e_idle, e_white, e_black, e_analyze } mode;
127 typedef enum { c_none, c_update, c_reset, c_quit, c_move } command;
129 command command_to_engine;
130 mode engine_mode;
132 void engine_sleep() {
133 engine_mutex.wake();
135 if (command_to_engine == c_none) {
136 engine_mutex.sleep();
140 void *engine_thread() {
141 Log::debug("Engine thread started.");
143 engine_mutex.grab();
145 while (1) {
146 pause_search = false;
147 command tmp = command_to_engine;
148 command_to_engine = c_none;
150 cout << "Engine loop " << tmp << endl;
152 switch (tmp) {
153 case c_none:
154 switch(engine_mode) {
155 case e_idle: engine_sleep(); break;
156 case e_white:
157 case e_black:
158 case e_analyze:
159 current_search.lock.grab();
160 current_search.init();
161 current_search.lock.release();
163 official_board_mutex.grab();
165 if ((engine_mode == e_white && !official_board.wtm()) ||
166 (engine_mode == e_black && official_board.wtm()) ||
167 (official_board.game_state() != G::in_progress)) {
168 official_board_mutex.release();
169 engine_sleep();
170 break;
173 G board = official_board;
174 official_board_mutex.release();
176 engine_mutex.release();
177 engine_mutex.wake();
179 think(board, current_search, _time);
181 engine_mutex.grab();
183 current_search.lock.grab();
184 if (!current_search.in_progress) {
185 search_result.update(current_search);
187 current_search.lock.release();
189 if (!pause_search && command_to_engine == c_none) {
190 cout << "WILL BE SENDING" << endl;
191 command_to_engine = c_move;
192 pause_search = true;
196 break;
198 case c_quit:
199 engine_mutex.release();
201 Log::debug("Engine thread finished.");
202 return NULL;
204 case c_reset:
205 // czyszczenie tablicy transpozycji itp.
207 case c_update:
208 pause_search = false;
209 search_result.lock.grab();
210 search_result.init();
211 search_result.lock.release();
213 // Log::debug("reset/pause");
215 break;
217 case c_move:
218 Log::debug("c_move entered");
220 official_board_mutex.grab();
221 if ((official_board.wtm() && engine_mode != e_white) ||
222 ((!official_board.wtm()) && engine_mode != e_black)) {
223 official_board_mutex.release();
224 // Log::debug("c_move break - wrong side to move!!!");
225 break;
228 official_board_mutex.release();
230 search_result.lock.grab();
231 typename G::move bestmove;
233 if (search_result.pv.size() == 0 && !search_result.resign) {
234 Log::debug("PANIC! No vaild search result found. Making random move!");
236 bestmove = -1;
237 } else {
238 bestmove = search_result.pv[search_result.pv.size()-1];
240 search_result.init();
241 search_result.lock.release();
243 official_board_mutex.grab();
244 official_board.prepare_moves();
245 if (bestmove == -1 && !search_result.resign) {
246 vector<typename G::move> moves;
248 while(official_board.has_next_move()) {
249 moves.push_back(official_board.get_next_move());
252 if (moves.size() == 0) {
253 Log::error("Move requested in a final position (with no moves available).");
254 } else {
255 bestmove = moves[random() % moves.size()];
259 if (bestmove != -1) {
260 official_board.make_move(bestmove);
261 cout << "bestmove " << official_board.print_move(bestmove) << endl;
262 } else if (search_result.resign) {
263 cout << "concede" << endl;
266 switch (official_board.game_state()) {
267 case G::white_wins:
268 cout << "endgame white" << endl;
269 break;
270 case G::black_wins:
271 cout << "endgame black" << endl;
272 break;
273 case G::draw:
274 cout << "endgame draw" << endl;
275 break;
278 official_board_mutex.release();
280 break;
281 default:
282 Log::error("Internal error: bad command sent to the engine thread.");
286 Log::debug("Engine thread finished.");
287 return NULL;
290 static void* engine_thread_helper(void *context) {
291 return ((Engine *) context) -> engine_thread();
294 void display_feedback() {
295 cout << "info feedback XXX" << endl;
300 * Wątek clock obsługuje dwa zadania:
302 * w przypadku, gdy toczy się gra i silnik ma swój ruch decyduje, w którym momencie zakończyć myślenie i wysłać ruch
303 * w przypadku, gdy feedback jest włączony, co sekundę wysyła informacje o wynikach szukania do interfejsu
304 * (wtedy, gdy silnik pracuje)
307 Mutex clock_mutex;
309 void *clock_thread() {
310 Log::debug("Clock thread started.");
312 clock_mutex.grab();
314 while (1) {
316 * Działamy
318 switch (engine_mode) {
319 case e_idle:
320 break;
321 case e_white:
322 case e_black:
323 official_board_mutex.grab();
325 if (!pause_search &&
326 ((engine_mode == e_white && official_board.wtm()) ||
327 (engine_mode == e_black && !official_board.wtm()))
330 official_board_mutex.release();
332 ec_prepare("c_move");
333 command_to_engine = c_move;
334 pause_search = true;
335 ec_send();
336 } else {
337 official_board_mutex.release();
340 break;
341 case e_analyze:
342 display_feedback();
343 break;
347 * Usypiamy
349 timeval tp, tpo;
350 tpo = tp;
351 gettimeofday(&tp, NULL);
353 if (tp.tv_sec != tpo.tv_sec) {
354 // cout << "debug " << getpid() << " engine_mode " << engine_mode << " board.wtm() " << official_board.wtm() << endl;
357 timespec ts;
358 ts.tv_sec = tp.tv_sec;
359 ts.tv_nsec = tp.tv_usec * 1000;
360 ts.tv_nsec += (random() % 500000000);
361 if (ts.tv_nsec >= 1000000000) {
362 ts.tv_sec += 1;
363 ts.tv_nsec -= 1000000000;
366 ts.tv_sec += 5;
368 clock_mutex.sleep(ts);
369 // cout << "CLOCK TICK" << endl;
371 if (!interface_loop) {
372 break;
376 clock_mutex.release();
378 Log::debug("Clock thread finished.");
379 return NULL;
382 static void* clock_thread_helper(void *context) {
383 return ((Engine *) context) -> clock_thread();
386 bool interface_loop;
388 void *interface_thread() {
389 Log::debug("Interface thread started.");
391 CPCommandParser parser(cin, *this);
393 while (interface_loop) {
394 string line = parser.nextCommand();
395 try {
396 parser.parse(line);
397 } catch (int error) {
398 if (error == 2)
399 cout << "Unknown command." << endl;
400 else
401 cout << "Command corrupted." << endl;
405 Log::debug("Interface thread finished.");
406 return NULL;
409 static void* interface_thread_helper(void *context) {
410 return ((Engine *) context) -> interface_thread();
413 void ec_prepare(string msg) {
414 ec_prep++;
415 cout << "ec_prepare " << msg << " (" << ec_prep << "); command_to_engine " << command_to_engine << endl;
417 engine_mutex.grab();
418 while (command_to_engine != c_none) {
419 engine_mutex.sleep();
423 void ec_send() {
424 cout << "ec_send (" << ec_prep << ")";
426 ec_prep--;
428 engine_mutex.release();
429 engine_mutex.wake();
432 public:
434 void main() {
435 interface_loop = true;
437 pthread_attr_t attr;
438 pthread_t thr[3];
441 if (pthread_attr_init(&attr) != 0) {
442 Log::error("pthread_attr_init");
445 if (pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE) != 0) {
446 Log::error("pthread_attr_setdetachstate");
449 if (pthread_create(&thr[0], &attr, engine_thread_helper,this) != 0 ||
450 pthread_create(&thr[1], &attr, clock_thread_helper, this) != 0 ||
451 pthread_create(&thr[2], &attr, interface_thread_helper, this) != 0) {
452 Log::error("pthread_create");
456 if (pthread_join(thr[0],NULL) != 0 ||
457 pthread_join(thr[1],NULL) != 0 ||
458 pthread_join(thr[2],NULL) != 0) {
459 error("pthread_join");
462 Log::debug("Engine::main() exiting.");
465 /*****
466 * Implementacja metod CPEventListenera
467 * (Komunikacja GUI -> silnik.)
471 * Wszystkie poniższe metody wywoływane są przez wątek nasłuchujący na
472 * wejściu i nie wykonują bezpośrednio poleceń, a kolejkują je
473 * w kolejce komend zegara lub silnika i wysyłają sygnał, że jest następne
474 * polecenie.
477 void init() {
478 engine_mode = e_idle;
479 official_board.clear();
482 virtual void startGame(std::string position, CPRole role,
483 int gameTime, int bonusTime) {
485 switch (role) {
486 case CP_ROLE_WHITE: // Log::debug("startGame as white");
487 break;
488 case CP_ROLE_BLACK: //Log::debug("startGame as black");
489 break;
490 default: break;
493 ec_prepare("startGame");
495 command_to_engine = c_update;
496 pause_search = true;
498 switch (role) {
499 case CP_ROLE_WHITE: engine_mode = e_white; break;
500 case CP_ROLE_BLACK: engine_mode = e_black; break;
501 case CP_ROLE_ANALYST: engine_mode = e_analyze; break;
502 default: break;
505 search_result.lock.grab();
506 search_result.init();
507 search_result.lock.release();
509 official_board_mutex.grab();
510 official_board.clear();
511 official_board_mutex.release();
513 ec_send();
514 // Log::debug("startGame send");
518 virtual void move(CPColor color, std::string move) {
519 // Log::debug("got opp move: " + move);
521 switch (color) {
522 case CP_COLOR_WHITE:
523 cout << "white moves " << move << " Im playing";
525 if (!official_board.wtm()) cout << "!!!!!!!!!!!!! ";
526 break;
527 case CP_COLOR_BLACK:
528 cout << "black moves Im playing";
529 if (official_board.wtm()) cout << "!!!!!!!!!!!!! ";
530 break;
531 default: break;
534 switch (engine_mode) {
535 case e_white: cout << " white ";break;
536 case e_black: cout << " black "; break;
537 default: break;
540 if (official_board.wtm()) {
541 cout << " on board white" << endl;
542 } else {
543 cout << " on board black" << endl;
546 ec_prepare("move");
547 command_to_engine = c_update;
548 pause_search = true;
550 official_board_mutex.grab();
551 typename G::move mv = official_board.parse_move(move);
553 cout << " parsed as " << official_board.print_move(mv) << endl;
555 /*official_board.prepare_moves();
556 bool valid = false;
557 while (official_board.has_next_move() && !valid) {
558 if (official_board.get_next_move() == mv) {
559 valid = true;
562 if (valid) {*/
563 official_board.make_move(mv);
564 /*} else {
565 Log::error("Invalid move.");
568 switch (official_board.game_state()) {
569 case G::white_wins:
570 cout << "endgame White wins" << endl;
571 break;
572 case G::black_wins:
573 cout << "endgame Black wins" << endl;
574 break;
575 case G::draw:
576 cout << "endgame Draw" << endl;
577 break;
580 official_board_mutex.release();
582 ec_send();
585 virtual void feedback(bool on) {
586 Log::log("feedback not implemented.");
589 virtual void setOption(std::map<std::string, std::string> & options) {
590 Log::log("setOption not implemented.");
593 virtual void getMove(CPColor color, int moveTime,
594 int gameTime) {
596 // Log::debug("getMove");
598 if ((color == CP_COLOR_WHITE && engine_mode == e_white) ||
599 (color == CP_COLOR_BLACK && engine_mode == e_black)) {
601 // do uzupełnienia
602 return;
605 ec_prepare("getMove");
606 if (color == CP_COLOR_WHITE) {
607 engine_mode = e_white;
608 } else {
609 engine_mode = e_black;
611 command_to_engine = c_update;
612 pause_search = true;
613 _time = gameTime;
614 ec_send();
617 virtual void forceMove() {
618 ec_prepare("forceMove");
619 command_to_engine = c_move;
620 pause_search = true;
621 ec_send();
624 virtual void stop() {
625 ec_prepare("stop");
626 engine_mode = e_idle;
627 command_to_engine = c_update;
628 pause_search = true;
629 ec_send();
632 virtual void endGame(CPResult result) {
633 ec_prepare("endGame");
634 engine_mode = e_idle;
635 command_to_engine = c_update;
636 pause_search = true;
637 ec_send();
640 virtual void quit() {
641 interface_loop = false;
643 ec_prepare("quit");
644 command_to_engine = c_quit;
645 pause_search = true;
646 ec_send();
648 clock_mutex.wake();
652 virtual void connect(std::string name) {};
653 virtual void bestMove(std::string move) {};
654 virtual void error(std::string message) {};
655 virtual void concede() {};
656 virtual void info(std::map<std::string, std::string> & options) {};