/* * stats.cpp * SGF Tools * * Created by Mark Lentczner on 4/29/07. * */ #include #include #include #include extern "C" { #include "all.h" #include "protos.h" } #include #include #include #include #include #include #include struct SGFInfo *sgfc = NULL; char option_warnings = TRUE; char option_keep_unknown_props = TRUE; char option_keep_obsolete_props = TRUE; char option_del_empty_nodes = FALSE; char option_del_move_markup = FALSE; char option_interactive = FALSE; char option_linebreaks = 1; char option_fix_variation = FALSE; char option_findstart = 1; char option_game_signature = FALSE; char option_strict_checking = FALSE; char option_reorder_variations = FALSE; using namespace std; class Stat { public: Stat(); void noteUse(); void noteUse(const string&); void noteUse(const struct PropValue*); void printOn(ostream&, const string& name, const string& indent) const; enum ValueHandling { NoValues, FirstValue, SecondValue, BothValues, Value = FirstValue }; Stat& addGroup(const string& groupName); Stat& addProperty(const string& propName, ValueHandling vh = NoValues); Stat& forValue(const string&); static void buildSGFTree(); static void printSGFTree(ostream&); static void newSource(); static Stat& forProperty(const string& propName); private: int mUseCount; // number of times used int mSourceCount; // number of sources that used it int mLastSource; typedef pair StatEntry; typedef vector StatVector; Stat* mParentStat; StatVector mChildStats; ValueHandling mValueHandling; typedef map StatMap; static int sSource; static Stat sSGFTree; static StatMap sProperties; static Stat* sUnknownGroup; Stat& addChild(StatVector::iterator, const string& childName); friend bool operator<(const string&, const StatEntry&); friend bool operator<(const StatEntry&, const string&); // Not implemented: Stat(const Stat&); Stat& operator=(const Stat&); }; Stat::Stat() : mUseCount(0), mSourceCount(0), mLastSource(-1), mParentStat(0), mValueHandling(NoValues) { } void Stat::noteUse() { mUseCount += 1; if (sSource != mLastSource) { mSourceCount += 1; mLastSource = sSource; } if (mParentStat) mParentStat->noteUse(); } void Stat::noteUse(const string& v) { forValue(v).noteUse(); } void Stat::noteUse(const struct PropValue* v) { string s; switch (mValueHandling) { case NoValues: default: noteUse(); return; case FirstValue: s = v->value; break; case SecondValue: s = v->value2; break; case BothValues: s = v->value; s += ":"; s += v->value2; break; } noteUse(s); } void Stat::printOn(ostream& out, const string& name, const string& indent) const { out << indent << name << " in " << mSourceCount << " files, used " << mUseCount << " times" << endl; for (StatVector::const_iterator i = mChildStats.begin(), end = mChildStats.end(); i != end; ++i) { i->second->printOn(out, i->first, indent + " "); } } Stat Stat::sSGFTree; Stat::StatMap Stat::sProperties; Stat* Stat::sUnknownGroup = 0; bool operator<(const string& v, const Stat::StatEntry& s) { return v < s.first; } bool operator<(const Stat::StatEntry& s, const string& v) { return s.first < v; } Stat& Stat::addChild(StatVector::iterator p, const string& childName) { Stat* child = new Stat(); mChildStats.insert(p, StatEntry(childName, child)); child->mParentStat = this; return *child; } Stat& Stat::addGroup(const string& groupName) { return addChild(mChildStats.end(), groupName); } Stat& Stat::addProperty(const string& propName, ValueHandling vh) { StatVector::iterator p = lower_bound(mChildStats.begin(), mChildStats.end(), propName); Stat& property = addChild(p, propName); property.mValueHandling = vh; sProperties[propName] = &property; return property; } Stat& Stat::forValue(const string& value) { StatVector::iterator p = lower_bound(mChildStats.begin(), mChildStats.end(), value); if (p != mChildStats.end() && p->first == value) { return *(p->second); } else { return addChild(p, value); } } void Stat::buildSGFTree() { Stat& move = sSGFTree.addGroup("move"); Stat& moveBasic = move.addGroup("basic"); moveBasic.addProperty("B"); moveBasic.addProperty("W"); Stat& moveMisc = move.addGroup("misc"); moveMisc.addProperty("KO"); moveMisc.addProperty("MN"); Stat& setup = sSGFTree.addGroup("setup"); setup.addProperty("AB"); setup.addProperty("AE"); setup.addProperty("AW"); setup.addProperty("PL"); Stat& annotation = sSGFTree.addGroup("annotation"); Stat& textAnnotation = annotation.addGroup("text"); textAnnotation.addProperty("C"); textAnnotation.addProperty("N"); Stat& nodeAnnotation = annotation.addGroup("node"); nodeAnnotation.addProperty("DM", Value); nodeAnnotation.addProperty("GB", Value); nodeAnnotation.addProperty("GW", Value); nodeAnnotation.addProperty("HO", Value); nodeAnnotation.addProperty("UC", Value); nodeAnnotation.addProperty("V"); Stat& moveAnnotation = annotation.addGroup("move"); moveAnnotation.addProperty("BM", Value); moveAnnotation.addProperty("DO"); moveAnnotation.addProperty("IT"); moveAnnotation.addProperty("TE", Value); Stat& markup = sSGFTree.addGroup("markup"); Stat& simpleMarkup = markup.addGroup("simple"); simpleMarkup.addProperty("CR"); simpleMarkup.addProperty("MA"); simpleMarkup.addProperty("SL"); simpleMarkup.addProperty("SQ"); simpleMarkup.addProperty("TR"); Stat& labelMarkup = markup.addGroup("label"); labelMarkup.addProperty("LB", SecondValue); labelMarkup.addProperty("LB-set"); Stat& graphicMarkup = markup.addGroup("graphic"); graphicMarkup.addProperty("AR"); graphicMarkup.addProperty("DD"); graphicMarkup.addProperty("LN"); Stat& goMarkup = markup.addGroup("go specific"); goMarkup.addProperty("TB"); goMarkup.addProperty("TW"); Stat& root = sSGFTree.addGroup("root"); root.addProperty("AP", false ? BothValues : FirstValue); root.addProperty("CA", Value); root.addProperty("FF", Value); root.addProperty("GM", Value); root.addProperty("ST", Value); root.addProperty("SZ", Value); Stat& gameInfo = sSGFTree.addGroup("game info"); gameInfo.addProperty("AN"); gameInfo.addProperty("BR"); gameInfo.addProperty("BT"); gameInfo.addProperty("CP"); gameInfo.addProperty("DT"); gameInfo.addProperty("EV"); gameInfo.addProperty("GN"); gameInfo.addProperty("GC"); gameInfo.addProperty("ON"); gameInfo.addProperty("OT"); gameInfo.addProperty("PB"); gameInfo.addProperty("PC"); gameInfo.addProperty("PW"); gameInfo.addProperty("RE"); gameInfo.addProperty("RO"); gameInfo.addProperty("RU"); gameInfo.addProperty("SO"); gameInfo.addProperty("TM"); gameInfo.addProperty("US"); gameInfo.addProperty("WR"); gameInfo.addProperty("WT"); Stat& goGameInfo = gameInfo.addGroup("go specific"); goGameInfo.addProperty("HA"); goGameInfo.addProperty("KM"); Stat& timing = sSGFTree.addGroup("timing"); timing.addProperty("BL"); timing.addProperty("OB"); timing.addProperty("OW"); timing.addProperty("WL"); Stat& misc = sSGFTree.addGroup("misc"); misc.addProperty("FG", FirstValue); misc.addProperty("PM", Value); misc.addProperty("VW"); sUnknownGroup = &sSGFTree.addGroup("unknown"); } void Stat::printSGFTree(ostream& out) { sSGFTree.printOn(out, "all properites", ""); } int Stat::sSource = -1; void Stat::newSource() { sSource += 1; } Stat& Stat::forProperty(const string& propName) { StatMap::iterator p = sProperties.find(propName); if (p != sProperties.end()) { return *p->second; } else { return sUnknownGroup->addProperty(propName); } } namespace { void statLBProperty(struct Property *prop) { typedef set StringSet; StringSet labels; struct PropValue *v; v = prop->value; while(v) { string l(v->value2); labels.insert(l + ","); v = v->next; } ostringstream s; copy(labels.begin(), labels.end(), ostream_iterator(s)); Stat::forProperty("LB-set").noteUse(s.str()); } void statProperty(struct Property *prop) { Stat& s = Stat::forProperty(prop->idstr); struct PropValue *v; v = prop->value; while(v) { s.noteUse(v); v = v->next; } if (prop->id == TKN_LB) { statLBProperty(prop); } } void statNode(struct Node *n) { struct Property *p; p = n->prop; while(p) { statProperty(p); p = p->next; } } void statTree(struct Node *n) { statNode(n); n = n->child; while (n) { if (n->sibling) { while (n) { statTree(n); n = n->sibling; } } else { statNode(n); n = n->child; } } } void statSGF(struct SGFInfo* sgf) { Stat::newSource(); struct Node *n; struct TreeInfo *info; n = sgf->root; info = sgf->tree; while(n) { if (info->GM == 1) statTree(n); n = n->sibling; info = info->next; } } int files_processed = 0; int files_examined = 0; void statSGFFile(const string& fileName) { struct SGFInfo sgf; memset(&sgf, 0, sizeof(struct SGFInfo)); /* init SGFInfo structure */ sgf.name = const_cast(fileName.c_str()); error_count = critical_count = warning_count = ignored_count = 0; LoadSGF(&sgf); ParseSGF(&sgf); files_processed += 1; if (error_count == 0 && critical_count == 0) { statSGF(&sgf); files_examined += 1; } FreeSGFInfo(&sgf); } void printStats() { cout << files_processed << " files processed" << endl; cout << " " << files_examined << " examined" << endl; cout << " " << (files_processed - files_examined) << " skipped due to errors" << endl; Stat::printSGFTree(cout); } } int main(int argc, char *argv[]) { Stat::buildSGFTree(); if (argc > 1) { for (int i = 1; i < argc; i++) { string fileName(argv[i]); statSGFFile(fileName); } } else { while (cin) { string fileName; getline(cin, fileName); if (!fileName.empty()) statSGFFile(fileName); } } printStats(); }