Rush Hour-Resolviendo el juego
Hora punta
si no estás familiarizado con él, el juego consiste en una colección de coches de diferentes tamaños, establecidos horizontal o verticalmente, en una cuadrÃcula NxM que tiene una sola salida.
Cada auto puede moverse hacia adelante / atrás en las direcciones en las que se encuentra, siempre y cuando otro auto no lo esté bloqueando. Usted puede nunca cambiar la dirección de un coche.
Hay un coche especial, por lo general es el rojo. Se establece en la misma fila en la que está la salida, y el objetivo de el juego consiste en encontrar una serie de movimientos (un movimiento - mover un coche N pasos hacia atrás o hacia adelante) que permitirá que el coche rojo para conducir fuera del laberinto.
He estado tratando de pensar cómo resolver este problema computacionalmente, y realmente no puedo pensar en ninguna buena solución.
Se me ocurrieron algunos:
- Retroceso. Esto es bastante simple-Recursión y un poco más de recursión hasta que encuentre la respuesta. Sin embargo, cada coche se puede mover de varias maneras diferentes, y en cada estado de juego los coches se pueden mover, y el árbol de juego resultante será ENORME.
- Algún tipo de algoritmo de restricción que tendrá en cuenta lo que necesita ser movido, y funcionará recursivamente de alguna manera. Esta es una idea muy aproximada, pero es una idea.
- Gráficos? Modelar los estados del juego como un gráfico y aplicar algún tipo de variación en un algoritmo de coloración, para resolver dependencias? Una vez más, esta es una idea muy aproximada.
- Un amigo sugirió algoritmos genéticos. Esto es posible, pero no es fácil. No se me ocurre una buena manera de hacer una función de evaluación, y sin eso no tenemos nada.
Entonces, la pregunta es: ¿Cómo crear un programa que tome una cuadrÃcula y el diseño del vehÃculo, y genere una serie de pasos necesarios para sacar el auto rojo?
Subtemas:
- Encontrando alguna solución.
- Encontrar una solución óptima (número mÃnimo de movimientos)
- Evaluar qué tan bueno es un estado actual
Ejemplo: ¿Cómo se puede mover los coches en este entorno, de modo que el coche rojo puede "salir" del laberinto a través de la salida a la derecha?
http://scienceblogs.com/ethicsandscience/upload/2006/12/RushHour.jpg
7 answers
Para la Hora Pico clásica, este problema es muy manejable con una simple búsqueda de amplitud primero. La configuración inicial más difÃcil conocida requiere 93 movimientos para resolver, con un total de solo 24132 configuraciones accesibles. Incluso un algoritmo de búsqueda ingenuamente implementado primero en amplitud puede explorar todo el espacio de búsqueda en menos de 1 segundo incluso en una máquina modesta.
Referencias
- Wikipedia/Hora Punta (juego de mesa)
- Inicial de la Hora Punta Configuraciones} se afirma que estos son los problemas "más difÃciles"
- Fuente del solucionador en ideone.com - con salida para la entrada "más difÃcil"
El solucionador de Java
Aquà está el código fuente completo de un solucionador exhaustivo de búsqueda primero en amplitud, escrito en estilo C.
import java.util.*;
public class RushHour {
// classic Rush Hour parameters
static final int N = 6;
static final int M = 6;
static final int GOAL_R = 2;
static final int GOAL_C = 5;
// the transcription of the 93 moves, total 24132 configurations problem
// from http://cs.ulb.ac.be/~fservais/rushhour/index.php?window_size=20&offset=0
static final String INITIAL = "333BCC" +
"B22BCC" +
"B.XXCC" +
"22B..." +
".BB.22" +
".B2222";
static final String HORZS = "23X"; // horizontal-sliding cars
static final String VERTS = "BC"; // vertical-sliding cars
static final String LONGS = "3C"; // length 3 cars
static final String SHORTS = "2BX"; // length 2 cars
static final char GOAL_CAR = 'X';
static final char EMPTY = '.'; // empty space, movable into
static final char VOID = '@'; // represents everything out of bound
// breaks a string into lines of length N using regex
static String prettify(String state) {
String EVERY_NTH = "(?<=\\G.{N})".replace("N", String.valueOf(N));
return state.replaceAll(EVERY_NTH, "\n");
}
// conventional row major 2D-1D index transformation
static int rc2i(int r, int c) {
return r * N + c;
}
// checks if an entity is of a given type
static boolean isType(char entity, String type) {
return type.indexOf(entity) != -1;
}
// finds the length of a car
static int length(char car) {
return
isType(car, LONGS) ? 3 :
isType(car, SHORTS) ? 2 :
0/0; // a nasty shortcut for throwing IllegalArgumentException
}
// in given state, returns the entity at a given coordinate, possibly out of bound
static char at(String state, int r, int c) {
return (inBound(r, M) && inBound(c, N)) ? state.charAt(rc2i(r, c)) : VOID;
}
static boolean inBound(int v, int max) {
return (v >= 0) && (v < max);
}
// checks if a given state is a goal state
static boolean isGoal(String state) {
return at(state, GOAL_R, GOAL_C) == GOAL_CAR;
}
// in a given state, starting from given coordinate, toward the given direction,
// counts how many empty spaces there are (origin inclusive)
static int countSpaces(String state, int r, int c, int dr, int dc) {
int k = 0;
while (at(state, r + k * dr, c + k * dc) == EMPTY) {
k++;
}
return k;
}
// the predecessor map, maps currentState => previousState
static Map<String,String> pred = new HashMap<String,String>();
// the breadth first search queue
static Queue<String> queue = new LinkedList<String>();
// the breadth first search proposal method: if we haven't reached it yet,
// (i.e. it has no predecessor), we map the given state and add to queue
static void propose(String next, String prev) {
if (!pred.containsKey(next)) {
pred.put(next, prev);
queue.add(next);
}
}
// the predecessor tracing method, implemented using recursion for brevity;
// guaranteed no infinite recursion, but may throw StackOverflowError on
// really long shortest-path trace (which is infeasible in standard Rush Hour)
static int trace(String current) {
String prev = pred.get(current);
int step = (prev == null) ? 0 : trace(prev) + 1;
System.out.println(step);
System.out.println(prettify(current));
return step;
}
// in a given state, from a given origin coordinate, attempts to find a car of a given type
// at a given distance in a given direction; if found, slide it in the opposite direction
// one spot at a time, exactly n times, proposing those states to the breadth first search
//
// e.g.
// direction = -->
// __n__
// / \
// ..o....c
// \___/
// distance
//
static void slide(String current, int r, int c, String type, int distance, int dr, int dc, int n) {
r += distance * dr;
c += distance * dc;
char car = at(current, r, c);
if (!isType(car, type)) return;
final int L = length(car);
StringBuilder sb = new StringBuilder(current);
for (int i = 0; i < n; i++) {
r -= dr;
c -= dc;
sb.setCharAt(rc2i(r, c), car);
sb.setCharAt(rc2i(r + L * dr, c + L * dc), EMPTY);
propose(sb.toString(), current);
current = sb.toString(); // comment to combo as one step
}
}
// explores a given state; searches for next level states in the breadth first search
//
// Let (r,c) be the intersection point of this cross:
//
// @ nU = 3 '@' is not a car, 'B' and 'X' are of the wrong type;
// . nD = 1 only '2' can slide to the right up to 5 spaces
// 2.....B nL = 2
// X nR = 4
//
// The n? counts how many spaces are there in a given direction, origin inclusive.
// Cars matching the type will then slide on these "alleys".
//
static void explore(String current) {
for (int r = 0; r < M; r++) {
for (int c = 0; c < N; c++) {
if (at(current, r, c) != EMPTY) continue;
int nU = countSpaces(current, r, c, -1, 0);
int nD = countSpaces(current, r, c, +1, 0);
int nL = countSpaces(current, r, c, 0, -1);
int nR = countSpaces(current, r, c, 0, +1);
slide(current, r, c, VERTS, nU, -1, 0, nU + nD - 1);
slide(current, r, c, VERTS, nD, +1, 0, nU + nD - 1);
slide(current, r, c, HORZS, nL, 0, -1, nL + nR - 1);
slide(current, r, c, HORZS, nR, 0, +1, nL + nR - 1);
}
}
}
public static void main(String[] args) {
// typical queue-based breadth first search implementation
propose(INITIAL, null);
boolean solved = false;
while (!queue.isEmpty()) {
String current = queue.remove();
if (isGoal(current) && !solved) {
solved = true;
trace(current);
//break; // comment to continue exploring entire space
}
explore(current);
}
System.out.println(pred.size() + " explored");
}
}
Hay dos lÃneas dignas de mención en el código fuente:
- El
break;
cuando se encuentra una solución- Esto se comenta ahora para que la amplitud primero la búsqueda explora el espacio de búsqueda completo, para confirmar los números dados en el sitio web vinculado anterior
- El {} [2] {} en[3]}
- Esencialmente esto cuenta cada movimiento de cualquier coche como un movimiento. Si un coche se mueve 3 espacios a la izquierda, eso es 3 movimientos. Para combinar esto como un movimiento (ya que implica que el mismo coche se mueve en la misma dirección), simplemente comente esta lÃnea. El sitio web vinculado no reconoce combo, por lo que esta lÃnea es ahora sin comentarios para que coincida con el número mÃnimo de movimientos dados. Con el conteo de combos, el problema de 93 movimientos solo requiere 49 movimientos combinados. Es decir, si hay un asistente de estacionamiento en el estacionamiento que mueve estos autos, sólo tendrÃa que entrar y salir de un auto 49 veces.
Descripción general del algoritmo
El algoritmo es esencialmente una búsqueda de amplitud, implementada con una cola como es tÃpico. Se mantiene un mapa predecesor para que cualquier estado pueda ser rastreado hasta la estado. Una clave nunca se volverá a asignar, y como las entradas se insertan en el orden de búsqueda de amplitud, el camino más corto está garantizado.
Un estado se representa como un NxM
-longitud String
. Cada char
representa una entidad en el tablero, almacenada en orden mayor de fila.
Los estados vecinos se encuentran escaneando las 4 direcciones desde un espacio vacÃo, buscando un tipo de automóvil apropiado, deslizándolo a medida que se acomoda la habitación.
Hay un montón de trabajo redundante aquà (por ejemplo, largos "callejones" son escaneado varias veces), pero como se mencionó anteriormente, aunque la versión generalizada es PSPACE-completa, la variante clásica de la Hora Punta es muy manejable por fuerza bruta.
Referencias de Wikipedia
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-23 17:39:04
Aquà está mi respuesta. Resuelve el rompecabezas del gran maestro en poco menos de 6 segundos.
Utiliza una búsqueda de amplitud primero (BFS). El truco es buscar un diseño de tablero que hayas visto antes en búsquedas anteriores y abortar esa secuencia. Debido al BFS, si has visto ese diseño antes de que ya hayas llegado allà de una manera más corta, asà que deja que squence siga tratando de resolverlo en lugar de este más largo.
#!perl
# Program by Rodos rodos at haywood dot org
use Storable qw(dclone);
use Data::Dumper;
print "Lets play Rush Hour! \n";
# Lets define our current game state as a grid where each car is a different letter.
# Our special car is a marked with the specific letter T
# The boarder is a * and the gloal point on the edge is an @.
# The grid must be the same witdh and height
# You can use a . to mark an empty space
# Grand Master
@startingGrid = (
['*','*','*','*','*','*','*','*'],
['*','.','.','A','O','O','O','*'],
['*','.','.','A','.','B','.','*'],
['*','.','T','T','C','B','.','@'],
['*','D','D','E','C','.','P','*'],
['*','.','F','E','G','G','P','*'],
['*','.','F','Q','Q','Q','P','*'],
['*','*','*','*','*','*','*','*']
);
# Now lets print out our grid board so we can see what it looks like.
# We will go through each row and then each column.
# As we do this we will record the list of cars (letters) we see into a hash
print "Here is your board.\n";
&printGrid(\@startingGrid);
# Lets find the cars on the board and the direction they are sitting
for $row (0 .. $#startingGrid) {
for $col (0 .. $#{$startingGrid[$row]} ) {
# Make spot the value of the bit on the grid we are looking at
$spot = $startingGrid[$row][$col];
# Lets record any cars we see into a "hash" of valid cars.
# If the splot is a non-character we will ignore it cars are only characters
unless ($spot =~ /\W/) {
# We will record the direction of the car as the value of the hash key.
# If the location above or below our spot is the same then the car must be vertical.
# If its not vertical we mark as it as horizonal as it can't be anything else!
if ($startingGrid[$row-1][$col] eq $spot || $startingGrid[$row+1] eq $spot) {
$cars{$spot} = '|';
} else {
$cars{$spot} = '-';
}
}
}
}
# Okay we should have printed our grid and worked out the unique cars
# Lets print out our list of cars in order
print "\nI have determined that you have used the following cars on your grid board.\n";
foreach $car (sort keys %cars) {
print " $car$cars{$car}";
}
print "\n\n";
end;
&tryMoves();
end;
# Here are our subroutines for things that we want to do over and over again or things we might do once but for
# clatiry we want to keep the main line of logic clear
sub tryMoves {
# Okay, this is the hard work. Take the grid we have been given. For each car see what moves are possible
# and try each in turn on a new grid. We will do a shallow breadth first search (BFS) rather than depth first.
# The BFS is achieved by throwing new sequences onto the end of a stack. You then keep pulling sequnces
# from the front of the stack. Each time you get a new item of the stack you have to rebuild the grid to what
# it looks like at that point based on the previous moves, this takes more CPU but does not consume as much
# memory as saving all of the grid representations.
my (@moveQueue);
my (@thisMove);
push @moveQueue, \@thisMove;
# Whlst there are moves on the queue process them
while ($sequence = shift @moveQueue) {
# We have to make a current view of the grid based on the moves that got us here
$currentGrid = dclone(\@startingGrid);
foreach $step (@{ $sequence }) {
$step =~ /(\w)-(\w)(\d)/;
$car = $1; $dir = $2; $repeat = $3;
foreach (1 .. $repeat) {
&moveCarRight($car, $currentGrid) if $dir eq 'R';
&moveCarLeft($car, $currentGrid) if $dir eq 'L';
&moveCarUp($car, $currentGrid) if $dir eq 'U';
&moveCarDown($car, $currentGrid) if $dir eq 'D';
}
}
# Lets see what are the moves that we can do from here.
my (@moves);
foreach $car (sort keys %cars) {
if ($cars{$car} eq "-") {
$l = &canGoLeft($car,$currentGrid);
push @moves, "$car-L$l" if ($l);
$l = &canGoRight($car,$currentGrid);
push @moves, "$car-R$l" if ($l);
} else {
$l = &canGoUp($car,$currentGrid);
push @moves, "$car-U$l" if ($l);
$l = &canGoDown($car,$currentGrid);
push @moves, "$car-D$l" if ($l);
}
}
# Try each of the moves, if it solves the puzzle we are done. Otherwise take the new
# list of moves and throw it on the stack
foreach $step (@moves) {
$step =~ /(\w)-(\w)(\d)/;
$car = $1; $dir = $2; $repeat = $3;
my $newGrid = dclone($currentGrid);
foreach (1 .. $repeat) {
&moveCarRight($car, $newGrid) if $dir eq 'R';
&moveCarLeft($car, $newGrid) if $dir eq 'L';
&moveCarUp($car, $newGrid) if $dir eq 'U';
&moveCarDown($car, $newGrid) if $dir eq 'D';
}
if (&isItSolved($newGrid)) {
print sprintf("Solution in %d moves :\n", (scalar @{ $sequence }) + 1);
print join ",", @{ $sequence };
print ",$car-$dir$repeat\n";
return;
} else {
# That did not create a solution, before we push this for further sequencing we want to see if this
# pattern has been encountered before. If it has there is no point trying more variations as we already
# have a sequence that gets here and it might have been shorter, thanks to our BFS
if (!&seen($newGrid)) {
# Um, looks like it was not solved, lets throw this grid on the queue for another attempt
my (@thisSteps) = @{ $sequence };
push @thisSteps, "$car-$dir$repeat";
push @moveQueue, \@thisSteps;
}
}
}
}
}
sub isItSolved {
my ($grid) = shift;
my ($row, $col);
my $stringVersion;
foreach $row (@$grid) {
$stringVersion .= join "",@$row;
}
# We know we have solve the grid lock when the T is next to the @, because that means the taxi is at the door
if ($stringVersion =~ /\T\@/) {
return 1;
}
return 0;
}
sub seen {
my ($grid) = shift;
my ($row, $col);
my $stringVersion;
foreach $row (@$grid) {
$stringVersion .= join "",@$row;
}
# Have we seen this before?
if ($seen{$stringVersion}) {
return 1;
}
$seen{$stringVersion} = 1;
return 0;
}
sub canGoDown {
my ($car) = shift;
return 0 if $cars{$car} eq "-";
my ($grid) = shift;
my ($row, $col);
for ($row = $#{$grid}; $row >= 0; --$row) {
for $col (0 .. $#{$grid->[$row]} ) {
if ($grid->[$row][$col] eq $car) {
# See how many we can move
$l = 0;
while ($grid->[++$row][$col] eq ".") {
++$l;
}
return $l;
}
}
}
return 0;
}
sub canGoUp {
my ($car) = shift;
return 0 if $cars{$car} eq "-";
my ($grid) = shift;
my ($row, $col);
for $row (0 .. $#{$grid}) {
for $col (0 .. $#{$grid->[$row]} ) {
if ($grid->[$row][$col] eq $car) {
# See how many we can move
$l = 0;
while ($grid->[--$row][$col] eq ".") {
++$l;
}
return $l;
}
}
}
return 0;
}
sub canGoRight {
my ($car) = shift;
return 0 if $cars{$car} eq "|";
my ($grid) = shift;
my ($row, $col);
for $row (0 .. $#{$grid}) {
for ($col = $#{$grid->[$row]}; $col >= 0; --$col ) {
if ($grid->[$row][$col] eq $car) {
# See how many we can move
$l = 0;
while ($grid->[$row][++$col] eq ".") {
++$l;
}
return $l;
}
}
}
return 0;
}
sub canGoLeft {
my ($car) = shift;
return 0 if $cars{$car} eq "|";
my ($grid) = shift;
my ($row, $col);
for $row (0 .. $#{$grid}) {
for $col (0 .. $#{$grid->[$row]} ) {
if ($grid->[$row][$col] eq $car) {
# See how many we can move
$l = 0;
while ($grid->[$row][--$col] eq ".") {
++$l;
}
return $l;
}
}
}
return 0;
}
sub moveCarLeft {
# Move the named car to the left of the passed grid. Care must be taken with the algoritm
# to not move part of the car and then come across it again on the same pass and move it again
# so moving left requires sweeping left to right.
# We need to know which car you want to move and the reference to the grid you want to move it on
my ($car) = shift;
my ($grid) = shift;
# Only horizontal cards can move left
die "Opps, tried to move a vertical car $car left" if $cars{$car} eq "|";
my ($row, $col);
for $row (0 .. $#{$grid}) {
for $col (0 .. $#{$grid->[$row]} ) {
if ($grid->[$row][$col] eq $car) {
die "Tried to move car $car left into an occupied spot\n" if $grid->[$row][$col-1] ne ".";
$grid->[$row][$col-1] = $car;
$grid->[$row][$col] = ".";
}
}
}
}
sub moveCarRight {
# Move the named car to the right of the passed grid. Care must be taken with the algoritm
# to not move part of the car and then come across it again on the same pass and move it again
# so moving right requires sweeping right to left (backwards).
# We need to know which car you want to move and the reference to the grid you want to move it on
my ($car) = shift;
my ($grid) = shift;
# Only horizontal cards can move right
die "Opps, tried to move a vertical car $car right" if $cars{$car} eq "|";
my ($row, $col);
for $row (0 .. $#{$grid}) {
for ($col = $#{$grid->[$row]}; $col >= 0; --$col ) {
if ($grid->[$row][$col] eq $car) {
die "Tried to move car $car right into an occupied spot\n" if $grid->[$row][$col+1] ne ".";
$grid->[$row][$col+1] = $car;
$grid->[$row][$col] = ".";
}
}
}
}
sub moveCarUp {
# Move the named car up in the passed grid. Care must be taken with the algoritm
# to not move part of the car and then come across it again on the same pass and move it again
# so moving right requires sweeping top down.
# We need to know which car you want to move and the reference to the grid you want to move it on
my ($car) = shift;
my ($grid) = shift;
# Only vertical cards can move up
die "Opps, tried to move a horizontal car $car up" if $cars{$car} eq "-";
my ($row, $col);
for $row (0 .. $#{$grid}) {
for $col (0 .. $#{$grid->[$row]} ) {
if ($grid->[$row][$col] eq $car) {
die "Tried to move car $car up into an occupied spot\n" if $grid->[$row-1][$col] ne ".";
$grid->[$row-1][$col] = $car;
$grid->[$row][$col] = ".";
}
}
}
}
sub moveCarDown {
# Move the named car down in the passed grid. Care must be taken with the algoritm
# to not move part of the car and then come across it again on the same pass and move it again
# so moving right requires sweeping upwards from the bottom.
# We need to know which car you want to move and the reference to the grid you want to move it on
my ($car) = shift;
my ($grid) = shift;
# Only vertical cards can move up
die "Opps, tried to move a horizontal car $car down" if $cars{$car} eq "-";
my ($row, $col);
for ($row = $#{$grid}; $row >=0; --$row) {
for $col (0 .. $#{$grid->[$row]} ) {
if ($grid->[$row][$col] eq $car) {
die "Tried to move car $car down into an occupied spot\n" if $grid->[$row+1][$col] ne ".";
$grid->[$row+1][$col] = $car;
$grid->[$row][$col] = ".";
}
}
}
}
sub printGrid {
# Print out a representation of a grid
my ($grid) = shift; # This is a reference to an array of arrays whch is passed as the argument
my ($row, $col);
for $row (0 .. $#{$grid}) {
for $col (0 .. $#{$grid->[$row]} ) {
print $grid->[$row][$col], " ";
}
print "\n";
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 11:37:25
En realidad, hay un documento del MIT que hace referencia especÃficamente a la Hora Punta (Usé el término de búsqueda "rompecabezas de bloques deslizantes")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-20 20:57:41
Debe recurrir (su solución "backtracking"). Esta es probablemente la única manera de resolver puzzles como este; la pregunta es cómo hacerlo rápido.
Como ha señalado, el espacio de búsqueda será grande, pero no demasiado grande, si tiene un tablero de tamaño razonable. Por ejemplo, has dibujado una cuadrÃcula de 6x6 con 12 coches en ella. Suponiendo que cada uno es un coche de tamaño 2, que da 5 espacios / por, por lo que a lo sumo 5^12 = 244,140,625 posiciones potenciales. Esto incluso cabe en un entero de 32 bits. Asà que una posibilidad es asigna una gran matriz, una ranura por posición potencial y usa la memoización para asegurarte de no repetir una posición.
Lo siguiente a tener en cuenta es que la mayorÃa de esas posiciones "potenciales" no son, de hecho, posibles (involucrarÃan autos superpuestos). Asà que en su lugar, usa una tabla hash para hacer un seguimiento de cada posición que has visitado. Esto tendrá una pequeña sobrecarga de memoria por entrada, pero probablemente será más eficiente en el espacio que la solución "huge array". Sin embargo, tomará un poco más de tiempo para cada acceso de entrada.
Como dice el artÃculo del MIT en @Daniel's answer, el problema es PSPACE-completo, lo que significa que muchos de los trucos utilizados para reducir la complejidad de los problemas de NP probablemente no se pueden usar.
Dicho esto, cualquiera de las dos soluciones anteriores al problema de la posición repetida deberÃa funcionar para cuadrÃculas más pequeñas. Todo estará determinado por cuán grande es el problema y cuánta memoria tiene su computadora; pero el ejemplo que mostró no deberÃa ser ningún problema, incluso para un computadora de escritorio común.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:51
Acabo de escribir mi implementación y experimentar con ella. Estoy de acuerdo con polygenelubricants en que el espacio de estado es muy pequeño para el juego clásico (tablero 6x6). Sin embargo, probé una implementación de búsqueda inteligente (A* search). TenÃa curiosidad con respecto a la reducción del espacio de estado explorado en comparación con un simple BFS.
El algoritmo A* se puede ver como una generalización de la búsqueda BFS. La decisión de qué camino explorar a continuación está determinada por una puntuación que combina tanto la longitud del camino (es decir, el número de movimientos) como un lÃmite inferior en los movimientos restantes cuentan. La forma en que elegà calcular esto último, es obtener la distancia del automóvil rojo desde la salida, y luego agregar 1 por cada vehÃculo en el camino, ya que debe moverse al menos una vez para despejar el camino. Cuando remplazo el cálculo del lÃmite inferior con una constante 0, obtengo un comportamiento BFS regular.
Después de inspeccionar cuatro rompecabezas de esta lista , encontré que A * search explora en promedio 16% menos estados que un BFS regular.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-24 23:28:57
Creo que la recursividad es una mala idea, a menos que lleve un registro de lo que ya ha visitado; podrÃa terminar recursivamente infinitamente moviendo un automóvil de un lado a otro.
Tal vez este sea un buen comienzo: Representar y almacenar cada estado del tablero como un gráfico no dirigido. Luego, para cada movimiento posible, revisa los estados anteriores para asegurarte de que no estás golpeando el mismo estado otra vez.
Ahora haga otro gráfico no dirigido donde los nodos representan estados y los bordes representan la capacidad de obtener de de un estado a otro moviendo un coche. Explora los estados hasta que uno de ellos sea una solución. A continuación, siga los bordes de nuevo al principio para encontrar una ruta de movimiento.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-20 23:01:45
Escribà un solucionador de sudoku. Si bien los detalles son completamente diferentes, creo que el problema general es similar. Por un lado, tratar de hacer heurÃstica inteligente en un solucionador de sudoku es mucho más lento que una solución de fuerza bruta. Probar cada movimiento, con algunas heurÃsticas simples y sin duplicados es el camino a seguir. Es un poco más difÃcil comprobar si hay estados de junta duplicados en hora punta, pero no mucho.
Si miras el tablero en tu muestra, solo hay 4 movimientos válidos. En cualquier con el tiempo, solo habrá unos pocos movimientos válidos.
En cada nivel de recursión, copie el estado del tablero e intente cada movimiento válido en el tablero. Para cada cuadrado vacÃo, mueva cada coche que pueda en ese cuadrado. Si el estado del nuevo tablero no está en la lista historial, recurra a otro nivel. Por lista de historia, quiero decir dar a cada nivel de recursión acceso a cada tablero que llevó a ese estado, probablemente en una lista vinculada. Utilice hashes para descartar rápidamente estados desiguales.
La clave para esto es tener un estado de placa simple que se puede copiar y modificar fácilmente. Probablemente una matriz con un int por cuadrado diciendo qué coche cubre ese cuadrado, si lo hay. A continuación, solo tiene que iterar a través de los cuadrados y averiguar movimientos legales. Un movimiento legal significa cuadrados vacÃos entre el cuadrado de prueba y un automóvil orientado hacia él.
Al igual que con el sudoku, la peor opción posible serÃa un algoritmo genético.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-21 03:19:29