Here's my solution (and my first attempt at a Ruby Quiz) - also 
completed without googling.

The distinguishing feature here compared to the other solutions posted 
so far, is that I'm doing the recursion by creating a tree of Square 
objects beforehand, in an effort to make it more OOPish.



#!/usr/bin/ruby

# Proof by induction
# Base case: a 2x2 board with one missing square.
#   Trivially, the remaining 3 squares form an L-tromino.
# Step:
#   Assume that any (2**(n-1) x 2**(n-1)) board with a square missing 
can be solved.
#   A (2**n x 2**n) board can be divided into four (2**(n-1) x 2**(n-1)) 
quadrants.
#   One of these quadrants contains the missing square.
#   An L-tromino can be placed in the centre of the board, rotated such 
that it occupies
#   one square of each of the other three quadrants.
#   The result of this is four quadrants, each of which has one square 
missing.
#   By our original assumption, each of these can be solved.

# Note that at each stage of subdivision, each quadrant contains 
precisely one
#   square that is either 1) the missing square, or 2) occupied by an 
L-tromino
#   that overlaps onto other quadrants.

class Square
	# Represents a square portion of the board.
	
	attr_reader :central_corner_x, :central_corner_y
	
	def initialize(size, left = 0, top = 0, central_corner_x = nil, 
central_corner_y = nil)
		@size = size # Size of this square
		@left = left # X coordinate of leftmost point
		@top = top # Y coordinate of topmost point
		@central_corner_x = central_corner_x
		@central_corner_y = central_corner_y
			# Coordinates of the corner closest to the middle
			# of the parent square (or nil if the square has no parent)
			
		if size > 1
			# divide into quadrants
			quad_size = @size / 2
			@quadrants = [
				Square.new(quad_size, @left, @top, @left + quad_size - 1, @top + 
quad_size - 1),
				Square.new(quad_size, @left + quad_size, @top, @left + quad_size, 
@top + quad_size - 1),
				Square.new(quad_size, @left, @top + quad_size, @left + quad_size - 
1, @top + quad_size),
				Square.new(quad_size, @left + quad_size, @top + quad_size, @left + 
quad_size, @top + quad_size)
			]
		end
	end
	
	def contains?(x, y)
		# Determine whether this square contains the given point
		(@left...(@left+@size)) === x && (@top...(@top+@size)) === y
	end
	
	def solve(board, missing_x, missing_y, count = 1)
		# board = a board which is to have the portion indicated by this 
Square object filled with L-trominos
		# missing_x, missing_y - the coordinates of a square not to be filled
		# count = the current L-tromino number
		# Returns the next available unused L-tromino number
		if @size == 1
			board[@top][@left] = count unless contains?(missing_x, missing_y)
			count
		else
			next_count = count + 1
			@quadrants.each { |quadrant|
				if quadrant.contains?(missing_x, missing_y)
					# a square in this quadrant is already missing - can solve the 
quadrant straight off
					next_count = quadrant.solve(board, missing_x, missing_y, next_count)
				else
					# artificially 'create' a missing square before solving the quadrant
					board[quadrant.central_corner_y][quadrant.central_corner_x] = count
					next_count = quadrant.solve(board, quadrant.central_corner_x, 
quadrant.central_corner_y, next_count)
				end
			}
			next_count
		end
	end
end

puts "Board magnitude? (1 = 2x2, 2 = 4x4, 3 = 8x8, 4 = 16x16...)"
n = gets.to_i
size = 2**n
digits = (n*2) / 5 + 1 # how many base-32 digits we need to give each 
L-tromino its own ID

board = (0...size).collect{ (0...size).collect { 0 } }
Square.new(size).solve(board, rand(size), rand(size))

board.each do |row|
	puts row.map{ |i| i.to_s(32).rjust(digits) }.join(' ')
end