csaRecord.cc
Go to the documentation of this file.
00001 #include "osl/record/csaRecord.h"
00002 #include "osl/record/csaIOError.h"
00003 #include "osl/state/simpleState.h"
00004 #include "osl/oslConfig.h"
00005 #include <boost/algorithm/string/classification.hpp>
00006 #include <boost/algorithm/string/split.hpp>
00007 #include <boost/algorithm/string/trim.hpp>
00008 #include <boost/foreach.hpp>
00009 #include <iostream>
00010 #include <fstream>
00011 #include <stdexcept>
00012 #include <cassert>
00013 #include <string>
00014 #include <sstream>
00015 
00016 /* ------------------------------------------------------------------------- */
00017 
00018 namespace osl
00019 {
00020   namespace record
00021   {
00022     namespace
00023     {
00024       const SearchInfo makeInfo(const SimpleState& initial,
00025                                 const std::string& line,
00026                                 Move last_move)
00027       {
00028         std::istringstream is(line);
00029         SearchInfo info;
00030         is >> info.value;
00031 
00032         NumEffectState state(initial);
00033         std::string s;
00034         while (is >> s)
00035         {
00036           if (s == csa::show(last_move)) // effective only if s is the first move in a comment
00037             continue;
00038           last_move = Move::INVALID();
00039           try
00040           {
00041             const Move move = ((s == "%PASS" || /* gekisashi */ s == "<PASS>")
00042                                ? Move::PASS(state.turn())
00043                                : csa::strToMove(s, state));
00044             if (move.isPass() || (move.isNormal() && state.isValidMove(move,false)))
00045             {
00046               info.moves.push_back(move);
00047               state.makeMove(move);
00048               continue;
00049             }
00050           }
00051           catch(CsaIOError& e)
00052           {
00053             // fall through
00054           }
00055           std::cerr << "drop illegal move in comment " << s << std::endl;
00056           break;
00057         }
00058         return info;
00059       }
00060       void csaParseLine(boost::shared_ptr<RecordVisitor>& rv, std::string s,
00061                         CArray<bool,9>& line_parsed,
00062                         bool parse_move_comment=true)
00063       {
00064         Record *rec=rv->getRecord();
00065         SimpleState* state=rv->getState();
00066         while (! s.empty() && isspace(s[s.size()-1])) // ignore trailing garbage
00067           s.resize(s.size()-1);
00068         if (s.length()==0) 
00069           return;
00070         switch(s.at(0)){
00071         case '\'': /* コメント行 */
00072           if (s.substr(1,2) == "* ")
00073             rv->getLastMove()->addComment(s.substr(3));
00074           else if (s.substr(1,2) == "**" && parse_move_comment) 
00075           {
00076             MoveRecord *mr = rv->getLastMove();
00077             rv->getLastMove()->info = makeInfo(*state, s.substr(3), mr ? mr->getMove() : Move::INVALID());
00078           }
00079           return;
00080         case '$': /* コメント行 */
00081           if (s.find("$START_TIME:") == 0) {
00082             const std::string YYMMDD = s.substr(12,10);
00083             rec->setDate(YYMMDD);
00084             return;
00085           }
00086           rec->addInitialComment(s.substr(1));
00087           return;
00088         case 'V': /* バージョン番号 */
00089           rec->setVersion(s.substr(1));
00090           return;
00091         case 'N': /* 対局者名 */
00092           switch(s.at(1)){
00093           case '+':
00094           case '-':
00095             rec->setPlayer(csa::charToPlayer(s.at(1)),s.substr(2));
00096             break;
00097           default:
00098             std::cerr << "Illegal csa line " << s << std::endl;
00099             throw CsaIOError("illegal csa line "+s);
00100           }
00101           break;
00102         case 'P': /* 開始盤面 */
00103           switch(s.at(1)){
00104           case 'I': /* 平手初期配置 */
00105             line_parsed.fill(true);
00106             state->init(HIRATE);
00107             break;
00108           case '+': /* 先手の駒 */
00109           case '-':{ /* 後手の駒 */
00110             Player pl=csa::charToPlayer(s.at(1));
00111             for(int i=2;i<=(int)s.length()-4;i+=4){
00112               Square pos=csa::strToPos(s.substr(i,2));
00113               if(s.substr(i+2,2) == "AL"){
00114                 state->setPieceAll(pl);
00115               }
00116               else{
00117                 Ptype ptype=csa::strToPtype(s.substr(i+2,2));
00118                 state->setPiece(pl,pos,ptype);
00119               }
00120             }
00121             break;
00122           }
00123           default:
00124             if(isdigit(s.at(1))){
00125               const int y=s.at(1)-'0';
00126               line_parsed[y-1] = true;
00127               for(unsigned int x=9,i=2;i<s.length();i+=3,x--){
00128                 if(s.at(i)==' ') continue;
00129                 Player pl=csa::charToPlayer(s.at(i));
00130                 Square pos(x,y);
00131                 Ptype ptype=csa::strToPtype(s.substr(i+1,2));
00132                 state->setPiece(pl,pos,ptype);
00133               }
00134             }
00135           }
00136           break;
00137         case '+':
00138         case '-':{
00139           Player pl=csa::charToPlayer(s.at(0));
00140           if(s.length()==1){
00141             state->setTurn(pl);
00142             rec->setInitialState(*state);
00143             state->initPawnMask();
00144           }
00145           else{ // actual moves
00146             const Move m = csa::strToMove(s,*state);
00147             if (! state->isValidMove(m))
00148             {
00149               std::cerr << "Illegal move " << m << std::endl;
00150               throw CsaIOError("illegal move "+s);
00151             }
00152             rv->addMoveAndAdvance(m);
00153             return;
00154           }
00155           break;
00156         }
00157         case 'T':
00158           (rv->getLastMove())->setTime(atoi(s.c_str()+1));
00159           return;
00160         case '%':
00161           if (s.find("%TORYO") == 0 || s.find("%ILLEGAL_MOVE") == 0)
00162             rec->setResult((state->turn() == BLACK) 
00163                            ? Record::WHITE_WIN : Record::BLACK_WIN);
00164           else if (s.find("%SENNICHITE") == 0)
00165             rec->setResult(Record::SENNNICHITE);
00166           else if (s.find("%KACHI") == 0)
00167             rec->setResult((state->turn() == BLACK) 
00168                            ? Record::BLACK_WIN : Record::WHITE_WIN);
00169           else if (s.find("%JISHOGI") == 0 || s.find("%HIKIWAKE") == 0)
00170             rec->setResult(Record::JISHOGI);
00171           else if (s.find("%+ILLEGAL_ACTION") == 0)
00172             rec->setResult(Record::WHITE_WIN);
00173           else if (s.find("%-ILLEGAL_ACTION") == 0)
00174             rec->setResult(Record::BLACK_WIN);
00175           return;
00176         default:
00177           throw CsaIOError("unknown character in csaParseLine "+s);
00178         }
00179       }
00180     } // anonymous namespace
00181   } // namespace record
00182 } // namespace osl
00183 
00184 osl::record::csa::
00185 InputStream::InputStream(std::istream& is) 
00186   : is(is), 
00187     rv(boost::shared_ptr<record::RecordVisitor>(new record::RecordVisitor()))
00188 {
00189   if (! is)
00190   {
00191     std::cerr << "InputStream::InputStream cannot read \n";
00192     abort();
00193   }
00194 }
00195   
00196 osl::record::csa::
00197 InputStream::InputStream(std::istream& is, boost::shared_ptr<record::RecordVisitor> rv) 
00198   : is(is), rv(rv)
00199 {
00200   if (! is)
00201   {
00202     std::cerr << "InputStream::InputStream cannot read \n";
00203     abort();
00204   }
00205 }
00206   
00207 osl::record::csa::
00208 InputStream::~InputStream(){}
00209   
00210 void osl::record::csa::
00211 InputStream::load(Record* rec)
00212 {
00213   //  rec->init();
00214   state.init();
00215   rv->setState(&state);
00216   rv->setRecord(rec);
00217   std::string line;
00218   CArray<bool, 9> line_parsed = {{ false }};
00219   while (std::getline(is, line)) 
00220   {
00221     // quick hack for \r
00222     if ((! line.empty())
00223         && (line[line.size()-1] == 13))
00224       line.erase(line.size()-1);
00225 
00226     std::vector<std::string> elements;
00227     boost::algorithm::split(elements, line, boost::algorithm::is_any_of(","));
00228     BOOST_FOREACH(std::string e, elements) {
00229       boost::algorithm::trim(e);
00230       boost::algorithm::trim_left(e);
00231       csaParseLine(rv, e, line_parsed, !OslConfig::inUnitTest());
00232     }
00233   }
00234   if (*std::min_element(line_parsed.begin(), line_parsed.end()) == false)
00235     throw CsaIOError("incomplete position description in csaParseLine");
00236   assert(state.isConsistent());
00237 }
00238 
00239 osl::record::csa::
00240 CsaFile::CsaFile(const std::string& fileName)
00241 {
00242   std::ifstream ifs(fileName.c_str());
00243   if (! ifs)
00244   {
00245     const std::string msg = "CsaFile::CsaFile file cannot read ";
00246     std::cerr << msg << fileName << "\n";
00247     throw CsaIOError(msg + fileName);
00248   }
00249   InputStream irs(ifs);
00250   irs.load(&rec);
00251 }
00252 
00253 osl::record::csa::
00254 CsaFile::~CsaFile()
00255 {
00256 }
00257 
00258 const osl::record::Record& osl::record::csa::
00259 CsaFile::getRecord() const
00260 {
00261   return rec;
00262 }
00263 
00264 const osl::NumEffectState osl::record::csa::
00265 CsaFile::getInitialState() const
00266 {
00267   return NumEffectState(rec.getInitialState());
00268 }
00269 
00270 /* ------------------------------------------------------------------------- */
00271 // ;;; Local Variables:
00272 // ;;; mode:c++
00273 // ;;; c-basic-offset:2
00274 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines