--- /dev/null
+Codename Naja
+=============
+
+Entry in PyWeek #18 <http://www.pyweek.org/18/>
+
+URL:
+ http://pyweek.org/e/naja
+Team:
+ Naja
+Members:
+ Simon Cross
+ Neil Muller
+ Adrianna Pinska
+ Stefano Rivera
+ David Sharpe
+ Jeremy Thurgood
+License:
+ see LICENSE.txt
+
+
+Requirements
+============
+
+The game requires pygame and pymunk. Requirements can be installed by
+
+ pip install -e .
+
+Or
+
+ pip install -r requirements.txt
+
+It was developed using python 2.7 and pygame 1.9.2. Older versions may or may
+not work.
+
+
+Running the Game
+----------------
+
+On Windows or Mac OS X, locate the "run_game.pyw" file and double-click it.
+
+Othewise open a terminal / console and "cd" to the game directory and run:
+
+ python run_game.py
+
+
+How to Play the Game
+--------------------
+
+There are no ducks.
+
+
+Development notes
+-----------------
+
+Creating a source distribution with::
+
+ ./scripts/build_unix.sh
+
+You may also generate Windows executables and OS X applications::
+
+ python setup.py py2exe
+ python setup.py py2app
+
+Later you might be able to upload files to PyWeek with::
+
+ python pyweek_upload.py
+
+Later you might be able to upload to the Python Package Index with::
+
+ python setup.py register
+ python setup.py sdist upload
--- /dev/null
+import sys
+
+import pygame
+
+from naja.options import parse_args
+
+
+def main():
+ '''Launch the nagslang'''
+ parse_args(sys.argv)
+ pygame.display.init()
+ pygame.font.init()
+
+ raise NotImplementedError("Sorry, we haven't written a game yet.")
--- /dev/null
+SCREEN = (800, 600)
+FPS = 40
+FONT = 'DejaVuSans.ttf'
+FONT_SIZE = 16
+
+DEFAULTS = dict(
+ debug=False,
+ sound=True,
+ music=True,
+)
+
+# Sound constants
+FREQ = 44100 # same as audio CD
+BITSIZE = -16 # unsigned 16 bit
+CHANNELS = 2 # 1 == mono, 2 == stereo
+BUFFER = 1024 # audio buffer size in no. of samples
+DEFAULT_SOUND_VOLUME = 1.0 # sound volume
+DEFAULT_MUSIC_VOLUME = 0.3 # music volume
--- /dev/null
+import optparse
+import os
+
+from naja.constants import DEFAULTS
+
+
+class AttrDict(dict):
+ '''A dict with attribute access'''
+ def __getattr__(self, attr):
+ return self[attr]
+
+
+options = AttrDict()
+
+
+def parse_args(args):
+ '''
+ Parse arguments and store them in the options dictionary.
+
+ Note: If you add arguments, you need to add an appropriate default to the
+ DEFAULTS dict.
+ '''
+ options.update(DEFAULTS)
+
+ options.debug = 'DEBUG' in os.environ
+
+ parser = optparse.OptionParser()
+ parser.add_option('--no-sound',
+ dest='sound', action='store_false', default=True,
+ help='Disable all sound, including music')
+
+ parser.add_option('--no-music',
+ dest='music', action='store_false', default=True,
+ help='Disable music (but not sound)')
+
+ opts, _ = parser.parse_args(args)
+
+ for k in DEFAULTS:
+ if getattr(opts, k, None) is not None:
+ options[k] = getattr(opts, k)
--- /dev/null
+'''
+Upload script specifically engineered for the PyWeek challenge.
+
+Handles authentication and gives upload progress feedback.
+'''
+import sys
+import os
+import httplib
+import cStringIO
+import socket
+import time
+import getopt
+
+
+class Upload(object):
+ def __init__(self, filename):
+ self.filename = filename
+
+boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+sep_boundary = '\n--' + boundary
+end_boundary = sep_boundary + '--'
+
+
+def mimeEncode(data, sep_boundary=sep_boundary, end_boundary=end_boundary):
+ '''Take the mapping of data and construct the body of a
+ multipart/form-data message with it using the indicated boundaries.
+ '''
+ ret = cStringIO.StringIO()
+ for key, value in data.items():
+ # handle multiple entries for the same name
+ if not isinstance(value, list):
+ value = [value]
+ for value in value:
+ ret.write(sep_boundary)
+ if isinstance(value, Upload):
+ ret.write('\nContent-Disposition: form-data; name="%s"' % key)
+ filename = os.path.basename(value.filename)
+ ret.write('; filename="%s"\n\n' % filename)
+ value = open(os.path.join(value.filename), "rb").read()
+ else:
+ ret.write('\nContent-Disposition: form-data; name="%s"' % key)
+ ret.write("\n\n")
+ value = str(value)
+ ret.write(str(value))
+ if value and value[-1] == '\r':
+ ret.write('\n') # write an extra newline
+ ret.write(end_boundary)
+ return ret.getvalue()
+
+
+class Progress(object):
+ def __init__(self, info, data):
+ self.info = info
+ self.tosend = len(data)
+ self.total = self.tosend / 1024
+ self.data = cStringIO.StringIO(data)
+ self.start = self.now = time.time()
+ self.sent = 0
+ self.num = 0
+ self.stepsize = self.total / 100 or 1
+ self.steptimes = []
+ self.display()
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ self.num += 1
+ if self.sent >= self.tosend:
+ print self.info, 'done', ' ' * (75 - len(self.info) - 6)
+ sys.stdout.flush()
+ raise StopIteration
+
+ chunk = self.data.read(1024)
+ self.sent += len(chunk)
+ #print (self.num, self.stepsize, self.total, self.sent, self.tosend)
+
+ if self.num % self.stepsize:
+ return chunk
+ self.display()
+ return chunk
+
+ def display(self):
+ # figure how long we've spent - guess how long to go
+ now = time.time()
+ steptime = now - self.now
+ self.steptimes.insert(0, steptime)
+ if len(self.steptimes) > 5:
+ self.steptimes.pop()
+ steptime = sum(self.steptimes) / len(self.steptimes)
+ self.now = now
+ eta = steptime * ((self.total - self.num) / self.stepsize)
+
+ # tell it like it is (or might be)
+ if now - self.start > 3:
+ M = eta / 60
+ H = M / 60
+ M = M % 60
+ S = eta % 60
+ if self.total:
+ s = '%s %2d%% (ETA %02d:%02d:%02d)' % (self.info,
+ self.num * 100. / self.total, H, M, S)
+ else:
+ s = '%s 0%% (ETA %02d:%02d:%02d)' % (self.info, H, M, S)
+ elif self.total:
+ s = '%s %2d%%' % (self.info, self.num * 100. / self.total)
+ else:
+ s = '%s %d done' % (self.info, self.num)
+ sys.stdout.write(s + ' ' * (75 - len(s)) + '\r')
+ sys.stdout.flush()
+
+
+class progressHTTPConnection(httplib.HTTPConnection):
+ def progress_send(self, str):
+ """Send `str' to the server."""
+ if self.sock is None:
+ self.connect()
+
+ p = Progress('Uploading', str)
+ for chunk in p:
+ sent = 0
+ while sent != len(chunk):
+ try:
+ sent += self.sock.send(chunk)
+ except socket.error, v:
+ if v[0] == 32: # Broken pipe
+ self.close()
+ raise
+ p.display()
+
+
+class progressHTTP(httplib.HTTP):
+ _connection_class = progressHTTPConnection
+
+ def _setup(self, conn):
+ httplib.HTTP._setup(self, conn)
+ self.progress_send = self._conn.progress_send
+
+
+def http_request(data, server, port, url):
+ h = progressHTTP(server, port)
+
+ data = mimeEncode(data)
+ h.putrequest('POST', url)
+ h.putheader('Content-type', 'multipart/form-data; boundary=%s' % boundary)
+ h.putheader('Content-length', str(len(data)))
+ h.putheader('Host', server)
+ h.endheaders()
+
+ h.progress_send(data)
+
+ errcode, errmsg, headers = h.getreply()
+
+ f = h.getfile()
+ response = f.read().strip()
+ f.close()
+
+ print '%s %s' % (errcode, errmsg)
+ if response:
+ print response
+
+
+def usage():
+ print '''This program is to be used to upload files to the PyWeek system.
+You may use it to upload screenshots or code submissions.
+
+REQUIRED ARGUMENTS:
+ -u username
+ -p password
+ -d description of file
+ -c file to upload
+ -e entry short name
+
+OPTIONAL ARGUMENTS:
+ -s file is a screenshot
+ -f file is FINAL submission
+ -h override default host name (www.pyweek.org)
+ -P override default host port (80)
+
+In order to qualify for judging at the end of the challenge, you MUST
+upload your source and check the "Final Submission" checkbox.
+'''
+
+
+if __name__ == '__main__':
+ try:
+ optlist, args = getopt.getopt(sys.argv[1:], 'e:u:p:sfd:h:P:c:')
+ except getopt.GetoptError, message:
+ print message
+ usage()
+ sys.exit(1)
+ host = 'www.pyweek.org'
+ port = 80
+ data = dict(version=2)
+ optional = {}
+ url = None
+ for opt, arg in optlist:
+ if opt == '-u':
+ data['user'] = arg
+ elif opt == '-p':
+ data['password'] = arg
+ elif opt == '-s':
+ optional['is_screenshot'] = 'yes'
+ elif opt == '-f':
+ optional['is_final'] = 'yes'
+ elif opt == '-d':
+ data['description'] = arg
+ elif opt == '-c':
+ data['content_file'] = Upload(arg)
+ elif opt == '-e':
+ url = '/e/%s/oup/' % arg
+ elif opt == '-h':
+ host = arg
+ elif opt == '-P':
+ port = int(arg)
+
+ if len(data) < 4 or url is None:
+ print 'Required argument missing'
+ usage()
+ sys.exit(1)
+
+ data.update(optional)
+ http_request(data, host, port, url)
--- /dev/null
+# Our dependencies are all specified in setup.py.
+-e .
--- /dev/null
+#! /usr/bin/env python
+
+import naja.__main__
+if __name__ == "__main__":
+ naja.__main__.main()
--- /dev/null
+#! /usr/bin/env python
+
+import naja.__main__
+if __name__ == "__main__":
+ naja.__main__.main()
--- /dev/null
+# setup.py
+# -*- coding: utf8 -*-
+# vim:fileencoding=utf8 ai ts=4 sts=4 et sw=4
+
+"""Setuptools setup.py file for naja."""
+
+from setuptools import setup, find_packages
+
+try:
+ import py2exe
+ py2exe # To make pyflakes happy.
+except ImportError:
+ pass
+
+# This should probably be pulled from constants.py
+VERSION_STR = "0.1"
+
+setup(
+ name="naja",
+ version=VERSION_STR,
+ description="naja: Game for PyWeek 18",
+
+ author=(", ".join([
+ "Simon Cross",
+ "Neil Muller",
+ "Adrianna Pinska",
+ "Stefano Rivera",
+ "David Sharpe",
+ "Jeremy Thurgood",
+ ])),
+ author_email="ctpug@googlegroups.com",
+
+ maintainer="Naja Team",
+ maintainer_email="ctpug@googlegroups.com",
+
+ url="http://ctpug.org.za/",
+ download_url="http://www.ctpug.org.za/gitweb/?p=naja.git",
+
+ license="MIT",
+
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: MacOS X',
+ 'Environment :: Win32 (MS Windows)',
+ 'Environment :: X11 Applications',
+ 'Intended Audience :: End Users/Desktop',
+ 'License :: OSI Approved :: MIT License',
+ 'Natural Language :: English',
+ 'Operating System :: Microsoft :: Windows',
+ 'Operating System :: POSIX',
+ 'Operating System :: MacOS :: MacOS X',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Topic :: Games/Entertainment',
+ ],
+
+ platforms=[
+ 'Linux',
+ 'Mac OS X',
+ 'Windows',
+ ],
+
+ # Dependencies
+ install_requires=['pygame'],
+
+ # Files
+ packages=find_packages(),
+ scripts=[
+ 'scripts/naja',
+ ],
+
+ # py2exe
+ windows=[{
+ 'script': 'scripts/naja',
+ 'icon_resources': [(0, "data/icons/naja.ico")],
+ }],
+ app=['scripts/naja'],
+ options={
+ 'py2exe': {
+ 'skip_archive': 1,
+ 'dist_dir': 'dist/naja-%s' % VERSION_STR,
+ 'packages': [
+ 'logging', 'encodings', 'naja',
+ ],
+ 'includes': [
+ 'pygame', 'pymunk',
+ ],
+ 'excludes': [
+ 'numpy',
+ ],
+ 'ignores': [
+ # all database modules
+ 'pgdb', 'Sybase', 'adodbapi',
+ 'kinterbasdb', 'psycopg', 'psycopg2', 'pymssql',
+ 'sapdb', 'pysqlite2', 'sqlite', 'sqlite3',
+ 'MySQLdb', 'MySQLdb.connections',
+ 'MySQLdb.constants.CR', 'MySQLdb.constants.ER',
+ # old datetime equivalents
+ 'DateTime', 'DateTime.ISO',
+ 'mx', 'mx.DateTime', 'mx.DateTime.ISO',
+ # email modules
+ 'email.Generator', 'email.Iterators', 'email.Utils',
+ ],
+ },
+ 'py2app': {
+ 'app': ['run_game.py'],
+ 'argv_emulation': True,
+ 'iconfile': 'data/icons/program/icon.icns',
+ 'packages': [
+ 'logging', 'encodings', 'pygame', 'naja', 'data',
+ ],
+ 'excludes': ['numpy'],
+ }},
+ data_files=[
+ # 'COPYRIGHT',
+ 'LICENSE.txt',
+ 'README.txt',
+ ],
+ include_package_data=True,
+)