絵でプログラムを書いてみよう
次のような、画像(picntu.png)を書きます。
この画像を解析して実行することができます。上側は、部品置き場です。下側を実行します。
左上から右に進み、下に移っていきます。Pythonと同じく、インデントでブロックを表します。
タートルグラフィックスのように、最初に座標(x=0, y=0)にいて、北を向いています。コマンドで移動したりできます。
サンプルの実行
bash
docker run -it --rm tsutomu7/picntu ./picntu picntu.png
>>>
0 0
0 2
2 2
2 0
部品の説明
- 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。
サンプルの説明
サンプル
def 0 | pri x | pri y
lop 4 | run 0
| lop 2 | go
| right
説明
- 以下をサブルーチン0とする
- x と y を出力
- 以下を4回繰り返す
- サブルーチン0をよぶ
- 2回、前に進む(2歩進む)
- 右を向く
任意の画像の実行
/tmpに画像ファイルを入れれば、次のように実行できます。絵のサイズは変更できますが、部品の位置は変えないでください。
bash
docker run -it --rm -v /tmp:/tmp tsutomu7/picntu ./picntu /tmp/画像ファイル名
第2引数を何かしら指定すると、画像から作成されたpythonを見ることができます。
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
ブラウザで実行
下記を実行して、ファイルを指定して"run"を押してください。
bash
docker run -it -d -p 5000:5000 tsutomu7/picntu
firefox localhost:5000
解析プログラム
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()