星田オステオパシー

プログラミング学習日記 2023/11/26~

2023/12/18 Minimaxをいい加減なんとかしたい
 探索の深度Plyも表示するようにして・・うーん・・動き自体は分かるけど、このValに前の深度のBest-moveが入ってくるのが絶対におかしい

 では、こういう感じでLoop中はBest-valを返すようにして、全てが終わったらBest-moveを返すように出来ないか?

 となると、LetでのBest-moveの束縛をもう1段階上に持ってくれば・・

 うーん?最後の出力は確かにBest-moveになってるけど、Valへの束縛は改善できてないな。そう言えばMinimax関数の最終返り値がValになるんだから関数内で位置を変えても意味がないよなあ

 じゃあConsでBest-valとBest-moveをペアで返して、判定時にはCarとCdrで選べるようにしたらどうかな?あ、でも・・Ply=0の時の返り値がNumだから駄目か〜w

 もうこうなったら無理矢理にでも動かしてやるぜ!ってことで、Ply=0での返り値もConsのペアで返すようにしてやる

 おおっ!ちゃんと動いた!多分狙った通りの挙動になってると思うんだコレで!ただ、最終的にCdrのBes-moveだけが返ってくるようにしたい

 Minimax関数内でどうやっても駄目なので受けてのSearcherでの受け方を返るしか無い

 まあ、Cdrで取り出せばいいだけなのだが。あれ?この形・・PairとLet-valuesの違いはあれどほぼ同じじゃないかw なるほどこういう事だったのか〜
 ただ、オリジナルの方はValuesで返すのはやっぱり納得行かないんだけどな?Loop内での受け方がLet-valuesになってないんだもん!

 とりあえず試行超絶錯誤の末に、とうとう納得の行くMinimaxが書けた!気がする。あ〜非常にスッキリした気分だ〜。これを利用すればαβカットも納得して書けるはず。それでようやく次へ進めるであろう


2023/12/17 オセロのMinimax検証
 Best-moveの更新も問題は無いし・・Val、Best-valも大丈夫か

 プレイヤーがWhiteからBlackに変わる時にBest-moveがそのまま持ち越されてるのは・・そういう風に書いてますけど、仕組みとしてはどうなんだ?

 で、問題はココよ。プレイヤーが切り替わる時に、Best-moveがValとして返ってくるからValがBest-moveになるんですよね、ここがずっと引っかかってる訳だな~

 オリジナルでは評価関数が味方はMax、敵はMiniってことでMinimaxになってるわけだけど、うーん・・?自軍はPly=0で評価関数が働くけど、敵軍はどうやってMiniってのを判定してるんだ?Ply=0になるまでコマ数は評価されないのに?そこんところの理解が足りてないと推測する。
 もしくは敵味方それぞれにBest-move Best-valを設定して、それぞれのターンでの最適値を記録していくってのはどうだろう?と。次回はその2つを考えよう。亀の如き歩み!


2023/12/15
話題その1 オセロw
 αβカットがまだ納得行かない。AchiとCutの書き換えがどうもなぁ〜・・どう考えても途中で返ってくるValが入らないと意味を成さないと思うんだけど・・

 再帰的な動きはもう分かるんだけど・・Valで返ってくるのが評価関数で出てくる数値だけだったら良いんだけどBest-moveもなんとかしようとしてるところがうまく行ってないんだけど・・もう自分で考えて作らないと理解できないんじゃないか。というわけでPaizaでの五目並べがやっぱり鍵になるかなと。でも、このままで足踏みし続けてもしょうがないので期限を今年いっぱいと決めよう。来年からは強制的にKatan-OとかTic-80とか・・Udemyの講座もあるし、そっちに行こう。もう十分考えたわい

話題その2 五目並べw
 αβカットの前にちゃんとミニマックスが動いてるのかが謎なのでデータの流れを可視化してチェック!
 しようと思っけどLegal-movesがオセロと違って制約がないのを良いことに「空いてる全マスをチェックする」という風にしていたのが災いして、出力すると無茶苦茶な数になってて・・まあ、作ってる最中から無駄だなぁとは思ってたんですが。
 そこで、とりあえず自コマに隣接してるコマのみをチェック対象にするOtonari-movesを作ることにした。うーん・・Legal-movesで作った空きコマのリストの各コマに対して8方向に隣接するコマを調べて1つでも自コマがあれば通すフィルターを作れば良いな?と。まず実験・・OKかな

 一旦まとめて作ったんだけど、そう言えばコマのリストを返さないといけないので1つにまとめてると駄目だった(^_^;) Otonari-pを作ってこれをFilterを使っていくようにしないと駄目だ。

 というわけで、検証が出来るようになった・・肝心の検証は明日以降で(^_^;)


 

2023/12/13 オセロ、まだ悩んでいる
 オセロのαβカットのコード。なんかまだ納得できないので数値を追う事にする。何が納得できないかと言うと・・Plyで評価関数の結果として数値を返すのに、途中のLoopではBest-moveを返してるのがValに入ってるのが気持ち悪いのだ。型が違うと言いましょうか・・それを見越した上で設計されてるのか?

 コードの途中経過を表示しまくるようにして流れを追う。なるほど、確かにPlyが進んで0になった状態があとに表示されるのに、それを元にしたWhiteの手が先に表示されてる。ここんところはちゃんと動いてるように思えるけど・・Valはその時点でBest-moveが返ってWhiteの手46が返ると。

 あとはAchiとCutの数値がどう変わるかだなぁ・・ちょっと時間がなかったので続きは明日の宿直Bで。なんとも遅い歩みだなぁ


 前回、重み付けコードを移植したけど実際のところよく分かってないので読み解き(を気分転換で行う)。あれ?新しい重み付けマップが出力されるのかと思ったら数値が出た。なんだこれは・・

 新しい重み付けマップを作るのが目的だと思ったが違った。評価関数じゃないか。これをOthelloにぶっ込むことで盤面の評価が得点として出力されるということか・・。コーナーのお隣を取ってると不利なので自コマが置いてあれば点数が下がると・・OKようやく理解したw


2023/12/11 
話題その1 オセロのαβ、よく考えたら間違っている
 前回の宿直Bでデータの流れを確認してて気づいたけどαβカットの処理、ループさせ始めた段階でAchieval>Cutoffなので終わってしまう。直さないと・・と思ったけど猛烈に眠たかったので次回の宿直Bに持ち越し。

話題その2 五目並べのαβコード
 理解してる気がしないので、実際に動かしたとしてデータの流れを追ってみる。それも恐ろしく原始的な方法で。コードの流れに沿って一々コメントで書き込んでいくというね

 どうせ動かないんじゃないかなぁ?と思ってたが、意外にも動いた!が、ちゃんとαβカットをしてるのかは分からない。一々表示させて検証しないとなぁ・・

 破壊的変更を使った方法で作ってたけど、非破壊でLet loopを使った方法で書いてみたらどうなるか?と試してみる。多分、こういう感じになるんじゃないかなぁ・・。でもどうせ動かないんでしょ?

 と、思ってたが動いた!うーん、これもαβカットがちゃんと働いてるのかなぁ・・?

 なんかここ数日、例のオルゴナイトをセットしてるせいか宿直Aでガンガン眠れるんですよねぇ。最初に「スマホと睡眠の関係」の話題を話していた人もオルゴナイトを仕掛けたらよく眠れるという話だし・・効果あるんじゃないか!?
 ま、そういうわけで宿直Aでのプログラミング学習はサボリ気味である、と。


2023/12/06 αβカット問題
 五目並べもオセロもαβカットの部分まで来たんだけど・・良く分からないので、実際の数値を仮定して紙に書いて追ってみる。うーん、分からん!何が分からないのかと言うと・・再帰で戻ってくる過程でAchievementとCutoffが更新されないといけないと思うんだけど(じゃないと不要部分のカットオフが出来ないよね?)、Loopだから潜っていく時に設定されるのに、また再帰で戻ってくる時にも変化するとか変じゃないか?と


 ちゃんとしたコードは書いてくれないけど、解説は期待できるClaudeに聞いてみる。

 うーん・・となるとValは常に数値、つまり評価関数で出てきた数値を入れるようにしないといけないわけだからBest-moveの返し方は見直さないといけない気がする

 えっ?このSchemeとのスコープの違いがちゃんと分かってないのでは(自分が)。とりあえず返って来る途中でAchievableとCutoffの数値に変更は無さそうだ。それでCutoff出来るんか?これも調べないと

 なるほど?これだったらコレで仕組みは理解しやすい気がするが!もう時間がない、引き続き理解に努める事にする。ここは踏ん張りどころと見た


2023/12/03 ForのVoid問題、本当に解決
 オセロ、前回の続き。重み付け改造の参照用マップを作ってる模様。
 読解したところ、これはベクターの各要素を全盤面の8方向を「お隣」としてリストにして保存するものっぽい。先頭を11とすると#((0 1 2 10 12 13 20 21 22) ... )となるわけか。もちろん無効なコマは飛ばされる(11未満とか端)わけだが・・
 それ以前にそもそも、Scheme RacketではこのLetから始まる束縛はあり得ないので戸惑う。GPTに聞くとやっぱりCL独特らしい。
 スコープの問題でScheme系だと外部からの参照が出来ないのだがCLだと平気だという。Scheme系に慣れてるとすごく不思議だ。

 普通だったら変換用の関数を書いて返ってきたリスト(ベクターの使用は面倒で止めたので)を大域変数として束縛するのだが・・気になったのでLetの代わりにDefineを使って直接書くとどうなるか試してみる。
 うーん・・Forを使うとどうしてもSet!を使わないとVoidが出てしまう。おかしい・・関数型言語なのにSet!前提の使い方しか出来ないなんてあり得るか?

 名前付きLetだったらこういう風に書けるんだけどなぁ・・

 気になるのでForでも何とかして書くことは出来んのか?と深夜1時までお得意のネバネバ戦法で色々と試す。Countを使ってList-refで読み込んでSet!で置き換えるとか・・?などなど

 そう言えばForとFor/listってどう違うんだっけ?とダメ元で入れ替えてみたところ・・コレだよ欲しかった値は!ええっ?For/listだと値が返るの?

 ファファファFu◯k! なんだよForってもともと副作用を利用するための構文やったんかい!くっそ~今夜だけでも4時間悩んでたじゃないか!
 まあでも・・・これにてForのVoid問題、完全解決!

 それにしてもGPT4の野郎、なんでVoid返ってくるんですかねぇ?と聞いたらBreakで止めてるからだね!とかテキトー言いやがって。Forだったら返り値は無いのでVoidだよ(笑)とか返してくれよ。シンギュラリティは遠いのう
 

2023/12/01 
話題その1 五目並べの続き
 まずは5連続になってるのに終了しなかった問題。Loopの中で連続変数の更新をしてるために終了判定が次のターンになってしまっていた、と。判定部分に評価関数を持ってきて・・解決

 続いて・・クロージャへの値の渡し方が曖昧だったので実験。Maximizerでの値を渡す際、もしかして・・渡す側の名前で判定されてたりするのか?とか。結果は数と順番だけが問題だった模様。なるほどねぇ


 Maimizerに渡すEval-fn部分、オセロだとCount-difference。それに該当するCount-renzoku-dir。外からMove(指し手)を受け取り、全方位をMapしてリストで連続数を返す部分。

 実際だとこういう感じになるから、これをまたLambdaでPlayerとBoardを受け取るようにして・・

 Maximizerの中では指し手を受け取って新たな盤面を作って、その連続数のリストを返すから、内部的にはこういう数値の受け渡しになって・・



 受け取った連続数のリストの中から最大数を抜き出して指し手(Move)を返すのがMaximizer、と。こいつは外部からPlayerとBoardを受け取るわけだけど・・

 PlayerとBoardはメイン関数Gomokuの内部でLetされて渡される・・と。この仕組みがややこしくて理解に苦しんだぜ(-_-;) 毎回外部からPlayerとBoardを渡さないで済むからスマートと言えるのかな

 と、ここで大ハマリ。画像を残してないけど連続数の判定がどうもおかしい。ストラテジーでは一直線に最大連続に向かってコマを置くはずが、途中から挙動がおかしくなってしまう。

 このように連続総数は4と正しく出てるのに、27に置いた場合5が返ってくるはずのCount-renzokuがちゃんと働いてないのだ。それを利用したCount-renzoku-allはちゃんと動いてるのになんで?
 これで8時間くらい悩んでましたかね・・

 この形だとちゃんと判定される。ということは・・最上段まで詰まってる場合はおかしくなる。となると・・安易に利用した例外処理が怪しいな・・

 試行超錯誤の結果、(not (equal? player (list-ref board (+ moveB dirB)の部分を他の「つながってない」判定の後に持ってくることで解決した(たまたま)。
 いやもう大変だった・・もしかして関数の中に独立したLoopを2つ入れて逐次で実行するのが許されてないのか?とか色々と考えて試してたので(-_-;)
 LoopAの部分も同じように判定の順番を入れ替えて・・

 ちゃんと最大連続を目指して動くようになった〜

 そしてここでようやく今回の本命「ちょっとだけ進化したMaximizer」の登場。設計前の狙いとしては「敵がリーチかかったら邪魔する」「指し手の判定基準を複数軸にする」ってのがまず思いつくね。
 リーチかかった場合、相手が指す前に、そこに置いてしまう。かかってない場合は相手に邪魔されにくいように、4軸の1つだけだった評価基準を4軸の合計(複数軸に対して連続数が増加する)に変更する。まあ、穴だらけだけどちょっとは見ごたえが出るだろうか?と

 ん〜?最後、なんでBのリーチを止めに行かなかった?という疑惑は残るけど、一応バトルっぽくなった、かな、と。
 堪能したので、ようやく次こそは本命のMinimaxに取り掛かります〜


話題その2 最古のChromebookでClaude.aiは動くのか?
 別に壊れても惜しくはない、ゆえにどこにもで持っていける、結果的に一番活躍している・・というマシーンChromebookC200MA。Paiza.ioが使えるのでまだ現役でいられるんですが、困ったことに生成AI系のWEBサービスが使えない。
 というか、最初は使えてたのにどんどん使えなくなってきている。これは一体どういうことなのか?検索しても(こんな古いマシンを使ってるやつがいないからだろうか?)同じようなことで困ってるやつさえいないらしく全然引っかからない。
 ChatGPT本家はもとより、BardにBing。頼みの綱だった派生サービスWrtnも駄目。それどころか翻訳サイトDeepLでさえも接続拒否されてしまう。うーん、セキュリティ上の問題で古いブラウザは拒否されるんかなぁ・・・せめてブラウザだけでもバージョン上げてもらえんか?Evil Googleよ。これじゃあChromebookの大量廃棄問題が解決出来ないぜ・・・

 という訳でGPT系ではないClaude.aiであれば使えるのでは!?

 試すのを楽しみにしていたので、宿直Aに入るなり実行。結果は・・!

 上の画像の通りでやっぱり駄目でした。C200MA、反応もC100Pよりもキビキビしてて快適なのに勿体ないなぁ・・



2023/11/30 
話題その1 オセロの続き ちょっとだけ前進
 というわけで続き。教科書では重み付けの配分を変えて実験するという流れ。で、その重み付けを変えたリスト?マップ?を生成するコードっぽい。とりあえずいつものように日本語訳をつけて・・と

 IfのElse節が無いんだけど?ということでClaude先生に聞いてみる。ほ~そうだったのか。'()で良いとは勉強になった(忘れてただけかも知れんが)。

 あと、Letブロックだけの存在も違和感バリバリなので聞いてみる。へぇ~なるほど。なんかコレも前にCametanさんから教えてもらった気がするが復習ということで。Defineで良いな

 Incf(インクリメントする)がRacketには無いのでイッチョClaude先生に作ってもらう。あ、マクロになるのか・・そういえばSet!の話題でそういうのがあったな。ついでに「基本的にどういう時にマクロが必要になるのか?」ってのをパターンで聞いて見る


 ふ~ん・・・なんか納得できないので、後から全部関数で出来ないのか試してみよう。マクロは全然活用できてないしなぁ~

 そんな感じで一部Racketに翻訳したところで時間切れ。続きは日曜日の宿直だな。

話題その2 Lua実行環境構築がダルい
 気分転換にLuaの実行環境でも構築してみるか?と挑戦したけど・・EXEファイル(Windowsなのだ)からの実行でインタプリタモードはそのままでオッケイなんだけど、エディタと連携させて・・・となると思ったようにならないのだった。Vscodeの拡張機能を入れて云々・・エラーで動かねぇ!専用の統合環境があるってのはやっぱり楽で良いなぁ


2023/11/29
話題その1 オセロの続き Void問題解決(-_-;)
 オセロ、次に行っても良かったんだけどどうしてもVoidが気になったのでちょっと調べる。Forの中でオプションによって返り値がどう違うのかをチェックしたり。GPTによるとBreakで止めてるからVoidになるとか言うんだけど、Finalでは単にその条件で最後、Breakはその前が最後になるだけじゃないか。まあ、覚えておいて損はないから良いけど。ちなみにGPT4で#:finalについて聞くと「Racketにそんな構文は無い」と言われたりする

 うーん、コレよコレ。なんでVoidになるのか・・・

 しょうがないのでGPT4を超えた!という噂の生成AIサービスClaudeを使ってみる。もしかしてお金がかかるのかな?と思ったので聞いてみたけど無料らしい。AIが嘘ついていたら・・感動するけど

 コードを放り込んで聞いてみると・・えっ?値が返ってないとな?いや、最後にBest-move返してると思うけど?

 まさか・・ということでForで動いているMinimaxをチェックしてみる。あ・・最後のBest-moveってForの中に入れるんじゃなくて、その上のLetに対して返すものだったのか(-_-;)

 というわけでちょっと書き換えたら・・ちゃんと最後まで動きました。ゲェェ!まあ、諦めなくて良かったけど。あ~そうか~・・Forはデータの書き換えだけやってたのか。これは恥ずかしい!勉強になったけどw

 見事に問題を指摘してくれたClaudeだけど、他のAIだとどうなんだ?と気になった。まずはGPT4を利用しているというWRTN。確かに分かってから読むとちゃんと指摘されてると分かる、が、Claudeほどの平明さがない。

 上の文章の後に出されるコードも(貼り付ければ分かるけど)、一見元のコードとどこが違うのかが分からない

 次に本家ChatGPTはどうなんだ?こっちは無料なのでGPT3.5。これも文章だけ読むとイマイチ分かりづらい

 意外にもこっちの方がWRTNよりも、まだ、多少わかりやすい。と言うか、分かった後ではよく分かる(Claudeよりは不親切だが)。
 というわけで、無料で無制限だし今後はClaudeをお供にして学習を続けようと思いました。

 ようやく次の段階へ進む。重み付け評価関数vs先読み関数。書籍では重み付けが勝つようになってるんだけど・・・あれ?
 まあでも疲れたので今日はこんくらいにしておくか~

話題その2 Pickcodeを試してみる
 Discoverニュースにて「JSやPythonにラクラク移行できるビジュアル言語が登場」というものがあったのでちょっとチェック。

 こんな感じで・・まあ全編英語なんですが。チュートリアルが充実してるので、気分転換にちょこちょこイジってみるのは面白いかも。

 具体的なサービス(ChatBotとか)を実際に作ってみるチュートリアルとかが用意されてるので「何を作ったら良いのか」って段階でも為になるかも

話題その3 Tic-80の使用可能言語
 JSも使えるという話だったのですが・・・チュートリアルをやろうと思ってWIKIを開いたら。えっ!?PythonはまだしもSchemeも使えるのか!これは面白いことになってきましたよ?

 うーん、でも実際のところ・・・チュートリアルはほぼすべてLuaなんですよね。ザッと見たところSchemeは0でした。ということはやっぱりLuaで書けるようにならんとイカンかなぁ



2023/11/28 
話題その1 五目並べの続き 一応動くようになる
 乱数発生問題。今となっては「意味ねぇ」となるんだけど、掃除をしながらアイデアを思いつく。ターンごとにランダム用のカウントを増やして、そのカウント分だけ乱数発生を繰り返すようにすれば、ちょっとでも乱数っぽくなるのではないか?と。カウントは盤面を表示するたびに増やすということでやってみた

 こんな感じでRandom-count分繰り返すようにすれば、とか思ったの。

 カウントが増えれば発生する乱数も変わるでしょ!けど、結局ゲームスタート数たびにRandom-countも初期化されるので同じやんけ!と(-_-;)

 結局Random-eltはもとに戻しまして

 メイン関数Gomokuを書いて・・・今回はお互いにRandom同士で対決。実行!

 おお〜・・ちゃんと勝敗が決するまで動いた!
 おっ!?おかしい!Wが5になってるけど直線で5ならんで無いよ!うーん、評価部分に問題ありか(-_-;)

 この時点でおかしくなってる。

 そしてこの時点で5になるということは?24の部分が25と判定されてるとか?けどそんな事ありうるのかなぁ・・そこまでちゃんと斜めは判定されてるのに・・まあ、次回調べます

話題その2 Tic-80、LUAだけじゃなくてJSが使えるのか・・


 Tic-80での8bit風ゲーム作り、LUAを覚えないといけないのは面倒というか、その後応用が利かなさそうなのがなぁ・・と思ってたんですけど、Javascriptが使えるなら学習に無駄がないな?まあ、ゲーム作りがそもそも無駄では?という話もあるが。というわけで次はJSかなぁ



2023/11/26
話題その1 オセロの続き αβカット、一応動くようになる
 別の宿直でボーッとしていて「そもそもオリジナルのあのIF入れ子の形がややこしさの元では無いのか?フラットに出来るのでは?」という考えが湧いて本日はまずそっちから。要するにCondを使ってみようということなのだけど。入れ子の部分は条件のAndでつないで・・個人的にはこっちが好みだなぁ

 今回は例のValues問題をなんとかしたい。実はすでにアイデアはあるのだ。五目並べで使った例外処理。あれを使えば・・

 エラーの内容はValに再帰のパターンでValuesで多値が返ってきたり、Ply=0でCount-differenceでただの数値が返ってきたりするのが問題であることを示している。今まではIfやらで場合分けをしようと思ってたが出来なかったのだけど、例外処理を使えば・・ふふふ

 Racketでの例外処理With-handlersを使ってエラーが出る再帰を使ったValへの束縛部分をカバーする。もしもエラーが出た場合は多値の予定なのに数値が返ってきてるはずなので、そこでCount-differenceを使って数値を返すと。
 ちなみにRacketにもGuardがあるのかな?と思ったけどありませんでした。Schemeと同じ書き方では無理なんか。

 Searcherも多値用を別に作る。

 で、動かしてみると・・うーん、最後の最後で終わってるのに判定が出来てなくてエラーが出るんだけど一応最後まで動いている!やったぜ!これでようやく次へ進める(^_^;) 二ヶ月くらいValues問題で止まってたんじゃないかな?我ながら執念深いね

 というわけで総集編。Forを使ったら簡潔だけどVoidが出るので使えない。あ、このVoidが出る原因もちゃんと調べないとなぁ・・

 Let loopの普通の再帰であれば一応動くというバージョン

 Condでフラットに条件をつなぐ個人的に好みのパターン

 多値を返しても大丈夫になった最新パターン。

 まあ時間はかかったけど勉強になったかな(^_^;) 問題はαβカットの仕組みがイマイチよく分かってないという点なのでこっちも次回はちゃんと理解しよう。

話題その2 ゲームエンジン「Gamemaker」

 先日、Chromeを開いてDiscoverを見てたら「Gamemaker」というゲームエンジン?開発環境?の話題が出ていた。なんか他の言語に移行しやすくて最初に挑戦してみるのにオススメなんだとか?そんな謳い文句よく聞くけどなぁ
 2D専用(そして基本無料)ということで気分転換にインストールしてみる。そのままだと全然使い方が分からないが・・

 チュートリアルにはYoutubeを使った動画が用意されていた。これが(英語だけど)すごく分かりやすかった!
 ふんふん、まずは画面を用意して登場するオブジェクトを用意、インスタンスで好きな数だけ表示してそれぞれのオブジェクトに移動用パラメータとか別のオブジェクトとの接触判定とかを設定していくのか・・えっ?もしかしてこれがオブジェクト指向ってヤツ!?
 特に気に入ったのが、ノーコードでブロックを組むように作るモードとちゃんとコードを書いていくモードがあること。チュートリアルでも画面分割して同時に並走して解説してくれてるのがナイス!やっぱりコード書かないとプログラミングしてる気分になれないもんなぁ(古いタイプ)

 21世紀、老後の趣味は盆栽じゃなくてプログラミングだぜ!お金かけずに死ぬまで遊べるぜ~
 
名前:
コメント:

※文字化け等の原因になりますので顔文字の投稿はお控えください。

コメント利用規約に同意の上コメント投稿を行ってください。

 

※ブログ作成者から承認されるまでコメントは反映されません。

  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「プログラミング」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事