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>& board_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           {
00074             MoveRecord *mr = rv->getLastMove();
00075             if (mr)
00076               mr->addComment(s.substr(3));
00077           }
00078           else if (s.substr(1,2) == "**" && parse_move_comment) 
00079           {
00080             MoveRecord *mr = rv->getLastMove();
00081             if (mr)
00082               mr->info = makeInfo(*state, s.substr(3), mr->getMove());
00083           }
00084           return;
00085         case '$': /* コメント行 */
00086           if (s.find("$START_TIME:") == 0) {
00087             const std::string YYMMDD = s.substr(12,10);
00088             rec->setDate(YYMMDD);
00089             return;
00090           }
00091           rec->addInitialComment(s.substr(1));
00092           return;
00093         case 'V': /* バージョン番号 */
00094           rec->setVersion(s.substr(1));
00095           return;
00096         case 'N': /* 対局者名 */
00097           switch(s.at(1)){
00098           case '+':
00099           case '-':
00100             rec->setPlayer(csa::charToPlayer(s.at(1)),s.substr(2));
00101             break;
00102           default:
00103             std::cerr << "Illegal csa line " << s << std::endl;
00104             throw CsaIOError("illegal csa line "+s);
00105           }
00106           break;
00107         case 'P': /* 開始盤面 */
00108           switch(s.at(1)){
00109           case 'I': /* 平手初期配置 */
00110             board_parsed.fill(true);
00111             state->init(HIRATE);
00112             break;
00113           case '+': /* 先手の駒 */
00114           case '-':{ /* 後手の駒 */
00115             Player pl=csa::charToPlayer(s.at(1));
00116             for(int i=2;i<=(int)s.length()-4;i+=4){
00117               Square pos=csa::strToPos(s.substr(i,2));
00118               if(s.substr(i+2,2) == "AL"){
00119                 state->setPieceAll(pl);
00120               }
00121               else{
00122                 Ptype ptype=csa::strToPtype(s.substr(i+2,2));
00123                 state->setPiece(pl,pos,ptype);
00124               }
00125             }
00126             break;
00127           }
00128           default:
00129             if(isdigit(s.at(1))){
00130               const int y=s.at(1)-'0';
00131               board_parsed[y-1] = true;
00132               for(unsigned int x=9,i=2;i<s.length();i+=3,x--){
00133                 if (s.at(i) != '+' && s.at(i) != '-' && s.find(" *",i)!=i) {
00134                   if (OslConfig::inUnitTest())
00135                     throw CsaIOError("parse board error " + s);
00136                   else
00137                     std::cerr << "possible typo for empty square " << s << "\n";
00138                 }   
00139                 if (s.at(i) != '+' && s.at(i) != '-') continue;
00140                 Player pl=csa::charToPlayer(s.at(i));
00141                 Square pos(x,y);
00142                 Ptype ptype=csa::strToPtype(s.substr(i+1,2));
00143                 state->setPiece(pl,pos,ptype);
00144               }
00145             }
00146           }
00147           break;
00148         case '+':
00149         case '-':{
00150           Player pl=csa::charToPlayer(s.at(0));
00151           if(s.length()==1){
00152             state->setTurn(pl);
00153             rec->setInitialState(*state);
00154             state->initPawnMask();
00155           }
00156           else{ // actual moves
00157             const Move m = csa::strToMove(s,*state);
00158             if (! state->isValidMove(m))
00159             {
00160               std::cerr << "Illegal move " << m << std::endl;
00161               throw CsaIOError("illegal move "+s);
00162             }
00163             rv->addMoveAndAdvance(m);
00164             return;
00165           }
00166           break;
00167         }
00168         case 'T':
00169         {
00170           MoveRecord *mr = rv->getLastMove();
00171           if (mr)
00172             mr->setTime(atoi(s.c_str()+1));
00173           return;
00174         }
00175         case '%':
00176           if (s.find("%TORYO") == 0 || s.find("%ILLEGAL_MOVE") == 0)
00177             rec->setResult((state->turn() == BLACK) 
00178                            ? Record::WHITE_WIN : Record::BLACK_WIN);
00179           else if (s.find("%SENNICHITE") == 0)
00180             rec->setResult(Record::SENNNICHITE);
00181           else if (s.find("%KACHI") == 0)
00182             rec->setResult((state->turn() == BLACK) 
00183                            ? Record::BLACK_WIN : Record::WHITE_WIN);
00184           else if (s.find("%JISHOGI") == 0 || s.find("%HIKIWAKE") == 0)
00185             rec->setResult(Record::JISHOGI);
00186           else if (s.find("%+ILLEGAL_ACTION") == 0)
00187             rec->setResult(Record::WHITE_WIN);
00188           else if (s.find("%-ILLEGAL_ACTION") == 0)
00189             rec->setResult(Record::BLACK_WIN);
00190           return;
00191         default:
00192           throw CsaIOError("unknown character in csaParseLine "+s);
00193         }
00194       }
00195     } // anonymous namespace
00196   } // namespace record
00197 } // namespace osl
00198 
00199 osl::record::csa::
00200 InputStream::InputStream(std::istream& is) 
00201   : is(is), 
00202     rv(boost::shared_ptr<record::RecordVisitor>(new record::RecordVisitor()))
00203 {
00204   if (! is)
00205   {
00206     std::cerr << "InputStream::InputStream cannot read \n";
00207     abort();
00208   }
00209 }
00210   
00211 osl::record::csa::
00212 InputStream::InputStream(std::istream& is, boost::shared_ptr<record::RecordVisitor> rv) 
00213   : is(is), rv(rv)
00214 {
00215   if (! is)
00216   {
00217     std::cerr << "InputStream::InputStream cannot read \n";
00218     abort();
00219   }
00220 }
00221   
00222 osl::record::csa::
00223 InputStream::~InputStream(){}
00224   
00225 void osl::record::csa::
00226 InputStream::load(Record* rec)
00227 {
00228   //  rec->init();
00229   state.init();
00230   rv->setState(&state);
00231   rv->setRecord(rec);
00232   std::string line;
00233   CArray<bool, 9> board_parsed = {{ false }};
00234   while (std::getline(is, line)) 
00235   {
00236     // quick hack for \r
00237     if ((! line.empty())
00238         && (line[line.size()-1] == 13))
00239       line.erase(line.size()-1);
00240 
00241     std::vector<std::string> elements;
00242     boost::algorithm::split(elements, line, boost::algorithm::is_any_of(","));
00243     BOOST_FOREACH(std::string e, elements) {
00244       boost::algorithm::trim(e);
00245       boost::algorithm::trim_left(e);
00246       csaParseLine(rv, e, board_parsed, !OslConfig::inUnitTest());
00247     }
00248   }
00249   if (*std::min_element(board_parsed.begin(), board_parsed.end()) == false)
00250     throw CsaIOError("incomplete position description in csaParseLine");
00251   assert(state.isConsistent());
00252 }
00253 
00254 osl::record::csa::
00255 CsaFile::CsaFile(const std::string& fileName)
00256 {
00257   std::ifstream ifs(fileName.c_str());
00258   if (! ifs)
00259   {
00260     const std::string msg = "CsaFile::CsaFile file cannot read ";
00261     std::cerr << msg << fileName << "\n";
00262     throw CsaIOError(msg + fileName);
00263   }
00264   InputStream irs(ifs);
00265   irs.load(&rec);
00266 }
00267 
00268 osl::record::csa::
00269 CsaFile::~CsaFile()
00270 {
00271 }
00272 
00273 const osl::record::Record& osl::record::csa::
00274 CsaFile::getRecord() const
00275 {
00276   return rec;
00277 }
00278 
00279 const osl::NumEffectState osl::record::csa::
00280 CsaFile::getInitialState() const
00281 {
00282   return NumEffectState(rec.getInitialState());
00283 }
00284 
00285 /* ------------------------------------------------------------------------- */
00286 // ;;; Local Variables:
00287 // ;;; mode:c++
00288 // ;;; c-basic-offset:2
00289 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines