從配置文件說到邏輯式編程

在游戲制作里黔牵,一般會(huì)存在大量的配置文件,比如說地圖的配置煤惩,NPC的配置嫉嘀,技能的配置,任務(wù)活動(dòng)的配置魄揉,
這些配置經(jīng)常是以 二維表的形式存在剪侮,我們的策劃喜歡強(qiáng)大的表格處理工具excel

這種配置,可以算得上是小型數(shù)據(jù)庫洛退,我曾見過超過10M的配置數(shù)據(jù)瓣俯,

面對(duì)這樣的配置類型红淡,我們有查詢的需求,比如說:

  • 查詢當(dāng)前玩家所在地圖所有可用的跳轉(zhuǎn)點(diǎn)
  • 查詢玩家當(dāng)前地圖上所有可交互的NPC降铸,將它們生成在玩家附近。
  • 滿足當(dāng)前條件的所有可接任務(wù)的集合摇零。
  • 獲取當(dāng)前條件下玩家可學(xué)習(xí)的技能集合推掸。

如果
我們面對(duì)非SQL系統(tǒng)的數(shù)據(jù)源,
我們需要在當(dāng)前的編程環(huán)境下驻仅,方便地表達(dá)查詢谅畅,

c#的解決辦法, linq, (其實(shí)我們可以在很多語言里實(shí)現(xiàn)類似的技術(shù), 不過這是另外一個(gè)題目了,stream API)
c# 對(duì)于任何數(shù)據(jù)類型噪服,如果實(shí)現(xiàn)了IEnumerable 接口毡泻,即視為可應(yīng)用linq查詢的數(shù)據(jù)源。

例: 查詢當(dāng)前玩家所在地圖所有可用的跳轉(zhuǎn)點(diǎn)
(假設(shè)可用跳轉(zhuǎn)點(diǎn)條件為粘优,當(dāng)前地圖仇味,等級(jí)要求,完成任務(wù)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);

對(duì)于不熟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并不能表達(dá)它優(yōu)勢(shì),但畢竟, linq給我們帶來了強(qiáng)大的表達(dá)能力嬉愧,我們初步實(shí)現(xiàn)了贩挣,“告訴計(jì)算機(jī)我們需要什么,而不是怎樣去做"没酣,
在計(jì)算機(jī)科學(xué)里王财,這樣的編程范式叫做”邏輯式編程“,邏輯式編程能做的遠(yuǎn)不止數(shù)據(jù)庫查詢(相對(duì)于大家對(duì)SQL這種特定語言的印象)裕便。

邏輯式編程語言(prolog)里绒净,只有三類表達(dá)式,

  • 事實(shí)(Facts), 它是數(shù)據(jù)集闪金,可以理解為各種數(shù)據(jù)源疯溺,數(shù)據(jù)表,
  • 規(guī)則(Rules): 對(duì)應(yīng)的是 合一規(guī)則哎垦, 可以理解成數(shù)據(jù)源的約束囱嫩,
  • 查詢(Queries): 問題求解

我們都學(xué)過一些基礎(chǔ)邏輯, 一個(gè)有名的例子(三段論):
一切人都會(huì)死,
蘇格拉底是人漏设,
所以蘇格拉底會(huì)死墨闲,

寫成prolog程序

human(Socrates)//事實(shí):
motal(X) :- human(X)//規(guī)則:
?- motal(Socrates)//查詢
>> yes

我們推導(dǎo)出 蘇格拉底 確實(shí)會(huì)死,

好吧郑口,我們強(qiáng)來鸳碧,在c# 表達(dá)如此邏輯

    class Mortal { }
    class Human : Mortal { }//規(guī)則盾鳞,人類是會(huì)死生物
    static void Main(string[] args)
    {
        var Socrates = new Human(); //事實(shí)
        if(Socrates is Mortal) //查詢
        {
            Console.WriteLine("yes!");
        }
    }

(好像我們發(fā)現(xiàn)了點(diǎn)什么...關(guān)于類型和邏輯的關(guān)系是個(gè)艱深的話題 )

以下,我做一個(gè)極為簡單的邏輯系統(tǒng)瞻离,在這系統(tǒng)里完成以上的邏輯式查詢, 為了易于表達(dá)腾仅,測(cè)試?yán)镂覀冎豢紤]一元的情況(arity), 比如說Query1(將只查詢一元屬性的Fact)

//lxf0525@gmail.com
class KnowlegeBase
{
    class Entity
    {
        public string name = "";
    }
    class Atom : Entity { }//原子enity
    class Variable : Entity { }//變量 enity, 將作為合一的名稱

    class Fact //事實(shí)
    {
        public static List<Fact> facts = new List<Fact>();
        //加入一個(gè)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//規(guī)則
    {
        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)
        {
            //基本事實(shí)的查詢
            var facts = Fact.getFact(factTag).Where(f => f.getFirstAtom().name == atomName);
            foreach(var f in facts)
            {
                yield return f.getFirstAtom();
            }
            //規(guī)則查詢,
            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

可以看到套利,這樣小的系統(tǒng)通過查詢的方式推励,確實(shí)證明了 ”蘇格拉底必死"這一命題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末肉迫,一起剝皮案震驚了整個(gè)濱河市验辞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喊衫,老刑警劉巖跌造,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異族购,居然都是意外死亡壳贪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門寝杖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撑碴,“玉大人,你說我怎么就攤上這事朝墩∽硗兀” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵收苏,是天一觀的道長亿卤。 經(jīng)常有香客問我,道長鹿霸,這世上最難降的妖魔是什么排吴? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮懦鼠,結(jié)果婚禮上钻哩,老公的妹妹穿的比我還像新娘。我一直安慰自己肛冶,他們只是感情好街氢,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著睦袖,像睡著了一般珊肃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天伦乔,我揣著相機(jī)與錄音厉亏,去河邊找鬼。 笑死烈和,一個(gè)胖子當(dāng)著我的面吹牛爱只,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播招刹,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼虱颗,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了蔗喂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤高帖,失蹤者是張志新(化名)和其女友劉穎缰儿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體散址,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乖阵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了预麸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞪浸。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖吏祸,靈堂內(nèi)的尸體忽然破棺而出对蒲,到底是詐尸還是另有隱情,我是刑警寧澤贡翘,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布蹈矮,位于F島的核電站,受9級(jí)特大地震影響鸣驱,放射性物質(zhì)發(fā)生泄漏泛鸟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一踊东、第九天 我趴在偏房一處隱蔽的房頂上張望北滥。 院中可真熱鬧,春花似錦闸翅、人聲如沸再芋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祝闻。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間联喘,已是汗流浹背华蜒。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豁遭,地道東北人叭喜。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像蓖谢,于是被迫代替她去往敵國和親捂蕴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容