///<summary>Enlarge undo/redo stack</summary>
 private void EnlargeStack()
 {
     iMovesAlloc *= 2;
     SokoMove[] bNew = new SokoMove[iMovesAlloc];
     bMoves.CopyTo(bNew, 0);
     bMoves = bNew;
 }
        ///<summary>Perform moving of player(updatable move - to get direction; to put pushing )</summary>
        private MoveResult MovePlayer(ref SokoMove uMove)
        {
            int iDX = 0, iDY = 0;
            MoveResult uRV;// = MoveResult.WayBlocked;

            switch (uMove & SokoMove.Direction)//Get direction vector from move
            {
                case SokoMove.Up: iDY = -1; break;
                case SokoMove.Down: iDY = 1; break;
                case SokoMove.Left: iDX = -1; break;
                case SokoMove.Right: iDX = 1; break;
            }
            int iX = iPlayerX + iDX;
            int iY = iPlayerY + iDY;
            int iX2 = iPlayerX + iDX * 2;
            int iY2 = iPlayerY + iDY * 2;

            if ((GetCell(iX2, iY2) & SokoCell.Background)!=0) return MoveResult.WayBlocked;//Box can not be pushed to background
            if ((GetCell(iX, iY) & SokoCell.Background) != 0) return MoveResult.WayBlocked;//Player can not move to background

            if ((bCells[iX, iY] & SokoCell.Box) != 0)
            {//New location contains box - it should be pushed

                if ((bCells[iX2, iY2] & SokoCell.MaskObstacle) == 0)
                {//No obstacle - box can be pushed
                    uMove |= SokoMove.Push;//Add push flag to move

                    if ((bCells[iX, iY] & SokoCell.Target) != 0)
                    {//Box moved from target
                        iNumFreeBoxes++;//Increment counter of free boxes and free targets
                        iNumFreeTargets++;
                    }
                    if ((bCells[iX2, iY2] & SokoCell.Target) != 0)
                    {//Box moved to target
                        uRV = MoveResult.MovedAndPushBoxToTarget;//Notify about this, level can be completed after this move
                        iNumFreeBoxes--;//Decrement counter of free boxes and free targets
                        iNumFreeTargets--;
                    }
                    else
                    {
                        uRV = MoveResult.MovedAndPushBox;//Usual push, not to target
                    }
                    if ((bCells[iX, iY] & SokoCell.CellDeadlock) != 0)
                    {//Box moved from cell-deadlock (possible only if cell-deadlock detection will fail)
                        iNumRemainExceedBoxes++;//Increment counter of exceeding boxes
                    }
                    if ((bCells[iX2, iY2] & SokoCell.CellDeadlock) != 0)
                    {//Box moved to cell-deadlock
                        iNumRemainExceedBoxes--;//Decrement counter of exceeding boxes
                    }

                    bCells[iX, iY] ^= SokoCell.Box;//Remove box from new player location
                    bCells[iX2, iY2] ^= SokoCell.Box;//Put box to new location

                    InvalidatePlayerMoveTree();//Flush player move tree (due to change of box configuration)
                    InvalidateBoxMoveTree();//Flush box move tree
                }
                else
                {//Obstacle - box can not be pushed
                    uRV = MoveResult.WayBlocked;
                    //goto lWayBlocked;
                }
            }
            else if ((bCells[iX, iY] & SokoCell.MaskObstacle) == 0)
            {//No box and no other obstacles - just move
                uRV = MoveResult.Moved;
            }
            else
            {//Otherwise - can not move
                uRV = MoveResult.WayBlocked;
            }

            if (uRV != MoveResult.WayBlocked)
            {
                SetPlayerPos(iX, iY);//Move player to new location
                uPlayerDir = uMove;//Update player direction to direction of this move
            }
            return uRV;
        }
 ///<summary>Update position statistics (move, multiplier)</summary>
 private void AddStats(SokoMove uMove, int iAdd)
 {
     uStats.iMoves += iAdd;//Moves updated always
     if ((uMove & SokoMove.Push) != 0)
         uStats.iPushes += iAdd;//Pushed updated on pushes
 }
 ///<summary>Enlarge reverted route array</summary>
 private void EnlargeReversStack()
 {
     iReversAlloc *= 2;
     SokoMove[] bNew = new SokoMove[iReversAlloc];
     uReversRoute.CopyTo(bNew, 0);
     uReversRoute = bNew;
 }
 ///<summary>Add new move to the end of reverted route</summary>
 private void AddNextRevertedMove(SokoMove uNewMove)
 {
     uReversRoute[iReversLen] = uNewMove;//Put move into route
     iReversLen++;
     if (iReversLen >= iReversAlloc)
         EnlargeReversStack();//Realloc route, if needed
 }
        ///<summary>Undo last move</summary>
        public MoveResult Undo()
        {
            if (iPosition > 0)
            {
                int iDX = 0, iDY = 0;
                MoveResult uRV;// = MoveResult.WayBlocked;

                //Check, that player there he should be    (is it really required?)
                if ((GetCell(iPlayerX, iPlayerY) & SokoCell.Player) == 0) return MoveResult.WayBlocked; //No player

                //Calc coordinates of shift
                switch (bMoves[iPosition - 1] & SokoMove.Direction)
                {
                    case SokoMove.Up: iDY = -1; break;
                    case SokoMove.Down: iDY = 1; break;
                    case SokoMove.Left: iDX = -1; break;
                    case SokoMove.Right: iDX = 1; break;
                }
                int iX = iPlayerX - iDX;//Where to move after undo
                int iY = iPlayerY - iDY;
                int iX2 = iPlayerX + iDX;//Where could be box, that was pushed in move, that now undo-ing
                int iY2 = iPlayerY + iDY;

                if ((GetCell(iX, iY) & SokoCell.MaskObstacle) != 0) return MoveResult.WayBlocked; //Unable to undo there

                uRV = MoveResult.Moved;

                if ((bMoves[iPosition - 1] & SokoMove.Push) != 0)
                {   //Move was push, so we need to pull box to prev location
                    if ((GetCell(iX2, iY2) & SokoCell.Box) == 0) return MoveResult.WayBlocked; //No box there it should be
                    uRV = MoveResult.MovedAndPushBox;

                    //Move box
                    bCells[iX2, iY2] ^= SokoCell.Box;
                    bCells[iPlayerX, iPlayerY] ^= SokoCell.Box;

                    //Update counters stats
                    if ((bCells[iX2, iY2] & SokoCell.Target) != 0)
                    {//Box moved from target
                        iNumFreeBoxes++;//Increment counter of free boxes and free targets
                        iNumFreeTargets++;
                    }
                    if ((bCells[iPlayerX, iPlayerY] & SokoCell.Target) != 0)
                    {//Box moved to target
                        iNumFreeBoxes--;//Decrement counter of free boxes and free targets
                        iNumFreeTargets--;
                    }
                    if ((bCells[iX2, iY2] & SokoCell.CellDeadlock) != 0)
                    {//Box unmoved from cell-deadlock
                        iNumRemainExceedBoxes++;//Increment counter of exceeding boxes
                    }
                    if ((bCells[iPlayerX, iPlayerY] & SokoCell.CellDeadlock) != 0)
                    {//Box unmoved to deadlock (possible only if cell-deadlock detection will fail)
                        iNumRemainExceedBoxes--;//Decrement counter of exceeding boxes
                    }

                    InvalidatePlayerMoveTree();//Flush player move tree (due to change of box configuration)
                    InvalidateBoxMoveTree();//Flush box move tree
                }
                SetPlayerPos(iX, iY);//Move player
                uPlayerDir = bMoves[iPosition - 1];//Update player direction to direction of previous move

                iPosition--;//Step one move down into undo stack

                AddStats(bMoves[iPosition], -1);//Update position statistics
                uLastMove = bMoves[iPosition];//Store move as last move (required for redrawing)

                return uRV;
            }
            return MoveResult.WayBlocked;//We at stack bottom, nothing to undo
        }
 ///<summary>Redo 1 move</summary>
 public MoveResult Redo()
 {
     if (iPosition < iMovesNum)//Only if some redo action exist
     {
         MoveResult bRes = MovePlayer(ref bMoves[iPosition]);//Try to move player
         if (bRes != MoveResult.WayBlocked)
         {//If move is successfull
             uLastMove = bMoves[iPosition];//Store last move (required for redrawing)
             AddStats(bMoves[iPosition], 1);//Update position statistics
             iPosition++;
         }
         return bRes;//Return result of move
     }
     return MoveResult.WayBlocked;//No redo action is possible
 }
 ///<summary>Move player, called by main form</summary>
 public MoveResult NewMove(SokoMove uNewMove)
 {
     MoveResult bRes = MovePlayer(ref uNewMove);//Try to move player
     if (bRes != MoveResult.WayBlocked)
     {
         uLastMove = uNewMove;//Store last move (required for redrawing)
         bMoves[iPosition] = uNewMove;//Store move into undo stack
         //IncStats(uNewMove);
         AddStats(uNewMove, 1);//Update position statistics
         FlushAllRedo();
         iPosition++; iMovesNum++;
         //iMovesNum = iPosition;//This will flush all redo if it was exist
         if (iMovesNum >= iMovesAlloc)//Enlarge undo/redo stack, if necessary
             EnlargeStack();
     }
     return bRes;
 }
 ///<summary>User command to move player</summary>
 public void ActionMovePlayer(SokoMove uDir)
 {
     MoveResult bRes = uGame.NewMove(uDir);
     /*
     //MoveResult bRes = uCurrentLevel.MovePlayer(ref uDir);
     MoveResult bRes = uUndo.NewMove(uDir);
     if (bRes != MoveResult.WayBlocked)
     {
         //uUndo.AddNewMove(uDir, bRes);
         //uUndo.AddNewMove(uDir);
         //this.Refresh();
         //this.Invalidate();
     }*/
     if (bRes != MoveResult.WayBlocked)
     {
         RemoveBoxHighlight();//Box highlighting is not valid anymore
         RedrawAround();
     }
     if (bRes == MoveResult.MovedAndPushBox || bRes == MoveResult.MovedAndPushBoxToTarget)
         uGame.MarkerCurrentMove();//If player moved a box - mark action as group
     if (bRes == MoveResult.MovedAndPushBoxToTarget)
     {//If player put box on target - check, may be level is solved
         CheckLevelCompletition();
     }
     CheckForDeadlocks();//Check for game-deadlock
     UpdateStatus();//Redraw moves/pushes counter
 }