Acciones

Diferencia entre revisiones de «Ejemplo 2»

De Lógica computacional y teoría de modelos (2019-20)

(Página creada con «<source lang = "prolog"> % Nuestro objetivo será diseñar un nivel de juego en el que el jugador % empieza en la casilla de arriba y a la izda del tablero, encuentra una %…»)
 
 
Línea 1: Línea 1:
 
<source lang = "prolog">
 
<source lang = "prolog">
% Nuestro objetivo será diseñar un nivel de juego en el que el jugador
+
% El objetivo será diseñar un nivel de juego en el que el jugador
% empieza en la casilla de arriba y a la izda del tablero, encuentra una
+
% empieza en la casilla de arriba y a la izquierdada del tablero, encuentra  
% gema en el muro de la mazmorra, la lleva a un altar central donde
+
% una gema en el muro de la mazmorra, la lleva a un altar central donde
% magicamente desbloquea la salida, y luego va hasta la salida, situada
+
% mágicamente desbloquea la salida, y luego va hasta la salida, situada
 
% abajo y a la derecha.
 
% abajo y a la derecha.
  
#const anchura=10.
+
#const n=10.
param("anchura",anchura).
+
param("n",n).
  
dim(1..anchura).
+
dim(1..n).
  
 
% Definimos las casillas, a partir de la dimensión de nuestro tablero.
 
% Definimos las casillas, a partir de la dimensión de nuestro tablero.
 
casilla((X,Y)) :- dim(X),dim(Y).
 
casilla((X,Y)) :- dim(X),dim(Y).
  
% Ahora vamos a definir el predicado adyacente, a partir de que sabemos
+
% Dos casillas son consecutivas o adyacentes si |X1-X2| + |Y1-Y2| == 1.
% que dos casillas son consecutivas o adyacentes si |X1-X2| + |Y1-Y2| == 1.
 
 
adyacente((X1,Y1),(X2,Y2)):- casilla((X1,Y1)), casilla((X2,Y2)),
 
adyacente((X1,Y1),(X2,Y2)):- casilla((X1,Y1)), casilla((X2,Y2)),
 
                               X1-X2 == 1, Y1-Y2 == 0.
 
                               X1-X2 == 1, Y1-Y2 == 0.
Línea 27: Línea 26:
 
% La entrada al laberinto es la casilla (1,1), y la salida es la opuesta en
 
% La entrada al laberinto es la casilla (1,1), y la salida es la opuesta en
 
% el tablero.
 
% el tablero.
empieza((1,1)).
+
salida((1,1)).
acaba((anchura,anchura)).
+
llegada((n,n)).
  
% Las casillas tienen a lo sumo un icono:
+
% Las casillas tienen a lo sumo una marca:
0 { icono(T,bloque); icono(T,gema); icono(T,altar) } 1 :- casilla(T).
+
0 { marca(T,pared); marca(T,gema); marca(T,altar) } 1 :- casilla(T).
  
 
% Hay exactamente un altar y una gema en todo el laberinto:
 
% Hay exactamente un altar y una gema en todo el laberinto:
:- not 1 {icono(T,altar)} 1.
+
:- not 1 {marca(T,altar)} 1.
:- not 1 {icono(T,gema)} 1.
+
:- not 1 {marca(T,gema)} 1.
  
% Vamos a usar una combinación de reglas y restricciones para obtener
+
% Para obtener una mazmorra interesante debe de haber muchas paredes.
% mazmorras interesantes. Si hay pocos bloques, no parece una mazmorra.
+
% Por tanto, al menos la mitad de las casillas deben ser paredes:
% Luego al menos la mitad de las casillas deben ser bloques:
+
:- not ((n*n)/2) {marca(T,pared)}.
:- not ((anchura*anchura)/2) {icono(T,bloque)}.
 
  
 
% Un altar debe tener alrededor algunas casillas en blanco.
 
% Un altar debe tener alrededor algunas casillas en blanco.
% Luego un altar no puede tener paredes circundantes a menos de dos casillas.
+
% Luego, un altar no puede tener paredes circundantes a menos de dos casillas.
0 {icono(T3,bloque): adyacente(T1,T2), adyacente(T2,T3)} 0 :- icono(T1,altar).
+
0 {marca(T3,pared): adyacente(T1,T2), adyacente(T2,T3)} 0 :- marca(T1,altar).
  
 
% Los altares no pueden estar en el borde del mapa. Para ello imponemos que
 
% Los altares no pueden estar en el borde del mapa. Para ello imponemos que
 
% tenga 4 casillas adyacentes:
 
% tenga 4 casillas adyacentes:
:- icono(T1,altar), not 4 {adyacente(T1,T2)}.
+
:- marca(T1,altar), not 4 {adyacente(T1,T2)}.
  
 
% Las gemas deben estar adheridas a las paredes circundantes.
 
% Las gemas deben estar adheridas a las paredes circundantes.
3 {icono(T2,bloque): adyacente(T1,T2)} :- icono(T1,gema).
+
3 {marca(T2,pared): adyacente(T1,T2)} :- marca(T1,gema).
  
% Un bloque empieza a tener aspecto de muro cuando va seguido de otros
+
% Una pared empieza a tener aspecto de muro cuando va seguido de otras
% bloques. Luego imponemos que todo bloque tenga al menos dos bloques
+
% paredes. Luego imponemos que toda pared tenga al menos dos paredes
 
% vecinos:
 
% vecinos:
2 {icono(T2,bloque): adyacente(T1,T2)} :- icono(T1,bloque).
+
2 {marca(T2,pared): adyacente(T1,T2)} :- marca(T1,pared).
  
 
% Presentación:
 
% Presentación:
#show icono/2.
+
#show marca/2.
  
  
Línea 74: Línea 72:
  
 
% Comenzamos pues en el estado 1.  
 
% Comenzamos pues en el estado 1.  
% El predicado sobre describe sobre que icono (o casilla blanca) se espera
+
% El predicado sobre describe sobre que marca (o casilla blanca) se espera
 
% que el jugador esté en cada etapa para pasarse el nivel.
 
% que el jugador esté en cada etapa para pasarse el nivel.
 
% En el estado 1, el jugador empieza en la casilla de inicio:
 
% En el estado 1, el jugador empieza en la casilla de inicio:
sobre(T,1) :- empieza(T).
+
sobre(T,1) :- salida(T).
  
 
% Además, si estamos sobre una casilla, el jugador debe poder pasar a estar
 
% Además, si estamos sobre una casilla, el jugador debe poder pasar a estar
 
% sobre otra casilla adyacente a esta sin cambiar de estado. Si el jugador
 
% sobre otra casilla adyacente a esta sin cambiar de estado. Si el jugador
% está sobre el icono de una gema o altar, entonces puede cambiar al siguiente
+
% está sobre la marca de una gema o altar, entonces puede cambiar al siguiente
 
% estado.
 
% estado.
{ sobre(T2,2) : adyacente(T1,T2) } :- sobre(T1,1), icono(T1,gema).
+
{ sobre(T2,2) : adyacente(T1,T2) } :- sobre(T1,1), marca(T1,gema).
{ sobre(T2,3) : adyacente(T1,T2) } :- sobre(T1,2), icono(T1,altar).
+
{ sobre(T2,3) : adyacente(T1,T2) } :- sobre(T1,2), marca(T1,altar).
 
{ sobre(T2,S) : adyacente(T1,T2) } :- sobre(T1,S).
 
{ sobre(T2,S) : adyacente(T1,T2) } :- sobre(T1,S).
  
% El jugador no puede estar nunca sobre el icono de un bloque.
+
% El jugador no puede estar nunca sobre la marca de una pared.
:- icono(T,bloque), sobre(T,S).
+
:- marca(T,pared), sobre(T,S).
  
 
% La casilla final debe tocarse en la etapa 3. Luego el predicado completo
 
% La casilla final debe tocarse en la etapa 3. Luego el predicado completo
 
% se verifica solo si se está sobre la casilla en la que hay que acabar y
 
% se verifica solo si se está sobre la casilla en la que hay que acabar y
 
% y estamos en la etapa 3.
 
% y estamos en la etapa 3.
completo :- acaba(T), sobre(T,3).
+
completo :- llegada(T), sobre(T,3).
 
:- not completo.
 
:- not completo.
  
% Finalmente, vamos a imponer que si se cumplen las restricciones de los iconos,
+
% Finalmente, vamos a imponer que si se cumplen las restricciones de las marcas,
% entonces es seguro que toda solución debe pasar por n casillas al menos,
+
% entonces es seguro que toda solución debe pasar por al menos n casillas,
 
% siendo n la anchura del tablero:
 
% siendo n la anchura del tablero:
__level_design(icono(T,Nombre)) :- icono(T,Nombre).
+
__level_design(marca(T,Nombre)) :- marca(T,Nombre).
__concept :- anchura { sobre(T,1) },
+
__concept :- n { sobre(T,1) },
             anchura { sobre(T,2) },
+
             n { sobre(T,2) },
             anchura { sobre(T,3) }.
+
             n { sobre(T,3) }.
  
 
</source>
 
</source>

Revisión actual del 09:57 3 feb 2020

% El objetivo será diseñar un nivel de juego en el que el jugador
% empieza en la casilla de arriba y a la izquierdada del tablero, encuentra 
% una gema en el muro de la mazmorra, la lleva a un altar central donde
% mágicamente desbloquea la salida, y luego va hasta la salida, situada
% abajo y a la derecha.

#const n=10.
param("n",n).

dim(1..n).

% Definimos las casillas, a partir de la dimensión de nuestro tablero.
casilla((X,Y)) :- dim(X),dim(Y).

% Dos casillas son consecutivas o adyacentes si |X1-X2| + |Y1-Y2| == 1.
adyacente((X1,Y1),(X2,Y2)):- casilla((X1,Y1)), casilla((X2,Y2)),
                               X1-X2 == 1, Y1-Y2 == 0.
adyacente((X1,Y1),(X2,Y2)):- casilla((X1,Y1)), casilla((X2,Y2)),
                               X2-X1 == 1, Y1-Y2 == 0.
adyacente((X1,Y1),(X2,Y2)):- casilla((X1,Y1)), casilla((X2,Y2)),
                               X1-X2 == 0, Y1-Y2 == 1.
adyacente((X1,Y1),(X2,Y2)):- casilla((X1,Y1)), casilla((X2,Y2)),
                               X2-X1 == 0, Y2-Y1 == 1.

% La entrada al laberinto es la casilla (1,1), y la salida es la opuesta en
% el tablero.
salida((1,1)).
llegada((n,n)).

% Las casillas tienen a lo sumo una marca:
0 { marca(T,pared); marca(T,gema); marca(T,altar) } 1 :- casilla(T).

% Hay exactamente un altar y una gema en todo el laberinto:
:- not 1 {marca(T,altar)} 1.
:- not 1 {marca(T,gema)} 1.

% Para obtener una mazmorra interesante debe de haber muchas paredes.
% Por tanto, al menos la mitad de las casillas deben ser paredes:
:- not ((n*n)/2) {marca(T,pared)}.

% Un altar debe tener alrededor algunas casillas en blanco.
% Luego, un altar no puede tener paredes circundantes a menos de dos casillas.
0 {marca(T3,pared): adyacente(T1,T2), adyacente(T2,T3)} 0 :- marca(T1,altar).

% Los altares no pueden estar en el borde del mapa. Para ello imponemos que
% tenga 4 casillas adyacentes:
:- marca(T1,altar), not 4 {adyacente(T1,T2)}.

% Las gemas deben estar adheridas a las paredes circundantes.
3 {marca(T2,pared): adyacente(T1,T2)} :- marca(T1,gema).

% Una pared empieza a tener aspecto de muro cuando va seguido de otras
% paredes. Luego imponemos que toda pared tenga al menos dos paredes
% vecinos:
2 {marca(T2,pared): adyacente(T1,T2)} :- marca(T1,pared).

% Presentación:
#show marca/2.


% Vemos que efectivamente, la gema aparece rodeada de muchos bloques.
% Pero no tenemos que olvidar que el nivel debe ser jugable. 


% REPRESENTACIÓN DEL ESPACIO DE SOLUCIONES VÁLIDAS:

% Para ello vamos a definir tres estados:
% Estado 1: inicial
% Estado 2: después de coger la gema
% Estado 3: después de llevar la gema al altar

% Comenzamos pues en el estado 1. 
% El predicado sobre describe sobre que marca (o casilla blanca) se espera
% que el jugador esté en cada etapa para pasarse el nivel.
% En el estado 1, el jugador empieza en la casilla de inicio:
sobre(T,1) :- salida(T).

% Además, si estamos sobre una casilla, el jugador debe poder pasar a estar
% sobre otra casilla adyacente a esta sin cambiar de estado. Si el jugador
% está sobre la marca de una gema o altar, entonces puede cambiar al siguiente
% estado.
{ sobre(T2,2) : adyacente(T1,T2) } :- sobre(T1,1), marca(T1,gema).
{ sobre(T2,3) : adyacente(T1,T2) } :- sobre(T1,2), marca(T1,altar).
{ sobre(T2,S) : adyacente(T1,T2) } :- sobre(T1,S).

% El jugador no puede estar nunca sobre la marca de una pared.
:- marca(T,pared), sobre(T,S).

% La casilla final debe tocarse en la etapa 3. Luego el predicado completo
% se verifica solo si se está sobre la casilla en la que hay que acabar y
% y estamos en la etapa 3.
completo :- llegada(T), sobre(T,3).
:- not completo.

% Finalmente, vamos a imponer que si se cumplen las restricciones de las marcas,
% entonces es seguro que toda solución debe pasar por al menos n casillas,
% siendo n la anchura del tablero:
__level_design(marca(T,Nombre)) :- marca(T,Nombre).
__concept :- n { sobre(T,1) },
             n { sobre(T,2) },
             n { sobre(T,3) }.