Skip to content

Commit 8739f90

Browse files
1fisedisiriak
andauthored
Opened issue: Added new Algorithm for "Eight queens puzzle" Problem, using N dimensions instead. (#198)
* Added a solution to the nxn queen problem. * Add more tests * Improve test coverage Co-authored-by: Andrii Siriak <siryaka@gmail.com>
1 parent 2f24696 commit 8739f90

File tree

4 files changed

+243
-1
lines changed

4 files changed

+243
-1
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Linq;
3+
using Algorithms.Problems.NQueens;
4+
5+
using FluentAssertions;
6+
7+
using NUnit.Framework;
8+
9+
namespace Algorithms.Tests.Problems.NQueens
10+
{
11+
public static class BacktrackingNQueensSolverTests
12+
{
13+
[TestCase(0, 0)]
14+
[TestCase(1, 1)]
15+
[TestCase(2, 0)]
16+
[TestCase(3, 0)]
17+
[TestCase(4, 2)]
18+
[TestCase(5, 10)]
19+
[TestCase(6, 4)]
20+
[TestCase(7, 40)]
21+
[TestCase(8, 92)]
22+
[TestCase(8, 92)]
23+
[TestCase(9, 352)]
24+
[TestCase(10, 724)]
25+
[TestCase(11, 2680)]
26+
[TestCase(12, 14200)]
27+
public static void SolvesCorrectly(int n, int expectedNumberOfSolutions)
28+
{
29+
// Arrange
30+
// Act
31+
var result = new BacktrackingNQueensSolver().BacktrackSolve(n).ToList();
32+
33+
// Assert
34+
result.Should().HaveCount(expectedNumberOfSolutions);
35+
foreach (var solution in result)
36+
{
37+
ValidateOneQueenPerRow(solution);
38+
ValidateOneQueenPerColumn(solution);
39+
ValidateOneQueenPerTopLeftBottomRightDiagonalLine(solution);
40+
ValidateOneQueenPerBottomLeftTopRightDiagonalLine(solution);
41+
}
42+
}
43+
44+
[Test]
45+
public static void NCannotBeNegative()
46+
{
47+
var n = -1;
48+
49+
Action act = () => new BacktrackingNQueensSolver().BacktrackSolve(n);
50+
51+
act.Should().Throw<ArgumentException>();
52+
}
53+
54+
private static void ValidateOneQueenPerRow(bool[,] solution)
55+
{
56+
for (var i = 0; i < solution.GetLength(1); i++)
57+
{
58+
var foundQueen = false;
59+
for (var j = 0; j < solution.GetLength(0); j++)
60+
{
61+
foundQueen = ValidateCell(foundQueen, solution[j, i]);
62+
}
63+
}
64+
}
65+
66+
private static void ValidateOneQueenPerColumn(bool[,] solution)
67+
{
68+
for (var i = 0; i < solution.GetLength(0); i++)
69+
{
70+
var foundQueen = false;
71+
for (var j = 0; j < solution.GetLength(1); j++)
72+
{
73+
foundQueen = ValidateCell(foundQueen, solution[i, j]);
74+
}
75+
}
76+
}
77+
78+
private static void ValidateOneQueenPerTopLeftBottomRightDiagonalLine(bool[,] solution)
79+
{
80+
for (var i = 0; i < solution.GetLength(0); i++)
81+
{
82+
var foundQueen = false;
83+
for (var j = 0; i + j < solution.GetLength(1); j++)
84+
{
85+
foundQueen = ValidateCell(foundQueen, solution[i + j, i]);
86+
}
87+
}
88+
89+
for (var i = 0; i < solution.GetLength(1); i++)
90+
{
91+
var foundQueen = false;
92+
for (var j = 0; i + j < solution.GetLength(0); j++)
93+
{
94+
foundQueen = ValidateCell(foundQueen, solution[j, i + j]);
95+
}
96+
}
97+
}
98+
99+
private static void ValidateOneQueenPerBottomLeftTopRightDiagonalLine(bool[,] solution)
100+
{
101+
for (var i = 0; i < solution.GetLength(0); i++)
102+
{
103+
var foundQueen = false;
104+
for (var j = 0; i - j >= 0; j++)
105+
{
106+
foundQueen = ValidateCell(foundQueen, solution[i - j, i]);
107+
}
108+
}
109+
110+
for (var i = 0; i < solution.GetLength(1); i++)
111+
{
112+
var foundQueen = false;
113+
for (var j = 0; i - j >= 0 && solution.GetLength(0) - j > 0; j++)
114+
{
115+
foundQueen = ValidateCell(foundQueen, solution[solution.GetLength(0) - j - 1, i - j]);
116+
}
117+
}
118+
}
119+
120+
static bool ValidateCell(bool foundQueen, bool currentCell)
121+
{
122+
if (foundQueen)
123+
{
124+
currentCell.Should().BeFalse();
125+
}
126+
127+
return foundQueen || currentCell;
128+
}
129+
}
130+
}

Algorithms.Tests/Problems/StableMarriage/GaleShapleyTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
using System.Collections.Generic;
33
using System.Linq;
44

5+
using Algorithms.Problems.StableMarriage;
6+
57
using NUnit.Framework;
68

7-
namespace Algorithms.Problems.StableMarriage
9+
namespace Algorithms.Tests.Problems.StableMarriage
810
{
911
/// <summary>
1012
/// The stable marriage problem (also stable matching problem or SMP)
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Algorithms.Problems.NQueens
5+
{
6+
public class BacktrackingNQueensSolver
7+
{
8+
/// <summary>
9+
/// Solves N-Queen Problem given a n dimension chessboard and using backtracking with recursion algorithm.
10+
/// If we find a dead-end within or current solution we go back and try another position for queen.
11+
/// </summary>
12+
/// <param name="n">Number of rows.</param>
13+
/// <returns>All solutions.</returns>
14+
public IEnumerable<bool[,]> BacktrackSolve(int n)
15+
{
16+
if (n < 0)
17+
{
18+
throw new ArgumentException(nameof(n));
19+
}
20+
21+
return BacktrackSolve(new bool[n, n], 0);
22+
}
23+
24+
private static IEnumerable<bool[,]> BacktrackSolve(bool[,] board, int col)
25+
{
26+
var solutions = col < board.GetLength(0) - 1
27+
? HandleIntermediateColumn(board, col)
28+
: HandleLastColumn(board);
29+
return solutions;
30+
}
31+
32+
private static IEnumerable<bool[,]> HandleIntermediateColumn(bool[,] board, int col)
33+
{
34+
// To start placing queens on possible spaces within the board.
35+
for (var i = 0; i < board.GetLength(0); i++)
36+
{
37+
if (CanPlace(board, i, col))
38+
{
39+
board[i, col] = true;
40+
41+
foreach (var solution in BacktrackSolve(board, col + 1))
42+
{
43+
yield return solution;
44+
}
45+
46+
board[i, col] = false;
47+
}
48+
}
49+
}
50+
51+
private static IEnumerable<bool[,]> HandleLastColumn(bool[,] board)
52+
{
53+
var n = board.GetLength(0);
54+
for (var i = 0; i < n; i++)
55+
{
56+
if (CanPlace(board, i, n - 1))
57+
{
58+
board[i, n - 1] = true;
59+
60+
yield return (bool[,])board.Clone();
61+
62+
board[i, n - 1] = false;
63+
}
64+
}
65+
}
66+
67+
/// <summary>
68+
/// Checks whether current queen can be placed in current position,
69+
/// outside attacking range of another queen.
70+
/// </summary>
71+
/// <param name="board">Source board.</param>
72+
/// <param name="row">Row coordinate.</param>
73+
/// <param name="col">Col coordinate.</param>
74+
/// <returns>true if queen can be placed in given chessboard coordinates; false otherwise.</returns>
75+
private static bool CanPlace(bool[,] board, int row, int col)
76+
{
77+
// To check whether there are any queens on current row.
78+
for (var i = 0; i < col; i++)
79+
{
80+
if (board[row, i])
81+
{
82+
return false;
83+
}
84+
}
85+
86+
// To check diagonal attack top-left range.
87+
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)
88+
{
89+
if (board[i, j])
90+
{
91+
return false;
92+
}
93+
}
94+
95+
// To check diagonal attack bottom-left range.
96+
for (int i = row + 1, j = col - 1; j >= 0 && i < board.GetLength(0); i++, j--)
97+
{
98+
if (board[i, j])
99+
{
100+
return false;
101+
}
102+
}
103+
104+
// Return true if it can use position.
105+
return true;
106+
}
107+
}
108+
}

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ This repository contains algorithms and data structures implemented in C# for ed
9191
* [Problems](./Algorithms/Problems/)
9292
* [Stable Marriage](./Algorithms/Problems/StableMarriage)
9393
* [Gale-Shapley](./Algorithms/Problems/StableMarriage/GaleShapley.cs)
94+
* [N-Queens](./Algorithms/Problems/NQueens)
95+
* [Backtracking](./Algorithms/Problems/NQueens/BacktrackingNQueensSolver.cs)
9496

9597
* [Data Structures](./DataStructures/)
9698
* [Bit Array](./DataStructures/BitArray.cs)

0 commit comments

Comments
 (0)