Created
August 25, 2018 14:39
-
-
Save cgrothaus/e65a7f6673ad4e4eb454bec16973e291 to your computer and use it in GitHub Desktop.
SET game kata
This file contains hidden or 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
require 'set' | |
## | |
# One solution for the set game kata. It is all about "SET", a card game. | |
# For details on task to solve and the rules of the game, see | |
# https://www.codewars.com/kata/set-the-card-game/ | |
module SetGame | |
ATTRIBUTES = %i[color shape number shading].freeze | |
COLORS = %i[red green blue].freeze | |
SHAPES = %i[rectangle triangle circle].freeze | |
NUMBERS = [1, 2, 3].freeze | |
SHADINGS = %i[solid empty striped].freeze | |
## | |
# spot all sets with the given set_size that fulfill the 'set game rule' | |
# in the given deck of cards | |
def self.spot_all_sets(deck, set_size: 3) | |
return [] if deck.size < set_size | |
deck.combination(set_size).select {|cards| set?(cards) } | |
end | |
## | |
# checks if the cards fulfill the 'set game rule': for every attribute, the | |
# set game rule must be fulfilled | |
# called with an array of cards, where each card is an instance of class Card | |
def self.set?(cards) | |
return false if cards.empty? | |
ATTRIBUTES.map do |attribute| | |
cards.map do |card| | |
card.send(attribute) | |
end | |
end .map do |values_of_all_cards_for_attribute| | |
values_fulfill_set_rule(values_of_all_cards_for_attribute) | |
end .all? | |
end | |
## | |
# checks if values fulfill the 'set game rule': all identical or all distinct | |
# called with an array of values | |
def self.values_fulfill_set_rule(values) | |
return false if values.empty? | |
num_distinct_elements = Set.new(values).size | |
num_distinct_elements == 1 || num_distinct_elements == values.size | |
end | |
def self.generate_all_cards | |
deck = [] | |
COLORS.each do |color| | |
SHAPES.each do |shape| | |
NUMBERS.each do |number| | |
SHADINGS.each do |shading| | |
deck << Card.new(color, shape, number, shading) | |
end | |
end | |
end | |
end | |
deck | |
end | |
Card = Struct.new(:color, :shape, :number, :shading) do | |
def to_s | |
"[#{color} #{shape} #{number} #{shading}]" | |
end | |
end | |
end | |
require 'spec_helper' | |
require 'set_game' | |
RSpec.describe SetGame do | |
let(:card1) { card(:red, :rectangle, 1, :solid) } | |
let(:card2) { card(:red, :rectangle, 1, :empty) } | |
let(:card3) { card(:red, :rectangle, 1, :striped) } | |
let(:card4) { card(:green, :triangle, 2, :empty) } | |
let(:card5) { card(:blue, :circle, 3, :solid) } | |
describe '.values_fulfill_set_rule' do | |
subject { described_class.values_fulfill_set_rule(input_values) } | |
context 'for 0 values' do | |
let(:input_values) { [] } | |
it { is_expected.to eq false } | |
end | |
context 'for n identical values' do | |
let(:input_values) { [1, 1, 1, 1] } | |
it { is_expected.to eq true } | |
end | |
context 'for n unique values' do | |
let(:input_values) { %i[red green blue yellow orange] } | |
it { is_expected.to eq true } | |
end | |
context 'for n values neither unique nor all identical' do | |
let(:input_values) { %i[rectangle triangle triangle] } | |
it { is_expected.to eq false } | |
end | |
end | |
describe '.set?' do | |
subject { described_class.set?(input_cards) } | |
context 'for 0 cards' do | |
let(:input_cards) { [] } | |
it { is_expected.to eq false } | |
end | |
context 'for 1 card' do | |
let(:input_cards) { [card1] } | |
it { is_expected.to eq true } | |
end | |
context 'for 3 identical cards' do | |
let(:input_cards) { [card1, card1, card1] } | |
it { is_expected.to eq true } | |
end | |
context 'for 3 cards differing in one attribute' do | |
let(:input_cards) { [card1, card2, card3] } | |
it { is_expected.to eq true } | |
end | |
context 'for 3 cards differing in all attributes' do | |
let(:input_cards) { [card3, card4, card5] } | |
it { is_expected.to eq true } | |
end | |
context 'for 3 cards having two common values in one attribute' do | |
let(:input_cards) { [card1, card2, card1] } | |
it { is_expected.to eq false } | |
end | |
context 'for 3 cards having two common values in all attributes' do | |
let(:input_cards) { [card2, card3, card4] } | |
it { is_expected.to eq false } | |
end | |
end | |
describe '.spot_all_sets' do | |
subject { described_class.spot_all_sets(deck) } | |
context 'empty deck' do | |
let(:deck) { [] } | |
it { is_expected.to eq [] } | |
end | |
context 'deck size < set size' do | |
let(:deck) { [card1, card2] } | |
it { is_expected.to eq [] } | |
end | |
context 'deck with no set' do | |
let(:deck) { [card2, card3, card4] } | |
it { is_expected.to eq [] } | |
end | |
context 'deck with one set' do | |
let(:deck) { [card1, card2, card3, card4] } | |
it { is_expected.to eq [[card1, card2, card3]] } | |
end | |
context 'deck with two sets' do | |
let(:deck) { [card1, card2, card3, card4, card5] } | |
it { is_expected.to eq [[card1, card2, card3], [card3, card4, card5]] } | |
end | |
context 'deck with all possible cards' do | |
let(:deck) { described_class.generate_all_cards } | |
it 'lets see what happens' do | |
puts "Spotted #{subject.size} sets in the full deck of #{deck.size} cards" | |
end | |
end | |
end | |
describe '.generate_all_cards' do | |
it 'should generate a deck of 81 cards' do | |
expect(described_class.generate_all_cards.size).to eq 81 | |
end | |
end | |
def card(color, shape, number, shading) | |
SetGame::Card.new(color, shape, number, shading) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment