Updating to version 1.3, release made by Mike Vanier (mvanier@bbb.caltech.edu).
[gnushogi.git] / gnushogi / genmove.c
diff --git a/gnushogi/genmove.c b/gnushogi/genmove.c
new file mode 100644 (file)
index 0000000..6490319
--- /dev/null
@@ -0,0 +1,1654 @@
+/*
+ * FILE: genmove.c
+ *
+ * ----------------------------------------------------------------------
+ * Copyright (c) 1993, 1994, 1995 Matthias Mutz
+ * Copyright (c) 1999 Michael Vanier and the Free Software Foundation
+ *
+ * GNU SHOGI is based on GNU CHESS
+ *
+ * Copyright (c) 1988, 1989, 1990 John Stanback
+ * Copyright (c) 1992 Free Software Foundation
+ *
+ * This file is part of GNU SHOGI.
+ *
+ * GNU Shogi is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 1, or (at your option) any
+ * later version.
+ *
+ * GNU Shogi is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNU Shogi; see the file COPYING.  If not, write to the Free
+ * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ * ----------------------------------------------------------------------
+ *
+ */
+
+#include "gnushogi.h"
+
+/* #define DONTUSE_HEURISTIC */
+
+short *TrP;
+
+static struct leaf  *node;
+static short sqking, sqxking;
+static short InCheck = false, GenerateAllMoves = false;
+static short check_determined = false;
+
+static short INCscore = 0;
+
+short deepsearchcut = true;
+short tas = false, taxs = false, ssa = false;
+
+short generate_move_flags = false;
+
+
+/*
+ * Ply limits for deep search cut.  No moves or drops flagged with "stupid"
+ * are considered beyond ply BEYOND_STUPID.  Only moves or drops flagged
+ * with "kingattack" are considered beyond ply BEYOND_KINGATTACK.  No moves
+ * or drops flagged with "questionable" are considered beyond ply
+ * BEYOND_QUESTIONABLE.  Only moves or drops flagged with "tesuji" are
+ * considered beyond ply BEYOND_TESUJI.  No drops are considered beyond ply
+ * BEYOND_DROP.  Exceptions: moves or drops that prevent check or give
+ * check are always considered.
+ */
+
+#define BEYOND_STUPID        0
+#define BEYOND_TIMEOUT       2
+#define BEYOND_KINGATTACK    6
+#define BEYOND_QUESTIONABLE  8
+#define BEYOND_TESUJI        8
+#define BEYOND_DROP         10
+
+#ifdef DONTUSE_HEURISTIC
+static short MaxNum[MAXDEPTH] =
+{
+    -1, 40, 80, 20, 40, 10, 5, 5, 5, 5,
+     5,  5,  5,  5,  5,  5, 5, 5, 5, 5,
+     5,  5,  5,  5,  5,  5, 5, 5, 5, 5,
+     5,  5,  5,  5,  5,  5, 5, 5, 5, 5,
+};
+#endif
+
+#ifdef HASHKEYTEST
+extern int CheckHashKey();
+extern char mvstr[4][6];
+#endif
+
+
+/*
+ * Update Arrays board[] and color[] to reflect the new board
+ * position obtained after making the move pointed to by node.
+ */
+
+inline static void
+GenMakeMove(short side,
+            short f,
+            short t,
+            short *tempb,  /* piece at to square */
+            short *tempc,  /* color of to square */
+            short promote_piece)
+{
+    short piece, upiece, n;
+
+    t = t & 0x7f;
+
+    if (f > NO_SQUARES)
+    {
+        piece = f - NO_SQUARES;
+
+        if (piece > NO_PIECES)
+            piece -= NO_PIECES;
+
+        board[t] = piece;
+        color[t] = side;
+        n = (Captured[side][piece])--;
+        UpdateDropHashbd(side, piece, n);
+        UpdateHashbd(side, piece, -1, t);
+        UpdatePieceList(side, t, ADD_PIECE);
+    }
+    else
+    {
+        *tempb = board[t];
+        *tempc = color[t];
+
+        if (*tempb != no_piece)
+        {
+            n = ++Captured[side][upiece = unpromoted[*tempb]];
+            UpdateDropHashbd(side, upiece, n);
+            UpdateHashbd(*tempc, *tempb, -1, t);
+            UpdatePieceList(*tempc, t, REMOVE_PIECE);
+        }
+
+        piece = board[f];
+        Pindex[t] = Pindex[f];
+        PieceList[side][Pindex[t]] = t;
+        color[f] = neutral;
+        board[f] = no_piece;
+        color[t] = side;
+
+        if (promote_piece)
+        {
+            UpdateHashbd(side, piece, f, -1);
+            board[t] = promoted[piece];
+            UpdateHashbd(side, board[t], -1, t);
+        }
+        else
+        {
+            board[t] = piece;
+            UpdateHashbd(side, piece, f, t);
+        }
+    }
+
+#ifdef HASHKEYTEST
+    if (CheckHashKey())
+    {
+        algbr(f, t, 0);
+        printf("error in GenMakeMove: %s\n", mvstr[0]);
+        exit(1);
+    }
+#endif
+}
+
+
+
+/*
+ * Take back a move.
+ */
+
+static void
+GenUnmakeMove(short side,
+              short f,
+              short t,
+              short tempb,
+              short tempc,
+              short promote_piece)
+{
+    short piece, upiece, n;
+
+    t = t & 0x7f;
+
+    if (f > NO_SQUARES)
+    {
+        piece = f - NO_SQUARES;
+
+        if (piece > NO_PIECES)
+            piece -= NO_PIECES;
+
+        board[t] = no_piece;
+        color[t] = neutral;
+        n = ++Captured[side][piece];
+        UpdateDropHashbd(side, piece, n);
+        UpdateHashbd(side, piece, -1, t);
+        UpdatePieceList(side, t, REMOVE_PIECE);
+    }
+    else
+    {
+        piece = board[t];
+        color[t] = tempc;
+        board[t] = tempb;
+        Pindex[f] = Pindex[t];
+        PieceList[side][Pindex[f]] = f;
+
+        if (tempb != no_piece)
+        {
+            /* FIXME: make this next line a bit more reasonable... */
+            n = (Captured[side][upiece = unpromoted[tempb]])--;
+            UpdateDropHashbd(side, upiece, n);
+            UpdateHashbd(tempc, tempb, -1, t);
+            UpdatePieceList(tempc, t, ADD_PIECE);
+        }
+
+        color[f] = side;
+
+        if (promote_piece)
+        {
+            UpdateHashbd(side, piece, -1, t);
+            board[f] = unpromoted[piece];
+            UpdateHashbd(side, board[f], f, -1);
+        }
+        else
+        {
+            board[f] = piece;
+            UpdateHashbd(side, piece, f, t);
+        }
+    }
+
+#ifdef HASHKEYTEST
+    if (CheckHashKey())
+    {
+        algbr(f, t, 0);
+        printf("error in GenUnmakeMove: %s\n", mvstr[0]);
+        exit(1);
+    }
+#endif
+}
+
+
+
+static void
+gives_check_flag(unsigned short *flags, short side, short f, short t)
+{
+    short tempb, tempc, blockable, promote_piece;
+    promote_piece = (*flags & promote) != 0;
+    GenMakeMove(side, f, t, &tempb, &tempc, promote_piece);
+
+    if (SqAttacked(sqxking, side, &blockable))
+        *flags |= check;
+
+    GenUnmakeMove(side, f, t, tempb, tempc, promote_piece);
+}
+
+
+inline static void
+Link(short side, short piece,
+     short from, short to, unsigned short local_flag, short s)
+{
+    if (*TrP == TREE)
+    {
+        ShowMessage("TREE overflow\n");
+    }
+    else
+    {
+        node->f = from;
+        node->t = (local_flag & promote) ? (to | 0x80) : to;
+        node->reply = 0;
+        node->flags = local_flag;
+        node->score = s;
+        node->INCscore = INCscore;
+
+        if (GenerateAllMoves)
+        {
+            /* FIXME: gimme a break! */
+            (*TrP)++, node++;
+        }
+        else if (InCheck)
+        {
+            /* only moves out of check */
+            short tempb, tempc, sq, threat, blockable, promote_piece;
+            promote_piece = (node->flags & promote) != 0;
+            GenMakeMove(side, node->f, node->t,
+                        &tempb, &tempc, promote_piece);
+            sq = (from == sqking) ? to : sqking;
+            threat = SqAttacked(sq, side ^ 1, &blockable);
+            GenUnmakeMove(side, node->f, node->t,
+                          tempb, tempc, promote_piece);
+
+            if (!threat)
+            {
+                /* FIXME! Gimme a break! */
+                (*TrP)++, node++;
+            }
+        }
+        else if (flag.tsume)
+        {
+            /* only moves that give check */
+            if (!(node->flags & check) && !check_determined)
+            {
+                /* determine check flag */
+                gives_check_flag(&node->flags, side, node->f, node->t);
+            }
+
+            if (node->flags & check)
+            {
+                /* FIXME! Gimme a break! */
+                (*TrP)++, node++;
+            }
+        }
+        else
+        {
+            /* FIXME! Gimme a break! */
+            (*TrP)++, node++;
+        }
+    }
+}
+
+
+inline int
+PromotionPossible(short color, short f, short t, short p)
+{
+    if (color == black)
+    {
+        if ((f < 54) && (t < 54))
+            return false;
+    }
+    else
+    {
+        if ((f > 26) && (t > 26))
+            return false;
+    }
+
+    /* FIXME: this can be simplified... */
+    switch (p)
+    {
+    case pawn:
+    case lance:
+    case knight:
+    case silver:
+    case bishop:
+    case rook:
+        return true;
+    };
+
+    return false;
+}
+
+
+inline int
+NonPromotionPossible(short color, short f,
+                     short t, short p)
+{
+    switch (p)
+    {
+    case pawn :
+        if (color == black)
+        {
+            return ((t < 72)
+                    ? true
+                    : (generate_move_flags ? ILLEGAL_TRAPPED : false));
+        }
+        else
+        {
+            return ((t > 8)
+                    ? true
+                    : (generate_move_flags ? ILLEGAL_TRAPPED : false));
+        }
+
+    case lance:
+        if (color == black)
+        {
+            return ((t < 72)
+                    ? true
+                    : (generate_move_flags ? ILLEGAL_TRAPPED : false));
+        }
+        else
+        {
+            return ((t > 8)
+                    ? true
+                    : (generate_move_flags ? ILLEGAL_TRAPPED : false));
+        }
+
+    case knight:
+        if (color == black)
+        {
+            return ((t < 63)
+                    ? true
+                    : (generate_move_flags ? ILLEGAL_TRAPPED : false));
+        }
+        else
+        {
+            return ((t > 17)
+                    ? true
+                    : (generate_move_flags ? ILLEGAL_TRAPPED : false));
+        }
+    }
+
+    return true;
+}
+
+
+#if defined FIELDBONUS || defined DROPBONUS
+
+/* bonus for possible next moves */
+
+inline static short
+field_bonus(short ply, short side, short piece,
+            short f, short t, unsigned short *local_flag)
+{
+    short s, u, ptyp;
+    unsigned char *ppos, *pdir;
+    short c1, c2;
+
+#ifdef SAVE_NEXTPOS
+    short d;
+#endif
+
+    c1 = side;
+    c2 = side ^ 1;
+    s = 0;
+    check_determined = true;
+
+    ptyp = ptype[side][piece];
+
+#ifdef SAVE_NEXTPOS
+    u = first_direction(ptyp, &d, t);
+#else
+    ppos = (*nextpos[ptyp])[t];
+    pdir = (*nextdir[ptyp])[t];
+    u = ppos[t];
+#endif
+
+    do
+    {
+        short coloru = color[u];
+
+        if (piece != king && GameCnt > 40)
+        {
+            if (distance(u, EnemyKing) <= 1)
+            {
+                /* can reach square near own king */
+                s += 2;
+                *local_flag |= kingattack;
+            }
+            else if (distance(u, OwnKing) <= 1)
+            {
+                /* can reach square near enemy king */
+                s++;
+                *local_flag |= kingattack;
+            }
+        }
+
+        if (coloru == side)
+        {
+            /* impossible next move */
+#ifdef SAVE_NEXTPOS
+            u = next_direction(ptyp, &d, t);
+#else
+            u = pdir[u];
+#endif
+        }
+        else
+        {
+            /* possible next move */
+            if (PromotionPossible(side, t, u, piece))
+            {
+                /* possible promotion in next move */
+                if (piece == pawn)
+                {
+                    s += 2;
+#ifdef TESUJIBONUS
+                    if (!InPromotionZone(side, t))
+                    {
+                        *local_flag |= tesuji; /* The dangling pawn */
+                        s++;
+                    }
+#endif
+                }
+                else
+                {
+                    s++;
+                }
+            }
+
+            if (coloru == neutral)
+            {
+                /* next move to an empty square */
+                if (u == FROMsquare)
+                {
+                    /* opponent has just left this square */
+                    s++;
+                }
+
+#ifdef SAVE_NEXTPOS
+                u = next_position(ptyp, &d, t, u);
+#else
+                u = ppos[u];
+#endif
+            }
+            else
+            {
+                /* attack opponents piece */
+#ifdef TESUJIBONUS
+                short boardu, upiece, rvupiece, rvuboard;
+#endif
+                s++;
+
+                if (u == TOsquare) /* opponent has moved to TOsquare */
+                    s++;
+
+                if ((boardu = board[u]) == king)
+                {
+                    s += 20; INCscore -= 18;
+                    *local_flag |= check; /* move threatens
+                                           * opponents king */
+                }
+
+#ifdef TESUJIBONUS
+                upiece = unpromoted[piece];
+                rvupiece = relative_value[upiece];
+                rvuboard = relative_value[unpromoted[boardu]];
+
+                if ((upiece == pawn) && (Captured[side][pawn] > 1))
+                {
+                    *local_flag |= tesuji; /* The joining pawn attack */
+                    s++;
+                }
+
+                if (rvupiece <= rvuboard)
+                {
+                    *local_flag |= tesuji; /* The striking pawn
+                                            * (piece) attack */
+
+                    if (f > NO_SQUARES)
+                        s += 2;
+                    else
+                        s++;
+
+                    if (upiece == pawn)
+                        s++;
+
+                    /* CHECKME: is this right? */
+                    if (((rvupiece == rvuboard) && (upiece == pawn))
+                        || (upiece == bishop) || (upiece == knight))
+                    {
+                        s++; /* The opposing pawn (piece) */
+
+                        if (upiece == pawn)
+                            s++;
+                    }
+                }
+#endif
+
+#ifdef SAVE_NEXTPOS
+                u = next_direction(ptyp, &d, t);
+#else
+                u = pdir[u];
+#endif
+            }
+        }
+    }
+    while (u != t);
+
+    INCscore += s;
+
+    return s;
+}
+
+#endif
+
+
+
+
+/*
+ * Add a move to the tree.  Assign a bonus to order the moves as follows:
+ * 1. Principle variation 2. Capture of last moved piece 3. Other captures
+ * (major pieces first) 4. Killer moves 5. Tesuji drops 6. Other Moves
+ * 7. Other drops. 8. Non-promoting moves
+ * If the flag.tsume is set, assign a high bonus for checks.
+ */
+
+/* inline */ void
+LinkMove(short ply, short f,
+         short t,
+         unsigned short local_flag,
+         short xside,
+         short score_if_impossible)
+{
+    short s = 0;
+    short side, piece, mv;
+    short flag_tsume, try_link = true;
+    short c1, c2, ds, is_drop = f > NO_SQUARES;
+    unsigned long as = 0;
+
+    flag_tsume = flag.tsume;
+
+    c1 = side = xside ^ 1;
+    c2 = xside;
+
+    /*
+     * Is it determined whether the move gives check ?
+     */
+
+    check_determined = ((local_flag & check) != 0);
+
+    mv = (f << 8) | ((local_flag & promote) ? (t | 0x80) : t);
+
+    if (f > NO_SQUARES)
+    {
+        piece = f - NO_SQUARES;
+
+        if (piece > NO_PIECES)
+            piece -= NO_PIECES;
+    }
+    else
+    {
+        piece = board[f];
+    }
+
+    if (score_if_impossible < 0)
+    {
+        /* The move is flagged as illegal. */
+        Link(side, piece,
+             f, t, local_flag, score_if_impossible);
+
+        return;
+    }
+
+    INCscore = 0;
+
+#ifdef HISTORY
+    s += history[hindex(side, mv)];
+#endif
+
+    /* If we're running short of tree nodes, go into tsume mode. */
+
+    if (!(local_flag & capture))
+    {
+        if (*TrP > (TREE - 300))
+        {
+            /* too close to tree table limit */
+            flag.tsume = true;
+        }
+    }
+
+    /* Guess strength of move and set flags. */
+
+    if ((piece != king) && (!in_opening_stage))
+    {
+        if (distance(t, EnemyKing) <= 1)
+        {
+            /* bonus for square near enemy king */
+            s += 15;
+            INCscore += 2;
+            local_flag |= kingattack;
+        }
+        else if (distance(t, OwnKing) <= 1)
+        {
+            /* bonus for square near own king */
+            s += 10;
+            INCscore++;
+            local_flag |= kingattack;
+        }
+    }
+
+    if (tas)  /* own attack array available */
+    {
+        /* square t defended by own piece (don't count piece to move) ? */
+        if (is_drop
+            ? (as = attack[side][t])
+            : (as = ((attack[side][t] & CNT_MASK) > 1)))
+            s += (ds = in_endgame_stage ? 100 : 10);
+    }
+
+    if (taxs)  /* opponents attack array available */
+    {
+        /* square t not threatened by opponent or
+         * defended and only threatened by opponents king ?
+         */
+        unsigned long axs;
+
+        if (!(axs = attack[xside][t])
+            || (tas && as && (axs & control[king]) && (axs & CNT_MASK) == 1))
+        {
+            /* FIXME: this is a mess; clean up. */
+            s += (ds = in_endgame_stage
+                  ? 200
+                  : (is_drop
+                     ? (InPromotionZone(side, t)
+                        ? 40 + relative_value[piece]
+                        : 10)
+                     : 20));
+        }
+    }
+
+    /* target square near area of action */
+
+    if (TOsquare >= 0)
+        s += (9 - distance(TOsquare, t));
+
+    if (FROMsquare >= 0)
+        s += (9 - distance(FROMsquare, t)) / 2;
+
+    /* target square near own or enemy king */
+
+    if (!in_opening_stage && piece != king)
+    {
+        if (balance[c1] < 50)
+            s += (9 - distance(EnemyKing, t)) * (50 - balance[c1]) / 20;
+        else
+            s += (9 - distance(OwnKing, t)) * (balance[c1] - 50) / 20;
+    }
+
+    if (f > NO_SQUARES)
+    {
+        /* bonus for drops, in order to place
+         * drops before questionable moves */
+        s += in_endgame_stage ? 25 : 10;
+
+        if (t == FROMsquare)
+        {
+            /* drop to the square the opponent has just left */
+            s += 5;
+        };
+
+        if (piece == gold)
+            s -= 32 / Captured[side][gold];
+        else if (piece == silver)
+            s -= 16 / Captured[side][silver];
+
+#if defined DROPBONUS
+        s += field_bonus(ply, side, piece, f, t, &local_flag);
+
+        if (s == 10 && piece != pawn)
+            local_flag |= questionable;
+#endif
+    }
+    else
+    {
+        /* bonus for moves (non-drops) */
+        int consider_last = false;
+
+        if (in_endgame_stage && Captured[side][gold])
+            s += 10;
+
+        s += 20;
+
+        if (t == FROMsquare)
+        {
+            /* move to the square the opponent has just left */
+            s += in_endgame_stage ? 10 : 1;
+        }
+
+        if (color[t] != neutral)
+        {
+            /* Captures */
+            if (in_endgame_stage)
+            {
+                s += relative_value[board[t]] - relative_value[piece];
+            }
+            else
+            {
+                s += (*value)[stage][board[t]] - relative_value[piece];
+            }
+
+            if (t == TOsquare) /* Capture of last moved piece */
+                s += in_endgame_stage ? 5 : 50;
+        }
+
+        if (local_flag & promote)
+        {
+            /* bonus for promotions */
+            s++;
+            INCscore += value[stage][promoted[piece]] - value[stage][piece];
+        }
+        else
+        {
+            /* bonus for non-promotions */
+            if (PromotionPossible(side, f, t, piece))
+            {
+#ifdef TESUJIBONUS
+                /* Look at non-promoting silver or knight */
+                if (piece == silver || piece == knight)
+                {
+                    local_flag |= tesuji; /* Non-promotion */
+                    s++;
+                }
+                else
+#endif
+                {
+                    consider_last = true;
+
+                    if (piece == pawn || piece == bishop || piece == rook)
+                    {
+                        local_flag |= stupid;
+                        INCscore -= 20;
+                    }
+                    else
+                    {
+                        local_flag |= questionable;
+                        INCscore -= 10;
+                    }
+                }
+            }
+        }
+
+        if (consider_last)
+        {
+            if (local_flag & stupid)
+                s = 0;
+            else
+                s = s % 20;
+        }
+        else
+        {
+#if defined FIELDBONUS
+            s += field_bonus(ply, side, piece, f, t, &local_flag);
+#endif
+        }
+    }
+
+#if defined CHECKBONUS
+    /* determine check flag */
+    if (!(local_flag & check) && !check_determined)
+    {
+        gives_check_flag(&local_flag, side, f, t);
+
+        if (local_flag & check)
+            s += 20;
+    }
+#endif
+
+    /* check conditions for deep search cut (flag.tsume = true) */
+
+#ifdef DEEPSEARCHCUT
+    if (!flag.tsume && deepsearchcut)
+    {
+        if ((ply > BEYOND_STUPID) && (local_flag & stupid))
+        {
+            try_link = flag.force || ((ply == 1) && (side != computer));
+        }
+        else if (hard_time_limit && (ply > BEYOND_TIMEOUT) && flag.timeout)
+        {
+            flag.tsume = true;
+        }
+        else if ((ply > BEYOND_KINGATTACK) && !(local_flag & kingattack))
+        {
+            flag.tsume = true;
+        }
+        else if ((ply > BEYOND_QUESTIONABLE) && (local_flag & questionable))
+        {
+            flag.tsume = true;
+#ifdef TESUJIBONUS
+        }
+        else if ((ply > BEYOND_TESUJI) && !(local_flag & tesuji))
+        {
+            flag.tsume = true;
+#endif
+        }
+        else if ((ply > BEYOND_DROP) && (f > NO_SQUARES))
+        {
+            flag.tsume = true;
+        }
+    }
+#endif
+
+    if (try_link || GenerateAllMoves)
+    {
+        Link(side, piece, f, t, local_flag,
+             s - ((SCORE_LIMIT + 1000) * 2));
+    }
+
+    flag.tsume = flag_tsume;
+}
+
+
+
+short
+DropPossible(short piece, short side, short sq)
+{
+    short r = row(sq), possible = true;
+
+    if (board[sq] != no_piece)
+    {
+        possible = false;
+    }
+    else if (piece == pawn)
+    {
+        if ((side == black) && (r == 8))
+        {
+            possible = (generate_move_flags ? ILLEGAL_TRAPPED : false);
+        }
+        else if ((side == white) && (r == 0))
+        {
+            possible = (generate_move_flags ? ILLEGAL_TRAPPED : false);
+        }
+        else if (PawnCnt[side][column(sq)])
+        {
+            possible = (generate_move_flags ? ILLEGAL_DOUBLED : false);
+        }
+
+        /* Pawn drops are invalid if they mate the opponent. */
+        if (possible)
+        {
+            short f, tempb, tempc;
+            f = pawn + NO_SQUARES;
+
+            if (side == white)
+                f += NO_PIECES;
+
+            GenMakeMove(side, f, sq, &tempb, &tempc, false);
+
+            if (IsCheckmate(side^1, -1, -1))
+                possible = (generate_move_flags ? ILLEGAL_MATE : false);
+
+            GenUnmakeMove(side, f, sq, tempb, tempc, false);
+        }
+    }
+    else if (piece == lance)
+    {
+        if ((side == black) && (r == 8))
+            possible = (generate_move_flags ? ILLEGAL_TRAPPED : false);
+        else if ((side == white) && (r == 0))
+            possible = (generate_move_flags ? ILLEGAL_TRAPPED : false);
+    }
+    else if (piece == knight)
+    {
+        if ((side == black) && (r >= 7))
+            possible = (generate_move_flags ? ILLEGAL_TRAPPED : false);
+        else if ((side == white) && (r <= 1))
+            possible = (generate_move_flags ? ILLEGAL_TRAPPED : false);
+    }
+
+    return possible;
+}
+
+
+#if defined DONTUSE_HEURISTIC
+static void
+SortMoves(short ply)
+{
+    short p;
+
+    for (p = TrPnt[ply]; p < TrPnt[ply+1]; p++)
+        pick(p, TrPnt[ply+1] - 1);
+}
+#endif /* DONTUSE_HEURISTIC */
+
+
+#ifdef DONTUSE_HEURISTIC
+
+static void
+DontUseMoves(short ply, short n)
+{
+    struct leaf  *p;
+    short i, k;
+
+   /* k = number of check moves + number of captures */
+    for (i = TrPnt[ply], k = 0; i < TrPnt[ply+1]; i++)
+    {
+        p = &Tree[i];
+
+        if ((p->flags & check) || (p->flags & capture))
+        {
+            if (++k >= n)
+                return;
+        }
+    }
+
+    /* use n moves */
+    for (i = TrPnt[ply]; i < TrPnt[ply+1]; i++)
+    {
+        p = &Tree[i];
+
+        if (!((p->flags & check) || (p->flags & capture)))
+        {
+            if (k < n)
+                k++;
+            else
+            {
+                p->score = DONTUSE;
+            }
+        }
+    }
+}
+
+#endif
+
+
+
+/*
+ * Generate moves for a piece. The moves are taken from the precalculated
+ * array nextpos/nextdir.  If the board is free, next move is chosen from
+ * nextpos else from nextdir.
+ */
+
+inline void
+GenMoves(short ply, short sq, short side,
+         short xside)
+{
+    short u, piece;
+    short ptyp, possible;
+#ifdef SAVE_NEXTPOS
+    short d;
+#else
+    unsigned char *ppos, *pdir;
+#endif
+
+    piece = board[sq];
+    ptyp = ptype[side][piece];
+
+#ifdef SAVE_NEXTPOS
+    u = first_direction(ptyp, &d, sq);
+#else
+    ppos = (*nextpos[ptyp])[sq];
+    pdir = (*nextdir[ptyp])[sq];
+    u = ppos[sq];
+#endif
+
+    do
+    {
+        unsigned short local_flag;
+        short  c;
+
+        if ((c = color[u]) == xside)
+            local_flag = capture;
+        else
+            local_flag = 0;
+
+        if (c != side && board[u] != king)
+        {
+            if (PromotionPossible(color[sq], sq, u, piece))
+            {
+                LinkMove(ply, sq, u, local_flag | promote, xside, true);
+
+                if ((possible
+                     = NonPromotionPossible(color[sq], sq, u, piece)))
+                {
+                    LinkMove(ply, sq, u, local_flag, xside, possible);
+                }
+            }
+            else
+            {
+                LinkMove(ply, sq, u, local_flag, xside, true);
+            }
+        }
+
+        if (c == neutral)
+#ifdef SAVE_NEXTPOS
+        {
+            u = next_position(ptyp, &d, sq, u);
+        }
+        else
+        {
+            u = next_direction(ptyp, &d, sq);
+        }
+#else
+        {
+            u = ppos[u];
+        }
+        else
+        {
+            u = pdir[u];
+        }
+#endif
+    }
+    while (u != sq);
+}
+
+
+
+
+/*
+ * Drop each piece in hand of "side" to square "u" (if allowed).
+ */
+
+static void
+DropToSquare(short side, short xside, short ply, short u)
+{
+    short i, possible;
+
+    for (i = pawn; i < king; i++)
+    {
+        if (Captured[side][i])
+        {
+            if ((possible = DropPossible(i, side, u)))
+            {
+                short f;
+                f = NO_SQUARES + i;
+
+                if (side == white)
+                    f += NO_PIECES;
+
+                LinkMove(ply, f, u, (dropmask | i), xside, possible);
+            }
+        }
+    }
+}
+
+
+
+/*
+ * Add drops of side that prevent own king from being in check
+ * from xside's sweeping pieces.
+ */
+
+static void
+LinkPreventCheckDrops(short side, short xside, short ply)
+{
+#ifdef SAVE_NEXTPOS
+    short d, dd;
+#else
+    unsigned char *ppos, *pdir;
+#endif
+    short piece, u, xu, square, ptyp;
+    short n, drop_square[9];
+
+    if (board[square = PieceList[side][0]] != king)
+        return;
+
+    for (piece = lance; piece <= rook; piece++)
+    {
+        if (piece == lance || piece == bishop || piece == rook)
+        {
+            /* check for threat of xside piece */
+            ptyp = ptype[side][piece];
+            n = 0;
+#ifdef SAVE_NEXTPOS
+            u = first_direction(ptyp, &d, square);
+#else
+            ppos = (*nextpos[ptyp])[square];
+            pdir = (*nextdir[ptyp])[square];
+            u = ppos[square];
+#endif
+
+            do
+            {
+                if (color[u] == neutral)
+                {
+#ifdef SAVE_NEXTPOS
+                    dd = d;
+                    xu = next_position(ptyp, &d, square, u);
+
+                    if (xu == next_direction(ptyp, &dd, square))
+                    {
+                        n = 0;  /* oops new direction */
+                    }
+                    else
+                    {
+                        drop_square[n++] = u;
+                    }
+#else
+
+                    if ((xu = ppos[u]) == pdir[u])
+                    {
+                        n = 0;  /* oops new direction */
+                    }
+                    else
+                    {
+                        drop_square[n++] = u;
+                    }
+#endif
+                    u = xu;
+                }
+                else
+                {
+                    if (color[u] == xside && (unpromoted[board[u]] == piece))
+                    {
+                        /* king is threatened by opponents piece */
+                        while (n > 0)
+                        {
+                            DropToSquare(side, xside, ply, drop_square[--n]);
+                        }
+                    }
+                    else
+                    {
+                        n = 0;
+                    }
+#ifdef SAVE_NEXTPOS
+                    u = next_direction(ptyp, &d, square);
+#else
+                    u = pdir[u];
+#endif
+                }
+            }
+            while (u != square);
+        }
+    }
+}
+
+
+
+/*
+ * Add drops that check enemy king.
+ */
+
+static void
+LinkCheckDrops(short side, short xside, short ply)
+{
+#ifdef SAVE_NEXTPOS
+    short d;
+#else
+    unsigned char *ppos, *pdir;
+#endif
+    short u, ptyp;
+    short square, piece;
+
+    if (board[square = PieceList[xside][0]] != king)
+        return;
+
+    for (piece = pawn; piece < king; piece++)
+    {
+        if (Captured[side][piece])
+        {
+            /*
+             * "side" has "piece" in hand. Try to make a piece move from
+             * opponents king square and drop this piece to each reachable
+             * empty square. This definitely gives check!  For a pawn drop
+             * it must not double pawns and it must not be checkmate!
+             */
+
+            ptyp = ptype[xside][piece];
+#ifdef SAVE_NEXTPOS
+            u = first_direction(ptyp, &d, square);
+#else
+            ppos = (*nextpos[ptyp])[square];
+            pdir = (*nextdir[ptyp])[square];
+            u = ppos[square];
+#endif
+            do
+            {
+                if (color[u] == neutral)
+                {
+                    if (piece != pawn || DropPossible(pawn, side, u))
+                    {
+                        short f;
+                        f = NO_SQUARES + piece;
+
+                        if (side == white)
+                            f += NO_PIECES;
+
+                        LinkMove(ply, f, u,
+                                 (dropmask | piece | check), xside, true);
+                    }
+
+#ifdef SAVE_NEXTPOS
+                    u = next_position(ptyp, &d, square, u);
+#else
+                    u = ppos[u];
+#endif
+                }
+                else
+                {
+#ifdef SAVE_NEXTPOS
+                    u = next_direction(ptyp, &d, square);
+#else
+                    u = pdir[u];
+#endif
+                }
+            }
+            while (u != square);
+        }
+    }
+}
+
+
+
+/*
+ * Fill the array Tree[] with all available moves for side to play. Array
+ * TrPnt[ply] contains the index into Tree[] of the first move at a ply.
+ * in_check = 0 side is not in check
+ * in_check > 1 side is in check
+ * in_check < 0 don't know
+ * in_check -2 indicates move generation for book moves
+ */
+
+void
+MoveList(short side, short ply,
+         short in_check, short blockable)
+{
+    short i, xside, u;
+    struct leaf  *firstnode;
+    short flag_tsume, num;
+
+#ifdef HISTORY
+    unsigned short hiHt = 0, hi0 = 0, hi1 = 0, hi2 = 0, hi3 = 0, hi4 = 0;
+#endif
+
+    flag_tsume = flag.tsume;
+
+    xside = side ^ 1;
+
+    sqking  = PieceList[side][0];
+    sqxking = PieceList[xside][0];
+
+    if (in_check >= 0)
+    {
+        InCheck = in_check;
+    }
+    else
+    {
+        InCheck = (board[sqking] == king)
+            ? SqAttacked(sqking, xside, &blockable)
+            : false;
+    }
+
+    GenerateAllMoves = (in_check == -2) || generate_move_flags;
+
+    if (InCheck /* && (ply > 1 || side == computer) */)
+    {
+        /* Own king in check */
+        flag.tsume = true;
+    }
+
+    TrP = &TrPnt[ply + 1];
+    *TrP = TrPnt[ply];
+
+    firstnode = node = &Tree[*TrP];
+
+    if (!PV)
+        Swag0 = killr0[ply];
+    else
+        Swag0 = PV;
+
+    Swag1 = killr1[ply];
+    Swag2 = killr2[ply];
+    Swag3 = killr3[ply];
+
+    if (ply > 2)
+        Swag4 = killr1[ply - 2];
+    else
+        Swag4 = 0;
+
+#ifdef HISTORY
+    if (use_history)
+    {
+        history[hiHt = hindex(side, SwagHt)] += 5000;
+        history[hi0  = hindex(side, Swag0)]  += 2000;
+        history[hi1  = hindex(side, Swag1)]  += 60;
+        history[hi2  = hindex(side, Swag2)]  += 50;
+        history[hi3  = hindex(side, Swag3)]  += 40;
+        history[hi4  = hindex(side, Swag4)]  += 30;
+    }
+#endif
+
+    for (i = PieceCnt[side]; i >= 0; i--)
+        GenMoves(ply, PieceList[side][i], side, xside);
+
+    if (!InCheck || blockable)
+    {
+        if (flag.tsume)
+        {
+            /* special drop routine for tsume problems */
+            if (InCheck)
+                LinkPreventCheckDrops(side, xside, ply);
+            else
+                LinkCheckDrops(side, xside, ply);
+        }
+        else
+        {
+            for (u = 0; u < NO_SQUARES; u++)
+                DropToSquare(side, xside, ply, u);
+        }
+    }
+
+#ifdef HISTORY
+    if (use_history)
+    {
+        history[hiHt] -= 5000;
+        history[hi0]  -= 2000;
+        history[hi1]  -= 60;
+        history[hi2]  -= 50;
+        history[hi3]  -= 40;
+        history[hi4]  -= 30;
+    }
+#endif
+
+    SwagHt = 0;           /* SwagHt is only used once */
+
+    if (flag.tsume && node == firstnode)
+        (*TrP)++;
+
+    GenCnt += (num = (TrPnt[ply+1] - TrPnt[ply]));
+
+#ifdef DONTUSE_HEURISTIC
+    /* remove some nodes in case of wide spread in depth */
+    if (!flag.tsume && (i = MaxNum[ply]) > 0 && num > i)
+    {
+        SortMoves(ply);
+        DontUseMoves(ply, i);
+    }
+#endif
+
+    flag.tsume = flag_tsume;
+}
+
+
+
+/*
+ * Fill the array Tree[] with all available captures for side to play.  If
+ * there is a non-promote option, discard the non-promoting move.  Array
+ * TrPnt[ply] contains the index into Tree[] of the first move at a ply.
+ * If flag.tsume is set, only add captures (but also the non-promoting)
+ * that threaten the opponents king.
+ *
+ * in_check = 0: side is not in check
+ * in_check > 1: side is in check
+ * in_check < 0: we don't know
+ */
+
+void
+CaptureList(short side, short ply,
+            short in_check, short blockable)
+{
+    short u, sq, xside;
+#ifdef SAVE_NEXTPOS
+    short d;
+#else
+    unsigned char *ppos, *pdir;
+#endif
+    short i, piece, flag_tsume;
+    small_short *PL;
+
+    xside = side ^ 1;
+
+    TrP = &TrPnt[ply + 1];
+    *TrP = TrPnt[ply];
+    node = &Tree[*TrP];
+
+    flag_tsume = flag.tsume;
+
+    sqking = PieceList[side][0];
+    sqxking = PieceList[xside][0];
+
+    if (in_check >= 0)
+    {
+        InCheck = in_check;
+    }
+    else
+    {
+        InCheck = (board[sqking] == king)
+            ? SqAttacked(sqking, xside, &blockable)
+            : false;
+    }
+
+    GenerateAllMoves = (in_check == -2);
+
+    if (InCheck && (ply > 1 || side == computer))
+    {
+        /* Own king is in check */
+        flag.tsume = true;
+    }
+
+    check_determined = false;
+
+    PL = PieceList[side];
+
+    for (i = 0; i <= PieceCnt[side]; i++)
+    {
+        short ptyp;
+        sq = PL[i];
+        piece = board[sq];
+        ptyp = ptype[side][piece];
+#ifdef SAVE_NEXTPOS
+        u = first_direction(ptyp, &d, sq);
+#else
+        ppos = (*nextpos[ptyp])[sq];
+        pdir = (*nextdir[ptyp])[sq];
+        u = ppos[sq];
+#endif
+        do
+        {
+            if (color[u] == neutral)
+            {
+#ifdef SAVE_NEXTPOS
+                u = next_position(ptyp, &d, sq, u);
+#else
+                u = ppos[u];
+#endif
+            }
+            else
+            {
+                if (color[u] == xside && board[u] != king)
+                {
+                    short PP;
+
+                    if ((PP = PromotionPossible(color[sq], sq, u, piece)))
+                    {
+                        Link(side, piece,
+                             sq, u, capture | promote,
+                             (*value)[stage][board[u]]
+#if !defined SAVE_SVALUE
+                             + svalue[board[u]]
+#endif
+                             - relative_value[piece]);
+                    }
+
+                    if (!PP || flag.tsume)
+                    {
+                        Link(side, piece,
+                             sq, u, capture,
+                             (*value)[stage][board[u]]
+#if !defined SAVE_SVALUE
+                             + svalue[board[u]]
+#endif
+                             - relative_value[piece]);
+                    }
+                }
+
+#ifdef SAVE_NEXTPOS
+                u = next_direction(ptyp, &d, sq);
+#else
+                u = pdir[u];
+#endif
+            }
+        }
+        while (u != sq);
+    }
+
+    flag.tsume = flag_tsume;
+
+    SwagHt = 0;           /* SwagHt is only used once */
+}
+
+
+
+
+/*
+ * If the king is in check, try to find a move that prevents check.
+ * If such a move is found, return false, otherwise return true.
+ * in_check = 0: side is not in check
+ * in_check > 1: side is in check
+ * in_check < 0: don't know
+ * blockable > 0 && check: check can possibly be prevented by a drop
+ * blockable = 0 && check: check can definitely not be prevented by a drop
+ * blockable < 0 && check: nothing known about type of check
+ */
+
+short
+IsCheckmate(short side, short in_check, short blockable)
+{
+    short u, sq, xside;
+#ifdef SAVE_NEXTPOS
+    short d;
+#else
+    unsigned char *ppos, *pdir;
+#endif
+    short i, piece;
+    small_short *PL;
+    short tempb, tempc, ksq, threat, dummy, sqking;
+    short InCheck;
+
+    xside = side ^ 1;
+
+    sqking = PieceList[side][0];
+
+    /*
+     * Checkmate is possible only if king is in check.
+     */
+
+    if (in_check >= 0)
+        InCheck = in_check;
+    else if (board[sqking] == king)
+        InCheck = SqAttacked(sqking, xside, &blockable);
+    else
+        InCheck = false;
+
+    if (!InCheck)
+        return false;
+
+    /*
+     * Try to find a move that prevents check.
+     */
+
+    PL = PieceList[side];
+
+    for (i = 0; i <= PieceCnt[side]; i++)
+    {
+        short ptyp;
+        sq = PL[i];
+        piece = board[sq];
+        ptyp = ptype[side][piece];
+#ifdef SAVE_NEXTPOS
+        u = first_direction(ptyp, &d, sq);
+#else
+        ppos = (*nextpos[ptyp])[sq];
+        pdir = (*nextdir[ptyp])[sq];
+        u = ppos[sq];
+#endif
+        do
+        {
+            if (color[u] == neutral || color[u] == xside)
+            {
+                GenMakeMove(side, sq, u, &tempb, &tempc, false);
+                ksq = (sq == sqking) ? u : sqking;
+                threat = SqAttacked(ksq, xside, &dummy);
+                GenUnmakeMove(side, sq, u, tempb, tempc, false);
+
+                if (!threat)
+                    return false;
+            }
+
+#ifdef SAVE_NEXTPOS
+            u = (color[u] == neutral)
+                ? next_position(ptyp, &d, sq, u)
+                : next_direction(ptyp, &d, sq);
+#else
+            u = (color[u] == neutral) ? ppos[u] : pdir[u];
+#endif
+        }
+        while (u != sq);
+    }
+
+    /*
+     * Couldn't find a move that prevents check.
+     * Try to find a drop that prevents check.
+     */
+
+    if (blockable != 0)
+    {
+        for (piece = king - 1; piece >= pawn; piece--)
+        {
+            if (Captured[side][piece])
+            {
+                for (u = 0; u < NO_SQUARES; u++)
+                {
+                    if (DropPossible(piece, side, u))
+                    {
+                        short f;
+                        f = NO_SQUARES + piece;
+
+                        if (side == white)
+                            f += NO_PIECES;
+
+                        GenMakeMove(side, f, u, &tempb, &tempc, false);
+                        threat = SqAttacked(sqking, xside, &dummy);
+                        GenUnmakeMove(side, f, u, tempb, tempc, false);
+
+                        if (!threat)
+                            return false;
+                    }
+                }
+
+                /*
+                 * If the piece could be dropped at any square, it is
+                 * impossible for any other piece drop to prevent check.
+                 * Drops are restricted for pawns, lances, and knights.
+                 */
+
+                if (piece > knight)
+                    break;
+            }
+        }
+    }
+
+    return true;
+
+}
+
+