জাভা - কিছুটা স্মার্ট / দ্রুত
বেশ কয়েকটি কোড এখানে। আমি "ধন পথে কোনও উপায় মুক্ত করার পক্ষে এটি কতটা সম্ভব" এর ক্রম হিসাবে ধাক্কাগুলি মূল্যায়নের মাধ্যমে দ্রুত হওয়ার চেষ্টা করছি, যা নিজেই দুটি ডিজকস্ট্রার ট্রভারসালের উপর ভিত্তি করে (একটি শিলার মুখোমুখি হয়ে থামলে, অন্যটি পাথর উপেক্ষা করে)। এটি বেশ সুন্দরভাবে কাজ করছে, এবং পেস্টবিনের একটি উদাহরণ যা লেখকের জন্য কষ্টকর বলে মনে হচ্ছে এই প্রয়োগের মাধ্যমে 2 সেকেন্ডের মধ্যে সমাধান করা হয়েছে। কিছু অন্যান্য উদাহরণ 30-40 সেকেন্ড সময় নেয়, যা আমি এখনও অনেক দীর্ঘ খুঁজে পেয়েছি, তবে জিনিসটি না ভেঙে আমি এর উন্নতির কোনও উপায় খুঁজে পাই না :)
আমি আরও ভাল কাঠামো বের করার জন্য আমার জিনিসগুলি বেশ কয়েকটি ফাইলে বিভক্ত করেছি (এছাড়াও কেন আমি রুবি থেকে জাভাতে স্যুইচ করেছি):
প্রবেশের পয়েন্ট:
import java.util.Date;
public class IndianaJones {
public static void main(final String[] args) throws Exception {
final Maze maze = new Maze(System.in);
final Date startAt = new Date();
final int solution = maze.solve();
final Date endAt = new Date();
System.out.printf("Found solution: %s in %d ms.",
solution < Integer.MAX_VALUE ? solution : "X",
endAt.getTime() - startAt.getTime());
}
}
দিকনির্দেশনা সহায়ক এনাম:
enum Direction {
UP(-1, 0), DOWN(1, 0), LEFT(0, -1), RIGHT(0, 1);
public final int drow;
public final int dcol;
private Direction(final int drow, final int dcol) {
this.drow = drow;
this.dcol = dcol;
}
public final Direction opposite() {
switch (this) {
case UP:
return DOWN;
case DOWN:
return UP;
case LEFT:
return RIGHT;
case RIGHT:
return LEFT;
}
return null;
}
}
"ধাঁধাঁ" এর একটি অবস্থিত অংশ উপস্থাপন করার জন্য একটি বিমূর্ত শ্রেণি:
abstract class PointOfInterest {
public final int row;
public final int col;
protected PointOfInterest(final int row, final int col) {
this.row = row;
this.col = col;
}
public final boolean isAt(final int row, final int col) {
return this.row == row && this.col == col;
}
@Override
public final String toString() {
return getClass().getSimpleName() + "(" + row + ", " + col + ")";
}
@Override
public final int hashCode() {
return row ^ col;
}
@Override
public final boolean equals(Object obj) {
if (this == obj)
return true;
if (!(obj instanceof PointOfInterest))
return false;
if (!getClass().equals(obj.getClass()))
return false;
final PointOfInterest other = (PointOfInterest) obj;
return row == other.row && col == other.col;
}
}
এবং অবশেষে, গোলকধাঁধা নিজেই:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
public class Maze {
private static final char WALL = '1';
private static final char INDY = '2';
private static final char GOAL = '3';
private static final char ROCK = '4';
private final Maze parent;
private final Set<Maze> visited;
private final boolean[][] map;
private final int[][] dijkstra;
private int[][] dijkstraGhost;
private String stringValue = null;
private int shortestSolution = Integer.MAX_VALUE;
private Goal goal = null;
private Indy indy = null;
private Set<Rock> rocks = new HashSet<>();
private Maze(final Maze parent, final Rock rock, final Direction direction) {
this.parent = parent;
this.visited = parent.visited;
map = parent.map;
dijkstra = new int[map.length][map[rock.row].length];
for (final int[] part : dijkstra)
Arrays.fill(part, Integer.MAX_VALUE);
goal = new Goal(parent.goal.row, parent.goal.col);
indy = new Indy(rock.row, rock.col);
for (final Rock r : parent.rocks)
if (r == rock)
rocks.add(new Rock(r.row + direction.drow, r.col + direction.dcol));
else
rocks.add(new Rock(r.row, r.col));
updateDijkstra(goal.row, goal.col, 0, true);
}
public Maze(final InputStream is) {
this.parent = null;
this.visited = new HashSet<>();
try (final BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String line = br.readLine();
final String[] sizeParts = line.split(" ");
final int height = Integer.parseInt(sizeParts[0]);
final int width = Integer.parseInt(sizeParts[1]);
map = new boolean[height][width];
dijkstra = new int[height][width];
int row = 0;
while ((line = br.readLine()) != null) {
for (int col = 0; col < line.length(); col++) {
final char c = line.charAt(col);
map[row][col] = c == WALL;
dijkstra[row][col] = Integer.MAX_VALUE;
if (c == INDY) {
if (indy != null)
throw new IllegalStateException("Found a second indy!");
indy = new Indy(row, col);
} else if (c == GOAL) {
if (goal != null)
throw new IllegalStateException("Found a second treasure!");
goal = new Goal(row, col);
} else if (c == ROCK) {
rocks.add(new Rock(row, col));
}
}
row++;
}
updateDijkstra(goal.row, goal.col, 0, true);
} catch (final IOException ioe) {
throw new RuntimeException("Could not read maze from InputStream", ioe);
}
}
public int getShortestSolution() {
Maze ptr = this;
while (ptr.parent != null)
ptr = ptr.parent;
return ptr.shortestSolution;
}
public void setShortestSolution(int shortestSolution) {
Maze ptr = this;
while (ptr.parent != null)
ptr = ptr.parent;
ptr.shortestSolution = Math.min(ptr.shortestSolution, shortestSolution);
}
private final boolean isRepeat(final Maze maze) {
return this.visited.contains(maze);
}
private final void updateDijkstra(final int row, final int col, final int value, final boolean force) {
if (row < 0 || col < 0 || row >= dijkstra.length || col >= dijkstra[row].length)
return;
if (map[row][col] || isRockPresent(row, col))
return;
if (dijkstra[row][col] <= value && !force)
return;
dijkstra[row][col] = value;
updateDijkstra(row - 1, col, value + 1, false);
updateDijkstra(row + 1, col, value + 1, false);
updateDijkstra(row, col - 1, value + 1, false);
updateDijkstra(row, col + 1, value + 1, false);
}
private final void updateDijkstraGhost(final int row, final int col, final int value, final boolean force) {
if (row < 0 || col < 0 || row >= dijkstra.length || col >= dijkstra[row].length)
return;
if (map[row][col] || isRockPresent(row, col))
return;
if (dijkstraGhost[row][col] <= value && !force)
return;
dijkstraGhost[row][col] = value;
updateDijkstraGhost(row - 1, col, value + 1, false);
updateDijkstraGhost(row + 1, col, value + 1, false);
updateDijkstraGhost(row, col - 1, value + 1, false);
updateDijkstraGhost(row, col + 1, value + 1, false);
}
private final int dijkstraScore(final int row, final int col) {
if (row < 0 || col < 0 || row >= dijkstra.length || col >= dijkstra[row].length)
return Integer.MAX_VALUE;
return dijkstra[row][col];
}
private final int dijkstraGhostScore(final int row, final int col) {
if (dijkstraGhost == null) {
dijkstraGhost = new int[map.length][map[indy.row].length];
for (final int[] part : dijkstraGhost)
Arrays.fill(part, Integer.MAX_VALUE);
updateDijkstraGhost(goal.row, goal.col, 0, true);
}
if (row < 0 || col < 0 || row >= dijkstra.length || col >= dijkstra[row].length)
return Integer.MAX_VALUE;
return dijkstraGhost[row][col];
}
private boolean isRockPresent(final int row, final int col) {
for (final Rock rock : rocks)
if (rock.isAt(row, col))
return true;
return false;
}
public boolean isEmpty(final int row, final int col) {
if (row < 0 || col < 0 || row >= map.length || col >= map[row].length)
return false;
return !map[row][col] && !isRockPresent(row, col) && !goal.isAt(row, col);
}
public int solve() {
return solve(0);
}
private int solve(final int currentDepth) {
System.out.println(toString());
visited.add(this);
if (isSolved()) {
setShortestSolution(currentDepth);
return 0;
}
if (currentDepth >= getShortestSolution()) {
System.out.println("Aborting at depth " + currentDepth + " because we know better: "
+ getShortestSolution());
return Integer.MAX_VALUE;
}
final Map<Rock, Set<Direction>> nextTries = indy.getMoveableRocks();
int shortest = Integer.MAX_VALUE - 1;
for (final Map.Entry<Rock, Set<Direction>> tries : nextTries.entrySet()) {
final Rock rock = tries.getKey();
for (final Direction dir : tries.getValue()) {
final Maze next = new Maze(this, rock, dir);
if (!isRepeat(next)) {
final int nextSolution = next.solve(currentDepth + 1);
if (nextSolution < shortest)
shortest = nextSolution;
}
}
}
return shortest + 1;
}
public boolean isSolved() {
return indy.canReachTreasure();
}
@Override
public String toString() {
if (stringValue == null) {
final StringBuilder out = new StringBuilder();
for (int row = 0; row < map.length; row++) {
if (row == 0) {
out.append('\u250C');
for (int col = 0; col < map[row].length; col++)
out.append('\u2500');
out.append("\u2510\n");
}
out.append('\u2502');
for (int col = 0; col < map[row].length; col++) {
if (indy.isAt(row, col))
out.append('*');
else if (goal.isAt(row, col))
out.append("$");
else if (isRockPresent(row, col))
out.append("@");
else if (map[row][col])
out.append('\u2588');
else
out.append(base64(dijkstra[row][col]));
}
out.append("\u2502\n");
if (row == map.length - 1) {
out.append('\u2514');
for (int col = 0; col < map[row].length; col++)
out.append('\u2500');
out.append("\u2518\n");
}
}
stringValue = out.toString();
}
return stringValue;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!obj.getClass().equals(getClass()))
return false;
final Maze other = (Maze) obj;
if (other.map.length != map.length)
return false;
for (int row = 0; row < map.length; row++) {
if (other.map[row].length != map[row].length)
return false;
for (int col = 0; col < map[row].length; col++)
if (other.map[row][col] != map[row][col])
return false;
}
return indy.equals(other.indy) && rocks.equals(other.rocks) && goal.equals(other.goal);
}
@Override
public int hashCode() {
return getClass().hashCode() ^ indy.hashCode() ^ goal.hashCode() ^ rocks.hashCode();
}
private final class Goal extends PointOfInterest {
public Goal(final int row, final int col) {
super(row, col);
}
}
private final class Indy extends PointOfInterest {
public Indy(final int row, final int col) {
super(row, col);
}
public boolean canReachTreasure() {
return dijkstraScore(row, col) < Integer.MAX_VALUE;
}
public SortedMap<Rock, Set<Direction>> getMoveableRocks() {
final SortedMap<Rock, Set<Direction>> out = new TreeMap<>();
@SuppressWarnings("unchecked")
final Set<Direction> checked[][] = new Set[map.length][map[row].length];
lookForRocks(out, checked, row, col, null);
return out;
}
private final void lookForRocks(final Map<Rock, Set<Direction>> rockStore,
final Set<Direction>[][] checked,
final int row,
final int col,
final Direction comingFrom) {
if (row < 0 || col < 0 || row >= checked.length || col >= checked[row].length)
return;
if (checked[row][col] == null)
checked[row][col] = EnumSet.noneOf(Direction.class);
if (checked[row][col].contains(comingFrom))
return;
for (final Rock rock : rocks) {
if (rock.row == row && rock.col == col) {
if (rock.canBeMoved(comingFrom) && rock.isWorthMoving(comingFrom)) {
if (!rockStore.containsKey(rock))
rockStore.put(rock, EnumSet.noneOf(Direction.class));
rockStore.get(rock).add(comingFrom);
}
return;
}
}
if (comingFrom != null)
checked[row][col].add(comingFrom);
for (final Direction dir : Direction.values())
if (comingFrom == null || dir != comingFrom.opposite())
if (isEmpty(row + dir.drow, col + dir.dcol) || isRockPresent(row + dir.drow, col + dir.dcol))
lookForRocks(rockStore, checked, row + dir.drow, col + dir.dcol, dir);
}
}
private final class Rock extends PointOfInterest implements Comparable<Rock> {
public Rock(final int row, final int col) {
super(row, col);
}
public boolean canBeMoved(final Direction direction) {
return isEmpty(row + direction.drow, col + direction.dcol);
}
public boolean isWorthMoving(final Direction direction) {
boolean worthIt = false;
boolean reachable = false;
int emptyAround = 0;
for (final Direction dir : Direction.values()) {
reachable |= (dijkstraScore(row, col) < Integer.MAX_VALUE);
emptyAround += (isEmpty(row + dir.drow, col + dir.dcol) ? 1 : 0);
if (dir != direction && dir != direction.opposite()
&& dijkstraScore(row + dir.drow, col + dir.dcol) < Integer.MAX_VALUE)
worthIt = true;
}
return (emptyAround < 4) && (worthIt || !reachable);
}
public int proximityIndice() {
final int ds = min(dijkstraScore(row - 1, col),
dijkstraScore(row + 1, col),
dijkstraScore(row, col - 1),
dijkstraScore(row, col + 1));
if (ds < Integer.MAX_VALUE)
return ds;
else
return min(dijkstraGhostScore(row - 1, col),
dijkstraGhostScore(row + 1, col),
dijkstraGhostScore(row, col - 1),
dijkstraGhostScore(row, col + 1));
}
@Override
public int compareTo(Rock o) {
return new Integer(proximityIndice()).compareTo(o.proximityIndice());
}
}
private static final char base64(final int i) {
if (i >= 0 && i <= 9)
return (char) ('0' + i);
else if (i < 36)
return (char) ('A' + (i - 10));
else
return ' ';
}
private static final int min(final int i1, final int i2, final int... in) {
int min = Math.min(i1, i2);
for (final int i : in)
min = Math.min(min, i);
return min;
}
}