Movimiento

Cada vez que el jugador realice un movimiento, el programa lo almacenará en una pila. Esto nos permitirá implementar una opción de deshacer.

Cada movimiento necesita almacenar dos valores:

origen:
Identificador de la pila de origen del Naipe.
destino:
Identificador de la pila de destino del Naipe.

Podríamos pensar que es necesario almacenar un tercer valor para indicar si la carta debe ser cambiada de cara. Pero esto sólo ocurrirá si la carta procede del Mazo, ya que es la única pila que contiene naipes boca abajo. En rigor, podremos prescindir de almacenar este valor, ya que es calculable. Este valor será verdadero siempre que la pila de origen sea el Mazo. Además, como del Mazo sólo podemos mover cartas al Montón, tendremos que volver los naipes que se muevan entre el Mazo y el Montón, y viceversa.

En cuanto a los métodos:

Origen:
Obtiene el valor del identificador de la pila de origen del Naipe.
Destino:
Obtiene el valor del identificador de la pila de destino del Naipe.

Tapete

Esta clase agrupará las distintas zonas de juego: mazo, montón, pilas de trabajo y pilas de salida. También tiene que mover los naipes de una a otra zona, a requerimiento del jugador.

Los datos que debe almacenar esta clase son:

baraja:
Una baraja, que contiene los cuarenta naipes con los que jugaremos.
zona:
Un array de diez pilas, una para cada zona de juego: mazo, montón, pilas de trabajo y pilas de salida.
movimiento:
Una pila con los movimientos realizados, que nos permitirá deshacerlos cuando el jugador lo requiera.
mano:
Contendrá el naipe que tenemos en la mano durante las acciones de arrastre durante el juego.
origen:
Identificador de la pila de origen del naipe arrastrado.

Los dos últimos datos se usarán cuando el jugador mueva un naipe de una pila a otra. En el dato 'mano' guardaremos el naipe que estamos moviendo, y que hemos retirado de la pila de 'origen'.

El movimiento puede terminar de dos formas:

  • Si el movimiento es válido, el naipe almacenado en 'mano' se colocará en la pila de destino.
  • Si no es válido, el naipe volverá a su pila de 'origen'.

En cuanto a los procedimientos:

Limpiar:
Deja el tablero limpio, vacía las pilas de naipes y de movimientos.
Iniciar:
Inicia una nueva partida. Mezcla las cartas, las coloca en el mazo y saca una carta para cada una de las pilas de trabajo y para el montón.
MoverNaipe:
Cambia un naipe de la pila indicada de origen a la de destino. Este método no se preocupa de si el movimiento es o no legal, según las reglas del juego. Esto debe ser así para permitir acciones como deshacer. Se limitará a hacer un Pop de la pila de origen y un Push en la de destino. Además, almacenará el movimiento en la pila de movimientos, para poder implementar la opción de deshacer.
Deshacer:
Realiza el movimiento contrario al último almacenado en la pila de movimientos. Este movimiento no debe almacenarse el la pila de movimientos, por razones obvias.
Pila:
Función para obtener una referencia a la pila identificada mediante un parámetro.
CogeNaipe:
Retira un naipe de la pila indicada y lo almacena en 'mano', y también guarda el 'origen'.
SueltaNaipe:
Coloca el naipe almacenado en 'mano' en la pila indicada. Indicaremos con el valor -1 que la pila de destino no es válida, ya sea porque el jugador ha soltado la carta en una zona sin pila, o porque el movimento es ilegal. Esta función también guarda el movimiento en la pila de movimientos, si es legal.
EnArrastre:
Devuelve un valor verdadero si tenemos un naipe en la 'mano'.
VerificarDobleClick:
Verifica si el naipe de la zona indicada puede ser extraído a una pila de salida. En ese caso devuelve el valor de esa pila, en caso contrario retorna con -1.
Origen:
Devuelve el valor de la pila de origen del naipe arrastrado.
Mano:
Devuelve una referencia al naipe almacenado en 'mano'.
MovimientoLegal:
Devuelve un valor verdadero si el movimiento determinado por el valor del naipe de 'mano', 'origen' y el destino indicado como parámetro es legal.
Terminado:
Devuelve un valor verdadero si el juego ha terminado con éxito.
SinSolucion:
Devuelve un valor verdadero si no quedan movimientos posibles y el juego no ha terminado.
MovimientoPosible:
Función auxiliar para 'SinSolucion', para analizar todos los movimientos posibles para comprobar si son legales.

Esta clase se encargará de interactuar con el jugador, comprobar si los movimientos son legales, según las reglas del juego, verificar si el juego se ha terminado, ya sea porque no hay movimientos posibles o porque se ha resuelto, etc.

También podemos usar esta clase para llevar un contador básico: cuántas cartas quedan en el mazo, cuántas hay en el montón y cuántas en las pilas de salida. Pero en realidad no es necesario llevar esta cuenta, ya que cada pila ya dispone de un contador interno que podemos usar para averiguar esos valores.

Para saber si el juego no tiene solución hay que verificar que no hay movimientos legales posibles, o que los que hay no conducen a la solución. Esto último es más complicado, y equivaldría a intentar resolver el juego de forma automática. Este objetivo se escapa de nuestras intenciones.