oh my
unknown
java
2 years ago
9.2 kB
3
Indexable
package org.aliance3; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang3.tuple.Pair; import org.glassfish.jersey.internal.guava.Maps; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import static java.util.Objects.requireNonNullElseGet; import static java.util.function.Predicate.not; public class IslandSolutionTest { @Test public void test() { var cols = 48; var rows = 20; var from = Island.Point.of(1, 1); var to = Island.Point.of(cols, rows); Island island; do { island = Island.newInstance(rows, cols); } while (!island.pathExists(from, to)); System.out.println(island); var pathOnMap = island.drawPathOnMap(from, to); System.out.println(pathOnMap); } } @FunctionalInterface interface Job { void doJob(); } class Island implements Iterable<Island.Point> { private final int rows; private final int cols; private int[][] mass; private List<Point> path; private Queue<Point> queue; private Map<Point, Point> map; public static Island newInstance(int rows, int cols) { return new Island(rows, cols); } private Island(int rows, int cols) { this.rows = rows; this.cols = cols; this.mass = generateMass(cols, rows); } public int countIslands() { return supplyWithoutModification(this::countIslandsModifying); } public boolean pathExists(Point from, Point to) { return !findPath(from, to).isEmpty(); } public List<Point> findPath(Point from, Point to) { if (isInBounds(from) && isInBounds(to)) { if (from.equals(to)) { return List.of(from); } Supplier<List<Point>> pathSupplier = () -> findPathModifying(from, to); return requireNonNullElseGet(path, () -> path = supplyWithoutModification(pathSupplier)); } throw new IllegalArgumentException(); } private List<Point> findPathModifying(Point from, Point to) { initCollections(); queue.add(from); markVisited(from); while (!queue.isEmpty() && !queue.peek().equals(to)) { final var curr = queue.poll(); neighbours(curr).stream() .peek(p -> map.putIfAbsent(p, curr)) .peek(this::markVisited) .forEach(queue::add); } var path = traverseThroughMap(to, map); clearCollections(); return path; } private void initCollections() { queue = requireNonNullElseGet(queue, () -> new ArrayDeque<>(cols * rows)); map = requireNonNullElseGet(map, () -> Maps.newHashMapWithExpectedSize(cols * rows)); } private void clearCollections() { queue.clear(); map.clear(); } @NotNull private List<Point> traverseThroughMap(Point start, Map<Point, Point> map) { var path = Stream.iterate(start, Objects::nonNull, map::get) .collect(Collectors.toCollection(ArrayList::new)); if (path.size() == 1) { return List.of(); } Collections.reverse(path); return path; } private void markVisited(Point point) { set(point, 0); } private int countIslandsModifying() { var k = new AtomicInteger(0); forEach(point -> { var val = get(point); if (val == 1) { k.incrementAndGet(); eraseIsland(point); } }); return k.get(); } private <T> T supplyWithoutModification(Supplier<T> f) { var holder = new AtomicReference<T>(); doWithoutModification(() -> { var res = f.get(); holder.set(res); }); return holder.get(); } private void doWithoutModification(Job job) { var copy = deepCopy(mass); job.doJob(); mass = copy; } private void eraseIsland(Point point) { markVisited(point); neighbours(point).forEach(this::eraseIsland); } private List<Point> neighbours(Point point) { var x = point.x(); var y = point.y(); var directions = new ArrayList<>(List.of( Point.of(x + 1, y), Point.of(x, y + 1), Point.of(x - 1, y), Point.of(x, y - 1) )); directions.removeIf(not(this::isValidAndNotMarked)); return directions; } private int get(Point point) { return get(point.x(), point.y()); } public void set(Point point, int val) { set(point.x(), point.y(), val); } private boolean isInBounds(Point point) { if (point.x() < 1 || point.x() > cols) { return false; } return point.y() >= 1 && point.y() <= rows; } private boolean isValidAndNotMarked(Point point) { return isInBounds(point) && get(point.x(), point.y()) != 0; } @Override public String toString() { var builder = new StringBuilder(rows * cols); forEachBiConsumer((x, y) -> { var val = get(x, y); if (x != 1) { builder.append(" "); } if (val == 1) { builder.append("+"); } else { builder.append("·"); } if (x == cols) { builder.append("\n"); } }); builder.append("Islands: ").append(countIslands()); return builder.toString(); } public String drawPathOnMap(Point from, Point to) { var builder = new StringBuilder(rows * cols); var pathOrdered = findPath(from, to); var path = new HashSet<>(pathOrdered); forEach(point -> { var val = get(point); if (point.x() != 1) { builder.append(" "); } if (path.contains(point)) { builder.append("0"); } else if (val == 1) { builder.append("+"); } else { builder.append("·"); } if (point.x() == cols) { builder.append("\n"); } }); builder.append("Path: ").append(pathOrdered); return builder.toString(); } private void forEachBiConsumer(BiConsumer<Integer, Integer> consumer) { forEach(point -> consumer.accept(point.x(), point.y())); } private int[][] generateMass(int xS, int yS) { mass = new int[xS][yS]; for (int i = 0; i < xS; ++i) { mass[i] = new int[yS]; } forEach(point -> set(point, coin())); return mass; } private int coin() { return ThreadLocalRandom.current().nextInt(100) > 50 ? 1 : 0; } private int get(int x, int y) { return mass[x - 1][y - 1]; } private void set(int x, int y, int val) { mass[x - 1][y - 1] = val; } private static int[][] deepCopy(int[][] matrix) { return Arrays.stream(matrix).map(el -> el.clone()).toArray($ -> matrix.clone()); } @NotNull @Override public Iterator<Point> iterator() { return new Iter(); } private class Iter implements Iterator<Point> { private int x = -1; private int y = 0; @Override public boolean hasNext() { return x != cols - 1 || y != rows - 1; } @Override public Point next() { if (x != cols - 1) { x += 1; } else { x = 0; y += 1; } return Point.of(x + 1, y + 1); } } public static class Point { private final Pair<Integer, Integer> pair; public static Point of(int x, int y) { return new Point(x, y); } private Point(int x, int y) { this.pair = Pair.of(x, y); } public int x() { return pair.getLeft(); } public int y() { return pair.getRight(); } @Override public int hashCode() { return pair.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj instanceof Point point) { return x() == point.x() && y() == point.y(); } return false; } @Override public String toString() { return String.format("(%d, %d)", x(), y()); } } }
Editor is loading...