public static IReadOnlyList <TetriminoNode> GetTetriminoDependencyGraph(IReadOnlyList <Tetrimino> tetriminos,
                                                                                int playAreaWidth, int playAreaHeight)
        {
            // Build block map
            var tetriminoNodes = new List <TetriminoNode>(tetriminos.Count);
            var memoizedMap    = new MemoizedBlock[playAreaHeight, playAreaWidth];

            foreach (var tetrimino in tetriminos)
            {
                var tetriminoNode = new TetriminoNode(tetrimino.Kind,
                                                      tetrimino.Position,
                                                      GeneratorHelper.GetFirstBlockPositionByPosition(tetrimino.Position, tetrimino.Kind,
                                                                                                      tetrimino.FacingDirection),
                                                      tetrimino.FacingDirection
                                                      );
                tetriminoNode.Blocks = tetriminoNode.MemoizedBlocks =
                    GetMemoizedBlocksForTetriminoNode(tetriminoNode, tetrimino);

                foreach (var block in tetriminoNode.MemoizedBlocks)
                {
                    memoizedMap[block.Position.Y, block.Position.X] = block;
                }
                tetriminoNodes.Add(tetriminoNode);
            }

            // Get dependency relationship
            foreach (var tetriminoNode in tetriminoNodes)
            {
                foreach (var block in tetriminoNode.MemoizedBlocks)
                {
                    // if a blocker under the current block is occupied then
                    // this tetrimino can not be placed until the underlying block's
                    // owner is placed, i.e., this tetrimino depends on the underlying
                    // block's owner.
                    var dependedBlockRow = block.Position.Y + 1;
                    var dependedBlockCol = block.Position.X;
                    if (!TryGetOccupiedTetriminoNode(
                            memoizedMap,
                            dependedBlockRow, dependedBlockCol,
                            playAreaWidth, playAreaHeight,
                            out var dependOn
                            ) || dependOn == tetriminoNode)
                    {
                        continue;
                    }
                    dependOn.DependedBy.Add(tetriminoNode);
                    tetriminoNode.Depending.Add(dependOn);
                }
            }

            return(tetriminoNodes);
        }
        /// <summary>
        ///     Get a list of playable and encapsulated <see cref="Tetrimino" /> in the pattern of the
        ///     periodic table.
        /// </summary>
        public static IReadOnlyList <Tetrimino> GetPlayablePattern(Random rand)
        {
            var dim0Len  = GeneratorHelper.PeriodicTableTemplate.GetLength(0);
            var dim1Len  = GeneratorHelper.PeriodicTableTemplate.GetLength(1);
            var template = new Block[dim0Len, dim1Len];

            for (var i = 0; i < dim0Len; i++)
            {
                for (var j = 0; j < dim1Len; j++)
                {
                    template[i, j] = new Block(GeneratorHelper.PeriodicTableTemplate[i, j].FilledBy,
                                               GeneratorHelper.PeriodicTableTemplate[i, j].Position,
                                               GeneratorHelper.PeriodicTableTemplate[i, j].AtomicNumber
                                               );
                }
            }

            var tetriminos = TetriminoSorter.Sort(
                GetPossibleTetriminoPattern(template, rand), dim1Len, dim0Len);

            Parallel.ForEach(tetriminos,
                             tetrimino =>
            {
                // Repositioning the tetriminos.
                var originalPosition = tetrimino.Position;
                var newPosition      = GeneratorHelper.GetInitialPositionByKind(tetrimino.Kind);
                var deltaX           = newPosition.X - originalPosition.X;
                var deltaY           = newPosition.Y - originalPosition.Y;
                var newBlocks        = new List <Block>(tetrimino.Blocks.Count);
                newBlocks
                .AddRange(
                    tetrimino.Blocks.Select(
                        block => new Block(block.FilledBy,
                                           new Position(block.Position.X + deltaX, block.Position.Y + deltaY),
                                           block.AtomicNumber,
                                           block.Identifier)
                        )
                    );
                tetrimino.Blocks   = newBlocks;
                tetrimino.Position = newPosition;

                // Randomly rotate the current Tetrimino.
                var rotationCount = rand.Next(0, Enum.GetValues(typeof(Direction)).Length);
                for (var i = 0; i < rotationCount; i++)
                {
                    tetrimino.TryRotate(RotationDirection.Right, _ => false);
                }
            });
            return(tetriminos);
        }