5

日本のパズルゲーム「River IQ Test」を解くプログラムを作ろうとしています。答えを調べることはできますが、それは楽しくも教育的でもありませんか? :)

ゲームの目的は次のとおりです。

ユーザーが川を渡って人を運ぶことができる筏を使用して、ユーザーはすべての人を左側から右側に移動する必要があります。いかだが使用されるたびに、その側の誰かが再び反対側に操縦してより多くの人を集めるまで、反対側にとどまります.

左側に次のような人がいます。

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

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

2 に答える 2

4

私はあなたがPROLOGを学んだことがないと思います。これらの問題はPROLOGでは古典的であり、さらに重要なことに、PROLOGは問題の本質のみを扱います。タイトルに論理とパズルを見たとき、あなたが論理言語を必要としていることは明らかでした。あなたがC#を要求したことは知っていますが、これは論理的な問題であるとあなたが言ったように、OOの問題ではありません。

深さpg.229セクション8.3のPrologプログラミングを参照してください。宣教師と人食い人解決策は、質問にあるすべてのコードよりも小さいことに注意してください。誤解しないでください。何年も前、私は同じボートに乗っていました。

私が知っている最高のプログラマーの中には、このような問題に取り組むのは解決策ではありませんが、自分たちで解決するために、後で使用できる重要なことを学ぶことができるからです。PROLOGがこれをどのように解決するかを理解するために数ヶ月かかります。そうすれば、誰かがあなたに提案をするよりもはるかにうまくいくでしょう。再帰とAIが問題をどのように解決するかを理解していない場合は、解決策を得るまでにほとんど理解できます。

編集

しかし、C#はそのような問題解決を行うことができませんか?

PROLOGには推論エンジンバックチェーンが組み込まれており、ルールが与えられた場合に解決策を見つける作業を行います。最初の問題を解決するよりも難しい、最初から作成することもできますが、C#にはPROLOGのオープンソース実装があり、John PoolによるC#Prologがあります。ソースコードを見てPROLOGがどのように機能するかを理解することはできますが、コードは高度に最適化されているため、最初にPROLOGを理解しない限り理解するのは簡単ではありません。

F#または関数型言語を知っている場合、純粋に関数型言語でプロローグインタープリターを実装するにはどうすればよいですか?役立つかもしれません。

基本的に問題は、一連のルールを作成し、すべてのルールを満たす結果が得られるまでルールの組み合わせを試すことです。

これらのキーワードとフレーズから始めて、ここでインターネットと質問を検索してください。

統一
構文統一
バックチェーン
推論エンジン
プロローグのしくみ

于 2012-12-02T21:00:34.830 に答える
0


これは、単純なアルゴリズム/手法であるバックトラッキングを使用して、再帰をサポートする任意の言語で比較的簡単に実行できます。

例として:

問題のある状態(両側に人)があり 、その状態から
可能なすべての交差点を列挙し、それぞれを試す関数があります。

交差が OK であれば、新しい状態を履歴に追加し、新しい状態から
自分自身を呼び出します。
それが戻ると、履歴から状態を削除し
、次のクロッシングを試みます。
交差点がなくなると、呼び出し元に戻ります。

最終状態のみが目標であるため、履歴が必要です。
そうしないと、ループに陥る可能性があります。
グループ A が一方通行に交差し、その後戻り、再び交差し、後方に戻ります。
または、A が一方通行に交差し、B が逆方向に交差し、B が戻り、A が戻ります。

(申し訳ありませんが、ここでコード フォーマッタを機能させる方法がわかりませんでした。)

于 2014-02-04T09:13:58.683 に答える