You can write your Box method without foreach loops or yield return:
public static IEnumerable<Tuple<int, int>> Box(int sizeX, int sizeY)
{
return
from x in Enumerable.Range(0, sizeX)
from y in Enumerable.Range(0, sizeY)
select Tuple.Create(x,y);
}
or equivalently:
public static IEnumerable<Tuple<int, int>> Box2(int sizeX, int sizeY)
{
return
Enumerable.Range(0,sizeX).SelectMany(x =>
Enumerable.Range(0,sizeY).Select(y =>
Tuple.Create(x,y)));
}
Standard, and less verbose, method of creating Tuples is Tuple.Create.
If you had made your Tile, Rule, and Board immutable, as they are; sharing state between them would not have been a problem. For example two Samurai Sudoku puzzles do share rules.
You are aggregating here.
SudokuProgress result = SudokuProgress.NO_PROGRESS;
foreach (SudokuTile tile in withoutNumber)
result = SudokuTile.CombineSolvedState(result,
tile.RemovePossibles(existingNumbers));
return result;
can be rewritten more clearly as:
return withoutNumber.Aggregate(
SudokuProgress.NO_PROGRESS,
(result, tile) => SudokuTile.CombineSolvedState(
result,
tile.RemovePossibles(existingNumbers)));