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

在游戏制作里,一般会存在大量的配置文件,比如说地图的配置,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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容