Robot in a Grid

This past weekend my wife and I spent time looking for protein-rich groceries. We were attempting to get those and additional items for a few families that wish to stay in lockdown despite the governor of Minnesota starting to relax the lockdown. The current orders went into effect last Saturday April 18, 2020 at midnight. Among the main items is that people in the state can go golfing and shooting in open ranges. Both Saturday and Sunday were sunny days. We could tell for the number of cars and people walking the dogs, jogging and just strolling that many people took advantage of the new rules.

On our side we will continue to try stay home as much as we can by limiting trips to the grocery stores and continue to use Jitsi to communicate more often with family and friends.

Today in the news it seems that many companies in the USA, Japan and Australia are in the process of moving their supply chains out of China. Hopefully all countries will follow suit. The cost to humanity for COVID-19 is incredibly high in lives and economies. We as humans regardless of nationalities or race should tell our governments to take the necessary steps to prevent this from happening again.

On a separate note, last week I finished an Azure course. It provided me with a full overview of most of the services offered by the Azure cloud. Will start today with the free courses on C# offered on Azure. I also signed up for a course on Python for machine learning offered by Stanford University. You can take the course for free if you just audit. I decided to take it for under $200 USD. That will give me a certificate when I satisfactorily complete the course. It should take 7 weeks to complete it. Currently I am on the first week.

I decided to take a look at a recursion problem 8.2 Robot in a Grid from the book “Cracking the Coding Interview” by Gayle Laakmann McDowell. If interested take a look at the requirements and suggested approach. I currently own a copy of version 6 of the book.

Just in case you do not have access to a copy of the book the edited requirements follow:

“Imagine a robot sitting on the upper left corner of a grid with r rows and c columns. The robot can only move in two directions, right and down, but certain cells are “off limits” such that the robot cannot step on them. Design and implement an algorithm (I will use Java) to find the path for the robot from the top left to the bottom right corners.”

Microsoft Windows [Version 10.0.18363.778]
(c) 2019 Microsoft Corporation. All rights reserved.

# **** ****
C:\Users\johnc>dir
04/16/2020  02:31 PM    <DIR>          .
04/16/2020  02:31 PM    <DIR>          ..
09/22/2019  08:00 AM    <DIR>          .eclipse
03/15/2020  08:38 AM                59 .gitconfig
04/07/2020  10:38 AM    <DIR>          .p2
04/16/2020  02:31 PM    <DIR>          .pylint.d
09/22/2019  08:00 AM    <DIR>          .tooling
03/10/2020  03:53 PM    <DIR>          .vscode
03/10/2020  04:43 PM    <DIR>          3D Objects
03/10/2020  04:43 PM    <DIR>          Contacts
04/08/2020  07:59 AM    <DIR>          ContainsCloseNums
02/17/2020  03:52 PM    <DIR>          Documents
04/17/2020  09:22 AM    <DIR>          Downloads
09/22/2019  07:57 AM    <DIR>          eclipse
09/22/2019  07:59 AM    <DIR>          eclipse-workspace_0
03/10/2020  04:43 PM    <DIR>          Favorites
03/10/2020  04:44 PM    <DIR>          Links
03/10/2020  04:43 PM    <DIR>          Music
09/12/2019  04:36 PM    <DIR>          NCH Software Suite
04/19/2020  07:12 AM    <DIR>          OneDrive
03/10/2020  04:43 PM    <DIR>          Saved Games
03/10/2020  04:43 PM    <DIR>          Searches
03/10/2020  04:43 PM    <DIR>          Videos
04/15/2020  01:45 PM    <DIR>          workspace0

# **** ****
C:\Users\johnc>cd workspace0

# **** ****
C:\Users\johnc\workspace0>dir
04/15/2020  01:45 PM    <DIR>          .
04/15/2020  01:45 PM    <DIR>          ..
04/01/2020  08:57 AM    <DIR>          BST-Search
04/10/2020  11:34 AM    <DIR>          ComposeRanges
04/08/2020  08:04 AM    <DIR>          ContainsCloseNums
04/05/2020  08:32 AM    <DIR>          GraphBFS
04/17/2020  03:41 PM    <DIR>          MissingNumber
03/29/2020  08:30 AM    <DIR>          ORCost
03/10/2020  04:24 PM    <DIR>          SortOnFrequency

# **** ****
C:\Users\johnc\workspace0>git clone https://github.com/JohnCanessa/RobotInAGrid.git
Cloning into 'RobotInAGrid'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 793 bytes | 5.00 KiB/s, done.

# **** ****
C:\Users\johnc\workspace0>dir
04/19/2020  07:42 AM    <DIR>          .
04/19/2020  07:42 AM    <DIR>          ..
04/01/2020  08:57 AM    <DIR>          BST-Search
04/10/2020  11:34 AM    <DIR>          ComposeRanges
04/08/2020  08:04 AM    <DIR>          ContainsCloseNums
04/05/2020  08:32 AM    <DIR>          GraphBFS
04/17/2020  03:41 PM    <DIR>          MissingNumber
03/29/2020  08:30 AM    <DIR>          ORCost
04/19/2020  07:42 AM    <DIR>          RobotInAGrid
03/10/2020  04:24 PM    <DIR>          SortOnFrequency

# **** ****
C:\Users\johnc\workspace0>cd RobotInAGrid

# **** ****
C:\Users\johnc\workspace0\RobotInAGrid>dir
04/19/2020  07:42 AM    <DIR>          .
04/19/2020  07:42 AM    <DIR>          ..
04/19/2020  07:42 AM               308 README.md

# **** ****
C:\Users\johnc\workspace0\RobotInAGrid>type README.md
# RobotInAGrid
Problem from Cracking the Coding Interview solved in Java

The book contains the sulutions to all problems.
This code is what I came up without using the proposed solution.

For additional info please take a look at the follwoing post in my blog:

T.B.D.

Thanks and enjoy!

John

I opened a command prompt on a Windows 10 computer. Navigated to a folder and cloned a GitHub repository I created a few minutes before to hold the project at hand. As you can see I included a README.md file which I will update with the URI to this post after it in on-line in my WordPress blog.

1 3
o o o

main <<< rows: 1 cols: 3
main <<< maze:
o o o
main <<< path: [(0,0), (0,1), (0,2)]
main <<< path: [(0,0), (0,1), (0,2)]

2 3
o o o 
o o o

main <<< rows: 2 cols: 3
main <<< maze:
o o o
o o o
main <<< path: [(0,0), (1,0), (1,1), (1,2)]
main <<< path: [(0,0), (0,1), (0,2), (1,2)]

4 5
o o o x o
x o x o o
o o x o o
o o o o o

main <<< rows: 4 cols: 5
main <<< maze:
o o o x o
x o x o o
o o x o o
o o o o o
main <<< path: [(0,0), (0,1), (1,1), (2,1), (3,1), (3,2), (3,3), (3,4)]
main <<< path: [(0,0), (0,1), (1,1), (2,1), (3,1), (3,2), (3,3), (3,4)]  

4 5
o o o x o
x o x o o
o o x o o
o o o x o

main <<< rows: 4 cols: 5
main <<< maze:
o o o x o
x o x o o
o o x o o
o o o x o
main <<< path: []
main <<< path: []

5 5
o o o o o
o o o o o
o o o o o
o o o o o
o o o o o

main <<< rows: 5 cols: 5
main <<< maze:
o o o o o
o o o o o
o o o o o
o o o o o
o o o o o
main <<< path: [(0,0), (1,0), (2,0), (3,0), (4,0), (4,1), (4,2), (4,3), (4,4)]
main <<< path: [(0,0), (0,1), (0,2), (0,3), (0,4), (1,4), (2,4), (3,4), (4,4)]


5 5
o o x o o
o o o o o
x o x o x
o o o o o
o o x o o

main <<< rows: 5 cols: 5
main <<< maze:
o o x o o
o o o o o
x o x o x
o o o o o
o o x o o
main <<< path: [(0,0), (1,0), (1,1), (2,1), (3,1), (3,2), (3,3), (4,3), (4,4)]
main <<< path: [(0,0), (0,1), (1,1), (1,2), (1,3), (2,3), (3,3), (3,4), (4,4)]

I come up with some test cases. The idea is to have the number of rows and columns for the grid in the first line. The following lines contain a row per line. A ‘o’ indicates that the cell is empty and can be used by the robot while an ‘x’ indicates that the cell is “off limits” so the robot must avoid them.

The idea is to display the path from the top left to the bottom right if one exists. In the examples all but one has a path. There is a second path. The second path may or may not be the same but are equivalent. The first path will be generated in a bottom up approach. The second path will be generated using a top down approach. Unless I am mistaken, the book only illustrates the first approach.

Note that for testing purposes I displayed the row and column counts and the maze (or grid) followed by two equivalent paths.

    /**
     * Test scaffolding.
     * The description does not describe the input format.
     * Specifically it talks about "off limit" cells.
     * Grid cells set to 'o' are open; otherwise are set to 'x' to indicate "off limit".
     */
    public static void main(String[] args) {
        
        // **** open scanner ****
        Scanner sc = new Scanner(System.in);

        // **** read number of rows in the grid ****
        String[] rc = sc.nextLine().trim().split(" ");
        int rows = Integer.parseInt(rc[0]);
        int cols = Integer.parseInt(rc[1]);

        // ???? ????
        System.out.println("main <<< rows: " + rows + " cols: " + cols);

        // **** declare and initialize the maze ****
        Boolean[][] maze = new Boolean[rows][];
        for (int r = 0; r < rows; r++) {
            maze[r] = new Boolean[cols];
        }

        // **** read the contents of the maze ****
        for (int r = 0; r < rows; r++) {
            String[] cs = sc.nextLine().trim().split(" ");
            for (int c = 0; c < cols; c++) {
                maze[r] = cs.equals("o") ? true : false;
            }
        }

        // **** close scanner ****
        sc.close();

        // ???? display the maze ????
        System.out.println("main <<< maze: ");
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                System.out.print(((maze[r] == true) ? "o": "x") + " ");
            }
            System.out.println();
        }

        // **** get the path from the top left corner to the bottom right (down up approach) ****
        ArrayList<Cell> path = getPath(maze);

        // **** display the path (if found) ****
        if (path != null)
            System.out.println("main <<< path: " + path.toString());

        // **** get the path from the top left corner to the bottom right (top down approach) ****
        Stack<Cell> pd = getPathTD(maze);

        // **** display the path (if found) ****
        if (path != null)
            System.out.println("main <<< path: " + pd.toString());
    }

We open a scanner to read data from the console. We start by processing the number or rows and columns. We then declare and populate the maze using the following lines. When done we close the scanner.

Just to be sure we have the proper maze, we display it.

We then generate a path from the top left corner to the bottom right corner. The first approach uses a bottom up approach. We then generate a second path (which may be identical or not). This second path uses a top – bottom approach. In both cases we display the generated paths. Both should display paths from the upper left corner to the bottom right corner.

/**
 * Use to reprecent a cell in the maze.
 */
class Cell {

    // **** ****
    public int r;
    public int c;


    /**
     * Constructor
     */
    public Cell(int r, int c) {
        this.r = r;
        this.c = c;
    }


    /**
     * Return string representation of this cell.
     */
    @Override
    public String toString() {
        return "(" + this.r + "," + this.c + ")";
    }
}

The Cell class is used to represent cells in the maze. A cell contains the associated row and column in the maze. We have a constructor and a method to generate a string for the cell.

    /**
     * Entry function to get the path from the end back to the start cell.
     * Using bottom up approach.
     */
    static ArrayList<Cell> getPath(Boolean[][] maze) {

        // **** check start condition ****
        if ((maze == null) || (maze.length == 0)) {
            return null;
        }

        // **** used to hold the path ****
        ArrayList<Cell> path = new ArrayList<Cell>();

        // **** start recursion (from the bottom right corner) ****
        if (getPath(maze, maze.length - 1, maze[0].length - 1, path) != null)
            return path;

        // **** path NOT found ****
        return null;
    }

This is the entry point for the bottom-up approach to generate the path. We perform a check and generate an array list to hold the path. The process starts from the lower right corner. If a path is found the method returns a path expressed as an array list. If the method returns false, then this method return a null value for the path.

   /**
     * Recursive procedure to get from s (top left corner) to e (bottom right corner).
     * Using bottom up approach.
     */
    static Boolean getPath(Boolean[][] maze, int row, int col, ArrayList<Cell> path) {

        // **** check for cell out of bounds or not available (moving left and up only) ****
        if ((col < 0) || (row < 0) || !maze[row][col]) {
            return false;
        }

        // **** recursive call(s) ****
        if (row == 0 && col == 0 ||                     // upper left corner
            getPath(maze, row, col - 1, path) ||        // up
            getPath(maze, row - 1, col, path)) {        // left

                // **** add this cell to the path ****
                Cell p = new Cell(row, col);
                path.add(p);

                // **** path found (end condition) ****
                return true;
        }

        // **** path NOT found (keep on recursing) ****
        return false;
    }

This recursive method checks if it is being called with coordinates outside of the maze. Note that the check is for columns and rows less than 0. The test is all we need since this function only makes moves up and left.

The recursive calls are performed until the upper top corner is reached. At that point we add the cell to the path and return true. If the path is not found the method returns false.

    /**
     * Entry function to get the path from the end back to the start cell.
     * Using top down approach.
     */
    static Stack<Cell> getPathTD(Boolean[][] maze) {

        // **** check start condition ****
        if ((maze == null) || (maze.length == 0)) {
            return null;
        }

        // **** used to hold the path ****
        Stack<Cell> path = new Stack<Cell>();

        // **** start recursion (from the top left corner) ****
        if (getPathTD(maze, 0, 0, path) != null)
            return path;

        // **** path NOT found ****
        return null;
    }

This method performs the same check as getPath. We then initialize a stack to hold the cells in the path. We are now ready to call the recursive method starting with the top left cell.

If the recursive call returns true the path in the stack is returned; otherwise we return a null path indicating that a path was not found.

    /**
     * Recursive procedure to get from s (top left corner) to e (bottom right corner).
     * Using top down apporoach.
     */
    static Boolean getPathTD(Boolean[][] maze, int row, int col, Stack<Cell> path) {

        // **** check for cell out of bounds or not available (moving right and down only) ****
        if ((col > maze[0].length - 1) || (row > maze.length - 1) || !maze[row][col]) {
            return false;
        }

        // **** add this cell to the path ****
        Cell p = new Cell(row, col);
        path.add(p);

        // **** ****
        if (row == maze.length - 1 && col == maze[0].length - 1)    // lower right corner
            return true;

        // **** recursion ****
        Boolean found = false;

        // **** ****
        found = getPathTD(maze, row, col + 1, path);                // right
        if (found)
            return true;
            
        // **** ****
        found = getPathTD(maze, row + 1, col, path);                // down
        if (found)
            return true;

        // **** remove this cell from the path ****
        path.pop();

        // **** path NOT found (keep on recursing) ****
        return false;
    }

We check to make sure we are in a valid cell in the maze. Note that in this approach we only move right and down.

We allocate a cell for the current position in the maze and add it to the path.

We check if we have reached the bottom right cell. If so we return true.

Now we make a recursive call to the right cell. The returned value is checked. If we found a path we just return true.

If we get to this point in the code we make a recursive call down. If we find a path we return true.

If we did not get a true from either recursive call, the path has not been able to reach the lower right corner, so we remove from the path the current cell that was added before making the recursive calls.

The function returns false.

# **** ****
C:\Users\johnc>dir
04/16/2020  02:31 PM    <DIR>          .
04/16/2020  02:31 PM    <DIR>          ..
09/22/2019  08:00 AM    <DIR>          .eclipse
03/15/2020  08:38 AM                59 .gitconfig
04/07/2020  10:38 AM    <DIR>          .p2
04/16/2020  02:31 PM    <DIR>          .pylint.d
09/22/2019  08:00 AM    <DIR>          .tooling
03/10/2020  03:53 PM    <DIR>          .vscode
03/10/2020  04:43 PM    <DIR>          3D Objects
03/10/2020  04:43 PM    <DIR>          Contacts
04/08/2020  07:59 AM    <DIR>          ContainsCloseNums
02/17/2020  03:52 PM    <DIR>          Documents
04/19/2020  07:48 AM    <DIR>          Downloads
09/22/2019  07:57 AM    <DIR>          eclipse
09/22/2019  07:59 AM    <DIR>          eclipse-workspace_0
03/10/2020  04:43 PM    <DIR>          Favorites
03/10/2020  04:44 PM    <DIR>          Links
03/10/2020  04:43 PM    <DIR>          Music
09/12/2019  04:36 PM    <DIR>          NCH Software Suite
04/20/2020  07:22 AM    <DIR>          OneDrive
03/10/2020  04:43 PM    <DIR>          Saved Games
03/10/2020  04:43 PM    <DIR>          Searches
03/10/2020  04:43 PM    <DIR>          Videos
04/19/2020  07:42 AM    <DIR>          workspace0

# **** ****
C:\Users\johnc>cd workspace0

# **** ****
C:\Users\johnc\workspace0>dir
04/19/2020  07:42 AM    <DIR>          .
04/19/2020  07:42 AM    <DIR>          ..
04/01/2020  08:57 AM    <DIR>          BST-Search
04/10/2020  11:34 AM    <DIR>          ComposeRanges
04/08/2020  08:04 AM    <DIR>          ContainsCloseNums
04/05/2020  08:32 AM    <DIR>          GraphBFS
04/17/2020  03:41 PM    <DIR>          MissingNumber
03/29/2020  08:30 AM    <DIR>          ORCost
04/19/2020  07:46 AM    <DIR>          RobotInAGrid
03/10/2020  04:24 PM    <DIR>          SortOnFrequency

# **** ****
C:\Users\johnc\workspace0>cd RobotInAGrid

# **** check that we are using th eproper GitHub repo ****
C:\Users\johnc\workspace0\RobotInAGrid>git remote -v
origin  https://github.com/JohnCanessa/RobotInAGrid.git (fetch)
origin  https://github.com/JohnCanessa/RobotInAGrid.git (push)

# **** ****
C:\Users\johnc\workspace0\RobotInAGrid>git status
On branch master
Your branch is up to date with 'origin/master'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        Solution.java

nothing added to commit but untracked files present (use "git add" to track)

# **** ****
C:\Users\johnc\workspace0\RobotInAGrid>git add Solution.java

# **** ****
C:\Users\johnc\workspace0\RobotInAGrid>git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   Solution.java

# **** commit changes ****
C:\Users\johnc\workspace0\RobotInAGrid>git commit -m "top down and down top approaches"
[master be72aa4] top down and down top approaches
 1 file changed, 213 insertions(+)
 create mode 100644 Solution.java

# **** ****
C:\Users\johnc\workspace0\RobotInAGrid>git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

# **** push changes to GitHub ****
C:\Users\johnc\workspace0\RobotInAGrid>git push origin master
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 1.82 KiB | 1.82 MiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/JohnCanessa/RobotInAGrid.git
   a980f25..be72aa4  master -> master

# **** ****
C:\Users\johnc\workspace0\RobotInAGrid>git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

# **** check the log ****
C:\Users\johnc\workspace0\RobotInAGrid>git log
commit be72aa433cf34548ce23d20b2dd7925514026b6d (HEAD -> master, origin/master, origin/HEAD)
Author: JohnCanessa <john.canessa@gmail.com>
Date:   Mon Apr 20 14:45:12 2020 -0500

    top down and down top approaches

commit a980f253e5c03b2a4bc01bcd3bb3cdabe15dc40d
Author: John Canessa <8019504+JohnCanessa@users.noreply.github.com>
Date:   Sun Apr 19 07:41:50 2020 -0500

    Create README.md

We check we are using the proper remote repository in GitHub. Commit the changes and push them to GitHub.

The entire code for this project can be found in my GitHub repository.

If you have comments or questions regarding this, or any other post in this blog, or if you would like for me to serve of assistance with any phase in the SDLC (Software Development Life Cycle) of a project associated with a product or service, please do not hesitate and leave me a note below. If you prefer, send me a private message using the following address:  john.canessa@gmail.com. I will reply as soon as possible.

Keep on reading and experimenting. It is the best way to learn, refresh your knowledge and enhance your developer toolset!

One last thing, thanks to all 630 subscribers to my blog!!!

John

Twitter:  @john_canessa

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.