-1

I created a program that solves a maze that reads a text file input. I managed to solve it using Depth-first Search and the result was fine on small mazes like this:

11 3
2 3
0 3
1 4
5 4
5 7
6 7
7 8
8 9
9 10
0 5

Everything was fine solving small to medium mazes but when the maze becomes larger, it gives me an ArrayIndexOutOfBoundsException error (see below).

Enter maze file: test2.txt
Enter start node: 0
Enter goal node: 1
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 207
    at MazeGraph.addArc(MazeGraph.java:95)
    at MazeGraph.<init>(MazeGraph.java:51)
    at MazeSystem.<init>(MazeSystem.java:25)
    at MazeSystem.main(MazeSystem.java:37)
Java Result: 1

My program should read automatically how many nodes are in the test2.txt (this is the large maze file) file but this error happens. The code looks like this:

MazeGraph.java

import java.io.*;
import static java.lang.System.out;
import java.util.*;

/**
 * This class creates a representation of Maze as a Graph by reading from a text
 * file.
 *
 * @author Muhammad
 */
public class MazeGraph {

    final int node; //For declaring constant value of a node.
    int arc;
    List<Integer>[] adjacencyList;
    static Set<Integer> setOfNodes = new HashSet<>();
    private static Scanner scanNodeSize;

    /**
     * This constructors takes an integer parameter for reading node indexes in
     * a list of adjacent nodes.
     *
     * @param node - integer parameter for passing the nodes value from the file
     * and create a list of adjacent nodes.
     */
    MazeGraph(int node) {
        this.node = node;
        this.arc = 0;//initialise to empty arcs
        adjacencyList = (List<Integer>[]) new List[node];
        for (int index = 0; index < node; index++) {
            adjacencyList[index] = new LinkedList<Integer>();
        }
    }

    /**
     * The main constructor that takes a String parameter for reading maze file.
     *
     * @param mazeFile
     */
    public MazeGraph(String mazeFile) {
        this(getNodeSize(mazeFile));
        Scanner scan;
        try {
            //Scan maze file.
            scan = new Scanner(new File(mazeFile));
            /*loop when it has next integer then read two nodes from the file and add arc for it.*/
            while (scan.hasNextInt()) {
                int node1 = scan.nextInt();
                int node2 = scan.nextInt();
                addArc(node1, node2);
            }
        } catch (FileNotFoundException ex) {
            out.println(ex.getMessage());
        }
    }

    /**
     * This method returns a size of the set of nodes by taking a String
     * parameter which the name of the maze file.
     *
     * @param mazeFile - String parameter for reading maze file for scanning the
     * size of the nodes.
     * @return - returns an integer value for the size of the set of nodes.
     */
    public static int getNodeSize(String mazeFile) {

        try {
            scanNodeSize = new Scanner(new File(mazeFile));
            while (scanNodeSize.hasNextInt()) {
                int node1 = scanNodeSize.nextInt();
                int node2 = scanNodeSize.nextInt();
                setOfNodes.add(node1);
                setOfNodes.add(node2);
            }
            return setOfNodes.size();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return setOfNodes.size();

    }

    /**
     * This method adds an arc by adding two different nodes in array of list
     * called adjacency list.
     *
     * @param node1 - first node.
     * @param node2 - next node.
     */
    private void addArc(int node1, int node2) {
        arc++; //Increase arc by one whenever this addArc method is called.
        adjacencyList[node1].add(node2);
        adjacencyList[node2].add(node1);

    }

    //Print the nodes and its arcs by looping through the adjacency list.
    public void print() {
        out.println(node + " Nodes, " + arc + " Arcs \n");
        for (int fromNode = 0; fromNode < node; fromNode++) {
            out.print(fromNode + " connected to ");
            for (int arcNode : adjacencyList[fromNode]) {
                out.print(arcNode + " ");
            }
            out.println();
        }
    }

    /**
     * This method returns a list of nodes to allow objects to be the target for
     * "for-each" statement in order to iterate through the nodes.
     *
     * @param nodes - an Integer parameter for getting the number of nodes in a
     * list.
     * @return - returns a list of nodes.
     */
    public Iterable<Integer> getAdjacencyList(int nodes) {
        return adjacencyList[nodes];
    }
}

In MazeGraph.java, the getNodeSize method returns an integer that is used for the constructor's parameter. To make it work, I have to take that getNodeSize method out in the constructor's parameter and instead put some larger value there like 300 for example. In stack trace, it says the problem is in the addArc method. I am trying to figure out on how to solve this problem if it works already in other mazes so that it also reads in large mazes automatically. Please, I would be grateful if someone can solve this.

This is the large file.

0 2
0 3
0 200
201 202
202 203
203 204
204 205
205 206
206 207
207 208
208 209
209 210
210 211
211 212
212 213
213 214
214 215
215 216
216 217
217 218
218 219
219 1
185 122
7 134
15 161
65 165
34 13
22 73
173 189
21 149
204 71
159 148
66 97
122 125
35 63
78 54
147 84
184 183
80 203
125 2
80 111
125 72
166 121
145 151
51 140
74 152
116 46
138 202
114 9
127 133
177 128
202 138
94 122
30 30
32 117
125 16
157 16
187 150
129 37
55 3
53 118
202 185
187 185
155 174
79 107
159 33
143 18
6 90
74 59
161 67
82 57
77 127
123 204
38 96
114 150
92 137
35 136
118 79
154 140
109 203
178 72
13 204
58 6
8 28
98 42
137 123
59 95
162 69
22 142
68 91
155 90
117 90
143 103
43 73
153 28
152 106
160 187
42 171
15 154
84 151
163 97
7 126
12 28
31 95
71 63
37 110
189 128
98 115
143 185
124 187
73 194
58 19
192 100
133 93
105 39
124 191
126 143
136 34
44 182
199 168
110 202
20 169
180 64
87 166
204 159
110 125
122 4
105 112
90 11
124 138
196 89
179 114
184 70
38 30
62 68
26 34
203 55
204 60
49 173
107 90
37 180
156 92
196 170
193 107
70 110
68 133
61 132
176 125
180 203
160 165
160 95
204 21
81 78
25 133
74 49
199 111
12 62
203 165
53 81
33 190
122 38
86 170
45 127
75 117
113 88
138 189
177 176
43 141
179 80
24 166
121 126
12 122
94 74
106 16
176 126
93 72
153 184
202 111
19 13
30 106
73 116
149 202
191 32
16 37
66 23
5 50
115 104
149 189
72 52
31 69
111 164
135 147
109 142
94 167
196 12
75 121
64 201
11 96
183 171
117 96
87 10
188 36
89 141
156 84
52 24
167 58
67 102
35 129
73 84
191 45
12 102
100 33
66 30
96 140
82 195
61 202
144 175
41 35
140 123
144 150
194 102
14 29
161 84
198 155
188 190
161 48
197 75
132 160
65 89
115 84
104 46
85 155
35 181
73 11
76 68
116 137
170 177
16 101
151 189
191 201
33 37
81 5
153 135
85 121
147 12
49 186
98 171
43 27
123 131
9 19
19 205
79 192
197 159
94 22
203 153
7 83
20 195
179 21
125 183
156 65
92 104
Muhammad Catubig
  • 301
  • 2
  • 15

1 Answers1

1

Okay, the problem is in getNodeSize(String). You return setOfNodes.size(). The problem is that if a node is a singleton (i.e. it has no connecting arcs), then it will not be listed in the maze file. For instance, suppose your maze file is "9999 1", nothing else. Then your getNodeSize() method will return 2. Your code then instantiates the array of nodes as having size 2. Then you try to add an arc between 9999 and 1 but receive an ArrayIndexOutOfBoundsException because there's only two positions in the array, but you're trying to index position 9999.

Instead, you should return the largest node Id observed plus one. The following code accomplishes this:

/**
 * This method returns a size of the set of nodes by taking a String
 * parameter which the name of the maze file.
 *
 * @param mazeFile - String parameter for reading maze file for scanning the
 * size of the nodes.
 * @return - returns an integer value for the size of the set of nodes.
 * @throws FileNotFoundException if the mazeFile could not be found
 */
public static int getNodeSize(String mazeFile) throws FileNotFoundException{
        scanNodeSize = new Scanner(new File(mazeFile));
        while (scanNodeSize.hasNextInt()) {
            int node1 = scanNodeSize.nextInt();
            int node2 = scanNodeSize.nextInt();
            setOfNodes.add(node1);
            setOfNodes.add(node2);
        }

        //Changes begin here
        Integer max = setOfNodes.stream().reduce(Integer::max).get();
        return max+1;
        //Changes end here

}

Note: I removed the try-catch block and replaced it with a throws declaration.

A better approach: It is generally frowned upon to mix an array and a Collection. It may be better to use a HashMap:

HashMap<Integer,List<Integer>> adjacencyList = new HashMap<>();

You can access the adjacency list for any node by calling adjacencyList.get(nodeId) and your constructor can be changed to:

public MazeGraph(String mazeFile) {
    adjacencyMap = new HashMap<>();
    Scanner scan;
    try {
        //Scan maze file.
        scan = new Scanner(new File(mazeFile));
        /*loop when it has next integer then read two nodes from the file and add arc for it.*/
        while (scan.hasNextInt()) {
            int node1 = scan.nextInt();
            int node2 = scan.nextInt();
            List<Integer> node1AdjList = adjacencyMap.getOrDefault(node1,new LinkedList<Integer>());
            List<Integer> node2AdjList = adjacencyMap.getOrDefault(node2,new LinkedList<Integer>());
            node1AdjList.add(node2);
            node2AdjList.add(node1);
            adjacencyMap.put(node1, node1AdjList);
            adjacencyMap.put(node2, node2AdjList);
        }
    } catch (FileNotFoundException ex) {
        out.println(ex.getMessage());
    }
    node = adjacencyMap.keySet().parallelStream().reduce(Integer::max).get();

}
Austin D
  • 6,678
  • 2
  • 26
  • 34
  • What is the other version of your code for jdk 7 because currently the system that I am using for is jdk 7 so basically I couldn't make it work because I think your code is in jdk 8 if that does make sense to you. Thanks. – Muhammad Catubig Feb 02 '16 at 23:47
  • Also, why did you put another boolean parameter in the constructor? Where did you use it for? I don't know where you have used it so I guess it is save to delete it. – Muhammad Catubig Feb 03 '16 at 00:11
  • That was an artifact from me testing the method; you can remove it. I edited my answer accordingly. – Austin D Feb 03 '16 at 01:41
  • Since you solved with jdk 8 I decided to change the source to jdk 8 and it works!! Thank you so much.. You solved my problem.. – Muhammad Catubig Feb 03 '16 at 02:21