Initial porting to the new component API.
[tagua/yd.git] / src / icsconnection.cpp
blob0fe6e6f62ef7681e806c48d37f40df72c501b944
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 <iostream>
12 #include <QRegExp>
13 #include <QStringList>
15 #include <core/state.h>
16 #include <core/statefactory.h>
17 #include <core/variant.h>
19 #include "icsconnection.h"
20 #include "poolinfo.h"
21 #include "positioninfo.h"
22 #include "player.h"
23 #include "gameinfo.h"
24 #include "pgnparser.h"
25 #include "icslistener.h"
26 #include "variants.h"
28 using namespace boost;
30 QRegExp ICSConnection::pressReturn("^Press return to enter the server as \"\\S+\":");
32 // example: Creating: azsxdc (++++) Hispanico (1684) unrated crazyhouse 3 0
33 QRegExp ICSConnection::creating("^Creating: (\\S+)\\s+\\((\\S*)\\)\\s+(\\S+)\\s+\\((\\S*)\\)"
34 "\\s+(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s*.*$");
36 // example: {Game 149 (azsxdc vs. Hispanico) Creating unrated crazyhouse match.}
37 // example: {Game 149 (azsxdc vs. Hispanico) Game aborted on move 1} *
38 QRegExp ICSConnection::game("^\\{Game\\s+(\\d+)\\s+\\((\\S+)\\s+vs\\.\\s+(\\S+)\\)\\s+"
39 "(\\S+.*)\\}(?:\\s+(\\S+.*)|\\s*)$");
40 QRegExp ICSConnection::unexamine("^You\\s+are\\s+no\\s+longer\\s+examining\\s+game\\s+(\\d+)");
41 QRegExp ICSConnection::unobserve("^Removing\\s+game\\s+(\\d+)\\s+from\\s+observation\\s+list\\s*");
43 // example: 124 848 shanmark 1155 damopn [ br 2 12] 2:04 - 2:08 (39-39) W: 3
44 //QRegExp ICSConnection::gameInfo("^(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+"
45 // "\\[\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s+\\]");
47 // 1 2 3 4 5 6 7 8 9
48 // example: Game 124: shanmark ( 848) damopn (1155) rated blitz 2 12
49 // Game 170: AstralVision (2167) vladx (2197) rated standard 15 0
50 QRegExp ICSConnection::observed_game("^Game\\s+(\\d+):\\s+(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
51 "(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
52 "(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)");
54 QRegExp ICSConnection::login("^login: $");
55 QRegExp ICSConnection::password("^password: $");
56 QRegExp ICSConnection::fics("^\\S+% ");
57 QRegExp ICSConnection::beep("^\a");
59 //ex: Movelist for game 95:
60 QRegExp ICSConnection::move_list_start("^Movelist\\s+for\\s+game\\s+(\\d+):");
61 //ex: npssnt (1586) vs. mikkk (1487) --- Sun Sep 3, 18:16 PDT 2006
62 QRegExp ICSConnection::move_list_players("^(\\S+)\\s+\\((\\S*)\\)\\s+vs.\\s+(\\S+)\\s+\\((\\S*)\\)\\s*.*");
63 //ex: Unrated blitz match, initial time: 3 minutes, increment: 0 seconds.
64 QRegExp ICSConnection::move_list_game("^(\\S+)\\s+(\\S+)\\s+.*initial\\s+time:\\s*(\\d+)"
65 "\\s+.*increment:\\s*(\\d+)\\s*.*");
66 //ex: Move npssnt mikkk
67 QRegExp ICSConnection::move_list_ignore1("^Move\\s+(\\S+)\\s+(\\S+)");
68 //ex: ---- ---------------- ----------------
69 QRegExp ICSConnection::move_list_ignore2("^( +|-+)+");
70 QRegExp ICSConnection::move_list_terminator("^\\s*$");
72 QRegExp ICSConnection::goForward("^Game (\\d+): (\\S+) goes forward (\\d+) moves?\\.");
73 QRegExp ICSConnection::goBack("^Game (\\d+): (\\S+) backs up (\\d+) moves?\\.");
76 ICSConnection::ICSConnection()
77 : incomingGameInfo(0)
78 , m_move_list_game_info(NULL)
79 , m_move_list_position_info(NULL)
80 , m_move_list_pool_info(NULL) {
81 state = Normal;
82 connect(this, SIGNAL(receivedLine(QString, int)), this, SLOT(process(QString)));
83 connect(this, SIGNAL(receivedText(QString, int)), this, SLOT(processPartialLine(QString)));
86 bool ICSConnection::test(const QRegExp& pattern, const QString& str) {
87 if (pattern.indexIn(str, m_processed_offset, QRegExp::CaretAtOffset) >= 0) {
88 m_processed_offset = pattern.matchedLength();
89 return true;
91 else return false;
94 void ICSConnection::processPartialLine(QString str) {
95 // std::cout << "processing (partial) " << str << std::endl;
96 if (test(fics, str)) {
97 // std::cout << "matched prompt" << std::endl;
98 prompt();
100 else if (test(beep, str)) {
101 notification();
103 else if (test(pressReturn, str)) {
104 registeredNickname();
106 else if (test(login, str)) {
107 loginPrompt();
109 else if (test(password, str)) {
110 passwordPrompt();
112 else {
113 // no match, but it could be a partial command
114 m_processed_offset = 0;
118 void ICSConnection::process(QString str) {
119 switch(state) {
120 case Normal:
121 if (test(creating, str)) {
122 delete incomingGameInfo;
123 incomingGameInfo = new GameInfo(creating, 1);
125 else if(test(observed_game, str)) {
126 if(incomingGameInfo)
127 delete incomingGameInfo;
128 incomingGameInfo = new GameInfo(Player(observed_game.cap(2), observed_game.cap(3).toInt()),
129 Player(observed_game.cap(4), observed_game.cap(5).toInt()),
130 observed_game.cap(6), observed_game.cap(7),
131 observed_game.cap(8).toInt(), observed_game.cap(9).toInt() );
132 int number = observed_game.cap(1).toInt();
133 incomingGameInfo->setGameNumber(number);
134 m_games[number] = ICSGameData(-1, incomingGameInfo->type());
135 //std::cout << "ok, obs " << number << " of type " << incomingGameInfo->type() << std::endl;
137 else if (test(game, str)) {
138 //std::cout << "matched game. incomingGameInfo = " << incomingGameInfo << std::endl;
139 if(game.cap(4).startsWith("Creating") || game.cap(4).startsWith("Continuing") ) {
140 if (!incomingGameInfo) {
141 //this should really never happen, but anyway...
142 QStringList info = game.cap(4).split(' ');
143 if(info.size() >= 3)
144 incomingGameInfo = new GameInfo(Player(game.cap(2), 0),
145 Player(game.cap(3), 0),
146 info[1], info[2], 0, 0 );
148 if (incomingGameInfo) {
149 int number = game.cap(1).toInt();
150 incomingGameInfo->setGameNumber(number);
151 m_games.insert(std::make_pair(number, ICSGameData(-1, incomingGameInfo->type())));
154 else {
155 if (!incomingGameInfo) {
156 int number = game.cap(1).toInt();
157 //std::cout << "matching game " << number << " end" << std::endl;
158 m_games.erase(number);
159 QString what = game.cap(4);
160 QString result = game.cap(5);
161 endingGame(what, result);
165 else if (test(unexamine, str)) {
166 //std::cout << "matching examined game end" << std::endl;
167 int gameNumber = unexamine.cap(1).toInt();
168 m_games.erase(gameNumber);
169 endingExaminedGame(gameNumber);
171 else if (test(unobserve, str)) {
172 int gameNumber = unobserve.cap(1).toInt();
173 m_games.erase(gameNumber);
174 endingObservedGame(gameNumber);
176 else if (test(move_list_start, str)) {
177 //std::cout << "entering move list state" << std::endl;
178 m_move_list_game_num = move_list_start.cap(1).toInt();
179 state = MoveListHeader;
181 else {
182 PositionInfo positionInfo;
183 bool game_start = positionInfo.load(m_games, str);
184 if (positionInfo.valid) {
185 int gameNumber = positionInfo.gameNumber;
186 GameList::const_iterator game_it = m_games.find(gameNumber);
187 Q_ASSERT(game_it != m_games.end());
189 bool incoming = incomingGameInfo &&
190 incomingGameInfo->gameNumber() == gameNumber;
192 if (game_start || incoming) {
193 // delete extraneous game info
194 if (incomingGameInfo &&
195 incomingGameInfo->gameNumber() != gameNumber) {
196 int n = incomingGameInfo->gameNumber();
197 if (n != -1)
198 m_games.erase(n);
199 delete incomingGameInfo;
200 incomingGameInfo = 0;
203 // no info on this game
204 if (!incomingGameInfo) {
205 WARNING("unexpected style 12 for game " << gameNumber);
206 incomingGameInfo = new GameInfo(Player(positionInfo.whitePlayer, 0),
207 Player(positionInfo.blackPlayer, 0),
208 "rated", "", 0, 0);
211 switch (positionInfo.relation) {
212 case PositionInfo::NotMyMove:
213 case PositionInfo::MyMove:
214 //std::cout << "creating game" << std::endl;
215 creatingGame(incomingGameInfo, positionInfo);
216 break;
217 case PositionInfo::Examining:
218 //std::cout << "creating examination" << std::endl;
219 creatingExaminedGame(incomingGameInfo, positionInfo);
220 break;
221 case PositionInfo::ObservingPlayed:
222 case PositionInfo::ObservingExamined:
223 //std::cout << "creating obs " << gameNumber << " " << incomingGameInfo->type() << std::endl;
224 creatingObservedGame(incomingGameInfo, positionInfo);
225 break;
226 default:
227 // unknown relation: ignoring
228 break;
231 delete incomingGameInfo;
232 incomingGameInfo = 0;
235 if (shared_ptr<ICSListener> listener = m_games[positionInfo.gameNumber].listener.lock())
236 listener->notifyStyle12(positionInfo, game_start);
238 if (positionInfo.relation == PositionInfo::MyMove) {
239 notification();
242 else {
243 PoolInfo pool_info(m_games, str);
244 if (pool_info.m_valid) {
245 // BROKEN
246 if (pool_info.m_added_piece != Piece()) {
247 if (shared_ptr<ICSListener> listener = m_games[pool_info.m_game_num].listener.lock())
248 listener->notifyPool(pool_info);
253 break;
255 case MoveListHeader:
256 if (test(move_list_players, str)){
257 //std::cout << "move list players: " << str << std::endl;
258 m_move_list_players = move_list_players.capturedTexts();
260 else if (test(move_list_game, str)){
261 //std::cout << "move list game: " << str << std::endl;
262 if (m_move_list_game_info)
263 delete m_move_list_game_info;
265 if (m_move_list_players.size()>=5)
266 m_move_list_game_info = new GameInfo(
267 Player(m_move_list_players[1], m_move_list_players[2].toInt()),
268 Player(m_move_list_players[3], m_move_list_players[4].toInt()),
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 else
273 m_move_list_game_info = new GameInfo( Player("unknown",0), Player("unknown",0),
274 move_list_game.cap(1).toLower(), move_list_game.cap(2),
275 move_list_game.cap(3).toInt(), move_list_game.cap(4).toInt()
277 m_move_list_game_info->setGameNumber(m_move_list_game_num);
279 //NOTE: here is where an unknown variant will be "upgraded" to the correct variant
280 m_games[m_move_list_game_num].setType(move_list_game.cap(2));
282 else if (test(move_list_terminator, str)) {
283 //std::cout << "move list ign3: " << str << std::endl;
285 else if (test(move_list_ignore1, str)){
286 //std::cout << "move list ign1: " << str << std::endl;
288 else if (test(move_list_ignore2, str)) {
289 //std::cout << "move list ign2: " << str << std::endl;
290 state = MoveListMoves;
292 else {
293 PositionInfo pi;
294 pi.load(m_games, str);
295 if (pi.valid)
296 m_move_list_position_info = new PositionInfo(pi);
297 else {
298 PoolInfo pooli(m_games, str);
299 if(pooli.m_valid)
300 m_move_list_pool_info = new PoolInfo(pooli);
303 break;
304 case MoveListMoves:
305 if (test(move_list_terminator, str)){
306 if (shared_ptr<ICSListener> listener = m_games[m_move_list_game_num].listener.lock()) {
307 StatePtr p;
308 if (m_move_list_position_info)
309 p = m_move_list_position_info->position;
310 else {
311 std::map<int, ICSGameData>::const_iterator gi = m_games.find(m_move_list_game_num);
312 if (gi == m_games.end()) {
313 ERROR("BUG: Received move list for unknown game " << m_move_list_game_num);
315 else {
316 Variant* variant = gi->second.variant;
317 IStateFactory* fact = requestComponent<IStateFactory>(
318 variant, "state_factory");
319 p = StatePtr(fact->createState());
320 p->setup();
324 if (p) {
325 if (m_move_list_pool_info) {
326 //BROKEN
327 //p->setPool(m_move_list_pool_info->m_pool);
330 PGN pgn(m_move_list);
331 if (!pgn.valid())
332 std::cout << "parse error on move list" << std::endl;
333 else
334 listener->notifyMoveList(m_move_list_game_num, p, pgn);
338 if (m_move_list_game_info)
339 delete m_move_list_game_info;
340 if (m_move_list_position_info)
341 delete m_move_list_position_info;
342 if (m_move_list_pool_info)
343 delete m_move_list_pool_info;
344 m_move_list_game_info = NULL;
345 m_move_list_position_info = NULL;
346 m_move_list_pool_info = NULL;
347 m_move_list = QString();
348 state = Normal;
350 else
351 m_move_list += str;
352 break;
355 m_processed_offset = 0;
358 void ICSConnection::setListener(int gameNumber, const weak_ptr<ICSListener>& listener) {
359 m_games[gameNumber].listener = listener;
362 void ICSConnection::startup() {
363 sendText("alias $ @");
364 sendText("iset startpos 1");
365 sendText("iset ms 1");
366 sendText("iset lock 1");
367 sendText("set interface Tagua-0.10 (http://www.tagua-project.org)");
368 sendText("set style 12");