📄 sgfparser.cpp
字号:
// Black player name if (!parseProperty(toParse, "PB", tmp)) return false; if (!tmp.isEmpty()) gameData->playerBlack = tmp; else gameData->playerBlack = "Black"; // Black player rank if (!parseProperty(toParse, "BR", tmp)) return false; if (!tmp.isEmpty()) gameData->rankBlack = tmp; else gameData->rankBlack = ""; // Board size if (!parseProperty(toParse, "SZ", tmp)) return false; if (!tmp.isEmpty()) gameData->size = tmp.toInt(); else gameData->size = 19; // Komi if (!parseProperty(toParse, "KM", tmp)) return false; if (!tmp.isEmpty()) gameData->komi = tmp.toFloat(); else gameData->komi = 0.0; // Handicap if (!parseProperty(toParse, "HA", tmp)) return false; if (!tmp.isEmpty()) gameData->handicap = tmp.toInt(); else gameData->handicap = 0; // Result if (!parseProperty(toParse, "RE", tmp)) return false; if (!tmp.isEmpty()) gameData->result = tmp; else gameData->result = ""; // Date if (!parseProperty(toParse, "DT", tmp)) return false; if (!tmp.isEmpty()) gameData->date = tmp; else gameData->date = ""; // Place if (!parseProperty(toParse, "PC", tmp)) return false; if (!tmp.isEmpty()) gameData->place = tmp; else gameData->place = ""; // Copyright if (!parseProperty(toParse, "CP", tmp)) return false; if (!tmp.isEmpty()) gameData->copyright = tmp; else gameData->copyright = ""; // Game Name if (!parseProperty(toParse, "GN", tmp)) return false; if (!tmp.isEmpty()) gameData->gameName = tmp; else gameData->gameName = ""; // Comments style if (!parseProperty(toParse, "ST", tmp)) return false; if (!tmp.isEmpty()) gameData->style = tmp.toInt(); else gameData->style = 1; // Timelimit if (!parseProperty(toParse, "TM", tmp)) return false; if (!tmp.isEmpty()) { gameData->timelimit = tmp.toInt(); gameData->timeSystem = absolute; } else { gameData->timelimit = 0; gameData->timeSystem = none; } // Overtime == time system if (!parseProperty(toParse, "OT", tmp)) return false; if (!tmp.isEmpty()) { gameData->overtime = tmp; if (tmp.contains("byo-yomi")) { // type: OT[5x30 byo-yomi] gameData->timeSystem = byoyomi; int pos1, pos2; if ((pos1 = tmp.find("x")) != -1) { pos2 = tmp.find("byo"); QString time = tmp.mid(pos1+1, pos2-pos1-1); gameData->byoTime = time.toInt(); gameData->byoPeriods = tmp.left(pos1).toInt(); gameData->byoStones = 0; qDebug(QString("byoyomi time system: %1 Periods at %2 seconds").arg(gameData->byoPeriods).arg(gameData->byoTime)); } } else if (tmp.contains(":")) { // type: OT[3:30] = byo-yomi?; int pos1; gameData->timeSystem = byoyomi; if ((pos1 = tmp.find(":")) != -1) { QString time = tmp.left(pos1); int t = time.toInt()*60 + tmp.right(tmp.length() - pos1 - 1).toInt(); gameData->byoTime = 30; gameData->byoPeriods = t/gameData->byoTime; gameData->byoStones = 0; qDebug(QString("byoyomi time system: %1 Periods at %2 seconds").arg(gameData->byoPeriods).arg(gameData->byoTime)); } } else if (tmp.contains("Canadian")) { // type: OT[25/300 Canadian] gameData->timeSystem = canadian; int pos1, pos2; if ((pos1 = tmp.find("/")) != -1) { pos2 = tmp.find("Can"); QString time = tmp.mid(pos1+1, pos2-pos1-1); gameData->byoTime = time.toInt(); gameData->byoStones = tmp.left(pos1).toInt(); qDebug(QString("Canadian time system: %1 seconds at %2 stones").arg(gameData->byoTime).arg(gameData->byoStones)); } } // simple check if (gameData->byoStones < 0) gameData->byoStones = 0; if (gameData->byoTime <= 0) { gameData->byoTime = 0;// gameData->timeSystem = none; } } else { gameData->overtime = "";// gameData->timeSystem = none; gameData->byoStones = 0; } // Game number gameData->gameNumber = 0; gameData->fileName = fileName; boardHandler->board->initGame(gameData, true); // Set sgf flag return true;}bool SGFParser::exportSGFtoClipB(QString *str, Tree *tree){ CHECK_PTR(tree); if (stream != NULL) delete stream; stream = new QTextStream(str, IO_WriteOnly); bool res = writeStream(tree); delete stream; stream = NULL; return res;}bool SGFParser::doWrite(const QString &fileName, Tree *tree){ CHECK_PTR(tree); QFile file(fileName); if (!file.open(IO_WriteOnly)) { QMessageBox::warning(0, PACKAGE, Board::tr("Could not open file:") + " " + fileName); return false; } if (stream != NULL) delete stream; stream = new QTextStream(&file); bool res = writeStream(tree); file.flush(); file.close(); delete stream; stream = NULL; return res;}bool SGFParser::writeStream(Tree *tree){ CHECK_PTR(stream); if (!initStream(stream)) { QMessageBox::critical(0, PACKAGE, Board::tr("Invalid text encoding given. Please check preferences!")); delete stream; return false; } Move *root = tree->getRoot(); if (root == NULL) return false; GameData *gameData = boardHandler->getGameData(); // Traverse the tree recursive in pre-order isRoot = true; traverse(root, gameData); return true;}void SGFParser::writeGameHeader(GameData *gameData){ // Assemble data for root node *stream << ";GM[1]FF[4]" // We play Go, we use FF 4 << "AP[" << PACKAGE << ":" << VERSION << "]"; // Application name : Version if (gameData->style >= 0 && gameData->style <= 4) *stream << "ST[" << gameData->style << "]"; else *stream << "ST[1]"; // We show vars of current node if (gameData->gameName.isEmpty()) // Skip game name if empty *stream << endl; else *stream << "GN[" << gameData->gameName << "]" // Game Name << endl; *stream << "SZ[" << gameData->size << "]" // Board size << "HA[" << gameData->handicap << "]" // Handicap << "KM[" << gameData->komi << "]"; // Komi// << endl; if (gameData->timelimit != 0) *stream << "TM[" << gameData->timelimit << "]"; // Timelimit if (!gameData->overtime.isEmpty()) *stream << "OT[" << gameData->overtime << "]" << endl; // Overtime if (!gameData->playerWhite.isEmpty()) *stream << "PW[" << gameData->playerWhite << "]"; // White name if (!gameData->rankWhite.isEmpty()) *stream << "WR[" << gameData->rankWhite << "]"; // White rank if (!gameData->playerBlack.isEmpty()) *stream << "PB[" << gameData->playerBlack << "]"; // Black name if (!gameData->rankBlack.isEmpty()) *stream << "BR[" << gameData->rankBlack << "]"; // Black rank if (!gameData->result.isEmpty()) *stream << "RE[" << gameData->result << "]"; // Result if (!gameData->date.isEmpty()) *stream << "DT[" << gameData->date << "]"; // Date if (!gameData->place.isEmpty()) *stream << "PC[" << gameData->place << "]"; // Place if (!gameData->copyright.isEmpty()) *stream << "CP[" << gameData->copyright << "]"; // Copyright *stream << endl;}void SGFParser::traverse(Move *t, GameData *gameData){ *stream << "("; int col = -1, cnt = 6; do { if (isRoot) { writeGameHeader(gameData); *stream << t->saveMove(isRoot); isRoot = false; } else { // do some formatting: add single B[]/W[] properties to one line, max: 10 // if more properties, e.g. B[]PL[]C] -> new line QString txt = t->saveMove(false); int cnt_old = cnt; cnt = txt.length(); if (col % 10 == 0 || col == 1 && cnt != 6 || cnt_old != 6 || col == -1) { *stream << endl; col = 0; } *stream << txt; col++; } Move *tmp = t->son; if (tmp != NULL && tmp->brother != NULL) { do { *stream << endl; traverse(tmp, NULL); } while ((tmp = tmp->brother) != NULL); break; } } while ((t = t->son) != NULL); *stream << endl << ")";}bool SGFParser::parseASCII(const QString &fileName, ASCII_Import *charset, bool isFilename){ QTextStream *txt = NULL; bool result = false; asciiOffsetX = asciiOffsetY = 0; #if 0 qDebug("BLACK STONE CHAR %c\n" "WHITE STONE CHAR %c\n" "STAR POINT CHAR %c\n" "EMPTY POINT CHAR %c\n" "HOR BORDER CHAR %c\n" "VER BORDER CHAR %c\n", charset->blackStone, charset->whiteStone, charset->starPoint, charset->emptyPoint, charset->hBorder, charset->vBorder);#endif if (isFilename) // Load from file { QFile file; if (fileName.isNull() || fileName.isEmpty()) { QMessageBox::warning(0, PACKAGE, Board::tr("No filename given!")); delete txt; return false; } file.setName(fileName); if (!file.exists()) { QMessageBox::warning(0, PACKAGE, Board::tr("Could not find file:") + " " + fileName); delete txt; return false; } if (!file.open(IO_ReadOnly)) { QMessageBox::warning(0, PACKAGE, Board::tr("Could not open file:") + " " + fileName); delete txt; return false; } txt = new QTextStream(&file); if (!initStream(txt)) { QMessageBox::critical(0, PACKAGE, Board::tr("Invalid text encoding given. Please check preferences!")); delete txt; return false; } result = parseASCIIStream(txt, charset); file.close(); } else // a string was passed instead of a filename, copy from clipboard { if (fileName.isNull() || fileName.isEmpty()) { QMessageBox::warning(0, PACKAGE, Board::tr("Importing ASCII failed. Clipboard empty?")); delete txt; return false; } QString buf(fileName); txt = new QTextStream(buf, IO_ReadOnly); if (!initStream(txt)) { QMessageBox::critical(0, PACKAGE, Board::tr("Invalid text encoding given. Please check preferences!")); delete txt; return false; } result = parseASCIIStream(txt, charset); } delete txt; return result;}bool SGFParser::parseASCIIStream(QTextStream *stream, ASCII_Import *charset){ CHECK_PTR(stream); QStrList asciiLines; asciiLines.setAutoDelete(TRUE); int i=0, first=-1, last=-1, y=1; bool flag=false; QString dummy = QString(QChar(charset->vBorder)).append(charset->vBorder).append(charset->vBorder); // "---" while (!stream->atEnd()) { QString tmp = stream->readLine(); asciiLines.append(tmp.latin1()); if (tmp.find('.') != -1) flag = true; if (tmp.find(dummy) != -1) { if (first == -1 && !flag) first = i; else last = i; } i++; } if (!flag) { GameData gd; QString ascii = asciiLines.getFirst(); // do some fast checks: one line string?qDebug("no standard ASCII file"); int nr = ascii.contains("0") + ascii.contains("b") + ascii.contains("w"); if (nr == 81) {qDebug("found 9x9"); gd.size = 9; gd.komi = 3.5; } else if (nr == 169) {qDebug("found 13x13"); gd.size = 13; gd.komi = 4.5; } else if (nr == 361) {qDebug("found 19x19"); gd.size = 19; gd.komi = 5.5; } else {qDebug(QString("found nr == %1").arg(nr)); return false; } gd.handicap = 0; if (gd.size != boardHandler->board->getBoardSize()) boardHandler->board->initGame(&gd, true); int i = 0; for (int y = 1; y <= gd.size; y++) for (int x = 1; x <= gd.size; x++) { while (ascii[i] != 'b' && ascii[i] != 'w' && ascii[i] != '0') i++; if (ascii[i] == 'b') { boardHandler->addStone(stoneBlack, x, y); } else if (ascii[i] == 'w') { boardHandler->addStone(stoneWhite, x, y); } i++; } asciiLines.clear(); return true; } // qDebug("Y: FIRST = %d, LAST = %d", first, last); if (first == -1 && last != -1) asciiOffsetY = boardHandler->board->getBoardSize() - last; QStrListIterator it(asciiLines); for (; it.current() && y < boardHandler->board->getBoardSize(); ++it) if (!doASCIIParse(it.current(), y, charset)) return false; asciiLines.clear(); return true;}bool SGFParser::doASCIIParse(const QString &toParse, int &y, ASCII_Import *charset){ int pos, x = 0, length = toParse.length(); if (!checkBoardSize(toParse, charset)) return false; for (pos=toParse.find(charset->emptyPoint, 0); pos<length; pos++) { // qDebug("READING %d/%d", x, y); if (x >= boardHandler->board->getBoardSize() - asciiOffsetX) // Abort if right edge of board reached break; // Empty point or star point if (toParse[pos] == charset->emptyPoint || toParse[pos] == charset->starPoint) x++; // Right border else if (x>0 && toParse[pos] == charset->hBorder) break; // White stone else if (toParse[pos] == charset->whiteStone && x && y) { x++; // qDebug("W %d/%d", x, y); boardHandler->addStone(stoneWhite, asciiOffsetX+x, asciiOffsetY+y); } // Black stone else if (toParse[pos] == charset->blackStone && x && y) { x++; // qDebug("B %d/%d", x, y); boardHandler->addStone(stoneBlack, asciiOffsetX+x, asciiOffsetY+y); } // Text label: a-z else if (toParse[pos] >= 'a' && toParse[pos] <= 'z') { x++; // qDebug("MARK: %d/%d - %c", x, y, toParse[pos].latin1()); boardHandler->editMark(asciiOffsetX+x, asciiOffsetY+y, markText, toParse[pos]); } // Number label: 1-9 else if (toParse[pos] >= '1' && toParse[pos] <= '9') { x++; // qDebug("NUMBER: %d/%d - %c", x, y, toParse[pos].latin1()); boardHandler->editMark(asciiOffsetX+x, asciiOffsetY+y, markNumber, toParse[pos]); } } if (x) y++; return true;}bool SGFParser::checkBoardSize(const QString &toParse, ASCII_Import *charset){ // Determine x offset int left = toParse.find(charset->hBorder), right = toParse.find(charset->hBorder, left+1); // qDebug("Left = %d, Right = %d", left, right); if (right == -1) { int first = toParse.find(charset->emptyPoint), tmp = toParse.find(charset->starPoint); first = first > tmp && tmp != -1 ? tmp : first; if (left > first) asciiOffsetX = boardHandler->board->getBoardSize() - (left - first)/2 - ((left-first)%2 ? 1 : 0); else asciiOffsetX = 0; // qDebug("ASSUMING PART OF BOARD ONLY. First = %d, ASCII_OFFSET_X = %d", first, asciiOffsetX); } else if (left > -1 && right > -1) { // qDebug("ASSUMING FULL BOARD. BOARD SIZE = %d", boardHandler->board->getBoardSize()); asciiOffsetX = 0; if ((right - left)/2 != boardHandler->board->getBoardSize()) // TODO: Warning and abort? qWarning("Board size does not fit."); } else asciiOffsetX = 0; return true;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -