Skip to content

Instantly share code, notes, and snippets.

@cgrothaus
Created August 25, 2018 14:39
Show Gist options
  • Save cgrothaus/e65a7f6673ad4e4eb454bec16973e291 to your computer and use it in GitHub Desktop.
Save cgrothaus/e65a7f6673ad4e4eb454bec16973e291 to your computer and use it in GitHub Desktop.
SET game kata
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