00001
00002
00003 #include "osl/move_probability/featureSet.h"
00004 #include "osl/move_probability/feature.h"
00005 #include "osl/move_generator/legalMoves.h"
00006 #include "osl/container/moveVector.h"
00007 #include "osl/record/csa.h"
00008 #include "osl/misc/binaryIO.h"
00009 #include "osl/oslConfig.h"
00010 #include <boost/accumulators/accumulators.hpp>
00011 #include <boost/accumulators/statistics/stats.hpp>
00012 #include <boost/accumulators/statistics/mean.hpp>
00013 #include <boost/accumulators/statistics/min.hpp>
00014 #include <boost/accumulators/statistics/max.hpp>
00015 #include <boost/foreach.hpp>
00016 #include <boost/format.hpp>
00017 #include <boost/thread/mutex.hpp>
00018 #include <fstream>
00019 #include <iomanip>
00020 #include <iostream>
00021 #include <cstdio>
00022
00023 osl::move_probability::
00024 FeatureSet::FeatureSet()
00025 {
00026 }
00027
00028 osl::move_probability::
00029 FeatureSet::~FeatureSet()
00030 {
00031 }
00032
00033 void osl::move_probability::
00034 FeatureSet::pushBack(Feature *f, bool light)
00035 {
00036 features.push_back(f);
00037 if (light)
00038 light_features.push_back(features.size()-1);
00039 }
00040
00041 void osl::move_probability::
00042 FeatureSet::addFinished()
00043 {
00044 offsets.resize(features.size()+1);
00045 offsets[0] = 0;
00046 for (size_t i=0; i<features.size(); ++i)
00047 offsets[i+1] = offsets[i] + features[i].dimension();
00048 }
00049
00050 double osl::move_probability::FeatureSet::
00051 matchExp(const StateInfo& state, Move move, const double * weights) const
00052 {
00053 MoveInfo info(state, move);
00054 assert(offsets.size() == features.size()+1);
00055 double sum = 0.0;
00056 for (size_t i=0; i<features.size(); ++i) {
00057 sum += features[i].match(state, info, offsets[i], weights);
00058 }
00059 return exp(sum);
00060 }
00061
00062 double osl::move_probability::FeatureSet::
00063 matchLight(const StateInfo& state, Move move, const double * weights) const
00064 {
00065 MoveInfo info(state, move);
00066 assert(offsets.size() == features.size()+1);
00067 double sum = 0.0;
00068 BOOST_FOREACH(size_t i, light_features) {
00069 sum += features[i].match(state, info, offsets[i], weights);
00070 }
00071 return sum;
00072 }
00073
00074
00075 void osl::move_probability::FeatureSet::
00076 analyze(const StateInfo& state, Move move, const double * weights) const
00077 {
00078 MoveInfo info(state, move);
00079 std::cerr << record::csa::show(move) << "\n";
00080 vector<std::pair<double, std::string> > out;
00081 for (size_t i=0; i<features.size(); ++i) {
00082 double s = features[i].match(state, info, offsets[i], weights);
00083 if (s)
00084 out.push_back(make_pair(s, features[i].name()));
00085 }
00086 std::sort(out.begin(), out.end());
00087 std::reverse(out.begin(), out.end());
00088 for (size_t i=0; i<out.size(); ++i) {
00089 std::cerr << boost::format("%16s %6.2f ") % out[i].second % out[i].first;
00090 if (i % 3 == 2)
00091 std::cerr << "\n";
00092 }
00093 if (out.size() % 3 != 0)
00094 std::cerr << "\n";
00095 }
00096
00097 double osl::move_probability::FeatureSet::
00098 generateRating(const StateInfo& state, WeightedMoveVector& out,
00099 const double * weights) const
00100 {
00101 assert(! state.dirty);
00102 MoveVector moves;
00103 LegalMoves::generate(*state.state, moves);
00104 double sum = 0.0;
00105 FixedCapacityVector<Move,128> unpromote_moves;
00106 BOOST_FOREACH(Move move, moves) {
00107 double score = matchExp(state, move, weights);
00108 out.push_back(WeightedMove(score, move));
00109 sum += score;
00110 }
00111 return sum;
00112 }
00113
00114 void osl::move_probability::FeatureSet::
00115 ratingToLogProb(const WeightedMoveVector& rating,
00116 double sum, MoveLogProbVector& out)
00117 {
00118 static const double scale = 100.0 / log(0.5);
00119 BOOST_FOREACH(WeightedMove move, rating) {
00120 double p = move.first/sum;
00121 if (std::isnan(p) || p <= 1.0/(1<<12))
00122 p = 1.0/(1<<12);
00123 const int logp = std::max(50, static_cast<int>(log(p)*scale));
00124 out.push_back(MoveLogProb(move.second, logp));
00125 }
00126 out.sortByProbability();
00127 }
00128
00129 void osl::move_probability::FeatureSet::
00130 generateLogProb(const StateInfo& state, MoveLogProbVector& out,
00131 const double * weights) const
00132 {
00133 WeightedMoveVector moves;
00134 double sum = generateRating(state, moves, weights);
00135 ratingToLogProb(moves, sum, out);
00136 }
00137
00138 bool osl::move_probability::FeatureSet::
00139 load(const char *base_filename, double * weights) const
00140 {
00141 std::string filename = std::string(base_filename) + ".txt";
00142 std::fill(weights, weights+dimension(), 0.0);
00143 std::ifstream is(filename.c_str());
00144 for (int i=0; i<dimension(); ++i) {
00145 is >> weights[i];
00146 if (! is) {
00147 std::cerr << "load failed at " << i << " in " << dimension()
00148 << " file " << filename << "\n";
00149 break;
00150 }
00151 }
00152 return is;
00153 }
00154
00155 bool osl::move_probability::FeatureSet::
00156 load_binary(const char *base_filename, double * weights) const
00157 {
00158 std::string filename = std::string(base_filename) + ".bin";
00159 std::fill(weights, weights+dimension(), 0.0);
00160 std::ifstream is(filename.c_str(), std::ios_base::binary);
00161 misc::BinaryElementReader<double> reader(is);
00162 for (int i=0; i<dimension(); ++i) {
00163 if (! reader.hasNext()) {
00164 std::cerr << "load failed at " << i << " in " << dimension()
00165 << " file " << filename << "\n";
00166 return false;
00167 }
00168 double value = reader.read();
00169 weights[i] = value;
00170 }
00171 return true;
00172 }
00173
00174 void osl::move_probability::FeatureSet::
00175 showSummary(const double * weights) const
00176 {
00177 for (size_t i=0; i<features.size(); ++i) {
00178 const Feature& f = features[i];
00179 #if (__GNUC_MINOR__ < 5)
00180 using namespace boost::accumulators;
00181 accumulator_set<double, stats<tag::mean, tag::min, tag::max> > acc;
00182 #endif
00183 int zero = 0;
00184 for (int j=offsets[i]; j<offsets[i+1]; ++j)
00185 if (weights[j]) {
00186 #if (__GNUC_MINOR__ < 5)
00187 acc(weights[j]);
00188 #endif
00189 }
00190 else
00191 ++zero;
00192 std::cerr << std::setw(16) << f.name()
00193 << " dim " << std::setw(5) << f.dimension() - zero
00194 << "/" << std::setw(5) << f.dimension()
00195 #if (__GNUC_MINOR__ < 5)
00196 << " min " << std::setw(6) << min(acc)
00197 << " max " << std::setw(6) << max(acc)
00198 << " mean " << std::setw(6) << mean(acc)
00199 #endif
00200 << "\n";
00201 }
00202 }
00203
00204
00205
00206 boost::scoped_array<double> osl::move_probability::
00207 StandardFeatureSet::weights;
00208 boost::scoped_array<double> osl::move_probability::
00209 StandardFeatureSet::tactical_weights;
00210
00211 osl::move_probability::StandardFeatureSet::
00212 StandardFeatureSet() : initialized(false)
00213 {
00214 pushBack(new TakeBackFeature, 1);
00215 pushBack(new CheckFeature, 1);
00216 pushBack(new SeeFeature, 1);
00217 pushBack(new ContinueCapture, 1);
00218 pushBack(new DropCaptured);
00219 pushBack(new SquareY, 1);
00220 pushBack(new SquareX, 1);
00221 pushBack(new KingRelativeY, 1);
00222 pushBack(new KingRelativeX, 1);
00223 pushBack(new FromEffect, 1);
00224 pushBack(new ToEffect, 1);
00225 pushBack(new FromEffectLong, 1);
00226 pushBack(new ToEffectLong, 1);
00227 pushBack(new Pattern(0,-1));
00228 pushBack(new Pattern(1,-1));
00229 pushBack(new Pattern(1,0));
00230 pushBack(new Pattern(0,1));
00231 pushBack(new Pattern(1,1));
00232 pushBack(new Pattern(1,-2));
00233 pushBack(new Pattern(0,-2));
00234 pushBack(new Pattern(0,2));
00235 pushBack(new Pattern(2,0));
00236 pushBack(new Pattern(1,2));
00237 pushBack(new MoveFromOpposingSliders);
00238 pushBack(new AttackToOpposingSliders);
00239 pushBack(new PawnAttack);
00240 pushBack(new CapturePtype, 1);
00241 pushBack(new BlockLong);
00242 pushBack(new BlockLongFrom);
00243 pushBack(new LanceAttack);
00244 pushBack(new BishopAttack);
00245 pushBack(new RookAttack);
00246 pushBack(new BreakThreatmate);
00247 pushBack(new SendOff);
00248 pushBack(new CheckmateIfCapture);
00249 pushBack(new OpposingPawn);
00250 pushBack(new DropAfterOpposingPawn);
00251 pushBack(new LongRecapture);
00252 pushBack(new SacrificeAttack);
00253 pushBack(new AddEffectLong);
00254 pushBack(new King5x5Ptype);
00255 pushBack(new KingBlockade);
00256 pushBack(new CoverFork);
00257 pushBack(new ThreatmateByCapture);
00258 pushBack(new LureDefender);
00259 pushBack(new CoverPawn);
00260 pushBack(new PromotionBySacrifice);
00261 pushBack(new EscapeThreatened);
00262 pushBack(new BookMove);
00263 addFinished();
00264 }
00265
00266 osl::move_probability::StandardFeatureSet::
00267 ~StandardFeatureSet()
00268 {
00269 }
00270
00271 const osl::move_probability::StandardFeatureSet&
00272 osl::move_probability::StandardFeatureSet::
00273 instance(bool verbose)
00274 {
00275 static StandardFeatureSet the_instance;
00276 the_instance.setUp(verbose);
00277 return the_instance;
00278 }
00279
00280 bool osl::move_probability::StandardFeatureSet::
00281 healthCheck()
00282 {
00283 return instance(true).ok();
00284 }
00285
00286 namespace osl
00287 {
00288 namespace move_probability
00289 {
00290 boost::mutex standardfeatureset_lock;
00291 }
00292 }
00293 bool osl::move_probability::StandardFeatureSet::
00294 setUp(bool verbose)
00295 {
00296 boost::mutex::scoped_lock lk(standardfeatureset_lock);
00297 static bool initialized = false;
00298 if (initialized)
00299 return true;
00300 initialized = true;
00301 weights.reset(new double[dimension()]);
00302 std::string filename = OslConfig::home();
00303 filename += "/data/move-order";
00304 if (verbose)
00305 std::cerr << "loading " << filename << ".bin ";
00306 const bool success = load_binary(filename.c_str(), &weights[0]);
00307 if (verbose)
00308 std::cerr << (success ? "success" : "failed\a") << "\n";
00309
00310 filename = OslConfig::home();
00311 filename += "/data/move-tactical.txt";
00312 const int tactical_dimension = 8*4;
00313 tactical_weights.reset(new double[tactical_dimension]);
00314 if (verbose)
00315 std::cerr << "loading " << filename << " ";
00316 std::ifstream is(filename.c_str());
00317 for (int i=0; i<tactical_dimension; ++i)
00318 is >> tactical_weights[i];
00319 if (verbose)
00320 std::cerr << (is ? "success" : "failed\a") << "\n";
00321 this->initialized = success && is;
00322 return this->initialized;
00323 }
00324
00325 void osl::move_probability::StandardFeatureSet::
00326 generateLogProb(const StateInfo& state, MoveLogProbVector& out) const
00327 {
00328 FeatureSet::generateLogProb(state, out, &weights[0]);
00329 }
00330
00331 void osl::move_probability::StandardFeatureSet::
00332 generateLogProb2(const StateInfo& state, MoveLogProbVector& out) const
00333 {
00334 WeightedMoveVector moves;
00335 double sum = FeatureSet::generateRating(state, moves, &weights[0]);
00336 double elapsed = 0.0, welapsed = 0.0, last_p = 1.0;
00337 std::sort(moves.begin(), moves.end());
00338 for (int i=moves.size()-1; i>=0; --i) {
00339 WeightedMove move = moves[i];
00340 static const double scale = 100.0 / log(0.5);
00341 if (i+1<(int)moves.size())
00342 welapsed = std::max(welapsed, std::min(moves[i+1].first,move.first*4));
00343 double p = move.first/(sum-elapsed+welapsed);
00344 if (std::isnan(p) || p <= 1.0/(1<<12))
00345 p = 1.0/(1<<12);
00346 else
00347 p = std::min(last_p, p);
00348 int logp = std::max(50, static_cast<int>(log(p)*scale));
00349 if (moves.size() - i <= 8)
00350 logp = std::min(logp, 300);
00351 else if (moves.size() - i <= 16)
00352 logp = std::min(logp, 500);
00353 out.push_back(MoveLogProb(move.second, logp));
00354 elapsed += move.first;
00355 welapsed = (welapsed+move.first)*(moves.size()-i)/moves.size();
00356 }
00357 }
00358
00359 void osl::move_probability::StandardFeatureSet::
00360 generateLogProb(const StateInfo& state, int , MoveLogProbVector& out, bool ) const
00361 {
00362 generateLogProb2(state, out);
00363 }
00364
00365 double osl::move_probability::StandardFeatureSet::
00366 matchLight(const StateInfo& state, Move move) const
00367 {
00368 return FeatureSet::matchLight(state, move, &weights[0]);
00369 }
00370
00371 int osl::move_probability::StandardFeatureSet::
00372 logProbTakeBack(const StateInfo& state, Move target) const
00373 {
00374 const int progress8 = state.progress8();
00375 const double sum = matchLight(state, target);
00376 return tacticalLogProb(progress8*4 + 0, sum);
00377 }
00378
00379 int osl::move_probability::StandardFeatureSet::
00380 logProbSeePlus(const StateInfo& state, Move target) const
00381 {
00382 const int progress8 = state.progress8();
00383 const double sum = matchLight(state, target);
00384 return tacticalLogProb(progress8*4 + 2, sum);
00385 }
00386
00387 int osl::move_probability::StandardFeatureSet::
00388 tacticalLogProb(int offset, double sum) const
00389 {
00390 static const double scale = 100.0 / log(0.5);
00391 double x = tactical_weights[offset] * sum + tactical_weights[offset+1];
00392 double p = 1/(1.0+exp(-x));
00393 return std::max(50, static_cast<int>(log(p)*scale));
00394 }
00395
00396
00397
00398
00399
00400
00401
00402
00403