Boggle in Ruby: Dice and Grids

A boggle board: a four by four grid of letters
By an amazing coincidence this Boggle grid includes the word “blog”. Credit: Rich Brooks/Flickr

Specifications

If we’re to make a Boggle like game in Ruby, first we’ll need to break down the game into a series of steps that we might be able to do in the program. Here’s how a game of Boggle goes down:

  • 16 letters in a 4x4 grid are presented to the players.
  • A timer is set for three minutes.
  • Players must find words in the grid and write them down in a list.
  • After the three minutes are up, players check each of their words to see if they are valid. Here are the rules:
    a. The word must be at least 3 letters long.
    b. The word must be an actual word, in the dictionary.
    c. The letters of the word must be in a chain through the grid, i.e. start at the first letter, and the second letter must be immediately adjacent in any of the surrounding 8 cells, and so on for the third letter.
    d. No cell can be used twice in a word.
  • If the word abides by all the rules, it scores points based on its length, which are added to that player’s total:
    3 Letters: 1 point
    4 Letters: 1 point
    5 Letters: 2 points
    6 Letters: 3 points
    7 Letters: 4 points
    8 or more Letters: 11 points
  • Display a list of the words the user has already entered.
  • Display the grid so the user can always see it.
  • After the game, provide the player with a report of their words, and indicate if they were valid.
  • Provide a list of words they could’ve got.

Today’s task

That sure is a lot! For today let’s focus on setting up the grid by rolling the dice, and showing that to the player.

Rolling the dice

We could set up the grid just by setting each element in a 2d array to a random letter of the alphabet, however in real life, the Boggle grid is set by rolling 16 6-sided dice, each with a single letter on each face (apart from “Qu”, which goes on a single face).

dice = [
["R", "I", "F", "O", "B", "X"],
["I", "F", "E", "H", "E", "Y"],
["D", "E", "N", "O", "W", "S"],
["U", "T", "O", "K", "N", "D"],
["H", "M", "S", "R", "A", "O"],
["L", "U", "P", "E", "T", "S"],
["A", "C", "I", "T", "O", "A"],
["Y", "L", "G", "K", "U", "E"],
["Qu", "B", "M", "J", "O", "A"],
["E", "H", "I", "S", "P", "N"],
["V", "E", "T", "I", "G", "N"],
["B", "A", "L", "I", "Y", "T"],
["E", "Z", "A", "V", "N", "D"],
["R", "A", "L", "E", "S", "C"],
["U", "W", "I", "L", "R", "G"],
["P", "A", "C", "E", "M", "D"],
]
rolls = dice.map do |die|
die[rand(6)]
end
rolls = dice.shuffle.map do |die|
die.sample
end
p rolls
# => ["F", "U", "S", "D", "S", "K", "E", "P", "R", "O", "O", "V", "I", "Qu", "U", "H"]

Making the grid

Right now, rolls is a list, not a grid, so let’s convert it into a 2d array:

grid = []
grid.push(rolls[0,4])
grid.push(rolls[4,4])
grid.push(rolls[8,4])
grid.push(rolls[12,4])
p grid
# => [["F", "U", "S", "D"], ["S", "K", "E", "P"], ["R", "O", "O", "V"], ["I", "Qu", "U", "H"]]
rolls.each_slice(4) do |slice|
p slice
end
# =>
["F", "U", "S", "D"]
["S", "K", "E", "P"]
["R", "O", "O", "V"]
["I", "Qu", "U", "H"]
grid = rolls.each_slice(4).to_a
p grid
# => [["F", "U", "S", "D"], ["S", "K", "E", "P"], ["R", "O", "O", "V"], ["I", "Qu", "U", "H"]]
def roll_dice
dice = [
["R", "I", "F", "O", "B", "X"],
["I", "F", "E", "H", "E", "Y"],
["D", "E", "N", "O", "W", "S"],
["U", "T", "O", "K", "N", "D"],
["H", "M", "S", "R", "A", "O"],
["L", "U", "P", "E", "T", "S"],
["A", "C", "I", "T", "O", "A"],
["Y", "L", "G", "K", "U", "E"],
["Qu", "B", "M", "J", "O", "A"],
["E", "H", "I", "S", "P", "N"],
["V", "E", "T", "I", "G", "N"],
["B", "A", "L", "I", "Y", "T"],
["E", "Z", "A", "V", "N", "D"],
["R", "A", "L", "E", "S", "C"],
["U", "W", "I", "L", "R", "G"],
["P", "A", "C", "E", "M", "D"],
]
rolls = dice.shuffle.map do |die|
die.sample
end

grid = rolls.each_slice(4).to_a
end
new_grid = roll_dice
def roll_dice
dice = [
["R", "I", "F", "O", "B", "X"],
....
["P", "A", "C", "E", "M", "D"],
]
dice.shuffle.map{ &:sample }.each_slice(4).to_a
end
new_grid = roll_dice
p new_grid
# => [["F", "U", "S", "D"], ["S", "K", "E", "P"], ["R", "O", "O", "V"], ["I", "Qu", "U", "H"]]

Displaying the grid

At the moment the grid isn’t very nice to look at, what with it being full of braces and quote marks, and it being on one line and all. It would be much nicer if it could look like this:

+-----+-----+-----+-----+
| F | U | S | D |
+-----+-----+-----+-----+
| S | K | E | P |
+-----+-----+-----+-----+
| R | O | O | V |
+-----+-----+-----+-----+
| I | Qu | U | H |
+-----+-----+-----+-----+
puts "+-----+-----+-----+-----+"
puts "| #{grid[0][0]} | #{grid[0][1]} | #{grid[0][2]} | #{grid[0][3]} |"
puts "+-----+-----+-----+-----+"
puts "| #{grid[1][0]} | #{grid[1][1]} | #{grid[1][2]} | #{grid[1][3]} |"
puts "+-----+-----+-----+-----+"
puts "| #{grid[2][0]} | #{grid[2][1]} | #{grid[2][2]} | #{grid[2][3]} |"
puts "+-----+-----+-----+-----+"
puts "| #{grid[3][0]} | #{grid[3][1]} | #{grid[3][2]} | #{grid[3][3]} |"
puts "+-----+-----+-----+-----+"
puts row = '+-----+-----+-----+-----+'

grid.each do |line|
puts "| #{line.join(' | ')} |"
puts row
end
# =>
+-----+-----+-----+-----+
| F | U | S | D |
+-----+-----+-----+-----+
| S | K | E | P |
+-----+-----+-----+-----+
| R | O | O | V |
+-----+-----+-----+-----+
| I | Qu | U | H |
+-----+-----+-----+-----+
puts row = '+-----+-----+-----+-----+'

grid.each do |line|
puts "| #{line.map{|c| c.ljust(2)}.join(' | ')} |"
puts row
end
# =>
+-----+-----+-----+-----+
| F | U | S | D |
+-----+-----+-----+-----+
| S | K | E | P |
+-----+-----+-----+-----+
| R | O | O | V |
+-----+-----+-----+-----+
| I | Qu | U | H |
+-----+-----+-----+-----+
def display(grid)
puts row = '+-----+-----+-----+-----+'
grid.each do |line|
puts "| #{line.map{|c| c.ljust(2)}.join(' | ')} |"
puts row
end
end

Getting classy

What if we want to have more than one game on the go? Let’s refactor everything into a class called Boggle:

class Boggle
def initialize
@grid = @@dice.shuffle.map{ &:sample }.each_slice(4).to_a
end

def display
puts row = '+-----+-----+-----+-----+'
@grid.each do |line|
puts "| #{line.map { |c| c.ljust(2)}.join(" | ")} |"
puts row
end
end
@@dice = [
['R', 'I', 'F', 'O', 'B', 'X'],
['I', 'F', 'E', 'H', 'E', 'Y'],
['D', 'E', 'N', 'O', 'W', 'S'],
['U', 'T', 'O', 'K', 'N', 'D'],
['H', 'M', 'S', 'R', 'A', 'O'],
['L', 'U', 'P', 'E', 'T', 'S'],
['A', 'C', 'I', 'T', 'O', 'A'],
['Y', 'L', 'G', 'K', 'U', 'E'],
['Qu', 'B', 'M', 'J', 'O', 'A'],
['E', 'H', 'I', 'S', 'P', 'N'],
['V', 'E', 'T', 'I', 'G', 'N'],
['B', 'A', 'L', 'I', 'Y', 'T'],
['E', 'Z', 'A', 'V', 'N', 'D'],
['R', 'A', 'L', 'E', 'S', 'C'],
['U', 'W', 'I', 'L', 'R', 'G'],
['P', 'A', 'C', 'E', 'M', 'D'],
]
end
new_game = Boggle.new
new_game.display
# =>
+-----+-----+-----+-----+
| F | U | S | D |
+-----+-----+-----+-----+
| S | K | E | P |
+-----+-----+-----+-----+
| R | O | O | V |
+-----+-----+-----+-----+
| I | Qu | U | H |
+-----+-----+-----+-----+

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Harry Turnbull

Harry Turnbull

13 Followers

Improviser with Gamez Improv, Improbotics, The Nursery Theatre and Hoopla Impro. Learning to program properly with Makers Academy.