Pythonでスタックマシン風の計算機

タイトルそのまま。

import math

global stack
stack = []

numbers = map(str, range(0,10))

operators = ["+", "-", "/", "*"]


def clear_stack():
    for i in range(len(stack)):
        stack.pop()

def concat(st):
    out = ""
    for i in range(0, len(st)):
        tmp = st.pop()
        if tmp in numbers or isinstance(tmp, float) or tmp == "." :
            out = str(tmp) + out
        else:
            stack.append(tmp)
            return float(out)
    return float(out)

def calc():
    r = stack.pop()
    op = stack.pop()
    l = stack.pop()

    if op == "+":
        return l + r
    if op == "-":
        return l - r
    if op == "/":
        return l / r
    if op == "*":
        return l * r

def solve(token):

    if token == ".":
        if stack[len(stack)-1] != "." or len(stack) != 0:
            stack.append(".")

    if token in numbers:
        print "NUM:" + str(token)
        stack.append(token)

    if token in operators:
        if stack[len(stack)-1] in operators:
            stack.pop()
            stack.append(token)
            return

        print "OP:" + str(token)
        stack.append(concat(stack))
        if len(stack) == 3:
            stack.append(calc())
        stack.append(token)

    if token == "=":
        stack.append(concat(stack))
        if len(stack) == 3:
            stack.append(calc())

    if token == "%" and not stack[len(stack)-1] in operators:
        stack.append(concat(stack))
        per = stack.pop()
        stack.append(stack[0] * (per/100))

    if token == "r":
        stack.append(concat(stack))
        tmp = stack.pop()
        stack.append(math.sqrt(tmp))

    if token == "b":
        stack.pop()

    if token == "i":
        stack.append(concat(stack))
        tmp = stack.pop()
        stack.append(1/tmp)

    if token == "c":
        stack.append(concat(stack))
        if not stack[len(stack)-1] in operators: stack.pop()

    if token == "C":
        clear_stack()

    return

if __name__ == "__main__":
    while 1:
        token = raw_input("> ")
        solve(token)
        print stack


bで一文字削除、rで平方根、iで逆数、cはCE、CはC、ほかはだいたい電卓と同じ。
まだ入力ステートの制御ができていないがそこそこ動く。
ざっくり書いたので非効率なところは多そう。

動作

$ python stack_calc.py 
> 1
NUM:1
['1']
> 2
NUM:2
['1', '2']
> =
[12.0]
> 1
NUM:1
[12.0, '1']
> + 
OP:+
[12.01, '+']
> /
[12.01, '/']
> 10
[12.01, '/']
> 1
NUM:1
[12.01, '/', '1']
> 0
NUM:0
[12.01, '/', '1', '0']
> =
[1.201]
> r
[1.0959014554237987]
> +
OP:+
[1.09590145542, '+']
> 8 
NUM:8
[1.09590145542, '+', '8']
> 4
NUM:4
[1.09590145542, '+', '8', '4']
> %
[1.09590145542, '+', 0.9205572225527999]
> =
[2.016458677973]
> +
OP:+
[2.01645867797, '+']
> 1
NUM:1
[2.01645867797, '+', '1']
> .
[2.01645867797, '+', '1', '.']
> 1
NUM:1
[2.01645867797, '+', '1', '.', '1']
> =
[3.11645867797]

Ubuntu 16.10でQuartus入れたらModelSimが動かなかったのでメモ

タイトルのとおり、ModelSimが動かなかったので動くまでを簡潔にメモしておく。

調べた結果、どうも32bitの必要なライブラリを入れて、libfreetypeをダウングレードしてやれば良いようで。
http://packages.ubuntu.com/uk/trusty-updates/i386/libfreetype6/download
から落としてきて

sudo dpkg -i libfreetype6_2.5.2-1ubuntu2.5_i386.deb

しようとしたらamd64とバージョン違うと怒られた。よって
http://packages.ubuntu.com/trusty/amd64/libfreetype6/download
落としてきて

sudo dpkg -i libfreetype6_2.5.2-1ubuntu2.5_amd64.deb
sudo dpkg -i libfreetype6_2.5.2-1ubuntu2.5_i386.deb

vsimにlibpng12-0:i386がいると言われたが

sudo aptitude install libpng12-0:i386

しようとしても「そんなものはない」と言われた。
しょうがないので同様に落としてきて

sudo dpkg -i libpng12-0_1.2.54-6ubuntu1_amd64.deb
sudo dpkg -i libpng12-0_1.2.54-6ubuntu1_i386.deb

でたぶんModelSimが動いた。詳細は思い出し次第書く。

真空管アンプ 自作計画Part1

起こり

何かの拍子で真空管を数本手に入れたので、試しに作ってみることにする。
参考書籍は以下。
www.amazon.co.jp

手持ちの真空管

6AU6, 6X4, 6BL8, 6DJ8, 12BH7A
各一本
調べたところ6DJ8と12BH7Aにはオーディオ用途の使用実績があるようなのでこれらを使ってみる。

回路(候補

上にあげた2本を用いたシングルアンプ(モノラル)
段を直結するかコンデンサを使うかはまだ未定。

不足部品(購入予定品)

電源トランス

春日無線のKmB90F(¥6000程度)

アウトプットトランス

東栄変成器のT-1200(¥1500程度)

抵抗、真空管ソケットなど

占めて¥3000程度?

実機におけるパネポンAI 盤面認識パート

AIの方針

まず動くことを目指す。
そのため連鎖は考えず、3つ消し・4つ消しをひたすら行う。

盤面認識

まず盤面を把握したい。
盤面はゆっくりとせりあがってくるため、パネルを切り出して判定するのは難しい。
よってパターンマッチングで盤面取得を目指す。
f:id:Ord:20160619012016p:plain
このような盤面に対して、各色のチップをテンプレートとしてパターンマッチングを行った。

CHIPS = {"R":1, "S":2, "G":3, "Y":4, "V":5}
board = [[0 for c in xrange(6)] for l in xrange(12)]

rate_comp = 2
temp_x = 35 / rate_comp
temp_y = 35 / rate_comp

threshold = 0.97
img = cv2.resize(clp, (WINDOW_SIZE_X/rate_comp, WINDOW_SIZE_Y/rate_comp), interpolation=cv2.cv.CV_INTER_NN)
for panel in CHIPS.keys():
    p = cv2.imread("panels/"+panel+"_cut.png")
    p = cv2.resize(p, (temp_x, temp_y), interpolation=cv2.cv.CV_INTER_NN)
    matches = cv2.matchTemplate(p, img, cv2.cv.CV_TM_CCORR_NORMED)
    for y in xrange(matches.shape[0]):
        for x in xrange(matches.shape[1]):
            if matches[y][x] > threshold:
                board[y/(CHIP_Y/rate_comp)][x/(CHIP_X/rate_comp)] = CHIPS[panel]
            

判定ののち、再現した盤面は以下のようになった。
f:id:Ord:20160619012020p:plain
うまくいっている。
しかし、場合によっては背景を誤検知することがあるため、要調整である。

問題点

遅い。

cv2.matchTemplate()

に一回当たり0.01秒程度かかっているので、高速化したいところ。
rate_compを適宜いじってうまくやりたいところだが、上げすぎると認識抜けが発生するため調整が難しい。

今後

とりあえずモチベーションのためにも、盤面判定はひとまず置いてAI本体の実装にかかる。

実機におけるボンブリスAI

ボンブリスとは?

Wikipedia参照のこと
ボンブリス - Wikipedia
つまりは爆発するテトリス。そのほかの違いはテトリミノの種類がかなり多くなっていることなど。

今回の目標

実機(SFC)である程度まともなAIを動かす
ボム部分の検知が面倒なのでとりあえずほぼテトリスAIと変わらないが、それは後々

構成

SFC -[USBキャプチャケーブル]-> PC -> Raspberry Pi -> SFC のような形。
キャプチャした映像をOpenCVPythonで解析して、入力信号をラズパイに投げ、分解したSFCコントローラから入力。
github.com


動作手順は以下の通り。

1.画像から盤面とネクストの領域を切り出す

pyautoguiでキャプチャし、numpy.asarray()でOpenCVで使える形に変換

2.ネクストのテトリミノを特定する

GaussianBlurを適用してアナログ取り込みによる誤差の影響を軽減したのち、二値化
取得しておいた以下のような各テトリミノの二値化画像と比較し、テトリミノを特定
f:id:Ord:20160619001724p:plain

3.盤面画像からブロックの有無を解析し、配列へ保存

背景はほぼ同一なので、チップを切り出して閾値と比較し、ブロックの有無を判定
これが
f:id:Ord:20160619002346p:plain
こうなる
f:id:Ord:20160619002035p:plain

4.ネクストミノが降るのを検知

ネクストミノが変わった瞬間を検知すればいいように思えるが、同じテトリミノが連続してネクストに来ることがある。
そのため画面上部にテトリミノが見えたときにもネクスト検知イベントが発火するようにしておく。

5.盤面とミノから最適位置を算出

テトリミノに対して、回転・落下をそれぞれシミュレートし、評価値の最も高い盤面を構成する操作を選択。
盤面をミノの最下点・囲まれたスペースの少なさなどによって評価するが、それぞれにかける重みは試行錯誤中。

6.移動量をRaspberry Piへ送信

テトリミノによって回転させたときの移動量や初期位置が異なるので、それらを考慮して操作量を算出、送出する。

7.Raspberry PiからSFCへ操作信号を入力

GPIOにつながったアナログスイッチ経由でSFCコントローラから信号を入力。
今回使用したICはこれ。
4回路アナログスイッチ SN74HC4066N: 半導体 秋月電子通商 電子部品 ネット通販

動作風景

www.youtube.com
概ね理想に近い動きをしているように見える。
しかし、10手目のT字ミノは明らかにおかしな動きをしている…

今後

ボンブリス要素が全くなかったのでボムを考慮したAIを作りたい
しかし、盤面のボムを判別するのは容易だが、ネクストのどの位置にボムが来ているかを判別するのが難しい。

Project Euler その14

Problem18

By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is 23.

3
7 4
2 4 6
8 5 9 3

That is, 3 + 7 + 4 + 9 = 23.

Find the maximum total from top to bottom of the triangle below:

75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

以下の三角形の頂点からスタートし、下の列の隣接する数字へ移動していくと、上から下までの最も大きな総和は23である。
3
7 4
2 4 6
8 5 9 3
すなわち、3 + 7 + 4 + 9 = 23である。

以下の三角形の上から下までの最大の和を求めよ。
75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

コード

D = 15

T ="75 95 64 17 47 82 18 35 87 10 20 04 82 47 65 19 01 23 75 03 34 88 02 77 73 07 63 67 99 65 04 28 06 16 70 92 41 41 26 56 83 40 80 70 33 41 48 72 33 47 32 37 16 94 29 53 71 44 65 25 43 91 52 97 51 14 70 11 33 28 77 73 17 78 39 68 17 57 91 71 52 38 17 14 91 43 58 50 27 29 48 63 66 04 68 89 53 67 30 73 16 69 87 40 31 04 62 98 27 23 09 70 98 73 93 38 53 60 04 23"
T = map(int, T.split(" "))
N = [0]*len(T)
j = D - 1
k = 0

for i in range(len(T)-D,len(T)):
    N[i] = T[i]

for i in range(0,len(T)-D)[::-1]:
    N[i] = max([N[i+j],N[i+j+1]])+T[i]
    k += 1
    if k == j:
        k = 0
        j -= 1

print N[0]

上からたどると重いので下からたどっていく。
ある要素の左下・右下の数字のうち大きな方をその要素に足し合わせていく。
これを下から繰り返し行えば最大値は容易に求まる。

Project Euler その13

Problem15

Starting in the top left corner of a 2×2 grid, and only being able to move to the right and down, there are exactly 6 routes to the bottom right corner.

How many such routes are there through a 20×20 grid?

2x2の格子の左上の隅からスタートし、左か下にしか進むことはできない。
右下の隅までのルートはきっかり6つ。

では、20x20の格子ではいくつのルートがあるだろうか?

コード

factorial = lambda z: reduce(lambda x,y: x*y, range(1,z+1))
print factorial(40)/(factorial(20)*factorial(20))

何のことはなく40!/(20!*20!)を計算しているだけ。

Problem17

2^15 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.

What is the sum of the digits of the number 2^1000?

2^15 は32768であり、それの各桁の和は3 + 2 + 7 + 6 + 8 = 26となる。

2^1000の各桁の和は幾つになるか?

コード

N = 2**1000
S = 0
for i in str(N):
    S += int(i)

print S

多倍長整数のおかげでそのまんま。