IchigoJam のフォントの白マスの数の分布を調べる

[IchigoJam](https://ichigojam.net/) のフォントのそれぞれの文字は、8×8ピクセルである。
これら64ピクセルの中に、白いピクセルがいくつあるかをそれぞれの文字について調べ、その分布を見てみた。

今回は、IchigoJam BASIC 1.4.3 (IchigoJam Q) で調査を行った。

※IchigoJamはjig.jpの登録商標です。

# 同じ白マス数の文字が最大何個あるかを調べる

まず、同じ白マス数の文字が最大何個あるのかを調べてみた。

## プログラム

```
10 CLV
20 FOR I=0 TO #FF
30 C=0
40 FOR J=0 TO 7
50 P=PEEK(8*I+J)
60 FOR K=0 TO 7
70 C=C+(P&1):P=P>>1
80 NEXT
90 NEXT
100 [C]=[C]+1
110 NEXT
120 M=0
120 FOR I=0 TO 64
130 IF M<[I] THEN M=[I]
140 NEXT
150 PRINT M
```

## 実行結果

実行には約1分かかり、最大数は「29」であるという結果が得られた。

## 解説

`I` のループが、それぞれの文字に対応する。
`J` のループが、それぞれの文字のフォントの行に対応する。
`K` のループが、フォントの行内の各ビットに対応する。

`J` と `K` のループを用いてフォント内の白いピクセル (`1` のビット) の数を数え、変数 `C` に格納する。
カウントが完了したら、配列のこの `C` を添字とする要素に1を足す。

最後に、配列を走査し、最大値を取得する。

# 白マス数ごとの文字数のヒストグラムを作る

同じ白マス数を持つ文字の数の最大は「29」であることがわかった。
ということは、横32文字の画面で、左の3文字を白マス数に割り当てることでちょうどよくヒストグラムが作成できそうである。
これを行うプログラムを作成した。

## プログラム

```
10 ' ﾊﾟﾀｰﾝ ﾌﾞﾝﾌﾟ ﾋｽﾄｸﾞﾗﾑ
20 CLV:FOR I=0 TO 64 STEP 22
30 M=I+21:If 64<M THEN M=64
40 CLS:FOR J=I TO M
50 PRINT DEC$(J,2)
60 NEXT
70 FOR C=0 TO #FF
80 N=0
90 FOR J=0 TO 7
100 D=PEEK(8*C+J)
110 FOR K=0 TO 7
120 N=N+(D&1):D=D>>1
130 NEXT:NEXT
140 IF I<=N AND N<=M THEN POKE#900+(N-I)*32+[N]+3,C*(N<>0)+#30*(N=0):[N]=[N]+1
150 NEXT
160 IF INKEY()=0 THEN GOTO 160
170 NEXT
```

## 実行結果

以下のヒストグラムが得られた。

![白マス分布ヒストグラム 1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/81e1fb04-7962-06e0-205d-749ce32061ac.png)
![白マス分布ヒストグラム 2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/00d56c39-0b0f-bcb5-c96a-f5e59a5b7c4e.png)
![白マス分布ヒストグラム 3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/de2aee64-2608-fdad-bbe4-02e4189cd61e.png)

## 解説

白マスの数は0～64の65種類考えられるので、これを縦24マスの画面に表示するため、3ページに分割する。
それぞれのページにおいて、前のプログラムと同様の処理により各文字の白マスの数をカウントし、カウント結果がページ内の数であった場合はその文字を画面に出力する。
制御文字も出力できるよう、出力は `PRINT` ではなくVRAMへの `POKE` により行う。
出力する位置は、配列を用いて管理する。
白マス数が0の文字は出力しても見えないので、かわりに `0` を出力する。

# 白マス数ごとの文字数のランキングを作る

続いて、白マス数を文字数の降順に並べ、ランキングを作ってみた。

## プログラム

```
10 ' ﾊﾟﾀｰﾝ ﾌﾞﾝﾌﾟ ﾗﾝｷﾝｸﾞ
20 FOR I=0 TO 64
30 [I]=I
40 NEXT
50 FOR C=0 TO #FF
60 N=0
70 FOR I=0 TO 7
80 D=PEEK(8*C+I)
90 FOR J=0 TO 7
100 N=N+(D&1):D=D>>1
110 NEXT:NEXT
120 [N]=[N]+#80
130 NEXT
140 FOR I=63 TO 0 STEP -1
150 FOR J=0 TO I
160 A=[J]>>7:B=[J+1]>>7:C=[J]&#7F:D=[J]&#7F
170 IF A<B OR (A=B AND C>D) THEN T=[J]:[J]=[J+1]:[J+1]=T
180 NEXT:NEXT
190 FOR I=0 TO 64 STEP 22
200 M=I+21:If 64<M THEN M=64
210 [70]=0:FOR J=I+1 TO M+1
220 [70+J-I]=[70+J-I-1]+(([J-1]>>7)+21)/22+(([J-1]>>7)=0)
230 NEXT
240 CLS:FOR J=I TO M
250 LOCATE 0,[70+J-I]:PRINT DEC$(J+1,2);DEC$([J]&#7F,3);DEC$([J]>>7,4);
260 NEXT
270 LOCATE 0,[70+M+1-I]
280 FOR C=0 TO #FF
290 N=0
300 FOR J=0 TO 7
310 D=PEEK(8*C+J)
320 FOR K=0 TO 7
330 N=N+(D&1):D=D>>1
340 NEXT:NEXT
350 FOR J=I TO M
360 IF N=[J]&#7F THEN Y=[70+J-I]&#3F:X=[70+J-I]>>6:POKE#900+(Y+X/22)*32+X%22+10,C:[70+J-I]=[70+J-I]+#40
370 NEXT
380 NEXT
390 IF INKEY()=0 THEN GOTO 390
400 NEXT
```

## 実行結果

最初のページが表示され始めるまで (すなわち、白マス数カウントとソートの処理) に約1分半かかり、各ページの表示にそれぞれ約2分かかった。

表示されている数字は左から、順位、白マス数、文字数である。
今回は、文字数を数字で表示するので、白マス数0であってもかわりに別の文字を表示する処理は行わない。

![白マス分布ランキング 1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/1127d80b-93e8-c5e5-6051-c334c3fe35c5.png)
![白マス分布ランキング 2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/3ff6e87e-7289-9c46-08dd-1626fd7a285d.png)
![白マス分布ランキング 3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/81a5d9ad-3313-0865-717d-76fbf5bb9e58.png)

半分近くの白マス数について、該当する文字が無いことがわかった。

## 解説

まず、配列の添字0～64の下位7ビットを使い、それぞれに対応する白マス数を記録する。(20～40行目)
次に、前のプログラムと同様の白マス数カウントを行い、配列の上位8ビット (符号ビットを除く) を用いて白マス数ごとの文字数を記録する。(50～130行目)

カウントが完了したら、配列のソート (バブルソート) を行う。(140～180行目)
文字数が違う場合は文字数の降順で並べ、文字数が同じ場合は白マス数の昇順で並べる。

そして、再び3ページにわけてランキングの表示を行う。(190～400行目)
今回は文字を表示するエリアの幅が29文字より狭いので、表示を複数行にまたがらせる処理が要求される。
そこで、最初に表示に用いる行数を求め、各要素を表示する位置を求める。(210～230行目)
表示に用いる要素は基本的に文字数をエリア幅の22で割った商(端数切り上げ)だが、文字数が0のときも行数は0ではなく1とする。
数値情報を出力 (240～270行目) した後、再びカウントを行い、ページ内の白マス数に該当する文字を画面に表示する。
表示を開始する行の情報が配列の各要素の下位6ビットに格納されているので、残りのビットを用いて何文字表示したかを管理する。
最後に、次のページの表示に移る前にキー入力を待機する。(390行目)

# ライセンス

今回のプログラムは、[CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/deed.ja) でライセンスする。

# キャラクターコード一覧と実機のフォントの違い

今回のプログラムを [OneFiveCrowd](https://onefivecrowd.mikecat.work/) で実行すると、実機と実行結果が違っていた。
詳しく見たところ、OneFiveCrowdのフォントは
[IchigoJam キャラクターコード一覧 - イチゴジャム レシピ](https://15jamrecipe.jimdofree.com/%E3%83%84%E3%83%BC%E3%83%AB/%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E4%B8%80%E8%A6%A7/)
に基づいており、これに実機のフォントとの違いがあることがわかった。
具体的には、今回の実機 (1.4.3) と「IchigoJam キャラクター一覧 1.4 20200424.pdf」の間には、少なくとも以下の違いがある。

* 「ウ」(`#B3`) および「ワ」(`#DC`) の左側で下がっている部分が、実機では2ドットであるが、キャラクターコード一覧では1ドットである。
* 手を降ろしている人 (`#F9`) において、実機では胴体と足が8近傍でのみ繋がっているが、キャラクターコード一覧では胴体が1ドット伸びて4近傍で繋がっている。

以下のプログラムで、これらの文字を出力する。

```
10 VIDEO 7
20 LOCATE 0,1
30 PRINT "ｳﾜ";CHR$(#F9)
40 IF INKEY()=0 THEN GOTO 40
```

以下が実機での出力結果である。

![違いを発見した文字 (実機)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/193ff15f-f4e5-bc65-7ede-b788f0607042.png)

以下がOneFiveCrowdでの出力結果である。

![違いを発見した文字 (OneFiveCrowd)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/416e6968-3d43-ef84-1f62-58318e772232.png)

これらの違いは白マス数の違いに繋がったため発見できたが、白マス数は変わらない他の違いもある可能性が考えられる。
また、キャラクターコード一覧のもととなっているバージョンは不明であり、実機でキャラクターコード一覧のフォントと一致するファームウェアのバージョンが存在する可能性もある。