All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
csaRecord.cc
Go to the documentation of this file.
1 #include "osl/record/csaRecord.h"
4 #include "osl/oslConfig.h"
5 #include <boost/algorithm/string/classification.hpp>
6 #include <boost/algorithm/string/split.hpp>
7 #include <boost/algorithm/string/trim.hpp>
8 #include <boost/foreach.hpp>
9 #include <iostream>
10 #include <fstream>
11 #include <stdexcept>
12 #include <cassert>
13 #include <string>
14 #include <sstream>
15 
16 /* ------------------------------------------------------------------------- */
17 
18 namespace osl
19 {
20  namespace record
21  {
22  namespace
23  {
24  const SearchInfo makeInfo(const SimpleState& initial,
25  const std::string& line,
26  Move last_move)
27  {
28  std::istringstream is(line);
29  SearchInfo info;
30  is >> info.value;
31 
32  NumEffectState state(initial);
33  std::string s;
34  while (is >> s)
35  {
36  if (s == csa::show(last_move)) // effective only if s is the first move in a comment
37  continue;
38  last_move = Move::INVALID();
39  try
40  {
41  const Move move = ((s == "%PASS" || /* gekisashi */ s == "<PASS>")
42  ? Move::PASS(state.turn())
43  : csa::strToMove(s, state));
44  if (move.isPass()
45  || (move.isNormal() && state.isValidMove(move,false)))
46  {
47  state.makeMove(move);
48  if (! state.inCheck(alt(state.turn()))) {
49  info.moves.push_back(move);
50  continue;
51  }
52  // fall through
53  }
54  }
55  catch(CsaIOError& e)
56  {
57  // fall through
58  }
59  std::cerr << "drop illegal move in comment " << s << std::endl;
60  break;
61  }
62  return info;
63  }
64  void csaParseLine(boost::shared_ptr<RecordVisitor>& rv, std::string s,
65  CArray<bool,9>& board_parsed,
66  bool parse_move_comment=true)
67  {
68  Record *rec=rv->getRecord();
69  SimpleState* state=rv->getState();
70  while (! s.empty() && isspace(s[s.size()-1])) // ignore trailing garbage
71  s.resize(s.size()-1);
72  if (s.length()==0)
73  return;
74  switch(s.at(0)){
75  case '\'': /* コメント行 */
76  if (s.substr(1,2) == "* ")
77  {
78  MoveRecord *mr = rv->getLastMove();
79  if (mr)
80  mr->addComment(s.substr(3));
81  }
82  else if (s.substr(1,2) == "**" && parse_move_comment)
83  {
84  MoveRecord *mr = rv->getLastMove();
85  if (mr)
86  mr->info = makeInfo(*state, s.substr(3), mr->getMove());
87  }
88  return;
89  case '$': /* コメント行 */
90  if (s.find("$START_TIME:") == 0) {
91  const std::string YYMMDD = s.substr(12,10);
92  rec->setDate(YYMMDD);
93  return;
94  }
95  rec->addInitialComment(s.substr(1));
96  return;
97  case 'V': /* バージョン番号 */
98  rec->setVersion(s.substr(1));
99  return;
100  case 'N': /* 対局者名 */
101  switch(s.at(1)){
102  case '+':
103  case '-':
104  rec->setPlayer(csa::charToPlayer(s.at(1)),s.substr(2));
105  break;
106  default:
107  std::cerr << "Illegal csa line " << s << std::endl;
108  throw CsaIOError("illegal csa line "+s);
109  }
110  break;
111  case 'P': /* 開始盤面 */
112  switch(s.at(1)){
113  case 'I': /* 平手初期配置 */
114  board_parsed.fill(true);
115  state->init(HIRATE);
116  break;
117  case '+': /* 先手の駒 */
118  case '-':{ /* 後手の駒 */
119  Player pl=csa::charToPlayer(s.at(1));
120  for(int i=2;i<=(int)s.length()-4;i+=4){
121  Square pos=csa::strToPos(s.substr(i,2));
122  if(s.substr(i+2,2) == "AL"){
123  state->setPieceAll(pl);
124  }
125  else{
126  Ptype ptype=csa::strToPtype(s.substr(i+2,2));
127  state->setPiece(pl,pos,ptype);
128  }
129  }
130  break;
131  }
132  default:
133  if(isdigit(s.at(1))){
134  const int y=s.at(1)-'0';
135  board_parsed[y-1] = true;
136  for(unsigned int x=9,i=2;i<s.length();i+=3,x--){
137  if (s.at(i) != '+' && s.at(i) != '-' && s.find(" *",i)!=i) {
138  if (OslConfig::inUnitTest())
139  throw CsaIOError("parse board error " + s);
140  else
141  std::cerr << "possible typo for empty square " << s << "\n";
142  }
143  if (s.at(i) != '+' && s.at(i) != '-') continue;
144  Player pl=csa::charToPlayer(s.at(i));
145  Square pos(x,y);
146  Ptype ptype=csa::strToPtype(s.substr(i+1,2));
147  state->setPiece(pl,pos,ptype);
148  }
149  }
150  }
151  break;
152  case '+':
153  case '-':{
154  Player pl=csa::charToPlayer(s.at(0));
155  if(s.length()==1){
156  state->setTurn(pl);
157  rec->setInitialState(*state);
158  state->initPawnMask();
159  }
160  else{ // actual moves
161  const Move m = csa::strToMove(s,*state);
162  if (! state->isValidMove(m))
163  {
164  std::cerr << "Illegal move " << m << std::endl;
165  throw CsaIOError("illegal move "+s);
166  }
167  rv->addMoveAndAdvance(m);
168  return;
169  }
170  break;
171  }
172  case 'T':
173  {
174  MoveRecord *mr = rv->getLastMove();
175  if (mr)
176  mr->setTime(atoi(s.c_str()+1));
177  return;
178  }
179  case '%':
180  if (s.find("%TORYO") == 0 || s.find("%ILLEGAL_MOVE") == 0)
181  rec->setResult((state->turn() == BLACK)
183  else if (s.find("%SENNICHITE") == 0)
184  rec->setResult(Record::SENNNICHITE);
185  else if (s.find("%KACHI") == 0)
186  rec->setResult((state->turn() == BLACK)
187  ? Record::BLACK_WIN : Record::WHITE_WIN);
188  else if (s.find("%JISHOGI") == 0 || s.find("%HIKIWAKE") == 0)
189  rec->setResult(Record::JISHOGI);
190  else if (s.find("%+ILLEGAL_ACTION") == 0)
191  rec->setResult(Record::WHITE_WIN);
192  else if (s.find("%-ILLEGAL_ACTION") == 0)
193  rec->setResult(Record::BLACK_WIN);
194  return;
195  default:
196  throw CsaIOError("unknown character in csaParseLine "+s);
197  }
198  }
199  } // anonymous namespace
200  } // namespace record
201 } // namespace osl
202 
204 InputStream::InputStream(std::istream& is)
205  : is(is),
206  rv(boost::shared_ptr<record::RecordVisitor>(new record::RecordVisitor()))
207 {
208  if (! is)
209  {
210  std::cerr << "InputStream::InputStream cannot read \n";
211  abort();
212  }
213 }
214 
216 InputStream::InputStream(std::istream& is, boost::shared_ptr<record::RecordVisitor> rv)
217  : is(is), rv(rv)
218 {
219  if (! is)
220  {
221  std::cerr << "InputStream::InputStream cannot read \n";
222  abort();
223  }
224 }
225 
228 
231 {
232  // rec->init();
233  state.init();
234  rv->setState(&state);
235  rv->setRecord(rec);
236  std::string line;
237  CArray<bool, 9> board_parsed = {{ false }};
238  while (std::getline(is, line))
239  {
240  // quick hack for \r
241  if ((! line.empty())
242  && (line[line.size()-1] == 13))
243  line.erase(line.size()-1);
244 
245  std::vector<std::string> elements;
246  boost::algorithm::split(elements, line, boost::algorithm::is_any_of(","));
247  BOOST_FOREACH(std::string e, elements) {
248  boost::algorithm::trim(e);
249  boost::algorithm::trim_left(e);
250  csaParseLine(rv, e, board_parsed, !OslConfig::inUnitTest());
251  }
252  }
253  if (*std::min_element(board_parsed.begin(), board_parsed.end()) == false)
254  throw CsaIOError("incomplete position description in csaParseLine");
255  assert(state.isConsistent());
256 }
257 
259 CsaFile::CsaFile(const std::string& fileName)
260 {
261  std::ifstream ifs(fileName.c_str());
262  if (! ifs)
263  {
264  const std::string msg = "CsaFile::CsaFile file cannot read ";
265  std::cerr << msg << fileName << "\n";
266  throw CsaIOError(msg + fileName);
267  }
268  InputStream irs(ifs);
269  irs.load(&rec);
270 }
271 
274 {
275 }
276 
279 {
280  return rec;
281 }
282 
283 const osl::NumEffectState osl::record::csa::
285 {
286  return NumEffectState(rec.getInitialState());
287 }
288 
289 /* ------------------------------------------------------------------------- */
290 // ;;; Local Variables:
291 // ;;; mode:c++
292 // ;;; c-basic-offset:2
293 // ;;; End: