///<summary>Build reverted route of player moving to specific cell (x,y of destination, route to add, build short tree or full)</summary> private MoveResult PlayerTravelRoute(int iXdest, int iYdest, SokobanGame uRoute, bool bUseShortTree) { if (iPlayerX == iXdest && iPlayerY == iYdest) return MoveResult.Moved;//Player already there - nothing to do if (bUseShortTree) {//Using short tree - only to reach destination BuildPlayerMoveTree_UpTo(iPlayerX, iPlayerY, iXdest, iYdest); } else {//Using full tree - for all level BuildPlayerMoveTree(iPlayerX, iPlayerY); } int iMoveLen = iMoveTree[iXdest, iYdest]; if (iMoveLen < 0) return MoveResult.WayBlocked;//Negative values - destination unreachable //Building reverted route from destination to start int x = iXdest; int y = iYdest; int iValue = iMoveLen; while (iValue > 0) {//Find nearest cell thet have value less then iValue and move player there if (iMoveTree[x - 1, y] >= 0 && iMoveTree[x - 1, y] < iValue) { iValue = iMoveTree[x - 1, y]; uRoute.AddNextRevertedMove(SokoMove.Right); x--; continue; } if (iMoveTree[x + 1, y] >= 0 && iMoveTree[x + 1, y] < iValue) { iValue = iMoveTree[x + 1, y]; uRoute.AddNextRevertedMove(SokoMove.Left); x++; continue; } if (iMoveTree[x, y - 1] >= 0 && iMoveTree[x, y - 1] < iValue) { iValue = iMoveTree[x, y - 1]; uRoute.AddNextRevertedMove(SokoMove.Down); y--; continue; } if (iMoveTree[x, y + 1] >= 0 && iMoveTree[x, y + 1] < iValue) { iValue = iMoveTree[x, y + 1]; uRoute.AddNextRevertedMove(SokoMove.Up); y++; continue; } return MoveResult.WayBlocked;//Failed to travel (something very wrong) } return MoveResult.Moved; }
///<summary>Build reverted route of box pushing to specific cell (x,y of destination, route to add)</summary> private MoveResult BoxTravelRoute(int iXdest, int iYdest, SokobanGame uRoute) { //Box move tree must be build before calling int z; int iDir = -1, iDirPre; MoveResult uRv; //Matrix of paths short[,] iMatrix = new short[4, 4]; iMatrix[PLAYER_TO_DOWN, PLAYER_TO_DOWN] = 0; iMatrix[PLAYER_TO_DOWN, PLAYER_TO_LEFT] = BMTF_DOWN_TO_LEFT; iMatrix[PLAYER_TO_DOWN, PLAYER_TO_RIGHT] = BMTF_DOWN_TO_RIGHT; iMatrix[PLAYER_TO_DOWN, PLAYER_TO_UP] = BMTF_UP_TO_DOWN; iMatrix[PLAYER_TO_LEFT, PLAYER_TO_DOWN] = BMTF_DOWN_TO_LEFT; iMatrix[PLAYER_TO_LEFT, PLAYER_TO_LEFT] = 0; iMatrix[PLAYER_TO_LEFT, PLAYER_TO_RIGHT] = BMTF_LEFT_TO_RIGHT; iMatrix[PLAYER_TO_LEFT, PLAYER_TO_UP] = BMTF_UP_TO_LEFT; iMatrix[PLAYER_TO_RIGHT, PLAYER_TO_DOWN] = BMTF_DOWN_TO_RIGHT; iMatrix[PLAYER_TO_RIGHT, PLAYER_TO_LEFT] = BMTF_LEFT_TO_RIGHT; iMatrix[PLAYER_TO_RIGHT, PLAYER_TO_RIGHT] = 0; iMatrix[PLAYER_TO_RIGHT, PLAYER_TO_UP] = BMTF_UP_TO_RIGHT; iMatrix[PLAYER_TO_UP, PLAYER_TO_DOWN] = BMTF_UP_TO_DOWN; iMatrix[PLAYER_TO_UP, PLAYER_TO_LEFT] = BMTF_UP_TO_LEFT; iMatrix[PLAYER_TO_UP, PLAYER_TO_RIGHT] = BMTF_UP_TO_RIGHT; iMatrix[PLAYER_TO_UP, PLAYER_TO_UP] = 0; int x = iXdest; int y = iYdest; if (!IsCellAchivedByBoxMoveTree(x, y)) return MoveResult.WayBlocked; //Destination is unreachable for current box moving tree //Find cell around destination with minimum value - there player will stop after pushing box int iValue = short.MaxValue; for (z = 0; z < 4; z++) if (iBoxMoveTree[x, y, z] < iValue && iBoxMoveTree[x, y, z] >= 0) { iDir = z; iValue = iBoxMoveTree[x, y, z]; } if (iValue == 0 || iDir == -1) { return MoveResult.WayBlocked; //If no cell found - destination is unreachable } //int iMoveLen = iValue; SokobanGame uTempLevel = new SokobanGame(this);//Create temprorary level for pathfinding //Remove from temp-level: player and box-beeing-moved uTempLevel.bCells[iPlayerX, iPlayerY] ^= SokoCell.Player; uTempLevel.bCells[iBoxMoveTreeFromX, iBoxMoveTreeFromY] ^= SokoCell.Box; //Build reverted route lLoop1: //Check pushing box switch (iDir) { case PLAYER_TO_DOWN: y++;//Player to down, so he push box to up to reach this state if (iBoxMoveTree[x, y, iDir] > iValue || iBoxMoveTree[x, y, iDir] < 0) {//Player is on cell with incorrect value, something wrong or route is completed y--;//Return player goto lEnd;//Exit building route } uRoute.AddNextRevertedMove(SokoMove.Up | SokoMove.Push);//Add push-to-up to reverted route break; case PLAYER_TO_UP://By analogue... y--; if (iBoxMoveTree[x, y, iDir] > iValue || iBoxMoveTree[x, y, iDir] < 0) { y++; goto lEnd; } uRoute.AddNextRevertedMove(SokoMove.Down | SokoMove.Push); break; case PLAYER_TO_LEFT: x--; if (iBoxMoveTree[x, y, iDir] > iValue || iBoxMoveTree[x, y, iDir] < 0) { x++; goto lEnd; } uRoute.AddNextRevertedMove(SokoMove.Right | SokoMove.Push); break; case PLAYER_TO_RIGHT: x++; if (iBoxMoveTree[x, y, iDir] > iValue || iBoxMoveTree[x, y, iDir] < 0) { x--; goto lEnd; } uRoute.AddNextRevertedMove(SokoMove.Left | SokoMove.Push); break; default://Unknown direction - something wrong goto lEnd; } if (x == iBoxMoveTreeFromX && y == iBoxMoveTreeFromY) {//Box is in place - root of box move tree if (bBoxMoveTreeRootDirections[iDir]) { //This location of player is reachable from start goto lEnd; } } iValue = iBoxMoveTree[x, y, iDir];//Update value to current box-move-tree cell (added 20.10.2008 to fix bug with additional moves after reaching box) iDirPre = iDir;//Store direction of pushing to compare later //Find cell around box with lesser value on box-move-tree than current - if found, player should move there (around box or by complex path) to proceed (reverted) pushing for (z = 0; z < 4; z++)//Loop on directions if (iBoxMoveTree[x, y, z] < iValue && iBoxMoveTree[x, y, z] >= 0)//Cell with better value (than current) //if (z == iDirPre || (iMatrix[z, iDirPre] & iBoxMoveTree[x, y, BMT_FLAGS]) != 0)//This cell can be reached from current by moving around box (or this is current cell) if ((iMatrix[z, iDirPre] & iBoxMoveTree[x, y, BMT_FLAGS]) != 0)//This cell can be reached from current by moving around box (current cell may not pass previous "if") { iDir = z;//Store new diection iValue = iBoxMoveTree[x, y, z];//Store new value } if (iDir != iDirPre) {//New direction is differ from previous - should build moving route around box (otherwise - go to pushing) //Set box to temp. level to use pathfinding around this box uTempLevel.bCells[x, y] ^= SokoCell.Box; uRv = MoveResult.WayBlocked; uTempLevel.InvalidatePlayerMoveTree();//Flush player move tree //Set player location for start of path finding - new cell near box uTempLevel.iPlayerX = x; uTempLevel.iPlayerY = y; switch (iDir) { case PLAYER_TO_DOWN: uTempLevel.iPlayerY++; break; case PLAYER_TO_UP: uTempLevel.iPlayerY--; break; case PLAYER_TO_LEFT: uTempLevel.iPlayerX--; break; case PLAYER_TO_RIGHT: uTempLevel.iPlayerX++; break; } //Find path from new cell to previous switch (iDirPre) { case PLAYER_TO_DOWN: uRv = uTempLevel.PlayerTravelRoute(x, y + 1, uRoute, true); break; case PLAYER_TO_UP: uRv = uTempLevel.PlayerTravelRoute(x, y - 1, uRoute, true); break; case PLAYER_TO_LEFT: uRv = uTempLevel.PlayerTravelRoute(x - 1, y, uRoute, true); break; case PLAYER_TO_RIGHT: uRv = uTempLevel.PlayerTravelRoute(x + 1, y, uRoute, true); break; } //If no path - something wrong if (uRv == MoveResult.WayBlocked) return uRv; //Remove box uTempLevel.bCells[x, y] ^= SokoCell.Box; } goto lLoop1;//Go to next push lEnd: if (x != iBoxMoveTreeFromX || y != iBoxMoveTreeFromY) return MoveResult.WayBlocked; //if box not reach root of tree - path not builded //Need to find path for player to box //Set box to temp. level to use pathfinding around this box uTempLevel.bCells[x, y] ^= SokoCell.Box; uRv = MoveResult.WayBlocked; uTempLevel.InvalidatePlayerMoveTree();//Flush player move tree uTempLevel.iPlayerX = iPlayerX; uTempLevel.iPlayerY = iPlayerY;//Set player start location - get it from actual level switch (iDir)//Find path { case PLAYER_TO_DOWN: uRv = uTempLevel.PlayerTravelRoute(x, y + 1, uRoute, true); break; case PLAYER_TO_UP: uRv = uTempLevel.PlayerTravelRoute(x, y - 1, uRoute, true); break; case PLAYER_TO_LEFT: uRv = uTempLevel.PlayerTravelRoute(x - 1, y, uRoute, true); break; case PLAYER_TO_RIGHT: uRv = uTempLevel.PlayerTravelRoute(x + 1, y, uRoute, true); break; } if (uRv == MoveResult.WayBlocked) return uRv;//No path - something wring //Reverted route builded return MoveResult.MovedAndPushBox; }