「エントロピーと秩序」のプログラムをpythonで書く～スターリングエンジン

「エントロピーと秩序」のプログラムをpythonで書く第4弾。
今回は「スターリングエンジン」というタイトルがついているbasicのプログラムをpythonで書き直します。

元のプログラムは２ピストン型の[スターリングエンジン](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3)を、BASICのLINE命令を多用して頑張ってアニメーション化しています。

1. 移動後のピストンの位置に黒でLINEを引く
2. 移動前のピストンの位置に白でLINEを引く(前のLINEを消す)

を繰り返してアニメーション化しているのですが、特にピストンが上へ動く時のコードに苦労の跡が見られます。マジックナンバーが多くて理解するのに時間がかかりました・・・

このプログラムで初めてGOSUB(一種の関数)が出てきました。GOSUBでは呼び出し先で何事もなかったように変数(I)を参照できるんですね。

```text
40 FOR I=65 TO 100:
50 GOSUB 1500
60 NEXT I
...
1400 REM AからBへのピストンの動き
1500 LINE(67,I)-(172,)),1:LINE(67,20+I)-(172,20+I),1
...
```

pythonの関数では当然無理なので引数で渡すように書き直しています。

```python
def gosub1500(i):
    """ AからBへのピストンの動き """
    pyxel.line(67, i, 172, i, 0)
    ...
```

今回は[pyxel](https://github.com/kitao/pyxel)というpython向けレトロゲームエンジンを使って書いてみました。
basic同様、一番左上の座標が(0, 0)になるので数字的に移植しやすかったです。
(matplotlibの場合、左下の座標が(0, 0)になる)

```python
import pyxel

LL = 100 # = int(input("何サイクル行いますか"))

pyxel.init(642, 164)
pyxel.cls(7)

def main():
    gosub1000()
    for l in range(LL):
        for i in range(65, 101):
            gosub1500(i)
        for i in range(100, 29, -1):
            k = 130 - i
            gosub2000(i, k)
        for i in range(100, 64, -1):
            gosub2500(i)
        for i in range(30, 66):
            k = 95 - i
            gosub2800(i, k)


def gosub1000():
    """ エンジンの描画 """
    # 左シリンダー
    pyxel.line(53, 120, 53, 20, 8)
    pyxel.line(53, 20, 186, 20, 8)
    pyxel.line(186, 30, 186, 120, 8)

    # 蓄熱装置
    pyxel.line(186, 20, 229, 20, 10)
    pyxel.line(229, 20, 229, 5, 10)
    pyxel.line(229, 5, 413, 5, 10)
    pyxel.line(413, 5, 413, 20, 10)
    pyxel.line(413, 20, 456, 20, 10)
    pyxel.line(186, 30, 229, 30, 10)
    pyxel.line(229, 30, 229, 45, 10)
    pyxel.line(229, 45, 413, 45, 10)
    pyxel.line(413, 45, 413, 30, 10)
    pyxel.line(413, 30, 456, 30, 10)

    # 右
    pyxel.line(456, 20, 589, 20, 12)
    pyxel.line(589, 20, 589, 120, 12)
    pyxel.line(456, 30, 456, 120, 12)

    # ピストン
    pyxel.rectb(67, 65, 106, 20, 0)
    pyxel.line(122, 85, 122, 159, 0)
    pyxel.line(521, 50, 521, 159, 0)
    pyxel.rectb(470, 30, 107, 20, 0)
    
    # 蓄熱室
    for i in range(8, 44):
        pyxel.line(252, i, 390, i, 12)

    pyxel.rectb(285, 120, 73, 38, 0)


def gosub1500(i):
    """ AからBへのピストンの動き """
    pyxel.line(67, i, 172, i, 0)
    pyxel.line(67, i + 20, 172, i + 20, 0)
    pyxel.line(67, i - 1, 172, i - 1, 7)
    pyxel.line(68, i + 19, 171, i + 19, 7)
    pyxel.pset(287 + 2 * (i - 65), 120, 10)
    pyxel.pset(285 + 2 * (i - 65), 120, 0)
    pyxel.flip()


def gosub2000(i, k):
    """ BからCへのピストンの動き """
    pyxel.line(67, i, 172, i, 0)
    pyxel.line(67, i + 20, 172, i + 20, 0)
    pyxel.line(470, k, 576, k, 0)
    pyxel.line(470, k + 20, 576, k + 20, 0)
    pyxel.line(67, i + 21, 121, i + 21, 7)
    pyxel.line(123, i + 21, 172, i + 21, 7)
    pyxel.line(68, i + 1, 171, i + 1, 7)
    pyxel.line(123, i + 21, 172, i + 21, 7)
    pyxel.line(468, k - 1, 577, k - 1, 7)
    pyxel.line(471, k + 19, 575, k + 19, 7)
    pyxel.line(252, 43 - (100 -i) // 2, 390, 43 - (100 - i) // 2, 8)
    pyxel.pset(357, 122 + (100 - i) // 2, 10)
    pyxel.pset(357, 120 + (100 - i) // 2, 0)
    pyxel.flip()


def gosub2500(i):
    """ CからDへのピストンの動き """
    pyxel.line(470, i, 576, i, 0)
    pyxel.line(470, i + 20, 576, i + 20, 0)
    pyxel.line(471, i + 1, 575, i + 1, 7)
    pyxel.line(470, i + 21, 520, i + 21, 7)
    pyxel.line(522, i + 21, 576, i + 21, 7)
    pyxel.pset(285 + 2 * (i - 65), 157, 10)
    pyxel.pset(287 + 2 * (i - 65), 157, 0)
    pyxel.flip()


def gosub2800(i, k):
    """ DからAへのピストンの動き """
    pyxel.line(67, i, 172, i, 0)
    pyxel.line(67, i + 20, 172, i + 20, 0)
    pyxel.line(470, k, 576, k, 0)
    pyxel.line(470, k + 20, 576, k + 20, 0)
    pyxel.line(67, i - 1, 172, i - 1, 7)
    pyxel.line(68, i + 19, 171, i + 19, 7)
    pyxel.line(471, k + 1, 575, k + 1, 7)
    pyxel.line(470, k + 21, 520, k + 21, 7)
    pyxel.line(522, k + 21, 576, k + 21, 7)
    pyxel.line(252, i - 22, 390, i - 22, 12)
    pyxel.pset(285, 185 - i, 10)
    pyxel.pset(285, 186 - i, 0)
    pyxel.flip()


if __name__ == '__main__':
    main()
```

正解の動きを見てないのであってるか不安です。

![engine.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2109677/c791ffb4-ba86-5ef2-c741-0ac455da2e84.jpeg)