Commits

Peter Ward committed 8406c15

Add start of tutorial.

  • Participants
  • Parent commits fbf403c

Comments (0)

Files changed (12)

 syntax: glob
 *~
 *.py[co]
+
+docs/build
+docs/tutorial.pdf
+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 && \
+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()

docs/firstbot.tex

+\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.
+

docs/introduction.tex

+\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.
+#!/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'))
+%%- 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 %%
+

docs/print_bot.py

+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()

docs/random_avoid.tex

+\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.
+

docs/random_simple.py

+from random import choice
+
+def random_bot(board, position):
+    return choice('UDLR')

docs/random_simple.tex

+%%- 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'])|.
+

docs/tutorial.tex

+\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}