Last active
March 30, 2017 22:45
-
-
Save harmaty/3d617bd42bb6ee3313e119800d7982ac to your computer and use it in GitHub Desktop.
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' | |
class Point | |
attr_accessor :name, :x, :y | |
class << self | |
def parse(str) | |
name, x, y = str.split(',') | |
new(name, x.to_i, y.to_i) | |
end | |
def triple_collinear?(p1, p2, p3) | |
(p1.x - p2.x) * (p1.y - p3.y) == (p1.y - p2.y) * (p1.x - p3.x) | |
end | |
end | |
def initialize(name, x, y) | |
@name, @x, @y = name, x, y | |
end | |
def ==(other) | |
name == other.name && x == other.x && y == other.y | |
end | |
def to_s | |
"#{name},#{x},#{y}" | |
end | |
end | |
# Finds all lines containing at least three points from the given array | |
# input looks like: ['A,1,1', 'B,2,2', 'C,3,3', 'D,4,4', 'E,2,4', 'G,2,6', 'H,3,1', 'J,3,5', 'K,5,5', 'L,6,2'] | |
# output: ["ABCDK", "BEG", "CHJ", "DGJL"] | |
def find_lines(arr) | |
# turn array of strings into array of points | |
points = arr.map { |i| Point.parse(i) } | |
# make all possible combinations of pairs of points | |
pairs = points.combination(2) | |
pairs.inject(Set.new) do |lines, pair| | |
# find all points collinear to the given pair of points | |
collinear_points = pair + (points - pair).select { |p| Point.triple_collinear?(*pair, p) } | |
# if we've found some points let's make alphabetically ordered string like 'ABCD' and add it to the set of lines | |
lines << collinear_points.sort_by(&:name).map(&:name).join if collinear_points.length > 2 | |
lines | |
end.sort | |
end |
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 'find_lines' | |
describe Point do | |
describe '.parse' do | |
context "given 'A,3,5'" do | |
let(:pnt) { 'A,3,5' } | |
subject(:point) { Point.parse(pnt) } | |
it '#name returns A' do | |
expect(point.name).to eql('A') | |
end | |
it "#x returns 3" do | |
expect(point.x).to eql(3) | |
end | |
it '#y returns 5' do | |
expect(point.y).to eql(5) | |
end | |
it '#to_s is idempotent' do | |
expect(point.to_s).to eql(pnt) | |
end | |
end | |
end | |
describe '.triple_collinear?' do | |
context 'collinear triple' do | |
it 'returns true' do | |
a = Point.parse('A,0,0') | |
b = Point.parse('A,1,2') | |
c = Point.parse('A,2,4') | |
expect(Point.triple_collinear?(a, b, c)).to be true | |
end | |
end | |
context 'not collinear triple' do | |
it 'returns false' do | |
a = Point.parse('A,0,0') | |
b = Point.parse('A,1,2') | |
c = Point.parse('A,2,6') | |
expect(Point.triple_collinear?(a, b, c)).to be false | |
end | |
end | |
end | |
end | |
describe '#find_lines' do | |
subject { find_lines(points) } | |
context 'not collinear points' do | |
let(:points) { ['A,0,0', 'B,0,2', 'C,2,0', 'D,2,2'] } | |
it 'returns nothing' do | |
is_expected.to eql([]) | |
end | |
end | |
context 'points are on one line' do | |
let(:points) { ['A,1,1', 'B,2,2', 'C,3,3', 'D,4,4', 'E,5,5'] } | |
it 'returns a line' do | |
is_expected.to eql(["ABCDE"]) | |
end | |
end | |
context 'several points are collinear' do | |
let(:points) { ['A,1,1', 'B,2,2', 'C,3,3', 'D,4,4', 'E,2,4', 'G,2,6', 'H,3,1', 'J,3,5', 'K,5,5', 'L,6,2'] } | |
it 'returns lines' do | |
is_expected.to eq(["ABCDK", "BEG", "CHJ", "DGJL"]) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment