AdventOfCode 2023 Day 16

Part One

很像Day 10的走迷宫,一束光进来,遇到\ / 可以反射,遇到|—产生散射。求最后光走过的路径长度。

.|...\....
|.-.\.....
.....|-...

already_visited

需要判断某处位置是否已经走过,否则递归查询无法终止。因此通过一张表来记录所有访问过的记录。

CREATE OR REPLACE FUNCTION already_visit(_row integer, _col integer, _move text) RETURNS INTEGER AS $$
DECLARE
  _result INTEGER;
BEGIN
  _result := (SELECT COUNT(*) FROM path WHERE r = _row AND c = _col AND move = _move);
  IF _result = 0 THEN
    INSERT INTO PATH(r, c, move) values(_row, _col, _move);
  END IF;
RETURN _result;
END;
$$ LANGUAGE plpgsql;

反射与散射

通过记录next_move来决定下一次光的走向。

反射,修改下方向即可。

WHEN obj = '\' AND pre_move = 'E' THEN 'S'
WHEN obj = '\' AND pre_move = 'W' THEN 'N'
WHEN obj = '\' AND pre_move = 'N' THEN 'W'
WHEN obj = '\' AND pre_move = 'S' THEN 'E'
WHEN obj = '/' AND pre_move = 'E' THEN 'N'
WHEN obj = '/' AND pre_move = 'W' THEN 'S'
WHEN obj = '/' AND pre_move = 'N' THEN 'E'
WHEN obj = '/' AND pre_move = 'S' THEN 'W'

散射的话,后续会有两个方向

WHEN obj = '|' AND pre_move IN ('N', 'S') THEN pre_move
WHEN obj = '|' AND pre_move IN ('E', 'W') THEN 'NS'
WHEN obj = '-' AND pre_move IN ('E', 'W') THEN pre_move
WHEN obj = '-' AND pre_move IN ('N', 'S') THEN 'EW'

最终SQL

WITH RECURSIVE maze AS (
  SELECT _row, row_number() over (partition by _row) as _col, obj
  FROM (
    SELECT row_number() over () as _row, unnest(regexp_split_to_array(line, '')) as obj 
    FROM lance_input
  ) t
),
path as (
  SELECT 1 as _row, 0 as _col, 'E' as next_move, false as already_visited
 
  UNION ALL
 
  SELECT _row, _col, next_move, already_visit(_row, _col, next_move) != 0 as already_visited
  FROM (
    SELECT a._row, a._col,
           CASE WHEN obj = '.' THEN pre_move 
                WHEN obj = '|' AND pre_move IN ('N', 'S') THEN pre_move
                WHEN obj = '|' AND pre_move IN ('E', 'W') THEN 'NS'
                WHEN obj = '-' AND pre_move IN ('E', 'W') THEN pre_move
                WHEN obj = '-' AND pre_move IN ('N', 'S') THEN 'EW'
                WHEN obj = '\' AND pre_move = 'E' THEN 'S'
                WHEN obj = '\' AND pre_move = 'W' THEN 'N'
                WHEN obj = '\' AND pre_move = 'N' THEN 'W'
                WHEN obj = '\' AND pre_move = 'S' THEN 'E'
                WHEN obj = '/' AND pre_move = 'E' THEN 'N'
                WHEN obj = '/' AND pre_move = 'W' THEN 'S'
                WHEN obj = '/' AND pre_move = 'N' THEN 'E'
                WHEN obj = '/' AND pre_move = 'S' THEN 'W'
           END as next_move
    FROM (
      SELECT CASE WHEN next_move = 'N' then _row - 1 WHEN next_move = 'S' then _row + 1 ELSE _row END as _row,
             CASE WHEN next_move = 'E' then _col + 1 WHEN next_move = 'W' then _col - 1 ELSE _col END as _col,
             next_move as pre_move
      FROM (
        SELECT _row, _col, unnest(regexp_split_to_array(next_move, '')) as next_move
        FROM path
        WHERE NOT already_visited
      ) t
    ) a JOIN maze b
    ON a._row = b._row
    AND a._col = b._col
  ) t
)
SELECT COUNT(distinct _row * 110 + _col)
FROM (
  SELECT _row, _col, next_move
  FROM path
) t

Part Two

第二关增加了入口,可以从外围任意入口进入,从而找出路径最长的入口。

the beam enters from any edge tile and heading away from that edge

生成入口

第一关的入口是最左上角向东发出,可以看作起点为(1, 0)。现在外围都可以进入的话,那么就会有4*N个入口

entrance AS (
  SELECT _row, _col, next_move
  FROM (
      SELECT id as _row, 0 as _col, 'E' as next_move
      FROM seq limit 110
  )t

  UNION ALL

  SELECT _row, _col, next_move
  FROM (
    SELECT id as _row, 111 as _col, 'W' as next_move
    FROM seq limit 110
  )t

  UNION ALL

  SELECT _row, _col, next_move
  FROM (
    SELECT 0 as _row, id as _col, 'S' as next_move
    FROM seq limit 110
  )t

  UNION ALL

  SELECT _row, _col, next_move
  FROM (
    SELECT 111 as _row, id as _col, 'N' as next_move
    FROM seq limit 110
  )t
)

已访问函数

CREATE OR REPLACE FUNCTION already_visit2(_entrance integer, _row integer, _col integer, _move text) RETURNS INTEGER AS $$
DECLARE
  _result INTEGER;
BEGIN
  _result := (SELECT COUNT(*) FROM path2 WHERE e = _entrance AND r = _row AND c = _col AND move = _move);
  IF _result = 0 THEN
    INSERT INTO PATH2(e, r, c, move) values(_entrance, _row, _col, _move);
  END IF;
RETURN _result;
END;
$$ LANGUAGE plpgsql;

最终SQL

WITH RECURSIVE maze AS (
  SELECT _row, row_number() over (partition by _row) as _col, obj
  FROM (
    SELECT row_number() over () as _row, unnest(regexp_split_to_array(line, '')) as obj 
    FROM lance_input
  ) t
),
seq AS (
  SELECT 1 as id
  UNION ALL
  SELECT id + 1 as id
  FROM seq
),
entrance AS (
  SELECT _row, _col, next_move
  FROM (
      SELECT id as _row, 0 as _col, 'E' as next_move
      FROM seq limit 110
  )t

  UNION ALL

  SELECT _row, _col, next_move
  FROM (
    SELECT id as _row, 111 as _col, 'W' as next_move
    FROM seq limit 110
  )t

  UNION ALL

  SELECT _row, _col, next_move
  FROM (
    SELECT 0 as _row, id as _col, 'S' as next_move
    FROM seq limit 110
  )t

  UNION ALL

  SELECT _row, _col, next_move
  FROM (
    SELECT 111 as _row, id as _col, 'N' as next_move
    FROM seq limit 110
  )t
),
path as (
  SELECT _row * 110 + _col as _entrance, _row, _col, next_move, false as already_visited
  FROM entrance
 
  UNION ALL
 
  SELECT _entrance, _row, _col, next_move, already_visit2(_entrance, _row, _col, next_move) != 0 as already_visited
  FROM (
    SELECT a._row, a._col, _entrance,
           CASE WHEN obj = '.' THEN pre_move 
                WHEN obj = '|' AND pre_move IN ('N', 'S') THEN pre_move
                WHEN obj = '|' AND pre_move IN ('E', 'W') THEN 'NS'
                WHEN obj = '-' AND pre_move IN ('E', 'W') THEN pre_move
                WHEN obj = '-' AND pre_move IN ('N', 'S') THEN 'EW'
                WHEN obj = '\' AND pre_move = 'E' THEN 'S'
                WHEN obj = '\' AND pre_move = 'W' THEN 'N'
                WHEN obj = '\' AND pre_move = 'N' THEN 'W'
                WHEN obj = '\' AND pre_move = 'S' THEN 'E'
                WHEN obj = '/' AND pre_move = 'E' THEN 'N'
                WHEN obj = '/' AND pre_move = 'W' THEN 'S'
                WHEN obj = '/' AND pre_move = 'N' THEN 'E'
                WHEN obj = '/' AND pre_move = 'S' THEN 'W'
           END as next_move
    FROM (
      SELECT CASE WHEN next_move = 'N' then _row - 1 WHEN next_move = 'S' then _row + 1 ELSE _row END as _row,
             CASE WHEN next_move = 'E' then _col + 1 WHEN next_move = 'W' then _col - 1 ELSE _col END as _col,
             next_move as pre_move, _entrance
      FROM (
        SELECT _entrance, _row, _col, unnest(regexp_split_to_array(next_move, '')) as next_move
        FROM path
        WHERE NOT already_visited
      ) t
    ) a JOIN maze b
    ON a._row = b._row
    AND a._col = b._col
  ) t
)
SELECT _entrance, COUNT(distinct _row * 110 + _col)
FROM (
  SELECT _entrance, _row, _col, next_move
  FROM path
) t
GROUP BY _entrance
ORDER BY 2 DESC;

发表评论