230 794,38 na biegach 20 x 20, 100 000
Ostatnia aktualizacja: W końcu zbudowałem idealne dynamiczne rozwiązanie 2-ścieżkowe. Powiedziałem doskonale, ponieważ poprzednia wersja faktycznie nie jest symetryczna, łatwiej było uzyskać dłuższą ścieżkę, jeśli pijak przeszedł jedną ścieżkę nad drugą. Obecny jest symetryczny, dzięki czemu może uzyskać wyższą oczekiwaną liczbę kroków. Po kilku próbach wydaje się, że wynosi około 230 tys., Co oznacza poprawę w stosunku do poprzedniego, wynoszącą około 228 tys. Ale statystycznie rzecz biorąc liczby te wciąż mieszczą się w swoich ogromnych odchyleniach, więc nie twierdzę, że jest to znacznie lepsze, ale uważam, że powinno to być lepsze niż poprzednia wersja.
Kod znajduje się na dole tego postu. Jest aktualizowany, dzięki czemu jest znacznie szybszy niż poprzednia wersja, wykonując 1000 uruchomień w 23s.
Poniżej znajduje się przykładowy przebieg i przykładowy labirynt:
Idealny piechur
Średnia: 230794,384
Maks .: 1514506
Min: 25860
Ukończone w 2317.374s
_ _ _ _ _ _ _ _ _ _ _ _ _.
| | | | | | | | | | | | | | | _ _ _ _
| | | | | | | | | | | | | | | | _ _ _ _
| | | | | | | | | | | | | | | _ _ _ _ |
| | | | | | | | | | | | | | | | _ _ _ _
| | | | | | | | | | | | | | | _ _ _ _ |
| | | | | | | | | | | | | | | | _ _ _ _
| | | | | | | | | | | | | | | _ _ _ _ |
| | | | | | | | | | | | | | _ | | _ _ _ _
| | | | | | | | | | | | | _ _ _ _ _ _ |
| | | | | | | | | | | | | | _ _ _ _ _ _
| | | | | | | | | | | | | _ _ _ _ _ _ |
| | | | | | | | | | | | | | _ _ _ _ _ _
| | | | | | | | | | | | | _ _ _ _ _ _ |
| | | | | | _ | | _ | | _ | | _ | | _ _ _ _ _ _
| | | | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | | | | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| | | | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ | | _ | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Poprzednie zgłoszenia
Wreszcie mogę dopasować wynik Sparra! = D.
Opierając się na moich wcześniejszych eksperymentach (patrz na dole tego postu), najlepszą strategią jest posiadanie podwójnej ścieżki i zamknięcie jednej, gdy pijak dotrze do dowolnego z nich, a zmienna wynika z tego, jak dobrze możemy dynamicznie przewidzieć, dokąd pójdzie pijak zwiększyć szansę, że dostanie się na dłuższą ścieżkę.
Tak więc w oparciu o moją DOUBLE_PATH
strategię zbudowałem kolejny, który zmienia labirynt (mój DOUBLE_PATH
labirynt można łatwo modyfikować) w zależności od ruchu pijaka. Gdy będzie podążał ścieżką z więcej niż jedną dostępną opcją, zamknę ścieżki tak, aby pozostawić tylko dwie możliwe opcje (jedna, z której przyszedł, druga nietrawerowana).
Brzmi to podobnie do tego, co osiągnął Sparr, jak pokazuje wynik. Różnica w stosunku do jego jest zbyt mała, aby uznać ją za lepszą, ale powiedziałbym, że moje podejście jest bardziej dynamiczne niż on, ponieważ mój labirynt jest bardziej modyfikowalny niż Sparra =)
Wynik z przykładowym labiryntem końcowym:
EXTREME_DOUBLE_PATH
Średnia: 228034,89
Maks .: 1050816
Min: 34170
Ukończone w 396,728s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Sekcja Eksperymentów
Najlepsza okazuje się ta sama strategia co stokastic, jestem dumny z eksperymentowania przy użyciu różnych strategii i drukowania dobrych wyników :)
Każdy z drukowanych labiryntów poniżej jest ostatnim labiryntem po dotarciu pijaka do domu, więc mogą się nieznacznie różnić od biegu do biegu ze względu na losowość ruchów pijaka i dynamikę przeciwnika.
Opiszę każdą strategię:
Jedna ścieżka
Jest to najprostsze podejście, które utworzy jedną ścieżkę od wejścia do wyjścia.
SINGLE_PATH
Średnia: 162621.612
Maks .: 956694
Min: 14838
Ukończone w 149,430
_ _ _ _ _ _ _ _ _ _ _
| | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Wyspa (poziom 0)
To podejście próbuje złapać pijaka w prawie odizolowanej wyspie. Nie działa tak dobrze, jak się spodziewałem, ale jest to jeden z moich pierwszych pomysłów, więc go uwzględniam.
Do wyjścia prowadzą dwie ścieżki, a gdy pijak zbliża się do jednej z nich, przeciwnik zamyka ją, zmuszając go do znalezienia drugiego wyjścia (i prawdopodobnie ponownie uwięziony na wyspie)
WYSPA
Średnia: 74626,070
Maks .: 428560
Min: 1528
Ukończone w 122.512s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Podwójna ścieżka
Jest to najczęściej dyskutowana strategia, polegająca na posiadaniu dwóch ścieżek o równej długości do wyjścia i zamykaniu jednej z nich, gdy pijak zbliży się do jednej z nich.
DOUBLE_PATH
Średnia: 197743,472
Maks .: 1443406
Min: 21516
Ukończone w 308,177
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Wyspa (poziom 1)
Zainspirowani wieloma ścieżkami wyspy i wysoką liczbą kroków w pojedynczej ścieżce, łączymy wyspę z wyjściem i wykonujemy labirynt pojedynczej ścieżki na wyspie, tworząc w sumie trzy ścieżki wyjścia i podobnie jak w poprzednim przypadku, zamknij dowolną z wyjdź, gdy pijak się zbliży.
Działa to nieco lepiej niż czysta pojedyncza ścieżka, ale nadal nie pokonuje podwójnej ścieżki.
WYSPA
Średnia: 166265.132
Maks .: 1162966
Min: 19544
Ukończone w 471,982s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ | _
| | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Wyspa (poziom 2)
Próbując rozwinąć poprzedni pomysł, stworzyłem zagnieżdżoną wyspę, tworząc w sumie pięć ścieżek, ale wydaje się, że to nie działa dobrze.
WYSPA
Średnia: 164222,712
Maks .: 927608
Min: 22024
Ukończone w 793,591
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _
| | _ _ _ _ _ _ _ _ | _ | |
| | | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| _ | _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Wyspa (poziom 3)
Zauważając, że podwójna ścieżka faktycznie działa lepiej niż pojedyncza ścieżka, zróbmy wyspę podwójną ścieżką!
Rezultatem jest poprawa w stosunku do wyspy (poziom 1), ale wciąż nie pokonuje podwójnej ścieżki.
Dla porównania wynik dla podwójnej ścieżki wielkości wyspy wynosi średnio 131 134,42 ruchów. To dodaje dość znacznej liczby ruchów (około 40k), ale nie wystarcza, aby pokonać podwójną ścieżkę.
WYSPA
Średnia: 171730,090
Maks .: 769080
Min: 29760
Ukończone w 587,646
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ | |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Wyspa (poziom 4)
Ponownie, eksperymentowanie z zagnieżdżoną wyspą i znowu to nie działa tak dobrze.
WYSPA
Średnia: 149723.068
Maks .: 622106
Min: 25752
Ukończone w 830.889s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ |
| | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | | |
| | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | | |
| | _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Wniosek
Podsumowując, dowodzi to, że posiadanie pojedynczej długiej ścieżki od aktualnej pozycji pijaka do wyjścia działa najlepiej, co osiąga się dzięki strategii podwójnej ścieżki, ponieważ po zamknięciu wyjścia pijak będzie musiał przebyć maksymalną możliwą odległość, aby dostać się do wyjście.
To dalsze wskazówki, że podstawową strategią powinna nadal być podwójna ścieżka, i możemy jedynie modyfikować dynamikę tworzenia ścieżek, co zrobiło Sparr. Uważam więc, że jego strategia jest najlepsza!
Kod
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.TreeSet;
public class Walker {
enum Strategy{
SINGLE_PATH,
ISLAND,
DOUBLE_PATH,
EXTREME_DOUBLE_PATH,
PERFECT_DOUBLE_PATH,
}
int width,height;
int x,y; //walker's position
int dX,dY; //destination
Point[][] points;
int stepCount = 0;
public static void main(String[]args){
int side = 20;
// runOnce(side, Strategy.EXTREME_DOUBLE_PATH, 0);
runOnce(side, Strategy.PERFECT_DOUBLE_PATH, 0);
// for(Strategy strategy: Strategy.values()){
// runOnce(side, strategy, 0);
// }
// runOnce(side, Strategy.ISLAND, 1);
// runOnce(side, Strategy.ISLAND, 2);
// Scanner scanner = new Scanner(System.in);
// System.out.println("Enter side, strategy (SINGLE_PATH, ISLAND, DOUBLE_PATH, EXTREME_DOUBLE_PATH), and level:");
// while(scanner.hasNext()){
// side = scanner.nextInt();
// Strategy strategy = Strategy.valueOf(scanner.next());
// int level = scanner.nextInt();
// scanner.nextLine();
// runOnce(side, strategy, level);
// System.out.println("Enter side, strategy (SINGLE_PATH, ISLAND, DOUBLE_PATH, EXTREME_DOUBLE_PATH), and level:");
// }
// scanner.close();
}
private static Walker runOnce(int side, Strategy strategy, int level) {
Walker walker = null;
long total = 0;
int max = 0;
int min = Integer.MAX_VALUE;
double count = 1000;
long start = System.currentTimeMillis();
for(int i=0; i<count; i++){
walker = new Walker(0,0,side,side,side-1,side-1, strategy, level, false);
total += walker.stepCount;
max = Math.max(walker.stepCount, max);
min = Math.min(walker.stepCount, min);
// System.out.println("Iteration "+i+": "+walker.stepCount);
}
System.out.printf("%s\nAverage: %.3f\nMax: %d\nMin:%d\n",strategy, total/count, max, min);
System.out.printf("Completed in %.3fs\n", (System.currentTimeMillis()-start)/1000.0);
walker.printPath();
return walker;
}
private void createIsland(int botLeftX, int botLeftY, int topRightX, int topRightY){
for(int i=botLeftY+1; i<topRightY; i++){
if(i>botLeftY+1) deletePath(points[botLeftX][i].right());
if(i<topRightY-1) deletePath(points[topRightX][i].left());
}
for(int i=botLeftX+1; i<topRightX; i++){
if(i>botLeftX+1) deletePath(points[i][botLeftY].up());
if(i<topRightX-1) deletePath(points[i][topRightY].down());
}
}
private void createSinglePath(int botLeftX, int botLeftY, int topRightX, int topRightY){
for(int i=botLeftY; i<topRightY; i++){
if(i==topRightY-1 && (topRightY+1-botLeftY)%2==0){
for(int j=botLeftX; j<topRightX; j++){
if(j==topRightX-1 && (j-botLeftX)%2==0){
deletePath(points[topRightX][topRightY].down());
} else {
deletePath(points[j][topRightY-1+((j-botLeftX)%2)].right());
}
}
} else {
for(int j=botLeftX+(i-botLeftY)%2; j<topRightX+((i-botLeftY)%2); j++){
deletePath(points[j][i].up());
}
}
}
}
private void createDoublePath(int botLeftX, int botLeftY, int topRightX, int topRightY){
for(int i=botLeftY; i<topRightY; i++){
if(i>botLeftY && (width%4!=1 || i<topRightY-1)) deletePath(points[width/2-1][i].right());
if(i==topRightY-1 && (topRightY+1-botLeftY)%2==1){
for(int j=botLeftX; j<topRightX; j++){
if((j-botLeftX)%2==0 || j<topRightX-1){
deletePath(points[j][topRightY-1+((j-botLeftX)%2)].right());
} else {
deletePath(points[topRightX-1][topRightY-1].right());
}
}
} else {
if((i-botLeftY)%2==0){
for(int j=botLeftX+1; j<topRightX; j++){
deletePath(points[j][i].up());
}
} else {
for(int j=botLeftX; j<topRightX+1; j++){
if(j!=width/2 && j!=width/2-1){
deletePath(points[j][i].up());
}
}
}
}
}
}
public Walker(int startingX,int startingY, int Width, int Height, int destinationX, int destinationY, Strategy strategy, int level, boolean animate){
width = Width;
height = Height;
dX = destinationX;
dY = destinationY;
x=startingX;
y=startingY;
points = new Point[width][height];
for(int y=0; y<height; y++){
for(int x=0; x<width; x++){
points[x][y] = new Point(x,y);
}
}
for(int y=0; y<height; y++){
for(int x=0; x<width; x++){
if(x<width-1) new Edge(points[x][y], points[x+1][y]);
if(y<height-1) new Edge(points[x][y], points[x][y+1]);
}
}
if(strategy == Strategy.SINGLE_PATH) createSinglePath(0,0,width-1,height-1);
if(strategy == Strategy.DOUBLE_PATH) createDoublePath(0,0,width-1,height-1);
List<EdgeList> edgeLists = new ArrayList<EdgeList>();
if(strategy == Strategy.ISLAND){
List<Edge> edges = new ArrayList<Edge>();
if(level==0){
createIsland(0,0,width-1,height-1);
deletePath(points[width-2][height-2].right());
deletePath(points[width-2][height-2].up());
} else {
for(int i=0; i<level; i++){
createIsland(i,i,width-1-i, height-1-i);
}
createDoublePath(level,level,width-1-level,height-1-level);
for(int i=height-1; i>=height-level; i--){
edges.add(points[i-2][i].right());
edges.add(points[i][i-2].up());
edgeLists.add(new EdgeList(points[i-1][i].right(), points[i][i-1].up()));
}
}
edges.add(points[width-1-level][height-1-level].down());
edges.add(points[width-1-level][height-1-level].left());
edgeLists.add(new EdgeList(edges.toArray(new Edge[0])));
}
int[] availableVerticals = new int[height];
if(strategy == Strategy.EXTREME_DOUBLE_PATH){
for(int i=1; i<width-1; i++){
deletePath(points[i][0].up());
}
availableVerticals[0] = 2;
for(int i=1; i<height; i++){
availableVerticals[i] = width;
}
}
boolean[][] available = new boolean[width][height];
if(strategy == Strategy.PERFECT_DOUBLE_PATH){
for(int x=0; x<width; x++){
for(int y=0; y<height; y++){
if(x%2==1 && y%2==1){
available[x][y] = true;
} else {
available[x][y] = false;
}
}
}
}
// printPath();
while(!walk()){
if(animate)try{Thread.sleep(500);}catch(InterruptedException e){}
if(strategy == Strategy.ISLAND){
if(x==y && (x==1 || (x>=2 && x<=level))){
if(!hasBeenWalked(points[x][x].down())){
deletePath(points[x][x].down());
} else if(!hasBeenWalked(points[x][x].left())){
deletePath(points[x][x].left());
}
}
}
if(strategy == Strategy.EXTREME_DOUBLE_PATH){
Point cur = points[x][y];
int untravelled = 0;
for(Edge edge: cur.edges) if(edge!=null && !edge.walked) untravelled++;
if(untravelled>1){
if(cur.up()!=null && availableVerticals[y]>2 && !cur.up().walked){
deletePath(cur.up());
availableVerticals[y]--;
}
if(cur.down()!=null && !cur.down().walked){
deletePath(cur.down());
availableVerticals[y-1]--;
}
if(cur.up()!=null && cur.left()!=null && !cur.left().walked){
deletePath(cur.left());
deletePath(points[x][y+1].left());
}
if(cur.up()!=null && cur.right()!=null && !cur.right().walked){
deletePath(cur.right());
if(y<height-1) deletePath(points[x][y+1].right());
}
}
}
if(strategy == Strategy.PERFECT_DOUBLE_PATH){
Point cur = points[x][y];
int untravelled = 0;
for(Edge edge: cur.edges) if(edge!=null && !edge.walked) untravelled++;
if(x%2!=1 || y%2!=1){
if(untravelled>1){
if(cur.down()==null && hasBeenWalked(cur.right())){
if(canBeDeleted(cur.up())) deletePath(cur.up());
}
if(cur.down()==null && hasBeenWalked(cur.left())){
if(x%2==0 && y%2==1 && canBeDeleted(cur.right())) deletePath(cur.right());
else if(cur.right()!=null && canBeDeleted(cur.up())) deletePath(cur.up());
}
if(cur.left()==null && hasBeenWalked(cur.up())){
if(canBeDeleted(cur.right())) deletePath(cur.right());
}
if(cur.left()==null && hasBeenWalked(cur.down())){
if(x%2==1 && y%2==0 && canBeDeleted(cur.up())) deletePath(cur.up());
else if (cur.up()!=null && canBeDeleted(cur.right())) deletePath(cur.right());
}
}
} else {
if(!hasBeenWalked(cur.left())){
if(x>1 && available[x-2][y]){
if(untravelled>1){
available[x-2][y] = false;
deletePath(cur.up());
}
} else if(cur.up()!=null){
if(canBeDeleted(cur.left())) deletePath(cur.left());
if(canBeDeleted(points[x][y+1].left())) deletePath(points[x][y+1].left());
}
}
if(!hasBeenWalked(cur.down())){
if(y>1 && available[x][y-2]){
if(untravelled>1){
available[x][y-2] = false;
deletePath(cur.right());
}
} else if(cur.right()!=null){
if(canBeDeleted(cur.down())) deletePath(cur.down());
if(canBeDeleted(points[x+1][y].down())) deletePath(points[x+1][y].down());
}
}
}
}
if(strategy == Strategy.DOUBLE_PATH || strategy == Strategy.EXTREME_DOUBLE_PATH
|| strategy == Strategy.PERFECT_DOUBLE_PATH){
if(x==width-2 && y==height-1 && points[width-1][height-1].down()!=null){
deletePath(points[width-1][height-1].left());
}
if(x==width-1 && y==height-2 && points[width-1][height-1].left()!=null){
deletePath(points[width-1][height-1].down());
}
} else if(strategy == Strategy.ISLAND){
for(EdgeList edgeList: edgeLists){
boolean deleted = false;
for(Edge edge: edgeList.edges){
if(edge.start.x == x && edge.start.y == y){
if(!hasBeenWalked(edge)){
deletePath(edge);
edgeList.edges.remove(edge);
if(edgeList.edges.size() == 1){
edgeLists.remove(edgeList);
}
deleted = true;
break;
}
}
}
if(deleted) break;
}
}
if(animate)printPath();
}
}
public boolean hasBeenWalked(Edge edge){
if(edge == null) return false;
return edge.walked;
}
public boolean canBeDeleted(Edge edge){
if(edge == null) return false;
return !edge.walked;
}
public List<Edge> getAdjacentUntravelledEdges(){
List<Edge> result = new ArrayList<Edge>();
for(Edge edge: points[x][y].edges){
if(edge!=null && !hasBeenWalked(edge)) result.add(edge);
}
return result;
}
public void printPath(){
StringBuilder builder = new StringBuilder();
for(int y=height-1; y>=0; y--){
for(int x=0; x<width; x++){
Point point = points[x][y];
if(this.x==x && this.y==y){
if(point.up()!=null) builder.append('?');
else builder.append('.');
} else {
if(point.up()!=null) builder.append('|');
else builder.append(' ');
}
if(point.right()!=null) builder.append('_');
else builder.append(' ');
}
builder.append('\n');
}
System.out.print(builder.toString());
}
public boolean walk(){
ArrayList<Edge> possibleMoves = new ArrayList<Edge>();
Point cur = points[x][y];
for(Edge edge: cur.edges){
if(edge!=null) possibleMoves.add(edge);
}
int random = (int)(Math.random()*possibleMoves.size());
Edge move = possibleMoves.get(random);
move.walked = true;
if(move.start == cur){
x = move.end.x;
y = move.end.y;
} else {
x = move.start.x;
y = move.start.y;
}
stepCount++;
if(x==dX && y == dY){
return true;
} else {
return false;
}
}
public boolean isSolvable(){
TreeSet<Point> reachable = new TreeSet<Point>();
Queue<Point> next = new LinkedList<Point>();
next.offer(points[x][y]);
reachable.add(points[x][y]);
while(next.size()>0){
Point cur = next.poll();
ArrayList<Point> neighbors = new ArrayList<Point>();
if(cur.up()!=null) neighbors.add(cur.up().end);
if(cur.right()!=null) neighbors.add(cur.right().end);
if(cur.down()!=null) neighbors.add(cur.down().start);
if(cur.left()!=null) neighbors.add(cur.left().start);
for(Point neighbor: neighbors){
if(!reachable.contains(neighbor)){
if(neighbor == points[dX][dY]) return true;
reachable.add(neighbor);
next.offer(neighbor);
}
}
}
return false;
}
public boolean deletePath(Edge toDelete){
if(toDelete == null) return true;
// if(toDelete.walked){
// System.err.println("Edge already travelled!");
// return false;
// }
int startIdx = toDelete.getStartIdx();
int endIdx = toDelete.getEndIdx();
toDelete.start.edges[startIdx] = null;
toDelete.end.edges[endIdx] = null;
// if(!isSolvable()){
// toDelete.start.edges[startIdx] = toDelete;
// toDelete.end.edges[endIdx] = toDelete;
// System.err.println("Invalid deletion!");
// return false;
// }
return true;
}
static class EdgeList{
List<Edge> edges;
public EdgeList(Edge... edges){
this.edges = new ArrayList<Edge>();
this.edges.addAll(Arrays.asList(edges));
}
}
static class Edge implements Comparable<Edge>{
Point start, end;
boolean walked;
public Edge(Point start, Point end){
walked = false;
this.start = start;
this.end = end;
this.start.edges[getStartIdx()] = this;
this.end.edges[getEndIdx()] = this;
if(start.compareTo(end)>0){
Point tmp = end;
end = start;
start = tmp;
}
}
public Edge(int x1, int y1, int x2, int y2){
this(new Point(x1,y1), new Point(x2,y2));
}
public boolean exists(){
return start.edges[getStartIdx()] != null || end.edges[getEndIdx()] != null;
}
public int getStartIdx(){
if(start.x == end.x){
if(start.y < end.y) return 0;
else return 2;
} else {
if(start.x < end.x) return 1;
else return 3;
}
}
public int getEndIdx(){
if(start.x == end.x){
if(start.y < end.y) return 2;
else return 0;
} else {
if(start.x < end.x) return 3;
else return 1;
}
}
public boolean isVertical(){
return start.x==end.x;
}
@Override
public int compareTo(Edge o) {
int result = start.compareTo(o.start);
if(result!=0) return result;
return end.compareTo(o.end);
}
}
static class Point implements Comparable<Point>{
int x,y;
Edge[] edges;
public Point(int x, int y){
this.x = x;
this.y = y;
edges = new Edge[4];
}
public Edge up(){ return edges[0]; }
public Edge right(){ return edges[1]; }
public Edge down(){ return edges[2]; }
public Edge left(){ return edges[3]; }
public int compareTo(Point o){
int result = Integer.compare(x, o.x);
if(result!=0) return result;
result = Integer.compare(y, o.y);
if(result!=0) return result;
return 0;
}
}
}