///<summary>Result retrieving</summary> public void GetResults(SokobanGame uDestinationGame) { uDestinationGame.DownloadDeadlocks(uTempGame); //uMainGame.Xdeadlocks }
///<summary>Provide info about game(source game - to copy)</summary> public void PleaseGetGame(SokobanGame uSourceGame) { uTempGame = new SokobanGame(uSourceGame); }
/* ///<summary>Wave algorithm of building box move tree (x-start,y-start)</summary> public FunctionResult BuildBoxMoveTreeFull_OLD(int iXstart, int iYstart) { int x, y, z; int iStop; short iThisCheck, iNextCheck; short iLeftToDown, iLeftToRight, iLeftToUp, iRightToDown, iRightToUp, iUpToDown; int iCount = 0; iBoxMoveTree = new short[iXsize, iYsize, 5]; SokobanLevel uTempLevel = new SokobanLevel(this); //remove box from it's source location and player from current location uTempLevel.bCells[iPlayerX, iPlayerY] ^= SokoCell.Player; uTempLevel.bCells[iXstart, iYstart] ^= SokoCell.Box; for (y = 0; y < iYsize; y++) for (x = 0; x < iXsize; x++) { for (z = 0; z < 4; z++) iBoxMoveTree[x, y, z] = MT_NOT_REACHED; iBoxMoveTree[x, y, BMT_FLAGS] = 0; if ((uTempLevel.bCells[x, y] & SokoCell.Obstacle) != 0 || x == 0 || y == 0 || x == (iXsize - 1) || y == (iYsize - 1)) { for (z = 0; z < 4; z++) iBoxMoveTree[x, y, z] = MT_BLOCKED; } else { if ((uTempLevel.bCells[x, y - 1] & SokoCell.Obstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_UP] = MT_BLOCKED;//blocked if ((uTempLevel.bCells[x - 1, y] & SokoCell.Obstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_LEFT] = MT_BLOCKED;//blocked if ((uTempLevel.bCells[x, y + 1] & SokoCell.Obstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_DOWN] = MT_BLOCKED;//blocked if ((uTempLevel.bCells[x + 1, y] & SokoCell.Obstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = MT_BLOCKED;//blocked } } BuildPlayerMoveTree(iPlayerX, iPlayerY); z = 0; if (iMoveTree[iXstart - 1, iYstart] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_LEFT] = iMoveTree[iXstart - 1, iYstart]; z = 1; } if (iMoveTree[iXstart + 1, iYstart] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_RIGHT] = iMoveTree[iXstart + 1, iYstart]; z = 2; } if (iMoveTree[iXstart, iYstart - 1] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_UP] = iMoveTree[iXstart, iYstart - 1]; z = 3; } if (iMoveTree[iXstart, iYstart + 1] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_DOWN] = iMoveTree[iXstart, iYstart + 1]; z = 4; } if (z == 0) { //No way to selected box from current player location - exit return FunctionResult.StartIsBlocked; } iBoxMoveTree[iXstart, iYstart, BMT_FLAGS] = BMTF_ACHIVED | BMTF_TO_CHECK_EVEN; iThisCheck = BMTF_TO_CHECK_EVEN; iNextCheck = BMTF_TO_CHECK_ODD; iStop = 0; while (iStop == 0) { iStop = 1; for (y = 1; y < (iYsize - 1); y++) for (x = 1; x < (iXsize - 1); x++) if ((iBoxMoveTree[x, y, BMT_FLAGS] & iThisCheck) != 0) { iCount++; //required to check uTempLevel.bCells[x, y] ^= SokoCell.Box; //tempr. set box uTempLevel.InvalidatePlayerMoveTree();//remove player move tree to not stumble with it, if it was calculate for current cell but for other box configuration uTempLevel.BuildPlayerMoveTree(x - 1, y);//build player move tree from left to box iLeftToRight = uTempLevel.iMoveTree[x + 1, y]; iLeftToUp = uTempLevel.iMoveTree[x, y - 1]; iLeftToDown = uTempLevel.iMoveTree[x, y + 1]; uTempLevel.BuildPlayerMoveTree(x + 1, y);//from right iRightToDown = uTempLevel.iMoveTree[x, y + 1]; iRightToUp = uTempLevel.iMoveTree[x, y - 1]; uTempLevel.BuildPlayerMoveTree(x, y + 1); //from down iUpToDown = uTempLevel.iMoveTree[x, y - 1]; uTempLevel.bCells[x, y] ^= SokoCell.Box; //remove box if (iLeftToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_LEFT; if (iLeftToRight > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_LEFT_TO_RIGHT; if (iLeftToUp > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_LEFT; if (iRightToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_RIGHT; if (iRightToUp > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_RIGHT; if (iUpToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_DOWN; //if (x == 6 && y == 7) //{ // x = x; //} if (iBoxMoveTree[x, y, PLAYER_TO_UP] >= 0) {//Player-to-up is achived if (iLeftToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + iLeftToUp); iStop = 0; } if (iUpToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + iUpToDown); iStop = 0; } if (iRightToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + iRightToUp); iStop = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_LEFT] >= 0) {//Player-to-left is achived if (iLeftToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + iLeftToUp); iStop = 0; } if (iLeftToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + iLeftToDown); iStop = 0; } if (iLeftToRight > 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + iLeftToRight); iStop = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] >= 0) {//Player-to-right is achived if (iLeftToRight > 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + iLeftToRight); iStop = 0; } if (iRightToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + iRightToDown); iStop = 0; } if (iRightToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + iRightToUp); iStop = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_DOWN] >= 0) {//Player-to-down is achived if (iLeftToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + iLeftToDown); iStop = 0; } if (iUpToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + iUpToDown); iStop = 0; } if (iRightToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + iRightToDown); iStop = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_UP] >= 0) {//Player-to-up is achived if (iBoxMoveTree[x, y + 1, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y + 1, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + 1); iBoxMoveTree[x, y + 1, BMT_FLAGS] |= iNextCheck;//Set "to check" flag iStop = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_LEFT] >= 0) {//Player-to-left is achived if (iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + 1); iBoxMoveTree[x + 1, y, BMT_FLAGS] |= iNextCheck; iStop = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] >= 0) {//Player-to-right is achived if (iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + 1); iBoxMoveTree[x - 1, y, BMT_FLAGS] |= iNextCheck; iStop = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_DOWN] >= 0) {//Player-to-down is achived if (iBoxMoveTree[x, y - 1, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y - 1, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + 1); iBoxMoveTree[x, y - 1, BMT_FLAGS] |= iNextCheck; iStop = 0; } } //Remove "to check" flag from this cell iBoxMoveTree[x, y, BMT_FLAGS] &= BMTF_MASK_CLEAR_TOCHECK; iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_ACHIVED; } if (iThisCheck == BMTF_TO_CHECK_ODD) { iThisCheck = BMTF_TO_CHECK_EVEN; iNextCheck = BMTF_TO_CHECK_ODD; } else { iThisCheck = BMTF_TO_CHECK_ODD; iNextCheck = BMTF_TO_CHECK_EVEN; } } iBoxMoveTreeFromX = iXstart; iBoxMoveTreeFromY = iYstart; return FunctionResult.OK; } */ //with XXYY - 100ms on aenigma-42, 62ms on aborelia-22 //with UpTo only - 120ms on aenigma-42, 62ms on aborelia-22 //without - 140ms on aenigma-42, 70ms on aborelia-22, 66 on aenigma-39, 54 on aenigma-36 //UpTo + 6step - 85 on aenigma-42, 60 on aenigma-39, 50 on aenigma-36, 66 on aborelia-22 ///<summary>Wave algorithm of building box move tree (x,y of box start, flag of stop on cell-deadlock or not)</summary> public FunctionResult BuildBoxMoveTreeFull(int iXstart, int iYstart, bool bStopOnDeadlocks) { /* * Function build box move tree by wave algorithm - from current location box moving propagated * to all possible (by game rulse) directions with chosing minimal (by moves) actions to reach * one box position by different ways. * Resulting tree can be used to quickly build moving-pushing sequence to push box into any location (only if location is achived) */ int x, y, z; short iLeftToDown, iLeftToRight, iLeftToUp, iRightToDown, iRightToUp, iUpToDown; bool bLeft, bRight, bUp, bDown; iBoxMoveTree = new short[iXsize, iYsize, 5];//Alloc box move tree array SokobanGame uTempLevel = new SokobanGame(this);//Temprorary level as copy of current CoordQueue uQueue = new CoordQueue();//Queue for verifying cells SokoCell bObstacle = SokoCell.MaskObstacle;//Ordinary obstacle mask if (bStopOnDeadlocks) bObstacle |= SokoCell.CellDeadlock;//Add cell-deadlock as obstacle //Remove box from it's source location and player from current location uTempLevel.bCells[iPlayerX, iPlayerY] ^= SokoCell.Player; uTempLevel.bCells[iXstart, iYstart] ^= SokoCell.Box; //Mark forbidden branches of tree for (y = 0; y < iYsize; y++) for (x = 0; x < iXsize; x++) { for (z = 0; z < 4; z++) iBoxMoveTree[x, y, z] = MT_NOT_REACHED;//All - not yet reached iBoxMoveTree[x, y, BMT_FLAGS] = 0; if ((uTempLevel.bCells[x, y] & bObstacle) != 0 || x == 0 || y == 0 || x == (iXsize - 1) || y == (iYsize - 1)) {//Obstacle on cell or border cell (topmost, bottommost, leftmost and rightmost lines of level MUST contain ONLY walls or backgrounds, so box move tree should not reach them) for (z = 0; z < 4; z++) iBoxMoveTree[x, y, z] = MT_BLOCKED; } else {//Check blocking by obstacle at player position if ((uTempLevel.bCells[x, y - 1] & SokoCell.MaskObstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_UP] = MT_BLOCKED;//blocked if ((uTempLevel.bCells[x - 1, y] & SokoCell.MaskObstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_LEFT] = MT_BLOCKED;//blocked if ((uTempLevel.bCells[x, y + 1] & SokoCell.MaskObstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_DOWN] = MT_BLOCKED;//blocked if ((uTempLevel.bCells[x + 1, y] & SokoCell.MaskObstacle) != 0) iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = MT_BLOCKED;//blocked } } //Check that box is reachable by player and set "seed"/root for box move tree BuildPlayerMoveTree(iPlayerX, iPlayerY); for (z = 0; z < 4; z++) bBoxMoveTreeRootDirections[z] = false; z = 0; if (iMoveTree[iXstart - 1, iYstart] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_LEFT] = iMoveTree[iXstart - 1, iYstart]; z = 1; bBoxMoveTreeRootDirections[PLAYER_TO_LEFT] = true; } if (iMoveTree[iXstart + 1, iYstart] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_RIGHT] = iMoveTree[iXstart + 1, iYstart]; z = 2; bBoxMoveTreeRootDirections[PLAYER_TO_RIGHT] = true; } if (iMoveTree[iXstart, iYstart - 1] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_UP] = iMoveTree[iXstart, iYstart - 1]; z = 3; bBoxMoveTreeRootDirections[PLAYER_TO_UP] = true; } if (iMoveTree[iXstart, iYstart + 1] >= 0) { iBoxMoveTree[iXstart, iYstart, PLAYER_TO_DOWN] = iMoveTree[iXstart, iYstart + 1]; z = 4; bBoxMoveTreeRootDirections[PLAYER_TO_DOWN] = true; } if (z == 0) { //No way to specified box from current player location - exit return FunctionResult.StartIsBlocked; } //Root of tree - start location iBoxMoveTree[iXstart, iYstart, BMT_FLAGS] = BMTF_ACHIVED | BMTF_TO_CHECK; uQueue.AddNext(iXstart, iYstart); //Propagating tree to all cells - by checking cells, received from queue and adding new cells to this queue x = 0; y = 0; while (uQueue.Get(ref x, ref y))//Get next cell from queue { if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_TO_CHECK) != 0) { //If cell is marked as required to check (i.e. was marked but not yet checked since that) //Get blocking state of 4 possible player positions around box bUp = (iBoxMoveTree[x, y, PLAYER_TO_UP] != MT_BLOCKED); bLeft = (iBoxMoveTree[x, y, PLAYER_TO_LEFT] != MT_BLOCKED); bRight = (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] != MT_BLOCKED); bDown = (iBoxMoveTree[x, y, PLAYER_TO_DOWN] != MT_BLOCKED); //Paths between all this 4 positions - initially they are not known iLeftToRight = MT_NOT_REACHED; iLeftToUp = MT_NOT_REACHED; iLeftToDown = MT_NOT_REACHED; iRightToDown = MT_NOT_REACHED; iRightToUp = MT_NOT_REACHED; iUpToDown = MT_NOT_REACHED; if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_ACHIVED) != 0) { //Not first pass of this cell iCountDebug++;//Count all additional passes } uTempLevel.bCells[x, y] ^= SokoCell.Box;//Temprorary put box to this cell uTempLevel.InvalidatePlayerMoveTree();//Flush player move tree to not stumble with it, if it was calculate for current cell but for other box configuration //If some positions around box is blocked - block all corresponded paths if (!bLeft) { iLeftToDown = MT_BLOCKED; iLeftToRight = MT_BLOCKED; iLeftToUp = MT_BLOCKED; } if (!bRight) { iRightToDown = MT_BLOCKED; iLeftToRight = MT_BLOCKED; iRightToUp = MT_BLOCKED; } if (!bUp) { iUpToDown = MT_BLOCKED; iRightToUp = MT_BLOCKED; iLeftToUp = MT_BLOCKED; } if (!bDown) { iLeftToDown = MT_BLOCKED; iRightToDown = MT_BLOCKED; iUpToDown = MT_BLOCKED; } /* //Check simple "turn over box in two steps" if (bLeft) { if (bUp) if ((uTempLevel.bCells[x - 1, y - 1] & SokoCell.Obstacle) ==0) iLeftToUp =2; if (bDown) if ((uTempLevel.bCells[x - 1, y + 1] & SokoCell.Obstacle) == 0) iLeftToDown = 2; if (iLeftToUp == 2 && iLeftToDown == 2) iUpToDown = 4;//two turns - go box around in 4 steps } if (bRight ) { if (bUp) if ((uTempLevel.bCells[x + 1, y - 1] & SokoCell.Obstacle) == 0) iRightToUp = 2; if (bDown) if ((uTempLevel.bCells[x + 1, y + 1] & SokoCell.Obstacle) == 0) iRightToDown = 2; if (iRightToUp == 2 && iRightToDown == 2) iUpToDown = 4;//two turns - go box around in 4 steps }/**/ //Check simple "turn over box" in two steps if (bLeft) { if (bUp) if ((uTempLevel.bCells[x - 1, y - 1] & SokoCell.MaskObstacle) == 0) iLeftToUp = 2;//Left, up and left-up cells are empty - so player can move from up to left in 2 steps if (bDown) if ((uTempLevel.bCells[x - 1, y + 1] & SokoCell.MaskObstacle) == 0) //Left, down and left-down cells are empty ... { iLeftToDown = 2; if (iLeftToUp == 2) iUpToDown = 4;//Two turns - player can go around box in 4 steps } } if (bRight) {//By analogue... if (bUp) if ((uTempLevel.bCells[x + 1, y - 1] & SokoCell.MaskObstacle) == 0) iRightToUp = 2; if (bDown) if ((uTempLevel.bCells[x + 1, y + 1] & SokoCell.MaskObstacle) == 0) { iRightToDown = 2; if (iRightToUp == 2) iUpToDown = 4;//Two turns - player can go around box in 4 steps } } //Check left-to-right pass in two-turn-combo and three-turn-combo if (iRightToUp == 2 && iLeftToUp == 2) {//Can go right-to-up and from up-to-left -> can go right-to-left in 4 steps iLeftToRight = 4; if (iLeftToDown == MT_NOT_REACHED && iRightToDown == 2) iLeftToDown = 6;//Also can go right-to-down -> can go left-to-down is 6 steps (if not yet) else if (iLeftToDown == 2 && iRightToDown == MT_NOT_REACHED) iRightToDown = 6;//Also can go left-to-down -> can go right-to-down in 6 steps (if not yet) } else if (iRightToDown == 2 && iLeftToDown == 2) {//By analogue... iLeftToRight = 4; if (iLeftToUp == MT_NOT_REACHED && iRightToUp == 2) iLeftToUp = 6; else if (iLeftToUp == 2 && iRightToUp == MT_NOT_REACHED) iRightToUp = 6; } if (iLeftToRight == MT_NOT_REACHED || iLeftToUp == MT_NOT_REACHED || iLeftToDown == MT_NOT_REACHED) {//Some left-to is not yet calculated - can calculate them only by player move tree uTempLevel.BuildPlayerMoveTree(x - 1, y);//Build player move tree from left to box //uTempLevel.BuildPlayerMoveTree_UpTo(x - 1, y, x + 1, y);//from left to right //uTempLevel.BuildPlayerMoveTree_XXYY(x - 1, y, x+1,x,y-1,y+1);//from left iLeftToRight = uTempLevel.iMoveTree[x + 1, y];//And get reachability from player move tree iLeftToUp = uTempLevel.iMoveTree[x, y - 1]; iLeftToDown = uTempLevel.iMoveTree[x, y + 1]; /* * inefficient block if (iLeftToRight > 0 && iLeftToUp <0) {//there is way from left to right, but no way from left to up => no way from right to up iRightToUp = MT_WILL_NOT_REACH; } if (iLeftToRight > 0 && iLeftToDown < 0) {//there is way from left to right, but no way from left to down => no way from right to down iRightToDown = MT_WILL_NOT_REACH; } if (iLeftToUp > 0 && iLeftToDown < 0) {//there is way from left to up, but no way from left to down => no way from up to down iUpToDown = MT_WILL_NOT_REACH; } if (iLeftToRight < 0 && iLeftToUp > 0) {//there is way from left to right, but no way from left to up => no way from right to up iRightToUp = MT_WILL_NOT_REACH; } if (iLeftToRight < 0 && iLeftToDown > 0) {//there is way from left to right, but no way from left to down => no way from right to down iRightToDown = MT_WILL_NOT_REACH; } if (iLeftToUp < 0 && iLeftToDown > 0) {//there is way from left to up, but no way from left to down => no way from up to down iUpToDown = MT_WILL_NOT_REACH; }*/ /* if (iLeftToRight * iLeftToUp < 0) {//there is way from left to right, but no way from left to up => no way from right to up iRightToUp = MT_WILL_NOT_REACH; } if (iLeftToRight * iLeftToDown < 0) {//there is way from left to right, but no way from left to down => no way from right to down iRightToDown = MT_WILL_NOT_REACH; } if (iLeftToUp * iLeftToDown < 0) {//there is way from left to up, but no way from left to down => no way from up to down iUpToDown = MT_WILL_NOT_REACH; }/**/ } if (iRightToDown == MT_NOT_REACHED || iRightToUp == MT_NOT_REACHED) {//By analogue... uTempLevel.BuildPlayerMoveTree(x + 1, y);//from right //uTempLevel.BuildPlayerMoveTree_XYY(x + 1, y,x, y+1,y-1);//from right iRightToDown = uTempLevel.iMoveTree[x, y + 1]; iRightToUp = uTempLevel.iMoveTree[x, y - 1]; /* inefficient block if (iRightToDown * iRightToUp < 0) {//there is way from right to down, but no way from right to up or visa-versa => no way from up to down iUpToDown = MT_WILL_NOT_REACH; }/**/ /* if (iRightToDown >0 && iRightToUp < 0) {//there is way from right to down, but no way from right to up or visa-versa => no way from up to down iUpToDown = MT_WILL_NOT_REACH; } if (iRightToDown <0 && iRightToUp > 0) {//there is way from right to down, but no way from right to up or visa-versa => no way from up to down iUpToDown = MT_WILL_NOT_REACH; }*/ } if (iUpToDown == MT_NOT_REACHED) {//By analogue... up-to-down is still unknown uTempLevel.BuildPlayerMoveTree(x, y + 1); //from down //uTempLevel.BuildPlayerMoveTree_UpTo(x, y + 1,x,y-1); //from down iUpToDown = uTempLevel.iMoveTree[x, y - 1]; } uTempLevel.bCells[x, y] ^= SokoCell.Box; //Remove temp. box //Set flags of possiblity to walk by paths around box if (iLeftToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_LEFT; if (iLeftToRight > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_LEFT_TO_RIGHT; if (iLeftToUp > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_LEFT; if (iRightToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_RIGHT; if (iRightToUp > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_RIGHT; if (iUpToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_DOWN; //Check propagating tree "around box" if (iBoxMoveTree[x, y, PLAYER_TO_UP] >= 0) {//Player-to-up is achived by tree if (iLeftToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED)//Can walk up-to-left and left is not yet achived { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + iLeftToUp); }//Achive left if (iUpToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED)//Can walk up-to-down and down is not yet achived { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + iUpToDown); }//Achive down if (iRightToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED)//By analogue... { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + iRightToUp); } } if (iBoxMoveTree[x, y, PLAYER_TO_LEFT] >= 0) {//Player-to-left is achived by tree ... by analogue... if (iLeftToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + iLeftToUp); } if (iLeftToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + iLeftToDown); } if (iLeftToRight > 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + iLeftToRight); } } if (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] >= 0) {//Player-to-right is achived by tree ... by analogue... if (iLeftToRight > 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + iLeftToRight); } if (iRightToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + iRightToDown); } if (iRightToUp > 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + iRightToUp); } } if (iBoxMoveTree[x, y, PLAYER_TO_DOWN] >= 0) {//Player-to-down is achived by tree ... by analogue... if (iLeftToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + iLeftToDown); } if (iUpToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + iUpToDown); } if (iRightToDown > 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + iRightToDown); } } //Check propagating tree by pushing box if (iBoxMoveTree[x, y, PLAYER_TO_UP] >= 0) {//Player-to-up is achived by tree if (iBoxMoveTree[x, y + 1, PLAYER_TO_UP] == MT_NOT_REACHED) {//Player-to-up is not achived for cell to down (from currnet) - so box can be pushed to current cell from cell-to-down iBoxMoveTree[x, y + 1, PLAYER_TO_UP] = (short)(iBoxMoveTree[x, y, PLAYER_TO_UP] + 1);//Achive player-to-up for cell-to-down iBoxMoveTree[x, y + 1, BMT_FLAGS] |= BMTF_TO_CHECK;//Set "to check" flag for cell-to-down uQueue.AddNext(x, y + 1);//Add cell-to-down into queue } } if (iBoxMoveTree[x, y, PLAYER_TO_LEFT] >= 0) {//Player-to-left is achived by tree if (iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) //By analogue... { iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_LEFT] + 1); iBoxMoveTree[x + 1, y, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x + 1, y); } } if (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] >= 0) {//Player-to-right is achived if (iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) //By analogue... { iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] = (short)(iBoxMoveTree[x, y, PLAYER_TO_RIGHT] + 1); iBoxMoveTree[x - 1, y, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x - 1, y); } } if (iBoxMoveTree[x, y, PLAYER_TO_DOWN] >= 0) {//Player-to-down is achived if (iBoxMoveTree[x, y - 1, PLAYER_TO_DOWN] == MT_NOT_REACHED) //By analogue... { iBoxMoveTree[x, y - 1, PLAYER_TO_DOWN] = (short)(iBoxMoveTree[x, y, PLAYER_TO_DOWN] + 1); iBoxMoveTree[x, y - 1, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x, y - 1); } } //Remove "to check" flag for this cell iBoxMoveTree[x, y, BMT_FLAGS] &= BMTF_MASK_CLEAR_TOCHECK; iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_ACHIVED;//Cell is achived by tree } } //Queue ends - tree builded //Remember position of root of tree iBoxMoveTreeFromX = iXstart; iBoxMoveTreeFromY = iYstart; return FunctionResult.OK; }
///<summary>Calculate cell-deadlocks for current level and mark all cell-deadlock. ///cell-Deadlock - is cell, from there you cannot push box to target</summary> public FunctionResult CalcDeadlocks() { /* * Function build simplified box move tree for only one box at level and player stand on current location * Then first wave is propagated thru this tree - from targets to other cells but backward by connections of box move tree * Second wave is propagated thru same tree - from boxes to other cells forward by connections of box move tree * All branches of tree, reached by both waves - create not cell-deadlock in corresponding cell (because you can push some box by connections to some target) * All cells, not reached by any of waves - is cell-deadlocks * * (estimated as better to previos version - CalcDeadlocks_old() */ int x, y, z; short iLeftToDown, iLeftToRight, iLeftToUp, iRightToDown, iRightToUp, iUpToDown; int iLeft, iRight, iUp, iDown; int iToCheck; //int iNeiCells; //SokoCell bMask; iBoxMoveTree = new short[iXsize, iYsize, 5];//Alloc box move tree array SokobanGame uTempLevel = new SokobanGame(this);//Temprorary level as copy of current CoordQueue uQueue = new CoordQueue();//Queue for verifying cells //Remove boxes and player from level for (y = 0; y < iYsize; y++) for (x = 0; x < iXsize; x++) uTempLevel.bCells[x, y] &= SokoCell.FilterForCalcCellDeadlocks; //Stage 1. Mark forbidden branches of tree for (y = 0; y < iYsize; y++) for (x = 0; x < iXsize; x++) { iBoxMoveTree[x, y, BMT_FLAGS] = 0; if ((uTempLevel.bCells[x, y] & SokoCell.MaskObstacle) != 0 || x == 0 || y == 0 || x == (iXsize - 1) || y == (iYsize - 1)) {//Obstacle on cell or border cell (topmost, bottommost, leftmost and rightmost lines of level MUST contain ONLY walls or backgrounds, so box move tree should not reach them) for (z = 0; z < 4; z++) iBoxMoveTree[x, y, z] = MT_BLOCKED; } else { iToCheck=0;//Call may not require deep checking for (z = 0; z < 4; z++) iBoxMoveTree[x, y, z] = MT_NOT_REACHED;//Not yet reached uTempLevel.bCells[x, y] ^= SokoCell.Box;//Temprorary put box to this cell uTempLevel.InvalidatePlayerMoveTree();//Flush player move tree to not stumble with it, if it was calculate for current cell but for other box configuration uTempLevel.BuildPlayerMoveTree(iPlayerX, iPlayerY);//Build player move tree - to check reachability of box from all sides if ((uTempLevel.bCells[x, y - 1] & SokoCell.MaskObstacle) != 0) //Obstacle from up iBoxMoveTree[x, y, PLAYER_TO_UP] = MT_BLOCKED;//Player-to-up is blocked else if (uTempLevel.iMoveTree[x,y-1]>=0)//No obstacle and move tree reach cell to up { iBoxMoveTree[x, y, PLAYER_TO_UP] = 0; iToCheck = 1; }//Player-to-up is available and cell should be checked further if ((uTempLevel.bCells[x - 1, y] & SokoCell.MaskObstacle) != 0) //By analogue... iBoxMoveTree[x, y, PLAYER_TO_LEFT] = MT_BLOCKED; else if (uTempLevel.iMoveTree[x-1, y] >= 0) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = 0; iToCheck = 1; } if ((uTempLevel.bCells[x, y + 1] & SokoCell.MaskObstacle) != 0) //By analogue... iBoxMoveTree[x, y, PLAYER_TO_DOWN] = MT_BLOCKED; else if (uTempLevel.iMoveTree[x, y+1] >= 0) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = 0; iToCheck = 1; } if ((uTempLevel.bCells[x + 1, y] & SokoCell.MaskObstacle) != 0) //By analogue... iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = MT_BLOCKED; else if (uTempLevel.iMoveTree[x+1, y] >= 0) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = 0; iToCheck = 1; } if (iToCheck>0) //Cell should be check deeply { uQueue.AddNext(x, y);//Add cell to queue iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_TO_CHECK; //Mark cell as not cheked } /* //Old and unoptimized uTempLevel.BuildPlayerMoveTree(x - 1, y);//build player move tree from left to box iLeftToRight = uTempLevel.iMoveTree[x + 1, y]; iLeftToUp = uTempLevel.iMoveTree[x, y - 1]; iLeftToDown = uTempLevel.iMoveTree[x, y + 1]; uTempLevel.BuildPlayerMoveTree(x + 1, y);//from right iRightToDown = uTempLevel.iMoveTree[x, y + 1]; iRightToUp = uTempLevel.iMoveTree[x, y - 1]; uTempLevel.BuildPlayerMoveTree(x, y + 1); //from down //uTempLevel.BuildPlayerMoveTree_UpTo(x, y + 1,x,y-1); //from down iUpToDown = uTempLevel.iMoveTree[x, y - 1]; /**/ //(more optimization?) //Paths between all this 4 positions - initially they are not known iLeftToRight = MT_NOT_REACHED; iLeftToUp = MT_NOT_REACHED; iLeftToDown = MT_NOT_REACHED; iRightToDown = MT_NOT_REACHED; iRightToUp = MT_NOT_REACHED; iUpToDown = MT_NOT_REACHED; /* //Also old and unoptimized // (sasq6, level50 - 5460 ms) //0x01 - up, 0x02 - up-right, 0x04 - right, 0x08 - right-down, 0x10 - down, 0x20 - down-left, 0x40 - left, 0x80 - left-up iNeiCells = 0; if (uTempLevel.iMoveTree[x, y - 1] == MT_BLOCKED) iNeiCells |= 0x01; if (uTempLevel.iMoveTree[x, y + 1] == MT_BLOCKED) iNeiCells |= 0x10; if (uTempLevel.iMoveTree[x-1, y] == MT_BLOCKED) iNeiCells |= 0x40; if (uTempLevel.iMoveTree[x+1, y] == MT_BLOCKED) iNeiCells |= 0x04; if (uTempLevel.iMoveTree[x-1, y - 1] == MT_BLOCKED) iNeiCells |= 0x80; if (uTempLevel.iMoveTree[x-1, y + 1] == MT_BLOCKED) iNeiCells |= 0x20; if (uTempLevel.iMoveTree[x + 1, y-1] == MT_BLOCKED) iNeiCells |= 0x02; if (uTempLevel.iMoveTree[x + 1, y+1] == MT_BLOCKED) iNeiCells |= 0x08; if ((iNeiCells & 0x07) == 0 || (iNeiCells & 0xFD) == 0) iRightToUp = 1;//iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_RIGHT; if ((iNeiCells & 0xC1) == 0 || (iNeiCells & 0x7F) == 0) iLeftToUp = 1;//iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_LEFT; if ((iNeiCells & 0x1F) == 0 || (iNeiCells & 0xF1) == 0) iUpToDown = 1;//iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_DOWN; if ((iNeiCells & 0x70) == 0 || (iNeiCells & 0xDF) == 0) iLeftToDown = 1;//iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_LEFT; if ((iNeiCells & 0x1C) == 0 || (iNeiCells & 0xF7) == 0) iRightToDown = 1;//iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_RIGHT; if ((iNeiCells & 0x7C) == 0 || (iNeiCells & 0xC7) == 0) iLeftToRight = 1;//iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_LEFT_TO_RIGHT; /**/ //Last optimized: (sasq6, level50 - 3280 ms) //Get number of moves to reach each side of box iUp = uTempLevel.iMoveTree[x, y - 1]; iLeft = uTempLevel.iMoveTree[x - 1, y]; iRight = uTempLevel.iMoveTree[x + 1, y]; iDown = uTempLevel.iMoveTree[x, y + 1]; uTempLevel.InvalidatePlayerMoveTree();//Flush player move tree to not stumble with it, if it was calculate for current cell but for other box configuration //If some positions around box is blocked - block all corresponded paths if (iLeft == MT_BLOCKED) { iLeftToDown = MT_BLOCKED; iLeftToRight = MT_BLOCKED; iLeftToUp = MT_BLOCKED; } if (iRight == MT_BLOCKED) { iRightToDown = MT_BLOCKED; iLeftToRight = MT_BLOCKED; iRightToUp = MT_BLOCKED; } if (iUp == MT_BLOCKED) { iUpToDown = MT_BLOCKED; iRightToUp = MT_BLOCKED; iLeftToUp = MT_BLOCKED; } if (iDown == MT_BLOCKED) { iLeftToDown = MT_BLOCKED; iRightToDown = MT_BLOCKED; iUpToDown = MT_BLOCKED; } if (iUp >= 0) { if (iLeft >= 0) iLeftToUp = 1; //Left and Up is reachable from player start, so path between then exist if (iRight >= 0) iRightToUp = 1; //By analogue... if (iDown >= 0) iUpToDown = 1; //By analogue... } if (iLeft >= 0) { if (iRight >= 0) iLeftToRight = 1; //By analogue... if (iDown >= 0) iLeftToDown = 1; //By analogue... } if (iRight >= 0) { if (iDown >= 0) iRightToDown = 1; //By analogue... } //Check simple "turn over box in two steps" if (iLeft != MT_BLOCKED) { if (iUp != MT_BLOCKED) if ((uTempLevel.bCells[x - 1, y - 1] & SokoCell.MaskObstacle) == 0) iLeftToUp = 2; //Left, up and left-up cells are empty - so player can move from up to left in 2 steps if (iDown != MT_BLOCKED) if ((uTempLevel.bCells[x - 1, y + 1] & SokoCell.MaskObstacle) == 0) //Left, down and left-down cells are empty ... { iLeftToDown = 2; if (iLeftToUp > 0) iUpToDown = 4;//Two turns - player can go around box in 4 steps } } if (iRight != MT_BLOCKED) {//By analogue... if (iUp != MT_BLOCKED) if ((uTempLevel.bCells[x + 1, y - 1] & SokoCell.MaskObstacle) == 0) iRightToUp = 2; if (iDown != MT_BLOCKED) if ((uTempLevel.bCells[x + 1, y + 1] & SokoCell.MaskObstacle) == 0) { iRightToDown = 2; if (iRightToUp > 0) iUpToDown = 4;//Two turns - player can go around box in 4 steps } } //Check left-to-right pass in two-turn-combo and three-turn-combo if (iRightToUp > 0 && iLeftToUp > 0) {//Can go right-to-up and from up-to-left -> can go right-to-left in 4 steps iLeftToRight = 4; if (iRightToDown > 0) iLeftToDown = 6;//Also can go right-to-down -> can go left-to-down is 6 steps (if not yet) else if (iLeftToDown > 0) iRightToDown = 6;//Also can go left-to-down -> can go right-to-down in 6 steps (if not yet) } else if (iRightToDown == 2 && iLeftToDown == 2) {//By analogue... iLeftToRight = 4; if (iRightToUp > 0) iLeftToUp = 6; else if (iLeftToUp > 0) iRightToUp = 6; } if (iLeftToRight == MT_NOT_REACHED || iLeftToUp == MT_NOT_REACHED || iLeftToDown == MT_NOT_REACHED) {//Some left-to is not yet calculated - can calculate them only by player move tree uTempLevel.BuildPlayerMoveTree(x - 1, y);//Build player move tree from left to box iLeftToRight = uTempLevel.iMoveTree[x + 1, y];//And get reachability from player move tree iLeftToUp = uTempLevel.iMoveTree[x, y - 1]; iLeftToDown = uTempLevel.iMoveTree[x, y + 1]; } if (iRightToDown == MT_NOT_REACHED || iRightToUp == MT_NOT_REACHED) {//By analogue... uTempLevel.BuildPlayerMoveTree(x + 1, y);//from right iRightToDown = uTempLevel.iMoveTree[x, y + 1]; iRightToUp = uTempLevel.iMoveTree[x, y - 1]; } if (iUpToDown == MT_NOT_REACHED) {//By analogue... up-to-down is still unknown uTempLevel.BuildPlayerMoveTree_UpTo(x, y + 1, x, y - 1);//from up only to down iUpToDown = uTempLevel.iMoveTree[x, y - 1]; } //Set flags of possiblity to walk by paths around box if (iLeftToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_LEFT; if (iLeftToRight > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_LEFT_TO_RIGHT; if (iLeftToUp > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_LEFT; if (iRightToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_DOWN_TO_RIGHT; if (iRightToUp > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_RIGHT; if (iUpToDown > 0) iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_UP_TO_DOWN; //Remove box uTempLevel.bCells[x, y] ^= SokoCell.Box; } } //Stage 2. Build box move tree //Propagating tree to all cells - by checking cells, received from queue and adding new cells to this queue x = 0; y = 0; while (uQueue.Get(ref x, ref y))//Get next cell from queue { if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_TO_CHECK) != 0) { //If cell is marked as required to check (i.e. was marked but not yet checked since that) if (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] >= 0) {//To-right is achived if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_RIGHT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) {//Right-to-down is possible and to-down is not achived - we can go around box and achive to-down iBoxMoveTree[x, y, PLAYER_TO_DOWN] = 0;//To-down now achived } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_LEFT_TO_RIGHT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = 0;//By analogue... } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_RIGHT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = 0;//By analogue... } } if (iBoxMoveTree[x, y, PLAYER_TO_LEFT] >= 0) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_LEFT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = 0; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_LEFT_TO_RIGHT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = 0; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_LEFT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_UP] >= 0) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_DOWN) != 0 && iBoxMoveTree[x, y, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] = 0; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_LEFT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = 0; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_RIGHT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_DOWN] >= 0) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_LEFT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] = 0; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_RIGHT) != 0 && iBoxMoveTree[x, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] = 0; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_DOWN) != 0 && iBoxMoveTree[x, y, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x, y, PLAYER_TO_UP] = 0; } } if (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] >= 0) {//To-right is achived if (iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] == MT_NOT_REACHED) //To-right for cell to left is not achived - we can push box to left there { iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] = 0; //To-right for cell to left is now achived iBoxMoveTree[x - 1, y, BMT_FLAGS] |= BMTF_TO_CHECK;//Mark cell to left for further checking uQueue.AddNext(x - 1, y);//Add cell to left into queue } } if (iBoxMoveTree[x, y, PLAYER_TO_LEFT] >= 0) {//By analogue... if (iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] == MT_NOT_REACHED) { iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] = 0; iBoxMoveTree[x + 1, y, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x + 1, y); } } if (iBoxMoveTree[x, y, PLAYER_TO_UP] >= 0) {//By analogue... if (iBoxMoveTree[x , y+1, PLAYER_TO_UP] == MT_NOT_REACHED) { iBoxMoveTree[x , y+1, PLAYER_TO_UP] = 0; iBoxMoveTree[x , y+1, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x , y+1); } } if (iBoxMoveTree[x, y, PLAYER_TO_DOWN] >= 0) {//By analogue... if (iBoxMoveTree[x , y-1, PLAYER_TO_DOWN] == MT_NOT_REACHED) { iBoxMoveTree[x , y-1, PLAYER_TO_DOWN] = 0; iBoxMoveTree[x , y-1, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x , y-1); } } iBoxMoveTree[x, y, BMT_FLAGS] &= BMTF_MASK_CLEAR_TOCHECK;//This cell is checked - it should not be checked again, until not marked again } } uQueue.Reset();//Clean queue //Stage 3. Mark all targets on level as non-cell-deadlocks //For all cells of level for (y = 1; y < (iYsize-1); y++) for (x = 1; x < (iXsize-1); x++) { if ((bCells[x, y] & SokoCell.Target)!=0) {//If cell contain target uQueue.AddNext(x, y);//Add cell to queue for checking iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_TO_CHECK;//Mark cell for further checking for (z = 0; z < 4; z++) if (iBoxMoveTree[x, y, z] >= 0)//If branch is achived by tree iBoxMoveTree[x, y, z] |= MT_BACKWAVE_REACH;//Mark cell for backwave } if ((bCells[x, y] & SokoCell.Box) != 0) {//If cell contain box uQueue.AddNext(x, y);//Add cell to queue for checking iBoxMoveTree[x, y, BMT_FLAGS] |= BMTF_TO_CHECK;//Mark cell for further checking for (z = 0; z < 4; z++) if (iBoxMoveTree[x, y, z] >= 0)//If branch is achived by tree iBoxMoveTree[x, y, z] |= MT_FRONTWAVE_REACH;//Mark cell for frontwave } } //Stage 4. Propagate backward and forward waves by box move tree connections //Propagating - by checking cells, received from queue and adding new cells to this queue x = 0; y = 0; while (uQueue.Get(ref x, ref y))//Get next cell from queue { if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_TO_CHECK) != 0) { //If cell is marked as required to check (i.e. was marked but not yet checked since that) //4.1 - backwave if ((iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//To-right is non-cell-deadlock if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_BACKWAVE) == 0) {//Right-to-down is possible and to-down is achived - we can go around box and achive to-down iBoxMoveTree[x, y, PLAYER_TO_DOWN] |= MT_BACKWAVE_REACH;//To-down now non-cell-deadlock } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_LEFT_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] |= MT_BACKWAVE_REACH;//By analogue... } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_UP] |= MT_BACKWAVE_REACH;//By analogue... } } if ((iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] |= MT_BACKWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_LEFT_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] |= MT_BACKWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_UP] |= MT_BACKWAVE_REACH; } } if ((iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_DOWN) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] |= MT_BACKWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] |= MT_BACKWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] |= MT_BACKWAVE_REACH; } } if ((iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] |= MT_BACKWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] |= MT_BACKWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_DOWN) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_UP] |= MT_BACKWAVE_REACH; } } if ((iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//To-right is non-cell-deadlock if ((iBoxMoveTree[x + 1, y, PLAYER_TO_RIGHT] & MT_CHECK_BACKWAVE) == 0) //To-right for cell to right is achived - we can push box from there to here (to left), so cell to right is also non-cell-deadlock { iBoxMoveTree[x + 1, y, PLAYER_TO_RIGHT] |= MT_BACKWAVE_REACH;//To-right for cell to right is non-cell-deadlock now iBoxMoveTree[x + 1, y, BMT_FLAGS] |= BMTF_TO_CHECK;//Mark cell to right for further checking uQueue.AddNext(x + 1, y);//Add cell to right into queue } } if ((iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x - 1, y, PLAYER_TO_LEFT] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x - 1, y, PLAYER_TO_LEFT] |= MT_BACKWAVE_REACH; iBoxMoveTree[x - 1, y, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x - 1, y); } } if ((iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y - 1, PLAYER_TO_UP] & MT_CHECK_BACKWAVE) == 0) { iBoxMoveTree[x, y - 1, PLAYER_TO_UP] |= MT_BACKWAVE_REACH; iBoxMoveTree[x, y - 1, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x, y - 1); } } if ((iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y + 1, PLAYER_TO_DOWN] & MT_BACKWAVE_REACH) == 0) { iBoxMoveTree[x, y + 1, PLAYER_TO_DOWN] |= MT_BACKWAVE_REACH; iBoxMoveTree[x, y + 1, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x, y + 1); } } //4.2 - frontwave if ((iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//To-right is non-cell-deadlock if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_FRONTWAVE) == 0) {//Right-to-down is possible and to-down is achived - we can go around box and achive to-down iBoxMoveTree[x, y, PLAYER_TO_DOWN] |= MT_FRONTWAVE_REACH;//To-down now non-cell-deadlock } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_LEFT_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] |= MT_FRONTWAVE_REACH;//By analogue... } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_UP] |= MT_FRONTWAVE_REACH;//By analogue... } } if ((iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] |= MT_FRONTWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_LEFT_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] |= MT_FRONTWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_UP] |= MT_FRONTWAVE_REACH; } } if ((iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_DOWN) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_DOWN] |= MT_FRONTWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] |= MT_FRONTWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] |= MT_FRONTWAVE_REACH; } } if ((iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_LEFT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_LEFT] |= MT_FRONTWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_DOWN_TO_RIGHT) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_RIGHT] |= MT_FRONTWAVE_REACH; } if ((iBoxMoveTree[x, y, BMT_FLAGS] & BMTF_UP_TO_DOWN) != 0 && (iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y, PLAYER_TO_UP] |= MT_FRONTWAVE_REACH; } } if ((iBoxMoveTree[x, y, PLAYER_TO_RIGHT] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//To-right is non-cell-deadlock if ((iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] & MT_CHECK_FRONTWAVE) == 0) //To-right for cell to right is achived - we can push box from here to there (to right) { iBoxMoveTree[x - 1, y, PLAYER_TO_RIGHT] |= MT_FRONTWAVE_REACH;//To-right for cell to right is non-cell-deadlock now iBoxMoveTree[x - 1, y, BMT_FLAGS] |= BMTF_TO_CHECK;//Mark cell to right for further checking uQueue.AddNext(x - 1, y);//Add cell to right into queue } } if ((iBoxMoveTree[x, y, PLAYER_TO_LEFT] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x + 1, y, PLAYER_TO_LEFT] |= MT_FRONTWAVE_REACH; iBoxMoveTree[x + 1, y, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x + 1, y); } } if ((iBoxMoveTree[x, y, PLAYER_TO_UP] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y + 1, PLAYER_TO_UP] & MT_CHECK_FRONTWAVE) == 0) { iBoxMoveTree[x, y + 1, PLAYER_TO_UP] |= MT_FRONTWAVE_REACH; iBoxMoveTree[x, y + 1, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x, y + 1); } } if ((iBoxMoveTree[x, y, PLAYER_TO_DOWN] & MT_CHECK_FRONTWAVE) == MT_FRONTWAVE_REACH) {//By analogue... if ((iBoxMoveTree[x, y - 1, PLAYER_TO_DOWN] & MT_FRONTWAVE_REACH) == 0) { iBoxMoveTree[x, y - 1, PLAYER_TO_DOWN] |= MT_FRONTWAVE_REACH; iBoxMoveTree[x, y - 1, BMT_FLAGS] |= BMTF_TO_CHECK; uQueue.AddNext(x, y - 1); } } iBoxMoveTree[x, y, BMT_FLAGS] &= BMTF_MASK_CLEAR_TOCHECK;//This cell is checked - it should not be checked again, until not marked again } } //Stage 5. Convert all non-cell-deadlocks from box move tree into cell markers //For all cells of level for (y = 0; y < iYsize; y++) for (x = 0; x < iXsize; x++) { for (z = 0; z < 4; z++)//For all positions of player around box on this cell if (iBoxMoveTree[x, y, z] == MT_BOTHWAVE_REACH)//If at least one direction is reached by both waves, then whole cell is not cell-deadlock //if ((iBoxMoveTree[x, y, z] & MT_CHECK_BACKWAVE) == MT_BACKWAVE_REACH) goto lSkip;//Skip marking cell bCells[x, y] |= SokoCell.CellDeadlock;//All cells, that was not reached by both waves - is cell-deadlocks lSkip: ; } return FunctionResult.OK; }
///<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; }
///<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>Load position from file (filename, levelset name, level number, force loading level/set)</summary> public FunctionResult LoadPosition(string sFileName, ref string sLevelSet, ref int iLevelNum, bool bForceLoadLevel) { IniHold.IniFile uIni = new IniHold.IniFile();//IniHold object - to treat position file as ini-file uIni.LoadIni(sFileName);//Load file as ini-file string sPos = uIni.GetItemValue("Position");//Get sequence of moves //If sequence is empty or not present - exit if (sPos.Length == 0) return FunctionResult.NothingToDo; string sQuestion; string sFileLevelSet = uIni.GetItemValue("LevelSet","<unknown>").ToLower(); int iFileLevel = OQConvertTools.string2int(uIni.GetItemValue("Level", "0")); iLevelNum++;//Levels numbered from 1 in files if (bForceLoadLevel) { } else { if (sFileLevelSet != sLevelSet || iFileLevel != iLevelNum) //Position may be not for current level - warn user { string sLevelNum; if (iFileLevel == 0) sLevelNum = "<unknown> level"; //Level number not specified in file else sLevelNum = "level " + iFileLevel.ToString();//Level number specified if (sFileLevelSet != sLevelSet) //Levelset is different sQuestion = "This position from " + sLevelNum + " of " + sFileLevelSet + " levelset\r\nAre you sure?"; else //Levelset is the same, but levelnumber is different sQuestion = "This position from " + sLevelNum + "\r\nAre you sure?"; if (MessageBox.Show(sQuestion, "Loading position", MessageBoxButtons.OKCancel, MessageBoxIcon.None, MessageBoxDefaultButton.Button2) == DialogResult.Cancel) //Ask user return FunctionResult.Canceled; //Exit, if user decide so } } sLevelSet = sFileLevelSet; iLevelNum = iFileLevel-1; int i; char[] cLurd = sPos.ToCharArray(); iMovesNum = 0;//Flush undo stack for (i = 0; i < cLurd.Length; i++)//Iterate thru all loaded moves { //Decode characters into moves switch(cLurd[i]) { case 'l': bMoves[iMovesNum] = SokoMove.Left; break; case 'r': bMoves[iMovesNum] = SokoMove.Right; break; case 'u': bMoves[iMovesNum] = SokoMove.Up; break; case 'd': bMoves[iMovesNum] = SokoMove.Down; break; case 'L': bMoves[iMovesNum] = SokoMove.PushLeft; break; case 'R': bMoves[iMovesNum] = SokoMove.PushRight; break; case 'U': bMoves[iMovesNum] = SokoMove.PushUp; break; case 'D': bMoves[iMovesNum] = SokoMove.PushDown; break; default: continue;//Skip all other } iMovesNum++; if (iMovesNum >= iMovesAlloc) EnlargeStack();//Realloc stack if needed } return FunctionResult.OK; }*/ ///<summary>Copy deadlocks from other game - in case of background calc</summary> public void DownloadDeadlocks(SokobanGame uGameWithDeadlocks) { for (int i = 0; i < iXsize; i++) for (int j = 0; j < iYsize; j++) if ((uGameWithDeadlocks.bCells[i, j] & SokoCell.CellDeadlock)!=0) { bCells[i, j] |= SokoCell.CellDeadlock; } }
///<summary>Save new record into file</summary> public FunctionResult SaveNewRecord(SokobanGame uSolution) { /* * All solutions of level are sequentaly save into file (currently) * This is kinda log of solving levels * On loading all solutions are checked for best ones */ uLevels[iCurrentLevel].CheckSolutionForRecord(uSolution.uStats);//Update records of current level //Prepare string for writing into file string sSolutionString = iCurrentLevel.ToString("00000"); sSolutionString += ", "; sSolutionString += uSolution.uStats.iMoves.ToString("00000"); sSolutionString += ", "; sSolutionString += uSolution.uStats.iPushes.ToString("00000"); sSolutionString += ", "; sSolutionString += "'" + uSolution.uStats.sName+ "'"; System.IO.StreamWriter hAppend;//For writing file try { hAppend = new System.IO.StreamWriter(sRecordFile, true);//Open file for append hAppend.WriteLine(sSolutionString);//Write string hAppend.Close();//Close file } catch { return FunctionResult.ErrorOnWritingFile;//Unable to open or write file } return FunctionResult.OK;//Successfully }
///<summary>Load level to play it (game, level number)</summary> public FunctionResult LoadLevel(SokobanGame uDstLevel, int iLevelNumber) { if (iLevelNumber < 0 || iLevelNumber >= iLevelsNum) return FunctionResult.OutOfLevelSet; //Incorrent number specified //if (!bKeepPosition) uDstLevel.FlushPosition();//Reset position (undo stack etc.) uDstLevel.InvalidateBoxMoveTree();//Reset BMT uDstLevel.CopyFrom(uLevels[iLevelNumber]);//Copy level content into game uDstLevel.ReAnalyze();//Update level info (box/target counters etc.) iCurrentLevel = iLevelNumber;//Store number of loaded level return FunctionResult.OK;//Successfully }
//<summary>Lock level scrolling</summary> //private bool bScrollLock; //################################################################################################################ //Constructor ///<summary>Top-level contructor for SokobanCompact</summary> public formMain() { FunctionResult uRV; InitializeComponent(); hGameSkin = null; uBackBuffer = null; bDeadlockMessage = false; bHaveUnfinishedPosition = false; Cursor.Current = Cursors.WaitCursor;//Waiting cursor - while loading levelset and skin //Get handle of assembly hExecAssem = System.Reflection.Assembly.GetExecutingAssembly(); //Get file name of assembly string sAppFilePath = hExecAssem.GetModules()[0].FullyQualifiedName; //Get path only sApplicationDirectory = System.IO.Path.GetDirectoryName(sAppFilePath); //Add delimiter at the end if (!sApplicationDirectory.EndsWith(@"\")) sApplicationDirectory += @"\"; //Calc paths for folders //sSavesDirectory = sApplicationDirectory + @"Saves\"; sLevelsDirectory = sApplicationDirectory + @"Levels\"; sSolutionsDirectory = sApplicationDirectory + @"Solutions\"; sSkinsDirectory = sApplicationDirectory + @"Skins\"; sBackgroundsDirectory = sApplicationDirectory + @"Backgrounds\"; //Try to create all required folders, to not bother in future try { //System.IO.Directory.CreateDirectory(sSavesDirectory); System.IO.Directory.CreateDirectory(sBackgroundsDirectory); System.IO.Directory.CreateDirectory(sSkinsDirectory); System.IO.Directory.CreateDirectory(sSolutionsDirectory); System.IO.Directory.CreateDirectory(sLevelsDirectory); } catch (System.IO.IOException) {}//Dont know that to do, if creation fails //Calc rectangles on statusbar int iX = 3; int iStatusHeight = pictureStatus.Height;//Height of status bar Graphics uGr1 = CreateGraphics();//Get graphics of current form SizeF uCounterSizes = uGr1.MeasureString(" 0000", Font);//Measure sample string for get actual font sizes int iCounterWidth = (int)uCounterSizes.Width;//Width of counter - with of sample string int iCounterY0 = (int)(iStatusHeight-uCounterSizes.Height)/2;//Text positions - valign center int iSpace = iCounterWidth/10;//Space between fields - 10% if counter width uGr1.Dispose();//Release graphics of form uRectIndic = new Rectangle(iX, (iStatusHeight-16)/2, 16, 16);//for solved/not-solved indicator iX += uRectIndic.Width + iSpace; uRectMoves = new Rectangle(iX, iCounterY0, iCounterWidth, 16);//for counter of moves iX += uRectMoves.Width + iSpace; uRectPushes = new Rectangle(iX, iCounterY0, iCounterWidth, 16);//for counter of pushes iX += uRectPushes.Width + iSpace; uRectMessage = new Rectangle(iX, iCounterY0, ClientRectangle.Width - iX, 16); //all rest space - for non-modal message //Font f1 = this.Font; //f1.me //this.gr //Graphic .MeasureString //bScrollLock = false; bResize = true; iBottomOfForm = pictureStatus.Top;//this done here for perform recenter of level before any redraws //Create and load settings uSetting = new Settings(); if (uSetting.Load(sApplicationDirectory + sConfigFile) != FunctionResult.OK) { //Settings not loaded - first start or failure of file uSetting = new Settings();//reset to default, just in case... } menuScrollLock.Checked = uSetting.bScrollLock; if (uSetting.bLogActions) { uLog = new LogFile(); uLog.Start(sApplicationDirectory + sLogFile); uLog.LogString(ActionID.Start, "Started SokobanCompact; v" + hExecAssem.GetName().Version); } //Load level set list uLevelSetList = new SokobanLevelSetList(sApplicationDirectory + sLevelSetList, sLevelsDirectory, sSolutionsDirectory); uLevelSetList.LoadList(); iSkinSize = 0; //Load skinset uSkinSet = new SkinSet(); //uSkinSet.Load(sSkinsDirectory + "\\" + "oq.sks");//TODO: move skinset name into Settings uSkinSet.Load(sSkinsDirectory + uSetting.sSkinSet); if (!uSetting.bAutosize) { //No autosize? Load skin now uRV = LoadSkin(sSkinsDirectory + uSetting.sSkin); if (iSkinSize == 0) { LogSimpleLine(ActionID.ShowDialog, "Error; Failed to load skin, null skin created; " + uSetting.sSkin+"; "+uRV.ToString()); MessageBox.Show("Failed to load skin '" + uSetting.sSkin + "' \r\nNull skin will be loaded, " + uRV.ToString(), "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); GenNullSkin(); } } UpdateBackground();//Create background according to settings //Create game uGame = new SokobanGame(); //THREAD uBackgroundCalc = new BackgroundThread(BackgroundFinished);//Create thread for background iSelectorMode = 0;//Selector initialy in "play" mode SetToolBarMode();//Refresh selector //Load levelset uLevelSet = new SokobanLevelSet(); uRV = uLevelSetList.LoadLevelSet(uLevelSet, uSetting.sLastLevelSet); if (uRV != FunctionResult.OK) { //Something happens with levelset file LogSimpleLine(ActionID.ShowDialog, "Error; Failed to load levelset, random will be generated; " + uSetting.sLastLevelSet+"; "+uRV.ToString()); MessageBox.Show("Unable to load LevelSet '" + uSetting.sLastLevelSet + "', result: " + uRV.ToString() + "\r\nRandom level will be loaded.", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); uLevelSetList.GenNullLevelSet(uLevelSet);//Generate levelset with 1 random level uSetting.iLastLevelPlayed = 0;//Res } //Start last played level uRV = LoadLevel(uGame, uSetting.iLastLevelPlayed); if (uRV == FunctionResult.OK) { //Loaded successfully AfterLoadLevel(); } else { //Level not loaded (only variant - FunctionResult.OutOfLevelSet) LogSimpleLine(ActionID.ShowDialog, "Error; Failed to load level, random will be choosen; " + uSetting.iLastLevelPlayed.ToString() + "; " + uRV.ToString()); MessageBox.Show("Unable to load level " + uSetting.iLastLevelPlayed.ToString() + ", result: " + uRV.ToString() + "\r\nRandom level will be selected.", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); ActionRandLevel(); } if (iSkinSize < 1) { LogSimpleLine(ActionID.ShowDialog, "Error; No skin loaded, null skin created"); MessageBox.Show("Failed to load skin\r\nNull skin will be loaded", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); GenNullSkin(); RecenterLevel(); } NonModalMessage(uLevelSet.sTitle + ", " + uGame.sTitle); Cursor.Current = Cursors.Default;//remove wait cursor }
private FunctionResult LoadLevel(SokobanGame uDstLevel, int iLevelNumber) { AutosavePosition(); return uLevelSet.LoadLevel(uDstLevel, iLevelNumber); }