AWS Enclave & KMS

AWS Enclave is a type of VM whose host machine cannot access its memory. The host machine can only use certain APIs that’s provided by an enclave via vsock. This secure feature is provided by AWS Nitro System, therefore an enclave has to run on a Nitro-based EC2 instance. Also, an enclave image is built upon a docker image with aws nitro-cli.

Other than secure from host machine, a running enclave instance can generate attestation document that can somehow (I’m planning to dig deep in later posts) be verified by AWS services like its key management service (AWS KMS), so an enclave can use kms:decrypt without exposing any encryption/decryption details to host machine.

Think about the following scenario: you have a web service and wants to distribute a client library, and you need to make sure your web service only communicate to the client you distribute to. Or, only your client can understand the response.

If you haven’t seen the difficulties in this problem, hint: how can you prevent the host intercepting your http (even https) to get the messages and poke the memory of your client to get the secret, then make a fake (probably malicious) client to cheat?

AWS Enclave provides a solution like this:

  • use a secret to encrypt all messages
  • store the secret inside your client, so your client can decrypt the messages
  • distribute your feature inside a enclave image, so your user cannot access the secret from memory
  • decrypt with a service that can check the validity of enclave attestation document
    1. you may decrypt with kms:decrypt and setup key policy to automate attestation
    2. you may implement your own attestation and let the client load the key on start

Next part is my following the code to find corresponding APIs in the sample enclave-kms project. May be too detailed and not so interesting. If you are ready click ‘Page 2’ (WordPress never failed to surprise me).

再碎碎念一篇,然后发个技术博好了

原来的标题叫《我似乎解决了那个值得解决的问题,但于是更加困惑了》

说说彩六,1943.9小时。围绕着它度过了过山车一般的一个月,快乐也忙乱的一个月。认识了一些人,失去了一些人;经历了从惊喜到失望,从“啊原来这就是”到“哦其实不可能”——然后我还是在自己原来的地方。看起来风浪很大,不过终究还是无聊的生命中无聊的一个月。有一段发觉自己原来从未真正体验过彩虹六号,而今只觉得我可能永远也不会真正体验彩虹六号。所以就这样吧,最后一次谈这个话题。

关于ClubHouse:无聊至极。

算了不写了,先睡觉了。真是到了却道天凉好个秋的时候了。

如果这个主题反复出现,说明它是一个值得解决的问题

我听了一部广播剧,然后被其制作之精良给感动到了。

这篇博客是完全的碎碎念,原标题:我的人生有1800小时的彩虹六号。

截至此时,steam记载我的彩虹六号围攻游戏时间是1799.4小时,其中包含一些躺下睡着了忘记关游戏的挂机时间。不过毕竟数字在那里,我们就这样算。

问题在于,坦白说,我好像没有在哪一件事情上投入过这么长的时间。所以常常会想,如果这些时间被用来做A,做B,做C,此刻我的生活会不会有什么不一样。应该会的。好像太久没有专注在什么当中,也太久没有感受到所谓“成就感”了——以至于写一篇博客都很难。愚以为,除非是技术主题的,想写出一篇不自怨自艾的博客多多少少需要点mansplaining的自信。

最近在读…此时我的眼睛在书桌周围疯狂地扫视每本书的书脊,但无一想得起内容来。大概最近什么都没有在读。二月初有一个难得的面向全公司做去年项目的汇报机会,但是苦思冥想(大概也没有很认真地)过后还是只能记得零散的片段,暂且没法连成一个什么整体来讲述,酷似这篇博客。这种无法深入思考的感受自从居家隔离开始便困扰着我—没有责怪居家隔离的意思—因为我是擅长在人前表演出积极的样子的,然后依着fake it till you make it的定律,往往在实际上也能小有成就,至少做得还不赖。但离开了众人后失去了对比,就很难明白自己都在做些什么。

广播剧的名字叫《顾小姐和曲小姐》,作者晚之。没有什么应该特意在此推荐的理由,也没有任何传统意义上的“文学价值”。All Credits Here. 我很好奇这些从五湖四海聚在一起的配音演员和美工们是出于什么想法在做,或许会盈利吗?我不知道。但是确实让我回想起小时候和论坛的坛友们一起考虑做NJ(Net DJ,现在不知道还在不在用这个词了,可能统一叫主播了)的时候,几个女生录节目,我负责在论坛上加MP3播放器。那时候Flash还是主流的媒体工具,之后Adobe用十年的时间来缓慢的下葬它,最后被大连高铁胜利攻关。说这个也不单是怀旧,大抵是我最近看了韩寒新开的直播空间,满满的少年感,心里不住地羡慕,也想找一下少年的感觉的原因。

和付桐一样地,老板最近也常和我提起First Principle(我还是分不太清Principle/Principal)的事。互联网行业总是被形容成瞬息万变的风口,但就First Principle而言似乎还真的没有什么大变化。黑盒白盒,输入输出。另外,郭德纲老师说过,计算机也是两件事——一个是程序,一个是存储。他说出来的时候我还小诧异了一下,不知道是拾谁人的牙慧,但对相声演员来说这个总结足够惊艳。之后话题就回到腌咸菜了。

我可以理解国内义务教育教师口中流行的那句“不要对了就沾沾自喜,要记住自己哪里错了”,但我真心认为很多时候“知道自己为什么是对的”更难。照着标准答案分辨错误是显然的,但是在没有标准答案的世界里不知道自己为什么是正确的,好像会很迷茫很辛苦。

从对我有意义的方面来看,我的生活变化很小。一样的三八生活(八小时睡觉,八小时工作,八小时做自己。并且居家隔离的背景似乎还强化了这一点),一样的东奔西走结果什么都做不成。十年如一日地希望减重。责备自己之后又原谅自己;再到原谅得有些过分。那天在散步的时候我问自己:

如果每时每刻的我都是我,那么我应该满足哪一个我的要求?是那个要求“及时行乐”的我吗?从来、永远都不是吗?

回答第一个分句是容易的,道德和自我要求让我很容易否决其建议——总是满足最近处的需要长远来看绝不是好的策略。但妥善回答第二个问题有点难度。如果我从不满足当下的我的(属于七宗罪之一的)愿望,是不是也不算过有俗世兴味的生活?我内心里是不想脱俗的。

最后我是这样和自己约定的:不是每个长远的目标都值得追求;但是如果一件事总是在我的新年愿望中出现,那么就不加犹豫地做它。长久以来东奔西走的灵魂可能也确实觉得合理,想到这个准则的那一瞬间,身体里好像有什么东西落了地。

所以我说,如果这个主题反复出现,说明它是一个值得解决的问题。

我们应该真诚地生活——看《绿皮书》

本文有剧透成分。

在欣赏电影和剧情向游戏的时候我很少两次踏入同一条河流,破例的情况屈指可数:小时候想和表哥为了熬夜而熬夜看过二十几遍《终结者2》;实在太喜欢《金刚狼3》夹在现实主义和浪漫主义之间的奇妙路线,看过首映之后,等黑白版发售之后又在宾馆电视里看了一次黑白版;应该还有,但是确实屈指可数。

从影片上映起至今,我看过三次《绿皮书》。第一次在影院,第二次在回美国的飞机上,第三次忘了。万事开头难——这部电影女朋友比我早看到一些,之后就全力推荐我去看。不过她的话术有些糟糕,讲故事梗概的时候还是把它归为了“讲种族问题的那点事儿”,搞得我下意识有点抵触。不久之后,我刚好在mall里躲雨的时候发现里面有家影院,就贡献了《绿皮书》一张票。名义上,我想,这样回去和老婆好交代;实际上是同期的片子要么看过了,要么真提不起兴趣。

看完之后直接的记忆只有感动和震撼,因为它绝不是“讲种族问题的那点事儿”。如果您也和我一样因为这类概括而错过了它,我想说,早看晚看都来得及。写到这里,虽然还没入题,还是有一点题外话想说。惯于和网友对线,疏于整理日志来记录自己的思想,似乎让我不设置好一个靶子便很难有的放矢。鉴于此,请原谅我复制豆瓣的简介如下:

托尼(维果·莫腾森 Viggo Mortensen 饰)是一个吊儿郎当游手好闲的混混,在一家夜总会做侍者。这间夜总会因故要停业几个月,可托尼所要支付的房租和生活费不会因此取消,所以他的当务之急是去寻找另一份工作来填补这几个月的空缺。在这个节骨眼上,一位名叫唐雪莉(马赫沙拉·阿里 Mahershala Ali 饰)的黑人钢琴家提出雇佣托尼。

  唐雪莉即将开始为期八个星期的南下巡回演出,可是,那个时候南方对黑人的歧视非常的严重,于是托尼便成为了唐雪莉的司机兼保镖。一路上,两人迥异的性格使得他们之间产生了很多的矛盾,与此同时,唐雪莉在南方所遭受的种种不公平的对待也让托尼对种族歧视感到深恶痛绝。

https://movie.douban.com/subject/27060077/

在我眼里,托尼不是一个简单的、可以用“吊儿郎当”和“游手好闲”来形容的混混。他人情练达,关系网广泛:无论经济状况如何都能经营和睦的家庭;和兄弟们有健康的关系;在社会上总有人愿意为他提供工作。他有小聪明:用“帽子戏法”骗取大佬的欢心,懂得怎么用灰色手段快速解决问题。他做事果决:该出手时就出手,能打也抗打,因此大家也都觉得他可靠。所以即使是一开始的托尼,也不是一个随处可见的混混。

但不止于此。我总觉得他有一种莫名的魅力——能感觉到,但很难表达。和不同的朋友聊过很多次,才渐渐和每个阶段的自己统一了话术。当然这个角色有忠诚、勇敢之类很明显的优点,在此就不再提。

这个角色打动我最深的地方,是他对于高雅的生活的毫不掩饰的向往。他纵使不习惯和不同肤色、不同阶级的人生活从而显得有些尴尬,但当他第一次体验到唐的演奏的时候所流露出的欣赏是无比真诚的,他甚至立刻开始和旁边的小哥自夸:“看,那是我朋友,是不是牛B?”。看似平常,但是我很少见到谁在自己不欣赏的人做出有趣的成就时给出相似的赞美。我甚至说它是一切使托尼的生活发生好转的主要原因,因为它成全了托尼的那种真诚的拿来主义。唐教他给老婆写情信。他不排斥唐那套文绉绉的行文方式,也不在乎别人是不是能看出他在偷学拼写。他很单纯,“有人教我如何给我的老婆写好信——这是一件好事”。想想现实中人面对如此简单的命题,还要拧巴许久才能承认(甚至不承认)其正确性,很难不被他的纯粹所打动。

相比之下,唐这个人是非常拧巴的——这两个人的特点似乎都可以归咎于环境。托尼是小平民,大概也没受过好的教育。有聪明(我甚至说他有智慧),但是受限于眼界,只能在力所能及的地方略施小计。唐,他的拧巴也有原因:南方白人的怒拳相向倒还好,北方白人暗戳戳的歧视确实很让人难以捉摸;而同时他作为一个黑皮肤贵族又难以和底层的黑人相处。在肤色和阶级两个维度上都和主流群体完美错过,确实别扭。

之所以说这绝不是一部“种族问题”电影,一个原因是众所周知的讲“种族问题”的电影都不争气。总结起来就是它们一贯的表现手法都会陷入身份政治的圈套里。但我认为《绿皮书》是反身份政治的,它的白人混混、黑人精英的设定如果被理解成“白人也有烂货,黑人也有高尚的品格”其实是一种逆向歧视(你细品),所以绝非如此。我认为它和身份政治无关,甚至是反身份政治的理由是:我没有看出在身份意义上这两个人解决了任何事情。我不相信托尼在经历此事后就会对所有黑人视如兄弟,同样也不相信唐会在未来的“黑白两道”走得更加容易。他们之间温柔的化学反应,恰恰是因为两人发现了彼此作为个体的闪光点,有让自己向往的部分——唐过得自尊、富足而高雅,托尼活得简单、通透又真诚。

然后有一天二号靶问我,“你不觉得托尼从一开始的深度歧视,到后面接受唐到家里过圣诞,这个转变有点过于突兀吗?”我不觉得突兀,但是统一话术还是用了几分钟时间。

结论是这样的:我不认为电影一开始的托尼打心眼里就那么讨厌黑人。他把黑人水管工用过的水杯扔掉,在我看来更像是一种表演。我不知道这算不算Toxic Masculinity的一部分(这个话题可以之后再聊),但是在东北男人看来,这种“表演”是很好理解的。要在雄性之中脱颖而出,一个屡试不爽的方式就是在大家向往的方向做得过火。靠规则管理可以解决的事情,一定要做个物理屏障,Alpha;明明有棍子,非要用拳头打老虎,Alpha;口罩戴着尴尬又不舒服,所以干脆不戴,Alpha;你们都暗戳戳的搞种族歧视,我一定要明着搞,显得不共戴天,Alpha。(我真不是在说川普。)托尼下意识地进行如此“表演”是很好共情的,毕竟他一直以来赖以生存的手段就是不断让人认为自己是Alpha。

但唐的生活状态就缺乏了表演吗?唐有更多的表演,虽然并不Alpha。印象最深的、被我谈论最多次的就是炸鸡环节:托尼请唐吃炸鸡,唐婉拒;托尼认为黑人都喜欢吃炸鸡,所以表示了惊讶;唐认为托尼用肤色概括自己,所以表达了愤怒;托尼并没有理解他的愤怒,依然盛情邀请他吃炸鸡;唐抱着怀疑和不满的态度试了一下,发现真好吃。然后俩人吃了一路。

他们因着各自的理由表演下去,这理由主要来自一种群体幻觉。因为有着种族歧视的历史,又有半个国家还未开化,托尼隐约觉得他的兄弟们讨厌黑人。也因此,他的兄弟们觉得他也不喜欢黑人。每个人的表演在Alpha争夺战中愈演愈烈,变成了习惯性的、无来由的歧视。也是相似的原因,唐觉得每一个把他和“黑人”连接起来的符号都是对他的侮辱,以至于他一度与两个群体都脱离了联系。这些幻觉在反复的强化中被赋予了实体,直到两个灵魂真正的交会才能澄清。故事结尾,唐终于愿意坦诚“自己的孤独是自己的选择”,决定去托尼家敲门。托尼也愿意为这个珍贵的朋友放弃自己营造的Alpha幻觉。而见到唐的一家人,在尴尬了一秒钟后马上邀请唐在桌边就坐。在那一秒钟里,我能听见全家人的幻觉融化的声音。

我容易且愿意被真诚的生活打动,是因为我自己也是一个别扭人。感觉似乎应该再加一段合适的结尾,但是太久没有写字,写到这里已经头皮发麻了。姚若龙的一句歌词我一直记忆犹新,已经从早恋时代哼唱到快到晚婚。“这是我们的纪念日”,范玮琪唱,但不是纪念什么相爱、牵手、拥抱、接吻。她唱“纪念我们开始对自己诚实”。所以大抵也确乎是一个重要的日子。

Understanding Position-Based Dynamics

This post will be my notes on PDB study and will only include points I feel note-worthy. It is by no means a well-rounded tutorial. Btw I learned the principals from this online course: https://www.youtube.com/watch?v=fH3VW9SaQ_c (kudos)

Along with the note I’ll implement the distance constraint to illustrate how it works.

Reminders

  • PBD does not even try to simulate actual physics. It only looks good. All physics ‘references’ (for example, so-called momentum conservation) are attempts to make the scene look real.
  • PDB algorithm is a constraint solver. The algorithm introduced in the course is one-constraint-a-time, Gauss-Seidel method.

Math Review

  • Gauss-Seidel updates proposed values during iteration
  • Jacobi updates values only after each iteration (symmetrical?)

PBD Loop

before mainloop, all vertices should be assigned an initial position x_i, velocity v_i and invert-mass w_i (1/m_i).

  1. for each vert i, update proposed velocity v_i = v_i + t * w_i * external_force_i
  2. damp velocities. a simple way is times 0.99
  3. for each vert i, update proposed position p_i = x_i + t * v_i
  4. for each vert i, detect collection and generate collision constraints x_i -> p_i
  5. solve constraints – it’s numerical solver, so here will be an inner loop
  6. for each vert i, finalize velocity v_i to be actual velocity (p_i – x_i) / t
  7. for each vert i, finalize position x_i to be p_i

Cheatsheet

Distance constraint
C(p_i, p_j) = |p_i, p_j| - d
(look carefully you will notice it is not Hooke's law)

For constraint C(p1, p2), with invert mass of w1, w2 respectively,
Gradient of p1: (p1-p2)/|p1-p2|
Gradient of p2: (p2-p1)/|p2-p1|
Update of p1: -w1/(w1+w2)*(|p1-p2|-d)*(p1-p2)/|p1-p2|
Update of p2: +w2/(w1+w2)*(|p1-p2|-d)*(p1-p2)/|p1-p2|

Code

GitHub Gist: https://gist.github.com/stormouse/90a8d22582ad4e9bc6c91c4a5f80e842

https://editor.p5js.org/stormouse/present/lbMzg3OsY

Research: Indoor CQC Map Generation (1)

Objective: write an algorithm that can create a floor plan (single story) that is reasonable enough to be used as a top-down close quarter combat (CQC) game map.

I don’t know how to do it yet, so I’d study some basic floorplan design principles. The remaining of the article would be my notes watching this video.


Inputs

  • Size of the floorplan
  • Number of stories
  • Single/Multi-family
  • Number of each type of rooms, also garage
  • Thickness of external/internal walls

Features of Space/Room

Y/S/N – Yes/Semi/No

  • Sqft needs (Y/S/N)
  • Adjacencies (list of room references, immediate or not)
  • Public access (Y/S/N)
  • Daylight/Views (Y/S/N)
  • Acoustical privacy (Y/S/N)
  • Plumbing (Y/S/N)
  • Special equipment (DW, MW, Refri…)

Macro Constraint Solving

  1. Fill in the features of each unary feature (anything other than adjacency) with average value + variation
  2. Create a few reasonble and strict requirements on adjacency
  3. Constraint solving on adjacency
  4. Generate Relationship-Diagram (9:08 in the video)

Grouping by Constraints

  1. Group rooms by their features. (e.g. kitchen+living room + dining room since none of them requires privacy)
  2. Place rooms in different directions and makes it somewhat reasonable.

Abstract Floor Plan

  1. Follow the grouping result, draw circles of correct scale with respect to the actual space requirement, at the approximate place we planned earlier.
  2. Ensure each “connected component” is pure, rather than mixed with private and non-private.
  3. Set daylight direction and try to satisfy lighting/view requirements.
  4. Resolve adjacency problems by not allowing adjacency edges to cross. (How)

Bubble Diagram

Draw bubbles (fat circles just like in the abstract floor plan) and make them fit.

Block Diagram

Growing bubbles and make them squarer. Okay to leave some space in between squares as hallways.

(How to make things align though?)

Figure out room sizes

Tip: sum of furniture area + circulation room ~= final room size

Ideas: pre-make furniture assets so save some difficulty generating furnitures.

Open Correct Doors

  • Open correct doors according to the adjacency map.
  • Make sure every room can be accessed
  • Special restrictions for bathroom
  • Keep doors away from beds, toilets, desks

Walls

  • Pluming walls
  • Exterior walls
  • Seperating walls
  • No walls (Imaginary walls that’s removed with common sense, e.g. living & dining & kitchen)
  • Half walls (Shelves or anything that works as a half-separation)

UNet Multiple Character Solution

(This post is originally from the wiki I wrote for my recent game project Runner or Dinnerhttps://github.com/stormouse/horror-story-alpha/wiki/UNet-Multiple-Character-Solution)

Dead ends

  • Add components according to the character type after a playerPrefab is spawned: Unity does not support adding NetworkBehaviour to a spawned GameObject
  • Bind every possible component to playerPrefab and disable some of them according to the character type: Too hard to organize component relationships on that prefab. Dirty component references.

Solution

Unreal Method

Let playerPrefab be a ‘player controller’,and the characters are Pawns/Characters in Unreal concepts. A player controller can possess a character.

Pros: this method is universal, easily comprehensible, fully-controlled

Cons: you have to implement possess, and native helper components like NetworkTransform and NetworkAnimator cannot help you anymore.

P.S: I haven’t think over how to implement this feature, will research on Unreal’s implementation further.

Spawn-time Replacement (Our Way!)

If your game has a lobby scene, a traditional way to share information between lobbyPlayer and gamePlayer is to customize OnLobbyServerSceneLoadedForPlayer

// class NetworkLobbyPlayer
public override bool OnLobbyServerSceneLoadedForPlayer(GameObject lobbyPlayer,
                                                       GameObject gamePlayer);

This callback runs when the server notices a client has finished loading game scene. By default it spawns a game player prefab and transfer the control from lobbyPlayer to gamePlayer. But we can ignore the existing gamePlayer and create a new character for this client, which requires ReplacePlayerForConnection

public static bool ReplacePlayerForConnection(Networking.NetworkConnection conn,
                                              GameObject player, 
                                              short playerControllerId);

From server’s view, clients are just connections. We can get a client’s connection from a NetworkIdentity that is spawned for that client, for us that is, the lobbyPlayer.

var clientConn = lobbyPlayer.GetComponent<NetworkIdentity>().connectionToClient;

Then we spawn a new character prefab for the client to ‘possess’.

// instantiate on server side
var newPlayer = Instantiate(characterPrefab);

// tell all clients to spawn (auto-synced)
NetworkServer.Spawn(newPlayer);

// destroy auto-created gamePlayer                      
NetworkServer.Destroy(gamePlayer);

No need to worry about synchronization problem. The Spawn method of NetworkServer will ensure that every client scene will create a same copy when they are ready.

Finally, we return false for OnLobbyServerSceneLoadedForPlayer to prevent transfer of player control, because we have destroyed the game player and given the control to newPlayer by replacement.

How to Build a Heatmap for Tactic AI : Attempt 2

After a little bit more thinking I changed the propagation function from

Heat(neighbor) += Heat(this) * decay / numberOfOpenNeighbors

to

Heat(neighbor) = Max(Heat(neighbor), Heat(this) - numOpenNeighbors * decay)

And now the decay factor is the valve for linear decreasing speed.

Intuitively you can see the ‘heat’ is decreasing linearly relative to ‘distance’ from heat source. But remember, we want a 1/distance^2 like curve for it, therefore we apply a post-processing step to the temperature we got for each grid. That is:

(1) Distance(grid) = Heat(source) - Heat(grid)
(2) Factor(grid) = 1 / (Distance(grid) * Distance(grid) + 1)
(3) Factor(grid) = Factor(grid) - 1 / (Heat(source) * Heat(source) + 1)
(4) Heat(grid) = Heat(source) * Factor(grid)

By applying step (2), we generated a 1/distance^2 like function. (The +1 term on the denominator is to avoid divide by zero at source point.) But you can see that even a grid originally with Heat = 0 will be assigned with a positive factor. Step (3) is responsible for eliminating those residues.

Here I present the test results:

Compared with the result of last post, the result of this new mathematical model yields more reasonable area of influence, as well as easier-to-tune heatmap parameters.

Hope it helps you somehow!

PS: this algorithm takes 1~2 ms to run on my Unity3D scene, with grid map size ~60*40, which is acceptable for my current need, but not as good as expected. Later post I might talk about my experiments on optimizing the algorithm.


			

How to Build a Heatmap for Tactic AI : Attempt 1

I’ve been doing experiments on game AI, tactic team AI to be specific. And currently I’m looking forward to make use of a heatmap.

My idea is to make a grid map and assign heat value (or temperature) on grids so that it represents value of some property that can be reasoned about by an agent. An example would be to represent the potential danger. Think of a group of defenders who wants to defend a room with two entrance from waves of zombies. The area close to the two entrances are most dangerous, therefore have highest temperature in the heatmap. In addition to those two points, places nearby should also be ranked more dangerous than any other places. And a corner of the room could be a safer place to hold (although it’s not always true).

However I’d like the heatmap to have some extra characteristics. Namely, I want the emission of the heat satisfy this constraint:

The narrower the space is, the harder for the heat to go away.

It makes sense because it’s most dangerous if you are trapped in a small cage or a narrow corridor with a ruthless zombie than in a open space, where you can run and turn freely.

My first implementation attempt is to use Breadth-First Search to spread the heat and decrease the energy as it goes farther and farther. And to satisfy the requirement above, I made the decrease factor negatively related to the number of open spaces (neighbor grid without obstacles) next to a grid. For instance, let’s say first there’s a heat source somewhere in the grid field, and 5 out of it’s 8 neighbors are obstacles. And those 3 open neighbors will share all the energy the heat source emits. On the other hand, if all 8 neighbors of some heat source are open, the heat emit will be evenly distributed among them, therefore each neighbor receives less energy.

Intuitively this method tends to keep energy in small room and makes heat in narrow hallways to travel further before the energy becomes insignificant, than in a wide open space. However, the experiment didn’t work out well.

In the experiment I use the number of grids a heat source can influence to indicate whether this model makes sense. In common sense, a heat source with the same initial energy should influence same number of grids regardless of geometry, assuming the obstacles are made of insulation materials. But this naive model cannot guarantee it. Also, exponentially decreasing energy feels wrong. You can see the results from the image below. (Depth of green-ish color indicates the energy. Two heat sources are on the same row, different columns of the field)

exponential_decreasing_by_open_neighbors
Heat in narrow spaces does influence further, but the area of influence is unproportional.

Next step I’ll figure out how to preserve the area of influence on any geometry, and try to invite some 1/distance^2 like factor into the function because it feels more natural.