00001 #include "osl/record/kisen.h"
00002 #include "osl/record/csaIOError.h"
00003 #include "osl/pieceStand.h"
00004 #include "osl/misc/iconvConvert.h"
00005 #include <boost/filesystem/convenience.hpp>
00006 #include <boost/foreach.hpp>
00007 #include <iostream>
00008
00009 namespace osl
00010 {
00011 namespace record
00012 {
00013 Square KisenUtils::convertSquare( int pos ){
00014 assert(1<=pos && pos<=0x51);
00015 int y=((pos-1)/9)+1, x=((pos-1)%9)+1;
00016 return Square(x,y);
00017 }
00018 int KisenUtils::convertSquare(Square pos)
00019 {
00020 return ((pos.y() - 1) * 9 + 1) + pos.x() - 1;
00021 }
00022
00023 Move KisenUtils::convertMove(SimpleState const& state,int c0,int c1){
00024 Move move;
00025
00026 if(1<=c1 && c1<=0x51){
00027 Square from=convertSquare(c1),to;
00028 Piece fromPiece=state.pieceOnBoard(from);
00029 if (! fromPiece.isPiece())
00030 throw CsaIOError("Square error");
00031 assert(fromPiece.isPiece());
00032 assert(fromPiece.owner()==state.turn() ||
00033 (std::cerr << c1 << "," << from << "," << fromPiece << std::endl,0)
00034 );
00035 bool isPromote=false;
00036 if(1<=c0 && c0<=0x51){
00037 to=convertSquare(c0);
00038 }
00039 else if(0x65<=c0 && c0<=0xb5){
00040 to=convertSquare(c0-0x64);
00041 isPromote=true;
00042 }
00043 else{
00044 throw CsaIOError("c0 range error");
00045 }
00046 Piece toPiece=state.pieceAt(to);
00047 if (! toPiece.isEmpty() && toPiece.owner()!=alt(state.turn()))
00048 throw CsaIOError("inconsintent move (to)");
00049 Ptype ptype=fromPiece.ptype();
00050 if(isPromote)ptype=promote(ptype);
00051 const Ptype captured = toPiece.ptype();
00052 if (captured == KING)
00053 return Move::INVALID();
00054 move=Move(from,to,ptype,captured,isPromote,state.turn());
00055 }
00056 else{
00057 assert(0x65<=c1);
00058 assert(1<=c0&&c0<=0x51);
00059 Square to=convertSquare(c0);
00060 Ptype ptype=PTYPE_EMPTY;
00061 int piece_on_stand = c1;
00062 const Ptype ptypes[]={ROOK,BISHOP,GOLD,SILVER,KNIGHT,LANCE,PAWN};
00063 for(size_t i=0;i<sizeof(ptypes)/sizeof(Ptype);i++){
00064 int count=state.countPiecesOnStand(state.turn(),ptypes[i]);
00065 if(count>0){
00066 if(piece_on_stand>0x64){
00067 piece_on_stand-=count;
00068 if(piece_on_stand<=0x64) ptype=ptypes[i];
00069 }
00070 }
00071 }
00072 assert(ptype!=PTYPE_EMPTY ||
00073 (std::cerr << state << to << " " << c1
00074 << " " << piece_on_stand << std::endl, false));
00075 move=Move(to,ptype,state.turn());
00076 }
00077 if (! state.isValidMove(move,true)) {
00078 std::cerr << "warning: bad move in kisen\n" << state << move << "\n";
00079 return Move();
00080 }
00081 assert(state.isValidMove(move,true) ||
00082 (std::cerr << state << move << std::endl, false));
00083 return move;
00084 }
00085
00086
00087 KisenFile::KisenFile(const std::string& fileName)
00088 :ifs(fileName.c_str()),initialState(HIRATE), fileName(fileName)
00089 {
00090 if (! ifs)
00091 throw CsaIOError("KisenFile not found");
00092 ifs.seekg(0,std::ios::end);
00093 assert((ifs.tellg() % 512)==0);
00094 numberOfGames=ifs.tellg()/512;
00095 }
00096
00097 const vector<Move> KisenFile::getMoves(size_t index)
00098 {
00099 assert(index<size());
00100 vector<Move> moves;
00101
00102 ifs.seekg(index*512,std::ios::beg);
00103 CArray<unsigned char, 512> cbuf;
00104 ifs.read(reinterpret_cast<char *>(&cbuf[0]),512);
00105 NumEffectState state;
00106
00107 Player turn=BLACK;
00108 for(size_t turnCount=0;
00109 (turnCount*2 < cbuf.size())
00110 && cbuf[turnCount*2]!=0 && cbuf[turnCount*2+1]!=0;
00111 turnCount++, turn=alt(turn)){
00112 if(turnCount==KisenFile::maxMoves || cbuf[ turnCount *2 ] == 0 || cbuf[ turnCount * 2 + 1 ] == 0 ){ break; }
00113 int c0=cbuf[turnCount*2], c1=cbuf[turnCount*2+1];
00114 if (moves.empty() && c0 == 0xff && c1 == 0xff)
00115 break;
00116 const Move move=KisenUtils::convertMove(state,c0,c1);
00117 if (move.isInvalid())
00118 break;
00119 moves.push_back(move);
00120 state.makeMove(move);
00121 assert(state.isConsistent( true ) );
00122 }
00123 return moves;
00124 }
00125 #ifndef MINIMAL
00126 const std::string KisenFile::ipxFileName(const std::string& filename)
00127 {
00128 namespace bf = boost::filesystem;
00129 const bf::path ipxfilename = bf::change_extension(bf::path(filename), ".ipx");
00130 #if BOOST_FILESYSTEM_VERSION >= 3
00131 return ipxfilename.string();
00132 #else
00133 return ipxfilename.file_string();
00134 #endif
00135 }
00136
00137 KisenIpxFile::KisenIpxFile(const std::string& fileName)
00138 :ifs(fileName.c_str()), file_name(fileName)
00139 {
00140 if (! ifs)
00141 throw CsaIOError("KisenIpxFile not found");
00142 ifs.seekg(0,std::ios::end);
00143 assert((ifs.tellg() % 256)==0);
00144 numberOfGames=ifs.tellg()/256;
00145 }
00146 const std::string KisenIpxFile::getPlayer(size_t index,Player pl)
00147 {
00148 assert(index<size());
00149 vector<Move> moves;
00150 ifs.seekg(index*256,std::ios::beg);
00151 CArray<unsigned char, 256> cbuf;
00152 ifs.read(reinterpret_cast<char *>(&cbuf[0]),256);
00153 int startIndex=0;
00154 if(pl==WHITE)startIndex=14;
00155 CArray<char,15> buf;
00156 buf[14]='\0';
00157 strncpy(&buf[0],reinterpret_cast<char *>(&cbuf[startIndex]),14);
00158 return std::string(&buf[0]);
00159 }
00160 unsigned int KisenIpxFile::getRating(size_t index,Player pl)
00161 {
00162 assert(index<size());
00163 vector<Move> moves;
00164 ifs.seekg(index*256,std::ios::beg);
00165 CArray<unsigned char, 256> cbuf;
00166 ifs.read(reinterpret_cast<char *>(&cbuf[0]),256);
00167 int startIndex=0324;
00168 if(pl==WHITE)startIndex=0326;
00169 return cbuf[startIndex]+256*cbuf[startIndex+1];
00170 }
00171 unsigned int KisenIpxFile::getResult(size_t index)
00172 {
00173 assert(index<size());
00174 ifs.seekg(index*256,std::ios::beg);
00175 CArray<unsigned char, 256> cbuf;
00176 ifs.read(reinterpret_cast<char *>(&cbuf[0]),256);
00177 return cbuf[64+48+6];
00178 }
00179 const std::string KisenIpxFile::getTitle(size_t index,Player pl)
00180 {
00181 assert(index<size());
00182 vector<Move> moves;
00183 ifs.seekg(index*256,std::ios::beg);
00184 CArray<unsigned char, 256> cbuf;
00185 ifs.read(reinterpret_cast<char *>(&cbuf[0]),256);
00186 int startIndex=28;
00187 if(pl==WHITE)startIndex+=8;
00188 CArray<char,9> buf;
00189 buf[8]='\0';
00190 strncpy(&buf[0],reinterpret_cast<const char*>(&cbuf[startIndex]),8);
00191 return std::string(&buf[0]);
00192 }
00193 boost::gregorian::date KisenIpxFile::getStartDate(size_t index)
00194 {
00195 assert(index<size());
00196 ifs.seekg(index*256,std::ios::beg);
00197 CArray<unsigned char, 256> cbuf;
00198 ifs.read(reinterpret_cast<char *>(&cbuf[0]),256);
00199 const int startIndex=84;
00200 const unsigned int year = cbuf[startIndex] + 256*cbuf[startIndex+1];
00201 const unsigned int month = cbuf[startIndex+2];
00202 const unsigned int day = cbuf[startIndex+3];
00203 try {
00204 const boost::gregorian::date d = boost::gregorian::date(year, month, day);
00205 return d;
00206 } catch (std::out_of_range& e) {
00207 std::cerr << e.what() << ": ["
00208 << index << "] " << year << "-" << month << "-" << day << "\n";
00209 return boost::gregorian::date(boost::gregorian::not_a_date_time);
00210 }
00211 }
00212
00213 KisenPlusFile::KisenPlusFile(const std::string& fileName)
00214 :ifs(fileName.c_str()),initialState(HIRATE)
00215 {
00216 if (! ifs)
00217 throw CsaIOError("KisenPlusFile not found");
00218 ifs.seekg(0,std::ios::end);
00219 assert((ifs.tellg() % 2048)==0);
00220 numberOfGames=ifs.tellg()/2048;
00221 }
00222
00223 const vector<Move> KisenPlusFile::getMoves(size_t index)
00224 {
00225 vector<Move> moves;
00226 vector<int> times;
00227 getMoves(index, moves, times);
00228 return moves;
00229 }
00230
00231 void KisenPlusFile::getMoves(size_t index,
00232 vector<Move>& moves, vector<int>& times)
00233 {
00234 assert(index<size());
00235
00236 ifs.seekg(index*2048,std::ios::beg);
00237 CArray<unsigned char, 2048> cbuf;
00238 ifs.read(reinterpret_cast<char *>(&cbuf[0]),2048);
00239 NumEffectState state;
00240 for (size_t i = 0;
00241 i < 2048 && cbuf[i]!=0 && cbuf[i+1]!=0;
00242 i += 8)
00243 {
00244 int c0 = cbuf[i];
00245 int c1 = cbuf[i + 1];
00246 bool is_promote = false;
00247 Move move;
00248
00249 if (c0 > 100)
00250 {
00251 is_promote = true;
00252 c0 = 256 - c0;
00253 }
00254
00255 Square to(c0 % 10, c0 / 10);
00256
00257 if (c1 < 10)
00258 {
00259
00260 move = Move(to,
00261 PieceStand::order[c1 - 1],
00262 state.turn());
00263 }
00264 else
00265 {
00266 Square from(c1 % 10, c1 / 10);
00267 Ptype type = state.pieceAt(from).ptype();
00268 if (is_promote)
00269 type = promote(type);
00270 move = Move(from, to,
00271 type, state.pieceAt(to).ptype(),
00272 is_promote, state.turn());
00273 }
00274 moves.push_back(move);
00275 times.push_back(cbuf[i + 7] * 60 + cbuf[i + 6]);
00276 state.makeMove(move);
00277 assert(state.isConsistent( true ) );
00278 }
00279 }
00280 #endif
00281 }
00282 }
00283
00284 osl::record::
00285 KisenFile::~KisenFile()
00286 {
00287 }
00288 #ifndef MINIMAL
00289 osl::record::
00290 KisenIpxFile::~KisenIpxFile()
00291 {
00292 }
00293
00294 void osl::record::
00295 OKisenStream::save(const SimpleState& src, const vector<Move> &moves)
00296 {
00297 if (!(src == SimpleState(HIRATE)))
00298 {
00299 std::cerr << "Can not save non-HIRATE record" << std::endl;
00300 return;
00301 }
00302 NumEffectState state;
00303 const int max_length = std::min(256, static_cast<int>(moves.size()));
00304 for (int i = 0; i < max_length; ++i)
00305 {
00306 const Move move = moves[i];
00307 if (!move.isDrop())
00308 {
00309 int from = KisenUtils::convertSquare(move.from());
00310 int to = KisenUtils::convertSquare(move.to());
00311 if (move.isPromotion())
00312 {
00313 to += 100;
00314 }
00315 os << static_cast<char>(to) << static_cast<char>(from);
00316 }
00317 else
00318 {
00319 int to = KisenUtils::convertSquare(move.to());
00320 int count = 1;
00321 BOOST_FOREACH(Ptype ptype, PieceStand::order)
00322 {
00323 if (ptype == move.ptype())
00324 {
00325 break;
00326 }
00327 count += state.countPiecesOnStand(move.player(), ptype);
00328 }
00329 count += 100;
00330 os << static_cast<char>(to) << static_cast<char>(count);
00331 }
00332 state.makeMove(moves[i]);
00333 }
00334 for (int i = max_length; i < 256; ++i)
00335 {
00336 os << '\0' << '\0';
00337 }
00338 }
00339
00340 void osl::record::
00341 OKisenStream::save(Record *record)
00342 {
00343 vector<Move> moves;
00344 vector<int> time;
00345 record->getMoves(moves, time);
00346 SimpleState state = record->getInitialState();
00347 save(state, moves);
00348 }
00349
00350 void osl::record::
00351 KisenIpxWriter::writeString(const std::string &name, size_t length)
00352 {
00353 for (size_t i = 0; i < length; ++i)
00354 {
00355 if (i < name.length())
00356 {
00357 os << name[i];
00358 }
00359 else
00360 {
00361 os << '\0';
00362 }
00363 }
00364 }
00365
00366 void osl::record::
00367 KisenIpxWriter::writeRating(int rating)
00368 {
00369 int high = rating / 256;
00370 int low = rating % 256;
00371 os << static_cast<char>(low) << static_cast<char>(high);
00372 }
00373
00374 void osl::record::
00375 KisenIpxWriter::writeStartDate(int year, int month, int day, int hour, int min)
00376 {
00377 const int high_year = year / 256;
00378 const int low_year = year % 256;
00379 os << static_cast<char>(low_year)
00380 << static_cast<char>(high_year)
00381 << static_cast<char>(month)
00382 << static_cast<char>(day)
00383 << static_cast<char>(hour)
00384 << static_cast<char>(min);
00385 }
00386
00387 void osl::record::
00388 KisenIpxWriter::save(const Record &record,
00389 int black_rating, int white_rating,
00390 const std::string &black_title,
00391 const std::string &white_title)
00392 {
00393
00394
00395 #ifndef _WIN32
00396 writeString(misc::IconvConvert::convert("EUC-JP", "SJIS", record.getPlayer(BLACK)), 14);
00397 writeString(misc::IconvConvert::convert("EUC-JP", "SJIS", record.getPlayer(WHITE)), 14);
00398 #endif
00399 writeString(black_title, 8);
00400 writeString(white_title, 8);
00401 for (int i = 44; i < 84; ++i)
00402 {
00403 os << '\0';
00404 }
00405 const boost::gregorian::date start_date = record.getDate();
00406 if (!start_date.is_special()) {
00407
00408 writeStartDate(start_date.year(), start_date.month(), start_date.day(), 9, 0);
00409 } else {
00410 for (int i = 84; i < 90; ++i)
00411 {
00412 os << '\0';
00413 }
00414 }
00415 for (int i = 90; i < 118; ++i)
00416 {
00417 os << '\0';
00418 }
00419 vector<Move> moves;
00420 vector<int> time;
00421 record.getMoves(moves, time);
00422
00423 if (moves.size() <= 256)
00424 {
00425 if (moves.size() % 2 == 0)
00426 os << static_cast<char>(KisenIpxFile::WHITE_WIN);
00427 else
00428 os << static_cast<char>(KisenIpxFile::BLACK_WIN);
00429 }
00430 else
00431 {
00432 if (moves.size() % 2 == 0)
00433 os << static_cast<char>(KisenIpxFile::WHITE_WIN_256);
00434 else
00435 os << static_cast<char>(KisenIpxFile::BLACK_WIN_256);
00436 }
00437 for (int i = 119; i < 212; ++i)
00438 {
00439 os << '\0';
00440 }
00441 writeRating(black_rating);
00442 writeRating(white_rating);
00443 for (int i = 216; i < 256; ++i)
00444 {
00445 os << '\0';
00446 }
00447 }
00448 #endif
00449
00450
00451
00452