diff options
Diffstat (limited to 'snakegame/snake.py')
-rw-r--r-- | snakegame/snake.py | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/snakegame/snake.py b/snakegame/snake.py new file mode 100644 index 0000000..89e97da --- /dev/null +++ b/snakegame/snake.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python + +from __future__ import division + +import sys +import time +import string +import random +from colour import hash_colour +from random import randint +from collections import deque +from copy import deepcopy +import traceback + +from common import * + +class SnakeEngine(object): + def __init__(self, rows, columns, n_apples, wrap=False, results=False, + *args, **kwargs): + super(SnakeEngine, self).__init__(*args, **kwargs) + + self.wrap = wrap + self.bots = {} + self.results = None + if results: + self.results = open('results.csv', 'a+') + + self.new_game(rows, columns, n_apples) + + def get_random_position(self): + x = randint(0, self.columns - 1) + y = randint(0, self.rows - 1) + return (x, y) + + def replace_random(self, old, new): + for i in xrange(self.rows * self.columns): + x, y = self.get_random_position() + if self.board[y][x] == old: + self.board[y][x] = new + return x, y + + def new_game(self, rows, columns, n_apples): + self.game_ticks = 0 + self.game_id = random.randint(0, sys.maxint) + + self.letters = list(string.lowercase) + self.letters.reverse() + + self.rows = rows + self.columns = columns + + # make board + self.board = [[Squares.EMPTY for x in xrange(columns)] for y in xrange(rows)] + for i in xrange(n_apples): + x, y = self.get_random_position() + self.board[y][x] = Squares.APPLE + + def add_bot(self, bot): + """ + A bot is a callable object, with this method signature: + def bot_callable( + board=[[cell for cell in row] for row in board], + position=(snake_x, snake_y) + ): + return random.choice('RULD') + """ + letter = self.letters.pop() + + name = bot.__name__ + colour = hash_colour(name) + + position = self.replace_random(Squares.EMPTY, letter.upper()) + if position is None: + raise KeyError, "Could not insert snake into the board." + + self.bots[letter] = [bot, colour, deque([position])] + return letter + + def remove_bot(self, letter): + letter = letter.lower() + + time_score = self.game_ticks + + for row in self.board: + for x, cell in enumerate(row): + if cell.lower() == letter: + row[x] = Squares.EMPTY + + bot = self.bots[letter] + del self.bots[letter] + + if not self.results: + return + + try: + name = bot[0].__name__ + except AttributeError: + pass + else: + apple_score = len(bot[2]) + self.results.write('%s,%s,%s,%s\n' % \ + (self.game_id, name, apple_score, time_score)) + self.results.flush() + + def update_snakes(self, directions_id=id(directions)): + assert id(directions) == directions_id, \ + "The common.directions dictionary has been modified since startup..." + + self.game_ticks += 1 + + for letter, (bot, colour, path) in self.bots.items(): + board = deepcopy(self.board) + try: + x, y = path[-1] + d = bot(board, (x, y)) + + # Sanity checking... + assert isinstance(d, basestring), \ + "Return value should be a string." + d = d.upper() + assert d in directions, "Return value should be 'U', 'D', 'L' or 'R'." + + # Get new position. + dx, dy = directions[d] + nx = x + dx + ny = y + dy + + if self.wrap: + ny %= self.rows + nx %= self.columns + else: + if ny < 0 or ny >= self.rows or nx < 0 or nx >= self.columns: + self.remove_bot(letter) + continue + + oldcell = self.board[ny][nx] + if oldcell in (Squares.EMPTY, Squares.APPLE): + # Move snake forward. + self.board[ny][nx] = letter.upper() + path.append((nx, ny)) + + # Make old head into body. + self.board[y][x] = letter.lower() + + if oldcell == Squares.APPLE: + # Add in an apple to compensate. + self.replace_random(Squares.EMPTY, Squares.APPLE) + else: + # Remove last part of snake. + ox, oy = path.popleft() + self.board[oy][ox] = Squares.EMPTY + else: + self.remove_bot(letter) + + except: + print "Exception in bot %s (%s):" % (letter.upper(), bot) + print '-'*60 + traceback.print_exc() + print '-'*60 + self.remove_bot(letter) + |