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