private static IDictionary <IEnumerable <Point>, int> ParseEntries(IEnumerable <IEnumerable <string> > lines, IDynamicEntryPuzzle puzzle) { var entries = new Dictionary <IEnumerable <Point>, int>(); foreach (IEnumerable <string> line in lines) { try { IEnumerable <Point> cells = line.Take(line.Count() - 1) .Select(pointstr => pointstr.Split(',').Select(coordstr => int.Parse(coordstr))) .Select(coordinates => new Point(coordinates.ElementAt(0), coordinates.ElementAt(1))); if (puzzle.LinearEntries() && (cells.Count() > 2 || cells.Count() < 1)) { throw new ArgumentOutOfRangeException(); } entries[cells] = int.Parse(line.Last()); } catch (FormatException e) { throw new FormatException("Cells in an entry must be represented as pairs of comma-separated coordinates.", e); } catch (ArgumentOutOfRangeException e) { throw new ArgumentOutOfRangeException(puzzle.LinearEntries() ? "An entry must be represented by its first and its last cell as pairs of comma-separated coordinates and its value, all separated by spaces." : "An entry must be represented by a list of its cells as pairs of comma-separated coordinates and its value, all separated by spaces.", e); } catch (Exception) { throw; } } return(entries); }
/// <summary> /// Initializes a Puzzle instance, defining its size, entries and potentially cell values and changing its State from Uninitialized to Initialized. /// </summary> /// <param name="args"> /// The required arguments depend on the DefinitionType of the Puzzle as follows: /// <list type="bullet"> /// <item>Values: An int?[,] the size of the grid that contains initially known cell values.</item> /// <item>ValuesAndWritability: An int?[,] with initially known cell values and a bool[,] of the same size indicating whether each cell can be written to.</item> /// <item>Entries: An IDictionary<IEnumerable<Point>, int> with the extent and associated value of every entry where these features can vary.</item> /// </list> /// </param> public void Initialize(params object[] args) { if (State != PuzzleState.Uninitialized) { throw new InvalidOperationException("Only uninitialized puzzles can be initialized."); } int?[,] grid = null; IDictionary <IEnumerable <Point>, int> entries = null; IDynamicEntryPuzzle thisDEP = null; switch (DefinitionType) { case PuzzleDefinitionType.Values: if (args.Count() != 1 || !(args[0] is int?[, ])) { throw new ArgumentException("Puzzle types defined by given values must be initialized with exactly one argument of type int?[,]."); } grid = (int?[, ])args[0]; Values = new ISet <int?> [grid.GetLength(1), grid.GetLength(0)]; Writable = cell => true; break; case PuzzleDefinitionType.ValuesAndWritability: if (args.Count() != 2 || !(args[0] is int?[, ]) || !(args[1] is bool[, ])) { throw new ArgumentException("Puzzle types defined by given values and writability must be initialized with exactly two arguments of types int?[,] and bool[,]."); } grid = (int?[, ])args[0]; bool[,] writability = (bool[, ])args[1]; if (grid.GetLength(0) != writability.GetLength(0) || grid.GetLength(1) != writability.GetLength(1)) { throw new ArgumentException("The grids indicating the given values and writability of a puzzle defined by those features must be the same size."); } Values = new ISet <int?> [grid.GetLength(1), grid.GetLength(0)]; Writable = cell => writability[cell.Y, cell.X]; break; case PuzzleDefinitionType.Entries: if (args.Count() != 1 || !(args[0] is IDictionary <IEnumerable <Point>, int>)) { throw new ArgumentException("Puzzle types defined by the extents and values of their entries must be initialized with exactly one argument of type IDictionary<IEnumerable<Point>, int>."); } thisDEP = this as IDynamicEntryPuzzle; if (thisDEP == null) { throw new ArgumentException("Puzzle types defined by the extents and values of their entries must implement IDynamicEntryPuzzle."); } entries = (IDictionary <IEnumerable <Point>, int>)args[0]; Values = new ISet <int?> [entries.Keys.SelectMany(cells => cells).Max(cell => cell.Y) + 1, entries.Keys.SelectMany(cells => cells).Max(cell => cell.X) + 1]; if (thisDEP.OneEntryPerCell()) { Writable = cell => true; } else { Writable = cell => GetCandidates(cell).Count != 1 || GetCandidates(cell).Single() != null; } break; default: throw new NotSupportedException(); } if (!ValidGridSizes.Contains(GridSize.Width) || !ValidGridSizes.Contains(GridSize.Height)) { throw new ArgumentException(GridSizeExceptionMessage[ValidGridSizes]); } if (MustBeSquare && Values.GetLength(0) != Values.GetLength(1)) { throw new ArgumentException("This puzzle type must have the same number of rows and columns."); } IEnumerable <int?> possibleValues = Enumerable.Range(MinCellValue, MaxCellValue - MinCellValue + 1).Select(n => (int?)n); switch (DefinitionType) { case PuzzleDefinitionType.Values: case PuzzleDefinitionType.ValuesAndWritability: foreach (int y in Enumerable.Range(0, Values.GetLength(0))) { foreach (int x in Enumerable.Range(0, Values.GetLength(1))) { Values[y, x] = grid[y, x].HasValue ? new HashSet <int?> { grid[y, x].Value } } } : new HashSet <int?>(possibleValues); break;