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"
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)
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 
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 
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 
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 
248 
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 
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 
296 {
297 }
298 
301 {
302  return rec;
303 }
304 
305 const osl::NumEffectState osl::record::kakinoki::
307 {
308  return NumEffectState(rec.getInitialState());
309 }
310 
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: