ORANGE pico で話題の問題の解説をしてみる

例のSNSでこんな問題を見つけた。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">小1向けの全周を求める算数の問題。<br>こんなの小1が解けるの！？<br>普通に難しいと思うんやが。。 <a href="https://t.co/VgsSbUs2lP">pic.twitter.com/VgsSbUs2lP</a></p>&mdash; UltramanTrevia (@ZekeTraz) <a href="https://twitter.com/ZekeTraz/status/1733049605129118010?ref_src=twsrc%5Etfw">December 8, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

面白そうだったので、[ORANGE pico](http://www.picosoft.co.jp/orange/) で解いてみた。
…というよりは、頭の中で解いた後、恣意的にパラメータなどを設定して解説を作成した、という形である。

今回は、この問題に関して以下のことを仮定した。

* 全ての線は同一平面上にある。
* 全ての角は直角である。
* 線は角以外で曲がっていない(直線の一部である)。
* 「14mm」の右側の引き出し線が角とずれているが、これは無視してこのセグメント全体の長さが 14mm であると解釈する。

# プログラム

```
10 white=rgb(255,255,255):divnum=20:sikix=20:sikiy=170:sikic=white:kotaex=220:kotaey=170:kotaec=white
20 dim xs(8)=[100,100,155,155,155,100,235,155,174]
30 dim ys(8)=[20,140,70,20,105,20,20,70,105]
40 dim lxs(8)=[0,74,80,80,19,55,0,0,0]
50 dim lys(8)=[120,0,0,0,0,0,50,35,35]
60 dim cs(8)=[white,white,white,white,white,white,white,white,white,white]
70 goto 220
80 cls
90 for j=0 to 8
100 curc=cs(j) & &HFFFF:line xs(j),ys(j),xs(j)+lxs(j),ys(j)+lys(j),curc
110 next
120 curc=cs(0) & &HFFFF:gprint xs(0)-34,ys(0)+lys(0)/2-4,"21mm",curc
130 line xs(0),ys(0),xs(0)-10,ys(0)+lys(0)/2-6,curc
140 cury=ys(0)+lys(0)/2+6:line xs(0)+lxs(0),ys(0)+lys(0),xs(0)-10,cury,curc
150 curc=cs(1) & &HFFFF:gprint xs(1)+lxs(1)/2-16,ys(1)+2,"13mm",curc
160 line xs(1),ys(1),xs(1)+lxs(1)/2-18,ys(1)+6,curc
170 curx=xs(1)+lxs(1)/2+18:line xs(1)+lxs(1),ys(1)+lys(1),curx,ys(1)+6,curc
180 curc=cs(2) & &HFFFF:gprint xs(2)+lxs(2)/2-16,ys(2)-10,"14mm",curc
190 line xs(2),ys(2),xs(2)+lxs(2)/2-18,ys(2)-6,curc
200 curx=xs(2)+lxs(2)/2+18:line xs(2)+lxs(2),ys(2)+lys(2),curx,ys(2)-6,curc
210 return
220 gosub 80
230 ' ｳｴ ﾉ ｺ ﾉ ｼﾞ ｦ ｲﾄﾞｳ ｻｾﾙ
240 if inkey()==0 then goto 240
250 sx2=xs(2):sx3=xs(3):sx6=xs(6):dx2=xs(8)
260 for i=0 to divnum
270 xs(2)=sx2+(dx2-sx2)*i/divnum:xs(3)=sx3+(dx2-sx2)*i/divnum:xs(6)=sx6+(dx2-sx2)*i/divnum
280 gosub 80
290 pause 1000/divnum
300 next
310 ' ﾀﾃ ﾉ ｾﾝ ｦ ｲﾄﾞｳ ｻｾﾙ
320 if inkey()==0 then goto 320
330 sx7=xs(7):sx8=xs(8):dx=xs(6)
340 for i=0 to divnum
350 xs(7)=sx7+(dx-sx7)*i/divnum:xs(8)=sx8+(dx-sx8)*i/divnum
360 gosub 80
370 pause 1000/divnum
380 next
390 ' ﾖｺ ﾉ ｾﾝ ｦ ｲﾄﾞｳ ｻｾﾙ
400 if inkey()==0 then goto 400
410 sy2=ys(2):dy2=ys(1):sy4=ys(4):dy4=ys(5)
420 for i=0 to divnum
430 ys(2)=sy2+(dy2-sy2)*i/divnum:ys(4)=sy4+(dy4-sy4)*i/divnum
440 gosub 80
450 pause 1000/divnum
460 next
470 ' ｺﾀｴ ｦ ﾀﾞｽ
480 if inkey()==0 then goto 480
490 gprint sikix,sikiy,"   21 2+(13+14) 2=96",sikic
500 cpeek &H82B5:gput sikix+8*0,sikiy,8,8000,sikic
510 cpeek &H82AB:gput sikix+8*1,sikiy,8,8000,sikic
520 cpeek &H817E:gput sikix+8*5,sikiy,8,8000,sikic:gput sikix+8*15,sikiy,8,8000,sikic
530 gprint kotaex,kotaey,"    96mm",kotaec
540 cpeek &H82B1:gput kotaex+8*0,kotaey,8,8000,kotaec
550 cpeek &H82BD:gput kotaex+8*1,kotaey,8,8000,kotaec
560 cpeek &H82A6:gput kotaex+8*2,kotaey,8,8000,kotaec
570 line kotaex,kotaey+10,kotaex+8*8,kotaey+10,kotaec
580 if inkey()==0 then goto 580
```

# 操作方法

何かキーを押すと、表示が次の段階に進む。

# 解説

* 10～60行目：描画する図形の配置と色を定義する。
* 80～210行目：現在のパラメータに基づいて図形を描画する。長さの表記は定義の1～3番目に対し決め打ちで行う。
* 230～300行目、310～380行目、390～460行目：それぞれ、キー入力を待機後、移動開始座標と移動終了座標もしくは移動幅を保存し、アニメーションで移動を行う。
* 470～580行目：キー入力を待機後、式と答えを表示する。最後にもう一度キー入力を待機する。

# 実行結果

最初の図形。これの全周を求めることが目的である。

![実行結果 1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d936c01b-f7c7-53b0-fa7d-1132d6d8e917.png)

まず、右上のコの字型の部分を右にずらし、左端の位置が右下の縦線の位置に合うようにする。

![実行結果 2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/62789f61-8e94-a84f-4e10-5a1dafc98a70.png)

縦線を右側にずらし、位置を揃える。

![実行結果 3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/894cfc6f-10ec-2551-4ea9-7ddbe2194b1a.png)

横線をずらし、空いた部分を埋める。
すると、全体が長方形になる。

![実行結果 4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/d4d9714e-e92e-7221-5ebf-316e9ec0174a.png)

最後に、この長方形の縦の長さと横の長さをそれぞれ2倍して合計すれば、全周が求まる。
~~しまった、掛け算は小1だと習ってなくて禁止の可能性が考えられるな…~~

![実行結果 5](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/99c7fed7-e152-3c37-0da9-27431dccfaaf.png)

# 今回新たに発見した罠

## すぐ Formula too cmplex が出る

今回のプログラムの開発中、以下の書き方をすると、以下の Formula too cmplex が出た。
1行の1回の実行で何個も Formula too cmplex が出ることもあった。

```
100 line xs(i),ys(i),xs(i)+lxs(i),ys(i)+lys(i),cs(i) & &HFFFF
Formula too cmplex in 100
Formula too cmplex in 100

140 line xs(0)+lxs(0),ys(0)+lys(0),xs(0)-10,ys(0)+lys(0)/2+6,curc
Formula too cmplex in 140
Formula too cmplex in 140
Formula too cmplex in 140
Formula too cmplex in 140
Formula too cmplex in 140
Formula too cmplex in 140
Formula too cmplex in 140

170 line xs(1)+lxs(1),ys(1)+lys(1),xs(1)+lxs(1)/2+18,ys(1)+6,curc
Formula too cmplex in 170

200 line xs(2)+lxs(2),ys(2)+lys(2),xs(2)+lxs(2)/2+18,ys(2)-6,curc
Formula too cmplex in 200
```

[Out of memory の罠](https://qiita.com/mikecat_mixc/items/c9348725881d8e46c607#%E6%B6%88%E3%81%95%E3%82%8C%E3%81%AA%E3%81%84%E7%A8%8B%E5%BA%A6%E3%81%AE%E5%AD%97%E6%95%B0%E3%81%AE%E8%A1%8C%E3%81%A7%E3%82%82%E6%8B%92%E5%90%A6%E3%81%95%E3%82%8C%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E3%81%82%E3%82%8B)と違って入力時ではなく実行時に出るため、出た位置はわかりやすく、長い式を変数にくくるなど分割することで対処できる。
とはいえ、こんな数個しか項が無く、カッコ(配列アクセスのカッコは除く)も使われていない式でも拒否されるとは驚きである。

## cpoke は gprint には効かない

`cpoke` コマンドを用いると、指定のキャラクターコードに対応するパターンを設定できる。
しかし、この設定は `gprint` コマンドでの描画には反映されないようである。

![cpoke と gprint の実験](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/59cf2475-01e7-d073-ed30-825f3f0419fc.png)

この例では、まず `cpoke &H40,0` で `&H40` (`@`) のパターンを変更している。
次に、`print` を用いてこの文字を出力している。すると、変更後のパターンが出力されている。
そして、`gprint` を用いてこの文字を出力している。すると、**変更前のパターン**のアットマークが出力されている。

公式のコマンド一覧の `cpoke` や `gprint` の説明にはこのように `gprint` では無効ということは書かれていない。
プリンターに文字コードのみを送信するコマンドではパターンの設定が効かないことが予想できるかもしれないが、`gprint` は ORANGE pico 内で完結する処理であり、パターンの設定が反映されることを期待するのは妥当だろう。

## デフォルトのキャラクターパターンがテキスト画面とグラフィック画面で違う

これは上記の罠「cpoke は gprint には効かない」の検証中、さらに発見した罠である。
以下のプログラムで、テキスト画面とグラフィック画面に同じ文字列を描画してみる。

```
10 cls:white=rgb(255,255,255)
20 for y=0 to 15
30 for x=0 to 15
40 c$=chr$(y*16+x)
50 locate x+2,y+4:print c$;
60 gprint 8*(x+22),8*(y+4),c$,white
70 next
80 next
90 if inkey()==0 then goto 90
```

すると、以下の結果が得られた。

![テキスト画面とグラフィック画面のパターンの比較](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/12301381-a9ba-238b-b7d9-2993f7e864e0.png)

特に、&H7F～&H9F、および&HE0～&HE5において、キャラクターパターンの違いがみられる。

## gprint が一部の文字の描画で非直感的な動作をする

上記の罠「デフォルトのキャラクターパターンがテキスト画面とグラフィック画面で違う」の検証中に、さらに罠が発見された。
上記のプログラムに、

```
65 pause 100
```

を追加し、1文字ずつ描画させると、&H80～&H9F、および&HE0～&HFF のエリアにおいて、一度描画された文字が書き換わる様子がみられた。

<iframe width="640" height="480" src="https://www.youtube.com/embed/pc6nYZduB9M?si=xl1bObGMgGw2kl0k" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

そこで、以下のプログラムにより、このエリアの文字を1個ずつ離して描画してみた。
また、文字の描画範囲を示すため、8×8のエリアの外周にそれぞれ正方形を描画している。

```
10 cls:white=rgb(255,255,255)
20 for y=0 to 3
30 for x=0 to 15
40 if y<2 then c$=chr$(&H80+y*16+x) else c$=chr$(&HE0+(y-2)*16+x)
50 px=320*(y+1)/5:py=200*(x+1)/17
60 line px-1,py-1,px+8,py-1,white:line px-1,py-1,px-1,py+8,white
70 line px+8,py+8,px-1,py+8,white:line px+8,py+8,px+8,py-1,white
80 gprint px,py,c$,white
90 next
100 next
110 if inkey()==0 then goto 110
```

すると、以下の結果が得られた。

![gprint の追加実験](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/2bb2690f-f0a9-d1d2-f937-8e54d9e86f7f.png)

一部の文字において、描画した正方形の右側が消えており、1文字しか描画していないにもかかわらず8×8の領域の外側に描画が行われていることがわかる。

しかも、リセットして再び実行すると、以下のまた違う実行結果が得られた。

![orange_pico_gprint_test_2_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/4d4ff973-651f-81e9-f03e-f987d56c3645.png)

はみ出す文字、何がはみ出したか、ともに変わっている。
結果は実行ごとにかわり、安定しないようである。

さらに、以下のプログラムにより、まず白く塗りつぶすことで、8×8の領域からはみ出して描画が行われる領域の調査を試みた。

```
10 cls:white=rgb(255,255,255)
20 for y=0 to 199
30 line 0,y,319,y,white
40 next
50 for y=0 to 3
60 for x=0 to 15
70 if y<2 then c$=chr$(&H80+y*16+x) else c$=chr$(&HE0+(y-2)*16+x)
80 px=320*(y+1)/5:py=200*(x+1)/17
90 gprint px,py,c$,white
100 next
110 next
120 if inkey()==0 then goto 120
```

しかし、結果は以下のようになり、領域外への描画は確認できなかった。

![gprint の追加実験 2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/89f51f2f-ada1-d17b-9d32-3426420c0e09.png)

このように、`gprint` における &H80～&H9F、および&HE0～&HFF の文字の描画の挙動は非直感的であり、罠が多そうである。