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