|
用霹雳火pilisky的思路写出来的MODEL:迭代次数很少,利用前述四个推理规则搞定,只是前两行的顺序有些讲究。性能比上述写法都更好。
WITH cells AS ( ------- 构造所有单元格及其坐标
SELECT LEVEL cell_id -------- 从左上角开始,从左到右从上到下从1开始编号
,CEIL(LEVEL/:v_width) r -------- 行号,1~v_height
,MOD(LEVEL-1,:v_width)+1 c -------- 列号,1~v_width
,SUBSTR(:v_str,LEVEL,1) cnt -------- 该单元格周围的地雷数目
FROM DUAL CONNECT BY LEVEL<=:v_width*:v_height
)
,cells2 AS (
SELECT cell_id,r,c,DECODE(cnt,' ',0,TO_NUMBER(cnt)) AS cnt,MAX(CASE WHEN cell_id=1 THEN cnt END) OVER() AS cnt_1
------- 用CASE计算周围的空位数
,CASE WHEN cnt=' ' THEN 0
ELSE CASE WHEN r>1 THEN
CASE WHEN c>1 THEN DECODE(SUBSTR(:v_str,cell_id-:v_width-1,1),' ',1,0) ELSE 0 END
+DECODE(SUBSTR(:v_str,cell_id-:v_width,1),' ',1,0)
+CASE WHEN c<:v_width THEN DECODE(SUBSTR(:v_str,cell_id-:v_width+1,1),' ',1,0) ELSE 0 END
ELSE 0
END
+CASE WHEN c>1 THEN DECODE(SUBSTR(:v_str,cell_id-1,1),' ',1,0) ELSE 0 END
+CASE WHEN c<:v_width THEN DECODE(SUBSTR(:v_str,cell_id+1,1),' ',1,0) ELSE 0 END
+CASE WHEN r<:v_height THEN
CASE WHEN c>1 THEN DECODE(SUBSTR(:v_str,cell_id+:v_width-1,1),' ',1,0) ELSE 0 END
+DECODE(SUBSTR(:v_str,cell_id+:v_width,1),' ',1,0)
+CASE WHEN c<:v_width THEN DECODE(SUBSTR(:v_str,cell_id+:v_width+1,1),' ',1,0) ELSE 0 END
ELSE 0
END
END AS empty_cnt
from cells c1
)
,guess AS (SELECT 0 AS mine FROM DUAL UNION ALL SELECT 1 FROM DUAL)
,cells3 AS (
SELECT guess.mine AS solution_id
,cells2.cell_id
,cells2.r
,cells2.c
,cells2.cnt
,CASE WHEN cnt_1=' ' AND cnt>0 AND (r,c) IN ((1,2),(2,1),(2,2)) THEN cnt-guess.mine
ELSE cells2.cnt
END AS new_cnt
,CASE WHEN cnt_1=' ' AND (r,c) IN ((1,2),(2,1),(2,2)) THEN cells2.empty_cnt-1
ELSE cells2.empty_cnt
END AS empty_cnt
,CASE WHEN cells2.cell_id=1 AND cells2.cnt=0 THEN guess.mine ELSE -1 END AS guess_val
,COUNT(CASE WHEN cell_id>1 AND cnt=0 THEN 1 END) OVER(PARTITION BY guess.mine) AS empty_cells_cnt
FROM cells2,guess
WHERE guess.mine=0 AND SUBSTR(:v_str,1,1)<>' ' OR SUBSTR(:v_str,1,1)=' '
)
,empty_cells AS (
SELECT r,c,ROW_NUMBER() OVER(ORDER BY CASE WHEN cell_id<=2*:v_width THEN c ELSE 9999 END
,CASE WHEN cell_id<=2*:v_width THEN r ELSE 9999 END
,cell_id
) empty_id
FROM cells2
WHERE cnt=0 AND cell_id>1 ---- 跳过(1,1)
)
,t AS (
SELECT * FROM cells3
MODEL
REFERENCE e
ON (SELECT * FROM empty_cells)
DIMENSION BY (empty_id)
MEASURES (r,c)
MAIN m
PARTITION BY (solution_id)
DIMENSION BY (r,c)
MEASURES (cnt,empty_cnt,new_cnt,guess_val,0 fail_flag,r r2, c c2,-1 tmp_val,empty_cells_cnt)
RULES SEQUENTIAL ORDER
ITERATE (10000) ----- 循环次数设为不可能达到的上限
UNTIL (fail_flag[1,1]=1 OR ITERATION_NUMBER >=empty_cells_cnt[1,1])
(
r2[1,1] = e.r[ITERATION_NUMBER+1]
,c2[1,1] = e.c[ITERATION_NUMBER+1]
,tmp_val[1,1] = CASE WHEN cnt[r2[1,1]-1,c2[1,1]]=0 THEN
guess_val[r2[1,1]-1,c2[1,1]]
WHEN cnt[r2[1,1]-1,c2[1,1]-1]>0 AND new_cnt[r2[1,1]-1,c2[1,1]-1] = 0 THEN 0
WHEN cnt[r2[1,1]-1,c2[1,1]-1]>0 AND new_cnt[r2[1,1]-1,c2[1,1]-1] = empty_cnt[r2[1,1]-1,c2[1,1]-1] THEN 1
ELSE -1
END
,guess_val[r2[1,1],c2[1,1]]=CASE WHEN tmp_val[1,1]=-1 OR guess_val[r2[1,1],c2[1,1]]=9 THEN guess_val[r2[1,1],c2[1,1]]
WHEN guess_val[r2[1,1],c2[1,1]]<>-1 AND guess_val[r2[1,1],c2[1,1]]<>tmp_val[1,1] THEN 9
ELSE tmp_val[1,1]
END
,tmp_val[1,1] = CASE WHEN cnt[r2[1,1]-1,c2[1,1]]=0 THEN
guess_val[r2[1,1]-1,c2[1,1]]
WHEN cnt[r2[1,1]-1,c2[1,1]]>0 AND new_cnt[r2[1,1]-1,c2[1,1]] = 0 THEN 0
WHEN cnt[r2[1,1]-1,c2[1,1]]>0 AND new_cnt[r2[1,1]-1,c2[1,1]] = empty_cnt[r2[1,1]-1,c2[1,1]] THEN 1
ELSE -1
END
,guess_val[r2[1,1],c2[1,1]]=CASE WHEN tmp_val[1,1]=-1 OR guess_val[r2[1,1],c2[1,1]]=9 THEN guess_val[r2[1,1],c2[1,1]]
WHEN guess_val[r2[1,1],c2[1,1]]<>-1 AND guess_val[r2[1,1],c2[1,1]]<>tmp_val[1,1] THEN 9
ELSE tmp_val[1,1]
END
,tmp_val[1,1] = CASE WHEN cnt[r2[1,1],c2[1,1]-1]=0 THEN
guess_val[r2[1,1],c2[1,1]-1]
WHEN cnt[r2[1,1],c2[1,1]-1]>0 AND new_cnt[r2[1,1],c2[1,1]-1] = 0 THEN 0
WHEN cnt[r2[1,1],c2[1,1]-1]>0 AND new_cnt[r2[1,1],c2[1,1]-1] = empty_cnt[r2[1,1],c2[1,1]-1] THEN 1
ELSE -1
END
,guess_val[r2[1,1],c2[1,1]]=CASE WHEN tmp_val[1,1]=-1 OR guess_val[r2[1,1],c2[1,1]]=9 THEN guess_val[r2[1,1],c2[1,1]]
WHEN guess_val[r2[1,1],c2[1,1]]<>-1 AND guess_val[r2[1,1],c2[1,1]]<>tmp_val[1,1] THEN 9
ELSE tmp_val[1,1]
END
,tmp_val[1,1] = guess_val[r2[1,1],c2[1,1]]
,fail_flag[1,1]=CASE WHEN tmp_val[1,1]=-1 OR tmp_val[1,1]=9 THEN 1 ELSE 0 END
,new_cnt[r2[1,1]-1,c2[1,1]-1] = new_cnt[r2[1,1]-1,c2[1,1]-1] - tmp_val[1,1]
,new_cnt[r2[1,1]-1,c2[1,1] ] = new_cnt[r2[1,1]-1,c2[1,1] ] - tmp_val[1,1]
,new_cnt[r2[1,1]-1,c2[1,1]+1] = new_cnt[r2[1,1]-1,c2[1,1]+1] - tmp_val[1,1]
,new_cnt[r2[1,1] ,c2[1,1]-1] = new_cnt[r2[1,1] ,c2[1,1]-1] - tmp_val[1,1]
,new_cnt[r2[1,1] ,c2[1,1]+1] = new_cnt[r2[1,1] ,c2[1,1]+1] - tmp_val[1,1]
,new_cnt[r2[1,1]+1,c2[1,1]-1] = new_cnt[r2[1,1]+1,c2[1,1]-1] - tmp_val[1,1]
,new_cnt[r2[1,1]+1,c2[1,1] ] = new_cnt[r2[1,1]+1,c2[1,1] ] - tmp_val[1,1]
,new_cnt[r2[1,1]+1,c2[1,1]+1] = new_cnt[r2[1,1]+1,c2[1,1]+1] - tmp_val[1,1]
,empty_cnt[r2[1,1]-1,c2[1,1]-1]= empty_cnt[r2[1,1]-1,c2[1,1]-1] - 1
,empty_cnt[r2[1,1]-1,c2[1,1] ]= empty_cnt[r2[1,1]-1,c2[1,1] ] - 1
,empty_cnt[r2[1,1]-1,c2[1,1]+1]= empty_cnt[r2[1,1]-1,c2[1,1]+1] - 1
,empty_cnt[r2[1,1] ,c2[1,1]-1]= empty_cnt[r2[1,1] ,c2[1,1]-1] - 1
,empty_cnt[r2[1,1] ,c2[1,1]+1]= empty_cnt[r2[1,1] ,c2[1,1]+1] - 1
,empty_cnt[r2[1,1]+1,c2[1,1]-1]= empty_cnt[r2[1,1]+1,c2[1,1]-1] - 1
,empty_cnt[r2[1,1]+1,c2[1,1] ]= empty_cnt[r2[1,1]+1,c2[1,1] ] - 1
,empty_cnt[r2[1,1]+1,c2[1,1]+1]= empty_cnt[r2[1,1]+1,c2[1,1]+1] - 1
)
)
SELECT res
FROM (
SELECT LISTAGG(CASE WHEN cnt=0 THEN CASE WHEN guess_val=1 THEN '*' ELSE ' ' END ELSE TO_CHAR(cnt) END) WITHIN GROUP (ORDER BY r,c) as res
FROM (
SELECT t.*,MAX(fail_flag) OVER(PARTITION BY solution_id) fail
,SUM(CASE WHEN cnt=0 THEN guess_val END) OVER(PARTITION BY solution_id) mine_cnt
FROM t
WHERE cnt IS NOT NULL
)
WHERE fail=0 AND mine_cnt=:v_cnt
GROUP BY solution_id
)
WHERE ROWNUM=1
; |
|