-
-
Save ciaranarcher/3151132 to your computer and use it in GitHub Desktop.
Minesweeper Kata
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
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 |
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 '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