public Level(Level other) : base(new Array2D<Cell>(other.Data)) { // Copy state information. name = other.name; sokobanAssessed = other.sokobanAssessed; boxesAssessed = other.boxesAssessed; outsideAssessed = other.outsideAssessed; sokobanRow = other.sokobanRow; sokobanColumn = other.sokobanColumn; sokobans = other.sokobans; boxes = other.boxes; targets = other.targets; insideSquares = other.insideSquares; isClosed = other.isClosed; unfinishedBoxes = other.unfinishedBoxes; markAccessible = other.markAccessible; validate = other.validate; boxMap = new Array2D<int>(other.boxMap); boxCoordinates = (Coordinate2D[])other.BoxCoordinates.Clone(); insideCoordinates = other.insideCoordinates; Initialize(); }
private void AssessOutside() { if (!boxesAssessed) { AssessBoxes(); } outsideAssessed = true; insideCoordinates = null; // Determine whether there are any occupants. Array2D<bool> outsideMap = GetMap(); if (boxes == 0 && sokobans == 0) { // Initially set all the wall squares to be outside as a barrier. for (int row = 0; row < levelHeight; row++) { for (int column = 0; column < levelWidth; column++) { outsideMap[row, column] = IsWall(row, column); } } // Mark all the floor squares accessible from the perimeter as outside. foreach (Coordinate2D coord in data.PerimeterCoordinates) { if (IsFloor(coord) && !outsideMap[coord]) { outsideMap.FloodFill(coord, false, true); } } } else { // Note: Don't use coordinate iterators since this needs to be as fast as possible. // Initially set all the floor squares to be outside. for (int row = 0; row < levelHeight; row++) { for (int column = 0; column < levelWidth; column++) { outsideMap[row, column] = IsFloor(row, column); } } // Mark all the cells accessible to boxes or the sokoban as inside. for (int i = 0; i < boxes; i++) { Coordinate2D coord = boxCoordinates[i]; outsideMap.FloodFill(coord.Row, coord.Column, true, false); } if (sokobans == 1) { outsideMap.FloodFill(sokobanRow, sokobanColumn, true, false); } else { for (int row = 0; row < levelHeight; row++) { for (int column = 0; column < levelWidth; column++) { if (IsSokoban(row, column)) { outsideMap.FloodFill(row, column, true, false); } } } } } // Mark or clear the outside flag and at the same // time notice if the level is closed or not. isClosed = true; insideSquares = 0; for (int row = 0; row < levelHeight; row++) { for (int column = 0; column < levelWidth; column++) { if (IsFloor(Data[row, column])) { if (outsideMap[row, column]) { Data[row, column] |= Cell.Outside; } else { Data[row, column] &= ~Cell.Outside; insideSquares++; if (IsEdge(row, column)) { // Found a inside square on the perimeter. isClosed = false; } } } } } if (insideSquares == 0) { isClosed = false; } // Inside and outside aren't useful if the level isn't closed. if (!isClosed) { insideSquares = 0; for (int row = 0; row < levelHeight; row++) { for (int column = 0; column < levelWidth; column++) { if (outsideMap[row, column]) { Data[row, column] &= ~Cell.Outside; } } } } }