public void KeyDown(object sender, KeyEventArgs e) { // NOTE: The only way for cursor key events (up,down,left,right) // to make it to this function is for the main form to implement // the following: // // protected override bool ProcessDialogKey ( Keys keyData ) // // and explicitly invoke this KeyDown() method with the // an appropriately formed KeyEventArgs instance. // ALSO, the KeyPreview property of the main Form must be set to // 'true' for that override method to be called. STUserInterface.HandleKeyPress ( STEngine.GetMainForm( ).mGRControl.GetGR( ), STEngine.GetMainForm( ).Handle, 0, 0, STEngine.GetGame( ), e.KeyCode, e.Shift, e.Control ); }
static void Main( ) { Application.EnableVisualStyles( ); Application.SetCompatibleTextRenderingDefault(false); STEngine.Start( ); }
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); }
public static void HandleKeyPress ( GR gr, IntPtr hwnd, int wParam, int lParam, STGame game, Keys keyCode, bool shiftKeyState, bool controlKeyState ) { STGameState gameState = game.GetGameState( ); // Priority of key press handling: // (1) Instructions; ESCAPE exits instructions; // (2) File Menu; ESCAPE exits file menu; // (3) Calibrate; ESCAPE cancels calibrate mode; // (4) Video Capture; ESCAPE quits application; // (5) Normal; ESCAPE quits application; // INSTRUCTIONS if (0 != game.InstructionGetState( )) { switch (keyCode) { case Keys.Down: case Keys.Next: // Page-Down case Keys.Right: { // Next page game.InstructionsNextPage( ); } break; case Keys.Up: case Keys.Prior: // Page-Up case Keys.Left: { // Previous page game.InstructionsPreviousPage( ); } break; default: { // User hit a key, but it wasn't relevant, so exit menu. game.InstructionsHide( ); // NOTE: Don't resume! : game.InputEventResume(); } break; } return; } else if (keyCode == Keys.I) { game.InstructionsShow( ); game.InputEventPause( ); return; } // FILE LIST if (true == gameState.mShowFileList) { switch (keyCode) { case Keys.Next: // Page-Down { // Next page gameState.mFirstItem += 20; } break; case Keys.Prior: // Page-Up { // Previous page gameState.mFirstItem -= 20; } break; case Keys.Down: { // Next Item gameState.mRelativeItem++; if (gameState.mRelativeItem > 19) { gameState.mFirstItem++; gameState.mRelativeItem = 19; } } break; case Keys.Up: { // Previous Item gameState.mRelativeItem--; if (gameState.mRelativeItem < 0) { gameState.mFirstItem--; gameState.mRelativeItem = 0; } } break; case Keys.Return: { // Load item gameState.mLoadFlag = true; } break; default: { // User hit a key, but it wasn't relevant, so exit menu. gameState.mShowFileList = false; // NOTE: Don't resume. : game.InputEvent_Resume(); } break; } return; } else if ((keyCode == Keys.L) && (true == shiftKeyState)) { // SHIFT-L will read a text file in to the game state. game.InputEventPause( ); gameState.mShowFileList = true; gameState.mFirstItem = 0; gameState.mRelativeItem = 0; gameState.mLoadFlag = false; STEngine.GetFileList().ScanDirectory(STEngine.GetApplicationPath( )); return; } // Calibrate Mode // (NOTE: See how normal mode enters calibrate mode by pressing 'C'.) if (true == game.GetCalibrationModeFlagValue( )) { if ((Keys.Escape == keyCode) || (Keys.C == keyCode)) { game.SetCalibrationModeFlagValue(false); game.InputEventResume( ); return; } if (keyCode == Keys.V) { if (false == game.GameIsSpawnFromVideoCapture( )) { // Set up sane conditions game.InputEventReset( ); game.InputEventShowNextPieceOff( ); game.InputEventAutoRestartOff( ); // Initialize Video Capture STEngine.GetVideoProcessing( ).Initialize(gr, hwnd); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.InputEventVideoStart( ); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); } else { STEngine.GetVideoProcessing( ).Terminate( ); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.InputEventVideoStop( ); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); } return; } switch (keyCode) { // training mode piece selection case Keys.D0: game.SetCalibrationModeShapeCode(0); break; case Keys.D1: game.SetCalibrationModeShapeCode(1); break; case Keys.D2: game.SetCalibrationModeShapeCode(2); break; case Keys.D3: game.SetCalibrationModeShapeCode(3); break; case Keys.D4: game.SetCalibrationModeShapeCode(4); break; case Keys.D5: game.SetCalibrationModeShapeCode(5); break; case Keys.D6: game.SetCalibrationModeShapeCode(6); break; case Keys.D7: game.SetCalibrationModeShapeCode(7); break; case Keys.D8: game.SetCalibrationModeShapeCode(0); break; case Keys.D9: game.SetCalibrationModeShapeCode(0); break; } return; } // Video Capture // The following is not mutually-exclusive with normal game play. if (true == game.GameIsSpawnFromVideoCapture( )) { if (keyCode == Keys.Return) { STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.ClearPreviousClassification( ); game.InputEventReset( ); System.Threading.Thread.Sleep(200); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.ClearPreviousClassification( ); game.InputEventReset( ); System.Threading.Thread.Sleep(200); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.ClearPreviousClassification( ); game.InputEventReset( ); System.Threading.Thread.Sleep(200); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.ClearPreviousClassification( ); game.InputEventReset( ); System.Threading.Thread.Sleep(200); } if (keyCode == Keys.V) { if (false == game.GameIsSpawnFromVideoCapture( )) { // Set up sane conditions game.InputEventReset( ); game.InputEventShowNextPieceOff( ); game.InputEventAutoRestartOff( ); // Initialize Video Capture STEngine.GetVideoProcessing( ).Initialize(gr, hwnd); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.InputEventVideoStart( ); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); } else { STEngine.GetVideoProcessing( ).Terminate( ); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.InputEventVideoStop( ); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); } return; } } // Console Mode if (true == gameState.mShowConsole) { if (keyCode == Keys.Delete) { STEngine.GetConsole( ).ClearAllLines( ); } else { // Any key other than delete or P (pause) exits console mode. if (keyCode != Keys.P) { gameState.mShowConsole = false; } } } else { if ((keyCode == Keys.Q) && (true == shiftKeyState)) { // SHIFT-Q : Console gameState.mShowConsole = true; } } // Normal Game Play // QUIT KEY: ESCAPE if (keyCode == Keys.Escape) { Form form = (Form)STEngine.GetMainForm( ); form.Close( ); return; } // Enter Calibrate Mode if (keyCode == Keys.C) { game.SetCalibrationModeFlagValue(true); game.SetCalibrationModeShapeCode(1); game.InputEventPause( ); } // Enable Video Capture if (keyCode == Keys.V) { if (false == game.GameIsSpawnFromVideoCapture( )) { // Set up sane conditions game.InputEventReset( ); game.InputEventShowNextPieceOff( ); game.InputEventAutoRestartOff( ); // Initialize Video Capture STEngine.GetVideoProcessing( ).Initialize(gr, hwnd); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); game.InputEventVideoStart( ); STEngine.GetVideoProcessing( ).ClearRegionStatus( ); } } // Reset Game if (keyCode == Keys.Return) { if (true == shiftKeyState) { game.InputEventHardReset( ); } else { game.InputEventReset( ); } } if (keyCode == Keys.P) { if (true == game.GameIsPaused( )) { game.InputEventResume( ); } else { game.InputEventPause( ); } } if (keyCode == Keys.A) { if (true == shiftKeyState) { STStrategyManager.SelectNextStrategy( ); } else { if (true == game.GameIsAI( )) { game.InputEventAIStop( ); } else { game.InputEventAIStart( ); } } } if (keyCode == Keys.T) { if (game.GameIsOutputToRS232( )) { game.InputEventRS232Stop( ); STRS232.TerminatePort( ); } else { STRS232.InitializePort( ); game.InputEventRS232Start( ); } } if ((keyCode == Keys.Subtract) || (keyCode == Keys.OemMinus)) // 0xbd { if (false == shiftKeyState) { game.InputEventGameSpeedDecrease( ); } } if ((keyCode == Keys.Add) || (keyCode == Keys.Oemplus)) // 0xbb { if (false == shiftKeyState) { game.InputEventGameSpeedIncrease( ); } } if ((keyCode == Keys.W) && (true == shiftKeyState)) { // SHIFT-W will write out a text file (c:\tetris_state.txt) game.InputEventGameStateWriteToFile( ); } if (keyCode == Keys.Next) // Page-Down { // Page-Down: Decrease Board Size game.InputEventGameBoardDecrease( ); } if (keyCode == Keys.Prior) // Page-Up { // Page-Up: Increase Board Size game.InputEventGameBoardIncrease( ); } if (true == controlKeyState) { if (keyCode == Keys.Up) { game.InputEventGameBoardIncreaseHeight( ); } if (keyCode == Keys.Left) { game.InputEventGameBoardDecreaseWidth( ); } if (keyCode == Keys.Right) { game.InputEventGameBoardIncreaseWidth( ); } if (keyCode == Keys.Down) { game.InputEventGameBoardDecreaseHeight( ); } } // COLOR SCHEME if ((keyCode == Keys.K) && (true == shiftKeyState)) { if (false == game.GetGameState( ).mMonochromeColorMode) { game.GetGameState( ).mMonochromeColorMode = true; } else { game.GetGameState( ).mMonochromeColorMode = false; } } // Non Video-Capture Options if (false == game.GameIsSpawnFromVideoCapture( )) { // Only respond to user piece-control input if AI is not active. if (false == game.GameIsAI( )) { if (false == controlKeyState) { if (keyCode == Keys.Up) { game.InputEventRotate( ); } if (keyCode == Keys.Left) { game.InputEventLeft( ); } if (keyCode == Keys.Right) { game.InputEventRight( ); } if (keyCode == Keys.Down) { game.InputEventDrop( ); } if (keyCode == Keys.Space) { game.InputEventDrop( ); } } } if (keyCode == Keys.Z) { if ((STPieceSequence.STPieceSelectionSource.AlternatingSAndZ) == game.GetPieceSequenceSourceType( )) { // Since we're in S/Z mode, stop. game.InputEventSZPieceModeStop( ); } else { // Start S/Z mode. game.InputEventSZPieceModeStart( ); } } if (keyCode == Keys.S) { // S will cycle the shadow mode. game.InputEventShadowModeCycle( ); } if ((keyCode == Keys.J) && (true == shiftKeyState)) { // SHIFT-J : Add line of random junk to bottom of the pile. game.InputEventAddRowOfJunk( ); } if ((keyCode == Keys.H) && (true == shiftKeyState)) { // SHIFT-H : Hint Mode if (true == game.GameIsHintMode( )) { game.InputEventHintModeStop( ); } else { game.InputEventHintModeStart( ); } } if (keyCode == Keys.N) { if (true == game.GameIsShowNextPiece( )) { game.InputEventShowNextPieceOff( ); } else { game.InputEventShowNextPieceOn( ); } } if (keyCode == Keys.X) { game.InputEventToggleMoveAnimation( ); } if (keyCode == Keys.U) { if (true == game.GameIsAutoRestart( )) { game.InputEventAutoRestartOff( ); } else { game.InputEventAutoRestartOn( ); } } if (keyCode == Keys.F) { game.InputEventToggleAutoWriteFile( ); } if ((keyCode == Keys.R) && (true == shiftKeyState)) { // SHIFT-R : Soft reset (game goes back to same random seed) game.InputEventSoftReset( ); } } }
public void Paint(object sender, PaintEventArgs e) { if (null == sender) { return; } if (false == (sender is GRControl)) { return; } GRControl grControl = (sender as GRControl); GR gr = grControl.GetGR( ); int clientWidth = grControl.ClientRectangle.Width; int clientHeight = grControl.ClientRectangle.Height; if (clientWidth <= 0) { clientWidth = 1; } if (clientHeight <= 0) { clientHeight = 1; } // Viewport gr.glViewport(0, 0, clientWidth, clientHeight); // Clear the viewport gr.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); gr.glClear(GR.GL_COLOR_BUFFER_BIT | GR.GL_DEPTH_BUFFER_BIT); // Basic rendering conditions gr.glEnable(GR.GL_DEPTH_TEST); gr.glDepthFunc(GR.GL_LEQUAL); gr.glEnable(GR.GL_CULL_FACE); gr.glCullFace(GR.GL_BACK); gr.glFrontFace(GR.GL_CCW); gr.glMatrixMode(GR.GL_PROJECTION); gr.glLoadIdentity( ); gr.gluOrtho2D(0.0, (double)clientWidth, 0.0, (double)clientHeight); gr.glMatrixMode(GR.GL_MODELVIEW); gr.glLoadIdentity( ); // TETRIS GAME ITERATION STUserInterface.PerformGameIterations ( STEngine.GetGame( ), grControl.GetPreviousFrameDurationSeconds( ) ); // TETRIS GAME DRAWING System.Drawing.Point controlRelativePoint = grControl.PointToClient(Cursor.Position); int clientRelativeCursorX = controlRelativePoint.X; int clientRelativeCursorY = controlRelativePoint.Y; STGameDrawing.DrawScreen ( clientWidth, clientHeight, clientRelativeCursorX, clientRelativeCursorY, gr, STEngine.GetGame( ), STEngine.GetConsole( ) ); // Flush all the current rendering and flip the back buffer to the front. gr.wglSwapBuffers(grControl.GetHDC( )); }
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 currentBestPriority = 0; int trialTranslationDelta = 0; int trialRotationDelta = 0; double trialMerit = 0.0; int trialPriority = 0; bool moveAcceptable = false; int count = 0; STBoard tempBoard = new STBoard(); STPiece tempPiece = new STPiece(); int maxOrientations = 0; maxOrientations = STPiece.GetMaximumOrientationsOfShape(piece.GetShape()); #if DEBUGGING_PRINT_STATEMENTS STEngine.GetConsole().AddLine(" "); STEngine.GetConsole().AddLine("STStrategyPierreDellacherieOnePiece2003"); #endif 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, ref trialPriority ); #if DEBUGGING_PRINT_STATEMENTS STEngine.GetConsole().AddToLastLine ( " M: " + trialMerit + " R: " + trialRotationDelta + " dX: " + trialTranslationDelta + " P: " + trialPriority ); #endif // 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)) ) { currentBestMerit = trialMerit; currentBestPriority = trialPriority; 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); }