Skip to content

Instantly share code, notes, and snippets.

@cmcenearney
Last active February 2, 2016 02:35
Show Gist options
  • Save cmcenearney/6d0e7e628c12b1a9ed8e to your computer and use it in GitHub Desktop.
Save cmcenearney/6d0e7e628c12b1a9ed8e to your computer and use it in GitHub Desktop.
import scala.collection.mutable
/**
* Code to solve a type of puzzle known as a "spelling bee" or "word hive".
*
* M I
* T U A
* N O
*
* Make a list of eligible words, where eligible means
* - only the letters in the hive are used
* - the central letter is used at least once
* - words are five letters long or more
*
* Score words 3 points if all seven letters are used, 1 point for all others.
*
* For example the above hive could yield `ammunition` for 3 points, and `munition` for 1 point.
*
* val hive = WordHive('u', Set('a','m','n','i','u','t','o'))
* val hiveTrie = new HiveTrie
* val solution = hiveTrie.solve(hive)
*
* Using the default list of words that ships with most *nix distros, solution.score() should be 116.
*
* Using the international Scrabble list, "sowpods", it will be 95.
*
*/
case class WordHive(centralChar: Char, chars: Set[Char], minLength: Int = 5) {
private def centralCharUsed(word: String): Boolean = word.toSet.contains(centralChar)
private def lengthOk(word: String): Boolean = word.length >= minLength
private def onlyHiveCharsUser(word: String): Boolean = word.toSet.subsetOf(chars)
def qualifies(word: String): Boolean = centralCharUsed(word) && lengthOk(word) && onlyHiveCharsUser(word)
def scoreWord(word: String): Int = if (word.toSet.equals(chars)) 3 else 1
}
class Solution(wordHive: WordHive) {
val words = mutable.Set[String]()
def addWord(word: String): Unit = words.add(word)
def score(): Int = words.foldLeft(0)((acc, word) => acc + wordHive.scoreWord(word))
}
class HiveTrie(pathToDict: String = "/usr/share/dict/words") extends Trie[Char] {
init()
def init(): Unit = {
val dict = scala.io.Source.fromFile(pathToDict)
dict.getLines().filter(_.length > 0).foreach(add(_))
}
def solve(hive: WordHive): Solution = {
val states: mutable.Stack[SearchState] = mutable.Stack(SearchState(hive, "", root))
val s = new Solution(hive)
while (states.nonEmpty) {
val srch = states.pop()
if (srch.node.isWord && hive.qualifies(srch.word)) s.addWord(srch.word)
srch.node.children
.filter(n => srch.wordHive.chars.contains(n._1))
.foreach(n => states.push(srch.copy(word = srch.word + n._1, node = n._2)))
}
s
}
private case class SearchState(wordHive: WordHive, word: String, node: Node[Char])
}
class Trie[T] {
class Node[T]() {
var children: Map[T, Node[T]] = Map[T, Node[T]]()
var isWord: Boolean = false
}
val root = new Node[T]()
private def getNode(s: Seq[T], n: Node[T]): Option[Node[T]] = {
if (s.isEmpty) Some(n)
else n.children.get(s.head).flatMap(nn => getNode(s.tail, nn))
}
def contains(s: Seq[T]): Boolean = getNode(s, root) match {
case Some(node) => node.isWord
case None => false
}
def containsPrefix(s: Seq[T]): Boolean = getNode(s, root) match {
case Some(node) => true
case None => false
}
def add(s: Seq[T]): Node[T] = {
def go(s: Seq[T], n: Node[T]): Node[T] = {
if (s.isEmpty) {
n.isWord = true
n
}
else n.children.get(s.head) match {
case Some(node) => go(s.tail, node)
case None =>
val newNode = new Node[T]()
n.children = n.children.updated(s.head, newNode)
go(s.tail, newNode)
}
}
go(s, root)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment