5

我正在尝试制作一个解决日本益智游戏“河流智商测试”的程序,我知道,我可以查找解决方案,但现在不会有趣和有教育意义吗?:)

这是游戏的目标:

使用木筏,用户可以运送人过河,用户必须将所有的人从左侧运送到右侧。每次使用木筏时,它都会停留在另一侧,直到另一侧的人再次将它引到另一侧以吸引更多人。

左边有以下人开始:

  • 1 囚犯
  • 1名警察
  • 1 父亲
  • 2 个儿子
  • 1 母亲
  • 2个女儿

下面是类的层次结构:

乘客

  • 飞行员
    • 家长
      • 母亲
      • 父亲
    • 警察
  • 囚犯
  • 孩子
    • 女儿
    • 儿子

必须遵守以下规则:

  • 一次只有 2 个人在木筏上。
  • 只有警察、父亲和母亲才能驾驶木筏
  • 在没有警察在场的情况下,囚犯不能在其他人在场的情况下被安置在木筏上或河的任何一侧。
  • 没有母亲在场,父亲不能和女儿一起在木筏上或河的两边。
  • 没有父亲在场,母亲不能和儿子一起在木筏上或河的两边。

我还需要完成的是:

  • 使用试错解决逻辑直到解决难题的逻辑
  • 打印最终成功解决方案的逻辑
  • 检查是否每个人都到达另一边的逻辑。

这是我当前的代码:

程序类(解决逻辑应该放在这里)

class Program
{
    Side _leftSide = new Side(Side.RL_Side.RL_LeftSide);
    Side _rightSide = new Side(Side.RL_Side.RL_RightSide);
    Raft _myRaft = new Raft();
    static void Main(string[] args)
    {
        // TODO: put systematic trial-and-error solving logic here
        // TODO: make sure that successful solution is printed to console



        Console.ReadLine();
    }
}

乘客列表类

public class PassengerList : List<Passenger>
{
    public bool CheckRulesObeyed()
    {
        bool isPoliceman = isPresent<Policeman>();
        bool isPrisoner = isPresent<Prisoner>();
        bool isFather = isPresent<Father>();
        bool isSon = isPresent<Son>();
        bool isMother = isPresent<Mother>();
        bool isDaughter = isPresent<Daughter>();
        // ----------------------------------------------
        bool isPrisoner_NonPoliceman = (isPrisoner && (isFather || isMother || isDaughter || isSon));

        bool isPrisonerRuleViolated = (!(isPoliceman && isPrisoner) && isPrisoner_NonPoliceman);
        bool isDaughterRuleViolated = ((isFather && isDaughter) && !isMother);
        bool isSonRuleViolated = ((isMother && isSon) && !isFather);

        bool AreAllRulesObeyed = !(isPrisonerRuleViolated && isDaughterRuleViolated && isSonRuleViolated);

        return AreAllRulesObeyed;
    }

    private bool isPresent<T>() where T: Passenger
    {
        foreach (Passenger p in this)
        {
            if (p is T)
                return true;
        }
        return false;
    }
}

Side Class(如在河边)

public class Side
{
    public enum RL_Side
    { 
        RL_RightSide,
        RL_LeftSide
    }

    public RL_Side _whichSide;

    public PassengerList _myPeople;

    public Side(RL_Side side)
    {
        _whichSide = side;
        _myPeople = new PassengerList();

        // left side starts with all the people
        if (_whichSide == RL_Side.RL_LeftSide)
        {
            _myPeople.Add(new Prisoner());
            _myPeople.Add(new Policeman());
            _myPeople.Add(new Father());
            _myPeople.Add(new Son());
            _myPeople.Add(new Son());
            _myPeople.Add(new Mother());
            _myPeople.Add(new Daughter());
            _myPeople.Add(new Daughter());
        }
    }

    public bool didEveryoneMakeItToRightSide()
    {
        if (_whichSide == RL_Side.RL_RightSide)
        { 
            // TODO: implement logic that checks and returns if everyone is on the right side.

        }
        return false;
    }
}

筏类

public class Raft
{
    public Side _mySide;
    public PassengerList _myPassengers;

    public void ChangeSides(Side Destination)
    {
        _mySide = Destination;
    }

    public bool LoadRaft(Pilot myPilot, Passenger myPassenger)
    {
        bool areRulesObeyed = true;
        _myPassengers.Add(myPilot);
        _myPassengers.Add(myPassenger);

        areRulesObeyed = _myPassengers.CheckRulesObeyed();

        if (areRulesObeyed == false)
        {
            UnloadRaft();
        }

        return areRulesObeyed;

    }
    public void UnloadRaft()
    {
        foreach (Passenger p in _myPassengers)
        {
            _mySide._myPeople.Add(p);
            _myPassengers.Remove(p);
        }
    }
}
4

2 回答 2

4

我认为您从未学习过PROLOG。这些问题在PROLOG中是经典的,更重要的是PROLOG只处理问题的本质。当我在标题中看到逻辑和谜题时,很明显你需要一种逻辑语言。我知道您要求使用 C#,但这不是 OO 问题,正如您所说,这是一个逻辑问题。

参见Prolog 深度编程pg.229 第 8.3 节。传教士和食人者 请注意,解决方案比问题中的所有代码都小。不要误会我的意思,很多年前我在同一条船上,双关语。

我认识的一些最优秀的程序员处理此类问题不是为了解决方案,而是因为自己解决问题,他们知道他们会学到一些重要的东西,以后可以使用。花几个月的时间来了解 PROLOG 如何解决这个问题,你会比给你建议的人好得多。如果您不了解递归以及 AI 如何解决问题,您通常会在获得解决方案时了解。

编辑

但是,C# 是否无法解决这种问题?

PROLOG 有一个内置的推理引擎反向链接,这是在给定规则的情况下找到解决方案的工作。您可以从头开始创建一个,这比解决最初的问题更难,但是有一个 C# 中 PROLOG 的开源实现, John Pool 的C#Prolog。虽然您可以查看源代码以了解 PROLOG 的工作原理,但该代码经过大量优化,因此除非您先了解 PROLOG,否则不容易理解。

如果您了解 F# 或函数式语言,如何用纯函数式语言实现 prolog 解释器?可能有帮助。

基本上,问题归结为创建一组规则,然后尝试组合规则,直到获得满足所有规则的结果。

从这些关键字和短语开始,在此处搜索互联网和问题。

统一
句法统一
回链
推理引擎
prolog 的工作原理

于 2012-12-02T21:00:34.830 回答
0

您可以使用任何支持递归的语言相对容易地做到这一点,
使用回溯,一种简单的算法/技术。

例如:

你有问题状态(每一边的人)
你有一个函数可以枚举
来自该状态的所有可能的交叉点,并尝试每一个。

如果交叉正常,它将新状态添加到历史记录中,
并从新状态调用自身。
当它返回时,它会从历史记录中删除状态
并尝试下一次交叉。
当它用完交叉口时,它会返回给它的调用者。

您需要历史,因为唯一的最终状态是目标。
否则,您可能会陷入循环:
A组单向交叉,然后返回,再次交叉,返回等。
或A组单向交叉,B交叉返回,B返回,A返回等。

(抱歉,我不知道如何让代码格式化程序在这里工作。)

于 2014-02-04T09:13:58.683 回答