############################################################################
#
#	File:     cross.icn
#
#	Subject:  Program to display intersection of words
#
#	Author:   William P. Malloy
#
#	Date:     June 10, 1988
#
############################################################################
#
#   This file is in the public domain.
#
############################################################################
#  
#     This program takes a list of words and tries to arrange them
#  in cross-word format so that they intersect. Uppercase letters
#  are mapped into lowercase letters on input.  For example, the
#  input
#  
#          and
#          eggplants
#          elephants
#          purple
#  
#  produces the output
#       +---------+
#       | p       |
#       | u e     |
#       | r g     |
#       | p g     |
#       |elephants|
#       | e l     |
#       |   and   |
#       |   n     |
#       |   t     |
#       |   s     |
#       +---------+
#  
#  Diagnostics: The program objects if the input contains a nonal-
#  phabetic character.
#  
#  Comments: This program produces only one possible intersection
#  and it does not attempt to produce the most compact result.  The
#  program is not very fast, either.  There is a lot of room for
#  improvement here. In particular, it is natural for Icon to gen-
#  erate a sequence of solutions.
#  
############################################################################

global fast, place, array, csave, fsave, number

procedure main()
   local words, nonletter, line
   nonletter := ~&letters
   words := []

   while line := map(read()) do
      if upto(nonletter,line) then stop("input contains nonletter")
      else put(words,line)
   number := *words
   kross(words)

end

procedure kross(words)
   local one, tst, t
   array := [get(words)]
   t := 0
   while one := get(words) do {
      tst := *words
      if fit(one,array,0 | 1) then
	 t := 0
      else {
	 t +:= 1
         put(words,one)
	 if t > tst then
	    break
	 }
      }
   if *words = 0 then Print(array)
   else write(&errout,"cannot construct puzzle")
end

procedure fit(word,matrix,where)
   local i, j, k, l, one, test, t, s
   s := *matrix
   t := *matrix[1]
   every k := gen(*word) do
      every i := gen(s) do
         every j := gen(t) do
	    if matrix[i][j] == word[k] then {
               # test for vertical fit
               if where = 0 then {
                  test := 0
                  every l := (i - k + 1) to (i + (*word - k)) do
                     if tstv(matrix,i,j,l,s,t) then {
                        test := 1
                        break
                        }
                  if test = 0 then
                     return putvert(matrix,word,i,j,k)
                  }
               if where = 1 then {
                  test := 0
                  every l := (j - k + 1) to (j + (*word - k)) do
                     if tsth(matrix,i,j,l,s,t) then {
                        test := 1
                        break
                        }
                  if test = 0 then
                     return puthoriz(matrix,word,i,j,k)
                  }
               }
end

procedure tstv(matrix,i,j,l,s,t)
   return ((matrix[(l ~= i) & (s >= l) & (0 < l)][0 < j-1] ~== " ") |
      (matrix[(l ~= i) & (s >= l) & (0 < l)][t >= j + 1] ~== " ") |
      (matrix[(i ~= l-1) & (s >= l-1) & (0 < l-1)][j] ~== " ") |
      (matrix[(i ~= l + 1) & (s >= l+1) & (0 < l + 1)][j] ~== " ") |
      (matrix[(l ~= i) & (s >= l) & (0 < l)][j] ~== " "))
end

procedure tsth(matrix,i,j,l,s,t)
   return ((matrix[0 < i-1][(l ~= j) & (t >= l) & (0 < l)] ~== " ") |
      (matrix[s >= i + 1][(l ~= j) & (t >= l) & (0 < l)] ~== " ") |
      (matrix[i][(j ~= l-1) & (t >= l-1) & (0 < l-1)] ~== " ") |
      (matrix[i][(j ~= l + 1) & (t >= l + 1) & (0 < l + 1)] ~== " ") |
      (matrix[i][(l ~= j) & (t >= l) & (0 < l)] ~== " "))
end

procedure gen(i)
   local tmp, up, down
   tmp := i / 2
   if (i % 2) = 1 then
      tmp +:= 1
   suspend tmp
   up := tmp
   down := tmp
   while (up < i) do {
      suspend up +:= 1
      suspend (down > 1) & (down -:= 1)
      }
end

# put `word' in vertically at pos(i,j)

procedure putvert(matrix,word,i,j,k)
   local hdim, vdim, up, down, l, m, n
   vdim := *matrix
   hdim := *matrix[1]
   up := 0
   down := 0
   up := abs(0 > (i - k))
   down := abs(0 > ((vdim - i) - (*word - k)))
   every m := 1 to up do
      push(matrix,repl(" ",hdim))
   i +:= up
   every m := 1 to down do
      put(matrix,repl(" ",hdim))
   every l := 1 to *word do
      matrix[i + l - k][j] := word[l]
   return matrix
end

# put `word' in horizontally at position i,j in matrix

procedure puthoriz(matrix,word,i,j,k)
   local hdim, vdim, left, right, l, m, n
   vdim := *matrix
   hdim := *matrix[1]
   left := 0
   right := 0
   left := (abs(0 > (j - k))) | 0
   right := (abs(0 > ((hdim - j) - (*word - k)))) | 0
   every m := 1 to left do
      every l := 1 to vdim do
  	 matrix[l] := " " || matrix[l]
   j +:= left
   every m := 1 to right do
      every l := 1 to vdim do
 	 matrix[l] ||:= " "
   every l := 1 to *word do
      matrix[i][j + l - k] := word[l]
   return matrix
end

procedure Print(matrix)
   local i
   write("+",repl("-",*matrix[1]),"+")
   every i := 1 to *matrix do
      write("|",matrix[i],"|")
   write("+",repl("-",*matrix[1]),"+")
end
