// The following one-ply board evaluation function is adapted from the
        // "xtris" application (multi-player Tetris for the X Window system),
        // created by Roger Espel Llima <*****@*****.**>
        //
        // From the "xtris" documentation:
        //
        //   "The values for the coefficients were obtained with a genetic algorithm
        //   using a population of 50 sets of coefficients, calculating 18 generations
        //   in about 500 machine-hours distributed among 20-odd Sparc workstations.
        //   This resulted in an average of about 50,000 completed lines."
        //
        // The following people contributed "ideas for the bot's decision algorithm":
        //
        // Laurent Bercot      <*****@*****.**>
        // Sebastien Blondeel  <*****@*****.**>
        //
        //
        // The algorithm computes 6 values on the whole pile:
        //
        // [1] height   = max height of the pieces in the pile
        // [2] holes    = number of holes (empty positions with a full position somewhere
        //                above them)
        // [3] frontier = length of the frontier between all full and empty zones
        //		            (for each empty position, add 1 for each side of the position
        //		            that touches a border or a full position).
        // [4] drop     = how far down we're dropping the current brick
        // [5] pit      = sum of the depths of all places where a long piece ( ====== )
        //                would be needed.
        // [6] ehole    = a kind of weighted sum of holes that attempts to calculate
        //                how hard they are to fill.
        //
        // droppedPieceRow is the row where we're dropping the piece,
        // which is already in the board.  Note that full lines have not been
        // dropped, so we need to do special tests to skip them.


        void PrivateStrategyEvaluate
        (
            STBoard board,
            STPiece piece,
            ref double rating
        )
        {
            rating = 0.0;

            if (false == piece.IsValid( ))
            {
                return;
            }

            int width  = 0;
            int height = 0;

            width  = board.GetWidth( );
            height = board.GetHeight( );


            int pieceDropDistance = 0;

            pieceDropDistance = (height - piece.GetY());



            int coeff_f      = 260;
            int coeff_height = 110;
            int coeff_hole   = 450;
            int coeff_y      = 290;
            int coeff_pit    = 190;
            int coeff_ehole  = 80;


            int[] lineCellTotal = new int [(1 + 20 + 200)];
            int[] lin           = new int [(1 + 20 + 200)];
            int[] hol           = new int  [(1 + 10 + 200) * (1 + 20 + 400)];
            int[] blockedS      = new int[(1 + 10 + 200)];

            // If width is greater than 200, or height is greater than 400,
            // just give up. Really, this algorithm needs to be repaired to
            // avoid the use of memory!
            if (width > 200)
            {
                return;
            }
            if (height > 400)
            {
                return;
            }



            int x = 0;
            int y = 0;


            // NOTE: ALL ARRAYS ARE ACCESSED WITH 0 BEING FIRST ELEMENT

            // Fill lineCellTotal[] with total cells in each row.

            for (y = 1; y <= height; y++)
            {
                lineCellTotal[(y - 1)] = 0;
                for (x = 1; x <= width; x++)
                {
                    if (board.GetCell(x, y) > 0)
                    {
                        lineCellTotal[(y - 1)]++;
                    }
                }
            }



            // Clobber blocked column array

            for (x = 1; x <= width; x++)
            {
                blockedS[(x - 1)] = (-1);
            }



            // Clear Lin array.

            for (y = 1; y <= height; y++)
            {
                lin[(y - 1)] = 0;
            }



            // Embedded Holes

            int eHoles = 0;

            for (y = height; y >= 1; y--)               // Top-to-Bottom
            {
                for (x = 1; x <= width; x++)
                {
                    if (board.GetCell(x, y) > 0)
                    {
                        hol     [(width * (y - 1)) + (x - 1)] = 0;
                        blockedS[(x - 1)] = y;
                    }
                    else
                    {
                        hol     [(width * (y - 1)) + (x - 1)] = 1;
                        if (blockedS[(x - 1)] >= 0)
                        {
                            int y2 = 0;

                            y2 = blockedS[(x - 1)];

                            // If this more than two rows ABOVE current row, set
                            // to exactly two rows above.
                            if (y2 > (y + 2))
                            {
                                y2 = (y + 2);
                            }

                            // Descend to current row

                            for ( ; y2 > y; y2--)
                            {
                                if (board.GetCell(x, y2) > 0)
                                {
                                    hol[(width * (y - 1)) + (x - 1)] += lin[(y2 - 1)];
                                }
                            }
                        }
                        lin[(y - 1)] += hol[(width * (y - 1)) + (x - 1)];
                        eHoles       += hol[(width * (y - 1)) + (x - 1)];
                    }
                }
            }



            // Determine Max Height

            int maxHeight = 0;

            for (x = 1; x <= width; x++)
            {
                for (y = height; y >= 1; y--)                   // Top-to-Bottom
                {
                    // If line is complete, ignore it for Max Height purposes...
                    if (width == lineCellTotal[(y - 1)])
                    {
                        continue;
                    }

                    if ((y > maxHeight) &&
                        (board.GetCell(x, y) > 0))
                    {
                        maxHeight = y;
                    }
                }
            }



            // Count buried holes

            int holes   = 0;
            int blocked = 0;

            for (x = 1; x <= width; x++)
            {
                blocked = 0;

                for (y = height; y >= 1; y--)                   // Top-to-Bottom
                {
                    // If line is complete, skip it!
                    if (width == lineCellTotal[(y - 1)])
                    {
                        continue;
                    }

                    if (board.GetCell(x, y) > 0)
                    {
                        blocked = 1;                          // We encountered an occupied cell; all below is blocked
                    }
                    else
                    {
                        // All of the following is in the context of the cell ( x, y )
                        // being UN-occupied.

                        // If any upper row had an occupied cell in this column, this
                        // unoccupied cell is considered blocked.
                        if (0 != blocked)
                        {
                            holes++;                             // This unoccupied cell is buried; it's a hole.
                        }
                    }
                }
            }



            // Count Frontier

            int frontier = 0;

            for (x = 1; x <= width; x++)
            {
                for (y = height; y >= 1; y--)                   // Top-to-Bottom
                {
                    // If line is complete, skip it!
                    if (width == lineCellTotal[(y - 1)])
                    {
                        continue;
                    }

                    if (0 == board.GetCell(x, y))
                    {
                        // All of the following is in the context of the cell ( x, y )
                        // being UN-occupied.

                        // If row is not the top, and row above this one is occupied,
                        // then this unoccupied cell counts as a frontier.
                        if ((y < height) &&
                            (board.GetCell(x, (y + 1)) > 0))
                        {
                            frontier++;
                        }

                        // If this row is not the bottom, and the row below is occupied,
                        // this unoccupied cell counts as a frontier.
                        if ((y > 1) &&
                            (board.GetCell(x, (y - 1)) > 0))
                        {
                            frontier++;
                        }

                        // If the column is not the first, and the column to the left is
                        // occupied, then this unoccupied cell counts as a frontier.
                        // Or, if this *is* the left-most cell, it is an automatic frontier.
                        //  (since the beyond the board is in a sense "occupied")
                        if (((x > 1) &&
                             (board.GetCell(x - 1, y) > 0)) ||
                            (1 == x))
                        {
                            frontier++;
                        }

                        // If the column is not the right-most, and the column to the right is
                        // occupied, then this unoccupied cell counts as a frontier.
                        // Or, if this *is* the right-most cell, it is an automatic frontier.
                        //  (since the beyond the board is in a sense "occupied")
                        if (((x < width) &&
                             (board.GetCell(x + 1, y) > 0)) ||
                            (width == x))
                        {
                            frontier++;
                        }
                    }
                }
            }



            int v = 0;

            for (x = 1; x <= width; x++)
            {
                // NOTE: The following seems to descend as far as a 2-column-wide
                //       profile can fall for each column.
                // Scan Top-to-Bottom
                y = height;
                while
                (
                    // Line is not below bottom row...
                    (y >= 1) &&
                    // Cell is unoccupied or line is full...
                    ((0 == board.GetCell(x, y)) ||
                     (width == lineCellTotal[(y - 1)])) &&

                    (
                        // (Not left column AND (left is empty OR line full))
                        ((x > 1) &&
                         ((0 == board.GetCell(x - 1, y)) ||
                          (width == lineCellTotal[(y - 1)])))
                        ||                 // ...OR...
                        // (Not right column AND (right is empty OR line full))
                        ((x < width) &&
                         ((0 == board.GetCell(x + 1, y)) ||
                          (width == lineCellTotal[(y - 1)]))))
                )
                {
                    y--;                     // Descend
                }

                // Count how much further we can fall just considering obstacles
                // in our column.
                int p = 0;
                p = 0;
                for (  ;
                       ((y >= 1) && (0 == board.GetCell(x, y)));
                       y--, p++)
                {
                    ;
                }

                // If this is a deep well, it's worth punishing.
                if (p >= 2)
                {
                    v -= (coeff_pit * (p - 1));
                }
            }



            // compute rating by summing weighted factors
            rating  = (float)(v);
            rating -= (float)(coeff_f * frontier);
            rating -= (float)(coeff_height * maxHeight);
            rating -= (float)(coeff_hole * holes);
            rating -= (float)(coeff_ehole * eHoles);
            rating += (float)(coeff_y * pieceDropDistance);                    // Reward drop depth!
        }