diff options
author | Peter Ward <peteraward@gmail.com> | 2012-07-21 12:14:16 +1000 |
---|---|---|
committer | Peter Ward <peteraward@gmail.com> | 2012-07-21 12:14:16 +1000 |
commit | b99240781b813dd030e18750bb9d2ac62711c249 (patch) | |
tree | 62c76efdd9d4115756d92f7eadff6b912f1aee76 | |
parent | 38ad46d7ce7cb965726d2af5dee8f90261b4a44a (diff) |
Refactoring into a single engine and viewers.
-rw-r--r-- | snakegame/__init__.py | 28 | ||||
-rw-r--r-- | snakegame/engine.py (renamed from snakegame/engines/base.py) | 6 | ||||
-rw-r--r-- | snakegame/engines/__init__.py | 15 | ||||
-rw-r--r-- | snakegame/utils.py | 18 | ||||
-rw-r--r-- | snakegame/viewers/__init__.py | 10 | ||||
-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() |