Skip to content

Instantly share code, notes, and snippets.

@harmaty
Last active March 30, 2017 22:45
Show Gist options
  • Save harmaty/3d617bd42bb6ee3313e119800d7982ac to your computer and use it in GitHub Desktop.
Save harmaty/3d617bd42bb6ee3313e119800d7982ac to your computer and use it in GitHub Desktop.
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
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