summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Ward <peteraward@gmail.com>2012-07-21 12:14:16 +1000
committerPeter Ward <peteraward@gmail.com>2012-07-21 12:14:16 +1000
commitb99240781b813dd030e18750bb9d2ac62711c249 (patch)
tree62c76efdd9d4115756d92f7eadff6b912f1aee76
parent38ad46d7ce7cb965726d2af5dee8f90261b4a44a (diff)
Refactoring into a single engine and viewers.
-rw-r--r--snakegame/__init__.py28
-rw-r--r--snakegame/engine.py (renamed from snakegame/engines/base.py)6
-rw-r--r--snakegame/engines/__init__.py15
-rw-r--r--snakegame/utils.py18
-rw-r--r--snakegame/viewers/__init__.py10
-rw-r--r--snakegame/viewers/curses.py (renamed from snakegame/engines/curses.py)21
-rw-r--r--snakegame/viewers/pygame.py (renamed from snakegame/engines/pygame.py)79
-rw-r--r--snakegame/viewers/pyglet.py (renamed from snakegame/engines/pyglet.py)73
8 files changed, 117 insertions, 133 deletions
diff --git a/snakegame/__init__.py b/snakegame/__init__.py
index c3e9f4e..c4dbc83 100644
--- a/snakegame/__init__.py
+++ b/snakegame/__init__.py
@@ -1,4 +1,5 @@
-from snakegame.engines import BUILTIN_ENGINES
+from snakegame.engine import Engine
+from snakegame.viewers import BUILTIN_VIEWERS
def first(d):
for item in d:
@@ -14,22 +15,13 @@ def import_thing(name, default_obj):
mod = __import__(pkg, fromlist=[obj])
return getattr(mod, obj)
-def load_engine(name, builtins=BUILTIN_ENGINES):
- engine = BUILTIN_ENGINES.get(name, name)
- return import_thing(engine, 'Engine')
-
def main(argv=None):
import argparse
parser = argparse.ArgumentParser(conflict_handler='resolve')
parser.add_argument(
- '-e', '--engine',
- default=first(BUILTIN_ENGINES),
- )
- parser.add_argument(
- '-l', '--loop',
- action='store_true',
- default=False,
+ '-v', '--viewer',
+ default=first(BUILTIN_VIEWERS),
)
parser.add_argument(
'-w', '--width',
@@ -46,17 +38,15 @@ def main(argv=None):
parser.add_argument('bot', nargs='+')
args = parser.parse_args(argv)
- engine = load_engine(args.engine)
+ viewer_name = BUILTIN_VIEWERS.get(args.viewer, args.viewer)
+ viewer_class = import_thing(viewer_name, 'Viewer')
- game = engine(args.height, args.width, args.apples)
+ game = Engine(args.height, args.width, args.apples)
for name in args.bot:
bot = import_thing(name, 'bot')
game.add_bot(bot)
- game.run()
-
- if args.loop:
- while True:
- game.run()
+ viewer = viewer_class(game)
+ viewer.run()
diff --git a/snakegame/engines/base.py b/snakegame/engine.py
index 2797aa1..7ba9e0b 100644
--- a/snakegame/engines/base.py
+++ b/snakegame/engine.py
@@ -188,3 +188,9 @@ class Engine(object):
print '-'*60
self.remove_bot(letter)
+ def __iter__(self):
+ yield self.board
+ while self.bots:
+ self.update_snakes()
+ yield self.board
+
diff --git a/snakegame/engines/__init__.py b/snakegame/engines/__init__.py
index e03894f..8b13789 100644
--- a/snakegame/engines/__init__.py
+++ b/snakegame/engines/__init__.py
@@ -1,16 +1 @@
-try:
- from collections import OrderedDict as MaybeOrderedDict
-except ImportError:
- MaybeOrderedDict = dict
-from snakegame.engines.base import Engine
-
-BUILTIN_ENGINES = MaybeOrderedDict()
-
-def add_engine(name):
- class_name = name.title() + 'Engine'
- BUILTIN_ENGINES[name] = 'snakegame.engines.%s:%s' % (name, class_name)
-
-add_engine('pyglet')
-add_engine('pygame')
-add_engine('curses')
diff --git a/snakegame/utils.py b/snakegame/utils.py
new file mode 100644
index 0000000..162c0b2
--- /dev/null
+++ b/snakegame/utils.py
@@ -0,0 +1,18 @@
+try:
+ from collections import OrderedDict as MaybeOrderedDict
+except ImportError:
+ MaybeOrderedDict = dict
+
+def scale_aspect((source_width, source_height), (target_width, target_height)):
+ source_aspect = float(source_width) / source_height
+ target_aspect = float(target_width) / target_height
+ if source_aspect > target_aspect:
+ # restrict width
+ width = target_width
+ height = float(width) / source_aspect
+ else:
+ # restrict height
+ height = target_height
+ width = height * source_aspect
+ return (width, height)
+
diff --git a/snakegame/viewers/__init__.py b/snakegame/viewers/__init__.py
new file mode 100644
index 0000000..7864e39
--- /dev/null
+++ b/snakegame/viewers/__init__.py
@@ -0,0 +1,10 @@
+from snakegame.utils import MaybeOrderedDict
+
+BUILTIN_VIEWERS = MaybeOrderedDict()
+
+def add_viewer(name):
+ BUILTIN_VIEWERS[name] = 'snakegame.viewers.%s:Viewer' % name
+
+add_viewer('pyglet')
+add_viewer('pygame')
+add_viewer('curses')
diff --git a/snakegame/engines/curses.py b/snakegame/viewers/curses.py
index 715f321..f8a9602 100644
--- a/snakegame/engines/curses.py
+++ b/snakegame/viewers/curses.py
@@ -1,15 +1,15 @@
from __future__ import absolute_import
import curses
-from functools import wraps
import time
from snakegame import common
-from snakegame.engines import Engine
-class CursesEngine(Engine):
- def new_game(self, *args):
- super(CursesEngine, self).new_game(*args)
+class Viewer(object):
+ def __init__(self, engine, *args, **kwargs):
+ super(Viewer, self).__init__(*args, **kwargs)
+
+ self.engine = engine
self.window = curses.initscr()
curses.start_color()
@@ -21,9 +21,9 @@ class CursesEngine(Engine):
self.APPLE_COLOUR = curses.color_pair(1)
self.SNAKE_COLOUR = curses.color_pair(4)
- def draw_board(self):
+ def draw_board(self, board):
# Draw grid.
- for y, row in enumerate(self.board):
+ for y, row in enumerate(board):
for x, cell in enumerate(row):
char = '.'
colour = self.EMPTY_COLOUR
@@ -41,17 +41,14 @@ class CursesEngine(Engine):
self.window.addstr(y, x, char, colour)
def run(self):
- while self.bots:
+ for board in self.engine:
# Clear the screen.
self.window.erase()
# Draw the board.
- self.draw_board()
+ self.draw_board(board)
# Update the display.
self.window.refresh()
time.sleep(0.025)
- # Let the snakes move!
- self.update_snakes()
-
diff --git a/snakegame/engines/pygame.py b/snakegame/viewers/pygame.py
index b15f3a9..8b8fca4 100644
--- a/snakegame/engines/pygame.py
+++ b/snakegame/viewers/pygame.py
@@ -9,7 +9,7 @@ from pygame.image import load
pygame.init()
from snakegame import common
-from snakegame.engines import Engine
+from snakegame.utils import scale_aspect
sprite_cache = {}
@@ -25,29 +25,18 @@ def load_sprite(filename):
def load_image(filename, xscale, yscale):
image = load_sprite(filename)
- new_size = scale_aspect(image.get_size(), (xscale, yscale))
- return pygame.transform.smoothscale(image, new_size)
-
-def scale_aspect((source_width, source_height), (target_width, target_height)):
- source_aspect = source_width / source_height
- target_aspect = target_width / target_height
- if source_aspect > target_aspect:
- # restrict width
- width = target_width
- height = width / source_aspect
- else:
- # restrict height
- height = target_height
- width = height * source_aspect
- return (width, height)
-
-class PygameEngine(Engine):
+ w, h = scale_aspect(image.get_size(), (xscale, yscale))
+ return pygame.transform.smoothscale(image, (int(w), int(h)))
+
+class Viewer(object):
EDGE_COLOR = (255, 255, 255)
EDGE_WIDTH = 1
- def __init__(self, rows, columns, n_apples,
- width=800, height=600, fullscreen=False,
- **kwargs):
+ def __init__(self, engine, width=800, height=600, fullscreen=False, **kwargs):
+ super(Viewer, self).__init__(**kwargs)
+
+ self.engine = engine
+
flags = 0
if fullscreen:
flags |= pygame.FULLSCREEN
@@ -56,15 +45,13 @@ class PygameEngine(Engine):
self.width = width
self.height = height
- super(PygameEngine, self).__init__(rows, columns, n_apples,
- **kwargs)
-
- def new_game(self, rows, columns, n_apples):
- super(PygameEngine, self).new_game(rows, columns, n_apples)
+ self.columns = None
+ self.rows = None
+ def on_resize(self):
# make board surface
self.board_width, self.board_height = scale_aspect(
- (columns, rows), (self.width, self.height)
+ (self.columns, self.rows), (self.width, self.height)
)
self.surface = pygame.Surface((self.board_width, self.board_height))
@@ -75,12 +62,12 @@ class PygameEngine(Engine):
self.apple = load_image('images/apple.png', xscale, yscale)
self.eyes = load_image('images/eyes.png', xscale, yscale)
- def draw_board(self):
+ def draw_board(self, board):
xscale = self.board_width / self.columns
yscale = self.board_height / self.rows
# Draw grid.
- for y, row in enumerate(self.board):
+ for y, row in enumerate(board):
for x, cell in enumerate(row):
left = int(x * xscale)
top = int(y * yscale)
@@ -96,18 +83,26 @@ class PygameEngine(Engine):
if cell == common.APPLE:
self.surface.blit(self.apple, r.topleft)
- elif cell.isalpha(): # Snake...
- colour = self.bots[cell.lower()][1]
+ elif common.is_snake(cell):
+ bot = self.engine.bots[cell.lower()]
+ colour = bot[1]
self.surface.fill(colour, r)
- if cell.isupper(): # Snake head
+ if common.is_snake_head(cell):
self.surface.blit(self.eyes, r.topleft)
def run(self):
clock = pygame.time.Clock()
running = True
- while running and self.bots:
+
+ for board in self.engine:
+ columns, rows = common.get_size(board)
+ if columns != self.columns or rows != self.rows:
+ self.columns = columns
+ self.rows = rows
+ self.on_resize()
+
for event in pygame.event.get():
if event.type == pygame.QUIT or \
(event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
@@ -120,7 +115,7 @@ class PygameEngine(Engine):
self.surface.fill((0, 0, 0))
# Draw the board.
- self.draw_board()
+ self.draw_board(board)
# Center the board.
x = (self.width - self.board_width) / 2
@@ -131,22 +126,6 @@ class PygameEngine(Engine):
pygame.display.flip()
clock.tick(20)
- # Let the snakes move!
- self.update_snakes()
-
if running:
time.sleep(2)
-#if __name__ == '__main__':
-# import sys
-# from processbot import BotWrapper
-#
-# rows, columns, apples = map(int, sys.argv[1:4])
-# game = PygameEngine(rows, columns, apples)
-# for filename in sys.argv[4:]:
-# bot = BotWrapper(filename)
-# game.add_bot(bot)
-# game.run()
-#
-# # Early window close, late process cleanup.
-# pygame.display.quit()
diff --git a/snakegame/engines/pyglet.py b/snakegame/viewers/pyglet.py
index 1d88ccf..9b7a023 100644
--- a/snakegame/engines/pyglet.py
+++ b/snakegame/viewers/pyglet.py
@@ -7,48 +7,48 @@ pyglet.resource.reindex()
from pyglet import gl
from snakegame import common
-from snakegame.engines import Engine
-
-def scale_aspect((source_width, source_height), (target_width, target_height)):
- source_aspect = float(source_width) / source_height
- target_aspect = float(target_width) / target_height
- if source_aspect > target_aspect:
- # restrict width
- width = target_width
- height = float(width) / source_aspect
- else:
- # restrict height
- height = target_height
- width = height * source_aspect
- return (width, height)
-
-class PygletEngine(Engine, pyglet.window.Window):
+from snakegame.utils import scale_aspect
+
+class Viewer(pyglet.window.Window):
EDGE_COLOR = (255, 255, 255, 255)
EDGE_WIDTH = 2
- def __init__(self, rows, columns, n_apples, *args, **kwargs):
- kwargs.setdefault('caption', 'SnakeGame Window')
- kwargs.setdefault('resizable', True)
-
- super(PygletEngine, self).__init__(
- rows, columns, n_apples,
- *args, **kwargs
+ def __init__(self, engine, caption='SnakeGame Window', resizable=True, **kwargs):
+ super(Viewer, self).__init__(
+ caption=caption,
+ resizable=resizable,
+ **kwargs
)
+ self.engine = engine
+ self.engine_iter = iter(engine)
+
gl.glEnable(gl.GL_BLEND)
gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
pyglet.clock.schedule_interval(lambda t: self.update_snakes(), 1/30.0)
- def new_game(self, *args):
- super(PygletEngine, self).new_game(*args)
- self.on_resize(self.width, self.height)
+ self.board = None
+ self.columns = None
+ self.rows = None
+
+ def update_snakes(self, *args):
+ self.board = next(self.engine_iter, None)
+ if self.board is None:
+ pyglet.app.exit()
+ return
+
+ columns, rows = common.get_size(self.board)
+ if columns != self.columns or rows != self.rows:
+ self.columns = columns
+ self.rows = rows
+ self.on_resize(self.width, self.height)
def on_resize(self, width, height):
- super(PygletEngine, self).on_resize(width, height)
+ super(Viewer, self).on_resize(width, height)
- assert width == self.width
- assert height == self.height
+ if self.board is None:
+ return
# make board surface
self.board_width, self.board_height = scale_aspect(
@@ -73,6 +73,9 @@ class PygletEngine(Engine, pyglet.window.Window):
def on_draw(self):
self.clear()
+ if self.board is None:
+ return
+
xscale = float(self.board_width) / self.columns
yscale = float(self.board_height) / self.rows
@@ -96,23 +99,19 @@ class PygletEngine(Engine, pyglet.window.Window):
w, h = self.apple.size
self.apple.blit(left + (xscale - w) / 2.0, top - h, width=w, height=h)
- elif cell.isalpha(): # Snake...
- colour = self.bots[cell.lower()][1] + (255,)
+ elif common.is_snake(cell):
+ bot = self.engine.bots[cell.lower()]
+ colour = bot[1] + (255,)
gl.glPolygonMode(gl.GL_FRONT, gl.GL_FILL)
pyglet.graphics.draw(4, gl.GL_POLYGON,
('v2f', r),
('c4B', colour * 4),
)
- if cell.isupper(): # Snake head
+ if common.is_snake_head(cell):
w, h = self.eyes.size
self.eyes.blit(left, top - h, width=w, height=h)
- def update_snakes(self, *args):
- if not self.bots:
- pyglet.app.exit()
- super(PygletEngine, self).update_snakes(*args)
-
def run(self):
pyglet.app.run()