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