kakinoki.cc
Go to the documentation of this file.
00001 /* kakinoki.cc
00002  */
00003 #include "osl/record/kakinoki.h"
00004 #include "osl/record/kanjiMove.h"
00005 #include "osl/record/kanjiCode.h"
00006 #include "osl/misc/sjis2euc.h"
00007 #include "osl/misc/iconvConvert.h"
00008 #include "osl/state/simpleState.h"
00009 #include <boost/algorithm/string/split.hpp>
00010 #include <boost/algorithm/string/replace.hpp>
00011 #include <boost/algorithm/string/classification.hpp>
00012 #include <boost/date_time/gregorian/gregorian.hpp>
00013 #include <boost/foreach.hpp>
00014 #include <iostream>
00015 #include <fstream>
00016 #include <stdexcept>
00017 #include <cassert>
00018 #include <string>
00019 #include <sstream>
00020 
00021 namespace osl
00022 {
00023   namespace record
00024   {
00025     void kakinokiParseLine(boost::shared_ptr<RecordVisitor>& rv,
00026                            std::string s, CArray<bool,9>& board_parsed)
00027     {
00028       static const KanjiMove& Kanji_Move = KanjiMove::instance();
00029       static const CArray<std::string,11> n_str = {{
00030           "", K_K1, K_K2, K_K3, K_K4, K_K5, K_K6, K_K7, K_K8, K_K9, K_K10
00031         }};
00032       Record *record=rv->getRecord();
00033       SimpleState* state=rv->getState();
00034       // header
00035       if (s[0] == '|') {
00036         if (s.size() < 1+3*9+1+2)
00037           throw KakinokiIOError("board too short in kakinokiParseLine "+s);
00038         const int y = std::find(n_str.begin(), n_str.end(), s.substr(s.size()-2))
00039           - n_str.begin();
00040         if (! (1 <= y && y <= 9))
00041           throw KakinokiIOError("unknown y in kakinokiParseLine "+s);
00042         board_parsed[y-1] = true;
00043         for (unsigned int x=9,i=1;i<s.length()&&x>0;i+=3,x--) {
00044           std::pair<Player,Ptype> pp=kakinoki::strToPiece(s.substr(i,3));
00045           if (! isPiece(pp.second))
00046             continue;
00047           state->setPiece(pp.first, Square(x,y), pp.second);
00048         }
00049       }
00050       if (s.find(K_TESUU "--") == 0) {
00051         // moves start
00052         if (std::find(board_parsed.begin(), board_parsed.end(), true)
00053             == board_parsed.end()) {
00054           state->init(HIRATE);
00055           board_parsed.fill(true);
00056         }
00057         if (*std::min_element(board_parsed.begin(), board_parsed.end()) == false)
00058           throw KakinokiIOError("incomplete position description in kakinokiParseLine");
00059         state->initPawnMask();
00060         record->setInitialState(*state);
00061         return;
00062       }
00063       if (s.size() > 6)
00064       {
00065         if (s.find(K_BLACK K_COLON) == 0) {
00066           record->setPlayer(BLACK, s.substr(6));
00067           return;
00068         }
00069         if (s.find(K_WHITE K_COLON) == 0) {
00070           record->setPlayer(WHITE, s.substr(6));
00071           return;
00072         }
00073         if (s.find(K_KISEN K_COLON) == 0)
00074         {
00075           record->setTounamentName(s.substr(6));
00076           return;
00077         }
00078         if (s.find(K_KAISHI K_NICHIJI K_COLON) == 0) {
00079           boost::gregorian::date date =
00080             boost::gregorian::from_string(s.substr(strlen(K_KAISHI K_NICHIJI K_COLON)));
00081           record->setDate(date);
00082           return;
00083         }
00084         if (s.find(K_MOCHIGOMA K_COLON) != s.npos
00085             && s.find(K_NASHI) == s.npos) {
00086           std::string piece_str = s.substr(s.find(K_COLON)+2);
00087           boost::algorithm::replace_all(piece_str, K_SPACE, " ");
00088           std::vector<std::string> pieces;
00089           boost::algorithm::split(pieces, piece_str,
00090                                   boost::algorithm::is_any_of(" "));
00091           Player player;
00092           if (s.find(K_BLACK) == 0) player = BLACK;
00093           else if (s.find(K_WHITE) == 0) player = WHITE;
00094           else throw KakinokiIOError("error in stand "+ s);
00095 
00096           BOOST_FOREACH(const std::string& e, pieces) {
00097             if (e.empty()) continue;
00098             if (e.size() < 2) throw KakinokiIOError("error in stand "+ e);
00099             const Ptype ptype = Kanji_Move.toPtype(e.substr(0,2));
00100             int n = 1;
00101             if (e.size() >= 4)
00102               n = std::find(n_str.begin(),n_str.end(),e.substr(2,2))
00103                 - n_str.begin();
00104             if (e.size() >= 6)
00105               n = n * ((e.substr(2,2) == K_K10) ? 1 : 10)
00106                 + (std::find(n_str.begin(),n_str.end(),e.substr(4,2))
00107                    - n_str.begin());
00108             for (int i=0; i<n; ++i)
00109               state->setPiece(player, Square::STAND(), ptype);
00110           }
00111         }
00112       }
00113 
00114       // moves
00115       if (s[0] == '*') 
00116       {
00117         MoveRecord *mr = rv->getLastMove();
00118         if (mr)
00119           mr->addComment(s.substr(1));
00120         return;
00121       }
00122       if (s[0] != ' ') 
00123       {
00124         if (rv->getLastMove() == 0)
00125           record->addInitialComment(s);
00126         return;                 // otherwise ignore
00127       }
00128       if (s.find(K_TORYO) != s.npos)
00129       {
00130         record->setResult((state->turn() == BLACK)
00131                           ? Record::WHITE_WIN : Record::BLACK_WIN);
00132         return;
00133       }
00134 
00135       {
00136         // replace '(' and ')' with white space if exists
00137         size_t p = s.find('(');
00138         if (p != s.npos)
00139           s.replace(p, 1, 1, ' ');
00140         p = s.find(')');
00141         if (p != s.npos)
00142           s.replace(p, 1, 1, ' ');
00143       }      
00144       Move last_move;
00145       if (rv->getLastMove())
00146         last_move = rv->getLastMove()->getMove();
00147       const Move m = kakinoki::strToMove(s, *state, last_move);
00148       if (m.isNormal()) {
00149         if (! state->isValidMove(m)) {
00150           std::ostringstream ss;
00151           ss << *state << IconvConvert::eucToLang(s) << "\n" << m;
00152           std::cerr << ss.str();
00153           throw KakinokiIOError(ss.str());
00154         }       
00155         rv->addMoveAndAdvance(m);
00156       }
00157     }
00158   }
00159 }
00160 
00161 std::pair<osl::Player,osl::Ptype> osl::record::
00162 kakinoki::strToPiece(const std::string& s)
00163 {
00164   static const KanjiMove& Kanji_Move = KanjiMove::instance();
00165   if (s.size() != 3 || (s[0] != 'v' && s[0] != ' '))
00166     throw KakinokiIOError("error in strToPiece " + s);
00167   const Player pl = s[0] == ' ' ? BLACK : WHITE;
00168   const Ptype ptype = Kanji_Move.toPtype(s.substr(1,2));
00169   return std::make_pair(pl, ptype);
00170 }
00171 
00172 osl::Move osl::record::
00173 kakinoki::strToMove(const std::string& s, const SimpleState& state, Move last_move)
00174 {
00175   static const KanjiMove& Kanji_Move = KanjiMove::instance();
00176   std::istringstream is(s);
00177   int move_number, from_number;
00178   std::string move_string;
00179   is >> move_number >> move_string;
00180 
00181   Square to, from;
00182   if (move_string.substr(0,2) == K_ONAZI)
00183     to = last_move.to();
00184   else
00185     to = Kanji_Move.toSquare(move_string.substr(0,4));
00186   if (to == Square())           // resign?
00187     return Move();
00188   
00189   Ptype ptype;
00190   size_t cur = 4;
00191   if (move_string.substr(cur,2) == K_NARU) // PLANCE, PKIGHT, PSILVER
00192   {
00193     assert(move_string.size() >= cur+4);
00194     ptype = Kanji_Move.toPtype(move_string.substr(cur,4));
00195     cur += 4;
00196   }
00197   else
00198   {
00199     ptype = Kanji_Move.toPtype(move_string.substr(cur,2));
00200     cur += 2;
00201   }
00202   if (move_string.size() >= cur+2 && move_string.substr(cur,2)
00203       == K_UTSU)
00204     from = Square();
00205   else 
00206   {
00207     if (! (is >> from_number))
00208       throw KakinokiIOError("error in move from");
00209     from = Square(from_number / 10, from_number % 10);
00210   }
00211   
00212   bool is_promote = false;
00213   if (move_string.size() >= cur+2 && move_string.substr(cur,2) == K_NARU)
00214     is_promote = true;
00215 
00216   if (from.isPieceStand())
00217     return Move(to, ptype, state.turn());
00218   Ptype captured = state.pieceOnBoard(to).ptype();
00219   return Move(from, to, is_promote ? promote(ptype) : ptype,
00220               captured, is_promote, state.turn());
00221 }
00222 
00223 osl::record::kakinoki::
00224 InputStream::InputStream(std::istream& is) 
00225   : is(is), 
00226     rv(boost::shared_ptr<record::RecordVisitor>(new record::RecordVisitor()))
00227 {
00228   if (! is)
00229   {
00230     std::cerr << "InputStream::InputStream cannot read \n";
00231     abort();
00232   }
00233 }
00234   
00235 osl::record::kakinoki::
00236 InputStream::InputStream(std::istream& is, boost::shared_ptr<record::RecordVisitor> rv) 
00237   : is(is), rv(rv)
00238 {
00239   if (! is)
00240   {
00241     std::cerr << "InputStream::InputStream cannot read \n";
00242     throw KakinokiIOError("file open failed");
00243   }
00244 }
00245   
00246 osl::record::kakinoki::
00247 InputStream::~InputStream(){}
00248   
00249 void osl::record::kakinoki::
00250 InputStream::load(Record* rec)
00251 {
00252   //  rec->init();
00253   state.init();
00254   rv->setState(&state);
00255   rv->setRecord(rec);
00256   std::string line;
00257   CArray<bool, 9> board_parsed = {{ false }};
00258   while (std::getline(is, line)) 
00259   {
00260     // quick hack for \r
00261     if ((! line.empty())
00262         && (line[line.size()-1] == 13))
00263       line.erase(line.size()-1);
00264     if (line.length()==0) 
00265       continue;
00266     // to euc
00267     line = misc::sjis2euc(line);
00268     // skip variations
00269     if (line.find(K_HENKA) == 0)
00270       break;
00271     if (! line.empty() && line[0] == '#' 
00272         && line.find("separator") != line.npos)
00273       break;                    // tanase shogi
00274     
00275     kakinokiParseLine(rv, line, board_parsed);
00276   }
00277   assert(state.isConsistent());
00278 }
00279 
00280 osl::record::kakinoki::
00281 KakinokiFile::KakinokiFile(const std::string& filename)
00282 {
00283   std::ifstream ifs(filename.c_str());
00284   if (! ifs)
00285   {
00286     const std::string msg = "KakinokiFile::KakinokiFile file cannot read ";
00287     std::cerr << msg << filename << "\n";
00288     throw KakinokiIOError(msg + filename);
00289   }
00290   InputStream irs(ifs);
00291   irs.load(&rec);
00292 }
00293 
00294 osl::record::kakinoki::
00295 KakinokiFile::~KakinokiFile()
00296 {
00297 }
00298 
00299 const osl::record::Record& osl::record::kakinoki::
00300 KakinokiFile::getRecord() const
00301 {
00302   return rec;
00303 }
00304 
00305 const osl::NumEffectState osl::record::kakinoki::
00306 KakinokiFile::getInitialState() const
00307 {
00308   return NumEffectState(rec.getInitialState());
00309 }
00310 
00311 bool osl::record::kakinoki::
00312 KakinokiFile::isKakinokiFile(const std::string& filename)
00313 {
00314   std::ifstream is(filename.c_str());
00315   std::string line;
00316   if (! is || ! getline(is, line)) 
00317     return false;
00318   line = misc::sjis2euc(line);
00319   return line.find("Kifu for Windows") != line.npos
00320     || line.find("KIFU") != line.npos
00321     || line.find(K_SENKEI) == 0
00322     || (line.find("#") == 0 && line.find(K_KIFU) != line.npos);
00323 }
00324 
00325 // ;;; Local Variables:
00326 // ;;; mode:c++
00327 // ;;; c-basic-offset:2
00328 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines