<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>The “Invent with Python” Blog</title>
	<atom:link href="http://inventwithpython.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://inventwithpython.com/blog</link>
	<description>News about Al Sweigart’s programming books.</description>
	<lastBuildDate>Fri, 10 May 2013 02:00:10 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>New Forums for the Books</title>
		<link>http://inventwithpython.com/blog/2013/05/09/new-forums-for-the-books/</link>
		<comments>http://inventwithpython.com/blog/2013/05/09/new-forums-for-the-books/#comments</comments>
		<pubDate>Thu, 09 May 2013 21:36:41 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1421</guid>
		<description><![CDATA[I&#8217;ve been meaning to add forums to the website where readers of the programming books could talk to each other and ask questions. I&#8217;ve held off on doing this for a while until I could figure out a way to handle spam. However, I&#8217;ve decided instead to set up a subreddit for all three books [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve been meaning to add forums to the website where readers of the programming books could talk to each other and ask questions. I&#8217;ve held off on doing this for a while until I could figure out a way to handle spam. However, I&#8217;ve decided instead to set up a subreddit for all three books (in effect, making Reddit the host for the forums).</p>
<p>Feel free to <a href="mailto:al@inventwithpython.com">email me</a> any questions you have as always, but these forums are also now available to use:</p>
<p><a href="http://www.reddit.com/r/inventwithpython/">/r/inventwithpython</a></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2013%2F05%2F09%2Fnew-forums-for-the-books%2F&amp;title=New%20Forums%20for%20the%20Books" id="wpa2a_2"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2013/05/09/new-forums-for-the-books/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Multithreaded Python Tutorial with the &#8220;Threadworms&#8221; Demo</title>
		<link>http://inventwithpython.com/blog/2013/04/22/multithreaded-python-tutorial-with-threadworms/</link>
		<comments>http://inventwithpython.com/blog/2013/04/22/multithreaded-python-tutorial-with-threadworms/#comments</comments>
		<pubDate>Mon, 22 Apr 2013 16:49:41 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1065</guid>
		<description><![CDATA[The code for this tutorial can be downloaded here: threadworms.py or from GitHub. This code works with Python 3 or Python 2, and you need Pygame installed as well in order to run it. Click the animated gif to view a larger version. This is a tutorial on threads and multithreaded programs in Python, aimed [...]]]></description>
				<content:encoded><![CDATA[<p>The code for this tutorial can be downloaded here: <a href="/threadworms.py">threadworms.py</a> or from <a href="https://github.com/asweigart/threadworms">GitHub</a>. This code works with <a href="">Python 3 or Python 2</a>, and you need <a href="">Pygame</a> installed as well in order to run it.</p>
<p><a href="/blog/images/threadworms_anim.gif"><img src="/blog/images/threadworms_anim_small.gif" /></p>
<p>Click the animated gif to view a larger version.</a></p>
<p>This is a tutorial on <a href="https://en.wikipedia.org/wiki/Thread_(computing)">threads</a> and multithreaded programs in Python, aimed at beginning programmers. It helps if you know the <a href="http://www.voidspace.org.uk/python/articles/OOP.shtml">basics of classes</a> (what they are, how you define methods, and that methods always have self as the first parameter, what subclasses (i.e. child classes) are and how a method can be inherited from a parent class, etc.) <a href="https://en.wikibooks.org/wiki/Python_Programming/Classes">Here&#8217;s a more in-depth classes tutorial.</a></p>
<p>The example used is a <a href="https://en.wikipedia.org/wiki/Snake_(video_game)">&#8220;Nibbles&#8221; or &#8220;Snake&#8221;</a> style clone that has multiple worms running around a grid-like field, with each worm running in a separate thread.<br />
<span id="more-1065"></span></p>
<h2>What are threads and why are they useful?</h2>
<p>You can skip this section if you already know what threads are and just want to see how to use them in Python.</p>
<p>When you run a normal Python program, the program execution starts at the first line and goes down line by line. Loops and function calls may cause the program execution to jump around, but it is fairly easy to see from the code which line will get executed next at any given point. You can put your finger on the first line of code in the .py file on the screen, and then trace through the next lines of code that are executed. This is <strong>single-threaded programming</strong>.</p>
<p>However, using multiple threads is like putting a second finger down on your code. Each finger still moves the same way, but now they are executing code simultaneously.</p>
<p>Actually, they aren&#8217;t executing simultaneously. Your two fingers are taking turns at which one executes code. Computers with multicore CPUs can actually run multiple instructions simultaneously, but there is a feature of Python programs called the <a href="https://en.wikipedia.org/wiki/Global_Interpreter_Lock">GIL (Global Interpreter Lock)</a> that limits a Python program to one core only.</p>
<p>The Python interpreter will run one thread for a while, and then pause it to run another thread for a while. But it does this so fast that it seems like they are running simultaneously.</p>
<p>You can start dozens or hundreds of threads in your program (that&#8217;s a lot of fingers). This doesn&#8217;t automatically make your programs dozens or hundreds of times faster though (all the threads are still sharing the same CPU) but it can make your program more efficient.</p>
<p>For example, say you write a function that will download a file full of names, then sorts the names, and then writes these names to a file on your computer. If there are hundreds of files your program needs to process, you would put a call to this function in a loop and it would handle each file serially: download, sort, write, download, sort, write, download, sort, write&#8230;</p>
<p>Each of these three steps use different resources on your computer: downloading uses the network connection, sorting uses the CPU, writing the file uses the hard drive. Also, there are tiny pauses within each of these steps. For example, the server you are downloading the file from may be slow and your computer&#8217;s Internet connection has bandwidth to spare.</p>
<p>It would be better if you could call this function hundreds of times in parallel by using one thread for each file. Not only would this make better use of your bandwidth, but if some files download sooner than others, the CPU can be used to sort them while the network connection continues to work. This makes more efficient use of your computer.</p>
<h2>What makes multithreaded programming tricky?</h2>
<p>Of course, in the above case, each thread is doing its own separate thing and doesn&#8217;t need to communicate or synchronize anything with the other threads. You could just write the simple single-threaded version of the download-sort-write program and run the program hundreds of times separately. (Though it might be a pain to type &#038; click each time to run the program each with a different file to download.)</p>
<p>Many multithreaded programs <em>share access to the same variables</em>, but this is where things can get  tricky.</p>
<p><img src="/blog/images/threadworms_tickets.jpg" /></p>
<p><span style="font-size: 0.6em;">Photo from <a href="https://secure.flickr.com/photos/bradmontgomery/6818868037/sizes/m/in/photostream/">Brad Montgomery</a>)</span></p>
<p>Here&#8217;s a common metaphor that is used: Say you have two robot ticket sellers. Their tasks are simple:</p>
<ol>
<li>Ask the customer which seat they want.</li>
<li>Check a list to see if the seat is available.</li>
<li>Get the ticket for that seat.</li>
<li>Cross that seat off the list.</li>
</ol>
<p>A customer asks Robot A for seat 42. Robot A checks that the seat is available from the list and finds that it is, so it grabs the ticket. But before Robot A can cross the seat off the list, Robot B is asked by a different customer for seat 42. Robot B checks the list and sees that the seat is still available, so it tries to grab the ticket for the seat. But Robot B can&#8217;t find the ticket for seat 42. <strong>THIS DOES NOT COMPUTE</strong>, and Robot B&#8217;s electronic brain explodes. Robot A then crosses seat 42 off of the list.</p>
<p>The above problem happens because although the two robots (or rather, two threads) are executing independently, they are both reading and modifying a shared list (or rather, a variable). Your programs can get very hard-to-fix bugs which are also difficult to even reproduce, since Python&#8217;s thread execution switching is <a href="https://en.wikipedia.org/wiki/Nondeterministic_algorithm">nondeterministic</a>, that is, done differently each time the program is run. We aren&#8217;t used to having the data in variables &#8220;magically&#8221; change from one line to the next just because a different thread was executed in between them.</p>
<p>When the execution switches from one thread to another, this is known as a <strong>context switch</strong>.</p>
<p>There is also the problem of <strong>deadlocks</strong>, which is commonly explained using the metaphor of the <a href="https://en.wikipedia.org/wiki/Dining_philosophers_problem">Dining Philosophers</a>. Five philosophers are sitting around a circular table eating spaghetti but require two forks to do so. There is one fork between each philosopher (for a total of five forks). The method the philosophers use to eat is this:</p>
<ol>
<li>Philosophize for a while.</li>
<li>Pick up the fork on your left.</li>
<li>Wait until the fork on your right is available.</li>
<li>Pick up the fork on your right.</li>
<li>Eat.</li>
<li>Put the forks down.</li>
<li>Go back to step 1.</li>
</ol>
<p><img src="/blog/images/threadworms_dining.gif" /></p>
<p>Aside from the fact that they&#8217;ll be sharing forks with their neighbors (eww), it seems like this method will work. But sooner or later everyone at the table will end up with the fork on their left in their hand and waiting for the fork on their right. But because everyone is holding on to the fork their neighbor is waiting for and won&#8217;t put it down until they&#8217;ve eaten, the philosophers are in a <em>deadlock state</em>. They will be holding forks in their left hand but never getting a fork in their right hand, so they never eat and never put down the fork in their left hand. The philosophers all starve to death (except for Voltaire who is actually a robot. Without spaghetti, his electronic brain explodes.)</p>
<p>There is also a similar situation called a <strong>livelock</strong>. This is when no work gets done because the threads are too generous at making a resource available. The best metaphor of this is when two people are walking towards each other down a hall. They step to the side to let the other person walk past, but end up blocking each other. So they both step back to the other side, but end up blocking each other again. They continue doing this until they starve/electronic-brain-explode.</p>
<p>There are a few other problems that can come up with multithreaded programming such as starvation (no seriously, that&#8217;s what it is called) and generally fall under the label of <a href="https://en.wikipedia.org/wiki/Concurrency_(computer_science)">&#8220;Concurrency&#8221;</a> in computer science. But we will only treat a simplified case.</p>
<h2>Locks</h2>
<p>One way to prevent bugs with multithreaded programming is by using locks. Before a thread reads or modifies a shared variable, it attempts to <strong>&#8220;acquire&#8221;</strong> a lock. If it can acquire the lock, the thread goes on to read or modify the variable. If the thread cannot acquire the lock, it waits until the lock becomes available.</p>
<p>When the thread is done with the shared variable, it will <strong>&#8220;release&#8221;</strong> the lock so that some other thread waiting for the lock can acquire it.</p>
<p>Going back to our robot ticket seller metaphor, this is like having a robot pick up the list (the list is a &#8220;lock&#8221;), and then reading it the ticket is available, grabbing the ticket, and then crossing out the seat on the list. When the robot puts the list back down, it is &#8220;releasing the lock&#8221;. If another robot needs to pick up the list but it is not there, it will wait until the list is available.</p>
<p>You can cause bugs by writing code that forgets to release a lock. This will cause a deadlock situation since the other threads will hang and do nothing while waiting for a lock to bereleased.</p>
<h2>Threads in Python</h2>
<p>Okay, let&#8217;s write a Python program that demonstrates how to use threads and locks. This program is based off of my &#8220;Snake&#8221; clone in <a href="http://inventwithpython.com/pygame/chapter6.html">Chapter 6</a> of my <a href="http://inventwithpython.com/pygame/chapters">Making Games with Python &#038; Pygame</a> book. Except instead of a worm running around eating apples, we&#8217;ll just have the worm running around the screen. And instead of just one worm, we will have multiple worms. Each worm will be controlled by a separate thread. The shared variable will have the data structure that represents which places on the screen (called &#8220;cells&#8221; in this program) are occupied by a worm. A worm cannot move forward to occupy a cell if another worm is already there. We will use locks to ensure that the worms don&#8217;t occupy the same cell as another worm.</p>
<p>The code for this tutorial can be downloaded here: <a href="/threadworms.py">threadworms.py</a> or from <a href="https://github.com/asweigart/threadworms">GitHub</a>. This code works with <a href="">Python 3 or Python 2</a>, and you need <a href="">Pygame</a> installed as well in order to run it.</p>
<p>Here&#8217;s a summary of the thread-related code in our threadworms.py program:</p>
<pre>import threading</pre>
<p>Python&#8217;s thread library is in a module named <code>threading</code>, so first import this module.</p>
<pre>GRID_LOCK = threading.Lock()</pre>
<p>The class Lock in the threading module has <code>acquire()</code> and <code>release()</code> methods. We will create a new <code>Lock</code> object and store it in a global variable named <code>GRID_LOCK</code>. (Since the state of the grid-like screen and which cells are occupied is stored in a global variable named <code>GRID</code>. The pun was unintended.)</p>
<pre># A global variable that the Worm threads check to see if they should exit.
WORMS_RUNNING = True</pre>
<p>Our <code>WORMS_RUNNING</code> global variable is regularly checked by the worm threads to see if they should quit. Calling <code>sys.exit()</code> will not stop the program because it only quits the thread that made the call. As long as there are other threads still running the program will continue. The main thread in our program (which handles the Pygame drawing and event handling) will set <code>WORMS_RUNNING</code> to <code>False</code> before it calls <code>pygame.quit()</code> and <code>sys.exit()</code>. The next time a thread checks <code>WORMS_RUNNING</code>, it will quit, until eventually the last thread quits and then the program terminates.</p>
<pre>class Worm(threading.Thread):
    def __ init__(self, name='Worm', maxsize=None, color=None, speed=None):
        threading.Thread.__init__(self)
        self.name = name</pre>
<p>The thread&#8217;s code must start from a class that is a child of the <code>Thread</code> class (which is in the <code>threading</code> module). Our <code>Thread</code> subclass will be named <code>Worm</code> since it controls You don&#8217;t need an <code>__init__()</code> function, but since our Worm classes uses one we need to call the <code>threading.Thread</code> class&#8217;s <code>__init__()</code> method first. Also optional is to override the name member. Our <code>__init__()</code> function uses the string <code>'Worm'</code> by default, but we can supply each thread with a unique name. Python will display the thread&#8217;s name in the error message if it crashes.</p>
<pre>GRID_LOCK.acquire()
# ...some code that reads or modifies GRID...
GRID_LOCK.release()</pre>
<p>Before we read or modify the value in the <code>GRID</code> variable, the thread&#8217;s code should attempt to acquire the lock. If the lock isn&#8217;t available, the method call to <code>acquire()</code> will not return and instead <strong>&#8220;block&#8221;</strong> until the lock becomes available. The thread is paused while this happens. This way, we know that the code after the <code>acquire()</code> call will only happen if the thread has acquired the lock.</p>
<p>Acquiring and releasing a lock around a bit of code ensures that another thread does not execute this code while the current thread is. This makes the code <strong>atomic</strong> because the code is always executed as a single unit.</p>
<p>After the thread&#8217;s code is done with the <code>GRID</code> variable, the lock can be released by calling the <code>release()</code> method.</p>
<pre>def run(self):
	# thread code goes here.</pre>
<p>A thread starts when the <code>Worm</code> class (which is a subclass of <code>threading.Thread</code>) has its <code>start()</code> method called. We don&#8217;t have to implement <code>start()</code> in the <code>Worm</code> class because it is inherited from the <code>threading.Thread</code> class. When the <code>start()</code> method is called, a new thread is created and the code inside the <code>run()</code> method is executed in this new thread. Do not call the <code>run()</code> method directly, as this won&#8217;t create the new thread.</p>
<p>This is important to know: to start the thread call the <code>start()</code> method, but the code that gets run in the new thread is in <code>run()</code>. We don&#8217;t have to define <code>start()</code> because it is inherited from <code>threading.Thread</code>. We do need to define <code>run()</code> since that is where our thread&#8217;s code will go.</p>
<p>When the <code>run()</code> method returns (or <code>sys.exit()</code> is called in the thread), the thread will be destroyed. All threads in a program must be destroyed before the program terminates. The program will still be running as long as there is one running thread.</p>
<p>So when <code>start()</code> is called, this is when you would place your second finger on the source code in <code>run()</code> to start tracing the code. Your first finger will continue tracing the code after the line that has the <code>start()</code> call.</p>
<h2>A Simple Multithreaded Example</h2>
<p>Before we go into the Threadworm code, let&#8217;s just look at a dead simple multithreaded program:</p>
<pre>import threading
TOTAL = 0
class CountThread(threading.Thread):
    def run(self):
        global TOTAL
        for i in range(100):
            TOTAL = TOTAL + 1
        print('%s\n' % (TOTAL))
a = CountThread()
b = CountThread()
a.start()
b.start()</pre>
<p>This program defines a new class called <code>CountThread</code>. When a <code>CountThread</code> object&#8217;s <code>start()</code> method is called, a new thread is created which will loop 100 times and increment the <code>TOTAL</code> global variable (which is shared between the variables) by <code>1</code> on each iteration of the loop.</p>
<p>Since we are creating two <code>CountThread</code> objects, whichever one finishes last should display <code>200</code>. Each thread increases <code>TOTAL</code> by <code>100</code> and there are two threads. When we run this program, that&#8217;s what we see:</p>
<pre>100
200</pre>
<p>Because the first number is <code>100</code>, we can tell that probably what happened is that one thread ran through the entire loop before a context switch happened.</p>
<p>However, if we change <code>range(100)</code> to <code>range(100000)</code>, we would expect the second number to be <code>200000</code>, since each thread increases <code>TOTAL</code> by <code>100000</code> and there are two threads. But when we run the program, something like this appears (your numbers may be slightly different):</p>
<pre>143294
149129</pre>
<p>That second number is not <code>200000</code>! It&#8217;s quite less than that actually. The reason this happened is because we did not use locks around the code the reads and modifies the <code>TOTAL</code> variable, which is shared among multiple threads.</p>
<p>Look at this line:</p>
<pre>TOTAL = TOTAL + 1</pre>
<p>If <code>TOTAL</code> was set to <code>99</code>, then you would expect <code>TOTAL + 1</code> to evaluate to <code>99 + 1</code> and then to <code>100</code>, and then <code>100</code> is stored as the new value in <code>TOTAL</code>. Then on the next iteration, <code>TOTAL + 1</code> would be <code>100 + 1</code> or <code>101</code>, which is stored as the new value in <code>TOTAL</code>.</p>
<p>But say when <code>TOTAL + 1</code> gets evaluated as <code>99 + 1</code>, the execution switches to the other thread, which is also about to execute the <code>TOTAL = TOTAL + 1</code> line. The value in <code>TOTAL</code> is still <code>99</code>, so <code>TOTAL + 1</code> in this second thread gets evaluated to <code>99 + 1</code>.</p>
<p>Then, another context switch happens back to the first thread where TO<code>TAL = 99 + 1</code> is in the middle of being executed. The integer <code>100</code> is assigned to <code>TOTAL</code>. Now execution switches back to the second thread again.</p>
<p>In this second thread, <code>TOTAL = 99 + 1</code> is about to be executed. Even though <code>TOTAL</code> is now <code>100</code>, the <code>TOTAL + 1</code> in this second thread has already been evaluated as <code>99 + 1</code>. So the second thread also ends up assigning the integer <code>100</code> to <code>TOTAL</code>. Even though this <code>TOTAL = TOTAL + 1</code> has been executed twice (once by each thread), the value in <code>TOTAL</code> has really only been incremented by <code>1</code>!</p>
<p>The problem is, the line of code <code>TOTAL = TOTAL + 1</code> is not atomic. The context switch can happen right in the middle of the line being executed. We need to use locks around this code to make this an atomic operation.</p>
<p>This new code fixes this problem:</p>
<pre>import threading
TOTAL = 0
MY_LOCK = threading.Lock()
class CountThread(threading.Thread):
    def run(self):
        global TOTAL
        for i in range(100000):
            MY_LOCK.acquire()
            TOTAL = TOTAL + 1
            MY_LOCK.release()
        print('%s\n' % (TOTAL))
a = CountThread()
b = CountThread()
a.start()
b.start()</pre>
<p>When we run this code, this is what is outputted (your first number might be a little different):</p>
<pre>199083
200000</pre>
<p>That the second number is <code>200000</code> tells us that the <code>TOTAL = TOTAL + 1</code> line was correctly executed each of the 200,000 times it was run.</p>
<h2>Explaining the Threadworms Program</h2>
<p>I&#8217;m going to use the <a href="/threadworms_nocomments.py">threadworms_nocomments.py</a> version of the program since it doesn&#8217;t have the very verbose comments in it. The line numbers have been included at the front of each line (they are not a part of the actual Python source code). I skip a lot of the commented sections because they are self-explanatory. You don&#8217;t really need to know Pygame to follow this code. Pygame is only responsible for creating the window and drawing the lines and rectangles on it.</p>
<p>One thing to know is that Pygame uses a tuple of three integers to represent colors. The integers each span from <code>0</code> to <code>255</code> and represent the RGB (Red-Green-Blue) value of the color. So <code>(0, 0, 0)</code> is black and <code>(255, 255, 255)</code> is white and <code>(255, 0, 0)</code> is red and <code>(255, 0, 255)</code> is purple, etc.</p>
<pre>  9. import random, pygame, sys, threading
 10. from pygame.locals import *
 11.
 12. # Setting up constants
 13. NUM_WORMS = 24  # the number of worms in the grid
 14. FPS = 30        # frames per second that the program runs
 15. CELL_SIZE = 20  # how many pixels wide and high each "cell" in the grid is
 16. CELLS_WIDE = 32 # how many cells wide the grid is
 17. CELLS_HIGH = 24 # how many cells high the grid is</pre>
<p>The top part of the code imports some modules our program needs and defines some constant values. Feel free to edit these constant values. Increasing or decreasing the <code>FPS</code> value doesn&#8217;t change how fast the worms run around, it just changes how often the screen updates. If you set this value very low, it looks like the worms are teleporting since they move multiple spaces in between screen updates.</p>
<p><code>CELL_SIZE</code> is how big each square on the screen&#8217;s grid is (in pixels). If you want to change the number of cells, modify the <code>CELLS_WIDE</code> and <code>CELLS_HIGH</code> constants.</p>
<pre> 20. GRID = []
 21. for x in range(CELLS_WIDE):
 22.     GRID.append([None] * CELLS_HIGH)</pre>
<p>The <code>GRID</code> global variable will contain data that tracks the state of the grid. It is a simple list of lists so that <code>GRID[x][y]</code> will refer to the cell at the X and Y coordinate. (In programming, the (0, 0) origin is at the top-left corner of the screen. X increases going to the right (just like in mathematics classes) but Y increases going down.)</p>
<p>If <code>GRID[x][y]</code> is set to None, then that cell is unoccupied. Otherwise, <code>GRID[x][y]</code> will be set to an RGB triplet. (This information is used when drawing the grid to the screen.)</p>
<pre> 24. GRID_LOCK = threading.Lock() # pun was not intended</pre>
<p>Line 24 creates a Lock object which our threads&#8217; code will acquire and release before reading or modifying <code>GRID</code>.</p>
<pre> 26. # Constants for some colors.
 27. #             R    G    B
 28. WHITE     = (255, 255, 255)
 29. BLACK     = (  0,   0,   0)
 30. DARKGRAY  = ( 40,  40,  40)
 31. BGCOLOR = BLACK             # color to use for the background of the grid
 32. GRID_LINES_COLOR = DARKGRAY # color to use for the lines of the grid
RGB tuples are kind of hard to read, so I usually set up some constants for them.
 33.
 34. # Calculate total pixels wide and high that the full window is
 35. WINDOWWIDTH = CELL_SIZE * CELLS_WIDE
 36. WINDOWHEIGHT = CELL_SIZE * CELLS_HIGH
 37.
 38. UP = 'up'
 39. DOWN = 'down'
 40. LEFT = 'left'
 41. RIGHT = 'right'</pre>
<p>Some more simple constants. I use constants like <code>DOWN</code> and <code>RIGHT</code> instead of strings like <code>'down'</code> and <code>'right'</code> because if I make a typo using constants (i.e. <code>DWON</code>) then Python will immediately crash with a <code>NameError</code> exception. This is much better than if I make a typo like <code>'dwon'</code> which won&#8217;t immediately crash the program will cause bugs later on, making it more difficult to track down.</p>
<pre> 43. HEAD = 0
 44. BUTT = -1 # negative indexes count from the end, so -1 will always be the last index</pre>
<p>Each worm will be represent by a list of dictionaries like <code>{'x': 42, 'y': 7}</code>. Each of these dictionaries represents a single body segment of the worm. The dictionary at the front of the list (at index <code>0</code>) is the head and the dictionary at the end of the list (at index <code>-1</code>, using Python&#8217;s nice negative indexing which begins counting from the end) is the butt of the worm.</p>
<p>(In computer science, &#8220;head&#8221; often refers to the first item in a queue or list, and &#8220;tail&#8221; refers to every item after the head. So I use &#8220;butt&#8221; to refer to just the last item. Also, I am silly.)</p>
<p><img src="/blog/images/threadworms_coordinates.png" /></p>
<p>The above worm would be represented with a list that looks like this: <code>[{'x': 7, 'y': 2}, {'x': 7, 'y': 3}, {'x': 7, 'y': 4}, {'x': 8, 'y': 4}, {'x': 9, 'y': 4}, {'x': 10, 'y': 4}, {'x': 11, 'y': 4}, {'x': 11, 'y': 3}, {'x': 11, 'y': 2}]</code></p>
<pre> 46. # A global variable that the Worm threads check to see if they should exit.
 47. WORMS_RUNNING = True</pre>
<p>As long as one thread is running, the program will continue to execute. The main thread that does the screen drawing will also detect when the user has clicked the close button on the window or pressed the Esc key, so it needs a way to tell the worm threads to quit. We will code the worm threads to constantly check <code>WORMS_RUNNING</code>. If <code>WORMS_RUNNING</code> is set to <code>False</code>, then the thread will terminate itself.</p>
<pre> 49. class Worm(threading.Thread): # "Thread" is a class in the "threading" module.
 50.     def __init__(self, name='Worm', maxsize=None, color=None, speed=None):</pre>
<p>Here&#8217;s our <code>Worm</code> class. It is a child class of the <code>threading.Thread</code> class. Each worm can have a name (which appears if the thread crashes, helping us identify which thread crashed), and a size, color, and speed. Default values are provided, but we can specify these ourselves if we want.</p>
<pre> 56.         threading.Thread.__init__(self) # since we are overriding the Thread class, we need to first call its __init__() method.</pre>
<p>Since we are overriding the <code>__init__()</code> method, we need to call the parent classes <code>__init__()</code> method so that it can initialize all the thread stuff. (We don&#8217;t need to know how it works, just remember to call it.)</p>
<pre> 57.
 58.         self.name = name
 59.
 60.         # Set the maxsize to the parameter, or to a random maxsize.
 61.         if maxsize is None:
 62.             self.maxsize = random.randint(4, 10)
 63.
 64.             # Have a small chance of a super long worm.
 65.             if random.randint(0,4) == 0:
 66.                 self.maxsize += random.randint(10, 20)
 67.         else:
 68.             self.maxsize = maxsize
 69.
 70.         # Set the color to the parameter, or to a random color.
 71.         if color is None:
 72.             self.color = (random.randint(60, 255), random.randint(60, 255), random.randint(60, 255))
 73.         else:
 74.             self.color = color
 75.
 76.         # Set the speed to the parameter, or to a random number.
 77.         if speed is None:
 78.             self.speed = random.randint(20, 500) # wait time before movements will be between 0.02 and 0.5 seconds
 79.         else:
 80.             self.speed = speed</pre>
<p>The above code sets up a worm with random values for the size, color, and speed unless specific values were specified for the parameters.</p>
<pre> 82.         GRID_LOCK.acquire() # block until this thread can acquire the lock
 83.
 84.         while True:
 85.             startx = random.randint(0, CELLS_WIDE - 1)
 86.             starty = random.randint(0, CELLS_HIGH - 1)
 87.             if GRID[startx][starty] is None:
 88.                 break # we've found an unoccupied cell in the grid
 89.
 90.         GRID[startx][starty] = self.color # modify the shared data structure
 91.
 92.         GRID_LOCK.release()</pre>
<p>We need to determine a random starting location for the worm. To make this easier, all worms begin with a length of one body segment and grow until they reach their full maximum size. But we need to make sure that the random location on the grid we come up with isn&#8217;t already occupied. This involves reading and modifying the <code>GRID</code> global variable, so we need to acquire and release the <code>GRID_LOCK</code> lock before doing this.</p>
<p>(As a side note, you might be wondering why we don&#8217;t have a &#8220;global GRID&#8221; line at the beginning of this method. <code>GRID</code> is a global variable and we are modifying it in this method, and without a <code>global</code> statement Python should consider this a local variable that just happens to have the same name as the <code>GRID</code> global variable. But if you look closer, we only change values inside the <code>GRID</code> list of lists, but never the value in <code>GRID</code> itself. That is, we have code that looks like &#8220;<code>GRID[startx][starty] = self.color</code>&#8221; but never &#8220;<code>GRID = someValue</code>&#8220;. Because we don&#8217;t actually modify <code>GRID</code> itself, Python considers the use of the variable name <code>GRID</code> in this method to refer to the global variable <code>GRID</code>.)</p>
<p>We keep looping until we&#8217;ve found an unoccupied cell, and then mark that cell as now occupied. After this, we are done reading and modifying <code>GRID</code> so we release the <code>GRID_LOCK</code> lock.</p>
<p>(Another side note, if there are no free cells on the grid, this loop will continue to loop forever and the thread will &#8220;hang&#8221;. Since the other threads will continue to run, you might not notice this problem. The new worm will not be created but the rest of the program continues to run normally. However, when you try to quit, since the hanging thread never gets to check <code>WORMS_RUNNING</code> to know it should quit and the program will refuse to terminate. You will have to force the program to shut down through your operating system. Just be sure not to add more worms than you have space for.)</p>
<pre> 96.         self.body = [{'x': startx, 'y': starty}]
 97.         self.direction = random.choice((UP, DOWN, LEFT, RIGHT))</pre>
<p>The starting body segment is added to the <code>body</code> member variable. The <code>body</code> member variable will be a list of all the locations of segments of the body. The direction that the worm is heading in is stored in the <code>direction</code> member variable.</p>
<p>Technically, since this worm right now only has one body segment that is both the first and last item in the list, the worm&#8217;s head is the same as its butt.</p>
<pre>100.     def run(self):
101.         while True:
102.             if not WORMS_RUNNING:
103.                 return # A thread terminates when run() returns.</pre>
<p>The <code>run()</code> method is the method that is called when the worm&#8217;s <code>start()</code> method is called. The code in <code>run()</code> is executed in a brand new thread. We will have an infinite loop that causes the worm to continuously move around the grid. The first thing we do on each iteration of the loop is check if <code>WORMS_RUNNING</code> is set to <code>False</code>, and if so, we should return from this method.</p>
<p>The thread will terminate itself if we either call <code>sys.exit()</code> from the thread or when the <code>run()</code> method returns.</p>
<pre>105.             # Randomly decide to change direction
106.             if random.randint(0, 100) &lt; 20: # 20% to change direction
107.                 self.direction = random.choice((UP, DOWN, LEFT, RIGHT))</pre>
<p>On each move, there&#8217;s a 20% chance that the worm randomly changes direction. (Although the new direction could be the same as the current direction. But I wanted to write this code out quickly.)</p>
<pre>109.             GRID_LOCK.acquire() # don't return (that is, block) until this thread can acquire the lock
110.
111.             nextx, nexty = self.getNextPosition()
112.             if nextx in (-1, CELLS_WIDE) or nexty in (-1, CELLS_HIGH) or GRID[nextx][nexty] is not None:
113.                 # The space the worm is heading towards is taken, so find a new direction.
114.                 self.direction = self.getNewDirection()
115.
116.                 if self.direction is None:
117.                     # No places to move, so try reversing our worm.
118.                     self.body.reverse() # Now the head is the butt and the butt is the head. Magic!
119.                     self.direction = self.getNewDirection()
120.
121.                 if self.direction is not None:
122.                     # It is possible to move in some direction, so reask for the next postion.
123.                     nextx, nexty = self.getNextPosition()
124.
125.             if self.direction is not None:
126.                 # Space on the grid is free, so move there.
127.                 GRID[nextx][nexty] = self.color # update the GRID state
128.                 self.body.insert(0, {'x': nextx, 'y': nexty}) # update this worm's own state
129.
130.                 # Check if we've grown too long, and cut off tail if we have.
131.                 # This gives the illusion of the worm moving.
132.                 if len(self.body) &gt; self.maxsize:
133.                     GRID[self.body[BUTT]['x']][self.body[BUTT]['y']] = None # update the GRID state
134.                     del self.body[BUTT] # update this worm's own state (heh heh, worm butt)
135.             else:
136.                 self.direction = random.choice((UP, DOWN, LEFT, RIGHT)) # can't move, so just do nothing for now but set a new random direction
137.
138.             GRID_LOCK.release()</pre>
<p>The above code handles moving the worm one space. Since this involves reading and modifying <code>GRID</code>, we need to acquire the <code>GRID_LOCK</code> lock first. Essentially, the worm will try to move one space in the direction that it&#8217;s direction member variable says. If this cell is beyond the border of the grid or is already occupied, then the worm will change its direction. If the worm is blocked on all sides, then the worm reverses itself so that the butt becomes the head and the head becomes the butt. If the worm still can&#8217;t move in any direction, then it will just stay put for now.</p>
<pre>140.             pygame.time.wait(self.speed)</pre>
<p>After the worm has moved one space (or at least tried to), we will put the thread to sleep. Pygame has a function called <code>wait()</code> that does the same thing as <code>time.sleep()</code>, except that the argument to <code>wait()</code> is in integer of milliseconds instead of seconds.</p>
<p>Pygame&#8217;s <code>pygame.time.wait()</code> and the Python Standard Library&#8217;s <code>time.time()</code> functions (and Pygame&#8217;s <code>tick()</code> method) are smart enough to tell the operating system to put the thread to sleep for a while and just run other threads instead. Of course, while the OS could interrupt our thread at any time to hand execution off to a different thread, calling <code>wait()</code> or <code>sleep()</code> is a way we can explicitly say, &#8220;Go ahead and don&#8217;t run this thread for X milliseconds.&#8221;</p>
<p>This wouldn&#8217;t happen if we have &#8220;wait&#8221; code like this:</p>
<pre>startOfWait = time.time()
while time.time() - 5 > startOfWait:
    pass # do nothing for 5 seconds</pre>
<p>The above code also implements &#8220;waiting&#8221;, but to the OS it looks like your thread is still executing code (even though this code is doing nothing but looping until 5 seconds has passed). This is inefficient, because time spent executing the above pointless loop is time that could have been spent executing other thread&#8217;s code.</p>
<p>Of course, if ALL worms&#8217; threads are sleeping, then the computer can know it can use the CPU to run other programs besides our Python Threadworms script.</p>
<pre>143.     def getNextPosition(self):
144.         # Figure out the x and y of where the worm's head would be next, based
145.         # on the current position of its "head" and direction member.
146.
147.         if self.direction == UP:
148.             nextx = self.body[HEAD]['x']
149.             nexty = self.body[HEAD]['y'] - 1
150.         elif self.direction == DOWN:
151.             nextx = self.body[HEAD]['x']
152.             nexty = self.body[HEAD]['y'] + 1
153.         elif self.direction == LEFT:
154.             nextx = self.body[HEAD]['x'] - 1
155.             nexty = self.body[HEAD]['y']
156.         elif self.direction == RIGHT:
157.             nextx = self.body[HEAD]['x'] + 1
158.             nexty = self.body[HEAD]['y']
159.         else:
160.             assert False, 'Bad value for self.direction: %s' % self.direction
161.
162.         return nextx, nexty</pre>
<p>The <code>getNextPosition()</code> figures out where the worm will go next given the position of its head and the direction it is going.</p>
<pre>165.     def getNewDirection(self):
166.         x = self.body[HEAD]['x'] # syntactic sugar, makes the code below more readable
167.         y = self.body[HEAD]['y']
168.
169.         # Compile a list of possible directions the worm can move.
170.         newDirection = []
171.         if y - 1 not in (-1, CELLS_HIGH) and GRID[x][y - 1] is None:
172.             newDirection.append(UP)
173.         if y + 1 not in (-1, CELLS_HIGH) and GRID[x][y + 1] is None:
174.             newDirection.append(DOWN)
175.         if x - 1 not in (-1, CELLS_WIDE) and GRID[x - 1][y] is None:
176.             newDirection.append(LEFT)
177.         if x + 1 not in (-1, CELLS_WIDE) and GRID[x + 1][y] is None:
178.             newDirection.append(RIGHT)
179.
180.         if newDirection == []:
181.             return None # None is returned when there are no possible ways for the worm to move.
182.
183.         return random.choice(newDirection)</pre>
<p>The <code>getNewDirection()</code> method returns a direction (one of the <code>UP</code>, <code>DOWN</code>, <code>LEFT</code>, or <code>RIGHT</code> strings) that is for an unoccupied cell within the grid. If there are no available cells the head could move towards, the method returns None.</p>
<pre>185. def main():
186.     global FPSCLOCK, DISPLAYSURF
187.
188.     # Draw some walls on the grid
189.     squares = """
190. ...........................
191. ...........................
192. ...........................
193. .H..H..EEE..L....L.....OO..
194. .H..H..E....L....L....O..O.
195. .HHHH..EE...L....L....O..O.
196. .H..H..E....L....L....O..O.
197. .H..H..EEE..LLL..LLL...OO..
198. ...........................
199. .W.....W...OO...RRR..MM.MM.
200. .W.....W..O..O..R.R..M.M.M.
201. .W..W..W..O..O..RR...M.M.M.
202. .W..W..W..O..O..R.R..M...M.
203. ..WW.WW....OO...R.R..M...M.
204. ...........................
205. ...........................
206. """
207.     #setGridSquares(squares)</pre>
<p>The <code>setGridSquares()</code> function can be used to draw static blocks on the grid by passing a multiline string. The period characters represent no change, a space character means &#8220;set this to be unoccupied&#8221; and any other character will represent a static block to place on the grid. You can uncomment line 207 if you want to see the &#8220;Hello worm&#8221; text written out in blocks.</p>
<pre>209.     # Pygame window set up.
210.     pygame.init()
211.     FPSCLOCK = pygame.time.Clock()
212.     DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
213.     pygame.display.set_caption('Threadworms')</pre>
<p>This is standard Pygame setup code to create a window for our program.</p>
<pre>215.     # Create the worm objects.
216.     worms = [] # a list that contains all the worm objects
217.     for i in range(NUM_WORMS):
218.         worms.append(Worm())
219.         worms[-1].start() # Start the worm code in its own thread.</pre>
<p>This code creates the <code>Worm</code> objects and then creates their threads by calling the <code>start()</code> method. The code in each worm&#8217;s <code>run()</code> method will begin executing in a separate thread at this point.</p>
<pre>221.     while True: # main game loop
222.         handleEvents()
223.         drawGrid()
224.
225.         pygame.display.update()
226.         FPSCLOCK.tick(FPS)</pre>
<p>The main game loop is pretty simple. The <code>handleEvents()</code> function will be checking if the user is terminating the program and the <code>drawGrid()</code> function will draw  the grid lines and cells to the screen. The <code>pygame.display.update()</code> function tells the window to update the screen, after which the <code>tick()</code> method will pause for however long is needed to achieve the framerate specified in <code>FPS</code>.</p>
<pre>229. def handleEvents():
230.     # The only event we need to handle in this program is when it terminates.
231.     global WORMS_RUNNING
232.
233.     for event in pygame.event.get(): # event handling loop
234.         if (event.type == QUIT) or (event.type == KEYDOWN and event.key == K_ESCAPE):
235.             WORMS_RUNNING = False # Setting this to False tells the Worm threads to exit.
236.             pygame.quit()
237.             sys.exit()</pre>
<p>The Pygame events can tell us when the user has pressed the Esc key or clicked on the close button for the window. In this case we want to set <code>WORMS_RUNNING</code> to <code>False</code> so that the threads will terminate themselves and then the main thread shuts down Pygame and exits.</p>
<pre>240. def drawGrid():
241.     # Draw the grid lines.
242.     DISPLAYSURF.fill(BGCOLOR)
243.     for x in range(0, WINDOWWIDTH, CELL_SIZE): # draw vertical lines
244.         pygame.draw.line(DISPLAYSURF, GRID_LINES_COLOR, (x, 0), (x, WINDOWHEIGHT))
245.     for y in range(0, WINDOWHEIGHT, CELL_SIZE): # draw horizontal lines
246.         pygame.draw.line(DISPLAYSURF, GRID_LINES_COLOR, (0, y), (WINDOWWIDTH, y))</pre>
<p>This code draws the screen based on the values in <code>GRID</code>. But first it draws the grid lines.</p>
<pre>248.     # The main thread that stays in the main loop (which calls drawGrid) also
249.     # needs to acquire the GRID_LOCK lock before modifying the GRID variable.
250.     GRID_LOCK.acquire()
251.
252.     for x in range(0, CELLS_WIDE):
253.         for y in range(0, CELLS_HIGH):
254.             if GRID[x][y] is None:
255.                 continue # No body segment at this cell to draw, so skip it
256.
257.             color = GRID[x][y] # modify the GRID data structure
258.
259.             # Draw the body segment on the screen
260.             darkerColor = (max(color[0] - 50, 0), max(color[1] - 50, 0), max(color[2] - 50, 0))
261.             pygame.draw.rect(DISPLAYSURF, darkerColor, (x * CELL_SIZE,     y * CELL_SIZE,     CELL_SIZE,     CELL_SIZE    ))
262.             pygame.draw.rect(DISPLAYSURF, color,       (x * CELL_SIZE + 4, y * CELL_SIZE + 4, CELL_SIZE - 8, CELL_SIZE - 8))
263.
264.     GRID_LOCK.release() # We're done messing with GRID, so release the lock.</pre>
<p>Because this code reads the <code>GRID</code> variable, we will first acquire the <code>GRID_LOCK</code> lock. If a cell is occupied (that is, it is set to an RGB tuple value inside the <code>GRID</code> variable) the code draws in the cell.</p>
<pre>267. def setGridSquares(squares, color=(192, 192, 192)):
268.     # squares is set to a value like:
269.     # """
270.     # ......
271.     # ...XX.
272.     # ...XX.
273.     # ......
274.     # """
275.
276.     squares = squares.split('\n')
277.     if squares[0] == '':
278.         del squares[0]
279.     if squares[-1] == '':
280.         del squares[-1]
281.
282.     GRID_LOCK.acquire()
283.     for y in range(min(len(squares), CELLS_HIGH)):
284.         for x in range(min(len(squares[y]), CELLS_WIDE)):
285.             if squares[y][x] == ' ':
286.                 GRID[x][y] = None
287.             elif squares[y][x] == '.':
288.                 pass
289.             else:
290.                 GRID[x][y] = color
291.     GRID_LOCK.release()</pre>
<p>The setGridSquares() can write static blocks to the grid and was explained previously.</p>
<pre>294. if __name__ == '__main__':
295.     main()</pre>
<p>The above is a Python trick. Instead of putting the main code in the global scope, we put it into a function named <code>main()</code> which is called from the bottom. This guarantees that all the functions have been defined before the code in <code>main()</code> runs. The <code>__name__</code> variable is only set to the string <code>'__main__'</code> if this program was run itself, as opposed to imported as a module by another program.</p>
<h2>Summary</h2>
<p>That&#8217;s it! Multithreaded programming is fairly simple to explain, but it can be tricky to understand how to get your own multithreaded programs to work correctly. The best way to learn is to practice by writing your own programs.</p>
<p>Actually, the way we have our code set up, even if we got rid of the locks it would still run almost perfectly. Nothing would crash, although there would sometimes be the case where two worms are approaching the same cell and end up both occupying it. They would then seemingly move through each other. Using locks ensures that only one worm can occupy a cell at any time.</p>
<p>Good luck!</p>
<p><img src="/blog/images/threadworms_screenshot.png" /></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2013%2F04%2F22%2Fmultithreaded-python-tutorial-with-threadworms%2F&amp;title=Multithreaded%20Python%20Tutorial%20with%20the%20%E2%80%9CThreadworms%E2%80%9D%20Demo" id="wpa2a_4"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2013/04/22/multithreaded-python-tutorial-with-threadworms/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>&#8220;Hacking Secret Ciphers with Python&#8221; Released</title>
		<link>http://inventwithpython.com/blog/2013/04/15/hacking-secret-ciphers-with-python-released/</link>
		<comments>http://inventwithpython.com/blog/2013/04/15/hacking-secret-ciphers-with-python-released/#comments</comments>
		<pubDate>Mon, 15 Apr 2013 17:30:34 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1369</guid>
		<description><![CDATA[My third book, Hacking Secret Ciphers with Python, is finished. It is free to download under a Creative Commons license, and available for purchase as a physical book on Amazon for $25 (which qualifies it for free shipping). This book is aimed at people who have no experience programming or with cryptography. The book goes [...]]]></description>
				<content:encoded><![CDATA[<p>My third book, <em>Hacking Secret Ciphers with Python</em>, is finished. It is free to download under a <a href="https://creativecommons.org/">Creative Commons license</a>, and available for purchase as a physical book on Amazon for $25 (which qualifies it for free shipping). This book is aimed at people who have no experience programming or with cryptography. The book goes through writing Python programs that not only implement several ciphers but also can hack these ciphers.</p>
<p><center><img src="http://inventwithpython.com/images/cover_hackingciphers_thumb.png" style="border: 1px solid black;" /></center></p>
<ul>
<li><a href="http://inventwithpython.com/HackingSecretCiphersWithPython.pdf">Download PDF of <em>Hacking Secret Ciphers with Python</em></a></li>
<li><a href="http://inventwithpython.com/hacking/chapters">View HTML version online.</a></li>
<li><a href="http://www.amazon.com/gp/product/1482614375/ref=as_li_qf_sp_asin_tl?ie=UTF8&#038;camp=1789&#038;creative=9325&#038;creativeASIN=1482614375&#038;linkCode=as2&#038;tag=playwithpyth-20">Buy a physical book on Amazon</a></li>
</ul>
<p>100% of the proceeds from the book sales will be donated to the <a href="https://www.eff.org/">Electronic Frontier Foundation</a>, <a href="https://creativecommons.org/">Creative Commons</a>, and <a href="https://www.torproject.org/">The Tor Project</a>.</p>
<p>Each chapter presents a new program and explains how the source code works. At the same time, various ciphers and cryptography concepts are explored. This book covers:</p>
<ul>
<li><a href="http://inventwithpython.com/hacking/chapter6.html">The Caesar cipher</a> <a href="http://inventwithpython.com/hacking/chapter7.html">(and how to hack it)</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter8.html">The Transposition cipher</a> <a href="http://inventwithpython.com/hacking/chapter13.html">(and how to hack it)</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter10.html">Writing programs to automatically test our programs</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter11.html">Encrypting files</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter12.html">Programmatically detecting English</a></li>
<li><a href="http://inventwithpython.com/hacking/">The Simple substitution cipher</a> <a href="http://inventwithpython.com/hacking/chapter16.html">(and how to hack it)</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter14.html">Modular arithmetic</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter14.html">The Multiplicative cipher</a> <a href="http://inventwithpython.com/hacking/">(and how to hack it)</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter15.html">The Affine cipher</a> <a href="http://inventwithpython.com/hacking/chapter16.html">(and how to hack it)</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter17.html">The Simple substitution cipher</a> <a href="http://inventwithpython.com/hacking/chapter18.html">(and how to hack it)</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter19.html">The Vigenere cipher</a> <a href="http://inventwithpython.com/hacking/chapter21.html">(and how to hack it)</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter20.html">Frequency analysis</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter22.html">One-time pads</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter23.html">Generating prime numbers that are hundreds of digits long</a></li>
<li><a href="http://inventwithpython.com/hacking/chapter24.html">Public key cryptography and the RSA cipher</a></li>
</ul>
<p>I first started this book two years ago. The Word doc calculates my editing time for the file at 85,860 minutes (not including the time to write and debug the programs). The book is over 400 pages long with over 1700 lines of code written for the programs (not including whitespace and comments).</p>
<p>The book&#8217;s website is at <a href="http://inventwithpython.com/hacking">http://inventwithpython.com/hacking</a></p>
<p>Feel free to email me questions or comments at <a href="mailto:al@inventwithpython.com">al@inventwithpython.com</a> or leave a comment below.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2013%2F04%2F15%2Fhacking-secret-ciphers-with-python-released%2F&amp;title=%E2%80%9CHacking%20Secret%20Ciphers%20with%20Python%E2%80%9D%20Released" id="wpa2a_6"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2013/04/15/hacking-secret-ciphers-with-python-released/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Taking Punches is a Sucker’s Game</title>
		<link>http://inventwithpython.com/blog/2013/03/25/taking-punches-is-a-suckers-game/</link>
		<comments>http://inventwithpython.com/blog/2013/03/25/taking-punches-is-a-suckers-game/#comments</comments>
		<pubDate>Mon, 25 Mar 2013 17:44:33 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1346</guid>
		<description><![CDATA[There are two camps when it comes to issues of harassment, bullying, diversity, and making inclusive communities, especially when the medium is online or in the tech industry. Without trying to let my own bias tilt my presentation, I think these two camps can be summed up with the following sayings: &#8220;Even if you think [...]]]></description>
				<content:encoded><![CDATA[<p>There are two camps when it comes to issues of harassment, bullying, diversity, and making inclusive communities, especially when the medium is online or in the tech industry. Without trying to let my own bias tilt my presentation, I think these two camps can be summed up with the following sayings:</p>
<p><center><strong>&#8220;Even if you think it&#8217;s a light jab, you shouldn&#8217;t throw punches.&#8221;</strong></p>
<p>and</p>
<p><strong>&#8220;I&#8217;ve taken worse and haven&#8217;t been bruised. You need to learn how to take a punch.&#8221;</strong></center></p>
<p>If you don&#8217;t think the low rates of women and minorities participating in the tech industry is fundamentally a problem that should be corrected, you can stop reading now and save yourself a few minutes. But software developers are in high demand. There&#8217;s a lot of great software out there waiting to be written. And while the current generation is much more technically literate than say, 30 years ago, raising the general level of technical expertise would blossom the possibilities for more sophisticated products and avenues of communication. We should get as many people across all demographics on board as possible.</p>
<p>I&#8217;m not going to talk solely about sexism itself in the tech industry (though at the front of my views on that are pointing out how widely the Internet blames Adria Richards for the dongle-joker&#8217;s firing rather than Play Haven, who did the actual firing.) But I also want to talk about inclusion and how to build community, and the attitudes that tear community down.</p>
<p><span id="more-1346"></span></p>
<p>Again, there are a lot of people who don&#8217;t view it as a problem, or at least if it is a problem it&#8217;s one that doesn&#8217;t deserve addressing. Most of the time these are people who are already in the community, and see any active outreach as unnecessary or even unfair. <em>&#8220;I didn&#8217;t need someone to hold my hand. If that&#8217;s what they need (or worse, demand) then they&#8217;re self-selecting themselves out of tech industry anyway.&#8221;</em></p>
<p>I simplify (perhaps oversimplify, but this just my casual commentary) the issue into the <em>&#8220;Don&#8217;t throw punches&#8221;</em> and <em>&#8220;Learn to take a punch&#8221;</em> camps but they encompass a lot of attitudes that I&#8217;ve seen before (especially in the last couple of days): <em>&#8220;That wasn&#8217;t professional.&#8221; &#8220;Lighten up, it&#8217;s just a joke.&#8221; &#8220;Not cool.&#8221; &#8220;It was a private conversation anyway.&#8221;</em></p>
<p>I simplify the issue into two camps, and I completely, utterly place my chips in the former, not the later. <strong>Being able to take a punch may be sufficient to get along in a heterogeneous community, but discouraging people from throwing them is absolutely necessary for holding one together.</strong></p>
<p><center>* * *</center></p>
<p>&#8220;I&#8217;ve taken worse and haven&#8217;t been bruised.&#8221;</p>
<p>The comment in poor taste, the slide with the technically-not-nude porn image, the staring, the unsolicited proposition, the following, the boys locker room humor, the unconsented photographing, the hired booth staffer in the skintight outfit meant to arouse &#8220;the crowd&#8221; (i.e. heterosexual men), the &#8220;not a <em>real</em> developer&#8221; comments, the flier with outright sexist garbage anonymously posted up on the public wall, the antagonism you get when you tear down the flier.</p>
<p>As common as these things are at tech and geek conferences (and they are common), they aren&#8217;t things people call the cops over. Often the police aren&#8217;t even brought in when it escalates to stalking or an arm-grab. But they are more than enough to make people give up on participating. Since it doesn&#8217;t involve law enforcement or courts, many people treat these things lightly. The common attitude (and it is common) is: If there&#8217;s a problem, it&#8217;s because the person bringing up the problem is creating it.</p>
<p>This trivializing of incidents as <em>&#8220;you&#8217;re too sensitive&#8221;</em> sends a clear message: <em>&#8220;That&#8217;s your problem. You&#8217;re on your own.&#8221;</em></p>
<p>The later camp either hasn&#8217;t experienced this targeted at them or if they have, they&#8217;ve accepted it as a small part of the price of admission. This isn&#8217;t welcoming, it&#8217;s hazing. Hazing is neat little psychological trick: by undergoing some humiliation or pain, you can get someone to ascribe value to a community (after all, if it wasn&#8217;t valuable, why did they take those punches for it?) It further also makes them want (or at least allow) to continue hazing new members: if you had to do it, then why should they get a free pass?</p>
<p>I could criticize this as a race to the bottom, but there&#8217;s no bottom. Outside of something that brings in the police, there is no bound to how bad <strong>accepted</strong> behavior can get when &#8220;learn to take a punch&#8221; is your cover-all. And people can get very creative when it comes to socially-cruel ostracism when they feel entitled to it.</p>
<p>At the core, I see the <em>&#8220;don&#8217;t be so sensitive&#8221;</em> attitude as the well-intentioned cousin of trolling: It devolves into a staring contest to see who will break first. To admit offense is to lose. To win you will need either indifference or the strength to suffer in silence. <strong>That&#8217;s a sucker&#8217;s game and either way, you will live with the knowledge that tomorrow you will be dealing with the same bullshit that you had to deal with today, because if you speak up it will only get worse.</strong> Especially if you&#8217;re a woman or minority.</p>
<p>This isn&#8217;t the foundation you can attract and build a community on and it&#8217;s not something anyone who wants to grow a community can ignore. (I point to Anil Dash&#8217;s blog post <a href="http://dashes.com/anil/2011/07/if-your-websites-full-of-assholes-its-your-fault.html">&#8220;If Your Website&#8217;s Full of Assholes, It&#8217;s Your Fault&#8221;</a> as a great take on this.) Learning to lighten up, to not be so easily offended, to not make a big deal out of it, or to take a punch is never the raison d&#8217;etre of any community. People don&#8217;t pay conference registration fees to test their emotional mettle for abuse. (If PyCon&#8217;s increased attendance and rates of women&#8217;s participation demonstrate anything, it&#8217;s that people pay money for the exact opposite.)</p>
<p>Having resilience to not let insensitive comments or outright abuse drive you away is a good thing to have. It&#8217;s admirable to be shatterproof. But it&#8217;s callous to demand it from others. I thank the PyCon board and volunteers for putting together a great conference and a great community. They&#8217;ve had the guts to stick to their guns about not just having a <a href="https://us.pycon.org/2013/about/code-of-conduct/">Code of Conduct</a> (the mere existence of which hasn&#8217;t been without resistance) but also <a href="https://us.pycon.org/2013/about/code-of-conduct/harassment-incidents-staff/">the spine to enforce it</a> and <a href="https://us.pycon.org/2013/about/code-of-conduct/harassment-incidents/">provide guidance for attendees to do the same</a>. They&#8217;ve had to take quite a few punches themselves for this (and not just in the last few days), but I&#8217;m grateful that they do and that they push to ensure others won&#8217;t have to.</p>
<p><em>(If you&#8217;re interested in more on this topic, I recommend John Scalzi&#8217;s blog posts <a href="http://whatever.scalzi.com/2011/08/31/the-sort-of-crap-i-dont-get/">&#8220;The Sort of Crap I Don&#8217;t Get&#8221;</a> and <a href="http://whatever.scalzi.com/2012/05/15/straight-white-male-the-lowest-difficulty-setting-there-is/">&#8220;Straight White Male: The Lowest Difficulty Setting There Is&#8221;</a> (and the rest of his blog in general) and the <a href="http://geekfeminism.wikia.com/wiki/Resources_for_allies">Geek Feminism Wiki</a>.)</em></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2013%2F03%2F25%2Ftaking-punches-is-a-suckers-game%2F&amp;title=Taking%20Punches%20is%20a%20Sucker%E2%80%99s%20Game" id="wpa2a_8"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2013/03/25/taking-punches-is-a-suckers-game/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>What Professional Games Use Pygame?</title>
		<link>http://inventwithpython.com/blog/2013/02/19/what-professional-games-use-pygame/</link>
		<comments>http://inventwithpython.com/blog/2013/02/19/what-professional-games-use-pygame/#comments</comments>
		<pubDate>Tue, 19 Feb 2013 17:19:59 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1303</guid>
		<description><![CDATA[This came up as a question on Stack Overflow a while ago. While Python is noted for its use in many games, here&#8217;s a list of professional-quality games that use Pygame: Dangerous High School Girls in Trouble is a 2D RPG by Keith Nemitz that was nominated for a Writers Guild Award, won &#8220;Most Innovative [...]]]></description>
				<content:encoded><![CDATA[<p>This came up as <a href="http://stackoverflow.com/questions/432016/what-commercial-games-have-been-written-with-pygame">a question on Stack Overflow</a> a while ago. While Python is noted for its use in many games, here&#8217;s a list of professional-quality games that use Pygame:</p>
<table>
<tr>
<td><strong><a href="http://www.mousechief.com/dhsg/index.html"><img src="/blog/images/pro_pygame_dangerous.png" style="margin: 4px; float: left" />Dangerous High School Girls in Trouble</a></strong> is a 2D RPG by Keith Nemitz that was nominated for a Writers Guild Award, won &#8220;Most Innovative Game&#8221; by the Casual Games Association, and was also selected as a finalist for the 2008 IndieCade Festival of Independent Games. It is <a href="http://store.steampowered.com/app/27400/">available for purchase off of Steam</a>. <a href="https://www.youtube.com/watch?v=PuiaAOUtzSQ">Gameplay video</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="http://ahatestory.com/"><img src="/blog/images/pro_pygame_analog.png" style="margin: 4px; float: left" />Analogue: A Hate Story</a></strong> is a visual novel-style game that was a 2012 IndieCade Finalist. As of December 2012, Analogue has sold at 40,000 copies. <a href="http://store.steampowered.com/app/209370/?snr=1_200_200_254_tab-NewReleasesFilteredDLC_2">Steam store link (with videos).</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="http://unityofcommand.net/"><img src="/blog/images/pro_pygame_unity.png" style="margin: 4px; float: left" />Unity of Command</a></strong> is a turn-based, operational-level wargame that covers the entire 1942/43 Stalingrad Campaign on the Eastern Front. <a href="https://www.youtube.com/watch?v=c1hjpHkJ9QY">Trailer video.</a> <a href="http://store.steampowered.com/app/218090/">Available on Steam.</td>
</tr>
</table>
<p><span id="more-1303"></span></p>
<table>
<tr>
<td><strong><a href="http://fretsonfire.sourceforge.net/"><img src="/blog/images/pro_pygame_frets.png" style="margin: 4px; float: left" />Frets on Fire</a></strong> is an open source Guitar Hero clone. <a href="https://www.youtube.com/watch?v=LWowbQkuwwo">Gameplay video</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="http://p4.hostingprod.com/@mousechief.com/7GS/by_Mousechief.html"><img src="/blog/images/pro_pygame_7grandsteps2.png" style="margin: 4px; float: left" />7 Grand Steps</a></strong> is a board-like puzzle game from Keith Nemitz of Mousechief and is a 2013 Independent Games Festival finalist. <a href="http://www.youtube.com/watch?v=gEH3IjFVcY0">Trailer video</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="http://www.metin2.com/"><img src="/blog/images/pro_pygame_metin2.png" style="margin: 4px; float: left" />Metin2</a></strong> is an Oriental-themed action MMORPG. <a href="https://www.youtube.com/watch?v=TX_hByMsUkU">Trailer video</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="http://www.galcon.com/classic/index.html"><img src="/blog/images/pro_pygame_galcon2.png" style="margin: 4px; float: left" />Galcon 2</a></strong> is a high-paced space shooter game. <a href="https://www.youtube.com/watch?v=r-z-Pd9RcGM">Gameplay video</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="https://en.wikipedia.org/wiki/SolarWolf"><img src="/blog/images/pro_pygame_solarwolf.png" style="margin: 4px; float: left" />SolarWolf</a></strong> is an updated clone of the 1983 arcade shooter Solar Fox. It is included with several Linux distributions. <a href="http://pygame.org/shredwheat/solarwolf/">Download links</a> <a href="https://www.youtube.com/watch?v=KUJqHji0MRA">Gameplay video</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="http://www.deduy.com"><img src="/blog/images/pro_pygame_detective.png" style="margin: 4px; float: left" />División Especial de Detectives</a></strong> is an original puzzle game in Spanish that explores the culture and history of Uruguay. <a href="https://www.youtube.com/watch?v=QerZ4qiIoI0">Gameplay video</a></td>
</tr>
</table>
<table>
<tr>
<td><strong><a href="http://www.mousechief.com/witchs_yarn/index.html"><img src="/blog/images/pro_pygame_witchsyarn.png" style="margin: 4px; float: left" />The Witch&#8217;s Yarn</a></strong> is an interactive story game from Mousechief. <a href="https://www.youtube.com/watch?v=teNXZZJ9qTY">Gameplay video</a></td>
</tr>
</table>
<p>While not a game, someone has mentioned that the Minecraft editor <a href="http://www.mcedit.net/">MCEdit</a> used Pygame. <a href="https://www.youtube.com/watch?v=o5FxiJuaMPo">MCEdit tutorial video</a></p>
<p>Feel free to add any other professional-quality games that have been made using Pygame in the comments section!</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2013%2F02%2F19%2Fwhat-professional-games-use-pygame%2F&amp;title=What%20Professional%20Games%20Use%20Pygame%3F" id="wpa2a_10"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2013/02/19/what-professional-games-use-pygame/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Why I Recommend Against Hackety Hack</title>
		<link>http://inventwithpython.com/blog/2012/12/27/why-i-recommend-against-hackety-hack/</link>
		<comments>http://inventwithpython.com/blog/2012/12/27/why-i-recommend-against-hackety-hack/#comments</comments>
		<pubDate>Thu, 27 Dec 2012 18:47:31 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1250</guid>
		<description><![CDATA[Disclosure: I sell a couple books that teach programming to kids (and are free to download). I don&#8217;t personally view them as being in competition with Hackety Hack, but someone else might. Hackety Hack was a project originally started by _why the lucky stiff to teach kids programming in Ruby. It often comes up in [...]]]></description>
				<content:encoded><![CDATA[<p><img src="/blog/images/hackety-mouse-hand.png" style="float: right;"/></p>
<p><em>Disclosure: I sell a couple books that teach programming to kids (and are <a href="http://inventwithpython.com">free to download</a>). I don&#8217;t personally view them as being in competition with Hackety Hack, but someone else might.</em></p>
<p><a href="http://hackety.com/">Hackety Hack</a> was a project originally started by <a href="https://en.wikipedia.org/wiki/Why_the_lucky_stiff">_why the lucky stiff</a> to teach kids programming in Ruby. It often comes up in &#8220;I want to teach my kid programming&#8221; forum threads. So I downloaded Hackety Hack and decided to give it a try.</p>
<p><strong>I found Hackety Hack to be frustrating and was very unimpressed with it, and do not recommend it as a way to teach programming to a beginner.</p>
<p>It&#8217;s kind of crap.</strong></p>
<p>Its main flaws are:</p>
<p><span id="more-1250"></span></p>
<ul>
<li>It has little actual content. It is very short and covers programming neither widely nor deeply.</li>
<li>The presentation style is sort of a &#8220;wall of text&#8221; format, where programming concepts almost never have more than one example.</li>
<li>There are NO error messages or feedback at all if you make a typo in your code. (Pressing Ctrl-/ does not bring up an error console in the Windows version like the instructions say it does.)</li>
<li>And at least for the Windows version, it crashes a lot. A LOT. I had to restart it at least a half dozen times before making it through all the way. It even crashed twice while I was writing this blog post. (<a href="http://www.reddit.com/r/ruby/comments/m1hcu/environment_for_absolute_beginners_besides/">Apparently this is also a problem with the OS X version as well.</a>)</li>
<li>The editor doesn&#8217;t have basic features: you can highlight text, but there&#8217;s no point because you can&#8217;t copy or paste. There is no Select All. There is no Undo. Pressing Ctrl-C, Ctrl-V, Ctrl-Z, or Ctrl-A just inserts a weird Unicode error character into the text. (There&#8217;s nothing like highlighting a bunch of code, pressing Ctrl-C to overwrite it all with a Unicode character, and then pressing Ctrl-Z to just get another Unicode character, realizing that all your code is permanently gone.)
</ul>
<p>There&#8217;s a few other minor gripes I have with it: The upload feature doesn&#8217;t work at all (even though you&#8217;ll get a &#8220;Uploaded!&#8221; confirmation). On the &#8220;Your Preferences&#8221; page, the link to set up an account just takes you to the <a href="http://hackety.com/">http://hackety.com/</a> home page, instead of specifically to the <a href="http://hackety.com/users/sign_up">http://hackety.com/users/sign_up</a> page (though there isn&#8217;t much at all to the online content anyway). It doesn&#8217;t remember the accounts that get signed in, making switching between multiple accounts (like in a classroom setting) a pain. After uploading, the menu doesn&#8217;t give you the URL where I can find your uploaded program online. (But again, the upload functionality doesn&#8217;t work.)</p>
<p>In the file editor, if you don&#8217;t save your program before running it, clicking Run simply does nothing (there&#8217;s no, &#8220;You need to save first.&#8221; message). Your program won&#8217;t run until you remember to first click Save.</p>
<p><strong>Hackety Hack seems like it hasn&#8217;t had a lot of time spent polishing it or paying attention to the details.</strong> (For example, there&#8217;s a couple grammar and spelling mistakes in the text.) And again, there really isn&#8217;t all that much to Hackety Hack. It is short. Very short. There are only four lessons:</p>
<ul>
<li><strong>Lesson 1: A Tour of Hackety Hack.</strong> This goes over the basics of the buttons and menus in Hackety Hack itself. Sure, fine. Every piece of software needs this, and it&#8217;s adequate. But it could have easily been built into the first lesson (like &#8220;tutorial levels&#8221; in games).</li>
<li><strong>Lesson 2: Basic Programming.</strong> This covers some turtle-related coding. There&#8217;s a &#8220;Programming is like making a to-do list.&#8221; comparison. The user sets the background color. The user draws with a turtle, changing the pen color. The user then draws a box using a simple loop. It&#8217;s okay that this is boring; it&#8217;s the beginning stuff. But there&#8217;s nothing BUT the boring stuff. The lesson ends after the loop-box-drawing part. There&#8217;s nothing more.</li>
<li><strong>Lesson 3: Basic Ruby.</strong> There&#8217;s the traditional &#8220;Print hello world&#8221; example, using &#8220;alert&#8221; (which, oddly, is actually more a JavaScript style thing). It covers basic math operations, though the word &#8220;operator&#8221; is never mentioned. There are brief explanations of string replication, objects &#038; data types, variables &#038; assignment. It has an &#8220;ask&#8221; function for user input (again, this is not part of standard Ruby). It explains if/else statements. But nothing is placed in context of making programs, until the very end which has a meh Guess The Number game (even more meh than usual: you only get one guess and the number is not random but instead pre-programmed).</li>
<li><strong>Lesson 4: Basic Shoes.</strong> Summary: Basic layout management for the window, adding ui buttons that run code when pressed, displaying images, placing a text field. That&#8217;s it. And I don&#8217;t recommend using a new and unsupported module like Shoes instead of just learning JavaScript &#038; HTML, because it means that as a programmer you will hit a brick wall very soon with Shoes&#8217; limitations.</li>
</ul>
<p>If you are a beginner who wants to learn programming you will be much better off using <a href="http://scratch.mit.edu/">MIT&#8217;s Scratch</a>, <a href="http://www.codecademy.com/">Codecademy</a>, or <a href="http://tryruby.org">Try Ruby</a> (originally created by _why but has since been much improved). <a href="http://kidsruby.com/">Kids Ruby</a> is a project based off of Hackety Hack that is polished, though I found problems with it as well. (I could see it expanding and improving in the future though.)</p>
<p>The <a href="https://github.com/hacketyhack/hacketyhack">github page for Hackety Hack</a> hasn&#8217;t been updated in about a year (and has dozens of <a href="https://github.com/hacketyhack/hacketyhack/issues">untouched open issues</a>), which I take to mean that the project is dead. I don&#8217;t see it getting better anytime soon, but I also don&#8217;t know why Hackety Hack had such a wide reputation to begin with, except for the (arguably well-deserved) good reputation of _why behind it.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2012%2F12%2F27%2Fwhy-i-recommend-against-hackety-hack%2F&amp;title=Why%20I%20Recommend%20Against%20Hackety%20Hack" id="wpa2a_12"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2012/12/27/why-i-recommend-against-hackety-hack/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>8-bit NES Legend of Zelda Map Data</title>
		<link>http://inventwithpython.com/blog/2012/12/10/8-bit-nes-legend-of-zelda-map-data/</link>
		<comments>http://inventwithpython.com/blog/2012/12/10/8-bit-nes-legend-of-zelda-map-data/#comments</comments>
		<pubDate>Mon, 10 Dec 2012 17:00:30 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1222</guid>
		<description><![CDATA[I&#8217;ve created Python &#038; Pygame script that lets you walk around the overworld map of the original Legend of Zelda game on the 8-bit Nintendo. There are no monsters or levels or items; it is simply a walking tour. The Link walking sprite animation is implemented by my Pyganim module. More importantly, this program does [...]]]></description>
				<content:encoded><![CDATA[<p>I&#8217;ve created Python &#038; Pygame script that lets you walk around the overworld map of the original Legend of Zelda game on the 8-bit Nintendo. There are no monsters or levels or items; it is simply a walking tour. The Link walking sprite animation is implemented by my <a href="http://inventwithpython.com/pyganim">Pyganim module</a>.</p>
<p>More importantly, this program does provide the raw map data the entire world map (something I haven&#8217;t been able to find on the web.) Getting this from the Zelda ROM is actually a pain due to the tricks used to store the map info. The game doesn&#8217;t store individual tiles and their XY location, but rather have one of three color schemes for the border and a color scheme for the center tiles. Even then, the game only stores <em>columns</em> of tiles, and then each room references which columns it uses. (You can notice the same columns being used in different rooms, even though their color scheme may change.) </p>
<p>These tricks aren&#8217;t really needed with today&#8217;s computers for a game as simple as Zelda, so I&#8217;ve compiled the tile map data for each individual location on the map. Here&#8217;s the <a href="/nes_zelda_overworld_tile_map.txt">world map data file</a> (it is also included in the main download below.)</p>
<h2><a href="/zeldawalkingtour.zip">Download the Zelda Walking Tour program.</a></h2>
<h2><a href="/nes_zelda_overworld_tile_map.txt">View raw tile map data.</a></h2>
<p>(Just unzip all the files and run the nesZeldaWalkingTour.py file with Python. Runs with both Python 2 and Python 3. Requires the <a href="http://pygame.org">Pygame module</a> to be installed.)</p>
<p>You can also download the code from the <a href="https://github.com/asweigart/nes_zelda_map_data">GitHub project</a>.</p>
<p><img src="/blog/images/zeldatour/nes_zelda_tour_screenshot1.png" /></p>
<p><span id="more-1222"></span></p>
<p>Blocking is not implemented, which means you can freely walk through walls:</p>
<p><img src="/blog/images/zeldatour/nes_zelda_tour_screenshot2.png" /></p>
<p>The tiles are stored in overworldtiles.png:</p>
<p><img src="/blog/images/zeldatour/overworldtiles.png" /></p>
<p>To find which tiles correspond to the hex numbers in the world map data file, use this key (the numbers start at 0 and simply increasing going to the right):</p>
<p><img src="/blog/images/zeldatour/labeled_overworldtiles.png" /></p>
<p>And just for grins, I have a pixel-perfect single image of the entire world map, without enemies or anything else polluting it. The colors may be a bit off from the actual game, but they are consistent:</p>
<p><a href="/blog/images/zeldatour/entire_worldmap_single_image.png"><img src="/blog/images/zeldatour/entire_worldmap_single_image_THUMB.png" /></a></p>
<p>If you&#8217;d like to work on a similar project to this, here&#8217;s a good page I&#8217;ve found that I wish I had seen before I started: <a href="http://blog.tojicode.com/2012/08/more-gpu-tile-map-demos-zelda.html">More GPU Tile map demos (Zelda)</a></p>
<p>Some general stats about the NES Zelda map data:</p>
<ul>
<li>The entire overworld is 4096 x 1344 pixels, 16 x 8 rooms, and 256 x  88 tiles in size.</li>
<li>Each room (a single screen) is 16 x 11 tiles in size (the bottom row only shows the top half of the tile). It is 256 x 176 pixels in size (if you count the bottom half of the bottom row).</li>
<li>Each tile, including Link himself, is 16 x 16 pixels in size.</li>
<li>There are seven colors used on the overworld map (though the RGB values may not be perfect):
<ul>
<li>(32, 56, 236)&nbsp;&nbsp;blue</li>
<li>(252, 252, 252)&nbsp;&nbsp;white</li>
<li>(200, 76, 12)&nbsp;&nbsp;brown</li>
<li>(0, 168, 0)&nbsp;&nbsp;green</li>
<li>(116, 116, 116)&nbsp;&nbsp;gray</li>
<li>(252, 216, 168)&nbsp;&nbsp;tan</li>
<li>(0, 0, 0)&nbsp;&nbsp;black</li>
</ul>
</li>
</ul>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2012%2F12%2F10%2F8-bit-nes-legend-of-zelda-map-data%2F&amp;title=8-bit%20NES%20Legend%20of%20Zelda%20Map%20Data" id="wpa2a_14"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2012/12/10/8-bit-nes-legend-of-zelda-map-data/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Programming AI Bots for Zombie Dice</title>
		<link>http://inventwithpython.com/blog/2012/11/21/how-to-make-ai-bots-for-zombie-dice/</link>
		<comments>http://inventwithpython.com/blog/2012/11/21/how-to-make-ai-bots-for-zombie-dice/#comments</comments>
		<pubDate>Wed, 21 Nov 2012 18:00:18 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1159</guid>
		<description><![CDATA[This tutorial outlines how to create a tournament simulation program for the Zombie Dice game in Python. With this tournament program, you can also code your own AI bots to play Zombie Dice against each other. You can quickly test out how the different strategies the bots use compare against each other over thousands of [...]]]></description>
				<content:encoded><![CDATA[<p>This tutorial outlines how to create a tournament simulation program for the Zombie Dice game in <a href="http://python.org">Python</a>. With this tournament program, you can also code your own AI bots to play Zombie Dice against each other. You can quickly test out how the different strategies the bots use compare against each other over thousands of simulated games. This tutorial assumes you know basic Python programming. (You can learn to program from the free book on this site, <a href="http://inventwithpython.com/chapters">&#8220;Invent Your Own Computer Games with Python&#8221;</a>.</p>
<p>Computer game simulations make for great science fair experiments, and since the rules to Zombie Dice are so simple it&#8217;s fairly easy to program a simulation for it.</p>
<h2>The Rules of Zombie Dice</h2>
<p>Zombie Dice is a dice rolling game that is fun for groups, quick to play, and can be learned in a couple minutes. It has a &#8220;push your luck&#8221; game mechanic.</p>
<p>Each of the players is a zombie. Each of the dice represents a human victim. You want to eat brains and avoid shotgun blasts. If the footsteps come up, the human has eluded you. The rules are summarized below: (Or <a href="http://www.sjgames.com/dice/zombiedice/img/ZDRules_English.pdf">view the official rules PDF</a> or <a href="http://www.sjgames.com/dice/zombiedice/demo.html">view the Flash tutorial</a>.)</p>
<p><span id="more-1159"></span></p>
<ol>
<li>On your turn grab 3 dice from the cup and roll them. Dice are colored red, yellow, and green and have footsteps, brains, or shotgun icons.</li>
<li>Set aside any brain and shotgun dice.</li>
<li>If you have 3 shotguns, your turn ends with a score of 0.</li>
<li>You can decide to either stop rolling or roll again.</li>
<li>If you stop, your score for this turn is the number of brains you have.</li>
<li>If you re-roll, keep the footsteps dice you rolled, set aside the brain and shotgun dice rolled, and randomly grab dice to replace the brain and shotgun dice. Roll the 3 dice.</li>
<li>Go back to step 4. You basically can keep re-rolling for as long as you want to push your luck.</li>
</ol>
<p>To win:</p>
<li>Once a player gets 13 brains, finish the round. If there is a tie, the tied players play a single tie breaker round.</li>
<li>Also, if you run out of dice for your turn, note the number of brain dice you&#8217;ve rolled and place those brain dice back into the cup. Then continue as normal.</li>
<p>The colors note your odds of winning. The entire dice set has 6 green, 4 yellow, and 3 red dice.</p>
<p><img src="/blog/images/zombiedice_diceset.png" /></p>
<p><a href="http://youtu.be/PlYAcsJxLM4?t=1m10s">Here&#8217;s a video of gameplay:</p>
<p><img src="/blog/images/zombie_dice_youtube.png" /></a></p>
<h2>Running the Tournament Program</h2>
<p><a href="/files/zombiedicesim.zip">Download the Zombie Dice simulation code.</a> This should run with Python 2 and Python 3. You can also find it on <a href="https://github.com/asweigart/zombiedice">GitHub</a>. The two main files are:</p>
<ul>
<li><strong>zombiedice.py</strong> is the basic simulator and outputs text results when the tournament is finished.</li>
<li><strong>zombiedice_web.py</strong> runs a web server and launches your browser to provide a GUI and real-time updates while the tournament is in progress.</li>
</ul>
<p><a href="/blog/images/zombiedice_screenshot1.png"><img src="/blog/images/zombiedice_screenshot1.png" width="600" /></a></p>
<p><a href="/blog/images/zombiedice_screenshot2.gif"><img src="/blog/images/zombiedice_screenshot2.gif" width="600" /></a></p>
<p>When you click the &#8220;Begin Tournament&#8221; button, the simulation will begin with the loaded bots:</p>
<p><a href="/blog/images/zombiedice_screenshot3.gif"><img src="/blog/images/zombiedice_screenshot3.gif" width="600" /></a></p>
<p>When the simulation has finished, you can reload the page to start a new tournament.</p>
<p>To edit which bots are being run in the simulation, modify the <code>BOTS</code> list at the top of zombiedice_web.py.</p>
<h2>The AI Bots</h2>
<p>Each zombie AI bot will be a class that has:</p>
<ul>
<li>A <code>name</code> member which is set in the constructor function. The name uniquely identifies the bot in a tournament (this way you can have multiple bots from the same class compete against each other.)</li>
<li>A <code>turn()</code> method that has a <code>gameState</code> tuple passed to it.</li>
<li>The <code>turn()</code> method calls a global <code>roll()</code> function (<code>zombiedice.roll()</code> if your bot is in another file that imports zombiedice.py) as many times as it likes. The turn is over when the <code>turn()</code> method returns. (Calls to <code>roll()</code> after getting three shotgun blasts have no effect, and roll() simply returns an empty list.)</li>
<li>Optionally you can have a <code>newGame()</code> and/or <code>endGame()</code> methods that are called so that the bot can know when a game has started and ended (in case the bot should change its strategy if it is winning or losing a multi-game tournament.)</li>
</ul>
<p><code>
<pre># A "skeleton" zombie class. Get it? Skeleton? Heh heh heh... eh... nevermind.
class MyZombie(object):
    def __init__(self, name):
        self.name = name
    def newGame(self):
        pass # do nothing
    def endGame(self, gameState):
        pass # do nothing
    def turn(gameState):
        results = roll()</pre>
<p></code></p>
<p>The <code>gameState</code> parameter is a dictionary with the following keys:</p>
<ul>
<li>Key <code>'order'</code> has a value of a list of player order like <code>['player1name', 'player2name', ...]</code>.</li>
<li>Key <code>'scores'</code> has a value of a dictionary with scores like <code>{'player1name': player1score, 'player2name': player2score, ...}</code>.</li>
<li>Key <code>'round'</code> has a value of an integer telling which round it is in the current game. (The first round is 1.)</li>
</ul>
<p>The return value of <code>roll()</code> is a list of three tuples that have the color and icon of the rolls. For example:<br />
<code>[('green', 'shotgun'), ('red', 'footsteps'), ('yellow', 'brains')]</code></p>
<p>There are uppercase constant variables that you can use instead of string values, just to avoid typos:<br />
<code>[(GREEN, SHOTGUN), (RED, FOOTSTEPS), (YELLOW, BRAINS)]</code></p>
<p>For example, here&#8217;s the code for a Zombie Dice bot that keeps rolling until they have at least 3 brains:</p>
<p><code>
<pre>class ThreeBrainsThenStops(object):
    def __init__(self, name):
        self.name = name
    def newGame(self):
        pass # do nothing
    def endGame(self, gameState):
        pass # do nothing
    def turn(self, gameState):
        brains = 0
        while brains < 3:
            results = roll()
            if results = []:
                return
            for i in results:
                if i[1] == BRAINS:
                    brains += 1</pre>
<p></code></p>
<h2>Loading Bots into the Tournament Program</h2>
<p>Once you have the code for your bot, you can run it in a tournament by adding an instance of the bot to a list which is passed to runTournament(). For example, in zombiedice.py (the text-based tournament program):</p>
<p><code>
<pre>bots = [ZombieBot_MonteCarlo('MonteCarlo', 40, 100),
        ZombieBot_MinNumShotgunsThenStops('Min2ShotgunsBot', 2),
        ZombieBot_RandomCoinFlip('RandomBot'),
        ]
runTournament(bots, 1000)</pre>
<p></code></p>
<p>Each object made from a bot class needs to be given a unique name in the constructor. Some classes may have other parameters for the constructor. In zombiedice_web.py, this code is here:</p>
<p><code>
<pre>BOTS = [zombiedice.ZombieBot_MonteCarlo('MonteCarloBot', 40, 100),
        zombiedice.ZombieBot_MonteCarlo('FastMonteCarloBot', 40, 20), # executes faster because it runs fewer experimental rolls
        zombiedice.ZombieBot_MinNumShotgunsThenStops('Min2ShotgunsBot', 2),
        zombiedice.ZombieBot_MinNumShotgunsThenStops('Min1ShotgunBot', 1),
        #zombiedice.ZombieBot_HumanPlayer('Human'), # uncomment if you want to play (learn the rules to Zombie Dice first though)
        zombiedice.ZombieBot_RollsUntilInTheLead('RollsUntilInTheLeadBot'),
        zombiedice.ZombieBot_RandomCoinFlip('RandomBot'),
        ]</pre>
<p></code></p>
<p>The code further down in zombiedice_web.py will handle launching the web server.</p>
<h2>The Included Bots</h2>
<p>The zombiedice.py program has some basic bots:</p>
<ul>
<li>The <code>ZombieBot_RandomCoinFlip</code> bot simply has a 50/50 chance of continuing to call the <code>roll()</code> function or stopping. It'll most likely lose most games.</li>
<li>The <code>ZombieBot_MinNumShotgunsThenStops</code> bot will continue to call the <code>roll()</code> function until it has a minimum number of shotgun rolls. This minimum can be set by passing an integer for the <code>minShotguns</code> parameter.</li>
<li>The <code>ZombieBot_HumanPlayer</code> bot actually uses <code>print()</code> and <code>input()</code> calls so that a human player can play against the bots.</li>
<li>The <code>ZombieBot_RollsUntilInTheLead</code> bot will keep rolling until it gets into the lead compared to the other bots. This bot takes higher risks once it starts trailing.</li>
<li>The <code>ZombieBot_MonteCarlo</code> bot is the most sophisticated of all the bots. It basically runs several random experiments to see if the next roll would result in 3 or more shotguns. You can pass different values to the constructors to tell it how many experiments to run on each turn and what percentage of them must not result in death to roll again.</li>
</ul>
<p>Write your own bots and then see how they do against these simple bots. You can post your code to <a href="https://gist.github.com">https://gist.github.com</a> and post a link to it in the comments section below.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2012%2F11%2F21%2Fhow-to-make-ai-bots-for-zombie-dice%2F&amp;title=Programming%20AI%20Bots%20for%20Zombie%20Dice" id="wpa2a_16"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2012/11/21/how-to-make-ai-bots-for-zombie-dice/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Designing a Button UI Module for Pygame</title>
		<link>http://inventwithpython.com/blog/2012/10/30/creating-a-button-ui-module-for-pygame/</link>
		<comments>http://inventwithpython.com/blog/2012/10/30/creating-a-button-ui-module-for-pygame/#comments</comments>
		<pubDate>Tue, 30 Oct 2012 16:00:44 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1054</guid>
		<description><![CDATA[Pygame is a 2D graphics and gaming library for Python. It&#8217;s pretty nifty because it essentially gives you a blank window that you can draw any shapes or lines or images you want on it. But it doesn&#8217;t come with any UI elements like buttons, scrollbars, or check boxes. This post will go through not [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://pygame.org">Pygame</a> is a 2D graphics and gaming library for Python. It&#8217;s pretty nifty because it essentially gives you a blank window that you can draw any shapes or lines or images you want on it. But it doesn&#8217;t come with any UI elements like buttons, scrollbars, or check boxes. This post will go through not only creating a button class for Pygame, but also the reasoning behind why I&#8217;ve set up the code as it is. This is more of a &#8220;how to create a module other people can use&#8221; tutorial than a UI or Pygame tutorial.</p>
<p>This tutorial assumes you know the basics of Pygame and Python programming. If you don&#8217;t, it&#8217;s probably easy enough to follow anyway.</p>
<p>A button is a common user interface (UI) control that is used in many software applications. It seems simple enough: there&#8217;s a button on the window and you click on it and something happens. But there&#8217;s a lot of details we should plan out ahead of time. Remember, we want to make a generic button class so that other programmers can use this in their games and programs. Once you&#8217;ve read through the process here, you&#8217;ll be familiar with how to make your own modules for UI elements.</p>
<p>Designing a UI button class is good a good programming practice exercise.</p>
<p><span style="font-size: 1.4em"><a href="http://inventwithpython.com/pygbutton_src.zip">Download the PygButton module here.</a> You can also look at just the <a href="http://inventwithpython.com/pygbutton.py">pygbutton.py</a> file itself.</span></p>
<h2>The Feature List (and the Non-Feature List)</h2>
<p>First, let&#8217;s create a list of design details for the buttons:<br />
<span id="more-1054"></span></p>
<ol>
<li>Can have any width or height.</li>
<li>The buttons can have text on them. The font and size can be customized.</li>
<li>The background color of the button can be changed to any RGB value, as can the foreground (text) color.</li>
<li>The button&#8217;s properties (bgcolor, bgcolor, text, font, size, etc.) can be dynamically changed.</li>
<li>The button has three states: normal, down (when the mouse cursor has pressed down on the button), and highlight (when the mouse cursor is over the button but not pressing down).
<li>Pygame&#8217;s mouse events are passed to the button&#8217;s handleEvent() method, which update&#8217;s the button&#8217;s state and calls any event-handling code.</li>
<li>The button recognizes 6 different types of events: mouse enter, mouse exit, mouse down, mouse up, mouse click, and mouse move. (These are explained later.)</li>
<li>Instead of text, the user will be able to specify images for the three different states. We&#8217;ll call these image-based buttons.</li>
<li>The button&#8217;s visibility can be toggled on and off.</li>
</ol>
<p>And it&#8217;s always a good idea to come up with a list of things we specifically won&#8217;t implement (to avoid feature creep each time we think, &#8220;Hey, it&#8217;d be cool if we could&#8230;&#8221;). These features could always be implemented later.</p>
<ol>
<li>Must be rectangular (i.e. can&#8217;t be oval).</li>
<li>No transparency.</li>
<li>No more than the three states.</li>
<li>No hotkeys attached to them, or keyboard focus.</li>
<li>No special &#8220;double click&#8221; event (it&#8217;ll just be two click events).</li>
<li>For now, the highlight state looks identical to the normal state for text-based buttons.</li>
<li>A button is either text-based or image-based, there&#8217;s no hybrid.</li>
<li>No &#8220;disabled&#8221; state.</li>
<li>Only one font &#038; color at a time for text-based buttons.</li>
<li>The text caption will always be centered, not left- or right-aligned.</li>
</ol>
<p><em>(But you can add these features to your own code if you want.)</em></p>
<h2>Design Details</h2>
<p>Whenever you&#8217;re designing something, always do a prior art search first. Looking at how buttons on a web page work is a good case to examine, for example.</p>
<p>The buttons have three states and can have a different appearance for each state.</p>
<ul>
<li>The <strong>&#8220;normal&#8221; state</strong> is what the button looks like when it has not been clicked and the mouse is not over it.</li>
<li>The <strong>&#8220;highlight&#8221; state</strong> is what the button looks like when the mouse is hovering over it, but not clicking it. We can use this to add some kind of highlighting behavior when the mouse glides over the button. For normal text-based buttons, this state will look identical to the normal state.</li>
<li>The <strong>&#8220;down&#8221; state</strong> is what the button looks like when it is being clicked down.</li>
</ul>
<p>There are also six different &#8220;button events&#8221; that the buttons can produce based on the Pygame mouse events that are passed to them:</p>
<ul>
<li><strong>Enter</strong> &#8211; When a MOUSEMOTION event has told the button that the mouse is over the button when previously it wasn&#8217;t.</li>
<li><strong>Exit</strong> &#8211; When a MOUSEMOTION event has told the button that the mouse is no longer over the button when previously it was.</li>
<li><strong>Move</strong> &#8211; When the button has received a MOUSEMOTION event.</li>
<li><strong>Down</strong> &#8211; When the mouse is pressed down on the button.</li>
<li><strong>Up</strong> &#8211; When the mouse is released on the button.</li>
<li><strong>Click</strong> &#8211; When the mouse was pressed down on the button and released over the button. (Releasing the mouse off of the button does not trigger the click event.)</li>
</ul>
<p><em>(Note: The buttons won&#8217;t produce Pygame USEREVENTS. I didn&#8217;t see a significant need for them.)</em></p>
<p>As to how the mouse button looks, I&#8217;ll be using the Windows look-and-feel of buttons. Here&#8217;s what they look like zoomed in:</p>
<p><img src="http://inventwithpython.com/blog/images/pygbutton_buttons.png" /></p>
<p>Notice that the 3D appearance is caused by drawing these black, gray, and white outlines. These lines don&#8217;t change if the background color of the button changes.</p>
<h2>What the API will Look Like</h2>
<p>Before diving into coding, we need a concrete plan for how other programmers will use this module. <strong>It doesn&#8217;t matter how sophisticated your library is, if it is opaque, difficult to learn, and inconsistent no one will want to learn it and it will not be used.</strong> It&#8217;s important to get these details right the first time, because making changes (like changing a function name or getting rid of a class) later on could break other people&#8217;s code that uses your library. This means they won&#8217;t adopt newer versions and new features (since the newer version breaks their code), which further limits the popularity of your module.</p>
<p>The button&#8217;s API will have three main parts: the constructor function that creates it, the function that draws the button to a pygame.Surface object (to display it on the screen), and a handleEvent() method that we can pass pygame.Event objects to so it knows what is happening in the program. The code will roughly look like this:</p>
<p><code>
<pre>myButton = pygbutton.PygButton(rectObj, 'Caption text')
...
for event in pygame.event.get(): # event handling loop
    myButton.handleEvent(event)
...
myButton.draw(displaySurface)</pre>
<p></code></p>
<p>Before we start coding, we should write out the method names and parameters for the PygButton class first. This will help cement what we want to code before we start coding:</p>
<ul>
<li><code>def __init__(self, rect=None, caption='', bgcolor=LIGHTGRAY, fgcolor=BLACK, font=None, normal=None, down=None, highlight=None)</code> &#8211; The constructor. Note that pretty much everything has a default argument. If the user just wants asimple button, we shouldn&#8217;t have to make her write out tons of boilerplate code. Let&#8217;s just supply default values.</li>
<li><code>def handleEvent(self, eventObj)</code> &#8211; Changes the button&#8217;s state if the Pygame event passed is relevant.</li>
<li><code>def draw(self, surfaceObj)</code> &#8211; Draws the button (in its current state) to the surfaceObj surface.</li>
<li><code>def mouseClick(self, event)</code> &#8211; Called when the button has a click event. (These methods don&#8217;t do anything in the PygButton class, but you can override this class to implement code in these methods.)</li>
<li><code>def mouseEnter(self, event)</code> &#8211; Called when the button has a &#8220;mouse enter&#8221; event.</li>
<li><code>def mouseExit(self, event)</code> &#8211; Called when the button has a &#8220;mouse exit&#8221; event.</li>
<li><code>def mouseMove(self, event)</code> &#8211; Called when the button has a &#8220;mouse move&#8221; event.</li>
<li><code>def mouseDown(self, event)</code> &#8211; Called when the button has a mouse button down event.</li>
<li><code>def mouseUp(self, event)</code> &#8211; Called when the button has a mouse button up event.</li>
<li><code>def setSurfaces(self, normalSurface, downSurface=None, highlightSurface=None)</code> &#8211; Let&#8217;s the user specify either image filenames or pygame.Surface objects to use for each of the states. (This sets the button to be an image-based button.)</li>
</ul>
<p>And here are some properties that we&#8217;d like to set for the PygButton class. Whenever you think you&#8217;ll need a get and set method for something (i.e. getCaption() and setCaption() instead of just a caption property), this is a strong indication that a property would be better instead.</p>
<ul>
<li><code>caption</code> &#8211; The string for the text caption in the center of the button.</li>
<li><code>rect</code> &#8211; A pygame.Rect object which gives the position and size of the button.</li>
<li><code>visible</code> &#8211; A boolean that sets the button to visible (True) or invisible (False).</li>
<li><code>fgcolor</code> &#8211; An RGB tuple or pygame.Color object for the text (foreground) color.</li>
<li><code>bgcolor</code> &#8211; An RGB tuple or pygame.Color object for the background color.</li>
<li><code>font</code> &#8211; A pygame.font.Font object for the font (and size) to use for the text caption.</li>
</ul>
<p>Setting any of these properties (other than rect) will result in the button becoming a text-based button if it was previously an image-based button. Setting the rect property of an image-based button simply resizes the images.</p>
<p>Note that we don&#8217;t have properties for setting the normal, down, and highlight Surfaces. This is because when we switch from a normal text-based button (which uses the caption, fgcolor, bgcolor, and font properties) to an image-based button, we want to set the images for all three Surfaces at the same time (even though we have defaults for the down and highlight surfaces.)</p>
<h2>The Preamble Code</h2>
<p>Here&#8217;s the code that goes at the top of the pygbutton.py file. It imports Pygame and calls the init() function for the fonts and creates a few constants that we&#8217;ll use in the module.</p>
<p><code>
<pre>import pygame
from pygame.locals import *
pygame.font.init()
PYGBUTTON_FONT = pygame.font.Font('freesansbold.ttf', 14)
BLACK     = (  0,   0,   0)
WHITE     = (255, 255, 255)
DARKGRAY  = ( 64,  64,  64)
GRAY      = (128, 128, 128)
LIGHTGRAY = (212, 208, 200)</pre>
<p></code></p>
<h2>The Constructor Function</h2>
<p>The constructor function is fairly straight forward. There are many different attributes that we can customize for a button, but we can always just create a standard default button.</p>
<p><code>
<pre>class PygButton(object):
    def __init__(self, rect=None, caption='', bgcolor=LIGHTGRAY, fgcolor=BLACK, font=None, normal=None, down=None, highlight=None):
        if rect is None:
            self._rect = pygame.Rect(0, 0, 30, 60)
        else:
            self._rect = pygame.Rect(rect)
        self._caption = caption
        self._bgcolor = bgcolor
        self._fgcolor = fgcolor
        if font is None:
            self._font = PYGBUTTON_FONT
        else:
            self._font = font
        # tracks the state of the button
        self.buttonDown = False # is the button currently pushed down?
        self.mouseOverButton = False # is the mouse currently hovering over the button?
        self.lastMouseDownOverButton = False # was the last mouse down event over the mouse button? (Used to track clicks.)
        self._visible = True # is the button visible
        self.customSurfaces = False # button starts as a text button instead of having custom images for each surface
        if normal is None:
            # create the surfaces for a text button
            self.surfaceNormal = pygame.Surface(self._rect.size)
            self.surfaceDown = pygame.Surface(self._rect.size)
            self.surfaceHighlight = pygame.Surface(self._rect.size)
            self._update() # draw the initial button images
        else:
            # create the surfaces for a custom image button
            self.setSurfaces(normal, down, highlight)</pre>
<p></code></p>
<p>For image-based buttons, the setSurfaces() method is called, which handles the default images for the Down and Highlight state if they are unspecified. It also checks that the images are the same size. Note that the user can specify either pygame.Surface objects or string filename values.</p>
<p><code>
<pre>    def setSurfaces(self, normalSurface, downSurface=None, highlightSurface=None):
        """Switch the button to a custom image type of button (rather than a
        text button). You can specify either a pygame.Surface object or a
        string of a filename to load for each of the three button appearance
        states."""
        if downSurface is None:
            downSurface = normalSurface
        if highlightSurface is None:
            highlightSurface = normalSurface
        if type(normalSurface) == str:
            self.origSurfaceNormal = pygame.image.load(normalSurface)
        if type(downSurface) == str:
            self.origSurfaceDown = pygame.image.load(downSurface)
        if type(highlightSurface) == str:
            self.origSurfaceHighlight = pygame.image.load(highlightSurface)
        if self.origSurfaceNormal.get_size() != self.origSurfaceDown.get_size() != self.origSurfaceHighlight.get_size():
            raise Exception('foo')
        self.surfaceNormal = self.origSurfaceNormal
        self.surfaceDown = self.origSurfaceDown
        self.surfaceHighlight = self.origSurfaceHighlight
        self.customSurfaces = True
        self._rect = pygame.Rect((self._rect.left, self._rect.top, self.surfaceNormal.get_width(), self.surfaceNormal.get_height()))</code></pre>
<p>Note that the PygButton class also stores the original images in the origSurfaceNormal, origSurfaceDown, and origSurfaceHighlight member variables. This is so that when the code does a resize, we are resizing the original images. The button could be resized multiple times, and this would result in poor quality if we tried to resize and previously resized image. (The same way a photocopy of a photocopy of a photocopy reduces the image quality.)</p>
<h2>The draw() Method</h2>
<p>The draw() method is straightforward since it only copies the surfaceNormal, surfaceDown, and surfaceHighlight properties to the passed pygame.Surface object. The draw() method is called whenever the button's current state needs to be drawn to a Surface object. Drawing the buttons themselves will be handled by the _update() method.</p>
<p><code>
<pre>def draw(self, surfaceObj):
    """Blit the current button's appearance to the surface object."""
    if self._visible:
        if self.buttonDown:
            surfaceObj.blit(self.surfaceDown, self._rect)
        elif self.mouseOverButton:
            surfaceObj.blit(self.surfaceHighlight, self._rect)
        else:
            surfaceObj.blit(self.surfaceNormal, self._rect)</pre>
<p></code></p>
<p>The _update() method will be called whenever the appearance of the buttons has been modified. This happens when the text, background color, size, etc. of the button has changed. This is why the name of _update() begins with an underscore; it's only called by the class's code itself. It shouldn't be called by the user.</p>
<p>The _update() method is mostly drawing code for text-based buttons (or resizing the images for image-based buttons).</p>
<p><code>
<pre>    def _update(self):
        """Redraw the button's Surface object. Call this method when the button has changed appearance."""
        if self.customSurfaces:
            self.surfaceNormal    = pygame.transform.smoothscale(self.origSurfaceNormal, self._rect.size)
            self.surfaceDown      = pygame.transform.smoothscale(self.origSurfaceDown, self._rect.size)
            self.surfaceHighlight = pygame.transform.smoothscale(self.origSurfaceHighlight, self._rect.size)
            return
        w = self._rect.width # syntactic sugar
        h = self._rect.height # syntactic sugar
        # fill background color for all buttons
        self.surfaceNormal.fill(self.bgcolor)
        self.surfaceDown.fill(self.bgcolor)
        self.surfaceHighlight.fill(self.bgcolor)
        # draw caption text for all buttons
        captionSurf = self._font.render(self._caption, True, self.fgcolor, self.bgcolor)
        captionRect = captionSurf.get_rect()
        captionRect.center = int(w / 2), int(h / 2)
        self.surfaceNormal.blit(captionSurf, captionRect)
        self.surfaceDown.blit(captionSurf, captionRect)
        # draw border for normal button
        pygame.draw.rect(self.surfaceNormal, BLACK, pygame.Rect((0, 0, w, h)), 1) # black border around everything
        pygame.draw.line(self.surfaceNormal, WHITE, (1, 1), (w - 2, 1))
        pygame.draw.line(self.surfaceNormal, WHITE, (1, 1), (1, h - 2))
        pygame.draw.line(self.surfaceNormal, DARKGRAY, (1, h - 1), (w - 1, h - 1))
        pygame.draw.line(self.surfaceNormal, DARKGRAY, (w - 1, 1), (w - 1, h - 1))
        pygame.draw.line(self.surfaceNormal, GRAY, (2, h - 2), (w - 2, h - 2))
        pygame.draw.line(self.surfaceNormal, GRAY, (w - 2, 2), (w - 2, h - 2))
        # draw border for down button
        pygame.draw.rect(self.surfaceDown, BLACK, pygame.Rect((0, 0, w, h)), 1) # black border around everything
        pygame.draw.line(self.surfaceDown, WHITE, (1, 1), (w - 2, 1))
        pygame.draw.line(self.surfaceDown, WHITE, (1, 1), (1, h - 2))
        pygame.draw.line(self.surfaceDown, DARKGRAY, (1, h - 2), (1, 1))
        pygame.draw.line(self.surfaceDown, DARKGRAY, (1, 1), (w - 2, 1))
        pygame.draw.line(self.surfaceDown, GRAY, (2, h - 3), (2, 2))
        pygame.draw.line(self.surfaceDown, GRAY, (2, 2), (w - 3, 2))
        # draw border for highlight button
        self.surfaceHighlight = self.surfaceNormal</pre>
<p></code></p>
<h2>The Event Callback Methods</h2>
<p>There are two ways that we can execute code in response to button-related events. The first is to have a method in the PygButton class (and its subclasses) get called that contains the code we want to run.</p>
<p>We'll just put stub functions for these methods. Any subclasses that inherit from PygButton can override these methods and use any code they want. But for now, they do nothing:</p>
<p><code>
<pre>    def mouseClick(self, event):
        pass # This class is meant to be overridden.
    def mouseEnter(self, event):
        pass # This class is meant to be overridden.
    def mouseMove(self, event):
        pass # This class is meant to be overridden.
    def mouseExit(self, event):
        pass # This class is meant to be overridden.
    def mouseDown(self, event):
        pass # This class is meant to be overridden.
    def mouseUp(self, event):
        pass # This class is meant to be overridden.</pre>
<p></code></p>
<h2>The handleEvent() Method</h2>
<p>Whenever our program calls pygame.event.get_events() to retrieve all the events generated (for keyboard, mouse, etc. events) we should pass them to handleEvent() so the buttons can update their state. The second way to execute code in response to button events is with the return value of handleEvent().</p>
<p>The handleEvent() method has been set up so that it returns a list of all button events that have happened due to the normal Pygame events passed to handleEvent(). So if a mouse move Pygame event has happened over the button (when previously the mouse cursor wasn't over the button), the handleEvent() method will return the list ['enter', 'move'].</p>
<p>The caller of handleEvent() can perform any actions in response to these events.</p>
<p>Here's the code for handleEvent():</p>
<p><code>
<pre>def handleEvent(self, eventObj):
    if eventObj.type not in (MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN) or not self._visible:
        # The button only cares bout mouse-related events (or no events, if it is invisible)
        return []
    retVal = []
    hasExited = False
    if not self.mouseOverButton and self._rect.collidepoint(eventObj.pos):
        # if mouse has entered the button:
        self.mouseOverButton = True
        self.mouseEnter(eventObj)
        retVal.append('enter')
    elif self.mouseOverButton and not self._rect.collidepoint(eventObj.pos):
        # if mouse has exited the button:
        self.mouseOverButton = False
        hasExited = True # call mouseExit() later, since we want mouseMove() to be handled before mouseExit()
    if self._rect.collidepoint(eventObj.pos):
        # if mouse event happened over the button:
        if eventObj.type == MOUSEMOTION:
            self.mouseMove(eventObj)
            retVal.append('move')
        elif eventObj.type == MOUSEBUTTONDOWN:
            self.buttonDown = True
            self.lastMouseDownOverButton = True
            self.mouseDown(eventObj)
            retVal.append('down')
    else:
        if eventObj.type in (MOUSEBUTTONUP, MOUSEBUTTONDOWN):
            # if an up/down happens off the button, then the next up won't cause mouseClick()
            self.lastMouseDownOverButton = False
    # mouse up is handled whether or not it was over the button
    doMouseClick = False
    if eventObj.type == MOUSEBUTTONUP:
        if self.lastMouseDownOverButton:
            doMouseClick = True
        self.lastMouseDownOverButton = False
        if self.buttonDown:
            self.buttonDown = False
            self.mouseUp(eventObj)
            retVal.append('up')
        if doMouseClick:
            self.buttonDown = False
            self.mouseClick(eventObj)
            retVal.append('click')
    if hasExited:
        self.mouseExit(eventObj)
        retVal.append('exit')
    return retVal</pre>
<p></code></p>
<h2>PygButton Properties</h2>
<p>Instead of having simple member variables for the caption, rect, visible, fgcolor, bgcolor, and font, we can use <a href="http://docs.python.org/3.2/library/functions.html#property">Python properties</a> instead. This is better, because each time these values get updated we need to run some code that updates the Surface objects that hold the button's look. In other languages, this would require the use of bulky get and set methods. Python's property() function lets us assign methods to be called whenever the member variables need to be get or set.</p>
<p><code>
<pre>    def _propGetCaption(self):
        return self._caption
    def _propSetCaption(self, captionText):
        self.customSurfaces = False
        self._caption = captionText
        self._update()
    def _propGetRect(self):
        return self._rect
    def _propSetRect(self, newRect):
        # Note that changing the attributes of the Rect won't update the button. You have to re-assign the rect member.
        self._update()
        self._rect = newRect
    def _propGetVisible(self):
        return self._visible
    def _propSetVisible(self, setting):
        self._visible = setting
    def _propGetFgColor(self):
        return self._fgcolor
    def _propSetFgColor(self, setting):
        self.customSurfaces = False
        self._fgcolor = setting
        self._update()
    def _propGetBgColor(self):
        return self._bgcolor
    def _propSetBgColor(self, setting):
        self.customSurfaces = False
        self._bgcolor = setting
        self._update()
    def _propGetFont(self):
        return self._font
    def _propSetFont(self, setting):
        self.customSurfaces = False
        self._font = setting
        self._update()
    caption = property(_propGetCaption, _propSetCaption)
    rect = property(_propGetRect, _propSetRect)
    visible = property(_propGetVisible, _propSetVisible)
    fgcolor = property(_propGetFgColor, _propSetFgColor)
    bgcolor = property(_propGetBgColor, _propSetBgColor)
    font = property(_propGetFont, _propSetFont)</pre>
<p></code></p>
<h2>Example Programs</h2>
<p><strong>This is a very important step.</strong> We need to accompany the module with some example programs that show how simple it is to actually use the module in a working program. I would even say that <strong>including example programs is more important than having documentation</strong> (for smaller libraries, at least.)</p>
<p><a href="http://inventwithpython.com/pygbutton_src.zip">Download the PygButton module and example programs here.</a></p>
<p><img src="http://inventwithpython.com/blog/images/pygbutton_test1_screenshot.png" /></p>
<p><img src="http://inventwithpython.com/blog/images/pygbutton_test2_screenshot.png" /></p>
<p><img src="http://inventwithpython.com/blog/images/pygbutton_test3_screenshot.png" /></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2012%2F10%2F30%2Fcreating-a-button-ui-module-for-pygame%2F&amp;title=Designing%20a%20Button%20UI%20Module%20for%20Pygame" id="wpa2a_18"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2012/10/30/creating-a-button-ui-module-for-pygame/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Am I Too Old to Learn Programming?</title>
		<link>http://inventwithpython.com/blog/2012/09/27/am-i-too-old-to-learn-programming/</link>
		<comments>http://inventwithpython.com/blog/2012/09/27/am-i-too-old-to-learn-programming/#comments</comments>
		<pubDate>Thu, 27 Sep 2012 17:29:47 +0000</pubDate>
		<dc:creator>Al Sweigart</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">http://inventwithpython.com/blog/?p=1067</guid>
		<description><![CDATA[No. &#8230; Wow, this was an easy blog post to write. &#160; &#160; &#160; &#160; &#160; I suppose a longer answer would be more satisfying. I have no idea how old you, the reader, are but that&#8217;s irrelevant. No, you are not too old to learn programming. If you wonder if you are too old [...]]]></description>
				<content:encoded><![CDATA[<p>No.</p>
<p>&#8230;</p>
<p>Wow, this was an easy blog post to write.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>I suppose a longer answer would be more satisfying.</p>
<p> I have no idea how old you, the reader, are but that&#8217;s irrelevant. No, you are not too old to learn programming. If you wonder if you are too old to become a professional software developer and are under the age of 50, the answer is still no. (And even above that age, the answer is merely &#8220;probably&#8221;.)</p>
<p>But you might <em>feel</em> too old, or at least feel forever crippled because you didn&#8217;t start coding the instant you developed fine motor skills. But I still assure you: You are not too old to learn programming.</p>
<p><span id="more-1067"></span></p>
<blockquote><p><strong>&#8220;Software development, like professional sports, has a way of making thirty-year old men feel decrepit.</strong></p>
<p><em>-Neal Stephenson, &#8220;Snow Crash&#8221;</em></p></blockquote>
<p>I have a two stories to tell. The first involves me in high school. I was a nerdy teenager who wanted to become a &#8220;leet&#8221; computer hacker. I had rudimentary knowledge of Qbasic. I had started to make some GUI programs with my pirated copy of Visual Basic 4. I heard of <a href="http://www.2600.com/">2600 magazine (&#8220;The Hacker Quarterly&#8221;)</a> and started going to the meetings on the first Friday of the month at the mall.</p>
<p>The first time I went, I felt like everyone was smarter and more knowledgeable than I was (and they were). I resolved to hit the books and study continuously so that for the next month&#8217;s meeting, I wouldn&#8217;t feel like such a noob.</p>
<p>This feeling did not go away by the next month&#8217;s 2600 meeting.</p>
<p>This feeling did not go away for the next several dozen 2600 meetings.</p>
<p>Half the days I wake up, I still feel shockingly unprepared and ignorant about the skills that I am paid a salary for. That feeling is perfectly normal. Everyone in the universe has that.</p>
<p>Computing is a field that is effectively infinite. It is easy to despair that you will never reach the horizon no matter how long you travel but realize that this feeling like you&#8217;re a clueless idiot, to a certain extent, is O-K. And don&#8217;t forget about the many miles you <em>can</em> accomplish.</p>
<p>&nbsp;</p>
<p>The second story is about how I first learned programming. I was one of those people who started learning programming in the third grade. My friend had found Fred D&#8217;Ignazio&#8217;s book, &#8220;Invent Your Own Computer Games&#8221; at the school library, and this was the book that taught me programming in BASIC by making small games. I was already addicted to Nintendo, and immediately took a liking to programming and began to make a few of my own games.</p>
<p><img src="http://inventwithpython.com/images/dignazio_bookcover.jpg" /></p>
<p>Here&#8217;s the thing though. Most of the games I made were just slight variations of games that I had already seen in D&#8217;Ignazio&#8217;s book. And I never was able to wrap my head around the Tic Tac Toe AI he presented in that book. My programming aptitude for <strong>the next several years</strong> didn&#8217;t really improve all that much. It was a fun hobby and maybe it did give me a slight edge, but looking back on it now I can&#8217;t help but think, <strong>&#8220;You&#8217;ve been programming since you were 9? So what? All your programs back then probably sucked.&#8221;</strong></p>
<p>Mine sure did. The reason I wrote <a href="http://inventwithpython.com">&#8220;Invent Your Own Computer Games with Python&#8221;</a> was because there were so many different advanced concepts I would have been fully capable of learning, but I just didn&#8217;t have access to approachable resources. I didn&#8217;t even know what to look for. At this point though, the amount of programming knowledge I gained between the ages of 9 and 18 could be obtained by anyone with an internet connection who committed to study a couple hours a day for three or four months. Or even shorter if you find programming to be fun, because it&#8217;s easy to kill a dozen hours coding on a weekend.</p>
<p>Those years of &#8220;programming since I was 9&#8243; didn&#8217;t really matter that much in the long run. What difference it did have was it showed me that programming wasn&#8217;t an impossible skill to acquire.</p>
<p>The reasons I market &#8220;Invent with Python&#8221; as a book for kids is because 1) kids, in fact, can learn programming and 2) so adults will think its possible for them to learn. There&#8217;s a large mystique around software development that repels a lot of people who don&#8217;t think they&#8217;re qualified since they aren&#8217;t silicon valley geniuses or because they say <a href="http://inventwithpython.com/blog/2012/03/18/how-much-math-do-i-need-to-know-to-program-not-that-much-actually/">&#8220;oh, I&#8217;m not really good at math&#8221;</a>. <strong>The largest hurdle to programming that I&#8217;ve encountered in people isn&#8217;t intellectual so much as psychological.</strong></p>
<p>Learning to code is much like learning to play a musical instrument. Yes, it does take hours and hours of concerted practice. But even if you don&#8217;t do it professionally, it&#8217;s still fun and enriching. It helps to be Mozart and start at the age of five with a pushy composer father, but it is far from necessary. By the time you reach my level of experience and knowledge, you will realize how silly the &#8220;am I too old?&#8221; question is.</p>
<p>No, you are not too old to learn programming.</p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Finventwithpython.com%2Fblog%2F2012%2F09%2F27%2Fam-i-too-old-to-learn-programming%2F&amp;title=Am%20I%20Too%20Old%20to%20Learn%20Programming%3F" id="wpa2a_20"><img src="http://inventwithpython.com/blog/wp-content/plugins/add-to-any/share_save_171_16.png" width="171" height="16" alt="Share"/></a></p>]]></content:encoded>
			<wfw:commentRss>http://inventwithpython.com/blog/2012/09/27/am-i-too-old-to-learn-programming/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 1.424 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2013-05-24 21:52:59 -->
