上司のターミナルにマトリックスを走らせよう！

Livesense Advent Caelendar 2015 12日目担当の[@sion_cojp](https://twitter.com/sion_cojp)です。

## 振り返り・・・

前はインフラエンジニアとして働いており、ムカついたときは[上司のターミナルに汽車を走らせてました。](http://qiita.com/sion_cojp/items/e4c61485f9204b2abf95)
今はWebアプリエンジニアとして部署異動し、APIを開発しております。

## この記事をご覧になっている、そこのあなた。

仕事中にこんなことはありませんか？<br><br><br>

### <b>部署異動したけど、やっぱり上司から無理な依頼をされた。ちくしょう。。。</b>

わかります。その気持ち。ええ。
~~せっかく異動したんだから、高い寿司とかおごってくれてもいいのに。。~~
でも異動出来た事には感謝しないとです。社会って複雑ですよね。
<br><br><br><br><br><br>

#<b>そんな上司の作業中のターミナル（端末）にマトリックスを走らせてやりましょう！！</b>
<br><br><br><br><br><br>

### まずは準備。
<br>

## ①上司が作業しているホストを探そう
何かしらの方法でげっとします。（鬼門）
またまた私は上司のモニターを後ろからこっそり覗いてやりました。
<br><br><br>
のんびりPHPのレガシーコードと格闘してるわ。
#### <b>馬鹿め。</b>
上司が開発しているサーバ情報をげっと。

## ②マトリックスを流すプログラムを書く。
適当に作る。
MATRIX_ANGRY_CHARS　に怒りの気持ちをぶつけましょう。
プログラムを流すと分かりますが、怒りの部分は<b>バレないように</b>なってるので安心してください。<br>
MATRIX_THANKS_CHARS　には感謝の気持ちを入れましょう。

```py:matrix_curses.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import unicode_literals
import locale
import time
import curses
import sys, string
import random
import struct
locale.setlocale(locale.LC_ALL, '')
encoding = locale.getpreferredencoding()

########################################################################
# const

MIN_SPEED = 1
MAX_SPEED = 2
MATRIX_ANGRY_CHARS = "遅刻連絡くらいメールじゃなくてチャットでいいじゃん 波動拳打ち込みたい　ほぼ逝きかけました"
MATRIX_THANKS_CHARS = "THANKS DEAR TAKU !!!"
MATRIX_COLOR_WORD = 46
MATRIX_COLOR_BACK = 232

########################################################################

class FallingChar(object):
    matrixchr = list(MATRIX_ANGRY_CHARS)
    normal_attr = curses.A_NORMAL
    highlight_attr = curses.A_REVERSE

    def __init__(self, width, MIN_SPEED, MAX_SPEED):
        self.x = 0
        self.y = 0
        self.speed = 1
        self.char = ' '
        self.reset(width, MIN_SPEED, MAX_SPEED)

    def reset(self, width, MIN_SPEED, MAX_SPEED):
        self.char = random.choice(FallingChar.matrixchr).encode(encoding)
        self.x = random.randint(1, width - 1)
        self.y = 0
        self.speed = random.randint(MIN_SPEED, MAX_SPEED)

    def tick(self, scr):
        height, width = scr.getmaxyx()
        self.out_of_screen_reset(width, height)
        scr.addstr(self.y, self.x, self.char, curses.color_pair(1))
        self.char = random.choice(FallingChar.matrixchr).encode(encoding)
        self.y += 1
        if not self.out_of_screen_reset(width, height):
            scr.addstr(self.y, self.x, self.char, curses.A_REVERSE)

    def out_of_screen_reset(self, width, height):
        if self.x > width-2:
            self.reset(width, MIN_SPEED, MAX_SPEED)
            return True
        if self.y > height-2:
            self.reset(width, MIN_SPEED, MAX_SPEED)
            return True
        return False

def rand():
    a = 1
    while True:
        a ^= (a << 1);
        a ^= (a >> 135);
        a ^= (a << 104);
        yield a

r = rand()
def randint(_min, _max):
    n = r.next()
    return (n % (_max - _min)) + _min

def color():
    curses.start_color()
    curses.init_pair(1, MATRIX_COLOR_WORD, MATRIX_COLOR_BACK)
    curses.curs_set(0)
    curses.noecho()

def main():
    scr = curses.initscr()
    scr.nodelay(1)
    color()

    height, width = scr.getmaxyx()
    lines = []
    for i in range(8):
        l = FallingChar(width, MIN_SPEED, MAX_SPEED)
        lines.append(l)

    scr.refresh()
    while True:
        for line in lines:
            line.tick(scr)
        for i in range(30):
            x = randint(0, width-1)
            y = randint(0, height-1)
            scr.addstr(y, x, ' ')

        scr.refresh()
        time.sleep(0.04)

def last_main():
    curses.endwin()
    curses.curs_set(1)
    curses.reset_shell_mode()
    curses.echo()

    screen = curses.initscr()
    color()
    lastchr = string.join(list(MATRIX_THANKS_CHARS), " ")

    last_height, last_width = screen.getmaxyx()
    start_width = last_width/2 - len(lastchr)/2
    for i in range(last_height/2):
        screen.clear()
        screen.addstr(i, start_width, lastchr, curses.color_pair(1))
        screen.refresh()
        time.sleep(0.2)
    screen.getch()
    curses.endwin()

try:
    main()
except KeyboardInterrupt:
    try:
        last_main()
    except KeyboardInterrupt:
        curses.endwin()
        curses.curs_set(1)
        curses.reset_shell_mode()
        curses.echo()
```

## テストで走らせてみよう

```bash
$ python /tmp/matrix_curses.py
```

<br><br><br>
<b>よし。</b>


## ④上司のターミナルにマトリックスを走らせよう！

```bash
$ w
````

私の上司、takuが/dev/pts/○○　だと言う事が判明。
すかさずコマンド投入。

```bash
$ python /tmp/matrix_curses.py > /dev/pts/○○
```
<br><br><br><br>
![mymovie1.gif](https://qiita-image-store.s3.amazonaws.com/0/29405/518bf94c-e0dd-f5dd-f8e8-33bd37636246.gif)
<br><br>
次に、自分が満足したり、上司の怒りがマックスになったところで、ctrl + cを押す。
<br><br>
![mymovie2.gif](https://qiita-image-store.s3.amazonaws.com/0/29405/24d6d4a5-c2dd-5092-beba-1650339ebfe8.gif)

<br><br>THANKS DEAR TAKU!!!<br><br>

###<b> 達成。本当にありがとうございました。</b>

## ⑤最後に
障害時など、大事なときはダメ。絶対。
決して怒らない、ユーモアたっぷりで温厚で優しい上司にやってね！