public void FloodFill() { Bitmap map = new Bitmap(new string[] { "111111111111111111", "100000000000001001", "100000000000001001", "100001111111001001", "100001010001001001", "100001000101001001", "100001111111000001", "111011000000001111", "100000001000010001", "100000001000010001", "111111111111111111" }); Debug.WriteLine(map.ToString()); Evaluator<LocationNode> eval = new Evaluator<LocationNode>(); FloodFillStrategy strategy = new FloodFillStrategy(map, new VectorInt(2, 2)); eval.Evaluate(strategy); Debug.WriteLine(strategy.Result.ToString()); List<LocationNode> path = strategy.GetShortestPath(new VectorInt(9, 9)); Assert.IsNotNull(path); Debug.WriteLine(StringHelper.Join(path, delegate(LocationNode node) { return node.Location.ToString(); }, ", ")); Debug.WriteLine(map.ToString()); // Expected value Assert.AreEqual(new VectorInt(9, 9), path[path.Count - 1].Location); }
/// <summary> /// Strong Constructor. Start with the dead map including all walls. /// </summary> /// <param name="goalMap"></param> /// <param name="wallMap"></param> public DeadMapState(SolverNode currentNode, Bitmap goalMap, Bitmap wallMap, DeadMapAnalysis deadMapAnalysis) : this(goalMap, wallMap, deadMapAnalysis) { this.crateMap = currentNode.CrateMap; this.moveMap = currentNode.MoveMap; this.dynamicNode = currentNode; }
/// <summary> /// Generate the move map for a current position /// </summary> /// <param name="boundryMap"></param> /// <param name="crateMap">May be null, assumes boundryMap is constraintmap</param> /// <param name="playerPosition"></param> /// <returns></returns> public static Bitmap GenerateMoveMap(Bitmap boundryMap, Bitmap crateMap, VectorInt playerPosition) { Bitmap constraints = boundryMap; if (crateMap != null) constraints = boundryMap.BitwiseOR(crateMap); // Flood fill from player position to remove any unreachable positions FloodFillStrategy floodFill = new FloodFillStrategy(constraints, playerPosition); Evaluator<LocationNode> eval = new Evaluator<LocationNode>(); eval.Evaluate(floodFill); return floodFill.Result; }
/// <summary> /// Strong Constructor. Start with the dead map including all walls. /// </summary> /// <param name="goalMap"></param> /// <param name="wallMap"></param> public DeadMapState(Bitmap goalMap, Bitmap wallMap, DeadMapAnalysis deadMapAnalysis) : base("Dead Map", wallMap.Size) { this.goalMap = goalMap; this.wallMap = wallMap; this.deadMapAnalysis = deadMapAnalysis; this.mapSize = wallMap.Size; if (goalMap == null) throw new ArgumentNullException("goalMap"); if (wallMap == null) throw new ArgumentNullException("goalMap"); if (deadMapAnalysis == null) throw new ArgumentNullException("deadMapAnalysis"); }
/// <summary> /// Find all recesses (goal and non-goal) in puzzle map /// </summary> /// <param name="staticAnalysis"></param> /// <param name="outRecesses"></param> /// <param name="outRecessMap"></param> public void FindRecesses(StaticAnalysis staticAnalysis, out List<Recess> outRecesses, out Bitmap outRecessMap) { outRecessMap = new Bitmap(staticAnalysis.WallMap.Size); outRecesses = new List<Recess>(); foreach (VectorInt corner in staticAnalysis.CornerMap.TruePositions) { CheckRecessFromCorner(staticAnalysis, outRecesses, outRecessMap, corner, Direction.Up); CheckRecessFromCorner(staticAnalysis, outRecesses, outRecessMap, corner, Direction.Down); CheckRecessFromCorner(staticAnalysis, outRecesses, outRecessMap, corner, Direction.Left); CheckRecessFromCorner(staticAnalysis, outRecesses, outRecessMap, corner, Direction.Right); } }
//############################################################## //############################################################## //############################################################## // Set operators /// <summary> /// Is the set a superset. Will return true is aSet==aSuperSet /// </summary> /// <param name="aSet"></param> /// <param name="aSuperSet"></param> /// <returns></returns> public bool isSuperSet(Bitmap aSuperSet) { // Unoptimised: Should be much faster to iterator through aSet until a single failure is found return (this.BitwiseAND(aSuperSet) == this); }
/// <summary> /// Overload /// </summary> /// <param name="rhs"></param> /// <returns></returns> public Bitmap BitwiseOR(Bitmap rhs) { return BitwiseOR(this, rhs); }
/// <summary> /// Overload /// </summary> /// <param name="rhs"></param> /// <returns></returns> public Bitmap BitwiseAND(Bitmap rhs) { return BitwiseAND(this, rhs); }
//############################################################## //############################################################## //############################################################## // Bitwise Functions /// <summary> /// Perform a bitwize OR operation on the two maps /// </summary> /// <param name="lhs"></param> /// <param name="rhs"></param> /// <returns></returns> public static Bitmap BitwiseOR(Bitmap lhs, Bitmap rhs) { if (lhs.Size.X != rhs.Size.X && lhs.Size.Y != rhs.Size.Y) throw new InvalidOperationException( string.Format("Bitmaps BitwiseOR may only be performed on identical sizes lhs:{0}; rhs:{1}", lhs.Size, rhs.Size)); Bitmap result = new Bitmap(lhs.Size); for (int ccy = 0; ccy < lhs.Size.Y; ccy++) { result.map[ccy] = lhs.map[ccy] | rhs.map[ccy]; } return result; }
/// <summary> /// Perform a bitwize OR operation on the two maps /// </summary> /// <param name="lhs"></param> /// <param name="rhs"></param> /// <returns></returns> public static Bitmap BitwiseNOT(Bitmap lhs) { Bitmap result = new Bitmap(lhs.Size); for (int ccx = 0; ccx < lhs.Size.X; ccx++) for (int ccy = 0; ccy < lhs.Size.Y; ccy++) { result[ccx, ccy] = !lhs[ccx, ccy]; } return result; }
/// <summary> /// Evluate a boolean result for each value in the Matrix creating a result boolean map /// </summary> /// <param name="boolAction"></param> /// <returns></returns> public Bitmap EvalBoolean(EvalBooleanDelegate boolAction) { Bitmap result = new Bitmap(Size); for (int ccx = 0; ccx < Width; ccx++) for (int ccy = 0; ccy < Height; ccy++) { result[ccx, ccy] = boolAction(values[ccx, ccy]); } return result; }
/// <summary> /// Average the Matrix values in generations /// </summary> /// <returns></returns> public Matrix Average() { // Clone Matrix result = new Matrix(this); // Start will all set values Bitmap currentValues = result.BooleanNotZero(); // Fill in the blanks bool valueSet = false; do { valueSet = false; Bitmap nextValues = new Bitmap(currentValues); for (int ccx = 0; ccx < Width; ccx++) for (int ccy = 0; ccy < Height; ccy++) { if (currentValues[ccx,ccy] == false) { // Not set float total = 0; int count = 0; // Left if (ccx - 1 > 0 && currentValues[ccx - 1, ccy]) { total += result[ccx - 1, ccy]; } count++; // Right if (ccx+1 < Width && currentValues[ccx+1,ccy]) { total += result[ccx + 1, ccy]; } count++; // Up if (ccy - 1 > 0 && currentValues[ccx, ccy-1]) { total += result[ccx, ccy - 1]; } count++; // Down if (ccy + 1 < Height && currentValues[ccx, ccy+1]) { total += result[ccx, ccy + 1]; } count++; if (total > 0) { valueSet = true; result[ccx, ccy] = total/count; nextValues[ccx, ccy] = true; } } } currentValues = nextValues; } while (valueSet); return result; }
/// <summary> /// Generate a bitmap for a particular cell state /// </summary> /// <param name="cell">Search for</param> /// <returns></returns> public Bitmap ToBitmap(CellStates cell) { SizeInt size = Size; Bitmap result = new Bitmap(size); for (int cx = 0; cx < size.X; cx++) for (int cy = 0; cy < size.Y; cy++) { if (this[cx, cy] == cell) result[cx, cy] = true; } return result; }
/// <summary> /// Average the Matrix values in generations /// </summary> /// <param name="VoidCells">Positions that are not include (null/void/zero) in the average process</param> /// <returns></returns> public Matrix Average(Bitmap VoidCells) { // Clone Matrix result = new Matrix(this); // Start will all set values Bitmap currentValues = result.BooleanNotZero(); // Fill in the blanks bool valueSet = false; do { valueSet = false; Bitmap nextValues = new Bitmap(currentValues); for (int ccx = 0; ccx < Width; ccx++) for (int ccy = 0; ccy < Height; ccy++) { // Skip void cells if (VoidCells[ccx, ccy]) continue; if (currentValues[ccx,ccy] == false) { // Not set float total = 0; int count = 0; // Left if (ccx - 1 > 0 && currentValues[ccx - 1, ccy]) { total += result[ccx - 1, ccy]; count++; } // Right if (ccx+1 < Width && currentValues[ccx+1,ccy]) { total += result[ccx + 1, ccy]; count++; } // Up if (ccy - 1 > 0 && currentValues[ccx, ccy-1]) { total += result[ccx, ccy - 1]; count++; } // Down if (ccy + 1 < Height && currentValues[ccx, ccy+1]) { total += result[ccx, ccy + 1]; count++; } if (false) { // Top Left if (ccx - 1 > 0 && ccy - 1 > 0 && currentValues[ccx - 1, ccy - 1]) { total += result[ccx - 1, ccy - 1]; count++; } // Top Right if (ccx + 1 < Width && ccy - 1 > 0 && currentValues[ccx + 1, ccy - 1]) { total += result[ccx + 1, ccy - 1]; count++; } // Bottom Left if (ccx - 1 > 0 && ccy + 1 < Height && currentValues[ccx - 1, ccy + 1]) { total += result[ccx - 1, ccy + 1]; count++; } // Bottom Right if (ccx + 1 < Width && ccy + 1 < Height && currentValues[ccx + 1, ccy + 1]) { total += result[ccx + 1, ccy + 1]; count++; } } if (count > 0) { valueSet = true; result[ccx, ccy] = total/count/3f; nextValues[ccx, ccy] = true; } } } currentValues = nextValues; } while (valueSet); return result; }
/// <summary> /// Moving in each direction try to find another non-goal corner /// </summary> private void CheckRecessFromCorner(StaticAnalysis staticAnalysis, List<Recess> outRecesses, Bitmap outRecessMap, VectorInt CheckPos, Direction CheckDirection) { RectangleInt region = new RectangleInt(new VectorInt(0, 0), outRecessMap.Size.Subtract(1, 1)); VectorInt pos = CheckPos; Direction sideA; Direction sideB; // Check Recess wall side if (CheckDirection == Direction.Up || CheckDirection == Direction.Down) { sideA = Direction.Left; sideB = Direction.Right; } else { sideA = Direction.Up; sideB = Direction.Down; } bool hasSideA = true; bool hasSideB = true; // Try to find another corner with SideA or SideB while (region.Contains(pos)) { // Check Fail if (staticAnalysis.WallMap[pos]) return; // Wall in way- Fail if (hasSideA) { if (!staticAnalysis.WallMap[pos.Offset(sideA)]) hasSideA = false; } if (hasSideB) { if (!staticAnalysis.WallMap[pos.Offset(sideB)]) hasSideB = false; } if (!hasSideA && !hasSideB) return; // Check success // Don't check the first time if (pos != CheckPos) { if (staticAnalysis.CornerMap[pos]) { Recess newRecess = new Recess("Recess #"+(outRecesses.Count+1).ToString(), outRecessMap.Size); outRecesses.Add(newRecess); // Winner // Mark all in path as corner VectorInt setTrue = CheckPos; while (setTrue != pos) { // Individual newRecess[setTrue] = true; // Master (all recesses) outRecessMap[setTrue] = true; // Next setTrue = setTrue.Offset(CheckDirection); } // Do the final node newRecess[pos] = true; outRecessMap[pos] = true; newRecess.GoalCount = newRecess.BitwiseAND(staticAnalysis.GoalMap).Count; return; } } // Next pos = pos.Offset(CheckDirection); } }