public override bool FindPattern(GameBoard board)
        {
            Board    = board;
            AllCells = Board.GetAllLines(Coord.ROWS).SelectMany(t => t).ToList();
            for (int coord = Coord.ROWS; coord <= Coord.COLUMNS; coord++)
            {
                int secCoord = 1 - coord;
                for (int iValue = 1; iValue <= Board.Dimension; iValue++)
                {
                    int count;

                    // IGrouping.  Key is main coordinate, list is list of 2nd coordinates
                    List <IGrouping <int, int> > lineInfo = AllCells
                                                            .Where(cell => cell.IsPencilmarkSet(iValue))
                                                            .GroupBy(cell => cell.GetCoordinate(coord), cell => cell.GetCoordinate(secCoord))
                                                            .Where(g => (count = g.Count()) >= 2 && count <= MaxCellsPerLine)
                                                            .ToList();

                    if (lineInfo.Count < FishSize)
                    {
                        continue;
                    }

                    var selectedLines    = new List <int>();
                    var selectedSecLines = new List <int>();
                    var selectedFinCells = new List <int[]>();
                    var fishCells        = new List <Cell>();
                    if (SelectFish(lineInfo, secCoord, 0 /*start*/, iValue,
                                   selectedLines, selectedSecLines, selectedFinCells, fishCells))
                    {
                        int pmMask = 1 << affectedPencilmark;

                        Solution = new SolveInfo();
                        affectedCells.ForEach(cell => Solution.AddAction(cell, CellRole.Affected,
                                                                         GetPmFindings(cell, pmMask, PMRole.Remove)));

                        patternCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern,
                                                                        GetPmFindings(cell, pmMask, PMRole.Pattern)));

                        if (FinCells == null)
                        {
                            FinCells = new List <Cell>();
                        }

                        FinCells.ForEach(cell => Solution.AddAction(cell, CellRole.Pattern2,
                                                                    GetPmFindings(cell, pmMask, PMRole.Pattern)));

                        List <int> involvedLineIndexes = patternCells
                                                         .Select(cell => cell.GetCoordinate(coord))
                                                         .Distinct()
                                                         .ToList();


                        involvedLineIndexes
                        .Select(index => Board.GetLine(coord, index))
                        .SelectMany(t => t)
                        .Except(patternCells)
                        .Except(FinCells)
                        .ToList()
                        .ForEach(cell => Solution.AddAction(cell, CellRole.InvolvedLine));

                        string csvCells = CellsInCsvFormat(patternCells);
                        Solution.Description = string.Format("{0}{1} on {2} for pencilmark {4}  Cells involved: {3}.",
                                                             Finned ? "Finned " : (Sashimi ? "Sashimi " : string.Empty),
                                                             FishNames[FishSize - 2],
                                                             coord == Coord.ROWS ? "rows" : "columns",
                                                             csvCells,
                                                             iValue);

                        return(true);
                    }
                }
            }

            return(false);
        }
        /// <summary>
        /// Looks for the fish pattern recursively
        /// </summary>
        /// <param name="lineInfo">A List of IGrouping<int, int> where the key is the
        /// index of the main coordinate (e.g. row) and the list holds the indexes of the
        /// secondary coordinates (e.g. columns). </param>
        /// <param name="secCoord">Indicates if the secondary coordinate is row or column.</param>
        /// <param name="start">Initial index where to start the search in the lineInfo list.</param>
        /// <param name="pmValue">Value of the PencilMark being processed.</param>
        /// <param name="indexesFishLines">List of the indexes of the lines containing pencilmarks which
        /// can be fish.  Initially the list is empty and it fills up as recursion kicks in and the
        /// different possible combinations are tried.</param>
        /// <param name="indexesSecFishLines">Indexes of the secondary lines of cells considered in the
        /// pattern.  Starts empty and fills as recursion deepens.</param>
        /// <param name="indexesSecFishLinesFreqOne">The indexes of secondary lines which appear only
        /// once in the patterns.  These will help identify fins.</param>
        /// <returns></returns>
        private bool SelectFish(List <IGrouping <int, int> > lineInfo,
                                int secCoord,
                                int start,
                                int pmValue,
                                List <int> indexesFishLines,
                                List <int> indexesSecFishLines,
                                List <int[]> coordsFreqOne,
                                List <Cell> fishCells)
        {
            int        indexLine;
            int        mainCoord = 1 - secCoord;
            List <int> secIndexesConsidered, candidatesMultiFreq = null;

            for (int i = start; i < lineInfo.Count; i++, indexesFishLines.Remove(indexLine))
            {
                // if there are not enough lines to form a fish pattern there's no
                // point in continue searching
                if (indexesFishLines.Count + (lineInfo.Count - i) < FishSize)
                {
                    return(false);
                }

                indexLine = lineInfo[i].Key;
                indexesFishLines.Add(indexLine);
                secIndexesConsidered = lineInfo[i].ToList();

                List <int> colsFreqOne = coordsFreqOne.Select(t => t[1]).ToList();
                candidatesMultiFreq = secIndexesConsidered
                                      .Intersect(colsFreqOne)
                                      .Union(indexesSecFishLines)
                                      .ToList();

                int mfCount = candidatesMultiFreq.Count;
                if (mfCount > FishSize)
                {
                    continue;
                }

                // the frequency one candidates plus the new candidates which are not part
                // of multi frequency list are the total candidates for frequency one.
                List <int[]> candidatesFreqOne = coordsFreqOne
                                                 .Union(secIndexesConsidered.Select(t => new int[] { indexLine, t }))
                                                 .Where(t => !candidatesMultiFreq.Contains(t[1]))
                                                 .ToList();

                // those in the same secondary coordinate (e.g. column) count as 1 since they
                // are in the one line of a possible fish.
                int freqOneCount = candidatesFreqOne
                                   .Select(t => t[1])
                                   .Distinct() // this should not be necessary
                                   .Count();

                if (mfCount + freqOneCount > MaxCellsCumulative)
                {
                    continue;
                }

                if (indexesFishLines.Count < FishSize &&
                    !SelectFish(lineInfo, secCoord, i + 1, pmValue, indexesFishLines,
                                candidatesMultiFreq, candidatesFreqOne, fishCells))
                {
                    continue;
                }

                // if unwinding recursion and the result is known, no need to recalculate it
                // on every step out.
                if (affectedCells != null && affectedCells.Count > 0)
                {
                    return(true);
                }

                /// Start possible templatized solutions.  Simple fish case
                if (!Finned && !Sashimi)
                {
                    if (candidatesFreqOne.Count > 0 || mfCount != FishSize)
                    {
                        indexesFishLines.Remove(indexLine);
                        return(false);
                    }

                    affectedCells = AllCells
                                    .Where(cell => candidatesMultiFreq.Contains(cell.GetCoordinate(secCoord)) &&
                                           !indexesFishLines.Contains(cell.GetCoordinate(mainCoord)))
                                    .Where(cell => cell.IsPencilmarkSet(pmValue))
                                    .ToList();

                    if (affectedCells.Count == 0)
                    {
                        continue;
                    }

                    patternCells = AllCells
                                   .Where(cell => indexesFishLines.Contains(cell.GetCoordinate(mainCoord)) &&
                                          candidatesMultiFreq.Contains(cell.GetCoordinate(secCoord)))
                                   .ToList();

                    affectedPencilmark = pmValue;
                    return(true);
                }

                // The following is common for Finned or Sashimi
                if (freqOneCount < 1 || freqOneCount > (Sashimi ? 3 : 2))
                {
                    continue;
                }

                int rowIndex = (mainCoord == Coord.ROWS) ? 0 : 1;
                int colIndex = 1 - rowIndex;
                FinCells = candidatesFreqOne
                           .Select(t => Board.GetCell(t[rowIndex], t[colIndex]))
                           .ToList();

                if (Finned && !Sashimi &&
                    (
                        (freqOneCount == 2 &&
                         (FinCells[0].GetCoordinate(mainCoord) != FinCells[1].GetCoordinate(mainCoord) ||
                          FinCells[0].GetCoordinate(Coord.SECTION) != FinCells[1].GetCoordinate(Coord.SECTION)))
                        ||
                        !VerifyFishFin(candidatesMultiFreq, secCoord, pmValue))
                    )
                {
                    continue;
                }

                if (Sashimi)
                {
                    // if there is only one frequency-one cell, it has to be the fin.
                    if (freqOneCount == 1 &&
                        !VerifyFishFin(candidatesMultiFreq, secCoord, pmValue))
                    {
                        continue;
                    }

                    // if there are 2, there are several options
                    //  a) None are fins.
                    //  b) Both are fins
                    //  c) F1 is a fin and F2 is not
                    //  d) F2 is a fin and F1 is not
                    // More than one statement can be true in some cases.  If so, select the
                    // first one found where pencilmarks can be turned OFF.
                    if (freqOneCount == 2)
                    {
                        var finCandidateInfo = candidatesFreqOne
                                               .Select(cfo => new
                        {
                            Cell    = Board.GetCell(cfo[rowIndex], cfo[colIndex]),
                            IsScale = candidatesMultiFreq.Contains(cfo[1])
                        }).ToList();

                        int sureScaleCount = finCandidateInfo.Where(fc => fc.IsScale).Count();

                        // a) None are fins
                        if (sureScaleCount == 2 || mfCount + 2 /*freqOneCount*/ == FishSize)
                        {
                            continue;
                        }

                        // b) Both are fins
                        if (mfCount == FishSize)
                        {
                            FinCells = finCandidateInfo
                                       .Where(fc => !fc.IsScale)
                                       .Select(fc => fc.Cell)
                                       .ToList();

                            if (FinCells.Count != 2 ||
                                !VerifyFishFin(indexesSecFishLines, secCoord, pmValue))
                            {
                                continue;
                            }
                        }

                        // c) One is a fin, and one is a scale
                        // c-i) The scale is known to be a scale
                        if (sureScaleCount == 1)
                        {
                            FinCells = finCandidateInfo
                                       .Where(fc => !fc.IsScale)
                                       .Select(fc => fc.Cell)
                                       .ToList();

                            if (FinCells.Count != 1)
                            {
                                throw new UnexpectedStateException("Number of fin cells should be 1.");
                            }

                            if (!VerifyFishFin(indexesSecFishLines, secCoord, pmValue))
                            {
                                continue;
                            }
                        }
                        else if (sureScaleCount == 0)   // c-ii) Fin and Scale may be interchangeable
                        {
                            if (mfCount != FishSize - 1)
                            {
                                continue;
                            }

                            bool foundFin = false;
                            for (int finChance = 0; finChance < 2; finChance++)
                            {
                                FinCells.Clear();
                                FinCells.Add(finCandidateInfo[finChance].Cell);
                                Cell scaleCandidate   = finCandidateInfo[1 - finChance].Cell;
                                var  tempSecFishLines = indexesSecFishLines
                                                        .Union(new List <int> {
                                    scaleCandidate.GetCoordinate(secCoord)
                                })
                                                        .ToList();

                                if ((foundFin = VerifyFishFin(indexesSecFishLines, secCoord, pmValue)))
                                {
                                    break;
                                }
                            }

                            if (!foundFin)
                            {
                                continue;
                            }
                        }

                        var otherScale = finCandidateInfo
                                         .FirstOrDefault(fci => !FinCells.Contains(fci.Cell));

                        candidatesMultiFreq.Add(otherScale.Cell.GetCoordinate(secCoord));
                    }

                    // if there are 3, then 2 are fins on the same line
                    if (freqOneCount == 3)
                    {
                        List <IGrouping <int, int[]> > groupedSingles = candidatesFreqOne
                                                                        .GroupBy(t => t[0])
                                                                        .ToList();


                        IGrouping <int, int[]> finData = groupedSingles
                                                         .FirstOrDefault(g => g.Count() == 2);

                        IGrouping <int, int[]> scaleData = groupedSingles
                                                           .FirstOrDefault(g => g.Count() == 1);

                        if (finData == null)
                        {
                            continue;
                        }

                        if (scaleData == null)
                        {
                            throw new UnexpectedStateException("Expected scale or Sashimi cell not found.");
                        }

                        FinCells = finData
                                   .Select(t => Board.GetCell(t[rowIndex], t[colIndex]))
                                   .ToList();

                        if (FinCells.Select(fc => fc.GetCoordinate(Coord.SECTION))
                            .Distinct()
                            .Count() > 1)
                        {
                            continue;
                        }

                        candidatesMultiFreq.Add(scaleData.ToList()[0][1]);

                        if (!VerifyFishFin(candidatesMultiFreq, secCoord, pmValue))
                        {
                            continue;
                        }
                    }
                }

                affectedCells = FinSection
                                .Where(cell => candidatesMultiFreq.Contains(cell.GetCoordinate(secCoord)) &&
                                       !indexesFishLines.Contains(cell.GetCoordinate(mainCoord)) &&
                                       cell.IsPencilmarkSet(pmValue))
                                .ToList();

                if (affectedCells.Count == 0)
                {
                    continue;
                }

                patternCells = AllCells
                               .Where(cell => indexesFishLines.Contains(cell.GetCoordinate(mainCoord)) &&
                                      candidatesMultiFreq.Contains(cell.GetCoordinate(secCoord)))
                               .ToList();

                affectedPencilmark = pmValue;
                return(true);
            }

            affectedCells = null;
            return(false);
        }