You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Prolog水排序解谜器异常:重复结果、状态残留与GoalCheck不一致

Prolog水排序解谜程序异常排查与修复

问题1:查询goal(S)首次返回S=s0,按分号得false,再次查询才输出目标状态

原因

  1. search谓词逻辑偏差:当前search(S)的判定逻辑是「S是目标状态,或从S可抵达目标状态」,而非「S是从初始状态s0出发能到达的目标状态」。查询goal(S)时,Prolog会优先将S绑定为s0(因为s0可抵达目标),而非直接返回最终目标状态。
  2. 动态事实未自动清理:pour操作通过assert永久向数据库添加中间状态的bottle事实,第一次查询后这些事实残留,第二次查询时直接匹配到目标状态的事实,才返回正确结果。

解决方案

  • 重构search为带状态传递的递归谓词,明确从初始状态s0出发,追踪并返回最终目标状态;
  • 添加状态清理逻辑,每次查询前清空所有动态bottle事实(除初始状态),避免残留事实干扰结果。

问题2:执行goal(S)后查询goalCheck(S)返回状态值而非布尔值,初始加载时查询则返回false

原因

  1. 初始状态不满足目标:初始状态s0下没有两个瓶子装满同色液体,goalCheck(s0)返回false符合预期;
  2. Prolog变量绑定特性:执行goal(S)后S已被绑定为某个状态,此时查询goalCheck(S)会尝试匹配目标条件,若匹配成功,Prolog会输出满足条件的变量绑定(如C1=b, C2=r),而非单纯的true——这是Prolog默认行为,当谓词成功且存在未绑定变量时,会输出变量绑定结果。

解决方案

  • 若需强制获取布尔结果,可查询(goalCheck(S) -> true; false),无论变量是否绑定,都会返回true或false;
  • 确保goal(S)返回的S是目标状态而非初始状态,此时goalCheck(S)会直接返回true(或确定的变量绑定值)。

问题3:修改瓶子初始内容后重新consult,结果无变化,需重启后再consult才生效

原因

动态事实未被清理:初始的bottle(..., s0)事实是静态定义的,consult时不会覆盖已存在的动态bottle事实(包括之前pour操作添加的中间状态事实),新的初始状态被旧事实干扰,导致结果无变化。

解决方案

  • 添加clear_bottles/0谓词,用于清理所有bottle事实并重新加载初始状态,每次修改初始内容或查询前调用该谓词:
    clear_bottles :-
        retractall(bottle(_, _, _, _)),
        % 重新初始化初始状态
        bottle(1, b, r, s0),
        bottle(2, b, r, s0),
        bottle(3, e, e, s0).
    
  • 或将初始状态的bottle事实改为动态,确保consult时旧事实被完全替换。

修改后的完整代码

:- dynamic bottle/4.

% 清理所有瓶子事实并重新初始化初始状态
clear_bottles :-
    retractall(bottle(_, _, _, _)),
    bottle(1, b, r, s0),
    bottle(2, b, r, s0),
    bottle(3, e, e, s0).

% Valid pour action: The action is valid if:
% 1. The destination bottle is not full (i.e., its top and bottom colors are not the same).
% 2. The source bottle is not empty (i.e., it has a color in the top layer).
% 3. The top color layers of both the source and destination bottles are the same.
% 4. If the destination bottle is empty, pouring is always valid as long as the source bottle is not empty.

validPour(Source, Destination, S) :-
    bottle(Source, TopColor, BotColorSrc, S),
    bottle(Destination, TopColorDest, BotColorDest, S),
    \+ isEmpty(Source, S),
    (
        (TopColor \= e, (isEmpty(Destination, S) ; (TopColorDest == e, BotColorDest == TopColor)))
        ;
        (TopColor == e, BotColorSrc \= e, (isEmpty(Destination, S) ; (TopColorDest == e, BotColorDest == BotColorSrc)))
    ).

isEmpty(Bottle, S) :- bottle(Bottle, e, e, S).

% Pour action: Pour from bottle i to bottle j(使用assertz保证事实添加顺序)
pour(Source, Destination, Third, S, NextState) :-
    (bottle(Source, Color1, BotColorSrc, S) ; bottle(Source, e, Color1, S)),
    bottle(Destination, Color2, BotColorDest, S),
    bottle(Third, Color3, BotColorThird, S),
    NextState = result(pour(Source, Destination), S),
    Color1 \= e,
    % 更新目标瓶子状态
    (isEmpty(Destination, S) ->
        assertz(bottle(Destination, e, Color1, NextState))
        ;
        (BotColorDest \= e, BotColorDest == Color1, Color2 == e ->
            assertz(bottle(Destination, Color1, BotColorDest, NextState))
        )
    ),
    % 更新源瓶子状态
    (bottle(Source, Color1, BotColorSrc, S) ->
        assertz(bottle(Source, e, BotColorSrc, NextState))
        ;
        bottle(Source, e, Color1, S) ->
            assertz(bottle(Source, e, e, NextState))
    ),
    % 更新第三个瓶子状态
    assertz(bottle(Third, Color3, BotColorThird, NextState)).

goalCheck(S) :-
    % Case 1: Bottles 1 and 2 are uniform and full, Bottle 3 is empty
    (bottle(1, C1, C1, S), bottle(2, C2, C2, S), bottle(3, e, e, S), C1 \= e, C2 \= e, C1 \= C2)
    ;
    % Case 2: Bottles 2 and 3 are uniform and full, Bottle 1 is empty
    (bottle(2, C1, C1, S), bottle(3, C2, C2, S), bottle(1, e, e, S), C1 \= e, C2 \= e, C1 \= C2)
    ;
    % Case 3: Bottles 1 and 3 are uniform and full, Bottle 2 is empty
    (bottle(1, C1, C1, S), bottle(3, C2, C2, S), bottle(2, e, e, S), C1 \= e, C2 \= e, C1 \= C2).

% 重构search:从指定状态出发,递归寻找目标状态
search(CurrentState, GoalState) :-
    goalCheck(CurrentState),
    GoalState = CurrentState.
search(CurrentState, GoalState) :-
    \+ goalCheck(CurrentState),
    (
        (validPour(1, 2, CurrentState), pour(1, 2, 3, CurrentState, NextState), search(NextState, GoalState))
        ;
        (validPour(1, 3, CurrentState), pour(1, 3, 2, CurrentState, NextState), search(NextState, GoalState))
        ;
        (validPour(3, 1, CurrentState), pour(3, 1, 2, CurrentState, NextState), search(NextState, GoalState))
        ;
        (validPour(3, 2, CurrentState), pour(3, 2, 1, CurrentState, NextState), search(NextState, GoalState))
        ;
        (validPour(2, 3, CurrentState), pour(2, 3, 1, CurrentState, NextState), search(NextState, GoalState))
        ;
        (validPour(2, 1, CurrentState), pour(2, 1, 3, CurrentState, NextState), search(NextState, GoalState))
    ).

% goal/1:先清理状态,再从初始状态s0开始搜索目标状态
goal(GoalState) :-
    clear_bottles,
    search(s0, GoalState).

使用说明

  1. 查询目标状态:直接调用goal(S),会返回从s0出发到达的目标状态;
  2. 修改初始状态:修改clear_bottles中的初始bottle断言,调用clear_bottles或重新consult文件即可生效;
  3. 检查状态是否为目标:调用(goalCheck(S) -> true; false)获取布尔结果,或直接调用goalCheck(S)查看变量绑定。

内容的提问来源于stack exchange,提问作者Ahmed Haitham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 05:29:52