///<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 }