La dificultad de este videojuego (y de otros
posteriores) es que tendremos que trabajar con tablas y llevar un doble juego
de coordenadas: uno para la tabla y otro para la representación visual del
juego en función del contenido de la tabla.
1.- Trabajando con tablas (vectores
multidimensionales): aunque la representación visual es la que es, este
juego está pensado sobre una tabla de 21 columnas y 15 filas. Para ello primero
llenamos todas las celdas de la tabla con el valor 0. A la celda donde hay comida
le damos el valor -1 (marcado en rojo). Y la celda que tiene la serpiente le
damos un valor con el que comienza la cabeza de la serpiente: 1 (marcado en
azul). Un ejemplo de esto (con una tabla de 8x8) sería así:
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
-1
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Para definir una tabla se usa mdarray y podríamos
hacerlo con esta sintaxis:
make
"tabla (mdarray (21 16) 1)
de esta manera definimos una variable que se llama tabla que va a ser una variable
multidimensional (tendrá una composición de 21 columnas y 16 filas). El último
número (el 1) sirve para indicar que no empiece en 0, sino en 1. Trabajar con
tablas (o vectores multidimensionales) es algo engorroso y a veces no obtenemos
el resultado deseado. En este caso, puede verse en la programación que en el
procedimiento comienzo se define lo
siguiente:
make
"size_x 21
make
"size_y 16
make
"tabla (mdarray (list :size_x :size_y) 1)
es decir, en vez de usar directamente números
usamos variables que contienen esos números y las metemos como una lista
ordenada (es lo que hace el comando list).
Una vez definida ya nuestra tabla podemos rellenar
el valor de la celdas de la misma:
mdsetitem
(list :cx :cy) :tabla -1 ;cx y cy son
coordenadas de la comida
el comando mdsetitem sirve para
dar valores dentro del vector multidimensional, en este caso haremos que el
contenido de tabla, en su celda cx,
cy, valga -1.
Para llenar toda la tabla de
valores 0:
for [i 1 21 1] [for [j 1 16 1] [mdsetitem (list :i :j)
:tabla 0]]
hacemos lo mismo que en el caso
anterior, pero usando dos bucles for
anidados para llenen toda la tabla con el valor 0 (obviamente este paso se hace
sólo al principio antes de llenarlo con el valor de la comida escrito
anteriormente).
Finalmente escribimos el
contenido de la celda en la que irá la cabeza de la serpiente:
mdsetitem (list :xx :yy) :tabla :tamanio
siendo :tamanio una variable cuyo primer valor será 1 y que irá aumentando
según “coma” la serpiente.
2.- Trabajando con dos
sistemas simultáneos de coordenadas, matriz y visual: tendremos dos
sistemas de coordenadas: (xx,yy) serán las coordenadas de la cabeza de la
serpiente correspondientes a la celda que ocupa en la tabla y (x,y) serán las
coordenadas de la cabeza de la serpiente correspondiente a la posición en la
pantalla. De igual manera tendremos (cx,cy) para la situación en la tabla de la
comida y (ccx, ccy) para su representación en la pantalla. Esta situación es
debido a que los cálculos se realizan sobre la tabla y, obviamente, no vamos a
jugar con un único píxel que se mueve, queremos algo más grande. En nuestro
caso algo 8 veces más grande. Ajustando cambio de escala y origen en función de
dónde hemos dibujado el campo, tenemos algo como esto para las coordenadas de
la cabeza de la serpiente:
make "x -80
make "y -80
make "xx ((x/8)+30)
make "yy ((y/8)+16)
esto nos deja (-80,-80) para
dibujar la serpiente cuya posición en la tabla será de (20,6).
De igual manera, para la comida:
make
"ccx (random 90)-180
make
"ccy (random 40)-70
make
"cx int ((ccx/8)+30)
make
"cy int ((ccy/8)+16)
el cambio de escala es el mismo
(dividir entre 8 y sumar un valor constante) pero introduciendo que la comida
aparece de manera aleatoria.
En resumen, vamos a manejar dos
conjuntos de coordenadas: una para los cálculos de lo que debe hacer el juego
(sobre la tabla) y el otro para poder visualizar dónde están los elementos en
la pantalla.
3.- El caracter virgulilla
(~): una ojeada al código nos permite
ver que hacemos uso por primera vez de este caracter. Se usa cuando la línea es
demasiado larga para que siga leyendo el siguiente renglón como si no hubiera
un intro:
for [i 1 21 1] [for [j 1 15 1] [make "dentro
mditem (list :i :j) :tabla ~
if dentro > 0 [mdsetitem
(list :i :j) :tabla dentro-1] ~
if dentro = 1 [penup setxy ((i*8)-240) ((j*8)-128)
pendown borraserpiente]]]
de tal forma que estos tres
renglones el compilador los interpreta como una única línea (que es en realidad
lo que deben ser).
4.- Explicación del
funcionamiento del videojuego: supongamos que tenemos la posición indicada
en el apartado anterior y supongamos que la cabeza de la serpiente (la
serpiente sólo ocupa una celda de momento) se esté moviendo hacia la izquierda.
Tenemos 4 posibilidades:
-
Posibilidad 1: que choque con la pared
de la izquierda (esto se hizo con coordenadas normales, no con las matrices,
algo ya explicado anteriormente): if x
<= -230 [fin], es decir, si la coordenada “x” de la serpiente es menor
que -230 llama al procedimiento fin
que acaba la partida.
-
Posibilidad 2: la cabeza de la
serpiente entra en una celda cuyo valor es 0. Si esto es así, no ocurre nada,
salvo que la coordenada xx disminuye
en una unidad (pues iba hacia la izquierda). Para saber el contenido de una
celda definimos una variable llamada contenido que precisamente mira el
contenido de la tabla en las coordenadas que ocuparía la cabeza de la serpiente
en el siguiente paso: make
"contenido mditem (list :xx :yy) :tabla.
-
Posibilidad 3: la cabeza de la
serpiente entra en una celda cuyo valor es mayor o igual a 1, esto significa
que ha chocado contra su propio cuerpo y, por lo tanto, pierde. Para ello: if contenido >= 1 [fin].
-
Posibilidad 4: la cabeza de la
serpiente entra en una celda cuyo valor es -1, lo que significa que ha
“chocado” con la comida, de esta forma el tamaño de la serpiente aumenta en uno
y la comida debe aparecer en otro lado de la pantalla. Para ello: if contenido = -1 [comer].
En el procedimiento principal juego se llama al procedimiento ciclo que recorre todas las casillas y
si el contenido es mayor que uno le resta uno, es el encargado de realizar la
simulación del movimiento de la serpiente. Veamos el funcionamiento con este
ejemplo:
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
-1
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
La serpiente (de tamaño 1 y en
azul) se acerca (suponemos movimiento a la izquierda) a la comida. Como el
cuadro en el que va a entrar tiene un 0 no ocurre nada.
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
-1
|
1
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Ahora la serpiente entrará en
una casilla en la que hay comida, por el que su tamaño aumentará en uno y la comida
aparecerá en otra casilla. Como el tamaño aumenta en uno, el valor del
contenido de la casilla en la que estaba la comida aumenta en uno, es decir,
vale dos, por lo que justo en ese momento:
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
-1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
2
|
1
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Si ahora movemos hacia arriba
para buscar la comida, el procedimiento ciclo
resta uno a todos los contenidos mayores que cero y el procedimiento serpiente
pone un contenido igual al tamaño en el punto en el que está la cabeza de la
serpiente:
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
-1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
2
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Siguiendo el procedimiento:
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
3
|
0
|
-1
|
0
|
0
|
0
|
0
|
0
|
2
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Si restamos uno de nuevo a todos
los contenidos mayores que cero y ponemos el valor del tamaño (ahora 3) en la
cabeza de la serpiente, si giramos a la derecha:
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
2
|
3
|
-1
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
1
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
0
|
Y así sucesivamente vamos
simulando el movimiento de la serpiente.
Con todo esto ya podemos entender
el código del juego.
Código del juego
;
; Videojuego de la serpiente
; Raultecnologia
; el programa esta en LOGO para usar directamente en el UCBLogo
; para empezar el juego teclea el procedimiento inicio
;
: en este juego usaremos los procedimientos MDARRAY, MDITEM y MDSETITEM
; para trabajar con vectores que tienen la informacion de que hay en cada celda
;
;
; el procedimiento inicio establece la pantalla de presentacion
;
to inicio
clearscreen
hideturtle
setpencolor 7
penup
setxy -200 140
label "Videojuego_de_la_serpiente
setxy -200 110
label "Usa_las_teclas_q_a_o_p_para_mover_a_la_serpiente
setxy -200 0
setpencolor 3
label "Pulsa_cualquier_tecla_para_empezar
make "tecla rc
comienzo
end
;
; el procedimiento comida genera al azar las coordenadas para
; que apareza la comida y la dibuja
;
to comida
make "ccx (random 90)-180
make "ccy (random 40)-70
make "cx int ((ccx/8)+30)
make "cy int ((ccy/8)+16)
make "haydentro mditem (list :cx :cy) :tabla
;miramos el contenido de la celda para la comida, si esta ocupada reiniciamos
if haydentro <> 0 [comida]
mdsetitem (list :cx :cy) :tabla -1
;la comida se marca con el numero -1
penup
setxy (cx*8)-236 (cy*8)-124
pendown
setpensize 1
setpencolor 4
for [i 1 3 1] [arc 360 i]
end
;
; el procedimiento comienzo establece las condiciones iniciales
;
to comienzo
clearscreen
campo
penup
hideturtle
make "tamanio 1
make "x -80
make "y -80
make "xx ((x/8)+30)
make "yy ((y/8)+16)
;manejamos dos tipos de coordenadas. x, y son las de la pantalla para dibujar
;xx, yy son las de las celdas correspondientes
make "puntos 0
make "vx -8
make "vy 0
;vx, vy son las velocidades iniciales
setxy -200 40
label "Puntos
setxy -100 40
label puntos
make "size_x 21
make "size_y 16
make "tabla (mdarray (list :size_x :size_y) 1)
;definimos el tamanio de la tabla
for [i 1 21 1] [for [j 1 16 1] [mdsetitem (list :i :j) :tabla 0]]
comida
mdsetitem (list :cx :cy) :tabla -1
serpiente
juego
end
;
; el procedimiento ciclo recorre todas las casillas y si el contenido de la celda es positivo,
; resta uno, y si es 1 borra la serpiente, pues es la cola
;
to ciclo
for [i 1 21 1] [for [j 1 15 1] [make "dentro mditem (list :i :j) :tabla ~
if dentro > 0 [mdsetitem (list :i :j) :tabla dentro-1] ~
if dentro = 1 [penup setxy ((i*8)-240) ((j*8)-128) pendown borraserpiente]]]
end
;
; el procedimiento campo sencillamente dibuja el campo de juego
;
to campo
setpencolor 4
penup
setxy -228 -117
setpensize 8
pendown
fd 121
rt 90
fd 161
rt 90
fd 121
rt 90
fd 161
rt 90
penup
end
;
; el procedimiento serpiente dibuja la cabeza de la serpiente
;
to serpiente
penup
setxy x y
mdsetitem (list :xx :yy) :tabla :tamanio
pendown
setpensize 1
setpencolor 7
for [i 1 8 1] [fd i rt 90 fd i rt 90 fd i rt 90 fd i rt 90]
end
;
; el procedimiento borraserpiente borra el cuadro anterior
;
to borraserpiente
setpencolor 0
setpensize 1
pendown
for [i 1 8 1] [fd i rt 90 fd i rt 90 fd i rt 90 fd i rt 90]
end
;
; el procedimiento juego controla las condiciones del juego
;
to juego
mdsetitem (list :xx :yy) :tabla :tamanio
if keyp [make "tecla rc]
if tecla = "q [make "vx 0 make "vy 8]
if tecla = "a [make "vx 0 make "vy -8]
if tecla = "p [make "vx 8 make "vy 0]
if tecla = "o [make "vx -8 make "vy 0]
if x > -78 [fin]
if x <= -230 [fin]
if y >= -4 [fin]
if y <= -116 [fin]
make "x x+vx
make "y y+vy
make "xx ((x/8)+30)
make "yy ((y/8)+16)
make "contenido mditem (list :xx :yy) :tabla
if contenido = -1 [comer]
if contenido >= 1 [fin]
serpiente
ciclo
juego
end
to comer
penup
setxy x y
borraserpiente
make "tamanio tamanio+1
make "puntos puntos+10
penup
setxy -100 40
setpencolor 4
label puntos
comida
end
;
; el procedimiento fin establece el final de la partida
;
to fin
clearscreen
setpencolor 7
penup
setxy 0 0
label "has_terminado_la_partida
setxy 0 -30
label "puntos=
setxy 70 -30
label puntos
setxy 0 -100
label "pulsa_cualquier_tecla_para_comenzar
make "tecla2 rc
inicio
end
; Videojuego de la serpiente
; Raultecnologia
; el programa esta en LOGO para usar directamente en el UCBLogo
; para empezar el juego teclea el procedimiento inicio
;
: en este juego usaremos los procedimientos MDARRAY, MDITEM y MDSETITEM
; para trabajar con vectores que tienen la informacion de que hay en cada celda
;
;
; el procedimiento inicio establece la pantalla de presentacion
;
to inicio
clearscreen
hideturtle
setpencolor 7
penup
setxy -200 140
label "Videojuego_de_la_serpiente
setxy -200 110
label "Usa_las_teclas_q_a_o_p_para_mover_a_la_serpiente
setxy -200 0
setpencolor 3
label "Pulsa_cualquier_tecla_para_empezar
make "tecla rc
comienzo
end
;
; el procedimiento comida genera al azar las coordenadas para
; que apareza la comida y la dibuja
;
to comida
make "ccx (random 90)-180
make "ccy (random 40)-70
make "cx int ((ccx/8)+30)
make "cy int ((ccy/8)+16)
make "haydentro mditem (list :cx :cy) :tabla
;miramos el contenido de la celda para la comida, si esta ocupada reiniciamos
if haydentro <> 0 [comida]
mdsetitem (list :cx :cy) :tabla -1
;la comida se marca con el numero -1
penup
setxy (cx*8)-236 (cy*8)-124
pendown
setpensize 1
setpencolor 4
for [i 1 3 1] [arc 360 i]
end
;
; el procedimiento comienzo establece las condiciones iniciales
;
to comienzo
clearscreen
campo
penup
hideturtle
make "tamanio 1
make "x -80
make "y -80
make "xx ((x/8)+30)
make "yy ((y/8)+16)
;manejamos dos tipos de coordenadas. x, y son las de la pantalla para dibujar
;xx, yy son las de las celdas correspondientes
make "puntos 0
make "vx -8
make "vy 0
;vx, vy son las velocidades iniciales
setxy -200 40
label "Puntos
setxy -100 40
label puntos
make "size_x 21
make "size_y 16
make "tabla (mdarray (list :size_x :size_y) 1)
;definimos el tamanio de la tabla
for [i 1 21 1] [for [j 1 16 1] [mdsetitem (list :i :j) :tabla 0]]
comida
mdsetitem (list :cx :cy) :tabla -1
serpiente
juego
end
;
; el procedimiento ciclo recorre todas las casillas y si el contenido de la celda es positivo,
; resta uno, y si es 1 borra la serpiente, pues es la cola
;
to ciclo
for [i 1 21 1] [for [j 1 15 1] [make "dentro mditem (list :i :j) :tabla ~
if dentro > 0 [mdsetitem (list :i :j) :tabla dentro-1] ~
if dentro = 1 [penup setxy ((i*8)-240) ((j*8)-128) pendown borraserpiente]]]
end
;
; el procedimiento campo sencillamente dibuja el campo de juego
;
to campo
setpencolor 4
penup
setxy -228 -117
setpensize 8
pendown
fd 121
rt 90
fd 161
rt 90
fd 121
rt 90
fd 161
rt 90
penup
end
;
; el procedimiento serpiente dibuja la cabeza de la serpiente
;
to serpiente
penup
setxy x y
mdsetitem (list :xx :yy) :tabla :tamanio
pendown
setpensize 1
setpencolor 7
for [i 1 8 1] [fd i rt 90 fd i rt 90 fd i rt 90 fd i rt 90]
end
;
; el procedimiento borraserpiente borra el cuadro anterior
;
to borraserpiente
setpencolor 0
setpensize 1
pendown
for [i 1 8 1] [fd i rt 90 fd i rt 90 fd i rt 90 fd i rt 90]
end
;
; el procedimiento juego controla las condiciones del juego
;
to juego
mdsetitem (list :xx :yy) :tabla :tamanio
if keyp [make "tecla rc]
if tecla = "q [make "vx 0 make "vy 8]
if tecla = "a [make "vx 0 make "vy -8]
if tecla = "p [make "vx 8 make "vy 0]
if tecla = "o [make "vx -8 make "vy 0]
if x > -78 [fin]
if x <= -230 [fin]
if y >= -4 [fin]
if y <= -116 [fin]
make "x x+vx
make "y y+vy
make "xx ((x/8)+30)
make "yy ((y/8)+16)
make "contenido mditem (list :xx :yy) :tabla
if contenido = -1 [comer]
if contenido >= 1 [fin]
serpiente
ciclo
juego
end
to comer
penup
setxy x y
borraserpiente
make "tamanio tamanio+1
make "puntos puntos+10
penup
setxy -100 40
setpencolor 4
label puntos
comida
end
;
; el procedimiento fin establece el final de la partida
;
to fin
clearscreen
setpencolor 7
penup
setxy 0 0
label "has_terminado_la_partida
setxy 0 -30
label "puntos=
setxy 70 -30
label puntos
setxy 0 -100
label "pulsa_cualquier_tecla_para_comenzar
make "tecla2 rc
inicio
end