All computer source code presented on this page, unless it includes attribution to another author, is provided by Ed Halley under the Artistic License. Use such code freely and without any expectation of support. I would like to know if you make anything cool with the code, or need questions answered.
# boards - a "things" library space that supports board games and toys

import vectors ; from vectors import *
import things ; from things import *
import pieces ; from pieces import *
import goals ; from goals import *


class Board (Space):

    def __init__(self, board=None):
        super(Board, self).__init__(VectorContext())
        if not board: board = "board-800x480.png"
        image = App.resource(board).convert()
        self.board = pygame.transform.scale(image, (720, 420))
        self.players = [ ]
        self.player = 0
        self.rules = { }
        self.phase = None

    def background(self, surface):
        surface.blit( self.board, (0,0) )

    def next_player(self):
        if not self.players:
        self.players = (self.players + 1) % len(self.players)

    def can_drag(self, thing):
        return True

    def zone_drop(self, zone, what):

    def get_zones(self):
        followers = self.followers()
        zones = [ zone for zone in followers if isinstance(zone, Zone) ]
        return zones

    def nearest_zone(self, where, test=None):
        '''Find the zone that's closest, or the closest empty zone.'''
        zones = self.get_zones()
        if callable(test):
            zones = [ zone for zone in zones if test(zone) ]
        if not zones:
            return None
        places = [ zone.xform.translation() for zone in zones ]
        (which, nearest) = (0, where.distance(places[0]))
        for i in range(1, len(zones)):
            d = where.distance(places[i])
            if d < nearest:
                (which, nearest) = (i, d)
        return (zones[which], V())

    def nearest_drop(self, thing, where):
        where = V(where[0], where[1], 0)
        return (self, where)


class Zone (Piece):

    def __init__(self, name, image=None, size=1.0):
        super(Zone, self).__init__() = name
        self.set_image(image, size)

    def set_image(self, image, size=1.0):
        if isinstance(image, str):
            image = App.resource(image)
        if image is not None and size != 1.0:
            image = pygame.transform.rotozoom(image, 0, size)
        self.image = image

    def get_image(self):
        return self.image

    def get_image(self):
        return self.image

    def xevent(self, event):
        # do not call super's version


class _SampleBoard (Board):

    cards = { 'kawa': 0x5ddd,
              'chii.sai': 0x5c0f,
              'mizu': 0x6c34,
              '':0x5207, }

    def __init__(self, board=None):
        super(_SampleBoard, self).__init__()
        self.die = Die()
        self.die.move( V(100, 275, self.die.height) )

        self.unknown = Zone('unknown',
        self.unknown.move( V(100, 100, 0) )

        self.correct = Zone('correct',
        self.correct.move( V(600, 100, 0) )

        self.mistake = Zone('mistake',
        self.mistake.move( V(600, 275, 0) )


    def nearest_drop(self, thing, where):
        if isinstance(thing, Card):
            zones = [ zone.get_topmost() for zone in
                      [ self.unknown, self.correct, self.mistake ] ]
            places = [ zone.xform.translation() for zone in zones ]
            (which, nearest) = (0, where.distance(places[0]))
            for i in range(1, len(zones)):
                d = where.distance(places[i])
                if d < nearest:
                    nearest = d
                    which = i
            return (zones[which], V())
        return super(_SampleBoard, self).nearest_drop(thing, where)

    def event(self, event):
        super(_SampleBoard, self).event(event)
        if event.type == KEYDOWN:
            if event.key == K_SPACE:
                if self.die.is_idle():
                    self.die.append_goal(QuakeGoal(self.die, amount=20))
                    self.die.append_goal(QuakeGoal(self.die, cycles=3, amount=20))

    def make_deck(self, zone=None):
        self.deck = []
        self.font = App.resource('epkgobld.ttf', 36)
        follow = zone
        if not follow: follow = self
        for i in range(10):
            front = random.choice(
            back = unichr([front] )
            card = Card( (front, back), font=self.font, size=0.75 )
            follow = card

if __name__ == '__main__':
    app = App('Boardgame', (720, 420))
    Piece._shadows = True = _SampleBoard()

Contact Ed Halley by email at
Text, code, layout and artwork are Copyright © 1996-2013 Ed Halley.
Copying in whole or in part, with author attribution, is expressly allowed.
Any references to trademarks are illustrative and are controlled by their respective owners.
Make donations with PayPal - it's fast, free and secure!