
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "gallery/event_handling/pong_sgskip.py"
.. LINE NUMBERS ARE GIVEN BELOW.

.. only:: html

    .. meta::
        :keywords: codex

    .. note::
        :class: sphx-glr-download-link-note

        :ref:`Go to the end <sphx_glr_download_gallery_event_handling_pong_sgskip.py>`
        to download the full example code.

.. rst-class:: sphx-glr-example-title

.. _sphx_glr_gallery_event_handling_pong_sgskip.py:


====
Pong
====

A Matplotlib based game of Pong illustrating one way to write interactive
animations that are easily ported to multiple backends.

.. note::
    This example exercises the interactive capabilities of Matplotlib, and this
    will not appear in the static documentation. Please run this code on your
    machine to see the interactivity.

    You can copy and paste individual parts, or download the entire example
    using the link at the bottom of the page.

.. GENERATED FROM PYTHON SOURCE LINES 17-330

.. code-block:: Python


    import time

    import matplotlib.pyplot as plt
    import numpy as np
    from numpy.random import randint, randn

    from matplotlib.font_manager import FontProperties

    instructions = """
    Player A:       Player B:
      'e'      up     'i'
      'd'     down    'k'

    press 't' -- close these instructions
                (animation will be much faster)
    press 'a' -- add a puck
    press 'A' -- remove a puck
    press '1' -- slow down all pucks
    press '2' -- speed up all pucks
    press '3' -- slow down distractors
    press '4' -- speed up distractors
    press ' ' -- reset the first puck
    press 'n' -- toggle distractors on/off
    press 'g' -- toggle the game on/off

      """


    class Pad:
        def __init__(self, disp, x, y, type='l'):
            self.disp = disp
            self.x = x
            self.y = y
            self.w = .3
            self.score = 0
            self.xoffset = 0.3
            self.yoffset = 0.1
            if type == 'r':
                self.xoffset *= -1.0

            if type == 'l' or type == 'r':
                self.signx = -1.0
                self.signy = 1.0
            else:
                self.signx = 1.0
                self.signy = -1.0

        def contains(self, loc):
            return self.disp.get_bbox().contains(loc.x, loc.y)


    class Puck:
        def __init__(self, disp, pad, field):
            self.vmax = .2
            self.disp = disp
            self.field = field
            self._reset(pad)

        def _reset(self, pad):
            self.x = pad.x + pad.xoffset
            if pad.y < 0:
                self.y = pad.y + pad.yoffset
            else:
                self.y = pad.y - pad.yoffset
            self.vx = pad.x - self.x
            self.vy = pad.y + pad.w/2 - self.y
            self._speedlimit()
            self._slower()
            self._slower()

        def update(self, pads):
            self.x += self.vx
            self.y += self.vy
            for pad in pads:
                if pad.contains(self):
                    self.vx *= 1.2 * pad.signx
                    self.vy *= 1.2 * pad.signy
            fudge = .001
            # probably cleaner with something like...
            if self.x < fudge:
                pads[1].score += 1
                self._reset(pads[0])
                return True
            if self.x > 7 - fudge:
                pads[0].score += 1
                self._reset(pads[1])
                return True
            if self.y < -1 + fudge or self.y > 1 - fudge:
                self.vy *= -1.0
                # add some randomness, just to make it interesting
                self.vy -= (randn()/300.0 + 1/300.0) * np.sign(self.vy)
            self._speedlimit()
            return False

        def _slower(self):
            self.vx /= 5.0
            self.vy /= 5.0

        def _faster(self):
            self.vx *= 5.0
            self.vy *= 5.0

        def _speedlimit(self):
            if self.vx > self.vmax:
                self.vx = self.vmax
            if self.vx < -self.vmax:
                self.vx = -self.vmax

            if self.vy > self.vmax:
                self.vy = self.vmax
            if self.vy < -self.vmax:
                self.vy = -self.vmax


    class Game:
        def __init__(self, ax):
            # create the initial line
            self.ax = ax
            ax.xaxis.set_visible(False)
            ax.set_xlim([0, 7])
            ax.yaxis.set_visible(False)
            ax.set_ylim([-1, 1])
            pad_a_x = 0
            pad_b_x = .50
            pad_a_y = pad_b_y = .30
            pad_b_x += 6.3

            # pads
            pA, = self.ax.barh(pad_a_y, .2,
                               height=.3, color='k', alpha=.5, edgecolor='b',
                               lw=2, label="Player B",
                               animated=True)
            pB, = self.ax.barh(pad_b_y, .2,
                               height=.3, left=pad_b_x, color='k', alpha=.5,
                               edgecolor='r', lw=2, label="Player A",
                               animated=True)

            # distractors
            self.x = np.arange(0, 2.22*np.pi, 0.01)
            self.line, = self.ax.plot(self.x, np.sin(self.x), "r",
                                      animated=True, lw=4)
            self.line2, = self.ax.plot(self.x, np.cos(self.x), "g",
                                       animated=True, lw=4)
            self.line3, = self.ax.plot(self.x, np.cos(self.x), "g",
                                       animated=True, lw=4)
            self.line4, = self.ax.plot(self.x, np.cos(self.x), "r",
                                       animated=True, lw=4)

            # center line
            self.centerline, = self.ax.plot([3.5, 3.5], [1, -1], 'k',
                                            alpha=.5, animated=True, lw=8)

            # puck (s)
            self.puckdisp = self.ax.scatter([1], [1], label='_nolegend_',
                                            s=200, c='g',
                                            alpha=.9, animated=True)

            self.canvas = self.ax.figure.canvas
            self.background = None
            self.cnt = 0
            self.distract = True
            self.res = 100.0
            self.on = False
            self.inst = True    # show instructions from the beginning
            self.pads = [Pad(pA, pad_a_x, pad_a_y),
                         Pad(pB, pad_b_x, pad_b_y, 'r')]
            self.pucks = []
            self.i = self.ax.annotate(instructions, (.5, 0.5),
                                      name='monospace',
                                      verticalalignment='center',
                                      horizontalalignment='center',
                                      multialignment='left',
                                      xycoords='axes fraction',
                                      animated=False)
            self.canvas.mpl_connect('key_press_event', self.on_key_press)

        def draw(self):
            draw_artist = self.ax.draw_artist
            if self.background is None:
                self.background = self.canvas.copy_from_bbox(self.ax.bbox)

            # restore the clean slate background
            self.canvas.restore_region(self.background)

            # show the distractors
            if self.distract:
                self.line.set_ydata(np.sin(self.x + self.cnt/self.res))
                self.line2.set_ydata(np.cos(self.x - self.cnt/self.res))
                self.line3.set_ydata(np.tan(self.x + self.cnt/self.res))
                self.line4.set_ydata(np.tan(self.x - self.cnt/self.res))
                draw_artist(self.line)
                draw_artist(self.line2)
                draw_artist(self.line3)
                draw_artist(self.line4)

            # pucks and pads
            if self.on:
                self.ax.draw_artist(self.centerline)
                for pad in self.pads:
                    pad.disp.set_y(pad.y)
                    pad.disp.set_x(pad.x)
                    self.ax.draw_artist(pad.disp)

                for puck in self.pucks:
                    if puck.update(self.pads):
                        # we only get here if someone scored
                        self.pads[0].disp.set_label(f"   {self.pads[0].score}")
                        self.pads[1].disp.set_label(f"   {self.pads[1].score}")
                        self.ax.legend(loc='center', framealpha=.2,
                                       facecolor='0.5',
                                       prop=FontProperties(size='xx-large',
                                                           weight='bold'))

                        self.background = None
                        self.ax.figure.canvas.draw_idle()
                        return
                    puck.disp.set_offsets([[puck.x, puck.y]])
                    self.ax.draw_artist(puck.disp)

            # just redraw the Axes rectangle
            self.canvas.blit(self.ax.bbox)
            self.canvas.flush_events()
            if self.cnt == 50000:
                # just so we don't get carried away
                print("...and you've been playing for too long!!!")
                plt.close()

            self.cnt += 1

        def on_key_press(self, event):
            if event.key == '3':
                self.res *= 5.0
            if event.key == '4':
                self.res /= 5.0

            if event.key == 'e':
                self.pads[0].y += .1
                if self.pads[0].y > 1 - .3:
                    self.pads[0].y = 1 - .3
            if event.key == 'd':
                self.pads[0].y -= .1
                if self.pads[0].y < -1:
                    self.pads[0].y = -1

            if event.key == 'i':
                self.pads[1].y += .1
                if self.pads[1].y > 1 - .3:
                    self.pads[1].y = 1 - .3
            if event.key == 'k':
                self.pads[1].y -= .1
                if self.pads[1].y < -1:
                    self.pads[1].y = -1

            if event.key == 'a':
                self.pucks.append(Puck(self.puckdisp,
                                       self.pads[randint(2)],
                                       self.ax.bbox))
            if event.key == 'A' and len(self.pucks):
                self.pucks.pop()
            if event.key == ' ' and len(self.pucks):
                self.pucks[0]._reset(self.pads[randint(2)])
            if event.key == '1':
                for p in self.pucks:
                    p._slower()
            if event.key == '2':
                for p in self.pucks:
                    p._faster()

            if event.key == 'n':
                self.distract = not self.distract

            if event.key == 'g':
                self.on = not self.on
            if event.key == 't':
                self.inst = not self.inst
                self.i.set_visible(not self.i.get_visible())
                self.background = None
                self.canvas.draw_idle()
            if event.key == 'q':
                plt.close()


    fig, ax = plt.subplots()
    canvas = ax.figure.canvas
    animation = Game(ax)

    # disable the default key bindings
    if fig.canvas.manager.key_press_handler_id is not None:
        canvas.mpl_disconnect(fig.canvas.manager.key_press_handler_id)


    # reset the blitting background on redraw
    def on_redraw(event):
        animation.background = None


    # bootstrap after the first draw
    def start_anim(event):
        canvas.mpl_disconnect(start_anim.cid)

        start_anim.timer.add_callback(animation.draw)
        start_anim.timer.start()
        canvas.mpl_connect('draw_event', on_redraw)


    start_anim.cid = canvas.mpl_connect('draw_event', start_anim)
    start_anim.timer = animation.canvas.new_timer(interval=1)

    tstart = time.time()

    plt.show()
    print('FPS: %f' % (animation.cnt/(time.time() - tstart)))


.. _sphx_glr_download_gallery_event_handling_pong_sgskip.py:

.. only:: html

  .. container:: sphx-glr-footer sphx-glr-footer-example

    .. container:: sphx-glr-download sphx-glr-download-jupyter

      :download:`Download Jupyter notebook: pong_sgskip.ipynb <pong_sgskip.ipynb>`

    .. container:: sphx-glr-download sphx-glr-download-python

      :download:`Download Python source code: pong_sgskip.py <pong_sgskip.py>`

    .. container:: sphx-glr-download sphx-glr-download-zip

      :download:`Download zipped: pong_sgskip.zip <pong_sgskip.zip>`


.. only:: html

 .. rst-class:: sphx-glr-signature

    `Gallery generated by Sphinx-Gallery <https://sphinx-gallery.github.io>`_
