From c35db75eba1f67c6d6bbca9fefe7aaefb6b6d6e9 Mon Sep 17 00:00:00 2001 From: Peter Ward Date: Sun, 29 Jul 2012 23:43:29 +1000 Subject: Add start of tutorial. --- .hgignore | 3 +++ docs/Makefile | 27 ++++++++++++++++++++++ docs/firstbot.py | 8 +++++++ docs/firstbot.tex | 22 ++++++++++++++++++ docs/introduction.tex | 37 +++++++++++++++++++++++++++++ docs/jinja2 | 51 ++++++++++++++++++++++++++++++++++++++++ docs/macros.tex | 19 +++++++++++++++ docs/print_bot.py | 9 ++++++++ docs/random_avoid.tex | 47 +++++++++++++++++++++++++++++++++++++ docs/random_simple.py | 4 ++++ docs/random_simple.tex | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ docs/tutorial.tex | 47 +++++++++++++++++++++++++++++++++++++ 12 files changed, 337 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/firstbot.py create mode 100644 docs/firstbot.tex create mode 100644 docs/introduction.tex create mode 100755 docs/jinja2 create mode 100644 docs/macros.tex create mode 100644 docs/print_bot.py create mode 100644 docs/random_avoid.tex create mode 100644 docs/random_simple.py create mode 100644 docs/random_simple.tex create mode 100644 docs/tutorial.tex 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} -- cgit v1.2.3