package dominoes
type Domino [2]int
type Chain []Domino
func MakeChain(in []Domino) ([]Domino, bool) {
chain := Chain{}
input := make([]Domino, len(in))
copy(input, in)
return chain, chain.recurse(input)
}
func (c *Chain) recurse(items Chain) bool {
// No more Domino to put in the Chain
if len(items) == 0 {
// check if the Chain is valid
return c.valid()
}
// Check which one can be next.
validitems := items.poss().keep(c.last())
for _, it := range validitems {
*c = append(*c, it)
items = items.remove(it)
if c.recurse(items) {
return true
} else {
// the chain does not work, remove last, add it back to options
*c = (*c)[:len(*c)-1]
items = append(items, it)
}
}
return false
}
// poss returns all unique possibilities (flips) for the reciver Dominoes.
func (in Chain) poss() Chain {
ret := []Domino{}
for _, d := range in {
ret = append(ret, d)
if d.rotate() != d {
ret = append(ret, d.rotate())
}
}
return ret
}
// keep returns elemens which can fit to k value. k=0 means everything fits.
func (orig Chain) keep(k int) []Domino {
// first item could be anything
if k == 0 {
return orig
}
var new []Domino
for _, e := range orig {
if e[0] == k {
new = append(new, e)
}
}
return new
}
// remove deletes elem from the slice. Recognizes rotates, but only removes 1 item.
func (orig Chain) remove(elem Domino) []Domino {
var (
i int
d Domino
)
for i, d = range orig {
if d == elem || d.rotate() == elem {
break
}
}
orig[i] = orig[len(orig)-1]
return orig[:len(orig)-1]
}
// last returns last value of the Chain.
func (c Chain) last() int {
if len(c) == 0 {
return 0
}
return c[len(c)-1][1]
}
// rotate flips a Domino.
func (d Domino) rotate() Domino {
d[0], d[1] = d[1], d[0]
return d
}
// valid checks if a Chain is valid (first-last matches).
func (c Chain) valid() bool {
if len(c) == 0 {
return true
}
return c[0][0] == c[len(c)-1][1]
}