セルの並び。位置=添字。並びは読んだ時点の写しだが、セルそのものは実体——読んだ後に枠が 増減しても並びは変わらず、セルの中身の出入りはそのまま見える。並びを辿りながら中身を動かす 読み手(画面の詰め直し、PlayScreenViewのpackToFrontIfHidden)が、各周でそのセルの今の中身を読む。
スタックの区別を畳み込んだ、中身全部の写し。読んだ時点の顔ぶれを写して返すのは契約 (SlotSystem.md 1節)。
非公開has枠の数が決まっているスロットか(=空セルを残して位置を安定させるか、SlotSystem.md 3節)。 枠は増えず、空いた枠は残り、その枠を位置として指せる。
中身を、積み重なっているまとまりごとに分けたもの(空セルは含まない。先頭が代表)。contentsと 同じく、読んだ時点の顔ぶれを写して返すのは契約(SlotSystem.md 1節)——まとまりの内側も写す ので、受け取った側は辿っている途中でスタックの出入りがあっても構わない。
通常の追加。合流できる既存スタックがあればそこへ、無ければ新規スタックとして型の合う空き枠へ、 枠が増えるスロットで空きが無ければ末尾へ入れる。
objが自分1個だけでセルを占めているなら、そのスタック。既存スタックへ合流していれば(=新しいセルを 消費していなければ)undefined。「合流は枠を消費しない」を、位置を決める側が問い合わせるための口。
非公開indexこのObjectStackがセルの並びの何番目にあるか(=位置。枠数固定のスロットでは固定番号)。属していなければ-1。 位置を知りたい呼び出し側は、対象の具体的なObjectStack(cells / findStackContainingで 得る)を渡す。型(ObjectDef)では引かない——束ねない型(stackable: false)は同じDefでも1個ずつ 別スタックになるため、Defは位置を一意に決めない。
位置を指定して入れる(SlotPosition参照)。合流が指定位置に優先することは tryMergeIntoMatchingStack参照。
位置を指定して並び替える。動くのは1個ではなくスタック丸ごと。
並び替えを1個ずつの出し入れで行うことはできない。抜いた1個を入れ直すとき、残った同種のスタックへの 合流が位置指定より優先される(insertAt)ため、必ず元の位置へ戻ってしまうため。
枠を指した並び替えは指した枠と入れ替える——相手が空なら実質の移動になり、元の枠が空く。 詰めないので単純な2者間のswapで足りる。
隙間を指した並び替えで詰める向きは、スタックが抜けた跡の側を先に試す。そちらには必ず空きがあるので、 遠くのセルを動かさずに済む。自分の両隣の隙間へ落とした場合は、跡がそのまま行き先になるので 何も動かさない。
same_slotによる置き換え(置き場の決まりはSlotSystem.md 3節)。合流先が無ければ置き換えオブジェクトを新規 スタックとして、originが居たセル(originCellIndex)を基準に配置する(SameSlotSpawnSite参照)。自動整列は行わない (同種はObjectStack内で整列されるため、スタック間の位置は著者が見た位置を保つ)。
非公開points枠を直接指す指定として扱えるか。枠を指せるのは枠数を決めたスロットだけなので、前詰めの スロットでは隙間の指定として読む——空き枠は末尾の受け皿だけで、枠の位置がそのまま並びの終わりを 指すため。指す側がどちらのスロットかを知らなくて済むよう、読み替えはここで行う。
objの代表チェーンが変わったかもしれないとき、今の所属スタックの固定識別子に合致しなくなっていれば抜いて 入れ直し(既存スタックへ合流/新規スタック)、「同種は1スタックにまとまる」という不変条件を中身の変化後も 保つ。非stackableは対象外(個体ごとの別スタックで、合流判定の相手が居ないため実害が無い)。
非公開shiftgapIndexの隙間へ、step方向へ既存のセルをずらして場所を作る動かし方。
非公開shift空いているセル(cellIndex)をincomingで埋める動かし方(埋まっているか、枠の宣言に合わなければ失敗)。
非公開shift位置の指定に従ってincomingを置くときの、中身の動かし方(置けなければundefined)。枠の指定はその枠を 埋め(埋まっていれば失敗)、隙間の指定はまず右方向へ、それが無理なら左方向へ既存のセルをずらして 場所を作る。
どの指定でも、枠の宣言(acceptとmax)に合わない置き方はしない——指した枠も、ずらされる中身の 移る先も同じく見る(SlotSystem.md 3節)。合わなければ失敗で、型の合う別の枠へは振り替えない。
非公開shiftoriginCellIndexのstep隣へ、最寄りの空きセルをその方向からずらして場所を作る動かし方。空きが無いか、 ずらした先の枠の宣言に合わなければundefined(=そちらへは置けない。呼び出し側が反対方向や fallbackへ進む)。
非公開take新規スタックを置ける空き枠。型の合う空き枠が無ければ、入っている物を型の合う別の空き枠へ ずらして空ける(pathToCellFor)。それも無理なら、枠が増えるスロットでは末尾に足す。
非公開try動かし方に従ってスタックを置く(shiftが無ければ何もせずfalse)。emptyAtからtargetへ、間の中身を step方向へ1つずつずらす(targetを空ける)。枠の宣言は添字に留まり、動くのは中身だけ。押し出しは セル単位で行うため、押し出されるスタック(同種複数個)の中身の相対順序は変わらない。
非公開try末尾に空き枠を1つ足す。枠が増えるスロットだけで、枠数を決めたスロットは足せない(undefined)。 枠の宣言は共有の1つ(cellsを宣言していないスロットなので、どの位置も同じ受け入れ方をする)。
非公開try合流できる既存スタックがあればそこへ入れる。「同種は1スタックにまとまる」という不変条件は位置の指定より 優先されるため、中身を加える経路は位置指定の有無によらず必ず最初にここを通す。
束ねられない型(ObjectDef.stackable=false)と、枠のmaxが埋まっている相手は合流しない (呼び出し側は新規スタックの生成へ進む)。
非公開try非公開trygapIndexの隙間へ、step方向へ既存のセルをずらして場所を作り、スタックを入れる。
非公開try枠を指した並び替え。中身だけを入れ替える(枠の宣言はそれぞれの添字に留まる)。入れ替えた先の枠の 宣言に合わない中身が出るなら入れ替えない(shiftForPositionと同じ規則)。
candidateと同じ型のものを、かさを見ずにあと何個置けるか。合流できる枠の残りと、型の合う空き枠 (中身をずらして空く枠を含む)に入る数の合計。枠が増えるスロットは上限が無い。束ねられない型(stackable=false)は1枠に1個しか 入らないので、maxがいくつでも空き枠の数がそのまま上限になる。
非公開vacateセルを空にする。枠数が決まっていれば空の枠として残し、そうでなければ取り除いて前詰めする。
スロットの中身の並び。位置=枠の添字で、置き場所を作る(=ずらす)のもここ。
枠数を決めたスロットと前詰めのスロットは、別々の置き方をしているのではなく同じずらし方に乗る。 前詰めのスロットは枠の宣言が全て同じ1つなので、「末尾に枠を1つ生やしてから、そこへ向けて中身を ずらす」ことが挿入と同じ結果になる。両者の差は次だけで、それぞれ1箇所でしか効かない (SlotSystem.md 3節):