ORANGE pico で「大石泉すき」診断のシミュレーション

今日は12月3日。「いずみ」と読むことができ、大石泉の日。

# 大石泉すき (診断)

**大石泉すき** (診断) とは、[診断メーカー](https://shindanmaker.com/)上の診断の一つで、日替わりで「大」「石」「泉」「す」「き」の5文字の中からランダムに5回文字が選ばれ、表示されるものである。

[大石泉すき](https://shindanmaker.com/733464)

自分は、この診断を原則として毎日行い、結果について以下の定義の「ヒット」および「ブロー」の判定を行ってツイート/ポストしている。

* 「ヒット」：ある文字が「大石泉すき」となるためにあるべき位置にある
* 「ブロー」：「ヒット」にならなかった文字で「大石泉すき」を構成するものが、位置にかかわらずある (「ヒット」で構成した文字を除き、構成するために使う数までのみを有効とする)

例として、今日の結果を見てみよう。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">石き泉き石<a href="https://twitter.com/hashtag/%E5%A4%A7%E7%9F%B3%E6%B3%89%E3%81%99%E3%81%8D?src=hash&amp;ref_src=twsrc%5Etfw">#大石泉すき</a> <a href="https://twitter.com/hashtag/shindanmaker?src=hash&amp;ref_src=twsrc%5Etfw">#shindanmaker</a><a href="https://t.co/zVrrjIc4eC">https://t.co/zVrrjIc4eC</a><br>1ヒット2ブロー</p>&mdash; みけCAT (@mikecat_mixc) <a href="https://twitter.com/mikecat_mixc/status/1731040104511385818?ref_src=twsrc%5Etfw">December 2, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

* 石：「大石泉すき」を構成するが、あるべき位置ではないので、ブロー
* き：「大石泉すき」を構成するが、あるべき位置ではないので、ブロー
* 泉：「大石泉すき」を構成するためにあるべき位置にあるので、ヒット
* き：「大石泉すき」を構成する「き」は既にあり、貢献しないので、なんでもない
* 石：「大石泉すき」を構成する「石」は既にあり、貢献しないので、なんでもない

あわせて「1ヒット2ブロー」である。

# 今回の制作物

今回は、[ORANGE pico](http://www.picosoft.co.jp/orange/) でこの「大石泉すき」(診断) をシミュレーションし、ヒット数とブロー数を計算し、統計を取るプログラムを作成した。

## プログラム

```
10 cpeek &H91E5:cpoke &HF0,-1:cpeek &H90CE:cpoke &HF1,-1:cpeek &H90F2:cpoke &HF2,-1:cpeek &H82B7:cpoke &HF3,-1:cpeek &H82AB:cpoke &HF4,-1:cpeek &H82CC:cpoke &HF5,-1:cpeek &H8B74:cpoke &HF6,-1:cpeek &H815F:cpoke &HF7,-1
20 cpeek &H82AA:cpoke &HF8,-1:cpeek &H8F6F:cpoke &HF9,-1:cpeek &H82BD:cpoke &HFA,-1:cpeek &H91B5:cpoke &HFB,-1:cpeek &H82C1:cpoke &HFC,-1
30 dim choice(4):dim exists(4)
40 cls:locate 0,10:print "ﾋｯﾄ\xF7";:locate 2,9:print "\xF7ﾌﾞﾛｰ";
50 for i=0 to 5
60 locate 4+6*i+5,9:print i;:locate 3,11+i:print i;
70 next
80 for h=0 to 5
90 for b=0 to 5
100 work=0:gosub 1000+100*h+10*b
110 locate 4+6*b+5,11+h:print 0;
120 next
130 next
140 locate 0,18:print "ﾄｰﾀﾙ";:locate 4+7,18:print 0;
150 locate 14,18:print "\xF0\xF1\xF2";:locate 18+7,18:print 0;
160 locate 27,18:print "\xF0\xF1\xF2\xF5\xF6";:locate 32+7,18:print 0;
170 locate 0,0:trynum=0:total=0:ooisiizumi=0:gyaku=0
180 k=inkey()
190 if k==&H72 || k==&H52 then goto 40
200 if k==&H73 || k==&H53 then trynum=0
210 if trynum<>0 then goto 300
220 if k==&H31 then trynum=1
230 if k==&H32 then trynum=10
240 if k==&H33 then trynum=100
250 if k==&H34 then trynum=1000
260 if k==&H35 then trynum=10000
270 if k==&H36 then trynum=100000
280 if k==&H37 then trynum=1000000
290 if k==&H30 then trynum=-1
300 if k==&H6F || k==&H4F then trynum=-2
310 if trynum==0 then goto 180
320 if trynum>0 then trynum=trynum-1
330 total=total+1
340 result$="":hit=0:blow=0
350 for i=0 to 4
360 choice(i)=rnd(5)
370 result$=result$+chr$(&HF0+choice(i))
380 if choice(i)==i then hit=hit+1:exists(i)=1 else exists(i)=0
390 next
400 for i=0 to 4
410 if exists(choice(i))==0 then blow=blow+1:exists(choice(i))=1
420 next
430 locate 18,1:print result$;:locate 16,3:print hit;"ﾋｯﾄ ";blow;"ﾌﾞﾛｰ";
440 locate 16,5:if instr(result$,"\xF0\xF1\xF2") then print "｢\xF0\xF1\xF2｣\xF8\xF9\xFA";:ooisiizumi=ooisiizumi+1:locate 18,18:print format$("%8d",ooisiizumi); else print "        ";
450 locate 16,6:if hit==5 then print "\xFB\xFC\xFA!!!!!";:goto 470
460 if instr(result$,"\xF2\xF1\xF0") then print "｢\xF0\xF1\xF2｣\xF5\xF6\xF8\xF9\xFA";:gyaku=gyaku+1:locate 32,18:print format$("%8d",gyaku);  else print "          ";
470 locate 4,18:print format$("%8d",total);
480 gosub 2000+100*hit+10*blow
490 work=work+1
500 gosub 1000+100*hit+10*blow
510 locate 4+6*blow,11+hit:print format$("%6d",work);
520 if trynum==-2 && hit==5 then trynum=0
530 goto 180

1000 h0b0=work:return
1010 h0b1=work:return
1020 h0b2=work:return
1030 h0b3=work:return
1040 h0b4=work:return
1050 h0b5=work:return
1100 h1b0=work:return
1110 h1b1=work:return
1120 h1b2=work:return
1130 h1b3=work:return
1140 h1b4=work:return
1150 h1b5=work:return
1200 h2b0=work:return
1210 h2b1=work:return
1220 h2b2=work:return
1230 h2b3=work:return
1240 h2b4=work:return
1250 h2b5=work:return
1300 h3b0=work:return
1310 h3b1=work:return
1320 h3b2=work:return
1330 h3b3=work:return
1340 h3b4=work:return
1350 h3b5=work:return
1400 h4b0=work:return
1410 h4b1=work:return
1420 h4b2=work:return
1430 h4b3=work:return
1440 h4b4=work:return
1450 h4b5=work:return
1500 h5b0=work:return
1510 h5b1=work:return
1520 h5b2=work:return
1530 h5b3=work:return
1540 h5b4=work:return
1550 h5b5=work:return

2000 work=h0b0:return
2010 work=h0b1:return
2020 work=h0b2:return
2030 work=h0b3:return
2040 work=h0b4:return
2050 work=h0b5:return
2100 work=h1b0:return
2110 work=h1b1:return
2120 work=h1b2:return
2130 work=h1b3:return
2140 work=h1b4:return
2150 work=h1b5:return
2200 work=h2b0:return
2210 work=h2b1:return
2220 work=h2b2:return
2230 work=h2b3:return
2240 work=h2b4:return
2250 work=h2b5:return
2300 work=h3b0:return
2310 work=h3b1:return
2320 work=h3b2:return
2330 work=h3b3:return
2340 work=h3b4:return
2350 work=h3b5:return
2400 work=h4b0:return
2410 work=h4b1:return
2420 work=h4b2:return
2430 work=h4b3:return
2440 work=h4b4:return
2450 work=h4b5:return
2500 work=h5b0:return
2510 work=h5b1:return
2520 work=h5b2:return
2530 work=h5b3:return
2540 work=h5b4:return
2550 work=h5b5:return
```

## 解説

* 10～20行目：使う文字を内蔵フォントから読み込む。
* 30行目：ヒット・ブローの判定に用いる配列を定義する。
* 40～170行目：画面表示と統計を初期化する。
* 180～300行目：キー操作を受け付ける。
* 310～320行目：自動実行の管理を行う。
* 330～530行目：診断のシミュレーションとヒット・ブロー判定を行い、統計を更新する。
* 1000～1550行目：配列のかわりに統計情報をストアする。
* 2000～2550行目：配列のかわりに統計情報をロードする。

配列だと1要素で16ビットしか使えないので、普通の変数を並べて行番号でアクセスできるようにすることで、1要素32ビットの配列を擬似的に実現している。

配列 `choice` に診断結果を格納し、配列 `exists` で診断結果に含まれる文字を管理する。
まずヒットの情報を `exists` に格納する。
続いて、配列 `choice` に格納した診断結果を用い、ブローの判定 (すなわち、今回は目標の「大石泉すき」に同じ文字は1個ずつしか無いので、まだ出ていない文字が出たかの判定) を行う。
さらに、診断結果に「大石泉」およびその逆「泉石大」が含まれるかを判定し、判定結果に応じた画面表示とカウントの更新を行う。

## 操作方法

キーボードを用いて操作を行う。

* `1`：1回診断を行う。
* `2`：10回診断を行う。
* `3`：100回診断を行う。
* `4`：1,000回診断を行う。
* `5`：10,000回診断を行う。
* `6`：100,000回診断を行う。
* `7`：1,000,000回診断を行う。
* `0` (ゼロ)：無限に診断を行う。
* `O` (オー)：「大石泉すき」が出るまで診断を行う。
* `S`：診断の自動実行を止める。
* `R`：診断の自動実行を止め、統計をリセットする。

`S` および `R` 以外の操作は、診断の自動実行中は無効である。

## ライセンス

今回のプログラムは、[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja) でライセンスする。
このプログラム (改造したものを含む) を公開の場で利用する際は、出典を示していただけると嬉しい。
これは、Qiitaの利用規約に基づくプログラムの利用を妨げるものではない。

# 実行結果例

## 初期画面

![初期画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/112c04c5-fc73-b9f1-97e9-bd8ca1acfe91.png)

## 1回診断を行った結果

![1回診断を行った結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/5e56e720-2ed3-3be4-9983-9d9524725a00.png)

## 「大石泉」の逆が出た

![「大石泉」の逆が出た](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/01d8c2e0-a959-291a-fcb6-9202457437ed.png)

## 「大石泉」が出た

![「大石泉」が出た](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/80012986-37af-7583-f11e-f599b641d402.png)

## 「大石泉すき」が揃った

![「大石泉すき」が揃った](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/064b85a7-1adc-6323-a179-858022c30921.png)

## 100,000回診断を行った結果

![100,000回診断を行った結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/8ac25581-1b23-98ca-2c3b-87110e0d39a5.png)

## 動画：10,000回診断を行う

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

# ちなみに

たとえば、診断結果が「大大大大大」のとき1ヒット0ブロー、「すきすきす」のとき0ヒット2ブローとなる。
「大石泉すき」を構成する文字しか出てこないので、0ヒット0ブローはあり得ない。
さらに、文字が1種類のときは必ず正しい位置にもその文字があるので、0ヒット1ブローもあり得ない。
また、5文字しかないので、ヒットの数とブローの数を足して5を超えるのもあり得ない。

# 関連記事

[いつになったら「大石泉すき」は揃うのか？ #Python - Qiita](https://qiita.com/tsuru2_mitsuru/items/2f80e61635e3c51c6bc5) (他の人の記事)