Task
Solve Dinesman's multiple dwelling problem but in a way that most naturally follows the problem statement given below.

Solutions are allowed (but not required) to parse and interpret the problem text, but should remain flexible and should state what changes to the problem text are allowed. Flexibility and ease of expression are valued.

Examples may be be split into "setup", "problem statement", and "output" sections where the ease and naturalness of stating the problem and getting an answer, as well as the ease and flexibility of modifying the problem are the primary concerns.

Example output should be shown here, as well as any comments on the examples flexibility.



The problem
Baker, Cooper, Fletcher, Miller, and Smith live on different floors of an apartment house that contains only five floors.


   Baker does not live on the top floor.
   Cooper does not live on the bottom floor. 
   Fletcher does not live on either the top or the bottom floor.
   Miller lives on a higher floor than does Cooper.
   Smith does not live on a floor adjacent to Fletcher's. 
   Fletcher does not live on a floor adjacent to Cooper's.

Where does everyone live?

package main
 
import "fmt"
 
// The program here is restricted to finding assignments of tenants (or more
// generally variables with distinct names) to floors (or more generally
// integer values.)  It finds a solution assigning all tenants and assigning
// them to different floors.
 
// Change number and names of tenants here.  Adding or removing names is
// allowed but the names should be distinct; the code is not written to handle
// duplicate names.
var tenants = []string{"Baker", "Cooper", "Fletcher", "Miller", "Smith"}
 
// Change the range of floors here.  The bottom floor does not have to be 1.
// These should remain non-negative integers though.
const bottom = 1
const top = 5
 
// A type definition for readability.  Do not change.
type assignments map[string]int
 
// Change rules defining the problem here.  Change, add, or remove rules as
// desired.  Each rule should first be commented as human readable text, then
// coded as a function.  The function takes a tentative partial list of
// assignments of tenants to floors and is free to compute anything it wants
// with this information.  Other information available to the function are
// package level defintions, such as top and bottom.  A function returns false
// to say the assignments are invalid.
var rules = []func(assignments) bool{
    // Baker does not live on the top floor
    func(a assignments) bool {
        floor, assigned := a["Baker"]
        return !assigned || floor != top
    },
    // Cooper does not live on the bottom floor
    func(a assignments) bool {
        floor, assigned := a["Cooper"]
        return !assigned || floor != bottom
    },
    // Fletcher does not live on either the top or the bottom floor
    func(a assignments) bool {
        floor, assigned := a["Fletcher"]
        return !assigned || (floor != top && floor != bottom)
    },
    // Miller lives on a higher floor than does Cooper
    func(a assignments) bool {
        if m, assigned := a["Miller"]; assigned {
            c, assigned := a["Cooper"]
            return !assigned || m > c
        }
        return true
    },
    // Smith does not live on a floor adjacent to Fletcher's
    func(a assignments) bool {
        if s, assigned := a["Smith"]; assigned {
            if f, assigned := a["Fletcher"]; assigned {
                d := s - f
                return d*d > 1
            }
        }
        return true
    },
    // Fletcher does not live on a floor adjacent to Cooper's
    func(a assignments) bool {
        if f, assigned := a["Fletcher"]; assigned {
            if c, assigned := a["Cooper"]; assigned {
                d := f - c
                return d*d > 1
            }
        }
        return true
    },
}
 
// Assignment program, do not change.  The algorithm is a depth first search,
// tentatively assigning each tenant in order, and for each tenant trying each
// unassigned floor in order.  For each tentative assignment, it evaluates all
// rules in the rules list and backtracks as soon as any one of them fails.
//
// This algorithm ensures that the tenative assignments have only names in the
// tenants list, only floor numbers from bottom to top, and that tentants are
// assigned to different floors.  These rules are hard coded here and do not
// need to be coded in the the rules list above.
func main() {
    a := assignments{}
    var occ [top + 1]bool
    var df func([]string) bool
    df = func(u []string) bool {
        if len(u) == 0 {
            return true
        }
        tn := u[0]
        u = u[1:]
    f:
        for f := bottom; f <= top; f++ {
            if !occ[f] {
                a[tn] = f
                for _, r := range rules {
                    if !r(a) {
                        delete(a, tn)
                        continue f
                    }
                }
                occ[f] = true
                if df(u) {
                    return true
                }
                occ[f] = false
                delete(a, tn)
            }
        }
        return false
    }
    if !df(tenants) {
        fmt.Println("no solution")
        return
    }
    for t, f := range a {
        fmt.Println(t, f)
    }
}