Source code for dedoku.techniques.wwing
"""W-Wing technique.
Take two bivalue cells with the same candidate pair ``{a, b}`` that share
no house, plus a house in which digit ``b`` has exactly two homes (a strong
link), one home seeing the first pair cell and the other seeing the second.
If neither pair cell held ``a``, both would hold ``b``, and both ends of
the strong link would be blocked — leaving that house with no home for
``b`` at all. Hence at least one pair cell holds ``a``, and ``a`` can be
eliminated from every cell that sees both pair cells.
"""
from __future__ import annotations
from itertools import combinations
from typing import TYPE_CHECKING
from .base import Elimination, Step, Technique
if TYPE_CHECKING:
from ..grid import Grid
__all__ = ["WWing"]
[docs]
class WWing(Technique):
"""Eliminate via two same-pair cells joined by a strong link."""
name = "W-Wing"
[docs]
def apply(self, grid: Grid) -> Step | None:
"""Find the first productive W-Wing on the board.
:param grid: The board to inspect and mutate.
:type grid: Grid
:returns: The eliminations performed, or ``None`` if no productive
pattern exists.
:rtype: Step | None
"""
bivalues = [cell for cell in grid.cells if cell.is_bivalue]
for first, second in combinations(bivalues, 2):
if first.candidates != second.candidates or first.sees(second):
continue
pair = sorted(first.candidates)
for link_digit, elim_digit in (
(pair[0], pair[1]),
(pair[1], pair[0]),
):
for unit in grid.units:
homes = unit.cells_with_candidate(link_digit)
if len(homes) != 2:
continue
if any(home in (first, second) for home in homes):
continue
left, right = homes
if not (
(left.sees(first) and right.sees(second))
or (left.sees(second) and right.sees(first))
):
continue
eliminations: list[Elimination] = []
common = set(first.peers) & set(second.peers)
for cell in sorted(common, key=lambda c: c.position):
if cell.remove_candidate(elim_digit):
eliminations.append(
Elimination(cell.row_index, cell.column_index, elim_digit)
)
if eliminations:
pair_text = ", ".join(str(d) for d in pair)
return Step(
technique=self.name,
description=(
f"W-Wing on pair {{{pair_text}}} at "
f"{first.label} and {second.label}, joined "
f"by the strong link on digit {link_digit} "
f"in {unit.name}: digit {elim_digit} is "
f"removed from every cell seeing both"
),
eliminations=tuple(eliminations),
)
return None