How To Level Up Your Angular Unit Testing Game (2/3)

<em>This is the second post in the series about Angular unit testing. If you are unfamiliar with using Angular CLI, please read the first post in the series.</em>

This is the second post in the series about Angular unit testing. If you are unfamiliar with using Angular CLI, please read the first post in the series.

In the previous post, we learned how to pass parameters into Angular CLI to run unit tests. In this post, we'll dive into Karma configuration and make the test runner more snazzy by configuring it for our unique needs. It's time to level up your testing game and get pumped up to unit test!

Follow Along

We're using Angular CLI to test the application and using code from a previous post I wrote.

You can clone the tutorial-angular-httpclient and run tests too. All the level ups discussed in the posts are found in "test-configuration" branch.

Karma Configuration

In your Angular app, find $/src/karma.config.js. This is the config file Karma uses to run tests. Let's start editing it!

Reporters

Out of the box, Angular CLI enables 'progress' and 'kjhtml' reporters. In v7 and v5, you can configure reporters via commandline but sadly, that option was removed in v6. We can still make changes via the config file for v6 though!

If you are using Angular v5 or v7, pass in a comma separated list of reporters using the reporters flag.

To change karma.config.js, in the reporters array, remove 'progress' and replace it with 'dots'. Now when you run tests, the output to the console uses the dot reporter instead of the progress reporter.

Add A New Reporter

But our test output could be way better. It's nothing to write home about yet. Let's try adding a reporter.

Add the 'karma-spec-reporter' npm package

npm install karma-spec-reporter --save-dev

Require the reporter in karma.conf.js by adding the require statement to the plugins array

plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma'),
      require('karma-spec-reporter')
    ],

Add to the reporters array

reporters: ['kjhtml', 'spec']

Now when you run tests, you'll see an output like this

There's all sorts of reporters available for Karma. If you want unit test output available in your CI environment, you can add a reporter for JUnit output and configure Jenkins to consume JUnit reports as an example. If you need some more flair in your life, add the nyan cat progress reporter.

Code Coverage

Now that you know how to run tests and set it up to get the output format you like, it's time to measure coverage.

Keep in mind test coverage metrics can't determine if an application is working as intended or if the tests you have are "good" by testing the important things.

Angular CLI automatically adds and configures Istanbul as a coverage reporter. In karma.conf.js there's a section for coverageIstanbulReporter and it's already set up to generate reports. Prior to v7 of Angular, it only had 'html' and 'lcov' enabled out of the box. Add 'text-summary' to the array if it's not already there.

Run test with the coverage flag enabled. In v6, use --code-coverage.

ng test --watch false --browsers ChromeHeadless --codeCoverage

Now you have nice output in the console for coverage summary.

Running coverage with 'html' report creates a visual overview of coverage in html format. You can view coverage results for the entire app broken down per file.

And step into coverage report for a single file and see the number of times each line of code was executed.

Add native report formats such as 'text', 'json', 'Cobertura', 'TeamCity', or use the 'lcov' report as part of your CI process so you can show test coverage trend lines per build.

Maintaining Thresholds

If you want to ensure a certain level of coverage, you can configure coverageIstanbulReporter object in karma.conf.js to add thresholds.

To the thresholds object, fill in keys for statements, lines, branches, and functions.

coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../coverage'),
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true,
      thresholds: {
            statements: 80,
            lines: 80,
            branches: 80,
            functions
      }
    },

Now when you run tests with code coverage, your tests will complete with failure if your project doesn't meet the thresholds of coverage.

Setting Up angular.json For Defaults

Sure, we can create npm scripts to wrap all the parameters we send in while running unit tests, but there's cases where we always want a certain configuration set. An example might be code coverage. Despite watch or browsers configurations, we always want to run coverage. We can set that up in angular.json.

In $/angular.json file, find the configuration for "test". It has an "options" property. Add a new property for "codeCoverage" and set the value to true.

You can set more properties than just coverage. All parameters that you pass in to Angular CLI can be configured. Check out the schema for angular.json for more details.


By :  Alisa


Develop this one fundamental skill if you want to become a successful developer

Throughout my career, a multitude of people have asked me&nbsp;<em>what does it take to become a successful developer?</em>

Throughout my career, a multitude of people have asked me what does it take to become a successful developer?

It’s a common question newbies and those looking to switch careers often ask — mostly because they see the potential paycheck. There is also a Hollywood level of coolness attached to working with computers nowadays. Being a programmer or developer is akin to being a doctor or lawyer. There is job security.

But a lot of people who try to enter the profession don’t make it. So what is it that separates those who make it and those who don’t? 

Read full article here

Best 17 Angular Libraries Every Angular Developers Should Know in 2019

Best 17 Angular Libraries Every Angular Developers Should Know in 2019

In this article, we list 17 useful Angular libraries that can help as you develop applications with Angular.

Angular is a web development framework for building robust single-page applications and systems. Developed and maintained by Google and community maintainers, Angular is a great library for building large scale web applications.

Angular has a huge and active community, thus, a lot of libraries have been introduced by the community to plug holes and extend the tooling provided by Angular. Today, we’ll look at some libraries that can be introduced into existing applications — libraries ranging from utility libraries to UI component libraries.

1. ng-bootstrap

It seems fair to start with the Angular implementation of the most popular UI library. The ng-bootstrap library was built from the top down using TypeScript. Unlike the previous version, it has dropped jQuery as a dependency, specifying Bootstrap’s CSS as its only other dependency. With most JavaScript components implemented, the library seems like a complete solution when using Bootstrap with Angular — as active development is ongoing, more components will be included. With almost 7k stars on GitHub, ng-bootstrap seems like a very popular choice for a lot of Angular developers.

The Angular.js version of this project is still available here, although it isn’t actively maintained.

2. Angular Google Maps

Using the Google Maps library in Angular is always a serious hassle because the library is loaded using a script tag, so type definitions aren’t readily available. This causes some compile errors that need a lot of hacking to get rid of.

The Angular Google Maps library provides services and directives for implementing Google Maps services. There are directives available for creating maps, using markers, etc. The library also provides an async function that is useful for checking if the Google Maps library is loaded on the webpage.

The project has amassed almost 2k stars on GitHub. Visit their documentation to get started.

3. ngx-translate

Building an application that supports multiple languages can be a serious struggle, especially for single-page applications. The ngx-translate is a great library for managing multiple languages in your Angular application. It provides services to load translations that can be used throughout the application. Translations can be defined and loaded using the TranslateService, and onChange listeners are also available for handling language changes within the application.

The setup is pretty straightforward, and the library is well documented with detailed examples. Visit their GitHub page to get started.

4. Angular2-jwt

Managing single-page applications that use web tokens for authentication usually requires using interceptors to append headers to network requests. While this is easy to implement, it is difficult to filter out requests that don’t require access tokens. This is where this impressive library comes in. Using the angular-jwt package by Auth0, you can load access tokens from the local storage or session storage. It provides an HttpInterceptor that appends authentication headers to the requests. The ability to blacklist or whitelist a domain is also available.

With almost 2k stars on GitHub, it is a well-documented library with adequate examples and only requires a few steps to get started.

5. AngularFire2

Looking to implement real-time functionality in your Angular application? Well look no further, this library uses the power of RxJS, Firebase and Angular to deliver data synchronization in real time. It also provides services and providers to query documents and collections on Cloud Firebase and the realtime database, handles authentication using Firebase, handles file upload to Cloud Storage, and sends Push Notifications. The package also supports server-side rendering and offline functionality. You can easily import each individual module to handle whichever functionality is required in your application. All documentation can be found in the library’s GitHub page.

6. ng2-file-upload

Handling file uploads in any single-page application isn’t a task that’s fun to deal with. It would be great if an external library could handle file upload within your web application. Valon-software, the makers of ngx-bootstrap, has you covered with ng2-file-upload, a library that makes file upload a breeze.

The library supports drag-and-drop functionality alongside the good old file select implementation. It provides a utility class (FileUploader) that handles the different file upload methods. It also provides events to monitor the file upload progress, as well as errors and success during the upload.

The library is actively maintained and has almost 2k stars on Github.

7. Angular Material 2

The list wouldn’t be complete without mentioning a library that implements Google’s Material Design specifications. Angular Material 2 is a components library created by the Angular team. It features a set of components implementing the Material Design specs, ranging from buttons to dialogs, bottom sheets, etc. It features fully customizable themes and a rich set of components that can be used to quickly build an application. Angular Material 2 comes with almost 40 components, with more components under development and four pre-built themes.

Get started with Angular Material 2 by visiting their documentation or GitHub page.

8. ngrx/store

Managing state in small applications isn’t really complicated and state can be easily managed within individual components, but when there’s a need to share data between several components, the need for a proper state management system arises. NgRx offers reactive libraries optimized for Angular. It offers reactive statement for Angular in a package called ngrx/store. This package uses RxJS technologies to offer state management similar to Redux. The store allows developers write consistent and performant applications in a state-controlled environment. Very similar to Redux, the ngrx/store library uses Action, Reducers, Select and Store to manage the data flow within Angular applications. Get started with ngrx/store by following the steps listed in the library’s documentation.

9. Cloudinary Angular SDK

Cloudinary is SaaS web platform for managing media assets on mobile and web applications. It provides services for upload, storage, manipulation and delivery of media assets. Cloudinary offers an SDK for Angular that can be used in Angular applications for resizing and image conversion. The SDK can also be used for delivering different image sizes on different screens. It allows for easy delivery of video and image assets from Cloudinary’s storage.

Visit Cloudinary’s website to read more about about end-to-end management of media assets. The SDK can be found here on GitHub.

10. ng2-pdf-viewer

The ng2-pdf-viewer is a library for viewing and interacting with PDFs on a web application. The library providers a component for rendering PDF documents. The component can also be used for performing operations on the selected PDF like: resizing, rotating, searching through the document, etc. You can render files locally or provide a link to an external document. This library is great for managing PDF files on your web application, and there’s a lot it can handle using directives.

Visit their official documentation page or their page on GitHub.

11. ngx-charts

When working with data in a web application, the need for data visualization arises, thus the need for a data visualization library that can handle various forms of customizations while rendering. ngx-charts is quite interesting because their charts rely mostly on using Angular to animate SVGs, which offers more speed and flexibility as the library has been optimized for use in Angular.

It also uses d3 for math functions, scales and axis, etc. It comes with ten or more color schemes while making the charts fully customizable using CSS. Visit their demo page to view the different themes and color schemes available and their GitHub page to get started with the library. The library has garnered almost 3k stars on GitHub and is actively maintained.

12. ng-seed/universal

This great library has so many features packaged within it, it should be the Swiss army knife for every Angular developer. It consists of the following packages:

  • ngx-meta: for handling meta tags, title tags and SEO enhancement.
  • ngx-cache: for managing application wide data.
  • ngx-auth: for managing jwt-based authentication.

It comes with a couple of other packages for handling server-side rendering, lazy loading, state management and webpack configurations.

Clone the repository on GitHub and follow the instructions to get started.

13. Augury

When building web applications, browser DevTools play an important part in the development process. It provides features for debugging, diagnosing and editing web applications. When dealing with Angular applications, DevTools only lets you interact with the end product of your code, which means your Angular components, directives, etc. have been converted to JavaScript, HTML and CSS.

Augury as a browser extension allows you debug and visualize your Angular application in its pre-compiled state. With Augury, you can inspect your components and ensure they’re functioning as they should. Augury works better with source maps, so ensure that you generate source maps for a better experience while using Augury.

You can download the extension for Chrome or Firefox. Visit their GitHub page if you wish to contribute or raise issues.

14. ngx-moment

Moment.js is a utility library for manipulating time (not what you think). It provides a set of functions for parsing, formatting, validating, etc. dates and time using JavaScript. ngx-moment builds on the Moment.js library, providing Angular pipes for use within components. It comes packed with pipes for the functions provided by Moment.js, thus effectively eliminating the overhead of importing the functions into every component for use.

The library is actively maintained and is relatively easy to get started with. Visit the GitHub page and run through the documentation to get started.

15. ngx pipes

Fun times when Angular.js came packed with a set of pipes for transforming data before rendering. Filters is what they were called in Angular.js. Well, for some performance reasons, more recent Angular versions don't include pipes for filtering or ordering lists. Angular pipes is a library that contains a set of useful pipes for use in your Angular project. It contains pipes for performing actions like: trimming, reversing, matching and scanning strings, plucking, shuffling and ordering Arrays.

It is well documented and easy to integrate. Getting started should be a breeze and, soon enough, you’ll start getting more done with pipes. Visit the documentation or their GitHub page to get started.

16. Angular Epic Spinners

When dealing with interactivity on a webpage, you have to think about notifying users when processes not visible to them are ongoing. When the time comes, you are required to display a loading indicator. Some sites have custom loading indicators for their application, but if you’d rather have a set of easily available spinners, then this spinners library should be your go-to.

Angular Epic Spinners is built on the epic-spinners library, with Angular components for each component available in the library. Each component can be imported as an individual module and rendered anywhere within your application. You can select from any of 20 indicators available in the library. You can view the demo page or head straight to their GitHub page.

17. Apollo Angular

GraphQL is a query language for APIs and a runtime for fulfilling queries made with data. It allows developers to request for data they need in specific areas of their application. Apollo client is a library used to consume data from GraphQL endpoints. Apollo has different client libraries for consuming data on the frontend – libraries exist for React, Angular, Vue, etc.

Apollo Angular is a client library built for Angular applications to consume GraphQL endpoints. Apollo Angular is agnostic of any router used within the application. It also supports server-side rendering. The documentation page is well written with adequate examples to help you get started.

Summary

People sometimes avoid using external libraries in their applications during development. While that’s acceptable in some instances, external libraries can help reduce development time significantly. There are a lot of libraries that might have achieved whatever you’re struggling with during development. The task is finding the right library that fits into your applications and ensuring it fulfills its purpose. Happy coding.

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

Angular 8 (formerly Angular 2) - The Complete Guide

Angular & NodeJS - The MEAN Stack Guide

The Web Developer Bootcamp

Angular 8 is coming

MEAN Stack Angular 8 CRUD Web Application

Angular 8 + Spring Boot 2.2: Build a CRUD App Today!

Game Development with Python: Snake Game

Game Development with Python: Snake Game

Today, I’m taking you along for a journey in game development. We are making it with the classic game of Snake with Kivy, Python

Hello, there.

A lot of people want to start programming apps for Android, but they prefer not to use Android Studio and/or Java. Why? Because it's an overkill. "I just wanna create Snake and nothing more!"

Let's snake without java! (with a bonus at the end)

Get familiarized

First app

Please confirm that you have already installed Kivy (if not, follow the instructions) and ran buildozer init in the project directory.

Let's run our first app:

# main.py
from kivy.app import App
from kivy.uix.widget import Widget

class WormApp(App):
    def build(self):
        return Widget()

if __name__ == '__main__':
    WormApp().run()

_First run of the application_

We created a Widget. Analogously, we can create a button or any other UI element:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button

class WormApp(App):
    def build(self):
        self.but = Button()
        self.but.pos = (100, 100)
        self.but.size = (200, 200)
        self.but.text = "Hello, cruel world"

        self.form = Widget()
        self.form.add_widget(self.but)
        return self.form

if __name__ == '__main__':
    WormApp().run()

_Creating a button_

Wow! Congratulations! You've created a button!

.kv files

However, there's another way to create UI elements. First, we implement our form:

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.button import Button

class Form(Widget):
    def __init__(self):
        super().__init__()
        self.but1 = Button()
        self.but1.pos = (100, 100)
        self.add_widget(self.but1)

class WormApp(App):
    def build(self):
        self.form = Form()
        return self.form

if __name__ == '__main__':
    WormApp().run()

Then, we create a «worm.kv» file.

# worm.kv
<Form>:
    but2: but_id

    Button:
        id: but_id
        pos: (200, 200)

What just happened? We created another Button and assigned id as but_id. Then, but_id was matched to but2 of the form. It means that now we can refer to this button by but2.

class Form(Widget):
    def __init__(self):
        super().__init__()
        self.but1 = Button()
        self.but1.pos = (100, 100)
        self.add_widget(self.but1)   #
        self.but2.text = "OH MY"

_Creating a new button_

Graphics

What we do next is creating a graphical element. First, we implement it in worm.kv:

<Form>:

<Cell>:
    canvas:
        Rectangle:
            size: self.size
            pos: self.pos

We linked the rectangle's position to self.pos and its size to self.size. So now, those properties are available from Cell, for example, once we create a cell, we can do:


class Cell(Widget):
    def __init__(self, x, y, size):
        super().__init__()
        self.size = (size, size)   # As you can see, we can change self.size which is "size" property of a rectangle
        self.pos = (x, y)

class Form(Widget):
    def __init__(self):
        super().__init__()
        self.cell = Cell(100, 100, 30)
        self.add_widget(self.cell)

_Creating a cell_

Ok, we have created a cell.

Necessary Methods

Let's try to move it. To do that, we should add Form.update function and schedule it.

from kivy.app import App
from kivy.uix.widget import Widget
from kivy.clock import Clock

class Cell(Widget):
    def __init__(self, x, y, size):
        super().__init__()
        self.size = (size, size)
        self.pos = (x, y)

class Form(Widget):
    def __init__(self):
        super().__init__()
        self.cell = Cell(100, 100, 30)
        self.add_widget(self.cell)

    def start(self):
        Clock.schedule_interval(self.update, 0.01)

    def update(self, _):
        self.cell.pos = (self.cell.pos[0] + 2, self.cell.pos[1] + 3)

class WormApp(App):
    def build(self):
        self.form = Form()
        self.form.start()
        return self.form

if __name__ == '__main__':
    WormApp().run()

The cell will move across the form. As you can see, we can schedule any function with Clock.

Next, let's make a touch event. Rewrite Form:

class Form(Widget):
    def __init__(self):
        super().__init__()
        self.cells = []

    def start(self):
        Clock.schedule_interval(self.update, 0.01)

    def update(self, _):
        for cell in self.cells:
            cell.pos = (cell.pos[0] + 2, cell.pos[1] + 3)

    def on_touch_down(self, touch):
        cell = Cell(touch.x, touch.y, 30)
        self.add_widget(cell)
        self.cells.append(cell)

Each touch_down creates a cell with coordinates = (touch.x, touch.y) and size of 30. Then, we add it as a widget of the form AND to our own array (in order to easily access them).

Now you can tap on your form and generate cells.

_Generating multiple cells_

Neat settings

Because we want to get a nice snake, we should distinguish the graphical positions and the actual positions of cells.

Why?

_A lot of reasons to do so. All logic should be connected with the so-called actual data, while the graphical data is the result of the actual data. For example, if we want to make margins, the actual pos of the cell will be (100, 100) while the graphical pos of the rectangle — (102, 102).

P. S. We wouldn't do it if we dealt with classical on_draw. But here, we don't have to program on_draw._

Let's fix the worm.kv file:

<Form>:

<Cell>:
    canvas:
        Rectangle:
            size: self.graphical_size
            pos: self.graphical_pos

and main.py:

...
from kivy.properties import *
...
class Cell(Widget):
    graphical_size = ListProperty([1, 1])
    graphical_pos = ListProperty([1, 1])

    def __init__(self, x, y, size, margin=4):
        super().__init__()
        self.actual_size = (size, size)
        self.graphical_size = (size - margin, size - margin)
        self.margin = margin
        self.actual_pos = (x, y)
        self.graphical_pos_attach()

    def graphical_pos_attach(self):
        self.graphical_pos = (self.actual_pos[0] - self.graphical_size[0] / 2, self.actual_pos[1] - self.graphical_size[1] / 2)
...
class Form(Widget):
    def __init__(self):
        super().__init__()
        self.cell1 = Cell(100, 100, 30)
        self.cell2 = Cell(130, 100, 30)
        self.add_widget(self.cell1)
        self.add_widget(self.cell2)
...

_Connecting cells_

The margin appeared, so it looks pretty although we created the second cell with X = 130 instead of 132. Later, we will make smooth motion based on the distance between actual_pos and graphical_pos.

Coding the Worm

Implementation

Init config in main.py

class Config:
    DEFAULT_LENGTH = 20
    CELL_SIZE = 25
    APPLE_SIZE = 35
    MARGIN = 4
    INTERVAL = 0.2
    DEAD_CELL = (1, 0, 0, 1)
    APPLE_COLOR = (1, 1, 0, 1)

(Trust me, you'll love it!)

Then, assign config to the app:

class WormApp(App):
    def __init__(self):
        super().__init__()
        self.config = Config()
        self.form = Form(self.config)

    def build(self):
        self.form.start()
        return self.form

Rewrite init and start:

class Form(Widget):
    def __init__(self, config):
        super().__init__()
        self.config = config
        self.worm = None

    def start(self):
        self.worm = Worm(self.config)
        self.add_widget(self.worm)
        Clock.schedule_interval(self.update, self.config.INTERVAL)

Then, the Cell:

class Cell(Widget):
    graphical_size = ListProperty([1, 1])
    graphical_pos = ListProperty([1, 1])

    def __init__(self, x, y, size, margin=4):
        super().__init__()
        self.actual_size = (size, size)
        self.graphical_size = (size - margin, size - margin)
        self.margin = margin
        self.actual_pos = (x, y)
        self.graphical_pos_attach()

    def graphical_pos_attach(self):
        self.graphical_pos = (self.actual_pos[0] - self.graphical_size[0] / 2, self.actual_pos[1] - self.graphical_size[1] / 2)

    def move_to(self, x, y):
        self.actual_pos = (x, y)
        self.graphical_pos_attach()

    def move_by(self, x, y, **kwargs):
        self.move_to(self.actual_pos[0] + x, self.actual_pos[1] + y, **kwargs)

    def get_pos(self):
        return self.actual_pos

    def step_by(self, direction, **kwargs):
        self.move_by(self.actual_size[0] * direction[0], self.actual_size[1] * direction[1], **kwargs)

Hopefully, it's more or less clear.

and finally the Worm:

class Worm(Widget):
    def __init__(self, config):
        super().__init__()
        self.cells = []
        self.config = config
        self.cell_size = config.CELL_SIZE
        self.head_init((100, 100))
        for i in range(config.DEFAULT_LENGTH):
            self.lengthen()

    def destroy(self):
        for i in range(len(self.cells)):
            self.remove_widget(self.cells[i])
        self.cells = []

    def lengthen(self, pos=None, direction=(0, 1)):
        # If pos is set, we put the cell in pos, otherwise accordingly to the specified direction
        if pos is None:
            px = self.cells[-1].get_pos()[0] + direction[0] * self.cell_size
            py = self.cells[-1].get_pos()[1] + direction[1] * self.cell_size
            pos = (px, py)
        self.cells.append(Cell(*pos, self.cell_size, margin=self.config.MARGIN))
        self.add_widget(self.cells[-1])

    def head_init(self, pos):
        self.lengthen(pos=pos)

Let's give life to our wormie.

_IT'S ALIVE!_

Motion

Now, we will make it move.

It's simple:

class Worm(Widget):
...
    def move(self, direction):
        for i in range(len(self.cells) - 1, 0, -1):
            self.cells[i].move_to(*self.cells[i - 1].get_pos())
        self.cells[0].step_by(direction)
class Form(Widget):
    def __init__(self, config):
        super().__init__()
        self.config = config
        self.worm = None
        self.cur_dir = (0, 0)

    def start(self):
        self.worm = Worm(self.config)
        self.add_widget(self.worm)
        self.cur_dir = (1, 0)
        Clock.schedule_interval(self.update, self.config.INTERVAL)

    def update(self, _):
        self.worm.move(self.cur_dir)

_He moving!_

It's alive! It's alive!

Controlling

As you could judge by the preview image, the controls of the snake will be the following:

class Form(Widget):
...
    def on_touch_down(self, touch):
        ws = touch.x / self.size[0]
        hs = touch.y / self.size[1]
        aws = 1 - ws
        if ws > hs and aws > hs:
            cur_dir = (0, -1)         # Down
        elif ws > hs >= aws:
            cur_dir = (1, 0)          # Right
        elif ws <= hs < aws:
            cur_dir = (-1, 0)         # Left
        else:
            cur_dir = (0, 1)           # Up
        self.cur_dir = cur_dir

_Even better!_

Cool.

Creating the Fruit

First, we initialize it.

class Form(Widget):
...
    def __init__(self, config):
        super().__init__()
        self.config = config
        self.worm = None
        self.cur_dir = (0, 0)
        self.fruit = None
...
    def random_cell_location(self, offset):
        x_row = self.size[0] // self.config.CELL_SIZE
        x_col = self.size[1] // self.config.CELL_SIZE
        return random.randint(offset, x_row - offset), random.randint(offset, x_col - offset)

    def random_location(self, offset):
        x_row, x_col = self.random_cell_location(offset)
        return self.config.CELL_SIZE * x_row, self.config.CELL_SIZE * x_col

    def fruit_dislocate(self):
        x, y = self.random_location(2)
        self.fruit.move_to(x, y)
...
    def start(self):
        self.fruit = Cell(0, 0, self.config.APPLE_SIZE, self.config.MARGIN)
        self.worm = Worm(self.config)
        self.fruit_dislocate()
        self.add_widget(self.worm)
        self.add_widget(self.fruit)
        self.cur_dir = (1, 0)
        Clock.schedule_interval(self.update, self.config.INTERVAL)

The current result:

_Creating the fruit_

Now, we should implement some Worm methods:

class Worm(Widget):
...
    # Here we get all the positions of our cells
    def gather_positions(self):
        return [cell.get_pos() for cell in self.cells]
    # Just check if our head has the same position as another Cell
    def head_intersect(self, cell):
        return self.cells[0].get_pos() == cell.get_pos()

...and add this check to update().

class Form(Widget):
...
    def update(self, _):
        self.worm.move(self.cur_dir)
        if self.worm.head_intersect(self.fruit):
            directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
            self.worm.lengthen(direction=random.choice(directions))
            self.fruit_dislocate()

Detection of Self Tile Hitting

We want to know whether the head has the same position as one of the worm's cells.

class Form(Widget):
...
    def __init__(self, config):
        super().__init__()
        self.config = config
        self.worm = None
        self.cur_dir = (0, 0)
        self.fruit = None
        self.game_on = True

    def update(self, _):
        if not self.game_on:
            return
        self.worm.move(self.cur_dir)
        if self.worm.head_intersect(self.fruit):
            directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
            self.worm.lengthen(direction=random.choice(directions))
            self.fruit_dislocate()
       if self.worm_bite_self():
            self.game_on = False

    def worm_bite_self(self):
        for cell in self.worm.cells[1:]:
            if self.worm.head_intersect(cell):
                return cell
        return False

_Losing game if snake runs into itself_

Coloring, Decorating, and Code Refactoring

Let's start with code refactoring.

Rewrite and add

class Form(Widget):
...
    def start(self):
        self.worm = Worm(self.config)
        self.add_widget(self.worm)
        if self.fruit is not None:
            self.remove_widget(self.fruit)
        self.fruit = Cell(0, 0, self.config.APPLE_SIZE)
        self.fruit_dislocate()
        self.add_widget(self.fruit)
        Clock.schedule_interval(self.update, self.config.INTERVAL)
        self.game_on = True
        self.cur_dir = (0, -1)

    def stop(self):
        self.game_on = False
        Clock.unschedule(self.update)

    def game_over(self):
        self.stop()
...
    def on_touch_down(self, touch):
        if not self.game_on:
            self.worm.destroy()
            self.start()
            return
        ...

Now, if the worm is dead (frozen), and you tap again, the game will be reset.
Now, let's go to decorating and coloring.

worm.kv

<Form>:
    popup_label: popup_label
    score_label: score_label

    canvas:
        Color:
            rgba: (.5, .5, .5, 1.0)

        Line:
            width: 1.5
            points: (0, 0), self.size

        Line:
            width: 1.5
            points: (self.size[0], 0), (0, self.size[1])

    Label:
        id: score_label
        text: "Score: " + str(self.parent.worm_len)
        width: self.width

    Label:
        id: popup_label
        width: self.width

<Worm>:

<Cell>:
    canvas:
        Color:
            rgba: self.color
        Rectangle:
            size: self.graphical_size
            pos: self.graphical_pos

Rewrite WormApp:

class WormApp(App):
    def build(self):
        self.config = Config()
        self.form = Form(self.config)
        return self.form

    def on_start(self):
        self.form.start()

_Adding a score_

Let's color it. Rewrite Cell in .kv:

<Cell>:
    canvas:
        Color:
            rgba: self.color

        Rectangle:
            size: self.graphical_size
            pos: self.graphical_pos

Add this to Cell.init

self.color = (0.2, 1.0, 0.2, 1.0)    # 

and this to Form.start

self.fruit.color = (1.0, 0.2, 0.2, 1.0)

Great, enjoy your snake

_The finished product!_

Finally, we will make a «game over» label

class Form(Widget):
...
    def __init__(self, config):
    ...
        self.popup_label.text = ""
...
    def stop(self, text=""):
        self.game_on = False
        self.popup_label.text = text
        Clock.unschedule(self.update)

    def game_over(self):
        self.stop("GAME OVER" + " " * 5 + "\ntap to reset")

and make the hit cell red:

instead of

    def update(self, _):
    ...
        if self.worm_bite_self():
            self.game_over()
    ...

write

    def update(self, _):
        cell = self.worm_bite_self()
        if cell:
            cell.color = (1.0, 0.2, 0.2, 1.0)
            self.game_over()

_GAME OVER_

Are you still paying attention? Coming next is the most interesting part.

Bonus Section — Smooth Motion

Because the worm's step is equal to the cell_size, it's not that smooth. But we want to make it step as frequently as possible, without rewriting the entire logic of the game. So, we need to create a mechanism moving our graphical poses but not our actual poses. So, I wrote a simple file:

smooth.py

from kivy.clock import Clock
import time

class Timing:
    @staticmethod
    def linear(x):
        return x

class Smooth:
    def __init__(self, interval=1.0/60.0):
        self.objs = []
        self.running = False
        self.interval = interval

    def run(self):
        if self.running:
            return
        self.running = True
        Clock.schedule_interval(self.update, self.interval)

    def stop(self):
        if not self.running:
            return
        self.running = False
        Clock.unschedule(self.update)

    def setattr(self, obj, attr, value):
        exec("obj." + attr + " = " + str(value))

    def getattr(self, obj, attr):
        return float(eval("obj." + attr))

    def update(self, _):
        cur_time = time.time()
        for line in self.objs:
            obj, prop_name_x, prop_name_y, from_x, from_y, to_x, to_y, start_time, period, timing = line
            time_gone = cur_time - start_time
            if time_gone >= period:
                self.setattr(obj, prop_name_x, to_x)
                self.setattr(obj, prop_name_y, to_y)
                self.objs.remove(line)
            else:
                share = time_gone / period
                acs = timing(share)
                self.setattr(obj, prop_name_x, from_x * (1 - acs) + to_x * acs)
                self.setattr(obj, prop_name_y, from_y * (1 - acs) + to_y * acs)
        if len(self.objs) == 0:
            self.stop()

    def move_to(self, obj, prop_name_x, prop_name_y, to_x, to_y, t, timing=Timing.linear):
        self.objs.append((obj, prop_name_x, prop_name_y, self.getattr(obj, prop_name_x), self.getattr(obj, prop_name_y), to_x,
                          to_y, time.time(), t, timing))
        self.run()

class XSmooth(Smooth):
    def __init__(self, props, timing=Timing.linear, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.props = props
        self.timing = timing

    def move_to(self, obj, to_x, to_y, t):
        super().move_to(obj, *self.props, to_x, to_y, t, timing=self.timing)

This module is not the most elegant solution ©. It's a bad solution and I acknowledge it. It is an only-hello-world solution.

So you just create smooth.py and copy-paste this code to the file.
Finally, let's make it work:

class Form(Widget):
...
    def __init__(self, config):
    ...
        self.smooth = smooth.XSmooth(["graphical_pos[0]", "graphical_pos[1]"])

Then, we replace self.worm.move() with

class Form(Widget):
...
    def update(self, _):
    ...
        self.worm.move(self.cur_dir, smooth_motion=(self.smooth, self.config.INTERVAL))

And this is how methods of Cell should look like:

class Cell(Widget):
...
    def graphical_pos_attach(self, smooth_motion=None):
        to_x, to_y = self.actual_pos[0] - self.graphical_size[0] / 2, self.actual_pos[1] - self.graphical_size[1] / 2
        if smooth_motion is None:
            self.graphical_pos = to_x, to_y
        else:
            smoother, t = smooth_motion
            smoother.move_to(self, to_x, to_y, t)

    def move_to(self, x, y, **kwargs):
        self.actual_pos = (x, y)
        self.graphical_pos_attach(**kwargs)

    def move_by(self, x, y, **kwargs):
        self.move_to(self.actual_pos[0] + x, self.actual_pos[1] + y, **kwargs)

That's it, thank you for your attention!

How the final result works, My final code

Thank for reading ! Originally published on dzone.com

Learn more

Beginning Game Development with Python

Python Online Multiplayer Game Development Tutorial