diff --git a/spec/parse_spec.cr b/spec/parse_spec.cr new file mode 100644 index 0000000..73d3e4f --- /dev/null +++ b/spec/parse_spec.cr @@ -0,0 +1,62 @@ +require "./spec_helper" + +describe Sudoku::Parser do + + it "should parse an empty board" do + board_string = "....\n....\n....\n...." + board = Sudoku::Parser.parse(board_string, block_size: 2) + board.size.should eq 4 + board.block_size.should eq 2 + + (0..3).each { |x| + (0..3).each { |y| + board.get(x, y).should eq nil + } + } + end + + it "should raise an exception if number of rows and colums is different" do + expect_raises(Sudoku::ParseException) do + Sudoku::Parser.parse("....\n....", block_size: 2) + end + end + + it "should put numbers correctly on the board" do + board_string = "1...\n.2..\n..3.\n...4" + board = Sudoku::Parser.parse(board_string, block_size: 2) + + board.get(0,0).should eq 1 + board.get(1,1).should eq 2 + board.get(2,2).should eq 3 + board.get(3,3).should eq 4 + end + + it "should raise an exception if a cell contains a character that is not a number or a dot" do + expect_raises(Sudoku::ParseException) do + Sudoku::Parser.parse(".A..\n....\n....\n....", block_size: 2) + end + end + + it "should parse 9x9 boards correctly" do + board_string = + "8........\n" \ + "..36.....\n" \ + ".7..9.2..\n" \ + ".5...7...\n" \ + "....457..\n" \ + "...1...3.\n" \ + "..1....68\n" \ + "..85...1.\n" \ + ".9....4.." + + board = Sudoku::Parser.parse(board_string, block_size: 3) + + board.get(0,0).should eq 8 + board.get(2,1).should eq 3 + board.get(3,1).should eq 6 + board.get(1,2).should eq 7 + board.get(4,2).should eq 9 + board.get(8,8).should eq nil + end + +end \ No newline at end of file diff --git a/src/board.cr b/src/board.cr index 24da810..dcd2948 100644 --- a/src/board.cr +++ b/src/board.cr @@ -10,9 +10,14 @@ module Sudoku @grid : Array(Array(Value)) getter grid + + getter size + getter block_size def initialize(@size : Int32, @block_size : Int32) - raise BoardException.new() unless @size.divisible_by?(@block_size) + if !@size.divisible_by?(@block_size) + raise BoardException.new("Invalid block size: #{@block_size} for board size: #{@size}") + end @grid = Array(Array(Value)).new(@size) { Array(Value).new(@size, nil) } end diff --git a/src/parser.cr b/src/parser.cr index 7800c99..955dcdc 100644 --- a/src/parser.cr +++ b/src/parser.cr @@ -1,7 +1,41 @@ module Sudoku - # Parse a sudoku board from a string. Return a board. + + class ParseException < Exception + end + class Parser - def self.parse(board_str) + + # Parse a sudoku board from a string. Return a board. + # The string must contain valid board rows. Each row must be separated by a newline. + # Blanks are represented by dots. + # E.g.: + # .1.2 + # 2..4 + # .3.. + # .... + def self.parse(board_str, block_size) + rows = board_str.split('\n') + board_size = rows.size + board = Sudoku::Board.new(board_size, block_size) + + rows.each_with_index { |row, y| + if row.size != board_size + raise ParseException.new("Invalid row size: #{row.size} for row: #{row}") + end + + row.each_char_with_index { |c, x| + case c + when '.' + board.put(nil, x, y) + when .number? + board.put(c.to_s.to_i, x, y) + else + raise ParseException.new("Invalid character: #{c}") + end + } + } + + board end end end \ No newline at end of file