可能具有挑战性:我如何在这个解谜程序中实现解谜逻辑

  • 本文关键字:解谜 程序 实现 挑战性 c# logic
  • 更新时间 :
  • 英文 :


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

这是游戏的目标:

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

左边有以下人开始:

  • 1囚犯
  • 1名警察
  • 1父亲
  • 2儿子
  • 1母亲
  • 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();
}
}

PassengerList类

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;
}
}

边类(如在,河边)

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);
}
}
}

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

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

我认识的一些最好的程序员处理这样的问题并不是为了解决问题,而是因为要自己解决问题,他们知道自己会学到一些重要的东西,然后再使用。花几个月的时间来了解PROLOG是如何解决这个问题的,你会比别人给你建议的要好得多。如果你不了解递归以及人工智能如何解决问题,那么当你得到解决方案时,你基本上会理解。

编辑

C#不能解决那种问题吗?

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

如果你知道F#或函数式语言,如何用纯函数式语言实现prolog解释器?可能会有所帮助。

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

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

统一
句法统一
Back-chaining
推理引擎
prolog如何工作

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

举个例子:

你有问题状态(两边都有人)
你有一个函数,它列举了该状态下所有可能的交叉点
,并尝试每一个。

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

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

(对不起,我不知道如何使代码格式化程序在这里工作。)

最新更新