Moved ICS related API to the ICSAPI abstract class.
[tagua/yd.git] / src / icsconnection.cpp
blob8a4f99486c6ee391c33f97288a6ad5c4d288e619
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@sns.it>
3 (c) 2006 Maurizio Monge <maurizio.monge@kdemail.net>
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 */
11 #include <iostream>
12 #include <QRegExp>
13 #include <QStringList>
15 #include "icsconnection.h"
16 #include "poolinfo.h"
17 #include "positioninfo.h"
18 #include "algebraicnotation.h"
19 #include "player.h"
20 #include "gameinfo.h"
21 #include "pgnparser.h"
22 #include "icslistener.h"
23 #include "variants/variants.h"
25 using namespace boost;
27 QRegExp ICSConnection::pressReturn("^Press return to enter the server as \"\\S+\":");
29 // example: Creating: azsxdc (++++) Hispanico (1684) unrated crazyhouse 3 0
30 QRegExp ICSConnection::creating("^Creating: (\\S+)\\s+\\((\\S*)\\)\\s+(\\S+)\\s+\\((\\S*)\\)"
31 "\\s+(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s*.*$");
33 // example: {Game 149 (azsxdc vs. Hispanico) Creating unrated crazyhouse match.}
34 // example: {Game 149 (azsxdc vs. Hispanico) Game aborted on move 1} *
35 QRegExp ICSConnection::game("^\\{Game\\s+(\\d+)\\s+\\((\\S+)\\s+vs\\.\\s+(\\S+)\\)\\s+"
36 "(\\S+.*)\\}(?:\\s+(\\S+.*)|\\s*)$");
37 QRegExp ICSConnection::unexamine("^You\\s+are\\s+no\\s+longer\\s+examining\\s+game\\s+(\\d+)");
38 QRegExp ICSConnection::unobserve("^Removing\\s+game\\s+(\\d+)\\s+from\\s+observation\\s+list\\s*");
40 // example: 124 848 shanmark 1155 damopn [ br 2 12] 2:04 - 2:08 (39-39) W: 3
41 //QRegExp ICSConnection::gameInfo("^(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+"
42 // "\\[\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s+\\]");
44 // 1 2 3 4 5 6 7 8 9
45 // example: Game 124: shanmark ( 848) damopn (1155) rated blitz 2 12
46 // Game 170: AstralVision (2167) vladx (2197) rated standard 15 0
47 QRegExp ICSConnection::observed_game("^Game\\s+(\\d+):\\s+(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
48 "(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
49 "(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)");
51 QRegExp ICSConnection::login("^login: $");
52 QRegExp ICSConnection::password("^password: $");
53 QRegExp ICSConnection::fics("^\\S+% ");
54 QRegExp ICSConnection::beep("^\a");
56 //ex: Movelist for game 95:
57 QRegExp ICSConnection::move_list_start("^Movelist\\s+for\\s+game\\s+(\\d+):");
58 //ex: npssnt (1586) vs. mikkk (1487) --- Sun Sep 3, 18:16 PDT 2006
59 QRegExp ICSConnection::move_list_players("^(\\S+)\\s+\\((\\S*)\\)\\s+vs.\\s+(\\S+)\\s+\\((\\S*)\\)\\s*.*");
60 //ex: Unrated blitz match, initial time: 3 minutes, increment: 0 seconds.
61 QRegExp ICSConnection::move_list_game("^(\\S+)\\s+(\\S+)\\s+.*initial\\s+time:\\s*(\\d+)"
62 "\\s+.*increment:\\s*(\\d+)\\s*.*");
63 //ex: Move npssnt mikkk
64 QRegExp ICSConnection::move_list_ignore1("^Move\\s+(\\S+)\\s+(\\S+)");
65 //ex: ---- ---------------- ----------------
66 QRegExp ICSConnection::move_list_ignore2("^( +|-+)+");
67 QRegExp ICSConnection::move_list_terminator("^\\s*$");
69 QRegExp ICSConnection::goForward("^Game (\\d+): (\\S+) goes forward (\\d+) moves?\\.");
70 QRegExp ICSConnection::goBack("^Game (\\d+): (\\S+) backs up (\\d+) moves?\\.");
73 ICSConnection::ICSConnection()
74 : incomingGameInfo(0)
75 , m_move_list_game_info(NULL)
76 , m_move_list_position_info(NULL)
77 , m_move_list_pool_info(NULL) {
78 state = Normal;
79 connect(this, SIGNAL(receivedLine(QString, int)), this, SLOT(process(QString)));
80 connect(this, SIGNAL(receivedText(QString, int)), this, SLOT(processPartialLine(QString)));
83 bool ICSConnection::test(const QRegExp& pattern, const QString& str) {
84 if (pattern.indexIn(str, m_processed_offset, QRegExp::CaretAtOffset) >= 0) {
85 m_processed_offset = pattern.matchedLength();
86 return true;
88 else return false;
91 void ICSConnection::processPartialLine(QString str) {
92 // std::cout << "processing (partial) " << str << std::endl;
93 if (test(fics, str)) {
94 // std::cout << "matched prompt" << std::endl;
95 prompt();
97 else if (test(beep, str)) {
98 notification();
100 else if (test(pressReturn, str)) {
101 registeredNickname();
103 else if (test(login, str)) {
104 loginPrompt();
106 else if (test(password, str)) {
107 passwordPrompt();
109 else {
110 // no match, but it could be a partial command
111 m_processed_offset = 0;
115 void ICSConnection::process(QString str) {
116 switch(state) {
117 case Normal:
118 if (test(creating, str)) {
119 delete incomingGameInfo;
120 incomingGameInfo = new GameInfo(creating, 1);
122 else if(test(observed_game, str)) {
123 if(incomingGameInfo)
124 delete incomingGameInfo;
125 incomingGameInfo = new GameInfo(Player(observed_game.cap(2), observed_game.cap(3).toInt()),
126 Player(observed_game.cap(4), observed_game.cap(5).toInt()),
127 observed_game.cap(6), observed_game.cap(7),
128 observed_game.cap(8).toInt(), observed_game.cap(9).toInt() );
129 int number = observed_game.cap(1).toInt();
130 incomingGameInfo->setGameNumber(number);
131 m_games[number] = ICSGameData(-1, incomingGameInfo->type());
132 //std::cout << "ok, obs " << number << " of type " << incomingGameInfo->type() << std::endl;
134 else if (test(game, str)) {
135 //std::cout << "matched game. incomingGameInfo = " << incomingGameInfo << std::endl;
136 if(game.cap(4).startsWith("Creating") || game.cap(4).startsWith("Continuing") ) {
137 if (!incomingGameInfo) {
138 //this should really never happen, but anyway...
139 QStringList info = game.cap(4).split(' ');
140 if(info.size() >= 3)
141 incomingGameInfo = new GameInfo(Player(game.cap(2), 0),
142 Player(game.cap(3), 0),
143 info[1], info[2], 0, 0 );
145 if (incomingGameInfo) {
146 int number = game.cap(1).toInt();
147 incomingGameInfo->setGameNumber(number);
148 m_games[number] = ICSGameData(-1, incomingGameInfo->type());
151 else {
152 if (!incomingGameInfo) {
153 int number = game.cap(1).toInt();
154 //std::cout << "matching game " << number << " end" << std::endl;
155 m_games.erase(number);
156 QString what = game.cap(4);
157 QString result = game.cap(5);
158 endingGame(what, result);
162 else if (test(unexamine, str)) {
163 //std::cout << "matching examined game end" << std::endl;
164 int gameNumber = unexamine.cap(1).toInt();
165 m_games.erase(gameNumber);
166 endingExaminedGame(gameNumber);
168 else if (test(unobserve, str)) {
169 int gameNumber = unobserve.cap(1).toInt();
170 m_games.erase(gameNumber);
171 endingObservedGame(gameNumber);
173 else if (test(move_list_start, str)) {
174 //std::cout << "entering move list state" << std::endl;
175 m_move_list_game_num = move_list_start.cap(1).toInt();
176 state = MoveListHeader;
178 else {
179 PositionInfo positionInfo(m_games, str);
180 if (positionInfo.valid) {
181 std::cout << "matched style12: " << str << std::endl;
183 // if this is the first style12 for this game, notify game creation
184 int gameNumber = positionInfo.gameNumber;
185 bool game_start = ( !m_games.count(gameNumber)
186 || (incomingGameInfo &&
187 (incomingGameInfo->gameNumber() == gameNumber
188 || incomingGameInfo->gameNumber()==-1) ) );
190 if (game_start) {
191 //std::cout << "notifying game creation" << std::endl;
192 if(!m_games.count(gameNumber))
193 m_games[gameNumber] = ICSGameData(-1, "unknown"); //???
195 if (incomingGameInfo && (incomingGameInfo->gameNumber() != -1)
196 && (incomingGameInfo->gameNumber() != gameNumber) ) {
197 m_games.erase(incomingGameInfo->gameNumber());
198 delete incomingGameInfo;
199 incomingGameInfo = NULL; // discard game info
202 if (!incomingGameInfo) {
203 std::cout << "warning, got unexpected style12!!! " << gameNumber << std::endl;
204 incomingGameInfo = new GameInfo(Player(positionInfo.whitePlayer, 0),
205 Player(positionInfo.blackPlayer, 0),
206 "rated", "unknown", 0, 0 );
209 switch (positionInfo.relation) {
210 case PositionInfo::NotMyMove:
211 case PositionInfo::MyMove:
212 //std::cout << "creating game" << std::endl;
213 creatingGame(incomingGameInfo, positionInfo);
214 break;
215 case PositionInfo::Examining:
216 //std::cout << "creating examination" << std::endl;
217 creatingExaminedGame(incomingGameInfo, positionInfo);
218 break;
219 case PositionInfo::ObservingPlayed:
220 case PositionInfo::ObservingExamined:
221 //std::cout << "creating obs " << gameNumber << " " << incomingGameInfo->type() << std::endl;
222 creatingObservedGame(incomingGameInfo, positionInfo);
223 break;
224 default:
225 // unknown relation: ignoring
226 break;
228 delete incomingGameInfo;
229 incomingGameInfo = NULL;
232 std::cout << "known game. emitting style12 signal" << std::endl;
233 m_games[positionInfo.gameNumber].index = positionInfo.index();
234 if (shared_ptr<ICSListener> listener = m_games[positionInfo.gameNumber].listener.lock())
235 listener->notifyStyle12(positionInfo, game_start);
236 // time(positionInfo.whiteTime, positionInfo.blackTime);
237 if (positionInfo.relation == PositionInfo::MyMove) {
238 notification();
241 else {
242 PoolInfo pool_info(m_games, str);
243 if (pool_info.m_valid) {
244 // BROKEN
245 if (!pool_info.m_added_piece) {
246 if (shared_ptr<ICSListener> listener = m_games[pool_info.m_game_num].listener.lock())
247 listener->notifyPool(pool_info);
252 break;
254 case MoveListHeader:
255 if (test(move_list_players, str)){
256 //std::cout << "move list players: " << str << std::endl;
257 m_move_list_players = move_list_players.capturedTexts();
259 else if (test(move_list_game, str)){
260 //std::cout << "move list game: " << str << std::endl;
261 if (m_move_list_game_info)
262 delete m_move_list_game_info;
264 if (m_move_list_players.size()>=5)
265 m_move_list_game_info = new GameInfo(
266 Player(m_move_list_players[1], m_move_list_players[2].toInt()),
267 Player(m_move_list_players[3], m_move_list_players[4].toInt()),
268 move_list_game.cap(1).toLower(), move_list_game.cap(2),
269 move_list_game.cap(3).toInt(), move_list_game.cap(4).toInt()
271 else
272 m_move_list_game_info = new GameInfo( Player("unknown",0), Player("unknown",0),
273 move_list_game.cap(1).toLower(), move_list_game.cap(2),
274 move_list_game.cap(3).toInt(), move_list_game.cap(4).toInt()
276 m_move_list_game_info->setGameNumber(m_move_list_game_num);
278 //NOTE: here is where an unknown variant will be "upgraded" to the correct variant
279 m_games[m_move_list_game_num].setType(move_list_game.cap(2));
281 else if (test(move_list_terminator, str)) {
282 //std::cout << "move list ign3: " << str << std::endl;
284 else if (test(move_list_ignore1, str)){
285 //std::cout << "move list ign1: " << str << std::endl;
287 else if (test(move_list_ignore2, str)) {
288 //std::cout << "move list ign2: " << str << std::endl;
289 state = MoveListMoves;
291 else {
292 PositionInfo pi(m_games, str);
293 if (pi.valid)
294 m_move_list_position_info = new PositionInfo(pi);
295 else {
296 PoolInfo pooli(m_games, str);
297 if(pooli.m_valid)
298 m_move_list_pool_info = new PoolInfo(pooli);
301 break;
302 case MoveListMoves:
303 if (test(move_list_terminator, str)){
304 if (shared_ptr<ICSListener> listener = m_games[m_move_list_game_num].listener.lock()) {
305 AbstractPosition::Ptr p;
306 if (m_move_list_position_info)
307 p = m_move_list_position_info->position;
308 else {
309 std::map<int, ICSGameData>::const_iterator gi = m_games.find(m_move_list_game_num);
310 if (gi == m_games.end()) {
311 ERROR("BUG: Received move list for unknown game " << m_move_list_game_num);
313 else {
314 VariantInfo* variant = gi->second.variant;
315 p = variant->createPosition();
316 p->setup();
320 if (p) {
321 if (m_move_list_pool_info) {
322 //BROKEN
323 //p->setPool(m_move_list_pool_info->m_pool);
326 PGN pgn(m_move_list);
327 if (!pgn.valid())
328 std::cout << "parse error on move list" << std::endl;
329 else
330 listener->notifyMoveList(m_move_list_game_num, p, pgn);
334 if (m_move_list_game_info)
335 delete m_move_list_game_info;
336 if (m_move_list_position_info)
337 delete m_move_list_position_info;
338 if (m_move_list_pool_info)
339 delete m_move_list_pool_info;
340 m_move_list_game_info = NULL;
341 m_move_list_position_info = NULL;
342 m_move_list_pool_info = NULL;
343 m_move_list = QString();
344 state = Normal;
346 else
347 m_move_list += str;
348 break;
351 m_processed_offset = 0;
354 void ICSConnection::setListener(int gameNumber, const weak_ptr<ICSListener>& listener) {
355 m_games[gameNumber].listener = listener;
358 void ICSConnection::startup() {
359 sendText("alias $ @");
360 sendText("iset startpos 1");
361 sendText("iset ms 1");
362 sendText("iset lock 1");
363 sendText("set interface Tagua-0.10 (http://www.tagua-project.org)");
364 sendText("set style 12");