Note
この実例のパラメータ(5色・容量4・空筒2)は、実は初級編の実例と同じで、Color Sort本体の「初級」設定と同じ組み合わせです。つまり、この記事でいう「中級」はアプリ側の難度が上がったという意味ではなく、同じ条件下でも「どちらへ注ぐか」の判断が必要になる場面がある、というこの解説記事シリーズの読み物としての順番を指します。アプリの「初級・中級・上級・超上級」はcolorsort/app.jsのLEVEL_PRESETS(色数・容量・同色を分ける数・空き筒数の組み合わせ)であり、生成される個々の盤面が「注ぎ方に気をつける場面を含むかどうか」までは判定していません。
結論。Color Sortでは、合法な注ぎ(注ぎ先の一番上と色が一致する、または注ぎ先が空)がその場では複数存在しても、そのうち1つだけが最終的に詰みへ向かい、他は最後まで解ける、ということが実際に起こる。 見た目だけでは区別できないため、迷ったら注いだ後の盤面に対してsolve(アプリでは「1手のヒント」)を確認するのが確実な見分け方であることを、この記事の実例(3択のうち1つだけが不可解)で示します。
まず、複数の合法手が生まれるまで進める
出題時点の盤面です(下から積まれた順、括弧内は既定で隠れる下2層)。
| 筒 | 内容(下→上) |
|---|---|
| 1 | (D, E), B, C |
| 2 | (C, A), D, A |
| 3 | (B, D), D, C |
| 4 | (B, A), A, E |
| 5 | (C, E), E, B |
| 6 | 空 |
| 7 | 空 |
最初の2手は、初級編と同じ「注ぎ先の一番上と色が一致する、または空の筒へ」という進め方で迷わず決まります。
- 筒1→筒6: 筒1の一番上のC(1枚)を、空の筒6へ。筒1は(D,E),Bに。
- 筒1→筒7: 筒1の一番上のB(1枚)を、もう1本の空の筒7へ。筒1は(D,E)だけに(=D,Eの2枚のみ、いずれも元は隠れていた層)。
この時点の盤面は次のとおりです。
| 筒 | 内容(下→上) |
|---|---|
| 1 | D, E |
| 2 | (C, A), D, A |
| 3 | (B, D), D, C |
| 4 | (B, A), A, E |
| 5 | (C, E), E, B |
| 6 | C |
| 7 | B |
合法な3択と、1つだけが詰みへ向かう理由
この状態で、色が一致する合法な注ぎは次の3つです(いずれも即座に不可能な理由はなく、画面上でも普通にタップして実行できます)。
| 候補 | 注ぎ | 一致する色 | 注いだ後、最後まで解けるか |
|---|---|---|---|
| A | 筒3→筒6 | C(筒3の一番上・筒6の一番上とも C) | 解ける(E.solveで確認) |
| B | 筒4→筒1 | E(筒4の一番上・筒1の一番上とも E) | 解けない(E.solveがsolved: false, truncated: falseを返す=探索の打ち切りではなく、確認済みの詰み) |
| C | 筒5→筒7 | B(筒5の一番上・筒7の一番上とも B) | 解ける(E.solveで確認) |
候補Bは、筒4の一番上のE(1枚)を、筒1の一番上のE(色が一致)へ注ぐという、候補A・候補Cと見た目にはまったく同種の操作です。しかし実際に注いでみると、筒1はD,E,E(Dの上にEが2枚積み上がった状態)になり、筒4はB,A,Aになります。この記事の実例では、この1手を選んだ時点で、以後どんな順で注いでも色を揃えきることができないことをE.solveで確認しました(nodes: 28の探索で、打ち切りではなく解なしと確定)。
候補Bを実際に選んでも、この時点では画面に警告は出ません。盤面には依然として合法な注ぎ手が残っており(E.hasAnyMoveがtrue)、手詰まりですという表示(E.isDeadlocked)にもなりません。もし「1手のヒント」を押していれば、colorsort/app.jsのnextAssistMoveが内部で同じE.solveを呼び、現在の状態からは解けません。一手戻すか最初からをお試しください。という実際のメッセージが表示されます(このメッセージは、探索が上限に達した場合の「現在の状態からの解法が探索の上限内で見つかりませんでした」という別の文言とはコード上区別されており、この記事の候補Bは後者ではなく前者に該当することを確認済みです)。この注ぎは筒を完成させていないため、E.canUndoはtrueのまま(「一手戻す」で候補A・Cの状態へ戻れます)。
なぜ候補Bだけが詰みへ向かうのか(この記事での読み方)
E.solveは「解けるか・解けないか」だけを返し、理由までは説明しません。以下は、盤面の中身を見た筆者の解釈です(engineが明示的に述べている理由ではありません)。候補Bは、筒1の底に眠っているD(1枚)の上に、新たにE(1枚)を積み増します。これで筒1はD,E,Eとなり、Dを掘り出すには、いずれこの2枚のEをまた注ぎ出す必要があります。一方、候補A・候補Cはどちらも、すでに空の筒(6・7)へ同色を積み増すだけで、他の色を巻き込みません。この盤面では、それ以外の色(A・B・C・D)を捌ききるための空き容量に余裕がなく、候補Bで筒1にEを2枚積んでしまうと、そのEを注ぎ出す行き先が最後まで確保できなくなる、という状況になっていました。この読み方はこの実例固有の観察であり、「一番上の色が一致する注ぎのうち、空筒以外への合流は避けるべき」という一般的なルールとしては確認していません(次章の続きで、他の一致する合流(候補A自体も筒3→筒6という空筒経由ではない合流)がそのまま正解手順に含まれることからも分かるとおり、色の合流そのものが悪いわけではありません)。
候補Aを選んで最後まで解く
候補A(筒3→筒6)を選んだ場合の、実際にengineが確認した19手すべてです(E.solveの結果をそのままE.pourGameで適用し、最後にE.isClearedがtrueになることを確認済み)。
| 手 | 注ぎ | 移る色・枚数 | 注ぎ後の筒の状態(下→上) |
|---|---|---|---|
| 1 | 筒1→筒6 | Cを1枚 | 1:DEB 2:CADA 3:BDDC 4:BAAE 5:CEEB 6:C 7:(空) |
| 2 | 筒1→筒7 | Bを1枚 | 1:DE 2:CADA 3:BDDC 4:BAAE 5:CEEB 6:C 7:B |
| 3 | 筒3→筒6 | Cを1枚(候補A) | 1:DE 2:CADA 3:BDD 4:BAAE 5:CEEB 6:CC 7:B |
| 4 | 筒5→筒7 | Bを1枚 | 1:DE 2:CADA 3:BDD 4:BAAE 5:CEE 6:CC 7:BB |
| 5 | 筒5→筒1 | Eを2枚 | 1:DEEE 2:CADA 3:BDD 4:BAAE 5:C 6:CC 7:BB |
| 6 | 筒6→筒5 | Cを2枚 | 1:DEEE 2:CADA 3:BDD 4:BAAE 5:CCC 6:(空) 7:BB |
| 7 | 筒1→筒6 | Eを3枚 | 1:D 2:CADA 3:BDD 4:BAAE 5:CCC 6:EEE 7:BB |
| 8 | 筒3→筒1 | Dを2枚 | 1:DDD 2:CADA 3:B 4:BAAE 5:CCC 6:EEE 7:BB |
| 9 | 筒7→筒3 | Bを2枚 | 1:DDD 2:CADA 3:BBB 4:BAAE 5:CCC 6:EEE 7:(空) |
| 10 | 筒2→筒7 | Aを1枚 | 1:DDD 2:CAD 3:BBB 4:BAAE 5:CCC 6:EEE 7:A |
| 11 | 筒1→筒2 | Dを1枚 | 1:DD 2:CADD 3:BBB 4:BAAE 5:CCC 6:EEE 7:A |
| 12 | 筒2→筒1 | Dを2枚 | 1:DDDD(完成) 2:CA 3:BBB 4:BAAE 5:CCC 6:EEE 7:A |
| 13 | 筒2→筒7 | Aを1枚 | 1:DDDD 2:C 3:BBB 4:BAAE 5:CCC 6:EEE 7:AA |
| 14 | 筒5→筒2 | Cを3枚 | 1:DDDD 2:CCCC(完成) 3:BBB 4:BAAE 5:(空) 6:EEE 7:AA |
| 15 | 筒4→筒5 | Eを1枚 | 1:DDDD 2:CCCC 3:BBB 4:BAA 5:E 6:EEE 7:AA |
| 16 | 筒6→筒5 | Eを3枚 | 1:DDDD 2:CCCC 3:BBB 4:BAA 5:EEEE(完成) 6:(空) 7:AA |
| 17 | 筒4→筒6 | Aを2枚 | 1:DDDD 2:CCCC 3:BBB 4:B 5:EEEE 6:AA 7:AA |
| 18 | 筒3→筒4 | Bを3枚 | 1:DDDD 2:CCCC 3:(空) 4:BBBB(完成) 5:EEEE 6:AA 7:AA |
| 19 | 筒6→筒7 | Aを2枚 | 1:DDDD 2:CCCC 3:(空) 4:BBBB 5:EEEE 6:(空) 7:AAAA(完成) |
19手を終えた時点でE.isClearedがtrue(色が揃い、隠れていた10個の「?」もすべて露出済み)であることを確認しました。最終盤面は、筒1=D4枚・筒2=C4枚・筒3=空・筒4=B4枚・筒5=E4枚・筒6=空・筒7=A4枚です。
誤りやすい判断
- 「一番上の色が一致しているから安全」は、初級編の実例ほど単純には成り立たない。 この記事の候補Bのように、色が一致していても唯一詰みへ向かう注ぎが存在します。合流させる前に、「この色を後で困らず注ぎ出せる行き先が残っているか」を考える価値があります。
- 画面に警告が出ないからといって、その1手が正しかったとは限らない。 候補Bを選んだ直後は
手詰まりですという表示は出ません(合法な注ぎはまだ残っているため)。本当に詰んだかどうかを確かめる確実な方法は、「1手のヒント」を押して現在の状態からは解けませんという応答が返るかどうかです。 - 迷ったら「一手戻す」で戻せる。 候補Bのような注ぎは筒を完成させていないため、
canUndoはtrueのままです。「賭け」ではなく、試してヒントで確認し、ダメなら戻すという運用ができます。
読者の検証方法
- 同じ盤面は、色数5・容量4・空筒2・シード
notes-colorsort-medium-easyset-234で再現できます。E.generate({ numColors: 5, capacity: 4, duplicateColors: 0, emptyTubes: 2, seed: 'notes-colorsort-medium-easyset-234' })を実行すれば、この記事と同じtubes配列が得られます。 - 分岐点(表の手3の直前、
tubes = [["D","E"],["C","A","D","A"],["B","D","D","C"],["B","A","A","E"],["C","E","E","B"],["C"],["B"]])から、候補Bにあたる注ぎ(4番目の筒→1番目の筒)をE.applyPourで適用し、その結果に対してE.solve(next, 4, 20000)を呼ぶと{ solved: false, truncated: false }が返ることを、誰でも手元で確認できます。 - アプリ上で候補Bを実際に試す場合は、注いだ直後に「1手のヒント」を押すと、この記事が説明したとおりの
現在の状態からは解けませんという応答が出ることを確認できます(この操作自体はヒントの通常の使い方の範囲内で、解答補助あり扱いにはなりますが、盤面を壊しません)。
この記事が確認していないこと
- 「合流が詰みへ向かうかどうか」を、注ぐ前に見た目だけで判定する一般的な方法は示していません。この記事で確実だと言えるのは、注いだ後に
E.solve(アプリでは「1手のヒント」)で確認するという事後確認の方法だけです。 - 候補Bが詰みへ向かう理由についての章(「なぜ候補Bだけが詰みへ向かうのか」)は、盤面を見た筆者の解釈であり、
colorsort/engine.jsのコードやコメントが明示している理由ではありません。 - この実例で1回だけ現れた「合法な3択のうち1つが詰み」という分岐が、他の盤面でどれくらいの頻度で起こるかは調べていません。上級編(その3)では、色数がさらに多い盤面で、同種の判断がもっと奥まった場所で必要になる実例を扱います。
この結果が言っていないこと
- 検証は
colorsort/engine.js・colorsort/app.js(いずれも記事執筆時点、生成結果のversion: 2で確認)を対象にしています。将来の版で表示・判定基準が変わる可能性があります。 - 「候補A」「候補B」「候補C」という呼び方はこの記事独自の説明用の表記です。アプリ本体の画面上にはこのような候補の一覧表示はありません。