「元に戻す」のしくみ 【ゲーム・プログラミング】【JavaScriptサンプル】

แชร์
ฝัง
  • เผยแพร่เมื่อ 15 พ.ย. 2024

ความคิดเห็น • 77

  • @paseri9697
    @paseri9697 2 ปีที่แล้ว +1

    関数の有用性と単機能にしておく必要性がよくわかりました

  • @ポルポト-q6y
    @ポルポト-q6y 3 ปีที่แล้ว +21

    プログラミングに初めて興味を持ちました。ありがとうございます。
    おかげで学校に遅刻しました

    • @zako57
      @zako57 3 ปีที่แล้ว +3

      ポルポトなのに学術的なものに興味を持つな

  • @contactMiu
    @contactMiu 3 ปีที่แล้ว +44

    「1つ戻す」機能を呼び出すたびに毎回、初期状態にいったん戻してから
    最後の操作の一つ手前までの操作を全部もう一度やり直すってことですよね。
    スマートではない気もしますが、実装は簡単・単純・確実ですね。

    • @kussytessy
      @kussytessy 3 ปีที่แล้ว +9

      ソースコードをよく見ると実は「一つ進む」も、毎回、初期状態にいったん戻してから
      最後の操作の一つ手前までの操作を全部もう一度やり直しているっぽい。
      つまり進むのも戻るのも同じロジックで動いてる。

    • @てんぷら-t6u
      @てんぷら-t6u 2 ปีที่แล้ว

      全てを初期状態からの差分で表してるってことですね。

  • @yossanyossan2798
    @yossanyossan2798 3 ปีที่แล้ว +20

    このイントネーション好き

    • @vaio-i1r
      @vaio-i1r 3 ปีที่แล้ว +1

      わかる、なんかこのイントネーションを聞きに来ちゃうww

    • @contactMiu
      @contactMiu 3 ปีที่แล้ว

      「プ↑ログラム」は私も初めて聞きました。どこ出身なんでしょうかね。
      ちなみに英語では「プロ↑グラム」です。
      「プ↑ログラム」は、もしかしたらここでしか聞けないかもしれません。

  • @artificiallyintelligence4287
    @artificiallyintelligence4287 3 ปีที่แล้ว +4

    ゲーム以外でも応用できそうですね
    参考にさせて頂きます!

  • @yllwtl
    @yllwtl 3 ปีที่แล้ว +23

    0:21 真夏の夜の…

  • @psytofu94
    @psytofu94 3 ปีที่แล้ว +3

    今回の合成関数、すごく参考になりました!
    他にも。こうやれば実は上手くいく、みたいなお話を聞きたいです!

  • @sage_goes1504
    @sage_goes1504 3 ปีที่แล้ว +11

    趣味で絵を書いててふと気になったのですが、ペイントツールの「乗算」や「オーバーレイ」などの色の計算方法の解説をしていただきたいです!
    色のRGBAから有限体かなんかの代数構造を定義して演算を定義してるんでしょうかね?

  • @エメラルド-h9h
    @エメラルド-h9h 3 ปีที่แล้ว +14

    独学でJS入門をやり終えた人にはちょうどいい難易度。なかなかこういう生きた教材はない

  • @goh0x00
    @goh0x00 3 ปีที่แล้ว +2

    計算量が増えそうだなと思ったけど、大体は戻れる回数に上限持たせるだろうから、最古の状態を初期ステータスに随時置き換えていけば、計算量の上限は一定になるから問題なさそう。勉強になりました。

  • @山田太郎-c3u2b
    @山田太郎-c3u2b 3 ปีที่แล้ว +14

    これってdrawの度に関数を合成する回数分計算するから、n回まで描画するときO(n^2)かかっちゃいますよね…。ただし、メモはしてないのでメモリはあんまり使いませんけど。

  • @umano9072
    @umano9072 3 ปีที่แล้ว +4

    戻すたびに初期状態からやりなおすようなやり方だともとに戻す操作が激重になるのが予想されるので、
    n回関数を適用する度結果を保存しておくんでしょうか。メメントパターンですね

  • @maikoando1542
    @maikoando1542 3 ปีที่แล้ว

    愚直な計算しか思いつかなかったので本当に目から鱗でした、素晴らしい動画をありがとうございます!!!!

  • @shyxdras
    @shyxdras 3 ปีที่แล้ว +2

    似た考え方はEvent Sourcingパターンで理解していましたが、根源的に合成関数まで帰着出来ることに気づかせていただきました。ありがとうございます☺️
    自分も踏んだ経験があるのですが、良くある方法は逆関数的な手法であるがゆえにルールによっては実装出来ない事すらありますよね(なんなら今回の倉庫番ですら不可能)

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว +1

      荷物を荷物で押した瞬間、「あっ……」って気づきますよね🐤

  • @もりもり-o1w
    @もりもり-o1w 3 ปีที่แล้ว

    ほかにも実装法がありそう🤔
    各ゲーム状態のログをとったり、
    元に戻すための計算を実装したり(アプリによっては元に戻せない状態のことも!?)
    画像編集ソフトとかどうやってるんだろう
    数学的な実装?で面白かったです😊

  • @hpchiyoda
    @hpchiyoda 3 ปีที่แล้ว

    面白かったです、毎回計算させるんですねー。
    これくらいならいいけど戻る時だけ再計算させるとか工夫が入りますね

  • @砂糖学
    @砂糖学 3 ปีที่แล้ว +7

    イントネーションをイン・トネーションっていう強強エンジニア臭がしますね。

  • @lilykily6643
    @lilykily6643 3 ปีที่แล้ว +34

    「行動(やったこと)」のログをとるのが合成関数、
    「状態(行動した後の結果、座標)」のログをとるのが愚直な方法ってことかな?

  • @hondasuzuki7345
    @hondasuzuki7345 3 ปีที่แล้ว +29

    数ある日本語のプログミングのチャンネルで唯一このチャンネルだけがきちんとプログラミングしてる。他は金の話ばっかでつまらん。

  • @d1Prczr6b29eM82Y
    @d1Prczr6b29eM82Y 3 ปีที่แล้ว +11

    なんかめっちゃ重そう

  • @tomokazu2023
    @tomokazu2023 2 ปีที่แล้ว

    どうやって戻すのかの方法も幾つか出て来ると楽しそうな気もします。

  • @kussytessy
    @kussytessy 3 ปีที่แล้ว +1

    「Xする」
    「XしてYする」
    「XしてYしてZする」
    とした状態で、一つ戻すためには「XしてYする」をすればいいのか。
    そして、JSのような第一級関数をサポートする言語なら、[X, Y, Z]のような配列を持っておけばいいということか。
    「状態」を配列に持つのではなく「関数」を配列に持つってのが面白いです!!

    • @kussytessy
      @kussytessy 3 ปีที่แล้ว

      あと個人的に面白いと思ったのはここです!
      calculate(funcs) {
      let c = this.copy();←ここ!(副作用があることを無視すればわざわざcopyしなくてもよさそう)
      for(let f of funcs) {
      c = f(c);
      }
      return c;
      }
      game = new Game().calculate(inputs); ←new?!
      これ状態を保持しているんじゃなくて、実質的には「書き換えられたあとのゲームの状態」を返り値として受け取り、常にそれを自分自身に再代入し続けているってことですよね。世界観が関数型言語チックで目からうろこでした。

  • @マイケル-b1z
    @マイケル-b1z 3 ปีที่แล้ว +3

    複雑な計算式になってしまった場合、1つ手前の作業に戻す手順を組み込みたいと考えた際にはどうすればいいのでしょう?
    1個1個読み込む方法以外にも良い選択肢があるのなら知りたいです。

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว +1

      式が複雑になりすぎる場合は、おそらくその後者の方法が最適ですね🐤
      作業を行うごとに状態を全て保存(例えばペイントソフトなら画像をまるごと保存)する感じですね

    • @マイケル-b1z
      @マイケル-b1z 3 ปีที่แล้ว +1

      @@heron-no-suugaku なるほど...ありがとうございます。
      これから専門学生として学んでいく身ですので、自分でも調べつつこれからも参考にさせていただきます。m(__)m

  • @ノリ-f3c
    @ノリ-f3c 3 ปีที่แล้ว

    大変勉強になりました。
    ちょっと理解が出来なかった部分があるので教えてください。
    動かすごとに毎回copyで複製して複製したもので計算して返してますが、
    copyをせずにthisに直接計算結果を渡して繋いでいき、thisを返すのではいけないのでしょうか?
    もしcopyじゃないとダメな理由があるのなら教えて頂きたいです。

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว

      calculate関数のコピーのことでしたら、ご指摘の通り不要ですね。
      let c = this.copy(); ではなく let c = this; で十分です。🐤
      movePlayer関数でわざわざコピーしているのは、5:39からの実演のためですね。
      例えば関数s(南に1歩歩いた後の状態を計算する関数)が、引数をコピーせずに変更してしまうと
      s(i) という式で、iが変更されてしまいます。
      この実演において i は常に初期状態を表してほしいので、それではマズいのです。
      とはいえ通常はやらない手法なので、なぜコピーしているのか動画内で触れるべきでしたね🐤

    • @ノリ-f3c
      @ノリ-f3c 3 ปีที่แล้ว

      @@heron-no-suugaku
      iの初期状態がe(e(i)).drawとかした後でもiが初期状態を維持できるように、
      コピーした値で実演しているといった解釈であっていますでしょうか?

  • @YASSHY
    @YASSHY 5 หลายเดือนก่อน

    戻っているのではなく、最初からやり直している?

  • @織田豊-b3z
    @織田豊-b3z 3 ปีที่แล้ว +16

    この形で書くと毎回初期位置から合成関数を使って現在位置を計算してるから、操作回数が増えれば増えるほど計算量が増大するかと思うんですけど
    計算量を抑えるには、戻せる回数に制限を設けて、その都度初期位置を更新すればいいんですかね?

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว +25

      一定回数操作するごとにキャッシュcを作れば、戻せる回数に制限をかけずに、計算回数が増えすぎることを防げますね。
      i = 初期状態
      c1 = (f3∘f2∘f1)(i)
      c2 = (f6∘f5∘f4)(c1)
      c3 = (f9∘f8∘f7)(c2)
      ……という具合です。何手目から「元に戻す」をしても、0~3回の計算で済みます。
      ただしキャッシュが多いほどメモリを食うので、計算回数とのトレードオフになります。
      倉庫番くらいならまったく無問題ですが、ひとつの状態が1GBあるゲームとかだと問題になるかもです🐤

    • @youtsube09
      @youtsube09 3 ปีที่แล้ว

      @@heron-no-suugaku DBの増分バックアップのイメージに近いですね。

  • @amuttyan4206
    @amuttyan4206 3 ปีที่แล้ว +2

    動画内では説明がありませんでしたけど、この考えを応用すれば、例えば5手進めたうちの3手目だけを無かったことにする……なんてこともできそうですね。

    • @Bird.jp_Love-English-Fixes-plz
      @Bird.jp_Love-English-Fixes-plz 3 ปีที่แล้ว +2

      その場合って3手目が抜けた分元々の4手目が新しい3手目として認識されるみたいに、抜けた3手目以降の手が全体的に一つ繰り下がって、最終的な状態が再計算されるんですかね。
      Javascript 触ったことないのでトンチンカンなこと言ってたらすみません...

    • @amuttyan4206
      @amuttyan4206 3 ปีที่แล้ว +2

      @@Bird.jp_Love-English-Fixes-plz
      その通りかと思います。
      私もJSはほとんど触ったことがありませんが、この動画で紹介されている考え方はほぼすべての言語に適用できる内容ですので、その調子でどんどん結果を予想してみると面白いかもしれませんね。
      後で調べ直しているうちに分かったのですが、実際、一部の3DCADソフトなどでは手順のうちから任意の操作だけをなかったことにするという機能が存在しているものもあるようです。

  • @ksmr6252
    @ksmr6252 3 ปีที่แล้ว +1

    「愚直な方法」は、毎回盤面全体を保存、のほうがそれっぽいかも?

  • @SaajidAkram
    @SaajidAkram 3 ปีที่แล้ว +1

    Hi Heron, I am a big fan of you and I really like your content. I also learned a lot of Japanese new words from your channel. However, I think you made a teeny tiny mistake with the thumbnail used for this video. The thumbnail says [fn-1(...(f3(f2(f1(i)))...))]. We usually use the notion [n-1] with zero-based numbering system. But since you used f1 to denote the first function; the last one is supposed to be fn NOT fn-1. I am really sorry, I know this is a useless thing to add to the great content you've made but it is my wat to say I really like you and I really want to see more of your videos ... All the best

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว

      thank you, I very appreciate that you pointed out. 🐤
      I used n-1 to represent "undo", is this not a good representation?
      like below (i = inital state):
      current game state:
      fn( ... f3(f2(f1(i)))) ... )
      undo a time:
      fn-1 ( ... f3(f2(f1(i))) ... )

  • @ようつべ太郎-h9u
    @ようつべ太郎-h9u 3 ปีที่แล้ว +2

    o(y(i(o(k(o(y(i(i(114514)))))))))
    この動画を見なきゃハイパー愚直に盤面の列を残すところだったゾ

  • @ytakaoka6826
    @ytakaoka6826 3 ปีที่แล้ว

    Switchのスーファミエミュレータ、巻き戻し機能ってどうやって実現してるのかな…と思ってたけど、この仕組みなのかな。
    数秒に一度メモリのスナップショットを取りつつ、それ以外のタイミングではボタン入力だけ保存しておく。そして巻き戻しが呼び出されたらスナップショット時点から超高速でユーザー操作を再現する、と。
    いやでもスーファミくらいなら愚直にメモリ内容を毎秒保存した方が簡単なのかな。

  • @ay-oha
    @ay-oha 3 ปีที่แล้ว +6

    なんか、確率漸化式を思い出した。

  • @あああ-n2d8k
    @あああ-n2d8k 3 ปีที่แล้ว +2

    C++でオセロゲームに待った機能を付けたかったけど、合成関数の手法が使えそう?
    盤面を別の配列に全部記憶させる方針でいこうとしていたけど、どっちのほうがいいだろうか。

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว +9

      1手戻る機能をつけるだけなら、配列で全部記憶する方が簡単ではありますね
      ただ、例えばリプレイ機能をつける場合は、関数合成の方が良いですね
      関数合成した式がそのままリプレイデータとなりますし、
      式を "黒先攻,e6,f6,c4" みたいな感じに変換すれば、ツイッターとか経由でリプレイデータを共有可能になり、夢が広がります🐤

    • @NA-dd4qv
      @NA-dd4qv 3 ปีที่แล้ว

      オセロならいいけど、将棋は殺されるのでやめた方がいいですよ。

  • @mariomario6385
    @mariomario6385 3 ปีที่แล้ว +2

    Wordなどの編集ソフトは流石にこの手法じゃないよなあ、どうやってるんだろうか

  • @jojo-fz9cb
    @jojo-fz9cb 3 ปีที่แล้ว

    calculate関数の実体はどこにあるの?

  • @hijiki422
    @hijiki422 3 ปีที่แล้ว

    ありがとうございます!とても参考になります!
    ただ、少し気になるのですが、これを実際にゲームに使うとプレイヤーが試行錯誤のために移動した回数分だけの長さで合成関数が作成され、
    それが何万ステップともなれば、1歩動くたびにゲームが固まる、というような問題が起きる可能性はないでしょうか?
    もちろん、実際に固まるかは処理速度に依存するとは思いますが、どんどん1歩ごとの負荷が高くなるのでは?ということが気になりました!
    よければご教示ください。

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว +2

      100歩ごとにキャッシュを作れば、どんなに歩いても0~99回の計算しか要らなくなるので、固まらなくなりますよ🐤
      ただしキャッシュはゲームの状態そのものなので、メモリをそこそこ食うという欠点があります(例えばオセロなら8x8の盤面すべてをキャッシュする必要がある)
      オセロくらいなら無問題ですが、キャッシュひとつが1GBとかあるゲームだと問題になるかもです

    • @hijiki422
      @hijiki422 3 ปีที่แล้ว

      @@heron-no-suugaku なるほど!目から鱗でした!ありがとうございます~!応援しています😍

  • @user-lapustube13
    @user-lapustube13 3 ปีที่แล้ว

    RPGなんかのセーブデータは「愚直な方法」に近いでしょうか。
    特に「復活の呪文」は現在の進行状況 (操作キャラの位置や持ち物、フラグの回収状況等) をその時点で記録するんでしょうね。
    それならそれで、状況を再現するようにプレイすれば同じ復活の呪文が生成されるのか気になります。

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว +1

      乱数のないゲームなら、同じプレイをすると同じ進行状況になりますね🐤
      乱数があってもシード値を合わせればOKです

  • @FizmimiFiz
    @FizmimiFiz 3 ปีที่แล้ว

    リフレクション的なのかとおもた

  • @cmplstofB
    @cmplstofB 3 ปีที่แล้ว +3

    OSの操作を全部函数にすれば,OS単位で「元に戻す」みたいなことも可能……いや,非現実的すぎるけどw

    • @sage_goes1504
      @sage_goes1504 3 ปีที่แล้ว +1

      そういうアーキテクチャがあってもおかしくなさそう
      一つ一つの命令とか手続きが複雑になるから重そうだけど

    • @cmplstofB
      @cmplstofB 3 ปีที่แล้ว

      @@sage_goes1504 もし実現するとしたら,OSの上で動く応用プログラムにはOSが用意したAPI以外触らせない,とかそういう規制も必要そう。

  • @BadApple959
    @BadApple959 3 ปีที่แล้ว +4

    Commandパターンってのもありますね

  • @さかなくん-l5d
    @さかなくん-l5d 3 ปีที่แล้ว

    pythonでも動画をアップしてほしいです!!

  • @0x19840228
    @0x19840228 3 ปีที่แล้ว +8

    岡チャンネルにイントネーションが似てる

  • @ばく-n5g
    @ばく-n5g 3 ปีที่แล้ว

    操作を記憶する配列を作るから出るバグなので、単に人と荷の座標を記憶する配列をそれぞれ用意すれば良さそう?あくまでこのゲームの場合の話だけど
    ただ、これだと空間計算量が無視できなそう

    • @heron-no-suugaku
      @heron-no-suugaku  3 ปีที่แล้ว +3

      ・操作だけを記憶する(空間計算量が最小)
      ・n歩ごとにキャッシュする(空間/時間計算量のトレードオフ)
      ・1歩ごとにキャッシュする(時間計算量が最小)
      ゲームやアプリによって使い分けるのが良いですね🐤

  • @usar-xx1uk4pp9h
    @usar-xx1uk4pp9h 3 ปีที่แล้ว +4

    毎回初期値からってことか

  • @ゆびまる-y3i
    @ゆびまる-y3i 3 ปีที่แล้ว

    どうしても連想してしまう
    クパァ

  • @onsen_tanago
    @onsen_tanago 3 ปีที่แล้ว +15

    さりげなく淫夢ぶっ込んでくるな

  • @szkfk
    @szkfk 3 ปีที่แล้ว

    荷を押したの?この中の中で?(合成関数)

  • @adrd5534
    @adrd5534 3 ปีที่แล้ว +2

    関数スタックにおまかせの実装はどうかと

  • @地味に濡れたタオル
    @地味に濡れたタオル 3 ปีที่แล้ว

    へー、わかんね

  • @わけワカメ-f3o
    @わけワカメ-f3o 3 ปีที่แล้ว +5

    内容は面白かったけど最初臭そうな雰囲気がありましたね

  • @siorisati241
    @siorisati241 3 ปีที่แล้ว

    あっこれかぁ!(f○g(x))

  • @sage_goes1504
    @sage_goes1504 3 ปีที่แล้ว

    アクセントだけ合っててrとかlとかvの発音gmなのマジで気になる...w

  • @前田日明-w8u
    @前田日明-w8u 3 ปีที่แล้ว

    いやいやいや、盤面を含む必要な全状態をUNDO可能回数分記憶していく、以外にまともな解法は無いでしょう、どう考えても。
    合成関数風な記述は数学的にはシンプルで大変結構ですが、初期状態から一々計算し直すのは無駄杉る。