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