def analyze(wk: AnalyzeWk, how_anlz_list: List[HowToAnalyze]) -> bool: """消去法 ある枡にメモが一つしかなければその枡の値が確定できる Args: wk (AnalyzeWk): ワーク how_anlz_list (List[HowToAnalyze]): 解析方法 Returns: bool: エラーの場合にFalse """ # メモ値がひとつしかない=そこの枡にはそれしか入らない for squ in wk.all_squ_list: if len(squ.memo_val_list) == 1: squ.val = squ.memo_val_list[0] squ.memo_val_list.clear() # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze(Method.ELIMIONATION_ONE_MEMO) how_anlz.commit_val = squ.val how_anlz.changed_squ = squ how_anlz.msg = MsgFactory.how_to_elimionation_one_memo(how_anlz) how_anlz_list.append(how_anlz) return True
def __post_init__(self) -> None: """コンストラクタの後に呼ばれるメソッド """ from sudokuapp.util.MsgFactory import MsgFactory # 画面から来た状態を保持するため、解析履歴に投入 start_howto: HowToAnalyze = HowToAnalyze(Method.START) start_howto.msg = MsgFactory.start_analyze() self.histroy_list.append(History(self.flame.clone(), [start_howto])) for area in self.flame.area_list: for squ in area.squ_list: # 全枡リスト self.all_squ_list.append(squ) # 全枡辞書 self._all_squ_dict[self._create_key_for_squ_dict( squ.row, squ.clm)] = squ # ヒント枡 if squ.hint_val is not None: self.hint_list.append(squ) # 行辞書 if squ.row not in self.row_dict: self.row_dict[squ.row] = list() self.row_dict[squ.row].append(squ) # 列辞書 if squ.clm not in self.clm_dict: self.clm_dict[squ.clm] = list() self.clm_dict[squ.clm].append(squ)
def addHistryForErr(self, how_anlz_list_err: List[HowToAnalyze]) -> None: """枠をヒストリーに追加 エラー用 Args: how_anlz_list (List[HowToAnalyze]): 解析方法 """ from sudokuapp.util.MsgFactory import MsgFactory how_to_list: List[HowToAnalyze] = list() how_to_title: HowToAnalyze = HowToAnalyze(Method.ERROR_CHECK) how_to_title.msg = MsgFactory.error_info(len(how_anlz_list_err)) how_to_list.append(how_to_title) how_to_list.extend(how_anlz_list_err) self.histroy_list.append(History(self.flame.clone(), how_to_list))
def _removeMemo( how_anlz_list: List[HowToAnalyze], region: Region, squ_list: List[Square] ) -> None: """メモの除外 ヒント(値)によってメモを除外 Args: how_anlz_list (List[ChangeHistroy]): 解析方法 region (Region): 領域 squ_list (List[Square]): 枡リスト """ # 確定枡を抽出 not_none_squ_list: List[Square] = SudokuUtil.find_fixed_squ_from_region( squ_list) # 未確定枡を抽出 none_squ_list: List[Square] = SudokuUtil.find_unfixed_squ_from_region( squ_list) # メモからヒント(値)を除外 for none_squ in none_squ_list: for memo in none_squ.memo_val_list[:]: for not_none_squ in not_none_squ_list: if memo == not_none_squ.get_fixed_val(): none_squ.memo_val_list.remove(memo) # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze( Method.ELIMIONATION) how_anlz.region = region how_anlz.remove_memo_list.append(memo) how_anlz.changed_squ = none_squ how_anlz.trigger_squ_list.append(not_none_squ) how_anlz.msg = MsgFactory.how_to_elimionation(how_anlz) how_anlz_list.append(how_anlz)
def _onlyMemo(how_anlz_list: List[HowToAnalyze], region: Region, squ_list: List[Square]) -> bool: """エリア内(行、列)にメモが一つしかない ⇒メモで値を確定させる Args: how_anlz_list (List[HowToAnalyze]): 解析方法 from_type (Region): 領域 squ_list (List[Square]): 枡リスト Returns: bool: エラーの場合にFalse """ wk_memo_dict: Dict[str, List[Square]] = dict() # 下記のようなDICTを作成 # 枡A memo=[1 2 3] # 枡B memo=[1 2] # 枡C memo=[1 2 3 4] # ⇒ # 1: [枡A 枡B 枡C] # 2: [枡A 枡B 枡C] # 3: [枡A 枡B] # 4: [枡C] for squ in squ_list: if (squ.get_fixed_val() is not None): continue for memo in squ.memo_val_list: if memo not in wk_memo_dict: wk_memo_dict[memo] = list() wk_memo_dict[memo].append(squ) # 複数のメモ値が入るメモを除外 # 1: [枡A 枡B 枡C] ←除外 # 2: [枡A 枡B 枡C] ←除外 # 3: [枡A 枡B] ←除外 # 4: [枡C] for loop_memo in list(wk_memo_dict.keys()): if len(wk_memo_dict[loop_memo]) != 1: del wk_memo_dict[loop_memo] # エラーチェック # 下記のような場合はNG # 枡A memo=[1 2] # 枡B memo=[1 2] # 枡C memo=[3 4] # ⇒ # 1: [枡A 枡B] ←除外 # 2: [枡A 枡B] ←除外 # 3: [枡C] ←3は枡Cにしか入らないが、、、 # 4: [枡C] ←4も枡Cにしか入らない。。。 # ⇒例えば3で枡Cを確定してしまうと # 4が入る枡がなくなってしまう # for memo, wk_squ_list in wk_memo_dict.items(): # if len(wk_squ_list) != 1: # continue # pass for memo, wk_squ_list in wk_memo_dict.items(): squ: Square = wk_squ_list[0] squ.val = memo squ.memo_val_list.clear() # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze(Method.ELIMIONATION_ONLY_MEMO) how_anlz.region = region how_anlz.commit_val = squ.val how_anlz.changed_squ = squ how_anlz.msg = MsgFactory.how_to_elimionation_only_memo(how_anlz) how_anlz_list.append(how_anlz) return True
def analyze(wk: AnalyzeWk, how_anlz_list: List[HowToAnalyze]) -> bool: """XYチェーン メモが2つしかない枡が共通の数字を介してチェーンを形成する場合、共通枡からメモを除外する解法 例> +[1]-------------------------+[2]-------------------------+ ... | 1:1(@) 1:2 1:3 | 1:4 1:5(?) 1:6 | ... | m=[N,M] ? ? | ? m=[?,N] ? | ... | 2:1 2:2 2:3 | 2:4 2:5 2:6 | ... | ? ? ? | ? ? ? | ... | 3:1 3:2 3:3 | 3:4 3:5 3:6 | ... | ? ? ? | ? ? ? | ... +[4]-------------------------+[5]-------------------------+ ... | 4:1 4:2 4:3 | 4:4 4:5 4:6 | ... | ? ? ? | ? ? ? | ... | 5:1 5:2 5:3 | 5:4 5:5 5:6 | ... | ? ? ? | ? ? ? | ... | 6:1 6:2 6:3 | 6:4 6:5 6:6 | ... | ? ? ? | ? ? ? | ... +[7]-------------------------+[8]-------------------------+ ... | 7:1 7:2 7:3($) | 7:4 7:5(+) 7:6 | ... | ? ? m=[O,P] | ? m=[N,P] ? | ... | 8:1 8:2 8:3 | 8:4 8:5 8:6 | ... | ? ? ? | ? ? ? | ... | 9:1(#) 9:2 9:3 | 9:4 9:5 9:6 | ... | m=[M,O] ? ? | ? ? ? | ... +----------------------------+----------------------------+ ... 上記例に当てはめて考えると以下のようなチェーンが形成される場合 @(m=[N,M]) =M= #(m=[M,O]) =O= $(m=[O,P]) =P= +(m=[N,P]) チェーンの始端と終端の交差枡?のメモからNを除外することができる。 検証> 交差枡(+)がNだと仮定しすると @=M #=O $=P +=N となり5列目にNが2つ出現し矛盾が発生する。 上記は@枡から検証したが、+枡がPから始まると仮定した場合でも矛盾が発生する。 ⇒ この事からXYチェーンの始端と終端の交差枡には共通のメモNがあると矛盾が存在するため メモNを除外できる XYチェーンの条件 ・3つ以上の枡でチェーンが形成されていること ・始端と終端に同じメモ(以降共通メモと称す)が存在すること ・始端の次の枡が共通メモでリンクされていないこと ・終端の前の枡が共通メモでリンクされていないこと Args: wk (AnalyzeWk): ワーク how_anlz_list (List[HowToAnalyze]): 解析方法 Returns: bool: エラーの場合にFalse """ # XYチェーン作成 all_chain_list: List[List[Chain]] = _create_xy_chain(wk) for chain_list in all_chain_list: first_squ = chain_list[0].squ last_squ = chain_list[len(chain_list) - 1].squ # 共通するメモを抽出 dup_memo_list = _get_dup_memo_list(first_squ, last_squ) # 共通枡を算出 share_list: List[Square] = _find_share_squ(wk, first_squ, last_squ) change_squ_memo_dict: Dict[int, List[Square]] = dict() for share_squ in share_list: if share_squ.get_fixed_val() is not None: continue # 共通枡がチェーンリストに含まれる場合は対象外 # TODO: こんなパターンないはず? # 共通するメモはチェーンの中に入ってこないはずだから下記のパターンは存在しない? # 後日考える chain_include = False for chain in chain_list: if share_squ == chain.squ: chain_include = True break if chain_include: continue # 共通枡にメモが存在する? for loop_memo in dup_memo_list: if loop_memo not in share_squ.memo_val_list: continue change_squ_list: List[Square] if loop_memo in change_squ_memo_dict: change_squ_list = change_squ_memo_dict[loop_memo] else: change_squ_list = list() change_squ_memo_dict[loop_memo] = change_squ_list change_squ_list.append(share_squ) # 対象なし if len(change_squ_memo_dict) == 0: # 次のチェーンリストを検索 continue # TODO: debug print("ヒットで") print(" chain_list={}".format(chain_list)) print(" share_list={}".format(share_list)) # 対象あり for loop_memo, change_squ_list in change_squ_memo_dict.items(): print(" ゲスよ loop_memo={} change_squ_list={}".format( loop_memo, change_squ_list)) chain_squ_list: List[Square] = list() for chain in chain_list: chain_squ_list.append(chain.squ) for change_squ in change_squ_list: print(" change_squ={}".format(change_squ)) # メモを除外 change_squ.memo_val_list.remove(loop_memo) # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze(Method.XY_CHAIN) how_anlz.changed_squ = change_squ how_anlz.remove_memo_list.append(loop_memo) how_anlz.trigger_squ_list.extend(chain_squ_list) how_anlz.chain_squ_list.extend(chain_squ_list) how_anlz.msg = MsgFactory.how_to_xy_chain(how_anlz) how_anlz_list.append(how_anlz) return True return True
def _find_x_wing(wk: AnalyzeWk, how_anlz_list: List[HowToAnalyze], region: Region) -> None: """X-Wing法 Args: wk (AnalyzeWk): ワーク how_anlz_list (List[HowToAnalyze]): 解析方法リスト region (Region): 領域 """ # 行辞書(または列辞書) region_dict: Dict[int, List] if region == Region.ROW: region_dict = wk.row_dict else: region_dict = wk.clm_dict for loop_memo in range(1, 10): # X-Wingの対象 # メモが2個の行(列)を抽出 two_memo_list: List[List[Square]] = list() for squ_list in region_dict.values(): # 行(列)内のメモを含む枡リストを取得 # +[1]-------------------------+[2]-------------------------+[3]-------------------------+ # | 1:1 1:2 1:3 | 1:4 1:5 1:6(*) | 1:7 1:8 1:9(*) | # | ? ? ? | m=[N,?] ? m=[N,?] | m=[N,?] ? m=[N,?] | <-この行の枡を取得、ただし枡数が2でないので対象外 # | 2:1 2:2 2:3 | 2:4 2:5 2:6 | 2:7 2:8 2:9 | # | ? ? val=N | ? ? ? | ? ? ? | # | 3:1 3:2 3:3 | 3:4 3:5 3:6(@) | 3:7 3:8 3:9($) | # | ? ? ? | ? ? m=[N,?] | ? ? m=[N,?] | <-この行の枡を取得 # +[4]-------------------------+[5]-------------------------+[6]-------------------------+ # | 4:1 4:2 4:3 | 4:4 4:5 4:6(*) | 4:7 4:8 4:9(*) | # | ? ? ? | m=[N,?] ? m=[N,?] | m=[N,?] ? m=[N,?] | <-この行の枡を取得、ただし枡数が2でないので対象外 # | 5:1 5:2 5:3 | 5:4 5:5 5:6($) | 5:7 5:8 5:9(@) | # | ? ? ? | ? ? m=[N,?]$ | ? ? m=[N,?]@ | <-この行の枡を取得 # | 6:1 6:2 6:3 | 6:4 6:5 6:6 | 6:7 6:8 6:9 | # | ? hint=N ? | ? ? ? | ? ? ? | # +[7]-------------------------+[8]-------------------------+[9]-------------------------+ # | 7:1 7:2 7:3 | 7:4 7:5 7:6 | 7:7 7:8 7:9 | # | m=[N,?] ? ? | ? m=[N,?] ? | ? ? ? | <-この行の枡を取得 # | 8:1 8:2 8:3 | 8:4 8:5 8:6 | 8:7 8:8 8:9 | # | m=[N,?] ? ? | ? m=[N,?] ? | ? ? ? | <-この行の枡を取得 # | 9:1 9:2 9:3 | 9:4 9:5 9:6 | 9:7 9:8 9:9 | # | ? ? ? | ? ? ? | ? hint=N ? | # +----------------------------+----------------------------+----------------------------+ # loop_memoを含む枡を抽出し、そのメモが2個かどうかを判定 include_list: List[ Square] = SudokuUtil.find_squ_include_memo_from_region( squ_list, loop_memo) # 行(列)内でメモが2個でないとX-Wingが成立しない if len(include_list) != 2: continue two_memo_list.append(include_list) # 発見出来た行(列)が2以下だとそもそもX-Wing対象にならない if len(two_memo_list) < 2: continue # 同一列(行)でグルーピング # 以下のような辞書を生成 # キー:(列、列) # 値:[[枡、枡],[枡、枡]] region_grouping_dict: Dict[Tuple[int, int], List[List[Square]]] = dict() for include_list in two_memo_list: # キー region_pair: Tuple[int, int] if region == Region.ROW: # 行の場合は列でグルーピング region_pair = (include_list[0].clm, include_list[1].clm) else: # 列の場合は行でグルーピング region_pair = (include_list[0].row, include_list[1].row) # 値 if region_pair not in region_grouping_dict: region_grouping_dict[region_pair] = list() region_list: List[List[Square]] = region_grouping_dict[region_pair] region_list.append(include_list) # グルーピング後、対象行(列)が2つでなければX-Wingの対象外 for region_pair in list(region_grouping_dict.keys()): region_list = region_grouping_dict[region_pair] if len(region_list) != 2: region_grouping_dict.pop(region_pair) # 対象なし if len(region_grouping_dict) == 0: continue # 除外するメモの検索対象辞書 # 行の場合は列から探し、列の場合は行から探す target_dict: Dict[int, List[Square]] if region == Region.ROW: target_dict = wk.clm_dict else: target_dict = wk.row_dict # 除去するメモを抽出 for region_pair, region_list in region_grouping_dict.items(): # X-Wingで見つかった枡 xwing_squ_list: List[Square] = list() for include_list in region_list: for squ in include_list: xwing_squ_list.append(squ) for region_pos in region_pair: # 同一列(行)からメモを含む枡を取得 # (除外対象枡) change_list = SudokuUtil.find_squ_include_memo_from_region( target_dict[region_pos], loop_memo) for change_squ in change_list: # 同一枡は消してはいけない if change_squ in xwing_squ_list: continue # メモを除外 change_squ.memo_val_list.remove(loop_memo) # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze(Method.X_WING) how_anlz.region = region how_anlz.changed_squ = change_squ how_anlz.remove_memo_list.append(loop_memo) how_anlz.trigger_squ_list.extend(xwing_squ_list) how_anlz.msg = MsgFactory.how_to_x_wing( how_anlz, region_pair) how_anlz_list.append(how_anlz) if len(how_anlz_list) > 0: return
def _analyze_naked_pair( wk: AnalyzeWk, how_anlz_list: List[HowToAnalyze], region: Region, squ_list: List[Square] ) -> None: """ネイキッドペア解析 Args: wk (AnalyzeWk): ワーク how_anlz_list (List[HowToAnalyze]): 解析方法 region (Region): 領域 """ # 未確定枡を取得 unfixed_list: List[Square] = SudokuUtil.find_unfixed_squ_from_region( squ_list) # 対象領域の全ての枡が確定している if len(unfixed_list) == 0: return # ペア数(2~8)を大きくしながら解析 # ※制限時は2~3 find_pair_cnt: int = 8 if Method.NAKED_PAIR in wk.limit_method_list: find_pair_cnt = 3 for naked_num in range(2, find_pair_cnt + 1): # 未確定枡数-1よりネイキッド数の方が大きくなったら処理終了 # [補足] # 未確定数とネイキッド数が同じ場合、必ず全ての枡がペアになるため除外するメモがなくなる # ⇒-1して終了条件の比較を行っている if len(unfixed_list) - 1 <= naked_num: return # ペア可能リストを算出 can_pair_list = list() for unfixed_squ in unfixed_list: if len(unfixed_squ.memo_val_list) > naked_num: continue # naked_num=3の場合 # +[1]--------------------------------------+ # | 1:1 1:2 1:3 | # | m=[N,M,O](@) m=[N,P](*) ? | # | 2:1 2:2 2:3 | # | m=[M,O]($) m=[M,Q](*) ? | # | 3:1 3:2 3:3 | # | m=[N,M](#) m=[O,R](*) m=[N,M,O,R,Q,S] | # +[4]--------------------------------------+ # @枡、$枡、#枡、*枡3個を抽出 # 1:1 m=[N,M,O](@) # 1:2 m=[N,P](*) # 2:1 m=[M,O]($) # 2:2 m=[M,Q](*) # 3:1 m=[N,M](#) # 3:2 m=[O,R](*) can_pair_list.append(unfixed_squ) # ペア可能リストがペア数より小さい場合は対象外 # 次のペア数を調べる if len(can_pair_list) < naked_num: continue # ペアを見つける # 例に当てはめると # 1:1 m=[N,M,O](@) # 1:2 m=[N,P](*) # 2:1 m=[M,O]($) # 2:2 m=[M,Q](*) # 3:1 m=[N,M](#) # 3:2 m=[O,R](*) # から # 1:1 m=[N,M,O](@) # 2:1 m=[M,O]($) # 3:1 m=[N,M](#) # を見つける for pivot_squ in can_pair_list[:]: pivot_pair: Set[int] = set(pivot_squ.memo_val_list) for compare_squ in can_pair_list[:]: if pivot_squ == compare_squ: continue pivot_pair = pivot_pair.union(set(compare_squ.memo_val_list)) if len(pivot_pair) > naked_num: break if len(pivot_pair) > naked_num: can_pair_list.remove(pivot_squ) # ネイキッド数≒枡数は対象外 if len(can_pair_list) != naked_num: continue # ペア発見! pair_set: Set[int] = set() for squ in can_pair_list: pair_set = pair_set.union(squ.memo_val_list) pair_list: List[int] = list(pair_set) pair_list.sort() # 変更枡抽出 change_squ_list: List[Square] = list() for squ in unfixed_list: if squ in can_pair_list: continue for memo in squ.memo_val_list: if memo in pair_list: change_squ_list.append(squ) break # ペアは見つかったが、変更枡が存在しない if len(change_squ_list) == 0: continue for change_squ in change_squ_list: for loop_memo in pair_list: if loop_memo not in change_squ.memo_val_list: continue # メモを除外 change_squ.memo_val_list.remove(loop_memo) # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze( Method.NAKED_PAIR) how_anlz.region = region how_anlz.changed_squ = change_squ how_anlz.remove_memo_list.append(loop_memo) how_anlz.trigger_squ_list.extend(can_pair_list) how_anlz.msg = MsgFactory.how_to_naked_pair( how_anlz, pair_list) how_anlz_list.append(how_anlz)
def analyze(wk: AnalyzeWk, how_anlz_list: List[HowToAnalyze]) -> bool: """ロックされた候補法 ある値がそのエリア内に同一行(列)にしか存在しない場合、 別エリアの行には存在しえない ⇒別エリアの同一行のメモを除外出来る 例> エリア[1]において1は(1:1),(1:2)にしか存在出来ない +[1]------------------+[2]----------------+[3]----+ | m=[1,2] i=[1,3] v=4 | m=[1,2] m=[1,3] ? | ? ? ? | | m=[2,3] v=5 v=6 | ? ? ? | ? ? ? | | v=7 v=8 v=9 | ? ? ? | ? ? ? | +---------------------+-------------------+-------+ 例えばエリア[2]の(1:4)(1:5)のどれかに1が入ってしまうと エリア1に1が入る枡がなくなってしまうため矛盾が生じてしまう。 ※エリア[3]も同様の事が言える ⇒エリア[2]の1行目のメモから1を除外出来る [補足] 列に関しても同様のアルゴリズムが適用出来る。 上記例だとエリア[1]の2に関しても1列目にしか存在しえないため、 エリア[4]、エリア[7]の1列目のメモ2を除外出来る。 Args: wk (AnalyzeWk): ワーク how_anlz_list (List[HowToAnalyze]): 解析方法 Returns: bool: エラーの場合にFalse """ for area in wk.flame.area_list: memo_squ_dict: Dict[int, List[Square]] = dict() # memo_squ_dict = dict() # エリアからメモを抽出 for squ in area.squ_list: for memo in squ.memo_val_list: if memo not in memo_squ_dict: memo_squ_dict[memo] = list() memo_squ_dict[memo].append(squ) for memo, squ_list in memo_squ_dict.items(): # ロックされた候補法の性質上、対象となる枡は2個または3個のみ if len(squ_list) == 2 or len(squ_list) == 3: pass else: continue # 対象となる領域ってある? target_region: Region = _target_region(squ_list) if target_region is None: continue # 変更対象枡を抽出 change_squ_list: List[Square] = _find_change_squ( wk, memo, target_region, squ_list) for change_squ in change_squ_list: # メモ除外 change_squ.memo_val_list.remove(memo) # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze(Method.LOCKED_CANDIDATES) how_anlz.region = target_region how_anlz.changed_squ = change_squ how_anlz.remove_memo_list.append(memo) how_anlz.trigger_squ_list.extend(squ_list) how_anlz.msg = MsgFactory.how_to_locked_candidates(how_anlz) how_anlz_list.append(how_anlz) # メモしか変更していないため、ループを継続すると別エリアの処理でおかしく可能性がある if len(how_anlz_list) > 1: return True return True
def analyze(wk: AnalyzeWk, how_anlz_list: List[HowToAnalyze]) -> bool: """シンプルチェーン 1つの数字に注目して 枡 =強= 枡 =弱= 枡 =強= 枡 =弱= 枡 =強= 枡 のように強リンクではじまって弱リンク、強リンク...の組み合わせが続き、 強リンクで終わるパターンで最初と最後の交差枡にはその数字は入らないという解法 ※弱リンクの部分は強リンクになっても可 ※強リンク、弱リンクの解説はLinkType.pyのdocstringを参照 例> +[1]-------------------------+[2]-------------------------+[3]-------------------------+ | 1:1 1:2 1:3 | 1:4 1:5 1:6 | 1:7 1:8 1:9 | | ? ? ? | ? ? ? | ? ? hint=N | | 2:1 2:2 2:3 | 2:4 2:5 2:6 | 2:7 2:8 2:9 | | ? ? ? | ? ? hint=N | ? ? ? | | 3:1 3:2 3:3 | 3:4 3:5 3:6 | 3:7 3:8 3:9 | | hint=N ? ? | ? ? ? | ? ? ? | +[4]-------------------------+[5]-------------------------+[6]-------------------------+ | 4:1 4:2 4:3 | 4:4 4:5(#) 4:6 | 4:7 4:8 4:9 | | ? ? m=[N,?] | ? m=[N,?] ? | ? ? ? | | 5:1 5:2 5:3 | 5:4 5:5 5:6 | 5:7 5:8 5:9 | | ? ? ? | ? ? ? | hint=N ? ? | | 6:1 6:2(*) 6:3 | 6:4(@) 6:5 6:6 | 6:7 6:8 6:9 | | ? m=[N,?] m=[N,?] | m=[N,?] ? ? | ? ? ? | +[7]-------------------------+[8]-------------------------+[9]-------------------------+ | 7:1 7:2 7:3 | 7:4 7:5 7:6 | 7:7 7:8 7:9 | | ? m=[N,?] m=[N,?] | ? ? ? | ? ? ? | | 8:1 8:2 8:3 | 8:4 8:5 8:6 | 8:7 8:8 8:9 | | ? ? ? | m=[N,?] m=[N,?] ? | ? hint=N ? | | 9:1 9:2(+) 9:3 | 9:4 9:5(!) 9:6 | 9:7 9:8 9:9 | | ? m=[N,?] ? | ? m=[N,?] ? | ? ? ? | +----------------------------+----------------------------+----------------------------+ ※8:8がNで確定している、上記のような状態の枠は本処理には来ないが、、、説明用に目をつぶる 数字Nで以下のチェーンが形成されている @枡 =強= #枡 =弱= !枡 =強= +枡 チェーンの最初の@枡と最後の+枡の交差枡(*枡)からNを除外することが出来る。 検証> 上記例に当てはめて交差枡*がNで確定すると仮定 ・@枡からNが除外される @枡 =強= #枡 =弱= !枡 =強= +枡 ^^^ ・#枡がNで確定する @枡 =強= #枡 =弱= !枡 =強= +枡 ^^^ ・!枡からNが除外される @枡 =強= #枡 =弱= !枡 =強= +枡 ^^^ ・+枡がNで確定する @枡 =強= #枡 =弱= !枡 =強= +枡 ^^^ +[1]-------------------------+[2]-------------------------+[3]-------------------------+ | 1:1 1:2 1:3 | 1:4 1:5 1:6 | 1:7 1:8 1:9 | | ? ? ? | ? ? ? | ? ? hint=N | | 2:1 2:2 2:3 | 2:4 2:5 2:6 | 2:7 2:8 2:9 | | ? ? ? | ? ? hint=N | ? ? ? | | 3:1 3:2 3:3 | 3:4 3:5 3:6 | 3:7 3:8 3:9 | | hint=N ? ? | ? ? ? | ? ? ? | +[4]-------------------------+[5]-------------------------+[6]-------------------------+ | 4:1 4:2 4:3 | 4:4 4:5(#) 4:6 | 4:7 4:8 4:9 | | ? ? m=[?] | ? v=N ? | ? ? ? | | 5:1 5:2 5:3 | 5:4 5:5 5:6 | 5:7 5:8 5:9 | | ? ? ? | ? ? ? | hint=N ? ? | | 6:1 6:2(*) 6:3 | 6:4(@) 6:5 6:6 | 6:7 6:8 6:9 | | ? v=N m=[?] | m=[?] ? ? | ? ? ? | +[7]-------------------------+[8]-------------------------+[9]-------------------------+ | 7:1 7:2 7:3 | 7:4 7:5 7:6 | 7:7 7:8 7:9 | | ? m=[?] m=[N,?] | ? ? ? | ? ? ? | | 8:1 8:2 8:3 | 8:4 8:5 8:6 | 8:7 8:8 8:9 | | ? ? ? | m=[N,?] m=[?] ? | ? hint=N ? | | 9:1 9:2(+) 9:3 | 9:4 9:5(!) 9:6 | 9:7 9:8 9:9 | | ? v=N ? | ? m=[?] ? | ? ? ? | +----------------------------+----------------------------+----------------------------+ 2列目でNが2つ出てくるため矛盾が生じる。 Args: wk (AnalyzeWk): ワーク how_anlz_list (List[HowToAnalyze]): 解析方法 Returns: bool: エラーの場合にFalse """ for loop_memo in range(1, 10): # チェーンを作成 all_chain_list: List[List[Chain]] =\ _create_simple_chain(wk, loop_memo) for chain_list in all_chain_list: first_squ = chain_list[0].squ last_squ = chain_list[len(chain_list) - 1].squ # 交差枡取得 cross_squ_list: List[Square] = SudokuUtil.find_cross_squ( wk, first_squ, last_squ) # 交差枡が変更対象かどうか判定 change_squ_list: List[Square] = list() for cross_squ in cross_squ_list: # 未確定かつメモが存在する枡が対象となる if cross_squ.get_fixed_val() is None and\ loop_memo in cross_squ.memo_val_list: change_squ_list.append(cross_squ) # 対象なし # ⇒次のチェーンをチェック if len(change_squ_list) == 0: continue # 対象あり chain_squ_list: List[Square] = list() for chain in chain_list: chain_squ_list.append(chain.squ) for change_squ in change_squ_list: # メモを除外 change_squ.memo_val_list.remove(loop_memo) # 解析方法生成 how_anlz: HowToAnalyze = HowToAnalyze(Method.SIMPLE_CHAIN) how_anlz.changed_squ = change_squ how_anlz.remove_memo_list.append(loop_memo) how_anlz.trigger_squ_list.extend(chain_squ_list) how_anlz.chain_squ_list.extend(chain_squ_list) how_anlz.msg = MsgFactory.how_to_simple_chain(how_anlz) how_anlz_list.append(how_anlz) return True return True