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
            );
        }
Exemplo n.º 2
0
 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);
        }