// WARNING: Moves requiring rotation must wait until piece has fallen by // at least one row. public override void GetBestMoveOncePerPiece ( STBoard board, STPiece piece, bool nextPieceFlag, // false == no next piece available or known STPiece.STPieceShape nextPieceShape, // None == no piece available or known ref int bestRotationDelta, // 0 or {0,1,2,3} ref int bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ) { bestRotationDelta = 0; bestTranslationDelta = 0; // We are given the current board, and the current piece // configuration. Our goal is to evaluate various possible // moves and return the best move we explored. PrivateStrategy ( false, board, piece, ref bestRotationDelta, // 0 or {0,1,2,3} ref bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ); }
public void ServerQueueSubmitPiece(STPiece.STPieceShape shape) { int pieceIndex = 0; pieceIndex = (int)STPiece.GetByteCodeValueOfShape(shape); this.mQueue.Add(pieceIndex); }
// UNUSED: //private int PrivateQueueGetItem ( ) //{ // if (this.mQueue.Count <= 0) // { // return (0); // } // int value = (this.mQueue[0]); // this.mQueue.RemoveAt( 0 ); // return (value); //} private void PrivateAdvanceQueue( ) { this.mCachedSelectedPieceShapeCurrent = STPiece.STPieceShape.None; this.mCachedSelectedPieceShapeNext = STPiece.STPieceShape.None; if (this.mQueue.Count <= 0) { return; } // We definitely have one shape in the queue int pieceShapeIndexCurrent = 0; pieceShapeIndexCurrent = this.PrivateQueuePeekItem( ); // Remove the shape from the queue this.mQueue.RemoveAt(0); // There might be another shape in the queue. We'll peek. int pieceShapeIndexNext = 0; pieceShapeIndexNext = this.PrivateQueuePeekItem( ); // Set current and next piece shapes this.mCachedSelectedPieceShapeCurrent = STPiece.GetShapeCorrespondingToByteCode((byte)pieceShapeIndexCurrent); this.mCachedSelectedPieceShapeNext = STPiece.GetShapeCorrespondingToByteCode((byte)pieceShapeIndexNext); }
void PrivateStrategyEvaluate ( STBoard board, STPiece piece, ref double rating ) { rating = 0.0; if (false == piece.IsValid( )) { return; } // The board was given to us with the piece already committed // to the board cells, so we can now collapse any completed // (fully-occupied) rows. board.CollapseAnyCompletedRows( ); // Note that this evaluation of pile height is AFTER collapsing // any completed rows. int pileHeight = 0; pileHeight = board.GetPileMaxHeight( ); // This simplistic strategy only punishes the maximum // height of the pile. rating = ((-1.0) * (double)pileHeight); }
// WARNING: When you get the "best" rotation and translation // from the following function, you must wait until the piece has // its origin at least as low as row 0 (zero) instead of its initial // row -1 (negative one) if any rotations (1,2,3) are required. // Perform all rotations, and then perform translations. This // avoids the problem of getting the piece jammed on the sides // of the board where rotation is impossible. *** // Also, the following strategy does not take advantage of the // possibility of using free-fall and future movements to // slide under overhangs and fill them in. public static void GetBestMoveOncePerPiece ( STBoard board, STPiece piece, bool nextPieceFlag, // false == no next piece available or known STPiece.STPieceShape nextPieceShape, // None == no piece available or known ref int bestRotationDelta, // 0 or {0,1,2,3} ref int bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ) { bestRotationDelta = 0; bestTranslationDelta = 0; STStrategy strategy = null; strategy = GetCurrentStrategy(); if (null == strategy) { return; } strategy.GetBestMoveOncePerPiece ( board, piece, nextPieceFlag, // false == no next piece available or known nextPieceShape, // None == no piece available or known ref bestRotationDelta, // 0 or {0,1,2,3} ref bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ); }
public virtual void GetBestMoveOncePerPiece ( STBoard board, STPiece piece, bool nextPieceFlag, // false == no next piece available or known STPiece.STPieceShape nextPieceShape, // None == no piece available or known ref int bestRotationDelta, // 0 or {0,1,2,3} ref int bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ) { bestRotationDelta = 0; bestTranslationDelta = 0; }
private void PrivateAdvanceRandom( ) { int pieceShapeIndexCurrent = 0; pieceShapeIndexCurrent = this.mSTRandom.GetIntegerInRangeUsingCurrentState(1, 7); this.mSTRandom.Advance( ); int pieceShapeIndexNext = 0; pieceShapeIndexNext = this.mSTRandom.GetIntegerInRangeUsingCurrentState(1, 7); // Set current and next piece shapes this.mCachedSelectedPieceShapeCurrent = STPiece.GetShapeCorrespondingToByteCode((byte)pieceShapeIndexCurrent); this.mCachedSelectedPieceShapeNext = STPiece.GetShapeCorrespondingToByteCode((byte)pieceShapeIndexNext); }
public static void HandleVideoCaptureGUI ( GR gr, float videoSheetX, float videoSheetY, float videoSheetWidth, float videoSheetHeight, STGame game, int clientWidth, int clientHeight, int clientRelativeCursorX, int clientRelativeCursorY ) { STGameState gameState = game.GetGameState( ); if (false == game.GameIsSpawnFromVideoCapture( )) { return; } gr.glBindTexture ( GR.GL_TEXTURE_2D, STEngine.GetVideoProcessing( ).mTextureOpenGLHandleBGR256x256 ); float x1 = 0.0f; float y1 = 0.0f; float x2 = 0.0f; float y2 = 0.0f; x1 = videoSheetX; y1 = videoSheetY; x2 = x1 + (videoSheetWidth - 1.0f); y2 = y1 + (videoSheetHeight - 1.0f); float u1 = 0.0f; float v1 = 0.0f; float u2 = 0.0f; float v2 = 0.0f; u1 = 0.0f; v1 = 0.0f; u2 = 0.5f; v2 = 1.0f; gr.glEnable(GR.GL_SCISSOR_TEST); gr.glScissor((int)(x1), (int)(y1), (int)((x2 - x1) + 1), (int)((y2 - y1) + 1)); gr.glEnable(GR.GL_TEXTURE_2D); gr.glColor3f(1.0f, 1.0f, 1.0f); gr.glBegin(GR.GL_QUADS); gr.glTexCoord2f(u1, v2); gr.glVertex2f(x1, y2); gr.glTexCoord2f(u1, v1); gr.glVertex2f(x1, y1); gr.glTexCoord2f(u2, v1); gr.glVertex2f(x2, y1); gr.glTexCoord2f(u2, v2); gr.glVertex2f(x2, y2); gr.glEnd( ); gr.glDisable(GR.GL_TEXTURE_2D); gr.glDisable(GR.GL_SCISSOR_TEST); int xTexelMin = 0; int yTexelMin = 0; int xTexelMax = 0; int yTexelMax = 0; int xScreenMin = 0; int yScreenMin = 0; int xScreenMax = 0; int yScreenMax = 0; // Only listen to the mouse in training/calibration mode if (true == gameState.mCalibrationModeFlag) { if (0 != GetAsyncKeyState(Keys.LButton)) { // Left button pressed if (0 == gameState.mSelectionState) { gameState.mSelectionState = 1; gameState.mSelectionX1 = clientRelativeCursorX; gameState.mSelectionY1 = ((clientHeight - 1) - clientRelativeCursorY); gameState.mSelectionX2 = clientRelativeCursorX; gameState.mSelectionY2 = ((clientHeight - 1) - clientRelativeCursorY); } else { gameState.mSelectionX2 = clientRelativeCursorX; gameState.mSelectionY2 = ((clientHeight - 1) - clientRelativeCursorY); } } else { // Left button released if (0 == gameState.mSelectionState) { // Nothing to do... } else { gameState.mSelectionState = 0; } } gr.glEnable(GR.GL_SCISSOR_TEST); gr.glScissor(0, 0, clientWidth, clientHeight); gr.glColor3f(1.0f, 0.0f, 0.0f); gr.glBegin(GR.GL_LINES); gr.glVertex2f((float)clientRelativeCursorX - 8.0f, (float)((clientHeight - 1) - clientRelativeCursorY)); gr.glVertex2f((float)clientRelativeCursorX + 8.0f, (float)((clientHeight - 1) - clientRelativeCursorY)); gr.glVertex2f((float)clientRelativeCursorX, (float)((clientHeight - 1) - clientRelativeCursorY) - 8.0f); gr.glVertex2f((float)clientRelativeCursorX, (float)((clientHeight - 1) - clientRelativeCursorY) + 8.0f); gr.glEnd( ); } if (0 != ((GetAsyncKeyState(Keys.Shift)) & 0x8000)) { if (0 != ((GetAsyncKeyState(Keys.Left)) & 0x8000)) { gameState.mSelectionX2--; } if (0 != ((GetAsyncKeyState(Keys.Right)) & 0x8000)) { gameState.mSelectionX2++; } if (0 != ((GetAsyncKeyState(Keys.Down)) & 0x8000)) { gameState.mSelectionY2--; } if (0 != ((GetAsyncKeyState(Keys.Up)) & 0x8000)) { gameState.mSelectionY2++; } } else { if (0 != ((GetAsyncKeyState(Keys.Left)) & 0x8000)) { gameState.mSelectionX1--; } if (0 != ((GetAsyncKeyState(Keys.Right)) & 0x8000)) { gameState.mSelectionX1++; } if (0 != ((GetAsyncKeyState(Keys.Down)) & 0x8000)) { gameState.mSelectionY1--; } if (0 != ((GetAsyncKeyState(Keys.Up)) & 0x8000)) { gameState.mSelectionY1++; } } xScreenMin = gameState.mSelectionX1; yScreenMin = gameState.mSelectionY1; xScreenMax = gameState.mSelectionX2; yScreenMax = gameState.mSelectionY2; xTexelMin = (int)(256.0f * (((float)xScreenMin - videoSheetX) / videoSheetHeight)); yTexelMin = (int)(256.0f * (((float)yScreenMin - videoSheetY) / videoSheetHeight)); xTexelMax = (int)(256.0f * (((float)xScreenMax - videoSheetX) / videoSheetHeight)); yTexelMax = (int)(256.0f * (((float)yScreenMax - videoSheetY) / videoSheetHeight)); int disregard = 0; if (xTexelMin < 0) { disregard = 1; xTexelMin = 0; } if (yTexelMin < 0) { disregard = 1; yTexelMin = 0; } if (xTexelMax < 0) { disregard = 1; xTexelMax = 0; } if (yTexelMax < 0) { disregard = 1; yTexelMax = 0; } if (xTexelMin > 255) { disregard = 1; xTexelMin = 255; } if (yTexelMin > 255) { disregard = 1; yTexelMin = 255; } if (xTexelMax > 255) { disregard = 1; xTexelMax = 255; } if (yTexelMax > 255) { disregard = 1; yTexelMax = 255; } if (xTexelMin > xTexelMax) { int swap = xTexelMin; xTexelMin = xTexelMax; xTexelMax = swap; } if (yTexelMin > yTexelMax) { int swap = yTexelMin; yTexelMin = yTexelMax; yTexelMax = swap; } // Only set region if in training mode! if ((true == gameState.mCalibrationModeFlag) && (0 == disregard)) { STEngine.GetVideoProcessing( ).SetRegion(xTexelMin, yTexelMin, xTexelMax, yTexelMax); } STEngine.GetVideoProcessing( ).GetRegion(ref xTexelMin, ref yTexelMin, ref xTexelMax, ref yTexelMax); xScreenMin = (int)(videoSheetX + (videoSheetHeight * (float)xTexelMin / 256.0f)); yScreenMin = (int)(videoSheetY + (videoSheetHeight * (float)yTexelMin / 256.0f)); xScreenMax = (int)(videoSheetX + (videoSheetHeight * (float)xTexelMax / 256.0f)); yScreenMax = (int)(videoSheetY + (videoSheetHeight * (float)yTexelMax / 256.0f)); x1 = videoSheetX; y1 = videoSheetY; x2 = x1 + (videoSheetWidth - 1.0f); y2 = y1 + (videoSheetHeight - 1.0f); int currentClassification = STEngine.GetVideoProcessing( ).GetRegionClassification( ); if (0 == currentClassification) { // If the previous classification was a PIECE, and the current classification // is something different, then submit the piece (which must have fallen // by a row by now). if ((gameState.mPreviousClassification >= 1) && (gameState.mPreviousClassification <= 7)) { game.SpawnSpecifiedPieceShape(STPiece.GetShapeCorrespondingToByteCode((byte)gameState.mPreviousClassification)); } } gameState.mPreviousClassification = currentClassification; Color color; color = STGameDrawing.GetCellValueColorARGB // Returns WHITE for unknown ( (byte)currentClassification, // 0..6 false // monochrome mode ); float red = 0.0f; float green = 0.0f; float blue = 0.0f; red = (float)(color.R) / 255.0f; green = (float)(color.G) / 255.0f; blue = (float)(color.B) / 255.0f; gr.glColor3f(red, green, blue); gr.glBegin(GR.GL_LINES); gr.glVertex2f((float)xScreenMin, (float)yScreenMin); gr.glVertex2f((float)xScreenMin, (float)yScreenMax); gr.glVertex2f((float)xScreenMax, (float)yScreenMin); gr.glVertex2f((float)xScreenMax, (float)yScreenMax); gr.glVertex2f((float)xScreenMin, (float)yScreenMin); gr.glVertex2f((float)xScreenMax, (float)yScreenMin); gr.glVertex2f((float)xScreenMin, (float)yScreenMax); gr.glVertex2f((float)xScreenMax, (float)yScreenMax); // Horizontal divider gr.glVertex2f((float)xScreenMin, (float)((yScreenMin + yScreenMax) / 2)); gr.glVertex2f((float)xScreenMax, (float)((yScreenMin + yScreenMax) / 2)); // Vertical dividers gr.glVertex2f((float)(xScreenMin + ((xScreenMax - xScreenMin) / 4)), (float)yScreenMin); gr.glVertex2f((float)(xScreenMin + ((xScreenMax - xScreenMin) / 4)), (float)yScreenMax); gr.glVertex2f((float)(xScreenMin + 2 * ((xScreenMax - xScreenMin) / 4)), (float)yScreenMin); gr.glVertex2f((float)(xScreenMin + 2 * ((xScreenMax - xScreenMin) / 4)), (float)yScreenMax); gr.glVertex2f((float)(xScreenMin + 3 * ((xScreenMax - xScreenMin) / 4)), (float)yScreenMin); gr.glVertex2f((float)(xScreenMin + 3 * ((xScreenMax - xScreenMin) / 4)), (float)yScreenMax); gr.glEnd( ); gr.glDisable(GR.GL_SCISSOR_TEST); }
private double PrivateStrategy ( bool flagCalledFromParentPly, // True if called from a parent level STBoard board, STPiece piece, ref int bestRotationDelta, // 0 or {0,1,2,3} ref int bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ) { if (false == piece.IsValid( )) { return(0.0); } int currentBestTranslationDelta = 0; int currentBestRotationDelta = 0; double currentBestMerit = (-1.0e+20); // Really bad! int trialTranslationDelta = 0; int trialRotationDelta = 0; double trialMerit = 0.0; bool moveAcceptable = false; int count = 0; STBoard tempBoard = new STBoard( ); STPiece tempPiece = new STPiece( ); int maxOrientations = 0; maxOrientations = STPiece.GetMaximumOrientationsOfShape(piece.GetShape( )); for ( trialRotationDelta = 0; trialRotationDelta < maxOrientations; trialRotationDelta++ ) { // Make temporary copy of piece, and rotate the copy. tempPiece.CopyFrom(piece); for (count = 0; count < trialRotationDelta; count++) { tempPiece.Rotate( ); } // Determine the translation limits for this rotated piece. bool moveIsPossible = false; int minDeltaX = 0; int maxDeltaX = 0; board.DetermineAccessibleTranslationsForPieceOrientation ( tempPiece, ref moveIsPossible, ref minDeltaX, // left limit ref maxDeltaX // right limit ); // Consider all allowed translations for the current rotation. if (true == moveIsPossible) { for ( trialTranslationDelta = minDeltaX; trialTranslationDelta <= maxDeltaX; trialTranslationDelta++ ) { // Evaluate this move // Copy piece to temp and rotate and translate tempPiece.CopyFrom(piece); for (count = 0; count < trialRotationDelta; count++) { tempPiece.Rotate( ); } tempPiece.Translate(trialTranslationDelta, 0); moveAcceptable = board.DetermineIfPieceIsWithinBoardAndDoesNotOverlapOccupiedCells ( tempPiece ); if (true == moveAcceptable) { // Because the piece can BE (not necessarily GET) at the goal // horizontal translation and orientation, it's worth trying // out a drop and evaluating the move. tempBoard.CopyFrom(board); tempBoard.FullDropAndCommitPieceToBoard ( tempPiece ); // Pierre Dellacherie (France) Board & Piece Evaluation Function this.PrivateStrategyEvaluate ( tempBoard, tempPiece, ref trialMerit ); // If this move is better than any move considered before, // or if this move is equally ranked but has a higher priority, // then update this to be our best move. if (trialMerit > currentBestMerit) { currentBestMerit = trialMerit; currentBestTranslationDelta = trialTranslationDelta; currentBestRotationDelta = trialRotationDelta; } } } } } // commit to this move bestTranslationDelta = currentBestTranslationDelta; bestRotationDelta = currentBestRotationDelta; return(currentBestMerit); }
// 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! }
private double PrivateStrategy ( bool flagCalledFromParentPly, // True if called from a parent level STBoard board, STPiece piece, ref int bestRotationDelta, // 0 or {0,1,2,3} ref int bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ) { if (false == piece.IsValid( )) { return(0.0); } int currentBestTranslationDelta = 0; int currentBestRotationDelta = 0; double currentBestMerit = (-1.0e20); int currentBestPriority = 0; int trialTranslationDelta = 0; int trialRotationDelta = 0; double trialMerit = 0.0; int trialPriority = 0; int maxOrientations = 0; bool moveAcceptable = false; int count = 0; STBoard tempBoard = new STBoard(); STPiece tempPiece = new STPiece(); maxOrientations = STPiece.GetMaximumOrientationsOfShape(piece.GetShape()); for ( trialRotationDelta = 0; trialRotationDelta < maxOrientations; trialRotationDelta++ ) { // Make temporary copy of piece, and rotate the copy. tempPiece.CopyFrom(piece); for (count = 0; count < trialRotationDelta; count++) { tempPiece.Rotate(); } // Determine the translation limits for this rotated piece. bool moveIsPossible = false; int minDeltaX = 0; int maxDeltaX = 0; board.DetermineAccessibleTranslationsForPieceOrientation ( tempPiece, ref moveIsPossible, // false==NONE POSSIBLE ref minDeltaX, // Left limit ref maxDeltaX // Right limit ); // Consider all allowed translations for the current rotation. if (true == moveIsPossible) { for ( trialTranslationDelta = minDeltaX; trialTranslationDelta <= maxDeltaX; trialTranslationDelta++ ) { // Evaluate this move // Copy piece to temp and rotate and translate tempPiece.CopyFrom(piece); for (count = 0; count < trialRotationDelta; count++) { tempPiece.Rotate( ); } tempPiece.Translate(trialTranslationDelta, 0); moveAcceptable = board.DetermineIfPieceIsWithinBoardAndDoesNotOverlapOccupiedCells ( tempPiece ); if (true == moveAcceptable) { // Since the piece can be (not necessarily GET) at the goal // horizontal translation and orientation, it's worth trying // out a drop and evaluating the move. tempBoard.CopyFrom(board); tempBoard.FullDropAndCommitPieceToBoard(tempPiece); trialPriority = 0; if (true == flagCalledFromParentPly) { // UNUSED: int rowsEliminated = 0; // UNUSED: rowsEliminated = tempBoard.CollapseAnyCompletedRows(); double weightTotalShadowedHoles = (-0.65); double weightPileHeightWeightedCells = (-0.10); double weightSumOfWellHeights = (-0.20); trialMerit = (weightTotalShadowedHoles) * (double)(tempBoard.GetTotalShadowedHoles( )); trialMerit += (weightPileHeightWeightedCells) * (double)(tempBoard.GetPileHeightWeightedCells( )); trialMerit += (weightSumOfWellHeights) * (double)(tempBoard.GetSumOfWellHeights( )); } else { double weightRowElimination = (0.30); double weightTotalOccupiedCells = (-0.00); double weightTotalShadowedHoles = (-0.65); double weightPileHeightWeightedCells = (-0.10); double weightSumOfWellHeights = (-0.20); int rowsEliminated = 0; rowsEliminated = tempBoard.CollapseAnyCompletedRows(); // Single Ply (No next piece) // Averages around 1310 rows in 10 games, with a min of 445 and a max of 3710. trialMerit = (weightRowElimination) * (double)(rowsEliminated); trialMerit += (weightTotalOccupiedCells) * (double)(tempBoard.GetTotalOccupiedCells( )); trialMerit += (weightTotalShadowedHoles) * (double)(tempBoard.GetTotalShadowedHoles( )); trialMerit += (weightPileHeightWeightedCells) * (double)(tempBoard.GetPileHeightWeightedCells( )); trialMerit += (weightSumOfWellHeights) * (double)(tempBoard.GetSumOfWellHeights( )); } // If this move is better than any move considered before, // or if this move is equally ranked but has a higher priority, // then update this to be our best move. if ( (trialMerit > currentBestMerit) || ((trialMerit == currentBestMerit) && (trialPriority > currentBestPriority)) ) { currentBestPriority = trialPriority; currentBestMerit = trialMerit; currentBestTranslationDelta = trialTranslationDelta; currentBestRotationDelta = trialRotationDelta; } } } } } // Commit to this move bestTranslationDelta = currentBestTranslationDelta; bestRotationDelta = currentBestRotationDelta; return(currentBestMerit); }
private double PrivateStrategyNextPiece ( STBoard board, STPiece piece, STPiece.STPieceShape nextPieceShape, // None == no piece available or known ref int bestRotationDelta, // 0 or {0,1,2,3} ref int bestTranslationDelta // 0 or {...,-2,-1,0,1,2,...} ) { if (false == piece.IsValid( )) { return(0.0); } int currentBestTranslationDelta = 0; int currentBestRotationDelta = 0; double currentBestMerit = (-1.0e20); int currentBestPriority = 0; int trialTranslationDelta = 0; int trialRotationDelta = 0; double trialMerit = 0.0; int trialPriority = 0; int maxOrientations = 0; bool moveAcceptable = false; int count = 0; STBoard tempBoard = new STBoard(); STPiece tempPiece = new STPiece(); maxOrientations = STPiece.GetMaximumOrientationsOfShape(piece.GetShape()); for ( trialRotationDelta = 0; trialRotationDelta < maxOrientations; trialRotationDelta++ ) { // Make temporary copy of piece, and rotate the copy. tempPiece.CopyFrom(piece); for (count = 0; count < trialRotationDelta; count++) { tempPiece.Rotate(); } // Determine the translation limits for this rotated piece. bool moveIsPossible = false; int minDeltaX = 0; int maxDeltaX = 0; board.DetermineAccessibleTranslationsForPieceOrientation ( tempPiece, ref moveIsPossible, // false==NONE POSSIBLE ref minDeltaX, // Left limit ref maxDeltaX // Right limit ); // Consider all allowed translations for the current rotation. if (true == moveIsPossible) { for ( trialTranslationDelta = minDeltaX; trialTranslationDelta <= maxDeltaX; trialTranslationDelta++ ) { // Evaluate this move // Copy piece to temp and rotate and translate tempPiece.CopyFrom(piece); for (count = 0; count < trialRotationDelta; count++) { tempPiece.Rotate(); } tempPiece.Translate(trialTranslationDelta, 0); moveAcceptable = board.DetermineIfPieceIsWithinBoardAndDoesNotOverlapOccupiedCells ( tempPiece ); if (true == moveAcceptable) { // Since the piece can be (not necessarily GET) at the goal // horizontal translation and orientation, it's worth trying // out a drop and evaluating the move. tempBoard.CopyFrom(board); tempBoard.FullDropAndCommitPieceToBoard(tempPiece); trialPriority = 0; // Okay, now do second move with "Next Piece" int nextPieceBestRotation = 0; // Dummy variable int nextPieceBestTranslation = 0; // Dummy variable STPiece nextPiece = new STPiece(); nextPiece.SetShape(nextPieceShape); nextPiece.SetX(tempBoard.GetPieceSpawnX()); nextPiece.SetY(tempBoard.GetPieceSpawnY() - 1); nextPiece.SetOrientation(1); trialMerit = PrivateStrategy ( true, // Not just a single ply; We are calling from a parent ply. tempBoard, nextPiece, ref nextPieceBestRotation, ref nextPieceBestTranslation ); // If this move is better than any move considered before, // or if this move is equally ranked but has a higher priority, // then update this to be our best move. if ( (trialMerit > currentBestMerit) || ((trialMerit == currentBestMerit) && (trialPriority > currentBestPriority)) ) { currentBestPriority = trialPriority; currentBestMerit = trialMerit; currentBestTranslationDelta = trialTranslationDelta; currentBestRotationDelta = trialRotationDelta; } } } } } // Commit to this move bestTranslationDelta = currentBestTranslationDelta; bestRotationDelta = currentBestRotationDelta; return(currentBestMerit); }
// 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 }. void PrivateStrategyEvaluate ( STBoard board, STPiece piece, ref double rating, ref int priority ) { rating = 0.0; priority = 0; if (false == piece.IsValid()) { return; } int boardWidth = 0; int boardHeight = 0; boardWidth = board.GetWidth(); boardHeight = board.GetHeight(); int pieceMinX = 0; int pieceMinY = 0; int pieceMaxX = 0; int pieceMaxY = 0; piece.GetTranslatedBoundingRectangle (ref pieceMinX, ref pieceMinY, ref pieceMaxX, ref pieceMaxY); // Landing Height (vertical midpoint) double landingHeight = 0.0; landingHeight = 0.5 * (double)(pieceMinY + pieceMaxY); int completedRows = 0; completedRows = board.GetTotalCompletedRows(); int erodedPieceCellsMetric = 0; if (completedRows > 0) { // Count piece cells eroded by completed rows before doing collapse on pile. int pieceCellsEliminated = 0; pieceCellsEliminated = board.CountPieceCellsEliminated(piece); // Now it's okay to collapse completed rows board.CollapseAnyCompletedRows(); // Weight eroded cells by completed rows erodedPieceCellsMetric = (completedRows * pieceCellsEliminated); } // Note that this evaluation of pile height is AFTER collapsing // any completed rows. int pileHeight = 0; pileHeight = board.GetPileMaxHeight(); // 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 = 0; boardRowTransitions = 2 * (boardHeight - pileHeight); // Only go up to the pile height, and later we'll account for the // remaining rows transitions (2 per empty row). int y = 0; for (y = 1; y <= pileHeight; y++) { boardRowTransitions += (board.GetTransitionCountForRow(y)); } int boardColumnTransitions = 0; int boardBuriedHoles = 0; int boardWells = 0; int x = 0; for (x = 1; x <= boardWidth; x++) { boardColumnTransitions += board.GetTransitionCountForColumn(x); boardBuriedHoles += board.GetBuriedHolesForColumn(x); boardWells += board.GetAllWellsForColumn(x); } // Final Rating rating = (0.0); rating += ((-1.0) * (landingHeight)); rating += ((1.0) * ((double)(erodedPieceCellsMetric))); rating += ((-1.0) * ((double)(boardRowTransitions))); rating += ((-1.0) * ((double)(boardColumnTransitions))); rating += ((-4.0) * ((double)(boardBuriedHoles))); rating += ((-1.0) * ((double)(boardWells))); // EXPLANATION: // [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 #if DEBUGGING_PRINT_STATEMENTS STEngine.GetConsole().AddLine ( " D:" + (21.0 - landingHeight) + " R:" + erodedPieceCellsMetric + " RC:" + (-boardRowTransitions) + " CC:" + (-boardColumnTransitions) + " H:" + (-4 * boardBuriedHoles) + " W:" + (-boardWells) ); #endif // 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 = 0; absoluteDistanceX = (piece.GetX() - board.GetPieceSpawnX()); if (absoluteDistanceX < 0) { absoluteDistanceX = (-(absoluteDistanceX)); } priority = 0; priority += (100 * absoluteDistanceX); if (piece.GetX() < board.GetPieceSpawnX()) { priority += 10; } priority -= (piece.GetOrientation( ) - 1); }