Replaced all std::cout with kDebug.
[tagua/yd.git] / src / icsconnection.cpp
blob0db46b24acbd998e4b68a74fa65d3ef1d441dc1e
1 /*
2 Copyright (c) 2006 Paolo Capriotti <p.capriotti@gmail.com>
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 "icsconnection.h"
12 #include <QRegExp>
13 #include <QStringList>
14 #include <KDebug>
15 #include "poolinfo.h"
16 #include "positioninfo.h"
17 #include "player.h"
18 #include "gameinfo.h"
19 #include "pgnparser.h"
20 #include "icslistener.h"
21 #include "variants.h"
23 using namespace boost;
25 QRegExp ICSConnection::pressReturn("^Press return to enter the server as \"\\S+\":");
27 // example: Creating: azsxdc (++++) Hispanico (1684) unrated crazyhouse 3 0
28 QRegExp ICSConnection::creating("^Creating: (\\S+)\\s+\\((\\S*)\\)\\s+(\\S+)\\s+\\((\\S*)\\)"
29 "\\s+(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s*.*$");
31 // example: {Game 149 (azsxdc vs. Hispanico) Creating unrated crazyhouse match.}
32 // example: {Game 149 (azsxdc vs. Hispanico) Game aborted on move 1} *
33 QRegExp ICSConnection::game("^\\{Game\\s+(\\d+)\\s+\\((\\S+)\\s+vs\\.\\s+(\\S+)\\)\\s+"
34 "(\\S+.*)\\}(?:\\s+(\\S+.*)|\\s*)$");
35 QRegExp ICSConnection::unexamine("^You\\s+are\\s+no\\s+longer\\s+examining\\s+game\\s+(\\d+)");
36 QRegExp ICSConnection::unobserve("^Removing\\s+game\\s+(\\d+)\\s+from\\s+observation\\s+list\\s*");
38 // example: 124 848 shanmark 1155 damopn [ br 2 12] 2:04 - 2:08 (39-39) W: 3
39 //QRegExp ICSConnection::gameInfo("^(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+"
40 // "\\[\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s+\\]");
42 // 1 2 3 4 5 6 7 8 9
43 // example: Game 124: shanmark ( 848) damopn (1155) rated blitz 2 12
44 // Game 170: AstralVision (2167) vladx (2197) rated standard 15 0
45 QRegExp ICSConnection::observed_game("^Game\\s+(\\d+):\\s+(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
46 "(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
47 "(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)");
49 QRegExp ICSConnection::login("^login: $");
50 QRegExp ICSConnection::password("^password: $");
51 QRegExp ICSConnection::fics("^\\S+% ");
52 QRegExp ICSConnection::beep("^\a");
54 //ex: Movelist for game 95:
55 QRegExp ICSConnection::move_list_start("^Movelist\\s+for\\s+game\\s+(\\d+):");
56 //ex: npssnt (1586) vs. mikkk (1487) --- Sun Sep 3, 18:16 PDT 2006
57 QRegExp ICSConnection::move_list_players("^(\\S+)\\s+\\((\\S*)\\)\\s+vs.\\s+(\\S+)\\s+\\((\\S*)\\)\\s*.*");
58 //ex: Unrated blitz match, initial time: 3 minutes, increment: 0 seconds.
59 QRegExp ICSConnection::move_list_game("^(\\S+)\\s+(\\S+)\\s+.*initial\\s+time:\\s*(\\d+)"
60 "\\s+.*increment:\\s*(\\d+)\\s*.*");
61 //ex: Move npssnt mikkk
62 QRegExp ICSConnection::move_list_ignore1("^Move\\s+(\\S+)\\s+(\\S+)");
63 //ex: ---- ---------------- ----------------
64 QRegExp ICSConnection::move_list_ignore2("^( +|-+)+");
65 QRegExp ICSConnection::move_list_terminator("^\\s*$");
67 QRegExp ICSConnection::goForward("^Game (\\d+): (\\S+) goes forward (\\d+) moves?\\.");
68 QRegExp ICSConnection::goBack("^Game (\\d+): (\\S+) backs up (\\d+) moves?\\.");
71 ICSConnection::ICSConnection()
72 : incomingGameInfo(0)
73 , m_move_list_game_info(NULL)
74 , m_move_list_position_info(NULL)
75 , m_move_list_pool_info(NULL) {
76 state = Normal;
77 connect(this, SIGNAL(receivedLine(QString, int)), this, SLOT(process(QString)));
78 connect(this, SIGNAL(receivedText(QString, int)), this, SLOT(processPartialLine(QString)));
81 bool ICSConnection::test(const QRegExp& pattern, const QString& str) {
82 if (pattern.indexIn(str, m_processed_offset, QRegExp::CaretAtOffset) >= 0) {
83 m_processed_offset = pattern.matchedLength();
84 return true;
86 else return false;
89 void ICSConnection::processPartialLine(QString str) {
90 // kDebug() << "processing (partial) " << str;
91 if (test(fics, str)) {
92 // kDebug() << "matched prompt";
93 prompt();
95 else if (test(beep, str)) {
96 notification();
98 else if (test(pressReturn, str)) {
99 registeredNickname();
101 else if (test(login, str)) {
102 loginPrompt();
104 else if (test(password, str)) {
105 passwordPrompt();
107 else {
108 // no match, but it could be a partial command
109 m_processed_offset = 0;
113 void ICSConnection::process(QString str) {
114 switch(state) {
115 case Normal:
116 if (test(creating, str)) {
117 delete incomingGameInfo;
118 incomingGameInfo = new GameInfo(creating, 1);
120 else if(test(observed_game, str)) {
121 if(incomingGameInfo)
122 delete incomingGameInfo;
123 incomingGameInfo = new GameInfo(Player(observed_game.cap(2), observed_game.cap(3).toInt()),
124 Player(observed_game.cap(4), observed_game.cap(5).toInt()),
125 observed_game.cap(6), observed_game.cap(7),
126 observed_game.cap(8).toInt(), observed_game.cap(9).toInt() );
127 int number = observed_game.cap(1).toInt();
128 incomingGameInfo->setGameNumber(number);
129 m_games[number] = ICSGameData(-1, incomingGameInfo->type());
130 //kDebug() << "ok, obs " << number << " of type " << incomingGameInfo->type();
132 else if (test(game, str)) {
133 //kDebug() << "matched game. incomingGameInfo = " << incomingGameInfo;
134 if(game.cap(4).startsWith("Creating") || game.cap(4).startsWith("Continuing") ) {
135 if (!incomingGameInfo) {
136 //this should really never happen, but anyway...
137 QStringList info = game.cap(4).split(' ');
138 if(info.size() >= 3)
139 incomingGameInfo = new GameInfo(Player(game.cap(2), 0),
140 Player(game.cap(3), 0),
141 info[1], info[2], 0, 0 );
143 if (incomingGameInfo) {
144 int number = game.cap(1).toInt();
145 incomingGameInfo->setGameNumber(number);
146 m_games.insert(std::make_pair(number, ICSGameData(-1, incomingGameInfo->type())));
149 else {
150 if (!incomingGameInfo) {
151 int number = game.cap(1).toInt();
152 //kDebug() << "matching game " << number << " end";
153 m_games.erase(number);
154 QString what = game.cap(4);
155 QString result = game.cap(5);
156 endingGame(what, result);
160 else if (test(unexamine, str)) {
161 //kDebug() << "matching examined game end";
162 int gameNumber = unexamine.cap(1).toInt();
163 m_games.erase(gameNumber);
164 endingExaminedGame(gameNumber);
166 else if (test(unobserve, str)) {
167 int gameNumber = unobserve.cap(1).toInt();
168 m_games.erase(gameNumber);
169 endingObservedGame(gameNumber);
171 else if (test(move_list_start, str)) {
172 //kDebug() << "entering move list state";
173 m_move_list_game_num = move_list_start.cap(1).toInt();
174 state = MoveListHeader;
176 else {
177 PositionInfo positionInfo;
178 bool game_start = positionInfo.load(m_games, str);
179 if (positionInfo.valid) {
180 int gameNumber = positionInfo.gameNumber;
181 GameList::const_iterator game_it = m_games.find(gameNumber);
182 Q_ASSERT(game_it != m_games.end());
184 bool incoming = incomingGameInfo &&
185 incomingGameInfo->gameNumber() == gameNumber;
187 if (game_start || incoming) {
188 // delete extraneous game info
189 if (incomingGameInfo &&
190 incomingGameInfo->gameNumber() != gameNumber) {
191 int n = incomingGameInfo->gameNumber();
192 if (n != -1)
193 m_games.erase(n);
194 delete incomingGameInfo;
195 incomingGameInfo = 0;
198 // no info on this game
199 if (!incomingGameInfo) {
200 kWarning() << "unexpected style 12 for game" << gameNumber;
201 incomingGameInfo = new GameInfo(Player(positionInfo.whitePlayer, 0),
202 Player(positionInfo.blackPlayer, 0),
203 "rated", "", 0, 0);
206 switch (positionInfo.relation) {
207 case PositionInfo::NotMyMove:
208 case PositionInfo::MyMove:
209 //kDebug() << "creating game";
210 creatingGame(incomingGameInfo, positionInfo);
211 break;
212 case PositionInfo::Examining:
213 //kDebug() << "creating examination";
214 creatingExaminedGame(incomingGameInfo, positionInfo);
215 break;
216 case PositionInfo::ObservingPlayed:
217 case PositionInfo::ObservingExamined:
218 //kDebug() << "creating obs " << gameNumber << " " << incomingGameInfo->type();
219 creatingObservedGame(incomingGameInfo, positionInfo);
220 break;
221 default:
222 // unknown relation: ignoring
223 break;
226 delete incomingGameInfo;
227 incomingGameInfo = 0;
230 if (shared_ptr<ICSListener> listener = m_games[positionInfo.gameNumber].listener.lock())
231 listener->notifyStyle12(positionInfo, game_start);
233 if (positionInfo.relation == PositionInfo::MyMove) {
234 notification();
237 else {
238 PoolInfo pool_info(m_games, str);
239 if (pool_info.m_valid) {
240 // BROKEN
241 if (!pool_info.m_added_piece) {
242 if (shared_ptr<ICSListener> listener = m_games[pool_info.m_game_num].listener.lock())
243 listener->notifyPool(pool_info);
248 break;
250 case MoveListHeader:
251 if (test(move_list_players, str)){
252 //kDebug() << "move list players: " << str;
253 m_move_list_players = move_list_players.capturedTexts();
255 else if (test(move_list_game, str)){
256 //kDebug() << "move list game: " << str;
257 if (m_move_list_game_info)
258 delete m_move_list_game_info;
260 if (m_move_list_players.size()>=5)
261 m_move_list_game_info = new GameInfo(
262 Player(m_move_list_players[1], m_move_list_players[2].toInt()),
263 Player(m_move_list_players[3], m_move_list_players[4].toInt()),
264 move_list_game.cap(1).toLower(), move_list_game.cap(2),
265 move_list_game.cap(3).toInt(), move_list_game.cap(4).toInt()
267 else
268 m_move_list_game_info = new GameInfo( Player("unknown",0), Player("unknown",0),
269 move_list_game.cap(1).toLower(), move_list_game.cap(2),
270 move_list_game.cap(3).toInt(), move_list_game.cap(4).toInt()
272 m_move_list_game_info->setGameNumber(m_move_list_game_num);
274 //NOTE: here is where an unknown variant will be "upgraded" to the correct variant
275 m_games[m_move_list_game_num].setType(move_list_game.cap(2));
277 else if (test(move_list_terminator, str)) {
278 //kDebug() << "move list ign3: " << str;
280 else if (test(move_list_ignore1, str)){
281 //kDebug() << "move list ign1: " << str;
283 else if (test(move_list_ignore2, str)) {
284 //kDebug() << "move list ign2: " << str;
285 state = MoveListMoves;
287 else {
288 PositionInfo pi;
289 pi.load(m_games, str);
290 if (pi.valid)
291 m_move_list_position_info = new PositionInfo(pi);
292 else {
293 PoolInfo pooli(m_games, str);
294 if(pooli.m_valid)
295 m_move_list_pool_info = new PoolInfo(pooli);
298 break;
299 case MoveListMoves:
300 if (test(move_list_terminator, str)){
301 if (shared_ptr<ICSListener> listener = m_games[m_move_list_game_num].listener.lock()) {
302 AbstractPosition::Ptr p;
303 if (m_move_list_position_info)
304 p = m_move_list_position_info->position;
305 else {
306 std::map<int, ICSGameData>::const_iterator gi = m_games.find(m_move_list_game_num);
307 if (gi == m_games.end()) {
308 kError() << "Received move list for unknown game " << m_move_list_game_num;
310 else {
311 VariantPtr variant = gi->second.variant;
312 p = variant->createPosition();
313 p->setup();
317 if (p) {
318 if (m_move_list_pool_info) {
319 //BROKEN
320 //p->setPool(m_move_list_pool_info->m_pool);
323 PGN pgn(m_move_list);
324 if (!pgn.valid())
325 kDebug() << "parse error on move list";
326 else
327 listener->notifyMoveList(m_move_list_game_num, p, pgn);
331 if (m_move_list_game_info)
332 delete m_move_list_game_info;
333 if (m_move_list_position_info)
334 delete m_move_list_position_info;
335 if (m_move_list_pool_info)
336 delete m_move_list_pool_info;
337 m_move_list_game_info = NULL;
338 m_move_list_position_info = NULL;
339 m_move_list_pool_info = NULL;
340 m_move_list = QString();
341 state = Normal;
343 else
344 m_move_list += str;
345 break;
348 m_processed_offset = 0;
351 void ICSConnection::setListener(int gameNumber, const weak_ptr<ICSListener>& listener) {
352 m_games[gameNumber].listener = listener;
355 void ICSConnection::startup() {
356 sendText("alias $ @");
357 sendText("iset startpos 1");
358 sendText("iset ms 1");
359 sendText("iset lock 1");
360 sendText("set interface Tagua-0.10 (http://www.tagua-project.org)");
361 sendText("set style 12");