美文网首页
从配置文件说到逻辑式编程

从配置文件说到逻辑式编程

作者: golden_age | 来源:发表于2016-08-06 18:10 被阅读0次

在游戏制作里,一般会存在大量的配置文件,比如说地图的配置,NPC的配置,技能的配置,任务活动的配置,
这些配置经常是以 二维表的形式存在,我们的策划喜欢强大的表格处理工具excel

这种配置,可以算得上是小型数据库,我曾见过超过10M的配置数据,

面对这样的配置类型,我们有查询的需求,比如说:

  • 查询当前玩家所在地图所有可用的跳转点
  • 查询玩家当前地图上所有可交互的NPC,将它们生成在玩家附近。
  • 满足当前条件的所有可接任务的集合。
  • 获取当前条件下玩家可学习的技能集合。

如果
我们面对非SQL系统的数据源,
我们需要在当前的编程环境下,方便地表达查询,

c#的解决办法, linq, (其实我们可以在很多语言里实现类似的技术, 不过这是另外一个题目了,stream API)
c# 对于任何数据类型,如果实现了IEnumerable 接口,即视为可应用linq查询的数据源。

例: 查询当前玩家所在地图所有可用的跳转点
(假设可用跳转点条件为,当前地图,等级要求,完成任务ID)

    class Potal
    {
        int mapID;
        int level;
        int taskID;
        ...
    };

    class Potals : IEnumerable<Potal>
    {
        ...
    }


...
var validPotals = from potal in potals
                  where potal.mapID == player.map.ID &&
                  potal.level <= player.level &&
                  player.taskDone.Contain(potal.taskID);

对于不熟linq语法的读者,我写成另一种形式

        var validPotals = potals.Where(potal =>
        {
            return
            potal.mapID == player.map.ID &&
            potal.level <= player.level &&
            player.taskDone.Contain(potal.taskID);
        });

翻译成最平凡的语法是,

        List<Potal> validPotals1 = new List<Potal>();
        foreach (var potal in potals)
        {
            if(potal.mapID == player.map.ID &&
                potal.level <= player.level &&
                player.taskDone.Contain(potal.taskID))
            {
                validPotals1.Add(potal);
            }
        }

在这样简单的例子里, linq并不能表达它优势,但毕竟, linq给我们带来了强大的表达能力,我们初步实现了,“告诉计算机我们需要什么,而不是怎样去做",
在计算机科学里,这样的编程范式叫做”逻辑式编程“,逻辑式编程能做的远不止数据库查询(相对于大家对SQL这种特定语言的印象)。

逻辑式编程语言(prolog)里,只有三类表达式,

  • 事实(Facts), 它是数据集,可以理解为各种数据源,数据表,
  • 规则(Rules): 对应的是 合一规则, 可以理解成数据源的约束,
  • 查询(Queries): 问题求解

我们都学过一些基础逻辑, 一个有名的例子(三段论):
一切人都会死,
苏格拉底是人,
所以苏格拉底会死,

写成prolog程序

human(Socrates)//事实:
motal(X) :- human(X)//规则:
?- motal(Socrates)//查询
>> yes

我们推导出 苏格拉底 确实会死,

好吧,我们强来,在c# 表达如此逻辑

    class Mortal { }
    class Human : Mortal { }//规则,人类是会死生物
    static void Main(string[] args)
    {
        var Socrates = new Human(); //事实
        if(Socrates is Mortal) //查询
        {
            Console.WriteLine("yes!");
        }
    }

(好像我们发现了点什么...关于类型和逻辑的关系是个艰深的话题 )

以下,我做一个极为简单的逻辑系统,在这系统里完成以上的逻辑式查询, 为了易于表达,测试里我们只考虑一元的情况(arity), 比如说Query1(将只查询一元属性的Fact)

//lxf0525@gmail.com
class KnowlegeBase
{
    class Entity
    {
        public string name = "";
    }
    class Atom : Entity { }//原子enity
    class Variable : Entity { }//变量 enity, 将作为合一的名称

    class Fact //事实
    {
        public static List<Fact> facts = new List<Fact>();
        //加入一个Fact(一元)
        public static Fact newFact1(string tag, string atomName)
        {
            Fact f = new Fact();
            f.tag = tag;
            f.ents.Add(new Atom() { name = atomName });
            facts.Add(f);
            return f;
        }

        public string tag = "";
        public List<Entity> ents = new List<Entity>();
        public Atom getFirstAtom()
        {
            return ents.First() as Atom;
        }

        public static IEnumerable<Fact> getFact(string tag)
        {
            return facts.Where(f => f.tag == tag);
        }
    }

    class Rule//规则
    {
        public Fact implication;
        public List<Fact> prerequisite = new List<Fact>();
        
        public static List<Rule> Rules = new List<Rule>();
        public static Rule newRule1(string tagImplication, string tagPrerequisite)
        {
            Fact f = new Fact();
            f.tag = tagImplication;
            f.ents = new List<Entity>() { new Variable() { name = "X"}};
            Fact f1 = new Fact();
            f1.tag = tagPrerequisite;
            f1.ents = new List<Entity>() { new Variable() { name = "X" } };

            Rule r = new Rule();
            r.implication = f;
            r.prerequisite.Add(f1);

            Rules.Add(r);
            return r;
        }

        public static IEnumerable<Rule> getRule(string implicationTag)
        {
            return Rules.Where(r => r.implication.tag == implicationTag);
        }
    }

    class Query //查询
    {

        //1元Fact的查询
        public static IEnumerable<Atom> Query1(string factTag, string atomName)
        {
            //基本事实的查询
            var facts = Fact.getFact(factTag).Where(f => f.getFirstAtom().name == atomName);
            foreach(var f in facts)
            {
                yield return f.getFirstAtom();
            }
            //规则查询,
            foreach(var r in Rule.getRule(factTag))
            {
                foreach(var f in r.prerequisite)
                {
                    foreach(var a in Query1(f.tag, atomName) )
                    {
                        yield return a;
                    }
                }
            }
        }
    }

   
    static void Main(string[] args)
    {
        Fact.newFact1("Human", "Socrates");
        Rule.newRule1("Mortal", "Human");
        Console.WriteLine(Query.Query1("Mortal", "Socrates").Count() > 0? yes : no);
    }
}
////
// >> yes

可以看到,这样小的系统通过查询的方式,确实证明了 ”苏格拉底必死"这一命题。

相关文章

  • 从配置文件说到逻辑式编程

    在游戏制作里,一般会存在大量的配置文件,比如说地图的配置,NPC的配置,技能的配置,任务活动的配置,这些配置经常是...

  • Day-14 - 类和对象(2018-10-17)

    一、面向对象编程 编程思想: 面向过程编程 ---> 算法,逻辑(数学逻辑) 函数式编程 ---> 函数,模块 ...

  • RxJava系列|RxJava简介(一)

    函数响应式编程 函数式编程是一种编程范式。 常见的编程范式有:命令式编程、函数式编程和逻辑式编程。 面向对象就是一...

  • 你真的理解面向对象吗?

    面向对象 我们常见的编程范式有命令式编程,函数式编程,逻辑式编程,而面向对象编程是一种命令式编程。 命令式编程是面...

  • 类和对象

    01-面向对象编程 ----编程思想 ✈ 1、面向过程编程 --> 算法,逻辑(数学逻辑)✈ 2、函数式编程 --...

  • day14-类和对象

    一、面向对象编程 1.编程思想 面向过程编程 ---> 算法,逻辑(数学逻辑) 函数式编程 ---> 函数,模块...

  • Rxjava总结

    概念 函数式编程就是一种编程范式,常见的编程范式有命令式编程 函数式编程 和逻辑式编程。。。常见的面向对象编程是一...

  • 2018-10-17-day14-类和对象

    一.面向对象编程 编程思想:1.面向过程编程 ---> 算法,逻辑(数学逻辑) 2.函数式编程 ---> 函数,...

  • Day13_类和对象

    一、面向对象编程 编程思想:1.面向过程编程 ---> 算法,逻辑(数学逻辑) 2.函数式编程 ---> 函数,...

  • python面向对象1

    编程思想 编程思想:面向过程编程(穷人思想)、函数式编程、面向对象编程(富豪) 面向过程编程: 算法和逻辑 函数式...

网友评论

      本文标题:从配置文件说到逻辑式编程

      本文链接:https://www.haomeiwen.com/subject/ylrgjttx.html