00001
00002
00003 #ifndef OSL_MOVE_PROBABILITY_FEATURE_H
00004 #define OSL_MOVE_PROBABILITY_FEATURE_H
00005
00006 #include "osl/move_probability/moveInfo.h"
00007 #include "osl/move_probability/stateInfo.h"
00008 #include "osl/effect_util/additionalEffect.h"
00009 #include "osl/effect_util/neighboring8Direct.h"
00010 #include "osl/neighboring8.h"
00011 #include <boost/foreach.hpp>
00012 #include <string>
00013
00014 namespace osl
00015 {
00016 namespace move_probability
00017 {
00018 class Feature
00019 {
00020 std::string my_name;
00021 int dim;
00022 public:
00023 Feature(std::string n, size_t d) : my_name(n), dim(d)
00024 {
00025 assert(dim > 0);
00026 }
00027 virtual ~Feature();
00028 std::string name() const { return my_name; }
00029 virtual double match(const StateInfo&, const MoveInfo&, int offset, const double *) const=0;
00030 size_t dimension() const { return dim; }
00031
00032 static int classifyEffect9(const NumEffectState& state, Player player, Square to)
00033 {
00034 const int a = std::min(2, state.countEffect(player, to));
00035 int d = std::min(2,state.countEffect(alt(player), to));
00036 if (a>d)
00037 d += AdditionalEffect::hasEffect(state, to, alt(player));
00038 return a*3 + d;
00039 }
00040 };
00041
00042 class CheckFeature : public Feature
00043 {
00044 public:
00045 enum { CHECK_CLASS = 4, RELATIVE_Y_CLASS = 3 };
00046 CheckFeature()
00047 : Feature("Check", CHECK_CLASS*PTYPE_SIZE*2*RELATIVE_Y_CLASS)
00048 {
00049 }
00050 static int checkIndex(const MoveInfo& move)
00051 {
00052 if (move.open_check)
00053 return 3;
00054 return (move.see > 0) ? 0 : ((move.see == 0) ? 1 : 2);
00055 }
00056 static int sign(const NumEffectState& state, Move move,
00057 Player player)
00058 {
00059 const Square king = state.kingSquare(alt(player));
00060 int ry = (move.to().y() - king.y())*playerToMul(player);
00061 if (ry == 0) return 0;
00062 return ry > 0 ? 1 : -1;
00063 }
00064 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00065 {
00066 if (! move.check && ! move.open_check)
00067 return 0;
00068 const Player player = move.player;
00069 int index = sign(*state.state, move.move, player)+1;
00070 index = (index*2 + move.move.isDrop())*PTYPE_SIZE
00071 + move.move.ptype();
00072 index = index*CHECK_CLASS + checkIndex(move);
00073 return w[offset+index];
00074 }
00075 };
00076 class TakeBackFeature : public Feature
00077 {
00078 public:
00079 TakeBackFeature() : Feature("TakeBack", 3)
00080 {
00081 }
00082 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00083 {
00084 if (! state.history->hasLastMove()
00085 || state.history->lastMove().to() != move.move.to())
00086 return 0;
00087 int match = 2;
00088 if (move.see >= 0) {
00089 --match;
00090 if (state.history->hasLastMove(2)
00091 && state.history->lastMove(2).to() == move.move.to())
00092 --match;
00093 }
00094 return w[offset + match];
00095 }
00096 };
00097 class SeeFeature : public Feature
00098 {
00099 public:
00100 enum { SeeClass = 23, YClass = 3, ProgressClass = 8 };
00101 SeeFeature() : Feature("SeeFeature", SeeClass*(3+ProgressClass))
00102 {
00103 }
00104 static int seeIndex(int see)
00105 {
00106 int index = see / 128;
00107 if (see > 0) {
00108 index = std::min(10, index);
00109 index += 12;
00110 } else if (see == 0) {
00111 index += 11;
00112 } else {
00113 index += 10;
00114 index = std::max(0, index);
00115 }
00116 return index;
00117 }
00118 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00119 {
00120 const int see_index = seeIndex(move.see);
00121 const Player player = move.player;
00122 const Square to = move.move.to();
00123 const int promote_index = to.canPromote(player)
00124 ? 1 : (to.canPromote(alt(player)) ? 2 : 0);
00125 double sum = w[offset+see_index+promote_index*SeeClass];
00126 int progress_index = YClass + state.progress8();
00127 sum += w[offset+see_index+progress_index*SeeClass];
00128 return sum;
00129 }
00130 };
00131
00132 class CapturePtype : public Feature
00133 {
00134 public:
00135 CapturePtype() : Feature("CapturePtype", PTYPE_SIZE*PTYPE_SIZE*2*3)
00136 {
00137 }
00138 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00139 {
00140 const Ptype captured = move.move.capturePtype();
00141 const Player player = move.player;
00142 int index = (move.move.ptype())*PTYPE_SIZE + captured;
00143 BOOST_STATIC_ASSERT(PTYPE_EDGE == 1);
00144 if (captured != PTYPE_EMPTY
00145 && captured == state.threatened[alt(player)].ptype())
00146 ++index;
00147 if (move.see > 0)
00148 index += PTYPE_SIZE*PTYPE_SIZE;
00149 if (captured != PTYPE_EMPTY)
00150 index += std::min(2, state.state->countPiecesOnStand(player, unpromote(captured)))
00151 * (2*PTYPE_SIZE*PTYPE_SIZE);
00152 return w[offset+index];
00153 }
00154 };
00155
00156 class ContinueCapture : public Feature
00157 {
00158 public:
00159 ContinueCapture() : Feature("ContinueCapture", 1) {}
00160 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00161 {
00162 if (move.move.capturePtype() == PTYPE_EMPTY
00163 || ! state.history->hasLastMove(2)
00164 || state.history->lastMove(2).to() != move.move.from())
00165 return 0;
00166 return w[offset];
00167 }
00168 };
00169
00171 class DropCaptured : public Feature
00172 {
00173 public:
00174 DropCaptured() : Feature("DropCaptured", PTYPE_SIZE - PTYPE_BASIC_MIN) {}
00175 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00176 {
00177 if (! move.move.isDrop()
00178 || ! state.history->hasLastMove(2)
00179 || ! state.history->lastMove(2).isNormal()
00180 || state.history->lastMove(2).capturePtype() != move.move.ptype())
00181 return 0.0;
00182 const size_t index = move.move.ptype() - PTYPE_BASIC_MIN;
00183 assert(index < dimension());
00184 return w[index + offset];
00185 }
00186 };
00187
00188 class SquareY : public Feature
00189 {
00190 public:
00191
00192 enum {
00193 DIM = 9*PTYPE_SIZE*16
00194 };
00195 SquareY() : Feature("SquareY", DIM)
00196 {
00197 }
00198 static int fromTo(Square to, Square from)
00199 {
00200 return std::max(-3, std::min(to.y() - from.y(), 3));
00201 }
00202 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00203 {
00204 const Move move = info.move;
00205 const Player P = info.player;
00206 const Square to = move.to().squareForBlack(P);
00207 size_t index = ((to.y()-1)*PTYPE_SIZE + move.ptype())*16;
00208 assert(index+16 <= dimension());
00209 const int from_to = move.isDrop() ? 0
00210 : fromTo(to, move.from().squareForBlack(P));
00211 double sum = w[offset + index + from_to + 3];
00212 if (move.isDrop() || move.isPromotion())
00213 sum += w[offset + index + 7];
00214 sum += w[offset + index + state.progress8()+8];
00215 return sum;
00216 }
00217 };
00218
00219 class SquareX : public Feature
00220 {
00221 public:
00222
00223 SquareX() : Feature("SquareX", 5*PTYPE_SIZE*16)
00224 {
00225 }
00226 static int fromTo(Square to, Square from)
00227 {
00228 return std::max(-3, std::min(to.x() - from.x(), 3));
00229 }
00230 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00231 {
00232 const Move move = info.move;
00233 int to = move.to().x();
00234 int from_to = move.isDrop() ? 0 : fromTo(move.to(), move.from());
00235 if (to > 5) {
00236 to = 10 - to;
00237 from_to = -from_to;
00238 }
00239 size_t index = ((to-1)*PTYPE_SIZE + move.ptype())*16;
00240 assert(index+16 <= dimension());
00241 double sum = w[offset + index + from_to + 3];
00242 if (move.isDrop() || move.isPromotion())
00243 sum += w[offset + index + 7];
00244 sum += w[offset + index + state.progress8()+8];
00245 return sum;
00246 }
00247 };
00248
00249 class KingRelativeY : public Feature
00250 {
00251 public:
00252
00253 enum { ONE_DIM = 17*PTYPE_SIZE*16 };
00254 KingRelativeY() : Feature("KingRelativeY", ONE_DIM*2)
00255 {
00256 }
00257 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00258 {
00259 const Move move = info.move;
00260 const Player P = info.player;
00261 const Ptype ptype = move.ptype();
00262
00263 const Square to = move.to().squareForBlack(P);
00264 const Square my_king = state.state->kingSquare(P).squareForBlack(P);
00265 const Square op_king = state.state->kingSquare(alt(P)).squareForBlack(P);
00266 const int from_to = move.isDrop() ? 0
00267 : SquareY::fromTo(to, move.from().squareForBlack(P));
00268
00269 size_t index = ((to.y()-my_king.y()+8)*PTYPE_SIZE + ptype)*16;
00270 assert(index+16 <= ONE_DIM);
00271 double sum = w[offset + index + from_to + 3];
00272 if (move.isDrop() || move.isPromotion())
00273 sum += w[offset + index + 7];
00274 sum += w[offset + index + state.progress8()+8];
00275
00276 index = ((to.y()-op_king.y()+8)*PTYPE_SIZE + ptype)*16;
00277 assert(index+16 <= ONE_DIM);
00278 sum += w[offset + ONE_DIM + index + from_to + 3];
00279 if (move.isDrop() || move.isPromotion())
00280 sum += w[offset + ONE_DIM + index + 7];
00281 sum += w[offset + ONE_DIM + index + state.progress8()+8];
00282 return sum;
00283 }
00284 };
00285 class KingRelativeX : public Feature
00286 {
00287 public:
00288
00289 enum { ONE_DIM = 9*PTYPE_SIZE*16 };
00290 KingRelativeX() : Feature("KingRelativeX", ONE_DIM*2)
00291 {
00292 }
00293 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00294 {
00295 const Move move = info.move;
00296 const Player P = info.player;
00297 const Ptype ptype = move.ptype();
00298
00299 const Square to = move.to();
00300 const Square my_king = state.state->kingSquare(P);
00301 const Square op_king = state.state->kingSquare(alt(P));
00302 const int from_to = move.isDrop() ? 0
00303 : SquareY::fromTo(to, move.from());
00304 int dx = to.x() - my_king.x(), fx = from_to;
00305 if (dx < 0) {
00306 dx = -dx;
00307 fx = -fx;
00308 }
00309 size_t index = (dx*PTYPE_SIZE + ptype)*16;
00310 assert(index+16 <= ONE_DIM);
00311 double sum = w[offset + index + fx + 3];
00312 if (move.isDrop() || move.isPromotion())
00313 sum += w[offset + index + 7];
00314 sum += w[offset + index + state.progress8()+8];
00315
00316 dx = to.x() - op_king.x(), fx = from_to;
00317 if (dx < 0) {
00318 dx = -dx;
00319 fx = -fx;
00320 }
00321 index = (dx*PTYPE_SIZE + ptype)*16;
00322 assert(index+16 <= ONE_DIM);
00323 sum += w[offset + ONE_DIM + index + fx + 3];
00324 if (move.isDrop() || move.isPromotion())
00325 sum += w[offset + ONE_DIM + index + 7];
00326 sum += w[offset + ONE_DIM + index + state.progress8()+8];
00327 return sum;
00328 }
00329 };
00330
00331 class FromEffect : public Feature
00332 {
00333 public:
00334 FromEffect() : Feature("FromEffect", PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE)
00335 {
00336 }
00337 double match(const StateInfo& state_info, const MoveInfo& info, int offset, const double *w) const
00338 {
00339 const Move move = info.move;
00340 if (move.isDrop())
00341 return 0;
00342 const NumEffectState& state = *state_info.state;
00343 const Ptype me = move.oldPtype();
00344 const Ptype support = state.findCheapAttack(info.player, move.from()).ptype();
00345 const Ptype attack = state.findCheapAttack(alt(info.player), move.from()).ptype();
00346 const size_t index = (((me * PTYPE_SIZE) + support) * PTYPE_SIZE) + attack;
00347 assert(index < dimension());
00348 return w[index + offset];
00349 }
00350 };
00351
00352 class ToEffect : public Feature
00353 {
00354 public:
00355 ToEffect() : Feature("ToEffect", PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE)
00356 {
00357 }
00358 static const Piece find(const NumEffectState& state,
00359 Square to, const PieceMask& remove,
00360 Player player)
00361 {
00362 assert(to.isOnBoard());
00363 PieceMask pieces = state.piecesOnBoard(player)
00364 & state.effectSetAt(to);
00365 pieces &= ~remove;
00366 return state.selectCheapPiece(pieces);
00367 }
00368 static void supportAttack(const NumEffectState& state,
00369 Square to,
00370 const PieceMask& my_pin,
00371 const PieceMask& op_pin,
00372 Player turn,
00373 std::pair<Ptype,Ptype>& out)
00374 {
00375 out.first = find(state, to, my_pin, turn).ptype();
00376 out.second = find(state, to, op_pin, alt(turn)).ptype();
00377 }
00378 static void supportAttack(const StateInfo& info, Square to, Move move,
00379 std::pair<Ptype,Ptype>& out)
00380 {
00381 const Player turn = info.state->turn();
00382 if (move.isDrop())
00383 return supportAttack(*(info.state), to, info.pin[turn],
00384 info.pin[alt(turn)], turn, out);
00385 PieceMask my_pin = info.pin[turn];
00386 my_pin.set(info.state->pieceAt(move.from()).number());
00387 supportAttack(*(info.state), to, my_pin, info.pin[alt(turn)],
00388 turn, out);
00389 }
00390 static size_t supportAttack(const StateInfo& info, Square to, Move move)
00391 {
00392 std::pair<Ptype,Ptype> pair;
00393 supportAttack(info, to, move, pair);
00394 return pair.first * PTYPE_SIZE + pair.second;
00395 }
00396 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00397 {
00398 const Move move = info.move;
00399 const Ptype me = move.ptype(), captured = move.capturePtype();
00400 const size_t position_index = supportAttack(state, move.to(), move);
00401 const size_t index = ((me * PTYPE_SIZE) + captured)
00402 * PTYPE_SIZE * PTYPE_SIZE + position_index;
00403 assert(index < dimension());
00404 return w[index + offset];
00405 }
00406 };
00407
00408 class FromEffectLong : public Feature
00409 {
00410 public:
00411 FromEffectLong() : Feature("FromEffectLong", PTYPE_SIZE*8*8)
00412 {
00413 }
00414 double match(const StateInfo& state_info, const MoveInfo& info, int offset, const double *w) const
00415 {
00416 const Move move = info.move;
00417 if (move.isDrop())
00418 return 0;
00419 const NumEffectState& state = *state_info.state;
00420 const Ptype ptype = move.oldPtype();
00421 const CArray<bool,3> me = {{
00422 state.longEffectAt<LANCE>(move.from(), info.player).any(),
00423 state.longEffectAt<BISHOP>(move.from(), info.player).any(),
00424 state.longEffectAt<ROOK>(move.from(), info.player).any(),
00425 }};
00426 const CArray<bool,3> op = {{
00427 state.longEffectAt<LANCE>(move.from(), alt(info.player)).any(),
00428 state.longEffectAt<BISHOP>(move.from(), alt(info.player)).any(),
00429 state.longEffectAt<ROOK>(move.from(), alt(info.player)).any(),
00430 }};
00431 size_t index = ptype;
00432 for (int i=0; i<3; ++i) {
00433 index *= 2; index += me[i];
00434 index *= 2; index += op[i];
00435 }
00436 assert(index < dimension());
00437 return w[index + offset];
00438 }
00439 };
00440
00441 class ToEffectLong : public Feature
00442 {
00443 public:
00444 ToEffectLong() : Feature("ToEffectLong", PTYPE_SIZE*8*8)
00445 {
00446 }
00447 double match(const StateInfo& state_info, const MoveInfo& info, int offset, const double *w) const
00448 {
00449 const Move move = info.move;
00450 const NumEffectState& state = *state_info.state;
00451 const Ptype ptype = move.oldPtype();
00452 NumBitmapEffect effect=state.effectSetAt(move.to());
00453 if (! move.isDrop())
00454 effect.reset(state.pieceOnBoard(move.from()).number()+8);
00455 const CArray<mask_t,3> pieces = {{
00456 effect.selectLong<LANCE>() >> 8,
00457 effect.selectLong<BISHOP>() >> 8,
00458 effect.selectLong<ROOK>() >> 8
00459 }};
00460 size_t index = ptype;
00461 for (int i=0; i<3; ++i) {
00462 index *= 2;
00463 index += (pieces[i] & state.piecesOnBoard(info.player).getMask(1)).any();
00464 index *= 2;
00465 index += (pieces[i] & state.piecesOnBoard(alt(info.player)).getMask(1)).any();
00466 }
00467 assert(index < dimension());
00468 return w[index + offset];
00469 }
00470 };
00471
00472 class PatternCommon : public Feature
00473 {
00474 public:
00475 enum {
00476 SupportSize = PTYPE_SIZE,
00477 AttackSize = PTYPE_SIZE, AttackBase = SupportSize,
00478 EffectSize = 9, EffectBase = AttackBase+AttackSize,
00479 OpKingSize = 4, OpKingBase = EffectBase+EffectSize,
00480 MyKingSize = 3, MyKingBase = OpKingBase+OpKingSize,
00481 PromotionSize = 2, PromotionBase = MyKingBase+MyKingSize,
00482 PinOpenSize = 4, PinOpenBase = PromotionBase + PromotionSize,
00483 LastToSize = 4, LastToBase = PinOpenBase + PinOpenSize,
00484 LastEffectChangedSize = 6, LastEffectChangedBase = LastToBase + LastToSize,
00485 SquareDim = LastEffectChangedBase + LastEffectChangedSize,
00486 PatternCacheSize = PTYPEO_SIZE*SquareDim,
00487 OneDim = PTYPE_SIZE*PatternCacheSize,
00488 };
00489 PatternCommon(const std::string& name, int dim) : Feature(name, dim)
00490 {
00491 }
00492 double addOne(const StateInfo& state, int offset,
00493 const double *w, Square position) const
00494 {
00495 if (! position.isOnBoardRegion() || state.state->pieceAt(position).isEdge()) {
00496 size_t basic = ptypeOIndex(PTYPEO_EDGE) *SquareDim;
00497 return w[offset + basic];
00498 }
00499 const StateInfo::pattern_square_t& cache
00500 = state.pattern_cache[position.index()];
00501 double sum = 0.0;
00502 for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i)
00503 sum += w[offset + cache[i]];
00504 return sum;
00505 }
00506 static void updateCache(StateInfo& info);
00507 private:
00508 static void updateCacheOne(Square target, StateInfo& info);
00509 };
00510
00511 template<bool TestPromotable>
00512 class PatternBase : public PatternCommon
00513 {
00514 int dx, black_dy;
00515 public:
00516 enum {
00517 PromotionSize = TestPromotable ? 3 : 1,
00518 DIM = PromotionSize * OneDim,
00519 };
00520 PatternBase(int x, int y)
00521 : PatternCommon(name(x,y), DIM), dx(x), black_dy(y)
00522 {
00523 }
00524 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00525 {
00526 const Move move = info.move;
00527 const Ptype ptype = move.ptype();
00528 int basic = ptype*PatternCacheSize;
00529 int basic_from = (move.isPromotion() ? PTYPE_EDGE : PTYPE_EMPTY)
00530 *PatternCacheSize;
00531 const Square to = move.to();
00532 if (TestPromotable && to.canPromote(info.player))
00533 offset += OneDim;
00534 else if (TestPromotable && to.canPromote(alt(info.player)))
00535 offset += 2*OneDim;
00536 int dy = info.player == BLACK ? black_dy : -black_dy;
00537 Square target = to + Offset(dx, dy);
00538 double sum = 0.0;
00539 if (move.from() != target)
00540 sum += addOne(state, offset+basic, w, target);
00541 else
00542 sum += addOne(state, offset+basic_from, w, target);
00543 if (dx == 0)
00544 return sum;
00545 target = to + Offset(-dx, dy);
00546 if (move.from() != target)
00547 sum += addOne(state, offset+basic, w, target);
00548 else
00549 sum += addOne(state, offset+basic_from, w, target);
00550 return sum;
00551 }
00552 static std::string name(int x, int y)
00553 {
00554 return std::string("Pattern")
00555 + (TestPromotable ? "P" : "")
00556 + "X" + (char)('2'+x) + "Y"+(char)('2'+y);
00557 }
00558 };
00559
00560 typedef PatternBase<false> Pattern;
00561 typedef PatternBase<true> PatternPromotion;
00562
00563 class MoveFromOpposingSliders : public Feature
00564 {
00565 public:
00566 MoveFromOpposingSliders() : Feature("MoveFromOpposingSliders", 36*PTYPE_SIZE)
00567 {
00568 }
00569 static int longPtype(const NumEffectState& state, Square position, Player player)
00570 {
00571 const int offset = PtypeFuns<LANCE>::indexNum*32;
00572 mask_t p = state.longEffectAt<LANCE>(position, player);
00573 if (p.any())
00574 return state.hasEffectAt(player, state.pieceOf(p.takeOneBit()+offset).square());
00575 p = state.longEffectAt<BISHOP>(position, player);
00576 if (p.any())
00577 return 2 + state.hasEffectAt(player, state.pieceOf(p.takeOneBit()+offset).square());
00578 p = state.longEffectAt<ROOK>(position, player);
00579 assert(p.any());
00580 return 4 + state.hasEffectAt(player, state.pieceOf(p.takeOneBit()+offset).square());
00581 }
00582 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00583 {
00584 const Square from = move.move.from();
00585 if (from.isPieceStand()
00586 || ! state.pinByOpposingSliders(state.state->pieceOnBoard(from)))
00587 return 0.0;
00588 const int me = longPtype(*state.state, from, move.player);
00589 const int op = longPtype(*state.state, from, alt(move.player));
00590 return w[offset + (me*6+op)*PTYPE_SIZE+move.move.ptype()];
00591 }
00592 };
00593 class AttackFromOpposingSliders : public Feature
00594 {
00595 public:
00596 AttackFromOpposingSliders() : Feature("AttackFromOpposingSliders", PTYPE_SIZE*PTYPE_SIZE*2)
00597 {
00598 }
00599 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00600 {
00601 const Move move = info.move;
00602 const Piece attack = state.state->findCheapAttack(alt(info.player), move.to());
00603 if (! state.pinByOpposingSliders(attack))
00604 return 0.0;
00605 if (state.state->countEffect(alt(info.player), move.to()) == 1)
00606 offset += PTYPE_SIZE*PTYPE_SIZE;
00607 double sum = w[offset + PTYPE_EMPTY*PTYPE_SIZE+attack.ptype()]
00608 + w[offset + move.ptype()*PTYPE_SIZE+PTYPE_EMPTY]
00609 + w[offset + move.ptype()*PTYPE_SIZE+attack.ptype()];
00610 if (info.see < 0)
00611 sum += w[offset + PTYPE_EMPTY*PTYPE_SIZE+PTYPE_EMPTY]*info.see/1024.0;
00612 return sum;
00613 }
00614 };
00615 class AttackToOpposingSliders : public Feature
00616 {
00617 public:
00618 AttackToOpposingSliders() : Feature("AttackToOpposingSliders", PTYPE_SIZE*PTYPE_SIZE*2)
00619 {
00620 }
00621 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
00622 {
00623 const Move move = info.move;
00624 double sum = 0.0;
00625 BOOST_FOREACH(Piece piece, state.pin_by_opposing_sliders) {
00626 if (! state.state->hasEffectIf(move.ptypeO(), move.to(),
00627 piece.square()))
00628 continue;
00629 int base = (piece.owner() == info.player) ? PTYPE_SIZE*PTYPE_SIZE : 0;
00630 sum += w[offset + base + PTYPE_EMPTY*PTYPE_SIZE+piece.ptype()]
00631 + w[offset + base + move.ptype()*PTYPE_SIZE+PTYPE_EMPTY]
00632 + w[offset + base + move.ptype()*PTYPE_SIZE+piece.ptype()];
00633 if (info.see < 0)
00634 sum += w[offset + base + PTYPE_EMPTY*PTYPE_SIZE+PTYPE_EMPTY]*info.see/1024.0;
00635 }
00636 return sum;
00637 }
00638 };
00639 class PawnAttack : public Feature
00640 {
00641 public:
00642 enum {
00643 PTYPE2_DIM = PTYPE_SIZE*2*PTYPE_SIZE*2*2,
00644 EFFECT_DIM = PTYPE_SIZE*2*8*9,
00645 BasicSize = PTYPE2_DIM+EFFECT_DIM,
00646 PawnSize = BasicSize*3,
00647 DIM = PawnSize*2
00648 };
00649 PawnAttack() : Feature("PawnAttack", DIM)
00650 {
00651 }
00652 std::pair<int,int> squareStatus(const NumEffectState& state, Player player, Square to, Square& front) const
00653 {
00654 int u = 0, uu = 0;
00655 const int dy = (player == BLACK) ? -1 : 1;
00656 Square position = to + Offset(0, dy);
00657 front = position;
00658 Piece piece = state.pieceAt(position);
00659 if (piece.isPiece())
00660 u = piece.ptype() + ((piece.owner() == player) ? PTYPE_SIZE : 0);
00661 assert(! piece.isEdge());
00662 piece = state.pieceAt(position + Offset(0, dy));
00663 if (piece.isPiece())
00664 uu = piece.ptype() + ((piece.owner() == player) ? PTYPE_SIZE : 0);
00665 return std::make_pair(u, uu);
00666 }
00667 double matchPtype(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00668 {
00669 const Player player = move.player;
00670 const Square to = move.move.to();
00671
00672 Square front;
00673 const std::pair<int,int> u = squareStatus(*state.state, player, to, front);
00674
00675 int promotion = 0;
00676 if (front.canPromote(player))
00677 promotion = 1;
00678 else if (front.canPromote(alt(player)))
00679 promotion = 2;
00680 offset += BasicSize*promotion;
00681
00682 bool pawn_drop = move.move.isDrop();
00683 const int index0 = (u.first*PTYPE_SIZE*2+u.second)*2 + pawn_drop;
00684 double sum = w[offset + index0];
00685
00686 const int effect = classifyEffect9(*state.state, player, front);
00687 const int index1 = u.first*8 + move.move.to().squareForBlack(player).y()-2;
00688 sum += w[offset + index1*9+effect];
00689 return sum;
00690 }
00691 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00692 {
00693 if (move.move.ptype() == PAWN)
00694 return matchPtype(state, move, offset, w);
00695 if (move.move.ptype() == LANCE
00696 && state.state->canDropPawnTo(move.player, move.move.to().x()))
00697 return matchPtype(state, move, offset+PawnSize, w);
00698 return 0.0;
00699 }
00700 };
00701
00702 class BlockLong : public Feature
00703 {
00704 public:
00705 enum {
00706 AttackPtype = 8,
00707 BasicAttack = AttackPtype*osl::PTYPEO_SIZE,
00708 OptionSize = 8,
00709 LongAttackSize = BasicAttack * OptionSize,
00710 DIM = PTYPE_SIZE * 2 * LongAttackSize
00711 };
00712 BlockLong() : Feature("BlockLong", DIM)
00713 {
00714 }
00715 static int longAttackIndex(osl::PtypeO ptypeo)
00716 {
00717 const Ptype ptype = getPtype(ptypeo);
00718 int index;
00719 if (ptype == LANCE) index = 0;
00720 else if (ptype == BISHOP) index = 1;
00721 else if (ptype == ROOK) index = 2;
00722 else {
00723 assert(ptype == PROOK || ptype == PBISHOP);
00724 index = 3;
00725 }
00726 if (getOwner(ptypeo) == WHITE) index += 4;
00727 return index;
00728 }
00729 static double addPiece(const StateInfo& state, Piece piece,
00730 Square to, const double *w, int offset)
00731 {
00732 assert(state.state->hasEffectByPiece(piece, to));
00733 const Direction d
00734 = Board_Table.getLongDirection<BLACK>(piece.square(), to);
00735 const StateInfo::long_attack_t&
00736 cache = state.long_attack_cache[piece.number()][longToShort(d)];
00737 double sum = 0.0;
00738 BOOST_FOREACH(int index, cache) {
00739 assert(index < LongAttackSize);
00740 sum += w[index+offset];
00741 }
00742 return sum;
00743 }
00744 static int ptypeSupport(Ptype moved, bool has_support)
00745 {
00746 return (moved*2 + has_support) * LongAttackSize;
00747 }
00748 static double findAll(const StateInfo& state, Player P,
00749 Square target, const double *w, int offset)
00750 {
00751 mask_t m = state.state->longEffectAt(target, P);
00752 double sum = 0.0;
00753 while (m.any()) {
00754 const Piece piece = state.state->pieceOf
00755 (m.takeOneBit() +PtypeFuns<LANCE>::indexNum*32);
00756 sum += addPiece(state, piece, target, w, offset);
00757 }
00758 m = state.state->longEffectAt(target, alt(P));
00759 while (m.any()) {
00760 const Piece piece = state.state->pieceOf
00761 (m.takeOneBit()+PtypeFuns<LANCE>::indexNum*32);
00762 sum += addPiece(state, piece, target, w, offset);
00763 }
00764 return sum;
00765 }
00766 static double findAll(const StateInfo& state, Move move, const double *w, int offset=0)
00767 {
00768 const Player P = move.player();
00769 Square target = move.to();
00770 int a = state.state->countEffect(P, target);
00771 offset += ptypeSupport(move.ptype(), a+move.isDrop()>1);
00772 return findAll(state, P, target, w, offset);
00773 }
00774 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00775 {
00776 return findAll(state, move.move, w, offset);
00777 }
00778 static void updateCache(StateInfo&);
00779 private:
00780 static void makeLongAttackOne(StateInfo& info,
00781 Piece piece, Direction d);
00782 };
00783 class BlockLongFrom : public Feature
00784 {
00785 public:
00786 enum {
00787 DIM = BlockLong::LongAttackSize
00788 };
00789 BlockLongFrom() : Feature("BlockLongFrom", DIM)
00790 {
00791 }
00792 static double findAll(const StateInfo& state, Move move, const double *w, int offset=0)
00793 {
00794 const Player P = move.player();
00795 Square target = move.from();
00796 return BlockLong::findAll(state, P, target, w, offset);
00797 }
00798 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00799 {
00800 if (move.move.isDrop())
00801 return 0;
00802 return findAll(state, move.move, w, offset);
00803 }
00804 };
00805 class LongRecapture : public Feature
00806 {
00807 public:
00808 enum {
00809 DIM = BlockLong::LongAttackSize * PTYPE_SIZE
00810 };
00811 LongRecapture() : Feature("LongRecapture", DIM)
00812 {
00813 }
00814 double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
00815 {
00816 if (move.see >= 0)
00817 return 0.0;
00818 const NumEffectState& state = *info.state;
00819 const Square to = move.move.to();
00820 int a = state.countEffect(move.player, to)+move.move.isDrop()-1;
00821 int d = state.countEffect(alt(move.player), to);
00822 if (d == 1
00823 || (d == 2 && a > 0
00824 && state.hasEffectByPiece(state.kingPiece(alt(move.player)), to))) {
00825 double sum = w[offset + (PTYPE_EMPTY)*BlockLong::LongAttackSize]
00826 *move.see/1024.0;
00827 offset += move.move.ptype() * BlockLong::LongAttackSize;
00828 const Piece opponent = state.findCheapAttack(alt(move.player), to);
00829 sum += BlockLong::findAll(info, move.player, opponent.square(), w, offset);
00830 return sum;
00831 }
00832 return 0.0;
00833 }
00834 };
00835 class AddEffectLong : public Feature
00836 {
00837 public:
00838 enum {
00839 DIM = PTYPE_SIZE*BlockLong::LongAttackSize
00840 };
00841 AddEffectLong() : Feature("AddEffectLong", DIM)
00842 {
00843 }
00844 static double addOne(Direction dir, const StateInfo& state, const MoveInfo& move, int offset, const double *w)
00845 {
00846 Offset diff = Board_Table.getOffset(move.player, dir);
00847 Square to = move.move.to() + diff;
00848 if (isLong(dir)) {
00849 while (state.state->pieceAt(to).isEmpty())
00850 to += diff;
00851 }
00852 if (! state.state->pieceAt(to).isPiece())
00853 return 0.0;
00854 return BlockLong::findAll(state, move.player, to, w, offset);
00855 }
00856 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00857 {
00858 offset += move.move.ptype()*BlockLong::LongAttackSize;
00859 unsigned int directions = Ptype_Table.getMoveMask(move.move.ptype());
00860 double sum = 0.0;
00861 do {
00862 Direction d = (Direction)(misc::BitOp::bsf(directions));
00863 directions &= directions-1;
00864 sum += addOne(d, state, move, offset, w);
00865 } while (directions);
00866 return sum;
00867 }
00868 };
00869 class LanceAttack : public Feature
00870 {
00871 public:
00872 enum {
00873 PatternCacheSize = PatternCommon::PatternCacheSize,
00874 DIM = PatternCacheSize*(8+4+4+1)
00875 };
00876 LanceAttack() : Feature("LanceAttack", DIM)
00877 {
00878 }
00879 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00880 {
00881 if (move.move.ptype() != LANCE
00882 || (! move.move.isDrop()
00883 && move.move.capturePtype() == PTYPE_EMPTY))
00884 return 0;
00885 const Offset up = Board_Table.getOffset(move.player, U);
00886 Square target = move.move.to() + up;
00887 while (state.state->pieceAt(target).isEmpty())
00888 target += up;
00889 if (state.state->pieceAt(target).isOnBoardByOwner(move.player)) {
00890 target += up;
00891 if (state.state->pieceAt(target).ptype() == LANCE) {
00892 while (state.state->pieceAt(target).isEmpty())
00893 target += up;
00894 }
00895 }
00896 if (state.state->pieceAt(target).isEdge())
00897 target -= up;
00898
00899 int y = move.move.to().y(), x = move.move.to().x();
00900 if (move.player == WHITE)
00901 y = 10-y;
00902 y -= 2;
00903 int dx1 = abs(state.state->kingSquare(move.player).x()-x);
00904 int dx2 = abs(state.state->kingSquare(alt(move.player)).x()-x);
00905 dx1 = std::min(dx1, 3);
00906 dx2 = std::min(dx2, 3);
00907 bool pawn = state.state->canDropPawnTo(alt(move.player), x);
00908 assert(! state.state->pieceAt(target).isEdge());
00909 const StateInfo::pattern_square_t& cache
00910 = state.pattern_cache[target.index()];
00911 double sum = 0.0;
00912 for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
00913 sum += w[offset + y*PatternCacheSize + cache[i]];
00914 sum += w[offset + (8+dx1)*PatternCacheSize + cache[i]];
00915 sum += w[offset + (12+dx1)*PatternCacheSize + cache[i]];
00916 if (! pawn)
00917 sum += w[offset + 16*PatternCacheSize + cache[i]];
00918 }
00919 return sum;
00920 }
00921 };
00922 class BishopAttack : public Feature
00923 {
00924 public:
00925 enum {
00926 PatternCacheSize = PatternCommon::PatternCacheSize,
00927 DIM = PatternCacheSize*2
00928 };
00929 BishopAttack() : Feature("BishopAttack", DIM)
00930 {
00931 }
00932 static double addSquare(Square target,
00933 const StateInfo& info,
00934 int offset, const double *w)
00935 {
00936 int type = 0;
00937 const StateInfo::pattern_square_t& cache
00938 = info.pattern_cache[target.index()];
00939 double sum = 0.0;
00940 for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
00941 sum += w[offset + type + cache[i]];
00942 }
00943 return sum;
00944 }
00945 template <Direction D,Ptype Type>
00946 static
00947 double addOne(const StateInfo& info, Square to,
00948 int offset, const double *w)
00949 {
00950 const NumEffectState& state = *info.state;
00951 const Offset diff = DirectionPlayerTraits<D,BLACK>::offset();
00952 Square target = to + diff;
00953 double sum = 0.0;
00954 if (state.pieceAt(target).isEdge())
00955 return sum;
00956 if (state.pieceAt(target).isPiece()
00957 && (state.pieceAt(target).ptype() != Type
00958 || state.pieceAt(target).owner() != state.turn())) {
00959 ;
00960 } else {
00961 while (state.pieceAt(target).isEmpty())
00962 target += diff;
00963 if (state.pieceAt(target).ptype() == Type
00964 && state.pieceAt(target).owner() == state.turn()) {
00965
00966 target += diff;
00967 while (state.pieceAt(target).isEmpty())
00968 target += diff;
00969 }
00970 if (state.pieceAt(target).isEdge())
00971 target -= diff;
00972 sum += addSquare(target, info, offset, w);
00973 if (! state.pieceAt(target).isPiece())
00974 return sum;
00975 }
00976
00977 target += diff;
00978 if (state.pieceAt(target).isEdge())
00979 return sum;
00980 while (state.pieceAt(target).isEmpty())
00981 target += diff;
00982 if (state.pieceAt(target).isEdge())
00983 target -= diff;
00984 sum += addSquare(target, info, offset+PatternCacheSize, w);
00985 return sum;
00986 }
00987 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
00988 {
00989 if (unpromote(move.move.ptype()) != BISHOP)
00990 return 0;
00991 double sum = 0.0;
00992 sum += addOne<UR,BISHOP>(state, move.move.to(), offset, w);
00993 sum += addOne<UL,BISHOP>(state, move.move.to(), offset, w);
00994 sum += addOne<DR,BISHOP>(state, move.move.to(), offset, w);
00995 sum += addOne<DL,BISHOP>(state, move.move.to(), offset, w);
00996 return sum;
00997 }
00998 };
00999 class RookAttack : public Feature
01000 {
01001 public:
01002 enum {
01003 DirectionSize = BishopAttack::DIM,
01004 DIM = DirectionSize*3
01005 };
01006 RookAttack() : Feature("RookAttack", DIM)
01007 {
01008 }
01009 double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
01010 {
01011 if (unpromote(move.move.ptype()) != ROOK)
01012 return 0;
01013 const Square to = move.move.to();
01014 double sum = 0.0;
01015 sum += BishopAttack::addOne<R,ROOK>(state, to, offset, w);
01016 sum += BishopAttack::addOne<L,ROOK>(state, to, offset, w);
01017 const bool pawn_drop = state.state->canDropPawnTo(alt(move.player), to.x());
01018 const int scale = pawn_drop ? 1 : 2;
01019 sum += BishopAttack::addOne<U,ROOK>(state, to, offset+DirectionSize*scale, w);
01020 sum += BishopAttack::addOne<D,ROOK>(state, to, offset+DirectionSize*scale, w);
01021 return sum;
01022 }
01023 };
01024 class BreakThreatmate : public Feature
01025 {
01026 public:
01027 enum {
01028 PatternCacheSize = PatternCommon::PatternCacheSize,
01029 AddEffectSize = PTYPE_SIZE * PatternCacheSize,
01030 OpenRoadSize = PTYPE_SIZE * PatternCacheSize, OpenRoadBase = AddEffectSize,
01031 KingMoveSize = PatternCacheSize, KingMoveBase = OpenRoadBase + OpenRoadSize,
01032 CaptureSize = PTYPE_SIZE*PTYPE_SIZE, CaptureBase = KingMoveBase + KingMoveSize,
01033 AddEffect8Size = PTYPE_SIZE*PatternCacheSize, AddEffect8Base = CaptureBase + CaptureSize,
01034 OtherMoveSize = 1, OtherMoveBase = AddEffect8Base + AddEffect8Size,
01035 DIM = OtherMoveBase + OtherMoveSize
01036 };
01037 BreakThreatmate() : Feature("BreakThreatmate", DIM)
01038 {
01039 }
01040 static bool isKingMove(Move move)
01041 {
01042 return move.ptype() == KING;
01043 }
01044 static bool isOpeningKingRoad(Move move, Square king)
01045 {
01046 return ! move.isDrop()
01047 && Neighboring8::isNeighboring8(move.from(), king);
01048 }
01049 static bool isDefendingThreatmate(Move move, Move threatmate,
01050 const NumEffectState& state)
01051 {
01052 if (move.to() == threatmate.to()
01053 || state.hasEffectIf(move.ptypeO(), move.to(),
01054 threatmate.to()))
01055 return true;
01056 if (threatmate.isDrop())
01057 return false;
01058 Offset32 offset32=Offset32(threatmate.from(),move.to());
01059 EffectContent effect=Ptype_Table.getEffect(move.ptypeO(),offset32);
01060 if (! effect.hasEffect())
01061 return false;
01062 if (effect.offset() == threatmate.to()-threatmate.from())
01063 return state.isEmptyBetween(threatmate.from(), move.to());
01064 return false;
01065 }
01066 static bool isDefendingKing8(Move move, Square king,
01067 const NumEffectState& state)
01068 {
01069 if (Neighboring8Direct::hasEffect
01070 (state, move.ptypeO(), move.to(), king))
01071 return true;
01072 mask_t m = state.longEffectAt(move.to(), alt(state.turn()));
01073 while (m.any()) {
01074 const Piece piece = state.pieceOf
01075 (m.takeOneBit() +PtypeFuns<LANCE>::indexNum*32);
01076 if (Neighboring8Direct::hasEffect
01077 (state, piece.ptypeO(), piece.square(), king))
01078 return true;
01079 }
01080 return false;
01081 }
01082 double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
01083 {
01084 if (! info.threatmate_move.isNormal())
01085 return 0;
01086 const NumEffectState& state = *info.state;
01087 const StateInfo::pattern_square_t& cache
01088 = info.pattern_cache[move.move.to().index()];
01089 const int PatternAny = ptypeOIndex(PTYPEO_EDGE)*PatternCommon::SquareDim;
01090 double sum = 0.0;
01091 if (isKingMove(move.move)) {
01092
01093 sum += w[offset + KingMoveBase + PatternAny];
01094 for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
01095 sum += w[offset + KingMoveBase + cache[i]];
01096 }
01097 } else {
01098 const Square king = state.kingSquare(move.player);
01099 if (isOpeningKingRoad(move.move, king)) {
01100
01101 int base = OpenRoadBase + move.move.ptype()*PatternCacheSize;
01102 sum += w[offset + base + PatternAny];
01103 for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
01104 sum += w[offset + base + cache[i]];
01105 }
01106 }
01107 if (isDefendingThreatmate(move.move, info.threatmate_move,
01108 state)) {
01109
01110 int base = move.move.ptype()*PatternCacheSize;
01111 sum += w[offset + base + PatternAny];
01112 for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
01113 sum += w[offset + base + cache[i]];
01114 }
01115 } else if (isDefendingKing8(move.move, king, state)) {
01116
01117 int base = move.move.ptype()*PatternCacheSize
01118 + AddEffect8Base;
01119 sum += w[offset + base + PatternAny];
01120 for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
01121 sum += w[offset + base + cache[i]];
01122 }
01123 }
01124 const Piece captured = state.pieceOnBoard(move.move.to());
01125
01126 if (captured.isPiece()) {
01127 if (Neighboring8Direct::hasEffect
01128 (state, captured.ptypeO(), captured.square(), king)) {
01129 sum += w[offset + CaptureBase
01130 + captured.ptype()*PTYPE_SIZE+move.move.ptype()];
01131 sum += w[offset + CaptureBase
01132 + captured.ptype()*PTYPE_SIZE];
01133 }
01134 else {
01135 sum += w[offset + CaptureBase + captured.ptype()*PTYPE_SIZE
01136 + PTYPE_EDGE];
01137 }
01138 }
01139 }
01140 if (sum == 0.0)
01141 sum += w[offset + OtherMoveBase];
01142 return sum;
01143 }
01144 };
01145
01146 class SendOff : public Feature
01147 {
01148 public:
01149 enum {
01150 DIM = PTYPE_SIZE,
01151 };
01152 SendOff() : Feature("SendOff", DIM)
01153 {
01154 }
01155 double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
01156 {
01157 if (! info.sendoffs.isMember(move.move.to()))
01158 return 0;
01159 return w[offset + move.move.ptype()];
01160 }
01161 };
01162
01163 class LureDefender : public Feature
01164 {
01165 public:
01166 enum {
01167 ATTACK_DIM = PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE,
01168 DIM = ATTACK_DIM*3,
01169 };
01170 LureDefender() : Feature("LureDefender", DIM)
01171 {
01172 }
01173 static double match(const NumEffectState& state, Move move, int see,
01174 const StateInfo::pinned_gs_t& pinned_list,
01175 int offset, const double *w)
01176 {
01177 const Square to = move.to(), king = state.kingSquare(alt(state.turn()));
01178 const Offset up = Board_Table.getOffset(state.turn(), U);
01179 const int basic_a = move.ptype() * PTYPE_SIZE*PTYPE_SIZE;
01180 double sum = 0.0;
01181 BOOST_FOREACH(PinnedGeneral defense, pinned_list) {
01182 if (to != defense.attack)
01183 continue;
01184 assert(defense.general.owner() != move.player());
01185 assert(defense.covered.owner() != move.player());
01186 if (defense.general.square() == move.to()+up)
01187 offset += ATTACK_DIM;
01188 else if (state.hasEffectIf(move.ptypeO(), move.to(), defense.general.square()))
01189 offset += ATTACK_DIM*2;
01190 int a = basic_a;
01191 if (defense.covered.square().canPromote(state.turn())
01192 && canPromote(move.ptype()))
01193 a = promote(move.ptype()) * PTYPE_SIZE*PTYPE_SIZE;
01194 const int b = defense.general.ptype() * PTYPE_SIZE;
01195 const int c = defense.covered.ptype();
01196 sum += w[offset];
01197 if (see < 0)
01198 sum += w[offset+1] * see/1024.0;
01199 sum += w[offset + a];
01200 sum += w[offset + b];
01201 sum += w[offset + c];
01202 sum += w[offset + a + b];
01203 sum += w[offset + a + c];
01204 sum += w[offset + b + c];
01205 if (defense.covered.square().canPromote(state.turn())) {
01206 sum += w[offset + a + PTYPE_EDGE];
01207 sum += w[offset + b + PTYPE_EDGE];
01208 }
01209 if (Neighboring8::isNeighboring8(defense.covered.square(), king)) {
01210 sum += w[offset + a + KING];
01211 sum += w[offset + b + KING];
01212 }
01213 sum += w[offset + a + b + c];
01214 break;
01215 }
01216 return sum;
01217 }
01218 double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
01219 {
01220 return match(*info.state, move.move, move.see,
01221 info.exchange_pins[alt(move.player)], offset, w);
01222 }
01223 };
01224
01225 class CheckmateIfCapture : public Feature
01226 {
01227 public:
01228 enum {
01229 DIM = PTYPE_SIZE*PTYPE_SIZE,
01230 };
01231 CheckmateIfCapture() : Feature("CheckmateIfCapture", DIM)
01232 {
01233 }
01234 double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
01235 {
01236 if (info.state->inCheck() || move.see > -256)
01237 return 0;
01238 const Square king = info.state->kingSquare(alt(move.player));
01239 if (move.move.capturePtype() != PTYPE_EMPTY
01240 && ! info.state->hasPieceOnStand<GOLD>(move.player)
01241 && ! info.state->hasPieceOnStand<SILVER>(move.player)
01242 && ! info.move_candidate_exists[alt(move.player)])
01243 return 0;
01244 if (! Neighboring8Direct::hasEffect
01245 (*info.state, move.move.ptypeO(), move.move.to(), king))
01246 return 0;
01247 if (hasSafeCapture(info.copy, move.move))
01248 return 0;
01249 int ptype_index = move.move.ptype()*PTYPE_SIZE;
01250 int capture_index = move.move.capturePtype();
01251 double sum = 0.0;
01252 sum += w[offset + ptype_index + capture_index];
01253 sum += w[offset + capture_index];
01254 sum += w[offset + ptype_index + PTYPE_EDGE];
01255 sum += w[offset + PTYPE_EDGE];
01256 return sum;
01257 }
01258 static bool hasSafeCapture(NumEffectState& state, Move);
01259 };
01260 class AttackKing8Long : public Feature
01261 {
01262 public:
01263 enum {
01264 DIM = PTYPE_SIZE*PTYPE_SIZE,
01265 };
01266 AttackKing8Long() : Feature("AttackKing8Long", DIM)
01267 {
01268 }
01269 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
01270 {
01271 const Move move = info.move;
01272 double sum = 0.0;
01273 BOOST_FOREACH(Piece piece, state.king8_long_pieces) {
01274 if (! state.state->hasEffectIf(move.ptypeO(), move.to(),
01275 piece.square()))
01276 continue;
01277 sum += w[offset + PTYPE_EMPTY*PTYPE_SIZE+piece.ptype()]
01278 + w[offset + move.ptype()*PTYPE_SIZE+PTYPE_EMPTY]
01279 + w[offset + move.ptype()*PTYPE_SIZE+piece.ptype()];
01280 if (state.pin_by_opposing_sliders.isMember(piece)
01281 && info.see < 0)
01282 sum += w[offset + PTYPE_EMPTY*PTYPE_SIZE+PTYPE_EDGE]*info.see/1024.0;
01283 }
01284 return sum;
01285 }
01286 };
01287 class OpposingPawn : public Feature
01288 {
01289 public:
01290 enum {
01291 DIM = 9 * 3 * 2,
01292 };
01293 OpposingPawn() : Feature("OpposingPawn", DIM)
01294 {
01295 }
01296 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
01297 {
01298 const Move move = info.move;
01299 if (move.ptype() != PAWN)
01300 return 0.0;
01301 const Square front = move.to() + Board_Table.getOffset(info.player, U);
01302 if (state.state->pieceAt(front).ptype() != PAWN)
01303 return 0.0;
01304 int king_x = abs(state.state->kingSquare(alt(info.player)).x() - front.x());
01305 int stand_pawn = state.state->countPiecesOnStand<PAWN>(info.player);
01306 if (move.isDrop())
01307 --stand_pawn;
01308 stand_pawn = std::min(2, stand_pawn);
01309 bool has_other = state.state->hasPieceOnStand<LANCE>(info.player)
01310 || state.state->hasPieceOnStand<KNIGHT>(info.player)
01311 || state.state->hasPieceOnStand<SILVER>(info.player)
01312 || state.state->hasPieceOnStand<GOLD>(info.player)
01313 || state.state->hasPieceOnStand<ROOK>(info.player)
01314 || state.state->hasPieceOnStand<BISHOP>(info.player);
01315 int index = (king_x * 3 + stand_pawn) * 2 + has_other;
01316 return w[offset + index];
01317 }
01318 };
01319
01320 class DropAfterOpposingPawn : public Feature
01321 {
01322 public:
01323 enum {
01324 DIM = 9 * 3 * 2 * 2,
01325 };
01326 DropAfterOpposingPawn() : Feature("DropAfterOpposingPawn", DIM)
01327 {
01328 }
01329 double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
01330 {
01331 const Move move = info.move;
01332 if (move.ptype() != PAWN || ! move.isDrop() || ! state.history->hasLastMove())
01333 return 0.0;
01334 int to_x = move.to().x();
01335 const Move last_move = state.history->lastMove();
01336 if (! last_move.isNormal() || last_move.isDrop()
01337 || last_move.to().x() != to_x
01338 || last_move.from().x() != to_x)
01339 return 0.0;
01340
01341 const Square front = move.to()+Board_Table.getOffset(info.player, U);
01342 if (! front.canPromote(info.player))
01343 return 0.0;
01344 int king_x = abs(state.state->kingSquare(alt(info.player)).x() - to_x);
01345 int stand_pawn = std::min(2, state.state->countPiecesOnStand<PAWN>(info.player)-1);
01346 bool has_other = state.state->hasPieceOnStand<LANCE>(info.player)
01347 || state.state->hasPieceOnStand<KNIGHT>(info.player)
01348 || state.state->hasPieceOnStand<SILVER>(info.player)
01349 || state.state->hasPieceOnStand<GOLD>(info.player)
01350 || state.state->hasPieceOnStand<ROOK>(info.player)
01351 || state.state->hasPieceOnStand<BISHOP>(info.player);
01352 bool follow_pawn_capture = last_move.capturePtype() == PAWN;
01353 int index = ((king_x * 3 + stand_pawn) * 2 + has_other) * 2 + follow_pawn_capture;
01354 return w[offset + index];
01355 }
01356 };
01357
01358 class CoverPawn : public Feature
01359 {
01360 public:
01361 enum {
01362 DIM = 9 * 2 * PTYPE_SIZE * PTYPE_SIZE
01363 };
01364 CoverPawn() : Feature("CoverPawn", DIM)
01365 {
01366 }
01367 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01368 {
01369 if (! si.history->hasLastMove() || si.state->inCheck()
01370 || mi.move.isCaptureOrPromotion())
01371 return 0.0;
01372 const Move last_move = si.history->lastMove();
01373 if (last_move.ptype() != PAWN)
01374 return 0.0;
01375 const Offset diff = Board_Table.getOffset(mi.player, U);
01376 const Square front = last_move.to()-diff, front2 = front-diff;
01377 if (si.state->pieceOnBoard(front).ptype() != PAWN
01378 || si.state->pieceAt(front2).isOnBoardByOwner(alt(mi.player)))
01379 return 0.0;
01380 const bool cover = si.state->hasEffectIf
01381 (mi.move.ptypeO(), mi.move.to(), front);
01382 const Ptype moved = cover ? mi.move.ptype() : PTYPE_EDGE,
01383 threatened = si.state->pieceAt(front2).ptype();
01384 const int a = std::min(2,si.state->countEffect(alt(mi.player), front) - 1);
01385 assert(a >= 0);
01386 const int b = std::min(2,si.state->countEffect(mi.player, front));
01387 const int ptype_index = moved*PTYPE_SIZE+threatened;
01388 const bool has_pawn = si.state->hasPieceOnStand(alt(mi.player),PAWN);
01389 const int pawn_index = PTYPE_SIZE*PTYPE_SIZE;
01390 const int effect_index = (a*3+b)*2*PTYPE_SIZE*PTYPE_SIZE
01391 + (has_pawn ? pawn_index : 0);
01392 assert(effect_index >= 0);
01393 return w[offset + threatened]
01394 + w[offset + ptype_index]
01395 + w[offset + effect_index + ptype_index];
01396 }
01397 };
01398 class SacrificeAttack : public Feature
01399 {
01400 public:
01401 enum {
01402 StandCount = 64,
01403 DIM = StandCount * PTYPE_SIZE * 2
01404 };
01405 SacrificeAttack() : Feature("SacrificeAttack", DIM)
01406 {
01407 }
01408 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01409 {
01410 const Square king = si.state->kingSquare(alt(mi.player));
01411 if (mi.see >= 0
01412 || (! Neighboring8Direct::hasEffect
01413 (*si.state, mi.move.ptypeO(), mi.move.to(), king)))
01414 return 0.0;
01415 assert(PieceStand::order[6] == PAWN);
01416 int stand = mi.standIndex(*si.state);
01417 int index = (stand * PTYPE_SIZE + mi.move.ptype()) * 2;
01418 double sum = w[offset + index];
01419 if (si.history->hasLastMove(2)) {
01420 Move my_last_move = si.history->lastMove(2);
01421 if (Neighboring8Direct::hasEffect
01422 (*si.state, my_last_move.ptypeO(), my_last_move.to(), king))
01423 sum += w[offset + index + 1];
01424 }
01425 return sum;
01426 }
01427 };
01428 class King5x5Ptype : public Feature
01429 {
01430 public:
01431 enum {
01432 ONE_DIM = 25 * 4 * PTYPE_SIZE * PTYPE_SIZE,
01433 DIM = 2 * ONE_DIM,
01434 };
01435 King5x5Ptype() : Feature("King5x5Ptype", DIM)
01436 {
01437 }
01438 static double addOne(Player king, Square center, const StateInfo& si, const MoveInfo& mi, int offset, const double *w)
01439 {
01440 const Square to = mi.move.to();
01441 int dx = center.x() - to.x();
01442 const int dy = center.y() - to.y();
01443 if (abs(dx) >= 3 || abs(dy) >= 3)
01444 return 0.0;
01445 if ((king == BLACK && center.x() > 5)
01446 || (king == WHITE && center.x() >= 5))
01447 dx = -dx;
01448 int sq_index = (dx+2)*5 + dy+2;
01449 bool a = mi.move.isDrop() ? si.state->hasEffectAt(mi.player, to)
01450 : si.state->countEffect(mi.player,to) >= 2;
01451 bool d = si.state->hasEffectAt(alt(mi.player), to);
01452 int index = (sq_index*4 + a*2+d) * PTYPE_SIZE*PTYPE_SIZE
01453 + mi.move.capturePtype()*PTYPE_SIZE + mi.move.ptype();
01454 return w[offset + index];
01455 }
01456 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01457 {
01458 return
01459 addOne(mi.player, si.state->kingSquare(mi.player), si, mi, offset, w)
01460 + addOne(alt(mi.player), si.state->kingSquare(alt(mi.player)), si, mi,
01461 offset+ONE_DIM, w);
01462 }
01463 };
01464 class KingBlockade : public Feature
01465 {
01466 public:
01467 enum {
01468 StandCount = SacrificeAttack::StandCount,
01469 BlockLastOne = 0, BlockFront = 1,
01470 BlockSideWide = 2, BlockSideOther = 3, BlockBack = 4,
01471 DIM = 5 * StandCount
01472 };
01473 KingBlockade() : Feature("KingBlockade", DIM)
01474 {
01475 }
01476 static bool blockAll(const King8Info& ki, Square king, Move move,
01477 const NumEffectState& state,
01478 const CArray<Direction,3>& directions)
01479 {
01480 int liberty = 0;
01481 BOOST_FOREACH(Direction d, directions) {
01482 if ((ki.liberty() & (1<<d)) == 0)
01483 continue;
01484 ++liberty;
01485 const Square sq = king + Board_Table.getOffset(alt(state.turn()), d);
01486 if (! state.hasEffectIf(move.ptypeO(), move.to(), sq))
01487 return false;
01488 }
01489 return liberty > 0;
01490 }
01491 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01492 {
01493 const NumEffectState& state = *si.state;
01494 const King8Info ki = si.king8Info(alt(mi.player));
01495 const Square king = state.kingSquare(alt(mi.player));
01496 if (ki.libertyCount() == 0
01497 || (! Neighboring8Direct::hasEffect
01498 (state, mi.move.ptypeO(), mi.move.to(), king)))
01499 return 0.0;
01500 int stand = mi.standIndex(state);
01501 offset += stand*5;
01502 double sum = 0.0;
01503 if (ki.libertyCount() == 1) {
01504 const Square sq = king
01505 + Board_Table.getOffset(alt(mi.player),
01506 (Direction)misc::BitOp::bsf(ki.liberty()));
01507 if (! state.hasEffectIf(mi.move.ptypeO(), mi.move.to(), sq))
01508 return 0.0;
01509 sum += w[offset+BlockLastOne];
01510
01511 }
01512 const CArray<Direction,3> front3 = {{ UL, U, UR }};
01513 if (blockAll(ki, king, mi.move, state, front3))
01514 sum += w[offset+BlockFront];
01515 const CArray<Direction,3> left3 = {{ UL, L, DL }};
01516 if (blockAll(ki, king, mi.move, state, left3)) {
01517 const bool wide = (mi.player== WHITE && king.x() < 5)
01518 || (mi.player== BLACK && king.x() > 5);
01519 sum += w[offset+(wide ? BlockSideWide : BlockSideOther)];
01520 }
01521 const CArray<Direction,3> right3 = {{ UR, R, DR }};
01522 if (blockAll(ki, king, mi.move, state, right3)) {
01523 const bool wide = (mi.player== BLACK && king.x() < 5)
01524 || (mi.player== WHITE && king.x() > 5);
01525 sum += w[offset+ (wide ? BlockSideWide : BlockSideOther)];
01526 }
01527 const CArray<Direction,3> back3 = {{ DL, D, DR }};
01528 if (blockAll(ki, king, mi.move, state, back3))
01529 sum += w[offset+BlockBack];
01530 return sum;
01531 }
01532 };
01533 class CoverFork : public Feature
01534 {
01535 public:
01536 enum {
01537 DIM = PTYPE_SIZE * PTYPE_SIZE * PTYPE_SIZE
01538 };
01539 CoverFork() : Feature("CoverFork", DIM)
01540 {
01541 }
01542 static bool defending(const NumEffectState& state, Move move, Square target)
01543 {
01544 if (state.countEffect(alt(state.turn()), target) > 1)
01545 return false;
01546 if (state.hasEffectIf(move.ptypeO(), move.to(), target))
01547 return true;
01548 Piece attacking = state.findCheapAttack(alt(state.turn()), target);
01549 Offset o = Board_Table.getShortOffsetNotKnight(Offset32(move.to(), target));
01550 if (o.zero()
01551 || ! Board_Table.isBetween(move.to(), attacking.square(), target))
01552 return false;
01553 return state.countEffect(state.turn(), move.to()) >= (1-move.isDrop());
01554 }
01555 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01556 {
01557 const NumEffectState& state = *si.state;
01558 PieceMask attacked = state.piecesOnBoard(mi.player)
01559 & state.effectedMask(alt(mi.player))
01560 & ~(state.effectedMask(mi.player));
01561 attacked.clearBit<PAWN>();
01562 offset += mi.move.ptype()*PTYPE_SIZE*PTYPE_SIZE;
01563 double sum = 0.0;
01564 while (attacked.any()) {
01565 Piece a = state.pieceOf(attacked.takeOneBit());
01566 if (! defending(state, mi.move, a.square()))
01567 continue;
01568 int index_a = a.ptype()*PTYPE_SIZE;
01569 PieceMask copy = attacked;
01570 while (copy.any()) {
01571 Piece b = state.pieceOf(copy.takeOneBit());
01572 if (! defending(state, mi.move, b.square()))
01573 continue;
01574 sum += w[offset];
01575 sum += w[offset+index_a];
01576 sum += w[offset+b.ptype()];
01577 sum += w[offset+index_a+b.ptype()];
01578 }
01579 }
01580 return sum;
01581 }
01582 };
01583 class ThreatmateByCapture : public Feature
01584 {
01585 public:
01586 enum {
01587 DIM = PTYPE_SIZE * PTYPE_SIZE
01588 };
01589 ThreatmateByCapture() : Feature("ThreatmateByCapture", DIM)
01590 {
01591 }
01592 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01593 {
01594 const Move move = mi.move;
01595 const Ptype captured = move.capturePtype();
01596 if (captured == PTYPE_EMPTY
01597 || (si.possible_threatmate_ptype
01598 & 1<<(captured-PTYPE_BASIC_MIN)) == 0)
01599 return 0.0;
01600 double sum = 0.0;
01601 sum += w[offset];
01602 if (mi.see < 0)
01603 sum += w[offset+1] * mi.see/1024.0;
01604 sum += w[offset+captured];
01605 sum += w[offset+move.ptype()*PTYPE_SIZE];
01606 sum += w[offset+move.ptype()*PTYPE_SIZE+captured];
01607 return sum;
01608 }
01609 };
01610
01611 class PromotionBySacrifice : public Feature
01612 {
01613 public:
01614 enum {
01615 DIM = 2*PTYPE_SIZE * PTYPE_SIZE
01616 };
01617 PromotionBySacrifice() : Feature("PromotionBySacrifice", DIM)
01618 {
01619 }
01620 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01621 {
01622 const Move move = mi.move;
01623 if (mi.see >= 0 || move.isDrop()
01624 || si.state->inCheck()
01625 || si.threatmate_move.isNormal())
01626 return 0.0;
01627 const NumEffectState& state = *si.state;
01628 mask_t m = state.longEffectAt(move.from(), state.turn());
01629 m &= ~mask_t::makeDirect(PtypeFuns<LANCE>::indexMask);
01630 double sum = 0.0;
01631 while (m.any()) {
01632 const Piece piece = state.pieceOf
01633 (m.takeOneBit() +PtypeFuns<LANCE>::indexNum*32);
01634 assert(piece.ptype() != LANCE);
01635 if (piece.isPromoted()
01636 || piece.square().canPromote(mi.player))
01637 continue;
01638 Offset o = Board_Table.getShortOffsetNotKnight(Offset32(move.from(), piece.square()));
01639 assert(! o.zero());
01640 bool can_promote = false;
01641 Square to = move.from()+o;
01642 if (to == move.to())
01643 continue;
01644 while (state[to].isEmpty()) {
01645 if (to.canPromote(mi.player)
01646 && ! state.hasEffectAt(alt(mi.player), to))
01647 can_promote = true;
01648 to += o;
01649 }
01650 assert(state[to] != piece);
01651 int index = 0;
01652 if (piece.ptype() == ROOK)
01653 index += PTYPE_SIZE*PTYPE_SIZE;
01654 index += move.ptype()*PTYPE_SIZE;
01655 if (to.canPromote(mi.player)
01656 && state[to].isOnBoardByOwner(alt(mi.player))
01657 && ! state.hasEffectAt(alt(mi.player), to)) {
01658 sum += w[offset];
01659 sum += w[offset+1]*mi.see/1024.0;
01660 if (mi.check)
01661 sum += w[offset+2];
01662 if (state.hasEffectAt(alt(mi.player), piece.square())) {
01663 sum += w[offset+3];
01664 if (mi.check)
01665 sum += w[offset+4];
01666 }
01667 if (state.hasEffectIf(piece.ptypeO(), to,
01668 state.kingSquare(alt(mi.player))))
01669 sum += w[offset+5];
01670 sum += w[offset + index + state[to].ptype()];
01671 }
01672 else if (can_promote) {
01673 sum += w[offset];
01674 sum += w[offset+1]*mi.see/1024.0;
01675 if (mi.check)
01676 sum += w[offset+2];
01677 if (state.hasEffectAt(alt(mi.player), piece.square())) {
01678 sum += w[offset+3];
01679 if (mi.check)
01680 sum += w[offset+4];
01681 }
01682 sum += w[offset + index];
01683 }
01684 }
01685 return sum;
01686 }
01687 };
01688
01689 class EscapeThreatened : public Feature
01690 {
01691 public:
01692 enum {
01693 DIM =(2*PTYPE_SIZE * PTYPE_SIZE) * 3 * PTYPE_SIZE
01694 };
01695 EscapeThreatened() : Feature("EscapeThreatened", DIM)
01696 {
01697 }
01698 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01699 {
01700 const NumEffectState& state = *si.state;
01701 const Move move = mi.move;
01702 const Piece target = si.threatened[mi.player];
01703 if (mi.see > 0 || mi.check || mi.open_check
01704 || ! move.isNormal()
01705 || ! target.isPiece() || state.inCheck()
01706 || si.threatmate_move.isNormal()
01707 || Neighboring8Direct::hasEffect
01708 (state, move.ptypeO(), move.to(), state.kingSquare(alt(mi.player))))
01709 return 0.0;
01710 const int t0 = target.ptype()*PTYPE_SIZE*2*PTYPE_SIZE*3;
01711 const int t1 = t0 + state.findCheapAttack(alt(mi.player), target.square()).ptype()*2*PTYPE_SIZE*3;
01712 const int t2 = t1 + state.hasEffectAt(mi.player, target.square())*PTYPE_SIZE*3;
01713 double sum = 0.0;
01714 if (! move.isDrop() && state[move.from()] == target) {
01715 sum += w[offset + t0];
01716 sum += w[offset + t1];
01717 sum += w[offset + t2];
01718 return sum;
01719 }
01720 if (state.hasEffectIf(move.ptypeO(), move.to(),
01721 target.square())) {
01722 if (move.isDrop()
01723 || ! state.hasEffectIf(move.oldPtypeO(), move.from(),
01724 target.square())) {
01725 sum += w[offset + t0 + move.ptype()];
01726 sum += w[offset + t1 + move.ptype()];
01727 sum += w[offset + t2 + move.ptype()];
01728 return sum;
01729 }
01730 }
01731
01732 offset += PTYPE_SIZE;
01733 if (move.isDrop())
01734 offset += PTYPE_SIZE;
01735 sum += w[offset + t0 + move.ptype()];
01736 sum += w[offset + t1 + move.ptype()];
01737 sum += w[offset + t2 + move.ptype()];
01738 return sum;
01739 }
01740 };
01741 class BookMove : public Feature
01742 {
01743 public:
01744 enum {
01745 DIM = 2
01746 };
01747 BookMove() : Feature("BookMove", DIM)
01748 {
01749 }
01750 double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
01751 {
01752 if (! si.bookmove[0].isNormal())
01753 return 0.0;
01754 if (mi.move == si.bookmove[0] || mi.move == si.bookmove[1])
01755 return w[offset];
01756 return w[offset+1];
01757 }
01758 };
01759 }
01760 }
01761 #endif
01762
01763
01764
01765