8 #include "CPEventListener.h"
9 #include "CPCommandParser.h"
10 #include "ConnectSix.h"
21 class Engine
: public CPEventListener
{
24 * Dostęp do danych i synchronizacja między wątkami.
27 pthread_mutex_t 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");
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");
45 if (pthread_mutex_lock(&mutex
) != 0)
46 Log::error("pthread_mutex_lock failed in Mutex.grab()");
50 if (pthread_mutex_unlock(&mutex
) != 0)
51 Log::error("pthread_mutex_unlock failed in Mutex.release()");
55 if (pthread_cond_wait(&cond
,&mutex
) != 0)
56 Log::error("pthread_cond_wait failed in Mutex.sleep()");
59 void sleep(timespec
&ts
) {
61 if ((x
= pthread_cond_timedwait(&cond
,&mutex
, &ts
)) != 0) {
63 Log::error("pthread_cond_wait failed in method Mutex.sleep(timespec)");
69 if (pthread_cond_signal(&cond
) != 0)
70 Log::error("pthread_cond_signal failed in Mutex.wake()");
84 int score_lower_bound
;
85 int score_upper_bound
;
88 vector
<typename
G::move
> pv
;
100 void update(SearchResult
&sr
) {
102 in_progress
= sr
.in_progress
;
103 nominal_depth
= sr
.nominal_depth
;
110 SearchResult search_result
;
114 SearchResult current_search
;
118 Mutex official_board_mutex
;
121 virtual void think(G
&, SearchResult
&, int) = 0;
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
;
132 void engine_sleep() {
135 if (command_to_engine
== c_none
) {
136 engine_mutex
.sleep();
140 void *engine_thread() {
141 Log::debug("Engine thread started.");
146 pause_search
= false;
147 command tmp
= command_to_engine
;
148 command_to_engine
= c_none
;
150 cout
<< "Engine loop " << tmp
<< endl
;
154 switch(engine_mode
) {
155 case e_idle
: engine_sleep(); break;
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();
173 G board
= official_board
;
174 official_board_mutex
.release();
176 engine_mutex
.release();
179 think(board
, current_search
, _time
);
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
;
199 engine_mutex
.release();
201 Log::debug("Engine thread finished.");
205 // czyszczenie tablicy transpozycji itp.
208 pause_search
= false;
209 search_result
.lock
.grab();
210 search_result
.init();
211 search_result
.lock
.release();
213 // Log::debug("reset/pause");
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!!!");
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!");
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).");
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()) {
268 cout
<< "endgame white" << endl
;
271 cout
<< "endgame black" << endl
;
274 cout
<< "endgame draw" << endl
;
278 official_board_mutex
.release();
282 Log::error("Internal error: bad command sent to the engine thread.");
286 Log::debug("Engine thread finished.");
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)
309 void *clock_thread() {
310 Log::debug("Clock thread started.");
318 switch (engine_mode
) {
323 official_board_mutex
.grab();
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
;
337 official_board_mutex
.release();
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;
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) {
363 ts
.tv_nsec
-= 1000000000;
368 clock_mutex
.sleep(ts
);
369 // cout << "CLOCK TICK" << endl;
371 if (!interface_loop
) {
376 clock_mutex
.release();
378 Log::debug("Clock thread finished.");
382 static void* clock_thread_helper(void *context
) {
383 return ((Engine
*) context
) -> clock_thread();
388 void *interface_thread() {
389 Log::debug("Interface thread started.");
391 CPCommandParser
parser(cin
, *this);
393 while (interface_loop
) {
394 string line
= parser
.nextCommand();
397 } catch (int error
) {
399 cout
<< "Unknown command." << endl
;
401 cout
<< "Command corrupted." << endl
;
405 Log::debug("Interface thread finished.");
409 static void* interface_thread_helper(void *context
) {
410 return ((Engine
*) context
) -> interface_thread();
413 void ec_prepare(string msg
) {
415 cout
<< "ec_prepare " << msg
<< " (" << ec_prep
<< "); command_to_engine " << command_to_engine
<< endl
;
418 while (command_to_engine
!= c_none
) {
419 engine_mutex
.sleep();
424 cout
<< "ec_send (" << ec_prep
<< ")";
428 engine_mutex
.release();
435 interface_loop
= true;
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.");
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
478 engine_mode
= e_idle
;
479 official_board
.clear();
482 virtual void startGame(std::string position
, CPRole role
,
483 int gameTime
, int bonusTime
) {
486 case CP_ROLE_WHITE
: // Log::debug("startGame as white");
488 case CP_ROLE_BLACK
: //Log::debug("startGame as black");
493 ec_prepare("startGame");
495 command_to_engine
= c_update
;
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;
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();
514 // Log::debug("startGame send");
518 virtual void move(CPColor color
, std::string move
) {
519 // Log::debug("got opp move: " + move);
523 cout
<< "white moves " << move
<< " Im playing";
525 if (!official_board
.wtm()) cout
<< "!!!!!!!!!!!!! ";
528 cout
<< "black moves Im playing";
529 if (official_board
.wtm()) cout
<< "!!!!!!!!!!!!! ";
534 switch (engine_mode
) {
535 case e_white
: cout
<< " white ";break;
536 case e_black
: cout
<< " black "; break;
540 if (official_board
.wtm()) {
541 cout
<< " on board white" << endl
;
543 cout
<< " on board black" << endl
;
547 command_to_engine
= c_update
;
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();
557 while (official_board.has_next_move() && !valid) {
558 if (official_board.get_next_move() == mv) {
563 official_board
.make_move(mv
);
565 Log::error("Invalid move.");
568 switch (official_board
.game_state()) {
570 cout
<< "endgame White wins" << endl
;
573 cout
<< "endgame Black wins" << endl
;
576 cout
<< "endgame Draw" << endl
;
580 official_board_mutex
.release();
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
,
596 // Log::debug("getMove");
598 if ((color
== CP_COLOR_WHITE
&& engine_mode
== e_white
) ||
599 (color
== CP_COLOR_BLACK
&& engine_mode
== e_black
)) {
605 ec_prepare("getMove");
606 if (color
== CP_COLOR_WHITE
) {
607 engine_mode
= e_white
;
609 engine_mode
= e_black
;
611 command_to_engine
= c_update
;
617 virtual void forceMove() {
618 ec_prepare("forceMove");
619 command_to_engine
= c_move
;
624 virtual void stop() {
626 engine_mode
= e_idle
;
627 command_to_engine
= c_update
;
632 virtual void endGame(CPResult result
) {
633 ec_prepare("endGame");
634 engine_mode
= e_idle
;
635 command_to_engine
= c_update
;
640 virtual void quit() {
641 interface_loop
= false;
644 command_to_engine
= c_quit
;
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
) {};