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.
13 #include <QStringList>
14 #include "icsconnection.h"
16 #include "positioninfo.h"
17 #include "algebraicnotation.h"
20 #include "pgnparser.h"
21 #include "icslistener.h"
22 #include "variants/variants.h"
24 using namespace boost
;
26 QRegExp
ICSConnection::pressReturn("^Press return to enter the server as \"\\S+\":");
28 // example: Creating: azsxdc (++++) Hispanico (1684) unrated crazyhouse 3 0
29 QRegExp
ICSConnection::creating("^Creating: (\\S+)\\s+\\((\\S*)\\)\\s+(\\S+)\\s+\\((\\S*)\\)"
30 "\\s+(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s*.*$");
32 // example: {Game 149 (azsxdc vs. Hispanico) Creating unrated crazyhouse match.}
33 // example: {Game 149 (azsxdc vs. Hispanico) Game aborted on move 1} *
34 QRegExp
ICSConnection::game("^\\{Game\\s+(\\d+)\\s+\\((\\S+)\\s+vs\\.\\s+(\\S+)\\)\\s+"
35 "(\\S+.*)\\}(?:\\s+(\\S+.*)|\\s*)$");
36 QRegExp
ICSConnection::unexamine("^You\\s+are\\s+no\\s+longer\\s+examining\\s+game\\s+(\\d+)");
37 QRegExp
ICSConnection::unobserve("^Removing\\s+game\\s+(\\d+)\\s+from\\s+observation\\s+list\\s*");
39 // example: 124 848 shanmark 1155 damopn [ br 2 12] 2:04 - 2:08 (39-39) W: 3
40 //QRegExp ICSConnection::gameInfo("^(\\d+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+"
41 // "\\[\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)\\s+\\]");
44 // example: Game 124: shanmark ( 848) damopn (1155) rated blitz 2 12
45 // Game 170: AstralVision (2167) vladx (2197) rated standard 15 0
46 QRegExp
ICSConnection::observed_game("^Game\\s+(\\d+):\\s+(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
47 "(\\S+)\\s+\\(\\s*(\\S+)\\s*\\)\\s+"
48 "(\\S+)\\s+(\\S+)\\s+(\\d+)\\s+(\\d+)");
50 QRegExp
ICSConnection::login("^login: $");
51 QRegExp
ICSConnection::password("^password: $");
52 QRegExp
ICSConnection::fics("^\\S+% ");
53 QRegExp
ICSConnection::beep("^\a");
55 //ex: Movelist for game 95:
56 QRegExp
ICSConnection::move_list_start("^Movelist\\s+for\\s+game\\s+(\\d+):");
57 //ex: npssnt (1586) vs. mikkk (1487) --- Sun Sep 3, 18:16 PDT 2006
58 QRegExp
ICSConnection::move_list_players("^(\\S+)\\s+\\((\\S*)\\)\\s+vs.\\s+(\\S+)\\s+\\((\\S*)\\)\\s*.*");
59 //ex: Unrated blitz match, initial time: 3 minutes, increment: 0 seconds.
60 QRegExp
ICSConnection::move_list_game("^(\\S+)\\s+(\\S+)\\s+.*initial\\s+time:\\s*(\\d+)"
61 "\\s+.*increment:\\s*(\\d+)\\s*.*");
62 //ex: Move npssnt mikkk
63 QRegExp
ICSConnection::move_list_ignore1("^Move\\s+(\\S+)\\s+(\\S+)");
64 //ex: ---- ---------------- ----------------
65 QRegExp
ICSConnection::move_list_ignore2("^( +|-+)+");
66 QRegExp
ICSConnection::move_list_terminator("^\\s*$");
68 QRegExp
ICSConnection::goForward("^Game (\\d+): (\\S+) goes forward (\\d+) moves?\\.");
69 QRegExp
ICSConnection::goBack("^Game (\\d+): (\\S+) backs up (\\d+) moves?\\.");
72 ICSConnection::ICSConnection()
74 , m_move_list_game_info(NULL
)
75 , m_move_list_position_info(NULL
)
76 , m_move_list_pool_info(NULL
) {
78 connect(this, SIGNAL(receivedLine(QString
, int)), this, SLOT(process(QString
)));
79 connect(this, SIGNAL(receivedText(QString
, int)), this, SLOT(processPartialLine(QString
)));
82 bool ICSConnection::test(const QRegExp
& pattern
, const QString
& str
) {
83 if (pattern
.indexIn(str
, m_processed_offset
, QRegExp::CaretAtOffset
) >= 0) {
84 m_processed_offset
= pattern
.matchedLength();
90 void ICSConnection::processPartialLine(QString str
) {
91 // std::cout << "processing (partial) " << str << std::endl;
92 if (test(fics
, str
)) {
93 // std::cout << "matched prompt" << std::endl;
96 else if (test(beep
, str
)) {
99 else if (test(pressReturn
, str
)) {
100 emit
registeredNickname();
102 else if (test(login
, str
)) {
105 else if (test(password
, str
)) {
106 emit
passwordPrompt();
109 // no match, but it could be a partial command
110 m_processed_offset
= 0;
114 void ICSConnection::process(QString str
) {
117 if (test(creating
, str
)) {
118 delete incomingGameInfo
;
119 incomingGameInfo
= new GameInfo(creating
, 1);
121 else if(test(observed_game
, str
)) {
123 delete incomingGameInfo
;
124 incomingGameInfo
= new GameInfo(Player(observed_game
.cap(2), observed_game
.cap(3).toInt()),
125 Player(observed_game
.cap(4), observed_game
.cap(5).toInt()),
126 observed_game
.cap(6), observed_game
.cap(7),
127 observed_game
.cap(8).toInt(), observed_game
.cap(9).toInt() );
128 int number
= observed_game
.cap(1).toInt();
129 incomingGameInfo
->setGameNumber(number
);
130 m_games
[number
] = ICSGameData(-1, incomingGameInfo
->type());
131 //std::cout << "ok, obs " << number << " of type " << incomingGameInfo->type() << std::endl;
133 else if (test(game
, str
)) {
134 //std::cout << "matched game. incomingGameInfo = " << incomingGameInfo << std::endl;
135 if(game
.cap(4).startsWith("Creating") || game
.cap(4).startsWith("Continuing") ) {
136 if(!incomingGameInfo
) {
137 //this should really never happen, but anyway...
138 QStringList info
= game
.cap(4).split(' ');
140 incomingGameInfo
= new GameInfo(Player(game
.cap(2), 0),
141 Player(game
.cap(3), 0),
142 info
[1], info
[2], 0, 0 );
144 if(incomingGameInfo
) {
145 int number
= game
.cap(1).toInt();
146 incomingGameInfo
->setGameNumber(number
);
147 m_games
[number
] = ICSGameData(-1, incomingGameInfo
->type());
151 if (!incomingGameInfo
) {
152 int number
= game
.cap(1).toInt();
153 //std::cout << "matching game " << number << " end" << std::endl;
154 m_games
.erase(number
);
155 QString what
= game
.cap(4);
156 QString result
= game
.cap(5);
157 emit
endingGame(what
, result
);
161 else if (test(unexamine
, str
)) {
162 //std::cout << "matching examined game end" << std::endl;
163 int gameNumber
= unexamine
.cap(1).toInt();
164 m_games
.erase(gameNumber
);
165 emit
endingExaminedGame(gameNumber
);
167 else if (test(unobserve
, str
)) {
168 int gameNumber
= unobserve
.cap(1).toInt();
169 m_games
.erase(gameNumber
);
170 emit
endingObservedGame(gameNumber
);
172 else if (test(move_list_start
, str
)) {
173 //std::cout << "entering move list state" << std::endl;
174 m_move_list_game_num
= move_list_start
.cap(1).toInt();
175 state
= MoveListHeader
;
178 PositionInfo
positionInfo(m_games
, str
);
179 if (positionInfo
.valid
) {
180 //std::cout << "matched style12" << std::endl;
182 // if this is the first style12 for this game, notify game creation
183 int gameNumber
= positionInfo
.gameNumber
;
184 bool game_start
= ( !m_games
.count(gameNumber
)
185 || (incomingGameInfo
&&
186 (incomingGameInfo
->gameNumber() == gameNumber
187 || incomingGameInfo
->gameNumber()==-1) ) );
190 //std::cout << "notifying game creation" << std::endl;
191 if(!m_games
.count(gameNumber
))
192 m_games
[gameNumber
] = ICSGameData(-1, "unknown"); //???
194 if (incomingGameInfo
&& (incomingGameInfo
->gameNumber() != -1)
195 && (incomingGameInfo
->gameNumber() != gameNumber
) ) {
196 m_games
.erase(incomingGameInfo
->gameNumber());
197 delete incomingGameInfo
;
198 incomingGameInfo
= NULL
; // discard game info
201 if(!incomingGameInfo
) {
202 std::cout
<< "warning, got unexpected style12!!! " << gameNumber
<< std::endl
;
203 incomingGameInfo
= new GameInfo(Player(positionInfo
.whitePlayer
, 0),
204 Player(positionInfo
.blackPlayer
, 0),
205 "rated", "unknown", 0, 0 );
208 switch (positionInfo
.relation
) {
209 case PositionInfo::NotMyMove
:
210 case PositionInfo::MyMove
:
211 //std::cout << "creating game" << std::endl;
212 emit
creatingGame(incomingGameInfo
, positionInfo
);
214 case PositionInfo::Examining
:
215 //std::cout << "creating examination" << std::endl;
216 emit
creatingExaminedGame(incomingGameInfo
, positionInfo
);
218 case PositionInfo::ObservingPlayed
:
219 case PositionInfo::ObservingExamined
:
220 //std::cout << "creating obs " << gameNumber << " " << incomingGameInfo->type() << std::endl;
221 emit
creatingObservedGame(incomingGameInfo
, positionInfo
);
224 // unknown relation: ignoring
227 delete incomingGameInfo
;
228 incomingGameInfo
= NULL
;
231 //std::cout << "known game. emitting style12 signal" << std::endl;
232 m_games
[positionInfo
.gameNumber
].index
= positionInfo
.index();
233 if (shared_ptr
<ICSListener
> listener
= m_games
[positionInfo
.gameNumber
].listener
.lock())
234 listener
->notifyStyle12(positionInfo
, game_start
);
235 // emit time(positionInfo.whiteTime, positionInfo.blackTime);
236 if(positionInfo
.relation
== PositionInfo::MyMove
) {
241 PoolInfo
pool_info(m_games
, str
);
242 if (pool_info
.m_valid
) {
243 if( !(m_games
[pool_info
.m_game_num
].variant
== "crazyhouse"
244 && pool_info
.m_added_piece
) )
245 if (shared_ptr
<ICSListener
> listener
= m_games
[pool_info
.m_game_num
].listener
.lock())
246 listener
->notifyPool(pool_info
);
253 if (test(move_list_players
, str
)){
254 //std::cout << "move list players: " << str << std::endl;
255 m_move_list_players
= move_list_players
.capturedTexts();
257 else if(test(move_list_game
, str
)){
258 //std::cout << "move list game: " << str << std::endl;
259 if(m_move_list_game_info
)
260 delete m_move_list_game_info
;
262 if(m_move_list_players
.size()>=5)
263 m_move_list_game_info
= new GameInfo(
264 Player(m_move_list_players
[1], m_move_list_players
[2].toInt()),
265 Player(m_move_list_players
[3], m_move_list_players
[4].toInt()),
266 move_list_game
.cap(1).toLower(), move_list_game
.cap(2),
267 move_list_game
.cap(3).toInt(), move_list_game
.cap(4).toInt()
270 m_move_list_game_info
= new GameInfo( Player("unknown",0), Player("unknown",0),
271 move_list_game
.cap(1).toLower(), move_list_game
.cap(2),
272 move_list_game
.cap(3).toInt(), move_list_game
.cap(4).toInt()
274 m_move_list_game_info
->setGameNumber(m_move_list_game_num
);
276 //NOTE: here is where an unknown variant will be "upgraded" to the correct variant
277 m_games
[m_move_list_game_num
].variant
= move_list_game
.cap(2);
279 else if(test(move_list_terminator
, str
)) {
280 //std::cout << "move list ign3: " << str << std::endl;
282 else if(test(move_list_ignore1
, str
)){
283 //std::cout << "move list ign1: " << str << std::endl;
285 else if(test(move_list_ignore2
, str
)) {
286 //std::cout << "move list ign2: " << str << std::endl;
287 state
= MoveListMoves
;
290 PositionInfo
pi(m_games
, str
);
292 m_move_list_position_info
= new PositionInfo(pi
);
294 PoolInfo
pooli(m_games
, str
);
296 m_move_list_pool_info
= new PoolInfo(pooli
);
301 if (test(move_list_terminator
, str
)){
302 if (shared_ptr
<ICSListener
> listener
= m_games
[m_move_list_game_num
].listener
.lock()) {
303 AbstractPosition::Ptr p
;
304 if(m_move_list_position_info
)
305 p
= m_move_list_position_info
->position
;
307 QString v
= m_games
.count(m_move_list_game_num
)
308 ? m_games
[m_move_list_game_num
].variant
: "unknown";
309 p
= Variant::variant(GameInfo::variantCode(v
))->createPosition();
312 if(m_move_list_pool_info
) {
314 //p->setPool(m_move_list_pool_info->m_pool);
317 PGN
pgn(m_move_list
);
319 std::cout
<< "parse error on move list" << std::endl
;
321 listener
->notifyMoveList(m_move_list_game_num
, p
, pgn
);
324 if(m_move_list_game_info
)
325 delete m_move_list_game_info
;
326 if(m_move_list_position_info
)
327 delete m_move_list_position_info
;
328 if(m_move_list_pool_info
)
329 delete m_move_list_pool_info
;
330 m_move_list_game_info
= NULL
;
331 m_move_list_position_info
= NULL
;
332 m_move_list_pool_info
= NULL
;
333 m_move_list
= QString();
341 m_processed_offset
= 0;
344 void ICSConnection::setListener(int gameNumber
, const weak_ptr
<ICSListener
>& listener
) {
345 m_games
[gameNumber
].listener
= listener
;
348 void ICSConnection::startup() {
349 sendText("alias $ @");
350 sendText("iset startpos 1");
351 sendText("iset ms 1");
352 sendText("iset lock 1");
353 sendText("set interface KBoard-0.9 (http://kboard.sf.net)");
354 sendText("set style 12");