Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// chapter=3 variant=non-det executionMethod=interpreter
const md = multiple_dwelling();
let res = '';
for (let i = 0; i < length(md); i=i+1) {
const person_floor = list_ref(md, i);
const person = head(person_floor);
const floor = stringify(head(tail(person_floor)));
res = res + person + floor + ',';
}
res;
// 'baker3,cooper2,fletcher4,miller5,smith1,'
12 changes: 12 additions & 0 deletions src/source-3-non-det-examples/__tests__/liars.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// chapter=3 variant=non-det executionMethod=interpreter
const liars = list(list('betty', betty), list('ethel', ethel), list('joan', joan),
list('kitty', kitty), list('mary', mary));
let res = '';
for (let i = 0; i < length(liars); i=i+1) {
const person_rank = list_ref(liars, i);
const person = head(person_rank);
const rank = stringify(head(tail(person_rank)));
res = res + person + rank + ',';
}
res;
// 'betty3,ethel5,joan2,kitty1,mary4,'
11 changes: 11 additions & 0 deletions src/source-3-non-det-examples/__tests__/queens.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// chapter=3 variant=non-det executionMethod=interpreter
const queens = queens_fast(8, 8);
let res = '';
for (let i = 0; i < length(queens); i=i+1) {
const position = list_ref(queens, i);
const x = stringify(head(position));
const y = stringify(tail(position));
res = res + '(' + x + ',' + y + '),';
}
res;
// '(4,8),(2,7),(7,6),(3,5),(6,4),(8,3),(5,2),(1,1),'
40 changes: 40 additions & 0 deletions src/source-3-non-det-examples/fast-multiple-dwelling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* SICP JS Exercise 4.31

This file contains a fast(er) solution to the multiple dwelling puzzle.
The key idea is that we perform every 'require' check immediately once
the information it needs is available. This minimises wasteful backtracking.
*/

function distinct(items) {
return is_null(items)
? true
: is_null(tail(items))
? true
: is_null(member(head(items), tail(items)))
? distinct(tail(items))
: false;
}

function multiple_dwelling() {
const baker = amb(1, 2, 3, 4, 5);
require(!(baker === 5));
const cooper = amb(1, 2, 3, 4, 5);
require(!(cooper === 1));
const fletcher = amb(1, 2, 3, 4, 5);
require(!(fletcher === 5));
require(!(fletcher === 1));
require(!(math_abs(fletcher - cooper) === 1));
const miller = amb(1, 2, 3, 4, 5);
require(miller > cooper);
const smith = amb(1, 2, 3, 4, 5);
require(!(math_abs(smith - fletcher) === 1));
require(distinct(list(baker, cooper, fletcher, miller, smith)));


return list(list("baker", baker),
list("cooper", cooper),
list("fletcher", fletcher),
list("miller", miller),
list("smith", smith));
}
// multiple_dwelling();
31 changes: 31 additions & 0 deletions src/source-3-non-det-examples/liars.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
SICP JS Exercise 4.33

This file contains a solution to the Liars' puzzle
*/

function distinct(items) {
return is_null(items)
? true
: is_null(tail(items))
? true
: is_null(member(head(items), tail(items)))
? distinct(tail(items))
: false;
}

const kitty = amb(1, 2, 3, 4, 5);
const mary = amb(1, 2, 3, 4, 5);
require(amb(kitty === 2, mary === 4));
const betty = amb(1, 2, 3, 4, 5);
require(amb(kitty === 2, betty === 3));
require(amb(mary === 4, betty === 1));
const ethel = amb(1, 2, 3, 4, 5);
const joan = amb(1, 2, 3, 4, 5);
require(amb(ethel === 1, joan === 2));
require(amb(joan === 3, ethel === 5));

require(distinct(list(betty, ethel, joan, kitty, mary)));

// list(list('betty', betty), list('ethel', ethel), list('joan', joan),
// list('kitty', kitty), list('mary', mary));
164 changes: 164 additions & 0 deletions src/source-3-non-det-examples/queens.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
SICP JS Exercise 4.35

This file contains two solutions for the n-queens puzzle using non-determinism.
Each of them makes use of the generate-and-test paradigm.

The first (queens_slow) uses non-determinism for generating both the row and column
for each position. It does so in an optimized manner, testing the corresponding condition
as soon as the choice is generated, thereby reducing the search space. However, this is not
enough to yield a quick solution when N = 8.
This solution also gives repeated solutions (i.e different permutations that have all the same positions) upon backtracking.

The second (queens_fast) uses non-determinism in picking only the row and not the column of each position, with the
columns being fixed. This further reduces the search space, yielding a quick solution when N = 8.
*/

const N = 8; // the number of queens and the size of the board
const empty_positions = null;

/******************************************************************************/
/* Slow version which uses non-determinism for both the columns and rows, */
/* perform testing of a required condition as soon as the choice is generated */
/******************************************************************************/

// const result_queens_slow = queens_slow(N, N);
// pretty_print(result_queens_slow, N);
// generate more solutions by entering 'try again' in the REPL

function queens_slow(board_size, num_queens) {
require(num_queens <= board_size);
const possible_positions = enum_list(1, board_size);

const result = accumulate(
(_, so_far) => {
const row = an_element_of(possible_positions);
require(is_row_safe(row, so_far));

const col = an_element_of(possible_positions);
require(is_col_safe(col, so_far));

const new_position = pair(row, col);
require(is_diagonal_safe(new_position, so_far));

const new_positions = pair(new_position, so_far);
return new_positions;
},
empty_positions,
enum_list(1, num_queens)
);

return result;
}

function is_row_safe(new_row, queen_positions) {
const rows = map(position => head(position), queen_positions);
return member(new_row, rows) === null;
}

function is_col_safe(new_col, queen_positions) {
const cols = map(position => tail(position), queen_positions);
return member(new_col, cols) === null;
}

function is_diagonal_safe(new_position, queen_positions) {
const new_sum = head(new_position) + tail(new_position);
const new_sub = head(new_position) - tail(new_position);
const sums = map(
position => head(position) + tail(position),
queen_positions
);

return (
member(new_sum, sums) === null &&
member(
new_sub,
map(position => head(position) - tail(position), queen_positions)
) === null
);
}

/******************************************************************************/
/* Fast version which uses non-determinism only for the rows, */
/* with the columns being hardcoded. */
/******************************************************************************/

// const result_queens_fast = queens_fast(N, N);
// pretty_print(result_queens_fast, N);
// generate more solutions by entering 'try again' in the REPL

function queens_fast(board_size, num_queens) {
require(num_queens <= board_size);
const possible_positions = enum_list(1, board_size);

function queen_cols(k) {
if (k === 0) {
return empty_positions;
} else {
const so_far = queen_cols(k - 1);

const new_row = an_element_of(possible_positions);
const new_position = pair(new_row, k);
require(is_safe(new_position, so_far));

const new_positions = pair(new_position, so_far);
return new_positions;
}
}
return queen_cols(num_queens);
}

function is_safe(new_position, positions) {
const new_row = head(new_position);
const new_col = tail(new_position);

return accumulate(
(position, so_far) => {
const row = head(position);
const col = tail(position);

return (
so_far &&
new_row - new_col !== row - col &&
new_row + new_col !== row + col &&
new_row !== row
);
},
true,
positions
);
}


/* Pretty prints a solution to the n-queens puzzle */
function pretty_print(result, board_size) {
function member_eq(v, xs) {
return is_null(xs)
? null
: equal(v, head(xs))
? xs
: member_eq(v, tail(xs));
}
const possible_positions = enum_list(1, board_size);

let col_index_str = " ";
for_each(i => {
col_index_str = col_index_str + stringify(i) + " ";
}, possible_positions);
display(col_index_str);

for_each(row => {
let row_str = stringify(row) + " ";
for_each(col => {
const position = pair(row, col);
const contains_position = member_eq(position, result) !== null;
if (contains_position) {
row_str = row_str + "Q ";
} else {
row_str = row_str + ". ";
}
}, possible_positions);

display(row_str);
}, possible_positions);
}