I'm creating a Java program to play the Connect Four game using the Minimax and Alpha-Beta Pruning algorithms. I'm adapting the following pseudocode for the Minimax algortihm:
Minimax pseudocode
The thing is, at the end of the game, one of the values I have to print is the number of nodes expanded during the execution of the algorithm, to give and idea of the memory usage and to compare it with the number of nodes expanded by Alpha-Beta Pruning. However, I don't really know what is the best way of keeping track of this value. A colleague of mine kept it as a field in the class that represented the grid. His solution involves keeping track of a certain maxDepth variable declared inside the Minimax class:
static Values decision(Tabuleiro tab,int depth){
Instant start = Instant.now();
int maxDepth = 1,children = 1;
Values v = new Values(-1*infinity,1);
for(Tabuleiro a: LibTabuleiro.makeDescendants(tab, 2)){
Values tmp = min(a,depth-1); maxDepth = Math.max(maxDepth,tmp.getY());
v = Values.max(v, new Values(tmp.getX(),a.getLast())); children++;
}
tab.setSize(maxDepth+children); /*setSize sets the field in the grid class
which contains the maximum number of nodes stored in memory simultaneously*/
Instant end = Instant.now();
return new Values((int)Duration.between(start,end).toMillis(),v.getY());
}
which in turn involves the use of this Values class:
public class Values {
int value;
int depth;
Values(int v,int d){
value = v;
depth = d;
}
int getX(){return value;}
int getY(){return depth;}
boolean lowerThan(Values other){return this.value<=other.value;}
boolean greaterThan(Values other){return this.value>=other.value;}
static Values max(Values a,Values b){
if(a.value >= b.value) return a;
else return b;
}
static Values min(Values a,Values b){
if(a.value <= b.value) return a;
else return b;
}
}
but I was wondering if there is a more straightforward way of doing this as, to be perfectly honest, I don't understand everything his code does and I shouldn't copy it either.
Thanks in advance,
For this program, I read in a excel file that lays out a map of towns, towns adjacent to them, and the distance between them, which looks like:
Bourke Nyngan 200
Brewarrina Walgett 134
Broken Hill Mildura 266
Broken Hill Wilcannia 195
Bungendore Queanbeyan 54
etc.
And that's working great. Everything seems perfect. I'm trying to make a program that if I give it two towns, it returns the shortest possible path between the two.
I can get my program to correctly read in the file, and set up everything, so I know that this issue isn't anything to do with the setup. To the best of my knowledge this part of my program works, as my program can get through it without throwing errors:
//returns the Vertex with the smallest distance from a list of Vertices
public static Vertex minDist(List<Vertex> Q){
int min = 2147483647;
Vertex closest = new Vertex("Closest");
for(Vertex v : Q){
if(v.distance < min){
closest = v;
}
}
return closest;
}
//used to relax (change the distance of a town)
public static void relax(Vertex u, Vertex v, int w){
if(v.distance > u.distance + w){
v.distance = u.distance + w;
v.predecessor = u;
}
}
public static void Dijkstra(Graph G, Vertex s){
Vertex u = new Vertex("not good");
List<Vertex> Q = V;
Vertex v = new Vertex("oh no");
//while Q is not empty:
while(!Q.isEmpty()){
//the vertex in Q that has the smallest distance (at first s with 0, then we relax and that changes things)
u = minDist(Q);
if(u.name.equals("Closest")){
//Q.remove(u);
return;
}
Q.remove(u);
S.add(u);
//for each edge e in u's adjacencyList:
for(Edge e : u.roadList){
if(e != null && !e.finish.name.equals(u.name) ){
v = e.finish;
relax(u,v,w(u,v)); //w(u,v) returns the distance between u and v
}
}
}
System.out.println("Q is null");
}
So I have that, and things look okay to me. I know it's a bit Frankenstein'ed together, but I got it to at least run without errors, because the ConcurrentModificationException gets thrown AFTER this method returns in my main method.
This is where my Dijkstra method gets called in my main method. I never reach the line in my code that prints "SHOULD REACH HERE" because the program throws the ConcurrentModificationException.
//if both towns exist and are unique, find the shortest route between them.
if(isTown(town1,V) && isTown(town2,V) && !town1.equals(town2)){
for(Vertex f : V){
if(f.name.equals(town2)){
destination = f;
}
}
System.out.println("Traveling...");
Graph G = new Graph(V,E);
for(Vertex s : V){
if(s.name.equals(town1)){
//////////////////DIJKSTRA STUFF GOES HERE///////////////////
initialize(G,s);
Dijkstra(G, s);
System.out.println("FINISHED DIJKSTRA");
//Print out the things in the vertex array S with their distances.
for(Vertex b : S){
System.out.println(b.name + " (" + b.distance + ")");
}
///////////////////////////////////////////////
}
}
System.out.println("SHOULD REACH HERE");
}
I have never seen a ConcurrentModificationException, my lab TA has never seen a ConcurrentModificationException, and even my professor has never seen a ConcurrentModificationException. Can I get some help with avoiding this? A person in a higher class said that he has only seen this happening when working with multiple threads, and I don't even know what that really means so I assume my program doesn't do that.
If I run the program with with town1 = Grafton and town2 = Bathurst, then the output should be:
First town: Grafton
Second town: Bathurst
Bathurst (820)
Lithgow (763)
Windsor (672)
Singleton (511)
Muswellbrook (463)
Tamworth (306)
Bendemeer (264)
Uralla (218)
Armidale (195)
Ebor (106)
Grafton
But is instead
First town: Grafton
Second town: Bathurst
Grafton (0)
Glen Innes (158)
Inverell (225)
Warialda (286)
Coffs Harbour (86)
I/O error: java.util.ConcurrentModificationException
You're getting this because you're removing from Q while iterating over it it. See Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop
I have code that enters the for loop within my Main method.
for (List<Point2D> points : output) {
currentPath = pathDistance(points);
if (shortest == 0){
shortest = currentPath;
} else if (currentPath < shortest) {
best = points;
shortest = currentPath;
}
}
where pathDistance is defined as
public static Double pathDistance(List<Point2D> path){
double distance = 0;
int count = path.size()-1;
for (int i = 0; i < count; i++) {
distance = distance + path.get(i).distance(path.get(i+1));
}
distance = distance + path.get(0).distance(path.get(count));
return distance;
}
But I keep getting the error
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.SubList.checkForComodification(Unknown Source)
at java.util.SubList.size(Unknown Source)
at java.util.Collections$SynchronizedCollection.size(Unknown Source)
at TSMain.pathDistance(TSMain.java:76)
at TSMain.main(TSMain.java:203)
I know this is supposed to mean that I am altering an object while an iteration depends on it, but I can't for the life of me figure out where that might be happening. Any help would be appreciated.
Your stacktrace shows that somewhere in your code subList is passed to Collections.synchronizedCollection (directly or indirectly). Like this
Set<List<Point2D>> output = Collections.singleton(
Collections.synchronizedCollection(data.subList(start, end)));
However it does not copy data list. And points subList is still pointing to a range in data list. And original list is modified at the momet path.size() call occurs.
You can easily fix your problem by doing explicit list copy before passing it to pathDistance
for(List<Point2D> points : output){
List<Point2D> pointsCopy = new ArrayList<>(points)
currentPath = pathDistance(pointsCopy);
// rest of code using pointsCopy
}
I also should notice that it looks like there is a problem with synchronization in your code. Wrapping sublists in synchronized collection is a bad idea because original list could be modified in unsafe manner w/o proper synchronization.
You can learn more about list modification checking by looking into AbstractList#modCount sources.
I write a simple code about dfs in a data file with 720 thousand vertex pair and find stack overflow. I am not quite sure whether it is caused by large data set or problems of my code. Any ideas is appreciated. Code part is showed below:
private void dfs(Graph G, int v) {
dfsMarked[v] = true;
for (Edge e : G.adj(v)) {
int w = e.other(v);
if (!dfsMarked[w]) {
dfsEdgeTo[w] = v;
dfs(G, w);
}
}
}
720 thousand vertex pairs with a pass spanning a few hundred thousand of them will easily overflow stack on most systems.
You needs to switch to an implementation of DFS that uses your own stack allocated independently of Java stack:
Stack<Integer> stack = new Stack<Integer>();
stack.push(start);
while (!stack.empty()) {
int v = stack.pop();
dfsMarked[v] = true;
for (Edge e : G.adj(v)) {
int w = e.other(v);
if (!dfsMarked[w]) {
dfsEdgeTo[w] = v;
stack.push(w);
}
}
}
Note: The above assumes that adjacency lists are unordered. If you need to preserve the specific ordering to match the recursive version, change the nested loop to enumerate adjacency lists in reverse.
YES, this is a homework project.
That being said, I'm looking to learn from my mistakes rather than just have someone do it for me.
My project is a word frequency list - I accept a text file (or website URL) and count the:
- Number of unique words, and
- How many times they appear.
All methods are provided for me except for one: the insert(E word) method, where the argument is a generic type word.
The word is stored in a Node (Linked List project) that also has a 'count' value, which is the value representing the number of times the word appears in the text being read.
What this method has to do is the following:
If the argument is already in the list, increment the count of that element. I have done this part
If the argument is not found in the list, append it to the list. I also have done this part.
sort the list by descending count value. i.e. highest -> lowest count
3.5. If two elements have the same count value, they are sorted by the dictionary order of their word.
I am VERY unfamiliar with Linked Lists, so as such I am running into a lot of NullPointerExceptions. This is my current insert method:
public void insert(E word){
if(word.equals("")){
return;
}
if(first == null){//if list is null (no elements)
/*Node item = new Node(word);
first = item;*/
first = new Node(word);
}
else{//first != null
Node itemToAdd = new Node(word);
boolean inList = false;
for(Node x = first; x != null; x=x.next){
if (x.key.equals(word)){// if word is found in list
x.count++;//incr
inList = true;//found in list
break;//get out of for
}//end IF
if(x.next == null && inList == false){//if end of list && not found
x.next = itemToAdd;//add to end of list
break;
}//end IF
}//end FOR
//EVERYTHING ABOVE THIS LINE WORKS.
if (!isSorted()){
countSort();
}
}//end ELSE
}//end method
My isSorted() method:
public boolean isSorted(){
for(Node copy = first; copy.next != null; copy = copy.next){
if (copy.count < copy.next.count){
return false;
}
}
return true;
}
and last but not least, the part where I'm struggling, the sort method:
public void countSort(){
for (Node x = first, p = x.next; p != null; x=x.next, p=p.next){
// x will start at the first Node, P will always be 1 node ahead of X.
if(x == first && (x.count < p.count)){
Node oldfirst = first;
x.next = p.next;
first = p;
first.next = oldfirst;
break;
}
if (x.count < p.count){
//copy.next == x.
Node oldfirst = first;
oldfirst.next = first.next;
x.next = p.next;
first = p;
first.next = oldfirst;
break;
}
if (x.count == p.count){
if(x.toString().charAt(0) < p.toString().charAt(0)){
//[x]->[p]->[q]
Node oldfirst = first;
x.next = p.next;
first = p;
first.next = oldfirst;
break;
}
}
}
}
Here is the output of my insert method when called by the classes/methods given to me:
Elapsed time:0.084
(the,60)
(of,49)
(a,39)
(is,46)
(to,36)
(and,31)
(can,9)
(in,19)
(more,7)
(thing,7)
(violent,3)
(things,3)
(from,9)
(collected,1)
(quotes,1)
(albert,1)
(einstein,2)
(any,2)
(intelligent,1)
(fool,1)
(make,1)
(bigger,1)
(complex,1)
(it,11)
(takes,1)
(touch,1)
(genius,1)
(lot,1)
(courage,1)
(move,1)
(opposite,1)
(direction,1)
(imagination,1)
(important,5)
(than,3)
(knowledge,3)
(gravitation,1)
(not,17)
(responsible,1)
(for,14)
(people,2)
(falling,1)
(love,2)
(i,13)
(want,1)
(know,3)
(god,4)
(s,8)
(thoughts,2)
(rest,2)
(are,11)
(details,2)
(hardest,1)
(world,7)
(understand,3)
(income,1)
(tax,1)
(reality,3)
(merely,1)
(an,7)
(illusion,2)
(albeit,1)
(very,3)
(persistent,2)
(one,12)
(only,7)
(real,1)
(valuable,1)
(intuition,1)
(person,1)
(starts,1)
(live,2)
(when,3)
(he,11)
(outside,1)
(himself,4)
(am,1)
(convinced,1)
(that,14)
(does,5)
(play,2)
(dice,1)
(subtle,1)
(but,8)
(malicious,1)
(weakness,2)
(attitude,1)
(becomes,1)
(character,1)
(never,3)
(think,1)
(future,2)
(comes,1)
(soon,1)
(enough,1)
(eternal,1)
(mystery,1)
(its,4)
(comprehensibility,1)
(sometimes,1)
My initial idea has been to try and loop the if(!isSorted()){ countSort();} part to just repeatedly run until it's sorted, but I seem to run into an infinite loop when doing that. I've tried following my professor's lecture notes, but unfortunately he posted the previous lecture's notes twice so I'm at a loss.
I'm not sure if it's worth mentioning, but they provided me an iterator with methods hasNext() and next() - how can I use this as well? I can't imagine they'd provide it if it were useless.
Where am I going wrong?
You are close. First the function to compare the items is not complete, so isSorted() could yield wrong results (if the count is the same but the words are in wrong order). This is also used to sort, so it's best to extract a method for the comparison:
// returns a value < 0 if a < b, a value > 0 if a > b and 0 if a == b
public int compare(Node a, Node b) {
if (a.count == b.count)
return a.word.compareTo(b.word);
// case-insensitive: a.word.toLoweCase().compareTo(b.word.toLowerCase())
} else {
return a.count - b.count;
}
}
Or simplified which is enough in your case:
public boolean correctOrder(Node a, Node b) {
if (a.count > b.count)
return true;
else if (a.count < b.count)
return false;
else
return a.word.compareTo(b.word) <= 0;
}
For the sort you seem to have chosen bubble sort, but you are missing the outer part:
boolean change;
do {
change = false;
Node oldX = null;
// your for:
for (Node x = first; x.next != null; x = x.next) {
if (!correctOrder(x, x.next)) {
// swap x and x.next, if oldX == null then x == first
change = true;
}
oldX = x;
}
} while (change);
We could use the help of Java native library implementation or more efficient sort algorithms, but judging from the exercise the performance of the sort algorithm is of no concern yet, first need to grasp basic concepts.
With looking your codes, it sounds like to me that two things can be done:
Firstly, you can make use of Comparable class method. So, I assume you wrote the class Node, thus you may want to inherit from Comparable class. When you inherited from that class, java will automatically provide you the compareTo method, and all you need to do is to specify in that method that "I want to compare according to your counts and I want it to be in ascending order."
**Edit(1):By the way, I forgot the mention before but after you impelement your compareTo method, you can use Collections.sort(LinkedList list), and it will be done.
The second solution came to mind is that you can sort your list during the countSort() operation with the technique of adding all to an another list with sorting and after add all them back to the real list. The sorting technique I'm trying to say is, keep going towards to the end of the list until you find a Node in the list that has a count smaller than currently adding Node's counts. Hope that doesn't confuse your head, but by this way you can achieve more clear method and less complicated view. To be clear I want to repeat the procedure:
Look the next
If (next is null), add it //You are at the end.
else{
if (count is smaller than current count), add it there
else, keep moving to the next Node. //while can be used for that.
}