Skip to content

Instantly share code, notes, and snippets.

@arjans
Created November 18, 2016 22:18
Show Gist options
  • Save arjans/616b120908b2c0c68f8d1147226bb847 to your computer and use it in GitHub Desktop.
Save arjans/616b120908b2c0c68f8d1147226bb847 to your computer and use it in GitHub Desktop.
Solver for the game of Set
#!/usr/bin/env ruby
#
# Solver for the game of Set.
#
class Card
# Constants deriving from the rules of the game
COLORS = ["red", "blue", "green"]
SHAPES = ["circle", "square", "triangle"]
NUMBERS = [1, 2, 3]
attr_accessor :color
attr_accessor :shape
attr_accessor :number
def initialize(color: nil, shape: nil, number: nil)
@color = color
@shape = shape
@number = number
end
# Check if a card is a valid card in Set.
def valid?
COLORS.include?(@color)
SHAPES.include?(@shape)
NUMBERS.include?(@number)
end
end
# set? takes 3 Cards and checks to see if they make a set.
# Returns true or false.
def set?(card1, card2, card3)
cards = [card1, card2, card3]
# Check to make sure all the cards are valid.
if cards.any? { |c| !c.valid? }
return "Cards are invalid."
end
# Checks each attribute to see if it would contribute to a valid set.
# For each attribute, 1 or 3 are valid numbers after the array of attributes is uniqued.
cards.collect(&:color).uniq.length != 2 &&
cards.collect(&:shape).uniq.length != 2 &&
cards.collect(&:number).uniq.length != 2
end
# generate takes 2 cards and generates the 3rd that would complete the set.
# Returns a Card object.
def generate(card1, card2)
# Checks if both Cards are valid.
if [card1, card2].any? { |c| !c.valid? }
return "Cards are invalid."
end
card3 = Card.new(color: nil, shape: nil, number: nil)
# Assigns color.
if card1.color == card2.color
card3.color = card1.color
else
card3.color = (Card::COLORS - [card1.color, card2.color]).first
end
# Assigns shape.
if card1.shape == card2.shape
card3.shape = card1.shape
else
card3.shape = (Card::SHAPES - [card1.shape, card2.shape]).first
end
# Assigns number.
if card1.number == card2.number
card3.number = card1.number
else
card3.number = (Card::NUMBERS - [card1.number, card2.number]).first
end
card3
end
# find_set takes a list of Cards and finds the first available set.
# Returns an array of 3 Cards that make a set, if one is found.
def find_set(list)
# Check number and validity of Cards.
if list.length < 3
return "Not enough cards."
elsif list.any? { |c| !c.valid? }
return "Invalid list."
end
# Brute force way to find first valid set.
# Worst case: steps increase as Cards cubed.
(0..(list.length - 3)).each do |i|
(1..(list.length - 2)).each do |j|
(2..(list.length - 1)).each do |k|
if set?(list[i], list[j], list[k])
return [list[i], list[j], list[k]]
end
end
end
end
"No set found."
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment