oh my

 avatar
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...