from pygame import Rect

from dark_stew.pgfw.GameChild import GameChild
from dark_stew.pool.Water import Water
from dark_stew.pool.Ring import Ring
from dark_stew.pool.stool.Stools import Stools
from dark_stew.pool.LED import LED

class Pool(GameChild, Rect):

    def __init__(self, parent, center, index):
        GameChild.__init__(self, parent)
        self.init_rect(center)
        self.water = Water(self, index)
        self.ring = Ring(self, index)
        self.stools = Stools(self)
        self.grow()
        # self.led = LED(self)

    def init_rect(self, center):
        Rect.__init__(self, 0, 0, 0, 0)
        self.center = center

    def grow(self):
        padding = self.get_configuration("pool", "padding")
        radius = self.ring.rect.w / 2 + padding
        self.inflate_ip(radius * 2, radius * 2)

    def update(self):
        self.water.update()
        self.ring.update()
        self.stools.draw()
        # self.led.update()
from os import listdir
from os.path import join
from math import sin, cos

from dark_stew.pgfw.Sprite import Sprite

class LED(Sprite):

    def __init__(self, parent):
        Sprite.__init__(self, parent, 20000)
        self.load_configuration()
        self.load_frames()
        self.set_position()

    def load_configuration(self):
        config = self.get_configuration("led")
        self.angle = config["angle"]
        self.offset = config["offset"]
        self.root = self.get_resource("led", "path")

    def load_frames(self):
        root = self.root
        for directory in listdir(root):
            self.load_from_path(join(root, directory), transparency=True,
                                ppa=False)

    def set_position(self):
        parent = self.parent
        cx, cy = parent.center
        distance = parent.ring.rect.w / 2 + self.offset
        angle = self.angle
        dx, dy = cos(angle) * distance, sin(angle) * distance
        self.rect.topleft = cx + dx, cy - dy

    def draw(self):
        x, y = self.rect.topleft
        surface = self.display_surface
        frame = self.get_current_frame()
        for dx, dy in self.parent.parent.parent.get_corners():
            surface.blit(frame, (x + dx, y + dy))
from pygame import Rect

from dark_stew.pgfw.GameChild import GameChild

class Stool(GameChild, Rect):

    def __init__(self, parent, center):
        GameChild.__init__(self, parent)
        self.display_surface = self.get_display_surface()
        self.image = self.parent.image
        self.init_rect(center)

    def init_rect(self, center):
        Rect.__init__(self, (0, 0), self.image.get_size())
        self.center = center

    def draw(self):
        x, y = self.topleft
        surface = self.display_surface
        for dx, dy in self.parent.parent.parent.parent.get_corners():
            surface.blit(self.image, (x + dx, y + dy))
from os.path import join
from math import sin, cos

from pygame import Surface
from pygame.image import load

from dark_stew.pgfw.GameChild import GameChild
from dark_stew.pool.stool.Stool import Stool

class Stools(GameChild, list):

    def __init__(self, parent):
        GameChild.__init__(self, parent)
        self.transparent_color = (0, 0, 0)
        self.load_configuration()
        self.load_image()
        self.add_stools()

    def load_configuration(self):
        config = self.get_configuration("stool")
        self.angles = config["angles"]
        self.offsets = config["offsets"]
        self.image_path = self.get_resource("stool", "path")

    def load_image(self):
        image = load(self.image_path)
        surface = Surface(image.get_size())
        color = self.transparent_color
        surface.fill(color)
        surface.set_colorkey(color)
        surface.blit(image, (0, 0))
        self.image = surface

    def add_stools(self):
        parent = self.parent
        radius = parent.ring.rect.w / 2
        cx, cy = parent.center
        for ii, measure in enumerate(zip(self.angles, self.offsets)):
            angle, offset = measure
            distance = radius + offset
            dx = cos(angle) * distance
            dy = sin(angle) * distance * (-1 if ii == 0 else 1)
            self.append(Stool(self, (cx - dx, cy + dy)))
            self.append(Stool(self, (cx + dx, cy + dy)))

    def draw(self):
        for stool in self:
            stool.draw()
from os import listdir
from os.path import join
from random import shuffle, random
from math import atan2, pi

from pygame import Rect
from pygame.image import load
from pygame.transform import flip

from dark_stew.pgfw.Sprite import Sprite
from dark_stew.aphids.Rod import Rod

class Aphids(Sprite):

    directions = 0, 1, 2, 3

    def __init__(self, parent):
        Sprite.__init__(self, parent)
        self.input = self.get_input()
        self.delegate = self.get_delegate()
        self.direction = self.directions[2]
        self.center = self.display_surface.get_rect().center
        self.sitting = False
        self.load_configuration()
        self.reset_chance()
        self.rod = Rod(self)
        self.load_frames()
        self.activate_frames(self.standing_front_frames, self.rest_framerate)
        self.place_collision_rect()
        self.play()
        self.subscribe(self.respond)

    def load_configuration(self):
        config = self.get_configuration("aphids")
        self.rest_framerate = config["rest-framerate"]
        self.walk_framerate = config["walk-framerate"]
        self.run_framerate = config["run-framerate"]
        self.collision_rect = Rect(config["collision-rect"])
        self.initial_chance = config["initial-chance"]
        self.chance_increase = config["chance-increase"]
        root = self.get_resource("aphids", "path")
        animation_path = config["animation-path"]
        self.moving_path = join(root, config["moving-path"], animation_path)
        standing_path = join(root, config["standing-path"])
        front_path = config["front-path"]
        self.standing_front_path = join(standing_path, front_path,
                                        animation_path)
        self.standing_side_path = join(standing_path, config["side-path"],
                                       animation_path)
        back_path = config["back-path"]
        self.standing_back_path = join(standing_path, config["back-path"],
                                       animation_path)
        sitting_path = join(root, config["sitting-path"])
        self.sitting_front_path = join(sitting_path, front_path, animation_path)
        self.sitting_back_path = join(sitting_path, back_path, animation_path)
        self.sitting_path = sitting_path

    def reset_chance(self):
        self.catch_chance = self.initial_chance

    def load_frames(self):
        load_path = self.load_path
        self.moving_right_frames = load_path(self.moving_path, random=False)
        self.moving_left_frames = load_path(self.moving_path, True, False)
        self.standing_front_frames = load_path(self.standing_front_path)
        self.standing_right_frames = load_path(self.standing_side_path)
        self.standing_left_frames = load_path(self.standing_side_path, True)
        self.standing_back_frames = load_path(self.standing_back_path)
        self.sitting_northwest_frames = load_path(self.sitting_front_path)
        self.sitting_northeast_frames = load_path(self.sitting_front_path, True)
        self.sitting_southwest_frames = load_path(self.sitting_back_path)
        self.sitting_southeast_frames = load_path(self.sitting_back_path, True)

    def load_path(self, root, mirror=False, random=True):
        frames = []
        names = listdir(root)
        if random:
            shuffle(names)
        else:
            names.sort()
        for path in [join(root, name) for name in names]:
            image = load(path)
            if mirror:
                image = flip(image, True, False)
            frames.append(self.fill_colorkey(image))
        return frames

    def place_collision_rect(self):
        self.collision_rect.move_ip(self.rect.topleft)

    def respond(self, event):
        if self.delegate.compare(event, "action"):
            monsters = self.parent.parent.monsters
            if not self.sitting and not monsters.current:
                self.find_seat()
            elif monsters.current:
                monsters.close()
            else:
                self.sitting = False
                self.rod.deactivate()
                if random() <= self.catch_chance:
                    self.parent.parent.monsters.open_random()
                self.reset_chance()

    def find_seat(self):
        parent = self.parent
        for dx, dy in parent.get_corners():
            rect = self.collision_rect.move(-dx, -dy)
            for pool in parent.pools:
                if rect.colliderect(pool):
                    self.sit(pool, rect)
                    return

    def sit(self, pool, rect):
        cx, cy = pool.center
        x, y = rect.center
        angle = atan2(y - cy, x - cx)
        framerate = self.rest_framerate
        stools = pool.stools
        self.parent.reset_active_directions()
        if angle >= 0 and angle < pi / 2:
            self.activate_frames(self.sitting_southeast_frames, framerate)
            stool = stools[3]
            dx, dy = 11, -27
            self.rod.activate(-33, -9)
        elif angle >= pi / 2 and angle < pi:
            self.activate_frames(self.sitting_southwest_frames, framerate)
            stool = stools[2]
            dx, dy = -14, -27
            self.rod.activate(12, -8, True)
        elif angle >= -pi and angle < -pi / 2:
            self.activate_frames(self.sitting_northwest_frames, framerate)
            stool = stools[0]
            dx, dy = -13, -29
            self.rod.activate(10, -12, True)
        else:
            self.activate_frames(self.sitting_northeast_frames, framerate)
            stool = stools[1]
            dx, dy = 11, -29
            self.rod.activate(-30, -12)
        self.sitting = True
        self.parent.shift(rect.centerx - stool.centerx + dx,
                          rect.centery - stool.centery + dy, False)

    def activate_frames(self, frames, framerate):
        if self.frames != frames:
            self.frames = frames
            self.frame_index = 0
            self.measure_rect()
            self.rect.center = self.center
            self.set_framerate(framerate)

    def update(self):
        if self.sitting:
            self.catch_chance += (1 - self.catch_chance) * self.chance_increase
        self.set_frames()
        Sprite.update(self)
        self.rod.update()

    def set_frames(self):
        parent = self.parent
        direction = parent.direction
        up, right, down, left = parent.directions
        if self.parent.scroll_active:
            if self.input.is_command_active("run"):
                framerate = self.run_framerate
            else:
                framerate = self.walk_framerate
            if direction in [right, up]:
                self.activate_frames(self.moving_right_frames, framerate)
            else:
                self.activate_frames(self.moving_left_frames, framerate)
        elif not self.sitting:
            framerate = self.rest_framerate
            if direction == up:
                self.activate_frames(self.standing_back_frames, framerate)
            elif direction == right:
                self.activate_frames(self.standing_right_frames, framerate)
            elif direction == down:
                self.activate_frames(self.standing_front_frames, framerate)
            else:
                self.activate_frames(self.standing_left_frames, framerate)
54.81.71.219
54.81.71.219
54.81.71.219
 
September 26, 2017

I made a video about my game Picture Processing for Out of Index 2017! Here is the video along with a transcript.

To save memory, video games are designed to repeat graphics. In raster-based games, image files like textures, tiles and sprites are loaded once into memory and drawn repeatedly by the program to create environments, characters, animations and text. In my puzzle game, 8 by 8 pixel tiles are used to create scenes the player has to recreate. For level 1, the tiles are a cloud, a tree, a mushroom, a character, sky, ground and rock.

An algorithm scrambles the tiles so that each tile is in the wrong memory address at the beginning of a level and the screen looks like a graphics glitch. When level 1 begins, the clouds may be where the trees should be, the mushrooms may be floating in the sky and the character may be switched with rock or the ground. The player's task is to put the tiles where they belong by swapping each tile with a tile in another memory address.

There are five levels, in order of difficulty, based on classic video games or classic video game genres.

The name of this game is taken from the Picture Processing Unit, a microprocessor designed by Nintendo for the Nintendo Entertainment System. The PPU is the hardware component responsible for translating image data into video signals for televisions and screens. It does this with a memory of 8 by 8 pixel tile data, which, along with palette and sprite attribute memory, generates each frame of a video game.

Companies often create lofty, evocative titles for hardware and products. What does the name Picture Processing Unit mean if we consider pictures something independent of a video screen? The phrase picture processing evokes the phrase image processing, a technique used to create applications such as automatic facial and emotion recognition. We often anthropomorphize electronic devices, infusing them with intelligence and souls, forgetting how much more infinitely complex the human mind is compared to a digital processor.

The game is named as a reference to Nintendo's microprocessor because the graphics are tile based, but it is also a reference to the players who are image processors, interpreting a picture from something deterministic into something non-deterministic.

The prototype of this game was created for a game jam called A Game By Its Cover where designers created video games based on imagined Nintendo game cartridges created by visual artists for an exhibition called My Famicase.

Picture Processing is based on one of the imagined cartridges from that exhibition. The cartridge's cover depicts a grid of unordered tiles and is described as a game where one inserts a game cartridge, sees a glitching screen, and meditates about the concept of beauty in imperfection. I added the idea that the player meditates into a state of transcendence until they are able to fix the game's graphics by accessing the memory telepathically.


↠ RSS Feed ↞