2022年9月21日水曜日

備忘録的に残してみる。

なんとなくだけど、ロシアの行動予測をメモしてみる。


(1) 2022/09/23に占領地で住民投票を行い、ロシアに併合する。

 同時に特別軍事作戦は終了。

(2)併合したヘルソン、サボリージャ、ドネツク、ルハンスク、クリミアへのウクライナの侵攻に対して、防衛戦闘を宣言。同時に国家総動員を行う。

(3)ロシア領土に侵攻してきたウクライナに対して、キーフの政治中枢へのタクティカルニューク攻撃

(4)ここらで手打ち


なんだけどね、(3)やっちゃうとそれで済むのか問題が発生するとか、強制的に併合してもいまでもゲリラ(パルチザン)いるのに治安を維持できるのかが不安。

 でも、現状からみたら、ウクライナ政府を崩壊させて、強制的に侵略した地域を併合する以外に手段はないのよなぁ。

 (3)のパンチが撃てないとすると、総動員しようがなにしようがじりじり押し戻されていく可能性が高いので、やっぱりこれだよなぁ。

 あぁ、どうなっちゃうんだかね。


2020年2月17日月曜日

AOSもたいがい壊れゲーでござる

シグマーさんの弓兵が壊れ気味だな。100ポイント足らずで修正こみ4+ヒットを4発。当たったらD6に増える。3ヒットもして出目が良いとダメージロールした後で2桁になるので(ゲーム中1,2回はなる)1ユニットまるまる持ってかれます。これを入れられるだけ入れたら(例えば1000ポイントで4,5体)もうゲームにならないな。ちなみにアーマーセーブ4+が最強くらいのこの世界で貫通ー2なのよねん。
 ちょっとでも見えてれば撃てるとか、カバーなんてほぼないとかっていう基本システムの上で、こんだけ数が出る武器を使わせちゃダメでしょうなぁ。だいたいが2D6ヒットって7ヒットだとしても通常射撃で出そうとすれば3+で10個以上ダイス振るわけだし、ましてや上振れした時の4D6で15ヒット以上とかもはや笑うしかない。せめて連射したときにはD6はやめていただきたいものでございまする。

 20体編成のユニットが10体射撃で死んでバトルショックでBrave7くらいあっても追加で5,6体持ってかれます(というか持っていかれた)。100ポイントの弓ひとつに。もう笑うしかないわな。

 さらに悪いことに、AoSの世界では接敵されていても射撃ができちゃうんですよね。接敵ロックすら通用しないのはさすがにシステムの問題といわざるをえない。ま、自分も同じの用意してやり返せばいいんでしょうけどね。遠距離修正とかもないしなぁ。本当にクソ。

2020年2月13日木曜日

何年ぶりの40Kプレイと感想

本当に久しぶりに40Kのゲームをプレイ。版が2回変わってますなぁ。
タウ星人が強いとか言われてるのでどんなものかと思ってました。とりあえずやってみた感想を。

・確かに射撃無双でした。
 - 貫通値の扱い
  昔の版では貫通値がターゲットのアーマー以下であれば問答無用でセーブできない代わりに、貫通値がアーマーを超えていればアーマーの値でアーマーセーブ。今の版では、アーマー値から貫通値を引いて(正確には足すだけど)セーブ値が悪くなる。さらに遮蔽(カバー)によるセーブが情景モデル内にユニットの兵すべてが入っていなければならず、アーマーセーブに+1の修正となったので、貫通値があるととにかくセーブ確率が落ちるので、アーマー3とかのモデルは結構しんどいことになってます。アーマーがもともと5とか6ならアーマーセーブ自体があまりできないのである意味変わらないけど。

 - カバーセーブ自体がなくなった
 遮蔽(カバー)があればとりあえず4+、5+でセーブで来ていたのが、上記のようにカバーの条件が厳しくなったうえにアーマーセーブの修正に変わりました。紙装甲のモデルには非常に厳しい世界になってます。

 ので、貫通値が高く、ダメージ値の多い武器はかなり活躍します。むかしなら、カバーセーブ前提で強力な武器でも結構外れてましたが、今のルールだと刺さりますぜー

・白兵戦は前とあまり変わらない印象
 接(WS)の差でヒットロールの目標値が変わっていたのですが、今は自分のWSで一方的にヒットします。ので、WS4クラスの兵のヒット値が良くなっている印象です。

で、アデプタソロリタス(シスターオブバトル)はどうなったかというと・・・

・バトルシスターは最少人数でヘビーボルターとふれーまーでも持たせてテレインに引きこもるのが吉。昔からですが攻(S)が低いので白兵戦ではほぼ無力です。また攻撃回数も昔のようにピストル持ってれば+1とはなりません。

・セラフィムも微妙になりました。
 ピストル持ちで白兵戦での攻撃回数がそれなりにあったのになんと1回になってしまいました。そして最大射程が12mvで貫通値のある武器がほとんどないとなると目標確保以外での使い方が思いつきません。残念です。

・レペンティア、アルコフラジェントは白兵戦できればかなり強い
 持ってる武器に貫通値があるので結構ささりますし攻撃回数も多い。ライノにでものせて2ターン目の白兵戦を狙うのはありじゃないかしらねぇ。とにかくバトルシスター系もセラフィム系も突っ込むのには苦しすぎます。

・ペニテントエンジンやモーティファイアも突っ込ませるといいぞ
 まぁまぁタフです。そして殴るとヤバい性能。殴りマシン♪

・リトリビューターはマルチメルタに策略で射程36mvかな?
 バトルシスターがヘビーボルター撃ってくれるなら、こいつの役割はもうこれしかない。ちょっと射程短いけど歩いてもヒット値下がらないし策略もある。もちろん、エクソシストでも行けると思うよん。

・カノネス、ユーニスなどのHQは他ユニットの支援がいいな。
 地味にスペシャルセーブ上げられたりヒットロールの出目1振り直しとかを周りのユニットに振りまくバフ担当。Sが低いので基本攻撃には向かないけど、強力な装備もあるので、ワンポイント殴ったり撃ったりもできるっちゃできる。セレスティンはどうなのかなぁ?

とまぁ、新時代の40Kではいろいろ修正が必要みたいです。まぁ、でもなんとか戦える気がしないでもない感じかなぁ。
 

2019年10月20日日曜日

MacBook Air 11 mid-2011の再生

発掘されたMacBook Airが使えるかいろいろ試してみた。
Core i5 1.6Ghz/Mem4GB/SSD256GB
なのでメモリがちょっと足りない感じですね。

(1)OSの更新
 El Capitanが入ってました。調べるとHigh Sierraまで行けるようなので、OSを上げてみた。まぁ、時間はかかったけどなんとかなったね。

(2)バッテリーが死んでる
 バッテリーを交換してください!となっているので、Amazonで六千円くらいで調達してみた。交換自体は15分くらいで終了。そしてバッテリーでの動作確認は完了!

したのだけど。

(3)バッテリーに充電できない!!!
 ということで、いろいろリセットも試みましたが改善せず、古いMegaSageアダプターも怪しいので、これも互換品をポチってみた。

で、ここまで(2019/10/20)

あとはアダプター換えてみて、それでもだめだと…無理して使う必要もないんですけどねぇ。どうしようかなぁ。

(2019/10/22)アダプター届きました。そして無事に充電できましたよ!!やったね。
あとは、Surface2 PROのほうもポチポチアップデート。

2019年10月4日金曜日

Droneいろいろ

2019年の春くらいにトイドローン(Tello)買ってそこからSpark,Mavic2と増やしてしまった。
値段の差がもろに性能に出る感じが面白いなぁ。
 Telloは屋内か無風に近い状態でないと使えない。カメラの角度調整ができない。そのかわりあまり気にせず使える感じ。
 Sparkは普通に使えるけど、ちょっと音が甲高い。
 そしてMavic2は超性能だなぁ。ノーマルモードでの速度が速い!カメラは超スムーズ。
電池の持ちも良い。別物ですねぇ。
 ただ、普通に使う分にはSparkでも十分なきがするので、つかいわけはいろいろありそうあ

2019年9月15日日曜日

PC-8001到着

8月上旬に発作的にポチッた物が届きました。
昔書いたスタートレックはなんとか復活させたいなぁ。
ちょこちょこと進捗など入れてこうと思う。

2019年9月10日火曜日

VPN関連メモ

MacOSで VPNを設定してみたけど、ルーティングができてないみたい。
なので、route addするわけでござるな。
あとは、dnsの振り分けをやってあげる必要があるなぁ。
 まぁ、ちょろっとやるだけだけど、なんか面倒くさい(笑)

2019年9月7日土曜日

PC環境とクラウド(主にストレージ)

文書やプログラム書くのに色々な環境を試すけど、いまひとつどれもしっくりこないのよね。
 いまは英語の文書を翻訳してPDFに埋め込んだりしてるんだけど、日本語訳を書き溜める場所とソフトがね。Evernote,OneNoteあたりはどうしても癖があっていけない。プレーンテキストにした時には置き場所が色々あって困ってしまう。
 iCloudかOnedriveにしたいところなんだけど使うOSがMac,iOS,WIN10てなもんでロック感が高いのはMacOS,iOSなのかな?自宅ではMacでいいんだけど外ではどうしてもそれ以外になってしまうのがねぇ。結局ホスティングしてる鯖にSCPってのが確実ってどうなんでしょうね。
 WIN,AndroidでiCloud使いやすければそれがいいんだけどなぁ。
あ、外でもMacBookってのはありっちゃアリなんだよねぇ。

2017年1月5日木曜日

[追試]新しいポケモンの出現率(2)




 アップデートで新しいポケモンの出現率があがったと言われています。
本当にそうなのか??追加検証の結果です。
 サンプル数は63。前回は105個ですので60%ですが、傾向は見えるのか?


孵化した数 新しいポケモン ヒット率 卵の出現率
2km 14 4 28.6% 22.2%
5km 41 0 0.0% 65.1%
10km 8 4 50.0% 12.7%
合計 63 8 12.7%
・新しいポケモンの出現率について
前回はヒット率が合計で5.7%。そこから12.7%となって約2倍の出現率という結果でした。
ただし・・・2kmは全部ププリン、5kmは41個でゼロ!!、10kmもエレキッドが3体にムチュールといった具合で、かぶりまくりです Orz。これじゃ出現率上がっても嬉しくないですねぇ。実際、新しく増えた訳ではないです。超がっかり。

・卵の出現率は変わったのか??
 前回の調査より若干ですが2km/10km卵の出現率が上がってますが、これは新しいポケモンの出現率向上の影響のように思います。つまり、あんまり変わってないと思われます。

ということで、いつになったらトゲピーが来るのやら。。。歩きながら操作するにもだいぶ寒くなってきたし、そろそろ冬眠というか休眠しようかと思ってます。
 ま、卵はいわゆるガチャみたいなものだし、卵ではなく孵化装置に課金されても、こんな出現率じゃぁねぇ。
 なんか新しい楽しみが増えないと、いつまでも冬眠しちゃうかもね。

2016年12月26日月曜日

ポケモンGO:卵の検証実験



12月から新しいポケモンが卵から出てくるようになりました。そして、26日からはその出現比率が変更になったようでございます。
 そういう意味ではもう不要かもしれないけど、せっかく実験したので、その結果をレポートしてみます。

孵化した数 新しいポケモン ヒット率 卵の出現率
2km 19 1 5.3% 18.1%
5km 78 2 2.6% 74.3%
10km 8 3 37.5% 7.6%
合計 105 6 5.7%
孵化装置使いまくって、12/14から12/25までの12日間で105個の卵を孵化させました!
で、新しいポケモンは7種類中5種類出ました。
 ププリン、ピチューX2、ブビィ、エレキッド、ムチュール
です。
 いやぁ、シンドイ話でございます。5kmx78ってことは2週間弱で軽く60kmは歩いたってことになりそうですよ。そして、5.7%という出現率にはちょっと呆れちゃいますね。どんだけ出ないんだって話です。孵化装置なしで毎日卵を1つ孵化させたとして、105日で6体!!ってどんだけって話ですわ。ナイアンテックによる孵化装置課金への罠ですが、これじゃ効率悪くないっすか?

 おまけですが、2km/5km/10kmの出現比率も概ね2/7/1って感じみたいですね。もしかすると2/8.5/0.5くらいかもしれません。

そして、26日から卵の出現率が変化したらしいという情報があります。今日拾った卵ですが、2km/5km/10kmで2/5/4という状況です。11個ではサンプルとしては少なすぎますが、10km卵の出現数が大幅にアップしてます!!!
 けどね、返すまでの距離もだいぶ増えちゃうわけなので、それでカイロスなんか出た日にはもうガッカリですわな。

100個孵化させた時点で課金やめてまったりにしようと思ってたのですが、もしかしたらもう少しだけ課金して、せめて50個くらいはサンプルとってみようかな。


2016年12月22日木曜日

4代目Bluetoothイヤホン



今まで使っていたSONYのMDR-EX31BNがだいぶくたびれてきたので、新しい機種に変えることに。
 同じSONYのMDR-1000にしようかとも思ったのだけど、ヘッドホンタイプを電車の中で使うのにはどうしても抵抗感があるのでござる。おっさんだからね。

 実はノイズキャンセルがついてることが必須なので、機種も絞られてきます。

オーディオテクニカのはその前に使ってたんだけど、断線2回やらかしたので、もうちょっと選びたくない。
BOSEのQC30にしようかとも思ったのだけど、売り切れてしばらくは手に入らないみたい。で、JBLのEVEREST elite100ってのを試そうと思いましたのです。

秋葉原の量販店で現物を確認、アンド試聴。音はすごくよい。ノイズキャンセルがちょっと弱い感じだけど、お買い上げ。

で、実際に試してみると、ノイズキャンセルの効果が弱く今持ってるEX31の方が良く効く感じ。で、イヤーピースの交換を試みてみた。
 EX31のと交換するだけでも効果があるので、SONY製のイヤーピースをM/Lでお買い上げ。でもね、なんか収まりが悪い???で、色々調べてCOMPLYのイヤーピースを試してみた。大きさがわからないので最初はLにしてみた。
 これは、昔からある耳栓、潰して耳に入れると形が復元して耳の中でぴったり収まるやつのイヤーピース版。なので、収まりも良く取れにくい。そして遮音性も高い!

 Lだとちょっと大きい感じだったので、Mを試したところいい感じに落ち着きました。

SONYの2種類、COMPLY2種類でなんと8K円くらい消費。馬鹿みたいだけど、これで通勤電車も快適になりそう。



2016年9月13日火曜日

MAC OS vs WINDOWS その2

MacBook(2015)にパラレルズ仕込んでWINDOWS10動かしました。普通にサクサク動いてるよ。これで解決したような気がするです。


 でもって、iPadの新型は発表されなかったので、iPad mini4を狙うか、アンドロイドで行くか思案中。旧式ゲーム機のエミュレータなんかは明らかにアンドロイド。そしてGUIの良さや基本能力の高さはiPadだね。で、7インチの軽さと横幅による使いやすさと、miniとの対比になるね。
 ま、もうしばらく様子見てからかなぁ。iPad Air2もあることだし、無理に乗り換えるのもちょっとね。

2016年9月4日日曜日

やっぱりWINDOWSは・・・

8月くらいからしばらく、メインをWINDOWS10にしてました。
一応マルチウィンドウもできるし、CPU的には2015のMACBOOKより速いかな?って思ってたんだけど、元に戻しちゃいました。てへへ。

 MACのスライドパッドがなんといっても使いやすすぎるのが一番。そしてターミナルも使いやすいしね。

 WINDOWSで使いたいソフトも若干あるので、仮想化でもいれようなかなぁ。できればその時には512GBくらいストレージ領域が欲しいけど。

2016年4月10日日曜日

Prologで書くオセロ:読み切り編

前回からちょっと間が空きました。

実は、中間評価関数を使った簡単な次の一手を出す部分はすぐに作ってしまいました。これの詳細は後日だします。

今日は、とりあえず動いた完全読み切りの部分です。完全読み切りとは、ゲームの終盤ですべての手を探索して、最も石を多く取れる手を求めることです。

 完全読み切りでは、縦型探索のアルファベータ枝刈りというのが一般的ですが、今回は、アルファベータを行わず、単純に最大値を求めるだけにしました。
 ゲームなので、勝てば良いという意味では、勝てる手を見つけた時点で終了するというのもあります。これも今回入れてません。

上に「とりあえず」って書いたように、まだ手を入れたいところはあるのですけど、まずは動作レポートってことで。

 実行性能ですが、下記の一手目で7,8秒かかっている感じです(Core-M3 1.1GHz、ザクッというと12inch MAC BOOKです)。空きが9箇所なのでかなり遅いです。早くするだけなら2、3倍は軽く早くなると思いますが、所詮はこんなもんですよね。Prologだもん。

Game Playing
[teabn,white,[18,28,81,82,26,36,35,25,24,23,45,56,74,75,63,62,53,44,42,76,78,68,67,57,87,86,85,84,83,58,48,47,38],[37,46,73,34,33,21,32,31,41,51,61,64,65,52,54,43,66,16,15,14,13,55]]
   1  2  3  4  5  6  7  8
1  .  X X X X X  . O
2  .  .   X O X O  . O
3 X O X X O O X O
4 X O X O X X O O
5 X O O O X X O O
6 X O O X O X O O
7  .  .   X O O O .   O

8  O O O O O O O .
 [Coms move,72,-43]


こんな感じになります。白の手番で72が最善手、勝敗は-42なので大敗ですね(笑)。

ここで逆らって72ではなくたとえば77に置いたりすると、次の先手番でこういう評価になります。

[teabn,black,[18,28,81,82,26,36,35,25,24,23,45,56,63,62,53,44,42,78,68,87,86,85,84,83,58,48,38],[77,76,75,74,67,57,47,37,46,73,34,33,21,32,31,41,51,61,64,65,52,54,43,66,16,15,14,13,55]]
    1  2  3 4  5  6  7  8
1  .  X X X X X .  O
2  .    . X O X O .  O
3 X O X X O O X O
4 X O X O X X X O
5 X O O O X X X O
6 X O O X O X X O
7  .   . X X X X X O

8 O O O O O O O  . 
 [Coms move,88,44]


88を取ってさらに1個多く取れるようです。

 プログラムですが、読み切り用に書いた部分の概要を以下に示します。

(1)読み切り用のメイン部分
doYomikiri(Teban,Move,Eval,Black,White)
 Teban:今の手番 (入力)
 Move:最善手(戻り値)
 Eval:評価値(石数の差分、戻り値)
 Black:黒石の置き場所リスト(入力)
 White:白石の置き場所リスト(入力)

・ゲーム終了時は石を数えて、勝敗を確認
・パスの時は、相手の手番で読み切りを継続
・置ける場所がある時は、先読みを実行。以下を呼び出す。
  doYomikiri1(Teban,Black,White,MoveList,Move,Eval,CMove,CEval).


(2)読み切りのサブ:候補手を回して最善手を求める(1)
doYomikiri1(Teban,Black,White,MoveList,Move,Eval,CMove,CEval).
 Teban:今の手番 (入力)
 Black:黒石の置き場所リスト(入力)
 White:白石の置き場所リスト(入力)
 MoveList:石の置ける場所リスト(入力)
 Move:最善手(戻り値)
 Eval:評価値(石数の差分、戻り値)
 CMove:今までの最善手(入力)
 CEval:今までの最高評価値(石数の差分、入力)

・置く場所がなくなったら今までの最善手/評価を返します。
・置ける場所があれば、
  - 石を置いて、Black , Whiteを更新(Black1,White1)
        - 相手番で読み切りを行う
   doYomikiri(Teban,Move,Eval,Black,White)
  - 最善評価値は相手の最大値なので、符号を入れ替える
   相手の勝ちは自分の負けですから。。
     - 現状での最善手/評価値を更新して、MoveListの残りを試す
   doYomikiri2(Teban,Black,White,Rest,Move,Eval,CMove,CEval,SEval,AMove)

(3)読み切りのサブ:最善手/評価値を入れ替えるため
doYomikiri2(Teban,Black,White,Rest,MoveList,Eval,CMove,CEval,SEval,AMove):-
Teban:今の手番 (入力)
 Black:黒石の置き場所リスト(入力)
 White:白石の置き場所リスト(入力)
 MoveList:石の置ける場所リスト(入力)
 Move:最善手(戻り値)
 Eval:評価値(石数の差分、戻り値)
 CMove:今までの最善手(入力)
 CEval:今までの最高評価値(石数の差分、入力)
 SEval:今探索した手の評価値(石数の差分、入力)
AMove:今探索した手(入力)

・今までの最高評価値(Ceval)と探索した手の評価値(SEval)を比較して、入れ替えて
doYomikiri1(Teban,Black,White,MoveList,Move,Eval,CMove,CEval)
 を呼び出します。

   こういうのも、  () -> ; 的な処理で書いてもいいのですけど、個人的にはこうやって分けちゃうことが多いです。なんとなくPrologでif分的な処理を内部に入れたくないという思いだけなんですけどね。あはは。

 あと、ここで、SEvalが1より大きければ探索を終了するって書き方ができます。とりあえず勝つ手を返すので、処理が(かなり)早くなります。

で、アルファベータにするには、doYomikiri(Teban,Move,Eval,Black,White)でアルファ値とベータ値も入力にして引き回すことになります。ただし、述語の本体でif分書かないといけなくなりますので、今回やったように述語を分けて処理するとか、if分的構文を気分悪いけど突っ込むのか。いやはや、こういうのって苦手ですよね,Prologは。

 もちろんforall的に手と評価値のペア求めて最大値探してもいいんですが(つーかProlog的には綺麗なんだと思う)、それじゃ枝刈りできないですからねぇ(笑)。
 あぁ、やっぱりこういう枝刈りにはなんか向いてない気がします。

時間があったら、下のコードも少し直していきます。行き当たりばったりなので、ちょっと綺麗じゃないです。

中間評価関数は、この機構をそのまま使って、石の数数えるところで評価関数を呼べば、そのまま完成です。オセロの中間評価関数については、何十年も前からいろいろあります。
おまけだけど書いておきます。

・基本は、相手の着手数(盤上で置ける場所)を最小、自分の着手数最大になるようなところを選択していきます。

 相手の選択肢を限定することは、相手が置きたくない場所(次にカドや辺を取られる)に置かせるという意味や、カドを取って辺を伸ばしていくと相手にひっくり返されない場所が増える(相手の着手数が減ります)、さらに自分の選択肢も増えていくという意味があります。

・カドの取り合いには行くつか定石的な手順があるので、そういうのを織り込んでいくこともあります。

 これは、文章だと書きにくい(笑)、たとえば相手にわざとカドを取らせて、大きく取り返すみたいのがあります。


---8<------8 p="">
countBoard(black,Eval,Black,White):-
    getListSize(Black,BCount),
    getListSize(White,WCount),
    Eval is BCount - WCount.

countBoard(white,Eval,Black,White):-
    getListSize(Black,BCount),
    getListSize(White,WCount),
    Eval is WCount - BCount.

doYomikiri(Teban,0,Eval,Black,White):-
    isGameEnd(Black,White),!,
    countBoard(Teban,Eval,Black,White).

doYomikiri(black,0,Eval,Black,White):-
    isPass(black,Black,White),!,
    doYomikiri(white,_,WEval,Black,White),
    Eval is -1 * WEval.

doYomikiri(white,0,Eval,Black,White):-
    isPass(white,Black,White),!,
    doYomikiri(black,_,BEval,Black,White),
    Eval is -1 * BEval.

doYomikiri(Teban,Move,Eval,Black,White):-
    makePutList(Teban,Black,White,MoveList),
    doYomikiri1(Teban,Black,White,MoveList,Move,Eval,0,-100).

doYomikiri1(Teban,Black,White,[],Move,Eval,Move,Eval).
doYomikiri1(black,Black,White,[AMove|Rest],Move,Eval,CMove,CEval) :-
    doMove(black,AMove,Black,White,Black1,White1),
    doYomikiri(white,_,SEval,Black1,White1),
    SEval1 is -1 * SEval,
    doYomikiri2(black,Black,White,Rest,Move,Eval,CMove,CEval,SEval1,AMove).

doYomikiri1(white,Black,White,[AMove|Rest],Move,Eval,CMove,CEval) :-
    doMove(white,AMove,Black,White,Black1,White1),
    doYomikiri(black,_,SEval,Black1,White1),
    SEval1 is -1 * SEval,
    doYomikiri2(white,Black,White,Rest,Move,Eval,CMove,CEval,SEval1,AMove).

doYomikiri2(Teban,Black,White,Rest,Move,Eval,CMove,CEval,SEval,AMove):-
    CEval < SEval,!,
    doYomikiri1(Teban,Black,White,Rest,Move,Eval,AMove,SEval).
doYomikiri2(Teban,Black,White,Rest,Move,Eval,CMove,CEval,SEval,AMove):-

    doYomikiri1(Teban,Black,White,Rest,Move,Eval,CMove,CEval).

2016年3月21日月曜日

Prologで作るオセロ盤


最近はAI(Deep Lerning)のプログラムに囲碁のプロが負けちゃう時代のようです。個人的にはAIって超うさんくさいのであります。囲碁の場合も学習でユニークな評価関数が出来たって話なのか?それとも囲碁特有の解き方が見つかったのかでだいぶ違いがあると思うですよね。AIってなんだろう??難しいテーマですなぁ。

で、AI言語(古い〜)といえばPrologですので、

  作ってみたよ。Prologでオセロ盤。使っているのはswi-prologです。

初期状態から打ち手を入れて、ゲーム終了になるまで繰り返します。

あくまでもお試しなので、UIその他は最小限の実装。「待った」はできません。
(ん?Prologで待ったかぁ。やりにくそうだな。ま、打ち手と盤を指すごとに残せばいいのか。)

徐々に追記しますが、とりあえず「ザクッ」っとつくったソースだけ晒します。効率や見た目はそこまでちゃんと詰めてないのはご容赦を。あと、一部デバッグ用のwriteln()が残ってます。ま、表示が汚れますけど、今は気にしない(笑)。

基本の考え方だけ

・盤情報の持ち方
 座標は11,21,....,78,88という2桁の数字にします。本当はA1,...H8ですが、処理をサボるためにこうなってます。
 黒い駒が置いてある座標のリストをBlack,白い駒が置いてある場所のリストをWhiteとして、引っ張り回します。

 play :- play(black,white,[44,55],[45,54]).
 →手番black,(次の手番white), 黒の座標リスト、白の座標リスト
  を食わせた、初期状態からのプレイはこんな感じです。

 普通だと、8X8の配列にしちゃうところですが、Prologと配列はそんなに相性がよいわけでもなし、インデックスで指せないならこの方がいいんじゃないかと思います。あとは、計算結果を残しておくためのデータベースなんてのは今時必須なのですが、高速に実装する方法を考えないといけない。Cとかなら、ハッシュ使ってO(1)に近い検索ができるけど、2分木じゃO(logn)になっちゃうからなぁ。ハッシュは調べないといかんし、これできないなら、しんどいわな。

・駒の反転 checkput()
 direction(方向リスト)に、ある地点から隣へ移動するために加算する数値をいれてあります。8方向に移動するのは、これを使います。
  direction([-11,-10,-9,-1,1,9,10,11]).
  →順に、[左上、上、右上、左、右、左下、下、右下]との駒の距離が入ってます。
   盤は8X8ですが、数字的には10増減するごとに上下しますので、ここは注意が必要。

 調べたい座標は、board(座標リスト)から引いてきて、
   空いている(Black,Whiteのmemberでない)場所からスタート。上記の[方向リスト]を足しながら、隣の場所を確認
     - 盤からはみ出たらfalseで終了
     - 自分の駒にあたったとき、途中に相手の駒があればtrue.その数と座標リストを返します。
     - 相手の駒に当たった時、ひっくり返す数をインクリメントして、さらに隣をチェックに行きます。
   
割とあっさり出来たような気がします(3時間くらい)。

さて、このプログラムの詳細もありますが、ここから、コンピューターによる打ち手を求めるプログラムを軽く作ってみるのが本題ですねぇ。

 まずは、中間評価関数を作って先読みなしで打つのを実装して、次に先読み。アルファベータ的ななにかを実装できるんか?それと、最後の数マスでは完全読み切りを作ることが重要かな。この辺りは、速度も気にしないといけないんだけど、今回は、このデータ構造でやれるかどうかを確認してみようと思います。

追記1:
・入力のところがなんか気に入らないですよね。こういうのがどうしてもPrologでオセロ作りたくない気分にさせる要因のひとつかなぁ。


#こういうのってCで書いた方が楽な気がしてたんですけど、思ったよりさっくりかけた。
#あとは、すくなくとも、自分より強いプログラムをさっくり記述できたら、Prologの勝ちだな(笑)
#さて、将棋盤とか作れるのかなぁ?より複雑になるので、もっと真面目に考えないといけなさそうです。

実行結果はこんな感じ、コピペしたらフォントの関係かちょっとずれてますね。うひひ。
---8<------8 p="">
16 ?- play.
Game Playing
[teabn,black,[44,55],[45,54]]
1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . . . . .
4 . . . O X . . .
5 . . . X O . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
Enter Place:53
[[54]]
Game Playing
[teabn,white,[53,54,44,55],[45]]
1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . O . . .
4 . . . O O . . .
5 . . . X O . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
Enter Place:63
[[54]]
Game Playing
[teabn,black,[53,44,55],[63,54,45]]
1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . O X . .
4 . . . O X . . .
5 . . . X O . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . .
Enter Place:64
[[54]]
Game Playing
[teabn,white,[64,54,53,44,55],[63,45]]
1 2 3 4 5 6 7 8
1 . . . . . . . .
2 . . . . . . . .
3 . . . . O X . .
4 . . . O O O . .
5 . . . X O . . .
6 . . . . . . . .
7 . . . . . . . .
8 . . . . . . . . 

ここからはソースコード
---8<------8 p="">
board([11,21,31,41,51,61,71,81,12,22,32,42,52,62,72,82,13,23,33,43,53,63,73,83,14,24,34,44,54,64,74,84,
       15,25,35,45,55,65,75,85,16,26,36,46,56,66,76,86,17,27,37,47,57,67,77,87,18,28,38,48,58,68,78,88]).

direction([-11,-10,-9,-1,1,9,10,11]).

member(X,[X|L]).
member(X,[_|L]):-member(X,L).


delete(X,[X|L],L).
delete(X,[Y|L],[Y|L1]):-
    delete(X,L,L1).

deletelist([],L,L).
deletelist([X|Rest],L,L1):-
    delete(X,L,L2),
    deletelist(Rest,L2,L1).

deletelists([],L,L).
deletelists([X|Rest],L,L1):-
    deletelist(X,L,L2),
    deletelists(Rest,L2,L1).

append([],X,X).
append([A|X],Y,[A|Z]):-
    append(X,Y,Z).

appendlists(Move,[],X,[Move|X]).
appendlists(Move,[L|Rest],List,List1):-
    append(L,List,List2),
    appendlists(Move,Rest,List2,List1).

isGameEnd(Black,White):-
    isPut(black,Black,White,_,_),!,fail.
isGameEnd(Black,White):-
    isPut(white,Black,White,_,_),!,fail.
isGameEnd(Black,White).


isPass(Teban,Black,White):-
    isPut(Teban,Black,White,_,_),!,fail.
isPass(Teban,Black,White).


printBoard(Teban,Black,White) :- 
    writeln([teabn,Teban,Black,White]),
    write('  1 2 3 4 5 6 7 8'),
    board(BD),
    printBoard1(BD,Black,White).

printBoard1([],Black,White).
printBoard1([BD|_],_,_):-
    BD // 10 =:= 1,X1 is BD mod 10,nl,write(X1),write(' '),fail.

printBoard1([BD|Rest],Black,White):-
    member(BD,Black),!,
    write("O "),
    printBoard1(Rest,Black,White).

printBoard1([BD|Rest],Black,White):-
    member(BD,White),!,
    write("X "),
    printBoard1(Rest,Black,White).

printBoard1([BD|Rest],Black,White):-
    write(". "),
    printBoard1(Rest,Black,White).

empty(P,Black,White):-
    not(member(P,Black)),
    not(member(P,White)).

checkput1(Teban,Black,White,X,X1,DX,Num,_):- X1 < 1 ,!,fail. 
checkput1(Teban,Black,White,X,X1,DX,Num,_):- X1 > 88,!,fail.
checkput1(Teban,Black,White,X,X1,DX,Num,_):- (X1 mod 10) =:= 0,!,fail.
checkput1(Teban,Black,White,X,X1,DX,Num,_):- (X1 mod 10) =:= 9,!,fail.
checkput1(Teban,Black,White,X,X1,DX,Num,_):- empty(X1,Black,White),!,fail.
checkput1(black,Black,White,X,X1,DX,0,_)  :- member(X1,Black),!,fail.
checkput1(black,Black,White,X,X1,DX,_,[])  :- member(X1,Black),!.
checkput1(white,Black,White,X,X1,DX,0,_)  :- member(X1,White),!,fail.
checkput1(white,Black,White,X,X1,DX,_,[])  :- member(X1,White),!.
checkput1(Teban,Black,White,X,X1,DX,Num,[X1|List])  :- 
    X2 is X1 + DX,
    Num1 is Num + 1,
    checkput1(Teban,Black,White,X1,X2,DX,Num1,List).

checkput(Teban,Black,White,X,List):- 
    direction(DL),
    member(DX,DL),
    X1 is X + DX ,checkput1(Teban,Black,White,X,X1,DX,0,List).

isPut(Teban,Black,White,X,List):-
    board(P),
    member(X,P),
    empty(X,Black,White),
    checkput(Teban,Black,White,X,List).

makePutList(Teban,Black,White,L):-
    findall(X,isPut(Teban,Black,White,X,List),L).

makeRevList(Teban,Move,Black,White,L):-
    findall(List,checkput(Teban,Black,White,Move,List),L).

doMove(black,Move,Black,White,Black1,White1):-
    makeRevList(black,Move,Black,White,List),
    writeln(List),
    deletelists(List,White,White1),
    appendlists(Move,List,Black,Black1).

doMove(white,Move,Black,White,Black1,White1):-
    makeRevList(white,Move,Black,White,List),
    writeln(List),
    deletelists(List,Black,Black1),
    appendlists(Move,List,White,White1).
   
getMove1(Teban,Move,Black,White):-
    makePutList(Teban,Black,White,List),
    member(Move,List).

getMove(Teban,Move,Black,White):-
    write('\nEnter Place:'),
    readln([Move]),
    getMove1(Teban,Move,Black,White).

getMove(Teban,Move,Black,White):-
    getMove(Teban,Move,Black,White).


play(Teban,Teban1,Black,White) :- 
    isGameEnd(Black,White),writeln('Game END'),
    printBoard(Teban,Black,White),nl.

play(Teban,Teban1,Black,White) :- 
    isPass(Teban,Black,White),!,writeln('Game Pass'),
    play(Teban1,Teban,Black,White).

play(Teban,Teban1,Black,White) :- writeln('Game Playing'),
    printBoard(Teban,Black,White),
    getMove(Teban,Move,Black,White),!,
    doMove(Teban,Move,Black,White,Black1,White1),
    play(Teban1,Teban,Black1,White1).


play :- play(black,white,[44,55],[45,54]).

2016年3月1日火曜日

CodeIQのデスコロ#2にPrologで挑戦してみた。


最近このサイトが面白くて遊んでます。CodeIQデスコロ#2

問題は、abcdefghijklmnopqrstuvwxyzの26文字を50回繰り返して出力するプログラムを書くこと。そしてそのソースコードをできるだけ短くすると。
 ただし、上記のアルファベット列、最初のaを1番目としてx番目の文字を、
・xが素数である
・xの中に「3」が含まれない。(3,13,33,130など、桁の数に3があるとだめ)
という条件を両方とも満たした場合、大文字にして出力します。なので、こんな感じになります。

aBcdEfGhijKlmn.....

最初はCで書いていたのですが、なかなか短くできず最短にはなれないことがわかってきたので、思いっきり日和ってPrologで書いてみました。

p(X,Y):-X>=Y*Y->X mod Y=\=0,p(X,Y+1);X>1.
t(X):-X mod 10=\=3,(X>9->t(X//10);true).
a(1300).
a(X):-Y is X+1,(p(Y,2),t(Y)->C is 65;C is 97),Z is X mod 26+C,format('~c',Z),a(Y).

:-a(0).

こんな感じ。
1行目のp(X,Y)で素数の判定。XがYで割り切れればアウト判定。
 Yの2乗がXより大きい場合は、割り切れないことが確定なのですが、Xが1の場合のみ失敗。
 そうでない場合は、XをYで割った余りが0でなければ、XをY+1で割り切れるかトライする。
って感じで、1行に無理やりまとめてます。ここで、普通はやっちゃいけないのが
  p(X,Y+1) の部分。PrologではY+1と書くと、Y+1を演算した結果を渡すのではなく、+(Y,1)という項をそのまま渡してしまいます。よって、気をつけないととんでもないことになるのですが、文字数を減らすため、わざと項のままつっこんでます。これは、再度呼び出されたp(X,Y)のなかの条件式を評価する時に展開されます。

2行目のt(X)はXに3が含まれているかのテスト。これもかなり論理をいじってます。
 X を10で割った余りが3でない場合(3で割れたらばfailして失敗)、Xが10以上であれば、t(X//10)で10で割った値に対してトライします。Xが一桁ならtueで成功と。ここもわざとt(X//10)にして項の評価を後回しにしてます。

最後のa(X)で文字を出していきます。
1300文字出したら終了
1300文字までは、上記条件を満たすかどうかをp(X,Y),t(X)で判定して、満たしていれば大文字にしています。

普通にPrologで書くとこんな感じになると思います。
素数かつ、3を含まない場合には大文字で、そうでなければ小文字で出力。1301まできたら終了。そして、素数判定も、「3」がある判定もある意味、普通に書くとこんな感じでしょうね。

isPrime(1):-!,fail.        //1は特別扱い
isPrime(X):-isPrime1(X,2). //それ以外は2からの数で割り切れるかチェック

isPrime1(X,Y):-X < Y*Y.
isPrime1(X,Y):-X mod Y =:= 0 ,!,fail.
isPrime1(X,Y):-Y1 is Y + 1,
              isPrime1(X,Y1).

isInThree(X) :- X mod 10 =:= 3. //3で割り切れればtrue.
isInThree(X) :- X > 9 , !,      //割り切れない場合、10以上であれば
              X1 is X // 10,    //数を10で割って、「3」があるかチェック
              isInThree(X1).

answer(1301).
answer(X) :- isPrime(X),
             not(isInThree(X)),
             Z is 65 + (X - 1) mod 26, //文字コードの計算です
             format('~c',Z),
             X1 is X + 1,
             answer(X1).
answer(X) :- Z is 97 + (X - 1) mod 26, //文字コードの計算です
             format('~c',Z),
             X1 is X + 1,
             answer(X1).

:-answer(1).

そして、最短になるまえに挫折した(笑)Cのソースもだしてみます。本当は1行に詰めてるんですが、読みにくいので適当に改行いれてます。

int n,i=1,f;
main(){
 for(;
      i<1301 p="">
      putchar((i++-1)%26+97-f*32)
      )
   for(n=f=i%10!=3&i/10%10!=3&i/100!=3&i>1;
        ++n
        f=i%n&&f
        );
exit(0);
}

for文のなかに初期化やその後の処理を無理やり詰めて、一文字でも短くしようとしています。セコイです。
2つめのforループですが素数判定と3があるかないかを判断しています。素数かつ3が含まれていない場合、f=1。そうでないならf=0として処理を進めます。

 まずfにiの中に3が含まれていれば0、含まれてなければ1となるように値を論理式で求めます。ここで、i=1つまりaの時は素数ではないのでむりやり0にします。そして、for文の処理のところ、f=i%n&&fで素数かどうかを判定しています。nは、iに3が含まれる時やi=1の時に0になっているので、いきなり1で割るかどうかを判定しちゃうのですが、すでにf=0となっているので、結果としては同じになるのです。わかりにくいですね。
 そして、2個目のfor文が終わるとputcharにはいって文字を出します。fの値によって大文字と小文字を判別します。
 まぁ、もっと短くなるようなので、参考程度に。


2015年6月22日月曜日

数独をPrologで解く

SWI-Prologを入れていろいろ遊んでます。
もともとはIchigo-JamでBASIC書いてるプログラムを、CやPrologで書いて比較するって感じでやってました(これはこれで、今後まとめます)。

ハノイの塔、8-Queen、覆面算(これはBASICで書いてない)、そしてCでは書いたことがある数独を解くプログラム。

超ナイーブに書いたところ、4X4のミニ数独は解けたけど、6X6でかなり時間がかかってしまうことが判明。9X9は多分無理!!!
 →単純に空いているところから数字入れて、入れた場所だけでチェックしてる感じ。

問題になるのは、1マス埋めるごとに、たて、横、そして周辺(ブロックになっている)の3つについて、矛盾が生じないかのチェックをしなければならないのに、していないことでした。
 しょうがないので、たて、横、ブロックのリストを全部もってまわって、埋まっている数字が重なっていないかどうかのチェックのみ入れてみました。

 これでなんとか9X9でも数秒程度で解けるようになりました。はふー。

Cだと、必然的に埋まる場所をチェックして先に値を入れていくのですが、これをPrologで実装するのは大変そうです。アルゴリズムの記述はPrologは苦手なのよね。その代わり、成立する条件だけ書けばよいわけなのですが、それだと遅すぎると。。。

いろいろ調べていたら、SWI-Prologには制約論理言語の拡張があるようでして、それを使うと、制約条件だけ記述して、探索は完全にシステム任せになるようです!

 試してみました。記述が少ない!、しかも早いぞ!!!(笑)

 まぁ、記述が楽になるのでいいのですけど、アルゴリズムの記述もプログラムの楽しみの一つですよねぇ。それがないのは、いいことなんでしょうかね????

 ということで、適当に書いたプログラムを晒してみます。

check,check1,check2,check3なんてあまりに適当すぎてなんですけどね。
check,check1で、問題に数字を割り当てていて、
check2,check3で、割り当てた結果で矛盾が生じていないかどうかをチェックしています。
#ま、みてわかるとも思えませんけど。

あ、実行結果を先につけますが、これだと、元の問題がわからないですねー。あはは。
http://www.conceptispuzzles.com/ja/index.aspx?uri=puzzle/sudoku
ここから問題を拾ってきました。

[2,4,1,3]
[1,3,2,4]
[3,1,4,2]
[4,2,3,1]
true .

[1] 111 ?- solve(6).
[2,5,4,6,3,1]
[6,1,3,5,2,4]
[4,2,6,1,5,3]
[1,3,5,2,4,6]
[5,4,1,3,6,2]
[3,6,2,4,1,5]
true .

[1] 112 ?- solve(10).
[7,4,1,3,6,8,9,2,5]
[3,2,9,7,4,5,6,8,1]
[8,6,5,1,2,9,4,7,3]
[9,8,3,4,1,7,2,5,6]
[6,5,7,2,9,3,8,1,4]
[2,1,4,5,8,6,7,3,9]
[5,9,6,8,3,2,1,4,7]
[4,3,8,6,7,1,5,9,2]
[1,7,2,9,5,4,3,6,8]

true 


------------------------
ここからがプログラム。

solve(N):-
    sudoku(N,X,Y,Z,L),
    check(X,L,[X,Y,Z,L]),
    check(Y,L,[X,Y,Z,L]),
    check(Z,L,[X,Y,Z,L]),
    maplist(writeln,X).

check([],_,_).
check([X|L],L1,ALL) :- 
    check1(X,L1,ALL),
    check(L,L1,ALL).

check1([],[],ALL).
check1([X|L],Y,[A,B,C,List]) :-
    delete(X,Y,L1),

    check2(A,List),
    check2(B,List),
    check2(C,List),
    check1(L,L1,[A,B,C,List]).

delete(X,[X|L],L).
delete(X,[Y|L],[Y|L1]):-
    delete(X,L,L1).

check2([],_):-!.
check2([X|L],L1):-
    check3(X,L1),
    check2(L,L1).

check3([],_):-!.
check3([X|L],L1):-
    var(X),!,check3(L,L1).
check3([X|L],L1):-
    delete(X,L1,L2),

    check3(L,L2).

sudoku(4,[[B11,B21,B31,B41],[B12,B22,B32,B42],[B13,B23,B33,B43],[B14,B24,B34,B44]],
       [[B11,B12,B13,B14],[B21,B22,B23,B24],[B31,B32,B33,B34],[B41,B42,B43,B44]],
       [[B11,B21,B12,B22],[B31,B41,B32,B42],[B13,B23,B14,B24],[B33,B43,B34,B44]],
       [1,2,3,4]):-
    B31 is 1,
    B22 is 3,
    B42 is 4,
    B13 is 3,
    B33 is 4,
    B24 is 2.


sudoku(6,[[B11,B21,B31,B41,B51,B61],[B12,B22,B32,B42,B52,B62],[B13,B23,B33,B43,B53,B63],
[B14,B24,B34,B44,B54,B64],[B15,B25,B35,B45,B55,B65],[B16,B26,B36,B46,B56,B66]],

       [[B11,B12,B13,B14,B15,B16],[B21,B22,B23,B24,B25,B26],[B31,B32,B33,B34,B35,B36],
[B41,B42,B43,B44,B45,B46],[B51,B52,B53,B54,B55,B56],[B61,B62,B63,B64,B65,B66]],

       [[B11,B21,B31,B12,B22,B32],[B13,B23,B33,B14,B24,B34],[B15,B25,B35,B16,B26,B36],
[B41,B51,B61,B42,B52,B62],[B43,B53,B63,B44,B54,B64],[B45,B55,B65,B46,B56,B66]],
       [1,2,3,4,5,6]):-
    B11 is 2,
    B61 is 1,
    B22 is 1,
    B52 is 2,
    B33 is 6,
    B43 is 1,
    B34 is 5,
    B44 is 2,
    B25 is 4,
    B55 is 6,
    B16 is 3,
    B66 is 5.

sudoku(10,[[B11,B21,B31,B41,B51,B61,B71,B81,B91],[B12,B22,B32,B42,B52,B62,B72,B82,B92],
[B13,B23,B33,B43,B53,B63,B73,B83,B93],[B14,B24,B34,B44,B54,B64,B74,B84,B94],
[B15,B25,B35,B45,B55,B65,B75,B85,B95],[B16,B26,B36,B46,B56,B66,B76,B86,B96],
[B17,B27,B37,B47,B57,B67,B77,B87,B97],[B18,B28,B38,B48,B58,B68,B78,B88,B98],
[B19,B29,B39,B49,B59,B69,B79,B89,B99] ],

       [[B11,B12,B13,B14,B15,B16,B17,B18,B19],[B21,B22,B23,B24,B25,B26,B27,B28,B29],
[B31,B32,B33,B34,B35,B36,B37,B38,B39],[B41,B42,B43,B44,B45,B46,B47,B48,B49],
[B51,B52,B53,B54,B55,B56,B57,B58,B59],[B61,B62,B63,B64,B65,B66,B67,B68,B69],
[B71,B72,B73,B74,B75,B76,B77,B78,B79],[B81,B82,B83,B84,B85,B86,B87,B88,B89],
[B91,B92,B93,B94,B95,B96,B97,B98,B99] ],

       [
[B11,B21,B31,B12,B22,B32,B13,B23,B33],[B14,B24,B34,B15,B25,B35,B16,B26,B36],
[B17,B27,B37,B18,B28,B38,B19,B29,B39],
[B41,B51,B61,B42,B52,B62,B43,B53,B63],[B44,B54,B64,B45,B55,B65,B46,B56,B66],
[B47,B57,B67,B48,B58,B68,B49,B59,B69],
[B71,B81,B91,B72,B82,B92,B73,B83,B93],[B74,B84,B94,B75,B85,B95,B76,B86,B96],
[B77,B87,B97,B78,B88,B98,B79,B89,B99]
       ],
       [1,2,3,4,5,6,7,8,9]):-
    B41 is 3,
    B51 is 6,
    B71 is 9,
    B22 is 2,
    B82 is 8,
    B13 is 8,
    B43 is 1,
    B63 is 9,
    B34 is 3,
    B54 is 1,
    B74 is 2,
    B94 is 6,
    B15 is 6,
    B95 is 4,
    B16 is 2,
    B36 is 4,
    B56 is 8,
    B76 is 7,
    B47 is 8,
    B67 is 2,
    B97 is 7,
    B28 is 3,
    B88 is 9,
    B39 is 2,
    B59 is 5,
    B69 is 4.


2015年5月26日火曜日

IchigoJamの変態BASIC


速度はかなり遅いIchigoJamです。8080の1Mhzより遅いような感じですからね。
ループカウンタを1000くらい回すと10秒近くかかります!?

そして、幾つか変態とも呼べる機能があるのです。
そのひとつが、GOTO文の飛び先に式を入れることができるという機能です。

つまり、
GOTO 100
ではなく
A=100
GOTO A
って感じで動いてしまうのです!??

これを利用して、ハノイの塔をちょろっと書き換えます。
(リストは最後につけます)

なんとなんと、4つあったIF文がたった1つになっちゃいました!!!
この一個も頑張れば減らせそうなんですが、論理演算使ってやるのもどうかな?と思うので、やっていません。

 それにしても、手続きを記述するだけとは言え、IF文一個でハノイの塔って動いちゃうんですねぇ。あひゃひゃ。
 種明かしは、スタックフレームに、次に実行すべき行数を入れて、そこに飛んでいくっていうだけなんです。まぁ、なんかアセンブラで、飛び先を直接書き換えてジャンプするみたいな、本当に低級言語な機能だと思います(褒めてます)。

そして、似たような変態機能がまだあるので、それを使うと、もしかしたら、オセロくらいは作れるかもしれません。こっちの紹介も後日やってみようと思います。

 そうそう、IF文も減ったので、実行速度は通常版より3割か4割くらい早くなりました。でもね、お仕事でこういうプログラム書いちゃダメですよ!!!(笑)
 つーか、書いた本人以外には、理解できないこと間違いなしです!!
解析できた方は、是非とも連絡くださいませ。

10    PRINT"TOWER OF HANOI"
20    A=1:C=0
30    [4]=1000
40    INPUT"LEVEL:",B
50    [5]=B
60    [6]=0
70    [7]=2
80    [8]=1
100  IF[A*5]>1 GOTO 200
140  PRINT [A*5],":",[A*5+1],"->",[A*5+2]
150  C=C+1
160 A=A-1
170 GOTO [A*5+4]
200 [A*5+4]=300
210 A=A+1
220 [A*5]=[(A-1)*5]-1
230 [A*5+1]=[(A-1)*5+1]
240 [A*5+2]=[(A-1)*5+3]
250 [A*5+3]=[(A-1)*5+2]
260 GOTO 100
300 PRINT [A*5],":",[A*5+1],"->",[A*5+2]
310 C=C+1
320 [A*5+4]=160
330 A=A+1
340 [A*5]=[(A-1)*5]-1
350 [A*5+1]=[(A-1)*5+3]
360 [A*5+2]=[(A-1)*5+2]
370 [A*5+3]=[(A-1)*5+1]
380 GOTO 100
1000 PRINT"TOTAL:",C
1010 END