00001 //---------------------------------------------------------------------------- 00002 /** @file SgGameWriter.cpp 00003 */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "SgSystem.h" 00007 #include "SgGameWriter.h" 00008 00009 #include <iostream> 00010 #include "SgDebug.h" 00011 #include "SgException.h" 00012 #include "SgNode.h" 00013 00014 using namespace std; 00015 using SgPropUtil::GetPointFmt; 00016 00017 //---------------------------------------------------------------------------- 00018 00019 SgGameWriter::SgGameWriter(ostream& out) 00020 : m_out(out), 00021 m_fileFormat(4), 00022 m_numPropsOnLine(0) 00023 { 00024 } 00025 00026 void SgGameWriter::WriteGame(SgNode& root, bool allProps, int fileFormat, 00027 const string& application, int gameNumber, 00028 int defaultSize) 00029 { 00030 if (! m_out) 00031 throw SgException("SgGameWriter: write error"); 00032 // Add file format property to root. If file format not specified, 00033 // default to file format read in, or FF[4] for new files. 00034 if (fileFormat != 0) 00035 m_fileFormat = fileFormat; 00036 else if (root.HasProp(SG_PROP_FORMAT)) 00037 m_fileFormat = root.GetIntProp(SG_PROP_FORMAT); 00038 root.Add(new SgPropInt(SG_PROP_FORMAT, m_fileFormat)); 00039 if (application != "") 00040 root.Add(new SgPropText(SG_PROP_APPLIC, application)); 00041 root.Add(new SgPropInt(SG_PROP_GAME, gameNumber)); 00042 // Reorder the main root properties in a fixed order to make games more 00043 // human-readable and to avoid unnecessary diffs. Note that they'll end 00044 // up in the opposite sequence. 00045 SgPropList& props = root.Props(); 00046 props.MoveToFront(SG_PROP_PLAYER); 00047 props.MoveToFront(SG_PROP_ADD_EMPTY); 00048 props.MoveToFront(SG_PROP_ADD_WHITE); 00049 props.MoveToFront(SG_PROP_ADD_BLACK); 00050 props.MoveToFront(SG_PROP_USER); 00051 props.MoveToFront(SG_PROP_SOURCE); 00052 props.MoveToFront(SG_PROP_ROUND); 00053 props.MoveToFront(SG_PROP_EVENT); 00054 props.MoveToFront(SG_PROP_PLACE); 00055 props.MoveToFront(SG_PROP_DATE); 00056 props.MoveToFront(SG_PROP_RESULT); 00057 props.MoveToFront(SG_PROP_PLAYER_BLACK); 00058 props.MoveToFront(SG_PROP_RANK_BLACK); 00059 props.MoveToFront(SG_PROP_TEAM_BLACK); 00060 props.MoveToFront(SG_PROP_PLAYER_WHITE); 00061 props.MoveToFront(SG_PROP_RANK_WHITE); 00062 props.MoveToFront(SG_PROP_TEAM_WHITE); 00063 props.MoveToFront(SG_PROP_GAME_NAME); 00064 props.MoveToFront(SG_PROP_APPLIC); 00065 props.MoveToFront(SG_PROP_SIZE); 00066 props.MoveToFront(SG_PROP_FORMAT); 00067 props.MoveToFront(SG_PROP_GAME); 00068 00069 SgPropPointFmt fmt = GetPointFmt(gameNumber); 00070 WriteSubtree(root, allProps, defaultSize, fmt); 00071 m_out.put('\n'); 00072 } 00073 00074 void SgGameWriter::WriteSubtree(const SgNode& nodeRef, bool allProps, 00075 int boardSize, SgPropPointFmt fmt) 00076 { 00077 // Start new sequence on a new line. 00078 StartNewLine(); 00079 00080 // Opening parenthesis. 00081 m_out.put('('); 00082 00083 // Write out main sequence first. 00084 const SgNode* node = &nodeRef; 00085 do 00086 { 00087 HandleProps(node, boardSize); 00088 m_out.put(';'); 00089 WriteNode(*node, allProps, boardSize, fmt); 00090 node = node->LeftMostSon(); 00091 } while (node && ! node->HasRightBrother()); 00092 00093 // Now either reached end of branch, or fork in linear sequence. Write out 00094 // each subtree recursively. 00095 while (node) 00096 { 00097 HandleProps(node, boardSize); 00098 WriteSubtree(*node, allProps, boardSize, fmt); 00099 node = node->RightBrother(); 00100 } 00101 00102 // Closing parenthesis. 00103 m_out.put(')'); 00104 } 00105 00106 void SgGameWriter::HandleProps(const SgNode* node, int& boardSize) const 00107 { 00108 int value; 00109 bool hasSizeProp = node->GetIntProp(SG_PROP_SIZE, &value); 00110 if (hasSizeProp) 00111 { 00112 if (value >= SG_MIN_SIZE && value <= SG_MAX_SIZE) 00113 boardSize = value; 00114 else 00115 SgWarning() << "Invalid size " << value; 00116 } 00117 } 00118 00119 void SgGameWriter::WriteNode(const SgNode& node, bool allProps, int boardSize, 00120 SgPropPointFmt fmt) 00121 { 00122 for (SgPropListIterator it(node.Props()); it; ++it) 00123 { 00124 SgProp* prop = *it; 00125 vector<string> values; 00126 // Check whether property should be written, and get its value. 00127 if ((allProps || ShouldWriteProperty(*prop)) 00128 && prop->ToString(values, boardSize, fmt, m_fileFormat)) 00129 { 00130 // Start specific properties on a new line to make file easier 00131 // to read. 00132 if (prop->Flag(SG_PROPCLASS_NEWLINE)) 00133 StartNewLine(); 00134 m_out << prop->Label(); 00135 for (vector<string>::const_iterator it2 = values.begin(); 00136 it2 != values.end(); ++it2) 00137 m_out << '[' << (*it2) << ']'; 00138 // Limit number of properties per line. 00139 if (++m_numPropsOnLine >= 10) 00140 StartNewLine(); 00141 } } 00142 00143 // Start first move node after root node on a new line. 00144 if (node.HasProp(SG_PROP_GAME)) 00145 StartNewLine(); 00146 } 00147 00148 void SgGameWriter::StartNewLine() 00149 { 00150 // Start a new line unless we're already at the beginning of a line. 00151 if (m_numPropsOnLine > 0) 00152 { 00153 m_numPropsOnLine = 0; 00154 m_out.put('\n'); 00155 } 00156 } 00157 00158 bool SgGameWriter::ShouldWriteProperty(const SgProp& prop) 00159 { 00160 // Only write out clean properties with FF[3]. 00161 // FUTURE: Could make standard adherence a separate option. 00162 if (m_fileFormat == 3 && prop.Flag(SG_PROPCLASS_NOTCLEAN)) 00163 return false; 00164 00165 if (prop.Label() == "") 00166 return false; 00167 00168 // don't write out time left properties (e.g. problem collections). 00169 if (prop.ID() == SG_PROP_TIME_BLACK || prop.ID() == SG_PROP_TIME_WHITE) 00170 return false; 00171 00172 // Default: write out all properties. 00173 return true; 00174 } 00175 00176 //---------------------------------------------------------------------------- 00177