kakinoki.cc Source File

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
kakinoki.cc
Go to the documentation of this file.
1 /* kakinoki.cc
2 */
3 #include "osl/record/kakinoki.h"
4 #include "osl/record/kanjiMove.h"
5 #include "osl/record/kanjiCode.h"
6 #include "osl/misc/sjis2euc.h"
7 #include "osl/misc/eucToLang.h"
8 #include "osl/state/simpleState.h"
9 #include <boost/algorithm/string/split.hpp>
10 #include <boost/algorithm/string/replace.hpp>
11 #include <boost/algorithm/string/classification.hpp>
12 #include <boost/date_time/gregorian/gregorian.hpp>
13 #include <boost/foreach.hpp>
14 #include <iostream>
15 #include <fstream>
16 #include <stdexcept>
17 #include <cassert>
18 #include <string>
19 #include <sstream>
20
21 namespace osl
22 {
23 namespace record
24 {
25 void kakinokiParseLine(boost::shared_ptr<RecordVisitor>& rv,
26 std::string s, CArray<bool,9>& board_parsed)
27 {
28 static const KanjiMove& Kanji_Move = KanjiMove::instance();
29 static const CArray<std::string,11> n_str = {{
30 "", K_K1, K_K2, K_K3, K_K4, K_K5, K_K6, K_K7, K_K8, K_K9, K_K10
31 }};
32 Record *record=rv->getRecord();
33 SimpleState* state=rv->getState();
34 // header
35 if (s[0] == '|') {
36 if (s.size() < 1+3*9+1+2)
37 throw KakinokiIOError("board too short in kakinokiParseLine "+s);
38 const int y = std::find(n_str.begin(), n_str.end(), s.substr(s.size()-2))
39 - n_str.begin();
40 if (! (1 <= y && y <= 9))
41 throw KakinokiIOError("unknown y in kakinokiParseLine "+s);
42 board_parsed[y-1] = true;
43 for (unsigned int x=9,i=1;i<s.length()&&x>0;i+=3,x--) {
44 std::pair<Player,Ptype> pp=kakinoki::strToPiece(s.substr(i,3));
45 if (! isPiece(pp.second))
46 continue;
47 state->setPiece(pp.first, Square(x,y), pp.second);
48 }
49 }
50 if (s.find(K_TESUU "--") == 0) {
51 // moves start
52 if (std::find(board_parsed.begin(), board_parsed.end(), true)
53 == board_parsed.end()) {
54 state->init(HIRATE);
55 board_parsed.fill(true);
56 }
57 if (*std::min_element(board_parsed.begin(), board_parsed.end()) == false)
58 throw KakinokiIOError("incomplete position description in kakinokiParseLine");
59 state->initPawnMask();
60 record->setInitialState(*state);
61 return;
62 }
63 if (s.size() > 6)
64 {
65 if (s.find(K_BLACK K_COLON) == 0) {
66 record->setPlayer(BLACK, s.substr(6));
67 return;
68 }
69 if (s.find(K_WHITE K_COLON) == 0) {
70 record->setPlayer(WHITE, s.substr(6));
71 return;
72 }
73 if (s.find(K_KISEN K_COLON) == 0)
74 {
75 record->setTounamentName(s.substr(6));
76 return;
77 }
78 if (s.find(K_KAISHI K_NICHIJI K_COLON) == 0) {
79 boost::gregorian::date date =
80 boost::gregorian::from_string(s.substr(strlen(K_KAISHI K_NICHIJI K_COLON)));
81 record->setDate(date);
82 return;
83 }
84 if (s.find(K_MOCHIGOMA K_COLON) != s.npos
85 && s.find(K_NASHI) == s.npos) {
86 std::string piece_str = s.substr(s.find(K_COLON)+2);
87 boost::algorithm::replace_all(piece_str, K_SPACE, " ");
88 std::vector<std::string> pieces;
89 boost::algorithm::split(pieces, piece_str,
90 boost::algorithm::is_any_of(" "));
91 Player player;
92 if (s.find(K_BLACK) == 0) player = BLACK;
93 else if (s.find(K_WHITE) == 0) player = WHITE;
94 else throw KakinokiIOError("error in stand "+ s);
95
96 BOOST_FOREACH(const std::string& e, pieces) {
97 if (e.empty()) continue;
98 if (e.size() < 2) throw KakinokiIOError("error in stand "+ e);
99 const Ptype ptype = Kanji_Move.toPtype(e.substr(0,2));
100 int n = 1;
101 if (e.size() >= 4)
102 n = std::find(n_str.begin(),n_str.end(),e.substr(2,2))
103 - n_str.begin();
104 if (e.size() >= 6)
105 n = n * ((e.substr(2,2) == K_K10) ? 1 : 10)
106 + (std::find(n_str.begin(),n_str.end(),e.substr(4,2))
107 - n_str.begin());
108 for (int i=0; i<n; ++i)
109 state->setPiece(player, Square::STAND(), ptype);
110 }
111 }
112 }
113
114 // moves
115 if (s[0] == '*')
116 {
117 MoveRecord *mr = rv->getLastMove();
118 if (mr)
119 mr->addComment(s.substr(1));
120 return;
121 }
122 if (s[0] != ' ')
123 {
124 if (rv->getLastMove() == 0)
125 record->addInitialComment(s);
126 return; // otherwise ignore
127 }
128 if (s.find(K_TORYO) != s.npos)
129 {
130 record->setResult((state->turn() == BLACK)
131 ? Record::WHITE_WIN : Record::BLACK_WIN);
132 return;
133 }
134
135 {
136 // replace '(' and ')' with white space if exists
137 size_t p = s.find('(');
138 if (p != s.npos)
139 s.replace(p, 1, 1, ' ');
140 p = s.find(')');
141 if (p != s.npos)
142 s.replace(p, 1, 1, ' ');
143 }
144 Move last_move;
145 if (rv->getLastMove())
146 last_move = rv->getLastMove()->getMove();
147 const Move m = kakinoki::strToMove(s, *state, last_move);
148 if (m.isNormal()) {
149 if (! state->isValidMove(m)) {
150 std::ostringstream ss;
151 ss << *state << misc::eucToLang(s) << "\n" << m;
152 std::cerr << ss.str();
153 throw KakinokiIOError(ss.str());
154 }
155 rv->addMoveAndAdvance(m);
156 }
157 }
158 }
159 }
160
161 std::pair<osl::Player,osl::Ptype> osl::record::
162 kakinoki::strToPiece(const std::string& s)
163 {
164 static const KanjiMove& Kanji_Move = KanjiMove::instance();
165 if (s.size() != 3 || (s[0] != 'v' && s[0] != ' '))
166 throw KakinokiIOError("error in strToPiece " + s);
167 const Player pl = s[0] == ' ' ? BLACK : WHITE;
168 const Ptype ptype = Kanji_Move.toPtype(s.substr(1,2));
169 return std::make_pair(pl, ptype);
170 }
171
172 osl::Move osl::record::
173 kakinoki::strToMove(const std::string& s, const SimpleState& state, Move last_move)
174 {
175 static const KanjiMove& Kanji_Move = KanjiMove::instance();
176 std::istringstream is(s);
177 int move_number, from_number;
178 std::string move_string;
179 is >> move_number >> move_string;
180
181 Square to, from;
182 if (move_string.substr(0,2) == K_ONAZI)
183 to = last_move.to();
184 else
185 to = Kanji_Move.toSquare(move_string.substr(0,4));
186 if (to == Square()) // resign?
187 return Move();
188
189 Ptype ptype;
190 size_t cur = 4;
191 if (move_string.substr(cur,2) == K_NARU) // PLANCE, PKIGHT, PSILVER
192 {
193 assert(move_string.size() >= cur+4);
194 ptype = Kanji_Move.toPtype(move_string.substr(cur,4));
195 cur += 4;
196 }
197 else
198 {
199 ptype = Kanji_Move.toPtype(move_string.substr(cur,2));
200 cur += 2;
201 }
202 if (move_string.size() >= cur+2 && move_string.substr(cur,2)
203 == K_UTSU)
204 from = Square();
205 else
206 {
207 if (! (is >> from_number))
208 throw KakinokiIOError("error in move from");
209 from = Square(from_number / 10, from_number % 10);
210 }
211
212 bool is_promote = false;
213 if (move_string.size() >= cur+2 && move_string.substr(cur,2) == K_NARU)
214 is_promote = true;
215
216 if (from.isPieceStand())
217 return Move(to, ptype, state.turn());
218 Ptype captured = state.pieceOnBoard(to).ptype();
219 return Move(from, to, is_promote ? promote(ptype) : ptype,
220 captured, is_promote, state.turn());
221 }
222
223 osl::record::kakinoki::
224 InputStream::InputStream(std::istream& is)
225 : is(is),
226 rv(boost::shared_ptr<record::RecordVisitor>(new record::RecordVisitor()))
227 {
228 if (! is)
229 {
230 std::cerr << "InputStream::InputStream cannot read \n";
231 abort();
232 }
233 }
234
235 osl::record::kakinoki::
236 InputStream::InputStream(std::istream& is, boost::shared_ptr<record::RecordVisitor> rv)
237 : is(is), rv(rv)
238 {
239 if (! is)
240 {
241 std::cerr << "InputStream::InputStream cannot read \n";
242 throw KakinokiIOError("file open failed");
243 }
244 }
245
246 osl::record::kakinoki::
247 InputStream::~InputStream(){}
248
249 void osl::record::kakinoki::
250 InputStream::load(Record* rec)
251 {
252 // rec->init();
253 state.init();
254 rv->setState(&state);
255 rv->setRecord(rec);
256 std::string line;
257 CArray<bool, 9> board_parsed = {{ false }};
258 while (std::getline(is, line))
259 {
260 // quick hack for \r
261 if ((! line.empty())
262 && (line[line.size()-1] == 13))
263 line.erase(line.size()-1);
264 if (line.length()==0)
265 continue;
266 // to euc
267 line = misc::sjis2euc(line);
268 // skip variations
269 if (line.find(K_HENKA) == 0)
270 break;
271 if (! line.empty() && line[0] == '#'
272 && line.find("separator") != line.npos)
273 break; // tanase shogi
274
275 kakinokiParseLine(rv, line, board_parsed);
276 }
277 assert(state.isConsistent());
278 }
279
280 osl::record::kakinoki::
281 KakinokiFile::KakinokiFile(const std::string& filename)
282 {
283 std::ifstream ifs(filename.c_str());
284 if (! ifs)
285 {
286 const std::string msg = "KakinokiFile::KakinokiFile file cannot read ";
287 std::cerr << msg << filename << "\n";
288 throw KakinokiIOError(msg + filename);
289 }
290 InputStream irs(ifs);
291 irs.load(&rec);
292 }
293
294 osl::record::kakinoki::
295 KakinokiFile::~KakinokiFile()
296 {
297 }
298
299 const osl::record::Record& osl::record::kakinoki::
300 KakinokiFile::getRecord() const
301 {
302 return rec;
303 }
304
305 const osl::NumEffectState osl::record::kakinoki::
306 KakinokiFile::getInitialState() const
307 {
308 return NumEffectState(rec.getInitialState());
309 }
310
311 bool osl::record::kakinoki::
312 KakinokiFile::isKakinokiFile(const std::string& filename)
313 {
314 std::ifstream is(filename.c_str());
315 std::string line;
316 if (! is || ! getline(is, line))
317 return false;
318 line = misc::sjis2euc(line);
319 return line.find("Kifu for Windows") != line.npos
320 || line.find("KIFU") != line.npos
321 || line.find(K_SENKEI) == 0
322 || (line.find("#") == 0 && line.find(K_KIFU) != line.npos);
323 }
324
325 // ;;; Local Variables:
326 // ;;; mode:c++
327 // ;;; c-basic-offset:2
328 // ;;; End: