Skip to Main Content

Data Structures

Computer Science Department

Braille Translator – 100 course points

This assignment will take MORE time to complete than the previous assignment. 

Start your assignment early! You need time to understand the assignment and to answer the many questions that will arise as you read the description and the code provided.

Refer to our Programming Assignments FAQ for instructions on how to install VSCode, how to use the command line and how to submit your assignments.

  • See this video on how to import the project into VSCode and how to submit into Autolab.

The assignment has two components:

  1. Coding (97 points) submitted through Autolab.
  2. Reflection (3 points) submitted through a form.
    • Submit the reflection AFTER you have completed the coding component.
    • Be sure to sign in with your RU credentials! (netid@scarletmail.rutgers.edu)
    • You cannot resubmit reflections but you can edit your responses before the deadline by clicking the Google Form link, signing in with their netid, and selecting “Edit your response”

Overview

Braille is a tactile writing system used by people with visual impairments. It was developed by Louis Braille in the early 19th century and consists of patterns of raised dots arranged in cells. Each cell contains up to six dots, representing letters, numbers, and punctuation. People read Braille by feeling these dots with their fingertips, allowing them to access written information through touch. 

A BST can be inefficient to store all keys and values — depending on insertion order, we can face runtimes of up to O(n) when inserting or searching. However, we can reduce this to constant time by using a binary tree with adaptations (this is NOT a BST). 

We can represent Braille characters vs. English characters as a tree of encodings — the root of a tree contains elements that “start with” a certain encoding. Our encoding algorithm is modified from how cells are actually numbered in Braille: we traverse row by row; R indicates a raised dot and L indicates a lowered dot. We will provide you with the encodings in all input files! This assignment will only cover translating lowercase, alphabetical characters whose encodings we will provide. Braille consists of much more than these letters and encodings; we’re omitting abbreviations and shorthand for simplicity’s sake.
  
 
As a reminder, bear in mind that this is NOT a binary search tree — a BST takes O(n) time to search for a given node if we know its encoding depending on order of insertion, but storing encodings at each level allows us to “translate” Braille to English in tilde 6 time (there are only 6 dots to encode lowercase characters). However, you STILL traverse left or right given a certain comparison, and you’ll still use tree traversals.
 
Here’s what the tree looks like when we encode a, s, and t. At each level, we store an encoding (ex: RRR) and the “next” nodes that we can have (either left/lowered, L and/or right/raised, R, these nodes can be encoded RRRL or RRRR). At each leaf, we store the character itself as well as its encoding — characters can only be placed at leaves.
  • R indicates a raised dot and L indicates a lowered dot

Implementation

Overview of files provided

  • The Symbol class allows you to create instances of Symbols; it stores information like the lowercase character it represents and the Braille encoding (as a String). Do not edit or submit to Autolab.
  • The BrailleTranslator class contains methods that manipulate trees in order to create a Braille encoding tree and translate from English to Braille and vice versa. Edit the empty methods with your solution, but DO NOT edit the provided ones or the method signatures of any method. This is the file you submit.
  • The TreeNode class represents a binary tree node; it contains a Symbol as its data and TreeNodes “left” and “right” representing the left and right subtrees of the tree. Getters and setters are provided. Do not edit or submit to Autolab.
  • StdIn and StdOut are libraries that handle input and output. Do not edit these classes.
  • Multiple input files are included; they store different letters and their Braille encodings (sequences of L’s and R’s). You are welcome to create your own input files, as they will not be submitted to Autolab.
For the Driver, most of the code is already implemented for you; you are welcome to update this class so that you can test your code and test edge cases. 

BrailleTranslator.java

  • DO NOT add new import statements.
  • DO NOT change any of the method’s signatures.

Methods to be implemented by you:

createSymbolTree (provided)

This method reads symbols from an input file and calls the following two methods to create a Symbol object and insert it into the tree rooted at treeRoot.

You are given input files containing different characters as well as their Braille encodings. They are formatted as follows:

  • One line specifying the number of characters, n
  • n lines with a character and its corresponding Braille encoding, space-separated

Do not update this method, it has been implemented for you. IMPLEMENT the first two methods (readSingleEncoding and addCharacter).

1. readSingleEncoding

This method does not take in any parameters. It reads ONE character and returns a symbol containing the character and encoding.
You can do the following to read one character:

  • Call StdIn.readChar() to read the character. You must use StdIn.readChar().
  • Call StdIn.readString() to read the encoding.
  • Call StdIn.readLine() to read the newline character \n.
Remember that the input file contains one line with the number of characters, and then n lines each with one character and one encoding (space separated). 

Return a Symbol object containing the character AND encoding. This will be added as a leaf node in the next method.

Test this using createSymbolTree AFTER you have implemented the next method. Use the debugger in VS Code to verify that symbols are being read and created correctly on each iteration of this method

EARLY SUBMISSION CONSISTS OF THIS METHOD AND addCharacter. Make sure you submit BOTH methods for early submission; we will not make exceptions if you only implement this method.

2. addCharacter

This method inserts a symbol into the tree rooted at treeRoot. Given the encoding from the symbol object, trace the encoding path (L = left, R = right), starting with an empty root. The Symbol object is the leaf, but you’ll need to insert its partial encodings as well. 

First, use .getEncoding() on the symbol in parameters to get the encoding you’ll need. You’ll insert its partial encodings as well as the leaf (character node) itself. Here’s how (you should also refer to the note below):

  1. ONLY leaf nodes have Symbol objects with characters. Initialize intermediate nodes with Character.MIN_VALUE characters and the partial encodings — the constructors below will handle the expected logic.
  2. Create intermediate nodes along the encoding path if nodes do not exist; do not put any characters in them. USE the (String encoding) constructor for symbols: new Symbol(partialEncoding). You should keep track of a partial encoding – the root has an empty string, and its left or right children would have either “L” or “R” as encodings.
    1. Note that in the example below, the next character is appended to the previous “partial” encoding. For instance, if the current node is “RL” and an L is the next character, the resulting string is “RLLL”. 
  3. The last digit of the encoding represents the position (left or right) of the character with respect to its parent. Characters are at level 6 on the tree (starting from level 0, the empty root). Use the new Symbol(character, encoding) constructor for leaves.
NOTE
  • The tree is made of TreeNodes, but note that this is NOT a binary search tree (BST).
  • A TreeNode contains a Symbol object
    • The TreeNode’s Symbol at the root has the empty String for encoding.
    • The TreeNode’s Symbol at the leaf level contains a character and an encoding. This represents a lowercase letter.
      • Use new Symbol(character, fullEncoding) to initialize a leaf symbol at this level.
      • The TreeNode’s Symbol at other levels (the intermediate nodes) contain a character with Character.MIN_VALUE and the partial encoding. Each encoding for non-leaves has the path we took.
      • Use new Symbol(partialEncoding) to initialize a symbol at these levels.
Here’s a step-by-step trace of inserting “A” into the tree if it is the first node. Note that in step 0, the root is an empty string — we haven’t gone left or right; there’s no encoding to store. The root is initialized using new Symbol(“”); – there IS a symbol at the root with an empty string. 

To test this method, use the provided createSymbolTree method.

Early submission. Implement the first two methods for extra credit: readSingleEncoding() and addCharacter(). We will not make exceptions if you only implement one method.

 
The resulting tree for small.in should look like this:
+--- ""
     |+R- R 
     |    |+R- RR 
     |    |    |+R- null
     |    |    --L- RRL 
     |    |         |+R- RRLR 
     |    |         |    |+R- null
     |    |         |    --L- RRLRL 
     |    |         |         |+R- null
     |    |         |         --L- d -> RRLRLL
     |    |         --L- RRLL 
     |    |              |+R- null
     |    |              --L- RRLLL 
     |    |                   |+R- null
     |    |                   --L- c -> RRLLLL
     |    --L- RL 
     |         |+R- RLR 
     |         |    |+R- null
     |         |    --L- RLRL 
     |         |         |+R- null
     |         |         --L- RLRLL 
     |         |              |+R- null
     |         |              --L- b -> RLRLLL
     |         --L- RLL 
     |              |+R- RLLR 
     |              |    |+R- RLLRR 
     |              |    |    |+R- null
     |              |    |    --L- o -> RLLRRL
     |              |    --L- null
     |              --L- RLLL 
     |                   |+R- null
     |                   --L- RLLLL 
     |                        |+R- null
     |                        --L- a -> RLLLLL
     --L- L 
          |+R- LR 
          |    |+R- LRR 
          |    |    |+R- LRRR 
          |    |    |    |+R- LRRRR 
          |    |    |    |    |+R- null
          |    |    |    |    --L- t -> LRRRRL
          |    |    |    --L- null
          |    |    --L- LRRL 
          |    |         |+R- LRRLR 
          |    |         |    |+R- null
          |    |         |    --L- s -> LRRLRL
          |    |         --L- null
          |    --L- null
          --L- null
 

3. getSymbolNode

Given a Braille encoding, traverse the tree to find its corresponding English character (at a leaf node) and return the tree node if it does, or return null if it doesn’t exist at all. 

  • Trace the encoding path as before (L = left, R = right), starting from the tree root.
  • Hint: s.charAt(i) will get you the character at a certain string position i.
DO NOT create another object! You must return the character node that is in the tree – this is so you’ll be able to use the nodes later.
 
Here’s an example of trying to find the node corresponding to LRRRRL; this tree DIFFERS from your input files.
“LRRLRL” should correspond to “s” using small.in – RUN createSymbolTree in the driver using small.in before running this method. 

4. findBrailleEncoding

Given an English character, check the tree rooted at treeRoot to see if it is in the tree and return the encoding used to get to the character. Return null if the character does not exist.

  • Use a tree traversal to find the character – unlike the previous method, you are given an English character and need to find an encoding. You can’t trace the encoding path since you have the character as a parameter.
  • Once you find a node containing the character, return the encoding stored in it by using the symbol’s .getEncoding(). 
  • Implement this method recursively. You are encouraged to use helper methods, just make them private.
“s” should return LRRLRL using small.in. RUN createSymbolTree in the driver using small.in before running this method.

5. encodingsStartWith

This method finds the amount of characters (leaves) within the tree rooted at the starting encoding node (ex: where the first n dots match in order). One question we can answer with this method is, “how many Braille characters start with two raised dots?”. Remember that characters will be under partial encodings — for instance, the character a will be contained in RLL’s subtree. 

Here’s how to complete this method: 

  • Use getSymbolNode to find the TreeNode that corresponds to the starting encoding (call it startNode). If this method is implemented correctly, it should work for partial encoding nodes as well (ex: those that hold partial encodings but not characters; non-leaf nodes).
  • Nothing happens if the node doesn’t exist in the tree – return an empty ArrayList in this case.
  • Conduct a preorder traversal (ex: preorder(startNode)) from the start node to find all leaves under it, populate an ArrayList of Symbols that store all characters under the startNode, and return an ArrayList of Symbols representing characters. 
    • Traverse the root of a subtree, then recursively visit its left and right subtrees.
    • This is similar to the preOrder() method in your BST traversal lab.

Here are a few examples that DIFFER from your input files. This is just a smaller tree we’re using to visualize our algorithms.


In small.in, after running createSymbolTree in the driver, encodingsStartWith(“LR”) should return Symbols representing s and t, as seen below:

s: LRRLRL
t: LRRRRL

6. translateBraille

This method allows you to translate an encoding representing a word in Braille to English! You will be given an input file containing a single string of encoding(s). 

You will traverse each six-character chunk within a string and call getSymbolNode on the chunk to find the character the encoding corresponds to. Afterward, append the character to a resulting string and return it. 

ASSUME for this method that all input files will be valid, and all encodings will correspond to valid characters. NOTE: ALL translate files can be tested with letters.in, but translate1.in can ONLY be tested with letters.in, NOT small.in.

Remember that you are given an input file within the parameters of this method. You MUST set standard input to the file name using StdIn.setFile(input); (where input is the file name) and read the string in that file accordingly.

  • Hint: string.substring(beginning, end) will give you a portion of a string from the beginning index (inclusive) to the end index (non-inclusive). For RLLLLL, string.substring(0, 5) will give you RLLLL (removes last character). 

IMPORTANT: This method is called using the parameter “input” as the FILE NAME. You must read the input file inside the translateBraille method; not in the driver. Example of how this method will be called: translateBraille(“translate0.in”).


Here’s an example – we process characters six at a time; there are no spaces inside the input file but characters are spaced out for visualization purposes:

When testing translate0.in in the driver using small.in, you should get “bat” (without quotes). Run createSymbolTree before this method.

 

7. deleteSymbol

In this method, you’ll delete a character (leaf node) from the tree, as well as its intermediate nodes if they don’t lead to characters anymore. Use findBrailleEncoding to find the corresponding encoding, given the character.

Next, traverse the tree similarly to how you traversed in getSymbolNode — traverse the tree until you reach the target node. If the target node is a leaf, look at its parent node and identify which child it is of the parent (left or right). Unhook it from the parent. (Another alternate approach is described in the last section below.)

Afterward, you’ll need to look at all partial nodes that led to the node you just deleted; remove one character from the end of the encoding as you go. For instance, if you just deleted RLLLLL, you’ll need to look at RLLLL (we removed an L) and see if that node is a leaf, deleting it if it is. 

  • Hint: string.substring(beginning, end) will give you a portion of a string from the beginning index (inclusive) to the end index (non-inclusive). For RLLLLL, string.substring(0, 5) will give you RLLLL.

To complete this method:

  • Find the parent and target nodes to delete.
    • As stated above, you can traverse the tree similarly to how you traversed in getSymbolNode, and keep track of both variables.
    • OR you can use getSymbolNode(String encoding) to find the parent and target – remember that you can find the parent encoding by removing the last character from the longer one.
  • If the target node is a leaf, unhook it from its parent. 
  • Repeat this for each partial encoding (ex: if deleting s – LRRLRL, check LRRLR, LRRL, etc)
  • Make sure that if treeRoot (the instance variable) is a leaf, that it’s also deleted as well. 
By deleting “s” with small.in and after running createSymbolTree, the result is the following:
+--- "" 
     |+R- R 
     |    |+R- RR 
     |    |    |+R- null
     |    |    --L- RRL 
     |    |         |+R- RRLR 
     |    |         |    |+R- null
     |    |         |    --L- RRLRL 
     |    |         |         |+R- null
     |    |         |         --L- d -> RRLRLL
     |    |         --L- RRLL 
     |    |              |+R- null
     |    |              --L- RRLLL 
     |    |                   |+R- null
     |    |                   --L- c -> RRLLLL
     |    --L- RL 
     |         |+R- RLR 
     |         |    |+R- null
     |         |    --L- RLRL 
     |         |         |+R- null
     |         |         --L- RLRLL 
     |         |              |+R- null
     |         |              --L- b -> RLRLLL
     |         --L- RLL 
     |              |+R- RLLR 
     |              |    |+R- RLLRR 
     |              |    |    |+R- null
     |              |    |    --L- o -> RLLRRL
     |              |    --L- null
     |              --L- RLLL 
     |                   |+R- null
     |                   --L- RLLLL 
     |                        |+R- null
     |                        --L- a -> RLLLLL
     --L- L 
          |+R- LR 
          |    |+R- LRR 
          |    |    |+R- LRRR 
          |    |    |    |+R- LRRRR 
          |    |    |    |    |+R- null
          |    |    |    |    --L- t -> LRRRRL
          |    |    |    --L- null
          |    |    --L- null
          |    --L- null
          --L- null
 

Implementation Notes

  • YOU MAY only update the methods with the WRITE YOUR CODE HERE line. 
  • COMMENT all print statements you have written from BrailleTranslator.java
  • DO NOT add any instance variables to the BrailleTranslator class.
  • DO NOT add any public methods to the BrailleTranslator class.
  • DO NOT add/rename the project or package statements.
  • DO NOT change the class BrailleTranslator name.
  • YOU MAY add private methods to the BrailleTranslator class.
  • YOU MAY use any of the libraries provided in the zip file.
  • DO NOT use System.exit()

VSCode Extensions

You can install VSCode extension packs for Java. Take a look at this tutorial. We suggest:

Importing VSCode Project

  1. Download BrailleTranslator.zip from Autolab Attachments.
  2. Unzip the file by double clicking.
  3. Open VSCode
    • Import the folder to a workspace through File > Open

Executing and Debugging

  • You can run your program through VSCode or you can use the Terminal to compile and execute. We suggest running through VSCode because it will give you the option to debug.
  • How to debug your code
  • If you choose the Terminal:
    • first navigate to BrailleTranslator directory/folder
      • to compile:  javac -d bin src/braille/*.java
      • to execute: java -cp bin braille.Driver

Before submission

COMMENT all printing statements you have written from BrailleTranslator.java

Collaboration policy. Read our collaboration policy here.

Submitting the assignment. Submit BrailleTranslator.java separately via the web submission system called Autolab. To do this, click the Assignments link from the course website; click the Submit link for that assignment.

Early submission. Implement the first two methods for extra credit: readSingleEncoding() and addCharacter(). We will not make exceptions if you only implement one method.

Getting help

If anything is unclear, don’t hesitate to drop by office hours or post a question on Piazza.

  • Find instructors office hours here  
  • Find tutors office hours on Canvas -> Tutoring
  • Find head TAs office hours here 
  • In addition to office hours we have the CSL in Hill 252, a community space staffed with community managers who are undergraduate students further along the CS major to answer questions.

Further Notes

This is an implementation of a trie (prefix tree), where for each position leading to a character, we make a decision to go left or right based on whether the next dot is raised or lowered respectively. More formally, this is a binary trie (prefix tree) where Braille dots are encoded as a series of Ls and Rs, and encodings (partial or full) are stored at each level and node. While our trie simply stores Braille letters, a common application of tries is to store strings or words.

 

By Kal Pandit and Seth Kelley