<?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/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>peak 5390</title>
	<atom:link href="http://peak5390.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://peak5390.wordpress.com</link>
	<description>a little of everything</description>
	<lastBuildDate>Fri, 10 May 2013 16:29:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='peak5390.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>peak 5390</title>
		<link>http://peak5390.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://peak5390.wordpress.com/osd.xml" title="peak 5390" />
	<atom:link rel='hub' href='http://peak5390.wordpress.com/?pushpress=hub'/>
		<item>
		<title>How Open Can a School Be?</title>
		<link>http://peak5390.wordpress.com/2013/05/10/how-open-can-a-school-be/</link>
		<comments>http://peak5390.wordpress.com/2013/05/10/how-open-can-a-school-be/#comments</comments>
		<pubDate>Fri, 10 May 2013 14:26:00 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[education]]></category>
		<category><![CDATA[gittip]]></category>
		<category><![CDATA[open company]]></category>
		<category><![CDATA[open school]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=2059</guid>
		<description><![CDATA[Applying Gittip&#8217;s &#8220;Open Company&#8221; concept to public schools Chad Whitacre, founder of Gittip, has made a name for himself recently through his commitment to running Gittip as an &#8220;open company&#8220;.  Gittip is a platform that allows people to support each &#8230; <a href="http://peak5390.wordpress.com/2013/05/10/how-open-can-a-school-be/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2059&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<h2>Applying Gittip&#8217;s &#8220;Open Company&#8221; concept to public schools</h2>
<p><a title="Chad Whitacre on twitter" href="https://twitter.com/whit537">Chad Whitacre</a>, founder of <a title="Gittip" href="http://gittip.com">Gittip</a>, has made a name for himself recently through his commitment to running Gittip as an &#8220;<a title="Gittip: The first &quot;open company&quot;" href="http://blog.gittip.com/post/26350459746/the-first-open-company">open company</a>&#8220;.  Gittip is a platform that allows people to support each other&#8217;s work through anonymous, recurring donations.  A TechCrunch reporter <a title="Turning down TechCrunch" href="https://medium.com/building-gittip/5886749a4ded">contacted Chad recently</a>, looking to interview him for a story about Gittip.  Chad agreed to the interview, as long as it could be done in the open &#8211; live streamed, and archived on YouTube.  The reporter replied something along the lines of, &#8220;Yeah, good luck with that.&#8221;</p>
<p>That refusal was actually the beginning of the story, because refusing a feature on TechCrunch is almost unheard of in the startup world.  <a title="Gittip takes ‘open source’ to new levels, finds limits" href="http://www.itbusiness.ca/news/gittip-takes-open-source-to-new-levels-finds-limits/33954">Other</a> <a title="Startup Gets You Weekly Cash Gifts For Your Work" href="http://mashable.com/2013/05/09/gittip/">journalists</a> were intrigued by the notion of openness applied to the interview process, and took Chad up on his offer to do interviews in the open.  As a result, Gittip and the concept of an open company has received lots of attention over the past few days.</p>
<p>I am a teacher, and this has me wondering what a fully &#8220;open school&#8221; might look like.  I have a strong motivation to want more openness in schools.  I work at a small alternative school, where many students have not been served well by traditional school structures.  My colleagues and I are always looking to adopt best practices from other schools &#8211; not theory, which is easy to come by, but actual practice.  It&#8217;s harder to do than it sounds, though.  We hear about something a school does well, and we want to adopt that policy, but all we can find as far as documentation goes is a brief statement about the policy.  The actual practice lives in the minds of staff at that school, or in some unfinished internal documentation. In the past week, I have reached out to three principals and educational leaders at schools and institutions who are pushing effective reform models.  All three were more than willing to share their school&#8217;s practice, but said effectively, &#8220;Our documentation is not in a format that is ready to share.&#8221; If these schools were built from the ground-up with openness in mind, and if openness were an accepted part of educational culture, we would have an easier time replicating their success.</p>
<h2>So, what would a fully &#8220;open school&#8221; look like?</h2>
<p>I don&#8217;t know the answer to this question, just as Chad doesn&#8217;t know exactly what an open company looks like yet.  But we can identify a few features, and start building an open school:</p>
<ul>
<li><strong>A fat school handbook that is subject to continuous revision.</strong> In my outreach to other schools, the single most valuable document I have found is a school handbook that lays out the general philosophy of a school, and also includes detailed documentation of how that philosophy is carried out on a daily basis.  A good example is <a title="Casco Bay High School handbook, 2012-2013" href="http://cbhs.portlandschools.org/files/2011/04/CBHS-Handbook-2012-2013-Version-1.pdf">Casco Bay High School&#8217;s handbook</a>, and Sanborn Regional High School&#8217;s <a title="Sanborn Regional High School - Assessment and Grading Guidelines" href="http://web.sau17.org/images/stories/Administration/curriculum/srhs%20grading%20guidelines.pdf">description of its grading policy</a>.  These documents are spelled out in enough detail that we can adapt their policies to our school, without having to pick the brains of those schools&#8217; staff members.</li>
<li><strong>A well-defined structure for educational standards, which supports continuous revision and the concept of &#8220;forking&#8221;.</strong>  Educational standards, for the most part, are handed to schools by outside bodies: state education departments, professional organizations, and now the Common Core. Schools should have the ability to modify educational standards on a continual basis, through well-defined processes to ensure that modifications are appropriate.  Schools should also have the ability to &#8220;fork&#8221; another school&#8217;s system.  Rather than starting from scratch, my school should be able to take a copy of your school&#8217;s work, and start iterating on your system in our own educational environment.  I have started an <a title="Open Competencies - Vision" href="https://github.com/openlearningtools/opencompetencies/blob/master/docs/VISION.md">open project</a> that would achieve these goals.</li>
<li><strong>A portable format for curriculum plans, and a fully open tool for building curriculum.</strong> Most professions have defined digital file formats that make their work more efficient. But educators share their documentation, almost exclusively, in .doc and .pdf formats. This makes it extremely laborious, and effectively impossible, to iterate on each other&#8217;s work.  We need to define a standard for curriculum files; an xml or a JSON for curriculum plans.  Along with this, we need a fully open tool for creating and sharing curriculum.  This tool would be fairly easy to build, once a standard is agreed upon.  Then if you share your plans with me, I can easily restructure your plans into my preferred format, and begin iterating on your work. I have also <a title="Lightning Talk: Abolishing the traditional 9th-12 grade high school structure" href="http://peak5390.wordpress.com/2013/03/17/lightning-talk-abolishing-the-traditional-high-school-structure/">written</a> and <a title="Lightning Talk: Abolishing the tradional 9th-12 grade high school structure" href="http://www.youtube.com/watch?feature=player_detailpage&amp;v=OL3De8BAhME#t=2189s">spoken</a> about this concept, in relation to open educational standards.</li>
</ul>
<h2>Conclusion</h2>
<p>I am very happy to have one foot in the education world, and one foot in the software development world.  Developers have spent decades working out efficient workflows, and building tools to support those methodologies. We could make significant, rapid improvements to public education by applying some of these longstanding concepts to the educational environment.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/2059/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/2059/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2059&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/05/10/how-open-can-a-school-be/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>
	</item>
		<item>
		<title>What really happened at PyCon 2013</title>
		<link>http://peak5390.wordpress.com/2013/03/21/what-really-happened-at-pycon-2013/</link>
		<comments>http://peak5390.wordpress.com/2013/03/21/what-really-happened-at-pycon-2013/#comments</comments>
		<pubDate>Thu, 21 Mar 2013 22:50:16 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[community]]></category>
		<category><![CDATA[pycon 2013]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=2028</guid>
		<description><![CDATA[We are hearing a lot about what happened at PyCon 2013, but most of what is being passed around tells only a tiny part of what really happened.  Here&#8217;s how PyCon went for me this year: The Educator&#8217;s Summit:  This &#8230; <a href="http://peak5390.wordpress.com/2013/03/21/what-really-happened-at-pycon-2013/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2028&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>We are hearing a lot about what happened at PyCon 2013, but most of what is being passed around tells only a tiny part of what really happened.  Here&#8217;s how PyCon went for me this year:</p>
<p><strong>The Educator&#8217;s Summit</strong>:  This year <a title="PyCon 2013" href="https://us.pycon.org/2013/">PyCon</a> hosted an <a title="PyCon 2013: Education Summit" href="https://us.pycon.org/2013/events/edusummit/">Educator&#8217;s Summit</a> for the first time.  I am so happy I went!  There was a very diverse group of people in attendance, interested in a variety of education-related issues.  I met someone who uses Python to teach undergraduate science classes.  I met people who are working on an open-source student information system.  I met people who are interested in bringing non-traditional programmers from competent to professional.  Overall, the summit was an opportunity for many people to make connections with people who share common goals in helping bring new people into the Python community as effectively and efficiently as possible.  I am confident that some of these ideas will see significant forward movement this year, directly as a result of this summit.  I hope this becomes a mainstay of PyCon for years to come.</p>
<p><strong>Young Coder&#8217;s Tutorials:</strong>  There were two days of <a title="PyCon:  Young Coders events" href="https://us.pycon.org/2013/events/letslearnpython/">tutorials for young people</a> new to Python.  From everything we saw and heard, they were a <a title="The Real Katie:  Young Coders" href="http://therealkatie.net/blog/2013/mar/19/pycon-2013-young-coders/">resounding</a> <a title="Mechanical Girl:  Young Coders" href="http://www.mechanicalgirl.com/post/young-coders-learning-python-pycon-2013/">success</a>.  It is wonderful to hear about all the work being done to bring people quickly into meaningful projects using Python, and it was good to see this happening at the conference itself.</p>
<p><strong>Opening Ceremony &#8211; Raspberry Pi for Everyone!  </strong>PyCon staff had been building us up to expect something even more impressive than dancing robots during the opening ceremony.  They outdid themselves when they announced that every member of the audience was going to receive their own <a title="Raspberry Pi" href="http://www.raspberrypi.org/">Raspberry Pi</a>, and that we had access to a lab for the next few days dedicated to playing with the devices.  Eben Upton, one of the creators of the Raspberry Pi, shared with us the brilliant insight that many young people used to have to &#8220;choose not to program&#8221; when they turned their computers on, in order to play games.  The Raspberry Pi is meant to bring young people back to that place where a computer is hackable as soon as you turn it on.  I understood it intellectually when he said it in the opening keynote, but I felt it in my soul when I plugged my bare Raspberry Pi board into the cables in the lab, and booted into the computer for the first time.</p>
<p><strong>Three days of talks:</strong>  The talks were completely inspiring.  Just like last year, I went to some talks that were right at my level, and some that flew right over my head.  I like that some talks fly over my head; it shows how much I can learn in this community, and that some people will always be doing way more complicated things than I am doing.  I love watching the design-focused talks, because they make me think about the data we collect differently.  If you haven&#8217;t watched any talks by Idan Gazit, go <a title="PyCon Videos:  Idan Gazit" href="http://pyvideo.org/search?models=videos.video&amp;q=idan+gazit">watch some</a>.  You will learn something, and you will have a good time doing so.</p>
<p><strong>The Hallway Track:</strong>  I can&#8217;t imagine that anyone goes to a talk during every scheduled session.  There are so many interesting people at PyCon, doing so many interesting things, that you&#8217;d have to try to not meet someone you connect with.  I met a number of people who helped me understand things in new and deeper ways, and I will continue to collaborate with a number of these people.</p>
<p><strong>Exhibitor Booths:  </strong>The booths at PyCon are like an extended hallway track, with better places to hang out.  I spent a lot of time with the folks at <a title="Gittip: Inspiring Generosity" href="https://www.gittip.com/">Gittip</a>, and my life was better for it.  But I also learned a lot by talking to people from AWS, Lincoln Loop, Caktus, SendGrid, Linode, OpenShift, and more.</p>
<p><strong>Job Fair:</strong>  The job fair at PyCon is enjoyable even if you&#8217;re not looking for a job.  Everyone was happy to explain what their companies do, and how they use Python, whether or not you are actually looking for a job.</p>
<p><strong>Poster Session:</strong>  I really enjoy the poster  sessions at PyCon.  It is a nice format in which you get to see an overview of someone&#8217;s work, and talk to them about what they have done.  I enjoyed learning how people are using Python to help smokers quit tobacco.  I enjoyed meeting Eric Holscher from <a title="Read the Docs" href="https://readthedocs.org/">Read the Docs</a>, and hearing from him how some teachers are hosting their syllabi on RtD, and letting students submit pull requests for suggested changes throughout a semester.  I also enjoyed watching Python luminaries like Guido mingling and talking to people about their projects.</p>
<p><strong>Food:  </strong>The food was wonderful, and I really liked the organization of exhibitor booths, the poster session, and the job fair in relation to the dining area.  It was nice to have one big open area, and move freely among all the different offerings.  Lunch is a nice place to just sit at a random table and meet new people, or sit with someone you just met and hash out some new ideas.</p>
<p><strong>Closing Ceremony:  </strong>The closing ceremony was short and sweet.  Jesse bowed out, and received a well-deserved standing ovation.  I did not know PyCon before he came along, but it is clear from watching the people I respect in the community that he has left it significantly better than he found it.  I wish we could all surround him again and give him another standing ovation, and carry him around on our collective shoulders for a while. That, or we can all attach our Raspberry Pi&#8217;s to quadcopters and use them to gently pat him on the shoulder and say, &#8220;Good job!&#8221;</p>
<p><strong>Lightning Talks:</strong>  I enjoyed lightning talks last year, and looked forward to them this year.  I didn&#8217;t see many this year, however, because I was preparing to give my own lightning talk.  I had no intention of giving a talk when I began the conference.  At the Educator&#8217;s Summit I had given a 30-second pitch for a possible talk about using python to &#8220;abolish the traditional 9th-12th grade high school structure&#8221;.  My pitch to give a lightning talk at the summit was not selected, but it start a trend of people coming up and telling me the various ways they had been hurt and even traumatized by the traditional structure of schools in the US and elsewhere.  People&#8217;s stories were so compelling, and the offer to help out on an open-source project were so consistent, that I decided to give a talk.</p>
<p>It was quite interesting to stand up and give even a <a title="Abolishing the traditional 9th-12th grade high school structure" href="http://peak5390.wordpress.com/2013/03/17/lightning-talk-abolishing-the-traditional-high-school-structure/">brief presentation</a> to 1500+ people that I respect greatly.  This community makes it relatively easy for people to share their ideas in this format.  My <a title="Slides:  Abolishing the traditional 9th-12th grade high school structure" href="http://www.scribd.com/doc/130835927/Lightning-Talk-Abolishing-the-Traditional-High-School-Structure-3-pdf">slides</a> were not as polished as most, but my ideas resounded with some people, and that was why I gave the talk in the first place.  I look forward to moving forward with this project, and if it goes anywhere meaningful I look forward to speaking more fully about the project and what it means for education on a larger scale.</p>
<p><strong>Code of Conduct:</strong>  The <a title="PyCon Code of Conduct" href="https://us.pycon.org/2013/about/code-of-conduct/">PyCon Code of Conduct</a> was tested twice that I know of, and it worked well.  There is an internet uproar going on that might suggest otherwise.  But if you  read the <a title="PyCon's response to an inapropriate incident on March 16th" href="http://pycon.blogspot.com/2013/03/pycons-response-to-inapropriate.html">two</a> <a title="PyCon's response to an inappropriate incident on March 17th" href="http://pycon.blogspot.com/2013/03/pycon-response-to-inappropriate.html">posts</a> by PyCon staff about what actually happened, you will see that conference staff dealt quite effectively and respectfully with both situations.  They are getting heat right now because they refuse to sit quietly and ignore a widespread problem.  I know in the long run most people will have the utmost respect for the work of PyCon staff in this area.  If you have a chance, please refer people to the PyCon statements for some grounding in what actually happened.</p>
<p><strong>Sprints</strong><strong>: </strong> I stayed for the first day of <a title="Development Sprints" href="https://us.pycon.org/2013/community/sprints/">sprints</a> this year, and I am so glad I did.  I got to sit with the <a title="Gittip: Inspiring Generosity" href="http://gittip.com">Gittip</a> folks while sprinting on my own project, and I got to see the people who create projects like <a title="Django" href="https://www.djangoproject.com/">Django</a> working.  To someone who has been mostly outside the professional programming community until the past year or so, it is inspiring to see this work being done.</p>
<p><strong>Thank you, thank you, thank you!  </strong>Thank you to the entire PyCon staff and the community itself.  PyCon is absolutely wonderful, and I hope to go back year after year.  I can&#8217;t wait to bring the rest of my family in a few years, and know that there will be something for everyone.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/2028/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/2028/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2028&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/03/21/what-really-happened-at-pycon-2013/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>
	</item>
		<item>
		<title>PyCon&#8217;s Code of Conduct: The Next Step</title>
		<link>http://peak5390.wordpress.com/2013/03/20/pycons-code-of-conduct-the-next-step/</link>
		<comments>http://peak5390.wordpress.com/2013/03/20/pycons-code-of-conduct-the-next-step/#comments</comments>
		<pubDate>Wed, 20 Mar 2013 15:19:59 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[code of conduct]]></category>
		<category><![CDATA[community]]></category>
		<category><![CDATA[pycon 2013]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[white ribbon]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=2017</guid>
		<description><![CDATA[A Brief Response to an Incident at PyCon I&#8217;ve been thinking a lot about an incident I witnessed at PyCon, which has received a fair amount of attention. On Sunday I was sitting a few rows behind Adria Richards during &#8230; <a href="http://peak5390.wordpress.com/2013/03/20/pycons-code-of-conduct-the-next-step/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2017&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<h2>A Brief Response to an Incident at PyCon</h2>
<p>I&#8217;ve been thinking a lot about an <a title="Adria Richards:  Forking and dongle jokes don't belong at tech conferences" href="http://butyoureagirl.com/14015/forking-and-dongle-jokes-dont-belong-at-tech-conferences/">incident</a> I witnessed at PyCon, which has received a fair amount of attention. On Sunday I was sitting a few rows behind Adria Richards during the closing ceremony. At one point I looked up to see conference staff approaching the men seated behind her. I watched the staff members ask these people to step out into the hallway, which they did without much commotion.  I didn&#8217;t hear anything that was said, but it was clear that this had something to do with the Code of Conduct.</p>
<p>I think it&#8217;s important to understand this situation, because it has become a very public test of the Python Software Foundation&#8217;s new Code of Conduct. It is difficult to judge everyone&#8217;s actions from a distance, because to fully evaluate the situation we&#8217;d need to know:</p>
<ul>
<li>exactly what was said in the initial conversation between Adria and these men;</li>
<li>the context in which that conversation occurred;</li>
<li>the exact conversation between the men involved and the conference staff;</li>
<li>the exact conversation between the men involved and their employer.</li>
</ul>
<p>One of these men <a title="HN comment" href="https://news.ycombinator.com/item?id=5404365">lost his job</a> as a result of this incident, and we are seeing numerous people blame Adria for that.  This is a clear example of a culture we live in, where it is more appropriate to blame the victim than to blame any other offenders. In some ways, it doesn&#8217;t matter whether Adria&#8217;s response to this situation was ideal or not: whether she should have posted the picture publicly, whether she gloated or not. That misses the point that everyone is responsible for responding to the situation appropriately. If the employer fired people as a gut reaction, then they are responsible for overreacting. While I am comfortable questioning some aspects of Adria&#8217;s response, I am not at all comfortable with telling her to be quiet and think of what this might mean for the people making unwelcoming comments.</p>
<h2>A critical question</h2>
<p>What do we do when no one person is severely violating the Code of Conduct, but instead we have a fair number of people throughout the conference making moderately inappropriate comments that can be just brushed off? Many people have grown a &#8220;thick skin&#8221; and do just that, but this response avoids the problem. Women shouldn&#8217;t have to grow &#8220;thick skin&#8221; to go into a technical field.</p>
<h2>Making the Code of Conduct more visible at PyCon 2014</h2>
<p>The PyCon staff did a great job of publicizing the new Code of Conduct this year. For those of us who understand and value the Code of Conduct, we saw it all around us in different forms. We saw the Education Summit, we saw the Young Coder&#8217;s workshop, we saw the PyLadies booth, and many other visible examples of a community that values diversity. Next year, we can take this a step further and make it visible to everyone exactly who supports the Code of Conduct.</p>
<p>One of Adria&#8217;s goals was clear: she wanted to take away the anonymity which allows this behavior to remain pervasive. There is a simple way we can do this for Adria, and for everyone else who was made to feel unwelcome at some point. We can use the <a title="White Ribbon campaign" href="http://www.whiteribbon.ca/">White Ribbon</a> concept, and <a title="The tech world needs a white ribbon campaign" href="http://peak5390.wordpress.com/2012/03/23/the-tech-world-needs-a-white-ribbon-project/">apply it to the conference setting</a>.</p>
<p>What would a White Ribbon campaign look like at PyCon? When you go to register at PyCon 2014, you would see a pile of small white ribbons next to the name badges. It doesn&#8217;t matter if they are actual ribbons, or small stickers in the shape of a ribbon. When you take your badge, you have the option of sticking one of the ribbons on your badge. Wearing the ribbon is a public, visible way of saying:</p>
<blockquote><p>I understand the Code of Conduct, and I am willing to call out unwelcoming behavior and comments when and where I see it. I will not just laugh uncomfortably when I hear comments with obvious innuendo. I will not give tacit approval to this kind of behavior.</p></blockquote>
<p>How does this change the situation? It does so in a number of specific ways:</p>
<ul>
<li>It takes the resopnsibility of calling out unwelcoming behavior off of individuals like Adria, and shares that responsibility between everyone wearing a white ribbon.</li>
<li>If enough people wear the ribbon, it makes it clear to most people, just by looking around them, that innuendo is not going to be received well.</li>
<li>It obviates the need for PyCon staff to intervene, except in the case of the most egregious offenders.</li>
<li>It is non-confrontational, but effective.</li>
</ul>
<p>If it works, then this becomes a symbol that can be adopted by other conferences, and in other contexts as well. The PyCon community has come a long way in the last few years towards becoming a fully welcoming community, where no one has to have &#8220;thick skin&#8221;. Let&#8217;s get all the way there.</p>
<h2>Clarifications, 3/23</h2>
<p>This issue has blown up to receive attention from an audience much larger than the attendees of PyCon. So I will offer a few clarifications after watching reactions to this post from the past few days:</p>
<p>Many commenters are responding to my use of the word &#8220;victim&#8221;. Of course hearing a dongle joke and a possible innuendo about forking does not put anyone in the same class of being a victim as someone who is assaulted. I still believe, that when this all started, Adria was trying to stand up to a culture where women have to listen to sexual innuendo on a steady basis. I know from speaking with many colleagues and friends that this culture is not comfortable in professional contexts for many women.</p>
<p>The ensuing flare-up of attention really came from PlayHaven&#8217;s firing of one of their employees as a result of this incident. If that had not happened, this event would not have grabbed the attention of people all over the internet. That is especially true when you acknowledge that no one was kicked out of the conference. &#8220;Men spoken briefly to by PyCon staff&#8221; does not grab anyone&#8217;s attention like &#8220;Man fired for making joke about dongles&#8221;.</p>
<p>Despite the role that PlayHaven played in this incident capturing everyone&#8217;s attention, the majority of disparaging remarks and attacks have been directed at Adria and SendGrid. Adria may have gloated a bit and made it harder to sympathize with her, but nothing she has done warrants attacks against her and SendGrid. Rational arguments are entirely appropriate, but attacks and threats are not.  I don&#8217;t think people should post pictures of people who annoy them publicly on a regular basis. I do recognize, however, that some form of activism like that can serve to bring an issue into a larger realm of discussion. I think that was the role Adria was originally trying to play.</p>
<p>The white ribbon concept does not need to be taken literally. There is a white ribbon project, in which men wear actual ribbons to signify their willingness to speak out against violence towards women. The point I was trying to make is that it might be a good idea to let people make a small visual indication that they support having a code of conduct. This makes it clear to many people that we do want a culture where people can let go of the thick skin they have developed elsewhere.</p>
<p>Most of the comments on this post are negative and dismissive. But there have been a number of &#8220;likes&#8221; and retweets of this article by people I respect in the Python community. So there are some ideas here that do resonate with people who have direct experience in the Python community. I can understand why people would not write supportive comments for ideas like these right now, given the overwhelming sentiment being expressed in these comments and elsewhere. I make no assumptions about how much support is out there, either.</p>
<p>In closing, the PyCon staff themselves have acted appropriately throughout the conference and during the ensuing flare-up. Their approaches to encouraging and supporting diversity are paying off in a variety of very visible, and measurable ways.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/2017/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/2017/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2017&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/03/20/pycons-code-of-conduct-the-next-step/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>
	</item>
		<item>
		<title>Lightning Talk: Abolishing the traditional high school structure</title>
		<link>http://peak5390.wordpress.com/2013/03/17/lightning-talk-abolishing-the-traditional-high-school-structure/</link>
		<comments>http://peak5390.wordpress.com/2013/03/17/lightning-talk-abolishing-the-traditional-high-school-structure/#comments</comments>
		<pubDate>Sun, 17 Mar 2013 08:45:35 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[education]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[competency education]]></category>
		<category><![CDATA[pycon 2013 lightning talk]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=2007</guid>
		<description><![CDATA[Update: You can view the actual talk on YouTube. There is much in public education that works well; more than is often reported in the current political climate.  But there is also much about education that is done poorly.  When &#8230; <a href="http://peak5390.wordpress.com/2013/03/17/lightning-talk-abolishing-the-traditional-high-school-structure/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2007&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Update: You can view the <a title="Lightning Talk: Abolishing the traditional 9th-12th grade high school structure" href="http://www.youtube.com/watch?feature=player_detailpage&amp;v=OL3De8BAhME#t=2189s">actual talk on YouTube</a>.</p>
<p>There is much in public education that works well; more than is often reported in the current political climate.  But there is also much about education that is done poorly.  When education is done poorly, it can be traumatic to many people.  Competency-based education restructures the school environment in a profound way.  <a title="Making Mastery Work - A Close Up View of Competency Education" href="http://www.nmefoundation.org/global/featured-research/homepage/making-mastery-work-a-close-up-view-of-competency">Recent education research</a> shows that competency education can be really effective.  However, almost every school that implements competency education runs into similar technical issues.</p>
<p>The following document gives a one-page overview of competency education, and an outline for some open tools that would help schools implement competency education efficiently:</p>
<div id="attachment_2008" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/03/abolishing-the-traditional-high-school-structure-pages.png"><img class="size-large wp-image-2008" alt="A one-page overview of competency education, and some open tools that can support competency ed." src="http://peak5390.files.wordpress.com/2013/03/abolishing-the-traditional-high-school-structure-pages.png?w=640&#038;h=413" width="640" height="413" /></a><p class="wp-caption-text">A one-page overview of competency education, and some open tools that can support competency ed.</p></div>
<p>Here is a slideshow covering the same material:</p>
<iframe class="scribd_iframe_embed" src="http://www.scribd.com/embeds/130835927/content?start_page=1&view_mode=&access_key=key-165d5ypaejlg3f8dwmqn" data-auto-height="true" scrolling="no" id="scribd_130835927" width="100%" height="500" frameborder="0"></iframe>
<div style="font-size:10px;text-align:center;width:100%"><a href="http://www.scribd.com/doc/130835927">View this document on Scribd</a></div>
<p>We are starting to work on the first piece, opencompetencies.  You might want to visit the <a title="Open Competencies repo" href="https://github.com/openlearningtools/opencompetencies">github repo</a>.  If you have questions or comments about this project, please get in touch!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/2007/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/2007/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=2007&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/03/17/lightning-talk-abolishing-the-traditional-high-school-structure/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/03/abolishing-the-traditional-high-school-structure-pages.png?w=640" medium="image">
			<media:title type="html">A one-page overview of competency education, and some open tools that can support competency ed.</media:title>
		</media:content>
	</item>
		<item>
		<title>A Gittip Milestone:  $100k per year in distributions</title>
		<link>http://peak5390.wordpress.com/2013/03/07/a-gittip-milestone-100k-per-year-in-distributions/</link>
		<comments>http://peak5390.wordpress.com/2013/03/07/a-gittip-milestone-100k-per-year-in-distributions/#comments</comments>
		<pubDate>Thu, 07 Mar 2013 23:52:42 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[crowd funding]]></category>
		<category><![CDATA[gittip]]></category>
		<category><![CDATA[open source]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=1980</guid>
		<description><![CDATA[Many people have been watching Gittip, wondering if it will grow to have a significant impact on the open source ecosystem.  Here is an interesting calculation I punched into my phone this morning: $1933.14 x 52 = $100,523.28 Gittip is &#8230; <a href="http://peak5390.wordpress.com/2013/03/07/a-gittip-milestone-100k-per-year-in-distributions/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1980&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<div id="attachment_1982" class="wp-caption alignright" style="width: 210px"><a href="http://peak5390.files.wordpress.com/2013/03/photo.png"><img class="size-medium wp-image-1982" alt="Gittip is now distributing over $100,000 per year to leaders of open source projects." src="http://peak5390.files.wordpress.com/2013/03/photo.png?w=200&#038;h=300" width="200" height="300" /></a><p class="wp-caption-text">Gittip is now distributing over $100,000 per year to leaders of open source projects.</p></div>
<p>Many people have been watching <a title="Gittip home page" href="https://www.gittip.com/">Gittip</a>, wondering if it will grow to have a significant impact on the open source ecosystem.  Here is an interesting calculation I punched into my phone this morning:</p>
<p style="text-align:center;"><strong>$1933.14 x 52 = $100,523.28</strong></p>
<p>Gittip is now distributing over $100,000 per year!  This is an important milestone.</p>
<p>In case you are unfamiliar with Gittip, it is a unique take on <a title="Gittip: About" href="https://www.gittip.com/about/">crowdfunding open source projects</a>.  If you want to support someone in their work on an open source project, you can register for a Gittip account.  You decide how much you want to &#8220;tip&#8221; the recipient each week, and then make a deposit.  Gittip withdraws the tip amount from your balance each week, and distributes it to the recipient of your choosing.  The minimum tip is $0.25, and the maximum you can tip any one person is $24.  Gittip&#8217;s goal is to offer predictable, sustainable, long-term support to the leaders of important open source and public service projects.</p>
<div id="attachment_1985" class="wp-caption alignright" style="width: 310px"><a href="http://peak5390.files.wordpress.com/2013/03/gittip-gifts.png"><img class=" wp-image-1985 " alt="The overall funding level on Gittip has been rising slowly but steadily for over 40 weeks now." src="http://peak5390.files.wordpress.com/2013/03/gittip-gifts.png?w=300&#038;h=122" width="300" height="122" /></a><p class="wp-caption-text">The overall funding level on Gittip has been rising slowly but steadily for over 40 weeks now.</p></div>
<p>The overall funding level on Gittip has been rising slowly but steadily.  $100,000 in annual distributions is an important milestone, because we are one step closer to seeing people earn their entire living through Gittip donations.  Right now the highest-tipped person is receiving $188.23 per week, which translates to $9,787.96 annually.  The tenth highest-tipped person is receiving $34.13 per week, or $1,774.76 annually.  We are still a long way from seeing any one person receive $100k per year through Gittip, but the overall platform achieving that level of participation is a good sign.</p>
<p>When we start to see people earning a living wage through Gittip, we will have a new patronage system on our hands.  Many people would consider a salary of $100,000 pretty luxurious, but when you consider taking out taxes and health insurance, we are talking about a moderate developer&#8217;s salary.  The kind of open source leaders who will earn this level of support through donations are certainly capable of earning a higher salary than this in the tech world.  This level of salary would mean that at least 4,000 people are supporting your work.  This broad support base would give the recipient a tremendous level of freedom in what projects they choose to focus on.</p>
<p>I loved Gittip the moment I saw it, and I hoped from the beginning to see steady growth in the project.  I am very happy to see this milestone come and go, and I look forward to the next significant milestone.  Now, back to working on my first Gittip-worthy open source project!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/1980/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/1980/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1980&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/03/07/a-gittip-milestone-100k-per-year-in-distributions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/03/photo.png?w=200" medium="image">
			<media:title type="html">Gittip is now distributing over $100,000 per year to leaders of open source projects.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/03/gittip-gifts.png?w=300" medium="image">
			<media:title type="html">The overall funding level on Gittip has been rising slowly but steadily for over 40 weeks now.</media:title>
		</media:content>
	</item>
		<item>
		<title>Balloon Ninja:  Wrapping Up</title>
		<link>http://peak5390.wordpress.com/2013/03/02/balloon-ninja-wrapping-up/</link>
		<comments>http://peak5390.wordpress.com/2013/03/02/balloon-ninja-wrapping-up/#comments</comments>
		<pubDate>Sat, 02 Mar 2013 16:45:47 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[balloon ninja]]></category>
		<category><![CDATA[phsgeek]]></category>
		<category><![CDATA[pygame]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=1844</guid>
		<description><![CDATA[Back to Tutorial Contents Balloon Ninja on GitHub In the last section we added some kittens to the game to make it more challenging, and we cleaned up Engine.py to be a true class. In this section we will post our &#8230; <a href="http://peak5390.wordpress.com/2013/03/02/balloon-ninja-wrapping-up/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1844&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<h2>Balloon Ninja on GitHub</h2>
<p>In the last section we <a title="Balloon Ninja:  Kittens!" href="http://peak5390.wordpress.com/2013/02/26/balloon-ninja-kittens/">added some kittens</a> to the game to make it more challenging, and we cleaned up Engine.py to be a true class. In this section we will post our project to GitHub, and make a list of features we&#8217;d like to implement if we were to take this project further.</p>
<p>If you are unfamiliar with it, <a title="GitHub" href="https://github.com/">GitHub</a> is a site for posting open-source projects. When a project is posted to GitHub, people can access your source code directly. They can contribute to your project, or they can &#8220;fork&#8221; your project and take it in their own direction. Before adding a project to GitHub, it is important to include a README file, and it is a good idea to include a LICENSE file as well. Here is the README.txt file for balloon_ninja:</p>
<pre class="brush: plain; title: ; wrap-lines: true; notranslate">Balloon Ninja is a simple game, intended to help beginners learn the Pygame framework.

Balloon Ninja is maintained by Eric Matthes:
ehmatthes@gmail.com
@ehmatthes

There is a full tutorial writeup at:

http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/

Requirements:  pygame

To run, checkout the repo and run balloon_ninja.py:
$ python balloon_ninja.py

License:
Balloon Ninja is released under the MIT License.  See LICENSE.txt</pre>
<p>There are a few basic things that go into a README file. We tell people a little about the project. We tell them who we are, and we may give people a way to contact us if they want to help out on the project. We can link to outside resources related to the project, such as this tutorial. We list the requirements for running the project, such as pygame for this project. We tell people how to run the project once they have it loaded onto their local machine. Finally, we state how the project is licensed.</p>
<p>There are an abundance of open-source licenses available to choose from. If you have not thought much about licensing before, it&#8217;s a good idea to visit the <a title="Open Source Initiative" href="http://opensource.org/">Open Source Initiative</a> site. There are a few <a title="OSI:  Popular open-source licenses" href="http://opensource.org/licenses">common open-source licenses</a> that should fit most people&#8217;s needs. The <a title="OSI: MIT license" href="http://opensource.org/licenses/MIT">MIT license</a> is a standard OS license, and should fit the purposes of this project. Briefly, the MIT license is a permissive license which allows people to &#8220;use, copy, modify, merge, publish, distribute, sublicense, and/or sell&#8221; the software. If anyone redistributes the software in any manner, they need to include the original copyright and the original MIT license.</p>
<p>To use the MIT license, you simply copy and paste the text of the license into a LICENSE.txt file, and include your name and the copyright year:</p>
<pre class="brush: plain; title: ; wrap-lines: true; notranslate">Copyright (c) 2013 Eric Matthes

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &quot;Software&quot;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.</pre>
<p>Now we have a complete project which we can push to a GitHub repository. I won&#8217;t go into a full explanation of how to use git here, but if you know how to use git locally I&#8217;ll give brief directions for <a title="GitHub Help:  Creating a Repository" href="https://help.github.com/articles/create-a-repo">posting your project to GitHub</a>. To post the project, you need a GitHub profile. Once you have registered at GitHub, click &#8220;New Repository&#8221;. Give your project a name, which should probably match the local name of your project. Then within your local project directory, issue the command:</p>
<pre class="brush: bash; title: ; wrap-lines: false; notranslate">git remote add origin https://github.com/username/balloon_ninja.git</pre>
<p>Now you can push your local project to your GitHub repository:</p>
<pre class="brush: bash; title: ; wrap-lines: false; notranslate">$ git push origin master</pre>
<p>This pushes your current master to your new GitHub repository. Here is what Balloon Ninja looks like <a title="Balloon Ninja on github" href="https://github.com/ehmatthes/balloon_ninja">on GitHub</a>:</p>
<div id="attachment_1883" class="wp-caption aligncenter" style="width: 650px"><a href="https://github.com/ehmatthes/balloon_ninja"><img class="size-large wp-image-1883" alt="Balloon Ninja on github." src="http://peak5390.files.wordpress.com/2013/03/bn_github.png?w=640&#038;h=350" width="640" height="350" /></a><p class="wp-caption-text">Balloon Ninja on github.</p></div>
<h2><a name="run_balloon_ninja"></a>Running Balloon Ninja</h2>
<p>Once we have a repository created, anyone can run our project. To try it out, navigate to a directory where you want to download a copy of the program and issue the following commands:</p>
<pre class="brush: bash; highlight: [1,8,9]; title: ; wrap-lines: false; notranslate">$ git clone git://github.com/ehmatthes/balloon_ninja
Cloning into balloon_ninja...
remote: Counting objects: 29, done.
remote: Compressing objects: 100% (23/23), done.
remote: Total 29 (delta 6), reused 27 (delta 5)
Receiving objects: 100% (29/29), 18.31 KiB, done.
Resolving deltas: 100% (6/6), done.
$ cd balloon_ninja
balloon_ninja$ python balloon_ninja.py</pre>
<p>The &#8220;clone&#8221; command creates a copy of the remote repository on your local system. The repository is created in its own directory in the current location. To run the program, cd into this new directory and run the main program file.</p>
<h2>Where to go from here?</h2>
<p>My main goal in writing this tutorial was to help people get started in using Pygame, by demonstrating the kind of thinking that goes into creating a game. Hopefully this has helped you become more comfortable starting to create your own game. If we were to develop Balloon Ninja further, there are many things we could do to make it more fun and interesting. Here are a few ideas:</p>
<ul>
<li>Add a &#8220;Pop All&#8221; feature.  When the player presses the space bar, all balloons in the current batch are popped, and you get points for them all.  You start out with one PopAll, and you can earn more at certain point levels.  Pop All leaves kittens to rise to the top of the screen on their own, where they will be scored.</li>
<li>Add sound.  The balloons make a popping sound when they are slashed, kittens cry out when they are slashed.  Maybe balloons and kittens make different sounds as they rise off the screen.</li>
<li>Add difficulty settings.  Players can control the settings that affect overall game play, such as speed scale, kitten ratio, batches needed to increase batch size, etc.</li>
<li>Keep track of the high score.</li>
<li>Scale the size of balloons and kittens.  Balloons get smaller as the game progresses, and kittens get larger.</li>
<li>Add a randomized balloon size factor.  In each batch, their will be different size balloons. If you do this, make the point values an attribute of each balloon, so that smaller balloons can be worth more than larger balloons.</li>
<li>Add &#8220;wind&#8221; to make the balloons drift horizontally.  Let wind value take a random walk.  This means the wind speed is initially 0.  Pick a random number between 0 and 1.  If the number is greater than 0.5, increase the wind speed.  If it is less than 0.5, decrease the wind speed.</li>
</ul>
<p>If you are just learning Pygame, it might be fun to try to implement any number of these features.  If you do something interesting with Balloon Ninja, please <a title="About - peak 5390" href="http://peak5390.wordpress.com/about/">get in touch</a> and share your final version.</p>
<p>If you are about to make your first game, and you want to keep things simple, consider making a game that only involves horizontal and vertical motion.  Try to clone a game such as Space Invaders, or make a simple game where you have to move a character out of a maze.  Once you are comfortable with the Pygame framework, you can move on to more advanced games.  Also, you might want to look at the &#8220;projects&#8221; sidebar on the <a title="Pygame home page" href="http://www.pygame.org/news.html">Pygame</a> home page.  There is a long list of games you can download and play, and then explore the sourcecode to see how  the game was implemented.  For example, here are a number of <a title="Pygame: space invaders" href="http://www.pygame.org/tags/spaceinvaders">Space Invaders clones</a>.</p>
<h2>Conclusion</h2>
<p>If you have any feedback, please feel free to <a title="About - peak 5390" href="http://peak5390.wordpress.com/about/">get in touch</a>.  If you have any questions, please feel free to post them to the comments and I will be happy to answer them there.  Thank you for reading, and good luck with your games!</p>
<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/1844/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/1844/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1844&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/03/02/balloon-ninja-wrapping-up/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/03/bn_github.png?w=640" medium="image">
			<media:title type="html">Balloon Ninja on github.</media:title>
		</media:content>
	</item>
		<item>
		<title>Dealing with Bullying:  An effective approach</title>
		<link>http://peak5390.wordpress.com/2013/02/26/dealing-with-bullying-an-effective-approach/</link>
		<comments>http://peak5390.wordpress.com/2013/02/26/dealing-with-bullying-an-effective-approach/#comments</comments>
		<pubDate>Wed, 27 Feb 2013 08:03:15 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[education]]></category>
		<category><![CDATA[bullying]]></category>
		<category><![CDATA[olweus violence prevention]]></category>
		<category><![CDATA[prevention]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=1851</guid>
		<description><![CDATA[A Brief Story I teach math and science at a small high school in southeast Alaska, and my classroom window looks out on the playground of a kindergarten-first grade school. I have a clear view every day of the back &#8230; <a href="http://peak5390.wordpress.com/2013/02/26/dealing-with-bullying-an-effective-approach/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1851&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<h2>A Brief Story</h2>
<p>I teach math and science at a small high school in southeast Alaska, and my classroom window looks out on the playground of a kindergarten-first grade school. I have a clear view every day of the back of the playground, a place that doesn&#8217;t always get the most supervision. My students and I watch little kids play there all day long. Most of the bullying we see is low-level shoving and name-calling, which appears to be self-regulated by peer groups.</p>
<p>Last week, I finally saw something that I needed to interrupt. I watched a kindergarten kid grab another kid by the collar, and shove him against a chain link fence with one hand. The bully made a gun out of his other hand, put it right up to the other kid&#8217;s face, and started saying something to the kid.</p>
<p>I walked over and cheerfully but assertively introduced myself. &#8220;Hi, I&#8217;m Eric, what&#8217;s your name?&#8221; The bully looked totally surprised, backed away from the other kid, and answered me honestly. As I listened to the bully, I remembered something I had learned about dealing with bullies effectively. The best approach is actually counter-intuitive: rather than giving in to the strong urge to confront the bully, it is much more effective to give your attention to the victim of the bullying. So I turned to the other kid and asked him his name. When he turned around to face me, my heart broke. He had snot running from both nostrils, and he just looked like a kid who got picked on a lot. But he answered me as well. I said to him, &#8220;It doesn&#8217;t look like you were being treated very well; I&#8217;m sorry you&#8217;ve had to deal with this today.&#8221; He agreed, and as I held a conversation with him for a moment I watched the five or six other kids who had been gathered around the scene from the beginning. They circled closer around the victim, but in a positive way. The bully continued to backpedal, but not in an attempt to get away. They had just all started to forget about him, and he seemed to have no place to be.</p>
<p>When the group dispersed, I called the bullying kid over again. I said to him, &#8220;Hey, you don&#8217;t really want to shoot anybody, do you?&#8221; &#8220;No!&#8221; he replied, and I suggested he might try to find other ways to play. He looked confused and thoughtful at the same time, as he wandered back to the school building.</p>
<h2>A powerful model of bullying dynamics</h2>
<p>I have been teaching for about 15 years now, and at this point I am very skeptical of commercial solutions to school-based issues. But a few years ago I attended a professional development workshop on the <a title="Olweus Bullying Prevention program" href="http://www.violencepreventionworks.org/public/index.page">Olweus Bullying Prevention</a>&#8216;s approach to dealing with bullying. I was completely impressed with how well this organization had studied the dynamics of bullying, with how well they have modeled those dynamics, and with their concrete recommendations for dealing with bullying.</p>
<h3>A clear definition of bullying</h3>
<p>The Olweus organization offers a clear <a title="Olweus:  What is bullying?" href="http://www.violencepreventionworks.org/public/faqs.page#Answer_numberCbQ1">definition of bullying</a>:</p>
<ul>
<li>Bullying is negative behavior aimed at a person who will have difficulty defending themselves;</li>
<li>Bullying is repetitive in nature;</li>
<li>Bullying is carried out by someone with an imbalance of power over someone else.</li>
</ul>
<h3>A powerful model of the bullying dynamic</h3>
<p>The Olweus model of bullying dynamics identifies eight roles that individuals typically play in a bullying situation. In the Olweus model, these roles are signified by letters. The following diagram of these roles is taken from an <a title="Hit Back at Bullies? Not at this school" href="http://www.npr.org/templates/story/story.php?storyId=125137071">NPR feature</a> about a school that has implemented the Olweus bullying and violence prevention model. The diagram itself was created by <a title="Olweus Teacher guide" href="http://www.hazelden.org/OA_HTML/ibeCCtpItmDspRte.jsp?AID=10273664&amp;PID=3444383&amp;item=9438&amp;sitex=10020:22372:US">Hazelden</a> <a title="Olweus Schoolwide guide" href="http://www.hazelden.org/OA_HTML/ibeCCtpItmDspRte.jsp?AID=10273664&amp;PID=3444383&amp;item=9444&amp;sitex=10020:22372:US">publishing</a>:</p>
<div id="attachment_1858" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/olweus_roles.jpg"><img class="size-large wp-image-1858" alt="The main roles in a typical bullying scenario.  We want to minimize the number of individuals on the left, and teach people how to play the roles on the right." src="http://peak5390.files.wordpress.com/2013/02/olweus_roles.jpg?w=640&#038;h=455" width="640" height="455" /></a><p class="wp-caption-text">The main roles in a typical bullying scenario. We want to minimize the number of individuals on the left, and teach people how to play the roles on the right.</p></div>
<ul>
<li><strong>A &#8211; The Bully.</strong> This is the person who leads the bullying. The bully is not necessarily the loudest person in the situation.</li>
<li><strong>B &#8211; Followers.</strong> These are people who are actively engaged in the bullying, even though they are not leading it.</li>
<li><strong>C &#8211; Supporters.</strong> These are the people who are outwardly supporting the bullying, without taking an active part in it. They are cheering on the bullying, laughing at it, and blocking access to help.</li>
<li><strong>D &#8211; Passive Supporters.</strong> These people want the bullying to continue, but are not openly supporting it.</li>
<li><strong>E &#8211; Disengaged Onlookers.</strong> These people don&#8217;t support the bullying, but don&#8217;t care to stop it either.</li>
<li><strong>F &#8211; Possible Defenders.</strong> These people do not like what they see. They would like to stop the bullying and help the victim, but they do not know how to do so.</li>
<li><strong>G &#8211; Defenders.</strong> These are the people who actively support the person who is being bullied, and who may confront the bully and the people who are supporting the bullying.</li>
<li><strong>H &#8211; The person who is being bullied.</strong></li>
</ul>
<p>These roles provide a powerful framework for breaking down bullying situations. With a little training, it is fairly easy to spot which role each individual is playing in any given situation. It is important to recognize, however, that individuals can play multiple roles. We can easily have &#8220;supporters&#8221; who are outwardly laughing about the bullying, while feeling bad about it and wishing they could do something to stop it.</p>
<h3>A clear recommendation for interrupting bullying</h3>
<p>There is a lot to the Olweus model, so I will share what I took away from the training I attended. For most of us, our instinctive reaction when we want to interrupt a bullying situation is to focus on confronting the bully. But this is counterproductive; it only serves to give the bully more attention, and more power. It is actually much more effective to take the counterintuitive approach of focusing on the person who is being bullied. We can talk to them in a supportive way, or offer to leave the situation with them. When we do this, we take the focus off of the bully and give the victim some power back.</p>
<p>This approach also offers us some guidance in dealing effectively with bullying over the long term. If bullying is happening in our community, we need to focus our overall approach on supporting people who are being bullied. If we keep this mindset, we will develop effective ways of holding bullies accountable, and educating them. But that will be done to serve the goal of supporting victims, rather than with a focus on punishing people.  Punishment may be necessary, but that punishment should serve the end of making a safe space for victims, not as an end in itself.</p>
<p>We all recognize that nothing is easy about dealing with an entrenched culture of bullying. In our staff conversation, we differentiated between bullying and assault. If we try to support a person who is being bullied, and it doesn&#8217;t work because the bully is so aggressive that they are going to hurt two people instead of one now, then we may be dealing with a person who has moved on from bullying and is now committing assault.</p>
<p>We also need to recognize that we don&#8217;t just support the victim and walk away, leaving the bully to go find another victim. If we are in a position of authority in the situation, we need to follow up by dealing directly with the bully. I won&#8217;t go into ways to deal with bullies at this point; that is a separate discussion. For now, the bigger point is that the best way to interrupt a bullying situation is often to focus on supporting the victim.</p>
<h2>Back to the story</h2>
<p>We can look back at the kindergarten playground situation in a more structured way now. First of all, we should ask whether it was actually bullying. The answer is a pretty simple yes:</p>
<ul>
<li>One person was acting aggressively toward another, and the victim appeared unlikely to defend himself.</li>
<li>I can&#8217;t say for sure that this was a repetitive situation, but I am comfortable guessing this was not the first time this person picked on this victim.</li>
<li>Finally, the bully was in a position of power by virtue of being stronger, more confident, and more approved of by his peers.</li>
</ul>
<p>Even at the kindergarten level, these dynamics apply.</p>
<p>Looking around the small crowd of students, I could see clear roles being played. There was one main aggressor, but the other students were standing around the victim in a way that probably made him feel cut off from help. I did not let the situation play out long enough to see if anyone would come to his defense. I can say, however, that as soon as I interrupted the situation a number of these students seemed relieved. And that is our point &#8211; that if we understand the dynamics of bullying situations, we can help bystanders develop the skills to interrupt bullying when and where it happens.</p>
<h2>Conclusion</h2>
<p>I am a practical person, and I believe there will always be bullying. But that is human nature. What matters is, do we have a clear way recognizing bullying when it begins to happen, and do we have enough people around who know how to deal with bullying situations effectively? If we do, we can keep bullying from destroying people&#8217;s lives as it can so easily do when it goes unchecked.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/1851/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/1851/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1851&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/02/26/dealing-with-bullying-an-effective-approach/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/olweus_roles.jpg?w=640" medium="image">
			<media:title type="html">The main roles in a typical bullying scenario.  We want to minimize the number of individuals on the left, and teach people how to play the roles on the right.</media:title>
		</media:content>
	</item>
		<item>
		<title>Balloon Ninja: Kittens!</title>
		<link>http://peak5390.wordpress.com/2013/02/26/balloon-ninja-kittens/</link>
		<comments>http://peak5390.wordpress.com/2013/02/26/balloon-ninja-kittens/#comments</comments>
		<pubDate>Tue, 26 Feb 2013 16:29:43 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[balloon ninja]]></category>
		<category><![CDATA[phsgeek]]></category>
		<category><![CDATA[pygame]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=1784</guid>
		<description><![CDATA[Back to Tutorial Contents Kittens! In the last section we improved the scoring system, which made the game a little more interesting. But so far the game strategy for playing Balloon Ninja is pretty simple. You just grab the sword, and &#8230; <a href="http://peak5390.wordpress.com/2013/02/26/balloon-ninja-kittens/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1784&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<h2>Kittens!</h2>
<p>In the last section we <a title="Balloon Ninja: A better scoring system" href="http://peak5390.wordpress.com/2013/02/21/balloon-ninja-a-better-scoring-system/">improved the scoring system</a>, which made the game a little more interesting. But so far the game strategy for playing Balloon Ninja is pretty simple. You just grab the sword, and swipe away at balloons. The only challenging aspects of the game are the steadily increasing number of balloons, and the steadily increasing speed of the balloons. Let&#8217;s add a new challenge: kittens!  Once implemented, the player will have to swipe as many balloons as possible while trying not to kill any kittens.</p>
<p>Our ultimate goal is to release about one kitten for every ten balloons. For now, let&#8217;s just try to make a kitten appear with every balloon. Once that is working, we will refine the logic for how often a kitten should be released. First, we need a Kitten.py file, which is similar to our Balloon.py file. A kitten is really just a balloon with a different image, so the Kitten.py file is very simple:</p>
<pre class="brush: python; title: ; wrap-lines: false; notranslate">import pygame
from Balloon import Balloon

class Kitten(Balloon):

    def __init__(self, screen, settings):

        Balloon.__init__(self, screen, settings)
        self.image = pygame.image.load('kitten_50px.png').convert_alpha()</pre>
<p>We need to import Balloon because the Kitten class inherits from the Balloon class, and we need to import pygame because we use the pygame.image.load function in the Kitten class. On line 4, we tell Python that the Kitten class inherits all of Balloon&#8217;s attributes and behavior. On line 6, we need to receive all of the information needed to create a balloon. On line 8, we give our kitten object all of the functionality of a balloon. Finally, on line 9, we override the balloon image with our kitten image.</p>
<p>I found an <a title="kitten image" href="http://www.flickr.com/photos/marcohebing/4043369767/">image of a kitten</a> that was licensed for reuse, and modified it by cropping, scaling, and making the background transparent. Here is the new image, which you should be able to right-click and save:</p>
<div id="attachment_1804" class="wp-caption aligncenter" style="width: 460px"><a href="http://peak5390.files.wordpress.com/2013/02/kitten_50px.png"><img class="size-full wp-image-1804" alt="A nice kitten, with a transparent background." src="http://peak5390.files.wordpress.com/2013/02/kitten_50px.png?w=640"   /></a><p class="wp-caption-text">A nice kitten, with a transparent background.</p></div>
<p>Now, we need to add some functions to Engine.py that will let us spawn a kitten:</p>
<pre class="brush: python; highlight: [4,11,13,15,27,39,40,41,42,63,65,67,68]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon
from Button import Button
from Kitten import Kitten

class Engine():

    def __init__(self):
        pass

    def release_batch(self, screen, settings, balloons, kittens):
        for x in range(0, settings.batch_size):
            self.spawn_balloon(screen, settings, balloons, kittens)

    def check_balloons(self, balloons, kittens, sword, scoreboard, screen, settings, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(sword.rect):
                self.pop_balloon(scoreboard, settings, balloon, balloons)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
                self.miss_balloon(scoreboard, balloon, balloons)
                self.spawn_balloon(screen, settings, balloons, kittens)
                continue

            balloon.blitme()

        if scoreboard.balloons_popped &gt; 0 or scoreboard.balloons_missed &gt; 0:
            scoreboard.popped_ratio = float(scoreboard.balloons_popped)/(scoreboard.balloons_popped + scoreboard.balloons_missed)
            if scoreboard.popped_ratio &lt; settings.min_popped_ratio:
                # Set game_active to false, empty the list of balloons, and increment games_played
                settings.game_active = False
                settings.games_played += 1

    def check_kittens(self, kittens, sword, scoreboard, screen, settings, time_passed):
        for kitten in kittens:
            kitten.update(time_passed)
            kitten.blitme()

    def update_sword(self, sword, mouse_x, mouse_y, settings):
        # Update the sword's position, and draw the sword on the screen
        sword.x_position = mouse_x
        if sword.grabbed:
            sword.y_position = mouse_y
        else:
            sword.y_position = sword.image_h/2 + settings.scoreboard_height
        sword.update_rect()
        sword.blitme()

    def miss_balloon(self, scoreboard, balloon, balloons):
        scoreboard.balloons_missed += 1
        balloons.remove(balloon)

    def pop_balloon(self, scoreboard, settings, balloon, balloons):
        scoreboard.balloons_popped += 1
        scoreboard.score += settings.points_per_balloon
        balloons.remove(balloon)

    def spawn_balloon(self, screen, settings, balloons, kittens):
        balloons.append(Balloon(screen, settings))
        self.spawn_kitten(screen, settings, kittens)

    def spawn_kitten(self, screen, settings, kittens):
        kittens.append(Kitten(screen, settings))

    def check_events(self, settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if sword.rect.collidepoint(mouse_x, mouse_y):
                    sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del balloons[:]
                    scoreboard.initialize_stats()
                    settings.initialize_game_parameters()
                    settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                sword.grabbed = False
    </pre>
<p>There are a lot of little changes in here, so I will explain them in the order we implemented them. The first change is to import our new class Kitten, on line 4. Then we want to make a simple function to release a kitten, which is almost identical to the spawn_balloon function. This is a two-line function called spawn_kitten, on lines 67 and 68. This function uses a list called &#8220;kittens&#8221; that we will create in balloon_ninja, which is just like the list we use to hold our balloons.</p>
<p>Where do we call this function from? We are going to want to call it from spawn_balloon, so for now we will just add a simple line in spawn_balloon. This is line 65. We need access to the kittens list, so line 63 modifies spawn_balloon to receive the list of kittens. Working backwards, this means lines 13 and 11, and lines 27 and 15 also need access to the kittens list. (These argument lists are getting a bit messy, so we will clean them up shortly.)</p>
<p>Finally, we need a function similar to check_balloons for our kittens. So lines 39-42 define check_kittens, which simply updates the position of each kitten before blitting it to the screen.</p>
<p>To see our kittens in action, we need to make a few changes to balloon_ninja.py, mostly driven by our changes to Engine.py:</p>
<pre class="brush: python; highlight: [26,28,46,47,62]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button
from Engine import Engine
from Instructions import Instructions

def run_game():
    # Get access to our game settings
    settings = Settings()
    engine = Engine()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)
    instructions = Instructions(screen, settings)

    # Create a list to hold our balloons, and our kittens
    balloons = []
    kittens = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        engine.check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            engine.update_sword(sword, mouse_x, mouse_y, settings)
            engine.check_balloons(balloons, kittens, sword, scoreboard, screen, settings, time_passed)
            engine.check_kittens(kittens, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            if len(balloons) == 0:
                # If we are not just starting a game, increase the balloon speed and points per balloon,
                #  and increment batches_finished
                if scoreboard.balloons_popped &gt; 0:
                    #  Increase the balloon speed for each new batch of balloons.
                    settings.balloon_speed *= settings.speed_increase_factor
                    settings.points_per_balloon = int(round(settings.points_per_balloon * settings.speed_increase_factor))
                    scoreboard.batches_finished += 1
                # If player has completed required batches, increase batch_size
                if scoreboard.batches_finished % settings.batches_needed == 0 and scoreboard.batches_finished &gt; 0:
                    settings.batch_size += 1
                engine.release_batch(screen, settings, balloons, kittens)
        else:
            # Game is not active, so...
            #  Show play button
            play_button.blitme()
            #  Show instructions for first few games.
            if settings.games_played &lt; 3:
                instructions.blitme()
            #  If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

run_game()</pre>
<p>We create an empty list of kittens on line 28. After this, there are only three lines that need to send the list of kittens to Engine.py. These are the calls to check_balloons on line 46, to check_kittens on line 47, and to release_batch on line 62.</p>
<p>Now if we play the game we see a whole bunch of kittens, which don&#8217;t affect game play at all yet:</p>
<div id="attachment_1818" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/kittens.png"><img class="size-large wp-image-1818" alt="One kitten is released for every balloon, but they don't affect game play at this point." src="http://peak5390.files.wordpress.com/2013/02/kittens.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">One kitten is released for every balloon, but they don&#8217;t affect game play at this point.</p></div>
<h2>Taming the Kittens</h2>
<p>Right now we are releasing one kitten for every balloon that is released. We need to make this a smaller ratio. We will do this by creating a setting called kitten_ratio, and using that setting to control how often a kitten is released. Let&#8217;s modify Settings.py next:</p>
<pre class="brush: python; highlight: [37,38]; title: ; notranslate">class Settings():

    def __init__(self):
        # screen parameters
        self.screen_width, self.screen_height = 800, 600
        self.bg_color = 200, 200, 200
        self.scoreboard_height = 50

        self.button_width, self.button_height = 250, 50
        self.button_bg = (0,163,0)
        self.button_text_color = (235,235,235)
        self.button_font, self.button_font_size = 'Arial', 24

        # game status
        self.game_active = False

        # game over conditions
        self.min_popped_ratio = 0.9
        self.games_played = 0

        self.initialize_game_parameters()

    def initialize_game_parameters(self):
        # game play parameters
        self.balloon_speed = 0.1
        # How quickly the speed of balloons rises
        #  ~1.05 during testing and ~1.01 for actual play
        self.speed_increase_factor = 1.05
        self.points_per_balloon = 10

        # Number of balloons to release in a spawning:
        self.batch_size = 1

        # Number of batches that must be completed to increment batch_size
        self.batches_needed = 3

        # Ratio of kittens released per balloon released:
        self.kitten_ratio = 0.10</pre>
<p>This setting will result in a 10% probability that a kitten is released every time a balloon is released. That is, one kitten will be released for every 10 balloons, but the player will not be able to predict when a kitten is about to be released. With this setting, we can also increase the ratio throughout the game to make it progressively more challenging.</p>
<p>Now let&#8217;s modify Engine.py, to use this ratio:</p>
<pre class="brush: python; highlight: [5,66,67,68]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon
from Button import Button
from Kitten import Kitten
from random import random

class Engine():

    def __init__(self):
        pass

    def release_batch(self, screen, settings, balloons, kittens):
        for x in range(0, settings.batch_size):
            self.spawn_balloon(screen, settings, balloons, kittens)

    def check_balloons(self, balloons, kittens, sword, scoreboard, screen, settings, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(sword.rect):
                self.pop_balloon(scoreboard, settings, balloon, balloons)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
                self.miss_balloon(scoreboard, balloon, balloons)
                self.spawn_balloon(screen, settings, balloons, kittens)
                continue

            balloon.blitme()

        if scoreboard.balloons_popped &gt; 0 or scoreboard.balloons_missed &gt; 0:
            scoreboard.popped_ratio = float(scoreboard.balloons_popped)/(scoreboard.balloons_popped + scoreboard.balloons_missed)
            if scoreboard.popped_ratio &lt; settings.min_popped_ratio:
                # Set game_active to false, empty the list of balloons, and increment games_played
                settings.game_active = False
                settings.games_played += 1

    def check_kittens(self, kittens, sword, scoreboard, screen, settings, time_passed):
        for kitten in kittens:
            kitten.update(time_passed)
            kitten.blitme()

    def update_sword(self, sword, mouse_x, mouse_y, settings):
        # Update the sword's position, and draw the sword on the screen
        sword.x_position = mouse_x
        if sword.grabbed:
            sword.y_position = mouse_y
        else:
            sword.y_position = sword.image_h/2 + settings.scoreboard_height
        sword.update_rect()
        sword.blitme()

    def miss_balloon(self, scoreboard, balloon, balloons):
        scoreboard.balloons_missed += 1
        balloons.remove(balloon)

    def pop_balloon(self, scoreboard, settings, balloon, balloons):
        scoreboard.balloons_popped += 1
        scoreboard.score += settings.points_per_balloon
        balloons.remove(balloon)

    def spawn_balloon(self, screen, settings, balloons, kittens):
        balloons.append(Balloon(screen, settings))
        # Periodically release a kitten:
        if random() &lt; settings.kitten_ratio:
            self.spawn_kitten(screen, settings, kittens)

    def spawn_kitten(self, screen, settings, kittens):
        kittens.append(Kitten(screen, settings))

    def check_events(self, settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if sword.rect.collidepoint(mouse_x, mouse_y):
                    sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del balloons[:]
                    scoreboard.initialize_stats()
                    settings.initialize_game_parameters()
                    settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                sword.grabbed = False
    </pre>
<p>On line 6 we need to import the random function. On line 67 we generate a random number between 0.0 and 1.0, and if that number is less than our kitten_ratio we release a balloon.</p>
<p>Now let&#8217;s scale this ratio every time batch_size is increased, to release more kittens as the game goes on. We do this in balloon_ninja.py:</p>
<pre class="brush: python; highlight: [55,57]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button
from Engine import Engine
from Instructions import Instructions

def run_game():
    # Get access to our game settings
    settings = Settings()
    engine = Engine()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)
    instructions = Instructions(screen, settings)

    # Create a list to hold our balloons, and our kittens
    balloons = []
    kittens = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        engine.check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            engine.update_sword(sword, mouse_x, mouse_y, settings)
            engine.check_balloons(balloons, kittens, sword, scoreboard, screen, settings, time_passed)
            engine.check_kittens(kittens, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            if len(balloons) == 0:
                # If we are not just starting a game, increase the balloon speed and points per balloon,
                #  and increment batches_finished
                if scoreboard.balloons_popped &gt; 0:
                    #  Increase the balloon speed, and other factors, for each new batch of balloons.
                    settings.balloon_speed *= settings.speed_increase_factor
                    settings.kitten_ratio *= settings.speed_increase_factor
                    settings.points_per_balloon = int(round(settings.points_per_balloon * settings.speed_increase_factor))
                    scoreboard.batches_finished += 1
                # If player has completed required batches, increase batch_size
                if scoreboard.batches_finished % settings.batches_needed == 0 and scoreboard.batches_finished &gt; 0:
                    settings.batch_size += 1
                engine.release_batch(screen, settings, balloons, kittens)
        else:
            # Game is not active, so...
            #  Show play button
            play_button.blitme()
            #  Show instructions for first few games.
            if settings.games_played &lt; 3:
                instructions.blitme()
            #  If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

run_game()</pre>
<p>This is really a one-line change. On line 57, just after we scale the balloon speed, we scale the kitten_ratio by the same amount. It doesn&#8217;t come through in a screenshot, but when you play through the game now, you can clearly see a steady increase in the number of kittens being released.</p>
<h2>Tracking kittens that survive</h2>
<p>The kittens are really just decoration at this point. Let&#8217;s detect when they are hit and when they make it to the top of the screen. When they make it to the top of the screen unhurt we will award points, but when they are hit we will deduct points. Kittens are more valuable than balloons, though, so let&#8217;s consider every kitten to be worth three balloons. We will start by making another setting:</p>
<pre class="brush: python; highlight: [39,40]; title: ; wrap-lines: false; notranslate">class Settings():

    def __init__(self):
        # screen parameters
        self.screen_width, self.screen_height = 800, 600
        self.bg_color = 200, 200, 200
        self.scoreboard_height = 50

        self.button_width, self.button_height = 250, 50
        self.button_bg = (0,163,0)
        self.button_text_color = (235,235,235)
        self.button_font, self.button_font_size = 'Arial', 24

        # game status
        self.game_active = False

        # game over conditions
        self.min_popped_ratio = 0.9
        self.games_played = 0

        self.initialize_game_parameters()

    def initialize_game_parameters(self):
        # game play parameters
        self.balloon_speed = 0.1
        # How quickly the speed of balloons rises
        #  ~1.05 during testing and ~1.01 for actual play
        self.speed_increase_factor = 1.05
        self.points_per_balloon = 10

        # Number of balloons to release in a spawning:
        self.batch_size = 1

        # Number of batches that must be completed to increment batch_size
        self.batches_needed = 3

        # Ratio of kittens released per balloon released:
        self.kitten_ratio = 0.10
        # Relative value of kittens, in terms of balloons:
        self.kitten_score_factor = 3</pre>
<p>This is a simple value that we will multiply by the point value of a balloon, to determine the point value of a kitten. Now, we will make a similar set of changes in Scoreboard.py:</p>
<pre class="brush: python; highlight: [34,35]; title: ; wrap-lines: false; notranslate">import pygame, pygame.font
from pygame.sprite import Sprite

class Scoreboard(Sprite):

    def __init__(self, screen, settings):

        Sprite.__init__(self)
        self.screen = screen
        self.settings = settings

        self.initialize_stats()

        # Set dimensions and properties of scoreboard
        self.sb_height, self.sb_width = settings.scoreboard_height, self.screen.get_width()
        self.rect = pygame.Rect(0,0, self.sb_width, self.sb_height)
        self.bg_color=(100,100,100)
        self.text_color = (225,225,225)
        self.font = pygame.font.SysFont('Arial', 18)

        # Set positions of individual scoring elements on the scoreboard
        self.x_popped_position, self.y_popped_position = 20.0, 10.0
        self.x_ratio_position, self.y_ratio_position = 150, 10.0
        self.x_points_position, self.y_points_position = 350, 10.0
        self.x_score_position, self.y_score_position = self.screen.get_width() - 200, 10.0

    def initialize_stats(self):
        # Game attributes to track for scoring
        self.balloons_popped = 0
        self.balloons_missed = 0
        self.score = 0
        self.popped_ratio = 1.0
        self.batches_finished = 0
        self.kittens_spared = 0
        self.kittens_killed = 0

    def prep_scores(self):
        self.popped_string = &quot;Popped: &quot; + str(self.balloons_popped)
        self.popped_image = self.font.render(self.popped_string, True, self.text_color)

        self.score_string = &quot;Score: &quot; + format(self.score, ',d')
        self.score_image = self.font.render(self.score_string, True, self.text_color)

        self.set_ratio_string()
        self.popped_ratio_image = self.font.render(self.popped_ratio_string, True, self.ratio_text_color)

        self.points_string = &quot;Points per Balloon: &quot; + str(self.settings.points_per_balloon)
        self.points_image = self.font.render(self.points_string, True, self.text_color)

    def set_ratio_string(self):
        if self.popped_ratio == 1.0:
            self.popped_ratio_string = &quot;Pop Rate: 100%&quot;
        else:
            self.popped_ratio_string = &quot;Pop Rate: &quot; + &quot;{0:.3}%&quot;.format(self.popped_ratio*100.0)
        if self.popped_ratio &lt; 0.95:
            self.ratio_text_color = (255, 51, 51)
        else:
            self.ratio_text_color = self.text_color

    def blitme(self):
        # Turn individual scoring elements into images that can be drawn
        self.prep_scores()
        # Draw blank scoreboard
        self.screen.fill(self.bg_color, self.rect)
        # Draw individual scoring elements
        self.screen.blit(self.popped_image, (self.x_popped_position, self.y_popped_position))
        self.screen.blit(self.points_image, (self.x_points_position, self.y_points_position))
        self.screen.blit(self.score_image, (self.x_score_position, self.y_score_position))
        self.screen.blit(self.popped_ratio_image, (self.x_ratio_position, self.y_ratio_position))</pre>
<p>We define two new variables that we will track, kittens_spared and kittens_killed. We are not going to display stats about kittens at this point, but we will easily be able to do so at a later time if we start tracking them now.</p>
<p>Finally, we need to make some more significant changes to Engine.py. These are not complicated changes, however, because all of the new code is similar to code we have already written for handling balloons:</p>
<pre class="brush: python; highlight: [41,45,46,47,48,49,50,51,69,70,71,72,79,80,81,82]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon
from Button import Button
from Kitten import Kitten
from random import random

class Engine():

    def __init__(self):
        pass

    def release_batch(self, screen, settings, balloons, kittens):
        for x in range(0, settings.batch_size):
            self.spawn_balloon(screen, settings, balloons, kittens)

    def check_balloons(self, balloons, kittens, sword, scoreboard, screen, settings, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(sword.rect):
                self.pop_balloon(scoreboard, settings, balloon, balloons)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
                self.miss_balloon(scoreboard, balloon, balloons)
                self.spawn_balloon(screen, settings, balloons, kittens)
                continue

            balloon.blitme()

        if scoreboard.balloons_popped &gt; 0 or scoreboard.balloons_missed &gt; 0:
            scoreboard.popped_ratio = float(scoreboard.balloons_popped)/(scoreboard.balloons_popped + scoreboard.balloons_missed)
            if scoreboard.popped_ratio &lt; settings.min_popped_ratio:
                # Set game_active to false, empty the list of balloons, and increment games_played
                settings.game_active = False
                settings.games_played += 1

    def check_kittens(self, kittens, sword, scoreboard, screen, settings, time_passed):
        # Find any kittens that have been killed, or have survived to top of screen
        for kitten in kittens:
            kitten.update(time_passed)

            if kitten.rect.colliderect(sword.rect):
                self.kill_kitten(scoreboard, settings, kitten, kittens)
                continue

            if kitten.y_position &lt; -kitten.image_h/2 + settings.scoreboard_height:
                self.spare_kitten(scoreboard, settings, kitten, kittens)
                continue

            kitten.blitme()

    def update_sword(self, sword, mouse_x, mouse_y, settings):
        # Update the sword's position, and draw the sword on the screen
        sword.x_position = mouse_x
        if sword.grabbed:
            sword.y_position = mouse_y
        else:
            sword.y_position = sword.image_h/2 + settings.scoreboard_height
        sword.update_rect()
        sword.blitme()

    def miss_balloon(self, scoreboard, balloon, balloons):
        scoreboard.balloons_missed += 1
        balloons.remove(balloon)

    def spare_kitten(self, scoreboard, settings, kitten, kittens):
        scoreboard.kittens_spared += 1
        scoreboard.score += settings.kitten_score_factor * settings.points_per_balloon
        kittens.remove(kitten)

    def pop_balloon(self, scoreboard, settings, balloon, balloons):
        scoreboard.balloons_popped += 1
        scoreboard.score += settings.points_per_balloon
        balloons.remove(balloon)

    def kill_kitten(self, scoreboard, settings, kitten, kittens):
        scoreboard.kittens_killed += 1
        scoreboard.score -= settings.kitten_score_factor * settings.points_per_balloon
        kittens.remove(kitten)

    def spawn_balloon(self, screen, settings, balloons, kittens):
        balloons.append(Balloon(screen, settings))
        # Periodically release a kitten:
        if random() &lt; settings.kitten_ratio:
            self.spawn_kitten(screen, settings, kittens)

    def spawn_kitten(self, screen, settings, kittens):
        kittens.append(Kitten(screen, settings))

    def check_events(self, settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if sword.rect.collidepoint(mouse_x, mouse_y):
                    sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del balloons[:]
                    scoreboard.initialize_stats()
                    settings.initialize_game_parameters()
                    settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                sword.grabbed = False
    </pre>
<p>Lines 45-47 handle the situation in which a sword has slashed a kitten. If so, we kill the kitten, and then continue dealing with the next kitten in our list. We don&#8217;t bother blitting this kitten to the screen, because it no longer exists. In a similar manner, lines 49-51 handle kittens that have been spared long enough to reach the top of the screen.</p>
<p>Lines 69-72 handle a kitten that has been spared. We increment the scoreboard&#8217;s tally of kittens_spared. Then we add to the score, by multiplying the current point value of a balloon by the kitten_score_factor. Finally, we remove this kitten from our list of kittens.</p>
<p>Lines 79-82 handle the sad situation of a kitten being slashed. In this unfortunate case, we tally the untimely demise using scoreboard&#8217;s kittens_killed variable. Then we take points off, equivalent to the current value of a kitten. Finally, we remove this kitten from the list of kittens.</p>
<p>If we play the game now, it takes a lot more skill to pop balloons consistently without killing too many kittens. If you can do so, however, you are rewarded with an appropriately higher score:</p>
<div id="attachment_1831" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/kitten_score.png"><img class="size-large wp-image-1831" alt="Spare a kitten, and your score rises significantly.  Slash a kitten, and you lose points." src="http://peak5390.files.wordpress.com/2013/02/kitten_score.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">Spare a kitten, and your score rises significantly. Slash a kitten, and you lose points.</p></div>
<h2>Revising our instructions</h2>
<p>We want to let people know how much we value kittens in this game. Let&#8217;s modify our Instructions.py file to share our love of kittens:</p>
<pre class="brush: python; highlight: [19,20,21]; title: ; wrap-lines: false; notranslate">import pygame.font
from pygame.sprite import Sprite

class Instructions(Sprite):

    def __init__(self, screen, settings):

        Sprite.__init__(self)
        self.screen = screen
        self.settings = settings

        self.text_color = (30,30,30)
        self.font = pygame.font.SysFont('Arial', 24)

        # Store the set of instructions
        self.instr_lines = [&quot;Move your mouse to swipe the sword back and forth.&quot;]
        self.instr_lines.append(&quot;Or, click on the sword to grab it.&quot;)
        self.instr_lines.append(&quot;Keep your pop rate above 90%.&quot;)
        self.instr_lines.append(&quot;--- Please spare the kittens! ---&quot;)
        self.instr_lines.append(&quot;Letting a kitten live earns several balloons' worth of points;&quot;)
        self.instr_lines.append(&quot;Killing a kitten loses several balloons' worth of points.&quot;)

        # The instruction message only needs to be prepped once, not on every blit
        self.prep_msg()

    def prep_msg(self):
        y_position = self.settings.screen_height/2 + self.settings.button_height
        self.msg_images, self.msg_x, self.msg_y = [], [], []
        for index, line in enumerate(self.instr_lines):
            self.msg_images.append(self.font.render(line, True, self.text_color))
            self.msg_x.append(self.settings.screen_width/2-self.font.size(line)[0]/2)
            self.msg_y.append(y_position + index*self.font.size(line)[1])

    def blitme(self):
        for msg_x, msg_y, msg_image in zip(self.msg_x, self.msg_y, self.msg_images):
            self.screen.blit(msg_image, (msg_x, msg_y))</pre>
<p>These are just a few lines to let people know the value of kittens if you let them survive, and the penalty if you kill a kitten:</p>
<div id="attachment_1833" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/kitten_instructions.png"><img class="size-large wp-image-1833" alt="A few lines to tell people how much we value kittens." src="http://peak5390.files.wordpress.com/2013/02/kitten_instructions.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">A few lines to tell people how much we value kittens.</p></div>
<h2>Making Engine.py a real class</h2>
<p>Our Engine class has made things easier, but it is really just a function library right now. The function definitions are getting messy, with many of the same arguments being passed around. Let&#8217;s try to clean this up a bit by giving the engine object access to most of the values that are being passed around.</p>
<pre class="brush: python; highlight: [9,10,11,12,13,14,15,17,18,19,21,24,27,28,31,32,33,38,39,40,42,43,45,47,50,51,54,55,60,62,63,64,66,67,68,74,75,76,77,79,80,81,82,84,85,86,87,89,90,92,93,95,96,98,103,104,108,109,110,111,113]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon
from Button import Button
from Kitten import Kitten
from random import random

class Engine():

    def __init__(self, screen, settings, scoreboard, balloons, kittens, sword):
        self.screen = screen
        self.settings = settings
        self.scoreboard = scoreboard
        self.balloons = balloons
        self.kittens = kittens
        self.sword = sword

    def release_batch(self):
        for x in range(0, self.settings.batch_size):
            self.spawn_balloon()

    def check_balloons(self, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in self.balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(self.sword.rect):
                self.pop_balloon(balloon)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + self.settings.scoreboard_height:
                self.miss_balloon(balloon)
                self.spawn_balloon()
                continue

            balloon.blitme()

        if self.scoreboard.balloons_popped &gt; 0 or self.scoreboard.balloons_missed &gt; 0:
            self.scoreboard.popped_ratio = float(self.scoreboard.balloons_popped)/(self.scoreboard.balloons_popped + self.scoreboard.balloons_missed)
            if self.scoreboard.popped_ratio &lt; self.settings.min_popped_ratio:
                # Set game_active to false, empty the list of balloons, and increment games_played
                self.settings.game_active = False
                self.settings.games_played += 1

    def check_kittens(self, time_passed):
        # Find any kittens that have been killed, or have survived to top of screen
        for kitten in self.kittens:
            kitten.update(time_passed)

            if kitten.rect.colliderect(self.sword.rect):
                self.kill_kitten(kitten)
                continue

            if kitten.y_position &lt; -kitten.image_h/2 + self.settings.scoreboard_height:
                self.spare_kitten(kitten)
                continue

            kitten.blitme()

    def update_sword(self, mouse_x, mouse_y):
        # Update the sword's position, and draw the sword on the screen
        self.sword.x_position = mouse_x
        if self.sword.grabbed:
            self.sword.y_position = mouse_y
        else:
            self.sword.y_position = self.sword.image_h/2 + self.settings.scoreboard_height
        self.sword.update_rect()
        self.sword.blitme()

    def miss_balloon(self, balloon):
        self.scoreboard.balloons_missed += 1
        self.balloons.remove(balloon)

    def spare_kitten(self, kitten):
        self.scoreboard.kittens_spared += 1
        self.scoreboard.score += self.settings.kitten_score_factor * self.settings.points_per_balloon
        self.kittens.remove(kitten)

    def pop_balloon(self, balloon):
        self.scoreboard.balloons_popped += 1
        self.scoreboard.score += self.settings.points_per_balloon
        self.balloons.remove(balloon)

    def kill_kitten(self, kitten):
        self.scoreboard.kittens_killed += 1
        self.scoreboard.score -= self.settings.kitten_score_factor * self.settings.points_per_balloon
        self.kittens.remove(kitten)

    def spawn_balloon(self):
        self.balloons.append(Balloon(self.screen, self.settings))
        # Periodically release a kitten:
        if random() &lt; self.settings.kitten_ratio:
            self.spawn_kitten()

    def spawn_kitten(self):
        self.kittens.append(Kitten(self.screen, self.settings))

    def check_events(self, play_button, mouse_x, mouse_y):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if self.sword.rect.collidepoint(mouse_x, mouse_y):
                    self.sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del self.balloons[:]
                    self.scoreboard.initialize_stats()
                    self.settings.initialize_game_parameters()
                    self.settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                self.sword.grabbed = False</pre>
<p>Most of this file has changed, so we will explain the kinds of changes made rather than explaining every single line that has changed. Lines 9-15 now define all of the main game parameters that engine functions need access to. These include the screen, settings, and scoreboard objects, and the collections of balloons and kittens, and the sword itself. There are a few items that will still be passed directly to individual functions.</p>
<p>Line 17 shows us how much simpler the function definitions are now. The function release_batch used to take 4 arguments in addition to the self object. Now the self object is the only argument needed! This leads to cleaner calls to other engine functions, so line 19 is significantly simpler as well. Many variables now need a &#8220;self&#8221; prefix, such as the settings object in line 18.</p>
<p>The rest of the changes in Engine.py follow this same line of thinking. We now need to make a few changes in balloon_ninja.py to use our new version of Engine.py:</p>
<pre class="brush: python; highlight: [32,33,40,47,48,49,65]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button
from Engine import Engine
from Instructions import Instructions

def run_game():
    # Get access to our game settings
    settings = Settings()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)
    instructions = Instructions(screen, settings)

    # Create a list to hold our balloons, and our kittens
    balloons = []
    kittens = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # Create our game engine, with access to appropriate game parameters:
    engine = Engine(screen, settings, scoreboard, balloons, kittens, sword)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        engine.check_events(play_button, mouse_x, mouse_y)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            engine.update_sword(mouse_x, mouse_y)
            engine.check_balloons(time_passed)
            engine.check_kittens(time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            if len(balloons) == 0:
                # If we are not just starting a game, increase the balloon speed and points per balloon,
                #  and increment batches_finished
                if scoreboard.balloons_popped &gt; 0:
                    #  Increase the balloon speed, and other factors, for each new batch of balloons.
                    settings.balloon_speed *= settings.speed_increase_factor
                    settings.kitten_ratio *= settings.speed_increase_factor
                    settings.points_per_balloon = int(round(settings.points_per_balloon * settings.speed_increase_factor))
                    scoreboard.batches_finished += 1
                # If player has completed required batches, increase batch_size
                if scoreboard.batches_finished % settings.batches_needed == 0 and scoreboard.batches_finished &gt; 0:
                    settings.batch_size += 1
                engine.release_batch()
        else:
            # Game is not active, so...
            #  Show play button
            play_button.blitme()
            #  Show instructions for first few games.
            if settings.games_played &lt; 3:
                instructions.blitme()
            #  If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

run_game()</pre>
<p>We move the creation of the engine object from the beginning of the file to just before the main loop, on line 33. We do this because we need to send the Engine some of the variables we have just defined. The rest of the changes involve simplifying the argument lists of each engine method we call. We no longer have to send screen, settings, balloons, etc. to each function; the only variables we explicitly send are the constantly updated values such as mouse position information and time information.</p>
<p>That is all for now. In the next section, we will wrap things up with a trip to GitHub and a list of future features that could be implemented.</p>
<p>Next: <a title="Balloon Ninja:  Wrapping Up" href="http://peak5390.wordpress.com/2013/03/02/balloon-ninja-wrapping-up/">Wrapping Up</a></p>
<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/1784/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/1784/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1784&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/02/26/balloon-ninja-kittens/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/kitten_50px.png" medium="image">
			<media:title type="html">A nice kitten, with a transparent background.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/kittens.png?w=640" medium="image">
			<media:title type="html">One kitten is released for every balloon, but they don&#039;t affect game play at this point.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/kitten_score.png?w=640" medium="image">
			<media:title type="html">Spare a kitten, and your score rises significantly.  Slash a kitten, and you lose points.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/kitten_instructions.png?w=640" medium="image">
			<media:title type="html">A few lines to tell people how much we value kittens.</media:title>
		</media:content>
	</item>
		<item>
		<title>Balloon Ninja:  A better scoring system</title>
		<link>http://peak5390.wordpress.com/2013/02/21/balloon-ninja-a-better-scoring-system/</link>
		<comments>http://peak5390.wordpress.com/2013/02/21/balloon-ninja-a-better-scoring-system/#comments</comments>
		<pubDate>Thu, 21 Feb 2013 16:41:13 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[balloon ninja]]></category>
		<category><![CDATA[phsgeek]]></category>
		<category><![CDATA[pygame]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=1731</guid>
		<description><![CDATA[Back to Tutorial Contents Pop Rate In the last section of this tutorial, we implemented some logic to determine when the game was over. This was fairly simple logic; the game is over when you miss more than 3 balloons. This &#8230; <a href="http://peak5390.wordpress.com/2013/02/21/balloon-ninja-a-better-scoring-system/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1731&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<h2>Pop Rate</h2>
<p>In the last section of this tutorial, we implemented some logic to determine <a title="Balloon Ninja: Start screen and game over" href="http://peak5390.wordpress.com/2013/02/15/balloon-ninja-start-screen-and-game-over/">when the game was over</a>. This was fairly simple logic; the game is over when you miss more than 3 balloons. This works, but once you get to larger batches of balloons being released, the game gets really hard. It might be a good idea to set a percentage of balloons that can&#8217;t be missed. We will actually call this a popped_ratio, and say that the player must keep popping at least 90% of the balloons in order to stay alive. Let&#8217;s add this parameter to Settings.py:</p>
<pre class="brush: python; highlight: [18]; title: ; wrap-lines: false; notranslate">class Settings():

    def __init__(self):
        # screen parameters
        self.screen_width, self.screen_height = 800, 600
        self.bg_color = 200, 200, 200
        self.scoreboard_height = 50

        self.button_width, self.button_height = 250, 50
        self.button_bg = (0,163,0)
        self.button_text_color = (235,235,235)
        self.button_font, self.button_font_size = 'Arial', 24

        # game status
        self.game_active = False

        # game over conditions
        self.min_popped_ratio = 0.9
        self.games_played = 0

        self.initialize_game_parameters()

    def initialize_game_parameters(self):
        # game play parameters
        self.balloon_speed = 0.1
        # How quickly the speed of balloons rises
        #  ~1.05 during testing and ~1.01 for actual play
        self.speed_increase_factor = 1.05
        self.points_per_balloon = 10

        # Number of balloons to release in a spawning:
        self.batch_size = 1
        # Number of balloons that need to be popped before increasing batch_size
        #  For actual play, probably want ~10; for testing, ~3
        self.pops_needed = 3</pre>
<p>We change line 18 from misses_allowed to min_popped_ratio, and set it to 90%. Now we need to modify Engine.py to use this value:</p>
<pre class="brush: python; highlight: [30,31,32]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon

class Engine():

    def __init__(self):
        pass

    def release_batch(self, screen, settings, balloons):
        for x in range(0, settings.batch_size):
            self.spawn_balloon(screen, settings, balloons)

    def check_balloons(self, balloons, sword, scoreboard, screen, settings, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(sword.rect):
                self.pop_balloon(scoreboard, settings, balloon, balloons)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
                self.miss_balloon(scoreboard, balloon, balloons)
                self.spawn_balloon(screen, settings, balloons)
                continue

            balloon.blitme()

        if scoreboard.balloons_popped &gt; 0 or scoreboard.balloons_missed &gt; 0:
            current_popped_ratio = float(scoreboard.balloons_popped)/(scoreboard.balloons_popped + scoreboard.balloons_missed)
            if current_popped_ratio &lt; settings.min_popped_ratio:
                # Set game_active to false, empty the list of balloons, and increment games_played
                settings.game_active = False
                settings.games_played += 1

    def update_sword(self, sword, mouse_x, mouse_y, settings):
        # Update the sword's position, and draw the sword on the screen
        sword.x_position = mouse_x
        if sword.grabbed:
            sword.y_position = mouse_y
        else:
            sword.y_position = sword.image_h/2 + settings.scoreboard_height
        sword.update_rect()
        sword.blitme()

    def miss_balloon(self, scoreboard, balloon, balloons):
        scoreboard.balloons_missed += 1
        balloons.remove(balloon)

    def pop_balloon(self, scoreboard, settings, balloon, balloons):
        scoreboard.balloons_popped += 1
        scoreboard.score += settings.points_per_balloon
        balloons.remove(balloon)
        # If we have popped enough balloons, increase batch_size:
        if scoreboard.balloons_popped % settings.pops_needed == 0:
            settings.batch_size += 1

    def spawn_balloon(self, screen, settings, balloons):
        balloons.append(Balloon(screen, settings))

    def check_events(self, settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if sword.rect.collidepoint(mouse_x, mouse_y):
                    sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del balloons[:]
                    scoreboard.initialize_stats()
                    settings.initialize_game_parameters()
                    settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                sword.grabbed = False</pre>
<p>On line 30, we need to make sure that at least one balloon has been popped or missed. Otherwise, we will get a division by zero error. For readability, we calculate the current popped ratio on a separate line. We have to convert the number of balloons popped to a decimal float number, so we can calculate our popped ratio accurately. The popped ratio is the number of balloons popped, divided by the sum of all balloons that have been popped or missed.</p>
<p>This results in much more exciting gameplay. Every missed balloon moves us closer to the game being over, but if we start to play better we can extend the game. Here&#8217;s a screenshot of typical play at this point:</p>
<div id="attachment_1740" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/missed_ratio.png"><img class="size-large wp-image-1740" alt="Basing the game active status on the ratio of balloons popped to balloons missed makes for extended, more exciting game play." src="http://peak5390.files.wordpress.com/2013/02/missed_ratio.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">Basing the game active status on the ratio of balloons popped to balloons missed makes for extended, more exciting game play.</p></div>
<h2>Displaying the Pop Rate</h2>
<p>Now let&#8217;s display this percentage on the scoreboard, so people can see how well they are doing. We need to move the popped_ratio so that it is a scoreboard variable, and then display that variable. Let&#8217;s modify Engine.py first:</p>
<pre class="brush: python; highlight: [31,32]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon

class Engine():

    def __init__(self):
        pass

    def release_batch(self, screen, settings, balloons):
        for x in range(0, settings.batch_size):
            self.spawn_balloon(screen, settings, balloons)

    def check_balloons(self, balloons, sword, scoreboard, screen, settings, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(sword.rect):
                self.pop_balloon(scoreboard, settings, balloon, balloons)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
                self.miss_balloon(scoreboard, balloon, balloons)
                self.spawn_balloon(screen, settings, balloons)
                continue

            balloon.blitme()

        if scoreboard.balloons_popped &gt; 0:
            scoreboard.popped_ratio = float(scoreboard.balloons_popped)/(scoreboard.balloons_popped + scoreboard.balloons_missed)
            if scoreboard.popped_ratio &lt; settings.min_popped_ratio:
                # Set game_active to false, empty the list of balloons, and increment games_played
                settings.game_active = False
                settings.games_played += 1

    def update_sword(self, sword, mouse_x, mouse_y, settings):
        # Update the sword's position, and draw the sword on the screen
        sword.x_position = mouse_x
        if sword.grabbed:
            sword.y_position = mouse_y
        else:
            sword.y_position = sword.image_h/2 + settings.scoreboard_height
        sword.update_rect()
        sword.blitme()

    def miss_balloon(self, scoreboard, balloon, balloons):
        scoreboard.balloons_missed += 1
        balloons.remove(balloon)

    def pop_balloon(self, scoreboard, settings, balloon, balloons):
        scoreboard.balloons_popped += 1
        scoreboard.score += settings.points_per_balloon
        balloons.remove(balloon)
        # If we have popped enough balloons, increase batch_size:
        if scoreboard.balloons_popped % settings.pops_needed == 0:
            settings.batch_size += 1

    def spawn_balloon(self, screen, settings, balloons):
        balloons.append(Balloon(screen, settings))

    def check_events(self, settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if sword.rect.collidepoint(mouse_x, mouse_y):
                    sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del balloons[:]
                    scoreboard.initialize_stats()
                    settings.initialize_game_parameters()
                    settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                sword.grabbed = False</pre>
<p>Here we just tell our engine to update the scoreboard popped_ratio variable that we are about to create, and then use that updated value. Here is our new Scoreboard.py:</p>
<pre class="brush: python; highlight: [25,32,44,45,47,48,49,50,51,52,53,54,55,66]; title: ; wrap-lines: false; notranslate">import pygame, pygame.font
from pygame.sprite import Sprite

class Scoreboard(Sprite):

    def __init__(self, screen, sb_height):

        Sprite.__init__(self)
        self.screen = screen

        self.initialize_stats()

        # Set dimensions and properties of scoreboard
        self.sb_height, self.sb_width = sb_height, self.screen.get_width()
        self.rect = pygame.Rect(0,0, self.sb_width, self.sb_height)
        self.bg_color=(100,100,100)
        self.text_color = (225,225,225)
        self.font = pygame.font.SysFont('Arial', 18)

        # Set positions of individual scoring elements on the scoreboard
        self.x_popped_position, self.y_popped_position = 20.0, 10.0
        self.x_missed_position, self.y_missed_position = 150.0, 10.0
        self.x_score_position, self.y_score_position = self.screen.get_width() - 200, 10.0
        self.x_ratio_position, self.y_ratio_position = 300, 10.0

    def initialize_stats(self):
        # Game attributes to track for scoring
        self.balloons_popped = 0
        self.balloons_missed = 0
        self.score = 0
        self.popped_ratio = 1.0

    def prep_scores(self):
        self.popped_string = &quot;Popped: &quot; + str(self.balloons_popped)
        self.popped_image = self.font.render(self.popped_string, True, self.text_color)

        self.missed_string = &quot;Missed: &quot; + str(self.balloons_missed)
        self.missed_image = self.font.render(self.missed_string, True, self.text_color)

        self.score_string = &quot;Score: &quot; + str(self.score)
        self.score_image = self.font.render(self.score_string, True, self.text_color)

        self.set_ratio_string()
        self.popped_ratio_image = self.font.render(self.popped_ratio_string, True, self.ratio_text_color)

    def set_ratio_string(self):
        if self.popped_ratio == 1.0:
            self.popped_ratio_string = &quot;Pop Rate: 100%&quot;
        else:
            self.popped_ratio_string = &quot;Pop Rate: &quot; + &quot;{0:.3}%&quot;.format(self.popped_ratio*100.0)
        if self.popped_ratio &lt; 0.95:
            self.ratio_text_color = (255, 51, 51)
        else:
            self.ratio_text_color = self.text_color

    def blitme(self):
        # Turn individual scoring elements into images that can be drawn
        self.prep_scores()
        # Draw blank scoreboard
        self.screen.fill(self.bg_color, self.rect)
        # Draw individual scoring elements
        self.screen.blit(self.popped_image, (self.x_popped_position, self.y_popped_position))
        self.screen.blit(self.missed_image, (self.x_missed_position, self.y_missed_position))
        self.screen.blit(self.score_image, (self.x_score_position, self.y_score_position))
        self.screen.blit(self.popped_ratio_image, (self.x_ratio_position, self.y_ratio_position))</pre>
<p>On line 25 we position the ratio just after the number of missed balloons on the scoreboard. On line 32 we initialize the ratio to 1.0, or 100%. Lines 44 and 45 create a string representing the ratio on the scoreboard, and then render the string to an image as we have done with our other game stats.</p>
<p>Since there is a bit of logic in creating the ratio stat, we create a helper function on lines 47-55. In this function, lines 48 and 49 report a ratio of 100% if the ratio is 1.0. Line 51 turns the decimal ratio, which may be something like 0.9877537 into a nicely formatted percentage such as &#8220;98.8%&#8221;. We multiply the ratio by 100.0 to turn it into a percentage, and then use python&#8217;s <a title="Python format documentation" href="http://docs.python.org/2/library/string.html#format-string-syntax">format</a> function to display 3 significant digits, along with a percent symbol. If you are curious about this function, take a look at this <a title="how to show Percentage in python" href="http://stackoverflow.com/questions/5306756/how-to-show-percentage-in-python">question on Stack Overflow</a>. Lines 52 through 55 display the ratio in red if the popped percentage is less than 95%.</p>
<p>Here is a screenshot at the end of a game now, showing the percentage of balloons popped throughout the game:</p>
<div id="attachment_1752" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/pop_rate.png"><img class="size-large wp-image-1752" alt="Game over screen, showing the pop rate at the end of the game." src="http://peak5390.files.wordpress.com/2013/02/pop_rate.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">Game over screen, showing the pop rate at the end of the game.</p></div>
<h2>Variable Scoring</h2>
<p>If you&#8217;ve been playing the game as it is being developed, you can see that it&#8217;s getting more challenging. Let&#8217;s reward the player for surviving longer by making balloons worth more as the game progresses. This will first require changes to balloon_ninja.py:</p>
<pre class="brush: python; highlight: [51,52]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button
from Engine import Engine

def run_game():
    # Get access to our game settings
    settings = Settings()
    engine = Engine()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings.scoreboard_height)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        engine.check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            engine.update_sword(sword, mouse_x, mouse_y, settings)
            engine.check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            #  Increase the balloon speed for each new batch of balloons.
            if len(balloons) == 0:
                settings.balloon_speed *= settings.speed_increase_factor
                engine.release_batch(screen, settings, balloons)
                if scoreboard.balloons_popped &gt; 0:
                    settings.points_per_balloon = int(round(settings.points_per_balloon * settings.speed_increase_factor))
        else:
            # Show play button
            play_button.blitme()
            # If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

run_game()</pre>
<p>Every time we release a new batch of balloons, we have been increasing the speed of the balloons. This is a good place to increase the score for each balloon as well. We do this only after the first balloon is popped, so that our first balloon is still 10 points. We are already calling initialize_settings every time the game starts over, so we don&#8217;t have to do anything more to reset the scoring each time a new game is started.</p>
<p>This is good, and it creates better scoring, but we should report the number of points each balloon is worth on the scoreboard. Our scoreboard is getting a bit crowded, though. Let&#8217;s stop reporting the number of missed balloons, since that information is contained in the Pop Rate statistic. Our new scoreboard.py will look like this:</p>
<pre class="brush: python; highlight: [7,11,16,22,23,24,25,26,31,45,46,65]; title: ; wrap-lines: false; notranslate">import pygame, pygame.font
from pygame.sprite import Sprite

class Scoreboard(Sprite):

    def __init__(self, screen, settings):

        Sprite.__init__(self)
        self.screen = screen
        self.settings = settings

        self.initialize_stats()

        # Set dimensions and properties of scoreboard
        self.sb_height, self.sb_width = settings.scoreboard_height, self.screen.get_width()
        self.rect = pygame.Rect(0,0, self.sb_width, self.sb_height)
        self.bg_color=(100,100,100)
        self.text_color = (225,225,225)
        self.font = pygame.font.SysFont('Arial', 18)

        # Set positions of individual scoring elements on the scoreboard
        self.x_popped_position, self.y_popped_position = 20.0, 10.0
        self.x_ratio_position, self.y_ratio_position = 150, 10.0
        self.x_points_position, self.y_points_position = 350, 10.0
        self.x_score_position, self.y_score_position = self.screen.get_width() - 200, 10.0

    def initialize_stats(self):
        # Game attributes to track for scoring
        self.balloons_popped = 0
        self.balloons_missed = 0
        self.score = 0
        self.popped_ratio = 1.0

    def prep_scores(self):
        self.popped_string = &quot;Popped: &quot; + str(self.balloons_popped)
        self.popped_image = self.font.render(self.popped_string, True, self.text_color)

        self.score_string = &quot;Score: &quot; + str(self.score)
        self.score_image = self.font.render(self.score_string, True, self.text_color)

        self.set_ratio_string()
        self.popped_ratio_image = self.font.render(self.popped_ratio_string, True, self.ratio_text_color)

        self.points_string = &quot;Points per Balloon: &quot; + str(self.settings.points_per_balloon)
        self.points_image = self.font.render(self.points_string, True, self.text_color)

    def set_ratio_string(self):
        if self.popped_ratio == 1.0:
            self.popped_ratio_string = &quot;Pop Rate: 100%&quot;
        else:
            self.popped_ratio_string = &quot;Pop Rate: &quot; + &quot;{0:.3}%&quot;.format(self.popped_ratio*100.0)
        if self.popped_ratio &lt; 0.95:
            self.ratio_text_color = (255, 51, 51)
        else:
            self.ratio_text_color = self.text_color

    def blitme(self):
        # Turn individual scoring elements into images that can be drawn
        self.prep_scores()
        # Draw blank scoreboard
        self.screen.fill(self.bg_color, self.rect)
        # Draw individual scoring elements
        self.screen.blit(self.popped_image, (self.x_popped_position, self.y_popped_position))
        self.screen.blit(self.points_image, (self.x_points_position, self.y_points_position))
        self.screen.blit(self.score_image, (self.x_score_position, self.y_score_position))
        self.screen.blit(self.popped_ratio_image, (self.x_ratio_position, self.y_ratio_position))</pre>
<p>The scoreboard needs access to our settings object now, so we have changed the function definition slightly. Instead of receiving the scoreboard height directly, it receives the settings object on line 7. On line 11 we make a self.settings variable, so we can access settings within our helper functions. On line 16, we now get the scoreboard height directly from the settings object.</p>
<p>On lines 22-26, we remove the balloons_missed position. We move the Pop Rate to the space where Missed used to live, and we place the Points per Balloon a little to the right of Pop Rate. Lines 45 and 46 set the string and rendered string image for the Points per Balloon stat, and line 65 blits this stat to the scoreboard. Notice that we have removed the lines in prep_scores() and blitme() that refer to balloons_missed. However, we have kept the self.balloons_missed stat on line 31, because we need this statistic in order to calculate the popped ratio.</p>
<p>Our new scoreboard looks like this, near the end of a game:</p>
<div id="attachment_1764" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/variable_points.png"><img class="size-large wp-image-1764" alt="Balloons are now worth more points as the game progresses." src="http://peak5390.files.wordpress.com/2013/02/variable_points.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">Balloons are now worth more points as the game progresses.</p></div>
<h2>Cleaning up the batch size</h2>
<p>We need to clean up the process by which the batch_size is being increased. Right now, we are increasing the batch size every time the player pops 10 balloons. This is fine early in the game, but it leads to exponential growth pretty quickly. Once the batch size is greater than 10, we start getting multiple increases in batch_size before the current batch is even completed. I am testing the game with an increase every time 3 balloons are popped, which magnifies the problem, but the problem will arise even with a more realistic value.</p>
<p>We can improve this by simply tracking the number of batches that are completed, and increase the batch size every time a number of batches have been finished. This is similar to the concept of moving up in levels or rounds in a game. Let&#8217;s start with Settings.py:</p>
<pre class="brush: python; highlight: [34,35]; title: ; wrap-lines: false; notranslate">class Settings():

    def __init__(self):
        # screen parameters
        self.screen_width, self.screen_height = 800, 600
        self.bg_color = 200, 200, 200
        self.scoreboard_height = 50

        self.button_width, self.button_height = 250, 50
        self.button_bg = (0,163,0)
        self.button_text_color = (235,235,235)
        self.button_font, self.button_font_size = 'Arial', 24

        # game status
        self.game_active = False

        # game over conditions
        self.min_popped_ratio = 0.9
        self.games_played = 0

        self.initialize_game_parameters()

    def initialize_game_parameters(self):
        # game play parameters
        self.balloon_speed = 0.1
        # How quickly the speed of balloons rises
        #  ~1.05 during testing and ~1.01 for actual play
        self.speed_increase_factor = 1.05
        self.points_per_balloon = 10

        # Number of balloons to release in a spawning:
        self.batch_size = 1

        # Number of batches that must be completed to increment batch_size
        self.batches_needed = 3</pre>
<p>We get rid of the last few lines in the file involving pops_needed. We replace this with lines 34 and 35, which set the number of batches a player needs to finish before seeing an increase in the number of balloons released in a batch.</p>
<p>Now let&#8217;s look at Scoreboard.py:</p>
<pre class="brush: python; highlight: [33]; title: ; wrap-lines: false; notranslate">import pygame, pygame.font
from pygame.sprite import Sprite

class Scoreboard(Sprite):

    def __init__(self, screen, settings):

        Sprite.__init__(self)
        self.screen = screen
        self.settings = settings

        self.initialize_stats()

        # Set dimensions and properties of scoreboard
        self.sb_height, self.sb_width = settings.scoreboard_height, self.screen.get_width()
        self.rect = pygame.Rect(0,0, self.sb_width, self.sb_height)
        self.bg_color=(100,100,100)
        self.text_color = (225,225,225)
        self.font = pygame.font.SysFont('Arial', 18)

        # Set positions of individual scoring elements on the scoreboard
        self.x_popped_position, self.y_popped_position = 20.0, 10.0
        self.x_ratio_position, self.y_ratio_position = 150, 10.0
        self.x_points_position, self.y_points_position = 350, 10.0
        self.x_score_position, self.y_score_position = self.screen.get_width() - 200, 10.0

    def initialize_stats(self):
        # Game attributes to track for scoring
        self.balloons_popped = 0
        self.balloons_missed = 0
        self.score = 0
        self.popped_ratio = 1.0
        self.batches_finished = 0

    def prep_scores(self):
        self.popped_string = &quot;Popped: &quot; + str(self.balloons_popped)
        self.popped_image = self.font.render(self.popped_string, True, self.text_color)

        self.score_string = &quot;Score: &quot; + str(self.score)
        self.score_image = self.font.render(self.score_string, True, self.text_color)

        self.set_ratio_string()
        self.popped_ratio_image = self.font.render(self.popped_ratio_string, True, self.ratio_text_color)

        self.points_string = &quot;Points per Balloon: &quot; + str(self.settings.points_per_balloon)
        self.points_image = self.font.render(self.points_string, True, self.text_color)

    def set_ratio_string(self):
        if self.popped_ratio == 1.0:
            self.popped_ratio_string = &quot;Pop Rate: 100%&quot;
        else:
            self.popped_ratio_string = &quot;Pop Rate: &quot; + &quot;{0:.3}%&quot;.format(self.popped_ratio*100.0)
        if self.popped_ratio &lt; 0.95:
            self.ratio_text_color = (255, 51, 51)
        else:
            self.ratio_text_color = self.text_color

    def blitme(self):
        # Turn individual scoring elements into images that can be drawn
        self.prep_scores()
        # Draw blank scoreboard
        self.screen.fill(self.bg_color, self.rect)
        # Draw individual scoring elements
        self.screen.blit(self.popped_image, (self.x_popped_position, self.y_popped_position))
        self.screen.blit(self.points_image, (self.x_points_position, self.y_points_position))
        self.screen.blit(self.score_image, (self.x_score_position, self.y_score_position))
        self.screen.blit(self.popped_ratio_image, (self.x_ratio_position, self.y_ratio_position))</pre>
<p>All we are doing here is providing a way to track the number of batches that have been completed. This is done on line 33, with the new variable &#8220;batches_finished&#8221;.</p>
<p>We also need to modify Engine.py:</p>
<pre class="brush: python; highlight: [51,52,53,54]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon

class Engine():

    def __init__(self):
        pass

    def release_batch(self, screen, settings, balloons):
        for x in range(0, settings.batch_size):
            self.spawn_balloon(screen, settings, balloons)

    def check_balloons(self, balloons, sword, scoreboard, screen, settings, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(sword.rect):
                self.pop_balloon(scoreboard, settings, balloon, balloons)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
                self.miss_balloon(scoreboard, balloon, balloons)
                self.spawn_balloon(screen, settings, balloons)
                continue

            balloon.blitme()

        if scoreboard.balloons_popped &gt; 0 or scoreboard.balloons_missed &gt; 0:
            scoreboard.popped_ratio = float(scoreboard.balloons_popped)/(scoreboard.balloons_popped + scoreboard.balloons_missed)
            if scoreboard.popped_ratio &lt; settings.min_popped_ratio:
                # Set game_active to false, empty the list of balloons, and increment games_played
                settings.game_active = False
                settings.games_played += 1

    def update_sword(self, sword, mouse_x, mouse_y, settings):
        # Update the sword's position, and draw the sword on the screen
        sword.x_position = mouse_x
        if sword.grabbed:
            sword.y_position = mouse_y
        else:
            sword.y_position = sword.image_h/2 + settings.scoreboard_height
        sword.update_rect()
        sword.blitme()

    def miss_balloon(self, scoreboard, balloon, balloons):
        scoreboard.balloons_missed += 1
        balloons.remove(balloon)

    def pop_balloon(self, scoreboard, settings, balloon, balloons):
        scoreboard.balloons_popped += 1
        scoreboard.score += settings.points_per_balloon
        balloons.remove(balloon)

    def spawn_balloon(self, screen, settings, balloons):
        balloons.append(Balloon(screen, settings))

    def check_events(self, settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if sword.rect.collidepoint(mouse_x, mouse_y):
                    sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del balloons[:]
                    scoreboard.initialize_stats()
                    settings.initialize_game_parameters()
                    settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                sword.grabbed = False</pre>
<p>We are not actually adding anything to Engine.py. In the function pop_balloon(), we need to remove the lines that increase the batch_size after a certain number of balloons have been popped. The new version of pop_balloon() should match what you see above.</p>
<p>Finally, we need to change balloon_ninja.py:</p>
<pre class="brush: python; highlight: [45,46,47,48,49,50,51,52,53,54,55,56,57,58]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button
from Engine import Engine

def run_game():
    # Get access to our game settings
    settings = Settings()
    engine = Engine()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        engine.check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            engine.update_sword(sword, mouse_x, mouse_y, settings)
            engine.check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            if len(balloons) == 0:
                # If we are not just starting a game, increase the balloon speed and points per balloon,
                #  and increment batches_finished
                if scoreboard.balloons_popped &gt; 0:
                    #  Increase the balloon speed for each new batch of balloons.
                    settings.balloon_speed *= settings.speed_increase_factor
                    settings.points_per_balloon = int(round(settings.points_per_balloon * settings.speed_increase_factor))
                    scoreboard.batches_finished += 1
                # If player has completed required batches, increase batch_size
                if scoreboard.batches_finished % settings.batches_needed == 0 and scoreboard.batches_finished &gt; 0:
                    settings.batch_size += 1
                engine.release_batch(screen, settings, balloons)
        else:
            # Show play button
            play_button.blitme()
            # If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

run_game()</pre>
<p>Here we restructure what happens when we have an empty set of balloons. First, we check a couple things if this is not the first batch. If it is not the first batch, we increase the balloon speed for the new batch, increase the points per balloon, and record that a batch has been completed. Then we check to see if the required number of batches has been completed to increase the batch size. If so, we increment batch_size. After all of these parameters have been updated, we release a new batch of balloons on line 58.</p>
<p>When you play the game now, you see a clear progression of steadily increasing batch sizes.</p>
<h2>Game instructions</h2>
<p>We know how to play the game, because we&#8217;ve been working with the code for some time now. It might seem obvious how the game works, but it is not necessarily apparent that you can grab the sword when you first start playing. Let&#8217;s add a brief set of instructions, which will be displayed when the game is inactive. We will do this by creating a class for the instructions. Create the following file, and call it Instructions.py:</p>
<pre class="brush: python; title: ; wrap-lines: false; notranslate">import pygame.font
from pygame.sprite import Sprite

class Instructions(Sprite):

    def __init__(self, screen, settings):

        Sprite.__init__(self)
        self.screen = screen
        self.settings = settings

        self.text_color = (30,30,30)
        self.font = pygame.font.SysFont('Arial', 24)

        # Store the set of instructions
        self.instr_lines = [&quot;Move your mouse to swipe the sword back and forth.&quot;]
        self.instr_lines.append(&quot;Or, click on the sword to grab it.&quot;)
        self.instr_lines.append(&quot;But keep your pop rate above 90%!&quot;)

        # The instruction message only needs to be prepped once, not on every blit
        self.prep_msg()

    def prep_msg(self):
        y_position = self.settings.screen_height/2 + self.settings.button_height
        self.msg_images, self.msg_x, self.msg_y = [], [], []
        for index, line in enumerate(self.instr_lines):
            self.msg_images.append(self.font.render(line, True, self.text_color))
            self.msg_x.append(self.settings.screen_width/2-self.font.size(line)[0]/2)
            self.msg_y.append(y_position + index*self.font.size(line)[1])

    def blitme(self):
        for msg_x, msg_y, msg_image in zip(self.msg_x, self.msg_y, self.msg_images):
            self.screen.blit(msg_image, (msg_x, msg_y))</pre>
<p>There isn&#8217;t really anything new in here. Anytime we are going to display text, we need to import pygame.font. Our instructions will be a sprite, so we import that as well. On lines 15-18, we store our instructions as a list of lines, so we can display each line centered on the screen.</p>
<p>On line 24, we set the y_position of the first line to be just below the center of the screen vertically. On line 25 we initialize empty lists to store our rendered message images, and each image&#8217;s x- and y- value. On line 26 we enumerate our loop so we have access to the loop&#8217;s index. We will use this index to push each line down below the previous one. Lines 28 and 29 use the pygame.font.size() function to center each line on the screen. This function takes in a piece of text, and returns the width and height of that text as it will be rendered. So, line 28 positions each line at the horizontal midpoint of the window, minus half the width of the rendered text. Line 29 pushes each successive line down by the height of a single line, times the index value of the loop.</p>
<p>The blitme() function for the Instructions class uses Python&#8217;s zip function. When given three lists of equal length, the zip function returns the first item in each list, then the second item in each list, and so forth. So each time we pass through the loop we have access to a msg_x value, a msg_y value, and a msg_image. We blit each of these images to the screen at the appropriate xy location.</p>
<p>We just need a couple changes to balloon_ninja.py to make these instructions appear on the screen:</p>
<pre class="brush: python; highlight: [8,24,62,63,64,65,66,67,68,69,70]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button
from Engine import Engine
from Instructions import Instructions

def run_game():
    # Get access to our game settings
    settings = Settings()
    engine = Engine()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)
    instructions = Instructions(screen, settings)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        engine.check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            engine.update_sword(sword, mouse_x, mouse_y, settings)
            engine.check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            if len(balloons) == 0:
                # If we are not just starting a game, increase the balloon speed and points per balloon,
                #  and increment batches_finished
                if scoreboard.balloons_popped &gt; 0:
                    #  Increase the balloon speed for each new batch of balloons.
                    settings.balloon_speed *= settings.speed_increase_factor
                    settings.points_per_balloon = int(round(settings.points_per_balloon * settings.speed_increase_factor))
                    scoreboard.batches_finished += 1
                # If player has completed required batches, increase batch_size
                if scoreboard.batches_finished % settings.batches_needed == 0 and scoreboard.batches_finished &gt; 0:
                    settings.batch_size += 1
                engine.release_batch(screen, settings, balloons)
        else:
            # Game is not active, so...
            #  Show play button
            play_button.blitme()
            #  Show instructions for first few games.
            if settings.games_played &lt; 3:
                instructions.blitme()
            #  If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

run_game()</pre>
<p>We import our new Instructions class on line 8, and instantiate an instructions object on line 24. While we are adding code to display the instructions, we clean up the logic a bit about what is happening when a game is inactive. First we show the play button (line 64). Then, if the player has completed fewer than 3 games, we display the instructions. Finally, if at least one game has been played we display the Game Over button.</p>
<p>Now, you should see the following screen in between games:</p>
<div id="attachment_1781" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/instructions.png"><img class="size-large wp-image-1781" alt="Instructions now appear on startup, and after the first few games." src="http://peak5390.files.wordpress.com/2013/02/instructions.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">Instructions now appear on startup, and after the first few games.</p></div>
<h2>Improving our score reporting</h2>
<p>Now that we are able to achieve higher scores, let&#8217;s clean up the score reporting so that commas are displayed in the score. This is a one-line change to Scoreboard.py:</p>
<pre class="brush: python; highlight: [40]; title: ; wrap-lines: false; notranslate">import pygame, pygame.font
from pygame.sprite import Sprite

class Scoreboard(Sprite):

    def __init__(self, screen, settings):

        Sprite.__init__(self)
        self.screen = screen
        self.settings = settings

        self.initialize_stats()

        # Set dimensions and properties of scoreboard
        self.sb_height, self.sb_width = settings.scoreboard_height, self.screen.get_width()
        self.rect = pygame.Rect(0,0, self.sb_width, self.sb_height)
        self.bg_color=(100,100,100)
        self.text_color = (225,225,225)
        self.font = pygame.font.SysFont('Arial', 18)

        # Set positions of individual scoring elements on the scoreboard
        self.x_popped_position, self.y_popped_position = 20.0, 10.0
        self.x_ratio_position, self.y_ratio_position = 150, 10.0
        self.x_points_position, self.y_points_position = 350, 10.0
        self.x_score_position, self.y_score_position = self.screen.get_width() - 200, 10.0

    def initialize_stats(self):
        # Game attributes to track for scoring
        self.balloons_popped = 0
        self.balloons_missed = 0
        self.score = 0
        self.popped_ratio = 1.0
        self.batches_finished = 0

    def prep_scores(self):
        self.popped_string = &quot;Popped: &quot; + str(self.balloons_popped)
        self.popped_image = self.font.render(self.popped_string, True, self.text_color)

        self.score_string = &quot;Score: &quot; + format(self.score, ',d')
        self.score_image = self.font.render(self.score_string, True, self.text_color)

        self.set_ratio_string()
        self.popped_ratio_image = self.font.render(self.popped_ratio_string, True, self.ratio_text_color)

        self.points_string = &quot;Points per Balloon: &quot; + str(self.settings.points_per_balloon)
        self.points_image = self.font.render(self.points_string, True, self.text_color)

    def set_ratio_string(self):
        if self.popped_ratio == 1.0:
            self.popped_ratio_string = &quot;Pop Rate: 100%&quot;
        else:
            self.popped_ratio_string = &quot;Pop Rate: &quot; + &quot;{0:.3}%&quot;.format(self.popped_ratio*100.0)
        if self.popped_ratio &lt; 0.95:
            self.ratio_text_color = (255, 51, 51)
        else:
            self.ratio_text_color = self.text_color

    def blitme(self):
        # Turn individual scoring elements into images that can be drawn
        self.prep_scores()
        # Draw blank scoreboard
        self.screen.fill(self.bg_color, self.rect)
        # Draw individual scoring elements
        self.screen.blit(self.popped_image, (self.x_popped_position, self.y_popped_position))
        self.screen.blit(self.points_image, (self.x_points_position, self.y_points_position))
        self.screen.blit(self.score_image, (self.x_score_position, self.y_score_position))
        self.screen.blit(self.popped_ratio_image, (self.x_ratio_position, self.y_ratio_position))</pre>
<p>This is another simple use of Python&#8217;s format function. Now when we play, we will see a cleanly-formatted score:</p>
<div id="attachment_1789" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/commas_score.png"><img class="size-large wp-image-1789" alt="The score is now displayed with  commas." src="http://peak5390.files.wordpress.com/2013/02/commas_score.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">The score is now displayed with commas.</p></div>
<p>We now have a dynamic and cleanly-reported scoring system. In the next post, we will add some kittens to the game.</p>
<p>Next: <a title="Balloon Ninja:  Kittens!" href="http://peak5390.wordpress.com/2013/02/26/balloon-ninja-kittens/">Kittens!</a></p>
<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/1731/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/1731/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1731&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/02/21/balloon-ninja-a-better-scoring-system/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/missed_ratio.png?w=640" medium="image">
			<media:title type="html">Basing the game active status on the ratio of balloons popped to balloons missed makes for extended, more exciting game play.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/pop_rate.png?w=640" medium="image">
			<media:title type="html">Game over screen, showing the pop rate at the end of the game.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/variable_points.png?w=640" medium="image">
			<media:title type="html">Balloons are now worth more points as the game progresses.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/instructions.png?w=640" medium="image">
			<media:title type="html">Instructions now appear on startup, and after the first few games.</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/commas_score.png?w=640" medium="image">
			<media:title type="html">The score is now displayed with  commas.</media:title>
		</media:content>
	</item>
		<item>
		<title>Balloon Ninja:  Start Screen and Game Over</title>
		<link>http://peak5390.wordpress.com/2013/02/15/balloon-ninja-start-screen-and-game-over/</link>
		<comments>http://peak5390.wordpress.com/2013/02/15/balloon-ninja-start-screen-and-game-over/#comments</comments>
		<pubDate>Sat, 16 Feb 2013 00:35:50 +0000</pubDate>
		<dc:creator>ehmatthes</dc:creator>
				<category><![CDATA[programming]]></category>
		<category><![CDATA[balloon ninja]]></category>
		<category><![CDATA[phsgeek]]></category>
		<category><![CDATA[pygame]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://peak5390.wordpress.com/?p=1669</guid>
		<description><![CDATA[Back to Tutorial Contents Start Screen In the last section we added some game logic to Balloon Ninja, which made it more interesting to play. Now let&#8217;s add a start button, so the game does not start as soon as we &#8230; <a href="http://peak5390.wordpress.com/2013/02/15/balloon-ninja-start-screen-and-game-over/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1669&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></description>
				<content:encoded><![CDATA[<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<h2>Start Screen</h2>
<p>In the last section we <a title="Balloon Ninja:  Adding game logic" href="http://peak5390.wordpress.com/2013/02/07/balloon-ninja-adding-game-logic/">added some game logic</a> to Balloon Ninja, which made it more interesting to play. Now let&#8217;s add a start button, so the game does not start as soon as we open the game.</p>
<p>In pygame, a button can be thought of like any other object on the screen. So we will make a file Button.py, which is similar to Balloon.py, Scoreboard.py, and Sword.py:</p>
<pre class="brush: python; title: ; wrap-lines: false; notranslate">import pygame.font
from pygame.sprite import Sprite

class Button(Sprite):

    def __init__(self, screen, x_pos, y_pos, settings, msg):

        Sprite.__init__(self)
        self.screen = screen
        self.settings = settings

        # Set dimensions and properties of button
        self.width, self.height = settings.button_width, settings.button_height
        self.x_position, self.y_position = x_pos, y_pos
        self.rect = pygame.Rect(x_pos, y_pos, self.width, self.height)
        self.font = pygame.font.SysFont(settings.button_font, settings.button_font_size)
        self.msg = msg

        # Button message only needs to be prepped once, not on every blit
        self.prep_msg()

    def prep_msg(self):
        # Turn msg into image that can be rendered
        self.msg_image = self.font.render(self.msg, True, self.settings.button_text_color)
        # Determine offset to center text on button
        self.msg_x = self.x_position + (self.width - self.msg_image.get_width()) / 2
        self.msg_y = self.y_position + (self.height - self.msg_image.get_height()) / 2

    def blitme(self):
        # Draw blank button, and draw message
        self.screen.fill(self.settings.button_bg, self.rect)
        self.screen.blit(self.msg_image, (self.msg_x, self.msg_y))</pre>
<p>On line 1 we import pygame.font so that we can add some text to our button. On line 6 we specify the parameters that are needed to make a button. A button needs to know what screen it will be drawn on, and it needs to know where it will be drawn. It needs access to the overall game settings, and it needs to know what text to display on the button. The rest of this file is similar to Scoreboard.py, except that the call to prep_msg() only needs to be done once. Since the message on our button never changes, we don&#8217;t need to process the text into an image every time the button is blitted.</p>
<p>We need to add some settings specific to our buttons:</p>
<pre class="brush: python; highlight: [9,10,11,12]; title: ; wrap-lines: false; notranslate">class Settings():

    def __init__(self):
        # screen parameters
        self.screen_width, self.screen_height = 800, 600
        self.bg_color = 200, 200, 200
        self.scoreboard_height = 50

        self.button_width, self.button_height = 250, 50
        self.button_bg = (0,163,0)
        self.button_text_color = (235,235,235)
        self.button_font, self.button_font_size = 'Arial', 24

        self.initialize_game_parameters()

    def initialize_game_parameters(self):
        # game play parameters
        self.balloon_speed = 0.1
        # How quickly the speed of balloons rises
        #  ~1.05 during testing and ~1.01 for actual play
        self.speed_increase_factor = 1.05
        self.points_per_balloon = 10

        # Number of balloons to release in a spawning:
        self.batch_size = 1
        # Number of balloons that need to be popped before increasing batch_size
        #  For actual play, probably want ~10; for testing, ~3
        self.pops_needed = 3</pre>
<p>We are setting things up so that we will easily be able to add more buttons. We include settings that cover a buttons width, height, background color, text color, font, and font size. These can be overridden for individual buttons, but for now we have an easy way to play around with the overall look and feel of any buttons we decide to implement.</p>
<p>Finally, we need to create the actual button in our main file:</p>
<pre class="brush: python; highlight: [6,17,18,51,52]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button

def run_game():
    # Get access to our game settings
    settings = Settings()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings.scoreboard_height)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []
    spawn_balloon(screen, settings, balloons)

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        check_events(sword, mouse_x, mouse_y)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        # Update the sword's position and check for popped or disappeared balloons
        update_sword(sword, mouse_x, mouse_y, settings)
        check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

        # If all balloons have disappeared, either through popping or rising,
        #  release a new batch of balloons.
        #  Increase the balloon speed for each new batch of balloons.
        if len(balloons) == 0:
            settings.balloon_speed *= settings.speed_increase_factor
            release_batch(screen, settings, balloons)

        # Display updated scoreboard
        scoreboard.blitme()

        # Show play button
        play_button.blitme()

        # Show the redrawn screen
        pygame.display.flip()

def release_batch(screen, settings, balloons):
    for x in range(0, settings.batch_size):
        spawn_balloon(screen, settings, balloons)

def check_balloons(balloons, sword, scoreboard, screen, settings, time_passed):
    # Find any balloons that have been popped,
    #  or have disappeared off the top of the screen
    for balloon in balloons:
        balloon.update(time_passed)

        if balloon.rect.colliderect(sword.rect):
            pop_balloon(scoreboard, settings, balloon, balloons)
            continue

        if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
            miss_balloon(scoreboard, balloon, balloons)
            spawn_balloon(screen, settings, balloons)
            continue

        balloon.blitme()

def update_sword(sword, mouse_x, mouse_y, settings):
    # Update the sword's position, and draw the sword on the screen
    sword.x_position = mouse_x
    if sword.grabbed:
        sword.y_position = mouse_y
    else:
        sword.y_position = sword.image_h/2 + settings.scoreboard_height
    sword.update_rect()
    sword.blitme()

def miss_balloon(scoreboard, balloon, balloons):
    scoreboard.balloons_missed += 1
    balloons.remove(balloon)

def pop_balloon(scoreboard, settings, balloon, balloons):
    scoreboard.balloons_popped += 1
    scoreboard.score += settings.points_per_balloon
    balloons.remove(balloon)
    # If we have popped enough balloons, increase batch_size:
    if scoreboard.balloons_popped % settings.pops_needed == 0:
        settings.batch_size += 1

def spawn_balloon(screen, settings, balloons):
    balloons.append(Balloon(screen, settings))

def check_events(sword, mouse_x, mouse_y):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            if sword.rect.collidepoint(mouse_x, mouse_y):
                sword.grabbed = True
        if event.type == pygame.MOUSEBUTTONUP:
            sword.grabbed = False

run_game()</pre>
<p>On line 6, we import our new Button. On lines 17 and 18, we create our play button. We want to center the button at first, so its x-position is half of the screen width, minus half the button width. The y-position is calculated similarly. For the moment, we just want to display our button and make sure it looks reasonable. So we blit the button on line 52, just after the scoreboard, and just before we flip the screen. Here is what we see when we run the game:</p>
<div id="attachment_1688" class="wp-caption aligncenter" style="width: 650px"><a href="http://peak5390.files.wordpress.com/2013/02/play_button_static.png"><img class="size-large wp-image-1688" alt="The play button appears at the center of the screen, but it doesn't do anything yet." src="http://peak5390.files.wordpress.com/2013/02/play_button_static.png?w=640&#038;h=500" width="640" height="500" /></a><p class="wp-caption-text">The play button appears at the center of the screen, but it doesn&#8217;t do anything yet.</p></div>
<p>Now we want to make sure the game does not start until we click the button. We start out by creating a variable in Settings.py called game_active, which will keep track of whether a game is currently being played:</p>
<pre class="brush: python; highlight: [14,15]; title: ; wrap-lines: false; notranslate">class Settings():

    def __init__(self):
        # screen parameters
        self.screen_width, self.screen_height = 800, 600
        self.bg_color = 200, 200, 200
        self.scoreboard_height = 50

        self.button_width, self.button_height = 250, 50
        self.button_bg = (0,163,0)
        self.button_text_color = (235,235,235)
        self.button_font, self.button_font_size = 'Arial', 24

        # game status
        self.game_active = False

        self.initialize_game_parameters()

    def initialize_game_parameters(self):
        # game play parameters
        self.balloon_speed = 0.1
        # How quickly the speed of balloons rises
        #  ~1.05 during testing and ~1.01 for actual play
        self.speed_increase_factor = 1.05
        self.points_per_balloon = 10

        # Number of balloons to release in a spawning:
        self.batch_size = 1
        # Number of balloons that need to be popped before increasing batch_size
        #  For actual play, probably want ~10; for testing, ~3
        self.pops_needed = 3</pre>
<p>We initialize this variable to False, so the game will not run immediately when the program loads. Now we need to modify our main file to use this setting:</p>
<pre class="brush: python; highlight: [32,37,38,39,40,41,42,43,44,45,46,47,48,49,50,104,111,112]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button

def run_game():
    # Get access to our game settings
    settings = Settings()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings.scoreboard_height)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []
    spawn_balloon(screen, settings, balloons)

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        check_events(settings, sword, play_button, mouse_x, mouse_y)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            update_sword(sword, mouse_x, mouse_y, settings)
            check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            #  Increase the balloon speed for each new batch of balloons.
            if len(balloons) == 0:
                settings.balloon_speed *= settings.speed_increase_factor
                release_batch(screen, settings, balloons)
        else:
            # Show play button
            play_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

def release_batch(screen, settings, balloons):
    for x in range(0, settings.batch_size):
        spawn_balloon(screen, settings, balloons)

def check_balloons(balloons, sword, scoreboard, screen, settings, time_passed):
    # Find any balloons that have been popped,
    #  or have disappeared off the top of the screen
    for balloon in balloons:
        balloon.update(time_passed)

        if balloon.rect.colliderect(sword.rect):
            pop_balloon(scoreboard, settings, balloon, balloons)
            continue

        if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
            miss_balloon(scoreboard, balloon, balloons)
            spawn_balloon(screen, settings, balloons)
            continue

        balloon.blitme()

def update_sword(sword, mouse_x, mouse_y, settings):
    # Update the sword's position, and draw the sword on the screen
    sword.x_position = mouse_x
    if sword.grabbed:
        sword.y_position = mouse_y
    else:
        sword.y_position = sword.image_h/2 + settings.scoreboard_height
    sword.update_rect()
    sword.blitme()

def miss_balloon(scoreboard, balloon, balloons):
    scoreboard.balloons_missed += 1
    balloons.remove(balloon)

def pop_balloon(scoreboard, settings, balloon, balloons):
    scoreboard.balloons_popped += 1
    scoreboard.score += settings.points_per_balloon
    balloons.remove(balloon)
    # If we have popped enough balloons, increase batch_size:
    if scoreboard.balloons_popped % settings.pops_needed == 0:
        settings.batch_size += 1

def spawn_balloon(screen, settings, balloons):
    balloons.append(Balloon(screen, settings))

def check_events(settings, sword, play_button, mouse_x, mouse_y):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            if sword.rect.collidepoint(mouse_x, mouse_y):
                sword.grabbed = True
            if play_button.rect.collidepoint(mouse_x, mouse_y):
                settings.game_active = True
        if event.type == pygame.MOUSEBUTTONUP:
            sword.grabbed = False

run_game()</pre>
<p>We have been calling check_events on line 32. This function now needs to watch for clicks on the play button, so it needs access to settings and to play_button. On line 104 the check_events function needs to accept these parameters. When the mouse button is pressed, we need to check whether the play button has been clicked. Line 111 makes this check, and if the button is pressed line 112 sets game_active to True.</p>
<p>On lines 37-50 we make sure the code that actually runs the game only executes when a game is active. We need the rest of the code in the main event loop to keep running, so that we can monitor mouse clicks and keep the screen refreshed. We move the code that displays the play button into an else clause, so it only appears when game_active is False.</p>
<p>The game doesn&#8217;t look much different now, but when you run the game it doesn&#8217;t start until you press play. This is better, but the game still runs forever once you press play.</p>
<h2>Game Over</h2>
<p>With our game_active flag, we can now easily decide when a game is over. To start out, let&#8217;s make the game stop when three balloons have been missed. The basic implementation of this only takes 2 new lines in our main file:</p>
<pre class="brush: python; highlight: [77,78]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button

def run_game():
    # Get access to our game settings
    settings = Settings()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings.scoreboard_height)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []
    spawn_balloon(screen, settings, balloons)

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        check_events(settings, sword, play_button, mouse_x, mouse_y)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            update_sword(sword, mouse_x, mouse_y, settings)
            check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            #  Increase the balloon speed for each new batch of balloons.
            if len(balloons) == 0:
                settings.balloon_speed *= settings.speed_increase_factor
                release_batch(screen, settings, balloons)
        else:
            # Show play button
            play_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

def release_batch(screen, settings, balloons):
    for x in range(0, settings.batch_size):
        spawn_balloon(screen, settings, balloons)

def check_balloons(balloons, sword, scoreboard, screen, settings, time_passed):
    # Find any balloons that have been popped,
    #  or have disappeared off the top of the screen
    for balloon in balloons:
        balloon.update(time_passed)

        if balloon.rect.colliderect(sword.rect):
            pop_balloon(scoreboard, settings, balloon, balloons)
            continue

        if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
            miss_balloon(scoreboard, balloon, balloons)
            spawn_balloon(screen, settings, balloons)
            continue

        if scoreboard.balloons_missed &gt; 3:
            settings.game_active = False

        balloon.blitme()

def update_sword(sword, mouse_x, mouse_y, settings):
    # Update the sword's position, and draw the sword on the screen
    sword.x_position = mouse_x
    if sword.grabbed:
        sword.y_position = mouse_y
    else:
        sword.y_position = sword.image_h/2 + settings.scoreboard_height
    sword.update_rect()
    sword.blitme()

def miss_balloon(scoreboard, balloon, balloons):
    scoreboard.balloons_missed += 1
    balloons.remove(balloon)

def pop_balloon(scoreboard, settings, balloon, balloons):
    scoreboard.balloons_popped += 1
    scoreboard.score += settings.points_per_balloon
    balloons.remove(balloon)
    # If we have popped enough balloons, increase batch_size:
    if scoreboard.balloons_popped % settings.pops_needed == 0:
        settings.batch_size += 1

def spawn_balloon(screen, settings, balloons):
    balloons.append(Balloon(screen, settings))

def check_events(settings, sword, play_button, mouse_x, mouse_y):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            if sword.rect.collidepoint(mouse_x, mouse_y):
                sword.grabbed = True
            if play_button.rect.collidepoint(mouse_x, mouse_y):
                settings.game_active = True
        if event.type == pygame.MOUSEBUTTONUP:
            sword.grabbed = False

run_game()</pre>
<p>In the function check_balloons, we just add a quick check to see if the number of balloons missed has exceeded 3. If it has, we change game_active to False. This works to stop the game, but there are some problems. We don&#8217;t see any message that the game is over, and we can&#8217;t restart the game because balloons_missed has not been reset.</p>
<p>Let&#8217;s make a few changes to clean up these issues. We will move the number of balloons that can be missed to settings, create a &#8220;Game Over&#8221; button, and reset game stats when the user presses the play button. Let&#8217;s deal with settings first:</p>
<pre class="brush: python; highlight: [17,18,19]; title: ; wrap-lines: false; notranslate">class Settings():

    def __init__(self):
        # screen parameters
        self.screen_width, self.screen_height = 800, 600
        self.bg_color = 200, 200, 200
        self.scoreboard_height = 50

        self.button_width, self.button_height = 250, 50
        self.button_bg = (0,163,0)
        self.button_text_color = (235,235,235)
        self.button_font, self.button_font_size = 'Arial', 24

        # game status
        self.game_active = False

        # game over conditions
        self.misses_allowed = 3
        self.games_played = 0

        self.initialize_game_parameters()

    def initialize_game_parameters(self):
        # game play parameters
        self.balloon_speed = 0.1
        # How quickly the speed of balloons rises
        #  ~1.05 during testing and ~1.01 for actual play
        self.speed_increase_factor = 1.05
        self.points_per_balloon = 10

        # Number of balloons to release in a spawning:
        self.batch_size = 1
        # Number of balloons that need to be popped before increasing batch_size
        #  For actual play, probably want ~10; for testing, ~3
        self.pops_needed = 3</pre>
<p>On line 18 we set the number of balloons the player is allowed to miss. On line 19, we start keeping track of the number of games that have been played. This will let us only display the &#8220;Game Over&#8221; button once a game has been completed.</p>
<p>Now let&#8217;s modify our main file to make a Game Over button, and to use our settings to determine when a game is over:</p>
<pre class="brush: python; highlight: [19,20,22,23,33,52,53,54,83,84,85,86,113,120,121,122,123,124,125,126]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button

def run_game():
    # Get access to our game settings
    settings = Settings()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings.scoreboard_height)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            update_sword(sword, mouse_x, mouse_y, settings)
            check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            #  Increase the balloon speed for each new batch of balloons.
            if len(balloons) == 0:
                settings.balloon_speed *= settings.speed_increase_factor
                release_batch(screen, settings, balloons)
        else:
            # Show play button
            play_button.blitme()
            # If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

def release_batch(screen, settings, balloons):
    for x in range(0, settings.batch_size):
        spawn_balloon(screen, settings, balloons)

def check_balloons(balloons, sword, scoreboard, screen, settings, time_passed):
    # Find any balloons that have been popped,
    #  or have disappeared off the top of the screen
    for balloon in balloons:
        balloon.update(time_passed)

        if balloon.rect.colliderect(sword.rect):
            pop_balloon(scoreboard, settings, balloon, balloons)
            continue

        if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
            miss_balloon(scoreboard, balloon, balloons)
            spawn_balloon(screen, settings, balloons)
            continue

        balloon.blitme()

    if scoreboard.balloons_missed &gt; settings.misses_allowed:
        # Set game_active to false, empty the list of balloons, and increment games_played
        settings.game_active = False
        settings.games_played += 1

def update_sword(sword, mouse_x, mouse_y, settings):
    # Update the sword's position, and draw the sword on the screen
    sword.x_position = mouse_x
    if sword.grabbed:
        sword.y_position = mouse_y
    else:
        sword.y_position = sword.image_h/2 + settings.scoreboard_height
    sword.update_rect()
    sword.blitme()

def miss_balloon(scoreboard, balloon, balloons):
    scoreboard.balloons_missed += 1
    balloons.remove(balloon)

def pop_balloon(scoreboard, settings, balloon, balloons):
    scoreboard.balloons_popped += 1
    scoreboard.score += settings.points_per_balloon
    balloons.remove(balloon)
    # If we have popped enough balloons, increase batch_size:
    if scoreboard.balloons_popped % settings.pops_needed == 0:
        settings.batch_size += 1

def spawn_balloon(screen, settings, balloons):
    balloons.append(Balloon(screen, settings))

def check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            if sword.rect.collidepoint(mouse_x, mouse_y):
                sword.grabbed = True
            if play_button.rect.collidepoint(mouse_x, mouse_y):
                # Play button has been pressed.  Empty list of balloons,
                #  initialize scoreboard and game parameters, and make game active.
                del balloons[:]
                scoreboard.initialize_stats()
                settings.initialize_game_parameters()
                settings.game_active = True
        if event.type == pygame.MOUSEBUTTONUP:
            sword.grabbed = False

run_game()</pre>
<p>There are a lot of changes here, so let&#8217;s go through them one at a time. (Then we&#8217;ll do some code cleanup, as our main file is getting pretty long.) On lines 19 and 20 we create our Game Over button. It has the same x-position as the Play button, but its y-position is moved upwards by twice the height of a button. Lines 22 and 23 are highlighted because we no longer want to spawn a balloon as soon as the program starts. That is now taken care of within the main loop, after the play button has been pressed. So we just create an empty list of balloons here.</p>
<p>On line 33, the call to check_events now needs the list of balloons. Let&#8217;s look at the actual function to see why. On line 113 we need to make sure the function receives the list of balloons. Once the player clicks the play button, we need to clear the existing list of balloons. Line 123 does this. Then we need to reset the game stats, such as the number of balloons popped and missed. We don&#8217;t do this as soon as a game is over, because we want those stats to be displayed while the Game Over message is being displayed. Finally, we set the game_active status to True, which will activate the game on the next pass through the main loop.</p>
<p>Lines 53 and 54 make sure the Game Over button is displayed when game_active is False. Remember that game_active starts out as False, so we only want to display this button if the player has already completed at least one game. It would be awkward to see a Game Over message as soon as you start the program.</p>
<p>Finally, line 83 uses the maximum number of balloons specified in settings instead of a hard-coded 3. It also increments the number of games played as soon as a game is completed.</p>
<h2>It&#8217;s time for an engine</h2>
<p>There are two more things we&#8217;d like to do in this section of the tutorial, but we need to do another round of code cleanup first. Our main file is now 130 lines, and just getting longer. Most of this file length comes from the series of functions we have outside of the main loop. Let&#8217;s move those functions to a file called Engine.py (our &#8220;game engine&#8221;). We are going to copy and paste most of these functions to our new file. For the purposes of the tutorial, I will show our current version of balloon_ninja.py, with the functions we want to move to Engine.py highlighted:</p>
<pre class="brush: python; highlight: [62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button

def run_game():
    # Get access to our game settings
    settings = Settings()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings.scoreboard_height)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            update_sword(sword, mouse_x, mouse_y, settings)
            check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            #  Increase the balloon speed for each new batch of balloons.
            if len(balloons) == 0:
                settings.balloon_speed *= settings.speed_increase_factor
                release_batch(screen, settings, balloons)
        else:
            # Show play button
            play_button.blitme()
            # If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

def release_batch(screen, settings, balloons):
    for x in range(0, settings.batch_size):
        spawn_balloon(screen, settings, balloons)

def check_balloons(balloons, sword, scoreboard, screen, settings, time_passed):
    # Find any balloons that have been popped,
    #  or have disappeared off the top of the screen
    for balloon in balloons:
        balloon.update(time_passed)

        if balloon.rect.colliderect(sword.rect):
            pop_balloon(scoreboard, settings, balloon, balloons)
            continue

        if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
            miss_balloon(scoreboard, balloon, balloons)
            spawn_balloon(screen, settings, balloons)
            continue

        balloon.blitme()

    if scoreboard.balloons_missed &gt; settings.misses_allowed:
        # Set game_active to false, empty the list of balloons, and increment games_played
        settings.game_active = False
        settings.games_played += 1

def update_sword(sword, mouse_x, mouse_y, settings):
    # Update the sword's position, and draw the sword on the screen
    sword.x_position = mouse_x
    if sword.grabbed:
        sword.y_position = mouse_y
    else:
        sword.y_position = sword.image_h/2 + settings.scoreboard_height
    sword.update_rect()
    sword.blitme()

def miss_balloon(scoreboard, balloon, balloons):
    scoreboard.balloons_missed += 1
    balloons.remove(balloon)

def pop_balloon(scoreboard, settings, balloon, balloons):
    scoreboard.balloons_popped += 1
    scoreboard.score += settings.points_per_balloon
    balloons.remove(balloon)
    # If we have popped enough balloons, increase batch_size:
    if scoreboard.balloons_popped % settings.pops_needed == 0:
        settings.batch_size += 1

def spawn_balloon(screen, settings, balloons):
    balloons.append(Balloon(screen, settings))

def check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            if sword.rect.collidepoint(mouse_x, mouse_y):
                sword.grabbed = True
            if play_button.rect.collidepoint(mouse_x, mouse_y):
                # Play button has been pressed.  Empty list of balloons,
                #  initialize scoreboard and game parameters, and make game active.
                del balloons[:]
                scoreboard.initialize_stats()
                settings.initialize_game_parameters()
                settings.game_active = True
        if event.type == pygame.MOUSEBUTTONUP:
            sword.grabbed = False

run_game()</pre>
<p>We will copy these functions into a new file called Engine.py. Once copied into the new file, we need to make a few changes to coordinate how the functions are called:</p>
<pre class="brush: python; highlight: [1,2,4,6,7,9,11,13,20,24,25,35,45,49,57,60]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Balloon import Balloon

class Engine():

    def __init__(self):
        pass

    def release_batch(self, screen, settings, balloons):
        for x in range(0, settings.batch_size):
            self.spawn_balloon(screen, settings, balloons)

    def check_balloons(self, balloons, sword, scoreboard, screen, settings, time_passed):
        # Find any balloons that have been popped,
        #  or have disappeared off the top of the screen
        for balloon in balloons:
            balloon.update(time_passed)

            if balloon.rect.colliderect(sword.rect):
                self.pop_balloon(scoreboard, settings, balloon, balloons)
                continue

            if balloon.y_position &lt; -balloon.image_h/2 + settings.scoreboard_height:
                self.miss_balloon(scoreboard, balloon, balloons)
                self.spawn_balloon(screen, settings, balloons)
                continue

            balloon.blitme()

        if scoreboard.balloons_missed &gt; settings.misses_allowed:
            # Set game_active to false, empty the list of balloons, and increment games_played
            settings.game_active = False
            settings.games_played += 1

    def update_sword(self, sword, mouse_x, mouse_y, settings):
        # Update the sword's position, and draw the sword on the screen
        sword.x_position = mouse_x
        if sword.grabbed:
            sword.y_position = mouse_y
        else:
            sword.y_position = sword.image_h/2 + settings.scoreboard_height
        sword.update_rect()
        sword.blitme()

    def miss_balloon(self, scoreboard, balloon, balloons):
        scoreboard.balloons_missed += 1
        balloons.remove(balloon)

    def pop_balloon(self, scoreboard, settings, balloon, balloons):
        scoreboard.balloons_popped += 1
        scoreboard.score += settings.points_per_balloon
        balloons.remove(balloon)
        # If we have popped enough balloons, increase batch_size:
        if scoreboard.balloons_popped % settings.pops_needed == 0:
            settings.batch_size += 1

    def spawn_balloon(self, screen, settings, balloons):
        balloons.append(Balloon(screen, settings))

    def check_events(self, settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if sword.rect.collidepoint(mouse_x, mouse_y):
                    sword.grabbed = True
                if play_button.rect.collidepoint(mouse_x, mouse_y):
                    # Play button has been pressed.  Empty list of balloons,
                    #  initialize scoreboard and game parameters, and make game active.
                    del balloons[:]
                    scoreboard.initialize_stats()
                    settings.initialize_game_parameters()
                    settings.game_active = True
            if event.type == pygame.MOUSEBUTTONUP:
                sword.grabbed = False</pre>
<p>On lines 1 and 2 the Engine class needs access to pygame and sys, and it needs to know how to make a Balloon. On line 4 we define our new class, and on lines 6 and 7 we have an empty __init__ function. For now, we don&#8217;t need to do anything specific when we create an engine, since the class is just a collection of functions right now. Since we are structuring this as a class, every line that defines a function needs the self variable passed to it as the first parameter. After that is taken care of, we need to modify any line that includes a call to an engine function, such as line 20. These calls now need to specify that they are calling a function within this class, which reads as self.pop_balloon(&#8230;) instead of just pop_balloon(&#8230;).</p>
<p>Finally, we need to modify balloon_ninja to use our new Engine. Here is the simplified version of balloon_ninja.py:</p>
<pre class="brush: python; highlight: [7,12,35,42,43,50]; title: ; wrap-lines: false; notranslate">import pygame, sys
from Settings import Settings
from Balloon import Balloon
from Sword import Sword
from Scoreboard import Scoreboard
from Button import Button
from Engine import Engine

def run_game():
    # Get access to our game settings
    settings = Settings()
    engine = Engine()

    # initialize game
    pygame.init()
    screen = pygame.display.set_mode( (settings.screen_width, settings.screen_height), 0, 32)
    clock = pygame.time.Clock()
    scoreboard = Scoreboard(screen, settings.scoreboard_height)
    play_button = Button(screen, settings.screen_width/2-settings.button_width/2,
                            settings.screen_height/2-settings.button_height/2, settings, &quot;Play Balloon Ninja&quot;)
    game_over_button = Button(screen, play_button.x_position, play_button.y_position-2*settings.button_height,
                            settings, &quot;Game Over&quot;)

    # Create a list to hold our balloons, and create our first balloon
    balloons = []

    # Create our dagger
    sword = Sword(screen, settings.scoreboard_height)

    # main event loop
    while True:
        # Advance our game clock, get the current mouse position, and check for new events
        time_passed = clock.tick(50)
        mouse_x, mouse_y = pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]
        engine.check_events(settings, scoreboard, sword, play_button, mouse_x, mouse_y, balloons)

        # Redraw the empty screen before redrawing any game objects
        screen.fill(settings.bg_color)

        if settings.game_active:
            # Update the sword's position and check for popped or disappeared balloons
            engine.update_sword(sword, mouse_x, mouse_y, settings)
            engine.check_balloons(balloons, sword, scoreboard, screen, settings, time_passed)

            # If all balloons have disappeared, either through popping or rising,
            #  release a new batch of balloons.
            #  Increase the balloon speed for each new batch of balloons.
            if len(balloons) == 0:
                settings.balloon_speed *= settings.speed_increase_factor
                engine.release_batch(screen, settings, balloons)
        else:
            # Show play button
            play_button.blitme()
            # If a game has just ended, show Game Over button
            if settings.games_played &gt; 0:
                game_over_button.blitme()

        # Display updated scoreboard
        scoreboard.blitme()

        # Show the redrawn screen
        pygame.display.flip()

run_game()</pre>
<p>These changes are really simple. On line 7 we import the Engine class, and on line 12 we make an engine object. Lines 35,42,43, and 50 are the only lines within our main run_game function that call engine functions, so these lines need a the &#8220;engine.&#8221; prefix on those functions.</p>
<p>We now have a much simpler main file to work with, and our overall program is organized reasonably well.</p>
<p>That&#8217;s enough for this section. In the next section we will improve our game over conditions, and implement a more interesting scoring system.</p>
<p>Next: <a title="Balloon Ninja: A better scoring system" href="http://peak5390.wordpress.com/2013/02/21/balloon-ninja-a-better-scoring-system/">A better scoring system</a></p>
<p>Back to <a title="Balloon Ninja: A simple pygame tutorial" href="http://peak5390.wordpress.com/2012/12/17/balloon-ninja-a-simple-pygame-tutorial/">Tutorial Contents</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/peak5390.wordpress.com/1669/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/peak5390.wordpress.com/1669/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=peak5390.wordpress.com&#038;blog=30274142&#038;post=1669&#038;subd=peak5390&#038;ref=&#038;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://peak5390.wordpress.com/2013/02/15/balloon-ninja-start-screen-and-game-over/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://2.gravatar.com/avatar/8654149218edba613bd0ec265759fc38?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">ehmatthes</media:title>
		</media:content>

		<media:content url="http://peak5390.files.wordpress.com/2013/02/play_button_static.png?w=640" medium="image">
			<media:title type="html">The play button appears at the center of the screen, but it doesn&#039;t do anything yet.</media:title>
		</media:content>
	</item>
	</channel>
</rss>
