private void FillUnkownCellsAsEmpties()
 {
     // вызывается, если все "черные" ячейки заполнены. заполняет неокрашенные ячейки пустотами
     for (int i = 0; i < RowsCount; i++)
     {
         for (int j = 0; j < ColumnsCount; j++)
         {
             if (mainField[i, j].State == StatesOfCell.unknown)
             {
                 mainField[i, j].State = StatesOfCell.empty;
                 Cell.FillCell(i, j, false);
             }
         }
     }
 }
 internal static void SetStatics(RowOrColumnToBeCalculated[] rowsAndColumnsToBeCalculated, CloneOfStaticsOfRowOrColumnToBeCalculated clone)
 {
     // возврат статических значений при вызове перебора всех вариантов
     CloneArray(clone.cloneRowOrColumnToBeCalculated, rowsAndColumnsToBeCalculated);
     rowsAndColumnsToBeCalculated = (RowOrColumnToBeCalculated[])clone.cloneRowOrColumnToBeCalculated.Clone();
     IsError           = false;
     IsMainFieldFilled = clone.isMainFieldFilled;
     TotalFilledCells  = clone.totalFilledCells;
     for (int i = 0; i < RowsCount; i++)
     {
         for (int j = 0; j < ColumnsCount; j++)
         {
             mainField[i, j]       = new Cell();
             mainField[i, j].State = clone.mainField[i, j].State;
             Cell.FillCell(i, j, mainField[i, j].State == StatesOfCell.empty ? false : true, mainField[i, j].State == StatesOfCell.unknown ? true : false);
         }
     }
 }
        private bool TryFillCellFullOrEmpty(RowOrColumnToBeCalculated[] rowsAndColumnsToBeCalculated, int deep = 0)
        {
            bool isAnyResultInThisItitration = false;

            // если варианты расположения условия не расчитаны - рассчитать
            if (!areAllVariantsCalculated)
            {
                VariantsCount = 0;
                SetPossibleSolusions();
                areAllVariantsCalculated = true;
                IsNeedToRemoveVariants   = false;
            }
            // если есть необходимость удалить неподходящие варианты* - удалить или дождаться выполнения удаления
            // *как только в какой-либо строке/столбце закрашивается любая ячейка, то часть вариантов расположения условия для столбца/строки этой ячейки можно отсечь
            if (IsNeedToRemoveVariants)
            {
                if (tasksForRemovingIrrelevantVariants.Count == 0)
                {
                    TryRemoveIrrelevantVariants();
                    IsNeedToRemoveVariants = false;
                }
                else
                {
                    //while (IsNeedToRemoveVariants)
                    Task.WaitAll(tasksForRemovingIrrelevantVariants.ToArray());
                }
            }
            if (VariantsCount == 0)
            {
                IsError = true;
                return(false);
            }
            (int, int)link = (IsRow ? IndexOfRowOrColumn : 0, IsRow ? 0 : IndexOfRowOrColumn);
            // проверка для каждой ячейки в дальнейшем решении
            for (int i = 0; i < lengthOfRowOrColumn; i++)
            {
                if (IsRow)
                {
                    link.Item2 = i;
                }
                else
                {
                    link.Item1 = i;
                }
                // если ячейка уже заполнена - переходим к следующей
                if (mainField[link.Item1, link.Item2].State == StatesOfCell.unknown)
                {
                    // сравниваем значение данной ячейки в каждом из вариантов
                    bool target = possibleSolutions[0][i];
                    bool areAllVariantsEquelForThisCell = true;
                    for (int j = 1; j < VariantsCount; j++)
                    {
                        if (possibleSolutions[j][i] != target)
                        {
                            areAllVariantsEquelForThisCell = false;
                            VariantsCount = possibleSolutions.Count;
                            break;
                        }
                    }
                    // если во всех вариантах расположения условия данная ячейка одинакова, то
                    if (areAllVariantsEquelForThisCell)
                    {
                        mainField[link.Item1, link.Item2].State = target ? StatesOfCell.full : StatesOfCell.empty;   // заполняем поле с решением
                        Cell.FillCell(link.Item1, link.Item2, target);                                               // красим ячейку для вывода пользователю
                        TotalFilledCells           += target ? 1 : 0;                                                // увеличиваем счетчик заполненных "черных" ячеек
                        isAnyResultInThisItitration = true;                                                          // указываем, что в данной итерации была заполнена как минимум 1 ячейка, т.е. следующую итерацию нужно начинать с элемента,
                                                                                                                     //  у которого меньше всего вариантов расположения условия
                        int rowOrColumnToChangeIndex = i + (IsRow ? RowsCount : 0);                                  // ячейка заполнена, значит можно отсечь часть вариантов, указываем индекс строки/столбца для удаления вариантов

                        if (rowsAndColumnsToBeCalculated[rowOrColumnToChangeIndex].areAllVariantsCalculated == true) // если все варианты уже расчитаны, то запускаем удаление неподходящих в фоне
                        {                                                                                            // в противном случае неподходящие варианты будут отсечены на этапе расчета
                            rowsAndColumnsToBeCalculated[rowOrColumnToChangeIndex].IsNeedToRemoveVariants = true;    // ***запуск расчета всех вариантов в фоне может занять очень большой объем памяти для больших кроссвордов
                            AddAndStartTaskForRemovingIrrelevantVariants(rowsAndColumnsToBeCalculated, rowOrColumnToChangeIndex, IndexOfRowOrColumn);
                        }
                    }
                }
            }
            if (VariantsCount == 1)                             // если остался только один вариант,
            {
                IsFull = true;                                  // строка заполнена
                if (TotalFilledCells == TotalFilledCellsTarget) // если все "черные" клетки заполнены,
                {
                    IsMainFieldFilled = true;                   // решение найдено
                    FillUnkownCellsAsEmpties();                 // заполняем оставшиеся (если такие будут) незаполненные ячейки "пустыми"
                    return(false);                              // выходим из цикла
                }
            }
            if (!isAnyResultInThisItitration &&                                                                                     // если ни одна ячейка не заполнена за текущую итерацию,
                deep + 1 < RowsCount + ColumnsCount &&                                                                              // просматривается не последний элемент,
                !rowsAndColumnsToBeCalculated[GetIndexOfRowOrColumnByNumberInQueue(rowsAndColumnsToBeCalculated, deep + 1)].IsFull) // следующий элемент не заполнен полностью,
            //то рекурсивный вызов следующего элемента
            {
                isAnyResultInThisItitration = rowsAndColumnsToBeCalculated[GetIndexOfRowOrColumnByNumberInQueue(rowsAndColumnsToBeCalculated, deep + 1)].TryFillCellFullOrEmpty(rowsAndColumnsToBeCalculated, deep + 1);
            }

            return(isAnyResultInThisItitration);
        }