Skip to content

Instantly share code, notes, and snippets.

@geofflane
Last active December 15, 2015 04:29
Show Gist options
  • Save geofflane/5201755 to your computer and use it in GitHub Desktop.
Save geofflane/5201755 to your computer and use it in GitHub Desktop.
Ruby user group minesweeper kata
require 'rspec/given'
describe "minesweeper" do
Given(:minesweeper) { Minesweeper.new(input) }
context "the example minesweeper" do
Given(:input) { "*...\n....\n.*..\n...." }
Then { minesweeper.solve.should == "*100\n2210\n1*10\n1110" }
end
context "minesweeper with mines in the last row" do
Given(:input) { "*...\n....\n.*..\n.**." }
Then { minesweeper.solve.should == "*100\n2210\n2*31\n2**1" }
end
end
class Minesweeper
MINE = '*'
NO_MINE = '.'
def initialize(input)
@input = input
end
def solve
grid = generate_grid
result = grid.each_with_index do |row, x|
row.each_with_index do |c, y|
row[y] = count_mines_from_position(grid, x, y) if c != MINE
end
end
result.collect(&:join).join("\n")
end
private
def generate_grid
@input.split("\n").map { |row_str| row_str.split(//) }
end
def count_mines_from_position(grid, x, y)
count_row(previous_row(grid, x), y) + count_row(grid[x], y) + count_row(grid[x+1], y)
end
def count_row(row, i)
return 0 if row.nil?
start = [i - 1, 0].max
row[start..(i+1)].count(MINE)
end
def previous_row(grid, x)
return nil if x-1 < 0
return grid[x-1]
end
end
@geofflane
Copy link
Author

OO-ized version where Grid and Row are captured as classes and handle the parsing of the input string, iterating, and printing. All of the details of minesweeper are still kept in the Minesweeper object.

require 'rspec/given'

describe "minesweeper" do
  Given(:minesweeper) { Minesweeper.new(input) }

  context "the example minesweeper" do
    Given(:input) { "*...\n....\n.*..\n...." }
    Then { minesweeper.solve.should == "*100\n2210\n1*10\n1110" }
  end

  context "minesweeper with mines in the last row" do
    Given(:input) { "*...\n....\n.*..\n.**." }
    Then { minesweeper.solve.should == "*100\n2210\n2*31\n2**1" }
  end
end

class Minesweeper
  MINE = '*'

  def initialize(input)
    @input = input
  end

  def solve
    grid = Grid.new(@input)
    grid.each_row do |row, x|
      row.each_column do |c, y|
        row[y] = count_mines_from_position(grid, x, y) if c != MINE
      end
    end

    grid.to_s
  end

private
  def count_mines_from_position(grid, x, y)
    count_row(grid.previous_row(x), y) + count_row(grid.row(x), y) + count_row(grid.next_row(x), y)
  end

  def count_row(row, i)
    return 0 if row.nil?

    start = [i - 1, 0].max
    row[start..(i+1)].count(MINE)
  end
end

class Row
  def initialize(row)
    @row = row
  end

  def each_column
    @row.each_with_index { |char,y| yield(char, y) }
  end

  def []=(i, val)
    @row[i] = val
  end

  def [](i)
    @row[i]
  end

  def to_s
    @row.join
  end
end

class Grid
  def initialize(input)
    @grid = input.split("\n").map { |row_str| Row.new(row_str.split(//)) }
  end

  def each_row
    @grid.each_with_index { |row, x| yield(row, x) }
  end

  def row(x)
    @grid[x]
  end

  def next_row(x)
    row(x+1)
  end

  def previous_row(x)
    return nil if x-1 < 0
    return @grid[x-1]
  end

  def to_s
    @grid.collect(&:to_s).join("\n")
  end
end

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