Skip to content

Instantly share code, notes, and snippets.

@ciaranarcher
Forked from stubotnik/minesweeper.rb
Created July 20, 2012 14:51
Show Gist options
  • Save ciaranarcher/3151132 to your computer and use it in GitHub Desktop.
Save ciaranarcher/3151132 to your computer and use it in GitHub Desktop.
Minesweeper Kata
module Minesweeper
class Game
attr_reader :fields
def initialize
@fields = []
@allow_input = true
@row_input_format = /[\.\*]+/
@output_buffer = []
@output_buffer_index = 0
end
def input (str)
raise 'input not processed. end-of-input message already received' unless @allow_input
# check if there are row inputs pending for the last declared field
if (@fields.size == 0 or @fields.last.data.size == @fields.last.num_rows)
input_field_data str
else
input_row_data str
end
end
def output
out_str = @output_buffer[@output_buffer_index]
@output_buffer_index += 1
out_str
end
private
def input_field_data (str)
match_data = str.match /(-?\d+)\s(-?\d+)/
if match_data
num_rows = match_data[1].to_i
num_cols = match_data[2].to_i
if num_rows == 0 and num_cols == 0
end_input
else
raise 'num rows not in range' unless num_rows.between? 1,100
raise 'num cols not in range' unless num_cols.between? 1,100
@fields.push Field.new num_rows, num_cols
end
else
match_data = str.match @row_input_format
raise 'too many rows, expected new field or end of input' if match_data
end
end
def input_row_data (str)
match_data = str.match @row_input_format
raise 'input does not define field row' if match_data.nil?
if match_data.to_s.size != @fields.last.num_cols
raise "too many columns input, expected #{@fields.last.num_cols}, received #{match_data.to_s.size}"
end
@fields.last.add_row match_data.to_s
end
def end_input
@allow_input = false
@fields.each do |field|
field.count_mines
end
fill_output_buffer
end
def fill_output_buffer
field_counter = 1
@fields.each do |field|
@output_buffer.push "Field ##{field_counter}:"
field.data.each do |row|
@output_buffer.push row.join
end
@output_buffer.push ""
field_counter += 1
end
end
end
class Field
attr_reader :num_rows, :num_cols, :data
def initialize (num_rows, num_cols)
@num_rows = num_rows
@num_cols = num_cols
@data = Array.new
end
def add_row (row_str)
@data.push row_str.split //
end
def count_mines
# replace all . characters with the number of adjactent mines
@data.each_index do |r|
@data[r].each_index do |c|
if @data[r][c] == '.'
@data[r][c] = 0
(-1..1).each do |x_r|
(-1..1).each do |x_c|
unless x_r == 0 and x_c == 0
if (r+x_r).between? 0, @num_rows-1 and
(c+x_c).between? 0, @num_cols-1 and
@data[r+x_r][c+x_c] == '*'
@data[r][c] += 1
end
end
end
end
end
end
end
end
end
end
require 'minesweeper'
describe Minesweeper do
before(:each) do
@ms = Minesweeper::Game.new
end
it 'creates fields from input' do
@ms.input '4 4'
@ms.fields.length.should eq 1
end
it 'sets field row & column size from input' do
@ms.input '4 6'
f = @ms.fields[0]
f.num_rows.should eq 4
f.num_cols.should eq 6
end
it 'allows row and column size in the range 1..100' do
expect {
@ms.input '199 0'
}.to raise_error 'num rows not in range'
expect {
@ms.input '1 0'
}.to raise_error 'num cols not in range'
end
it 'takes row input after field size input' do
@ms.input '4 4'
expect {
@ms.input '4 4'
}.to raise_error 'input does not define field row'
@ms.input '...*'
@ms.input '*.*.'
f = @ms.fields[0]
f.data[0].join.should eq '...*'
f.data[1].join.should eq '*.*.'
end
it 'expects row input to match defined column size' do
@ms.input '4 4'
expect {
@ms.input '...**'
}.to raise_error 'too many columns input, expected 4, received 5'
end
it 'expects number of row inputs to not be less than defined row size' do
@ms.input '2 4'
@ms.input '*...'
expect {
@ms.input '2 4'
}.to raise_error 'input does not define field row'
end
it 'expects number of row inputs to not be greater than defined row size' do
@ms.input '2 4'
@ms.input '*...'
@ms.input '*...'
expect {
@ms.input '*...'
}.to raise_error 'too many rows, expected new field or end of input'
end
it 'stops accepting input when n=m=0' do
@ms.input '2 4'
@ms.input '*...'
@ms.input '*...'
@ms.input '0 0'
expect {
@ms.input '2 4'
}.to raise_error 'input not processed. end-of-input message already received'
end
it 'outputs field number message and adjacent mine count' do
@ms.input '4 4'
@ms.input '*...'
@ms.input '....'
@ms.input '.*..'
@ms.input '....'
@ms.input '3 5'
@ms.input '**...'
@ms.input '.....'
@ms.input '.*...'
@ms.input '0 0'
@ms.output.should eq 'Field #1:'
@ms.output.should eq '*100'
@ms.output.should eq '2210'
@ms.output.should eq '1*10'
@ms.output.should eq '1110'
@ms.output.should eq ''
@ms.output.should eq 'Field #2:'
@ms.output.should eq '**100'
@ms.output.should eq '33200'
@ms.output.should eq '1*100'
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment