Archivo: February, 2008

SSAO (Screen Space Ambient Occlusion)

[ Blog: Elvis Enmanuel - gamedusa blog ]
2008:02:18 21:40:00

This weekend I'd been trying to implement ssao in my engine by giving the excelent tutorial of Iñigo Quilez and reading the gamedev forum. At now the effect has some glitches (maybe some projection bug), but for the moment I feel happy with results.

Happy coding :D

Versionando archivos Javascript automáticamente con Subversion

[ Blog: Kr0n - beer2beer ]
2008:02:17 22:57:42

Ya hablamos de servir archivos estáticos de una forma óptima. Este post puede leerse como una continuación del artículo “Automatically Version Your CSS and JavaScript Files” de Particletree, refinando la solución que proponen ahí.

Un breve resumen de ese artículo (léetelo!): A la hora de servir los archivos estáticos como .js o .css, la idea es que el cliente los cachée durante un periodo muy largo de tiempo (por ej. 10 años) y no tenga que andar descargándolos a cada petición de página que se realice. Pero si modificamos alguno de estos archivos, necesitamos una forma de “forzar” que el cliente se vuelva a descargar ese archivo modificado. Para esto introducimos la “versión” del archivo, en el nombre del mismo, ya sea en el propio nombre mediante mod_rewrite (o similar), ya sea en la querystring como parámetro. Al cambiar la versión del archivo, el browser lo descargaría de nuevo que es lo que nos interesa.

Todo esto está muy bien explicado en el comentado artículo.

Versionando

Ahora el tema viene por cómo fijar esa “versión” de cada archivo estático. La idea obviamente es no tener que hacerlo a mano, y por eso en el artículo de Particletree lo hacen mediante filemtime(), para sacar el timestamp de la última modificación del archivo en cuestión.

Aunque es un primer enfoque, a mí personalmente no me gusta mucho esa opción. Implicaría un costoso acceso a disco con cada petición que se solicite, y aunque se podría evitar este acceso innecesario mediante un script que almacene los timestamps en un include… ya puestos… ¿por qué no hacerlo con la versión del archivo dentro del repositorio?

El script: staticVersions.py

Aquí es donde viene mi aportación. Es un script Python que escanea los directorios que le pases como argumentos y devuelve un array PHP con el nombre de los archivos y su correspondiente versión en el repositorio (esto es, la última revisión en la que cambiaron).

Descarga el script aquí.

Ejemplo:
./staticVersions.py -d ./js -d ./css

<?php 

//$productionSite - if set and true, append versions to static files in template ‘top.tpl’
$productionSite = true;

//array with filenames (as used in template ‘top.tpl’) and file SVN last revision
$staticFilesVersions = array(
    ‘/js/register.js’ => ‘1′ ,
    ‘/js/post.js’ => ‘15′ ,
    ‘/js/interface.js’ => ‘25′ ,
    ‘/js/visualizeus.js’ => ‘73′ ,
    ‘/css/general.css’ => ‘1′ ,
    ‘/css/ie.css’ => ‘38′ ,
    ‘/css/user.css’ => ‘1′ ,
    ‘/css/visualizeus.css’ => ‘1′ ,
    ‘/css/default.css’ => ‘52′ ,
    ‘/css/post.css’ => ‘15′ 
);
?>

De forma que cada vez que hagamos un svn update, se llame a staticVersions.py y se actualice el include correspondiente. En mi caso lo hago con un sencillo shell script, porque no tenía muy claro si con el hook post-commit se podría montar algo. Si a alguno le da por investigarlo, soy todo oídos!

Cabe mencionar que las keys del array que defino encajan con mi estructura de archivos tal y como los uso en las plantillas y tal y como lo sirve el webserver. Si vas a usar el script, posiblemente tengas que adecuar esto a tu propia estructura.

Encajando todo esto con tus plantillas

Cómo ya comenté en su día, yo uso plantillas nativas en PHP, nada de Smarty ni similares, así que: ¿cómo encajar todo esto en tus templates?

Como ya habéis visto, me defino una variable global que indica si el sitio está en producción o en desarrollo, y en función de eso reescribo los nombres de cada archivo estático bien con su versión en el repositorio o bien con su ultimo timestamp de modificación. De eso se encarga la siguiente función:

function autoVersion($url) {

    if (isset($GLOBALS['productionSite']) && $GLOBALS['productionSite']) {
        if (array_key_exists($url, $GLOBALS['staticFilesVersions']))
            $version = $GLOBALS['staticFilesVersions'][$url];
        else $version = ‘1′;
    } else {
        $stat = stat($_SERVER['DOCUMENT_ROOT'] . $url);
        $version = $stat['mtime'];
    }

    return preg_replace(’!\.([a-z]+?)$!’, ".v$version.\$1", $url);
}

Y poco más, modificas en tu plantilla top (o similar) para que la parte de carga de archivos estáticos use la función autoVersion y estableces en mod_rewrite las correspondientes reglas para que Apache no se vuelva loco buscando archivos que no existen (ver artículo de Particletree) y listo!

Nota: Comentarios cerrados por spam. Si quieres añadir o corregir algo, usa el formulario de contacto.

Jade.Ai.Graphs ready for use

[ Blog: Vicente - Jad Engine Blog ]
2008:02:17 19:22:24

These last weeks I've been working on a very basic namespace for Jade.Ai: Jade.Ai.Graphs. This namespace contains the classes needed to execute graph-based algorithms. Currently it only supports Breadth-First-Search, Depth-First-Search and A*. In Jade 1.1 we had also Dijkstra algorithm but you can get the same result using A* with an heuristic that always returns 0.

I could have just ported the 1.1 code to 2.0, but I decided to rewrite it. I don't remember exactly how, but some time ago I found the Boost Graph Library (BGL) and I must admit I liked a lot how it was organized. After that I also found that there was a C# port of the BGL called QuickGraph (pretty amazing stuff). So I decided to port also the BGL to C# for Jade 2.0. Rest assured I didn't port the whole BGL to C# (it is pretty big), I only coded the classes I felt necessary for our needs (that's why I didn't simply use QuickGraph, it has too much stuff and there are some inner things I don't like much).

So, let's get a little overview of what you can find in Jade.Ai.Graphs (better read this looking at the Jade.Ai.Graphs code or you won't understand a thing :p):

  • Root/Base folder: the interfaces ISomethingGraph represent several types of graphs. The hierarchy starts in IGraph, that gives nearly no information, and ends in IVertexAndEdgeListGraph, where you have several methods and properties to get the vertices, edges, out edges,... The interfaces IMutableSomethingGraph represent the same information as the ISomethingGraph interfaces, but also give you Add/Remove operations so you can modify the graph. The idea is that you use a Mutable graph to build the graph from your data and then the algorithm uses the non-mutable graph to perform the algorithm (as the algorithm shouldn't modify the graph data at all).
  • Algorithms: here you can find the BFS, DFS and A* implementations. The algorithms are implemented so you can perform the full algorithm in one call (Compute() method) or you can execute step by step (Step() method). The Step() method is pretty useful for debugging and also for time-sliced searches (quite useful in games). Also, every Step the algorithms raise several events when interesting things happen: a vertex is discovered, an edge is examined,... The idea is that external classes will add handlers to those events to gather interesting data about the algorithm execution. I must admit that I'm not very happy with the BaseAlgorithm class, that's something I'll improve in the future for sure.
  • Implementations: if the root folder was full of interfaces, here you'll find implementations of those interfaces. Currently there's only an AdjacencyList class, which performs as a directed mutable graph that can have parallel edges if needed. This class implements the IMutableVertexAndEdgeListGraph so it covers all the interfaces in the base namespace. I will probably add in the future an UndirectedAdjacencyList and an AdjacencyMatrix class, but for now on the AdjacencyList is more than enough.
  • Observers: some useful classes that observe several events that the algorithms raise. PredecessorRecorderObserver tracks the predecessor of every vertex (useful to know the path to get from one vertex to another) and TargetObserver tracks if a given vertex has been reached. They are more an example than anything else, but they should work as inspiration for other custom observers when you are using the library.

And that's it :) But when I started the new AI library for Jade I did the promise of not writing a lot of code without tests and examples, so I have also added several examples in the AITestApplication project. To try them do the following:

  • Run the project.
  • Choose a source and a target vertex (target is not mandatory). For example if you want to try BFS and DFS choose source 0 and no target. If you want to try Dijkstra choose source A and target F.
  • Click the button of the algorithm you want to execute. You'll get a file dialog where you have to choose a graph file. I have included 2 examples in AITestApplication/Graph/Examples. The file format is pretty easy if you want to create your own graphs to test the code.
  • Once you choose the graph file the algorithm will execute and put the result in the big text box. You'll get the list of vertices in the order the algorithm examined them and if you set a target, the path from the source vertex to the target vertex.

Let's look a little at the code to give you an idea of what's happening (warning: code is very ugly right now, I'll clean it later on). The following code snippets are from GraphDemoPanel.cs, method buttonBFS_Click.

   1: // Create the grpah
   2: AdjacencyList<Vertex, Edge> graph = LoadGraph();
   3: if (graph == null)
   4:     return;
   5:  
   6: // Create the algorithm
   7: BreadthFirstSearch<Vertex, Edge> algorithm = new BreadthFirstSearch<Vertex, Edge>(graph);

First we use LoadGraph() to create an AdjacencyList. It has two generic parametters: the vertex type and the edge type. Second we create the BreadthFirstSearch algorithm passing him the graph (and with the same generic parametters as the AdjacencyList).

One of the things we want to track in the example is the order in witch the algorithm examines the vertices, so we write a simple method:

   1: private void ExamineVertex(object sender, VertexEventArgs<Vertex> e)
   2: {
   3:       _examinedVertices.Add(e.Vertex);
   4: }

_examinedVertices is a List<Vertex> where we add the vertices the algorithm discovers.

Now we should connect this method with our algorithm.

   1: _examinedVertices.Clear();
   2: algorithm.ExamineVertex += ExamineVertex;

Just add the method to the algorithm event and we are set :)

Let's add some other observers to the algorithm. First we declare them:

   1: TargetObserver<Vertex> targetObserver = new TargetObserver<Vertex>();
   2: PredecessorRecorderObserver<Vertex, Edge> predecessorRecorder = new PredecessorRecorderObserver<Vertex, Edge>();

We declare a TargetObserver to keep track of the target (if one is set) and a PredecessorRecorderObserver to keep track of the path to reach each vertex. Now time to connect them to the graph:

   1: // Track the predecessors
   2: predecessorRecorder.Attach(algorithm);
   3:  
   4: // If there is a target, set it and attach the observer to the algorithm
   5: if (textBoxTarget.Text.CompareTo(string.Empty) != 0)
   6: {
   7:     foreach (Vertex v in graph.Vertices)
   8:         if (v.Label.CompareTo(textBoxTarget.Text) == 0)
   9:         {
  10:             targetObserver.Target = v;
  11:             targetObserver.Attach(algorithm);
  12:             break;
  13:         }
  14: }

We just use the Attach method to connect the observer methods with the algorithm events. Easy :)

Time to initialize the algorithm:

   1: // Initialize the algorithm
   2: foreach (Vertex v in graph.Vertices)
   3:     if (v.Label.CompareTo(textBoxSource.Text) == 0)
   4:     {
   5:         algorithm.Initialize(v);
   6:         break;
   7:     }

You have to call to Initialize before executing the algorithm or it won't work!

And now let's execute the algorithm itself:

   1: // Iterate through the algorithm
   2: while (algorithm.State == AlgorithmState.Running && targetObserver.TargetFound == false)
   3:     algorithm.Step();

If we weren't tracking the target we could just call algorithm.Compute() and execute the full algorithm in one go, but we want to stop when we find the target so we have to execute it this way.

The rest of the code is just printing the data to the textbox, nothing special there.

And that's all about Jade.Ai.Graphs. I know it lacks an A* example; I'll do it, but I wanted something more spectacular as output instead of a simple textbox, so it will take me some time to put it up. Once the A* example is done I'll write some small tutorials for people who want to learn how to use the graphs and some explanations about the inner workings, but the code will be probably similar to the one I have just explained.

Now my next objective for Jade.Ai is to add Behavior Trees, Hierachical Task Networks and Planners. Most games nowadays use state machines and scripting for their Ai, but there has been a special interest lately in HTN and planners, specially after the game F.E.A.R. I have been following for quite time the excellent AI blog of Alex J. Champandard and I have decided that this kind of techniques would be really useful for Jade.Ai users. It's going to be a hard work as I know very little about the subject but I think it will be a worthwhile effort.

Next week don't expect something spectacular here: I have to make a big XNA demo with Kartones and Jorge so I'm going to be pretty busy :S

Primer testeo del Kukoo = Epic Fail

[ Blog: Josepho - Blog personal de Josepho ]
2008:02:16 16:35:00

¡Fin de exámenes!

[ Blog: tamudo - born to be freak ]
2008:02:13 14:59:00

Do you carry sensitive data on your laptop?

[ Blog: ZaelSiuS - Zitronensaft ]
2008:02:12 18:18:00
Do you carry sensitive or confidential data on your laptop on a daily basis? Have you ever thought what would happen if you got it robbed?

Despite the obvious loss of the laptop, it's the data loss that matters. Because I usually carry source code and other information that is supposed to be confidential on my laptop, I use Mac OS X's Filevault to keep the contents of my home folder encrypted. In Apple's marketing dept. words:
FileVault secures your home directory by encrypting its entire contents using the Advanced Encryption Standard with 128-bit keys. This high-performance algorithm automatically encrypts and decrypts in real time, so you don’t even know it’s happening.
Although Filevault uses 3DES effective 112bit, AES-128, and RSA-1024, it has some limitations. Firstly, it is vulnerable to dictionary attacks. But you probably are using a 'secure' password, don't you? Secondly, some proof-of-concept Filevault attacks have been developed by hackers, although certain special conditions must be met and it would be very rare for a thief to have enough knowledge to use them on your laptop.

If you want to know more about he internals of Filevault and how good it is from a security standpoint, be sure to check the following resources:

Unlocking Filevault: An analysis of Apple's disk encryption system
http://crypto.nsa.org/vilefault/23C3-VileFault.pdf

Secure Your Mac workshop at METALAB
http://metalab.at/wiki/SYMWorkshop

Lanzamos nuestro portal de juegos gratis DevilishFree

[ Blog: zwiTTeR - dfrriz - Ilustración, arte y videojuegos... ]
2008:02:12 10:57:00

Ya está online nuestro portal de juegos DevilishFree

[ Blog: zwiTTeR - DevilishGames ]
2008:02:12 02:00:00
DevilishFree.com es un portal web que reúne los clásicos videojuegos independientes de DevilishGames, renovados y actualizados. La web ofrece la posibilidad de descargar juegos completos para PC, probar demos y jugar online.

Infinitudes (A veces pasan cosas)

[ Blog: Mars Attacks - Éste no es el blog que estáis buscando ]
2008:02:10 02:01:00
Hay más números que tiempo en el Universo (suponiendo un Universo Cerrado). Si dispusiéramos de la máquina más rápida del Universo que viniera generando un número desde el inicio del Universo, al terminar el tiempo del Universo este número sería el mayor número representado posible. A efectos prácticos, bastaría que el infinito de los números fuera simplemente el número anterior mas uno.

Pero los matemáticos no son tan pragmáticos. En cualquier caso, se brinda la cuestión de que, en nuestra realidad, no existen los bucles infinitos (aunque los bucles infinitos existan), puesto que durarían como máximo hasta que el tiempo del Universo terminara.

Es, básicamente, un problema de nomenclatura. Deberían llamarse "bucles potencialmente infinitos en un Universo Abierto". ¿A quién hay que avisar para que revisen el concepto?

La pastilla. Ñam.

Sobre Dios, la Omnisciencia, el Libre Albedrío, y Satán (Esta mañana me he levantado…)

[ Blog: Mars Attacks - Éste no es el blog que estáis buscando ]
2008:02:10 01:27:00
Me entero por 20minutos (dónde, si no) que Susan ha desprecintado el infierno y a Satán. No sabemos si habrá dicho para sus adentros "Queda reinaugurado este local", pero el tema da que pensar.

No voy a entrar en profundas cuestiones teológicas. Sólo un par de proposiciones lógicas que demuestran que algo no encaja en la historia que se nos cuenta. Desde un punto de vista mitológico, pongamos que creemos que existe un dios.

Parto de los siguientes axiomas sobre ese dios:
-Dios es El que Es (Dios es la Existencia en sí misma, la esencia del Ser).
-Dios es Omnisciente.
-Dios creó a la Humanidad con el don del Libre Albedrío.

Hasta aquí espero que estemos de acuerdo todos

Y añado un par de hipótesis mutuamente excluyentes:
-Dios creó a Satán.
-Dios no creó a Satán.

Comenzamos con un problema en los propios axiomas, pues la Omnisciencia es antagónica al Libre Albedrío. El Libre Albedrío implica que no hay un futuro escrito, que tenemos la posibilidad de elegir nuestros caminos. Pero si Dios es Omnisciente, Él ya conoce a priori esos caminos y, desde el mismo momento de Su propia existencia, Él ya conoce todo lo que va a hacer, incluida la creación de los humanos, dotados, pues, de un imposible Libre Albedrío, puesto que también conoce qué van a hacer esos humanos. De hecho, ni siquiera le serviríamos como distracción, pues conoce perfectamente cómo vamos a "evolucionar en el Universo". Como un Tamagochi que has programado tú mismo con una sola función. Predecible y aburrido.

La cosa se termina de liar con el tema de Satán, por motivos obvios: si Dios creó a Satán, es un poco hijoputa crear una figura que sabe que se va a convertir en pura maldad y va a martirizar a su creación. Vendría a ser como estar embarazada de Hitler a sabiendas de lo que va a hacer el susodicho en el futuro.

Pero aún es más interesante si Dios no tuvo nada que ver con la creación de Satán, puesto que entonces Satán también es El que Es, ha sido capaz de convertirse en la propia esencia del Ser, y por lo tanto a priori debería tener los mismos superpoderes que Dios. Con lo que hay dos Dioses en realidad, uno bueno y otro malo, ambos Omniscientes. ¿Realmente los judíos/cristianos/etc. estarán adorando al que toca? ¿Será consciente Susan de todo esto, o preferirá mirar para otro lado?