private async void OnOkClicked(object sender, RoutedEventArgs e)
        {
            this.IsEnabled = false;

            try
            {
                BoardDescription templateDescription = this.boardFormatter.ParseBoardDescription(this.TemplateDescription);
                BoardTemplate template = new BoardTemplate(templateDescription.Width, templateDescription.Height, templateDescription.Values);

                BoardDescription goalDescription = this.boardFormatter.ParseBoardDescription(this.GoalDescription);
                BoardGoal goal = new BoardGoal(goalDescription.Width, goalDescription.Height, goalDescription.Values);

                Drill drillToSave = this.drill.Update(this.DrillName, template, goal);
                if (this.update)
                {
                    await this.drillService.Update(drillToSave);
                }
                else
                {
                    await this.drillService.Add(drillToSave);
                }

                this.SavedDrill = drillToSave;
                this.Close();
            }
            catch (InvalidBoardException exception)
            {
                MessageBox.Show(
                    this,
                    string.Join(Environment.NewLine, exception.Errors.Select(x => x.Message)),
                    "Invalid board",
                    MessageBoxButton.OK,
                    MessageBoxImage.Warning);
            }
            catch (InvalidDrillException exception)
            {
                MessageBox.Show(this, exception.Message, "Invalid drill", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
            catch
            {
                MessageBox.Show("An unexpected error occured during saving.");
            }
            finally
            {
                this.IsEnabled = true;
            }
        }
        public BoardState Generate(BoardTemplate template)
        {
            if (template == null)
            {
                throw new ArgumentNullException(nameof(template));
            }

            int[] values = template.GetValues();
            List<int> set = GetUnspecifiedValues(values);
            List<int> unspecifiedIndices = GetUnspecifiedIndices(values);

            foreach (int unspecifiedIndex in unspecifiedIndices)
            {
                int i = this.random.Next(0, set.Count);
                values[unspecifiedIndex] = set[i];
                set.RemoveAt(i);
            }

            if (!BoardValidator.IsSolvable(template.Width, template.Height, values))
            {
                int i = unspecifiedIndices[0];
                int j = unspecifiedIndices[1];
                if (values[i] == 0)
                {
                    i = unspecifiedIndices[2];
                }
                else if (values[j] == 0)
                {
                    j = unspecifiedIndices[2];
                }

                int temp = values[i];
                values[i] = values[j];
                values[j] = temp;
            }

            return new BoardState(template.Width, template.Height, values);
        }
        public BoardState Generate(BoardTemplate template, BoardGoal goal)
        {
            if (template == null)
            {
                throw new ArgumentNullException(nameof(template));
            }

            if (goal == null)
            {
                throw new ArgumentNullException(nameof(goal));
            }

            for (int i = 0; i < RetryCount; i++)
            {
                BoardState state = this.Generate(template);
                if (!state.Satisfies(goal))
                {
                    return state;
                }
            }

            throw new BoardGenerationException("Failed to generate a board that doesn't satisfy the specified goal.");
        }