All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
move_probability/feature.h
Go to the documentation of this file.
1 /* feature.h
2  */
3 #ifndef OSL_MOVE_PROBABILITY_FEATURE_H
4 #define OSL_MOVE_PROBABILITY_FEATURE_H
5 
10 #include "osl/neighboring8.h"
11 #include <boost/foreach.hpp>
12 #include <string>
13 
14 namespace osl
15 {
16  namespace move_probability
17  {
18  class Feature
19  {
20  std::string my_name;
21  int dim;
22  public:
23  Feature(std::string n, size_t d) : my_name(n), dim(d)
24  {
25  assert(dim > 0);
26  }
27  virtual ~Feature();
28  std::string name() const { return my_name; }
29  virtual double match(const StateInfo&, const MoveInfo&, int offset, const double *) const=0;
30  size_t dimension() const { return dim; }
31 
32  static int classifyEffect9(const NumEffectState& state, Player player, Square to)
33  {
34  const int a = std::min(2, state.countEffect(player, to));
35  int d = std::min(2,state.countEffect(alt(player), to));
36  if (a>d)
37  d += AdditionalEffect::hasEffect(state, to, alt(player));
38  return a*3 + d;
39  }
40  };
41 
42  class CheckFeature : public Feature
43  {
44  public:
45  enum { CHECK_CLASS = 4, RELATIVE_Y_CLASS = 3 };
48  {
49  }
50  static int checkIndex(const MoveInfo& move)
51  {
52  if (move.open_check)
53  return 3;
54  return (move.see > 0) ? 0 : ((move.see == 0) ? 1 : 2);
55  }
56  static int sign(const NumEffectState& state, Move move,
57  Player player)
58  {
59  const Square king = state.kingSquare(alt(player));
60  int ry = (move.to().y() - king.y())*playerToMul(player);
61  if (ry == 0) return 0;
62  return ry > 0 ? 1 : -1;
63  }
64  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
65  {
66  if (! move.check && ! move.open_check)
67  return 0;
68  const Player player = move.player;
69  int index = sign(*state.state, move.move, player)+1;
70  index = (index*2 + move.move.isDrop())*PTYPE_SIZE
71  + move.move.ptype();
72  index = index*CHECK_CLASS + checkIndex(move);
73  return w[offset+index];
74  }
75  };
76  class TakeBackFeature : public Feature
77  {
78  public:
79  TakeBackFeature() : Feature("TakeBack", 3)
80  {
81  }
82  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
83  {
84  if (! state.history->hasLastMove()
85  || state.history->lastMove().to() != move.move.to())
86  return 0;
87  int match = 2;
88  if (move.see >= 0) {
89  --match;
90  if (state.history->hasLastMove(2)
91  && state.history->lastMove(2).to() == move.move.to())
92  --match;
93  }
94  return w[offset + match];
95  }
96  };
97  class SeeFeature : public Feature
98  {
99  public:
100  enum { SeeClass = 23, YClass = 3, ProgressClass = 8 };
101  SeeFeature() : Feature("SeeFeature", SeeClass*(3+ProgressClass))
102  {
103  }
104  static int seeIndex(int see)
105  {
106  int index = see / 128;
107  if (see > 0) {
108  index = std::min(10, index);
109  index += 12;
110  } else if (see == 0) {
111  index += 11;
112  } else {
113  index += 10;
114  index = std::max(0, index);
115  }
116  return index;
117  }
118  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
119  {
120  const int see_index = seeIndex(move.see);
121  const Player player = move.player;
122  const Square to = move.move.to();
123  const int promote_index = to.canPromote(player)
124  ? 1 : (to.canPromote(alt(player)) ? 2 : 0);
125  double sum = w[offset+see_index+promote_index*SeeClass];
126  int progress_index = YClass + state.progress8();
127  sum += w[offset+see_index+progress_index*SeeClass];
128  return sum;
129  }
130  };
131 
132  class CapturePtype : public Feature
133  {
134  public:
135  CapturePtype() : Feature("CapturePtype", PTYPE_SIZE*PTYPE_SIZE*2*3)
136  {
137  }
138  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
139  {
140  const Ptype captured = move.move.capturePtype();
141  const Player player = move.player;
142  int index = (move.move.ptype())*PTYPE_SIZE + captured;
144  if (captured != PTYPE_EMPTY
145  && captured == state.threatened[alt(player)].ptype())
146  ++index;
147  if (move.see > 0)
148  index += PTYPE_SIZE*PTYPE_SIZE;
149  if (captured != PTYPE_EMPTY)
150  index += std::min(2, state.state->countPiecesOnStand(player, unpromote(captured)))
151  * (2*PTYPE_SIZE*PTYPE_SIZE);
152  return w[offset+index];
153  }
154  };
155 
156  class ContinueCapture : public Feature
157  {
158  public:
159  ContinueCapture() : Feature("ContinueCapture", 1) {}
160  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
161  {
162  if (move.move.capturePtype() == PTYPE_EMPTY
163  || ! state.history->hasLastMove(2)
164  || state.history->lastMove(2).to() != move.move.from())
165  return 0;
166  return w[offset];
167  }
168  };
169 
171  class DropCaptured : public Feature
172  {
173  public:
174  DropCaptured() : Feature("DropCaptured", PTYPE_SIZE - PTYPE_BASIC_MIN) {}
175  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
176  {
177  if (! move.move.isDrop()
178  || ! state.history->hasLastMove(2)
179  || ! state.history->lastMove(2).isNormal()
180  || state.history->lastMove(2).capturePtype() != move.move.ptype())
181  return 0.0;
182  const size_t index = move.move.ptype() - PTYPE_BASIC_MIN;
183  assert(index < dimension());
184  return w[index + offset];
185  }
186  };
187 
188  class SquareY : public Feature
189  {
190  public:
191  // PTYPE_SIZE, to [1,9], fromto [-3,3], drop|promote, progress8
192  enum {
194  };
195  SquareY() : Feature("SquareY", DIM)
196  {
197  }
198  static int fromTo(Square to, Square from) // [-3,3]
199  {
200  return std::max(-3, std::min(to.y() - from.y(), 3));
201  }
202  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
203  {
204  const Move move = info.move;
205  const Player P = info.player;
206  const Square to = move.to().squareForBlack(P);
207  size_t index = ((to.y()-1)*PTYPE_SIZE + move.ptype())*16;
208  assert(index+16 <= dimension());
209  const int from_to = move.isDrop() ? 0
210  : fromTo(to, move.from().squareForBlack(P));
211  double sum = w[offset + index + from_to + 3];
212  if (move.isDrop() || move.isPromotion())
213  sum += w[offset + index + 7];
214  sum += w[offset + index + state.progress8()+8];
215  return sum;
216  }
217  };
218 
219  class SquareX : public Feature
220  {
221  public:
222  // PTYPE_SIZE, to [1,5], fromto [-3,3], drop|promote, progress8
223  SquareX() : Feature("SquareX", 5*PTYPE_SIZE*16)
224  {
225  }
226  static int fromTo(Square to, Square from) // [-3,3]
227  {
228  return std::max(-3, std::min(to.x() - from.x(), 3));
229  }
230  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
231  {
232  const Move move = info.move;
233  int to = move.to().x();
234  int from_to = move.isDrop() ? 0 : fromTo(move.to(), move.from());
235  if (to > 5) {
236  to = 10 - to;
237  from_to = -from_to;
238  }
239  size_t index = ((to-1)*PTYPE_SIZE + move.ptype())*16;
240  assert(index+16 <= dimension());
241  double sum = w[offset + index + from_to + 3];
242  if (move.isDrop() || move.isPromotion())
243  sum += w[offset + index + 7];
244  sum += w[offset + index + state.progress8()+8];
245  return sum;
246  }
247  };
248 
249  class KingRelativeY : public Feature
250  {
251  public:
252  // PTYPE_SIZE, to [-8,8], fromto [-3,3], drop|promote, progress8
253  enum { ONE_DIM = 17*PTYPE_SIZE*16 };
254  KingRelativeY() : Feature("KingRelativeY", ONE_DIM*2)
255  {
256  }
257  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
258  {
259  const Move move = info.move;
260  const Player P = info.player;
261  const Ptype ptype = move.ptype();
262 
263  const Square to = move.to().squareForBlack(P);
264  const Square my_king = state.state->kingSquare(P).squareForBlack(P);
265  const Square op_king = state.state->kingSquare(alt(P)).squareForBlack(P);
266  const int from_to = move.isDrop() ? 0
267  : SquareY::fromTo(to, move.from().squareForBlack(P));
268 
269  size_t index = ((to.y()-my_king.y()+8)*PTYPE_SIZE + ptype)*16;
270  assert(index+16 <= ONE_DIM);
271  double sum = w[offset + index + from_to + 3];
272  if (move.isDrop() || move.isPromotion())
273  sum += w[offset + index + 7];
274  sum += w[offset + index + state.progress8()+8];
275 
276  index = ((to.y()-op_king.y()+8)*PTYPE_SIZE + ptype)*16;
277  assert(index+16 <= ONE_DIM);
278  sum += w[offset + ONE_DIM + index + from_to + 3];
279  if (move.isDrop() || move.isPromotion())
280  sum += w[offset + ONE_DIM + index + 7];
281  sum += w[offset + ONE_DIM + index + state.progress8()+8];
282  return sum;
283  }
284  };
285  class KingRelativeX : public Feature
286  {
287  public:
288  // PTYPE_SIZE, to [0,8], fromto [-3,3], drop|promote, progress8
289  enum { ONE_DIM = 9*PTYPE_SIZE*16 };
290  KingRelativeX() : Feature("KingRelativeX", ONE_DIM*2)
291  {
292  }
293  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
294  {
295  const Move move = info.move;
296  const Player P = info.player;
297  const Ptype ptype = move.ptype();
298 
299  const Square to = move.to();
300  const Square my_king = state.state->kingSquare(P);
301  const Square op_king = state.state->kingSquare(alt(P));
302  const int from_to = move.isDrop() ? 0
303  : SquareY::fromTo(to, move.from());
304  int dx = to.x() - my_king.x(), fx = from_to;
305  if (dx < 0) {
306  dx = -dx;
307  fx = -fx;
308  }
309  size_t index = (dx*PTYPE_SIZE + ptype)*16;
310  assert(index+16 <= ONE_DIM);
311  double sum = w[offset + index + fx + 3];
312  if (move.isDrop() || move.isPromotion())
313  sum += w[offset + index + 7];
314  sum += w[offset + index + state.progress8()+8];
315 
316  dx = to.x() - op_king.x(), fx = from_to;
317  if (dx < 0) {
318  dx = -dx;
319  fx = -fx;
320  }
321  index = (dx*PTYPE_SIZE + ptype)*16;
322  assert(index+16 <= ONE_DIM);
323  sum += w[offset + ONE_DIM + index + fx + 3];
324  if (move.isDrop() || move.isPromotion())
325  sum += w[offset + ONE_DIM + index + 7];
326  sum += w[offset + ONE_DIM + index + state.progress8()+8];
327  return sum;
328  }
329  };
330 
331  class FromEffect : public Feature
332  {
333  public:
335  {
336  }
337  double match(const StateInfo& state_info, const MoveInfo& info, int offset, const double *w) const
338  {
339  const Move move = info.move;
340  if (move.isDrop())
341  return 0;
342  const NumEffectState& state = *state_info.state;
343  const Ptype me = move.oldPtype();
344  const Ptype support = state.findCheapAttack(info.player, move.from()).ptype();
345  const Ptype attack = state.findCheapAttack(alt(info.player), move.from()).ptype();
346  const size_t index = (((me * PTYPE_SIZE) + support) * PTYPE_SIZE) + attack;
347  assert(index < dimension());
348  return w[index + offset];
349  }
350  };
351 
352  class ToEffect : public Feature
353  {
354  public:
356  {
357  }
358  static const Piece find(const NumEffectState& state,
359  Square to, const PieceMask& remove,
360  Player player)
361  {
362  assert(to.isOnBoard());
363  PieceMask pieces = state.piecesOnBoard(player)
364  & state.effectSetAt(to);
365  pieces &= ~remove;
366  return state.selectCheapPiece(pieces);
367  }
368  static void supportAttack(const NumEffectState& state,
369  Square to,
370  const PieceMask& my_pin,
371  const PieceMask& op_pin,
372  Player turn,
373  std::pair<Ptype,Ptype>& out)
374  {
375  out.first = find(state, to, my_pin, turn).ptype(); // support
376  out.second = find(state, to, op_pin, alt(turn)).ptype(); // attack
377  }
378  static void supportAttack(const StateInfo& info, Square to, Move move,
379  std::pair<Ptype,Ptype>& out)
380  {
381  const Player turn = info.state->turn();
382  if (move.isDrop())
383  return supportAttack(*(info.state), to, info.pin[turn],
384  info.pin[alt(turn)], turn, out);
385  PieceMask my_pin = info.pin[turn];
386  my_pin.set(info.state->pieceAt(move.from()).number());
387  supportAttack(*(info.state), to, my_pin, info.pin[alt(turn)],
388  turn, out);
389  }
390  static size_t supportAttack(const StateInfo& info, Square to, Move move)
391  {
392  std::pair<Ptype,Ptype> pair;
393  supportAttack(info, to, move, pair);
394  return pair.first * PTYPE_SIZE + pair.second;
395  }
396  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
397  {
398  const Move move = info.move;
399  const Ptype me = move.ptype(), captured = move.capturePtype();
400  const size_t position_index = supportAttack(state, move.to(), move);
401  const size_t index = ((me * PTYPE_SIZE) + captured)
402  * PTYPE_SIZE * PTYPE_SIZE + position_index;
403  assert(index < dimension());
404  return w[index + offset];
405  }
406  };
407 
408  class FromEffectLong : public Feature
409  {
410  public:
411  FromEffectLong() : Feature("FromEffectLong", PTYPE_SIZE*8*8)
412  {
413  }
414  double match(const StateInfo& state_info, const MoveInfo& info, int offset, const double *w) const
415  {
416  const Move move = info.move;
417  if (move.isDrop())
418  return 0;
419  const NumEffectState& state = *state_info.state;
420  const Ptype ptype = move.oldPtype();
421  const CArray<bool,3> me = {{
422  state.longEffectAt<LANCE>(move.from(), info.player).any(),
423  state.longEffectAt<BISHOP>(move.from(), info.player).any(),
424  state.longEffectAt<ROOK>(move.from(), info.player).any(),
425  }};
426  const CArray<bool,3> op = {{
427  state.longEffectAt<LANCE>(move.from(), alt(info.player)).any(),
428  state.longEffectAt<BISHOP>(move.from(), alt(info.player)).any(),
429  state.longEffectAt<ROOK>(move.from(), alt(info.player)).any(),
430  }};
431  size_t index = ptype;
432  for (int i=0; i<3; ++i) {
433  index *= 2; index += me[i];
434  index *= 2; index += op[i];
435  }
436  assert(index < dimension());
437  return w[index + offset];
438  }
439  };
440 
441  class ToEffectLong : public Feature
442  {
443  public:
444  ToEffectLong() : Feature("ToEffectLong", PTYPE_SIZE*8*8)
445  {
446  }
447  double match(const StateInfo& state_info, const MoveInfo& info, int offset, const double *w) const
448  {
449  const Move move = info.move;
450  const NumEffectState& state = *state_info.state;
451  const Ptype ptype = move.oldPtype();
452  NumBitmapEffect effect=state.effectSetAt(move.to());
453  if (! move.isDrop())
454  effect.reset(state.pieceOnBoard(move.from()).number()+8);
455  const CArray<mask_t,3> pieces = {{
456  effect.selectLong<LANCE>() >> 8,
457  effect.selectLong<BISHOP>() >> 8,
458  effect.selectLong<ROOK>() >> 8
459  }};
460  size_t index = ptype;
461  for (int i=0; i<3; ++i) {
462  index *= 2;
463  index += (pieces[i] & state.piecesOnBoard(info.player).getMask(1)).any();
464  index *= 2;
465  index += (pieces[i] & state.piecesOnBoard(alt(info.player)).getMask(1)).any();
466  }
467  assert(index < dimension());
468  return w[index + offset];
469  }
470  };
471 
472  class PatternCommon : public Feature
473  {
474  public:
475  enum {
476  SupportSize = PTYPE_SIZE,
477  AttackSize = PTYPE_SIZE, AttackBase = SupportSize, // 32
478  EffectSize = 9, EffectBase = AttackBase+AttackSize,
479  OpKingSize = 4, OpKingBase = EffectBase+EffectSize,
480  MyKingSize = 3, MyKingBase = OpKingBase+OpKingSize, // 48
481  PromotionSize = 2, PromotionBase = MyKingBase+MyKingSize,
482  PinOpenSize = 4, PinOpenBase = PromotionBase + PromotionSize,
483  LastToSize = 4, LastToBase = PinOpenBase + PinOpenSize,
484  LastEffectChangedSize = 6, LastEffectChangedBase = LastToBase + LastToSize,
485  SquareDim = LastEffectChangedBase + LastEffectChangedSize, // 64
486  PatternCacheSize = PTYPEO_SIZE*SquareDim,
487  OneDim = PTYPE_SIZE*PatternCacheSize,
488  };
489  PatternCommon(const std::string& name, int dim) : Feature(name, dim)
490  {
491  }
492  double addOne(const StateInfo& state, int offset,
493  const double *w, Square position) const
494  {
495  if (! position.isOnBoardRegion() || state.state->pieceAt(position).isEdge()) {
496  size_t basic = ptypeOIndex(PTYPEO_EDGE) *SquareDim;
497  return w[offset + basic];
498  }
499  const StateInfo::pattern_square_t& cache
500  = state.pattern_cache[position.index()];
501  double sum = 0.0;
502  for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i)
503  sum += w[offset + cache[i]];
504  return sum;
505  }
506  static void updateCache(StateInfo& info);
507  private:
508  static void updateCacheOne(Square target, StateInfo& info);
509  };
510 
511  template<bool TestPromotable>
512  class PatternBase : public PatternCommon
513  {
514  int dx, black_dy;
515  public:
516  enum {
517  PromotionSize = TestPromotable ? 3 : 1,
518  DIM = PromotionSize * OneDim,
519  };
520  PatternBase(int x, int y)
521  : PatternCommon(name(x,y), DIM), dx(x), black_dy(y)
522  {
523  }
524  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
525  {
526  const Move move = info.move;
527  const Ptype ptype = move.ptype();
528  int basic = ptype*PatternCacheSize;
529  int basic_from = (move.isPromotion() ? PTYPE_EDGE : PTYPE_EMPTY)
530  *PatternCacheSize;
531  const Square to = move.to();
532  if (TestPromotable && to.canPromote(info.player))
533  offset += OneDim;
534  else if (TestPromotable && to.canPromote(alt(info.player)))
535  offset += 2*OneDim;
536  int dy = info.player == BLACK ? black_dy : -black_dy;
537  Square target = to + Offset(dx, dy);
538  double sum = 0.0;
539  if (move.from() != target)
540  sum += addOne(state, offset+basic, w, target);
541  else // move from here
542  sum += addOne(state, offset+basic_from, w, target);
543  if (dx == 0)
544  return sum;
545  target = to + Offset(-dx, dy);
546  if (move.from() != target)
547  sum += addOne(state, offset+basic, w, target);
548  else
549  sum += addOne(state, offset+basic_from, w, target);
550  return sum;
551  }
552  static std::string name(int x, int y)
553  {
554  return std::string("Pattern")
555  + (TestPromotable ? "P" : "")
556  + "X" + (char)('2'+x) + "Y"+(char)('2'+y);
557  }
558  };
559 
562 
564  {
565  public:
566  MoveFromOpposingSliders() : Feature("MoveFromOpposingSliders", 36*PTYPE_SIZE)
567  {
568  }
569  static int longPtype(const NumEffectState& state, Square position, Player player)
570  {
571  const int offset = PtypeFuns<LANCE>::indexNum*32;
572  mask_t p = state.longEffectAt<LANCE>(position, player);
573  if (p.any())
574  return state.hasEffectAt(player, state.pieceOf(p.takeOneBit()+offset).square());
575  p = state.longEffectAt<BISHOP>(position, player);
576  if (p.any())
577  return 2 + state.hasEffectAt(player, state.pieceOf(p.takeOneBit()+offset).square());
578  p = state.longEffectAt<ROOK>(position, player);
579  assert(p.any());
580  return 4 + state.hasEffectAt(player, state.pieceOf(p.takeOneBit()+offset).square());
581  }
582  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
583  {
584  const Square from = move.move.from();
585  if (from.isPieceStand()
586  || ! state.pinByOpposingSliders(state.state->pieceOnBoard(from)))
587  return 0.0;
588  const int me = longPtype(*state.state, from, move.player);
589  const int op = longPtype(*state.state, from, alt(move.player));
590  return w[offset + (me*6+op)*PTYPE_SIZE+move.move.ptype()];
591  }
592  };
594  {
595  public:
596  AttackFromOpposingSliders() : Feature("AttackFromOpposingSliders", PTYPE_SIZE*PTYPE_SIZE*2)
597  {
598  }
599  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
600  {
601  const Move move = info.move;
602  const Piece attack = state.state->findCheapAttack(alt(info.player), move.to());
603  if (! state.pinByOpposingSliders(attack))
604  return 0.0;
605  if (state.state->countEffect(alt(info.player), move.to()) == 1)
606  offset += PTYPE_SIZE*PTYPE_SIZE;
607  double sum = w[offset + PTYPE_EMPTY*PTYPE_SIZE+attack.ptype()]
608  + w[offset + move.ptype()*PTYPE_SIZE+PTYPE_EMPTY]
609  + w[offset + move.ptype()*PTYPE_SIZE+attack.ptype()];
610  if (info.see < 0)
611  sum += w[offset + PTYPE_EMPTY*PTYPE_SIZE+PTYPE_EMPTY]*info.see/1024.0;
612  return sum;
613  }
614  };
616  {
617  public:
618  AttackToOpposingSliders() : Feature("AttackToOpposingSliders", PTYPE_SIZE*PTYPE_SIZE*2)
619  {
620  }
621  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
622  {
623  const Move move = info.move;
624  double sum = 0.0;
625  BOOST_FOREACH(Piece piece, state.pin_by_opposing_sliders) {
626  if (! state.state->hasEffectIf(move.ptypeO(), move.to(),
627  piece.square()))
628  continue;
629  int base = (piece.owner() == info.player) ? PTYPE_SIZE*PTYPE_SIZE : 0;
630  sum += w[offset + base + PTYPE_EMPTY*PTYPE_SIZE+piece.ptype()]
631  + w[offset + base + move.ptype()*PTYPE_SIZE+PTYPE_EMPTY]
632  + w[offset + base + move.ptype()*PTYPE_SIZE+piece.ptype()];
633  if (info.see < 0)
634  sum += w[offset + base + PTYPE_EMPTY*PTYPE_SIZE+PTYPE_EMPTY]*info.see/1024.0;
635  }
636  return sum;
637  }
638  };
639  class PawnAttack : public Feature
640  {
641  public:
642  enum {
643  PTYPE2_DIM = PTYPE_SIZE*2*PTYPE_SIZE*2*2,
644  EFFECT_DIM = PTYPE_SIZE*2*8*9,
645  BasicSize = PTYPE2_DIM+EFFECT_DIM,
646  PawnSize = BasicSize*3,
647  DIM = PawnSize*2
648  };
649  PawnAttack() : Feature("PawnAttack", DIM)
650  {
651  }
652  std::pair<int,int> squareStatus(const NumEffectState& state, Player player, Square to, Square& front) const
653  {
654  int u = 0, uu = 0;
655  const int dy = (player == BLACK) ? -1 : 1;
656  Square position = to + Offset(0, dy);
657  front = position;
658  Piece piece = state.pieceAt(position);
659  if (piece.isPiece())
660  u = piece.ptype() + ((piece.owner() == player) ? PTYPE_SIZE : 0);
661  assert(! piece.isEdge()); // pawn move
662  piece = state.pieceAt(position + Offset(0, dy));
663  if (piece.isPiece())
664  uu = piece.ptype() + ((piece.owner() == player) ? PTYPE_SIZE : 0);
665  return std::make_pair(u, uu);
666  }
667  double matchPtype(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
668  {
669  const Player player = move.player;
670  const Square to = move.move.to();
671 
672  Square front;
673  const std::pair<int,int> u = squareStatus(*state.state, player, to, front);
674 
675  int promotion = 0;
676  if (front.canPromote(player))
677  promotion = 1;
678  else if (front.canPromote(alt(player)))
679  promotion = 2;
680  offset += BasicSize*promotion;
681 
682  bool pawn_drop = move.move.isDrop();
683  const int index0 = (u.first*PTYPE_SIZE*2+u.second)*2 + pawn_drop;
684  double sum = w[offset + index0];
685 
686  const int effect = classifyEffect9(*state.state, player, front);
687  const int index1 = u.first*8 + move.move.to().squareForBlack(player).y()-2;
688  sum += w[offset + index1*9+effect];
689  return sum;
690  }
691  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
692  {
693  if (move.move.ptype() == PAWN)
694  return matchPtype(state, move, offset, w);
695  if (move.move.ptype() == LANCE
696  && state.state->canDropPawnTo(move.player, move.move.to().x()))
697  return matchPtype(state, move, offset+PawnSize, w);
698  return 0.0;
699  }
700  };
701 
702  class BlockLong : public Feature
703  {
704  public:
705  enum {
706  AttackPtype = 8,
707  BasicAttack = AttackPtype*osl::PTYPEO_SIZE,
708  OptionSize = 8, // King8, HasSupport, Promotable, Shadowing,...
709  LongAttackSize = BasicAttack * OptionSize,
710  DIM = PTYPE_SIZE * 2 * LongAttackSize
711  };
712  BlockLong() : Feature("BlockLong", DIM)
713  {
714  }
715  static int longAttackIndex(osl::PtypeO ptypeo) // [0,7]?
716  {
717  const Ptype ptype = getPtype(ptypeo);
718  int index;
719  if (ptype == LANCE) index = 0;
720  else if (ptype == BISHOP) index = 1;
721  else if (ptype == ROOK) index = 2;
722  else {
723  assert(ptype == PROOK || ptype == PBISHOP);
724  index = 3;
725  }
726  if (getOwner(ptypeo) == WHITE) index += 4;
727  return index;
728  }
729  static double addPiece(const StateInfo& state, Piece piece,
730  Square to, const double *w, int offset)
731  {
732  assert(state.state->hasEffectByPiece(piece, to));
733  const Direction d
734  = Board_Table.getLongDirection<BLACK>(piece.square(), to);
736  cache = state.long_attack_cache[piece.number()][longToShort(d)];
737  double sum = 0.0;
738  BOOST_FOREACH(int index, cache) {
739  assert(index < LongAttackSize);
740  sum += w[index+offset];
741  }
742  return sum;
743  }
744  static int ptypeSupport(Ptype moved, bool has_support)
745  {
746  return (moved*2 + has_support) * LongAttackSize;
747  }
748  static double findAll(const StateInfo& state, Player P,
749  Square target, const double *w, int offset)
750  {
751  mask_t m = state.state->longEffectAt(target, P);
752  double sum = 0.0;
753  while (m.any()) {
754  const Piece piece = state.state->pieceOf
755  (m.takeOneBit() +PtypeFuns<LANCE>::indexNum*32);
756  sum += addPiece(state, piece, target, w, offset);
757  }
758  m = state.state->longEffectAt(target, alt(P));
759  while (m.any()) {
760  const Piece piece = state.state->pieceOf
761  (m.takeOneBit()+PtypeFuns<LANCE>::indexNum*32);
762  sum += addPiece(state, piece, target, w, offset);
763  }
764  return sum;
765  }
766  static double findAll(const StateInfo& state, Move move, const double *w, int offset=0)
767  {
768  const Player P = move.player();
769  Square target = move.to();
770  int a = state.state->countEffect(P, target);
771  offset += ptypeSupport(move.ptype(), a+move.isDrop()>1);
772  return findAll(state, P, target, w, offset);
773  }
774  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
775  {
776  return findAll(state, move.move, w, offset);
777  }
778  static void updateCache(StateInfo&);
779  private:
780  static void makeLongAttackOne(StateInfo& info,
781  Piece piece, Direction d);
782  };
783  class BlockLongFrom : public Feature
784  {
785  public:
786  enum {
788  };
789  BlockLongFrom() : Feature("BlockLongFrom", DIM)
790  {
791  }
792  static double findAll(const StateInfo& state, Move move, const double *w, int offset=0)
793  {
794  const Player P = move.player();
795  Square target = move.from();
796  return BlockLong::findAll(state, P, target, w, offset);
797  }
798  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
799  {
800  if (move.move.isDrop())
801  return 0;
802  return findAll(state, move.move, w, offset);
803  }
804  };
805  class LongRecapture : public Feature
806  {
807  public:
808  enum {
810  };
811  LongRecapture() : Feature("LongRecapture", DIM)
812  {
813  }
814  double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
815  {
816  if (move.see >= 0)
817  return 0.0;
818  const NumEffectState& state = *info.state;
819  const Square to = move.move.to();
820  int a = state.countEffect(move.player, to)+move.move.isDrop()-1;
821  int d = state.countEffect(alt(move.player), to);
822  if (d == 1
823  || (d == 2 && a > 0
824  && state.hasEffectByPiece(state.kingPiece(alt(move.player)), to))) {
825  double sum = w[offset + (PTYPE_EMPTY)*BlockLong::LongAttackSize]
826  *move.see/1024.0;
827  offset += move.move.ptype() * BlockLong::LongAttackSize;
828  const Piece opponent = state.findCheapAttack(alt(move.player), to);
829  sum += BlockLong::findAll(info, move.player, opponent.square(), w, offset);
830  return sum;
831  }
832  return 0.0;
833  }
834  };
835  class AddEffectLong : public Feature
836  {
837  public:
838  enum {
840  };
841  AddEffectLong() : Feature("AddEffectLong", DIM)
842  {
843  }
844  static double addOne(Direction dir, const StateInfo& state, const MoveInfo& move, int offset, const double *w)
845  {
846  Offset diff = Board_Table.getOffset(move.player, dir);
847  Square to = move.move.to() + diff;
848  if (isLong(dir)) {
849  while (state.state->pieceAt(to).isEmpty())
850  to += diff;
851  }
852  if (! state.state->pieceAt(to).isPiece())
853  return 0.0;
854  return BlockLong::findAll(state, move.player, to, w, offset);
855  }
856  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
857  {
858  offset += move.move.ptype()*BlockLong::LongAttackSize;
859  unsigned int directions = Ptype_Table.getMoveMask(move.move.ptype());
860  double sum = 0.0;
861  do {
862  Direction d = (Direction)(misc::BitOp::bsf(directions));
863  directions &= directions-1;
864  sum += addOne(d, state, move, offset, w);
865  } while (directions);
866  return sum;
867  }
868  };
869  class LanceAttack : public Feature
870  {
871  public:
872  enum {
874  DIM = PatternCacheSize*(8+4+4+1)
875  };
876  LanceAttack() : Feature("LanceAttack", DIM)
877  {
878  }
879  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
880  {
881  if (move.move.ptype() != LANCE
882  || (! move.move.isDrop()
883  && move.move.capturePtype() == PTYPE_EMPTY))
884  return 0;
885  const Offset up = Board_Table.getOffset(move.player, U);
886  Square target = move.move.to() + up;
887  while (state.state->pieceAt(target).isEmpty())
888  target += up;
889  if (state.state->pieceAt(target).isOnBoardByOwner(move.player)) {
890  target += up;
891  if (state.state->pieceAt(target).ptype() == LANCE) {
892  while (state.state->pieceAt(target).isEmpty())
893  target += up;
894  }
895  }
896  if (state.state->pieceAt(target).isEdge())
897  target -= up;
898 
899  int y = move.move.to().y(), x = move.move.to().x();
900  if (move.player == WHITE)
901  y = 10-y;
902  y -= 2;
903  int dx1 = abs(state.state->kingSquare(move.player).x()-x);
904  int dx2 = abs(state.state->kingSquare(alt(move.player)).x()-x);
905  dx1 = std::min(dx1, 3);
906  dx2 = std::min(dx2, 3);
907  bool pawn = state.state->canDropPawnTo(alt(move.player), x);
908  assert(! state.state->pieceAt(target).isEdge());
909  const StateInfo::pattern_square_t& cache
910  = state.pattern_cache[target.index()];
911  double sum = 0.0;
912  for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
913  sum += w[offset + y*PatternCacheSize + cache[i]];
914  sum += w[offset + (8+dx1)*PatternCacheSize + cache[i]];
915  sum += w[offset + (12+dx1)*PatternCacheSize + cache[i]];
916  if (! pawn)
917  sum += w[offset + 16*PatternCacheSize + cache[i]];
918  }
919  return sum;
920  }
921  };
922  class BishopAttack : public Feature
923  {
924  public:
925  enum {
927  DIM = PatternCacheSize*2 // square:2
928  };
929  BishopAttack() : Feature("BishopAttack", DIM)
930  {
931  }
932  static double addSquare(Square target,
933  const StateInfo& info,
934  int offset, const double *w)
935  {
936  int type = 0;
937  const StateInfo::pattern_square_t& cache
938  = info.pattern_cache[target.index()];
939  double sum = 0.0;
940  for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
941  sum += w[offset + type + cache[i]];
942  }
943  return sum;
944  }
945  template <Direction D,Ptype Type>
946  static
947  double addOne(const StateInfo& info, Square to,
948  int offset, const double *w)
949  {
950  const NumEffectState& state = *info.state;
952  Square target = to + diff;
953  double sum = 0.0;
954  if (state.pieceAt(target).isEdge())
955  return sum;
956  if (state.pieceAt(target).isPiece()
957  && (state.pieceAt(target).ptype() != Type
958  || state.pieceAt(target).owner() != state.turn())) {
959  ; // almost included in direct pattern
960  } else {
961  while (state.pieceAt(target).isEmpty())
962  target += diff;
963  if (state.pieceAt(target).ptype() == Type
964  && state.pieceAt(target).owner() == state.turn()) {
965  // extend additional effect by the same ptype
966  target += diff;
967  while (state.pieceAt(target).isEmpty())
968  target += diff;
969  }
970  if (state.pieceAt(target).isEdge())
971  target -= diff;
972  sum += addSquare(target, info, offset, w);
973  if (! state.pieceAt(target).isPiece())
974  return sum;
975  }
976  // shadowing
977  target += diff;
978  if (state.pieceAt(target).isEdge())
979  return sum;
980  while (state.pieceAt(target).isEmpty())
981  target += diff;
982  if (state.pieceAt(target).isEdge())
983  target -= diff;
984  sum += addSquare(target, info, offset+PatternCacheSize, w);
985  return sum;
986  }
987  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
988  {
989  if (unpromote(move.move.ptype()) != BISHOP)
990  return 0;
991  double sum = 0.0;
992  sum += addOne<UR,BISHOP>(state, move.move.to(), offset, w);
993  sum += addOne<UL,BISHOP>(state, move.move.to(), offset, w);
994  sum += addOne<DR,BISHOP>(state, move.move.to(), offset, w);
995  sum += addOne<DL,BISHOP>(state, move.move.to(), offset, w);
996  return sum;
997  }
998  };
999  class RookAttack : public Feature
1000  {
1001  public:
1002  enum {
1003  DirectionSize = BishopAttack::DIM,
1004  DIM = DirectionSize*3
1005  };
1006  RookAttack() : Feature("RookAttack", DIM)
1007  {
1008  }
1009  double match(const StateInfo& state, const MoveInfo& move, int offset, const double *w) const
1010  {
1011  if (unpromote(move.move.ptype()) != ROOK)
1012  return 0;
1013  const Square to = move.move.to();
1014  double sum = 0.0;
1015  sum += BishopAttack::addOne<R,ROOK>(state, to, offset, w);
1016  sum += BishopAttack::addOne<L,ROOK>(state, to, offset, w);
1017  const bool pawn_drop = state.state->canDropPawnTo(alt(move.player), to.x());
1018  const int scale = pawn_drop ? 1 : 2;
1019  sum += BishopAttack::addOne<U,ROOK>(state, to, offset+DirectionSize*scale, w);
1020  sum += BishopAttack::addOne<D,ROOK>(state, to, offset+DirectionSize*scale, w);
1021  return sum;
1022  }
1023  };
1024  class BreakThreatmate : public Feature
1025  {
1026  public:
1027  enum {
1029  AddEffectSize = PTYPE_SIZE * PatternCacheSize,
1030  OpenRoadSize = PTYPE_SIZE * PatternCacheSize, OpenRoadBase = AddEffectSize,
1031  KingMoveSize = PatternCacheSize, KingMoveBase = OpenRoadBase + OpenRoadSize,
1032  CaptureSize = PTYPE_SIZE*PTYPE_SIZE, CaptureBase = KingMoveBase + KingMoveSize,
1033  AddEffect8Size = PTYPE_SIZE*PatternCacheSize, AddEffect8Base = CaptureBase + CaptureSize,
1034  OtherMoveSize = 1, OtherMoveBase = AddEffect8Base + AddEffect8Size, // should be penalized
1035  DIM = OtherMoveBase + OtherMoveSize
1036  };
1037  BreakThreatmate() : Feature("BreakThreatmate", DIM)
1038  {
1039  }
1040  static bool isKingMove(Move move)
1041  {
1042  return move.ptype() == KING;
1043  }
1044  static bool isOpeningKingRoad(Move move, Square king)
1045  {
1046  return ! move.isDrop()
1047  && Neighboring8::isNeighboring8(move.from(), king);
1048  }
1049  static bool isDefendingThreatmate(Move move, Move threatmate,
1050  const NumEffectState& state)
1051  {
1052  if (move.to() == threatmate.to()
1053  || state.hasEffectIf(move.ptypeO(), move.to(),
1054  threatmate.to()))
1055  return true;
1056  if (threatmate.isDrop())
1057  return false;
1058  Offset32 offset32=Offset32(threatmate.from(),move.to());
1059  EffectContent effect=Ptype_Table.getEffect(move.ptypeO(),offset32);
1060  if (! effect.hasEffect())
1061  return false;
1062  if (effect.offset() == threatmate.to()-threatmate.from())
1063  return state.isEmptyBetween(threatmate.from(), move.to());
1064  return false;
1065  }
1066  static bool isDefendingKing8(Move move, Square king,
1067  const NumEffectState& state)
1068  {
1070  (state, move.ptypeO(), move.to(), king))
1071  return true;
1072  mask_t m = state.longEffectAt(move.to(), alt(state.turn()));
1073  while (m.any()) {
1074  const Piece piece = state.pieceOf
1075  (m.takeOneBit() +PtypeFuns<LANCE>::indexNum*32);
1077  (state, piece.ptypeO(), piece.square(), king))
1078  return true;
1079  }
1080  return false;
1081  }
1082  double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
1083  {
1084  if (! info.threatmate_move.isNormal())
1085  return 0;
1086  const NumEffectState& state = *info.state;
1087  const StateInfo::pattern_square_t& cache
1088  = info.pattern_cache[move.move.to().index()];
1089  const int PatternAny = ptypeOIndex(PTYPEO_EDGE)*PatternCommon::SquareDim;
1090  double sum = 0.0;
1091  if (isKingMove(move.move)) {
1092  // king move
1093  sum += w[offset + KingMoveBase + PatternAny];
1094  for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
1095  sum += w[offset + KingMoveBase + cache[i]];
1096  }
1097  } else {
1098  const Square king = state.kingSquare(move.player);
1099  if (isOpeningKingRoad(move.move, king)) {
1100  // open king road
1101  int base = OpenRoadBase + move.move.ptype()*PatternCacheSize;
1102  sum += w[offset + base + PatternAny];
1103  for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
1104  sum += w[offset + base + cache[i]];
1105  }
1106  }
1107  if (isDefendingThreatmate(move.move, info.threatmate_move,
1108  state)) {
1109  // add effect to threatmate-move-at
1110  int base = move.move.ptype()*PatternCacheSize;
1111  sum += w[offset + base + PatternAny];
1112  for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
1113  sum += w[offset + base + cache[i]];
1114  }
1115  } else if (isDefendingKing8(move.move, king, state)) {
1116  // add effect to king8
1117  int base = move.move.ptype()*PatternCacheSize
1118  + AddEffect8Base;
1119  sum += w[offset + base + PatternAny];
1120  for (size_t i=0; i<cache.size() && cache[i] >= 0; ++i) {
1121  sum += w[offset + base + cache[i]];
1122  }
1123  }
1124  const Piece captured = state.pieceOnBoard(move.move.to());
1125  // capture
1126  if (captured.isPiece()) {
1128  (state, captured.ptypeO(), captured.square(), king)) {
1129  sum += w[offset + CaptureBase
1130  + captured.ptype()*PTYPE_SIZE+move.move.ptype()];
1131  sum += w[offset + CaptureBase
1132  + captured.ptype()*PTYPE_SIZE];
1133  }
1134  else {
1135  sum += w[offset + CaptureBase + captured.ptype()*PTYPE_SIZE
1136  + PTYPE_EDGE];
1137  }
1138  }
1139  }
1140  if (sum == 0.0)
1141  sum += w[offset + OtherMoveBase];
1142  return sum;
1143  }
1144  };
1145 
1146  class SendOff : public Feature
1147  {
1148  public:
1149  enum {
1150  DIM = PTYPE_SIZE,
1151  };
1152  SendOff() : Feature("SendOff", DIM)
1153  {
1154  }
1155  double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
1156  {
1157  if (! info.sendoffs.isMember(move.move.to()))
1158  return 0;
1159  return w[offset + move.move.ptype()];
1160  }
1161  };
1162 
1163  class LureDefender : public Feature
1164  {
1165  public:
1166  enum {
1168  DIM = ATTACK_DIM*3,
1169  };
1170  LureDefender() : Feature("LureDefender", DIM)
1171  {
1172  }
1173  static double match(const NumEffectState& state, Move move, int see,
1174  const StateInfo::pinned_gs_t& pinned_list,
1175  int offset, const double *w)
1176  {
1177  const Square to = move.to(), king = state.kingSquare(alt(state.turn()));
1178  const Offset up = Board_Table.getOffset(state.turn(), U);
1179  const int basic_a = move.ptype() * PTYPE_SIZE*PTYPE_SIZE;
1180  double sum = 0.0;
1181  BOOST_FOREACH(PinnedGeneral defense, pinned_list) {
1182  if (to != defense.attack)
1183  continue;
1184  assert(defense.general.owner() != move.player());
1185  assert(defense.covered.owner() != move.player());
1186  if (defense.general.square() == move.to()+up)
1187  offset += ATTACK_DIM;
1188  else if (state.hasEffectIf(move.ptypeO(), move.to(), defense.general.square()))
1189  offset += ATTACK_DIM*2;
1190  int a = basic_a;
1191  if (defense.covered.square().canPromote(state.turn())
1192  && canPromote(move.ptype()))
1193  a = promote(move.ptype()) * PTYPE_SIZE*PTYPE_SIZE;
1194  const int b = defense.general.ptype() * PTYPE_SIZE;
1195  const int c = defense.covered.ptype();
1196  sum += w[offset];
1197  if (see < 0)
1198  sum += w[offset+1] * see/1024.0;
1199  sum += w[offset + a];
1200  sum += w[offset + b];
1201  sum += w[offset + c];
1202  sum += w[offset + a + b];
1203  sum += w[offset + a + c];
1204  sum += w[offset + b + c];
1205  if (defense.covered.square().canPromote(state.turn())) {
1206  sum += w[offset + a + PTYPE_EDGE];
1207  sum += w[offset + b + PTYPE_EDGE];
1208  }
1209  if (Neighboring8::isNeighboring8(defense.covered.square(), king)) {
1210  sum += w[offset + a + KING];
1211  sum += w[offset + b + KING];
1212  }
1213  sum += w[offset + a + b + c];
1214  break;
1215  }
1216  return sum;
1217  }
1218  double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
1219  {
1220  return match(*info.state, move.move, move.see,
1221  info.exchange_pins[alt(move.player)], offset, w);
1222  }
1223  };
1224 
1226  {
1227  public:
1228  enum {
1230  };
1231  CheckmateIfCapture() : Feature("CheckmateIfCapture", DIM)
1232  {
1233  }
1234  double match(const StateInfo& info, const MoveInfo& move, int offset, const double *w) const
1235  {
1236  if (info.state->inCheck() || move.see > -256)
1237  return 0;
1238  const Square king = info.state->kingSquare(alt(move.player));
1239  if (move.move.capturePtype() != PTYPE_EMPTY
1240  && ! info.state->hasPieceOnStand<GOLD>(move.player)
1241  && ! info.state->hasPieceOnStand<SILVER>(move.player)
1242  && ! info.move_candidate_exists[alt(move.player)])
1243  return 0;
1245  (*info.state, move.move.ptypeO(), move.move.to(), king))
1246  return 0;
1247  if (hasSafeCapture(info.copy, move.move))
1248  return 0;
1249  int ptype_index = move.move.ptype()*PTYPE_SIZE;
1250  int capture_index = move.move.capturePtype();
1251  double sum = 0.0;
1252  sum += w[offset + ptype_index + capture_index];
1253  sum += w[offset + capture_index];
1254  sum += w[offset + ptype_index + PTYPE_EDGE];
1255  sum += w[offset + PTYPE_EDGE];
1256  return sum;
1257  }
1258  static bool hasSafeCapture(NumEffectState& state, Move);
1259  };
1260  class AttackKing8Long : public Feature
1261  {
1262  public:
1263  enum {
1265  };
1266  AttackKing8Long() : Feature("AttackKing8Long", DIM)
1267  {
1268  }
1269  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
1270  {
1271  const Move move = info.move;
1272  double sum = 0.0;
1273  BOOST_FOREACH(Piece piece, state.king8_long_pieces) {
1274  if (! state.state->hasEffectIf(move.ptypeO(), move.to(),
1275  piece.square()))
1276  continue;
1277  sum += w[offset + PTYPE_EMPTY*PTYPE_SIZE+piece.ptype()]
1278  + w[offset + move.ptype()*PTYPE_SIZE+PTYPE_EMPTY]
1279  + w[offset + move.ptype()*PTYPE_SIZE+piece.ptype()];
1280  if (state.pin_by_opposing_sliders.isMember(piece)
1281  && info.see < 0)
1282  sum += w[offset + PTYPE_EMPTY*PTYPE_SIZE+PTYPE_EDGE]*info.see/1024.0;
1283  }
1284  return sum;
1285  }
1286  };
1287  class OpposingPawn : public Feature
1288  {
1289  public:
1290  enum {
1291  DIM = /*king-x*/9 * /* stand-pawn */ 3 * /* others */ 2,
1292  };
1293  OpposingPawn() : Feature("OpposingPawn", DIM)
1294  {
1295  }
1296  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
1297  {
1298  const Move move = info.move;
1299  if (move.ptype() != PAWN)
1300  return 0.0;
1301  const Square front = move.to() + Board_Table.getOffset(info.player, U);
1302  if (state.state->pieceAt(front).ptype() != PAWN)
1303  return 0.0;
1304  int king_x = abs(state.state->kingSquare(alt(info.player)).x() - front.x());
1305  int stand_pawn = state.state->countPiecesOnStand<PAWN>(info.player);
1306  if (move.isDrop())
1307  --stand_pawn;
1308  stand_pawn = std::min(2, stand_pawn);
1309  bool has_other = state.state->hasPieceOnStand<LANCE>(info.player)
1310  || state.state->hasPieceOnStand<KNIGHT>(info.player)
1311  || state.state->hasPieceOnStand<SILVER>(info.player)
1312  || state.state->hasPieceOnStand<GOLD>(info.player)
1313  || state.state->hasPieceOnStand<ROOK>(info.player)
1314  || state.state->hasPieceOnStand<BISHOP>(info.player);
1315  int index = (king_x * 3 + stand_pawn) * 2 + has_other;
1316  return w[offset + index];
1317  }
1318  };
1319 
1321  {
1322  public:
1323  enum {
1324  DIM = /*king-x*/9 * /* stand-pawn */ 3 * /* others */ 2 * /* capturepawn */ 2,
1325  };
1326  DropAfterOpposingPawn() : Feature("DropAfterOpposingPawn", DIM)
1327  {
1328  }
1329  double match(const StateInfo& state, const MoveInfo& info, int offset, const double *w) const
1330  {
1331  const Move move = info.move;
1332  if (move.ptype() != PAWN || ! move.isDrop() || ! state.history->hasLastMove())
1333  return 0.0;
1334  int to_x = move.to().x();
1335  const Move last_move = state.history->lastMove();
1336  if (! last_move.isNormal() || last_move.isDrop()
1337  || last_move.to().x() != to_x
1338  || last_move.from().x() != to_x)
1339  return 0.0;
1340 
1341  const Square front = move.to()+Board_Table.getOffset(info.player, U);
1342  if (! front.canPromote(info.player))
1343  return 0.0;
1344  int king_x = abs(state.state->kingSquare(alt(info.player)).x() - to_x);
1345  int stand_pawn = std::min(2, state.state->countPiecesOnStand<PAWN>(info.player)-1);
1346  bool has_other = state.state->hasPieceOnStand<LANCE>(info.player)
1347  || state.state->hasPieceOnStand<KNIGHT>(info.player)
1348  || state.state->hasPieceOnStand<SILVER>(info.player)
1349  || state.state->hasPieceOnStand<GOLD>(info.player)
1350  || state.state->hasPieceOnStand<ROOK>(info.player)
1351  || state.state->hasPieceOnStand<BISHOP>(info.player);
1352  bool follow_pawn_capture = last_move.capturePtype() == PAWN;
1353  int index = ((king_x * 3 + stand_pawn) * 2 + has_other) * 2 + follow_pawn_capture;
1354  return w[offset + index];
1355  }
1356  };
1357 
1358  class CoverPawn : public Feature
1359  {
1360  public:
1361  enum {
1362  DIM = 9 * 2 * PTYPE_SIZE * PTYPE_SIZE
1363  };
1364  CoverPawn() : Feature("CoverPawn", DIM)
1365  {
1366  }
1367  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1368  {
1369  if (! si.history->hasLastMove() || si.state->inCheck()
1370  || mi.move.isCaptureOrPromotion())
1371  return 0.0;
1372  const Move last_move = si.history->lastMove();
1373  if (last_move.ptype() != PAWN)
1374  return 0.0;
1375  const Offset diff = Board_Table.getOffset(mi.player, U);
1376  const Square front = last_move.to()-diff, front2 = front-diff;
1377  if (si.state->pieceOnBoard(front).ptype() != PAWN // must be turn's pawn
1378  || si.state->pieceAt(front2).isOnBoardByOwner(alt(mi.player)))
1379  return 0.0;
1380  const bool cover = si.state->hasEffectIf
1381  (mi.move.ptypeO(), mi.move.to(), front);
1382  const Ptype moved = cover ? mi.move.ptype() : PTYPE_EDGE,
1383  threatened = si.state->pieceAt(front2).ptype();
1384  const int a = std::min(2,si.state->countEffect(alt(mi.player), front) - 1);
1385  assert(a >= 0);
1386  const int b = std::min(2,si.state->countEffect(mi.player, front));
1387  const int ptype_index = moved*PTYPE_SIZE+threatened;
1388  const bool has_pawn = si.state->hasPieceOnStand(alt(mi.player),PAWN);
1389  const int pawn_index = PTYPE_SIZE*PTYPE_SIZE;
1390  const int effect_index = (a*3+b)*2*PTYPE_SIZE*PTYPE_SIZE
1391  + (has_pawn ? pawn_index : 0);
1392  assert(effect_index >= 0);
1393  return w[offset + threatened]
1394  + w[offset + ptype_index]
1395  + w[offset + effect_index + ptype_index];
1396  }
1397  };
1398  class SacrificeAttack : public Feature
1399  {
1400  public:
1401  enum {
1402  StandCount = 64,
1403  DIM = StandCount * PTYPE_SIZE * 2
1404  };
1405  SacrificeAttack() : Feature("SacrificeAttack", DIM)
1406  {
1407  }
1408  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1409  {
1410  const Square king = si.state->kingSquare(alt(mi.player));
1411  if (mi.see >= 0
1413  (*si.state, mi.move.ptypeO(), mi.move.to(), king)))
1414  return 0.0;
1415  assert(PieceStand::order[6] == PAWN);
1416  int stand = mi.standIndex(*si.state);
1417  int index = (stand * PTYPE_SIZE + mi.move.ptype()) * 2;
1418  double sum = w[offset + index];
1419  if (si.history->hasLastMove(2)) {
1420  Move my_last_move = si.history->lastMove(2);
1421  if (my_last_move.isNormal()
1423  (*si.state, my_last_move.ptypeO(), my_last_move.to(), king))
1424  sum += w[offset + index + 1];
1425  }
1426  return sum;
1427  }
1428  };
1429  class King5x5Ptype : public Feature
1430  {
1431  public:
1432  enum {
1433  ONE_DIM = 25 * 4 * PTYPE_SIZE * PTYPE_SIZE,
1434  DIM = 2 * ONE_DIM,
1435  };
1436  King5x5Ptype() : Feature("King5x5Ptype", DIM)
1437  {
1438  }
1439  static double addOne(Player king, Square center, const StateInfo& si, const MoveInfo& mi, int offset, const double *w)
1440  {
1441  const Square to = mi.move.to();
1442  int dx = center.x() - to.x();
1443  const int dy = center.y() - to.y();
1444  if (abs(dx) >= 3 || abs(dy) >= 3)
1445  return 0.0;
1446  if ((king == BLACK && center.x() > 5)
1447  || (king == WHITE && center.x() >= 5))
1448  dx = -dx;
1449  int sq_index = (dx+2)*5 + dy+2;
1450  bool a = mi.move.isDrop() ? si.state->hasEffectAt(mi.player, to)
1451  : si.state->countEffect(mi.player,to) >= 2;
1452  bool d = si.state->hasEffectAt(alt(mi.player), to);
1453  int index = (sq_index*4 + a*2+d) * PTYPE_SIZE*PTYPE_SIZE
1454  + mi.move.capturePtype()*PTYPE_SIZE + mi.move.ptype();
1455  return w[offset + index];
1456  }
1457  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1458  {
1459  return
1460  addOne(mi.player, si.state->kingSquare(mi.player), si, mi, offset, w)
1461  + addOne(alt(mi.player), si.state->kingSquare(alt(mi.player)), si, mi,
1462  offset+ONE_DIM, w);
1463  }
1464  };
1465  class KingBlockade : public Feature
1466  {
1467  public:
1468  enum {
1470  BlockLastOne = 0, BlockFront = 1,
1471  BlockSideWide = 2, BlockSideOther = 3, BlockBack = 4,
1472  DIM = 5 * StandCount
1473  };
1474  KingBlockade() : Feature("KingBlockade", DIM)
1475  {
1476  }
1477  static bool blockAll(const King8Info& ki, Square king, Move move,
1478  const NumEffectState& state,
1479  const CArray<Direction,3>& directions)
1480  {
1481  int liberty = 0;
1482  BOOST_FOREACH(Direction d, directions) {
1483  if ((ki.liberty() & (1<<d)) == 0)
1484  continue;
1485  ++liberty;
1486  const Square sq = king + Board_Table.getOffset(alt(state.turn()), d);
1487  if (! state.hasEffectIf(move.ptypeO(), move.to(), sq))
1488  return false;
1489  }
1490  return liberty > 0;
1491  }
1492  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1493  {
1494  const NumEffectState& state = *si.state;
1495  const King8Info ki = si.king8Info(alt(mi.player));
1496  const Square king = state.kingSquare(alt(mi.player));
1497  if (ki.libertyCount() == 0
1499  (state, mi.move.ptypeO(), mi.move.to(), king)))
1500  return 0.0;
1501  int stand = mi.standIndex(state);
1502  offset += stand*5;
1503  double sum = 0.0;
1504  if (ki.libertyCount() == 1) {
1505  const Square sq = king
1508  if (! state.hasEffectIf(mi.move.ptypeO(), mi.move.to(), sq))
1509  return 0.0;
1510  sum += w[offset+BlockLastOne];
1511  // fall through
1512  }
1513  const CArray<Direction,3> front3 = {{ UL, U, UR }};
1514  if (blockAll(ki, king, mi.move, state, front3))
1515  sum += w[offset+BlockFront];
1516  const CArray<Direction,3> left3 = {{ UL, L, DL }};
1517  if (blockAll(ki, king, mi.move, state, left3)) {
1518  const bool wide = (mi.player== WHITE && king.x() < 5)
1519  || (mi.player== BLACK && king.x() > 5);
1520  sum += w[offset+(wide ? BlockSideWide : BlockSideOther)];
1521  }
1522  const CArray<Direction,3> right3 = {{ UR, R, DR }};
1523  if (blockAll(ki, king, mi.move, state, right3)) {
1524  const bool wide = (mi.player== BLACK && king.x() < 5)
1525  || (mi.player== WHITE && king.x() > 5);
1526  sum += w[offset+ (wide ? BlockSideWide : BlockSideOther)];
1527  }
1528  const CArray<Direction,3> back3 = {{ DL, D, DR }};
1529  if (blockAll(ki, king, mi.move, state, back3))
1530  sum += w[offset+BlockBack];
1531  return sum;
1532  }
1533  };
1534  class CoverFork : public Feature
1535  {
1536  public:
1537  enum {
1539  };
1540  CoverFork() : Feature("CoverFork", DIM)
1541  {
1542  }
1543  static bool defending(const NumEffectState& state, Move move, Square target)
1544  {
1545  if (state.countEffect(alt(state.turn()), target) > 1)
1546  return false;
1547  if (state.hasEffectIf(move.ptypeO(), move.to(), target))
1548  return true;
1549  Piece attacking = state.findCheapAttack(alt(state.turn()), target);
1551  if (o.zero()
1552  || ! Board_Table.isBetween(move.to(), attacking.square(), target))
1553  return false;
1554  return state.countEffect(state.turn(), move.to()) >= (1-move.isDrop());
1555  }
1556  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1557  {
1558  const NumEffectState& state = *si.state;
1559  PieceMask attacked = state.piecesOnBoard(mi.player)
1560  & state.effectedMask(alt(mi.player))
1561  & ~(state.effectedMask(mi.player));
1562  attacked.clearBit<PAWN>();
1563  offset += mi.move.ptype()*PTYPE_SIZE*PTYPE_SIZE;
1564  double sum = 0.0;
1565  while (attacked.any()) {
1566  Piece a = state.pieceOf(attacked.takeOneBit());
1567  if (! defending(state, mi.move, a.square()))
1568  continue;
1569  int index_a = a.ptype()*PTYPE_SIZE;
1570  PieceMask copy = attacked;
1571  while (copy.any()) {
1572  Piece b = state.pieceOf(copy.takeOneBit());
1573  if (! defending(state, mi.move, b.square()))
1574  continue;
1575  sum += w[offset];
1576  sum += w[offset+index_a];
1577  sum += w[offset+b.ptype()];
1578  sum += w[offset+index_a+b.ptype()];
1579  }
1580  }
1581  return sum;
1582  }
1583  };
1585  {
1586  public:
1587  enum {
1589  };
1590  ThreatmateByCapture() : Feature("ThreatmateByCapture", DIM)
1591  {
1592  }
1593  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1594  {
1595  const Move move = mi.move;
1596  const Ptype captured = move.capturePtype();
1597  if (captured == PTYPE_EMPTY
1599  & 1<<(captured-PTYPE_BASIC_MIN)) == 0)
1600  return 0.0;
1601  double sum = 0.0;
1602  sum += w[offset];
1603  if (mi.see < 0)
1604  sum += w[offset+1] * mi.see/1024.0;
1605  sum += w[offset+captured];
1606  sum += w[offset+move.ptype()*PTYPE_SIZE];
1607  sum += w[offset+move.ptype()*PTYPE_SIZE+captured];
1608  return sum;
1609  }
1610  };
1611 
1613  {
1614  public:
1615  enum {
1617  };
1618  PromotionBySacrifice() : Feature("PromotionBySacrifice", DIM)
1619  {
1620  }
1621  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1622  {
1623  const Move move = mi.move;
1624  if (mi.see >= 0 || move.isDrop()
1625  || si.state->inCheck()
1626  || si.threatmate_move.isNormal())
1627  return 0.0;
1628  const NumEffectState& state = *si.state;
1629  mask_t m = state.longEffectAt(move.from(), state.turn());
1630  m &= ~mask_t::makeDirect(PtypeFuns<LANCE>::indexMask);
1631  double sum = 0.0;
1632  while (m.any()) {
1633  const Piece piece = state.pieceOf
1634  (m.takeOneBit() +PtypeFuns<LANCE>::indexNum*32);
1635  assert(piece.ptype() != LANCE);
1636  if (piece.isPromoted()
1637  || piece.square().canPromote(mi.player))
1638  continue;
1640  assert(! o.zero());
1641  bool can_promote = false;
1642  Square to = move.from()+o;
1643  if (to == move.to())
1644  continue;
1645  while (state[to].isEmpty()) {
1646  if (to.canPromote(mi.player)
1647  && ! state.hasEffectAt(alt(mi.player), to))
1648  can_promote = true;
1649  to += o;
1650  }
1651  assert(state[to] != piece);
1652  int index = 0;
1653  if (piece.ptype() == ROOK)
1654  index += PTYPE_SIZE*PTYPE_SIZE;
1655  index += move.ptype()*PTYPE_SIZE;
1656  if (to.canPromote(mi.player)
1657  && state[to].isOnBoardByOwner(alt(mi.player))
1658  && ! state.hasEffectAt(alt(mi.player), to)) {
1659  sum += w[offset];
1660  sum += w[offset+1]*mi.see/1024.0;
1661  if (mi.check)
1662  sum += w[offset+2];
1663  if (state.hasEffectAt(alt(mi.player), piece.square())) {
1664  sum += w[offset+3];
1665  if (mi.check)
1666  sum += w[offset+4];
1667  }
1668  if (state.hasEffectIf(piece.ptypeO(), to,
1669  state.kingSquare(alt(mi.player))))
1670  sum += w[offset+5];
1671  sum += w[offset + index + state[to].ptype()];
1672  }
1673  else if (can_promote) {
1674  sum += w[offset];
1675  sum += w[offset+1]*mi.see/1024.0;
1676  if (mi.check)
1677  sum += w[offset+2];
1678  if (state.hasEffectAt(alt(mi.player), piece.square())) {
1679  sum += w[offset+3];
1680  if (mi.check)
1681  sum += w[offset+4];
1682  }
1683  sum += w[offset + index];
1684  }
1685  }
1686  return sum;
1687  }
1688  };
1689 
1690  class EscapeThreatened : public Feature
1691  {
1692  public:
1693  enum {
1695  };
1696  EscapeThreatened() : Feature("EscapeThreatened", DIM)
1697  {
1698  }
1699  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1700  {
1701  const NumEffectState& state = *si.state;
1702  const Move move = mi.move;
1703  const Piece target = si.threatened[mi.player];
1704  if (mi.see > 0 || mi.check || mi.open_check
1705  || ! move.isNormal()
1706  || ! target.isPiece() || state.inCheck()
1707  || si.threatmate_move.isNormal()
1709  (state, move.ptypeO(), move.to(), state.kingSquare(alt(mi.player))))
1710  return 0.0;
1711  const int t0 = target.ptype()*PTYPE_SIZE*2*PTYPE_SIZE*3;
1712  const int t1 = t0 + state.findCheapAttack(alt(mi.player), target.square()).ptype()*2*PTYPE_SIZE*3;
1713  const int t2 = t1 + state.hasEffectAt(mi.player, target.square())*PTYPE_SIZE*3;
1714  double sum = 0.0;
1715  if (! move.isDrop() && state[move.from()] == target) {
1716  sum += w[offset + t0];
1717  sum += w[offset + t1];
1718  sum += w[offset + t2];
1719  return sum;
1720  }
1721  if (state.hasEffectIf(move.ptypeO(), move.to(),
1722  target.square())) {
1723  if (move.isDrop()
1724  || ! state.hasEffectIf(move.oldPtypeO(), move.from(),
1725  target.square())) {
1726  sum += w[offset + t0 + move.ptype()];
1727  sum += w[offset + t1 + move.ptype()];
1728  sum += w[offset + t2 + move.ptype()];
1729  return sum;
1730  }
1731  }
1732  // not related move
1733  offset += PTYPE_SIZE;
1734  if (move.isDrop())
1735  offset += PTYPE_SIZE;
1736  sum += w[offset + t0 + move.ptype()];
1737  sum += w[offset + t1 + move.ptype()];
1738  sum += w[offset + t2 + move.ptype()];
1739  return sum;
1740  }
1741  };
1742  class BookMove : public Feature
1743  {
1744  public:
1745  enum {
1746  DIM = 2
1747  };
1748  BookMove() : Feature("BookMove", DIM)
1749  {
1750  }
1751  double match(const StateInfo& si, const MoveInfo& mi, int offset, const double *w) const
1752  {
1753  if (! si.bookmove[0].isNormal())
1754  return 0.0;
1755  if (mi.move == si.bookmove[0] || mi.move == si.bookmove[1])
1756  return w[offset];
1757  return w[offset+1];
1758  }
1759  };
1760  }
1761 }
1762 #endif /* OSL_MOVE_PROBABILITY_FEATURE_H */
1763 // ;;; Local Variables:
1764 // ;;; mode:c++
1765 // ;;; c-basic-offset:2
1766 // ;;; End: