<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ya he aprendido otra cosa :D &#187; Python</title>
	<atom:link href="http://www.aureoares.es/category/programacion/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.aureoares.es</link>
	<description>por Áureo Ares</description>
	<lastBuildDate>Thu, 17 Jul 2014 15:48:10 +0000</lastBuildDate>
	<language>es-ES</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
		<item>
		<title>Filtros Bloom escalables.</title>
		<link>http://www.aureoares.es/filtros-bloom-escalables/</link>
		<comments>http://www.aureoares.es/filtros-bloom-escalables/#comments</comments>
		<pubDate>Mon, 07 Apr 2014 18:51:32 +0000</pubDate>
		<dc:creator><![CDATA[Áureo Ares]]></dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web scraping]]></category>

		<guid isPermaLink="false">http://www.aureoares.es/?p=90</guid>
		<description><![CDATA[Cómo utilizar filtros Bloom escalables con Python. Funcionamiento y ejemplo de código.]]></description>
				<content:encoded><![CDATA[<p>Hace dos semanas publiqué un <a title="Filtros Bloom en web scraping." href="http://www.aureoares.es/filtros-bloom-en-web-scraping/">ejemplo de filtro Bloom básico</a>. En mis aventuras con web scraping el que realmente uso es una versión escalable ya que suele ser muy difícil estimar el número máximo de enlaces que voy a necesitar almacenar.</p>
<p>No existe (que yo sepa) un modo de aumentar el tamaño del array de bits de un filtro Bloom una vez que ya se han empezado a añadir elementos. El modo de implementar un filtro escalable no es ni más ni menos que utilizar más de un filtro a la vez.<span id="more-90"></span></p>
<p>Para ello, cada vez que un filtro se llena se crea uno nuevo de mayor capacidad. Los nuevos elementos se van añadiendo al último filtro creado y a la hora de buscar un elemento se busca en todos uno por uno.</p>
<p>La base del filtro escalable es por tanto muy sencilla. La mayor complicación sería escoger el modo de escalar la capacidad de los filtros, es decir, cómo de grande debe ser el nuevo en comparación con el anterior. En su momento me vino a la cabeza la posibilidad de que todos tengan el mismo tamaño e incluso de que fuesen cada vez más pequeños, pero hasta la fecha no se me ha ocurrido ningún extraño caso en el que pueda resultar útil.</p>
<p>Lo normal es utilizar una función lineal o exponencial. Yo he optado por permitir ambas ya que no era ningún esfuerzo pero en la práctica siempre me ha venido mejor la exponencial. Los cálculos son muy sencillos:</p><pre class="crayon-plain-tag"># initial_capacity = capacidad del primer filtro
# scale_factor = factor de aumento
# filters = array de filtros, len(filters) es el n&uacute;mero de filtros que ya haya
# Funci&oacute;n lineal:
capacity = int(initial_capacity * (scale_factor * len(filters)))
# Funci&oacute;n exponencial:
capacity = int(initial_capacity * (scale_factor ** len(filters)))</pre><p></p>
<p>&nbsp;</p>
<p>Añadir y buscar elementos también es muy sencillo:</p><pre class="crayon-plain-tag">def add(element):
    if filters[-1].is_full():
        # En el ejemplo del art&iacute;culo anterior, el filtro se creaba pasando como par&aacute;metros la capacidad y el margen de error.
        filters.append(BloomFilter(calc_next_capacity(), error_rate))
    filters[-1].add(element)
#
def lookup(string):
    for f in reversed(filters):
        if f.lookup(string): return True
    return False</pre><p>Lo único destacable es que al buscar es más eficiente recorrer los filtros en orden inverso (del último al primero), ya que de media se tardará menos en encontrar el elemento. Aunque en caso de no encontrarse la búsqueda tardará lo mismo se haga en un sentido u otro.</p>
<p>El código completo comentado se puede ver en <a title="Código completo en Google Code" href="https://code.google.com/p/python-scalable-bloom-filter/source/browse/" target="_blank">Google Code</a>.</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aureoares.es/filtros-bloom-escalables/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Filtros Bloom en web scraping.</title>
		<link>http://www.aureoares.es/filtros-bloom-en-web-scraping/</link>
		<comments>http://www.aureoares.es/filtros-bloom-en-web-scraping/#comments</comments>
		<pubDate>Mon, 24 Mar 2014 12:37:32 +0000</pubDate>
		<dc:creator><![CDATA[Áureo Ares]]></dc:creator>
				<category><![CDATA[Programación]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Web scraping]]></category>

		<guid isPermaLink="false">http://www.aureoares.es/?p=24</guid>
		<description><![CDATA[Cómo utilizar filtros Bloom el Python, orientado al web scraping. Funcionamiento y ejemplo de código.]]></description>
				<content:encoded><![CDATA[<p>En mis aventuras con <a title="Definición de web scraping en Wikipedia" href="http://es.wikipedia.org/wiki/Web_scraping" target="_blank">web scraping</a> me encontré con un problema muy común al detectar automáticamente las URLs:</p>
<p>Cuando sabes lo que buscas es muy fácil fabricar un listado de URLs que necesitas revisar. Puede incluso ser una sola URL. Sin embargo, cuando necesitas ir detectando sobre la marcha nuevas URLs tienes que guardar un registro de las que ya has visitado. De lo contrario te encontrarás visitando las mismas páginas enlazadas entre sí una y otra vez hasta el fin de los tiempos.<span id="more-24"></span></p>
<p>Si sabemos que serán pocas las URLs a visitar, esto no es un gran problema. Simplemente nos guardamos una lista de las que vamos visitando y nos aseguramos de no repetir. El problema está cuando nos enfrentamos a sitios web muy grandes de los que necesitamos revisar una gran cantidad de páginas. Entonces eso de tener la lista de URLs en memoria se convierte rápidamente en una mala idea.</p>
<p>En estos casos, si las URLs en sí son relevantes (es decir, si cada URL es un dato que necesitas guardar por alguna otra razón) seguramente necesites una base de datos. Pero si lo único que necesitas saber es si ya has visitado antes una URL, los filtros Bloom son una opción increíblemente eficiente y rápida.</p>
<h2>Bonita historia, pero ¿qué es un filtro Bloom?</h2>
<p>No lo voy a explicar en profundidad ya que aparece muy bien explicado en la <a title="Definición de filtro Bloom en Wikipedia (en inglés)" href="http://en.wikipedia.org/wiki/Bloom_filter" target="_blank">Wikipedia (inglés)</a>, incluyendo muchas referencias interesantes al final del artículo. De modo que comentaré lo más importante.</p>
<p>Las características principales de los filtros Bloom son las siguientes:</p>
<ul>
<li>Tiene un cierto <strong>margen de error</strong> (falsos positivos), calculable y ajustable.</li>
<li>Al buscar un elemento, un resultado negativo significa que con toda seguridad <strong>no está</strong> en la lista.</li>
<li>Un resultado positivo significa que un elemento <strong>probablemente está</strong> en la lista (debido a la posibilidad de falsos positivos).</li>
<li><span style="line-height: 1.5em;">Requiere muy <strong>poco espacio</strong> en memoria y las consultas son muy rápidas.</span></li>
</ul>
<p>El funcionamiento es muy sencillo, para implementarlo necesitaremos:</p>
<ul>
<li>Encontrar una implementación ya realizada que nos guste (nos parezca apropiada) y podamos utilizar (licencia), en cuyo caso ya habremos terminado :D.</li>
</ul>
<p>o</p>
<ul>
<li>Una lista de elementos binarios (bit array).</li>
<li>Una o más funciones de hashing.</li>
</ul>
<p>En mi caso necesitaba una implementación en Python ya que es el lenguaje que suelo utilizar para las tareas de scraping. La implementación de <a title="Pybloom en GitHub" href="https://pypi.python.org/pypi/pybloom/1.0.2" target="_blank">pybloom</a> la verdad es que no me gustó nada y no encontré ninguna otra que me sirviese. De modo que opté por hacerme la mía.</p>
<p>La elección de la <strong>función de hashing</strong> es muy importante. El requisito indispensable es que sea <strong>uniforme</strong> (todos los posibles valores tienen la misma probabilidad de aparecer como resultado) y <strong>determinista</strong> (un mismo valor de entrada genera siempre el mismo valor de resultado). También es importante, aunque no imprescindible, que sea <strong>no criptográfica</strong>. Las funciones de hashing criptográficas están muy bien para otros usos, pero son más lentas (este es uno de los principales inconvenientes que le veo a pybloom). No soy ningún experto en hashing, pero leyendo un poco la que más me convence por el momento es <a title="MurmurHash3" href="https://pypi.python.org/pypi/mmh3" target="_blank">MurmurHash3</a>.</p>
<p>Resumiendo, para la implementación en Python utilicé lo siguiente:</p>
<p></p><pre class="crayon-plain-tag"># Requiere instalar previamente bitarray y mmh3 (murmurhash3)
# sudo pip install bitarray
# sudo pip install mmh3

from bitarray import bitarray
import mmh3
from math import log, ceil</pre><p></p>
<h2>Añadir un elemento al filtro:</h2>
<p>Para añadir un elemento realizaremos varios hashes del mismo. Se pueden utilizar distintas funciones de hashing o una sola función con diferentes semillas. Teniendo ya una función que me gusta y sin saber cuántos hashes diferentes voy a necesitar, me parece más lógico utilizar una sola con diferentes semillas:</p>
<p></p><pre class="crayon-plain-tag"># hash_count = n&uacute;mero de hashes a realizar.
# size = tama&ntilde;o del bitarray.
for seed in xrange(hash_count):
	position = mmh3.hash(element, seed) % size
	bit_array[position] = 1
element_count += 1</pre><p></p>
<p>Los hashes mmh3 son numéricos, de modo que se pueden ajustar al tamaño del bitarray calculando el &#8220;módulo&#8221; (resto de división) con el operador &#8220;%&#8221;. El resultado es la posición del bitarray que marcamos a 1.</p>
<h2>Buscar un elemento en el filtro:</h2>
<p>Buscar un elemento en el filtro es muy parecido, simplemente comprobamos las posiciones que corresponderían al elemento que estamos buscando.</p>
<p></p><pre class="crayon-plain-tag"># hash_count = n&uacute;mero de hashes a realizar.
# size = tama&ntilde;o del bitarray.
for seed in xrange(self.hash_count):
	position = mmh3.hash(element, seed) % self.size
	if self.bit_array[position] == 0:
		return False
return True</pre><p></p>
<div style="width: 659px" class="wp-caption aligncenter"><a href="http://www.aureoares.es/wp-content/uploads/bloom_filter.png"><img class="size-full " src="http://www.aureoares.es/wp-content/uploads/bloom_filter.png" alt="Ejemplo de filtro Bloom." width="649" height="233" /></a><p class="wp-caption-text">Imagen extraída de la Wikipedia, donde se muestra la idea básica de un filtro Bloom utilizando 3 hashes para cada elemento.</p></div>
<h2>El tamaño del bitarray y el número de hashes:</h2>
<p>La mayoría de implementaciones que encontré eran clases cuyo constructor recibía como parámetros el tamaño del bitarray y el número de hashes a utilizar. Aunque se puede utilizar de esta manera, en la práctica creo que es mucho más cómodo especificar lo que realmente me importa: el número de elementos que quiero guardar y el margen de error que estoy dispuesto a permitir.</p>
<p>Para esto es necesario calcular el tamaño del bitarray y el número de hashes necesarios para poder almacenar el número de elementos que queremos con un margen de error menor o igual al especificado. Las fórmulas y su explicación se pueden ver también en la Wikipedia, pero escritas en Python serían algo así:</p>
<p></p><pre class="crayon-plain-tag"># capacity = n&uacute;mero de elementos a guardar.
# error_rate = tasa de error, entre 0 (0%) y 1 (100%). Por ejemplo, 0.01 ser&iacute;a un 1%.
def calc_size():
	return int(ceil(- (float(capacity) * log(float(error_rate))) / (log(2))**2))

# Para calcular el n&uacute;mero de hashes tenemos que haber calculado primero el tama&ntilde;o del bitarray.
# size = tama&ntilde;o del bitarray.
def calc_hash_count():
	return int(ceil((float(size) / float(capacity)) * log(2)))</pre><p></p>
<p>Utilizo la función &#8220;ceil&#8221; para asegurarme de que se cumplen los requisitos (redondeando hacia arriba).</p>
<h2>Uniones e intersecciones:</h2>
<p>Algo que no he visto hasta la fecha en ninguna implementación de filtros Bloom, al menos en Python, son las operaciones de unión e intersección entre dos filtros (desde el punto de vista de teoría de conjuntos). Son increíblemente fáciles de implementar y en concreto la unión de filtros me ha resultado bastante útil.</p>
<p>Las intersecciones también pueden ser útiles, pero en mi caso (pocos o ningún elemento en común) los resultados no me han parecido lo bastante fiables.</p>
<p>Más abajo en el código final de la clase se puede ver la implementación de estas dos operaciones, pero es tan sencillo como realizar las operaciones a nivel de bit AND (&amp;) y OR (|) entre los dos bitarray.</p>
<h2>Mi implementación:</h2>
<p>Esta es la clase que me hice. No es la que uso actualmente ya que más tarde hice un filtro Bloom escalable, más avanzado, que detallaré en otro artículo ya que este me está quedando más grande de lo que esperaba.</p>
<p>Los filtros Bloom tienen una infinidad de utilidades. Cabe destacar que esta clase fue creada específicamente para cubrir mis necesidades, por lo que hay algunas decisiones de implementación (como permitir añadir más elementos aunque el filtro esté al máximo de capacidad o devolver un valor vacío si no se puede realizar una unión) que pueden no ser buenas en otros contextos.</p>
<p></p><pre class="crayon-plain-tag">#!/usr/bin/python
# -*- coding: utf-8 -*-
#

# Requires bitarray and mmh3 (murmurhash3)
# sudo pip install bitarray
# sudo pip install mmh3

from bitarray import bitarray
import mmh3
from math import log, ceil

class BloomFilter:

        def __init__(self, capacity, error_rate):
                if not capacity &gt; 0: raise ValueError(&quot;capacity must be &gt; 0&quot;)
                if not (0 &lt; error_rate &lt; 1): raise ValueError(&quot;error_rate must be between 0 and 1.&quot;)
capacity.
                self.capacity = capacity
                self.element_count = 0
                self.error_rate = error_rate
                self.size = self.calc_size()
                self.hash_count = self.calc_hash_count()
                self.bit_array = bitarray(self.size)
                self.bit_array.setall(0)

        def add(self, element):
                for seed in xrange(self.hash_count):
                        position = mmh3.hash(element, seed) % self.size
                        self.bit_array[position] = 1
                self.element_count += 1

        def lookup(self, element):
                for seed in xrange(self.hash_count):
                        position = mmh3.hash(element, seed) % self.size
                        if self.bit_array[position] == 0:
                                return False
                return True

        def union(self, b):
                if self.size != b.size: return None
                if self.hash_count != b.hash_count: return None
                result = BloomFilter(self.capacity, self.error_rate)
                result.bit_array = self.bit_array | b.bit_array
                result.element_count = self.element_count + b.element_count
                return result

        def intersection(self, b):
                if self.size != b.size: return None
                if self.hash_count != b.hash_count: return None
                result = BloomFilter(self.capacity, self.error_rate)
                result.bit_array = self.bit_array &amp; b.bit_array
                result.element_count = result.calc_element_count()
                return result

        def is_full(self):
                if self.element_count &lt; self.capacity: return False
                else: return True

        def calc_size(self):
                return int(ceil(- (float(self.capacity) * log(float(self.error_rate))) / (log(2))**2))

        def calc_hash_count(self):
                return int(ceil((float(self.size) / float(self.capacity)) * log(2)))

        def calc_error_rate(self, use_capacity = False):
                if use_capacity: n = float(self.capacity)
                else: n = float(self.element_count)
                return (1.0 - (1.0 - 1.0 / float(self.size)) ** (float(self.hash_count) * n)) ** float(self.hash_count)

        def calc_element_count(self):
                x = float(self.bit_array.count())
                return int(ceil(- (float(self.size) * log(1.0 - (x / float(self.size)))) / float(self.hash_count)))

        def __contains__(self, string):
                return self.lookup(string)</pre><p></p>
<p>El código completo comentado, incluyendo la versión escalable, está alojado en <a title="Código completo del filtro Bloom en Google Code" href="https://code.google.com/p/python-scalable-bloom-filter/source/browse/" target="_blank">Google Code</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aureoares.es/filtros-bloom-en-web-scraping/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paige: Moviendo personajes.</title>
		<link>http://www.aureoares.es/paige-moviendo-personajes/</link>
		<comments>http://www.aureoares.es/paige-moviendo-personajes/#comments</comments>
		<pubDate>Wed, 16 Feb 2011 17:22:00 +0000</pubDate>
		<dc:creator><![CDATA[Áureo Ares]]></dc:creator>
				<category><![CDATA[Paige]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Proyectos]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Videojuegos]]></category>

		<guid isPermaLink="false">http://www.aureoares.es/?p=4</guid>
		<description><![CDATA[Estuve dándole vueltas al tema del control del personaje y poco a poco va tomando forma el módulo controller. Aún no me acaba de convencer del todo, pero ya irá mejorando. Se trata básicamente de crear un controlador para cada jugador y asignarle un personaje. Luego se van asignando eventos de pulsación de teclas (sólo [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Estuve dándole vueltas al tema del control del personaje y poco a poco va tomando forma el módulo controller. Aún no me acaba de convencer del todo, pero ya irá mejorando.</p>
<p>Se trata básicamente de crear un controlador para cada jugador y asignarle un personaje. Luego se van asignando eventos de pulsación de teclas (sólo teclas de momento) a funciones que afectarán a dicho personaje.<span id="more-4"></span></p>
<p>Sobre la gestión de colisiones&#8230;, menudas locuras he estado haciendo, hasta que me dí cuenta de que estaba cometiendo un error que parece ser (por lo que he leído luego en otros sitios) muy típico de novatos en esto de la programación de videojuegos: comprobar la colisión después de haber movido al personaje en lugar de hacerlo antes. En otras palabras, yo me preguntaba &#8220;¿ha chocado?&#8221; cuando lo que debería preguntarme es &#8220;¿chocaría?&#8221;. Cuando caí en la cuenta fue muy rápido de implementar.</p>
<p>Como siempre, me queda el consuelo de que no volveré a caer en un error de ese estilo.</p>
<p>Por último, he estado pensando en la cantidad de información que voy a tener que guardar sobre los mapas. Cuando empiece a pensar en un juego &#8220;de verdad&#8221; tendré que almacenar mucho más que el propio escenario: necesitaré los objetos y personajes no jugadores (NPC&#8217;s) que aparezcan en él, las coordenadas donde aparecerán, las acciones que realizarán&#8230; En fin, que toca pensar en bases de datos.</p>
<p>Antes creo que voy a tratar de implementar el &#8220;scrolling&#8221; de los mapas para poder mover a los personajes por mapas que excedan la resolución de la pantalla. Después comenzaré a pensar en el modelo de datos.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aureoares.es/paige-moviendo-personajes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paige: Creando los personajes.</title>
		<link>http://www.aureoares.es/paige-creando-los-personajes/</link>
		<comments>http://www.aureoares.es/paige-creando-los-personajes/#comments</comments>
		<pubDate>Mon, 24 Jan 2011 20:57:00 +0000</pubDate>
		<dc:creator><![CDATA[Áureo Ares]]></dc:creator>
				<category><![CDATA[Paige]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Proyectos]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Videojuegos]]></category>

		<guid isPermaLink="false">http://www.aureoares.es/?p=5</guid>
		<description><![CDATA[Ya tenemos escenario (o algo así). Toca poner personajes en él. Para ello he creado una clase base a partir de la cual crear las clases para los distintos tipos de personajes. Aclaro que cuando me refiero a personajes quiero decir cualquier personaje que aparezca en el juego, no sólo el del jugador. La principal [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Ya tenemos escenario (o algo así). Toca poner personajes en él. Para ello he creado una clase base a partir de la cual crear las clases para los distintos tipos de personajes. Aclaro que cuando me refiero a personajes quiero decir cualquier personaje que aparezca en el juego, no sólo el del jugador.</p>
<p>La principal diferencia entre los distintos tipos de personajes va a ser el movimiento. De momento voy a distinguir entre el movimiento tipo puzzle (el personaje se mueve tile por tile, no puede parar de moverse entre dos tiles) y el movimiento normal (el personaje se mueve &#8220;libremente&#8221;, sin la restricción anterior). He comenzado por implementar el movimiento normal ya que el movimiento tipo puzzle será un caso especial de éste.<span id="more-5"></span></p>
<p>El problema vendría al realizar juegos de plataformas, en los que el personaje sólo se mueve de izquierda a derecha y además &#8220;salta&#8221;. Éste caso lo voy a dejar para más adelante, tengo que estudiar cómo implementar el tema de la &#8220;gravedad&#8221;.</p>
<p>Por otra parte he creado una clase Player para el caso especial del jugador. He hecho algunas pruebas de movimiento, de animación e incluso de cambios de velocidad (para andar y correr, por ejemplo). Ya ayer subí los avances del <a href="http://code.google.com/p/paige/">proyecto</a>. Para la prueba uso las flechas del teclado para mover al personaje y la tecla Z para &#8220;correr&#8221;.</p>
<p>Lo siguiente que quiero hacer es buscar alguna manera un poco más elegante de implementar el control del personaje y comenzar con la gestión de colisiones.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aureoares.es/paige-creando-los-personajes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Paige: Creando el escenario.</title>
		<link>http://www.aureoares.es/paige-creando-el-escenario/</link>
		<comments>http://www.aureoares.es/paige-creando-el-escenario/#comments</comments>
		<pubDate>Wed, 19 Jan 2011 18:19:00 +0000</pubDate>
		<dc:creator><![CDATA[Áureo Ares]]></dc:creator>
				<category><![CDATA[Paige]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Proyectos]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Videojuegos]]></category>

		<guid isPermaLink="false">http://www.aureoares.es/?p=6</guid>
		<description><![CDATA[Todo juego necesita un escenario, por simple que sea. Como era de esperar, voy a utilizar escenarios en 2D. En principio he pensado en un escenario compuesto por una imagen de fondo y un mapa de tres capas basado en tiles. ¿Por qué tres capas? Pues porque las usaré para representar lo que está por [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Todo juego necesita un escenario, por simple que sea. Como era de esperar, voy a utilizar escenarios en 2D.</p>
<p>En principio he pensado en un escenario compuesto por una imagen de fondo y un mapa de tres capas basado en <a href="http://es.wikipedia.org/wiki/Tile">tiles</a>. ¿Por qué tres capas? Pues porque las usaré para representar lo que está por detrás de los personajes, lo que está al mismo nivel y lo que está por delante. Combinado con un fondo creo que se pueden crear mapas decentes para casi cualquier tipo de juego.</p>
<p>En realidad voy a utilizar una cuarta capa para las colisiones con el escenario, que no se dibujará en pantalla ya que servirá para saber por qué partes del escenario no se pueden mover los personajes. Al principio pensé en considerar que el personaje colisionaría con todo lo que estuviese en la capa intermedia (supuestamente a su mismo nivel), pero creo que va a ser útil tenerlo por separado.<span id="more-6"></span></p>
<p>Existen bastantes herramientas para generar mapas basados en tiles, como <a href="http://www.tilemapper.com/">Tile Mapper</a>, <a href="http://www.tilemap.co.uk/mappy.php">Mappy</a> o <a href="http://www.mapeditor.org/">Tiled</a>. Como no voy a meterme en mapas muy complejos y Tiled me ha gustado bastante, me quedo con él. En su <a href="http://sourceforge.net/apps/mediawiki/tiled/index.php?title=Examining_the_map_format">wiki</a> se explica el formato que utiliza y en <a href="http://razonartificial.com/2010/04/engine-iv-tiled-map-editor/">Razón Artificial</a> hay un buen ejemplo de cómo usarlo, entre otros artículos y tutoriales muy interesantes.</p>
<p>En cuanto al código, ya he subido la primera versión de las clases que gestionarán los mapas. Al proyecto le he llamado <a href="http://code.google.com/p/paige/">Paige</a> y cualquiera queda invitado a colaborar.</p>
<p>Comento brevemente su estructura:</p>
<ul>
<li>La clase Map es la que se encarga de cargar los mapas y dibujar en pantalla el fondo y las distintas capas.</li>
<li>La clase Tileset carga las imágenes de los tileset y las &#8220;trocea&#8221; para obtener la tabla de tiles individuales, además de dibujar tiles en la pantalla.</li>
<li>La clase Layer carga las capas del mapa y las dibuja en pantalla.</li>
</ul>
<p>También he subido unas imágenes y un mapa de prueba para ir viendo los resultados. De momento el mapa debe ser del mismo tamaño que la pantalla, más adelante trataré el tema del &#8220;scrolling&#8221;. El siguiente paso será el personaje.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aureoares.es/paige-creando-el-escenario/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python y videojuegos, ¿por qué no?</title>
		<link>http://www.aureoares.es/python-y-videojuegos-por-que-no/</link>
		<comments>http://www.aureoares.es/python-y-videojuegos-por-que-no/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 18:03:00 +0000</pubDate>
		<dc:creator><![CDATA[Áureo Ares]]></dc:creator>
				<category><![CDATA[Paige]]></category>
		<category><![CDATA[Programación]]></category>
		<category><![CDATA[Proyectos]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Videojuegos]]></category>

		<guid isPermaLink="false">http://www.aureoares.es/?p=7</guid>
		<description><![CDATA[Últimamente he empezado a interesarme por la programación de videojuegos. Lo cierto es que nunca me he topado con un juego escrito en Python salvo buscándolo específicamente, tal vez porque seguramente no sea el lenguaje más adecuado para ello. En cualquier caso me han entrado ganas de probar cómo sería eso de desarrollar un videojuego [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Últimamente he empezado a interesarme por la programación de videojuegos. Lo cierto es que nunca me he topado con un juego escrito en Python salvo buscándolo específicamente, tal vez porque seguramente no sea el lenguaje más adecuado para ello. En cualquier caso me han entrado ganas de probar cómo sería eso de desarrollar un videojuego y Python se me hace muy cómodo para probar cosas nuevas, así que he decidido darle una oportunidad.</p>
<p>Leyendo tanto sobre programación de videojuegos en general como temas más específicos de Python (principalmente <a href="http://www.pygame.org/">pygame</a>) he encontrado tantas cosas que llamaban mi atención que me he saturado. Aún no había resuelto cómo dibujar el escenario y ya me encontraba debatiendo conmigo mismo qué algoritmo de búsqueda de caminos implementar para los enemigos.<span id="more-7"></span></p>
<p>Al final he optado por hacer un experimento: voy a desarrollar al mismo tiempo una especie de &#8220;motor&#8221; y un videojuego, de manera que iré implementando funciones en el motor según las vaya necesitando, aunque trataré de hacerlo lo más general posible de manera que sirva para otros tipos de juegos. Para no partir totalmente de cero voy a basarme en pygame.</p>
<p>Ya he visto algunas cosillas por ahí parecidas a lo que quiero hacer, pero como lo que realmente me interesa es aprender prefiero hacerlo yo mismo desde el principio, aunque investigue cómo lo hacen los demás, claro está. El código del motor lo alojaré en Google Code y aquí iré comentando la experiencia. No tengo intención de hacer una especie de tutorial, sino más bien comentar las dificultades que encuentre, decisiones a las que me enfrente, errores que cometa, etc.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.aureoares.es/python-y-videojuegos-por-que-no/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
