Skip to content

Commit caf4fd8

Browse files
committed
2020 day 20 - changed how hashes are modeled and represented; still not there yet
1 parent 37369d3 commit caf4fd8

File tree

3 files changed

+222
-172
lines changed

3 files changed

+222
-172
lines changed

src/main/java/com/jeffrpowell/adventofcode/Direction.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,14 @@ public Direction opposite()
111111
}
112112
return null;
113113
}
114+
115+
public int rotation90Distance(Direction d) {
116+
int distance = 0;
117+
Direction current = d;
118+
while (current != this) {
119+
distance++;
120+
current = current.rotateRight90();
121+
}
122+
return distance;
123+
}
114124
}

src/main/java/com/jeffrpowell/adventofcode/aoc2020/Day20.java

Lines changed: 148 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
import java.awt.geom.Point2D;
77
import java.util.ArrayDeque;
88
import java.util.ArrayList;
9+
import java.util.Arrays;
910
import java.util.Collection;
10-
import java.util.Collections;
1111
import java.util.HashMap;
1212
import java.util.List;
1313
import java.util.Map;
14+
import java.util.Objects;
15+
import java.util.Set;
1416
import java.util.regex.Matcher;
1517
import java.util.regex.Pattern;
1618
import java.util.stream.Collector;
@@ -56,7 +58,7 @@ protected String part2(List<String> input) {
5658
placedTiles.put(new Point2D.Double(0, 0), tiles.get(firstTile));
5759
System.out.println(tiles.get(firstTile).raw.stream().collect(Collectors.joining("\n")));
5860
System.out.println();
59-
for (Integer hash : tiles.get(firstTile).hashCircle) {
61+
for (Integer hash : tiles.get(firstTile).getAllHashes()) {
6062
List<Tile> matchingTiles = tilesByHash.get(hash);
6163
if (matchingTiles.size() > 1) {
6264
tiles.get(firstTile).setOrientation(Direction.RIGHT, hash);
@@ -94,7 +96,7 @@ protected String part2(List<String> input) {
9496
done = false;
9597
int row = 0;
9698
while (!done) {
97-
int hash = lastPlacedTile.getNextHash(Direction.UP);
99+
int hash = lastPlacedTile.getNextHash(Direction.DOWN);
98100
final long lastId = lastPlacedTile.id;
99101
List<Tile> matchingTiles = tilesByHash.get(hash);
100102
if (matchingTiles.size() == 1) {
@@ -103,7 +105,7 @@ protected String part2(List<String> input) {
103105
else {
104106
row++;
105107
Tile nextTile = matchingTiles.stream().filter(t -> t.id != lastId).findAny().get();
106-
nextTile.setOrientation(Direction.DOWN, hash);
108+
nextTile.setOrientation(Direction.UP, hash);
107109
placedTiles.put(new Point2D.Double(column, row), nextTile);
108110
lastPlacedTile = nextTile;
109111
}
@@ -112,7 +114,7 @@ protected String part2(List<String> input) {
112114
// Set the bottom edge pieces
113115
done = false;
114116
while (!done) {
115-
int hash = lastPlacedTile.getNextHash(Direction.RIGHT);
117+
int hash = lastPlacedTile.getNextHash(Direction.LEFT);
116118
final long lastId = lastPlacedTile.id;
117119
List<Tile> matchingTiles = tilesByHash.get(hash);
118120
if (matchingTiles.size() == 1) {
@@ -121,7 +123,7 @@ protected String part2(List<String> input) {
121123
else {
122124
column--;
123125
Tile nextTile = matchingTiles.stream().filter(t -> t.id != lastId).findAny().get();
124-
nextTile.setOrientation(Direction.LEFT, hash);
126+
nextTile.setOrientation(Direction.RIGHT, hash);
125127
placedTiles.put(new Point2D.Double(column, row), nextTile);
126128
lastPlacedTile = nextTile;
127129
}
@@ -182,13 +184,9 @@ private Map<Integer, List<Tile>> buildTilesByHash(Collection<Tile> tiles) {
182184
tiles.stream().forEach(Tile::calculateHashes);
183185
Map<Integer, List<Tile>> tilesByHash = new HashMap<>();
184186
for (Tile t : tiles) {
185-
for (Integer h : t.hashCircle) {
186-
tilesByHash.putIfAbsent(h, new ArrayList<>());
187-
tilesByHash.get(h).add(t);
188-
}
189-
for (Integer h : t.hashCircleFlipped) {
190-
tilesByHash.putIfAbsent(h, new ArrayList<>());
191-
tilesByHash.get(h).add(t);
187+
for (Integer hash : t.getAllHashes()) {
188+
tilesByHash.putIfAbsent(hash, new ArrayList<>());
189+
tilesByHash.get(hash).add(t);
192190
}
193191
}
194192
return tilesByHash;
@@ -211,11 +209,10 @@ private Map<Tile.Location, List<Long>> classifyTiles(Collection<Tile> tiles, Map
211209
public static class Tile {
212210
public enum Location {CORNER, EDGE, MIDDLE}
213211
List<String> raw;
214-
List<String> rawRotatedCW;
215212
List<String> finalOrientation;
213+
Map<Direction, Integer> finalHashCircle;
216214
long id;
217-
List<Integer> hashCircle;
218-
List<Integer> hashCircleFlipped;
215+
List<Hash> hashCircle;
219216
boolean usingFlippedHashCircle;
220217
Location location;
221218

@@ -224,7 +221,6 @@ public Tile(long id) {
224221
this.raw = new ArrayList<>();
225222
this.location = Location.MIDDLE;
226223
this.hashCircle = new ArrayList<>();
227-
this.hashCircleFlipped = new ArrayList<>();
228224
this.usingFlippedHashCircle = false;
229225
this.finalOrientation = null;
230226
}
@@ -245,39 +241,30 @@ public Location getLocation() {
245241
return location;
246242
}
247243

248-
public void setOrientation(Direction d, int hash) {
249-
List<Integer> hashes;
250-
if (hashCircleFlipped.contains(hash)) {
251-
usingFlippedHashCircle = true;
252-
hashes = hashCircleFlipped;
253-
}
254-
else {
255-
usingFlippedHashCircle = false;
256-
hashes = hashCircle;
257-
}
258-
int hashPosition = hashes.indexOf(hash);
259-
switch (d) {
260-
case LEFT -> {
261-
Collections.rotate(hashes, -hashPosition);
262-
setFinalOrientation(-hashPosition);
263-
}
264-
case UP -> {
265-
Collections.rotate(hashes, -hashPosition + 1);
266-
setFinalOrientation(-hashPosition + 1);
267-
}
268-
case RIGHT -> {
269-
Collections.rotate(hashes, -hashPosition + 2);
270-
setFinalOrientation(-hashPosition + 2);
271-
}
272-
case DOWN -> {
273-
Collections.rotate(hashes, -hashPosition + 3);
274-
setFinalOrientation(-hashPosition + 3);
275-
}
244+
public void calculateHashes() {
245+
List<String> rotatedCW = rotateTileCW(raw);
246+
hashCircle.add(new Hash(rotatedCW.get(0), Direction.LEFT));
247+
hashCircle.add(new Hash(raw.get(0), Direction.UP));
248+
hashCircle.add(new Hash(rotatedCW.get(rotatedCW.size() - 1), Direction.RIGHT));
249+
hashCircle.add(new Hash(raw.get(raw.size() - 1), Direction.DOWN));
250+
for (int i = 0; i < 4; i++) {
251+
hashCircle.get(i).setRightNeighbor(hashCircle.get((i + 1) % 4));
276252
}
277253
}
278254

279-
private void setFinalOrientation(int rotationDistance) {
280-
if (usingFlippedHashCircle) {
255+
public Set<Integer> getAllHashes() {
256+
return Stream.concat(hashCircle.stream().map(h -> h.hash), hashCircle.stream().map(h -> h.flippedHash)).collect(Collectors.toSet());
257+
}
258+
259+
public void setOrientation(Direction d, int hash) {
260+
Hash matchingHash = hashCircle.stream().filter(h -> h.containsHash(hash)).findAny().get();
261+
int rotationDistance = matchingHash.rotateTo(d, d);
262+
setFinalOrientation(rotationDistance, matchingHash.flippedFromRaw);
263+
}
264+
265+
private void setFinalOrientation(int rotationDistance, boolean flippedFromRaw) {
266+
List<String> rawRotatedCW = rotateTileCW(raw);
267+
if (flippedFromRaw) {
281268
finalOrientation = switch (rotationDistance) {
282269
case 0, 4, -4 -> raw.stream().map(Tile::reverseString).collect(Collectors.toList());
283270
case 1, -3 -> reverseStream(rawRotatedCW.stream()).collect(Collectors.toList());
@@ -309,45 +296,14 @@ private static <T> Stream<T> reverseStream(Stream<T> s) {
309296
}
310297

311298
public int getNextHash(Direction d) {
312-
List<Integer> hashes;
313-
if (usingFlippedHashCircle) {
314-
hashes = hashCircleFlipped;
315-
}
316-
else {
317-
hashes = hashCircle;
318-
}
319-
return switch (d) {
320-
case LEFT -> hashes.get(0);
321-
case UP -> hashes.get(1);
322-
case RIGHT -> hashes.get(2);
323-
case DOWN -> hashes.get(3);
324-
default -> hashes.get(0);
325-
};
299+
Hash matchingHash = hashCircle.stream().filter(h -> h.getDirection() == d).findAny().get();
300+
return matchingHash.getHash();
326301
}
327302

328-
public void calculateHashes() {
329-
rawRotatedCW = rotateTileCW();
330-
hashCircle.add(rawRotatedCW.get(0).hashCode());
331-
hashCircle.add(raw.get(0).hashCode());
332-
hashCircle.add(reverseString(rawRotatedCW.get(rawRotatedCW.size() - 1)).hashCode());
333-
hashCircle.add(reverseString(raw.get(raw.size() - 1)).hashCode());
334-
hashCircleFlipped.add(rawRotatedCW.get(rawRotatedCW.size() - 1).hashCode());
335-
hashCircleFlipped.add(reverseString(raw.get(0)).hashCode());
336-
hashCircleFlipped.add(reverseString(rawRotatedCW.get(0)).hashCode());
337-
hashCircleFlipped.add(raw.get(raw.size() - 1).hashCode());
338-
if (Stream.concat(hashCircle.stream(), hashCircleFlipped.stream()).distinct().count() < 8L) {
339-
System.out.println(this.id + " has duplicate edge hashes!");
340-
}
341-
}
342-
343-
private static String reverseString(String s) {
344-
return new StringBuilder(s).reverse().toString();
345-
}
346-
347-
private List<String> rotateTileCW() {
348-
char[][] mat = new char[raw.size()][];
349-
for (int i = 0; i < raw.size(); i++) {
350-
mat[i] = raw.get(i).toCharArray();
303+
private List<String> rotateTileCW(List<String> image) {
304+
char[][] mat = new char[image.size()][];
305+
for (int i = 0; i < image.size(); i++) {
306+
mat[i] = image.get(i).toCharArray();
351307
}
352308
//https://stackoverflow.com/a/2800033
353309
final int M = mat.length;
@@ -358,11 +314,11 @@ private List<String> rotateTileCW() {
358314
ret[c][M-1-r] = mat[r][c];
359315
}
360316
}
361-
List<String> rotatedTile = new ArrayList<>();
362-
for (int i = 0; i < ret.length; i++) {
363-
rotatedTile.add(new String(ret[i]));
364-
}
365-
return rotatedTile;
317+
return Arrays.stream(ret).map(String::new).collect(Collectors.toList());
318+
}
319+
320+
private static String reverseString(String s) {
321+
return new StringBuilder(s).reverse().toString();
366322
}
367323

368324
@Override
@@ -374,5 +330,108 @@ public String toString() {
374330
return finalOrientation.stream().map(s -> " " + s + " ").collect(Collectors.joining("\n"));
375331
}
376332
}
333+
334+
static class Hash {
335+
int hash;
336+
int flippedHash;
337+
Direction direction;
338+
boolean flipped;
339+
boolean flippedFromRaw;
340+
private static final Set<Direction> TOP_LEFT = Set.of(Direction.UP, Direction.LEFT);
341+
private static final Set<Direction> BOTTOM_RIGHT = Set.of(Direction.DOWN, Direction.RIGHT);
342+
Hash rightNeighbor;
343+
344+
public Hash(String edge, Direction direction) {
345+
this.direction = direction;
346+
if (direction == Direction.DOWN || direction == Direction.RIGHT) {
347+
this.flippedHash = edge.hashCode();
348+
this.hash = reverseString(edge).hashCode();
349+
this.flipped = true;
350+
}
351+
else {
352+
this.hash = edge.hashCode();
353+
this.flippedHash = reverseString(edge).hashCode();
354+
this.flipped = false;
355+
}
356+
}
357+
358+
public void setRightNeighbor(Hash rightNeighbor) {
359+
this.rightNeighbor = rightNeighbor;
360+
}
361+
362+
public int getHash() {
363+
return flipped ? flippedHash : hash;
364+
}
365+
366+
public Direction getDirection() {
367+
return direction;
368+
}
369+
370+
public boolean containsHash(int hashcode) {
371+
return hash == hashcode || flippedHash == hashcode;
372+
}
373+
374+
public boolean isFlippedFromRaw() {
375+
return flippedFromRaw;
376+
}
377+
378+
/**
379+
*
380+
* @param originalNewSide
381+
* @param newSide
382+
* @return rotationDistance, +CW -CCW
383+
*/
384+
public int rotateTo(Direction originalNewSide, Direction newSide) {
385+
int rotationDistance = direction.rotation90Distance(newSide);
386+
if (rotationDistance == 0) {
387+
return 0;
388+
}
389+
if (TOP_LEFT.contains(direction) && BOTTOM_RIGHT.contains(newSide) ||
390+
BOTTOM_RIGHT.contains(direction) && TOP_LEFT.contains(newSide)) {
391+
flipped = !flipped;
392+
flippedFromRaw = true;
393+
}
394+
direction = newSide;
395+
Direction neighborsNewSide = newSide.rotateRight90();
396+
if (neighborsNewSide != originalNewSide) {
397+
rightNeighbor.rotateTo(originalNewSide, neighborsNewSide);
398+
}
399+
return rotationDistance;
400+
}
401+
402+
@Override
403+
public int hashCode() {
404+
int h = 7;
405+
h = 41 * h + this.hash;
406+
h = 41 * h + this.flippedHash;
407+
h = 41 * h + Objects.hashCode(this.direction);
408+
h = 41 * h + (this.flipped ? 1 : 0);
409+
return h;
410+
}
411+
412+
@Override
413+
public boolean equals(Object obj) {
414+
if (this == obj) {
415+
return true;
416+
}
417+
if (obj == null) {
418+
return false;
419+
}
420+
if (getClass() != obj.getClass()) {
421+
return false;
422+
}
423+
final Hash other = (Hash) obj;
424+
if (this.hash != other.hash) {
425+
return false;
426+
}
427+
if (this.flippedHash != other.flippedHash) {
428+
return false;
429+
}
430+
if (this.flipped != other.flipped) {
431+
return false;
432+
}
433+
return this.direction == other.direction;
434+
}
435+
}
377436
}
378437
}

0 commit comments

Comments
 (0)