summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Ward <peteraward@gmail.com>2012-07-29 23:43:29 +1000
committerPeter Ward <peteraward@gmail.com>2012-07-29 23:43:29 +1000
commitc35db75eba1f67c6d6bbca9fefe7aaefb6b6d6e9 (patch)
treed32b851da74ed9b053248351f308a734b8c2a8b6
parentb62e033801895ad633f702685e50cf367ae0adee (diff)
Add start of tutorial.
-rw-r--r--.hgignore3
-rw-r--r--docs/Makefile27
-rw-r--r--docs/firstbot.py8
-rw-r--r--docs/firstbot.tex22
-rw-r--r--docs/introduction.tex37
-rwxr-xr-xdocs/jinja251
-rw-r--r--docs/macros.tex19
-rw-r--r--docs/print_bot.py9
-rw-r--r--docs/random_avoid.tex47
-rw-r--r--docs/random_simple.py4
-rw-r--r--docs/random_simple.tex63
-rw-r--r--docs/tutorial.tex47
12 files changed, 337 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore
index e21f17c..2bece3c 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,3 +1,6 @@
syntax: glob
*~
*.py[co]
+
+docs/build
+docs/tutorial.pdf
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..8ea7df2
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,27 @@
+BUILD_DIR = build
+
+FILES = $(wildcard *.tex *.py)
+BUILD_FILES = $(patsubst %,${BUILD_DIR}/%,${FILES})
+
+LATEX=xelatex
+LATEX_FLAGS=-shell-escape
+
+.PHONY: all
+
+all: tutorial.pdf
+
+${BUILD_DIR}:
+ mkdir -p ${BUILD_DIR}
+
+${BUILD_DIR}/%.tex: %.tex ${BUILD_DIR}
+ ./jinja2 --latex < $< > $@
+
+${BUILD_DIR}/%.py: %.py
+ ln $< $@
+
+tutorial.pdf: ${BUILD_DIR}/tutorial.tex ${BUILD_FILES}
+ cd "${BUILD_DIR}" && \
+ ${LATEX} ${LATEX_FLAGS} tutorial
+ mv -f "${BUILD_DIR}/tutorial.pdf" tutorial.pdf
+# ${LATEX} ${LATEX_FLAGS} tutorial && \
+# ${LATEX} ${LATEX_FLAGS} tutorial && \
diff --git a/docs/firstbot.py b/docs/firstbot.py
new file mode 100644
index 0000000..b7abf4b
--- /dev/null
+++ b/docs/firstbot.py
@@ -0,0 +1,8 @@
+def up_bot(board, position):
+ return 'U'
+
+if __name__ == '__main__':
+ from snakegame.engines.pyglet import PygletEngine
+ engine = PygletEngine(10, 10, 20)
+ engine.add_bot(up_bot)
+ engine.run()
diff --git a/docs/firstbot.tex b/docs/firstbot.tex
new file mode 100644
index 0000000..221928c
--- /dev/null
+++ b/docs/firstbot.tex
@@ -0,0 +1,22 @@
+\section{Your First Bot}
+\fasttrack{Always move up.}
+
+Alright, let’s get started.
+If you think back to when you started programming, chances are the first program
+you ever wrote was one which printed out the immortal phrase “Hello World”.
+Well we can’t print stuff here, but our first bot is going to be almost as
+useless as that: our bot is just going to continually move up.
+
+Let’s have a look at the code:
+\pythonfile{firstbot.py}
+
+If you run this script (\texttt{python firstbot.py}),
+you should see a nice big board with some apples scattered over it, and a snake
+continually moving upwards.
+That snake is our bot: each time the game decides that our snake is allowed to
+move, it calls the \texttt{up\_bot} function, which immediately returns the
+string \mint{python}|'U'|, which means it should move the snake upwards.
+
+Got all that?
+Once you’re ready, we’ll move on to something a little more interesting.
+
diff --git a/docs/introduction.tex b/docs/introduction.tex
new file mode 100644
index 0000000..3f8d1d8
--- /dev/null
+++ b/docs/introduction.tex
@@ -0,0 +1,37 @@
+\section{Introduction}
+
+I assume you know the basics of Python:
+printing stuff,
+if/elif/else,
+for and while loops and lists.
+That’s really all you need to follow along at least the first four sections,
+and then dictionaries will start to come in handy.
+
+If you have no idea what I was just talking about, \emph{don’t panic}.
+All that means is that you’re not quite ready for this yet,
+and you need to start by learning Python using some of these excellent
+resources:
+\begin{itemize}
+ \item \url{http://openbookproject.net/thinkcs/python/english2e/}
+ \item \url{http://learnpythonthehardway.org/}
+ \item \url{http://docs.python.org/tutorial/}
+ \item Anyone you know who knows about Python, or is a programmer.
+\end{itemize}
+Don’t be discouraged if it doesn’t immediately make sense:
+programming can be difficult and frustrating,
+but if you put the effort in, it can also be a very rewarding, interesting and
+fun activity.
+
+If you are stuck with anything, Google it first.
+
+You’ll need to start by getting the code.
+The repository is at
+\url{http://hg.flowblok.id.au/snakegame},
+you can install it with pip:
+\begin{minted}{sh}
+$ pip install hg+http://hg.flowblok.id.au/snakegame#egg=SnakeGame
+\end{minted}
+
+Each section starts with a Fast track note:
+if you know what you’re doing, just write a bot which moves according to what it
+says in the fast track note, and you can skip that section.
diff --git a/docs/jinja2 b/docs/jinja2
new file mode 100755
index 0000000..a1c89e2
--- /dev/null
+++ b/docs/jinja2
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+import argparse
+import json
+from os import path
+import sys
+
+from jinja2 import Environment, FileSystemLoader
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--latex', action='store_true')
+parser.add_argument('template', nargs='?')
+parser.add_argument('data', nargs='?')
+
+args = parser.parse_args()
+
+if args.latex:
+ env = Environment(
+ block_start_string='%%',
+ block_end_string='%%',
+ variable_start_string='<',
+ variable_end_string='>',
+ comment_start_string='###',
+ comment_end_string='###',
+ )
+else:
+ env = Environment()
+
+if args.template:
+ dirname, basename = path.split(args.template)
+
+ env.loader = FileSystemLoader(dirname)
+ template = env.get_template(basename)
+
+ if args.data:
+ with open(args.data, 'rb') as f:
+ data = json.load(f)
+
+ else:
+ data = json.load(sys.stdin)
+
+else:
+ source = sys.stdin.read()
+ template = env.from_string(source.decode('utf-8'))
+ data = {}
+
+ env.loader = FileSystemLoader('.')
+
+output = template.render(data)
+
+sys.stdout.write(output.encode('utf-8'))
diff --git a/docs/macros.tex b/docs/macros.tex
new file mode 100644
index 0000000..2f16900
--- /dev/null
+++ b/docs/macros.tex
@@ -0,0 +1,19 @@
+%%- macro make_board(board) %%
+\begin{verbatim}
+%% for row in board %%
+%%- if loop.first %%
+<- ' ' >
+%%- for n in range(row |length) %%
+<- ' ' ~ n >
+%%- endfor %%
+< ' +' ~ '-+' * (row | length) >
+%%- endif %%
+< loop.index0 ~ '|' >
+%%- for cell in row %%
+<- cell >|
+%%- endfor %%
+< ' +' ~ '-+' * (row | length) >
+%%- endfor %%
+\end{verbatim}
+%%- endmacro %%
+
diff --git a/docs/print_bot.py b/docs/print_bot.py
new file mode 100644
index 0000000..8352aec
--- /dev/null
+++ b/docs/print_bot.py
@@ -0,0 +1,9 @@
+def print_bot(board, position):
+ print position
+ print board
+
+if __name__ == '__main__':
+ from snakegame.engines.pyglet import PygletEngine
+ engine = PygletEngine(3, 4, 3)
+ engine.add_bot(print_bot)
+ engine.run()
diff --git a/docs/random_avoid.tex b/docs/random_avoid.tex
new file mode 100644
index 0000000..75d5a3a
--- /dev/null
+++ b/docs/random_avoid.tex
@@ -0,0 +1,47 @@
+\section{Random Avoid Bot}
+\fasttrack{Choose a direction at random, but not one which will lead to immediate death.}
+
+The last bot we wrote had a big problem, it ran into its own tail.
+We don’t want our next bot to be that stupid, so we need to teach it how to not
+do that!
+
+But before we can do that, we need to know few more things about our bots.
+You might have noticed that our functions have two parameters,
+\texttt{board} and \texttt{position}.
+We haven’t had to use them so far, but we will now, so we need to know what they
+are.
+But rather than me just telling you what they are,
+why not have a look yourself?
+
+\pythonfile{print\_bot.py}
+
+You should see something like this (on a 4x3 board):
+\begin{minted}{pytb}
+(1, 2)
+[['.', '.', '*', '.'], ['.', '.', '*', '.'], ['.', 'A', '.', '.']]
+Exception in bot A (<'<'>function print_bot at 0x7f61165f2e60<'>'>):
+------------------------------------------------------------
+Traceback (most recent call last):
+ File "…/snakegame/engine.py", line 132, in update_snakes
+ "Return value should be a string."
+AssertionError: Return value should be a string.
+------------------------------------------------------------
+\end{minted}
+
+Ignore all the Exception stuff, that’s just because we didn’t return one of
+\pyinline|'L'|, \pyinline|'U'|, \pyinline|'D'| or \pyinline|'R'|.
+The first line is our position: it’s a \pyinline|tuple| of the x and y
+coordinates of our snake’s head.
+The second line is the board: it’s a list of each row in the board,
+and each row is a list of the cells in that row.
+
+Notice that if we index the board first by the y coordinate and then by the x
+coordinate, we can get the character in the board where our snake is:
+\pyinline|board[y][x] == board[2][1] == 'A'|.
+The head of our snake is always an uppercase character in the board,
+and the rest of our body (the tail) are always lowercase characters.
+
+This is all very well, but how do we stop our bot from eating its tail?
+Well, the answer is that we need to look at each of the squares surrounding our
+snake’s head, to see if we’ll die if we move into them or not.
+
diff --git a/docs/random_simple.py b/docs/random_simple.py
new file mode 100644
index 0000000..f7ca03c
--- /dev/null
+++ b/docs/random_simple.py
@@ -0,0 +1,4 @@
+from random import choice
+
+def random_bot(board, position):
+ return choice('UDLR')
diff --git a/docs/random_simple.tex b/docs/random_simple.tex
new file mode 100644
index 0000000..7ccb3a5
--- /dev/null
+++ b/docs/random_simple.tex
@@ -0,0 +1,63 @@
+%%- from "macros.tex" import make_board -%%
+
+\section{Random Bot}
+\fasttrack{Choose a direction at random.}
+
+The next bot we’ll write is one which instead of moving in just one direction,
+chooses a direction at random to move in.
+Go on, try writing it yourself! I’ll wait here until you’re ready.
+
+Got it working? Good work!
+But you’ve probably noticed that there’s a problem:
+it doesn’t take long for our random bot to die.
+But why does it die?
+The answer is that once it eats an apple, it then has a tail, and since it
+doesn’t know any better, it will happily move into the square where its tail is.
+
+\begin{board}
+\hfill
+%
+\begin{subfigure}{.3\linewidth}
+< make_board([' *', ' A* ', ' * '])>
+\caption{Our intrepid snake heads towards an apple. Next move: \textbf{R}}
+\label{brd:random-death:1}
+\end{subfigure}
+\hfill
+%
+\begin{subfigure}{.3\linewidth}
+< make_board(['* *', ' aA ', ' * ']) >
+\caption{It has eaten the apple, and now has a tail. Next move: \textbf{L}}
+\label{brd:random-death:2}
+\end{subfigure}
+\hfill
+%
+\begin{subfigure}{.3\linewidth}
+< make_board(['* *', ' ', ' * ']) >
+\caption{It decided to move left, and ran into itself, oh no!}
+\label{brd:random-death:3}
+\end{subfigure}
+%
+\hfill
+
+\caption{The last moves of Random Bot before death.}
+\label{brd:random-death}
+\end{board}
+
+\pagebreak
+
+By the way, how long was your solution?
+If you’re still learning Python, you might like to have a peek at my solution to
+this bot, it’s only three lines long.
+Hopefully you didn’t write too much more than that!
+
+\pythonfile{random_simple.py}
+
+There are two key things that make my solution work.
+The first is the \texttt{random.choice} function,
+which returns a random item chosen from a sequence you give it.
+The second thing is that a string is a sequence:
+it is made up of the characters in it.
+So if you write \mint{python}|choice('UDLR')|,
+that’s the same as if you had written
+\mint{python}|choice(['U', 'D', 'L', 'R'])|.
+
diff --git a/docs/tutorial.tex b/docs/tutorial.tex
new file mode 100644
index 0000000..951c7de
--- /dev/null
+++ b/docs/tutorial.tex
@@ -0,0 +1,47 @@
+\documentclass[12pt]{article}
+
+\usepackage{fontspec}
+
+\usepackage[pdfborder={0 0 0}]{hyperref}
+\usepackage[margin=20mm]{geometry}
+
+\usepackage{float}
+\usepackage{subcaption}
+
+\floatstyle{ruled}
+\newfloat{board}{bh}{brd}
+\floatname{board}{Board}
+\DeclareCaptionSubType{board}
+
+\usepackage{minted}
+
+\newmint[pyinline]{python}{}
+\newmintedfile{python}{}
+\usemintedstyle{tango}
+
+\setmainfont{Linux Libertine O}
+
+\setlength\parskip{2ex}
+\setlength\parindent{0mm}
+
+\widowpenalty=1000
+\clubpenalty=1000
+\newcommand\fasttrack[1]{\vspace{-2ex}\hfill\emph{Fast track: #1}\nopagebreak}
+
+\begin{document}
+
+\title{Writing SnakeGame bots}
+\author{Peter Ward}
+\date{July 29, 2012}
+\maketitle
+
+\input{introduction.tex}
+
+%\pagebreak
+\input{firstbot.tex}
+
+\input{random_simple.tex}
+
+\input{random_avoid.tex}
+
+\end{document}