[↑] @shiningdracon 寫道: 不比魚弱就行。 …
怎麼能這樣說呢,同一種毒藥對一種生物可能無效,對另一種生物就影響很大。
我能想到的一個例子就是人類使用的農藥經過食物使白頭海雕無法生出有硬殼的蛋。
[↑] @shiningdracon 寫道: “足夠隱蔽”,“足夠傷到龍”,“成本足夠低”是一個不可能三角。提升隱蔽性與殺傷力,意味着更高的製造複雜度與資源投入。而一旦規模擴大,不僅更容易暴露,被摧毀時的損失也會成倍增加。投毒需要具備多個前提條件 …
足夠傷到龍代表了一個潛臺詞就是龍很難傷到,這個就要看設定了,我默認爲不管是怎樣的生命形式血肉都是脆弱的。
不管是伏擊,投毒,還是懸賞,我認爲對龍來說不利因素都是需要隨時提心吊膽無時無刻防範。面對有組織性的對手,任何個體的長期生活規律都很容易被摸清後針對,這件事可能並不在短時間內發生,也許幾十年後忽遭冷箭,還是挺糟糕的
@shiningdracon 寫道: 因此毒性必須極強
成年龍或許不會受影響,萬一對幼龍來說是致命的呢
相比於領居的神祕死亡,後代的存活率降低可能更加難以引起注意 ?
@鏡中龍影 寫道: 1.設伏。暴露的弩炮如同高射炮對飛行器,主動性完全在對方,也很容易遭到摧毀,但是問題在於,弩炮可以有很多,而且可以被隱蔽。只有一條龍與一個種族作對,只要一次伏擊中被擊中,可能鬥爭就就此結束了。
“足夠隱蔽”,“足夠傷到龍”,“成本足夠低”是一個不可能三角。提升隱蔽性與殺傷力,意味着更高的製造複雜度與資源投入。而一旦規模擴大,不僅更容易暴露,被摧毀時的損失也會成倍增加。
@鏡中龍影 寫道: 2.投毒。公開組織軍隊並無法對龍個體產生威脅並且會暴露在龍的打擊之下。所以真正有效的進攻手段是暗中偵察並可能是給水源下毒。因爲目標是單一個體,只需要考慮取勝,也不需要擔心規模升級,手段底線,公約下滑。
投毒需要具備多個前提條件:做出強效的且難以被察覺的毒藥,持續而精確的偵查追蹤,在有限的時間內完成隱蔽投放的行動能力。
然而無尾野外行動能力有限,很難及時偵查到龍的臨時棲息地。即使偵查到了,也很難攜帶足量的毒藥穿越複雜地形完成投放而不被察覺。
中世紀的技術條件做不出無色無味且僅對龍致命的劇毒物質。藥效會被水源稀釋,因此毒性必須極強強。但這又會毒死水源中的魚和周圍其它生物,從而暴露痕跡。
由於缺乏偵查能力,行動只能依賴偶然性,提前潛伏在選定的水源,寄希望龍恰好在這段時間前來飲水,然後在不被發現的前提下完成投毒。這是不可能完成的任務。
相反,龍給無尾投毒就簡單多了。
@鏡中龍影 寫道: 3.懸賞。這是魔幻文學作品中最常見的手段;在懸賞之下龍必須無時無刻防備隨時突然出現的刺客,個體的精力是有限的。
懸賞的前提是存在具備執行能力的個體或羣體。然而除非僱傭的是另一條龍,不然任何無尾刺客都需要面對上述所有困難。問題的關鍵在於無尾不具備克服這些困難的能力,而不是沒有意願去做。
@鏡中龍影 寫道: 如果龍威脅了無尾的社會結構,那麼無尾的社會結構將會發生變化,產生一個持續抵抗龍的侵擾的新社會結構,可能不是現在人們熟悉的樣子。我覺得中世紀的封建結構主要依託於城堡對區域的控制,個人契約關係,騎士階級的武力統治力,農業生產力結構四個要素之上,如果外部條件發生改變,也許會有一種更適應游擊戰的社會結構出現,集體變成刻板越共。
大概率會形成散居的村落。在中世紀,對平民最嚴重的壓迫恰恰來自於王權和領主對土地與人身自由的控制。當王權失效,領主倒臺,上層階級的武力被瓦解,無尾平民反而獲得瞭解放。無尾未必有統合成一個單一國家的意願,不需要納稅、不需要面對橫徵暴斂的散居村落對平民而言是更好的生活方式。對龍而言這樣的村落也不是高價值目標。或許能達成這樣一種動態平衡。
2.投毒。
這個我覺得有難度~
我覺得,在戰爭時期,龍是不會天天飛回老窩睡覺的,也不會天天在家附近喝水。
龍的高機動性,最適合打遊擊了,每天都在不同的地方狩獵、喝水、睡覺,
中世紀無尾應該很難在"上百、上千個平方公里"中所有的水源投毒
如果龍真的成爲了中世紀無尾的頭疼目標,那麼無尾會做出針對性的舉措。
比起組織軍隊,真正成爲威脅的大概是以下三種手段
1.設伏。暴露的弩炮如同高射炮對飛行器,主動性完全在對方,也很容易遭到摧毀,但是問題在於,弩炮可以有很多,而且可以被隱蔽。只有一條龍與一個種族作對,只要一次伏擊中被擊中,可能鬥爭就就此結束了。
2.投毒。公開組織軍隊並無法對龍個體產生威脅並且會暴露在龍的打擊之下。所以真正有效的進攻手段是暗中偵察並可能是給水源下毒。因爲目標是單一個體,只需要考慮取勝,也不需要擔心規模升級,手段底線,公約下滑。
3.懸賞。這是魔幻文學作品中最常見的手段;在懸賞之下龍必須無時無刻防備隨時突然出現的刺客,個體的精力是有限的。
其實三個問題中暴露的一條龍對中世紀無尾的最大劣勢還是單一個體。即便是強力個體面對整個社會時也會有很多力不從心之處。
至於五位能不能在龍的持續侵擾下維持國家存在,無尾社會是具備適應性的,而且中世紀社會遠沒有當代社會這樣複雜宏大,如果龍威脅了無尾的社會結構,那麼無尾的社會結構將會發生變化,產生一個持續抵抗龍的侵擾的新社會結構,可能不是現在人們熟悉的樣子。我覺得中世紀的封建結構主要依託於城堡對區域的控制,個人契約關係,騎士階級的武力統治力,農業生產力結構四個要素之上,如果外部條件發生改變,也許會有一種更適應游擊戰的社會結構出現,集體變成刻板越共。
最后修改: 镜中龙影 (2026-03-20 07:27:34)
[↑] @shiningdracon 寫道: 這個營養級對應其實有問題,營養級並不正比於體型,頂級掠食者不等於營養級位置高。比如草原頂級掠食者主食是大型食草動物,營養級算三級。還要考慮一個物種可能喫多種獵物,屬於多個營養級。或許只有海里的大魚喫小 …
是的,但是我苦惱於如何還原不同食物鏈,如果用加權的方式權重就變成了一個新問題。
隨筆之一
在世界的某個褶皺裏,藏着一片沒有名字的花海。
山脈將它與塵世隔絕,雲霧將它與天空相連。沒有人知道那些花從何而來,它們只是開着,以一種近乎執拗的姿態,把每一種顏色都開到極致,把每一縷香氣都散進晨露之中。
龍生於此處。
沒有人見過它的誕生,也許它是從最深的那朵花心裏滾出來的,也許它是某個黎明從霧氣中凝結而成的。它的鱗片是花海所有顏色的總和,在陽光下會折射出不同的光,彷彿它本身就是這片土地的靈魂。
它隨日月起舞——清晨隨朝陽展翅,夜晚隨星辰盤旋。餓了,便俯身飲一口掛在花瓣上的露水,甘甜而透明,帶着泥土和花粉的氣息。它不知道何爲貪求,不知道何爲擁有,只是存在,就像花海存在一樣,渾然天成,別無所求。
有一天,山脈的縫隙裏出現了一個人。
他是個商人,走遍了無數市集,見過無數奇貨,自以爲天下沒有什麼能讓他失語。然而當他跌跌撞撞走入花海,他的腳步停了,他的眼睛停了,他攥着算盤的手也停了。
他從未見過如此顏色的花。整個世界的花市裏,沒有任何一朵能與之相比。他立刻開始盤算——若能帶幾株回去,整個城都會爲之傾倒,那黃金,那絲綢,那權貴們爭相獻上的一切……
他俯下身,伸出手,抓住了一朵他從未見過的、藍得近乎透明的花。
他只是想帶走它。
就在手指觸碰到花莖的瞬間,那朵花顫抖了一下,然後開始枯萎。不是尋常的枯萎——花瓣沒有皺縮,而是如同被黑暗溶解,轉眼變成一攤污泥,腥臭撲鼻。更可怕的是,那腐爛並未就此止步。它順着泥土,順着根系,順着風,悄無聲息地蔓延開去。
那人嚇得往後跌去,卻已逃不開他所引發的事。花海開始腐爛,一朵接一朵,一片接一片。
龍從高空看見了這一切。
龍俯衝下來。
它想用雙翼扇走腐爛,卻發現翼風只是讓腐敗的孢子飄得更遠;它想用爪子刨出健康的土壤,卻發現觸碰之處更快沉陷;它想用龍息焚燒腐爛,卻不敢——它不知道火焰是否也會傷害尚未腐爛的花。
它只能盤旋,眼睜睜地看着。
那片藍色的花消失了。那片金色的消失了。那片它每個清晨都要俯身親吻的白色小花,也在一個下午徹底化爲污泥。
整個花海像一幅畫被人從中心開始用水溶解,顏色一點一點地消失,直到只剩下黑色與腥臭。
而那個商人,早已沿着山縫倉皇而逃。
龍第一次感受到了一種陌生的東西,它灼燙,它膨脹,它無處安放。它比飢餓更難忍受,比寒冷更深入骨髓。
龍穿越了山脈。
它找到了那個商人,在他剛剛回到的城市邊緣,在他正向人們炫耀那雙沾滿污泥的鞋時,龍的影子落下來,遮住了整條街道的陽光。
商人死了。
然後是目睹這一切的人,然後是聽說這件事而湧來的人,然後是那些手持武器試圖討回公道的人。每殺一人,便有十人的仇恨被點燃;每平息一場騷亂,便有新的仇恨在更遠處生根。
龍不理解這是爲什麼。它只是要找到那個毀掉花海的人,它已經找到了,爲什麼仇恨不肯停止?
但仇恨有自己的生長邏輯,它不需要理由,只需要一個方向,然後它會自己找到燃料。
於是龍開始不再區分,它只是飛翔,只是焚燒,只是讓那些尖叫和刀劍停下來。它告訴自己,這是最後一次,下一次一定可以停——
但從未有過最後一次。
不知道過了多少年,龍回過神來,發現世界已經寂靜得像一具空殼。城市變成廢墟,廢墟長出荒草,荒草上停着沉默的烏鴉。沒有人聲,沒有鐘聲,沒有市集上此起彼伏的叫賣。
龍降落在一片廢土上。
它的鱗片不再折射光彩,它的翼膀沉得抬不起來,它的眼睛裏燃燒了太久的火,如今只剩灰燼。
就在龍以爲自己將在這片寂靜中消散時,廢土上出現了一個影子。
是個孩子。
龍沒有力氣抬起頭,只是側過眼睛看着那小小的身影一步一步走近。它想,也許這是最後一個人,也許他懷裏藏着什麼武器,也許這是仇恨鏈條的最末一環。龍甚至沒有力氣感到恐懼,更沒有力氣再次舉起爪子。
就讓它來吧。
孩子走到龍的面前,停下來,看了很久。然後,他把一隻小小的手,輕輕放在了龍的頭上。
就這樣。
沒有語言,沒有眼淚,只是一隻手,帶着體溫,帶着一種龍已經很久沒有感受到的——溫柔。
龍感受到了那隻手心裏的東西。不是恐懼,不是仇恨,不是哀求。是同情,是原諒,是一種比任何道理都更深的、無需理由的悲憫。
那孩子也許失去了父母,也許見過龍的爪影在城市上空掠過,也許他所知道的一切災難都與這頭龍有關。但他的心裏盛着的,仍然是這個。
龍的眼淚流了下來。它的形體開始變得透明,像清晨的霧,像花瓣上的露水。它消散了,一點一點,散進了那片寂靜的空氣裏。
龍從夢中醒來。
它睜開眼睛,看見的仍然是那片腐爛。那片被最初一雙手觸碰而敗壞的花海,黑色的,腥臭的,無聲無息地爛在那裏,像一個無解的傷口。
憤怒沒有來。
龍在原地靜了很久,久到朝陽從山脈那邊升起來,把第一縷光鋪在腐爛的土地上。它想起了夢裏的那隻手,想起了孩子心裏盛着的那些——
然後它深吸一口氣,低下頭,將龍息輕輕覆在腐爛之上。
不是憤怒的火,不是毀滅的焰。是一口吐息,柔和的,帶着清晨的溫度,帶着某種久違的溫柔。
大地燃燒了。
火焰是透明的,像琥珀,像蜂蜜,像陽光穿過薄薄的花瓣時透出來的顏色。腐爛的污泥在其中融化,化爲灰燼,灰燼落下,落進更深處的土壤。
然後,沉默。
然後,是一聲極輕微的聲響,像什麼東西正在破殼。
腐爛之下,有什麼東西在往上拱。那是根,是芽,是一種固執的、不需要任何理由的生長。第一朵花從灰燼中探出頭來,帶着被燃燒過的溫熱,帶着某種新的顏色——那是舊日的藍和金和白,也是從未有過的顏色。
然後是第二朵,第三朵,一片,一片又一片。
花海歸來了。
不是原來那個,也不是全然陌生的。它帶着被傷害過的記憶,也帶着被治癒過的痕跡,在清晨的光裏開得比任何時候都更茂盛,也更安靜。
龍在花海上方盤旋,隨日月而起舞,飲朝露而爲生。
-----end-----
靈感來源今天做的一個夢,那個夢挺長的,也挺零碎的,裏面也沒有龍元素,加上細節我也有些忘了,就不復述了。大概是“腐爛之下也可以潛藏着花海”以及“仇恨也沒有那麼重要”。因爲是夢改編,所以邏輯可能不嚴謹,意思到了就行。不打算解讀這篇腦洞的主旨,我覺得不同的讀者會有不同的理解,但只要能觸動心靈它就可以是正確的。
另外這篇依舊是我寫大綱,ai生成文章,使用ai爲Claude Sonnet 4.6(真的沒時間寫大段文字啊......)。
其它靈感來源:
【小說】瓊瑛龍之夢
b站某遊戲視頻,up和視頻名想不起來了,但裏面的“仇恨......其實也沒有那麼重要”這句我記憶猶新,什麼時候找到一定補鏈接。
有 1 位朋友喜欢这篇文章:NancalaStarry
防止變成龍騎士的坐騎
我稍微發散一下
經常有吐槽說龍噴火不符合生物原理,消耗的能量大於捕獵獲得的獵物的能量
但是自然界有一種情況是會讓動物毫不在乎浪費能量的…那就是…求偶!
所以綜上所述龍噴火其實是爲了求偶!龍噴火的圖都相當於人類搔首弄姿引誘異性的澀圖!
如果是海龍或者陸地龍倒是可以理解
但是如果是東龍或者有翅膀的四足西方龍的話
現實裏卻恐高
會不會說出去很丟龍臉
@羽落 寫道: | 第一營養級| 植物、藻類 | 生產者 || 第二營養級| 草食動物(斑馬、兔子) | 初級消費者 || 第三營養級| 小型肉食動物(狐狸、蛇) | 次級消費者 || 第四營養級 | 中型肉食動物(狼、豹) | 三級消費者 || 第五營養級 | 頂級掠食者(虎、大白鯊、北極熊) | 四級消費者 |
這個營養級對應其實有問題,營養級並不正比於體型,頂級掠食者不等於營養級位置高。比如草原頂級掠食者主食是大型食草動物,營養級算三級。還要考慮一個物種可能喫多種獵物,屬於多個營養級。或許只有海里的大魚喫小魚相對確定一些。
背景:中世紀的實際生產力,無魔法,龍具備高智商與理性決策能力。
龍的優勢:
制空
高攻高防高機動性
優秀的野外生存能力
中世紀的無尾幾乎沒有有效的防空手段,這使得龍成爲獨立高效的戰略襲擾者。
無尾的優勢:
組織動員能力
穩定的資源生產
持續將資源轉化爲武器的能力
技術的發展與傳承
歷史上的無尾確實憑藉這些優勢在漫長的時間裏逐步主宰地球。但是,在面對龍的情況下,這些優勢會系統性失效。
農業國家的核心是耕地產出,即是食物來源也是稅基。農業的特徵單位面積產出高,但週期長,易被破壞。龍不需要直接進攻城市,只需要週期性的摧毀農田、糧倉、牲畜、水利設施,即可讓農田無法正常生產。還可以污染飲用水,直接削減無尾聚居地的生存資源。如此便可引發食物短缺,饑荒,人口流失。無尾無法在失去農田的情況下維持城市。更進一步可以打擊交通運輸、地方行政機構,造成稅收、行政、軍事失效,屆時地方將脫離中央掌控。如此,就會引發連鎖反應,饑荒、叛亂、地方割據、王權失效。
相當於遊戲剛開局就飛龍騎臉屠農,兵力和科技都發展不起來。
無尾幾乎無法組織反擊。國家的組織能力依賴穩定糧食供應,發達的交通網絡,行政與軍事的集中。而這些恰恰是龍的優先打擊目標。一旦國家嘗試集中資源(例如建設防禦工事,調集軍隊,後勤運輸),這種集中本身就成爲高價值目標。結果是越試圖組織反擊,越暴露於打擊之下。
有人提到無尾可以建造弩炮等足夠對龍造成傷害的遠程武器,但問題在於大型工程建造週期長,需要大規模的人力物力調動。而龍具備空中偵查能力,高機動性的打擊能力,可以自由選擇攻擊時間,攻擊低點,攻擊目標。這意味着主動權完全在龍的控制下。無尾的高殺傷力武器無法保證部署在正確的地點,無法確保龍進入射程,也很難在暴露後保全。
無尾的防禦成本也是無底洞。要想保證安全就必須長期維持高強度的防禦,儘可能廣泛的覆蓋核心生產區域,承擔持續的維護成本。而龍的進攻可以自由調整頻率,不可預測。無尾的資源越集中,龍的攻擊收益越高。
相當於遊戲中以高機動性的英雄單位拖住對方整支高維護費用的軍隊,拖垮對手。
在現實的中世紀框架下,一條理性、高機動、具備空中優勢的龍,本質上是一個無尾無法強制交戰、無法承受長期對抗成本、也無法系統性防禦的戰略級存在。
所以問題似乎是:中世紀的無尾究竟能不能在龍的持續戰略襲擾下維持國家存在。
最后修改: shiningdracon (2026-04-14 17:49:49)
龍族的邊境防禦部隊由志願加入的龍組成.
但是怎樣的報酬才足夠呢? 人類和龍族交界的大片不穩定地區作爲領地的獎勵嗎?
將灰色地帶收歸龍有,這樣倒也一定程度上解決了龍族內部數量增長產生的領地緊張問題
真的有龍會去幹這種風險極高的幹架工作嗎
[正在聽Art of Rally的Patience and Determination]
參考貪喫蛇遊戲


<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>東方龍貪吃蛇</title>
<style>
:root {
--bg1: #0f172a;
--bg2: #1e293b;
--panel: rgba(15, 23, 42, 0.82);
--gold: #fbbf24;
--gold2: #fde68a;
--jade: #34d399;
--danger: #fb7185;
--text: #e2e8f0;
--muted: #94a3b8;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: "Segoe UI", "Noto Sans TC", sans-serif;
color: var(--text);
background:
radial-gradient(circle at top, rgba(251, 191, 36, 0.18), transparent 28%),
radial-gradient(circle at bottom right, rgba(52, 211, 153, 0.14), transparent 26%),
linear-gradient(180deg, var(--bg2), var(--bg1));
padding: 20px;
}
.wrap {
width: min(92vw, 760px);
background: var(--panel);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 24px;
box-shadow: 0 24px 60px rgba(0,0,0,0.35);
backdrop-filter: blur(8px);
padding: 20px;
}
.topbar {
display: flex;
gap: 12px;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.title h1 {
margin: 0;
font-size: clamp(26px, 4vw, 36px);
color: var(--gold2);
letter-spacing: 1px;
}
.title p {
margin: 6px 0 0;
color: var(--muted);
font-size: 14px;
}
.stats {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.chip {
background: rgba(255,255,255,0.06);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 999px;
padding: 10px 14px;
font-weight: 700;
font-size: 14px;
}
.chip span {
color: var(--gold2);
margin-left: 6px;
}
.game-box {
position: relative;
display: grid;
place-items: center;
}
canvas {
width: min(92vw, 640px);
height: min(92vw, 640px);
max-width: 640px;
max-height: 640px;
aspect-ratio: 1 / 1;
background:
linear-gradient(180deg, rgba(15, 23, 42, 0.94), rgba(2, 6, 23, 0.96));
border-radius: 20px;
border: 2px solid rgba(251, 191, 36, 0.25);
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.35);
}
.overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.panel {
pointer-events: auto;
text-align: center;
width: min(84%, 420px);
padding: 24px 22px;
border-radius: 22px;
background: rgba(15, 23, 42, 0.9);
border: 1px solid rgba(255,255,255,0.08);
box-shadow: 0 16px 40px rgba(0,0,0,0.4);
}
.panel h2 {
margin: 0 0 10px;
color: var(--gold2);
font-size: 28px;
}
.panel p {
margin: 8px 0;
color: var(--text);
line-height: 1.6;
}
.actions {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
margin-top: 16px;
}
button {
border: none;
border-radius: 999px;
padding: 12px 20px;
font-size: 15px;
font-weight: 800;
cursor: pointer;
color: #0f172a;
background: linear-gradient(135deg, var(--gold2), var(--gold));
box-shadow: 0 10px 24px rgba(251, 191, 36, 0.22);
transition: transform 0.15s ease, filter 0.15s ease;
}
button:hover {
transform: translateY(-1px);
filter: brightness(1.03);
}
.ghost {
background: rgba(255,255,255,0.08);
color: var(--text);
box-shadow: none;
border: 1px solid rgba(255,255,255,0.08);
}
.footer {
margin-top: 14px;
color: var(--muted);
font-size: 13px;
text-align: center;
line-height: 1.6;
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div class="wrap">
<div class="topbar">
<div class="title">
<h1>東方龍貪吃蛇</h1>
<p>以方向鍵或 WASD 操控龍身,吃靈珠變長,別撞到自己或牆壁。</p>
</div>
<div class="stats">
<div class="chip">分數 <span id="score">0</span></div>
<div class="chip">最高分 <span id="best">0</span></div>
<div class="chip">速度 <span id="speed">1</span></div>
</div>
</div>
<div class="game-box">
<canvas id="game" width="640" height="640"></canvas>
<div class="overlay" id="startOverlay">
<div class="panel">
<h2>開始遊戲</h2>
<p>你將操控一條東方龍在夜空中穿梭,吞食靈珠成長。</p>
<p>操作:方向鍵 / WASD</p>
<div class="actions">
<button id="startBtn">開始</button>
</div>
</div>
</div>
<div class="overlay hidden" id="gameOverOverlay">
<div class="panel">
<h2>遊戲結束</h2>
<p>本次分數:<strong id="finalScore">0</strong></p>
<p id="finalMessage">龍撞上障礙了,再試一次。</p>
<div class="actions">
<button id="restartBtn">重新開始</button>
<button class="ghost" id="closeBtn">查看畫面</button>
</div>
</div>
</div>
</div>
<div class="footer">
小提醒:每吃 4 顆靈珠,速度會提升一級。<br />
直接把這個 HTML 存成檔案後,用瀏覽器打開即可遊玩。
</div>
</div>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreEl = document.getElementById('score');
const bestEl = document.getElementById('best');
const speedEl = document.getElementById('speed');
const startOverlay = document.getElementById('startOverlay');
const gameOverOverlay = document.getElementById('gameOverOverlay');
const finalScoreEl = document.getElementById('finalScore');
const finalMessageEl = document.getElementById('finalMessage');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const closeBtn = document.getElementById('closeBtn');
const gridCount = 20;
const cell = canvas.width / gridCount;
let dragon = [];
let direction = { x: 1, y: 0 };
let nextDirection = { x: 1, y: 0 };
let orb = { x: 10, y: 10 };
let score = 0;
let best = Number(localStorage.getItem('dragonSnakeBest') || 0);
let level = 1;
let gameOver = false;
let started = false;
let frameTimer = 0;
let stepMs = 160;
let lastTime = 0;
bestEl.textContent = best;
function resetGame() {
dragon = [
{ x: 5, y: 10 },
{ x: 4, y: 10 },
{ x: 3, y: 10 },
{ x: 2, y: 10 }
];
direction = { x: 1, y: 0 };
nextDirection = { x: 1, y: 0 };
score = 0;
level = 1;
stepMs = 160;
gameOver = false;
scoreEl.textContent = score;
speedEl.textContent = level;
spawnOrb();
draw();
}
function spawnOrb() {
let valid = false;
while (!valid) {
orb = {
x: Math.floor(Math.random() * gridCount),
y: Math.floor(Math.random() * gridCount)
};
valid = !dragon.some(part => part.x === orb.x && part.y === orb.y);
}
}
function startGame() {
resetGame();
started = true;
startOverlay.classList.add('hidden');
gameOverOverlay.classList.add('hidden');
lastTime = performance.now();
frameTimer = 0;
requestAnimationFrame(loop);
}
function endGame(reason) {
gameOver = true;
started = false;
if (score > best) {
best = score;
localStorage.setItem('dragonSnakeBest', String(best));
bestEl.textContent = best;
}
finalScoreEl.textContent = score;
finalMessageEl.textContent = reason;
gameOverOverlay.classList.remove('hidden');
}
function update() {
direction = nextDirection;
const head = { ...dragon[0] };
const newHead = {
x: head.x + direction.x,
y: head.y + direction.y
};
if (
newHead.x < 0 || newHead.x >= gridCount ||
newHead.y < 0 || newHead.y >= gridCount
) {
endGame('東方龍撞上邊界了。');
return;
}
if (dragon.some(part => part.x === newHead.x && part.y === newHead.y)) {
endGame('東方龍撞到自己的龍身了。');
return;
}
dragon.unshift(newHead);
if (newHead.x === orb.x && newHead.y === orb.y) {
score += 10;
scoreEl.textContent = score;
if ((score / 10) % 4 === 0) {
level += 1;
speedEl.textContent = level;
stepMs = Math.max(70, stepMs - 12);
}
spawnOrb();
} else {
dragon.pop();
}
}
function drawBackground() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const bg = ctx.createLinearGradient(0, 0, 0, canvas.height);
bg.addColorStop(0, '#0f172a');
bg.addColorStop(1, '#020617');
ctx.fillStyle = bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < gridCount; y++) {
for (let x = 0; x < gridCount; x++) {
ctx.fillStyle = (x + y) % 2 === 0 ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.012)';
ctx.fillRect(x * cell, y * cell, cell, cell);
}
}
for (let i = 0; i < 24; i++) {
const sx = (i * 97) % canvas.width;
const sy = (i * 53) % canvas.height;
ctx.fillStyle = 'rgba(255,255,255,0.18)';
ctx.beginPath();
ctx.arc(sx, sy, 1.4, 0, Math.PI * 2);
ctx.fill();
}
}
function drawOrb() {
const cx = orb.x * cell + cell / 2;
const cy = orb.y * cell + cell / 2;
const glow = ctx.createRadialGradient(cx, cy, 2, cx, cy, cell * 0.45);
glow.addColorStop(0, 'rgba(253, 224, 71, 0.95)');
glow.addColorStop(1, 'rgba(253, 224, 71, 0)');
ctx.fillStyle = glow;
ctx.beginPath();
ctx.arc(cx, cy, cell * 0.48, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#fde68a';
ctx.beginPath();
ctx.arc(cx, cy, cell * 0.22, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#fbbf24';
ctx.lineWidth = 2;
ctx.stroke();
}
function drawDragon() {
dragon.forEach((part, index) => {
const px = part.x * cell;
const py = part.y * cell;
const isHead = index === 0;
if (isHead) {
const headGrad = ctx.createLinearGradient(px, py, px + cell, py + cell);
headGrad.addColorStop(0, '#fde68a');
headGrad.addColorStop(1, '#f59e0b');
ctx.fillStyle = headGrad;
roundRect(px + 3, py + 3, cell - 6, cell - 6, 10, true, false);
ctx.fillStyle = '#7c2d12';
const eyeOffsetX = direction.x === -1 ? 9 : direction.x === 1 ? cell - 13 : 10;
const eye2OffsetX = direction.x === -1 ? 9 : direction.x === 1 ? cell - 13 : cell - 14;
const eyeY = direction.y === 1 ? cell - 13 : 11;
ctx.beginPath();
ctx.arc(px + eyeOffsetX, py + eyeY, 2.3, 0, Math.PI * 2);
ctx.arc(px + eye2OffsetX, py + eyeY, 2.3, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#ef4444';
ctx.lineWidth = 2;
ctx.beginPath();
if (direction.x === 1) {
ctx.moveTo(px + cell - 4, py + cell / 2);
ctx.lineTo(px + cell + 6, py + cell / 2 - 3);
ctx.lineTo(px + cell + 9, py + cell / 2);
ctx.lineTo(px + cell + 6, py + cell / 2 + 3);
} else if (direction.x === -1) {
ctx.moveTo(px + 4, py + cell / 2);
ctx.lineTo(px - 6, py + cell / 2 - 3);
ctx.lineTo(px - 9, py + cell / 2);
ctx.lineTo(px - 6, py + cell / 2 + 3);
} else if (direction.y === -1) {
ctx.moveTo(px + cell / 2, py + 4);
ctx.lineTo(px + cell / 2 - 3, py - 6);
ctx.lineTo(px + cell / 2, py - 9);
ctx.lineTo(px + cell / 2 + 3, py - 6);
} else {
ctx.moveTo(px + cell / 2, py + cell - 4);
ctx.lineTo(px + cell / 2 - 3, py + cell + 6);
ctx.lineTo(px + cell / 2, py + cell + 9);
ctx.lineTo(px + cell / 2 + 3, py + cell + 6);
}
ctx.stroke();
ctx.strokeStyle = '#fef3c7';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(px + 8, py + 7);
ctx.lineTo(px + 11, py + 2);
ctx.moveTo(px + cell - 8, py + 7);
ctx.lineTo(px + cell - 11, py + 2);
ctx.stroke();
} else {
const t = index / dragon.length;
const bodyGrad = ctx.createLinearGradient(px, py, px + cell, py + cell);
bodyGrad.addColorStop(0, `rgba(52, 211, 153, ${0.95 - t * 0.25})`);
bodyGrad.addColorStop(1, `rgba(16, 185, 129, ${0.9 - t * 0.25})`);
ctx.fillStyle = bodyGrad;
roundRect(px + 4, py + 4, cell - 8, cell - 8, 9, true, false);
ctx.strokeStyle = 'rgba(253, 230, 138, 0.55)';
ctx.lineWidth = 1.6;
ctx.beginPath();
ctx.arc(px + cell / 2, py + cell / 2, cell * 0.18, Math.PI * 0.2, Math.PI * 1.8);
ctx.stroke();
}
});
}
function roundRect(x, y, w, h, r, fill, stroke) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.arcTo(x + w, y, x + w, y + h, r);
ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r);
ctx.arcTo(x, y, x + w, y, r);
ctx.closePath();
if (fill) ctx.fill();
if (stroke) ctx.stroke();
}
function draw() {
drawBackground();
drawOrb();
drawDragon();
}
function loop(timestamp) {
if (!started) return;
const delta = timestamp - lastTime;
lastTime = timestamp;
frameTimer += delta;
while (frameTimer >= stepMs && !gameOver) {
update();
frameTimer -= stepMs;
}
draw();
if (!gameOver) {
requestAnimationFrame(loop);
}
}
function setDirection(x, y) {
if (!started && startOverlay.classList.contains('hidden') === false) return;
if (direction.x === -x && direction.y === -y) return;
nextDirection = { x, y };
}
document.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
if (["arrowup", "arrowdown", "arrowleft", "arrowright", "w", "a", "s", "d", " "].includes(key)) {
e.preventDefault();
}
if (!started && (key === ' ' || key === 'enter')) {
startGame();
return;
}
if (key === 'arrowup' || key === 'w') setDirection(0, -1);
else if (key === 'arrowdown' || key === 's') setDirection(0, 1);
else if (key === 'arrowleft' || key === 'a') setDirection(-1, 0);
else if (key === 'arrowright' || key === 'd') setDirection(1, 0);
});
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
closeBtn.addEventListener('click', () => gameOverOverlay.classList.add('hidden'));
resetGame();
</script>
</body>
</html>
2026.3.14
確定編程語言爲Python,使用個體建模(Agent-Based Modeling, ABM),語言爲Python,框架爲Mesa。
ABM 由Agent、環境、規則、交互拓撲四大核心模塊構成,即:
1. Agent(主體)
定義:系統的基本決策單元,可對應人、企業、動物、車輛、細胞、機器人等。
核心屬性:狀態(如健康 / 感染、有錢 / 沒錢)、特徵(年齡、偏好、位置)、資源、記憶。
行爲規則:感知→決策→行動的邏輯(如 “行人靠右走”“投資者追漲殺跌”“病毒接觸傳播”),通常是有限理性的啓發式規則,而非完美最優決策。
2. 環境(Environment)
爲 Agent 提供活動空間與約束(如地理空間、網絡拓撲、資源池、物理規則)。
可靜態(如固定網格)或動態(如資源消耗、環境變化)。
3. 交互規則(Interaction Rules)
Agent 之間、Agent 與環境的互動機制(如碰撞避免、信息傳遞、交易、感染、競爭 / 合作)。
決定系統演化的核心動力。
4. 交互拓撲(Topology)
Agent 的連接 / 鄰居關係(如網格、隨機網絡、小世界網絡、無標度網絡),影響信息 / 影響的傳播範圍與速度。
第一步我計劃從地圖(Environment)開始,採用網格形式,網格大小1km*1km,總地圖大小300*300,溫度帶採用固定的形式,第一版先不考慮季節變化、天氣變化和生物羣系演替(但是有恢復和破壞)。使用雙層噪聲邏輯,柏林噪聲(Perlin Noise)生成高度,並結合緯度溫度帶與溼度噪聲來確定生物羣系,生物羣系用Whittaker 生物羣系分類模型。藉助gemini的deep search我收集到以下淨初級生產力(NPP)表格(https://gemini.google.com/share/0bcb3465a31d):
| 生物羣系 | 平均NPP (g/m²/yr) | 全球總面積 (10⁶ km²) | 全球總產出 (10⁹ t/yr) | 關鍵限制養分 |
| :---- | :-------------- | :-------------- | :--------------- | :------- |
| 熱帶雨林 | 2200 | 17.0 | 37.4 | 磷、鈉 |
| 熱帶季節林 | 1600 | 7.5 | 12.0 | 水分季節性 |
| 溫帶落葉林 | 1200 | 7.0 | 8.4 | 氮 |
| 溫帶針葉林 | 1300 | 5.0 | 6.5 | 氮、熱量 |
| 溫帶草原 | 600 | 9.0 | 5.4 | 氮、水分 |
| 北方針葉林 | 800 | 12.0 | 9.6 | 氮、熱量 |
| 稀樹草原 | 900 | 15.0 | 13.5 | 氮、水分脈衝 |
| 地中海灌叢 | 700 | 8.5 | 6.0 | 水分夏季缺失 |
| 苔原 | 140 | 8.0 | 1.1 | 氮、熱量 |
| 荒漠 | 40 - 90 | 18.0 - 42.0 | 1.6 - 1.7 | 水分、氮 |
| 極地冰原 | ~0 (宏觀) | 38.0 | ~0 | 極度低溫、液態水 |
|水 | 不計入 |--------------- | ---------------- | -------- |
現代地球的頂掠通常位於第四或第五營養級,具體取決於生態系統的複雜程度。
常規營養級劃分:
| 營養級 | 典型生物 | 角色 |
| --------- | -------------------- | ----- |
| 第一營養級| 植物、藻類 | 生產者 |
| 第二營養級| 草食動物(斑馬、兔子) | 初級消費者 |
| 第三營養級| 小型肉食動物(狐狸、蛇) | 次級消費者 |
| 第四營養級 | 中型肉食動物(狼、豹) | 三級消費者 |
| 第五營養級 | 頂級掠食者(虎、大白鯊、北極熊) | 四級消費者 |
根據林德曼效率(10%法則),能量每傳遞一個營養級損失約90%。森林的 2200 NPP 並不等於 2200 的“食物”。很大一部分是不可食用的木材,引入可食用係數(Edibility Factor, EF)。
因此得到公式:
再根據
Barbour, Michael G所著的Terrestrial plant ecology:
About 10% of NPP is consumed by biophage herbivores for typical terrestrial
vegetation. The percentage varies with vegetation: 2-3% for desert scrub or arc¬
tic/alpine tundra, 4-7% for forest, 10-15% for temperate grasslands with minimal
grazing, and 30-60% for African grasslands or grasslands managed for domesti¬
cated animals. The percentage can also fluctuate from year to year. Episodic out¬
breaks of herbivores such as tent caterpillars, locusts, budworms, rabbits, voles,
lemmings, or pathogenic microbes may raise the consumption to 50-100% of NPP.
(對於典型的陸地植被,大約10%的淨初級生產力 (NPP) 被食草性生物消耗。這個百分比因植被類型而異:沙漠灌木或北極/高山苔原爲2-3%,森林爲4-7%,放牧最少的溫帶草原爲10-15%,非洲草原或爲家畜管理的草原爲30-60%。這一百分比也可能隨年份波動。像帳篷毛毛蟲、蝗蟲、芽蟲、兔子、田鼠、旅鼠或致病微生物等食草生物的偶發性爆發,可能將消耗率提高到NPP的50-100%。)
(https://z-library.sk/book/Yv34Qpp8Om/terrestrial-plant-ecology.html)
其中有用數據有:
一級營養級到二級營養級的傳遞總效率(含EF計算後的結果):
極地冰原/苔原/荒漠:2-3%
森林:4-7%
溫帶草原:10-15%
家畜放牧的草原:30-60%
可以由此倒推EF。
結合生物羣系複雜度可以得到以下表格:
| 生物羣系 | NPP | EF | 有效可食NPP | 營養級 | 傳遞係數 | **頂掠可得能量** |
| --------- | ------- | ---- | ------- | --------- | ------ | ---------- |
| **熱帶雨林** | 2200 | 0.20 | 440 | 第五級 | 0.0002 | **0.088** |
| **熱帶季節林** | 1600 | 0.25 | 400 | 第五級 | 0.0002 | **0.080** |
| **溫帶落葉林** | 1200 | 0.30 | 360 | 第四級 | 0.002 | **0.720** |
| **溫帶針葉林** | 1300 | 0.18 | 234 | 第四級 | 0.002 | **0.468** |
| **稀樹草原** | 900 | 0.60 | 540 | 第四級 | 0.0015 | **0.81** |
| **溫帶草原** | 600 | 0.80 | 480 | 第四級 | 0.0015 | **0.72** |
| **北方針葉林** | 800 | 0.12 | 96 | 第四級 | 0.001 | **0.096** |
| **地中海灌叢** | 700 | 0.22 | 154 | 第四級 | 0.001 | **0.154** |
| **苔原** | 140 | 0.25 | 70 | 第三級 | 0.01 | **0.35** |
| **荒漠** | 65 | 0.25 | 26 | 第三級 | 0.01 | **0.163** |
| **極地冰原** | ~0 | ~0 | ~0 | — | — | **~0** |
| **水** | ~0 | ~0 | ~0 | — | — | **~0** |
但是實際現象是雨林中頂掠領地大小比草原的小十倍左右,另外具有飛行能力的龍在草原上的捕食效率可能遠大於雨林中的,因此以上模型還不完善,同時雨林的營養級和EF也還有討論餘地,待後續完善......