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);
        }
Example #2
0
        /// <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&lt;IEnumerable&lt;Point&gt;, int&gt; 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;