230,794.38 en 20x20, 100k carreras
Última actualización: finalmente construí una solución dinámica de 2 vías perfecta. Dije perfecto ya que la versión anterior en realidad no es simétrica, era más fácil obtener un camino más largo si el borracho tomaba un camino sobre el otro. El actual es simétrico, por lo que puede obtener un mayor número esperado de pasos. Después de algunas pruebas, parece estar alrededor de 230k, una mejora con respecto a la anterior que es de aproximadamente 228k. Pero estadísticamente hablando, esos números todavía están dentro de su gran desviación, por lo que no afirmo que esto sea significativamente mejor, pero creo que debería ser mejor que la versión anterior.
El código está al final de esta publicación. Se actualiza para que sea mucho más rápido que la versión anterior, completando 1000 ejecuciones en 23 segundos.
A continuación se muestra una ejecución de muestra y un laberinto de muestra:
Caminante perfecto
Promedio: 230794.384
Máx .: 1514506
Min: 25860
Completado en 2317.374s
_ _ _ _ _ _ _ _ _ _ _ _.
El | El | El | El | El | El | El | El | El | El | El | El | El | El | El | _ _ _ _
El | El | El | El | El | El | El | El | El | El | El | El | El | El | El | | _ _ _ _
El | El | El | El | El | El | El | El | El | El | El | El | El | El | El | _ _ _ _ |
El | El | El | El | El | El | El | El | El | El | El | El | El | El | El | | _ _ _ _
El | El | El | El | El | El | El | El | El | El | El | El | El | El | El | _ _ _ _ |
El | El | El | El | El | El | El | El | El | El | El | El | El | El | El | | _ _ _ _
El | El | El | El | El | El | El | El | El | El | El | El | El | El | El | _ _ _ _ |
El | El | El | El | El | El | El | El | El | El | El | El | El | | _ | | _ _ _ _
El | El | El | El | El | El | El | El | El | El | El | El | El | _ _ _ _ _ _ |
El | El | El | El | El | El | El | El | El | El | El | El | El | | _ _ _ _ _ _
El | El | El | El | El | El | El | El | El | El | El | El | El | _ _ _ _ _ _ |
El | El | El | El | El | El | El | El | El | El | El | El | El | | _ _ _ _ _ _
El | El | El | El | El | El | El | El | El | El | El | El | El | _ _ _ _ _ _ |
El | El | El | El | El | | _ | | _ | | _ | | _ | | _ _ _ _ _ _
El | El | El | El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | El | El | El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | El | El | El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | | _ | | _ | | _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Envíos anteriores
¡Finalmente puedo igualar el resultado de Sparr! = D
Basado en mis experimentos anteriores (ver al final de esta publicación), la mejor estrategia es tener un doble camino y cerrar uno cuando el borracho llegue a cualquiera de ellos, y la variable proviene de cuán bueno podemos predecir dinámicamente dónde irá el borracho. aumentar la posibilidad de que se meta en un camino más largo.
Entonces, según mi DOUBLE_PATH
estrategia, construí otro, que cambia el laberinto (mi DOUBLE_PATH
laberinto era fácilmente modificable) dependiendo del movimiento del borracho. A medida que toma un camino con más de una opción disponible, cerraré los caminos para dejar solo dos opciones posibles (una de la que vino, otra la no viajada).
Esto suena similar a lo que Sparr ha logrado, como lo muestra el resultado. La diferencia con la suya es demasiado pequeña para que se considere mejor, pero diría que mi enfoque es más dinámico que él, ya que mi laberinto es más modificable que el de Sparr =)
El resultado con una muestra final de laberinto:
EXTREME_DOUBLE_PATH
Promedio: 228034.89
Máx .: 1050816
Min: 34170
Completado en 396.728s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ | | _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Sección de Experimentos
Lo mejor resulta ser la misma estrategia que stokastic, me enorgullece experimentar usando varias estrategias e imprimir buenos resultados :)
Cada uno de los laberintos impresos a continuación es el último laberinto después de que el borracho haya llegado a casa, por lo que pueden ser ligeramente diferentes de una carrera a otra debido a la aleatoriedad en el movimiento del borracho y la dinamicidad del adversario.
Describiré cada estrategia:
Sendero Sencillo
Este es el enfoque más simple, que creará una única ruta desde la entrada hasta la salida.
SINGLE_PATH
Promedio: 162621.612
Max: 956694
Min: 14838
Completado en 149.430s
_ _ _ _ _ _ _ _ _ _
El | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | El |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Isla (nivel 0)
Este es un enfoque que intenta atrapar al borracho en una isla casi aislada. No funciona tan bien como esperaba, pero esta es una de mis primeras ideas, así que la incluyo.
Hay dos caminos que conducen a la salida, y cuando el borracho se acerca a uno de ellos, el adversario lo cierra y lo obliga a encontrar la otra salida (y posiblemente queda atrapado nuevamente en la isla)
ISLA
Promedio: 74626.070
Máx .: 428560
Min: 1528
Completado en 122.512s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
El | | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
| _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | _ | El |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Doble camino
Esta es la estrategia más discutida, que consiste en tener dos caminos de igual longitud hacia la salida, y cerrar uno de ellos cuando el borracho se acerque a uno de ellos.
DOUBLE_PATH
Promedio: 197743.472
Máx .: 1443406
Min: 21516
Completado en 308.177s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
_ _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ _
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Isla (nivel 1)
Inspirados por los múltiples caminos de la isla y el alto conteo de caminatas en un solo camino, conectamos la isla a la salida y hacemos un laberinto de un solo camino en la isla, creando en total tres caminos para salir, y de forma similar al caso anterior, cerramos cualquiera de los salga cuando el borracho se acerque.
Esto funciona un poco mejor que la ruta simple, pero aún así no vence a la ruta doble.
ISLA
Promedio: 166265.132
Máx .: 1162966
Min: 19544
Completado en 471.982s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ | _
El | El | | _ | | _ | | _ | | _ | | _ | | _ | | _ | | _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Isla (nivel 2)
Intentando expandir la idea anterior, creé una isla anidada, creando en total cinco caminos, pero no parece funcionar tan bien.
ISLA
Promedio: 164222.712
Máx .: 927608
Min: 22024
Completado en 793.591
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _
El | El | _ _ _ _ _ _ _ _ | _ |
El | El | El | | _ | | _ | | _ | | _ | | _ | | _ | | _ | El | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
| _ | _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Isla (nivel 3)
Al darse cuenta de que la doble ruta en realidad funciona mejor que la ruta única, ¡hagamos que la isla tenga doble ruta!
El resultado es una mejora sobre la Isla (nivel 1), pero aún así no supera el doble camino puro.
A modo de comparación, el resultado para el doble camino del tamaño de la isla es de 131,134.42 movimientos en promedio. Entonces, esto agrega un número bastante significativo de movimientos (alrededor de 40k), pero no lo suficiente como para vencer el doble camino.
ISLA
Promedio: 171730.090
Máx .: 769080
Min: 29760
Completado en 587.646
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
El | _ _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ _ |
El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Isla (nivel 4)
Nuevamente, experimentar con una isla anidada, y nuevamente no funciona tan bien.
ISLA
Promedio: 149723.068
Máx .: 622106
Min: 25752
Completado en 830.889s
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ |
El | El | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | _ |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
El | El | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | El |
El | El | | _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
El | El | _ _ _ _ _ _ _ | | _ _ _ _ _ _ _ | El |
El | | _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El | El |
| _ | _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | El |
| _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |
Conclusión
Con todo, esto prueba que tener un solo camino largo desde la posición actual del borracho hasta la salida funciona mejor, lo que se logra mediante la estrategia de doble camino, ya que después de cerrar una salida, el borracho tendrá que recorrer la distancia máxima posible para llegar a la salida.
Esto sugiere que la estrategia básica debe seguir siendo el doble camino, y solo podemos modificar la dinámica en la que se crean los caminos, lo que ha hecho Sparr. ¡Así que creo que su estrategia es el camino a seguir!
Código
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;
}
}
}