Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kanhirun/8715256 to your computer and use it in GitHub Desktop.
Save kanhirun/8715256 to your computer and use it in GitHub Desktop.
create a boggle board class
=begin========================================================================
EXERCISE: Create a Boggle Board Class
Ruby 2.1.0
Kel Kanhirun
January 30, 2014
I/O: #create_word, String => String
#get_row, Integer => Array
#get_col, Integer => Array
PROPOSITION
===============================================================================
(1) BoggleBoard initializes with an argument, dice_grid, which should provide a
2D array (by hypothesis)
From (1),
(2) Raise ArgumentError if dice_grid does not meet the hypothesis:
(Criteria A) dice_grid must be a 2-dimensional array
(Criteria B) dice_grid must hold only string objects
(Criteria C) dice_grid must be squared.
From (1) and (2),
(3) Create the following objects which meet or do not meet these one or
more conditions that confirm the hypothesis:
(Test A) Meets none of the criteria
(Test B) ["A"]["B"]["C"]
(Test C) ["A", "B"]["A", "C"]["B", "C"]
(Test D) ["A", "B", "C"]
(4) Reproduce methods (#create_word, #get_row, #get_col) from the previous
exercise.
(5) #create_word accepts the "standard form", (a, b), where a and b is a "valid pair of numbers".
(Defn. of standard form)
A standard form is a string object which has the proper opening and
close tags. The proper opening tags are either parentheses (), curly
braces {}, or square brackets [].
(Defn. of valid pairs)
A valid pair are two numbers that exists in the given two-dimensional
array and are given as a pair. That is, the numbers a and b, must both
be defined. The numbers a and b, are only valid if both a and b are
identifiable on the Boggle board.
From (5),
(6) Create tests for all cases of standard form and nonstandard forms.
(Criteria A) The input is a string object.
# On standard forms,
(Criteria B) A standard form of [] is provided.
(Criteria C) A standard form of {} is provided.
(Criteria D) A standard form of () is provided.
(Criteria E) One and only one form is accepted.
# On valid pairs,
(Criteria F) The object a is defined as a number.
(Criteria G) The object b is defined as a number.
(Criteria H) A comma is used to separated the two numbers.
(Criteria I) The number pair, a and b, are valid on the given board.
================================================================================
=end
# INITIAL CODE
class BoggleBoard
attr_reader :dice_grid
def initialize (dice_grid)
# see Proposition 3.
rows = Enumerator.new do |y|
dice_grid.each{ |obj| y << obj }
end
testA = (dice_grid.class != Array)
testB = (dice_grid.empty?)
testC = (dice_grid.any? { |obj| obj.class != Array })
testD = (rows.each{ |letter| (letter.class == String) and letter =~ /[a-z]/ })
testE = (rows.all? { |row| row.size == dice_grid.size })
raise TypeError.new("Argument is not an array.") if testA
raise ArgumentError.new("Array is empty.") if testB
raise ArgumentError.new("Argument is not entirely a 2-D array.") if testC
raise ArgumentError.new("Array does not hold all valid objects.") unless testD
raise ArgumentError.new("Array is not a square; have mismatched rows and columns.") unless testE
@dice_grid = dice_grid
end
def create_word (*coords)
coords.map{ |coord| @dice_grid[coord[0]][coord[1]] }.join("")
end
def get_row (i)
dice_grid[i]
end
def get_col (i)
dice_grid.map{ |row| row[i] }
end
def get_diagonal (a, b)
return (0..3).map{ |n| @dice_grid[n][n] } if a[0] == a[1] and b[0] and b[1]
return (0..3).map{ |n| @dice_grid.reverse[n][n] } if a.reduce(:+) == 3 and b.reduce(:+) == 3
end
end
# DRIVER TEST CODE
#===============================================================================
puts "returns TypeError when argument is a string: "
p BoggleBoard.new("string") rescue $!.class == TypeError #=> true
puts "returns TypeError when argument is a number: "
p BoggleBoard.new(6) rescue $!.class == TypeError #=> true
pp "returns ArgumentError when argument is an invalid array: "
p BoggleBoard.new([]) rescue $!.class == ArgumentError #=> true
puts "returns ArgumentError when the array has columns greater than its rows: "
dice_grid = Array.new(3) { ["a", "v", "c", "d"] }
p BoggleBoard.new(dice_grid) rescue $!.class == ArgumentError #=> true
puts "returns ArgumentError when the array has rows greater than its columns: "
dice_grid = Array.new(5) { ["a", "v", "x"] }
p BoggleBoard.new(dice_grid) rescue $!.class == ArgumentError #=> true
p 'returns... "code", "rad", "cat", "boat", "taker"?',
new_game.create_word([2,1], [1,1], [1,2], [0,3]) == "code", # => true
new_game.create_word([0,1], [0,2], [1,2]) == "rad" , # => true
new_game.create_word([2,1], [3,1], [3,0]) == "cat" , # => true
new_game.create_word([0,0], [1,1],[0,2], [1,3]) == "boat" , # => true
new_game.create_word([3,0], [3,1], [3,2], [3,3], [2,3]) == "taker" # => true
p "returns rows correctly?",
new_game.get_row(0) == ["b", "r", "a", "e"], # => true
new_game.get_row(1) == ["i", "o", "d", "t"], # => true
new_game.get_row(2) == ["e", "c", "l", "r"], # => true
new_game.get_row(3) == ["t", "a", "k", "e"], # => true
p "returns columns correctly?",
new_game.get_col(0) == [ "b", "i", "e", "t" ], # => true
new_game.get_col(1) == [ "r", "o", "c", "a" ], # => true
new_game.get_col(2) == [ "a", "d", "l", "k" ], # => true
new_game.get_col(3) == [ "e", "t", "r", "e" ], # => true
dice_grid = [["b", "r", "a", "e"],
["i", "o", "d", "t"],
["e", "c", "l", "r"],
["t", "a", "k", "e"]]
boggle_board = BoggleBoard.new(dice_grid)
p "returns the correct array when given two coordinates to define a left-diagonal: "
p boggle_board.get_diagonal([0,0], [1,1]) == ["b", "o", "l", "e" ] #=> true
p "returns nil when given invalid coordinates: "
p boggle_board.get_diagonal([0,1], [2,1]) == nil #=> true
p "returns the correct array when given two coordinates to define a right-diagonal: "
p boggle_board.get_diagonal([2,1], [3,0]) == ["t", "c", "d", "e"] #=> true
# REFLECTIONS
=begin=================================================================================
For this exercise, I wanted to include tests for correct error types in my driver
test code. This is done inline, as +rescue $!.class == ArgumentError+, where $!
is the global variable of the current exception.
In these cases, the exception has been raised but not yet handled. This phenomenon
allows me to perform a rescue operation before it terminates the program provided
that the correct error was raised.
I had trouble making lines 91 - 101 readable at first. keyword_case was inflexible,
because it only allows comparisons of == to be made. Unless I change the core functionality
of Ruby, and I definitely didn't want to do that. On the other hand, the if-branches
didn't make it anymore readable either. My first attempt, then, was to do everything inline as,
raise <error> if <condition>
and I liked that the statement emphasizes that it is a raising an error, but the statement was bulky.
So, I decided to condense these long conditions into variables of testA, testB, ... etc.
I didn't have enough time to incorporate all the features I wanted, but I felt that as
long as I am aware of how to deal with these issues, then I'll work under the hypothesis
that the arguments given are valid.
======================================================================================
=end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment