Tres en raya con el Arduino 
Partiendo del diseño hardware de los leds y los interruptores multiplexados realizado en anteriores posts de este blog he realizado una implementación “tonta” del juego de tres en raya.

Aspectos funcionales

La idea es realizar un juego de tres en raya utilizando la matriz de 3x3 leds en combinación con la matriz de 3x3 pulsadores descritas ambas en post anteriores. Esta implementación inicial tiene las siguientes características y limitaciones:
- El microcontrolador utiliza un algoritmo tonto para realizar movimientos: elige aleatoriamente sobre qué celda jugar en cada turno.
- Siempre comienza jugando el microcontrolador.
- En lugar de círculos y aspas utilizamos la marca “luz fija” y “luz parpadeante”. El microcontrolador juega siempre con “luz parpadeante”.
- En cuanto el juego termina (ya sea porque gana el microcontrolador, porque gana el jugador o porque quedan en tablas), el juego se para.

El funcionamiento es el siguiente:
- Tras el reset, el microcontrolador mueve y marca una casilla del tablero (el microcontrolador siempre juega con la marca “luz parpadeante”).
- El microcontrolador espera a que el jugador humano mueva activando el pulsador correspondiente a la casilla que quiere marcar.
- Al activar el pulsador, se marca el led correspondiente como “luz fija” e inmediatamente después, el microcontrolador vuelve a mover.
Así sucesivamente hasta que gane uno de los dos jugadores o el juego quede en tablas. Como se puede adivinar, al ser el jugador de la máquina un jugador “tonto” que elige sus movimientos de forma aleatoria, es muy fácil ganar :-). En sucesivas versiones intentaré ir mejorando la “inteligencia” del microcontrolador.

Diagrama de clases


Las principales clases serían las siguientes:
Game: Se encarga de la mecánica abstracta de cualquier juego de dos jugadores, los turnos, quién gana, etc.
GameBoard: Tablero de cualquier juego (abstracto).
TTTGameBoard: Tablero del juego del tres en raya.
TTTLedGameBoard: Tablero del juego del tres en raya especializado en visualizar su estado en la matriz de leds.
GamePlayer: Jugador de cualquier juego (abstracto).
TTTGamePlayer: Jugador del juego del tres en raya.
TTTRandomGamePlayer: Jugador “tonto” del tres en raya. Elige los movimientos aleatoriamente.
TTTHumanGamePlayer: Jugador humano del tres en raya. Elige los movimientos leyéndolos de la matriz de pulsadores.

A continuación, se puede ver la rutina principal. Se utilizan dos estados de juego ("jugando" y "terminado"):

#include "util.H"
#include "Timer.H"
#include "MyLedMatrixManager.H"
#include "MyKeyMatrixManager.H"
#include "TTTRandomGamePlayer.H"
#include "Game.H"
#include "TTTHumanGamePlayer.H"
#include "TTTLedGameBoard.H"

using namespace std;
using namespace avelino;

#define GAME_STATUS_PLAYING  1
#define GAME_STATUS_FINISHED 2

MyLedMatrixManager ledMatrixManager;
MyKeyMatrixManager keyMatrixManager;
TTTRandomGamePlayer p1;
TTTHumanGamePlayer p2;
Game g;
TTTLedGameBoard board;
uint8_t gameStatus;

int main() {
    Timer::init();
    ledMatrixManager.init();
    board.init(ledMatrixManager);
    p1.init(1, board);
    p2.init(2, board);
    keyMatrixManager.init(p2);
    g.init(p1, p2, board);
    gameStatus = GAME_STATUS_PLAYING;
    while (true) {
        ledMatrixManager.run();
        if (gameStatus == GAME_STATUS_PLAYING) {
            if (!g.isFinished()) {
                keyMatrixManager.run();
                g.run();
                board.show();
            }
            else 
                gameStatus = GAME_STATUS_FINISHED;
        }
        else if (gameStatus == GAME_STATUS_FINISHED) {
            //
        }
    }
    return 0;
} 


Consideraciones adicionales a tener en cuenta

La generación de números aleatorios

Para la implementación de la clase TTTRandomGamePlayer (el jugador “tonto”) se necesitan las funciones “srand” y “rand” de la librería estándar de C (u otras similares). En lugar de la clásica solución
srand(time(NULL));

que no puede ser utilizada en este caso ya que el sistema carece de reloj de tiempo real, opté por utilizar como semilla aleatoria la lectura de una de las entradas analógicas del microcontrolador que se encuentra sin cablear (al aire):
ADCManager::init();
srand(ADCManager::get(0));

Siendo ADCManager una clase con dos métodos estáticos: “init” inicializa el subsistema de conversión analógico-digital del microcontrolador y “get” lee la entrada analógica que se le pasa por parámetros.

Evaluación del tablero

La evaluación del tablero del tres en raya para determinar quién ha ganado tras cada movimiento la he implementado basándome en el algoritmo descrito en el documento “A general algorithm for tic-tac-toe board evaluation” del profesor Aaron Gordon (departamento de ciencias de la computación y sistemas de información de la universidad de Fort Lewis, Estados Unidos). Dicho algoritmo organiza la cuadrícula de 3x3 en forma de cuadrado mágico: un cuadrado en el que la suma de las columnas, las filas y las diagonales siempre da el mismo resultado. El algoritmo es muy ingenioso y eficiente y para entenderlo bien vale la pena leerse el artículo (aunque está en inglés, es muy sencillo de leer y se entiende perfectamente). De todas formas, en un futuro post abordaré el estudio de este interesante algoritmo.

En la sección soft puede descargarse el código fuente del proyecto.



[ 2 comentarios ] ( 929 visualizaciones )   |  [ 0 trackbacks ]   |  enlace permanente
  |    |    |    |   ( 3 / 716 )

<< <Anterior | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Siguiente> >>