oh my
unknown
java
3 years ago
9.2 kB
6
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...