Reputation: 31
The probleme is described in details here : https://adventofcode.com/2015/day/3 Shortly to summarize : I successively visit locations thanks to indications given in a text file, and i want to know how many locations I have visited at least once.
I have created a class Point like this :
public class Point {
private int x;
private int y;
public Point (int x, int y) {
this.x=x;
this.y=y;
}
public int getAbs() {
return x;
}
public int getOrd() {
return y;
}
}
It is simply a point formed by two coordinates, to indicate the location of a house. I use it here :
public class Exo3 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("C:/Users/mpv15_000/Documents/Advent/input3.txt");
int c = fr.read();
int x=0;
int y=0;
Point init = new Point(x,y);
List<Point> visitedHouses = new ArrayList<Point>();
visitedHouses.add(init);
Boolean b = true;
while ((c == '^') || (c == 'v') || (c == '<') || (c == '>'))
{
if (c == '^')
y++;
else if (c == 'v')
y--;
else if (c == '<')
x--;
else
x++;
for (Point p : visitedHouses) {
if ((p.getAbs() != x) || (p.getOrd() != y))
b = true;
else
b = false;
}
if (b == true) {
visitedHouses.add(new Point(x,y));
}
c = fr.read();
}
System.out.println("Number of houses visited at least once : " + visitedHouses.size());
fr.close();
}
}
I get "8193", which is the total number of iterations I suppose, but not what I am looking for. I want to know what are the locations I have visited at least once. I think the problem comes from the for-loop on the visitedHouses list. I would like to compare if the current x and y already exist in a Point located in this list. What should I do ?
Upvotes: 1
Views: 689
Reputation: 7753
As mentioned in the question comment above, you need to break the visitedHouses loop, as soon as you find the duplicated point.
For performance, I have changed the condition a bit inside the loop, see below:
boolean found = false;
for (Point p : visitedHouses) {
if ((p.getAbs() == x) && (p.getOrd() == y)) {
found = true;
break;
}
}
if (!found) {
visitedHouses.add(new Point(x,y));
}
Upvotes: 0
Reputation: 70959
the section of code
for (Point p : visitedHouses)
{
if ((p.getAbs() != x) || (p.getOrd() != y))
b = true;
else
b = false;
}
if (b == true)
{
visitedHouses.add(new Point(x,y));
}
looks especially problematic
If you find the "visited" house in the middle of the list, the next entry will not (hopefully) be a duplicate of that house, and will set the "b" flag to false, resulting in a duplicate.
This is due to the programming smell "caching a control variable". I recommend you don't store control variables (variables that are later checked in if statements to alter the behavior of your block.
Point newCandidate = new Point(x,y);
for (Point p : visitedHouses) {
if (newCandidate.getAbs() == p.getAbs() &&
newCandidate.getOrd() == p.getOrd()) {
newCandidate = null;
break;
}
}
if (newCandidate != null) {
visitedHouses.add(newCandidate);
}
However, this is still mostly like a control variable. What would be much faster is if we just used a java Set
like HashSet
.
public Point {
... same stuff you have ...
@Override
public int hashcode() {
return 37*x + 23*y + 13;
}
@Override
public boolean equals(Object object) {
if (! object instanceof Point) {
return false;
}
Point other = (Point)object;
if (this.x != other.x) { return false; }
if (this.y != other.y) { return false; }
return true;
}
}
Then you can do
Set<Point> visitedHouses = new HashSet<Point>();
// possibly initialize the first house
visitedHouses.add(new Point(0, 0));
while (... visiting each house ...) {
Point currentHouse = new Point(currentX, currentY);
visitedHouses.add(currentHouse);
}
and rely upon the Set
interface to assure that you never get a duplicate Point
entry in the Collection
as Set
s cannot contain duplicates (and your Point
now knows how to take two instances and determine if they are equal).
Not only will the last approach be much faster, since it is stored in a HashSet
you don't need to go through each Point
in the Set
to determine if you have a match for the current location. The hashcode(...)
is ran on the "point being used to query the Set" and lookup by it's returned value avoids 99% of the other entries. The small subset of entries that have a matching hashcode are then compared with the equals(...)
method to really determine if they are equivalent, or if they "just happen" to have the same hashcode.
Upvotes: 1
Reputation: 4120
You need to override equals
method in Point class to return true when x
and y
are the same.
something like this:
@Override
public boolean equals(Object other)
{
if (this == other) return true;
if (!(other instanceof Point)) return false;
return (this.x == ((Point)other).x) && (this.y == ((Point)other).y);
}
Then use Set instead of List. Set will hold only unique Points.
If you'd like to stay with List just do:
if (b == true)
{
Point visitedPoint = new Point(x,y);
if (!visitedHouses.contains(visitedPoint)) {
visitedHouses.add(visitedPoint);
}
}
BTW: how you identify visited House is up to you...
Upvotes: 1