Textbased User Interface for user and program taking turns
The very first thing that jumps out at me is the indirection of maintaining both a list of possible words and a seen list. Picking a better representation for your state will make bookkeeping easier.

type Word = String
type Words = [Word]
type Moves = Map.Map Char Words


Now fill in the logically necessary functions needed to work with that state. We need to be able to construct a state blob&mdash;

makeMoves :: Words -&gt; Moves
makeMoves = Map.fromListWith (++) . catMaybes . map tag
  where
    tag :: Word -&gt; Maybe (Char, Words)
    tag []      = Nothing
    tag w@(c:_) = Just (c, [w])


Remove played words from one&mdash;

remove :: Word -&gt; Moves -&gt; Moves
remove []      = id
remove w@(c:_) = Map.adjust (delete w) c


And choose a new word from one given the constraint imposed by the last play.

move :: Char -&gt; Moves -&gt; IO (Maybe (Word, Moves))
move c ms =
  let possible = Map.lookup c ms
  in  case possible of
        Nothing -&gt; return Nothing
        Just [] -&gt; return Nothing
        Just ws -&gt; do
          w &lt;- pick ws
          return $ Just (w, remove w ms)


Note the separation from any logic in taking turns, or which player is currently up. Testing these operations will be much easier then (load them up in GHCi and try it out), and integrating them into our monadic game playing code should be pretty straightforward, just follow the types.

processUser :: Moves -&gt; IO ()
processUser moves = do
    input &lt;- getLine
    case input of
      "quit" -&gt; return ()
      ""     -&gt; do putStrLn "You must enter a word."
                   processUser moves
      w      -&gt; processPC (last w) (remove w moves)


Note also how I have moved the logic for making a player move into the function where it makes sense to do so. User player code shouldn't drive computer player code, and vice versa. As an exercise, try making the necessary modifications and stylistic tweaks to processPC yourself.

You should make a best effort attempt at understanding all of the functions you write into your source. Copy and pasting pick in when you don't understand it is poor form, where'd you even find that definition? At least use a library. If you don't know what liftM is doing, just write the function out using do-notation as you would any other operation in the IO monad.

pick :: [a] -&gt; IO a
pick xs = do
    i &lt;- randomRIO (0, length xs - 1)
    return (xs !! i)




Spoilers&mdash;Here are the commit-level changes and final runnable code I produced while working through this.