sábado, 3 de febrero de 2018

La chica de febrero

 Lleva mucha ropa porque hace frío todavía, aquí por lo menos en el hemisferio norte. Habrá que esperar a la primavera señores.




Algoritmo de búsqueda

 Buenas. Pues nada el otro día pensando que cosas meter en este blog que me puedan ser útiles para mi o para otras personas, ya que no tengo discípulos, me acordé de un algoritmo que recientemente implementé pero que fue desarrollado hace long time ago (más de una década), a poco de comenzar en DarKBasic (un engine para crear juegos en Basic). Dicho algoritmo es simplemente comprender la idea, apenas mostraré código, quizás si porque ya lo tengo implementado pero sólo basicamente y con lo de basicamente me refiero a no una forma compleja a la que puede llegar. El algoritmo en si no solamente sirve para hacer búsquedas, mientras lo desarrollaba con boli y papel (así es como se crean los algoritmos) me di cuenta que puede usarse para rellenar un polígono (dentro de una matriz dimensional) entre otros posibles propósitos. 

 Cuando hablo de búsquedas me refiero a completar una ruta desde un punto A a otro punto B. No me refiero a las consultas desde luego, eso no tiene mérito ninguno aunque se de alguien muy entendido en base de datos que me diría que las consultas de datos son algo pero que muy complejo, cosa que no estoy de acuerdo pues desde mi punto de vista lo complejo es para quienes no comprenden como funciona algo. Comprender tampoco es que necesariamente tengas que aprenderte de puntillas (de la A a la Z) todo el proceso, todos los pasos, los nombres, fechas o hitos históricos, algo que normalmente es como funciona la educación o los métodos educativos ¿Por qué esta énfasis en lo de comprender? Porque a pesar de que usted, lector de esta publicación, pierda el código fuente o nunca pueda recuperar esta página para consultar el algoritmo, típico en los pica-códigos (tranquilo yo también empecé así), siempre que comprenda como funciona podrá desarrollar el algoritmo, tal como me ha pasado.


La teoría del buscarutas

 Cuando desarrollé el algoritmo lo llamé buscarutas porque principalmente ese era su propósito. Su deber era trazar una ruta desde un punto A hasta otro punto B, en su recorrido podría encontrarse con lugares no ya restringidos sino también casi cerrados (o cerrados, no hay ruta posible). Así es como me vino a la cabeza, aunque fue algo visual. Me vino como conseguiría llegar a otro punto que estuviera fuera de un vaso o con forma de vaso. Pensé en el agua, el agua cuando llena el vaso se desborda. Si el problema solamente fuera ese, llenar el vaso... Y así fue como empecé, el algoritmo primigenio inundaba la matriz con unos datos que se comportaría como el agua hasta que esta tocara el otro punto.

 Para inundar la matriz simplemente se comienza desde un punto y se comprueba si hay salida a las celdas próximas, de estar estas vacías o no encontrarse un obstáculo se llena la celda con un dato. En principio se usaba un dato que significaba que estaba ocupado llena esa celda pero luego se optó por un método numérico.

 Este proceso, el de inundar se hace recursivamente. Esto significa que cada celda comprobará a su vez que sus celdas próximas estén vacías y de no estarlo las rellenará indiferentemente de si esas celdas se alejan del destino o el origen, ya que es necesario inundar la superficie para tener claro los límites, salidas o caminos posibles. Dese cuenta querido lector que el algoritmo está diseñado para que afronte con éxito una ruta que lo saque del laberinto más enrevesado, y esto es literal, no hablo metafóricamente.

 Si en el desarrollo optamos por una depuración visual de la matriz (dimensional) podríamos ver un resultado así:


 Llegado a este punto, tenemos algo que de alguna forma llega hasta el otro punto, valga la redundancia. Pero lo que nos interesa es que aparte de encontrar el camino lo genere porque así a priori lo que tenemos es un sistema que nos puede indicar que existe un camino o que también podríamos usar para rellenar polígonos, por ejemplo. Para resolver el siguiente punto me tomó varios días, pensando y garabateando sobre el papel hasta llegar a la conclusión de que era necesario un índice, algo que indicara además que después de un número iba otro número, pero ¿Bajo que regla o reglas? ¿La distancia? La verdad es que el sistema apareció de una forma que no recuerdo bien, pero es cierto que empezó como un índice. El código usado consistía en lo siguiente, se trataba de valores numéricos que ascendían, se incrementaban según se inundaba la superficie, los valores crecían y crecían y por alguna razón, no me acuerdo sinceramente porque llegué a esta conclusión o si lo sabía os juro que no recuerdo en este instante (más de diez años de eso), decidí tomar el valor más grande para trazar la ruta o camino y luego buscar el valor más pequeño que me señalaría al origen (siempre de mayor a menor).


 Y así amigos fue como conseguí algo tan crucial como crear un mapa 3D y que una entidad 3D llegara al objetivo aunque este cambiara su posición en el mapa, sin atorarse o quedándose caminando infinitamente hacia la pared como un gilipollas. El algoritmo originalmente trabajaba con desplazamiento diagonal pero para un trabajo reciente se me pedía que el desplazamiento solo fuera en cruz, como el movimiento de la torre en ajedrez y la verdad no me supuso ningún problema adaptarlo. Por eso si domináis bien esta técnica no es que paséis a cinturón negro de programación pero podréis resolver montón de problemas, incluso quien sabe, redes neuronales (ahora tan de moda).

Aplicación práctica (ejemplo)

Aquí una porción de una aplicación desarrollada en VanillaScript. La porción recoge como primero se encuentra el camino, la inundación, y la segunda parte más pequeña la que genera el camino y lo guarda en un array. En ambos caso se usan bucles indeterminados, aunque no falta decir que para evitar el cuelgue o la aplicación no responde etc... usar un contador para impedir que se quede en un bucle infinito. Cuando este contador llegue a cero se podría interpretar que no hay caminos posibles.

        var ruta=[];
        // creamos caminos
        movimientos=0;
        ruta[0]=[hastaX, hastaY,0];
        this.mapa=replaceAt(this.mapa, hastaY*this.anchoMapa+hastaX,movimientos); 
        this.mapa=replaceAt(this.mapa, desdeY*this.anchoMapa+desdeX," "); 
        var bucle=100;
        while(encontrado!=true && bucle>0){
            bucle--;
            for (var i in ruta){
                var punto=ruta[i];
              
                if (punto[2]==0){
                    ruta[i]=[punto[0],punto[1],1];
                    
                    if (this.mapa[punto[1]*this.anchoMapa+(punto[0]-1)]==" ") { 
                        movimientos++;
                        ruta[movimientos]=[punto[0]-1, punto[1],0];
                        this.mapa=replaceAt(this.mapa, punto[1]*this.anchoMapa+(punto[0]-1),"*");                   
                        if (ruta[movimientos][0]==desdeX && ruta[movimientos][1]==desdeY){
                            encontrado=true;
                            break;
                        }
                    }
                    if (this.mapa[(punto[1]-1)*this.anchoMapa+punto[0]]==" ") { 
                        movimientos++;
                        ruta[movimientos]=[punto[0], punto[1]-1,0];
                        this.mapa=replaceAt(this.mapa, (punto[1]-1)*this.anchoMapa+punto[0],"*");                  
                        if (ruta[movimientos][0]==desdeX && ruta[movimientos][1]==desdeY){
                            encontrado=true;
                            break;
                        }
                    }
                    if (this.mapa[punto[1]*this.anchoMapa+(punto[0]+1)]==" ") { 
                        movimientos++;
                        ruta[movimientos]=[punto[0]+1, punto[1],0];
                        this.mapa=replaceAt(this.mapa, punto[1]*this.anchoMapa+(punto[0]+1),"*");
                        if (ruta[movimientos][0]==desdeX && ruta[movimientos][1]==desdeY){
                            encontrado=true;
                            break;
                        }
                    }
                    if (this.mapa[(punto[1]+1)*this.anchoMapa+punto[0]]==" ") { 
                        movimientos++;
                        ruta[movimientos]=[punto[0], punto[1]+1,0];
                        this.mapa=replaceAt(this.mapa, (punto[1]+1)*this.anchoMapa+punto[0],"*");
                        if (ruta[movimientos][0]==desdeX && ruta[movimientos][1]==desdeY){
                            encontrado=true;
                            break;
                        }
                    } 
                }               
            }
        }
        
        var ruta_final=[];
        if (bucle!=0) { // generamos ruta o camino
            for (var i=movimientos;anterior!=i;){
                var punto=ruta[i], anterior=i;
                for (var ii=i-1;ii>0;ii--){
                    var siguiente=ruta[ii];
                    if ((Math.abs(punto[0]-siguiente[0])==1 && Math.abs(punto[1]-siguiente[1])==0) ||
                        (Math.abs(punto[0]-siguiente[0])==0 && Math.abs(punto[1]-siguiente[1])==1)){
                        i=ii;
                        ruta_final.push(ruta[i]);
                        break;
                    } 
                }
            
            }
            ruta_final.push(ruta[0]);
        }


martes, 16 de enero de 2018

Las consolas se mueren

 Pues si. Lamento ser tan dramático con el titular pero hace meses que le doy vueltas a este asunto tan preocupante porque esas cajas de diversión que comenzaron hace tantísimos años tienen el tiempo contado.

¿Pero cómo cojones hemos llegado a ésto?

 Son varios factores en mi opinión y aquí los enumero, aunque es posible que falten algunos porque ya digo esta es mi opinión que nace de la experiencia, muchos años de vicio:
  • Desarrollos cada vez más caros. Si, el caso es que GNU está presente en el PC pero en las multiples plataformas de videojuegos no. Eso y que cada vez las consolas son más complejas hacen que invertir tiempo para crear tú propia librería por medio de la ingeniería inversa sea casi una pérdida de tiempo y que cueste más que comprar una licencia para publicar juegos para determinada consola usando cualquiera de los montones de motores disponibles, tipo Unity o Unreal por mencionar los más populares.
  • Público más exigente, que se puede traducir en que cada vez un listón más alto, porque no es lo mismo sacar el tetris para PS4 que aparte haría desmerecer la potencia del cacharro. Lo que está claro es que queremos juegos cada vez con más filigranas y a la vez muy adictivo, y eso se traduce en un equipo de desarrollo relativamente grande y de ahí se podría pasar al primer punto, un mayor gasto. Tiempo atrás quedó cuando un programador podía hacer la música, los gráficos y el juego mismo!!
  • La estafa de los DLC, ampliaciones, expansión, extensiones o como coño quieran llamarlo. Si, ahora lo que da dinero no es crear un juego sino crear el juego en cachitos, y por supuesto, vendiendo cada cachito de tal forma que la suma de todos los cachos valgan el doble de lo que originalmente se vendería el juego completo solo.
  • Un mercado cada vez más errático debido a los factores citados, que dicho de forma llana, sino hay juegos o pocos en una plataforma los jugadores tarde o temprano la abandonan.
  • Actualizaciones infinitas y juego online por cojones, y de esto experimentando hace unos días y que fue la gota que culminó el vaso que me ha llevado a escribir esta entrada. Os lo cuento para queden flipando. Adquirí hace tiempo un juego de Ubi soft, el For honor, tuve un problema con la cuenta de Ubi soft (el primer día) y nunca más volví a poder jugarlo pues me quedé a la espera de que ese soporte tan gentil (malditos hijoputas prepotentes) me solucionaran el problema. Es un juego por cojones ya no es suficiente que estés conectado con tú cuenta de PS4 sino va más lejos aun y solo funciona conectado y con cuenta de usuario Ubi Soft, a joderse se ha dicho porque yo lo valgo, más o menos así habrán dicho el máximo representante de Ubi Soft con esta absurda política. EEeeeh y que no he mencionado que aunque juegues online hay que hacer por cojones la instalación de tropecientos gigas en la memoria de la consola ¿Qué no tienes espacio? Pues no juegas o eliminas lo que tengas, o en el mejor de los casos para las tiendas te compras otro y no pasa nada (total unos pocos euros €€€). Pues esto dije, nunca más compro un juego de Ubi Soft, pues el For Honor a la semana lo descambié, y terminé comprando otra perla, el Destinity 2 de Bungie Studios. Emocionado llegué a casa y metí el juego, entonces la PS4 me indica que tiene que actualizarse, 3 gigas, bueno no pasa nada me dije. Una vez acabó de actualizar el juego pidió actualizarse porque sino no se ejecutaba, bueno no pasa nada me dije. YA cuando acabo, al cabo de unas horas, me dije por fin... y el juego nuevamente me indicó que tenía que volver a actualizarse, y todo esto para luego jugar a un juego con problemas en el servidor, pues hay que jugarlo conectado por cojones y con algunos bugs... en fin, alado sea el equipo Steam y el PC, saben a lo que me refiero?  

Alternativas al hambre de los jugadores


 La alternativa pasa bien o pasarte a la plataforma rival o competencia, la cual no es nada barata, pues las consolas han llegado a una política de precios casi absurda. O volver al PC, esa máquina que en lo que se refería a jugabilidad siempre había sido pobre pero que ahora con el abaratamientos de componentes y un mercado glorioso de portabilidades, únicos y de bajo presupuesto (indies), hacen que la máquina con más juegos y más entretenida sea el Personal Computer, elegido por la mayoría de jugadores o gamers. Porque ¿dónde verás emuladores de PS, Nintendo, Super Nintendo, Sega Megadrive y sin fin de máquinas de entretenimiento? pues en el PC sino.

Cambiar colores de los mensajes en consola, Laravel 5.1

 Bueno, al final averigüe como cambiarlo sin hacer tanto trasteo con js, que aparte de conseguirlo hice petar todo el Cloud9 en el intento y menos mal que usando ?reset=user pude dejarlo todo por default.
Lo primero, es que no es una solución que haya encontrado por ahí ni nada de eso, todo es investigación propia.
Lo segundo, que tanto para cambiar el esquema de colores del artisan como del tinker hay que toquetear en distintos sitios. Porque el artisan en verdad lo que hace cuando invocas al tinker es ejecutar una aplicación php denominada PsySh. Ambas, PsySh y Artisan usan algunos componentes de Symphony, por ejemplo el Console (Seguro que con el nombre os da una pista).

Cambiar esquemas de color del artisan

 Simplemente hay que irse al fichero en la raíz de nuestro proyecto Web, por ejemplo Laravel. Y editar el fichero artisan (un fichero de texto plano sin extensión), y colocar en la cabecera :

use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\ConsoleOutput;
...
 Luego en el mismo fichero localizamos la línea :
$status=$kernel->handle(
    $input=new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);
 Y antes de esta línea definimos nuestra propia salida personalizada que denominaremos como no $output , de esta forma :
$outputFormatter=new OutputFormatter(false,[
    // En este array vamos agregando los estilos para cada caso (solo me se algunos)
    'error'=>new OutputFormatterStyle('yellow','blue'),// fondo azul y letras amarillas, por ejemplo
]);

$output=new ConsoleOutput(ConsoleOutput::VERBOSITY_NORMAL,null,$outputFormatter);
 Una vez tenemos nuestra salida personalizada lista simplemente reemplazamos el new Symfony\Component\Console\Output\ConsoleOutput de la función handle que mostré anteriormente por nuestro objeto ConsoleOutput denominado $output. Y listo.

Cambiar esquemas de color del tinker (PsySh)

Para éste es un poco más sencillo, simplemente nos vamos a la carpeta vendor, donde el composer aloja todas las librerías php, y buscamos la carpeta psy. Y dentro de ella buscamos el fichero /psysh/src/psy/output/ShellOutPut.php y lo editamos. Dentro del fichero nos vamos abajo del todo en la función initFormatters() y veremos como define los estilos. Simplemente lo que haremos es reasignar los colores con la combinación que nos guste y en caso por ejemplo de cambiar el estilo de los mensajes de error, al no existir dentro de las definiciones la crearemos de la misma forma que el resto de estilos definidos pero con el nombre de error:
...
...
     * Initialize output formatter styles.
     */
    private function initFormatters()
    {
        $formatter=$this->getFormatter();

        // usamos la palabra 'error'
        $formatter->setStyle('error',new OutputFormatterStyle('black','yellow'));// ejemplo (un copy/paste del de abajo)
        
        $formatter->setStyle('warning',new OutputFormatterStyle('black','yellow'));
...
...
 Y listo calisto.

La sexy de enero


pequeña pero matona


Laravel 5.1 usando policies

 Buenas, en esta publicación indicaré como implementar sencillamente las policies en Laravel 5.1


¿Qué son las policies en Laravel?


 Las policies como indica su nombre son las políticas relacionadas con los permisos y/o autorizaciones. Van encaminadas a ofrecer una alternativa al midleware de Laravel, pues se puede hacer casi lo mismo con estas, pero con la diferencia de la actuación, pues mientras que con el midleware uno puede tratar la excepción dentro de la clase casi de forma opaca para el que la usa, en el Policy se maneja en el lugar, o función, donde queremos que actúe. Realmente al final se resume en devolver verdadero o falso según las condiciones de la operación.

 Bueno, conformes o no con mi definición de las policies siempre podrán consultarla directamente desde la documentación oficial de Laravel e interpretarla al gusto o literal.

https://laravel.com/docs/5.1/authorization#policies

Implementación y explicación



 Como habrán visto en la documentación el procedimiento es simple. Se crea una clase por medio de artisan, luego se añade (registro) a la clase AuthServiceProvider y luego la usamos en el lugar o función que queremos que actúe.



 Uno de los principales problemas que se nos puede presentar es la ausencia de esta clase, AuthServiceProvider, al recién instalar Laravel 5.1. Para ello podemos solucionar esto de forma sencilla. Nos vamos a la carpeta donde Composer aloja las librerías Php, la vendor de nuestro directorio de trabajo. Luego desde la ruta laravel/framework/src/Illuminate/Auth/ copiamos la clase citada a la ruta app/providers, cambiamos su espacio de trabajo por el de App\Providers y por último registramos esta clase en la configuración del laravel, config\app.php , en la clave 'providers' añadimos al array App\Providers\AuthServiceProvider::class . Podemos luego limpiar la clase y solamente dejarla así de momento para este propósito :



namespace App\Providers;
        
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * Register any application authentication / authorization services.
     *
     * @param  \Illuminate\Contracts\Auth\Access\Gate  $gate
     * @return void
     */
    public function boot(GateContract $gate)
    {
        $this->registerPolicies($gate);
    }
}

 Una vez hecho esto ya simplemente es ejecutar el comando artisan, php artisan make:policy y el nombre de la política que queremos aplicar. 


 Como esto era para clase he usado un proyecto que ya tenía hecho que trata de un portal de anuncios. En el proyecto originalmente el problema de impedir que editaran anuncios de los que los usuarios no fueran propietarios lo resolvía con un midleware. Pero como el profe nos pidió implementarlo para experimentar con este pues aplicaré el Policy para resolver el problema antes indicado.



 Creo entonces el Policy denominado PoliticaDePublicacion, el cual una vez ejecutado el comando artisan me aloja la clase en la ruta app\policies.



 Dentro de esta clase ya creada, como la idea es que en el momento después de la edición y al actualizar el anuncio se evalúe si el usuario puede o no modificar, pues creo un método denominado actualizar y le indico los dos parámetros que participan en esta evaluación o comprobación que no son otros que el usuario registrado y el anuncio a editar. Y al final queda una cosa así :



namespace App\Policies;

use Illuminate\Auth\Access\HandlesAuthorization;

use App\User;
use App\Anuncio;

class PoliticaDePublicacion
{
    
    public function actualiza(User $usuario,Anuncio $anuncio){
        return $usuario->id == $anuncio->id_usuario;
    }
}


 En el método o función se observa que simplemente se compara el id del usuario con el id del anunciante. Que en caso de corresponder devolverá verdadero y en caso contrario falso.



 Hecho esto ahora agregamos nuestra Policy al AuthServiceProvider. Para ello usaremos la propiedad protegida policies que toma una array asociativo. Donde en la parte de la clave usamos la ruta y el nombre de "una" clase entre comillas (o comilla simple), vinculante a nuestra Policy que será el valor que también se asignará por medio de la ruta completa con el nombre de la clase. Yo como clave escogí el controlador pero puedes usar también un modelo como se aprecia en la documentación pues eso depende de su uso.




namespace App\Providers;

use App\Anuncio;
use App\Policies\PoliticaDePublicacion;
        
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        'App\Http\Controllers\AnuncioController' => 'App\Policies\PoliticaDePublicacion',
    ];

    /**
     * Register any application authentication / authorization services.
     *
     * @param  \Illuminate\Contracts\Auth\Access\Gate  $gate
     * @return void
     */
    public function boot(GateContract $gate)
    {
        $this->registerPolicies($gate);
    }
}

 Ya hecho esto simplemente queda usarla dentro de la función donde vaya actualizar por medio del helper o función policy(), a la cual indicamos nuestra clase, en este caso el controlador, y ésta nos devolverá nuestra Policy vinculada pudiendo así usar los métodos definidos, en este caso actualiza().



namespace App\Http\Controllers;

...
use App\Http\Requests\ValidaAnuncio;

class AnuncioController extends Controller
{
...
    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(ValidaAnuncio $request, $id)
    {
        $anuncio = Anuncio::find($id);
        if (policy('App\Http\Controllers\AnuncioController')->actualiza($request->user(),$anuncio)){ 
            $anuncio->update(["titulo"=>$request->titulo, "cuerpo"=>$request->mensaje, "categoria"=>$request->categoria]);
            return "";
        }
        
        return redirect('/'); // en caso de no poderse modficar saltamos al inicio de la pa�gina   
    }
...
 
 También como alternativa podremos usar el $this en el argumento de la función policy() para indicar la clase en vez de escribir el nombre completo de esta(tal como muestro arriba en el código), pues estamos además dentro del controlador para cual se definió la política.


 Y ya por último, también indicar que podríamos usar otra forma que indica la documentación y que engaña mucho con el ejemplo que nos muestra, aunque en una notita lo deja bien claro. Se trata del método authorize(). Dicho método funciona de la siguiente forma; cuando devuelve true el flujo del programa continúa con normalidad y en caso contrario genera una excepción que responde con un código 403 (No autorizado), que en Laravel podremos definir con una vista personalizada. Para usar el authorize() hay que incluir la clase AuthorizesRequests dentro de nuestro controlador o clase pues sino el servidor nos lanza el mensaje de método no encontrado.


By default, the base App\Http\Controllers\Controller class included with Laravel uses the AuthorizesRequests trait. This trait provides the authorize method, which may be used to quickly authorize a given action and throw a HttpException if the action is not authorized.

 Además también habrá que definir los métodos a usar en el método boot() de la clase AuthServiceProvider. Usando a su vez el método define() del objeto GateContract de la citada función. Todo para que el authorize() pueda encontrar los métodos pues de lo contrario siempre generará la excepción 403.


Conclusión


En mi humilde opinión esto puede ser prescindible en el desarrollo de una aplicación con un framework en el que abundan todo tipo de funcionalidades.