feature.h Source File

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
6 #include "osl/move_probability/moveInfo.h"
7 #include "osl/move_probability/stateInfo.h"
8 #include "osl/effect_util/additionalEffect.h"
9 #include "osl/effect_util/neighboring8Direct.h"
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 };
46 CheckFeature()
47 : Feature("Check", CHECK_CLASS*PTYPE_SIZE*2*RELATIVE_Y_CLASS)
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;
143 BOOST_STATIC_ASSERT(PTYPE_EDGE == 1);
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 {
193 DIM = 9*PTYPE_SIZE*16
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:
334 FromEffect() : Feature("FromEffect", PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE)
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:
355 ToEffect() : Feature("ToEffect", PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE)
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
560 typedef PatternBase<false> Pattern;
561 typedef PatternBase<true> PatternPromotion;
562
563 class MoveFromOpposingSliders : public Feature
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 };
593 class AttackFromOpposingSliders : public Feature
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 };
615 class AttackToOpposingSliders : public Feature
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);
735 const StateInfo::long_attack_t&
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 {
787 DIM = BlockLong::LongAttackSize
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 {
809 DIM = BlockLong::LongAttackSize * PTYPE_SIZE
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 {
839 DIM = PTYPE_SIZE*BlockLong::LongAttackSize
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 {
873 PatternCacheSize = PatternCommon::PatternCacheSize,
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 {
926 PatternCacheSize = PatternCommon::PatternCacheSize,
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;
951 const Offset diff = DirectionPlayerTraits<D,BLACK>::offset();
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 {
1028 PatternCacheSize = PatternCommon::PatternCacheSize,
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 {
1069 if (Neighboring8Direct::hasEffect
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);
1076 if (Neighboring8Direct::hasEffect
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()) {
1127 if (Neighboring8Direct::hasEffect
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 {
1167 ATTACK_DIM = PTYPE_SIZE*PTYPE_SIZE*PTYPE_SIZE,
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
1225 class CheckmateIfCapture : public Feature
1226 {
1227 public:
1228 enum {
1229 DIM = PTYPE_SIZE*PTYPE_SIZE,
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;
1244 if (! Neighboring8Direct::hasEffect
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 {
1264 DIM = PTYPE_SIZE*PTYPE_SIZE,
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
1320 class DropAfterOpposingPawn : public Feature
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
1412 || (! Neighboring8Direct::hasEffect
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()
1422 && Neighboring8Direct::hasEffect
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 {
1469 StandCount = SacrificeAttack::StandCount,
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
1498 || (! Neighboring8Direct::hasEffect
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
1506 + Board_Table.getOffset(alt(mi.player),
1507 (Direction)misc::BitOp::bsf(ki.liberty()));
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 {
1538 DIM = PTYPE_SIZE * PTYPE_SIZE * PTYPE_SIZE
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);
1550 Offset o = Board_Table.getShortOffsetNotKnight(Offset32(move.to(), 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 };
1584 class ThreatmateByCapture : public Feature
1585 {
1586 public:
1587 enum {
1588 DIM = PTYPE_SIZE * PTYPE_SIZE
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
1598 || (si.possible_threatmate_ptype
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
1612 class PromotionBySacrifice : public Feature
1613 {
1614 public:
1615 enum {
1616 DIM = 2*PTYPE_SIZE * PTYPE_SIZE
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;
1639 Offset o = Board_Table.getShortOffsetNotKnight(Offset32(move.from(), piece.square()));
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 {
1694 DIM =(2*PTYPE_SIZE * PTYPE_SIZE) * 3 * PTYPE_SIZE
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()
1708 || Neighboring8Direct::hasEffect
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: