Skip to content

Commit 72c27fd

Browse files
committed
add nqueens solutions
1 parent 20e3387 commit 72c27fd

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
SICP JS Exercise 4.35
3+
4+
This file contains two solutions for the n-queens puzzle using non-determinism.
5+
Each of them makes use of the generate-and-test paradigm.
6+
7+
The first (queens_slow) uses non-determinism for generating both the row and column
8+
for each position. It does so in an optimized manner, testing the corresponding condition
9+
as soon as the choice is generated, thereby reducing the search space. However, this is not
10+
enough to yield a quick solution when N = 8.
11+
This solution also gives repeated solutions (i.e different permutations that have all the same positions) upon backtracking.
12+
13+
The second (queens_fast) uses non-determinism in picking only the row and not the column of each position, with the
14+
columns being fixed. This further reduces the search space, yielding a quick solution when N = 8.
15+
*/
16+
17+
18+
const N = 8; // the number of queens and the size of the board
19+
const empty_positions = null;
20+
21+
/* Pretty prints a solution to the n-queens puzzle */
22+
function pretty_print(result, board_size) {
23+
function member_eq(v, xs) {
24+
return is_null(xs) ? null : equal(v, head(xs)) ? xs : member_eq(v, tail(xs));
25+
}
26+
const possible_positions = enum_list(1, board_size);
27+
28+
let col_index_str = ' ';
29+
for_each(i => { col_index_str = col_index_str + stringify(i) + ' '; }, possible_positions);
30+
display(col_index_str);
31+
32+
for_each(row => {
33+
let row_str = stringify(row) + ' ';
34+
for_each(col => {
35+
const position = pair(row, col);
36+
const contains_position = member_eq(position, result) !== null;
37+
if (contains_position) {
38+
row_str = row_str + 'Q ';
39+
} else {
40+
row_str = row_str + '. ';
41+
}
42+
},
43+
possible_positions);
44+
45+
display(row_str);
46+
},
47+
possible_positions);
48+
}
49+
50+
51+
/******************************************************************************/
52+
/* Slow version which uses non-determinism for both the columns and rows, */
53+
/* perform testing of a required condition as soon as the choice is generated */
54+
/******************************************************************************/
55+
56+
// const result_queens_slow = queens_slow(N, N);
57+
// pretty_print(result_queens_slow, N);
58+
// generate more solutions by entering 'try again' in the REPL
59+
60+
function queens_slow(board_size, num_queens) {
61+
require(num_queens <= board_size);
62+
const possible_positions = enum_list(1, board_size);
63+
64+
const result = accumulate((_, so_far) => {
65+
const row = an_element_of(possible_positions);
66+
require(is_row_safe(row, so_far));
67+
68+
const col = an_element_of(possible_positions);
69+
require(is_col_safe(col, so_far));
70+
71+
const new_position = pair(row, col);
72+
require(is_diagonal_safe(new_position, so_far));
73+
74+
const new_positions = pair(new_position, so_far);
75+
return new_positions;
76+
}, empty_positions, enum_list(1, num_queens));
77+
78+
return result;
79+
}
80+
81+
function is_row_safe(new_row, queen_positions) {
82+
const rows = map(position => head(position), queen_positions);
83+
return member(new_row, rows) === null;
84+
}
85+
86+
function is_col_safe(new_col, queen_positions) {
87+
const cols = map(position => tail(position), queen_positions);
88+
return member(new_col, cols) === null;
89+
}
90+
91+
function is_diagonal_safe(new_position, queen_positions) {
92+
const new_sum = head(new_position) + tail(new_position);
93+
const new_sub = head(new_position) - tail(new_position);
94+
const sums = map(position => head(position) + tail(position), queen_positions);
95+
96+
return member(new_sum, sums) === null &&
97+
member(new_sub, map(
98+
position => head(position) - tail(position), queen_positions
99+
)) === null;
100+
}
101+
102+
103+
/******************************************************************************/
104+
/* Fast version which uses non-determinism only for the rows, */
105+
/* with the columns being hardcoded. */
106+
/******************************************************************************/
107+
108+
// const result_queens_fast = queens_fast(N, N);
109+
// pretty_print(result_queens_fast, N);
110+
// generate more solutions by entering 'try again' in the REPL
111+
112+
function queens_fast(board_size, num_queens) {
113+
require(num_queens <= board_size);
114+
const possible_positions = enum_list(1, board_size);
115+
116+
function queen_cols(k) {
117+
if (k === 0) {
118+
return empty_positions;
119+
} else {
120+
const so_far = queen_cols(k - 1);
121+
122+
const new_row = an_element_of(possible_positions);
123+
const new_position = pair(new_row, k);
124+
require(is_safe(new_position, so_far));
125+
126+
const new_positions = pair(new_position, so_far);
127+
return new_positions;
128+
}
129+
}
130+
return queen_cols(num_queens);
131+
}
132+
133+
function is_safe(new_position, positions) {
134+
const new_row = head(new_position);
135+
const new_col = tail(new_position);
136+
137+
return accumulate((position, so_far) => {
138+
const row = head(position);
139+
const col = tail(position);
140+
141+
return so_far &&
142+
new_row - new_col !==
143+
row - col &&
144+
new_row + new_col !==
145+
row + col &&
146+
new_row !== row;
147+
},
148+
true,
149+
positions);
150+
}

0 commit comments

Comments
 (0)