/// <summary> /// Initializes a new instance of the <see cref="PixPuzzle.Data.Path"/> class. /// </summary> /// <param name="firstCell">First cell.</param> /// <param name="expectedLength">Expected length.</param> public Path(Cell firstCell, int expectedLength) { if (firstCell == null) { throw new ArgumentException (); } Cells = new List<Cell> (); Cells.Add (firstCell); Color = firstCell.Color; ExpectedLength = expectedLength; }
/// <summary> /// Create a grid and register view data /// </summary> public void CreateGrid (int locationX, int locationY, IGridView view) { Logger.I ("Creating the " + Width + "x" + Height + " grid..."); // Calculate border size // TODO This sucks BorderWidth = 4; int borderStartX = GridLocation.X + (BorderWidth / 2); int borderStartY = GridLocation.Y + (BorderWidth / 2); BorderStartLocation = new Point (borderStartX, borderStartY); GridLocation = BorderStartLocation; // Create the grid Cells = new Cell[Width][]; for (int x=0; x<Width; x++) { Cells [x] = new Cell[Height]; for (int y=0; y<Height; y++) { Cell c = new Cell (); c.X = x; c.Y = y; Cells [x] [y] = c; c.Rect = new Rectangle ( GridLocation.X + c.X * CellSize, GridLocation.Y + c.Y * CellSize, CellSize, CellSize); } } // Initialize the view if (view == null) { throw new ArgumentException (); } this.View = view; }
public void DrawLastCellIncompletePath(Cell cell, Rectangle rect, string pathValue, CellColor color) { UIColor colorUnderText = UIColor.LightGray; // Draw a gray circle! int circleReductionValue = mParent.CellSize / 8; mContext.SetFillColor (colorUnderText.CGColor); mContext.FillEllipseInRect (new RectangleF(rect.X + circleReductionValue, rect.Y + circleReductionValue, mParent.CellSize-2*circleReductionValue, mParent.CellSize-2*circleReductionValue)); // Draw the text mContext.SetFillColor (UIColor.Black.CGColor); mContext.SelectFont ("Helvetica Neue", 12.0f, CGTextEncoding.MacRoman); mContext.ShowTextAtPoint (rect.X + mParent.CellSize/3, rect.Y + 2 * mParent.CellSize / 3, pathValue); }
public void DrawCellText(Cell cell, Rectangle location, string text, CellColor color) { mContext.SelectFont ("Helvetica Neue", 16.0f, CGTextEncoding.MacRoman); // Get the reverse color of the background float sumOfComponents = 0; float r, g, b, a; color.UIColor.GetRGBA (out r, out g, out b, out a); sumOfComponents = r + g + b; UIColor textColor = UIColor.Black; if (sumOfComponents < 0.25f) { textColor = UIColor.White; } else { textColor = UIColor.Black; } mContext.SetFillColor (textColor.CGColor); // Careful with the coordinates!!! // Remember it's a real mess because it's inverted mContext.ShowTextAtPoint (location.X + mParent.CellSize/3, location.Y + 2 * mParent.CellSize / 3, text); }
/// <summary> /// Removes all the cells that are after the given one /// </summary> /// <param name="cell">Cell.</param> public List<Cell> RemoveCellAfter(Cell cell) { int index = Cells.IndexOf (cell); List<Cell> cellsToRemove = new List<Cell> (); for (int i=index+1; i < Cells.Count; i++) { cellsToRemove.Add (Cells[i]); } foreach (var cellToRemove in cellsToRemove) { Cells.Remove (cellToRemove); cellToRemove.Path = null; } return cellsToRemove; }
public Cell PreviousCell(Cell cell) { if (Cells.Contains (cell) == false) return null; int index = Cells.IndexOf (cell); if(index - 1 >= 0) { return Cells [index - 1]; } return null; }
public Cell NextCell(Cell cell) { if (Cells.Contains (cell) == false) return null; int index = Cells.IndexOf (cell); if(index + 1 < Cells.Count) { return Cells [index + 1]; } return null; }
/// <summary> /// Request the grid to be updated, especially some cells that may have been modified /// </summary> public virtual void UpdateView (Cell[] cellsToRefresh) { Rectangle zoneToRefresh = Rectangle.Empty; // Get a complete rectangle of cells foreach (Cell cell in cellsToRefresh) { int x = GridLocation.X + cell.X * CellSize; int y = GridLocation.Y + cell.Y * CellSize; Rectangle cellRect = new Rectangle (x, y, CellSize, CellSize); if (zoneToRefresh == Rectangle.Empty) { zoneToRefresh = cellRect; } else { #if IOS if (zoneToRefresh.Contains (cellRect) == false || zoneToRefresh.IntersectsWith (cellRect) == false) { #elif WINDOWS_PHONE if (zoneToRefresh.Contains (cellRect) == false || zoneToRefresh.Intersects (cellRect) == false) { #endif zoneToRefresh = Rectangle.Union (cellRect, zoneToRefresh); } } } // foreach RefreshZone (zoneToRefresh); } void RefreshZone (Rectangle zoneToRefresh) { // Find cells to refresh in this rect int startX = zoneToRefresh.X / CellSize; int startY = zoneToRefresh.Y / CellSize; for (int x = startX; x < startX + (zoneToRefresh.Width / CellSize); x++) { for (int y = startY; y < startY + (zoneToRefresh.Height / CellSize); y++) { Cell c = GetCell (x, y); if (c != null) { c.IsToDraw = true; } } } // Trigger the refresh if (zoneToRefresh != Rectangle.Empty) { View.Refresh (zoneToRefresh); } }
public void UnselectCell(Cell cell, bool complete, bool cancel) { }
/// <summary> /// Add a cell to the path /// </summary> /// <param name="cell">Cell.</param> public void AddCell(Cell cell) { Cells.Add (cell); cell.Path = this; }
/// <summary> /// Removes the path linked to the given cell /// </summary> /// <param name="cell">Cell.</param> public void RemovePath (Cell cell) { if (cell != null) { if (cell.Path != null) { List<Cell> removedCells = cell.Path.DeleteItself (); UpdateView (removedCells.ToArray ()); } } }
/// <summary> /// Ends the current path creation /// </summary> /// <param name="success">If set to <c>true</c> success.</param> public void EndPathCreation (bool success = false) { // Make sure we're doing path work if (FirstPathCell != null && LastSelectedCell != null) { // Check if grid is complete // = if all cells are in a valid path bool isComplete = true; for (int x=0; x<Width; x++) { for (int y=0; y<Height; y++) { if (Cells [x] [y].Path != null) { isComplete &= (Cells [x] [y].Path.IsValid); } else { // Cell linked to no path, the grid is obvisouly not complete isComplete = false; break; } } } if (isComplete) { EndGrid (); } View.UnselectCell (LastSelectedCell, success, false); // Forget cells LastSelectedCell = null; FirstPathCell = null; } }
/// <summary> /// Create a path between the current known path and the given cell. /// </summary> /// <param name="cell">Cell.</param> public void CreatePath (Cell cell) { bool cancelMove = false; string cancelReason = string.Empty; // The path length must be respected bool lengthOk = (FirstPathCell.Path.Length < FirstPathCell.Path.ExpectedLength); if (lengthOk == false) { cancelMove = true; cancelReason = "The path is too long!"; } else { // We're in the grid and we moved (not the same cell) if (cell != null && cell != LastSelectedCell) { // Make sure we are one cell away from the previous one int x = cell.X - LastSelectedCell.X; int y = cell.Y - LastSelectedCell.Y; if (Math.Abs (x) > 1 || Math.Abs (y) > 1) { cancelMove = true; cancelReason = "Cannot create a path that is not in a cell next the to the first one."; } else { if (cell.Path == null) { // The cell is available for path // Add the cell to the currently edited path FirstPathCell.Path.AddCell (cell); cell.Path = FirstPathCell.Path; // Update the modified cells UpdateView (cell.Path.Cells.ToArray ()); } else { // Already a path in the target cell bool sameColor = cell.Path.Color.Equals (FirstPathCell.Path.Color); bool sameLength = (cell.Path.ExpectedLength == FirstPathCell.Path.ExpectedLength); // Is this an end? We need to be sure we close the path bool fusion = false; if (cell.IsPathStartOrEnd) { fusion = sameColor && sameLength && FirstPathCell != cell && (FirstPathCell.Path.Length + cell.Path.Length == FirstPathCell.Path.ExpectedLength); if (fusion == false) { cancelMove = true; cancelReason = "Cannot close the path: invalid lengths."; } } else { fusion = sameColor && sameLength && FirstPathCell != cell; if (fusion == false) { cancelMove = true; cancelReason = "Cannot mix two differents path."; } } // -- It's a completely different path, do not override if (fusion) { // Fusion between two paths parts Logger.I ("Fusion!"); FirstPathCell.Path.Fusion (cell.Path); cell.Path = FirstPathCell.Path; // Update the modified cells UpdateView (FirstPathCell.Path.Cells.ToArray ()); // Are we ending the path? if (FirstPathCell.Path.IsValid) { // End the creation, the path is complete Logger.I ("Path complete!"); EndPathCreation (true); } } else if (FirstPathCell.Path.Cells.Contains (cell) && Math.Abs (FirstPathCell.Path.IndexOf (LastSelectedCell) - FirstPathCell.Path.IndexOf (cell)) == 1 && cancelMove == false) { // We're getting back // Remove all the cells past the one we jut reached // The current cell will NOT be removed List<Cell> removedCells = FirstPathCell.Path.RemoveCellAfter (cell); // Update the modified cells // And the removed ones List<Cell> cellsToUpdate = new List<Cell> (); cellsToUpdate.AddRange (cell.Path.Cells); cellsToUpdate.AddRange (removedCells); UpdateView (cellsToUpdate.ToArray ()); } } } } else if (cell != FirstPathCell && cell != LastSelectedCell) { // The cell is invalid (probably out of the grid) // Stop the path cancelMove = true; cancelReason = "Invalid cell for path"; } } if (cancelMove) { Logger.I (cancelReason); EndPathCreation (false); View.UnselectCell (cell, false, true); } LastSelectedCell = cell; }
/// <summary> /// Starts a new path creation. /// </summary> /// <returns><c>true</c>, if a path was started, <c>false</c> otherwise.</returns> /// <param name="cell">Cell.</param> public bool StartPathCreation (Cell cell) { if (cell != null) { if (cell.Path != null) { // Check if the cell has a valid path object. // If not or already closed path, do nothing // (you cannot take a cell without path and start messing around) bool isPathClosed = cell.Path.IsClosed; bool isPathLastCell = cell.Path.IsLastCell (cell); if (isPathClosed == false && isPathLastCell) { FirstPathCell = cell.Path.FirstCell; LastSelectedCell = cell; View.SelectCell (cell); return true; } } } return false; }
public void DrawPath(Cell cell, Rectangle pathRect, Point direction, CellColor color) { mContext.DrawImage (pathRect, UIImageEx.GetImageWithOverlayColor(mPathImage, color.UIColor).CGImage); // The old drawing code for perfect paths // context.SetFillColor (color.UIColor.CGColor); // Draw in 2 parts: // First a rect // context.FillRect (pathRect); // // // Then an arc to the end // if (direction.X < 0) { // context.MoveTo (pathRect.Right, pathRect.Top); // context.AddCurveToPoint ( // pathRect.Right + pathRect.Width / 3, // pathRect.Top + pathRect.Height / 3, // pathRect.Right + pathRect.Width / 3, // pathRect.Top + 2 * pathRect.Height / 3, // pathRect.Right, // pathRect.Bottom // ); // } else if (direction.X > 0) { // context.MoveTo (pathRect.Left, pathRect.Top); // context.AddCurveToPoint ( // pathRect.Left - pathRect.Width / 3, // pathRect.Top + pathRect.Height / 3, // pathRect.Left - pathRect.Width / 3, // pathRect.Top + 2 * pathRect.Height / 3, // pathRect.Left, // pathRect.Bottom // ); // } // if (direction.Y < 0) { // context.MoveTo (pathRect.Left, pathRect.Bottom); // context.AddCurveToPoint ( // pathRect.Left + pathRect.Width / 3, // pathRect.Bottom + pathRect.Height / 3, // pathRect.Left + 2 * pathRect.Width / 3, // pathRect.Bottom + pathRect.Height / 3, // pathRect.Right, // pathRect.Bottom // ); // } else if (direction.Y > 0) { // context.MoveTo (pathRect.Left, pathRect.Top); // context.AddCurveToPoint ( // pathRect.Left + pathRect.Width / 3, // pathRect.Top - pathRect.Height / 3, // pathRect.Left + 2 * pathRect.Width / 3, // pathRect.Top - pathRect.Height / 3, // pathRect.Right, // pathRect.Top // ); // } // // context.FillPath (); }
/// <summary> /// Place in the chain /// </summary> /// <returns>The of.</returns> /// <param name="cell">Cell.</param> public int IndexOf(Cell cell) { return Cells.IndexOf (cell); }
public void SelectCell(Cell cell) { }
/// <summary> /// Check if the cell is the last of the path /// </summary> /// <returns><c>true</c> if this instance is last cell the specified cell; otherwise, <c>false</c>.</returns> /// <param name="cell">Cell.</param> public bool IsLastCell(Cell cell) { if (Cells.Count == 0) return false; return Cells [Cells.Count -1] == cell; }
public void DrawCellBase(Cell cell) { bool isValid = cell.Path != null && cell.Path.IsValid; CellColor cellColor = cell.Color; if (cell.Path != null) { cellColor = cell.Path.Color; } CGColor color = cellColor.UIColor.CGColor; mContext.SetFillColor (color); if (mParent.ShouldDisplayFilledCells == false) { // Draw a circle of the color // But reduce the circle value int circleReductionValue = mParent.CellSize / 10; RectangleF cellValueRect = new RectangleF (cell.Rect.X + circleReductionValue, cell.Rect.Y + circleReductionValue, mParent.CellSize - 2 * circleReductionValue, mParent.CellSize - 2 * circleReductionValue); UIImage image = null; if (isValid == false) { image = mSplashImage; image = UIImageEx.GetImageWithOverlayColor (image, cellColor.UIColor); mContext.DrawImage (cellValueRect, image.CGImage); } else { mContext.FillRect (cell.Rect); } } else { // Fill the whole cell to preview puzzle mContext.FillRect (cell.Rect); } }
/// <summary> /// Create a path from a given cell. /// </summary> /// <returns>The last cell.</returns> /// <param name="firstCell">First cell.</param> private Cell InitializePath (Cell firstCell) { Cell lastCell = firstCell; // Start from the first cell int count = 0; Stack<Cell> cellToExplore = new Stack<Cell> (); cellToExplore.Push (firstCell); // Try to move in another direction // Flood fill algorithm while (cellToExplore.Count > 0) { // Get the first cell to explore Cell currentCell = cellToExplore.Pop (); count++; // Mark this cell currentCell.IsMarked = true; // Get the neighbors // -- We use a 4-directional algorithms List<CellPoint> availableDirections = new List<CellPoint> () { new CellPoint(-1,0), new CellPoint(1,0), new CellPoint(0,-1), new CellPoint(0,1) }; // -- Use a random direction for better results int borders = 0; while (availableDirections.Count > 0) { int index = mRandom.Next (availableDirections.Count); CellPoint p = availableDirections [index]; availableDirections.Remove (p); Cell nextCell = GetCell (currentCell.X + p.X, currentCell.Y + p.Y); if (nextCell != null && nextCell.IsMarked == false && nextCell.Color.Equals (firstCell.Color)) { cellToExplore.Push (nextCell); } else { borders += 1; } } bool stopFloodFill = false; // If we got stuck in a corner if (borders == 4) { stopFloodFill = true; } // Limit maximum path lengh randomly int max = mRandom.Next (2, MaxPathLength); if (currentCell == GetCell (0, 0)) { max += 1; } if (count >= max) { stopFloodFill = true; } // Stop flood fill if (stopFloodFill) { cellToExplore.Clear (); // Mark as last lastCell = currentCell; } } firstCell.DefineCellAsPathStartOrEnd (count); lastCell.DefineCellAsPathStartOrEnd (count); return lastCell; }