1. #IWT2 Dojo US
Kata Sokoban Paso a Paso
www.iwt2.org
formacion@iwt2.org
2. Próximas actividades
• Próxima reunión
– Fecha: 9-noviembre-2012
– Tema: SOLID y GRASP.
– Síguenos en twitter: #IWT2
• Próxima reunión (tentativa)
– Fecha: 15-diciembre-2012
Hasta febrero – Tema: Pruebas.
3. Próximas actividades
• Inscripción: hasta
principios de diciembre.
• Comienzo en enero (todo
el mes)
• Completamente a
distancia.
• Materiales ya disponibles
en la web del grupo IWT2
http://goo.gl/fCRU1
4. Próximas actividades
• Foros de TDD.
– Facebook: grupo Test-Driven Development (TDD)
en español
– Twitter: #IWT2, @IWT2_Javier
– Información del IWT2 en Facebook y LinkedIn.
5. Objetivos
• Estudiar paso a paso
cómo resolver la kata
Sokoban con TDD
• Exponer qué otras
alternativas de diseño de
podrían haber tomado.
• Ofrecer notas sobre una
implementación real con
gráficos y varias
plataformas (escritorio y
Android).
Objetivos
5
6. Índice
1. Kata Sokoban.
2. Primer diseño (y
pruebas).
3. Cómo crear almacenes y
jugadores
4. Muévelo.
5. Moviendo la primera
caja.
6. El final del movimiento.
7. Terminar el juego.
8. Implementando el resto
del juego.
Índice
6
8. Cata - Sokoban
Reglas
• Sólo se puede empujar una
caja a la vez.
• No puedes empujar un muro.
• El jugador no puede caminar a
través de cajas y muros.
• El puzzle se soluciona cuando
todas las cajas están sobre
Glosario: casillas de almacenaje.
Almacén, Jugador, Mover
(jugador), Caja, Pared, Empujar, Ca
silla libre, Casilla de almacenaje http://en.wikipedia.org/wiki/Sokoban
8
9. Cata - Sokoban
Reglas
• Asumimos que el almacén es
perfecto.
• Siempre está rodeado de
muros.
• El jugador comienza en una
posición válida
• Las cajas comienzan en
posiciones válidas
• El almacén tiene, al
menos, una solución
9
10. Cata - Sokoban
Metas:
• Cómo representar un almacén.
• Determinar movimiento válido o no.
– Mueve contra una pared
– Mueve contra una casilla.
– Mueve contra una caja que no se puede
mover
– Mueve contra una caja que sí se puede
mover.
• Modificar el almacén con el resultado
de un movimiento válido.
– Jugador cambia de posición
– Jugador y caja cambian de posición.
• Detectar el fin del juego con éxito.
• Opción de deshacer movimientos
Libertad para priorizar
• Opción de añadir gráficos.
• Cualquier cosa que puedas compartir
10
12. Primeros pasos
Metas:
• Cómo representar un almacén.
• Determinar movimiento válido o no.
– Mueve contra una pared Tenemos que empezar por
– Mueve contra una casilla. algo. ¿Por dónde?
– Mueve contra una caja que no se puede
mover
– Mueve contra una caja que sí se puede
mover.
• Modificar el almacén con el resultado Mover
de un movimiento válido.
– Jugador cambia de posición Mover
– Jugador y caja cambian de posición.
• Detectar el fin del juego con éxito.
• Opción de deshacer movimientos
• Opción de añadir gráficos.
• Cualquier cosa que puedas compartir
12
13. Primeros pasos
Ejemplos prácticos (candidatos a casos de prueba)
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
28 casos de prueba posibles 13
14. Primeros pasos
Pruebas 101
Ejemplos prácticos (candidatos a casos de prueba)
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha
¿Cuálesderecha nuestros la El jugadorde caja se desplaza una casilla a la
Caja a la
son y casilla libre a valores y la prueba?
derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
El almacén y la posición del jugador
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
14
15. La Primera Prueb
¿Cómo será nuestra prueba? • ¿Cómo creamos el almacén?
1
• ¿Cómo le decimos dónde están
los muros?
• ¿Cómo sabemos dónde está el
jugador?
• ¿Cómo modelamos las
posiciones?
2
• ¿Cómo indicamos que queremos
mover al jugador?
Con nuestra primera prueba 3 • ¿Cómo sabemos que el jugador
vamos a empezar a contestar ha cambiado de posición?
a estas preguntas
y, así, veremos si elegimos
una buena alternativa o no
16. Primeros pasos
Creamos nuestro
• ¿Cómo indicamos que queremos
primer intento
mover al jugador?
de prueba
Así.
2
Aún no es una prueba completa:
No creo los objetos que estoy probando
¿Quiero probar si el movimiento es válido o el jugador se mueve?
No estoy verificando nada
17. Primeros pasos
También hemos tomado muchas decisiones de diseño.
¿Por qué devuelvo un
¿No hay objeto jugador?
booleano? ¿Qué significa?
¿Es e almacén el que tiene que ¿Indico los movimientos con
mover al jugador un enumerado?
18. Primeros pasos
• ¿Cómo creamos un almacén?
• ¿Cómo le ponemos un jugador?
• Tenemos varias opciones. 1
1) Escribimos pruebas que nos dirijan
a la creación e un objeto almacén
que conozca todo el entorno y Elegimos la tercera
pueda mover al jugador opción
2) Creamos mocks u objetos fake que
simulen un almacén y lo usamos
como queramos utilizarlo.
3) Implementamos u objeto almacén
y jugador con el código más
sencillo posible para enriquecerlo
cuando abordemos el punto 1.
19. Primeros pasos
• ¿Cómoc reamos un almacén?
• Tenemos varias opciones.
1) Escribimos pruebas que nos dirijan 1
a la creación e un objeto almacén
que conozca todo el entorno y
pueda mover al jugador
2) Creamos mocks u objetos fake que
simulen un almacén y explicado
No hemos lo usamos
mocks
como queramos utilizarlo.
3) Implementamos u objeto almacén
con el código más sencillo posible
Son la misma
para enriquecerlo cuando
abordemos el punto 1.
21. Cómo crear almacenes
¿Cómo le indico la información del almacén?
Acción Información de entrada Resultado
Crear un almacén Muros, casillas libres, cajas, etc. El objeto almacén conoce esta información.
El jugador está en la casilla inicial.
En las casillas indicadas hay cajas.
Las cajas y el jugador se mueven sobre las
casillas.
Hay dos tipos de casillas distintas: libres y de
almacenaje
Etc.
Centramos nuestro foco no en
funcionalidad sino en facilidad
23. Cómo continuar aplicando TDD
Este es un posible mínimo código
que pasa la prueba.
Al final arreglamos el
atajo con un método
accesor.
24. Cómo continuar aplicando TDD
Ya hemos diseñado cómo definir un almacén ¿Qué
hacemos ahora
Centramos nuestro foco en continuar la prueba de
mover a un jugador.
Completamos esta prueba.
25. Cómo continuar aplicando TDD
Decidimos cómo modelar
un jugador y vincularlo al
almacén.
Ya tenemos un almacén con casillas y bloques y un
jugador que se mueve. Ya podemos implementar
27. Cómo crear almacenes
Prueba: Código
• No usamos el método booleano. Cambiamos el set
• Refactorizar la creación de mapas por el add
28. Cómo crear almacenes
• Aún podemos refactorizar más.
• ¿Puede existir un almacén sin jugador
inicial?
• Si esto no es así podría ser buena idea
añadir el jugador al constructor y nos
ahorramos un método.
• De momento no lo hago, tal vez más
adelante
29. Primeros pasos
¿Qué hacemos ahora
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
29
30. Primeros pasos
Otros diseños alternativos que podríamos haber elegido.
Buscamos la manera que
nos resulte más cómoda
34. Muévelo
Seguimos
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
Esta prueba no me lleva a escribir ningún código
nuevo, me la salto.
34
35. Muévelo
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
Mi siguiente prueba / avance.
35
38. Muévelo
Segimos
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
38
39. Muévelo
• Detengámonos a pensar
• ¿Por dónde quiero seguir?
• ¿Empezamos a movernos en otras direcciones
o tenemos en cuenta las cajas en el
movimiento? Cajas
• Podemos abordar el subconjunto de pruebas
que hacen que una caja se mueva o el
subconjunto de pruebas que hacen que una
caja nos e mueva. Vamos por el segundo.
41. Moviendo la primera caja
Seguimos
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
Elijo la prueba que creo más interesante
41
42. Moviendo la primera caja
He tenido que decidir cómo
incorporar las cajas (buscando lo
más cómodo para nosotros)
Las coordenadas de la caja
no cambian
43. Moviendo la primera caja
Tendremos que escribir pruebas con más
cajas para evolucionar este atajo.
Podemos ver que el código es bastante malo. Siempre
incrementa la caja y solo trabaja con ella. Lo mejoraremos con
más pruebas.
46. Moviendo la primera caja
Seguimos
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
No quiero dar por finalizada esta funcionalidad porque el
código no funciona con más cajas. Escribo una prueba
adicional 46
47. Pruebas para cajas
Ahora hay más de una caja
Este es el fallo que me hace
evolucionar mi código.
48. Moviendo la primera caja
Vamos a hacer una pausa y vamos a reflexionar cómo queremos
guardar las cajas. Aplicando TDD claro
Escribimos rápidamente una
prueba que me haga poner
claro cómos e almacenarán
las cajas
49. Moviendo la primera caja
Quitamos el atributo b. Esto hace que falle una prueba, pero
remplazamos B por boxs.get(0)
Es un atajo equivalente que quitaremos cuando completemos
la prueba que dejamosa medias
50. Pruebas para cajas
Ya tenemos lo necesario para retomar esta
prueba e implementarla
53. Moviendo la primera caja
Seguimos
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
Siguiente prueba
53
55. No moviendo las caja
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
Ahora ya no debería importar si hago pruebas con una o
varias cajas. Si tengo dudas escribo pruebas adicionales. 55
59. No moviendo las caja
Seguimos
Acción Estado Resultado
Mover derecha Casilla libre a la derecha El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
59
60. No moviendo las cajas
No pasa el primer assert porque el jugador sí se mueve
61. No moviendo las cajas
El código es difícil de entender y de modificar. Se puede
simplificar (por ejemplo las llamadas para obtener las
coordenadas)
62. No moviendo las cajas
Seguimos
Acción Estado Resultado
Mover derecha Casilla libre a la derecha º El jugador se desplaza una casilla a la derecha
Mover derecha Casilla de almacenaje a la derecha Muévelo
El jugador se desplaza una casilla a la derecha
Mover derecha Muro a la derecha El jugador mantiene la misma posición
Mover derecha Caja a la derecha y casilla libre a la El jugador y la caja se desplaza una casilla a la
derecha de la caja derecha
Mover derecha Caja a la derecha y casilla de El jugador y la caja se desplaza una casilla a la
almacenaje a la derecha de la caja derecha
Mover derecha Caja a la derecha y otra caja libre El jugador y la caja mantiene la misma posición
a la derecha de la caja
Mover derecha Caja a la derecha y muro a la El jugador y la caja mantiene la misma posición
derecha de la caja
… … …
Terminado (mover a la derecha)
62
63. No moviendo las cajas
• Puedo añadir pruebas adicionales para
asegurarme de que todo funciona bien.
• Estas pruebas son redundantes (si hemos
hecho bien el estudio del problema) pero me
ayuda aganar seguridad en mi código.
65. Libertad
• Ahora hacemos lo mismo pero los demás
sentidos.
• Es decir copy & paste
• ¿Cómo tratamos una situación tan clara de copy
& paste?
• No tenemos por qué teclear código que ya
tenemos
• Lo que tenemos que hacer es pone mucho
interés en la refactorización y evitar que el copy &
paste me lleve a código duplicado (o sin pruebas)
• Veámoslo a continuación.
72. El final del movimiento
Aún se puede refactorizar
más, por ejemplo dividiendo en
varios métodos, cambiar los if
por un switch, etc.
Movimientos a derecha e izquierda, arriba y abajo
75. Terminar el juego
Ejemplos prácticos (candidatos a casos de prueba)
Acción Estado Resultado
Ver si es fin de Al menos una casilla de No es fin de partida
partida almacenaje no tiene encima una
casa
Ver si es fin de Todas las casillas de almacenaje Es fin de partida
partida tienen encima una casa
Continuamos aplicando TDD
75
76. Terminar el juego
Tenemos que escribir más pruebas para que este código
progrese (triangular).
Escribamos la siguiente prueba
77. Terminar el juego
Coloco las cajas directamente en
las casillas de almacenaje.
79. Terminar el juego
• Si hemos hecho bien el trabajo estudiando las
pruebas, el método anterior debería funcionar
bien en cualquier combinación.
• Esto es lo básico para trabajar. Ya podemos
implementar el juego.
81. Implementando el resto del juego
• Necesitamos poder pintar
gráficamente el almacén, leer el
teclado y convertir las
pulsaciones en movimientos y
gestionar los estados del juego
(jugando, fin de juego).
• He elegido LibGDX para
implementarlo (escritorio y
Android)
• No he utilizado TDD (pero
tengo pendiente hacer una
versión 2 aplicándola)