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( ); } } }
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); }
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( )); }
// 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); }