絵によるプログラミング

# <i class='fa fa-book' /> 絵でプログラムを書いてみよう

次のような、画像(picntu.png)を書きます。

![image](https://qiita-image-store.s3.amazonaws.com/0/13955/28397cc5-cdfe-a13e-fb1c-263aa11cc389.png)


この画像を解析して実行することができます。上側は、部品置き場です。下側を実行します。
左上から右に進み、下に移っていきます。Pythonと同じく、インデントでブロックを表します。
タートルグラフィックスのように、最初に座標(x=0, y=0)にいて、北を向いています。コマンドで移動したりできます。

# <i class='fa fa-book' /> サンプルの実行

```bash:bash
docker run -it --rm tsutomu7/picntu ./picntu picntu.png
>>>
0 0
0 2
2 2
2 0
```

# <i class='fa fa-book' /> 部品の説明
- go: 1歩進みます。(最初に(0, 0)にいます。)
- back: 1歩戻ります。
- left: 左を向きます。(最初に北を向いています。)
- right: 右を向きます。
- def 数字: 数字のサブルーチンを定義します。定義の範囲は、インデントで決まります。
- run 数字: 数字のサブルーチンを実行します。
- lop 数字: 数字の分、繰り返します。繰り返しの範囲は、インデントで決まります。
- pri 数字: 数字を出力します。
- if= 数字: 数字がメモリの値と等しいか判定します。範囲は、インデントで決まります。
- if< 数字: 数字がメモリの値を超えるか判定します。
- if> 数字: 数字がメモリの値未満か判定します。
- set 数字: メモリに数字を設定します。
- add 数字: メモリに数字を足します。
- sub 数字: メモリから数字を引きます。
- 数字を空欄にすると、メモリの値を用います。メモリには、1つの数字しか持てません。
- 数字は、0から9までで、10は0に戻ります。
- 数字のかわりに、下記のリテラルも使えます。

## リテラル
- 数字: 数字は、0から9までで、10は0に戻ります。
- i,j: lopのカウンタです。最初のループがiで、2番目のループがjになります。
- x,y: 座標を表します。
- r: 向きを表します。北=0、東=1．南=2、西=3。

# <i class='fa fa-book' /> サンプルの説明

```text:サンプル
def 0 | pri x | pri y
lop 4 | run 0
      | lop 2 | go
      | right
```
説明

- 以下をサブルーチン0とする
  - x と y を出力
- 以下を4回繰り返す
  - サブルーチン0をよぶ
  - 2回、前に進む(2歩進む)
  - 右を向く

# <i class='fa fa-book' /> 任意の画像の実行

/tmpに画像ファイルを入れれば、次のように実行できます。絵のサイズは変更できますが、部品の位置は変えないでください。

```bash:bash
docker run -it --rm -v /tmp:/tmp tsutomu7/picntu ./picntu /tmp/画像ファイル名
```

第2引数を何かしら指定すると、画像から作成されたpythonを見ることができます。

```bash:bash
docker run -it --rm tsutomu7/picntu ./picntu picntu.png 0
>>>
i, j, x, y, r, c = 0, 0, 0, 0, 0, 0
dx, dy = [0, 1, 0, 9], [1, 0, 9, 0]
def f0():
    print("\n%d" % x, end="");print(" %d" % y, end="");
for i in range(4):
    f0();
    for j in range(2):
        globals()["x"], globals()["y"] = (x + dx[r]) % 10, (y + dy[r]) % 10;
    globals()["r"] = (r + 1) % 4;

0 0
0 2
2 2
2 0
```

# <i class='fa fa-book' /> ブラウザで実行

下記を実行して、ファイルを指定して"run"を押してください。

```bash:bash
docker run -it -d -p 5000:5000 tsutomu7/picntu
firefox localhost:5000
```

![image](https://qiita-image-store.s3.amazonaws.com/0/13955/821e9d9a-f6ac-a156-0d03-5995bb03ab5e.png)


# <i class='fa fa-book' /> 解析プログラム

```py3:picntu.py
import cv2, sys, unionfind

if len(sys.argv) <= 1:
    print('%s image_file' % sys.argv[0][:-3])
    exit()
im = cv2.imread(sys.argv[1])
imtop = im[5:13, 4:86] # top of card
r1 = cv2.matchTemplate(im[160:], imtop, cv2.TM_CCOEFF_NORMED)
scom = 'go back lop if_eq set ' \
       'left right else if_lt add ' \
       'def run pri if_gt sub'.split()
slit = '0 1 i 2 3 j 4 5 x 6 7 y 8 9 r'.split()
coms = [] # command list
for y, x in [(i, j) for i, p in enumerate(r1 >= 0.999)
                    for j, q in enumerate(p) if q]:
    imcom = im[y+173:y+197,x+9:x+49]
    r2 = cv2.matchTemplate(im[:150,:460], imcom, cv2.TM_CCOEFF_NORMED)
    _, v, _, (x2, y2) = cv2.minMaxLoc(r2)
    if v < 0.999:
        continue
    i = (x2+33)//92 + (y2+8)//52*5
    arg = 'c'
    if i > 7 or (i+1)%6 > 2:
        r3 = cv2.matchTemplate(im[9:147,465:545], im[y+173:y+193,x+53:x+70],
                               cv2.TM_CCOEFF_NORMED)
        _, v, _, (x3, y3) = cv2.minMaxLoc(r3)
        if v >= 0.999:
            arg = slit[(x3+8)//26 + (y3+7)//25*3]
    coms.append([-1, -1, scom[i], arg, x, y])

n = len(coms)
ux = unionfind.unionfind(n)
uy = unionfind.unionfind(n)
for i in range(n):
    coi = coms[i]
    for j in range(i+1, n):
        coj = coms[j]
        if abs(coi[4] - coj[4]) < 12:
            ux.unite(i, j)
        if abs(coi[5] - coj[5]) < 12:
            uy.unite(i, j)
for i, g in enumerate(sorted(ux.groups(), key=lambda g: coms[g[0]][4])):
    for j in g:
        coms[j][1] = i
for i, g in enumerate(sorted(uy.groups(), key=lambda g: coms[g[0]][5])):
    for j in g:
        coms[j][0] = i

ex = []
ex.append('i, j, x, y, r, c = 0, 0, 0, 0, 0, 0\n')
ex.append('dx, dy = [0, 1, 0, 9], [1, 0, 9, 0]\n')
pr, s, lp = 0, ' ', []
for v, t, com, a, _, _ in sorted(coms):
    if v > pr or s[-1] == ':':
        ex.append('\n' + '    '*t)
    if com == 'go':
        s = 'globals()["x"], globals()["y"] = (x + dx[r]) % 10, (y + dy[r]) % 10;'
    elif com == 'back':
        s = 'globals()["x"], globals()["y"] = (x + dx[(r + 2) % 4]) % 10, (y + dy[(r + 2) % 4]) % 10;'
    elif com == 'left':
        s = 'globals()["r"] = (r + 3) % 4;'
    elif com == 'right':
        s = 'globals()["r"] = (r + 1) % 4;'
    elif com == 'def':
        s = 'def f%s():' % a
    elif com == 'run':
        s = 'f%s();' % a
    elif com == 'lop':
        while lp and lp[-1] >= t:
            lp.pop()
        s = 'for %s in range(%s):' % ('ijklmn'[len(lp)], a)
        lp.append(t)
    elif com == 'else':
        s = 'else:'
    elif com == 'pri':
        s = 'print("%s%%d" %% %s, end="");' % (' ' if v == pr and s[0] == 'p' else '\\n', a)
    elif com == 'if_eq':
        s = 'if c == %s:' % a
    elif com == 'if_lt':
        s = 'if c < %s:' % a
    elif com == 'if_gt':
        s = 'if c > %s:' % a
    elif com == 'set':
        s = 'globals()["c"] = %s;' % a
    elif com == 'add':
        s = 'globals()["c"] += %s;' % a
    elif com == 'sub':
        s = 'globals()["c"] -= %s;' % a
    ex.append(s)
    pr = v
if len(sys.argv) > 2:
    print(''.join(ex))
exec(''.join(ex))
print()
```

参考:
[タートルグラフィックスで亀と戯れる (前編)](http://qiita.com/shiracamus/items/1afee87f24c530f22adf)
[3歳児でもオモチャでプログラミングを学習できる「Cubetto」](http://gigazine.net/news/20160311-cubetto/)