Skip to content

Commit fb6d7eb

Browse files
committed
Refactor PartialJigsawInformation to JigsawInfo with builder pattern and type improvements
1 parent 2c82a8e commit fb6d7eb

File tree

8 files changed

+406
-185
lines changed

8 files changed

+406
-185
lines changed

exercises/practice/piecing-it-together/.meta/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
".meta/src/reference/java/PiecingItTogether.java"
1414
],
1515
"editor": [
16-
".meta/src/reference/java/PartialJigsawInformation.java"
16+
".meta/src/reference/java/JigsawInfo.java"
1717
]
1818
},
1919
"blurb": "Fill in missing jigsaw puzzle details from partial data",
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import java.util.Objects;
2+
import java.util.Optional;
3+
import java.util.OptionalDouble;
4+
import java.util.OptionalInt;
5+
6+
/**
7+
* Represents partial or complete information about a jigsaw puzzle,
8+
*
9+
* NOTE: There is no need to change this file and is treated as read only by the Exercism test runners.
10+
*/
11+
public class JigsawInfo {
12+
private final OptionalInt pieces;
13+
private final OptionalInt border;
14+
private final OptionalInt inside;
15+
private final OptionalInt rows;
16+
private final OptionalInt columns;
17+
private final OptionalDouble aspectRatio;
18+
private final Optional<String> format;
19+
20+
private JigsawInfo(Builder builder) {
21+
this.pieces = builder.pieces != null ? OptionalInt.of(builder.pieces) : OptionalInt.empty();
22+
this.border = builder.border != null ? OptionalInt.of(builder.border) : OptionalInt.empty();
23+
this.inside = builder.inside != null ? OptionalInt.of(builder.inside) : OptionalInt.empty();
24+
this.rows = builder.rows != null ? OptionalInt.of(builder.rows) : OptionalInt.empty();
25+
this.columns = builder.columns != null ? OptionalInt.of(builder.columns) : OptionalInt.empty();
26+
this.aspectRatio = builder.aspectRatio != null ? OptionalDouble.of(builder.aspectRatio) : OptionalDouble.empty();
27+
this.format = Optional.ofNullable(builder.format);
28+
}
29+
30+
public static class Builder {
31+
private Integer pieces;
32+
private Integer border;
33+
private Integer inside;
34+
private Integer rows;
35+
private Integer columns;
36+
private Double aspectRatio;
37+
private String format;
38+
39+
public Builder pieces(Integer pieces) {
40+
this.pieces = pieces;
41+
return this;
42+
}
43+
44+
public Builder border(Integer border) {
45+
this.border = border;
46+
return this;
47+
}
48+
49+
public Builder inside(Integer inside) {
50+
this.inside = inside;
51+
return this;
52+
}
53+
54+
public Builder rows(Integer rows) {
55+
this.rows = rows;
56+
return this;
57+
}
58+
59+
public Builder columns(Integer columns) {
60+
this.columns = columns;
61+
return this;
62+
}
63+
64+
public Builder aspectRatio(Double aspectRatio) {
65+
this.aspectRatio = aspectRatio;
66+
return this;
67+
}
68+
69+
public Builder format(String format) {
70+
this.format = format;
71+
return this;
72+
}
73+
74+
public JigsawInfo build() {
75+
return new JigsawInfo(this);
76+
}
77+
}
78+
79+
public OptionalInt getPieces() {
80+
return pieces;
81+
}
82+
83+
public OptionalInt getBorder() {
84+
return border;
85+
}
86+
87+
public OptionalInt getInside() {
88+
return inside;
89+
}
90+
91+
public OptionalInt getRows() {
92+
return rows;
93+
}
94+
95+
public OptionalInt getColumns() {
96+
return columns;
97+
}
98+
99+
public OptionalDouble getAspectRatio() {
100+
return aspectRatio;
101+
}
102+
103+
public Optional<String> getFormat() {
104+
return format;
105+
}
106+
107+
@Override
108+
public boolean equals(Object o) {
109+
if (o == null || getClass() != o.getClass()) return false;
110+
JigsawInfo that = (JigsawInfo) o;
111+
return Objects.equals(pieces, that.pieces) && Objects.equals(border, that.border) && Objects.equals(inside, that.inside) && Objects.equals(rows, that.rows) && Objects.equals(columns, that.columns) && Objects.equals(aspectRatio, that.aspectRatio) && Objects.equals(format, that.format);
112+
}
113+
114+
@Override
115+
public int hashCode() {
116+
return Objects.hash(pieces, border, inside, rows, columns, aspectRatio, format);
117+
}
118+
119+
@Override
120+
public String toString() {
121+
return "JigsawInfo{" +
122+
"pieces=" + pieces +
123+
", border=" + border +
124+
", inside=" + inside +
125+
", rows=" + rows +
126+
", columns=" + columns +
127+
", aspectRatio=" + aspectRatio +
128+
", format=" + format +
129+
'}';
130+
}
131+
}

exercises/practice/piecing-it-together/.meta/src/reference/java/PartialJigsawInformation.java

Lines changed: 0 additions & 67 deletions
This file was deleted.

exercises/practice/piecing-it-together/.meta/src/reference/java/PiecingItTogether.java

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
1-
import java.util.ArrayList;
2-
import java.util.List;
3-
import java.util.Objects;
4-
import java.util.Optional;
1+
import java.util.*;
52

63
public class PiecingItTogether {
74
private final double epsilon = 1e-6;
85

9-
public PartialJigsawInformation getCompleteInformation(PartialJigsawInformation input) {
10-
Integer rows = input.getRows().orElse(null);
11-
Integer cols = input.getColumns().orElse(null);
12-
Integer pieces = input.getPieces().orElse(null);
13-
Integer border = input.getBorder().orElse(null);
14-
Integer inside = input.getInside().orElse(null);
15-
Double aspect = input.getAspectRatio().orElse(null);
6+
public JigsawInfo getCompleteInformation(JigsawInfo input) {
7+
Integer rows = input.getRows().isPresent() ? input.getRows().getAsInt() : null;
8+
Integer cols = input.getColumns().isPresent() ? input.getColumns().getAsInt() : null;
9+
Integer pieces = input.getPieces().isPresent() ? input.getPieces().getAsInt() : null;
10+
Integer border = input.getBorder().isPresent() ? input.getBorder().getAsInt() : null;
11+
Integer inside = input.getInside().isPresent() ? input.getInside().getAsInt() : null;
12+
Double aspect = input.getAspectRatio().isPresent() ? input.getAspectRatio().getAsDouble() : null;
1613
String format = input.getFormat().orElse(null);
1714

1815
// Final information to be compared with the original input to detect inconsistencies
19-
PartialJigsawInformation result = null;
16+
JigsawInfo result = null;
2017

2118
// Check format and aspect ratio don't contradict each other or set aspect if format is square
2219
if (format != null && aspect != null) {
@@ -77,7 +74,7 @@ public PartialJigsawInformation getCompleteInformation(PartialJigsawInformation
7774
checkConsistencyOrThrow(result, input);
7875
return result;
7976
} else if (inside != null && inside == 0) {
80-
List<PartialJigsawInformation> validGuesses = new ArrayList<>();
77+
List<JigsawInfo> validGuesses = new ArrayList<>();
8178

8279
for (int fixed : List.of(1, 2)) {
8380
tryGuessWithFixedSide(fixed, true, aspect, input).ifPresent(validGuesses::add); // rows = fixed
@@ -99,7 +96,7 @@ public PartialJigsawInformation getCompleteInformation(PartialJigsawInformation
9996
}
10097

10198
// Brute force as a last resort
102-
List<PartialJigsawInformation> validGuesses = new ArrayList<>();
99+
List<JigsawInfo> validGuesses = new ArrayList<>();
103100

104101
// Brute-force using pieces
105102
if (pieces != null) {
@@ -220,14 +217,22 @@ private Optional<Integer> calculateOtherSide(int knownSide, boolean isRowKnown,
220217
return Optional.empty();
221218
}
222219

223-
private PartialJigsawInformation fromRowsAndCols(int rows, int cols) {
220+
private JigsawInfo fromRowsAndCols(int rows, int cols) {
224221
int pieces = rows * cols;
225222
int border = (rows == 1 || cols == 1) ? pieces : 2 * (rows + cols - 2);
226223
int inside = pieces - border;
227224
double aspect = (double) cols / rows;
228225
String format = getFormatFromAspect(aspect);
229226

230-
return new PartialJigsawInformation(pieces, border, inside, rows, cols, aspect, format);
227+
return new JigsawInfo.Builder()
228+
.pieces(pieces)
229+
.border(border)
230+
.inside(inside)
231+
.rows(rows)
232+
.columns(cols)
233+
.aspectRatio(aspect)
234+
.format(format)
235+
.build();
231236
}
232237

233238
/**
@@ -239,7 +244,7 @@ private PartialJigsawInformation fromRowsAndCols(int rows, int cols) {
239244
* @param input the original partial input with possibly known values
240245
* @throws IllegalArgumentException if any known value in the input conflicts with the computed result
241246
*/
242-
public void checkConsistencyOrThrow(PartialJigsawInformation computed, PartialJigsawInformation input) {
247+
public void checkConsistencyOrThrow(JigsawInfo computed, JigsawInfo input) {
243248
if (!valuesMatch(computed.getPieces(), input.getPieces()) || !valuesMatch(computed.getBorder(), input.getBorder()) || !valuesMatch(computed.getInside(), input.getInside()) || !valuesMatch(computed.getRows(), input.getRows()) || !valuesMatch(computed.getColumns(), input.getColumns()) || !valuesMatch(computed.getAspectRatio(), input.getAspectRatio()) || !valuesMatch(computed.getFormat(), input.getFormat())) {
244249
throw new IllegalArgumentException("Contradictory data");
245250
}
@@ -255,14 +260,14 @@ public void checkConsistencyOrThrow(PartialJigsawInformation computed, PartialJi
255260
* @param input the original input to check for consistency
256261
* @return an Optional containing a valid inferred configuration, or empty if the guess is invalid
257262
*/
258-
private Optional<PartialJigsawInformation> tryGuessWithFixedSide(int fixed, boolean isRowFixed, double aspect, PartialJigsawInformation input) {
263+
private Optional<JigsawInfo> tryGuessWithFixedSide(int fixed, boolean isRowFixed, double aspect, JigsawInfo input) {
259264
try {
260265
int other = isRowFixed ? roundIfClose(fixed * aspect) : roundIfClose(fixed / aspect);
261266

262267
int rows = isRowFixed ? fixed : other;
263268
int cols = isRowFixed ? other : fixed;
264269

265-
PartialJigsawInformation guess = fromRowsAndCols(rows, cols);
270+
JigsawInfo guess = fromRowsAndCols(rows, cols);
266271
checkConsistencyOrThrow(guess, input);
267272
return Optional.of(guess);
268273
} catch (IllegalArgumentException ignored) {
@@ -279,9 +284,9 @@ private Optional<PartialJigsawInformation> tryGuessWithFixedSide(int fixed, bool
279284
* @param input the original input to check for consistency
280285
* @return an Optional containing a valid configuration, or empty if inconsistent
281286
*/
282-
private Optional<PartialJigsawInformation> tryGuess(int rows, int cols, PartialJigsawInformation input) {
287+
private Optional<JigsawInfo> tryGuess(int rows, int cols, JigsawInfo input) {
283288
try {
284-
PartialJigsawInformation guess = fromRowsAndCols(rows, cols);
289+
JigsawInfo guess = fromRowsAndCols(rows, cols);
285290
checkConsistencyOrThrow(guess, input);
286291
return Optional.of(guess);
287292
} catch (IllegalArgumentException ignored) {
@@ -306,4 +311,20 @@ private <T> boolean valuesMatch(Optional<T> a, Optional<T> b) {
306311

307312
return true;
308313
}
314+
315+
private boolean valuesMatch(OptionalInt a, OptionalInt b) {
316+
if (a.isPresent() && b.isPresent()) {
317+
return a.getAsInt() == b.getAsInt();
318+
}
319+
320+
return true;
321+
}
322+
323+
private boolean valuesMatch(OptionalDouble a, OptionalDouble b) {
324+
if (a.isPresent() && b.isPresent()) {
325+
return Double.compare(a.getAsDouble(), b.getAsDouble()) == 0;
326+
}
327+
328+
return true;
329+
}
309330
}

0 commit comments

Comments
 (0)