San code – the eight queens problemThe eight queens problem is a traditional programming task used to illustrate how programming languages work. The objective is discover and print out all arrangements of eight queens on the chess board such that no queen attacks another queen. There are various refinements, e.g., one can ask for n queens on an nxn board. Here is a solution in San, followed by programming notes. # This is an implementation in San of the eight queens # problem, for which the requirement is to print all of # different ways that n queens can be placed on an nxn # such that no two queens attack each other. # # This programming task has three major parts, the # generation of trials, the determination of which squares # are attacked, and the printing of the results using a # specified format. # # In this implementation the generation of trials is done # by trying queen placements on all positions on the first # row, then for each placement trying all positions not # under attack in the next row, usw. Colors are used to # keep track of which squares are under attack; squares not # under attack are white, those attacked by a previous # placement are colored red. # # Each solution is printed on a separate line; the # positions are expressed in algebraic notation. Bilateral # symmetry is not exploited. Various features of the code # are discussed in the programming notes below. begin segment type=module, label=eightqueens begin defaults red := "red white := "white n := 8 begin init board $rsize := n $csize := n $[,] := white end init firsts.row := board$rfirst firsts.col := board$cfirst end defaults begin agent eight-queens cand.row := board$rfirst begin loop cand.col from [board$cfirst ... board$clast] <try board, queens, cand> end loop end eight-queens begin proc try args: board, queens, cand append cand to queens cand.row lt? board$rlast => begin mark-attacked-squares board, cand cand.row += 1 begin loop cand.col from [board$cfirst ... board$clast] board[cand.row,cand.col] eq? red => <> else <try board, queens, cand> end loop end else display-queens queens end try begin proc mark-attacked-squares args: board, cand board[,cand.col] := red board[cand.row,] := red begin loop init i := cand.row init j := cand.col i += 1; j -= 1 i gt? board$rlast => escape j lt? board$cfirst => escape board[i,j] := red end end begin loop init i := cand.row init j := cand.col i += 1; j += 1 i gt? board$rlast => escape j gt? board$clast => escape board[i,j] := red end end mark-attacked-squares begin display-queens args: queens, col-first init colmap[] <- [alphseq | keep n] begin loop queen from queens[] col := queen.col - firsts.col + colmap$rfirst row := queen.row - firsts.row + 1 sym := "{colmap[col]}{row} append sym to outlist[] end loop print {[outlist[] | separate 2H, ]} end display-queens NotesMorphs as tablesEvery morph is (potentially) a table. Tables have two vectors associated with them, a vector of row labels, and a vector of column labels. If foo is a morph, the row vector for foo is foo[] and the column vector is foo.[] . If there are no columns the row vector can be used as an array.Table elements can be referenced using any of several syntaxes, e.g. foo[i].[j] <# row i, column j> foo[i].a <# row i, column labelled a> foo[i,j] <# row i, column j]Slices (whole rows or columns) can also be referenced, e.g., foo[i].[] <# row i] foo[i,] <# row i> foo[,j] <# column j> foo[].a <# column labelled a>there are six values associated with the dimensioning of a morph’s table. They are: $rsize <# the number of rows> $rfirst <# index of the first row> $rlast <# index of the last row> $csize <# the number of columns> $cfirst <# index of the first column> $clast <# index of the last column>These values are constrained by the relationships $rsize = $rlast - $rfirst + 1 $csize = $clast - $cfirst + 1If either the first or the last index is altered the size is left unchanged and the other index is altered to fit. If the size is changed, either by an assignment to the size value or by an assignment to the label vector, the first index is left unchanged and the other two altered to fit. Fill assignment and scatter assignmentSan supports fill assignment, i.e., a vector or an entire table can be set to a specified value. For exampleboard[,cand.col] := redsets the cand.col column to be red. Similarly board[,] := whitesets the entire board to be white. San also supports scatter assignment with lists on each side of an assignment arrow. For example, i,j -> m,nsets m := i and n := j. If there are vectors or sequents on the source side there must be a terminal vector on the receiving side. For example in a[], x -> b, c[]the first element of a goes into b, and the remaining elements of a[] and x go into c[]. Visibility, defaults, and cloningAll items defined within a module (for a suitable definition of defined :-)) are visible within the module; however they are not accessible for reading and writing. Instead they are available for cloning, i.e., within a local scope all morphs referenced only have a local scope.A defaults block sets initial default values for items commonly used throughout the module. These can be over-ridden within individual execution frame, but the over-ride applies only within the frame. Sequent operators and sourcesAlphseq is a semi-infinite source, generating the sequence a, b, …, z, aa, ab, …, zz, aaa, …Keep and separate are built-in sequent operators. “Keep” truncates its input sequence to the first n items. In this instance, with n = 8, colmap[] is the letters a through h. “Separate” interpolates its argument between each pair of successive elements in its input. This page was last updated October 1, 2003. |