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