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