sash2104 / carcassonne

An AI for carcassonne
0 stars 0 forks source link

Undo処理の実装 #10

Closed mikarru closed 6 years ago

mikarru commented 6 years ago

実装がほぼ完了しました。

残作業

sash2104 commented 6 years ago

ちゃんとしたレビューは週末にするけど、パッと見て気になった一点だけ。 undo (redo) の処理は、Commandパターンを使うと実装が多少簡潔になるイメージがある。 参考 : http://gameprogrammingpatterns.com/command.html

俵君の実装でいうと、

みたいな。

実装コストはかかりそうだし、実際にCommandパターンでundoを実装すべきかは微妙だけど。 redoも必要、とか、undoの処理がもっと複雑になりそう、とかだったら検討の価値はあると思う。

mikarru commented 6 years ago

ありがとう。 Commandパターンは知らなかったから勉強してみる。Commandパターンを使うかどうかはその後考えてみるわ。

redo処理は少なくともBoardの中にはいらないような気がするな。 redo処理が必要だったら、Boardの外でタイルの置き方を覚えといて、それをもう一回置くような実装の方がBoardの実装がスッキリすると思う。

sash2104 commented 6 years ago

region同士を結合する処理、union-findってアルゴリズムが使えるかも? https://www.slideshare.net/chokudai/union-find-49066733 カルカソンヌで使えるかちょっと自信はないけど、知らなかったら参考にしてください。

sash2104 commented 6 years ago

主にロジックで気になった部分にコメントしました。 (効率化できそうな部分はいくつかあるけど、それは別の機会にまとめて対応した方がいい気がしたのであまりこめんとしてない)

mikarru commented 6 years ago

region同士を結合する処理、union-findってアルゴリズムが使えるかも?

テスト追加とか終わったら使えるか見てみるわ。ありがとう。 (作業遅れててすまない。)

mikarru commented 6 years ago

これまでの主な修正点

mikarru commented 6 years ago

寝落ちしてました。

UnionFind勉強してみたけど、Regionをグループ分けしたいって意味では、今の実装で一番単純なUnionFindになってるっていえると思う。 資料に書かれている経路圧縮とかランクといった効率化は、Regionではmergeをundoできる必要があるので難しそう。(資料18Pの補足で「Union-Find 木はグループをまとめることはできても,分割することはできない」ってあるね。)

sash2104 commented 6 years ago

確かに。undoのことを考えると効率化は難しそうだし、今の実装の方がよさそうだね。

mikarru commented 6 years ago

mergeRegionの中の条件にregion->isMerged()が含まれてると、本来mergeすべきときにmergeしないことがある気がしてて、

. 0 .
. * .
. a A

  • 0とaがadjacent_regions
  • aとAも同じregion
  • Aがaのparent みたいな場合、*とaはmergeされないような気がする?

よく考えたら上の場合も大丈夫やった。(上手くいきそうにないテストケース作ってみたけど、なんか上手いくのでずっと考えてた。)

Segment*から隣接するRegionを求める処理は、まずは隣接するSegmentを求めて、 そっからSegment.getRegion()で隣接しているRegionをもとめるんやけど、Regionaに含まれていたSegment sは、RegionaがRegionAにマージされたときに、s.getRegion()がRegionAを指すように変更されてるから、隣接するRegionとして得られるのはRegionaではなくてRegionAになる。

mikarru commented 6 years ago

主な変更点

残りの作業

mikarru commented 6 years ago

コード自体はマージしても機能的に問題ないと思うのでWIPを外します。

sash2104 commented 6 years ago

Segment*から隣接するRegionを求める処理は、まずは隣接するSegmentを求めて、 そっからSegment.getRegion()で隣接しているRegionをもとめるんやけど、Regionaに含まれていたSegment sは、RegionaがRegionAにマージされたときに、s.getRegion()がRegionAを指すように変更されてるから、隣接するRegionとして得られるのはRegionaではなくてRegionAになる。

なるほど。mergeRegionの中でs->setRegion(this)してるからうまくいくのか。 これって親の親がいる場合でもうまくいく?例えば、

. . . 
. * .
. a b c

みたいな場合

mikarru commented 6 years ago

その場合でも上手くいく。 b(aをマージ済み)をcにmergeするときに、aとbに含まれる全てのSegmentに対してs.setRegion(c)が呼び出されるから。 mergeRegionメンバ関数みてもらえばわかると思うけど、s.setRegionするsはb.segments_ではなくて、b.segmentBegin()で得られるイテレータでイテレートするSegment全部になってる。そして、それはaとbに含まれる全てのSegment

sash2104 commented 6 years ago

ありがとう。理解しました。 一旦この段階でマージしてもいいかなと思ってるんだけど、残りの作業にかいてあるコメント追加とCommandパターンの検討はすぐにやる予定?

mikarru commented 6 years ago

一々イテレートしてるから、処理的に見直す必要はあるかもしれんね。 見直すとしたら、mergeRegionのときにsegment->setRegion(this)を呼び出さないようにして、代わりにsegment->getRootRegionみたいなメンバ関数を作って、それでsegmentが所属しているRegionを得るようにする、みたいな感じかな。 この場合だとsegment->getRootRegionのときに木をたどる必要が出てくるので、どちらの方が早いかはユースケースによるよね。

mikarru commented 6 years ago

コメント追加はそんな時間かからないから、それ終わったらマージしてもらってもいい?(今日の19時までには終わらせる。) (Commandパターンはもう少し時間がかかるので別PRに回します。)

sash2104 commented 6 years ago

ありがとう。 高速化は、一通り実装終わった後にまとめて検討したいねー。

コメント追加はそんな時間かからないから、それ終わったらマージしてもらってもいい?

了解です。マージは急がないから19時までじゃなくても大丈夫だよ

mikarru commented 6 years ago

ちょっとだけどコメント追加しました。 問題なければマージして下さい ><

sash2104 commented 6 years ago

コメント追加ありがとうございます。 問題なさそうなのでマージします。