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