Here's my solution to find happy bases - note that I don't find any
happy bases (aside from 2 and 4) before I run out of memory, somewhere
near base 1700.  It wouldn't surprise me if no other happy bases
exist.

The program checks up to 2*(base-1)*(base-1) for numbers that fall
into an infinite loop (that is not '1' forever) when applying the
digit-square-sum function.  This is all that's needed, as it can
easily be shown that any number will eventually get less than that.

There's almost certainly some stuff I could do to be more parsimonious
with memory, chief among them finding a copy of narray that has the
.mod! method.  (I may revisit this and use a combination of div!, mul!
and sbt! to cut down on temporary arrays) However, this program as it
stands very quickly checks much farther out than I would have thought
necessary if there really were other happy bases out there to be
found.

#!/usr/bin/env ruby
require 'narray'

def dodigsum(base, initial)
  tmp = initial / (base ** NArray.to_na([[0],[1],[2]]))
  # I would use mod!, but my copy of narray doesn't have mod!
  tmp = tmp % base
  tmp.mul!(tmp)
  initial.fill!(0).add!(tmp.sum(1))
end

def checkbase(base = 10)
  base = base.to_i
  checklimit = 2*(base-1)*(base-1)
  check = NArray.int(checklimit + 1).indgen!
  check_initial = check.dup

  while true do
    dodigsum(base,check)
    if check.eq(check_initial).count_true > 2
      #Gobs of debugging info
      # lp = check.mul!(check.eq(check_initial)).mask(check.ge(2)).min
      # puts "#{base} has a loop on #{lp}"
      break
    end
    if check.le(1).count_true > checklimit
      puts "#{base} is a happy base"
      break
    end
  end
end

2.upto(3000) { |b|
  begin
    checkbase(b)
  rescue Interrupt
    puts "Checking #{b}"
    checkbase(b)
  rescue Error => e
    puts "Bailing on #{b}"
    raise e
  end
}

__END__


-- 
s=%q(  Daniel Martin -- martin / snowplow.org
       puts "s=%q(#{s})",s.map{|i|i}[1]       )
       puts "s=%q(#{s})",s.map{|i|i}[1]