private List <GrainModel> GetNonInitialGrainsWithMaxCount(GrainCellModel grainCell)
        {
            var grainsCounts       = grainCell.NeighboringCells.StatesCounts;
            var grainsWithMaxCount = new List <GrainModel>();

            int maxCount = 0;

            foreach (var grainCount in grainsCounts)
            {
                if (grainCount.Value > maxCount && grainCount.Key != ZeroState)
                {
                    maxCount = grainCount.Value;
                }
            }

            foreach (var grainCount in grainsCounts)
            {
                if (grainCount.Value == maxCount && grainCount.Key != ZeroState)
                {
                    grainsWithMaxCount.Add((GrainModel)grainCount.Key);
                }
            }

            return(grainsWithMaxCount);
        }
        private List <ICell> GetGrainCellsWithCenterOfMassWithinARadiusForAbsorbingBc(GrainCellModel centerGrainCell, int radius, List <List <ICell> > cellsState)
        {
            if (radius <= 0)
            {
                throw new ArgumentException("Radius must be grater than 0");
            }

            if (radius > RowCount / 2 || radius > ColumnCount / 2)
            {
                throw new ArgumentException("Radius must be smaller than grid size");
            }

            List <ICell>   grainCellsWithinARadius = new List <ICell>();
            GrainCellModel outsideGrainCell        = new GrainCellModel((GrainModel)ZeroState);
            GrainCellModel grainCellInSquare;

            int upperLeftGrainCellRow    = centerGrainCell.RowNumber - radius;
            int upperLeftGrainCellColumn = centerGrainCell.ColumnNumber - radius;

            for (int row = upperLeftGrainCellRow;
                 row <= upperLeftGrainCellRow + 2 * radius;
                 row++)
            {
                if (row >= -1 && row <= RowCount)
                {
                    for (int column = upperLeftGrainCellColumn;
                         column <= upperLeftGrainCellColumn + 2 * radius;
                         column++)
                    {
                        if (column >= -1 && column <= ColumnCount)
                        {
                            if (row == -1 || row == RowCount || column == -1 || column == ColumnCount)
                            {
                                grainCellInSquare = outsideGrainCell;
                            }
                            else
                            {
                                grainCellInSquare = (GrainCellModel)cellsState[row][column];
                            }

                            if (grainCellInSquare != centerGrainCell)
                            {
                                double distanceBetweenCentersOfMass = Math.Sqrt(
                                    Math.Pow(grainCellInSquare.GlobalCenterOfMass.X - centerGrainCell.GlobalCenterOfMass.X, 2) +
                                    Math.Pow(grainCellInSquare.GlobalCenterOfMass.Y - centerGrainCell.GlobalCenterOfMass.Y, 2)
                                    );

                                if (distanceBetweenCentersOfMass <= radius)
                                {
                                    grainCellsWithinARadius.Add(grainCellInSquare);
                                }
                            }
                        }
                    }
                }
            }

            return(grainCellsWithinARadius);
        }
        private List <ICell> GetGrainCellsWithCenterOfMassWithinARadiusForPeriodicBc(GrainCellModel centerGrainCell, int radius, List <List <ICell> > cellsState)
        {
            if (radius <= 0)
            {
                throw new ArgumentException("Radius must be grater than 0");
            }

            if (radius > RowCount / 2 || radius > ColumnCount / 2)
            {
                throw new ArgumentException("Radius must be smaller than grid size");
            }

            List <ICell> grainCellsWithinARadius = new List <ICell>();

            var upperLeftGrainCell = GetUpperLeftGrainCellFromSquarePerimiter(centerGrainCell, radius);

            double centerGrainCellLocalX = centerGrainCell.CenterOfMass.X + radius + centerGrainCell.Width / 2.0;
            double centerGrainCellLocalY = centerGrainCell.CenterOfMass.Y - radius - centerGrainCell.Height / 2.0;

            for (int movesMadeToNextRowCount = 0, globalRow = upperLeftGrainCell.RowNumber, localRow = 0;
                 movesMadeToNextRowCount < 2 * radius + 1;
                 movesMadeToNextRowCount++, globalRow++, localRow++)
            {
                if (globalRow >= RowCount)
                {
                    globalRow = 0;
                }

                for (int movesMadeToNextColumnCount = 0, globalColumn = upperLeftGrainCell.ColumnNumber, localColumn = 0;
                     movesMadeToNextColumnCount < 2 * radius + 1;
                     movesMadeToNextColumnCount++, globalColumn++, localColumn++)
                {
                    if (globalColumn >= ColumnCount)
                    {
                        globalColumn = 0;
                    }

                    var grainCellInSquare = (GrainCellModel)cellsState[globalRow][globalColumn];

                    if (grainCellInSquare != centerGrainCell)
                    {
                        double grainCellInSquareLocalX = grainCellInSquare.CenterOfMass.X + localColumn + grainCellInSquare.Width / 2.0;
                        double grainCellInSquareLocalY = grainCellInSquare.CenterOfMass.Y - localRow - grainCellInSquare.Height / 2.0;

                        double distanceBetweenCentersOfMass = Math.Sqrt(
                            Math.Pow(grainCellInSquareLocalX - centerGrainCellLocalX, 2) +
                            Math.Pow(grainCellInSquareLocalY - centerGrainCellLocalY, 2)
                            );

                        if (distanceBetweenCentersOfMass <= radius)
                        {
                            grainCellsWithinARadius.Add(grainCellInSquare);
                        }
                    }
                }
            }

            return(grainCellsWithinARadius);
        }
        private bool IsNoGrainWithinARadiusForPeriodicBc(GrainCellModel centerCell, int radius)
        {
            if (radius <= 0)
            {
                throw new ArgumentException("Radius must be grater than 0");
            }

            var upperLeftGrainCell = GetUpperLeftGrainCellFromSquarePerimiter(centerCell, radius);

            for (int rowInSquarePerimiter = 0, y = radius;
                 rowInSquarePerimiter < 2 * radius + 1;
                 rowInSquarePerimiter++, y--)
            {
                int rowInGrid = upperLeftGrainCell.RowNumber + rowInSquarePerimiter;

                if (rowInGrid < 0)
                {
                    rowInGrid += RowCount;
                }

                if (rowInGrid >= RowCount)
                {
                    rowInGrid -= RowCount;
                }

                for (int columnInSquarePerimiter = 0, x = -1 * radius;
                     columnInSquarePerimiter < 2 * radius + 1;
                     columnInSquarePerimiter++, x++)
                {
                    int columnInGrid = upperLeftGrainCell.ColumnNumber + columnInSquarePerimiter;

                    if (columnInGrid < 0)
                    {
                        columnInGrid += ColumnCount;
                    }

                    if (columnInGrid >= ColumnCount)
                    {
                        columnInGrid -= ColumnCount;
                    }

                    var cellInSquare = CurrentState[rowInGrid][columnInGrid];

                    double distanceFromCenter = Math.Pow(x, 2) + Math.Pow(y, 2);

                    if (distanceFromCenter <= Math.Pow(radius + 0.5, 2))
                    {
                        if (cellInSquare.State != ZeroState)
                        {
                            return(false);
                        }
                    }
                }
            }

            return(true);
        }
        private void SetCellGrainToRandomFromNeighborhood(GrainCellModel grainCell)
        {
            CreateNewStaticRandom();

            var grainsCounts = grainCell.NeighboringCells.StatesCounts;

            do
            {
                int randomNeighboringGrainInt = staticRandom.Next(grainsCounts.Count);

                grainCell.State = grainsCounts.ElementAt(randomNeighboringGrainInt).Key;
            } while (grainCell.State == ZeroState);
        }
        private List <ICell> GetGrainCellsWithinARadiusForPeriodicBc(GrainCellModel centerGrainCell, int radius, List <List <ICell> > cellsState)
        {
            if (radius <= 0)
            {
                throw new ArgumentException("Radius must be grater than 0");
            }

            if (radius > RowCount / 2 || radius > ColumnCount / 2)
            {
                throw new ArgumentException("Radius must be smaller than grid size");
            }

            List <ICell> grainCellsWithinARadius = new List <ICell>();

            var upperLeftGrainCell = GetUpperLeftGrainCellFromSquarePerimiter(centerGrainCell, radius);

            for (int movesMadeToNextRowCount = 0, row = upperLeftGrainCell.RowNumber, y = radius;
                 movesMadeToNextRowCount < 2 * radius + 1;
                 movesMadeToNextRowCount++, row++, y--)
            {
                if (row >= RowCount)
                {
                    row = 0;
                }

                for (int movesMadeToNextColumnCount = 0, column = upperLeftGrainCell.ColumnNumber, x = -1 * radius;
                     movesMadeToNextColumnCount < 2 * radius + 1;
                     movesMadeToNextColumnCount++, column++, x++)
                {
                    if (column >= ColumnCount)
                    {
                        column = 0;
                    }

                    var grainCellInSquare = cellsState[row][column];

                    if (grainCellInSquare != centerGrainCell)
                    {
                        double distanceFromCenter = Math.Sqrt(Math.Pow(x, 2) + Math.Pow(y, 2));

                        if (distanceFromCenter <= radius + 0.5)
                        {
                            grainCellsWithinARadius.Add(grainCellInSquare);
                        }
                    }
                }
            }

            return(grainCellsWithinARadius);
        }
        private bool IsNoGrainWithinARadius(GrainCellModel centerCell, int radius)
        {
            if (radius <= 0)
            {
                throw new ArgumentException("Radius must be grater than 0");
            }

            if (BoundaryCondition == BoundaryConditionModel.Periodic)
            {
                return(IsNoGrainWithinARadiusForPeriodicBc(centerCell, radius));
            }
            else
            {
                return(IsNoGrainWithinARadiusForAbsorbingBc(centerCell, radius));
            }
        }
        private bool IsNoGrainWithinARadiusForAbsorbingBc(GrainCellModel centerCell, int radius)
        {
            if (radius <= 0)
            {
                throw new ArgumentException("Radius must be grater than 0");
            }

            int upperLeftGrainCellRow    = centerCell.RowNumber - radius;
            int upperLeftGrainCellColumn = centerCell.ColumnNumber - radius;

            for (int row = upperLeftGrainCellRow, y = radius;
                 row <= upperLeftGrainCellRow + 2 * radius;
                 row++, y--)
            {
                if (row >= 0 && row < RowCount)
                {
                    for (int column = upperLeftGrainCellColumn, x = -1 * radius;
                         column <= upperLeftGrainCellColumn + 2 * radius;
                         column++, x++)
                    {
                        if (column >= 0 && column < ColumnCount)
                        {
                            var cellInSquare = CurrentState[row][column];

                            double distanceFromCenter = Math.Pow(x, 2) + Math.Pow(y, 2);

                            if (distanceFromCenter <= Math.Pow(radius + 0.5, 2))
                            {
                                if (cellInSquare.State != ZeroState)
                                {
                                    return(false);
                                }
                            }
                        }
                    }
                }
            }

            return(true);
        }
        private GrainModel GetGrainToExpand(GrainCellModel grainCell)
        {
            CreateNewStaticRandom();

            var grainsWithMaxCount = GetNonInitialGrainsWithMaxCount(grainCell);

            if (grainsWithMaxCount.Any())
            {
                if (grainsWithMaxCount.Count() == 1)
                {
                    return(grainsWithMaxCount.First());
                }
                else
                {
                    return(grainsWithMaxCount.ElementAt(staticRandom.Next(grainsWithMaxCount.Count())));
                }
            }
            else
            {
                return((GrainModel)ZeroState);
            }
        }
        private ICell GetUpperLeftGrainCellFromSquarePerimiter(GrainCellModel centerCell, int radius)
        {
            if (radius <= 0)
            {
                throw new ArgumentException("Radius must be grater than 0");
            }

            int upperLeftGrainCellRow    = centerCell.RowNumber - radius;
            int upperLeftGrainCellColumn = centerCell.ColumnNumber - radius;

            if (upperLeftGrainCellRow < 0)
            {
                upperLeftGrainCellRow += RowCount;
            }

            if (upperLeftGrainCellColumn < 0)
            {
                upperLeftGrainCellColumn += ColumnCount;
            }

            return(CurrentState[upperLeftGrainCellRow][upperLeftGrainCellColumn]);
        }
        private void AddNeighboringCellsToCellsState(List <List <ICell> > cellsState)
        {
            GrainCellModel outsideCell = new GrainCellModel((GrainModel)ZeroState);

            for (int row = 0, cellId = 0; row < RowCount; row++)
            {
                for (int column = 0; column < ColumnCount; column++, cellId++)
                {
                    if (cellsState[row][column].State == ZeroState)
                    {
                        switch (NeighborhoodType)
                        {
                        case CellNeighborhoodTypeModel.VonNeumann:
                        case CellNeighborhoodTypeModel.Moore:
                        case CellNeighborhoodTypeModel.RandomPentagonal:
                        case CellNeighborhoodTypeModel.LeftHexagonal:
                        case CellNeighborhoodTypeModel.RightHexagonal:
                        case CellNeighborhoodTypeModel.RandomHexagonal:
                            switch (BoundaryCondition)
                            {
                            case BoundaryConditionModel.Absorbing:
                                cellsState[row][column].NeighboringCells = new EightSidedGrainCellNeighborhood
                                {
                                    Top         = row == 0 ? outsideCell : cellsState[row - 1][column],
                                    TopRight    = row == 0 || column == ColumnCount - 1 ? outsideCell : cellsState[row - 1][column + 1],
                                    Right       = column == ColumnCount - 1 ? outsideCell : cellsState[row][column + 1],
                                    BottomRight = row == RowCount - 1 || column == ColumnCount - 1 ? outsideCell : cellsState[row + 1][column + 1],
                                    Bottom      = row == RowCount - 1 ? outsideCell : cellsState[row + 1][column],
                                    BottomLeft  = row == RowCount - 1 || column == 0 ? outsideCell : cellsState[row + 1][column - 1],
                                    Left        = column == 0 ? outsideCell : cellsState[row][column - 1],
                                    TopLeft     = row == 0 || column == 0 ? outsideCell : cellsState[row - 1][column - 1],
                                    Type        = NeighborhoodType
                                };

                                break;

                            case BoundaryConditionModel.Periodic:
                                cellsState[row][column].NeighboringCells = new EightSidedGrainCellNeighborhood
                                {
                                    Top         = cellsState[row == 0 ? RowCount - 1 : row - 1][column],
                                    TopRight    = cellsState[row == 0 ? RowCount - 1 : row - 1][column == ColumnCount - 1 ? 0 : column + 1],
                                    Right       = cellsState[row][column == ColumnCount - 1 ? 0 : column + 1],
                                    BottomRight = cellsState[row == RowCount - 1 ? 0 : row + 1][column == ColumnCount - 1 ? 0 : column + 1],
                                    Bottom      = cellsState[row == RowCount - 1 ? 0 : row + 1][column],
                                    BottomLeft  = cellsState[row == RowCount - 1 ? 0 : row + 1][column == 0 ? ColumnCount - 1 : column - 1],
                                    Left        = cellsState[row][column == 0 ? ColumnCount - 1 : column - 1],
                                    TopLeft     = cellsState[row == 0 ? RowCount - 1 : row - 1][column == 0 ? ColumnCount - 1 : column - 1],
                                    Type        = NeighborhoodType
                                };

                                break;
                            }
                            break;

                        case CellNeighborhoodTypeModel.Radial:
                            switch (BoundaryCondition)
                            {
                            case BoundaryConditionModel.Absorbing:
                                cellsState[row][column].NeighboringCells = new RadialGrainCellNeighborhood(
                                    GetGrainCellsWithinARadiusForAbsorbingBc(
                                        (GrainCellModel)cellsState[row][column],
                                        CellNeighborhoodRadius,
                                        cellsState),
                                    NeighborhoodType);
                                break;

                            case BoundaryConditionModel.Periodic:
                                cellsState[row][column].NeighboringCells = new RadialGrainCellNeighborhood(
                                    GetGrainCellsWithinARadiusForPeriodicBc(
                                        (GrainCellModel)cellsState[row][column],
                                        CellNeighborhoodRadius,
                                        cellsState),
                                    NeighborhoodType);
                                break;
                            }
                            break;

                        case CellNeighborhoodTypeModel.RadialWithCenterOfMass:
                            switch (BoundaryCondition)
                            {
                            case BoundaryConditionModel.Absorbing:
                                cellsState[row][column].NeighboringCells = new RadialGrainCellNeighborhood(
                                    GetGrainCellsWithCenterOfMassWithinARadiusForAbsorbingBc(
                                        (GrainCellModel)cellsState[row][column],
                                        CellNeighborhoodRadius,
                                        cellsState),
                                    NeighborhoodType);
                                break;

                            case BoundaryConditionModel.Periodic:
                                cellsState[row][column].NeighboringCells = new RadialGrainCellNeighborhood(
                                    GetGrainCellsWithCenterOfMassWithinARadiusForPeriodicBc(
                                        (GrainCellModel)cellsState[row][column],
                                        CellNeighborhoodRadius,
                                        cellsState),
                                    NeighborhoodType);
                                break;
                            }
                            break;
                        }
                    }
                }
            }
        }