Created
November 18, 2016 22:18
-
-
Save arjans/616b120908b2c0c68f8d1147226bb847 to your computer and use it in GitHub Desktop.
Solver for the game of Set
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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