AIにコードを書いてもらって失敗した話

将棋の局面を記述する書式にSFENという記述方式がある。
これは盤上の駒の配置、手番、先手と後手の持ち駒、手数などがコンパクトに文字列にまとめられたもので、Kyokumen.jpの局面検索に使われていたり、将棋エンジンの定跡ファイルにも使われていたりする。

SFENの書式についてはこの記事にうまく説明されている。

今回、YaneuraouさんがMIT Licenseで新たに公開された233万手の定跡ファイルをみたところ、後手番の局面がなく、先手後手盤面を反転して検索する方法がとられている。つまり、 例えば初期画面から後手が3四歩と指した局面が先手番として登録されている。 屋根裏王のエンジンにはflippedBoardというパラメーターがあり、これをOnするだけで対応するよう設計されているのだが、定跡ファイルをブラウザで将棋盤上に表示させる自分のWebページに追加するためには、後手の手番の時に検索をかけるSFENの局面を反転させてサーバーに問い合わせる必要が生じた。サーバーから帰ってくる候補手も同様に、例えば2六歩を8四歩などと反転させる必要がある

最初の局面を反転させる部分、TypeScript関数として用意すると以下のようになる。

 

このコードで何をやっているかというと

1.まずは SFEN文字列をスペースをデリミターとして文字列アレイに分解する。
2.それぞれのアレイ因子を board, turn, hands, moveCountという変数にわりあてる。 ちなみにMoveCountは今回の定跡ファイルに使用するにはゼロに置き換える必要がある。(上のコードでは未反映)
3.board(駒の盤面位置情報)をさらにdelimeter ‘/’ で各列に分解したアレイを生成
4. array.reverse()を使って列順をひっくり返す。上段の列が下段に、下段の列が上段になる。
5.さらに、さらに各列の文字列を一文字づつに分解してアレイを作る。
6.大文字の駒(後手の駒)を小文字(先手の駒)、小文字の駒(先手の駒)を大文字に置換する
7.array.reduce()を使って、+シンボル(成り駒)因子と次の駒の因子を繋げて一つのアレー因子にまとめる

8. これを.reverse()で左右反転
9. 各列のアレイを文字列に変換
10。各列の文字列アレーをを’デリミター/’で接合し、一つの文字列を生成。→flippedBoard
11. 手番もひっくり返す (flippedTurn)
12. 持ち駒を先手と後手入れ替える (pivotHands) この部分、使ったロジックは以下のとおり

B3Ps14pを S14Pb3pとしたい。
まずは大文字小文字を入れ替える
b3p2S14P (flipped)
最初の大文字のインデックスを取ればよいと思えるが、数字がついた場合(2S)となっていた場合はNG
なので文字列を逆転させてみる
P41S2p3b reversed
これで最初の小文字のインデックスを取る。上の例だと5。文字列長さ8なので 8-5=3
secondPart =flipped.slice(0,3) = b3p
firstPart = flipped.slice(3) =2S14P
これを組み立てる
final = firstPart+secondPart = 2S14Pb3p
うまくいっているのでは?

先手の持ち駒だけの場合 例えば 2P
小文字のインデックスはマイナス1 この場合は処理をスキップ
後手の持ち駒だけの場合 例えば14p 逆転文字列での小文字のインデックスは0 この場合も処理をスキップ

13 .flifppedboard flippedTurn flippedHands moveCountを一つの文字列につなぎ合わせ、この値を返す
とまあ、書いてしまえば簡単な作業の連続だが、実はこのコード、最初AIに聞いて提示されたものが動かなくて、修正を入れ始めたら、結局七割がたは書き直してしまった、というものになる。

最初、Brave Brouserの検索窓に ”Given sfen string, write a javascript function that returns flipped sfen” と打ったらあっさり回答をよこしたのでこれをTypeScriptに書き直し、そのまま定跡ファイルに使ってみたのだた、局面検索がまったくヒットしなかった。  コードを解析して分かったことはAIがくれた回答は上記のステップから 7,8そして12の処理が抜けていた。その結果盤面の上下反転はしたが、そのあとが左右反転をしていないため、ミラーイメージになってしまい、さらに持ち駒があった場合は先手と後手が逆のまま。 Webページの仕組みはこのsfenをサーバーに送ってサーバーのデータベースからこのsfenに対応するmoves情報を返してもらい、次の指し手の候補をリストアップするというものなので、ミラーイメージの局面照会にはヒットする候補手はありませんという答えしか返ってこない。

AIはSFENが何たるかをあまり理解できないまま、WEBに落ちている情報をかき集めて使ったようで、プロンプトで、SFENの定義から始め、期待されるアウトプットのイメージをしっかり伝えておかなければだめだったか。

逆に良いねと思ったのはアレイやオブジェクトの関数をチェイニングして記述する、いわゆるFunctionalな書き方になっていてスマートである。 これは真似したい。

とにかく、AIに仕事をさせたら、今の時点では検証が大事なわけだが、それなら最初から提供されたコードは参考までに留めておいて、実際に使うコードは自分で書いたほうが楽しいかもね(仕事でプログラミングしている人はこうは言えまい)。

追記:Yaneuraouさんからflipsfenをpythonで作成した時の顛末のリンクを教えていただいた。 https://yaneuraou.yaneu.com/2023/12/15/chatgpt-wrote-a-program-to-flip-a-shogi-board/  同じようなところでハマっているんだ、と可笑しくなった半面。 ちょっと待て、これは2年前の記事。 これを読んでいたらこんなに時間を使わなくてもよかったじゃん とも思った次第。

Using Vue with WebPack

I wanted to transfer my existing script to WebPack bundle.  The application is a simple page that uses  Vue.js.   Note that I only read Introduction part of Vue and started using it.  At that time, I did not even know the concept of Vue component files .  Thus my application was entirely in one JavaScript file.

I rewrote it using typescript.   WebPack bundling was necessary to make it work.

I had to decipher minor (but important) detail on NPM’s Vue  module that should be used along with WebPack. 

If I were to use a Vue package from node_modules/ and bundle it with WebPack, then the default module that will be used is Runtime-only ES module build (vue.runtime.esm.js.)

Now this verbiage is coming from VueJs.org V2 guide
https://vuejs.org/v2/guide/installation.html#Terms


” If you need to compile templates on the client (e.g. passing a string to the template option, or mounting to an element using its in-DOM HTML as the template), you will need the compiler and thus the full build:”

This applies to ‘getting started example’ code where  you use template directly on target html file, such as 

<div id="app"> {{message}} </div>

According to Vue documentation, runtime only module is 30% lighter and when I write my code in such a way that it utilize component file (*.vue), then  using vue-loader will precompile all the templates into render function (for those within component file)  so the default settings wil be not an issue.   Issue is when I create a new instance  of the view . 
Again, according to the guide

// this requires the compiler
new Vue({
template: '{{ hi }}'
})

// this does not
new Vue({
render (h) {
return h('div', this.hi)
}
})


If I use the first method but did not configure webpack to use full version of Vue module, resulting bundle file  is missing template compiler and I will be staring at the blank page.  In this situation,  looking into  source with F12 looks something like this

<body><!-- function(a,b,c,d) {.......} --></body>


Then how do I configure Webpack  so that it knows to bundle the full version of vue module instead of runtime-only module?  It was spelled right in the guide above.

module.exports = {
          // ...
          resolve: 
                {
                   //.....,
               alias: {'vue$': 'vue/dist/vue.esm.js' }
               //.......
      }

I needed to add  ‘alias’ clause to  the ‘resolve’ section of module.exports.  (for webpack v2 and above)
WebPack will then bundle full version of Vue module(vue.esm.js). 

Since I didn’t use .vue files for this project and put everything in a single html file, I did not  need to use ‘vue-loader’ either.
So this is a minimal requirement for Vue to work with WebPack.

Vue.js has a very detailed documentation which is good. All I needed was to read it through and understand the meaning of it (lol).  

By the way, another way to make it work is not tell WebPack about Vue at all.

  1. add script tag in your HTML writeup pointing to Vue CDN
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>


    2. Then tell Typescript  that ‘Vue’ is a global object

declare let Vue:any

Yes, use the bundler but not bundle the module. It sounds strange but it works

TypeScript その2

年末の休暇を利用してtypeScriptの演習を続行中。 現在まで理解できたところをとりあえず列挙。
nodejs が導入されている必要あり。

npm install typescript -g

これで tscコマンドが使えるようになる。

tsc example.ts

そして、これでexample.ts から example.jsがトランスパイルされる。

プロジェクトフォルダー内で

 tsc init

とやると、コンパイラー設定用のtsconfig.jsonが作成され規定値が設定される。例えば、出力のJavaScriptのレベルが

<pre>”target”:”es5″</pre>

などと記述されている。またコードをトランスパイルしたときのエラーの出力設定などもできる。今のところ全く変更の必要のないレベルでトライアル中。

TypeScript は 基本的には JavaScriptのsuper setなのだが、 ’let’, ‘const’, ‘arrow function ()=>’, ‘ … spread operator’,’Template strings’ などes2015の書式が既にサポートされている。 なのでexample.tsではこれらの言語機能を使ってプログラムを書き、tscでトランスパイルすると、これらの表記をまだサポートしていない現行のブラウザでも動くようなJavaScriptに書き換えてくれる。上のConfig例ではes5レベルのコードに置き換わる。 つまりBabelと同じような使い方ができる、。

type safeなので 異なったタイプの変数へのアサインは論外としてもその可能性が生じそうなコードにはエラー表示が鬼のようにでる。例外処理とか、Interface定義を記述して対処すると満足して何も言わなくなってくれるので、バグの可能性のあるコードを書く可能性が低くなる。

TypeScript 入門

TypeScriptのイントロがDev.office.comにあるので眺めてみた。

tsとして以下の例が載っている。

class Student {
    fullName: string;
    constructor( public firstName, public middleInitial, public lastName){
        this.fullName = firstName + " "+ middleInitial + " " + lastName;
    }
}
interface Person {
    firstName: string;
    lastName : string;
}

function greeter(person: Person){
    return "Hello, "+ person.firstName + " " + person.lastName;

}

let user =new Student("Jane","G.","Doe");

document.body.innerHTML=greeter(user);

これをJSにコンパイルするとこうなる(WebStormはプラグインが動いてダイナミックに自動生成する)

var Student = (function () {
    function Student(firstName, middleInitial, lastName) {
        this.firstName = firstName;
        this.middleInitial = middleInitial;
        this.lastName = lastName;
        this.fullName = firstName + " " + middleInitial + " " + lastName;
    }
    return Student;
}());
function greeter(person) {
    return "Hello, " + person.firstName + " " + person.lastName;
}
var user = new Student("Jane", "G.", "Doe");
document.body.innerHTML = greeter(user);

うむむむむ。JSのほうがパターンに親近感があって読みやすいんですけど。でもそういうことじゃなくてTypeやクラスの定義がしっかりできて、あとあとの管理が楽、ということなんだろうな。