public bool Equals(ProfessorChecker other)
 {
     if (ReferenceEquals(null, other)) return false;
     if (ReferenceEquals(this, other)) return true;
     return other._id.Equals(_id);
 }
        private IEnumerable<ProfessorChecker> FindMatchingChecker(ProfessorChecker[] solution, int solutionIndex, ProfessorChecker[] checkers)
        {
            var unused = checkers.Except(solution).ToList();

            var thisIndex = solutionIndex + 1;

            foreach (var unusedProfessorChecker in unused)
            {
                for (int i = 0; i < 4; i++)
                {
                    if (thisIndex < 4) //first row
                    {
                        var leftChecker = solution[solutionIndex];

                        if (leftChecker.Right.Color == unusedProfessorChecker.Left.Color &&
                            leftChecker.Right.BodyPart != unusedProfessorChecker.Left.BodyPart)
                        {
                            yield return unusedProfessorChecker.Clone();
                        }
                    }

                    else if (thisIndex % 4 == 0) //first column, in any other row
                    {
                        var topChecker = solution[thisIndex - 4];

                        if (topChecker.Bottom.Color == unusedProfessorChecker.Top.Color
                            && topChecker.Bottom.BodyPart != unusedProfessorChecker.Top.BodyPart)
                        {
                            yield return unusedProfessorChecker.Clone();
                        }
                    }

                    else //2nd-4th column in 2nd-4th row
                    {
                        var leftChecker = solution[thisIndex - 1];
                        var topChecker = solution[thisIndex - 4];

                        if (leftChecker.Right.Color == unusedProfessorChecker.Left.Color &&
                            leftChecker.Right.BodyPart != unusedProfessorChecker.Left.BodyPart &&
                            topChecker.Bottom.Color == unusedProfessorChecker.Top.Color &&
                            topChecker.Bottom.BodyPart != unusedProfessorChecker.Top.BodyPart)
                        {
                            yield return unusedProfessorChecker.Clone();
                        }
                    }

                    unusedProfessorChecker.TurnRight();
                }
            }
        }
        public IEnumerable<ProfessorChecker[]> Solve()
        {
            var bag = new ConcurrentBag<ProfessorChecker[]>();
            _checkers.AsParallel().ForAll(checker =>
                                              {
                                                  var checkers = _checkers.Select(c => c.Clone()).ToArray();
                                                  for (int i = 0; i < 4; i++)
                                                  {
                                                      var board = new ProfessorChecker[16];
                                                      board[0] = checker;

                                                      var solutions = BuildSolutionRecursively(board, 0, checkers).ToList();

                                                      foreach (var solution in solutions)
                                                      {
                                                          bag.Add(solution);
                                                      }

                                                      checker.TurnRight();
                                                  }
                                              });
            return bag;
        }
        private IEnumerable<ProfessorChecker[]> BuildSolutionRecursively(ProfessorChecker[] board, int solutionIndex, ProfessorChecker[] checkers)
        {
            var nextIndex = solutionIndex + 1;
            var matchingCheckers = FindMatchingChecker(board, solutionIndex, checkers).ToList();

            foreach (var nextChecker in matchingCheckers)
            {
                board[nextIndex] = nextChecker;

                if (nextIndex == 15)
                {
                    yield return board.Select(pc => pc.Clone()).ToArray();
                }

                if (nextIndex < 15)
                {
                    foreach (var solution in BuildSolutionRecursively(board, nextIndex, checkers))
                    {
                        yield return solution;
                    }
                }

                board[nextIndex] = null;
            }
        }
        public ProfessorGameSolver(ProfessorChecker[] checkers)
        {
            if (checkers.Length != 16)
                throw new ArgumentOutOfRangeException("checkers");

            _checkers = checkers;
        }