Piader v2 – scoreboard

We have a nice game and we may play it. But there is one problem we don’t see number of lives and our score. In this article we will create another view and position it on second display. I played with 40×4 for game and 20×4 for score but ended with one lcd with two vlcds šŸ™‚

We will also do something with difficulty as we have such setting it would be nice that it would do something

And of course we can’t forget about bugs, we will fix few along the way.

Scoreboard

This view won’t require much it is not complicated. Four labels are enough and this view is quite small it is 4×2 in size. But we need a screen for it. For me its gonna be 40×4 for game and 20×4 for scoreboard.
Update: I ended with one 20×4 with two vlcds: 4×4 and 16×4.
In main.py we create displays and their role. My file:

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""Piader v2"""

import RPi.GPIO as GPIO #pylint: disable=I0011,F0401
from charlcd import buffered #pylint: disable=I0011,F0401
from charlcd.drivers.gpio import Gpio #pylint: disable=I0011,F0401
from charlcd.drivers.i2c import I2C #pylint: disable=I0011,F0401
from charlcd import virtual_buffered #pylint: disable=I0011,F0401
from lcdmanager import manager #pylint: disable=I0011,F0401
import game

GPIO.setmode(GPIO.BCM)


def main():
    """set lcds and start game"""
    lcd_one = buffered.CharLCD(20, 4, Gpio(), 0, 0)
    lcd_one.init()

    drv = I2C(0x3a, 1)
    drv.pins['E2'] = 6
    lcd_three = buffered.CharLCD(40, 4, drv, 0, 0)
    lcd_three.init()

    vlcd_support = virtual_buffered.CharLCD(4, 4)
    vlcd_support.add_display(0, 0, lcd_one)
    vlcd_support.init()

    game_manager = manager.Manager(lcd_three)
    score_manager = manager.Manager(vlcd_support)

    my_game = game.Piader(game_manager, score_manager)
    my_game.main_loop()


main()

We are creating vlcd 4×4. Why such size ? Piader v1 was on two lcds, 20×4 and 16×2. On those displays two vlcd were made, 16×6 and 4×4. And that’s why we have 4×4 šŸ™‚
It is quite nice resolution for game but I can’t use it now, my 16×2 lcd burned šŸ™‚ And this bring us to current situation.
Back to main topic, scoreboard view looks like this:

# -*- coding: utf-8 -*-

""" scoreboard view
"""

import abstract.view as view
import lcdmanager.widget.pane as pane #pylint: disable=I0011,F0401
import lcdmanager.widget.label as label #pylint: disable=I0011,F0401

class Scoreboard(view.View):
    """Scoreboard view"""
    def __init__(self, lcdmanager, game):
        """create all widgets"""
        self.manager = lcdmanager
        self.pane = pane.Pane(0, 0, 'scoreboard')
        self.pane.width = lcdmanager.width
        self.pane.height = lcdmanager.height
        self.game = game
        score_label = label.Label(0, 0)
        score_label.label = "S"
        self.pane.add_widget(score_label)

        live_label = label.Label(0, 1)
        live_label.label = "L"
        self.pane.add_widget(live_label)

        self.score = label.Label(2, 0)
        self.score.label = "0"
        self.pane.add_widget(self.score)

        self.lives = label.Label(2, 1)
        self.lives.label = "0"
        self.pane.add_widget(self.lives)

        self.manager.add_widget(self.pane)

    def hide(self):
        """hide tab"""
        self.pane.visibility = False

    def show(self):
        """show tab"""
        self.pane.visibility = True

    def loop(self, action):
        """tick"""
        pass

See that we are not refreshing labels with current score and lives values because there is no connection between game and score view. We will take care of it later. First we need to initialize and make this view available.
Open game.py, add view import and initialization but this time view goes to separate variable. We don’t want to join it with other views because it is working independently from them. Another matter is that this view shows only when second manager is passed to init.
Add this at the end of __init__ function:

    if self.score_manager:
        self.scoreboard_view = scoreboard_view.Scoreboard(self.score_manager, self)

Next we need to add this to tick function:

    if self.score_manager:
        self.score_manager.render()
        self.score_manager.flush()

And one final addition just over the self.tick call:

    self.views[self.option['gui_current_tab']].loop(action)
    if self.score_manager:
        self.scoreboard_view.loop(action)

We need to connect two views, game and scoreboard to do this we will use configuration class as glue. Declare another variable:

        self.scoreboard = {
            'score': 0,
            'lives': 0
        }

in configuration.py.
To make it simple we will propagate changes in game view in loop function:

self.game.cfg.scoreboard['score'] = self._options['score']
self.game.cfg.scoreboard['lives'] = self._options['lives']

Add those lines above:

self.canvas.clear()

Last step require us to add score and lives display in loop function at scoreboard view:

self.score.label = str(self.game.cfg.scoreboard['score'])
self.lives.label = str(self.game.cfg.scoreboard['lives'])

We made it, we can see our lives and score.

Game over bug

After playing few games I found another bug. See for yourself:

bug

Gameover view should be on whole display but see that on bottom we have a player sprite. I suspect that pane rendering stops after rendering context, it is not rendering blank lines. It’s easy to check this, add another test for pane widget:

    def test_pane_should_render_blank_bottom_line(self):
        widget_label1 = label.Label(8, 1)
        self.pane.height = 4
        self.pane.width = 20
        widget_label1.label = "Some string "
        self.pane.add_widget(widget_label1)
        output = [
            "                    ",
            "        Some string ",
            "                    ",
            "                    ",
        ]
        assert_equal(self.pane.render(), output)

This test fails, now we are sure where the bug is šŸ™‚ To fix this problem change _crop_to_display to:

    def _crop_to_display(self, output):
        """prepare text to display by cropping it"""
        rows = [label[0:self.width].ljust(
            self.width, manager.TRANSPARENCY) for label in output]
        for _ in range(len(rows), self.height):
            rows.append(manager.TRANSPARENCY.ljust(self.width))

        return rows

With this fix we are back to green and our view is working correctly.

Difficulty

What to do with difficulty? I know ! Lets change shooting chance, on easy its gonna a be 30% and on hard 100%. Open enemy.py, first we need to pass config to it, add cfg to constructor and self.cfg = cfg to body. Next move to Game view and find enemy initializations and add missing parameter. Go back to enemy and under self.bombs assignment add:

if cfg.difficulty == 'easy':
    self.bombs['chance'] = 30

And this is it.

Stop! Jenkins time!

Not much here, few too long lines only.

Summary

Yey! We made it! Piader v2 is ready and playable!

Download LCD Manager

Download Piader v2

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s