Diferencia entre revisiones de «Ejemplo 1»
De Lógica computacional y teoría de modelos (2019-20)
(Página creada con «<source lang = "prolog"> % Definimos las dimensiones del tablero #const anchura = 5. dim(1..anchura). % Para cada casilla distinta a (1,1) le asignamos un solo movimiento:…») |
|||
Línea 1: | Línea 1: | ||
<source lang = "prolog"> | <source lang = "prolog"> | ||
− | % | + | % 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. |
− | :- dim(X), dim( | + | 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: | % Presentación: | ||
− | #show | + | #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) }. | ||
+ | |||
</source> | </source> |
Revisión del 09:56 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) }.