Skip to content

Instantly share code, notes, and snippets.

@kierendavies
Last active August 29, 2015 14:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kierendavies/346727019458e89fe5ed to your computer and use it in GitHub Desktop.
Save kierendavies/346727019458e89fe5ed to your computer and use it in GitHub Desktop.
Solver for the coloured-cubes puzzle that Caleb brought
# This program solves a puzzle I encountered recently.
# The puzzle consists of four cubes, each face of which has one of four colours.
# The objective is to arrange them in a column so that, looking down each side
# of the column, the four visible colours are all different.
require 'pp'
require 'set'
# A cube with colours attributed to its faces.
class Cube
# The face labels.
FACES = [:left, :right, :bottom, :top, :back, :front]
# The axis labels.
AXES = [:x, :y, :z]
# The face change mappings for rotations.
ROTATIONS = {
x: {
:bottom => :back,
:top => :front,
:back => :top,
:front => :bottom
},
y: {
:left => :front,
:right => :back,
:back => :left,
:front => :right
},
z: {
:left => :bottom,
:right => :top,
:bottom => :right,
:top => :left,
}
}
# The map specifying the colour on each face of the cube.
attr_reader :face_colours
# Create a new +Cube+ with the specified face colours in a +Hash+ or +Array+.
def initialize(face_colours)
if face_colours.is_a? Array
face_colours = Hash[FACES.zip(face_colours)]
end
@face_colours = face_colours
end
# Test if this +Cube+ is the same as another.
def eql?(other)
other.is_a?(Cube) && face_colours == other.face_colours
end
alias :== :eql?
# Compute a hash code for this object.
def hash
face_colours.hash
end
# Create a new +Cube+ which is equivalent to this one rotated 90 degrees about
# the specified axis, following the right-hand rule.
def rotate(axis)
Cube.new(
Hash[face_colours.map { |face, colour|
[ROTATIONS[axis][face] || face, colour]
}]
)
end
# Compute a +Set+ of all possible rotated versions of this cube.
def all_rotations
rotations = Set.new([self])
last_rotations = rotations
4.times do # because octahedral symmetry group
next_rotations = Set.new
last_rotations.each do |cube|
AXES.each do |axis|
next_rotations << cube.rotate(axis)
end
end
rotations.merge(next_rotations)
last_rotations = next_rotations
end
return rotations
end
# Refer method calls of a face name to face colour lookups.
def method_missing(name)
if FACES.include? name
face_colours[name]
end
end
end
cubes = [
Cube.new([:green, :green, :blue, :yellow, :red, :blue]),
Cube.new([:green, :yellow, :yellow, :yellow, :blue, :red]),
Cube.new([:red, :green, :green, :yellow, :blue, :red]),
Cube.new([:red, :green, :red, :yellow, :yellow, :blue])
]
all_rotations = cubes.map(&:all_rotations).map(&:to_a)
solution = all_rotations.first.product(*all_rotations.drop(1)).find { |cubes|
[:left, :right, :bottom, :top].all? { |face|
cubes.map(&face).to_set.size == cubes.size
}
}
PP.pp solution
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment