private static double EvaluteMove(IBoard board, IPiece piece) { int pieceMinX; int pieceMinY; int pieceMaxX; int pieceMaxY; piece.GetAbsoluteBoundingRectangle(out pieceMinX, out pieceMinY, out pieceMaxX, out pieceMaxY); int totalHeight = BoardHelper.GetTotalCellHeight(board); int completedRows = BoardHelper.GetTotalCompletedRows(board); int totalHoles = Enumerable.Range(1, board.Width).Aggregate(0, (i, i1) => i + BoardHelper.GetBuriedHolesForColumn(board, i1)); int totalBlockades = Enumerable.Range(1, board.Width).Aggregate(0, (i, i1) => i + BoardHelper.GetBlockadesForColumn(board, i1)); int edgeTouchingAnotherBlock = 0; // TODO int edgeTouchingWall = 0; // TODO int edgeTouchingFloor = 0; //double rating = 0; //rating += -0.03 * totalHeight; //rating += -7.5 * totalHoles; //rating += -3.5 * totalBlockades; //rating += 8.0 * completedRows; //rating += 3.0 * edgeTouchingAnotherBlock; //rating += 2.5 * edgeTouchingWall; //rating += 5.0 * edgeTouchingFloor; //double rating = 0; //rating += -3.78 * totalHeight; //rating += -2.31 * totalHoles; //rating += -0.59 * totalBlockades; //rating += 1.6 * completedRows; //rating += 3.97 * edgeTouchingAnotherBlock; //rating += 6.52 * edgeTouchingWall; //rating += 0.65 * edgeTouchingFloor; double rating = 0; rating += -0.868099 * totalHeight; rating += -2.45402 * totalHoles; rating += -0.236702 * totalBlockades; rating += 3.59764 * completedRows; rating += 5.33378 * edgeTouchingAnotherBlock; rating += 8.20521 * edgeTouchingWall; rating += 0.00 * edgeTouchingFloor; return(rating); }
// The following evaluation function was adapted from Pascal code submitted by: // Pierre Dellacherie (France). (E-mail : [email protected]) // // This amazing one-piece algorithm completes an average of roughly 600 000 // rows, and often attains 2 000 000 or 2 500 000 rows. However, the algorithm // sometimes completes as few as 15 000 rows. I am fairly certain that this // is NOT due to statistically abnormal patterns in the falling piece sequence. // // Pierre Dellacherie corresponded with me via e-mail to help me with the // conversion of his Pascal code to C++. // // WARNING: // If there is a single board and piece combination with the highest // 'rating' value, it is the best combination. However, among // board and piece combinations with EQUAL 'rating' values, // the highest 'priority' value wins. // // So, the complete rating is: { rating, priority }. private static void EvaluteMove(IBoard board, IPiece piece, out double rating, out int priority) { int pieceMinX; int pieceMinY; int pieceMaxX; int pieceMaxY; piece.GetAbsoluteBoundingRectangle(out pieceMinX, out pieceMinY, out pieceMaxX, out pieceMaxY); // Landing Height (vertical midpoint) double landingHeight = 0.5 * (pieceMinY + pieceMaxY); // int completedRows = BoardHelper.GetTotalCompletedRows(board); int erodedPieceCellsMetric = 0; if (completedRows > 0) { // Count piece cells eroded by completed rows before doing collapse on pile. int countPieceCellsEliminated = BoardHelper.CountPieceCellsEliminated(board, piece, true); // Now it's okay to collapse completed rows board.CollapseCompletedRows(); // Weight eroded cells by completed rows erodedPieceCellsMetric = (completedRows * countPieceCellsEliminated); } // int pileHeight = BoardHelper.GetPileMaxHeight(board); // Each empty row (above pile height) has two (2) "transitions" // (We could call ref_Board.GetTransitionCountForRow( y ) for // these unoccupied rows, but this is an optimization.) int boardRowTransitions = 2 * (board.Height - pileHeight); // Only go up to the pile height, and later we'll account for the // remaining rows transitions (2 per empty row). for (int y = 1; y <= pileHeight; y++) { boardRowTransitions += BoardHelper.GetTransitionCountForRow(board, y); } // int boardColumnTransitions = 0; int boardBuriedHoles = 0; int boardWells = 0; for (int x = 1; x <= board.Width; x++) { boardColumnTransitions += BoardHelper.GetTransitionCountForColumn(board, x); boardBuriedHoles += BoardHelper.GetBuriedHolesForColumn(board, x); boardWells += BoardHelper.GetAllWellsForColumn(board, x); } // Final rating // [1] Punish landing height // [2] Reward eroded piece cells // [3] Punish row transitions // [4] Punish column transitions // [5] Punish buried holes (cellars) // [6] Punish wells rating = 0.0; rating += -1.0 * landingHeight; rating += 1.0 * erodedPieceCellsMetric; rating += -1.0 * boardRowTransitions; rating += -1.0 * boardColumnTransitions; rating += -4.0 * boardBuriedHoles; rating += -1.0 * boardWells; // PRIORITY: // Priority is further differentiation between possible moves. // We further rate moves accoding to the following: // * Reward deviation from center of board // * Reward pieces to the left of center of the board // * Punish rotation // Priority is less important than the rating, but among equal // ratings we select the option with the greatest priority. // In principle we could simply factor priority in to the rating, // as long as the priority was less significant than the smallest // variations in rating, but for large board widths (>100), the // risk of loss of precision in the lowest bits of the rating // is too much to tolerate. So, this priority is stored in a // separate variable. int absoluteDistanceX = Math.Abs(piece.PosX - board.PieceSpawnX); priority = 0; priority += (100 * absoluteDistanceX); if (piece.PosX < board.PieceSpawnX) { priority += 10; } priority -= piece.Orientation - 1; }