00001
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/eucToLang.h"
00008 #include "osl/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 <iostream>
00014 #include <fstream>
00015 #include <stdexcept>
00016 #include <cassert>
00017 #include <string>
00018 #include <sstream>
00019
00020 void osl::kakinoki::
00021 KakinokiFile::parseLine(SimpleState& state, Record& record,
00022 std::string s, CArray<bool,9>& board_parsed)
00023 {
00024 static const KanjiMove& Kanji_Move = KanjiMove::instance();
00025 static const CArray<std::string,11> n_str = {{
00026 "", K_K1, K_K2, K_K3, K_K4, K_K5, K_K6, K_K7, K_K8, K_K9, K_K10
00027 }};
00028
00029 if (s[0] == '|') {
00030 if (s.size() < 1+3*9+1+2)
00031 throw KakinokiIOError("board too short in kakinokiParseLine "+s);
00032 const int y = std::find(n_str.begin(), n_str.end(), s.substr(s.size()-2))
00033 - n_str.begin();
00034 if (! (1 <= y && y <= 9))
00035 throw KakinokiIOError("unknown y in kakinokiParseLine "+s);
00036 board_parsed[y-1] = true;
00037 for (unsigned int x=9,i=1;i<s.length()&&x>0;i+=3,x--) {
00038 std::pair<Player,Ptype> pp=kakinoki::strToPiece(s.substr(i,3));
00039 if (! isPiece(pp.second))
00040 continue;
00041 state.setPiece(pp.first, Square(x,y), pp.second);
00042 }
00043 }
00044 if (s.find(K_TESUU "--") == 0) {
00045
00046 if (std::find(board_parsed.begin(), board_parsed.end(), true)
00047 == board_parsed.end()) {
00048 state.init(HIRATE);
00049 board_parsed.fill(true);
00050 }
00051 if (*std::min_element(board_parsed.begin(), board_parsed.end()) == false)
00052 throw KakinokiIOError("incomplete position description in kakinokiParseLine");
00053 state.initPawnMask();
00054 record.record.initial_state = NumEffectState(state);
00055 return;
00056 }
00057 if (s.size() > 6)
00058 {
00059 if (s.find(K_BLACK K_COLON) == 0) {
00060 record.player[BLACK] = s.substr(6);
00061 return;
00062 }
00063 if (s.find(K_WHITE K_COLON) == 0) {
00064 record.player[WHITE] = s.substr(6);
00065 return;
00066 }
00067 if (s.find(K_KISEN K_COLON) == 0)
00068 {
00069 record.tournament_name = s.substr(6);
00070 return;
00071 }
00072 if (s.find(K_KAISHI K_NICHIJI K_COLON) == 0) {
00073 boost::gregorian::date date =
00074 boost::gregorian::from_string(s.substr(strlen(K_KAISHI K_NICHIJI K_COLON),10));
00075 record.start_date = date;
00076 return;
00077 }
00078 if (s.find(K_MOCHIGOMA K_COLON) != s.npos
00079 && s.find(K_NASHI) == s.npos) {
00080 std::string piece_str = s.substr(s.find(K_COLON)+2);
00081 boost::algorithm::replace_all(piece_str, K_SPACE, " ");
00082 std::vector<std::string> pieces;
00083 boost::algorithm::split(pieces, piece_str,
00084 boost::algorithm::is_any_of(" "));
00085 Player player;
00086 if (s.find(K_BLACK) == 0) player = BLACK;
00087 else if (s.find(K_WHITE) == 0) player = WHITE;
00088 else throw KakinokiIOError("error in stand "+ s);
00089
00090 for (const auto& e: pieces) {
00091 if (e.empty()) continue;
00092 if (e.size() < 2) throw KakinokiIOError("error in stand "+ e);
00093 const Ptype ptype = Kanji_Move.toPtype(e.substr(0,2));
00094 int n = 1;
00095 if (e.size() >= 4)
00096 n = std::find(n_str.begin(),n_str.end(),e.substr(2,2))
00097 - n_str.begin();
00098 if (e.size() >= 6)
00099 n = n * ((e.substr(2,2) == K_K10) ? 1 : 10)
00100 + (std::find(n_str.begin(),n_str.end(),e.substr(4,2))
00101 - n_str.begin());
00102 for (int i=0; i<n; ++i)
00103 state.setPiece(player, Square::STAND(), ptype);
00104 }
00105 }
00106 }
00107
00108
00109 if (s[0] == '*')
00110 {
00111 if (record.moves().empty())
00112 Record::addWithNewLine(record.initial_comment, s.substr(1));
00113 else
00114 record.setMoveComment(s.substr(1));
00115 return;
00116 }
00117 if (s[0] != ' ')
00118 {
00119 if (record.moves().empty())
00120 Record::addWithNewLine(record.initial_comment, s);
00121 return;
00122 }
00123 if (s.find(K_TORYO) != s.npos)
00124 {
00125 record.result = ((state.turn() == BLACK)
00126 ? Record::WhiteWin : Record::BlackWin);
00127 return;
00128 }
00129
00130 {
00131
00132 size_t p = s.find('(');
00133 if (p != s.npos)
00134 s.replace(p, 1, 1, ' ');
00135 p = s.find(')');
00136 if (p != s.npos)
00137 s.replace(p, 1, 1, ' ');
00138 }
00139 Move last_move = record.lastMove();
00140 const Move m = kakinoki::strToMove(s, state, last_move);
00141 if (m.isNormal()) {
00142 if (! state.isValidMove(m)) {
00143 std::ostringstream ss;
00144 ss << state << misc::eucToLang(s) << "\n" << m;
00145 std::cerr << ss.str();
00146 throw KakinokiIOError(ss.str());
00147 }
00148 record.record.moves.push_back(m);
00149 NumEffectState copy(state);
00150 copy.makeMove(m);
00151 state = copy;
00152 }
00153 }
00154
00155 std::pair<osl::Player,osl::Ptype> osl::
00156 kakinoki::strToPiece(const std::string& s)
00157 {
00158 static const KanjiMove& Kanji_Move = KanjiMove::instance();
00159 if (s.size() != 3 || (s[0] != 'v' && s[0] != ' '))
00160 throw KakinokiIOError("error in strToPiece " + s);
00161 const Player pl = s[0] == ' ' ? BLACK : WHITE;
00162 const Ptype ptype = Kanji_Move.toPtype(s.substr(1,2));
00163 return std::make_pair(pl, ptype);
00164 }
00165
00166 osl::Move osl::
00167 kakinoki::strToMove(const std::string& s, const SimpleState& state, Move last_move)
00168 {
00169 static const KanjiMove& Kanji_Move = KanjiMove::instance();
00170 std::istringstream is(s);
00171 int move_number, from_number;
00172 std::string move_string;
00173 is >> move_number >> move_string;
00174
00175 Square to, from;
00176 if (move_string.substr(0,2) == K_ONAZI)
00177 to = last_move.to();
00178 else
00179 to = Kanji_Move.toSquare(move_string.substr(0,4));
00180 if (to == Square())
00181 return Move();
00182
00183 Ptype ptype;
00184 size_t cur = 4;
00185 if (move_string.substr(cur,2) == K_NARU)
00186 {
00187 assert(move_string.size() >= cur+4);
00188 ptype = Kanji_Move.toPtype(move_string.substr(cur,4));
00189 cur += 4;
00190 }
00191 else
00192 {
00193 ptype = Kanji_Move.toPtype(move_string.substr(cur,2));
00194 cur += 2;
00195 }
00196 if (move_string.size() >= cur+2 && move_string.substr(cur,2)
00197 == K_UTSU)
00198 from = Square();
00199 else
00200 {
00201 if (! (is >> from_number))
00202 throw KakinokiIOError("error in move from");
00203 from = Square(from_number / 10, from_number % 10);
00204 }
00205
00206 bool is_promote = false;
00207 if (move_string.size() >= cur+2 && move_string.substr(cur,2) == K_NARU)
00208 is_promote = true;
00209
00210 if (from.isPieceStand())
00211 return Move(to, ptype, state.turn());
00212 Ptype captured = state.pieceOnBoard(to).ptype();
00213 return Move(from, to, is_promote ? promote(ptype) : ptype,
00214 captured, is_promote, state.turn());
00215 }
00216
00217 osl::kakinoki::
00218 KakinokiFile::KakinokiFile(const std::string& filename)
00219 {
00220 std::ifstream is(filename);
00221 if (! is)
00222 {
00223 const std::string msg = "KakinokiFile::KakinokiFile file cannot read ";
00224 std::cerr << msg << filename << "\n";
00225 throw KakinokiIOError(msg + filename);
00226 }
00227 SimpleState work;
00228 work.init();
00229 std::string line;
00230 CArray<bool, 9> board_parsed = {{ false }};
00231 while (std::getline(is, line))
00232 {
00233
00234 if ((! line.empty())
00235 && (line[line.size()-1] == 13))
00236 line.erase(line.size()-1);
00237 if (line.length()==0)
00238 continue;
00239
00240 line = misc::sjis2euc(line);
00241
00242 if (line.find(K_HENKA) == 0)
00243 break;
00244 if (! line.empty() && line[0] == '#'
00245 && line.find("separator") != line.npos)
00246 break;
00247 parseLine(work, record, line, board_parsed);
00248 }
00249 assert(work.isConsistent());
00250 }
00251
00252 osl::kakinoki::
00253 KakinokiFile::~KakinokiFile()
00254 {
00255 }
00256
00257 bool osl::kakinoki::
00258 KakinokiFile::isKakinokiFile(const std::string& filename)
00259 {
00260 std::ifstream is(filename.c_str());
00261 std::string line;
00262 if (! is || ! getline(is, line))
00263 return false;
00264 line = misc::sjis2euc(line);
00265 return line.find("Kifu for Windows") != line.npos
00266 || line.find("KIFU") != line.npos
00267 || line.find(K_SENKEI) == 0
00268 || line.find(K_TEAIWARI) == 0
00269 || (line.find("#") == 0 && line.find(K_KIFU) != line.npos);
00270 }
00271
00272
00273
00274
00275