Create a two-player game with Python and Vue
Tags: python, pusher, vue, realtime
Published: 2018-11-20 06:53:58

---
title: Create a two-player game with Python and Vue
published: true
description: 
tags: python, pusher, vuejs, realtime
---

> You will need Python 3+, virtualenv and Flask installed on your machine.

The advent of the PC and the internet has redefined the term “entertainment” and the means by which it can be obtained. While a console or some special hardware would have been required to play games in the past, games are only a click away in today's world of technology.

In this tutorial, we will create a realtime tic-tac-toe game using Python and Pusher channels. Here’s a demo of how the game will look and behave upon creation:

![](//images.ctfassets.net/1es3ne0caaid/1RGRfjXfgQgYWYy2kqImOs/0c1db39f9371dbb773638ffe57bfd385/python-vue-game-demo.gif)

This multiplayer game will allow a player to connect using their preferred username (or generate a random username where a player doesn’t connect with a username) and choose to play with another player from a list of other online players.

The game itself follows the conventional principles of the popular [tic-tac-toe](https://en.wikipedia.org/wiki/Tic-tac-toe) game. The “online player(s)” feature is powered by [Pusher presence channels](https://pusher.com/docs/client_api_guide/client_presence_channels) and the realtime updates of a player’s move across multiple windows is powered by [Pusher private channels.](https://pusher.com/docs/client_api_guide/client_private_channels) The source code for this tutorial is available here [GitHub](https://github.com/neoighodaro/python-pusher-multiplayer-game).

Let’s get started.

## Prerequisites

To follow along, a basic knowledge of Python, Flask, JavaScript (ES6 syntax) and Vue is required. You will also need the following installed on your machine:

1. [Python (v3.x)](https://www.python.org/)
2. [Virtualenv](https://virtualenv.pypa.io/en/stable/)
3. [Flask](http://flask.pocoo.org/)

Virtualenv is great for creating isolated Python environments, so we can install dependencies in an isolated environment without polluting our global packages directory.

## Setting up the app environment

We will create the project folder and activate a virtual environment within it:

```
    $ mkdir python-pusher-mutiplayer-game
    $ cd python-pusher-mutiplayer-game
    $ virtualenv .venv
    $ source .venv/bin/activate # Linux based systems
    $ \path\to\env\Scripts\activate # Windows users

```

We will install [Flask](http://flask.pocoo.org/) using this command:

```
    $ pip install flask

```

## Setting up Pusher

To integrate Pusher into the multiplayer game, we need to create a Pusher channels application from the Pusher dashboard. If you don’t already have a Pusher account, head over to the [Pusher website](https://pusher.com/) and create one.

After creating an account, create a new channels application and enable client events from the application dashboard. To enable client events, click on **App settings** and scroll to the bottom of the page then select the option that says **Enable client events,** and update the **App settings.**

## Building the backend server

Back in the project directory, let’s install the [Python Pusher library](https://github.com/pusher/pusher-http-python) with this command:


```
    $ pip install pusher
```


We will create a new file and call it `app.py`, this is where we will write all the code for the Flask backend server. We will also create a folder and call it `templates`, this folder will hold the markup files for this application.

Let’s write some code to register the endpoints for the game and serve the view, open the `app.py` file and paste the following code:


```
    // File: ./app.py
    <span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request, jsonify, make_response, json
    <span class="hljs-keyword">from</span> pusher <span class="hljs-keyword">import</span> pusher

    app = Flask(__name__)

    pusher = pusher_client = pusher.Pusher(
      app_id=<span class="hljs-string">'PUSHER_APP_ID'</span>,
      key=<span class="hljs-string">'PUSHER_APP_KEY'</span>,
      secret=<span class="hljs-string">'PUSHER_APP_SECRET'</span>,
      cluster=<span class="hljs-string">'PUSHER_APP_CLUSTER'</span>,
      ssl=<span class="hljs-keyword">True</span>
    )

    name = <span class="hljs-string">''</span>

<span class="hljs-meta">    @app.route('/')</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span><span class="hljs-params">()</span>:</span>
      <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>)

<span class="hljs-meta">    @app.route('/play')</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">play</span><span class="hljs-params">()</span>:</span>
      <span class="hljs-keyword">global</span> name
      name = request.args.get(<span class="hljs-string">'username'</span>)
      <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'play.html'</span>)

<span class="hljs-meta">    @app.route("/pusher/auth", methods=['POST'])</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pusher_authentication</span><span class="hljs-params">()</span>:</span>
      auth = pusher.authenticate(
        channel=request.form[<span class="hljs-string">'channel_name'</span>],
        socket_id=request.form[<span class="hljs-string">'socket_id'</span>],
        custom_data={
          <span class="hljs-string">u'user_id'</span>: name,
          <span class="hljs-string">u'user_info'</span>: {
            <span class="hljs-string">u'role'</span>: <span class="hljs-string">u'player'</span>
          }
        }
      )
      <span class="hljs-keyword">return</span> json.dumps(auth)

    <span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
        app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">5000</span>, debug=<span class="hljs-keyword">True</span>)

    name = <span class="hljs-string">''</span>
```


> Replace the `PUSHER_APP_*` keys with the values on your Pusher dashboard.

In the code above, we defined three endpoints, here’s what they do:

- `/` - renders the front page that asks a player to connect with a username.
- `/play` - renders the game view.
- `/pusher/auth` - authenticates Pusher’s presence and private channels for connected players.

## Building the frontend

In the `templates` folder, we will create two files:

1. `index.html`
2. `play.html`

The `index.html` file will render the connection page, so open the `templates/index.html` file and paste the following code:



    <span class="hljs-comment"></span>
    <span class="hljs-meta"></span>
    <span class="hljs-tag"><<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">head</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1, shrink-to-fit=no"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">""</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"author"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"Neo Ighodaro"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">title</span>></span>TIC-TAC-TOE<span class="hljs-tag"></<span class="hljs-name">title</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">style</span>></span><span class="css">
                  <span class="hljs-selector-pseudo">:root</span> {
                    <span class="hljs-attribute">--input-padding-x</span>: .<span class="hljs-number">75rem</span>;
                    <span class="hljs-attribute">--input-padding-y</span>: .<span class="hljs-number">75rem</span>;
                  }
                  <span class="hljs-selector-tag">html</span>,
                  <span class="hljs-selector-tag">body</span>, <span class="hljs-selector-tag">body</span> > <span class="hljs-selector-tag">div</span> {
                    <span class="hljs-attribute">height</span>: <span class="hljs-number">100%</span>;
                  }
                  <span class="hljs-selector-tag">body</span> > <span class="hljs-selector-tag">div</span> {
                    <span class="hljs-attribute">display</span>: -ms-flexbox;
                    <span class="hljs-attribute">display</span>: flex;
                    <span class="hljs-attribute">-ms-flex-align</span>: center;
                    <span class="hljs-attribute">align-items</span>: center;
                    <span class="hljs-attribute">padding-top</span>: <span class="hljs-number">40px</span>;
                    <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-number">40px</span>;
                    <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f5f5f5</span>;
                  }
                  <span class="hljs-selector-class">.form-signin</span> {
                    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
                    <span class="hljs-attribute">max-width</span>: <span class="hljs-number">420px</span>;
                    <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
                    <span class="hljs-attribute">margin</span>: auto;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> {
                    <span class="hljs-attribute">position</span>: relative;
                    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> > <span class="hljs-selector-tag">input</span>,
                  <span class="hljs-selector-class">.form-label-group</span> > <span class="hljs-selector-tag">label</span> {
                    <span class="hljs-attribute">padding</span>: <span class="hljs-built_in">var</span>(--input-padding-y) <span class="hljs-built_in">var</span>(--input-padding-x);
                  }
                  <span class="hljs-selector-class">.form-label-group</span> > <span class="hljs-selector-tag">label</span> {
                    <span class="hljs-attribute">position</span>: absolute;
                    <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
                    <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
                    <span class="hljs-attribute">display</span>: block;
                    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
                    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">0</span>; <span class="hljs-comment">/* Override default `<label>` margin */</span>
                    <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
                    <span class="hljs-attribute">color</span>: <span class="hljs-number">#495057</span>;
                    <span class="hljs-attribute">cursor</span>: text; <span class="hljs-comment">/* Match the input under the label */</span>
                    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid transparent;
                    <span class="hljs-attribute">border-radius</span>: .<span class="hljs-number">25rem</span>;
                    <span class="hljs-attribute">transition</span>: all .<span class="hljs-number">1s</span> ease-in-out;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">::-webkit-input-placeholder</span> {
                    <span class="hljs-attribute">color</span>: transparent;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:-ms-input-placeholder</span> {
                    <span class="hljs-attribute">color</span>: transparent;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">::-ms-input-placeholder</span> {
                    <span class="hljs-attribute">color</span>: transparent;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">::-moz-placeholder</span> {
                    <span class="hljs-attribute">color</span>: transparent;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">::placeholder</span> {
                    <span class="hljs-attribute">color</span>: transparent;
                  }
                  <span class="hljs-selector-class">.form-label-group</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:placeholder-shown)</span> {
                    <span class="hljs-attribute">padding-top</span>: <span class="hljs-built_in">calc</span>(var(--input-padding-y) + <span class="hljs-built_in">var</span>(--input-padding-y) * (<span class="hljs-number">2</span> / <span class="hljs-number">3</span>));
                    <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-built_in">calc</span>(var(--input-padding-y) / <span class="hljs-number">3</span>);
                  }
                  <span class="hljs-selector-class">.form-label-group</span> <span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:not(</span><span class="hljs-selector-pseudo">:placeholder-shown)</span> ~ <span class="hljs-selector-tag">label</span> {
                    <span class="hljs-attribute">padding-top</span>: <span class="hljs-built_in">calc</span>(var(--input-padding-y) / <span class="hljs-number">3</span>);
                    <span class="hljs-attribute">padding-bottom</span>: <span class="hljs-built_in">calc</span>(var(--input-padding-y) / <span class="hljs-number">3</span>);
                    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">12px</span>;
                    <span class="hljs-attribute">color</span>: <span class="hljs-number">#777</span>;
                  }
            </span><span class="hljs-tag"></<span class="hljs-name">style</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">head</span>></span>
          <span class="hljs-tag"><<span class="hljs-name">body</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">form</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-signin"</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-center mb-4"</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://thestore.gameops.com/v/vspfiles/photos/Tic-Tac-Go-14.gif"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">""</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"72"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"72"</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h3 mb-3 font-weight-normal"</span>></span>TIC-TAC-TOE<span class="hljs-tag"></<span class="hljs-name">h1</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">p</span>></span>PUT IN YOUR DETAILS TO PLAY<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
                <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-label-group"</span>></span>
                    <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"name"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"inputUsername"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"username"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Username"</span> <span class="hljs-attr">required</span>=<span class="hljs-string">""</span> <span class="hljs-attr">autofocus</span>=<span class="hljs-string">""</span>></span>
                      <span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"inputUsername"</span>></span>Username<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
                <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-label-group"</span>></span>
                  <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"inputEmail"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Email address"</span> <span class="hljs-attr">autofocus</span>=<span class="hljs-string">""</span> <span class="hljs-attr">required</span>></span>
                    <span class="hljs-tag"><<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"inputEmail"</span>></span>Email address<span class="hljs-tag"></<span class="hljs-name">label</span>></span>
                <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-lg btn-primary btn-block"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"login"</span>></span>Connect<span class="hljs-tag"></<span class="hljs-name">button</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-5 mb-3 text-muted text-center"</span>></span>© 2017-2018<span class="hljs-tag"></<span class="hljs-name">p</span>></span>
              <span class="hljs-tag"></<span class="hljs-name">form</span>></span>
            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript">
            <span class="hljs-keyword">var</span> app = <span class="hljs-keyword">new</span> Vue({
              <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,
              <span class="hljs-attr">methods</span>: {
                <span class="hljs-attr">login</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
                  <span class="hljs-keyword">let</span> username = <span class="hljs-keyword">this</span>.$refs.username.value
                  <span class="hljs-keyword">let</span> email = <span class="hljs-keyword">this</span>.$refs.email.value
                  <span class="hljs-built_in">window</span>.location.replace(<span class="hljs-string">`/play?username=<span class="hljs-subst">${username}</span>&email=<span class="hljs-subst">${email}</span>`</span>);
                }
              }
            })
            </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
        <span class="hljs-tag"></<span class="hljs-name">body</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">html</span>></span>



When a player visits the connection page and puts in a username and email, the browser window will be redirected to the game view.

Let’s write the markup for the game view. Open the `play.html` file and paste the following code:



    <span class="hljs-comment"></span>
    <span class="hljs-meta"></span>
    <span class="hljs-tag"><<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">head</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1, shrink-to-fit=no"</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">title</span>></span>TIC-TAC-TOE<span class="hljs-tag"></<span class="hljs-name">title</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">head</span>></span>
    <span class="hljs-tag"><<span class="hljs-name">body</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container-fluid"</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container-fluid clearfix mb-3 shadow"</span>></span>
          <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"float-left my-3"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://thestore.gameops.com/v/vspfiles/photos/Tic-Tac-Go-14.gif"</span> <span class="hljs-attr">height</span>=<span class="hljs-string">"62px"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"62px"</span>
          /></span>
          <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"float-right w-25 py-3"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">img</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"my-3 mx-3 rounded-circle border"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://dfsanonymous.club/wp-content/uploads/2017/11/DFSAnonymous-NewLogo.png"</span>
              <span class="hljs-attr">height</span>=<span class="hljs-string">"62px"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"62px"</span> /></span>
            <span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"d-inline"</span>></span> {% raw %} {{ username }} {% endraw %} <span class="hljs-tag"></<span class="hljs-name">p</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
        <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
        <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row mx-5"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"height: 50vh"</span>></span>
          <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-8 h-50 align-self-center"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row border rounded invisible h-50 w-75 m-auto"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"font-size: 3.6rem"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"gameboard"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"playerAction"</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-100 pr-2 col border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"1"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"1"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col pr-2 border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"2"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"2"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col pr-2 border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"3"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"3"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-100"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-100 pr-2 col border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"4"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"4"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col pr-2 border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"5"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"5"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col pr-2 border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"6"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"6"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-100"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h-100 pr-2 col border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"7"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"7"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col pr-2 border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"8"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"8"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col pr-2 border border-dark"</span> <span class="hljs-attr">data-id</span>=<span class="hljs-string">"9"</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">"9"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
          <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-4 pl-3"</span>></span>
            <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row h-100"</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col border h-75 text-center"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"background: rgb(114, 230, 147);"</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"my-3"</span>></span> {% raw %} {{ players }} {% endraw %} online player(s) <span class="hljs-tag"></<span class="hljs-name">p</span>></span>
                <span class="hljs-tag"><<span class="hljs-name">hr</span>/></span>
                <span class="hljs-tag"><<span class="hljs-name">li</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"m-auto py-3 text-dark"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"cursor: pointer;"</span> <span class="hljs-attr">v-for</span>=<span class="hljs-string">"member in connectedPlayers"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"choosePlayer"</span>></span>
                  {% raw %} {{ member }} {% endraw %}
                <span class="hljs-tag"></<span class="hljs-name">li</span>></span>
              <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-100"</span>></span><span class="hljs-tag"></<span class="hljs-name">div</span>></span>
              <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col text-center py-3 border h-25"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"background: #b6c0ca; font-size: 1em; font-weight: bold"</span>></span>
                {% raw %} {{ status }} {% endraw %}
              <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
            <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
          <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
        <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
      <span class="hljs-tag"></<span class="hljs-name">div</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/vue/dist/vue.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://js.pusher.com/4.2/pusher.min.js"</span>></span><span class="undefined"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
      <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="undefined">

      </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">body</span>></span>
    <span class="hljs-tag"></<span class="hljs-name">html</span>></span>




The code above defines the layout of the game view but does not contain any interactivity or realtime features. In the scripts section, before the closing `body` tag, we included the Vue and Pusher libraries because they are required for the game to work.

Let’s include the JavaScript code that will drive the entire game process and define its logic.

In the same file, add the code below in between the `script` tag that is just before the closing `body` tag:




    <span class="hljs-keyword">var</span> app = <span class="hljs-keyword">new</span> Vue({
      <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>,

      <span class="hljs-attr">data</span>: {
        <span class="hljs-attr">username</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">players</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">connectedPlayers</span>: [],
        <span class="hljs-attr">status</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">pusher</span>: <span class="hljs-keyword">new</span> Pusher(<span class="hljs-string">'PUSHER_APP_KEY'</span>, {
          <span class="hljs-attr">authEndpoint</span>: <span class="hljs-string">'/pusher/auth'</span>,
          <span class="hljs-attr">cluster</span>: <span class="hljs-string">'PUSHER_APP_CLUSTER'</span>,
          <span class="hljs-attr">encrypted</span>: <span class="hljs-literal">true</span>
        }),
        <span class="hljs-attr">otherPlayerName</span>: <span class="hljs-string">''</span>,
        <span class="hljs-attr">mychannel</span>: {},
        <span class="hljs-attr">otherPlayerChannel</span>: {},
        <span class="hljs-attr">firstPlayer</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">turn</span>: <span class="hljs-number">0</span>,
        <span class="hljs-attr">boxes</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>]
      },

      created () {
        <span class="hljs-keyword">let</span> url = <span class="hljs-keyword">new</span> URL(<span class="hljs-built_in">window</span>.location.href);
        <span class="hljs-keyword">let</span> name = url.searchParams.get(<span class="hljs-string">"username"</span>);

        <span class="hljs-keyword">if</span> (name) {
          <span class="hljs-keyword">this</span>.username = name
          <span class="hljs-keyword">this</span>.subscribe();
          <span class="hljs-keyword">this</span>.listeners();
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">this</span>.username = <span class="hljs-keyword">this</span>.generateRandomName();
          location.assign(<span class="hljs-string">"/play?username="</span> + <span class="hljs-keyword">this</span>.username);
        }
      },

      <span class="hljs-attr">methods</span>: {
        <span class="hljs-comment">// We will add methods here</span>
      }
    });




> Replace the `PUSHER_APP_*` keys with the keys on your Pusher dashboard.


Above, we create a new instance of Vue and we target the `#app` selector. We define all the defaults in the `data` object and then in the `create()` function which is called automatically when the Vue component is created, we check for a user and assign the user to the username if one was supplied.

We also make calls to the `subscribe` and `listeners` methods. Let’s define those inside the `methods` object. Inside the `methods` object, paste the following functions:



    <span class="hljs-comment">// [...]</span>

    subscribe: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">let</span> channel = <span class="hljs-keyword">this</span>.pusher.subscribe(<span class="hljs-string">'presence-channel'</span>);
      <span class="hljs-keyword">this</span>.myChannel = <span class="hljs-keyword">this</span>.pusher.subscribe(<span class="hljs-string">'private-'</span> + <span class="hljs-keyword">this</span>.username)

      channel.bind(<span class="hljs-string">'pusher:subscription_succeeded'</span>, (player) => {
        <span class="hljs-keyword">this</span>.players = player.count - <span class="hljs-number">1</span>
        player.each(<span class="hljs-function">(<span class="hljs-params">player</span>) =></span> {
          <span class="hljs-keyword">if</span> (player.id != <span class="hljs-keyword">this</span>.username)
            <span class="hljs-keyword">this</span>.connectedPlayers.push(player.id)
        });
      });

      channel.bind(<span class="hljs-string">'pusher:member_added'</span>, (player) => {
        <span class="hljs-keyword">this</span>.players++;
        <span class="hljs-keyword">this</span>.connectedPlayers.push(player.id)
      });

      channel.bind(<span class="hljs-string">'pusher:member_removed'</span>, (player) => {
        <span class="hljs-keyword">this</span>.players--;
        <span class="hljs-keyword">var</span> index = <span class="hljs-keyword">this</span>.connectedPlayers.indexOf(player.id);
        <span class="hljs-keyword">if</span> (index > <span class="hljs-number">-1</span>) {
          <span class="hljs-keyword">this</span>.connectedPlayers.splice(index, <span class="hljs-number">1</span>)
        }
      });
    },

    <span class="hljs-attr">listeners</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">this</span>.pusher.bind(<span class="hljs-string">'client-'</span> + <span class="hljs-keyword">this</span>.username, (message) => {
        <span class="hljs-keyword">if</span> (confirm(<span class="hljs-string">'Do you want to start a game of Tic Tac Toe with '</span> + message)) {
          <span class="hljs-keyword">this</span>.otherPlayerName = message
          <span class="hljs-keyword">this</span>.otherPlayerChannel = <span class="hljs-keyword">this</span>.pusher.subscribe(<span class="hljs-string">'private-'</span> + <span class="hljs-keyword">this</span>.otherPlayerName)
          <span class="hljs-keyword">this</span>.otherPlayerChannel.bind(<span class="hljs-string">'pusher:subscription_succeeded'</span>, () => {
            <span class="hljs-keyword">this</span>.otherPlayerChannel.trigger(<span class="hljs-string">'client-game-started'</span>, <span class="hljs-keyword">this</span>.username)
          })
          <span class="hljs-keyword">this</span>.startGame(message)
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">this</span>.otherPlayerChannel = <span class="hljs-keyword">this</span>.pusher.subscribe(<span class="hljs-string">'private-'</span> + message)
          <span class="hljs-keyword">this</span>.otherPlayerChannel.bind(<span class="hljs-string">'pusher:subscription_succeeded'</span>, () => {
            <span class="hljs-keyword">this</span>.otherPlayerChannel.trigger(<span class="hljs-string">'client-game-declined'</span>, <span class="hljs-string">""</span>)
          })
          <span class="hljs-keyword">this</span>.gameDeclined()
        }
      }),

      <span class="hljs-keyword">this</span>.myChannel.bind(<span class="hljs-string">'client-game-started'</span>, (message) => {
        <span class="hljs-keyword">this</span>.status = <span class="hljs-string">"Game started with "</span> + message
        <span class="hljs-keyword">this</span>.$refs.gameboard.classList.remove(<span class="hljs-string">'invisible'</span>);
        <span class="hljs-keyword">this</span>.firstPlayer = <span class="hljs-number">1</span>;
        <span class="hljs-keyword">this</span>.turn = <span class="hljs-number">1</span>;
      })

      <span class="hljs-keyword">this</span>.myChannel.bind(<span class="hljs-string">'client-game-declined'</span>, () => {
        <span class="hljs-keyword">this</span>.status = <span class="hljs-string">"Game declined"</span>
      })

      <span class="hljs-keyword">this</span>.myChannel.bind(<span class="hljs-string">'client-new-move'</span>, (position) => {
        <span class="hljs-keyword">this</span>.$refs[position].innerText = <span class="hljs-keyword">this</span>.firstPlayer ? <span class="hljs-string">'O'</span> : <span class="hljs-string">'X'</span>
      })

      <span class="hljs-keyword">this</span>.myChannel.bind(<span class="hljs-string">'client-your-turn'</span>, () => {
        <span class="hljs-keyword">this</span>.turn = <span class="hljs-number">1</span>;
      })

      <span class="hljs-keyword">this</span>.myChannel.bind(<span class="hljs-string">'client-box-update'</span>, (update) => {
        <span class="hljs-keyword">this</span>.boxes = update;
      })

      <span class="hljs-keyword">this</span>.myChannel.bind(<span class="hljs-string">'client-you-lost'</span>, () => {
        <span class="hljs-keyword">this</span>.gameLost();
      })
    },

    <span class="hljs-comment">// [...]</span>



In the `subscribe` method, we subscribe to our Pusher presence channel, and then subscribe to the private channel for the current user. In the `listeners` method we register the listeners for all the events we are expecting to be triggered on the private channel we subscribed to.

Next, we will add other helper methods to our methods class. Inside the methods class, add the following functions to the bottom after the `listeners` method:



    <span class="hljs-comment">// Generates a random string we use as a name for a guest user</span>
    generateRandomName: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">let</span> text = <span class="hljs-string">''</span>;
      <span class="hljs-keyword">let</span> possible = <span class="hljs-string">'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'</span>;
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i < <span class="hljs-number">6</span>; i++) {
        text += possible.charAt(<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * possible.length));
      }
      <span class="hljs-keyword">return</span> text;
    },

    <span class="hljs-comment">// Lets you choose a player to play as.</span>
    choosePlayer: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
      <span class="hljs-keyword">this</span>.otherPlayerName = e.target.innerText
      <span class="hljs-keyword">this</span>.otherPlayerChannel = <span class="hljs-keyword">this</span>.pusher.subscribe(<span class="hljs-string">'private-'</span> + <span class="hljs-keyword">this</span>.otherPlayerName)
      <span class="hljs-keyword">this</span>.otherPlayerChannel.bind(<span class="hljs-string">'pusher:subscription_succeeded'</span>, () => {
        <span class="hljs-keyword">this</span>.otherPlayerChannel.trigger(<span class="hljs-string">'client-'</span> + <span class="hljs-keyword">this</span>.otherPlayerName, <span class="hljs-keyword">this</span>.username)
      });
    },

    <span class="hljs-comment">// Begins the game</span>
    startGame: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">name</span>) </span>{
      <span class="hljs-keyword">this</span>.status = <span class="hljs-string">"Game started with "</span> + name
      <span class="hljs-keyword">this</span>.$refs.gameboard.classList.remove(<span class="hljs-string">'invisible'</span>);
    },

    <span class="hljs-comment">// User declined to play</span>
    gameDeclined: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">this</span>.status = <span class="hljs-string">"Game declined"</span>
    },

    <span class="hljs-comment">// Game has ended with current user winning</span>
    gameWon: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">this</span>.status = <span class="hljs-string">"You WON!"</span>
      <span class="hljs-keyword">this</span>.$refs.gameboard.classList.add(<span class="hljs-string">'invisible'</span>);
      <span class="hljs-keyword">this</span>.restartGame()
    },

    <span class="hljs-comment">// Game has ended with current user losing</span>
    gameLost: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">this</span>.turn = <span class="hljs-number">1</span>;
      <span class="hljs-keyword">this</span>.boxes = [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>]
      <span class="hljs-keyword">this</span>.status = <span class="hljs-string">"You LOST!"</span>
      <span class="hljs-keyword">this</span>.$refs.gameboard.classList.add(<span class="hljs-string">'invisible'</span>);
      <span class="hljs-keyword">this</span>.restartGame()
    },

    <span class="hljs-comment">// Restarts a game</span>
    restartGame: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">for</span> (i = <span class="hljs-number">1</span>; i < <span class="hljs-number">10</span>; i++) {
        <span class="hljs-keyword">this</span>.$refs[i].innerText = <span class="hljs-string">""</span>
      }
      <span class="hljs-keyword">this</span>.$refs.gameboard.classList.remove(<span class="hljs-string">'invisible'</span>);
    },

    <span class="hljs-comment">// Checks tiles to see if the tiles passed are a match</span>
    compare: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">1</span>; i < <span class="hljs-built_in">arguments</span>.length; i++) {
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">arguments</span>[i] === <span class="hljs-number">0</span> || <span class="hljs-built_in">arguments</span>[i] !== <span class="hljs-built_in">arguments</span>[i - <span class="hljs-number">1</span>]) {
          <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
        }
      }

      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    },

    <span class="hljs-comment">// Checks the tiles and returns true if theres a winning play</span>
    theresAMatch: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">0</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">1</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">2</span>]) ||
        <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">3</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">4</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">5</span>]) ||
        <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">6</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">7</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">8</span>]) ||
        <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">0</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">3</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">6</span>]) ||
        <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">1</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">4</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">7</span>]) ||
        <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">2</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">5</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">8</span>]) ||
        <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">2</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">4</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">6</span>]) ||
        <span class="hljs-keyword">this</span>.compare(<span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">0</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">4</span>], <span class="hljs-keyword">this</span>.boxes[<span class="hljs-number">8</span>])
    },

    <span class="hljs-comment">// Checks to see if the play was a winning play</span>
    playerAction: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{
      <span class="hljs-keyword">let</span> index = e.target.dataset.id - <span class="hljs-number">1</span>
      <span class="hljs-keyword">let</span> tile = <span class="hljs-keyword">this</span>.firstPlayer ? <span class="hljs-string">'X'</span> : <span class="hljs-string">'O'</span>

      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.turn && <span class="hljs-keyword">this</span>.boxes[index] == <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">this</span>.turn = <span class="hljs-number">0</span>
        <span class="hljs-keyword">this</span>.boxes[index] = tile
        e.target.innerText = tile

        <span class="hljs-keyword">this</span>.otherPlayerChannel.trigger(<span class="hljs-string">'client-your-turn'</span>, <span class="hljs-string">""</span>)
        <span class="hljs-keyword">this</span>.otherPlayerChannel.trigger(<span class="hljs-string">'client-box-update'</span>, <span class="hljs-keyword">this</span>.boxes)
        <span class="hljs-keyword">this</span>.otherPlayerChannel.trigger(<span class="hljs-string">'client-new-move'</span>, e.target.dataset.id)

        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.theresAMatch()) {
          <span class="hljs-keyword">this</span>.gameWon()
          <span class="hljs-keyword">this</span>.boxes = [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>]
          <span class="hljs-keyword">this</span>.otherPlayerChannel.trigger(<span class="hljs-string">'client-you-lost'</span>, <span class="hljs-string">''</span>)
        }
      }
    },



Above, we have added several helper methods that the game needs to function properly and before each method, we have added a comment to show what the method does.

Let’s test the game now.

## Testing the game

We can test the game by running this command:



    $ flask run


Now if we visit [localhost:5000](http://localhost:5000), we should see the connection page and test the game:

![](//images.ctfassets.net/1es3ne0caaid/1RGRfjXfgQgYWYy2kqImOs/0c1db39f9371dbb773638ffe57bfd385/python-vue-game-demo.gif)

## Conclusion

In this tutorial, we have learned how to leverage the Pusher SDK in creating an online multiplayer game powered by a Python backend server.

The source code for this tutorial is available on [GitHub](https://github.com/neoighodaro/python-pusher-multiplayer-game)

This post first appeared on the [Pusher blog](https://pusher.com/tutorials/game-python-vue).