Closed mikarru closed 6 years ago
ちゃんとしたレビューは週末にするけど、パッと見て気になった一点だけ。 undo (redo) の処理は、Commandパターンを使うと実装が多少簡潔になるイメージがある。 参考 : http://gameprogrammingpatterns.com/command.html
俵君の実装でいうと、
みたいな。
実装コストはかかりそうだし、実際にCommandパターンでundoを実装すべきかは微妙だけど。 redoも必要、とか、undoの処理がもっと複雑になりそう、とかだったら検討の価値はあると思う。
ありがとう。 Commandパターンは知らなかったから勉強してみる。Commandパターンを使うかどうかはその後考えてみるわ。
redo処理は少なくともBoardの中にはいらないような気がするな。 redo処理が必要だったら、Boardの外でタイルの置き方を覚えといて、それをもう一回置くような実装の方がBoardの実装がスッキリすると思う。
region同士を結合する処理、union-findってアルゴリズムが使えるかも? https://www.slideshare.net/chokudai/union-find-49066733 カルカソンヌで使えるかちょっと自信はないけど、知らなかったら参考にしてください。
主にロジックで気になった部分にコメントしました。 (効率化できそうな部分はいくつかあるけど、それは別の機会にまとめて対応した方がいい気がしたのであまりこめんとしてない)
region同士を結合する処理、union-findってアルゴリズムが使えるかも?
テスト追加とか終わったら使えるか見てみるわ。ありがとう。 (作業遅れててすまない。)
これまでの主な修正点
寝落ちしてました。
UnionFind勉強してみたけど、Regionをグループ分けしたいって意味では、今の実装で一番単純なUnionFindになってるっていえると思う。 資料に書かれている経路圧縮とかランクといった効率化は、Regionではmergeをundoできる必要があるので難しそう。(資料18Pの補足で「Union-Find 木はグループをまとめることはできても,分割することはできない」ってあるね。)
確かに。undoのことを考えると効率化は難しそうだし、今の実装の方がよさそうだね。
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
になる。
主な変更点
残りの作業
コード自体はマージしても機能的に問題ないと思うのでWIPを外します。
Segment*から隣接するRegionを求める処理は、まずは隣接するSegmentを求めて、 そっからSegment.getRegion()で隣接しているRegionをもとめるんやけど、Regionaに含まれていたSegment sは、RegionaがRegionAにマージされたときに、s.getRegion()がRegionAを指すように変更されてるから、隣接するRegionとして得られるのはRegionaではなくてRegionAになる。
なるほど。mergeRegionの中でs->setRegion(this)してるからうまくいくのか。 これって親の親がいる場合でもうまくいく?例えば、
. . .
. * .
. a b c
で
みたいな場合
その場合でも上手くいく。 b(aをマージ済み)をcにmergeするときに、aとbに含まれる全てのSegmentに対してs.setRegion(c)が呼び出されるから。 mergeRegionメンバ関数みてもらえばわかると思うけど、s.setRegionするsはb.segments_ではなくて、b.segmentBegin()で得られるイテレータでイテレートするSegment全部になってる。そして、それはaとbに含まれる全てのSegment
ありがとう。理解しました。 一旦この段階でマージしてもいいかなと思ってるんだけど、残りの作業にかいてあるコメント追加とCommandパターンの検討はすぐにやる予定?
一々イテレートしてるから、処理的に見直す必要はあるかもしれんね。 見直すとしたら、mergeRegionのときにsegment->setRegion(this)を呼び出さないようにして、代わりにsegment->getRootRegionみたいなメンバ関数を作って、それでsegmentが所属しているRegionを得るようにする、みたいな感じかな。 この場合だとsegment->getRootRegionのときに木をたどる必要が出てくるので、どちらの方が早いかはユースケースによるよね。
コメント追加はそんな時間かからないから、それ終わったらマージしてもらってもいい?(今日の19時までには終わらせる。) (Commandパターンはもう少し時間がかかるので別PRに回します。)
ありがとう。 高速化は、一通り実装終わった後にまとめて検討したいねー。
コメント追加はそんな時間かからないから、それ終わったらマージしてもらってもいい?
了解です。マージは急がないから19時までじゃなくても大丈夫だよ
ちょっとだけどコメント追加しました。 問題なければマージして下さい ><
コメント追加ありがとうございます。 問題なさそうなのでマージします。
実装がほぼ完了しました。
残作業