<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">

<channel>
	<title>Kooneiform</title>
	
	<link>http://kooneiform.wordpress.com</link>
	<description>writing  +  text  +  games</description>
	<lastBuildDate>Sun, 19 Jul 2009 20:03:46 +0000</lastBuildDate>
	<generator>http://wordpress.com/</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<image>
		<url>http://www.gravatar.com/blavatar/b7bfdea260bb55838222f494beb2a4e7?s=96&amp;d=http://s.wordpress.com/i/buttonw-com.png</url>
		<title>Kooneiform</title>
		<link>http://kooneiform.wordpress.com</link>
	</image>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/Kooneiform" type="application/rss+xml" /><item>
		<title>timeless actions (a roguelike in Python, #12)</title>
		<link>http://kooneiform.wordpress.com/2009/07/19/timeless-actions-a-roguelike-in-python-12/</link>
		<comments>http://kooneiform.wordpress.com/2009/07/19/timeless-actions-a-roguelike-in-python-12/#comments</comments>
		<pubDate>Sun, 19 Jul 2009 20:03:46 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=361</guid>
		<description><![CDATA[One last important thing I needed to add to command handling was commands that take no time &#8212; certain out-of-game actions or meta commands like switching UI views and things like that. I&#8217;m imagining that most commands will take a player&#8217;s turn but I wanted to have support for actions that don&#8217;t. 
At the same [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=361&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>One last important thing I needed to add to command handling was commands that take no time &#8212; certain out-of-game actions or meta commands like switching UI views and things like that. I&#8217;m imagining that most commands will take a player&#8217;s turn but I wanted to have support for actions that don&#8217;t. </p>
<p>At the same time I wanted to centralize command parsing &#8212; before commands were enacted by calling the command directly which was OK but made it tedious to add things that would happen when any command was called. So here&#8217;s what I came up with &#8212; some significant changes to the Command class:</p>
<pre name="code" class="python">

class Command(object):
    def __init__(self):
        self.playbook = {
                        &#039;north&#039; : (&#039;takes turn&#039;, self.move, 0, -1),
                        &#039;east&#039;  : (&#039;takes turn&#039;, self.move, 1, 0),
                        &#039;south&#039; : (&#039;takes turn&#039;, self.move, 0, 1),
                        &#039;west&#039;  : (&#039;takes turn&#039;, self.move, -1, 0),
                        &#039;ctrl north&#039;    : (&#039;takes turn&#039;, self.ctrl, 0, -1),
                        &#039;ctrl east&#039;     : (&#039;takes turn&#039;, self.ctrl, 1, 0),
                        &#039;ctrl south&#039;    : (&#039;takes turn&#039;, self.ctrl, 0, 1),
                        &#039;ctrl west&#039;     : (&#039;takes turn&#039;, self.ctrl, -1, 0),
                        &#039;print history&#039; : (&#039;free action&#039;, self.print_history, None)}
</pre>
<p>And now commands are routed through a single function:</p>
<pre name="code" class="python">

    def do(self, string, enactor):
        command = self.playbook[string]
        command_name, args = command[1], command[2:]
        command_name(enactor, *args)
        self.history.append((string, enactor.name))
        return command[0]
</pre>
<p>Which gets called like this in the player input function:</p>
<pre name="code" class="python">

        command_name = self.ctrl + self.keycfg[key]
        game.message.text = &quot;Go!&quot;

        if game.command.do(command_name, player.thing) == &#039;free action&#039;:
            self.gameplay_update()
</pre>
<p>So the <strong>command_name</strong> is created, for example &#8216;ctrl north&#8217;, and is sent to <strong>command.do</strong>. That gets looked up in the playbook, and the command is called with its arguments. The lookup also tells the game if the action takes a turn or not &#8212; this information is returned to the original call, and if it&#8217;s a free action the player input function is called again without a turn passing. </p>
<p>The &#8216;history&#8217; is mostly a way to test this out. I have a vague idea of allowing playback of moves, but that&#8217;s going to require a lot of other work that&#8217;s not a priority right now. </p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/361/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/361/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/361/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/361/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/361/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/361/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/361/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/361/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/361/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/361/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=361&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/07/19/timeless-actions-a-roguelike-in-python-12/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>
	</item>
		<item>
		<title>oh no you mean I have to add things? (a roguelike in Python, #12)</title>
		<link>http://kooneiform.wordpress.com/2009/07/18/oh-no-you-mean-i-have-to-add-things-a-roguelike-in-python-12/</link>
		<comments>http://kooneiform.wordpress.com/2009/07/18/oh-no-you-mean-i-have-to-add-things-a-roguelike-in-python-12/#comments</comments>
		<pubDate>Sat, 18 Jul 2009 18:07:44 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=355</guid>
		<description><![CDATA[Up to now I&#8217;ve been motoring along quite happy with my simple arrangement &#8212; a player, a monster, some lamps, field-of-view, saving and loading. In theory I&#8217;d written the code as a framework that would make it easy to add stuff, but&#8230;I hadn&#8217;t actually added any stuff yet. I knew this was an important step. [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=355&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Up to now I&#8217;ve been motoring along quite happy with my simple arrangement &#8212; a player, a monster, some lamps, field-of-view, saving and loading. In theory I&#8217;d written the code as a framework that would make it easy to add stuff, but&#8230;I hadn&#8217;t actually added any stuff yet. I knew this was an important step. Would things totally break down? How easy would it be to add stuff?</p>
<p>So I&#8217;ve started to add stuff. Let&#8217;s see how easy it is to add something. Trying to keep things simple, I&#8217;m starting with windows and doors.</p>
<p>Now I already had windows, but these were a part of the hardcoded map. I&#8217;ve decided to make them of the Thing class instead, and so the hardcoded map will strictly represent terrain (at the moment just walls and ground). To make a window a thing this is what I had to do:</p>
<ul>
<li>remove the window from <strong>game.world.cell_types</strong> and the hardcoded map</li>
<li>add a test to <strong>Fov </strong>so that if a cell in field-of-view is a window, it&#8217;s marked as transparent in the fov map.</li>
<li>add the window to <strong>player.ui.chars</strong></li>
<li>remove adding the window to the viewport structure in the view.update, as now it&#8217; s just drawn with the other things.</li>
<li>create the window thing in <strong>Game</strong>.</li>
</ul>
<p>Adding doors followed a similar path as above (minus removing code of course as doors are totally new), but I also had to add the functionality of opening doors. So, I went to the <strong>Command </strong>class. Here is what the rulebook and rules look like now:</p>
<pre name="code" class="python">

class Command(object):
    def __init__(self):
        self.rulebook = {
                        &#039;move&#039; : (&#039;cell has monster&#039;, &#039;cell is unwalkable&#039;, &#039;cell has closed door&#039;, &#039;open door implicitly&#039;)}

        self.rules = {
                        &#039;cell has monster&#039; : &#039;game.world.cell_has_monster(thing.x+dx, thing.y+dy)&#039;,
                        &#039;cell is unwalkable&#039; : &#039;game.world.cell_is_unwalkable(thing.x+dx, thing.y+dy)&#039;,
                        &#039;cell has closed door&#039; : &#039;game.world.cell_has_closed_door(thing.x+dx, thing.y+dy)&#039;,
                        &#039;open door implicitly&#039; : &#039;game.world.open_door_implicitly(thing.x+dx, thing.y+dy)&#039;}
</pre>
<p>So I added &#8216;cell has closed door&#8217; and &#8216;open door implicitly&#8217; (bump to open &#8212; now that I think of it I might be able to make a general &#8216;bump to open&#8217; rule). Here are the corresponding methods in <strong>World</strong>. </p>
<pre name="code" class="python">

    def cell_has_closed_door(self, x, y):
        for thing in game.world.things:
            if (thing.x, thing.y) == (x, y) and thing.name == &#039;closed door&#039;:
                return True

    def open_door_implicitly(self, x, y):
        if self.cell_has_closed_door(x, y):
            for thing in game.world.things:
                if (thing.x, thing.y) == (x, y) and thing.name == &#039;closed door&#039;:
                    thing.name = &#039;open door&#039;
</pre>
<p>Though I set the name directly here, I think I&#8217;ll change this later to call a method on Thing to change its name. I&#8217;m trying to keep World as a state object only, changing its state from outside this object. </p>
<p>In player.ui.chars I have this:</p>
<pre name="code" class="python">

        self.chars = {
                        &#039;player&#039;    : &#039;@&#039;,
                        &#039;monster&#039;   : &#039;?&#039;,
                        &#039;ground&#039;    : &#039; &#039;,
                        &#039;wall&#039;      : &#039;#&#039;,
                        &#039;window&#039;    : libtcod.CHAR_DHLINE,
                        &#039;lamp&#039;      : &#039;Q&#039;,
                        &#039;open door&#039; : &#039;/&#039;,
                        &#039;closed door&#039;   : &#039;+&#039;}
</pre>
<p>So when I change the name to &#8216;open door&#8217;, the symbol changes, and the movement test for a closed door will pass as well. </p>
<p>Overall, not too bad. I think I&#8217;m going to have to generalize the field-of-view code a bit, as I can forsee adding a lot of special cases as I add objects if I keep doing what I&#8217;m doing. So far I&#8217;m happy with how the rulebook and rules works too.</p>
<p><strong>update:</strong></p>
<p>To make a close doors command required a few more changes. This requires either a command then command input (like a &#8216;c&#8217; key then direction) or a combination key like command + command. I&#8217;ve decided to go with the latter, and I&#8217;m using CTRL + direction. I think I like the idea of extending this in the future into a context sensitive command as well. As libtcod doesn&#8217;t currently have an easy way to test for a combined key press (I think it&#8217;s slated for 1.5) I just made a boolean in player.input like so:</p>
<pre name="code" class="python">

class Input(object):
    def __init__(self):
        self.update_state = self.intro_update
        file = open(&#039;keys.cfg&#039;, &#039;r&#039;)
        self.keycfg = yaml.load(file)
        self.ctrl = 0

    def get_key(self, key):
        if key.lctrl:
            self.ctrl = 1
        else:
            self.ctrl = 0

        if key.c:
            return chr(key.c)
        else:
            return key.vk
</pre>
<p>Every time I call <strong>get_key()</strong> (when a key is pressed) I check if the left control is pressed and then set the <strong>self.ctrl</strong> value accordingly. Then in <strong>Command </strong>I can do this:</p>
<pre name="code" class="python">

    def north(self, enactor):
        if player.input.ctrl:
            self.close_door(enactor, 0, -1)
        else:
            self.move(enactor, 0, -1)  
</pre>
<p>And then add the rules and a command for <strong>close_door()</strong> like normal. </p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/355/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/355/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/355/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/355/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/355/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/355/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/355/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/355/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/355/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/355/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=355&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/07/18/oh-no-you-mean-i-have-to-add-things-a-roguelike-in-python-12/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>
	</item>
		<item>
		<title>premature optimization is the root of all…oh wait (a roguelike in python, #11)</title>
		<link>http://kooneiform.wordpress.com/2009/07/16/premature-optimization-is-the-root-of-all-oh-wait-a-roguelike-in-python-11/</link>
		<comments>http://kooneiform.wordpress.com/2009/07/16/premature-optimization-is-the-root-of-all-oh-wait-a-roguelike-in-python-11/#comments</comments>
		<pubDate>Thu, 16 Jul 2009 17:43:08 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=347</guid>
		<description><![CDATA[Up to now I&#8217;ve been running the demo in a rather small window (about 40&#215;20 cells) with a small viewport (19&#215;19). Actually I like the idea of a game this size, but with the demo I&#8217;d like to try something bigger. Since I&#8217;m scrolling I knew I might run into issues with how fast the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=347&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>Up to now I&#8217;ve been running the demo in a rather small window (about 40&#215;20 cells) with a small viewport (19&#215;19). Actually I like the idea of a game this size, but with the demo I&#8217;d like to try something bigger. Since I&#8217;m scrolling I knew I might run into issues with how fast the screen redraws (as I&#8217;m redrawing the whole thing every turn).</p>
<p>So first I put the level in a text file and made it much bigger (it&#8217;s now about 80&#215;50), and then I increased the window size, and the viewport size to 40&#215;40. As I found out the game really got sluggish. Reducing the viewport to 30&#215;30 improved things to make it playable, and so I wanted to see exactly where things were slowing down. </p>
<p>To start, I firmly believe in first writing without optimization in mind. Much of what I write is very verbose, using strings insteads of integers for properties and so on. I think that for what I&#8217;m doing the bottlenecks are going to come in specific cases, and it&#8217;s not really necessary to bitshift when I can just multiply integers, or whatever real programmers do, I&#8217;m sure I&#8217;m getting the terms wrong here. </p>
<p>As with many things Python makes it really easy to use support modules and tools, at least for basic things. So what follows here is an idiot&#8217;s guide to profiling the bottlenecks in your code.</p>
<p>The <strong>cProfile </strong>module is dead simple to use and in the standard library. I used it in two ways. First, from the command line, you can do this:</p>
<p><strong>python -m cProfile -o nameOfYourOutputFile nameOfYourGameFile</strong></p>
<p>Your output file doesn&#8217;t need to exist first. What this will do is start up your game using the cProfile module to time how many seconds are spent in each function call. When you exit your game you&#8217;ll find the output file in the game&#8217;s directory. Then you can create a simple .py script like so (this is all in the Python documentation for cProfile by the way) &#8212; let&#8217;s say your output file was called test_profile:</p>
<pre name="code" class="python">

import pstats
p = pstats.Stats(&#039;test_profile&#039;)

p.sort_stats(&#039;time&#039;).print_stats(10)
</pre>
<p>Running this script will give you a breakdown of time spent. You can sort this output in various ways, see the docs for details.</p>
<p>Now what if you want to profile only one function in your code? When I ran the first script I saw that the viewport draw call was taking a significant portion of time, but the output doesn&#8217;t break down the draw function itself. All you need to do is something like this, where you call your draw function:</p>
<pre name="code" class="python">

        PROFILER.runcall(self.view.update)
        PROFILER.dump_stats(&#039;test_profile&#039;)
</pre>
<p>Remembering at the top of your game file to import cProfile and set up the profiler like so:</p>
<pre name="code" class="python">

#############################################
# imports
#############################################

import os
import random
import yaml
import cProfile

import libtcod.libtcodpy as libtcod

PROFILER = cProfile.Profile()
</pre>
<p>That&#8217;s it. Then you just run the pstats script above in the same way. </p>
<p>These are the calls that take the most time in my draw function:</p>
<pre name="code" class="python">

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      202    7.430    0.037   16.854    0.083 diana.py:427(draw)
   194171    1.873    0.000    4.267    0.000 C:\Documents and Settings\GO\My Documents\dev\diana\libtcod\libtcodpy.py:58(__mul__)
   194171    1.393    0.000    2.036    0.000 C:\Documents and Settings\GO\My Documents\dev\diana\libtcod\libtcodpy.py:74(int_to_col)
   182002    1.360    0.000    1.647    0.000 C:\Documents and Settings\GO\My Documents\dev\diana\libtcod\libtcodpy.py:448(console_put_char)
   181800    0.926    0.000    0.926    0.000 C:\Documents and Settings\GO\My Documents\dev\diana\libtcod\libtcodpy.py:454(console_set_back)
   194171    0.642    0.000    0.642    0.000 C:\Documents and Settings\GO\My Documents\dev\diana\libtcod\libtcodpy.py:50(__init__)
   181800    0.496    0.000    0.496    0.000 diana.py:415(viewport_ij_to_world_xy)
   181800    0.494    0.000    0.494    0.000 diana.py:117(cell_type_get)
    94940    0.390    0.000    0.390    0.000 diana.py:421(xy_in_viewport)
    83478    0.380    0.000    0.380    0.000 C:\Documents and Settings\GO\My Documents\dev\diana\libtcod\libtcodpy.py:1050(map_is_in_fov)
   194171    0.358    0.000    0.358    0.000 {isinstance}
   203134    0.345    0.000    0.345    0.000 {method &#039;append&#039; of &#039;list&#039; objects}
   182002    0.288    0.000    0.288    0.000 {ord}
</pre>
<p>So it looks like multiplying colors together takes a significant portion of time. I may have to look into precaching this information somehow. However there is an even easier way to speed things up, though it requires a third-party module &#8212; the <a href="http://psyco.sourceforge.net/">Psyco module</a>.</p>
<p>All you need to do to get Psyco working (after downloading and installing it) is put in your game file like this:</p>
<pre name="code" class="python">

if __name__ == &#039;__main__&#039;:
    try:
        import psyco
        psyco.full()
    except ImportError:
        pass

    game = Game()
    player = Awesome()

    while not game.exit:
        game.update()
        player.ui.draw()
</pre>
<p>You can also have Psyco optimize only specific functions in your code &#8212; as it uses more memory in its overhead, this may be important for some people. </p>
<p>I did notice a speed improvement with Psyco, but as it&#8217;s an external dependency my first line of attack will be speeding up the routines in the draw code itself. It may be enough to keep the viewport a reasonable size, thus drawing less to the screen. Of course I haven&#8217;t added any real gameplay yet&#8230;now that can&#8217;t take much time to compute, can it?</p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/347/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=347&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/07/16/premature-optimization-is-the-root-of-all-oh-wait-a-roguelike-in-python-11/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>
	</item>
		<item>
		<title>how I love refactoring (a roguelike in Python, a diversion)</title>
		<link>http://kooneiform.wordpress.com/2009/07/12/how-i-love-refactoring-a-roguelike-in-python-a-diversion/</link>
		<comments>http://kooneiform.wordpress.com/2009/07/12/how-i-love-refactoring-a-roguelike-in-python-a-diversion/#comments</comments>
		<pubDate>Sun, 12 Jul 2009 23:41:59 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=345</guid>
		<description><![CDATA[In working on adding player memory to the map (so cells not immediatly in field-of-view but previously seen are drawn) I realized something. My scrolling/camera code looked like this:


    def scroll(self):
        self.x_offset = min(max(0, player.thing.x - self.width//2), game.world.level_width - self.width)
      [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=345&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>In working on adding player memory to the map (so cells not immediatly in field-of-view but previously seen are drawn) I realized something. My scrolling/camera code looked like this:</p>
<pre name="code" class="python">

    def scroll(self):
        self.x_offset = min(max(0, player.thing.x - self.width//2), game.world.level_width - self.width)
        self.y_offset = min(max(0, player.thing.y - self.height//2), game.world.level_height - self.height)

        self.x_left_offset = min(player.thing.x, self.width//2)
        self.x_right_offset = max(0, (self.width//2 - (game.world.level_width - player.thing.x) + (self.width % 2)))
        self.focus_x =  self.x_left_offset + self.x_right_offset

        self.y_top_offset = min(player.thing.y, self.height//2)
        self.y_bottom_offset = max(0, (self.height//2 - (game.world.level_height - player.thing.y) + (self.height % 2)))
        self.focus_y =  self.y_top_offset + self.y_bottom_offset
</pre>
<p>When it really only needs to look like this:</p>
<pre name="code" class="python">

    def scroll_update(self):
        self.anchor_x = min(max(0, player.thing.x - self.width//2), game.world.level_width - self.width)
        self.anchor_y = min(max(0, player.thing.y - self.height//2), game.world.level_height - self.height)
</pre>
<p>The problem was I had been treating where the player was rendered as a special case &#8212; in effect putting the player and the camera focus position in the same basket. This wasn&#8217;t necessary at all &#8212; all you need to know is where the camera or view is positioned on the screen (I chose to keep track of the top left corner of the view with the variables <strong>anchor_x</strong> and <strong>anchor_y</strong>). Then you just need to translate world coordinates to view coordinates when you deal with the viewport. </p>
<p>I knew the previous code was too complicated. It feels good to get it into shape!</p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/345/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/345/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/345/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/345/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/345/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/345/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/345/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/345/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/345/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/345/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=345&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/07/12/how-i-love-refactoring-a-roguelike-in-python-a-diversion/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>
	</item>
		<item>
		<title>new fov (a roguelike in Python #10)</title>
		<link>http://kooneiform.wordpress.com/2009/07/02/new-fov-a-roguelike-in-python-10/</link>
		<comments>http://kooneiform.wordpress.com/2009/07/02/new-fov-a-roguelike-in-python-10/#comments</comments>
		<pubDate>Fri, 03 Jul 2009 07:27:03 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=342</guid>
		<description><![CDATA[As I began in the last few days to add more things to the map with a field-of-view (starting with some lamps) it seemed like it was getting harder to work in the field-of-view structure I had. Most of this had to do with translation of world to viewport coordinates, and I realized I was [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=342&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>As I began in the last few days to add more things to the map with a field-of-view (starting with some lamps) it seemed like it was getting harder to work in the field-of-view structure I had. Most of this had to do with translation of world to viewport coordinates, and I realized I was calculating things in terms of viewport coordinates in the field-of-view code. Conceptually this seemed a little strange &#8212; I decided to keep all game calculations in world coordinates, and only clip to the view port when drawing to the screen.</p>
<p>After a day of thinking and a night of rewriting I think I have it!</p>
<p><img src="http://kooneiform.files.wordpress.com/2009/07/newfov.png?w=350&#038;h=217" alt="newfov" title="newfov" width="350" height="217" class="alignleft size-full wp-image-343" /></p>
<p>Some more notes after the cut.</p>
<p><span id="more-342"></span></p>
<p>The full source in quite a rough state is below. As I mentioned the basic idea of the rewrite is to keep everything in world coordinates until I draw to the player&#8217;s view. So let&#8217;s start with the <strong>Fov </strong>class. </p>
<p>In the <strong>Fov </strong>update method I call <strong>cells_get()</strong>, which now is a method of <strong>World</strong>. This function grabs a section of the map depending on how big the field-of-view radius is; that&#8217;s another change from the old <strong>Fov</strong>, where for each update I would make an <strong>fov_map</strong> as big as the viewport. </p>
<p>Most of the code in <strong>cells_get</strong> deals with clipping off the section in case it&#8217;s near the level borders. Also I made provision for calling the function with no arguments (though I may get rid of that). </p>
<p>Whereas before this function returned a mapping of viewport coordinates and world coordinates, now it just returns world coordinates. </p>
<p>The next step in the <strong>Fov </strong>update is to account for the possibility that the thing calculating field-of-view is up against the level border, in which case the <strong>fov_map</strong> won&#8217;t be its full size. Finally with the size determined I allocate the map. </p>
<p>Next I needed to take the world coordinates returned by <strong>cells_get</strong> and determine field-of-view properties; by incrementing coordinates for the <strong>fov_map</strong> at the same time I create a mapping of world coordinates and fov_map coordinates, which I put in <strong>self.fov_map_to_world_coordinates </strong>(take these long variable names as my method of commenting!).</p>
<p>With this done, the next step was to rewrite the <strong>View</strong>&#8217;s draw method. So far this is what I&#8217;m doing; first, I make sure the viewport is updated with <strong>self.style() </strong>(I imagine I&#8217;ll get rid of this property before long and just directly call <strong>self.scroll()</strong>). Then I create a structure with <strong>maps </strong>that holds each fov_map with its mapping of fov and world coordinates. </p>
<p>I append the player&#8217;s maps last, as this ensures their view is drawn on top of everthing else. I think once I&#8217;m fully blending affects this may not be as important as it is now, though I&#8217;m still hazy on how this will work. </p>
<p>Next I look at each set of maps and the world coordinates in the <strong>maps </strong>structure, first determining if the coordinates are actually in the viewport at all; if not, I skip the following calculations for now (though I may want to do them later depending on what affects I have). Then I run the usual calculations for affect and cell types, finding the appropriate colors. I added a utility function to find viewport coordinates from world coordinates, though I could easily fold that into this code (which I may still do). </p>
<p>Currently I just draw all field-of-views, and of course I&#8217;ll change this to hide those not visible to the player.</p>
<p>That&#8217;s pretty much it. I like the logic of this much better. </p>
<p>And now I have lamps!</p>
<p>It feels good to get this done. </p>
<pre name="code" class="python">

#!/usr/bin/python

&#039;&#039;&#039;
libtcod OO python tutorial
This code modifies samples_py.py from libtcod 1.4.1. It shows a &#039;@&#039;
walking around a scrolling map with a source of light giving simple FOV.
It&#039;s in the public domain.
&#039;&#039;&#039;

#############################################
# imports
#############################################

import os
import random
import yaml

import libtcodpy as libtcod

#############################################
# be things
#############################################

class Thing(object):
    def __init__(self, name, x=-1, y=-1):
        self.name = name
        self.speed = &#039;normal&#039;
        self.x = x
        self.y = y

    def update(self):
        pass

class World(Thing):
    def __init__(self):
        self.cell_types = {
                            &#039; &#039; : [&#039; &#039;, &#039;transparent&#039;, &#039;walkable&#039;],
                            &#039;#&#039; : [&#039;#&#039;],
                            &#039;=&#039; : [&#039;=&#039;, &#039;transparent&#039;]}

        super(World, self).__init__(&#039;world&#039;)

        self.things = []

        self.level_width = 46
        self.level_height = 31
        self.level = [
        &#039;##############################################&#039;,
        &#039;#######################      #################&#039;,
        &#039;#####################    #     ###############&#039;,
        &#039;######################  ###        ###########&#039;,
        &#039;##################      #####             ####&#039;,
        &#039;################       ########    ###### ####&#039;,
        &#039;###############      #################### ####&#039;,
        &#039;################    ######                  ##&#039;,
        &#039;########   #######  ######   #     #     #  ##&#039;,
        &#039;########   ######      ###                  ##&#039;,
        &#039;########                                    ##&#039;,
        &#039;####       ######      ###   #     #     #  ##&#039;,
        &#039;#### ###   ########## ####                  ##&#039;,
        &#039;#### ###   ##########   ###########=##########&#039;,
        &#039;#### ##################   #####          #####&#039;,
        &#039;#### ###             #### #####          #####&#039;,
        &#039;####           #     ####                #####&#039;,
        &#039;########       #     #### #####          #####&#039;,
        &#039;########       #####      ####################&#039;,
        &#039;##########   #################################&#039;,
        &#039;##########   #################################&#039;,
        &#039;###########  #################   #############&#039;,
        &#039;############  ###############   ##############&#039;,
        &#039;#############   ###########     ##############&#039;,
        &#039;###############  #########      ##############&#039;,
        &#039;################   ####                  #####&#039;,
        &#039;################   ###   ###########   #######&#039;,
        &#039;###############   ##   ##############  #######&#039;,
        &#039;#############   ###  #################   #####&#039;,
        &#039;#############       ##################       #&#039;,
        &#039;##############################################&#039;,
        ]

        self.level_make()

    def level_make(self):
        self.level_state = []
        for line in self.level:
            line_data = []
            for cell in line:
                line_data.append(self.cell_types[cell])
            self.level_state.append(line_data)

    def cells_get(self, thing=None, radius=None):
        cells = []

        r = 0
        if radius:
            r = radius

        y_start = 0
        x_start = 0
        y_end = game.world.level_height
        x_end = game.world.level_width
        if thing:
            y_start = max(thing.y - r, 0)
            x_start = max(thing.x - r, 0)
            y_end = min(game.world.level_height, thing.y + r)+1
            x_end = min(game.world.level_width, thing.x + r)+1

        for y in range(y_start, y_end):
            for x in range(x_start, x_end):
                cells.append((x, y))

        return cells

    def cell_has_monster(self, x, y):
        for thing in game.world.things:
            if (thing.x, thing.y) == (x, y) and (thing.name == &#039;monster&#039; or thing.name == &#039;player&#039;):
                return True

    def cell_is_unwalkable(self, x, y):
        if not &#039;walkable&#039; in game.world.level_state[y][x]:
            return True

class Actor(Thing):
    def __init__(self, name, x=-1, y=-1, *handlers):
        super(Actor, self).__init__(name, x, y)

        self.brain = Brain()
        self.brain.add_handlers(self, *handlers)

        game.world.things.append(self)

    def update(self):
        self.brain.update()

#############################################
# do things
#############################################

class Brain(object):
    def __init__(self):
        self.handlers = []

    def add_handlers(self, thing, *handlers):
        for string in handlers:
            handler = eval(string)
            self.handlers.append(handler(thing, self))

    def remove_handlers(self, *handlers):
        for handler in handlers:
            self.handlers.remove(handler)

    def update(self):
        for handler in self.handlers:
            handler.update()

class Command(object):
    def __init__(self):
        self.rulebook = {
                        &#039;move&#039; : (&#039;cell has monster&#039;, &#039;cell is unwalkable&#039;)}

        self.rules = {
                        &#039;cell has monster&#039; : &#039;game.world.cell_has_monster(thing.x+dx, thing.y+dy)&#039;,
                        &#039;cell is unwalkable&#039; : &#039;game.world.cell_is_unwalkable(thing.x+dx, thing.y+dy)&#039;}

    def north(self, thing):
            self.move(thing, 0, -1)          

    def south(self, thing):
            self.move(thing, 0, 1)          

    def east(self, thing):
            self.move(thing, 1, 0)          

    def west(self, thing):
            self.move(thing, -1, 0)  

    def move(self, thing, dx, dy):
        if True in [eval(self.rules[rule]) for rule in self.rulebook[&#039;move&#039;]]:
            pass
        else:
            thing.x = thing.x + dx
            thing.y = thing.y + dy

class Fov(object):
    def __init__(self, thing, brain):
        self.thing = thing
        self.brain = brain
        self.fov_map = None
        self.fov_radius = 1
        self.fov_map_to_world_coordinates = []

    def update(self):
        self.fov_map_to_world_coordinates = []
        cells = game.world.cells_get(self.thing, self.fov_radius)

        cell_first = cells[0]
        cell_last = cells[len(cells)-1]

        difference_right = cell_last[0] - self.thing.x
        difference_bottom = cell_last[1] - self.thing.y
        difference_left = self.thing.x - cell_first[0]
        difference_top = self.thing.y - cell_first[1]

        fov_map_size_x = difference_right + difference_left + 1
        fov_map_size_y = difference_bottom + difference_top + 1

        self.fov_map = libtcod.map_new(fov_map_size_x, fov_map_size_y)

        for i in range(len(cells)):
            x, y = cells[i]
            fov_map_x = i % fov_map_size_x
            fov_map_y = i // fov_map_size_x
            cell = game.world.level_state[y][x]
            if &#039;walkable&#039; in cell and &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, True, True)
            elif &#039;walkable&#039; in cell:
                libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, False, True)
            elif &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.fov_map, fov_map_x, fov_map_y, True, False)

            if (x, y) == (self.thing.x, self.thing.y):
                fov_x_focus = fov_map_x
                fov_y_focus = fov_map_y

            self.fov_map_to_world_coordinates.append(((fov_map_x, fov_map_y), (x, y)))

        libtcod.map_compute_fov(self.fov_map, fov_x_focus, fov_y_focus, self.fov_radius, True)

        self.brain.fov_map = self.fov_map
        self.brain.fov_radius = self.fov_radius
        self.brain.fov_map_to_world_coordinates = self.fov_map_to_world_coordinates

class AI(object):
    def __init__(self, thing, brain):
        self.choices = [&#039;north&#039;, &#039;south&#039;, &#039;west&#039;, &#039;east&#039;]
        self.thing = thing

    def update(self):
        choice = random.randint(0, 3)
        command_name = self.choices[choice]
        command = getattr(game.command, command_name)
        command(self.thing)

class Light(object):
    def __init__(self, thing, brain):
        pass

    def update(self):
        pass

#############################################
# player
#############################################

class Awesome(object):
    def __init__(self):
        self.ui = UI()
        self.view = View()
        self.input = Input()

        game.view = self.view
        game.input = self.input

        if os.path.isfile(&#039;centaur.sav&#039;):
            file = open(&#039;centaur.sav&#039;, &#039;r&#039;)
            game.world.things = yaml.load(file)
            for thing in game.world.things:
                if thing.name == &#039;player&#039;:
                    self.thing = thing
            file.close()
        else:
            self.thing = Actor(&#039;player&#039;, 26, 16, &#039;Fov&#039;)

class UI(object):
    def __init__(self):
        self.width = 40
        self.height = 22

        self.state = self.intro_draw

        self.chars = {
                        &#039;player&#039;    : &#039;@&#039;,
                        &#039;monster&#039;   : &#039;?&#039;,
                        &#039;ground&#039;    : &#039; &#039;,
                        &#039;wall&#039;      : &#039;#&#039;,
                        &#039;window&#039;    : &#039;=&#039;,
                        &#039;lamp&#039;      : &#039;Q&#039;}

        self.cell_colors = {
                            &#039;wall&#039; : libtcod.Color(67, 65, 52),
                            &#039;ground&#039; : libtcod.Color(145, 140, 140),
                            &#039;dark&#039;: libtcod.Color(20, 20, 24),
                            &#039;lit&#039;: libtcod.Color(255, 255, 33),
                            &#039;visible&#039;: libtcod.Color(254, 254, 233)}    

        font = os.path.join(&#039;fonts&#039;, &#039;arial12x12.png&#039;)
        libtcod.console_set_custom_font(
                                        font,
                                        libtcod.FONT_LAYOUT_TCOD |
                                        libtcod.FONT_TYPE_GREYSCALE,
                                        32,
                                        <img src='http://s.wordpress.com/wp-includes/images/smilies/face-cool.png' alt='8)' class='wp-smiley' /> 

        libtcod.console_init_root(
                                self.width,
                                self.height,
                                &#039;Python Demo&#039;,
                                False)
        libtcod.console_credits()

    def draw(self):
        self.state()

    def intro_draw(self):
        libtcod.console_clear(0)
        libtcod.console_set_foreground_color(0, libtcod.white)
        libtcod.console_print_center(0, 20, 8, libtcod.BKGND_NONE, &quot;A Python Demo&quot;)
        libtcod.console_print_center(0, 20, 11, libtcod.BKGND_NONE, &quot;press any key&quot;)
        libtcod.console_flush()

    def gameplay_draw(self):
        libtcod.console_clear(0)

        libtcod.console_set_foreground_color(0, libtcod.white)
        libtcod.console_print_left(0, 1, 1, libtcod.BKGND_NONE, &quot;HJKL move around&quot;)
        libtcod.console_print_left(0, 20, 3, libtcod.BKGND_NONE, &quot;speed: %s&quot; % player.thing.speed)
        libtcod.console_print_left(0, 20, 4, libtcod.BKGND_NONE, &quot;game turn: %d&quot; % game.turns)
        libtcod.console_print_left(0, 20, 5, libtcod.BKGND_NONE, &quot;phase: %s&quot; % game.phase)
        libtcod.console_print_left(0, 20, 6, libtcod.BKGND_NONE, &quot;phase count: %d&quot; % game.phase_count)

        libtcod.console_set_foreground_color(0, libtcod.black)

        libtcod.console_blit(
                                player.view.screen,
                                0,
                                0,
                                player.view.width,
                                player.view.height,
                                0,
                                0,
                                self.height - player.view.height,
                                255)
        libtcod.console_flush()

class View(object):
    def __init__(self):
        self.width = 19
        self.height = 19
        self.focus_x = None
        self.focus_y = None
        self.style = self.scroll

        self.screen = libtcod.console_new(self.width, self.height)
        libtcod.console_set_foreground_color(self.screen, libtcod.black)

    def update(self):
        self.draw()

    def scroll(self):
        self.x_offset = min(max(0, player.thing.x - self.width//2), game.world.level_width - self.width)
        self.y_offset = min(max(0, player.thing.y - self.height//2), game.world.level_height - self.height)

        self.x_left_offset = min(player.thing.x, self.width//2)
        self.x_right_offset = max(0, (self.width//2 - (game.world.level_width - player.thing.x) + (self.width % 2)))
        self.focus_x =  self.x_left_offset + self.x_right_offset

        self.y_top_offset = min(player.thing.y, self.height//2)
        self.y_bottom_offset = max(0, (self.height//2 - (game.world.level_height - player.thing.y) + (self.height % 2)))
        self.focus_y =  self.y_top_offset + self.y_bottom_offset 

    def world_xy_to_view_xy(self, x, y):
        x = x - self.x_offset
        y = y - self.y_offset
        return (x, y)

    def draw(self):
        libtcod.console_clear(self.screen)

        self.style()

        maps = []
        for thing in game.world.things:
            if not thing.name == &#039;player&#039;:
                for handler in thing.brain.handlers:
                    if &#039;Fov&#039; in str(handler):
                        maps.append((thing.brain.fov_map, thing.brain.fov_map_to_world_coordinates))

        maps.append((player.thing.brain.fov_map, player.thing.brain.fov_map_to_world_coordinates))

        for tuple in maps:
            fov_map, fov_map_to_world_coordinates = tuple
            for cell in fov_map_to_world_coordinates:
                x, y = cell[1][0], cell[1][1]

                if (self.x_offset &lt;= x &lt;= self.x_offset+self.width and
                    self.y_offset &lt;= y &lt;= self.y_offset+self.height):
                        fov_map_x, fov_map_y = cell[0][0], cell[0][1]
                        i, j = self.world_xy_to_view_xy(x, y)

                        affect, cell_type = &#039;dark&#039;, &#039;wall&#039;

                        if libtcod.map_is_in_fov(fov_map, fov_map_x, fov_map_y):
                            affect = &#039;visible&#039;

                        if libtcod.map_is_walkable(fov_map, fov_map_x, fov_map_y):
                            cell_type = &#039;ground&#039;

                        color = player.ui.cell_colors[affect] * player.ui.cell_colors[cell_type]
                        libtcod.console_set_back(self.screen, i, j, color, libtcod.BKGND_SET)

                        if player.ui.chars[&#039;window&#039;] in game.world.level_state[y][x]:
                            libtcod.console_put_char(self.screen, i, j, libtcod.CHAR_DHLINE, libtcod.BKGND_NONE)

                        for thing in game.world.things:
                            if not thing.name == &#039;player&#039; and (thing.x, thing.y) == (x, y):
                                libtcod.console_put_char(self.screen, i, j, player.ui.chars[thing.name], libtcod.BKGND_NONE)

        libtcod.console_put_char(self.screen, self.focus_x, self.focus_y, player.ui.chars[player.thing.name], libtcod.BKGND_NONE)

class Input(object):
    def __init__(self):
        self.key = None
        self.keyboard = Keyboard()
        self.update_state = self.intro_update

    def update(self):
        self.update_state()

    def gameplay_update(self):
        if player.input.key:
            self.keyboard.update()

    def intro_update(self):
        self.key = libtcod.console_wait_for_keypress(True)

class Keyboard(object):
    def __init__(self):
        self.keycfg = {
                        &#039;k&#039; :   &#039;north&#039;,
                        &#039;j&#039; :   &#039;south&#039;,
                        &#039;h&#039; :   &#039;west&#039;,
                        &#039;l&#039; :   &#039;east&#039;}

    def get_key(self, key):
        if key.vk == libtcod.KEY_CHAR:
            return chr(key.c)
        else:
            return key.vk

    def update(self):
        key = self.get_key(player.input.key)
        if key in self.keycfg:
            command_name = self.keycfg[key]
            command = getattr(game.command, command_name)
            command(player.thing)

        player.input.key = None

#############################################
# game
#############################################

class Game(object):
    def __init__(self):
        self.world = World()
        self.command = Command()
        self.view = None
        self.input = None
        self.update_state = self.intro_update

        self.phases = [&#039;fast&#039;, &#039;normal&#039;, &#039;slow&#039;, &#039;quick&#039;, &#039;normal&#039;]
        self.phases_for = {
                            &#039;fast&#039; : (&#039;fast&#039;, &#039;normal&#039;, &#039;slow&#039;),
                            &#039;normal&#039; : (&#039;normal&#039;, &#039;slow&#039;),
                            &#039;slow&#039; : (&#039;normal&#039;),
                            &#039;quick&#039; : (&#039;normal&#039;, &#039;slow&#039;, &#039;quick&#039;),
                            &#039;fast+quick&#039; : (&#039;fast&#039;, &#039;normal&#039;, &#039;slow&#039;, &#039;quick&#039;),
                            &#039;fast+slow&#039; : (&#039;fast&#039;, &#039;normal&#039;),
                            &#039;quick+slow&#039; : (&#039;quick&#039;, &#039;normal&#039;),
                            &#039;fast+quick+slow&#039; : (&#039;fast&#039;, &#039;normal&#039;, &#039;quick&#039;)}

        self.phase = self.phases[0]
        self.phase_count = 0
        self.turns = 0

    def update(self):
        self.update_state()

    def intro_update(self):
        player.ui.draw()

        self.input.update()

        if os.path.isfile(&#039;centaur.sav&#039;):
            for thing in game.world.things: thing.brain.update()
        else:
            self.monster = Actor(&#039;monster&#039;, 20, 9, &#039;AI&#039;)
            self.lamp1 = Actor(&#039;lamp&#039;, 29, 7, &#039;Fov&#039;, &#039;Light&#039;)
            self.lamp1.brain.update()
            self.lamp2 = Actor(&#039;lamp&#039;, 41, 7, &#039;Fov&#039;, &#039;Light&#039;)
            self.lamp2.brain.update()
            self.lamp3 = Actor(&#039;lamp&#039;, 29, 12, &#039;Fov&#039;, &#039;Light&#039;)
            self.lamp3.brain.update()
            self.lamp4 = Actor(&#039;lamp&#039;, 41, 12, &#039;Fov&#039;, &#039;Light&#039;)
            self.lamp4.brain.update()

        player.thing.brain.update()
        player.view.update()

        self.update_state = self.gameplay_update
        player.ui.state = player.ui.gameplay_draw
        player.input.update_state = player.input.gameplay_update

        libtcod.console_clear(0)

    def gameplay_update(self):
        self.input.update()

        for thing in game.world.things:
            if game.phase in game.phases_for[thing.speed]:
                thing.update()

        self.view.update()

        self.phase_count += 1
        self.phase = self.phases[self.phase_count % 5]
        if game.phase in game.phases_for[&#039;normal&#039;]: self.turns += 1

#############################################
# get it started &amp; run
#############################################

if __name__ == &#039;__main__&#039;:
    game = Game()
    player = Awesome()

    while not libtcod.console_is_window_closed():
        game.update()
        player.ui.draw()

        if game.phase in game.phases_for[player.thing.speed]:
            player.input.key = libtcod.console_wait_for_keypress(True)
            if player.input.key.vk == libtcod.KEY_ESCAPE:
                file = open(&#039;centaur.sav&#039;, &#039;w&#039;)
                yaml.dump(game.world.things, file)
                file.close()
                break
</pre>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/342/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/342/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/342/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/342/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/342/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/342/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/342/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/342/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/342/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/342/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=342&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/07/02/new-fov-a-roguelike-in-python-10/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>

		<media:content url="http://kooneiform.files.wordpress.com/2009/07/newfov.png" medium="image">
			<media:title type="html">newfov</media:title>
		</media:content>
	</item>
		<item>
		<title>commands (a roguelike in Python #9)</title>
		<link>http://kooneiform.wordpress.com/2009/06/30/commands-a-roguelike-in-python-9/</link>
		<comments>http://kooneiform.wordpress.com/2009/06/30/commands-a-roguelike-in-python-9/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 00:19:35 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=340</guid>
		<description><![CDATA[I&#8217;ve been taking a stab at how to structure commands and effects of commands&#8230;Up to now I&#8217;ve done something simple where the command processing is all done in a function contained in the instance of Command. 
So the other day I started trying something like this:


class Command(object):
    def __init__(self):
    [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=340&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I&#8217;ve been taking a stab at how to structure commands and effects of commands&#8230;Up to now I&#8217;ve done something simple where the command processing is all done in a function contained in the instance of <strong>Command</strong>. </p>
<p>So the other day I started trying something like this:</p>
<pre name="code" class="python">

class Command(object):
    def __init__(self):
        self.rulebook = {
                        &#039;move&#039; : (&#039;cell has monster&#039;, &#039;cell is unwalkable&#039;)}

        self.rules = {
                        &#039;cell has monster&#039; : &#039;game.world.cell_has_monster(thing.x+dx, thing.y+dy)&#039;,
                        &#039;cell is unwalkable&#039; : &#039;game.world.cell_is_unwalkable(thing.x+dx, thing.y+dy)&#039;}

    def north(self, thing):
            self.move(thing, 0, -1)          

    def south(self, thing):
            self.move(thing, 0, 1)          

    def east(self, thing):
            self.move(thing, 1, 0)          

    def west(self, thing):
            self.move(thing, -1, 0)  

    def move(self, thing, dx, dy):
        if True in [eval(self.rules[rule]) for rule in self.rulebook[&#039;move&#039;]]:
            pass
        else:
            thing.x = thing.x + dx
            thing.y = thing.y + dy
</pre>
<p>The structure of commands/rules/rulebook is a little hazy at the moment, but the idea is that you write a command in the rulebook with a structure of keys. Each key in the rules points to a function, and some functions return True if it&#8217;s a simple check &#8212; if any are true, the command fails. However some functions could affect the game world, schedule events, create new objects, and so on as well. </p>
<p>This is basically the same as making these checks in the function of the command &#8212; calling the functions there. It&#8217;s a little easier for me to read the rulebook though to see what rules each command uses, and the list comprehension in the function of the command takes care of adding new rules. </p>
<p>I don&#8217;t really want to get too crazy with processing commands (such as calling before and after routines, or checking a bunch of generic &#8216;can I/am I&#8217; functions for each command), though it remains to be seen if the idea above will be easy enough to work with as I start adding commands and effects.  </p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/340/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/340/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/340/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/340/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/340/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/340/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/340/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/340/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/340/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/340/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=340&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/06/30/commands-a-roguelike-in-python-9/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>
	</item>
		<item>
		<title>better coloring (a roguelike in Python #8)</title>
		<link>http://kooneiform.wordpress.com/2009/06/26/better-coloring-a-roguelike-in-python-8/</link>
		<comments>http://kooneiform.wordpress.com/2009/06/26/better-coloring-a-roguelike-in-python-8/#comments</comments>
		<pubDate>Sat, 27 Jun 2009 07:00:30 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=336</guid>
		<description><![CDATA[I&#8217;ve started to experiment more with cell coloring. Up to now I&#8217;ve defined cell colors explicitly &#8212; if a cell is of the ground type and visible for example, it was a &#8216;visible ground&#8217; color, and similarly for a &#8216;dark wall&#8217; and so on. However I felt this wasn&#8217;t a very good way to do [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=336&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I&#8217;ve started to experiment more with cell coloring. Up to now I&#8217;ve defined cell colors explicitly &#8212; if a cell is of the ground type and visible for example, it was a &#8216;visible ground&#8217; color, and similarly for a &#8216;dark wall&#8217; and so on. However I felt this wasn&#8217;t a very good way to do colors, especially when you start mixing light and dark and other affects, like mist, smoke, fire, and so on. I don&#8217;t want to get too crazy here but I do like the look of colors and it&#8217;s one of libtcod&#8217;s strengths. </p>
<p>So after a few experiments I have a first pass at what I want to do. It looks like this:</p>
<p><img src="http://kooneiform.files.wordpress.com/2009/06/scalelight.png?w=300&#038;h=186" alt="scalelight" title="scalelight" width="300" height="186" class="alignleft size-medium wp-image-337" /></p>
<p>If you look at the samples.py file included with the libtcod Python wrapper you&#8217;ll see a similar thing going on in the field-of-view sample with the torchlight (though that is more sophisticated, using noise &#8212; I hope to do that soon). </p>
<p>My colors dictionary now looks like this:</p>
<pre name="code" class="python">

        self.cell_colors = {
                            &#039;wall&#039; : libtcod.Color(67, 65, 52),
                            &#039;ground&#039; : libtcod.Color(145, 140, 140),
                            &#039;dark&#039;: libtcod.Color(20, 20, 24),
                            &#039;lit&#039;: libtcod.Color(255, 255, 33),
                            &#039;visible&#039;: libtcod.Color(254, 254, 233)} 
</pre>
<p>And what I do now when figuring out the color of a cell is find the distance of that cell to the field-of-view origin (for example, the player) and use that distance as a scalar for the affect color. I should note that affects are colors like &#8216;dark&#8217;, &#8216;lit&#8217;, and so on. Also you don&#8217;t really need to find the distance, just the distance squared to get the appropriate scalar &#8212; this will save a bit on computation, or so I&#8217;ve heard. </p>
<p>In other words, the &#8216;visible&#8217; affect is most intense at its point of origin, and least at the maximum distance of the field-of-view radius. I think it does look better than using hard edges on affect boundaries. </p>
<p>This scalar is linear at the moment, so it creates a somewhat odd but not displeasing affect to my eyes. However I think I do want to randomize it a little with noise as done with the torchlight for samples.py.  </p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/336/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/336/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/336/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/336/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/336/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/336/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/336/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/336/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/336/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/336/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=336&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/06/26/better-coloring-a-roguelike-in-python-8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>

		<media:content url="http://kooneiform.files.wordpress.com/2009/06/scalelight.png?w=300" medium="image">
			<media:title type="html">scalelight</media:title>
		</media:content>
	</item>
		<item>
		<title>saving part one-and-a-half (a roguelike in Python…#7)</title>
		<link>http://kooneiform.wordpress.com/2009/06/24/saving-part-one-and-a-half-a-roguelike-in-python-7/</link>
		<comments>http://kooneiform.wordpress.com/2009/06/24/saving-part-one-and-a-half-a-roguelike-in-python-7/#comments</comments>
		<pubDate>Thu, 25 Jun 2009 06:11:11 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=333</guid>
		<description><![CDATA[I knew there was a reason I liked Python. In the last post I was going back and forth some on the readability of pickled files, so later tonight I looked a little closer at the PyYAML module. All I needed to do was change the pickle function calls to yaml function calls (as in, [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=333&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I knew there was a reason I liked Python. In the last post I was going back and forth some on the readability of pickled files, so later tonight I looked a little closer at the PyYAML module. All I needed to do was change the pickle function calls to yaml function calls (as in, change<strong> pickle.dump</strong> to <strong>yaml.dump</strong> &#8212; that&#8217;s it!) and the files now look like this:</p>
<pre name="code" class="python">

!!python/object:__main__.Actor
brain: &amp;amp;id001 !!python/object:__main__.Brain
  fov_map: 9773936
  handlers:
  - !!python/object:__main__.Fov
    brain: *id001
    fov_map: 9773936
    fov_radius: 8
    light_map: 9773904
  light_map: 9773904
  speed: normal
name: player
speed: normal
x: 32
y: 16
</pre>
<p>Of course I don&#8217;t know very much about the differences of pickle and yaml at this point (I knew I liked yaml from earlier exposure to it in some mud codebases) but this file format is at least looking much more to my taste!</p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/333/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/333/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/333/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/333/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/333/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/333/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/333/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/333/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/333/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/333/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=333&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/06/24/saving-part-one-and-a-half-a-roguelike-in-python-7/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>
	</item>
		<item>
		<title>saving part one (a roguelike in Python #7)</title>
		<link>http://kooneiform.wordpress.com/2009/06/24/saving-part-one-a-roguelike-in-python-7/</link>
		<comments>http://kooneiform.wordpress.com/2009/06/24/saving-part-one-a-roguelike-in-python-7/#comments</comments>
		<pubDate>Thu, 25 Jun 2009 01:19:49 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=331</guid>
		<description><![CDATA[I&#8217;ve been having a go with saving and loading in the Python demo. It may seem a little silly to do this before I have a game or for that matter, any content at all, but I&#8217;m following a bit of advice I read on rgrd and I&#8217;m glad I&#8217;m doing it&#8230;especially with loading the [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=331&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>I&#8217;ve been having a go with saving and loading in the Python demo. It may seem a little silly to do this before I have a game or for that matter, any content at all, but I&#8217;m following a bit of advice I read on rgrd and I&#8217;m glad I&#8217;m doing it&#8230;especially with loading the save state back in, it makes you think of how you structure the data to be saved in the program in the first place. </p>
<p>Luckily this first try is rather simple given Python&#8217;s <strong>pickle </strong>module. To save on exit I put this in the main loop:</p>
<pre name="code" class="python">

            if player.input.key.vk == libtcod.KEY_ESCAPE:
                file = open(&#039;centaur.sav&#039;, &#039;w&#039;)
                file2 = open(&#039;player.sav&#039;, &#039;w&#039;)
                pickle.dump(game.world.level_state, file)
                pickle.dump(player.thing, file2)
                file.close()
                file2.close()
                break
</pre>
<p>This saves the level data and the player object to two separate files, then closes the game &#8212; though <strong>game.world.level_state </strong>doesn&#8217;t actually change at all currently, the x,y position of the player object <em>does </em>change, and saving it allows you to start the demo, move the player, then restart the demo with the player in the new spot. </p>
<p>For a quick test I just did this in place of the creation of the player thing object;</p>
<pre name="code" class="python">

        if os.path.isfile(&#039;player.sav&#039;):
            print &#039;player file found&#039;
            file = open(&#039;player.sav&#039;, &#039;r&#039;)
            self.thing = pickle.load(file)
            game.world.things.append(self.thing)
            file.close()
        else:
            self.thing = Actor(&#039;player&#039;, 26, 16, Fov)
</pre>
<p>Note that I needed to add the player object back to the <strong>game.world.things</strong> list &#8212; otherwise, the player object brain&#8217;s update method will never be called (nor its field-of-view handler), as you aren&#8217;t adding it to the list by instancing the <strong>Actor </strong>class! Not doing this at first was a strange sight, as I moved and changed the player&#8217;s position without the screen updating. </p>
<p>As I mentioned, this test was a good thing to do early, as now I&#8217;m looking at how to arrange these saving and loading methods in the code. It seems like it would be a good idea to consolidate the saving and especially the loading as much as possible. Furthermore I&#8217;m not completely sold on the pickle module, as it creates files that look like this (in ASCII mode):</p>
<pre name="code" class="python">

ccopy_reg
_reconstructor
p1
(c__main__
Actor
p2
c__builtin__
object
p3
NtRp4
(dp5
S&#039;y&#039;
I6
sS&#039;x&#039;
I18
sS&#039;speed&#039;
</pre>
<p>I&#8217;m envisioning a case where I have a save file and I want to examine it without too much fuss. A simple text format would be much more readable. On the other hand, the pickle module is dead simple, and it <em>is</em> readable (and unpickle-able, of course). </p>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/331/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/331/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/331/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/331/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/331/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/331/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/331/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/331/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/331/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/331/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=331&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/06/24/saving-part-one-a-roguelike-in-python-7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>
	</item>
		<item>
		<title>updates in a minor key (a roguelike in Python #6)</title>
		<link>http://kooneiform.wordpress.com/2009/06/21/updates-in-a-minor-key-a-roguelike-in-python-6/</link>
		<comments>http://kooneiform.wordpress.com/2009/06/21/updates-in-a-minor-key-a-roguelike-in-python-6/#comments</comments>
		<pubDate>Sun, 21 Jun 2009 18:46:39 +0000</pubDate>
		<dc:creator>georgek</dc:creator>
				<category><![CDATA[roguelike]]></category>

		<guid isPermaLink="false">http://kooneiform.wordpress.com/?p=325</guid>
		<description><![CDATA[The Python demo has seen a lot of small changes as I attempt to drag it kicking and screaming into something a little more usable for a real game.

It&#8217;s at about 500 LOC at the moment and not yet incomprehensible to me &#8212; a good thing I think since I&#8217;m not working on it every [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=325&subd=kooneiform&ref=&feed=1" />]]></description>
			<content:encoded><![CDATA[<div class='snap_preview'><br /><p>The Python demo has seen a lot of small changes as I attempt to drag it kicking and screaming into something a little more usable for a real game.</p>
<p><img src="http://kooneiform.files.wordpress.com/2009/06/centaur011.png?w=350&#038;h=217" alt="centaur01" title="centaur01" width="350" height="217" class="alignleft size-full wp-image-327" /></p>
<p>It&#8217;s at about 500 LOC at the moment and not yet incomprehensible to me &#8212; a good thing I think since I&#8217;m not working on it every day &#8212;  and posted in full below, but I&#8217;ll try to highlight the bigger changes first. </p>
<p><span id="more-325"></span></p>
<p>One big change is I&#8217;m now using a different structure to hold the level information. I still have a hardcoded level, but when I initalize the world object I run the level through this function:</p>
<pre name="code" class="python">

    def level_make(self):
        self.level_state = []
        for line in self.level:
            line_data = []
            for cell in line:
                line_data.append(self.cell_types[cell])
            self.level_state.append(line_data)
</pre>
<p>The function <strong>level_make()</strong> uses the <strong>self.cell_types</strong> dictionary, which at the moment looks like this:</p>
<pre name="code" class="python">

        self.cell_types = {
                            &#039; &#039; : [&#039; &#039;, &#039;transparent&#039;, &#039;walkable&#039;],
                            &#039;#&#039; : [&#039;#&#039;],
                            &#039;=&#039; : [&#039;=&#039;, &#039;transparent&#039;]}
</pre>
<p>So instead of an array of strings as I had before with just the hardcoded map, now cell data is in an array of arrays. When I want to add a new cell type I&#8217;ll add it to the dictionary, and when I want to add new properties to the cells I can include it in the array. I don&#8217;t need to add a new property to each cell type &#8212; just the one where it counts. I hope this won&#8217;t get too confusing in the future. </p>
<p>I felt like I needed this structure to handle cases where the walkability or transparency of an individual cell was changed due to some area affect &#8212; it wouldn&#8217;t really do to just hope all ground cells always would be walkable.</p>
<p>As you would expect this level structure changes how you test for certain things &#8212; for example, in the field-of-view code, I now have code like this:</p>
<pre name="code" class="python">

if &#039;walkable&#039; in cell and &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.fov_map, j, i, True, True)
            elif &#039;walkable&#039; in cell:
                libtcod.map_set_properties(self.fov_map, j, i, False, True)
            elif &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.fov_map, j, i, True, False)
</pre>
<p>whereas before I tested the cell type directly. </p>
<p>In the screenshot you can see the colors have changed &#8212; it&#8217;s just temporary at the moment, but I&#8217;m trying to add better looking lights to the view. Currently I&#8217;m mocking it up with some ad hoc light code in the <strong>Fov </strong>class, with the goal of keeping track of a separate light map later down the road. </p>
<p>Of course, I can&#8217;t not point out that other little creature scampering around &#8212; the fearsome &#8216;?&#8217;.</p>
<p>Mr &#8216;?&#8217; is running on this behaviour handler:</p>
<pre name="code" class="python">

class AI(object):
    def __init__(self, thing, brain):
        self.choices = [&#039;up&#039;, &#039;down&#039;, &#039;left&#039;, &#039;right&#039;]
        self.thing = thing

    def update(self):
        choice = random.randint(0, 3)
        command_name = self.choices[choice]
        command_func = getattr(game.command, command_name)
        command_func(self.thing)
</pre>
<p>and I created it with an instancing of the <strong>Actor</strong> class in the setting up stages of the game object. It&#8217;s pretty dumb, as it chooses a direction randomly on each update. </p>
<p>One last small thing is the first attempt to hold all the game&#8217;s things in a list &#8212; the <strong>Actor </strong>class adds each new instance of itself to this list, for example. I&#8217;m unsure at the moment if the player should be in this list, though at the moment it is &#8212; I might change that to keep things simpler. </p>
<p>My next goal is to get some rudimentary saving and loading going. </p>
<p>Here&#8217;s the complete demo:</p>
<pre name="code" class="python">

#!/usr/bin/python

&#039;&#039;&#039;
libtcod OO python tutorial
This code modifies samples_py.py from libtcod 1.4.1. It shows a &#039;@&#039;
walking around a scrolling map with a source of light giving simple FOV.
It&#039;s in the public domain.
&#039;&#039;&#039;

#############################################
# imports
#############################################

import os, random

import libtcodpy as libtcod

#############################################
# be things
#############################################

class Thing(object):
    def __init__(self, name, x=-1, y=-1):
        self.name = name
        self.x = x
        self.y = y

    def update(self):
        pass

class World(Thing):
    def __init__(self):
        self.cell_types = {
                            &#039; &#039; : [&#039; &#039;, &#039;transparent&#039;, &#039;walkable&#039;],
                            &#039;#&#039; : [&#039;#&#039;],
                            &#039;=&#039; : [&#039;=&#039;, &#039;transparent&#039;]}

        super(World, self).__init__(&#039;world&#039;)
        self.walkable = [&#039; &#039;]

        self.things = []

        self.level_width = 46
        self.level_height = 31
        self.level = [
        &#039;##############################################&#039;,
        &#039;#######################      #################&#039;,
        &#039;#####################    #     ###############&#039;,
        &#039;######################  ###        ###########&#039;,
        &#039;##################      #####             ####&#039;,
        &#039;################       ########    ###### ####&#039;,
        &#039;###############      #################### ####&#039;,
        &#039;################    ######                  ##&#039;,
        &#039;########   #######  ######   #     #     #  ##&#039;,
        &#039;########   ######      ###                  ##&#039;,
        &#039;########                                    ##&#039;,
        &#039;####       ######      ###   #     #     #  ##&#039;,
        &#039;#### ###   ########## ####                  ##&#039;,
        &#039;#### ###   ##########   ###########=##########&#039;,
        &#039;#### ##################   #####          #####&#039;,
        &#039;#### ###             #### #####          #####&#039;,
        &#039;####           #     ####                #####&#039;,
        &#039;########       #     #### #####          #####&#039;,
        &#039;########       #####      ####################&#039;,
        &#039;##########   #################################&#039;,
        &#039;##########   #################################&#039;,
        &#039;###########  #################   #############&#039;,
        &#039;############  ###############   ##############&#039;,
        &#039;#############   ###########     ##############&#039;,
        &#039;###############  #########      ##############&#039;,
        &#039;################   ####                  #####&#039;,
        &#039;################   ###   ###########   #######&#039;,
        &#039;###############   ##   ##############  #######&#039;,
        &#039;#############   ###  #################   #####&#039;,
        &#039;#############       ##################       #&#039;,
        &#039;##############################################&#039;,
        ]

        self.level_make()

    def level_make(self):
        self.level_state = []
        for line in self.level:
            line_data = []
            for cell in line:
                line_data.append(self.cell_types[cell])
            self.level_state.append(line_data)

class Actor(Thing):
    def __init__(self, name, x=-1, y=-1, *handlers):
        super(Actor, self).__init__(name, x, y)

        self.brain = Brain()
        self.brain.add_handlers(self, *handlers)

        self.speed = self.brain.speed

        game.world.things.append(self)

    def update(self):
        self.brain.update()

#############################################
# do things
#############################################

class Brain(object):
    def __init__(self):
        self.handlers = []
        self.speed = &#039;normal&#039;

    def add_handlers(self, thing, *handlers):
        for handler in handlers:
            self.handlers.append(handler(thing, self))

    def remove_handlers(self, *handlers):
        for handler in handlers:
            self.handlers.remove(handler)

    def update(self):
        for handler in self.handlers:
            handler.update()

class Command(object):
    def __init__(self):
        pass

    def cell_has_monster(self, x, y):
        for thing in game.world.things:
            if (thing.x, thing.y) == (x, y):
                return True

    def up(self, thing):
        if not self.cell_has_monster(thing.x, thing.y-1) and &#039;walkable&#039; in game.world.level_state[thing.y-1][thing.x]:
            thing.y = thing.y - 1

    def down(self, thing):
        if not self.cell_has_monster(thing.x, thing.y+1) and &#039;walkable&#039; in game.world.level_state[thing.y+1][thing.x]:
            thing.y = thing.y + 1

    def right(self, thing):
        if not self.cell_has_monster(thing.x+1, thing.y) and &#039;walkable&#039; in game.world.level_state[thing.y][thing.x+1]:
            thing.x = thing.x + 1

    def left(self, thing):
        if not self.cell_has_monster(thing.x-1, thing.y) and &#039;walkable&#039; in game.world.level_state[thing.y][thing.x-1]:
            thing.x = thing.x - 1

class Fov(object):
    def __init__(self, thing, brain):
        self.brain = brain
        self.fov_map = None
        self.light_map = None
        self.fov_radius = 8

    def update(self):
        player.view.cells_get()

        self.fov_map = libtcod.map_new(player.view.width, player.view.height)
        self.light_map = libtcod.map_new(game.world.level_width, game.world.level_height)

        for cells in player.view.cells:
            x, y, j, i = cells
            cell = game.world.level_state[y][x]

            if &#039;walkable&#039; in cell and &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.fov_map, j, i, True, True)
            elif &#039;walkable&#039; in cell:
                libtcod.map_set_properties(self.fov_map, j, i, False, True)
            elif &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.fov_map, j, i, True, False)

            if &#039;walkable&#039; in cell and &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.light_map, j, i, True, True)
            elif &#039;walkable&#039; in cell:
                libtcod.map_set_properties(self.light_map, j, i, False, True)
            elif &#039;transparent&#039; in cell:
                libtcod.map_set_properties(self.light_map, j, i, True, False)

        libtcod.map_compute_fov(self.fov_map, player.view.focus_x, player.view.focus_y, self.fov_radius, True)
        libtcod.map_compute_fov(self.light_map, 15, 10, 2, True)

        self.brain.fov_map = self.fov_map
        self.brain.light_map = self.light_map

class AI(object):
    def __init__(self, thing, brain):
        self.choices = [&#039;up&#039;, &#039;down&#039;, &#039;left&#039;, &#039;right&#039;]
        self.thing = thing

    def update(self):
        choice = random.randint(0, 3)
        command_name = self.choices[choice]
        command_func = getattr(game.command, command_name)
        command_func(self.thing)

#############################################
# player
#############################################

class Awesome(object):
    def __init__(self):
        self.thing = Actor(&#039;player&#039;, 26, 16, Fov)
        self.ui = UI()
        self.view = View()
        self.input = Input()

        game.view = self.view
        game.input = self.input

class UI(object):
    def __init__(self):
        self.width = 40
        self.height = 22

        self.state = self.intro_draw

        self.chars = {
                        &#039;player&#039;    : &#039;@&#039;,
                        &#039;monster&#039;   : &#039;?&#039;,
                        &#039;ground&#039;    : &#039; &#039;,
                        &#039;wall&#039;      : &#039;#&#039;,
                        &#039;window&#039;    : &#039;=&#039;}

        self.cell_colors = {
                            &#039;dark wall&#039; : libtcod.Color(0, 0, 0),
                            &#039;visible wall&#039; : libtcod.Color(130, 110, 50),
                            &#039;dark ground&#039; : libtcod.Color(0, 0, 0),
                            &#039;visible ground&#039; : libtcod.Color(180, 180, 180),
                            &#039;lit wall&#039; : libtcod.Color(244, 199, 18),
                            &#039;lit ground&#039; : libtcod.Color(244, 231, 18)}

        font = os.path.join(&#039;fonts&#039;, &#039;arial12x12.png&#039;)
        libtcod.console_set_custom_font(
                                        font,
                                        libtcod.FONT_LAYOUT_TCOD |
                                        libtcod.FONT_TYPE_GREYSCALE,
                                        32,
                                        <img src='http://s.wordpress.com/wp-includes/images/smilies/face-cool.png' alt='8)' class='wp-smiley' /> 

        libtcod.console_init_root(
                                self.width,
                                self.height,
                                &#039;Python Demo&#039;,
                                False)
        libtcod.console_credits()

    def draw(self):
        self.state()

    def intro_draw(self):
        libtcod.console_clear(0)
        libtcod.console_set_foreground_color(0, libtcod.white)
        libtcod.console_print_center(0, 20, 8, libtcod.BKGND_NONE, &quot;A Python Demo&quot;)
        libtcod.console_print_center(0, 20, 11, libtcod.BKGND_NONE, &quot;press any key&quot;)
        libtcod.console_flush()

    def gameplay_draw(self):
        libtcod.console_clear(0)

        libtcod.console_set_foreground_color(0, libtcod.white)
        libtcod.console_print_left(0, 1, 1, libtcod.BKGND_NONE, &quot;HJKL move around&quot;)
        libtcod.console_print_left(0, 20, 3, libtcod.BKGND_NONE, &quot;speed: %s&quot; % player.thing.brain.speed)
        libtcod.console_print_left(0, 20, 4, libtcod.BKGND_NONE, &quot;game turn: %d&quot; % game.turns)
        libtcod.console_print_left(0, 20, 5, libtcod.BKGND_NONE, &quot;phase: %s&quot; % game.phase)
        libtcod.console_print_left(0, 20, 6, libtcod.BKGND_NONE, &quot;phase count: %d&quot; % game.phase_count)

        libtcod.console_set_foreground_color(0, libtcod.black)

        libtcod.console_blit(
                                player.view.screen,
                                0,
                                0,
                                player.view.width,
                                player.view.height,
                                0,
                                0,
                                self.height - player.view.height,
                                255)
        libtcod.console_flush()

class View(object):
    def __init__(self):
        self.width = 19
        self.height = 19
        self.focus_x = None
        self.focus_y = None
        self.style = self.scroll
        self.cells = []

        self.screen = libtcod.console_new(self.width, self.height)
        libtcod.console_set_foreground_color(self.screen, libtcod.black)

    def update(self):
        self.draw()

    def cells_get(self):
        self.cells = []

        self.style()

        for i in range(self.height):
            y = self.top_view_frame + i
            for j in range(self.width):
                x = self.left_view_frame + j
                self.cells.append((x, y, j, i))

    def scroll(self):
        self.left_view_frame = min(max(0, player.thing.x - self.width//2), game.world.level_width - self.width)
        self.top_view_frame = min(max(0, player.thing.y - self.height//2), game.world.level_height - self.height)

        self.x_left_offset = min(player.thing.x, self.width//2)
        self.x_right_offset = max(0, (self.width//2 - (game.world.level_width - player.thing.x) + (self.width % 2)))
        self.focus_x =  self.x_left_offset + self.x_right_offset

        self.y_top_offset = min(player.thing.y, self.height//2)
        self.y_bottom_offset = max(0, (self.height//2 - (game.world.level_height - player.thing.y) + (self.height % 2)))
        self.focus_y =  self.y_top_offset + self.y_bottom_offset 

    def draw(self):
        libtcod.console_clear(self.screen)

        for cells in self.cells:
            x, y, j, i = cells

            affect, cell = &#039;dark&#039;, &#039;wall&#039;
            if libtcod.map_is_in_fov(player.thing.brain.fov_map, j, i): affect = &#039;visible&#039;
            if libtcod.map_is_walkable(player.thing.brain.fov_map, j, i): cell = &#039;ground&#039;

            if (libtcod.map_is_in_fov(player.thing.brain.light_map, x, y) and
                libtcod.map_is_in_fov(player.thing.brain.fov_map, j, i)): affect = &#039;lit&#039;

            color = player.ui.cell_colors[&#039;%s %s&#039; % (affect, cell)]
            libtcod.console_set_back(self.screen, j, i, color, libtcod.BKGND_SET)    

            if player.ui.chars[&#039;window&#039;] in game.world.level_state[y][x]:
                libtcod.console_put_char(self.screen, j, i, libtcod.CHAR_DHLINE, libtcod.BKGND_NONE)

            for thing in game.world.things:
                if not thing.name == &#039;player&#039; and (thing.x, thing.y) == (x, y):
                    libtcod.console_put_char(self.screen, j, i, player.ui.chars[thing.name], libtcod.BKGND_NONE)

        libtcod.console_put_char(self.screen, self.focus_x, self.focus_y, player.ui.chars[player.thing.name], libtcod.BKGND_NONE)

class Input(object):
    def __init__(self):
        self.key = None
        self.keyboard = Keyboard()

    def intro_update(self):
        self.key = libtcod.console_wait_for_keypress(True)

    def update(self):
        if player.input.key:
            self.keyboard.update()

class Keyboard(object):
    def __init__(self):
        self.keycfg = {
                        &#039;k&#039; :   &#039;up&#039;,
                        &#039;j&#039; :   &#039;down&#039;,
                        &#039;h&#039; :   &#039;left&#039;,
                        &#039;l&#039; :   &#039;right&#039;}

    def get_key(self, key):
        if key.vk == libtcod.KEY_CHAR:
            return chr(key.c)
        else:
            return key.vk

    def update(self):
        key = self.get_key(player.input.key)
        if key in self.keycfg:
            command_name = self.keycfg[key]
            command_func = getattr(game.command, command_name)
            command_func(player.thing)

        player.input.key = None

#############################################
# game
#############################################

class Game(object):
    def __init__(self):
        self.world = World()
        self.command = Command()
        self.view = None
        self.input = None
        self.state = self.intro_update

        self.phases = [&#039;fast&#039;, &#039;normal&#039;, &#039;slow&#039;, &#039;quick&#039;, &#039;normal&#039;]
        self.phases_for = {
                            &#039;fast&#039; : (&#039;fast&#039;, &#039;normal&#039;, &#039;slow&#039;),
                            &#039;normal&#039; : (&#039;normal&#039;, &#039;slow&#039;),
                            &#039;slow&#039; : (&#039;normal&#039;),
                            &#039;quick&#039; : (&#039;normal&#039;, &#039;slow&#039;, &#039;quick&#039;),
                            &#039;fast+quick&#039; : (&#039;fast&#039;, &#039;normal&#039;, &#039;slow&#039;, &#039;quick&#039;),
                            &#039;fast+slow&#039; : (&#039;fast&#039;, &#039;normal&#039;),
                            &#039;quick+slow&#039; : (&#039;quick&#039;, &#039;normal&#039;),
                            &#039;fast+quick+slow&#039; : (&#039;fast&#039;, &#039;normal&#039;, &#039;quick&#039;)}

        self.phase = self.phases[0]
        self.phase_count = 0
        self.turns = 0

    def update(self):
        self.state()

    def intro_update(self):
        player.ui.draw()

        self.input.intro_update()

        game.state = game.gameplay_update
        player.ui.state = player.ui.gameplay_draw

        self.monster = Actor(&#039;monster&#039;, 30, 16, AI)

        player.thing.brain.update()
        player.view.update()

        libtcod.console_clear(0)

    def gameplay_update(self):
        self.input.update()

        for thing in game.world.things:
            if game.phase in game.phases_for[thing.speed]:
                thing.update()

        self.view.update()

        self.phase_count += 1
        self.phase = self.phases[self.phase_count % 5]
        if game.phase in game.phases_for[&#039;normal&#039;]: self.turns += 1

#############################################
# get it started &amp; run
#############################################

if __name__ == &#039;__main__&#039;:
    game = Game()
    player = Awesome()

    while not libtcod.console_is_window_closed():
        game.update()
        player.ui.draw()

        if game.phase in game.phases_for[player.thing.brain.speed]:
            player.input.key = libtcod.console_wait_for_keypress(True)
            if player.input.key.vk == libtcod.KEY_ESCAPE:
                break
</pre>
  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/kooneiform.wordpress.com/325/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/kooneiform.wordpress.com/325/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/kooneiform.wordpress.com/325/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/kooneiform.wordpress.com/325/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/kooneiform.wordpress.com/325/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/kooneiform.wordpress.com/325/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/kooneiform.wordpress.com/325/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/kooneiform.wordpress.com/325/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/kooneiform.wordpress.com/325/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/kooneiform.wordpress.com/325/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=kooneiform.wordpress.com&blog=108553&post=325&subd=kooneiform&ref=&feed=1" /></div>]]></content:encoded>
			<wfw:commentRss>http://kooneiform.wordpress.com/2009/06/21/updates-in-a-minor-key-a-roguelike-in-python-6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/edacead2fb1ad30f1bf7cf21d1bbea55?s=96&amp;d=identicon&amp;r=G" medium="image">
			<media:title type="html">kooneiform</media:title>
		</media:content>

		<media:content url="http://kooneiform.files.wordpress.com/2009/06/centaur011.png" medium="image">
			<media:title type="html">centaur01</media:title>
		</media:content>
	</item>
	</channel>
</rss>
