1from .bitboard import bitPosArray, iterBits, clearBit, firstBit
2from .attack import isAttacked, pinnedOnKing, getAttacks
3from .ldata import fromToRay, moveArray, directions, fileBits, rankBits,\
4    ray45, attack45, ray135, attack135, ray90, attack90, ray00, attack00, FILE, rays
5from pychess.Utils.const import EMPTY, PAWN,\
6    QUEEN, KNIGHT, BISHOP, ROOK, KING, WHITE, BLACK,\
7    SITTUYINCHESS, FISCHERRANDOMCHESS, SUICIDECHESS, GIVEAWAYCHESS, CAMBODIANCHESS,\
8    ATOMICCHESS, WILDCASTLECHESS, WILDCASTLESHUFFLECHESS, CRAZYHOUSECHESS, ASEAN_VARIANTS,\
9    HORDECHESS, PLACEMENTCHESS, BPAWN, sliders,\
10    A8, A6, G6, F6, H1, C3, B2, B3, A3, D6, D8, E3, E1, E8, C7, F2, D1, E6, H3, D3, H2, G7, H6, H7,\
11    ASEAN_QUEEN, ASEAN_BBISHOP, ASEAN_WBISHOP, NORMAL_MOVE, QUEEN_CASTLE, KING_CASTLE, ENPASSANT,\
12    KNIGHT_PROMOTION, BISHOP_PROMOTION, ROOK_PROMOTION, QUEEN_PROMOTION, KING_PROMOTION, NULL_MOVE,\
13    DROP_VARIANTS, DROP, B_OOO, B_OO, W_OOO, W_OO
14
15# The format of a move is as follows - from left:
16# 4 bits:  Descriping the type of the move
17# 6 bits:  cord to move from
18# 6 bits:  cord to move to
19
20shiftedFromCords = []
21for i in range(64):
22    shiftedFromCords.append(i << 6)
23
24shiftedFlags = []
25for i in NORMAL_MOVE, QUEEN_CASTLE, KING_CASTLE, ENPASSANT, \
26        KNIGHT_PROMOTION, BISHOP_PROMOTION, ROOK_PROMOTION, QUEEN_PROMOTION, KING_PROMOTION, NULL_MOVE, DROP:
27    shiftedFlags.append(i << 12)
28
29
30def newMove(fromcord, tocord, flag=NORMAL_MOVE):
31    return shiftedFlags[flag] + shiftedFromCords[fromcord] + tocord
32
33# Generate all moves
34
35
36def genCastles(board):
37    def generateOne(color, side, king_after, rook_after):
38        if side == 0:
39            castle = QUEEN_CASTLE
40        else:
41            castle = KING_CASTLE
42        king = board.ini_kings[color]
43        rook = board.ini_rooks[color][side]
44        blocker = clearBit(clearBit(board.blocker, king), rook)
45        stepover = fromToRay[king][king_after] | fromToRay[rook][rook_after]
46        if not stepover & blocker:
47            for cord in range(
48                    min(king, king_after), max(king, king_after) + 1):
49                if isAttacked(board, cord, 1 - color):
50                    return
51            if FILE(king) == 3 and board.variant in (WILDCASTLECHESS,
52                                                     WILDCASTLESHUFFLECHESS):
53                castle = QUEEN_CASTLE if castle == KING_CASTLE else KING_CASTLE
54            if board.variant == FISCHERRANDOMCHESS:
55                return newMove(king, rook, castle)
56            else:
57                return newMove(king, king_after, castle)
58
59    king = board.ini_kings[board.color]
60    wildcastle = FILE(king) == 3 and board.variant in (WILDCASTLECHESS,
61                                                       WILDCASTLESHUFFLECHESS)
62    if board.color == WHITE:
63        if board.castling & W_OO:
64            side = 0 if wildcastle else 1
65            move = generateOne(WHITE, side, board.fin_kings[WHITE][side],
66                               board.fin_rooks[WHITE][side])
67            if move:
68                yield move
69
70        if board.castling & W_OOO:
71            side = 1 if wildcastle else 0
72            move = generateOne(WHITE, side, board.fin_kings[WHITE][side],
73                               board.fin_rooks[WHITE][side])
74            if move:
75                yield move
76    else:
77        if board.castling & B_OO:
78            side = 0 if wildcastle else 1
79            move = generateOne(BLACK, side, board.fin_kings[BLACK][side],
80                               board.fin_rooks[BLACK][side])
81            if move:
82                yield move
83
84        if board.castling & B_OOO:
85            side = 1 if wildcastle else 0
86            move = generateOne(BLACK, side, board.fin_kings[BLACK][side],
87                               board.fin_rooks[BLACK][side])
88            if move:
89                yield move
90
91
92def genPieceMoves(board, piece, tcord):
93    """"
94    Used by parseSAN only to accelerate it a bit
95    """
96    moves = set()
97    friends = board.friends[board.color]
98    notfriends = ~friends
99    if piece == KNIGHT:
100        knights = board.boards[board.color][KNIGHT]
101        knightMoves = moveArray[KNIGHT]
102        for fcord in iterBits(knights):
103            if tcord in iterBits(knightMoves[fcord] & notfriends):
104                moves.add(newMove(fcord, tcord))
105        return moves
106
107    if piece == BISHOP:
108        bishops = board.boards[board.color][BISHOP]
109        if board.variant in ASEAN_VARIANTS:
110            bishopMoves = moveArray[ASEAN_WBISHOP if board.color == WHITE else
111                                    ASEAN_BBISHOP]
112            for fcord in iterBits(bishops):
113                if tcord in iterBits(bishopMoves[fcord] & notfriends):
114                    moves.add(newMove(fcord, tcord))
115            return moves
116        else:
117            blocker = board.blocker
118            for fcord in iterBits(bishops):
119                try:
120                    attackBoard = attack45[fcord][ray45[fcord] & blocker] | \
121                        attack135[fcord][ray135[fcord] & blocker]
122                except KeyError:
123                    attackBoard = 0
124                if tcord in iterBits(attackBoard & notfriends):
125                    moves.add(newMove(fcord, tcord))
126            return moves
127
128    if piece == ROOK:
129        blocker = board.blocker
130        rooks = board.boards[board.color][ROOK]
131        for fcord in iterBits(rooks):
132            try:
133                attackBoard = attack00[fcord][ray00[fcord] & blocker] | \
134                    attack90[fcord][ray90[fcord] & blocker]
135            except KeyError:
136                attackBoard = 0
137            if tcord in iterBits(attackBoard & notfriends):
138                moves.add(newMove(fcord, tcord))
139        return moves
140
141    if piece == QUEEN:
142        queens = board.boards[board.color][QUEEN]
143        if board.variant in ASEAN_VARIANTS:
144            queenMoves = moveArray[ASEAN_QUEEN]
145            for fcord in iterBits(queens):
146                if tcord in iterBits(queenMoves[fcord] & notfriends):
147                    moves.add(newMove(fcord, tcord))
148            # Cambodian extra first move
149            if board.variant == CAMBODIANCHESS:
150                if board.is_first_move[QUEEN][board.color]:
151                    if board.color == WHITE:
152                        if not board.arBoard[E3]:
153                            moves.add(newMove(E1, E3))
154                    else:
155                        if not board.arBoard[D6]:
156                            moves.add(newMove(D8, D6))
157            return moves
158        else:
159            blocker = board.blocker
160            for fcord in iterBits(queens):
161                try:
162                    attackBoard = attack45[fcord][ray45[fcord] & blocker] | \
163                        attack135[fcord][ray135[fcord] & blocker]
164                except KeyError:
165                    attackBoard = 0
166                if tcord in iterBits(attackBoard & notfriends):
167                    moves.add(newMove(fcord, tcord))
168
169                try:
170                    attackBoard = attack00[fcord][ray00[fcord] & blocker] | \
171                        attack90[fcord][ray90[fcord] & blocker]
172                except KeyError:
173                    attackBoard = 0
174                if tcord in iterBits(attackBoard & notfriends):
175                    moves.add(newMove(fcord, tcord))
176            return moves
177
178    if (board.variant == SUICIDECHESS or board.variant == GIVEAWAYCHESS) and piece == KING:
179        kings = board.boards[board.color][KING]
180        if kings:
181            kingMoves = moveArray[KING]
182            for fcord in iterBits(kings):
183                for tc in iterBits(kingMoves[fcord] & notfriends):
184                    if tc == tcord:
185                        moves.add(newMove(fcord, tcord))
186            return moves
187
188    return moves
189
190
191def gen_sittuyin_promotions(board):
192    from pychess.Variants import variants
193    blocker = board.blocker
194    notblocker = ~blocker
195
196    pawns = board.boards[board.color][PAWN]
197
198    queenMoves = moveArray[ASEAN_QUEEN]
199
200    def willDirectAttack(board, move, cord):
201        board_clone = board.clone()
202        board_clone.applyMove(move)
203        return board.friends[1 - board.color] & moveArray[ASEAN_QUEEN][cord]
204
205    promotion_zone = variants[SITTUYINCHESS].PROMOTION_ZONE[board.color]
206    for cord in iterBits(pawns):
207        if board.pieceCount[board.color][PAWN] == 1 or cord in promotion_zone:
208            # in place promotions
209            move = newMove(cord, cord, QUEEN_PROMOTION)
210            if not board.willGiveCheck(move) and not willDirectAttack(board, move, cord):
211                yield move
212
213            # queen move promotion
214            for c in iterBits(queenMoves[cord] & notblocker):
215                move = newMove(cord, c, QUEEN_PROMOTION)
216                if not board.willGiveCheck(move) and not willDirectAttack(board, move, c):
217                    yield move
218
219
220def genAllMoves(board, drops=True):
221    from pychess.Variants import variants
222    if drops and board.variant in DROP_VARIANTS:
223        for move in genDrops(board):
224            yield move
225
226    # In sittuyin you have to place your pieces before any real move
227    if board.variant == SITTUYINCHESS or board.variant == PLACEMENTCHESS:
228        if board.plyCount < 16:
229            return
230
231    blocker = board.blocker
232    notblocker = ~blocker
233    enpassant = board.enpassant
234
235    friends = board.friends[board.color]
236    notfriends = ~friends
237    enemies = board.friends[1 - board.color]
238
239    pawns = board.boards[board.color][PAWN]
240    knights = board.boards[board.color][KNIGHT]
241    bishops = board.boards[board.color][BISHOP]
242    rooks = board.boards[board.color][ROOK]
243    queens = board.boards[board.color][QUEEN]
244    kings = board.boards[board.color][KING]
245
246    PROMOTIONS = variants[board.variant].PROMOTIONS
247    # In sittuyin only one queen allowed to exist any time per side
248    if board.variant == SITTUYINCHESS and queens:
249        PROMOTIONS = (NORMAL_MOVE, )
250
251    # Knights
252    knightMoves = moveArray[KNIGHT]
253    for cord in iterBits(knights):
254        for c in iterBits(knightMoves[cord] & notfriends):
255            yield newMove(cord, c)
256
257    # King
258    if kings:
259        kingMoves = moveArray[KING]
260        # cord = firstBit(kings)
261        for cord in iterBits(kings):
262            for c in iterBits(kingMoves[cord] & notfriends):
263                if board.variant == ATOMICCHESS:
264                    if not board.arBoard[c]:
265                        yield newMove(cord, c)
266                else:
267                    yield newMove(cord, c)
268
269    if board.variant in ASEAN_VARIANTS:
270        # Rooks
271        for cord in iterBits(rooks):
272            try:
273                attackBoard = attack00[cord][ray00[cord] & blocker] | \
274                    attack90[cord][ray90[cord] & blocker]
275            except KeyError:
276                attackBoard = 0
277            for c in iterBits(attackBoard & notfriends):
278                yield newMove(cord, c)
279
280        # Queens
281        queenMoves = moveArray[ASEAN_QUEEN]
282        for cord in iterBits(queens):
283            for c in iterBits(queenMoves[cord] & notfriends):
284                yield newMove(cord, c)
285
286        # Bishops
287        bishopMoves = moveArray[ASEAN_WBISHOP if board.color == WHITE else
288                                ASEAN_BBISHOP]
289        for cord in iterBits(bishops):
290            for c in iterBits(bishopMoves[cord] & notfriends):
291                yield newMove(cord, c)
292
293    else:
294        # Rooks and Queens
295        for cord in iterBits(rooks | queens):
296            try:
297                attackBoard = attack00[cord][ray00[cord] & blocker] | \
298                    attack90[cord][ray90[cord] & blocker]
299            except KeyError:
300                attackBoard = 0
301            for c in iterBits(attackBoard & notfriends):
302                yield newMove(cord, c)
303
304    # Bishops and Queens
305        for cord in iterBits(bishops | queens):
306            try:
307                attackBoard = attack45[cord][ray45[cord] & blocker] | \
308                    attack135[cord][ray135[cord] & blocker]
309            except KeyError:
310                attackBoard = 0
311            for c in iterBits(attackBoard & notfriends):
312                yield newMove(cord, c)
313
314    # White pawns
315    pawnEnemies = enemies | (enpassant is not None and bitPosArray[enpassant] or 0)
316    if board.color == WHITE:
317
318        # One step
319
320        if board.variant == SITTUYINCHESS:
321            promotion_zone = []
322        else:
323            promotion_zone = variants[board.variant].PROMOTION_ZONE[WHITE]
324        movedpawns = (pawns >>
325                      8) & notblocker  # Move all pawns one step forward
326        for cord in iterBits(movedpawns):
327            if cord in promotion_zone:
328                for p in PROMOTIONS:
329                    yield newMove(cord - 8, cord, p)
330            else:
331                yield newMove(cord - 8, cord)
332
333        # Two steps
334
335        seccondrow = pawns & rankBits[1]  # Get seccond row pawns
336        movedpawns = (seccondrow >>
337                      8) & notblocker  # Move two steps forward, while
338        movedpawns = (movedpawns >>
339                      8) & notblocker  # ensuring middle cord is clear
340        for cord in iterBits(movedpawns):
341            yield newMove(cord - 16, cord)
342
343        # In horde white pawns on first rank may move two squares also
344        if board.variant == HORDECHESS:
345            firstrow = pawns & rankBits[0]  # Get first row pawns
346            movedpawns = (firstrow >>
347                          8) & notblocker  # Move two steps forward, while
348            movedpawns = (movedpawns >>
349                          8) & notblocker  # ensuring middle cord is clear
350            for cord in iterBits(movedpawns):
351                yield newMove(cord - 16, cord)
352
353        # Capture left
354
355        capLeftPawns = pawns & ~fileBits[0]
356        capLeftPawns = (capLeftPawns >> 7) & pawnEnemies
357        for cord in iterBits(capLeftPawns):
358            if cord in promotion_zone:
359                for p in PROMOTIONS:
360                    yield newMove(cord - 7, cord, p)
361            elif cord == enpassant:
362                yield newMove(cord - 7, cord, ENPASSANT)
363            else:
364                yield newMove(cord - 7, cord)
365
366        # Capture right
367
368        capRightPawns = pawns & ~fileBits[7]
369        capRightPawns = (capRightPawns >> 9) & pawnEnemies
370        for cord in iterBits(capRightPawns):
371            if cord in promotion_zone:
372                for p in PROMOTIONS:
373                    yield newMove(cord - 9, cord, p)
374            elif cord == enpassant:
375                yield newMove(cord - 9, cord, ENPASSANT)
376            else:
377                yield newMove(cord - 9, cord)
378
379    # Black pawns
380    else:
381
382        # One step
383
384        if board.variant == SITTUYINCHESS:
385            promotion_zone = []
386        else:
387            promotion_zone = variants[board.variant].PROMOTION_ZONE[BLACK]
388        movedpawns = (pawns << 8) & notblocker
389        movedpawns &= 0xffffffffffffffff  # contrain to 64 bits
390        for cord in iterBits(movedpawns):
391            if cord in promotion_zone:
392                for p in PROMOTIONS:
393                    yield newMove(cord + 8, cord, p)
394            else:
395                yield newMove(cord + 8, cord)
396
397        # Two steps
398
399        seccondrow = pawns & rankBits[6]  # Get seventh row pawns
400        # Move two steps forward, while ensuring middle cord is clear
401        movedpawns = seccondrow << 8 & notblocker
402        movedpawns = movedpawns << 8 & notblocker
403        for cord in iterBits(movedpawns):
404            yield newMove(cord + 16, cord)
405
406        # Capture left
407
408        capLeftPawns = pawns & ~fileBits[7]
409        capLeftPawns = capLeftPawns << 7 & pawnEnemies
410        for cord in iterBits(capLeftPawns):
411            if cord in promotion_zone:
412                for p in PROMOTIONS:
413                    yield newMove(cord + 7, cord, p)
414            elif cord == enpassant:
415                yield newMove(cord + 7, cord, ENPASSANT)
416            else:
417                yield newMove(cord + 7, cord)
418
419        # Capture right
420
421        capRightPawns = pawns & ~fileBits[0]
422        capRightPawns = capRightPawns << 9 & pawnEnemies
423        for cord in iterBits(capRightPawns):
424            if cord in promotion_zone:
425                for p in PROMOTIONS:
426                    yield newMove(cord + 9, cord, p)
427            elif cord == enpassant:
428                yield newMove(cord + 9, cord, ENPASSANT)
429            else:
430                yield newMove(cord + 9, cord)
431
432    # Sittuyin promotions
433    if board.variant == SITTUYINCHESS and pawns and not queens:
434        for move in gen_sittuyin_promotions(board):
435            yield move
436
437    # Cambodian extra first moves for king and queen
438    if board.variant == CAMBODIANCHESS:
439        if board.arBoard[board.ini_kings[board.color]] == KING and \
440                board.is_first_move[KING][board.color]:
441            if board.color == WHITE:
442                if not board.arBoard[B2]:
443                    yield newMove(D1, B2)
444                if not board.arBoard[F2]:
445                    yield newMove(D1, F2)
446            else:
447                if not board.arBoard[C7]:
448                    yield newMove(E8, C7)
449                if not board.arBoard[G7]:
450                    yield newMove(E8, G7)
451        if board.arBoard[board.ini_queens[board.color]] == QUEEN and \
452                board.is_first_move[QUEEN][board.color]:
453            if board.color == WHITE:
454                if not board.arBoard[E3]:
455                    yield newMove(E1, E3)
456            else:
457                if not board.arBoard[D6]:
458                    yield newMove(D8, D6)
459
460    # Castling
461    if kings:
462        for move in genCastles(board):
463            yield move
464
465################################################################################
466#   Generate capturing moves                                                   #
467################################################################################
468
469
470def genCaptures(board):
471    from pychess.Variants import variants
472
473    blocker = board.blocker
474    enpassant = board.enpassant
475
476    enemies = board.friends[1 - board.color]
477
478    pawns = board.boards[board.color][PAWN]
479    knights = board.boards[board.color][KNIGHT]
480    bishops = board.boards[board.color][BISHOP]
481    rooks = board.boards[board.color][ROOK]
482    queens = board.boards[board.color][QUEEN]
483    kings = board.boards[board.color][KING]
484
485    PROMOTIONS = variants[board.variant].PROMOTIONS
486    # In sittuyin promotion can't give capture
487    if board.variant == SITTUYINCHESS:
488        PROMOTIONS = (NORMAL_MOVE, )
489
490    # Knights
491    knightMoves = moveArray[KNIGHT]
492    for cord in iterBits(knights):
493        for c in iterBits(knightMoves[cord] & enemies):
494            yield newMove(cord, c)
495
496    # King
497    if kings:
498        kingMoves = moveArray[KING]
499        # cord = firstBit(kings)
500        for cord in iterBits(kings):
501            for c in iterBits(kingMoves[cord] & enemies):
502                if board.variant != ATOMICCHESS:
503                    yield newMove(cord, c)
504
505    # Rooks and Queens
506    if board.variant in ASEAN_VARIANTS:
507        for cord in iterBits(rooks):
508            try:
509                attackBoard = attack00[cord][ray00[cord] & blocker] | \
510                    attack90[cord][ray90[cord] & blocker]
511            except KeyError:
512                attackBoard = 0
513            for c in iterBits(attackBoard & enemies):
514                yield newMove(cord, c)
515    else:
516        for cord in iterBits(rooks | queens):
517            try:
518                attackBoard = attack00[cord][ray00[cord] & blocker] | \
519                    attack90[cord][ray90[cord] & blocker]
520            except KeyError:
521                attackBoard = 0
522            for c in iterBits(attackBoard & enemies):
523                yield newMove(cord, c)
524
525    # Bishops and Queens
526    if board.variant in ASEAN_VARIANTS:
527        bishopMoves = moveArray[ASEAN_WBISHOP if board.color == WHITE else
528                                ASEAN_BBISHOP]
529        for cord in iterBits(bishops):
530            for c in iterBits(bishopMoves[cord] & enemies):
531                yield newMove(cord, c)
532        queenMoves = moveArray[ASEAN_QUEEN]
533        for cord in iterBits(queens):
534            for c in iterBits(queenMoves[cord] & enemies):
535                yield newMove(cord, c)
536    else:
537        for cord in iterBits(bishops | queens):
538            try:
539                attackBoard = attack45[cord][ray45[cord] & blocker] | \
540                    attack135[cord][ray135[cord] & blocker]
541            except KeyError:
542                attackBoard = 0
543            for c in iterBits(attackBoard & enemies):
544                yield newMove(cord, c)
545
546    # White pawns
547    pawnEnemies = enemies | (enpassant is not None and bitPosArray[enpassant] or 0)
548
549    if board.color == WHITE:
550        promotion_zone = variants[board.variant].PROMOTION_ZONE[WHITE]
551
552        # Promotes
553
554        # Capture left
555
556        capLeftPawns = pawns & ~fileBits[0]
557        capLeftPawns = (capLeftPawns >> 7) & pawnEnemies
558        for cord in iterBits(capLeftPawns):
559            if cord in promotion_zone:
560                for p in PROMOTIONS:
561                    yield newMove(cord - 7, cord, p)
562            elif cord == enpassant:
563                yield newMove(cord - 7, cord, ENPASSANT)
564            else:
565                yield newMove(cord - 7, cord)
566
567        # Capture right
568
569        capRightPawns = pawns & ~fileBits[7]
570        capRightPawns = (capRightPawns >> 9) & pawnEnemies
571        for cord in iterBits(capRightPawns):
572            if cord in promotion_zone:
573                for p in PROMOTIONS:
574                    yield newMove(cord - 9, cord, p)
575            elif cord == enpassant:
576                yield newMove(cord - 9, cord, ENPASSANT)
577            else:
578                yield newMove(cord - 9, cord)
579
580    # Black pawns
581    else:
582        promotion_zone = variants[board.variant].PROMOTION_ZONE[BLACK]
583
584        # One step
585
586        # Capture left
587
588        capLeftPawns = pawns & ~fileBits[7]
589        capLeftPawns = capLeftPawns << 7 & pawnEnemies
590        for cord in iterBits(capLeftPawns):
591            if cord in promotion_zone:
592                for p in PROMOTIONS:
593                    yield newMove(cord + 7, cord, p)
594            elif cord == enpassant:
595                yield newMove(cord + 7, cord, ENPASSANT)
596            else:
597                yield newMove(cord + 7, cord)
598
599        # Capture right
600
601        capRightPawns = pawns & ~fileBits[0]
602        capRightPawns = capRightPawns << 9 & pawnEnemies
603        for cord in iterBits(capRightPawns):
604            if cord in promotion_zone:
605                for p in PROMOTIONS:
606                    yield newMove(cord + 9, cord, p)
607            elif cord == enpassant:
608                yield newMove(cord + 9, cord, ENPASSANT)
609            else:
610                yield newMove(cord + 9, cord)
611
612################################################################################
613#   Generate escapes from check                                                #
614################################################################################
615
616
617def genCheckEvasions(board):
618    from pychess.Variants import variants
619    color = board.color
620    opcolor = 1 - color
621
622    kcord = board.kings[color]
623    kings = board.boards[color][KING]
624    pawns = board.boards[color][PAWN]
625    queens = board.boards[board.color][QUEEN]
626    checkers = getAttacks(board, kcord, opcolor)
627
628    arBoard = board.arBoard
629    if bin(checkers).count("1") == 1:
630
631        PROMOTIONS = variants[board.variant].PROMOTIONS
632        # In sittuyin promotion move not allowed to capture opponent pieces
633        if board.variant == SITTUYINCHESS and board.boards[board.color][QUEEN]:
634            PROMOTIONS = (NORMAL_MOVE, )
635        promotion_zone = variants[board.variant].PROMOTION_ZONE[color]
636
637        # Captures of checking pieces (except by king, which we will test later)
638        chkcord = firstBit(checkers)
639        b = getAttacks(board, chkcord, color) & ~kings
640        for cord in iterBits(b):
641            if not pinnedOnKing(board, cord, color):
642                if arBoard[cord] == PAWN and chkcord in promotion_zone and board.variant != SITTUYINCHESS:
643                    for p in PROMOTIONS:
644                        yield newMove(cord, chkcord, p)
645                else:
646                    yield newMove(cord, chkcord)
647
648        # Maybe enpassant can help
649        if board.enpassant:
650            ep = board.enpassant
651            if ep + (color == WHITE and -8 or 8) == chkcord:
652                bits = moveArray[color == WHITE and BPAWN or PAWN][ep] & pawns
653                for cord in iterBits(bits):
654                    if not pinnedOnKing(board, cord, color):
655                        yield newMove(cord, ep, ENPASSANT)
656
657        # Lets block/capture the checking piece
658        if sliders[arBoard[chkcord]]:
659            bits = clearBit(fromToRay[kcord][chkcord], chkcord)
660
661            for cord in iterBits(bits):
662                b = getAttacks(board, cord, color)
663                b &= ~(kings | pawns)
664
665                # Add in pawn advances
666                if color == WHITE and cord > H2:
667                    if bitPosArray[cord - 8] & pawns:
668                        b |= bitPosArray[cord - 8]
669                    if cord >> 3 == 3 and arBoard[cord - 8] == EMPTY and \
670                            bitPosArray[cord - 16] & pawns:
671                        b |= bitPosArray[cord - 16]
672
673                elif color == BLACK and cord < H7:
674                    if bitPosArray[cord + 8] & pawns:
675                        b |= bitPosArray[cord + 8]
676                    if cord >> 3 == 4 and arBoard[cord + 8] == EMPTY and \
677                            bitPosArray[cord + 16] & pawns:
678                        b |= bitPosArray[cord + 16]
679
680                for fcord in iterBits(b):
681                    # If the piece is blocking another attack, we cannot move it
682                    if pinnedOnKing(board, fcord, color):
683                        continue
684                    if arBoard[fcord] == PAWN and cord in promotion_zone:
685                        for p in PROMOTIONS:
686                            yield newMove(fcord, cord, p)
687                    else:
688                        yield newMove(fcord, cord)
689
690                if board.variant == CRAZYHOUSECHESS:
691                    holding = board.holding[color]
692                    for piece in holding:
693                        if holding[piece] > 0:
694                            if piece == PAWN:
695                                if cord >= 56 or cord <= 7:
696                                    continue
697                            yield newMove(piece, cord, DROP)
698
699                if board.variant == SITTUYINCHESS and pawns and not queens:
700                    from .lmove import TCORD
701                    for move in gen_sittuyin_promotions(board):
702                        if TCORD(move) == cord:
703                            yield move
704
705    # If more than one checkers, move king to get out of check
706    if checkers:
707        escapes = moveArray[KING][kcord] & ~board.friends[color]
708    else:
709        escapes = 0
710
711    for chkcord in iterBits(checkers):
712        dir = directions[chkcord][kcord]
713        if sliders[arBoard[chkcord]]:
714            escapes &= ~rays[chkcord][dir]
715
716    for cord in iterBits(escapes):
717        if not isAttacked(board, cord, opcolor):
718            yield newMove(kcord, cord)
719
720
721def genDrops(board):
722    color = board.color
723    arBoard = board.arBoard
724    holding = board.holding[color]
725    for piece in holding:
726        if holding[piece] > 0:
727            for cord, elem in enumerate(arBoard):
728                if elem == EMPTY:
729                    # forbidden drop moves
730                    if board.variant == SITTUYINCHESS:
731                        if color == WHITE:
732                            if cord in (A3, B3, C3, D3) or cord > H3:
733                                continue
734                            if piece == ROOK and cord > H1:
735                                continue
736                        else:
737                            if cord in (E6, F6, G6, H6) or cord < A6:
738                                continue
739                            if piece == ROOK and cord < A8:
740                                continue
741
742                    elif board.variant == PLACEMENTCHESS:
743                        # drop pieces enabled on base line only
744                        if color == WHITE:
745                            if cord > H1:
746                                continue
747                        else:
748                            if cord < A8:
749                                continue
750
751                        # bishops must be on opposite colour squares
752                        base_line = arBoard[0:8] if color == WHITE else arBoard[56:64]
753                        occupied_colors = [0, 0]
754                        occupied_colors[cord % 2] += 1
755                        for i, baseline_piece in enumerate(base_line):
756                            if baseline_piece != EMPTY:
757                                occupied_colors[i % 2] += 1
758
759                        if holding[BISHOP] == 2 and piece != BISHOP:
760                            # occupying all same colored fields before any bishop dropped is no-no
761                            if occupied_colors[WHITE] == 4 or occupied_colors[BLACK] == 4:
762                                continue
763                        elif holding[BISHOP] == 1:
764                            for i, baseline_piece in enumerate(base_line):
765                                if baseline_piece == BISHOP:
766                                    first_bishop_cord = i
767                                    break
768                            # occupying all possible place of opp colored bishop is no-no
769                            if piece != BISHOP and occupied_colors[1 - first_bishop_cord % 2] == 4:
770                                continue
771                            # same colored bishop is no-no
772                            elif piece == BISHOP and first_bishop_cord % 2 == cord % 2:
773                                continue
774
775                    if piece == PAWN:
776                        if cord >= 56 or cord <= 7:
777                            continue
778
779                    yield newMove(piece, cord, DROP)
780