2024/12/06
話題その1 Rust移動不可述語
ゲームーバー関数を書いてみるか・・。とりあえずイテレータを作って、最上段である0-20の間にXが発生した時点で終わりにするとしよう(微妙に手を抜いている)。こんな感じかな?Claude先生~
全然駄目でしたw なるほど、AnyとかAllの時点でMapと同じでイテレータを引き受けて1つずつチェックしてくれるのか・・
しかしこの短さ・・うーん、これはちょっとRust良いかもな~
続いて、移動コマンドを入れた時にそこが画面端だったりXだったりしたらスルーするための述語を書くか・・実際の数を列挙してみないとピンと来ないので手書きで・・
うーん、こんな感じ?
こっちが添削後
本当に助かってるのでちょっとお礼を述べてみる。アカウントで過去のChatは全部保存されてるみたいなので、絶対に無意味というわけでも無いかも知れない。
というわけで、移動不可述語は多分完成した
話題その2 Godot入門本
ちょっと前に高校生(15歳w)が書いたGodot(軽量ゲームエンジン)の入門書が出るという記事を読んだのでリクエストしておいたが、忘れた頃に入荷のお知らせ。例によって新刊で学習関連の本は積極的に買ってくれる交野市なのだった・・スゲェぜ交野市!
RustのCrosstermが一段落したら、ちょっと隠し玉のゲームエンジンに挑戦して、次はGodotかなぁ・・1年はかかりそう
2024/12/05 テトリスモドキ、下降関数の続き
まずは固定化されたブロックのIndexを取得してと・・
可動ブロック下降後のCellsを作るために、一旦可動ブロックを消したCellsを用意して・・と
下降後はブロックのIndexを全可動ブロック幅分増やす
最後にIf節にブチ込んで、固定ブロックに当たるようだったら動かさずに固定化・・
とりあえず出来た気がするが・・長すぎる!!ちょっとこれは想定外・・関数化してないってのもあるけど、ちょっと1関数1画面ルールに明らかに抵触してますよコレは(-_-;)
ま、あくまで今回Rustの練習なので次回には気をつけるってことで。
よし!じゃあClaude先生に放り込んで「おかしなところが(あるんやったら)指摘してみィ!」と。
多数あるんか~い!
なんだ?たった5つじゃないか。そんなの僕基準で言えば「エクセレント」ですよ
ここはOK
unwrap_or(0)か・・デフォルト値とか今回のケースでは必要ないと思われるが?
あ、Pinned_blocksの型指定が間違ってるわ。でもケアレスミスのレベル
あ、Mapのラムダ、カッコが閉まってない、ケアレスミス
Map内のIndex、Bodyでは*がいらないか・・イテレータじゃなくなってるからかな?
Colletctの()がない
&block_indexs.mapの&がいらんか~・・あ、Iterにしないといけないからか。外部の変数から持ってきてるからいると思ったのだが、Iterにすればそれもいらんし。
Null_cells、Map内のxはIterを受けてるので&がいると思うんだけど無くても良いのか?
あれ?Null_cellsのMatchが・・x.as_str()でパターンマッチもIfガードを使ってない。
うーん、なるほど・・ラムダで参照をマッチさせることで取り出しても良いし、そのまま持ってきてAs_str()でキャストしても良いということか。こっちがよりRustの思想に沿ってるって言うならこっちを使うべきかな
あ、ここはもう全然ダメ。最初のIf節部分はちゃんとClaude先生に聞こう。
あ~なるほど。Containsって(セット).contains(含まれるかチェックしたい要素)なのか。逆やん!
ここは良さげ。
ってことで・・手に負えないほど間違ってる!って事は無かったな(^_^;)
一応、これで下降関数は出来たような?
あと、左右へのブロック移動、画面の両端ではそれ以上左右移動できない、回転後に両端をはみ出すようには回転出来ない・・ってのも追加しないといかんか・・
その前にゲームオーバーを作るかな
2024/12/04
話題その1 無理やりCLIテトリスの続き
じゃあブロック落としやっていきましょうか・・
うーん、CellsをMapで走査するってのは分かる・・その元になるのがBlock_indexsだから・・Mapの入れ子になるのかなぁ?分からん!
質問したら・・あ、そうかContainsだ!お前何回同じ事聞いてくんの?とか思われそう。なるほどContainsだったら入れ子にしなくても、元々対象が多数だから便利だな。
こんな感じですか?と上のコードを送り込んだら下のような修正を受けて返ってきた。書くのに時間をかけすぎたせいか、New_cellsを作ってる中でNew_cellsを呼び出しているというw
しかしそれに気づいていても書くことは出来なかったであろう。なるほど、Indexを調べずに全部変換するつもりなのか・・けど、これだとXで固定されてるブロックも変えられてしまうから変更しないと。
ところで・・上のコードはClaude先生に無理を言って、僕の書き方の続きを書いてもらったんだけど、最初にClaude先生が提案したコードはこう。
いや、そーなんですよね!何度も何度も任意の座標の要素を置き換える・・ってのを書くなら関数にせいよ!と。でも、最初こんなに何度も必要になるとか思ってなかったのだ。
もう一つの書き方としてZipを使ったもの。解説を見ると、前後のイテレータを同時に回してペアを作ると・・・それをForで引数(?)を2つパターンマッチみたいな感じで分解してベクターを置き換えていくというね・・1つ前のコードと違って必要なところだけを書き換えるから効率は良さそうだな?
あと、今更ながら提案されたベクターの要素入れ替えメソッドがコレ。うおお!これならWIDTHをプラスしたIndexと入れ替えるだけで済むじゃねぇか!もう今さら変更するのは面倒なので、次作以降で使うとしよう。一度、ザッとで良いのでメソッドを網羅的に見ておくべきだなぁ。
今日は日記を書きながら理解に努めてたので、時間があった割には作業は進みませんでして、以上です
話題その2 Gauche Windows版
Cametanさんの記事でWindows版の存在を知る。おお〜Racketはちょっと重たいし、Paiza.ioはオンラインでないと無理。オフラインで使える(であろう)Scheme系の処理システム良いじゃないですか〜。明日ちょっと試してみよう。
2024/12/03
次は・・そうか、可動ブロックと固定化されてるブロックの衝突判定をしないとイカンか?ということで、固定されたブロックは"X"にするとして・・例によってそのIndexを取得すると
で・・可動ブロックと固定ブロックが移動後に重なる場合は可動が固定化されるわけだから・・そのためのイディオムを教えてもらう。うっ!解説が欲しい。
あ、そうかw メソッドチェーンなので前から流れてきたデータをラムダ式の引数に取るんだけどLisp系のクセで後ろから見つけようとして混乱していたw。AllについてはBoolを返すわけね、なるほど
後は・・雑魚ブロックのIndexに軸ブロックのIndexを足さないといかんのでVecへの追加方法を・・今回は順番はどうでも良いのでPushで良いだろう。
で、こんな感じか。あれ?そう言えばこの判定って「ターンの終わりに可動ブロックを強制下降させる」時に必要なのであって、回転時には不要だよな?勘違いしていた・・とりあえず保存しておく。
回転の時に必要なのは座標が被っていた場合は回転させずスルーするって処理だった。ということで・・回転処理をIf節にぶち込んで・・こういう感じかな?これをClaude先生にまるごと渡して添削をお願いする
うーん、ここはOK
ここも基本OKだけどCells.iter()で良いのか・・
ここは問題あり。All内の&有り無し。なんでItem2だけ&がいるのだ?
これなら両方とも同じだから分かる。じゃあなんで最初にClaude先生が提示したのには&があったりなかったりだったのか?
え?型指定での&&は不要なのか・・
じゃあ実際のところ等価なのか?どうなってんの?
うーん・・もしかしてRustコンパイラの方で気を使ってくれてるってこと?
じゃあどっちも&無しでも良かったって事か?うーんまあ、でも本来はIterしたら&しないとイカンと言うのは勉強になったな。ベストプラクティスいただきました
ま、ここは合ってるな。けど、気になるのはnew_cells.iter()...の流れで破壊的な変更が行われてるって事か。関数言語的には毎度新たな変数を作っていくべきなんだろうけど・・これがRust的なのかなぁ
あ、やっぱり非破壊が推奨なのか・・ってコードを見たらコレも結構破壊的じゃねぇかw これじゃあ別に前のバージョンから戻す必要もねぇなぁ
あ、なるほど~Σ(・∀・;)
MapとFor_eachどこが違うのかと思ってたけど、Mapだと最後にCollectしないといかんのか!これでイディオム的な定番の書き方を覚えることが出来そうだ。Iter_mutの使い方もOK。これなら今後は(面倒なので次のプログラムからは)関数言語的な書き方が出来そうだ。うーん、最初からClaude先生に「関数言語的なアプローチでやってます」と宣言しておけば、そういう風にヒントをくれたんだろうけど・・ハッ!?こ、これが静的言語スタイルにおける型指定の必要性をメタ体験したという事か。
後は最後はVecにして返すと。Iterしてたらコレは忘れてはならぬ
ここからは夜の部
というわけで、添削済みのコードが一応完成した。
次はターン終了時にブロックをまとめて下降させる部分を書くわけだけど・・既存の可動ブロックを一旦全部消したWorldを作りたいのでMapとMatchを使った方法でやりたい。Map内にMatchを入れての書き方ってこれで大丈夫かな?と添削依頼。ちなみに、先程のMapの時はCollectでないと・・とかってのは、この学習日記を書いてる最中に気づいたことなので、この時点では突っ込まれてます
基本構造は大丈夫だったけど、Stringに変換するって部分が・・・まだ文字列リテラルの事分かってないな
要するにこうせいと
で、明日はこの続きからDown_blocks関数を作っていきます。
いや、今回は(正確には日記を書いてる翌日だけど)実にスッキリした!参照と非破壊、ココが分かってなかったら気持ち悪かったんだなぁ~
2024/12/02 入力部分
というわけで、前回までの添削などを経て回転関数が多分完成。ほぼ同じ内容なので逆回転も書いたんだけど・・本来なら左右を判定して1つの関数で書くべきなんだけど、今更面倒なので次回作を作るときには気をつけます。
いよいよコマンドを入れてブロックを操作する部分を書こうと。そのために標準入力のイディオムを教えてもらう。前に書いたけど当然のように忘れてるので・・。Expectのエラー表示じゃなくて、規定のキー入力以外は問答無用で行落としをしたいんだよなぁ
質問をするとUnwrap_orを勧められるが・・今回はちょっと違うな。便利そうだけど
Matchを使った方法・・おお、この流れで良いんじゃないか?
ところで今更だけどParseって何なんだ?
えっ!?ってことは変数の型指定をしておけば自動で変換してくれるってこと?
これは便利すぎる!これがあるなら面倒な型指定もやる値打ちがあるよなぁ
じゃあ入力部分を具体的に・・こんな感じかな?ってココで時間切れ。明日はこの続きを・・。
関係ないけど、Paiza.ioのテーマ変えました。Vibrantink、見やすくていいなぁ
2024/12/01 添削
あ、そうか・・UnwrapしないとSome(x)で返ってくるんだった
・新規で変数を作る場合には型指定をしないとイカンっぽい
・valueの参照外し、何故か不明なので解説を聴くか・・
なにコレ、超わかりやすい!そういうことか〜
・新規で変数を作るときには型指定をしないといけない(多分)
・そう言えば&indexの理由もよく分かってない、聞くか〜
えっ!?これが一緒って事は・・ただ単に比較の型を合わせればいいって事?
&&ってなんだ?と思ったけど・・iterの時点で参照になってるから、それとマッチするためにもう一枚&が必要ってことか。参照が累積するとか面倒すぎる気がするんだが・・
これ、人間が管理出来るんか〜?出来る限り参照が累積しないように気をつけるしかないな・・
最後はヨイショして締めました。気のせいか質問しっぱなしだと「その通りです!」とか「実に鋭い質問です!」とか言ってれにくい気がするんだよな〜・・世話になってるしご機嫌とっておかないと
2024/11/30 回転の続き
とりあえず座標回転はこういう感じかな?もっと一般化出来るのかも知れないけど・・
ついでに左回転も
上で作った回転関数をVecに対応するとなると・・Mapかな?と思うので使い方を聞いてみる。ふんふん・・・
流れとしては、まずはCellsのベクター内の"O"を"."に置き換えるってことなので・・どうするんだっけ?
ああ、enumerateでインデックスを付けたタプルを作って、Valueが一緒のインデックスをcontainsで保存してFor_eachで置き換えると・・。これでVec1全体が返ってくるのか・・。FilterとContainsで集められた要素ってのはVec1とは別に保存されたデータってことになるのかなぁ
うーん、上のコードでは&indexとはしてないのにこっちでは&を、しかも数字につけてるのか・・なんで参照なんや?
今回関係ないけど複数の要素を同時にチェックするのってどうするんだっけ?あっ!これ数日前にも見たじゃないか。うーん、この記憶力w
というわけで、とりあえず現時点での知識で書いてみたけどどうだろう?
なんか思ってたよりも無茶苦茶長くなってしまった。テストするのも面倒なので、最初からClaude先生にこのコードを渡して「文法的に問題ある部分ありますか(まるで「無いだろ?」と言わんばかりに)と聞いてみる
結果、デデン!修正版が返されました。うーん、随分違う気がするw。明日は自作と修正版の比較検討をやりますか~・・(^_^;)
2024/11/29 Rustテトリスモドキ、回転関数
回転する軸を特定するために@の場所を探したい・・さてどうすれば?はーん、なるほど便利なメソッドあるじゃないか
出てくるのはSome(N)型なのだが中身を取り出すには?お~なるほど!
続いてモブブロックの座標を調べたいのだが?ほお・・ちょ、ちょっと説明してほしいな
おお~!ちょっとLisp系のクセで内側から湧き出してくるイメージがあるんだけど、こっちはこっちで良いじゃないか
では中心の@から見てマイナスNならF・・って条件をMatchで作りたいが・・ガード節の意味はよくわからんが|X| if x== center -1 => ...って言うラムダ式のイメージか。
おー!こりゃ良いや!Rust・・ちょっと見直したぜ
明日はいよいよ回転関数完成かな?
2024/11/28
その1 エラーばっかりなのでClaude先生に添削してもらう
とりあえず画面が表示できないと心が折れそうなので一旦実行!
が・・うーん、エラー。どうも高階関数で食わせるデータの型が違うと。そんなの言われても(ちゃんと理解せずにコピペした部分なので)困るっての
ふーん・・形としてはany(|cell| cell == x))の方が相応しいな・・そもそもConditionとしてNot_containsに収めるBoolがNotが真になってるってネジレがややこしいわなw ところで&x.to_string、なんで&がいるのか?
あ~なるほど・・Mapで取り出した一つ一つも参照でないとイカンと・・これ、本当に世界で一番愛されてる言語か?面倒臭すぎる
結局、この「型を最初から合わせる」が正解やな。文字列と文字列リテラルを区別してなかった僕が悪かったです。
で、もうコンパイルエラーのキリがなので、とりあえず動くものに添削してもらって自作のと比較しようと。諦めた!
こちら自作のメイン関数
そしてこちらが添削版。ほーん・・
・Cells変数を自作版では作り直してるんだけど、添削版では普通に上書き
・elseは何もしないのが自作、else節でもCellsをしかもCloneして返してるのが添削
ってのが違いか。elseでも返すってのが致命的っぽいな
はい、ではこちらが自作のDraw_world関数。
こちら添削済み。うーん・・ここはさすがに問題ないかな
自作
・最後、Cellsを返すのに自作はスコープから外れてるわw。更に返すときには.to_vecしないといかん・・と。いや、最初からVecになってないか?と、思ったけど・・あっ!仮引数で&[String]にしてるからスライスになってるのか・・マジで面倒くさいw
データ書き換え部分の自作
・なるほど、ここも&[String]で受けてるから最初にVecにしないといかんのか。
さて、ここまでやってPaizaで実行・・したところ、Rngが依存関係が構築できてないので使えないという(PaizaではTomlの編集が出来ないので依存関係の追加は出来ない・・っぽい)。Random()も使えないのに、こっちも使えないとなるとPaizaではRustは乱数を使えないということ?まあ、自分で実装したら使えるんだけどサァ!
というわけで舞台をRustPlayGroundに移して実行。
出来た!
けど・・なんか思てたんと違う(^_^;)
明日こそは回転やります!
話題その2 もっとRustのコードをくれ!
思うにもっとRustのコードに触れないと上達せんな、と。だが、例のマイナビサイトのバックナンバーを見ても興味の湧く例が無いんですよね!暗号とかビットマップ表示とかシンセサイザーとか・・
で、確か以前ナイスなサイトを見つけたよなぁ・・とブックマークを漁って再発見。コーディングホラー?
例のこの本に掲載されてるプログラムをRustやらRubyやら様々な言語に片っ端から移植したものを載せてくれてるというもの。お~こりゃ良いや!これを使って片っ端からCrossterm対応にしていくのも良いかもな~
2024/11/27 まだRustでCLIテトリス
とりあえず今までのところを、未定義の関数なんかはコメントアウトして実行しておかしなところをあぶり出してみる(これがコンパイル言語だと面倒でしょ?Paiza.ioとかRustPlayGroundなら楽だけど)。
で、ここでいきなりエラー。うーん、なんで?Claude先生~
書き方も間違ってるんだけど、関数型言語的な運用をするならCellsをコピーするべきだと指摘される。ふーん・・でも、そのための借用では無いのか・・
同様にVecの書き換え関数もエラー発生。こちらもコピーを作って、そこにStringとして書き込みをしないといかんと。てっきり"@"でStringになるかと思ったけど、そうか1文字だとCharになるんか~ 違うわ文字列リテラルになるからか
なるほど・・参照の意味を間違ってたな。てっきり新たなコピーが作られてるのかと思ってた(参照って名前なのにw)。後はReturnが暗黙って言うのはReturnを書かないで良いだけのことだったのか・・(行末;を書かないのはこのためだったのですね)
今日は折角の休みなのに、たったこんだけで終わってしまった。明日は今回の失敗を活かして回転関数を作りますわ
2024/11/26 RustでCLIテトリスの続き
IF節ってどういう風に書くんでしたっけ?
・・などと言う超基本的な事をClaude先生に尋ねながら
うーん、こんな感じかなぁ・・とMainを書いて
乱数で発生ブロックの種類を決定して、Vecを書き換える関数にCellsと座標データを送信。これならブロックを定義しなくて良いのでナイスアイデアじゃないか!?ブロックの中心になる部分は@、ほかはOにすることで当初の計画だった
11
①
1
とかしなくても良くなった!というか、テトリスって全部のテトリミノが4つのブロックで出来てたんですね・・今更気づいた(パズルゲームは一切プレイしない主義)。この仕様のおかげで座標の引数を固定出来て非常に助かったわ~
さて、ここまで来てハッ!と気づく。ブロックを追加した関数からMainに帰りたいわけだけど、Mainって引数取れないっぽいか?単純にMainに戻っただけだとCellsが初期化されてしまうわけで・・となると、初期化はMain関数の外でStaticだったかで行い、それを破壊的変更で使い回すということに?うーん、そんな馬鹿な!Claude先生~!
あ、そうなんだ(^_^;) なんだよ上手く出来てるじゃないか~
後は例によって所有権とか変数の上書き(オーバーライド?)とかの問題か~・・でも明日は、楽しそうなブロック回転関数を書くとしよう。