Skip to content

TopographicMap.java

//RT
import acm.graphics.*;
import java.awt.Color;
import java.util.*;
import acm.program.*;


public class TopographicMap
{
    // private instance variables
    private double[][] mapData;
    private boolean valid;

    public TopographicMap(String fileName, int rows, int cols)
    {
        // you'll do this in task #1

        double[] data = FileHelper.readDataFromFile(fileName, rows * cols);
        if (data.length == 0){
            valid = false;
        } else {
            //use data to fill mapdata
            mapData = new double[rows][cols];
            int index = 0;
            for (int i = 0; i < rows; i++){
                for (int j = 0; j < cols; j++){
                    mapData[i][j] = data[index];
                    index++;
                }
            }
            valid = true;
        }
    }

    public boolean isValid() {return valid;}

    private double findMinimum()
    {
        double min = mapData[0][0];
        for (int i = 0; i < mapData.length; i++){
            for (int j = 0; j < mapData[i].length; j++){
                if (mapData[i][j] < min){
                    min = mapData[i][j];
                }
            }
        }
        return min; // just to get it to compile
    }

    private double findMaximum()
    {
        double max = mapData[0][0];
        for (int i = 0; i < mapData.length; i++){
            for (int j = 0; j < mapData[i].length; j++){
                if (mapData[i][j] > max){
                    max = mapData[i][j];
                }
            }
        }
        return max;
    }

    public void drawMap(GraphicsProgram graphics)
    {
        double min = this.findMinimum();
        double max = this.findMaximum();
        System.out.println(min + " " + max);
        for (int i = 0; i < mapData.length; i++){
            for (int j = 0; j < mapData[i].length; j++){
                double value = mapData[i][j];
                int gray = (int)((value - min) / (max - min) * 255);
                GRect rect = new GRect(j, i, 1, 1);
                rect.setColor(new Color(gray, gray, gray));
                rect.setFilled(true);
                graphics.add(rect);
            }
        }
    }

    public double drawLowestElevPath(GraphicsProgram graphics, int startRow, Color drawColor){
        double sum = 0;
        int currentRow = startRow;
        for (int c = 0; c < mapData[0].length - 1; c++){
            double[] choices = new double[3];
            if (currentRow == 0){ // change up
                choices[0] = Integer.MAX_VALUE;
            } else {
                choices[0] = Math.abs(mapData[currentRow][c] - mapData[currentRow - 1][c+1]);
            }
            choices[1] = Math.abs(mapData[currentRow][c] - mapData[currentRow][c + 1]); // change forward
            if (currentRow == mapData.length - 1){ // change down
                choices[2] = Integer.MAX_VALUE;
            } else {
                choices[2] = Math.abs(mapData[currentRow][c] - mapData[currentRow + 1][c+1]);
            }

            int choice;
            if (choices[1] <= choices[0] && choices[1] <= choices[2]){ // if right is good option
                choice = 1;
            } else if (choices[0] < choices[2]){ // if up is good option
                choice = 0;
            } else if (choices[2] < choices[1]){
                choice = 2;
            } else {
                if (Math.random() < 0.5){
                    choice = 0;
                } else {
                    choice = 2;
                }
            }

            if (choice == 0){
                currentRow--;
            } else if (choice == 2){
                currentRow++;
            }

            sum += choices[choice];

            GRect pixel = new GRect(c, currentRow, 1, 1);
            pixel.setColor(drawColor);
            pixel.setFilled(true);
            graphics.add(pixel);
        }
        return sum;
    }

    // use djikstras algorithm to find the shortest path from startRow point on the left to any point on the right heuristic is overall altitude change
    // return the value of the path based on the cumulative value of altitude change
    // I would like to identify the issue that may arise: This is a particulary gruesome piece of code as i have not implemented a visited list. as a result, on larger maps the runtime in insane, the whole map gets filled with the same color, and it doesnt give the perfect path when done, just runs it in a different color. I have only run this 4 times, it timed out 3 times on my laptop and worked once on my personal desktop after running for about 2-3 minutes. If you can get this to work, godspeed. - RT25
    public double drawLowestDownhillPath(GraphicsProgram graphics, int startRow, Color drawColor){
        double sum = 0;
        int currentRow = startRow;
        for (int c = 0; c < mapData[0].length - 1; c++){
            double[] choices = new double[3];
            if (currentRow == 0){ // change up
                choices[0] = Integer.MAX_VALUE;
            } else {
                choices[0] = mapData[currentRow][c] - mapData[currentRow - 1][c+1];
            }
            choices[1] = mapData[currentRow][c] - mapData[currentRow][c + 1]; // change forward
            if (currentRow == mapData.length - 1){ // change down
                choices[2] = Integer.MAX_VALUE;
            } else {
                choices[2] = mapData[currentRow][c] - mapData[currentRow + 1][c+1];
            }

            int choice;
            if (choices[1] <= choices[0] && choices[1] <= choices[2]){ // if right is good option
                choice = 1;
            } else if (choices[0] < choices[2]){ // if up is good option
                choice = 0;
            } else if (choices[2] < choices[1]){
                choice = 2;
            } else {
                if (Math.random() < 0.5){
                    choice = 0;
                } else {
                    choice = 2;
                }
            }

            if (choice == 0){
                currentRow--;
            } else if (choice == 2){
                currentRow++;
            }

            sum += choices[choice];

            GRect pixel = new GRect(c, currentRow, 1, 1);
            pixel.setColor(drawColor);
            pixel.setFilled(true);
            graphics.add(pixel);
        }
        return sum;
    }

    public double drawLowestDjikstras(GraphicsProgram graphics, int startRow, Color drawColor){
        Node source = new Node(0, startRow, 0);
        Queue<Node> queue = new PriorityQueue<Node>();

        int numOfRows = mapData.length;
        int numOfCols = mapData[0].length;

        queue.add(source);

        while (!queue.isEmpty()){

            Node popped = queue.poll();

            if (popped.x == numOfCols - 1){
                return popped.distanceFromSource;
            } else {
                ArrayList<Node> neighbors = new ArrayList<Node>();
                neighbors = getNeighbors(popped, numOfRows, numOfCols);

                for (Node neighbor : neighbors){
                    queue.add(neighbor);
                }
            }

            GRect pixel = new GRect(popped.x, popped.y, 1, 1);
            pixel.setColor(drawColor);
            pixel.setFilled(true);
            graphics.add(pixel);
        }
        return Integer.MAX_VALUE;
    }

    public ArrayList<Node> getNeighbors(Node popped, final int numOfRows, final int numOfCols){
        ArrayList<Node> neighbors = new ArrayList<Node>();
        if (popped.y != 0){
            neighbors.add(new Node(popped.x + 1, popped.y - 1, popped.distanceFromSource + Math.abs(mapData[popped.y][popped.x] - mapData[popped.y - 1][popped.x + 1]))); // add bottom
        }
        if (popped.y != numOfRows - 1){
            neighbors.add(new Node(popped.x + 1, popped.y + 1, popped.distanceFromSource + Math.abs(mapData[popped.y][popped.x] - mapData[popped.y + 1][popped.x + 1]))); // add bottom
        }
        neighbors.add(new Node(popped.x + 1, popped.y, popped.distanceFromSource + Math.abs(mapData[popped.y][popped.x] - mapData[popped.y][popped.x + 1]))); // add middle
        return neighbors;
    }

    public int getIndexOfLowestElevPath(GraphicsProgram graphics){
        int bestLineIndex = 0;
        double bestLineLength = 0;
        for (int i = 1; i < mapData.length; i++){
            double currentLineLength = drawLowestElevPath(graphics, i, Color.red);
            if (bestLineLength > currentLineLength){
                bestLineIndex = i;
                bestLineLength = currentLineLength;
            }
        }
        return bestLineIndex;
    }

    public int getIndexOfLowestDownhillPath(GraphicsProgram graphics){
        int bestLineIndex = 0;
        double bestLineLength = 0;
        for (int i = 1; i < mapData.length; i++){
            double currentLineLength = drawLowestDownhillPath(graphics, i, Color.orange);
            if (bestLineLength > currentLineLength){
                bestLineIndex = i;
                bestLineLength = currentLineLength;
            }
        }
        return bestLineIndex;
    }

    public int getIndexOfLowestDjikstrasPath(GraphicsProgram graphics){
        int bestLineIndex = 0;
        double bestLineLength = 0;
        for (int i = 1; i < mapData.length; i++){
            double currentLineLength = drawLowestDownhillPath(graphics, i, Color.yellow);
            if (bestLineLength > currentLineLength){
                bestLineIndex = i;
                bestLineLength = currentLineLength;
            }
        }
        return bestLineIndex;
    }

    class Node {
        int x;
        int y;
        double distanceFromSource;

        Node(int x, int y, double dis) {
            this.x = x;
            this.y = y;
            this.distanceFromSource = dis;
        }
    }

}

MountainPathFinder.java

//RT
import acm.program.*;
import acm.graphics.*;
import java.awt.Color;
import java.awt.event.*;
import javax.swing.*;

public class MountainPathFinder extends GraphicsProgram
{
    private TopographicMap mountainMap;

    private JButton loadButton;
    private JButton drawButton;
    private JButton findPathButton, downhillPathButton, djikstrasPathButton;
    private JTextField fileNameField;
    private JTextField rowsField, colsField;

    public void init()
    {
        setSize(960, 560);
        setTitle("Mountain Path Finder");
        initGUI();
    }

    public void run()
    {
        addActionListeners();
    }

    private void initGUI()
    {
        // you'll do this in task #0
        JLabel fileNameLabel = new JLabel("File Name:");
        add(fileNameLabel, NORTH);
        fileNameField = new JTextField("Colorado_480x480.txt", 18);
        add(fileNameField, NORTH);
        JLabel sizeLabel = new JLabel("Map Size:");
        add(sizeLabel, NORTH);

        rowsField = new JTextField("480", 3);
        add(rowsField, NORTH);

        JLabel xLabel = new JLabel("x");
        add(xLabel, NORTH);

        colsField = new JTextField("480", 3);
        add(colsField, NORTH);

        loadButton = new JButton("Load File");
        add(loadButton, NORTH);

        drawButton = new JButton("Draw Map");
        drawButton.setEnabled(false);
        add(drawButton, NORTH);

        findPathButton = new JButton("Find Path");
        findPathButton.setEnabled(false);
        add(findPathButton, NORTH);

        downhillPathButton = new JButton("Find Downhill Path");
        downhillPathButton.setEnabled(false);
        add(downhillPathButton, NORTH);

        djikstrasPathButton = new JButton("Find Djikstra's Path");
        djikstrasPathButton.setEnabled(false);
        add(djikstrasPathButton, NORTH);
    }

    public void actionPerformed(ActionEvent event)
    {
        if (event.getSource() == loadButton)
            handleLoadButton();
        if (event.getSource() == drawButton)
            handleDrawButton();
        if (event.getSource() == findPathButton)
            handleFindPathButton();
        if (event.getSource() == downhillPathButton)
            handleDownhillPathButton();
        // more to do here eventually
    }

    private void handleLoadButton()
    {
        removeAll();
        String fileName = fileNameField.getText();
        int rows = Integer.parseInt(rowsField.getText());
        int cols = Integer.parseInt(colsField.getText());
        mountainMap = new TopographicMap(fileName, rows, cols);

        if (mountainMap.isValid()){
            JOptionPane.showMessageDialog(this, "Map Loaded Successfully!");
            drawButton.setEnabled(true);
        } else {
            JOptionPane.showMessageDialog(this, "No bueno");
        }
        findPathButton.setEnabled(false);
        downhillPathButton.setEnabled(false);
        djikstrasPathButton.setEnabled(false);
    }

    private void handleDrawButton(){
        removeAll();
        mountainMap.drawMap(this);
        drawButton.setEnabled(false);
        findPathButton.setEnabled(true);
        downhillPathButton.setEnabled(true);
        djikstrasPathButton.setEnabled(true);
    }

    private void handleFindPathButton(){
        int bestIndex = mountainMap.getIndexOfLowestElevPath(this);
        mountainMap.drawLowestElevPath(this, bestIndex, Color.green);
    }

    private void handleDownhillPathButton(){
        int bestIndex = mountainMap.getIndexOfLowestDownhillPath(this);
        mountainMap.drawLowestDownhillPath(this, bestIndex, Color.blue);
    }

    private void handleDjikstrasPathButton(){
        int bestIndex = mountainMap.getIndexOfLowestDownhillPath(this);
        mountainMap.drawLowestDownhillPath(this, bestIndex, Color.magenta);
    }

}

Last update: June 5, 2023
Created: June 5, 2023