Skip to content

Instantly share code, notes, and snippets.

@avdgaag
Created August 29, 2012 18:54
Show Gist options
  • Save avdgaag/3517092 to your computer and use it in GitHub Desktop.
Save avdgaag/3517092 to your computer and use it in GitHub Desktop.
Solution to minefield code kata
class SurroundingRange
attr_reader :point, :field
def initialize(point, field)
@point, @field = point, field
end
def points
horizontal = [point.x - 1, 0].max..[point.x + 1, field.width - 1].min
vertical = [point.y - 1, 0].max..[point.y + 1, field.height - 1].min
horizontal.to_a.product(vertical.to_a).map { |x,y| Point.new(x,y) }.reject { |p| p === point }
end
end
class Point < Struct.new(:x, :y)
def ===(other)
other.x == self.x && other.y == self.y
end
end
class Field
BOMB = '*'
attr_reader :rows, :width, :height
def initialize(width, height)
@width, @height = width, height
@rows = []
end
def map
output = []
rows.each_with_index do |row, x|
line = ''
row.each_with_index do |char, y|
p = Point.new(x, y)
line << (bomb?(p) ? '*' : bombs_surrounding(p).to_s)
end
output << line
end
output.join("\n")
end
def bombs_surrounding(point)
range_around(point).select { |p| bomb?(p) }.count
end
def range_around(point)
SurroundingRange.new(point, self).points
end
def to_s
map
end
def <<(row)
@rows << row
self
end
def at(point)
rows.fetch(point.x).fetch(point.y)
end
def bomb?(point)
at(point) == BOMB
end
end
class InputReader
attr_reader :input
def initialize(input)
@input = input
end
def fields
fields = []
input.each_line do |line|
break if line.start_with?('0 0')
if line =~ /(\d+) (\d+)/
fields << Field.new($1.to_i, $2.to_i)
next
end
fields.last << line.chomp.split('')
end
fields
end
end
class Minesweeper
attr_reader :fields
def initialize(input)
@fields = InputReader.new(input).fields
end
def maps
i = 1
@fields.inject([]) do |output, field|
output << "Field ##{i}\n#{field.map}\n"
i += 1
output
end.join("\n")
end
end
describe SurroundingRange do
let(:field) { double width: 10, height: 10 }
subject { described_class.new(point, field) }
context 'when in the middle of a field' do
let(:point) { double x: 4, y: 4 }
it { should have(8).points }
its('points.first') { should be_kind_of(Point) }
end
context 'when in the horizontal edge of a field' do
let(:point) { double x: 0, y: 0 }
it { should have(3).points }
end
end
describe Field do
it 'finds a value by coordinates' do
field = described_class.new(2, 4) << %w[1 2 3 4] << %w[. a * b]
field.at(Point.new(0, 0)).should == '1'
field.at(Point.new(1, 2)).should == '*'
end
it 'knows when a square is a bomb' do
field = described_class.new(1, 4) << %w[. . * .]
field.bomb?(Point.new(0, 1)).should be_false
field.bomb?(Point.new(0, 2)).should be_true
end
it 'can count the number of bombs around a point' do
field = described_class.new(2, 4) << %w[. . * .] << %w[. . . *]
field.bombs_surrounding(Point.new(0, 0)).should == 0
field.bombs_surrounding(Point.new(0, 2)).should == 1
field.bombs_surrounding(Point.new(0, 3)).should == 2
end
it 'can generate a map with bomb counts' do
field = described_class.new(2, 4) << %w[. . * .] << %w[. . . *]
field.map.should == "01*2\n012*"
end
it 'can generate a map for a simpe field' do
field = described_class.new(1, 1) << %w[.]
field.map.should == "0"
end
end
describe InputReader do
subject { described_class.new(input) }
context 'without input' do
let(:input) { '' }
it { should have(0).fields }
end
context 'with just the input ending marker' do
let(:input) { '0 0' }
it { should have(0).fields }
end
context 'with a single field' do
let(:input) { "1 1\n.\n0 0" }
it { should have(1).fields }
end
context 'with multiple fields' do
let(:input) { "1 1\n.\n2 2\n..\n..\n0 0" }
it { should have(2).fields }
end
end
describe Minesweeper do
subject { described_class.new(input) }
context 'without input' do
let(:input) { '' }
its(:maps) { should == '' }
end
context 'with just the input ending marker' do
let(:input) { '0 0' }
its(:maps) { should == '' }
end
context 'with a single field' do
let(:input) { "1 1\n.\n0 0" }
its(:maps) { should == "Field #1\n0\n" }
end
context 'with multiple fields' do
let(:input) { "1 1\n.\n2 2\n..\n..\n0 0" }
its(:maps) { should == "Field #1\n0\n\nField #2\n00\n00\n" }
end
end
@avdgaag
Copy link
Author

avdgaag commented Aug 31, 2012

Same approach rewritten using TDD!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment