Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions src/main/java/com/adventofcode/flashk/day09/EventType1D.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@
public enum EventType1D {

START(1),
START_SQ_LOWER(2),
START_SQ_HIGHER(3),
END_SQ_HIGHER(4),
END_SQ_LOWER(5),
END(6);
START_RECTANGLE(2),
END_RECTANGLE(3),
END(4);

@Getter
private final int priority;
Expand Down
211 changes: 0 additions & 211 deletions src/main/java/com/adventofcode/flashk/day09/MovieTheater.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,217 +47,6 @@ public long solveA() {
return result;
}

public long solveB() {
long result = Long.MIN_VALUE;

// Buscar en cada par de coordenadas el rectángulo más grande.
for(int i = 0; i < redTiles.size(); i++) {
Point firstCorner = redTiles.get(i);

for(int j = i + 1; j < redTiles.size(); j++) {
Point secondCorner = redTiles.get(j);
long area = calculateArea(firstCorner, secondCorner);

if(area < result) {
continue;
}

if(isValidRectangle(firstCorner, secondCorner)) {
result = area;
}
}
}

return result;
}

private boolean isValidRectangle(Point firstCorner, Point secondCorner) {
// Make both a vertical and a valid sweep
return isValidSweepVertical(firstCorner, secondCorner) && isValidSweepHorizontal(firstCorner, secondCorner);
}

private boolean isValidSweepVertical(Point firstCorner, Point secondCorner) {

// Check horizontal segments
PriorityQueue<Event1D> events = prepareVerticalEvents(firstCorner, secondCorner);

// Will allow to check if there are segments over and below the square edges
TreeSet<Integer> yCoordinates = new TreeSet<>();

// Variables to handle square edge detection
int squareLowerY = -1;
int squareHigherY = -1;

boolean isValid = true;
while(!events.isEmpty() && isValid) {

Event1D currentEvent = events.poll();

switch(currentEvent.getType()) {
case START:
yCoordinates.add(currentEvent.getSegment().getLeft().y());
break;
case END:
yCoordinates.remove(currentEvent.getSegment().getLeft().y());

// Check square segments
isValid = hasCeiling(yCoordinates, squareHigherY) && hasFloor(yCoordinates, squareLowerY);

break;
case START_SQ_HIGHER:
squareHigherY = currentEvent.getSegment().getLeft().y();
isValid = hasCeiling(yCoordinates, squareHigherY);
break;
case START_SQ_LOWER:
squareLowerY = currentEvent.getSegment().getLeft().y();
isValid = hasFloor(yCoordinates, squareLowerY);
break;
case END_SQ_HIGHER:
case END_SQ_LOWER:
return true;
}

}

return isValid;

}

private static boolean hasFloor(TreeSet<Integer> coordinates, int squareLowerXY) {
// Calculation does not apply if there is no lower square edge
if(squareLowerXY == -1) {
return true;
}

return coordinates.floor(squareLowerXY) != null;
}

private static boolean hasCeiling(TreeSet<Integer> coordinates, int squareHigherXY) {
// Calculation does not apply if there is no higher square edge
if(squareHigherXY == -1){
return true;
}
return coordinates.ceiling(squareHigherXY) != null;
}

private PriorityQueue<Event1D> prepareVerticalEvents(Point firstCorner, Point secondCorner) {

PriorityQueue<Event1D> events = new PriorityQueue<>();

// There are no horizontal events if the rectangle is just a vertical line
if(firstCorner.x() == secondCorner.x()) {
return events;
}

// Rectangle horizontal segments events
int minX = Math.min(firstCorner.x(), secondCorner.x());
int maxX = Math.max(firstCorner.x(), secondCorner.x());
int minY = Math.min(firstCorner.y(), secondCorner.y());
int maxY = Math.max(firstCorner.y(), secondCorner.y());

// Upper segment
Segment1D higherSegment = new Segment1D(new Point(minX,maxY), new Point(maxX, maxY));
events.add(new Event1D(higherSegment.getLeft().x(), higherSegment, EventType1D.START_SQ_HIGHER));
events.add(new Event1D(higherSegment.getRight().x(), higherSegment, EventType1D.END_SQ_HIGHER));

// Lower segment
Segment1D lowerSegment = new Segment1D(new Point(minX,minY), new Point(maxX, minY));
if(!higherSegment.equals(lowerSegment)) {
events.add(new Event1D(lowerSegment.getLeft().x(), lowerSegment, EventType1D.START_SQ_LOWER));
events.add(new Event1D(lowerSegment.getRight().x(), lowerSegment, EventType1D.END_SQ_LOWER));
}

// Polygon segments
for(Segment1D segment : horizontalSegments) {
events.add(new Event1D(segment.getLeft().x(), segment, EventType1D.START));
events.add(new Event1D(segment.getRight().x(), segment, EventType1D.END));
}

return events;
}

private boolean isValidSweepHorizontal(Point firstCorner, Point secondCorner) {

// Check horizontal segments
PriorityQueue<Event1D> events = prepareHorizontalEvents(firstCorner, secondCorner);

// Will allow to check if there are segments left and right the square edges
TreeSet<Integer> xCoordinates = new TreeSet<>();

// Variables to handle square edge detection
int squareLowerX = -1;
int squareHigherX = -1;
boolean isValid = true;

while(!events.isEmpty() && isValid) {

Event1D currentEvent = events.poll();

switch(currentEvent.getType()) {
case START:
xCoordinates.add(currentEvent.getSegment().getUp().x());
break;
case END:
xCoordinates.remove(currentEvent.getSegment().getUp().x());

isValid = hasCeiling(xCoordinates, squareHigherX) && hasFloor(xCoordinates, squareLowerX);

break;
case START_SQ_HIGHER:
squareHigherX = currentEvent.getSegment().getUp().x();
isValid = hasCeiling(xCoordinates, squareHigherX);

break;
case START_SQ_LOWER:
squareLowerX = currentEvent.getSegment().getUp().x();
isValid = hasFloor(xCoordinates, squareLowerX);
break;
case END_SQ_HIGHER:
case END_SQ_LOWER:
return true;
}

}

return isValid;
}

private PriorityQueue<Event1D> prepareHorizontalEvents(Point firstCorner, Point secondCorner) {

PriorityQueue<Event1D> events = new PriorityQueue<>();

// There are no horizontal events if the rectangle is just a horizontal line
if(firstCorner.y() == secondCorner.y()) {
return events;
}

// Rectangle horizontal segments events
int minX = Math.min(firstCorner.x(), secondCorner.x());
int maxX = Math.max(firstCorner.x(), secondCorner.x());
int minY = Math.min(firstCorner.y(), secondCorner.y());
int maxY = Math.max(firstCorner.y(), secondCorner.y());

// Upper segment (right)
Segment1D rightSegment = new Segment1D(new Point(maxX,minY), new Point(maxX, maxY));
events.add(new Event1D(rightSegment.getDown().y(), rightSegment, EventType1D.START_SQ_HIGHER));
events.add(new Event1D(rightSegment.getUp().y(), rightSegment, EventType1D.END_SQ_HIGHER));

// Lower segment (left)
Segment1D leftSegment = new Segment1D(new Point(minX,minY), new Point(minX, maxY));
if(!rightSegment.equals(leftSegment)) {
events.add(new Event1D(leftSegment.getDown().y(), leftSegment, EventType1D.START_SQ_LOWER));
events.add(new Event1D(leftSegment.getUp().y(), leftSegment, EventType1D.END_SQ_LOWER));
}

// Polygon segments
for(Segment1D segment : verticalSegments) {
events.add(new Event1D(segment.getDown().y(), segment, EventType1D.START));
events.add(new Event1D(segment.getUp().y(), segment, EventType1D.END));
}

return events;
}

private long calculateArea(Point firstCorner, Point secondCorner) {
long dx = Math.abs(firstCorner.x() - secondCorner.x()) + 1;
long dy = Math.abs(firstCorner.y() - secondCorner.y()) + 1;
Expand Down
44 changes: 26 additions & 18 deletions src/main/java/com/adventofcode/flashk/day09/Segment1D.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,39 @@
import module java.base;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;

@Getter
@EqualsAndHashCode
public class Segment1D {
public class Segment1D implements Comparable<Segment1D> {

private Point up;
private Point down;
private Point left;
private Point right;
private boolean isVertical;
private final Point first;
private final Point second;
private final int minX;
private final int maxX;
private final int minY;
private final int maxY;
private final boolean isVertical;

Comment on lines +10 to 19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Segment1D.compareTo is inconsistent with @EqualsAndHashCode and may break TreeSet-based logic.

compareTo only uses minX and minY, while Lombok’s @EqualsAndHashCode includes all fields (first, second, minX, maxX, minY, maxY, isVertical). This allows two non-equal segments to compare as 0, violating the Comparable contract and breaking TreeSet behavior (dropped entries, wrong ceiling/floor). Please either make compareTo fully consistent with equals or remove @EqualsAndHashCode and define equals/hashCode to match the ordering semantics.

Comment on lines +10 to 19
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The natural ordering of Segment1D is inconsistent with its equality and with the custom comparators, which can cause subtle TreeSet/TreeMap issues.

@EqualsAndHashCode considers all fields, but compareTo (and the vertical/horizontal comparators) only use minX/minY. Two different segments that share minX/minY but differ in length/orientation will compare as 0 while not being equals(), violating the Comparable contract and breaking TreeSet/TreeMap semantics (elements can be dropped as “duplicates”). Please either align ordering with equality (e.g., include maxX, maxY, isVertical) or remove Comparable and rely solely on explicit comparators.

public Segment1D(Point first, Point second) {
public Segment1D(Point a, Point b) {
first = a;
second = b;
minX = Math.min(first.x(), second.x());
maxX = Math.max(first.x(), second.x());
minY = Math.min(first.y(), second.y());
maxY = Math.max(first.y(), second.y());
isVertical = first.x() == second.x();
}

@Override
public int compareTo(@NotNull Segment1D other) {

if(first.x() == second.x()) {
// Vertical segment
up = new Point(first.x(), Math.max(first.y(), second.y()));
down = new Point(first.x(), Math.min(first.y(), second.y()));
isVertical = true;
} else if(first.y() == second.y()) {
// Horizontal segment
left = new Point(Math.min(first.x(), second.x()), first.y());
right = new Point(Math.max(first.x(), second.x()), first.y());
isVertical = false;
// Order first by lowest 'x' coordinate
if(minX != other.minX) {
return Integer.compare(minX, other.minX);
}

// Order by lowest 'y' coordinate
return Integer.compare(minY, other.minY);
Comment on lines +35 to +39
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

The compareTo method has uncovered branches. Specifically, the return Integer.compare(minX, other.minX); (line 35) and return Integer.compare(minY, other.minY); (line 39) statements are not being executed by tests. This indicates that there are no test cases where two Segment1D objects have different minX values, or where they have the same minX but different minY values. Please add test cases to ensure these branches are covered and the comparison logic behaves as intended.

See Issue in Codacy

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.adventofcode.flashk.day09.refactor;

import module java.base;
import com.adventofcode.flashk.day09.Segment1D;

public class ComparatorSegment1DHorizontal implements Comparator<Segment1D> {

@Override
public int compare(Segment1D o1, Segment1D o2) {

if(o1.getMinX() != o2.getMinX()) {
return Integer.compare(o1.getMinX(), o2.getMinX());
}

return Integer.compare(o1.getMinY(), o2.getMinY());
}
Comment on lines +6 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Suggestion: The ComparatorSegment1DHorizontal class provides the exact same comparison logic as the compareTo method already implemented in Segment1D. This makes ComparatorSegment1DHorizontal redundant. You can remove this class and rely on Segment1D's natural ordering when sorting horizontal segments.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.adventofcode.flashk.day09.refactor;

import static java.lang.IO.println;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Nitpick: The import static java.lang.IO.println; is not used in this file and should be removed.


import module java.base;
import com.adventofcode.flashk.day09.Segment1D;

public class ComparatorSegment1DVertical implements Comparator<Segment1D> {

@Override
public int compare(Segment1D o1, Segment1D o2) {

if(o1.getMinY() != o2.getMinY()) {
return Integer.compare(o1.getMinY(), o2.getMinY());
}

return Integer.compare(o1.getMinX(), o2.getMinX());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.adventofcode.flashk.day09.refactor;

import module java.base;
import com.adventofcode.flashk.day09.EventType1D;
import com.adventofcode.flashk.day09.Segment1D;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;

@Getter
public class Event1DRefactor implements Comparable<Event1DRefactor> {

/// The exact coordinate that triggered the event.
///
/// Can be either an `x` or an `y` coordinate.
private final int coordinate;

/// The segment that triggered the event.
private Segment1D segment; // The segment this coordinate event belongs to.

/// The rectangle that triggered the event.
private Rectangle rectangle;

/// The {@link EventType1D type} of event.
private final EventType1D type;

public Event1DRefactor(int coordinate, Segment1D segment, EventType1D type) {
this.coordinate = coordinate;
this.segment = segment;
this.type = type;
}

public Event1DRefactor(int coordinate, Rectangle rectangle, EventType1D type) {
this.coordinate = coordinate;
this.rectangle = rectangle;
this.type = type;
}

@Override
public int compareTo(@NotNull Event1DRefactor other) {

// Priority on lower coordinate
if(this.coordinate != other.coordinate) {
return Integer.compare(this.coordinate, other.coordinate);
}

// In case of being same coordinates, give priority depending on the event type
return Integer.compare(this.type.getPriority(), other.type.getPriority());
}
}
Loading