Sign in

iiinova is part of the Atmosphere — an open, public network where you own your identity.

KiritaniAyaka

@ayk.moe
71 followers 36 following 441 posts

Student | Fullstack Developer | INFP

PostsRepliesMedia
KiritaniAyaka @ayk.moe · 03/08/2026
最近幾個版本的 GPT 都很愛動不動就換行,之前看到它這樣說話真的瞳孔地震
ChatGPT 的輸出:

十四、流通市值
很多人忽略。
例如:
上市:
總市值:
500億
流通:
10億
容易:
爆炒。
如果:
流通:
300億
很難炒。
110 1
KiritaniAyaka @ayk.moe · 18/04/2026
印證了之前的一個猜想——預製花嫁卡的顏色有問題。 起因是看到網上的照片似乎都比咱的卡在偏白的位置更清晰,多搜了點發現有的人的卡色彩非常鮮艷。一開始以為是拍照光線或者環境光原因,直到線下看到了其他人的卡… 然後就想會不會是預製卡做工問題,於是直接同卡面同號換卡,最後拿到的兩張區別如圖,兩張都是金邦達製造的,可以排除不同製卡商工藝區別,手機攝像比較拉,肉眼看可能區別更明顯。
兩張招商銀行 bilibili 花嫁聯名卡並排放在桌上,其中右邊的比左邊色彩更鮮艷一些。
050 5
KiritaniAyaka @ayk.moe · 04/04/2026
吃上了!誒嘿嘿 🤤
在 McDonald's 店內的餐盤上擺著兩份餐速食餐,包括漢堡、薯條、可樂和瀧情蜜意麥旋風,旁邊還有一張汪蘇瀧寫真小卡。手持一杯印著紅黑格紋背景和「素龍」的麥旋風,上面寫著「McDonald's McFlurry」。
080 8
KiritaniAyaka @ayk.moe · 02/04/2026
啊啊啊啊啊啊啊好想去吃 不過集齊所有寫真卡片估計不太可能了,一是咱一直以來的非酋體質,集卡據說是盲盒形式隨機抽一款,二是不管住處還是公司離麥當勞都有點遠,不可能天天去吃,咱也不可能為了集卡買一堆讓自己吃不完
一張亮黃色背景的宣傳截圖,中間是藝人汪蘇瀧的代言海報,旁邊有粉色麥旋風與粉色甜筒產品圖片;下方文字提到2026年3月底麥當勞與王楚欽代言合作結束及自2026年4月1日起汪蘇瀧成為麥當勞新的甜品代言人的資訊。麥當勞點餐頁面截圖,分類為「小食甜品/其他」並標示「上新」,商品包括「瀧情蜜意麥旋風買一送一」、「瀧情蜜意圓筒」、「瀧情蜜意雙旋圓筒」,並分別標示價格為¥16.5、¥5與¥5。一個音樂播放頁面,標題寫著「粉旋風」,歌手標示為汪蘇瀧,下方有歌詞「麥旋風攪拌雲朵的碎片」。一張聊天對話截圖,對話內容依次是「你會因為代言人去吃嗎」、「一般不會」、「但這是汪蘇瀧」、「他不一樣!」,對方回復「他不一樣!」;中間插入一張橘雪莉的表情包,表情包配字「舒服了」,下方對話接著:「咱要吃」與「還想集寫真卡片」
030 3
KiritaniAyaka @ayk.moe · 30/01/2026
咱的服務器還回來吃飯嗎😭
CloudCone Los Angels 的服務器於 2026 年 1 月 30 日 8:30(Asia/Shanghai 時間)出現重大故障的狀態頁。
140 4
KiritaniAyaka @ayk.moe · 27/12/2025
昨天發生一件奇怪的事,一個新賬號來咱的冷門倉庫裡開 issue 問 license,這個倉庫是很久之前的,那時候咱都沒開源意識所以也沒 license 還關注 license,一看就是開源老手了,那麼有什麼不能用大號說的…? 昨天咱加上了 MIT license,今天發現他直接銷號了🤯不是吧,為了問這個專門開個號
倉庫 KiritaniAyaka/markdown-it-ruby-parser 裡有人創建了一個 issue:

標題為:Need this project's license
內容為:Just want to know this project's license type

我關閉了 issue 並回復:Feel free to use it under MIT license
340 4
KiritaniAyaka @ayk.moe · 30/10/2025
一口一隻雞,我行你定行(x
拿著一顆敲開的活珠子,裡面露出了蛋黃和小雞胚胎肉
130 3
KiritaniAyaka @ayk.moe · 12/06/2025
JSX 中允許使用 class 作為 prop 傳遞並不見得是一件好事 class 仍然會在某些上下文被認為是 JavaScript 中的關鍵字,這時仍然避免不了重命名 更恐怖的是,項目裡出現 class 和 className 兩種不互通的傳遞 CSS 類規範,編寫通用組件的時候要麼處理更費勁,要麼就是埋下未來樣式不生效的隱患
一個渲染 class 和 className 這兩個 prop 的組件,並分別使用 class 和 className 作為 prop 傳遞,class 並不會自動傳遞到子組件的 className prop,反之亦然

代碼:

```js
import { render } from 'preact';

function MyComponent({ class: classA, className }) {
	return <div>
		<div>class: { classA }</div>
		<div>className: { className }</div>
	</div>
}

function Test() {
	return (
		<div>
			<MyComponent class="aaa" />
			<MyComponent className="sss" />
		</div>
	);
}

render(<Test />, document.getElementById('app'));
```

結果:

class: aaa
className:
class:
className: sss
110 1
KiritaniAyaka @ayk.moe · 02/05/2025
這個居然 TypeScript 不會報錯…從類型定義來看,似乎它的行為是如果傳入的不是 Promise 就原樣返回,找了大半天 bug… 一般沒有直接把非 Promise 傳進去的場景吧,感覺這個需要寫個 lint 規則約束一下…
一個 async 函數直接傳入了 Promise.all 的數組中Promise.all 的類型定義,返回值為 Awaited<T>,即如果參數不是 Promise 則原樣返回,是 Promise 則取出 Promise 的結果類型
140 4
KiritaniAyaka @ayk.moe · 22/04/2025
熊熊跟咱一模一樣的描述((
聊天記錄截圖:

這風颳得…
跟要世界末日了一樣
咱還想著開窗睡早上會涼快點(
啊wok這是下雨
110 1
KiritaniAyaka @ayk.moe · 09/04/2025
AI 逆天場面之「useEffect 裡 document.createElement」
在 React 項目中,AI 在 useEffect 中使用 document.createElement 創建元素,並設置 innerHTML。
130 3
KiritaniAyaka @ayk.moe · 17/03/2025
Got #Deno stickers🥳
Some Deno stickers on the table:

Deno logo with progress flag
Deno logo with rainbow flag
Deno logo with text
Rich Deno
JSR
Pure Deno brand logo
Sticker with code: "node".split().sort().join("");
120 2
KiritaniAyaka @ayk.moe · 12/03/2025
用 winhex 看了下原圖是 png,而從 atproto 下載的是 jpg。 用 magick 以 100 的質量把原圖轉換成 jpg 格式的話可以得到跟 atproto 上那張大小幾乎一樣的圖片(為什麼還差一點點就不是很清楚了,可能算法差異) 可以判定是圖片存到 atproto 之前轉換了一下格式
兩張圖片的十六進制,其中一個以 FFD8 開頭,另一個以 89504E47 開頭運行 magick .\Snipaste_2025-03-12_12-24-02.png -quality 100 ./jpeg-format.jpg 後得到的新圖片和 atproto 上下載的圖片大小分別是 136529 字節和 137003 字節
010 1
KiritaniAyaka @ayk.moe · 12/03/2025
想知道 atproto 會不會壓縮圖片之類的,上次發帖的時候注意了一下圖片的大小,結果令人匪夷所思 原截圖是 31.6KB,直接用瀏覽器訪問 atproto 上的 blob,HTTP 請求的總大小是 138KB,怎麼會事呢🤔
文件詳情頁,文件大小為 31.6KB直接訪問 atproto 上的 blob,HTTP 請求大小為 138KB
230 3
KiritaniAyaka @ayk.moe · 12/03/2025
搞不懂這個 OpenDAL…想了想自己沒有什麼跨語言的需求,還是決定換 unstorage 好了😇
控制台報錯:Error: could not resolve "@opendal/lib-win32-x64-msvc" into a module
然而在 package.json 中安裝了 @opendal/lib-win32-x64-msvc 的 0.47.11 版本
220 2
KiritaniAyaka @ayk.moe · 06/03/2025
🤯聽起來不錯
Phoenix 網站上的內容:We do plan to provide a way to cross-post between them so you don’t have to write duplicate posts.
100 1
KiritaniAyaka @ayk.moe · 04/03/2025
Windows 上這 emoji 怎麼用的對比這麼低的顏色…第一眼還以為是用兩隻手做出的兔子手勢(
Windows 上的擁抱 emoji
120 2
KiritaniAyaka @ayk.moe · 01/03/2025
用戶:How many "R"s are there in "Strawberry?"
Mercury-Coder:There is 1 "R" in "Strawberry".
用戶:Are you sure?
Mercury-Coder:Yes, I am sure. The word "Strawberry" contains one "R".
000 0
KiritaniAyaka @ayk.moe · 20/02/2025
BBC 將「detransition」譯為「性別逆轉」🤔
BBC 影片中字幕「鼓勵她進行性別逆轉」
120 2
KiritaniAyaka @ayk.moe · 18/02/2025
不如說整個 Winodws 都是這樣,便簽只是一脈相承(
Windows meme:

Knows you don't want to update
Forces you to update anyway
010 1
KiritaniAyaka @ayk.moe · 14/02/2025
Gemini 2 有事實核查功能,點了之後標綠的部分是可以從網上查到的信息,可以像 new bing 一樣查看相關網頁,方便分辨幻覺以及人工再核查
Gemini 回復:

税收优惠政策
企业所得税优惠

工资加计扣除:企业支付给残疾人员工的工资薪酬,可以在计算应纳税所得额时全额扣除,并可按照支付给残疾职工工资的100%加计扣除。

此段標綠,可展開查看相關網頁詳情:

Google 搜尋找到相似的資訊,內容如下:
三、根据《财政部国家税务总局关于安置残疾人员就业有关企业所得税优惠政策问题的通知》(财税〔2009〕70号)规定:“一、企业安置残疾人员的,在按照支付给残疾职工工资据实扣除的基础上,可以在计算应纳税所得额时按照支付给残疾职工工资的100%加计扣除。 ...
shenzhen.chinatax.gov.cn
020 2
KiritaniAyaka @ayk.moe · 14/02/2025
Vue 什麼時候出 4 了🤣
Wappalyzer 頁面顯示該網站使用了 Vue.js 的版本 4
210 2
KiritaniAyaka @ayk.moe · 10/02/2025
Same as the top comment. I feel just like a grain of sand in the tide of time, powerless...😢 www.youtube.com/watch?v=biUF...
@raghav9000: Man, I am graduating this year and can't even get an internship. Not even  for a very low stipend. I am hopeless.
110 1
KiritaniAyaka @ayk.moe · 04/02/2025
這是為什麼🤔試了下也不是強制使用 Google,而是遵循的 Chrome 的誰知,似乎是只允許用戶在 Chrome 內設置搜索引擎
We have had to remove the "search with" feature.
which allowed quick switching between search engines,
because of Chrome Web store policies.
This was NoT a voluntary choice but something
we had to do to comply. Thank you for your understanding.
However, you can still use this feature if you
download and install it from GitHub
Got it, don't showr this again
010 1
KiritaniAyaka @ayk.moe · 31/01/2025
寶藏項目,第一個 Star! #Blu-lyzer github.com/suibari/blu-...
blu-lyzer-2l 項目倉庫,star 數 1
120 2
KiritaniAyaka @ayk.moe · 31/01/2025
本來只是以為獲取環境變量的位置寫錯了名字,更正過來後發現還是不工作,然後發現自己 .env 文件裡也寫錯了,甚至兩個錯得還不一樣((
代碼:

const ENABLE_CACHE = Deno.env.get("CACGE")?.startsWith("e");.env 文件:
CAHCE=enabled
110 1
KiritaniAyaka @ayk.moe · 21/01/2025
用了四年多的英菲克鼠標壽終正寢了,45 塊錢這個壽命屬實讓咱有點驚訝,而且中途還摔過幾次,咱都以為完蛋了該換新鼠標了,結果只是出了一些小毛病還能湊合著用(
英菲克 PM6鼠標 傷痕累累
040 4
KiritaniAyaka @ayk.moe · 15/01/2025
接下來咱要表演一個魔術:(bushi
020 2
KiritaniAyaka @ayk.moe · 11/01/2025
不愧是熱門番 MyGO,半年多過去了還是有那麼多人在做種,有那麼多人在下載🥰
030 3
KiritaniAyaka @ayk.moe · 04/01/2025
然後嘗試修改了好多地方,但還是修復不好 看到項目裡幾個引用的 @std,想起上次解決的辦法,覺得應該又是哪個 @std 下的包在作妖,突然心血來潮更新了一下 Deno,之前 Workflow 裡一直在用 1.x,而咱本地 1.x 和 2.x 都有,更新到了跟咱本地相同的 Deno 2 版本,結果編譯就通過了… 所以緣由應該是 jsr:@std 有一些不能向後兼容的部分(尤其是在容器中編譯為單可執行文件這樣的邊緣場景),但是沒在他們的文檔上找到任何說明 但就算它不向後兼容 1.x,咱在本地不知道為什麼還是可以用 1.x 編譯通過,太詭異了簡直是玄學
將工作流文件中的 Deno 版本從 1.45.2 更改到了 2.1.1
000 0
KiritaniAyaka @ayk.moe · 04/01/2025
關鍵是一段使用了 JSR 上 @std/path 的代碼,咱百思不得其解,甚至跑去 GitHub 上把 @std/path 源碼讀了一遍,但一無所獲 然後抱著試一試的心態把 @std/path 的導入換成了 node:path(Deno 開發的 Nodejs 兼容層),結果編譯通過了!! 當時想著它能跑了就盡量不動這裡的代碼了,只留下了一行注釋,而至於出現的原因就不了了之了 最近需要重構這個項目,這個打印其實只是當時臨時用的,於是就整個文件刪掉了,離譜的是項目又開始在 Workflow 裡編譯報錯了…
// use `resolve` from `@std/path` will cause a weird compile error in workflow
const lastLogFile = resolve(dirname(programPath), "./last.log");
console.log("last log file:", lastLogFile);
100 1
KiritaniAyaka @ayk.moe · 04/01/2025
前陣子在寫自己的某個 Deno 項目時發現一個奇怪的現象 在 Workflow 裡用 deno compile 編譯成獨立可執行文件會報錯,信息很少(P1) 而且更奇怪的是,不管是在咱的開發機(Windows)上、部署的 Linux 服務器上,還是它運行 Workflow 使用的容器裡面編譯(咱用它的鏡像創建容器進去手動 deno compile)都沒有任何問題,只在 Gitea Workflow 裡出現 然後咱通過二分注釋法,找到了會出現這個問題的代碼
Download https://dl.deno.land/release/v1.45.2/denort-x86_64-unknown-linux-gnu.zip
Archive:  /tmp/.tmpjM1ynh/denort.zip
  inflating: denort                  
error: Writing temporary file 'server.tmp-7249eefa5c3ddbd4'
Caused by:
    No such file or directory (os error 2)
100 1
KiritaniAyaka @ayk.moe · 01/01/2025
濫用 BT 生態刷下載量以規避 PCDN 審查,之前只是有所耳聞,今天終於大開眼界了,滿速上傳半小時這幾個人進度一直是 0% 這下誰還敢做種,一個不小心就給自己做進運營商黑名單😅
BT 客戶端,四個已連接的用戶下載進度均為 0%
030 3
KiritaniAyaka @ayk.moe · 29/12/2024
不如叫「真黑」好了(bushi
主題切換彈窗,包含「亞麻」「黑曜」和「真黑」三個主題
010 1
KiritaniAyaka @ayk.moe · 24/12/2024
Day 19 #AdventOfCode 上次 day18 剛說完最近都是 BFS,這次就來動態規劃了() Part 1 dp 數組存儲的是這個長度能否實現,最終 dp 數組最後一個元素就是整個設計能否實現 Part 2 dp 數組存儲的是到當前長度為止有幾種實現方式,最後一個元素就是實現整個設計的方式數
export function solve(input: string) {
  const [towelsInput, designsInput] = input.split("\n\n");
  const towels = towelsInput.split(", ");
  const designs = designsInput.split("\n");
  function check(design: string) {
    const dp = Array<boolean>(design.length + 1).fill(false);
    dp[0] = true;
    for (let i = 0; i < design.length; i++) {
      if (dp[i]) {
        for (const t of towels) {
          if (design.startsWith(t, i)) dp[i + t.length] = true;
        }
      }
    }
    return dp[design.length];
  }
  return designs.reduce((acc, cur) => check(cur) ? acc + 1 : acc, 0);
}export function solve(input: string) {
  const [towelsInput, designsInput] = input.split("\n\n");
  const towels = towelsInput.split(", ");
  const designs = designsInput.split("\n");
  function check(design: string) {
    const dp = Array<number>(design.length + 1).fill(0);
    dp[0] = 1;
    for (let i = 0; i < design.length; i++) {
      if (dp[i]) {
        for (const t of towels) {
          if (design.startsWith(t, i)) dp[i + t.length] += dp[i];
        }
      }
    }
    return dp[design.length];
  }
  return designs.reduce((acc, cur) => acc += check(cur), 0);
}
000 0
KiritaniAyaka @ayk.moe · 22/12/2024
寫了些可以在 #AdventOfCode 裡用上的工具函數,發在了 JSR 上 jsr.io/@kiritaniayaka/surprise 都是類型安全的,狠狠地體驗了一把 TypeScript 類型體操,感覺類型體操就是一場盛大的遞歸藝術(
import { findPositions } from "jsr:@kiritaniayaka/surprise@0.5.0";

findPositions([1, 2, 3, 4, 5, 6, 7], 5); // [4]
findPositions([1, 2, 3, [4, 5, 6], 7], 5); // [3, 1]
findPositions([1, [2, 3, [4, 5, 6]], 7], 5); // [1, 2, 1]import { createDeepArray } from "jsr:@kiritaniayaka/surprise@0.5.0";

createDeepArray(0, 3); // number[]
createDeepArray(5, 3, 3); // number[][] with zero value 5
createDeepArray("Hello", 3, 3, 3); // string[][][] with zero value "Hello"
createDeepArray(false, 3, 3, 3, 3); // boolean[][][][] with zero value false
020 2
KiritaniAyaka @ayk.moe · 22/12/2024
Day 18 #AdventOfCode 都是 BFS(感覺這幾天好多 BFS…?),Part 2 直接暴力,每掉一個字節就重新 BFS 走一下看看有沒有路徑(
export function solve(input: string): number {
  type QueueItem = { x: number; y: number; steps: number };
  const size = 71;
  const bytes = 1024;
  const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] as const;
  const positions = input.trim().split("\n").map((line) =>
    line.split(",").map(Number)
  );
  const map: boolean[][] = Array.from(
    { length: size },
    () => Array(size).fill(false),
  );
  const inMap = (x: number, y: number) => x >= 0 && y >= 0 && x < size && y < size;

  for (let i = 0; i < bytes && i < positions.length; i++) {
    const [x, y] = positions[i];
    map[x][y] = true;
  }
  const visited: boolean[][] = Array.from(
    { length: size },
    () => Array(size).fill(false),
  );
  visited[0][0] = true;

  const queue: QueueItem[] = [{ x: 0, y: 0, steps: 0 }];
  while (queue.length) {
    const { x, y, steps } = queue.shift()!;
    if (x === size - 1 && y === size - 1) return steps;
    for (const [dx, dy] of directions) {
      const nx = x + dx;
      const ny = y + dy;
      if (inMap(nx, ny) && !map[nx][ny] && !visited[nx][ny]) {
        visited[nx][ny] = true;
        queue.push({ x: nx, y: ny, steps: steps + 1 });
      }
    }
  }
  return -1;
}export function solve(input: string): string {
  type QueueItem = { x: number; y: number };
  const size = 71;
  const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] as const;
  const positions = input.trim().split("\n").map((line) =>
    line.split(",").map(Number)
  );
  const map: boolean[][] = Array.from(
    { length: size },
    () => Array(size).fill(false),
  );
  const inMap = (x: number, y: number) =>
    x >= 0 && y >= 0 && x < size && y < size;

  function check() {
    const visited: boolean[][] = Array.from(
      { length: size },
      () => Array(size).fill(false),
    );
    visited[0][0] = true;
    const queue: QueueItem[] = [{ x: 0, y: 0 }];
    while (queue.length) {
      const { x, y } = queue.shift()!;
      if (x === size - 1 && y === size - 1) return true;
      for (const [dx, dy] of directions) {
        const nx = x + dx;
        const ny = y + dy;
        if (inMap(nx, ny) && !map[nx][ny] && !visited[nx][ny]) {
          visited[nx][ny] = true;
          queue.push({ x: nx, y: ny });
        }
      }
    }
    return false;
  }

  for (let i = 0; i < positions.length; i++) {
    const [x, y] = positions[i];
    map[x][y] = true;
    if (!check()) return `${x},${y}`;
  }
  return "";
}
010 1
KiritaniAyaka @ayk.moe · 16/12/2024
Day 16 #AdventOfCode 最近要考試了,可能不太會繼續參加了 今天的 Part 1 是典型 BFS,Part 2 沒想明白,感覺難度也在一點點上升,後面不一定能做出來了(
import { findPositions } from "jsr:@kiritaniayaka/surprise@0.4.0/array";
import { BinaryHeap } from "jsr:@std/data-structures@1.0.4/binary-heap";

type QueueElement = [number, number, number, Direction];
const DIRECTIONS = ["E", "S", "W", "N"] as const;
type Direction = typeof DIRECTIONS[number];
const MOVES = {
  "E": [0, 1],
  "S": [1, 0],
  "W": [0, -1],
  "N": [-1, 0],
};

export function solve(input: string): number {
  const map = input.split("\n").map((line) => line.split(""));
  const [[startX, startY]] = findPositions(map, "S");
  const [[endX, endY]] = findPositions(map, "E");
  const pq = new BinaryHeap<QueueElement>((a, b) => a[0] - b[0]);
  pq.push([0, startX, startY, "E"]);
  const visited = new Set<string>();

  while (pq.length) {
    const [cost, x, y, dir] = pq.pop()!;
    const state = `${x},${y},${dir}`;
    if (visited.has(state)) continue;
    visited.add(state);
    if (x === endX && y === endY) return cost;

    const [dx, dy] = MOVES[dir];
    const newX = x + dx;
    const newY = y + dy;

    if (map[newX][newY] !== "#") pq.push([cost + 1, newX, newY, dir]);
    const dirIndex = DIRECTIONS.indexOf(dir);
    pq.push([cost + 1000, x, y, DIRECTIONS[(dirIndex + 1) % 4]]);
    pq.push([cost + 1000, x, y, DIRECTIONS[(dirIndex + 3) % 4]]);
  }

  return -1;
}
000 0
KiritaniAyaka @ayk.moe · 15/12/2024
Day 15 #AdventOfCode 編寫一個 move 函數,當要移動的方向被擋住的時候遞歸移動物體,如果是障礙物就返回 false,成功移動返回 true Part 2 卡了好久沒做出來,總是一不小心就無限遞歸了,感覺今天腦子亂亂的不在狀態,就先醬吧
export function solve(input: string) {
  const DIRECTIONS = {
    "<": [0, -1],
    ">": [0, 1],
    "^": [-1, 0],
    "v": [1, 0],
  } as const;
  type Move = keyof typeof DIRECTIONS;
  const [mapInput, movesInput] = input.split("\n\n");
  const map = mapInput.split("\n").map((line) => line.split(""));
  const moves = movesInput.split("\n").flatMap((l) => l.split("")) as Move[];
  let x = map.findIndex((line) => line.includes("@"));
  let y = map[x].indexOf("@");

  function move(
    x: number,
    y: number,
    [dx, dy]: readonly [number, number],
  ): boolean {
    const newX = x + dx, newY = y + dy;
    const object = map[x][y];
    if (map[newX][newY] === "#") return false;
    if (map[newX][newY] === "." || move(newX, newY, [dx, dy])) {
      map[newX][newY] = object;
      map[x][y] = ".";
      return true;
    }
    return false;
  }

  moves.map((m) => DIRECTIONS[m]).forEach(([dx, dy]) => {
    if (move(x, y, [dx, dy])) [x, y] = [x + dx, y + dy];
  });
  return map.flatMap((r, x) => r.map((o, y) => o === "O" ? 100 * x + y : 0))
    .reduce((a, b) => a + b);
}
010 1
KiritaniAyaka @ayk.moe · 14/12/2024
Day 14 #AdventOfCode Part 1 中規中矩的模擬題,最後計算四個象限中機器人個數的地方用了點位運算縮短代碼(但難讀了qwq),總之就是大於小於的四種排列,分別轉換成二進制,剛好就是 0~3 對應四個象限 Part 2 蠻有意思的,甚至都不是算法題了,稍微有點 CTF 的味道了,直接用 sharp 把數組轉換成圖片輸出了,然後自己去找(
export function solve(input: string) {
  const numbersIn = (str: string) =>
    str.matchAll(/-?\d+(?:\.\d+)?/g).map((r) => parseFloat(r[0])).toArray();
  const SIZE_X = 101, SIZE_Y = 103, SECONDS = 100;
  const normalizePosition = (p: number, axis: "x" | "y") => {
    const SIZE = axis === "x" ? SIZE_X : SIZE_Y;
    while (p < 0) p += SIZE;
    return p % SIZE;
  };

  return input.split("\n").map((l) => numbersIn(l)).map(
    (robot) => [
      normalizePosition(robot[0] + robot[2] * SECONDS, "x"),
      normalizePosition(robot[1] + robot[3] * SECONDS, "y"),
      robot[2],
      robot[3],
    ],
  ).reduce((acc, [x, y]) => {
    if (x === SIZE_X >> 1 || y === SIZE_Y >> 1) return acc;
    acc[+(y > SIZE_Y >> 1) << 1 | +(x > SIZE_X >> 1)]++;
    return acc;
  }, Array<number>(4).fill(0)).reduce((a, b) => a * b);
}import sharp from "npm:sharp@0.33.5";
import { Buffer } from "node:buffer";
import { join } from "jsr:@std/path@1.0.8";

export async function solve(input: string) {
  const numbersIn = (str: string) =>
    str.matchAll(/-?\d+(?:\.\d+)?/g).map((r) => parseFloat(r[0])).toArray();
  const SIZE_X = 101, SIZE_Y = 103, SECONDS = 10000;
  const normalizePosition = (p: number, axis: "x" | "y") => {
    const SIZE = axis === "x" ? SIZE_X : SIZE_Y;
    while (p < 0) p += SIZE;
    return p % SIZE;
  };
  const robots = input.split("\n").map((l) => numbersIn(l));
  for (let i = 0; i <= SECONDS; i++) {
    const moved = robots.map(
      (robot) => [
        normalizePosition(robot[0] + robot[2] * i, "x"),
        normalizePosition(robot[1] + robot[3] * i, "y"),
        robot[2],
        robot[3],
      ],
    );

    const outputDir = join(import.meta.filename!, "..", "visualize");
    if (Deno.statSync(outputDir).isDirectory) {
      Deno.removeSync("./day14/visualize", { recursive: true });
    }
    const image = Buffer.alloc(SIZE_X * SIZE_Y, 0);
    for (const [x, y] of moved) image[x + y * SIZE_X] = 255;
    await sharp(image, {
      raw: {
        width: SIZE_X,
        height: SIZE_Y,
        channels: 1,
      },
    })
      .toFormat("jpeg")
      .toFile(join(outputDir, `${i}.jpg`));
  }
}機器人位置組成的聖誕樹彩蛋
020 2
KiritaniAyaka @ayk.moe · 14/12/2024
Day 13 #AdventOfCode Part 1 數據量很小,枚舉都行 Part 2 剛開始看到數據規模懵了,陷入了「用什麼算法才能解決」的泥潭,結果看到 GitHub 上一個大佬的代碼醍醐灌頂——這個方程組能把 ta(按鈕 A 按下的次數)消掉,變成 tb 關於其他變量的函數,然後就變成 O(1) 算法了😅真是數學專治一切花里胡哨 圖就放 P2 的了,上面提到的 GitHub 鏈接: github.com/letelete/adv...
export function solve(input: string) {
  const result = input.matchAll(
    /Button (?:A|B): X\+(\d+), Y\+(\d+)\nButton (?:A|B): X\+(\d+), Y\+(\d+)\nPrize: X=(\d+), Y=(\d+)/g,
  );
  const OFFSET = 10000000000000;
  function play(
    ax: number,
    ay: number,
    bx: number,
    by: number,
    px: number,
    py: number,
  ) {
    if ([ax, ay, bx, by].some((v) => v === 0)) return 0;
    [px, py] = [px + OFFSET, py + OFFSET];
    const tb = Math.floor((ay * px - ax * py) / (ay * bx - ax * by));
    const ta = Math.floor((px - bx * tb) / ax);
    return ax * ta + bx * tb === px && ay * ta + by * tb === py
      ? ta * 3 + tb
      : 0;
  }
  return result.reduce((acc, cur) => {
    const nums = cur.filter((_, i) => i > 0).map(Number);
    return acc + play(nums[0], nums[1], nums[2], nums[3], nums[4], nums[5]);
  }, 0);
}
040 4
KiritaniAyaka @ayk.moe · 13/12/2024
Day 12 #AdventOfCode Part 1 DFS 遍歷地圖並跳過曾經訪問過的位置,每遍歷到一個位置可以根據它與周圍四個格子的對比算出周長(四周每有一個與當前的不同,周長累計 +1) Part 2 與 Part 1 的唯一區別就是換用邊數計算,難點也在這裡,剛開始沒做出來,後來參考了紅迪上的大佬的想法——邊數等於角數
export function solve(input: string) {
  const map = input.split("\n").map((line) => line.split(""));
  const visited = Array.from(
    { length: map.length },
    () => Array(map[0].length).fill(false),
  );
  const directions = [[0, 1], [-1, 0], [0, -1], [1, 0]] as const;
  const inMap = (x: number, y: number) =>
    x >= 0 && x < map.length && y >= 0 && y < map[0].length;

  function dfs(x: number, y: number, type: string): number {
    const stack: [number, number][] = [[x, y]];
    let area = 0;
    let perimeter = 0;

    while (stack.length) {
      const [curX, curY] = stack.pop()!;
      if (visited[curX][curY]) continue;
      visited[curX][curY] = true;
      area++;

      for (const [dx, dy] of directions) {
        const newX = curX + dx;
        const newY = curY + dy;
        if (!inMap(newX, newY) || map[newX][newY] !== type) perimeter++;
        else if (!visited[newX][newY]) stack.push([newX, newY]);
      }
    }
    return area * perimeter;
  }

  return map
    .flatMap((r, x) => r.map((c, y) => visited[x][y] ? 0 : dfs(x, y, c)))
    .reduce((acc, cur) => acc + cur);
}Polaric_Spiral 在紅迪上的發帖:

Super simple part 1 implementation, just do a basic fill algorithm. For each new plot, add 4 to the perimeter and then subtract 1 for each adjacent plot.

I'm proud of my solution on part 2. The trick is to realize that the number of sides is equal to the number of corners, and you can count corners fairly easily. For each plot, check the neighbors in each pair of orthoganal directions. If neither match the source plot, as in the NE case:

...
##.
##.

you have an exterior corner. On the other hand, if both match the source plot and the corner plot doesn't match:

##.
###
###

you have an interior corner.

Counting interior corners this way ensures that each corner is counted once and only once.export function solve(input: string) {
  const map = input.split("\n").map((line) => line.split(""));
  const visited = Array.from(
    { length: map.length },
    () => Array(map[0].length).fill(false),
  );
  const directions = [[0, 1], [-1, 0], [0, -1], [1, 0]] as const;
  const inMap = (x: number, y: number) =>
    x >= 0 && x < map.length && y >= 0 && y < map[0].length;

  const countCorners = (x: number, y: number) =>
    [0, 1, 2, 3]
      .map((d) => [directions[d], directions[(d + 1) % 4]])
      .map(([[dx1, dy1], [dx2, dy2]]) => [
        map[x][y],
        map[x + dx1]?.[y + dy1],
        map[x + dx2]?.[y + dy2],
        map[x + dx1 + dx2]?.[y + dy1 + dy2],
      ])
      .filter(([self, left, right, mid]) =>
        (left !== self && right !== self) ||
        (left === self && right === self && mid !== self)
      )
      .length;

  function dfs(x: number, y: number, type: string): number {
    const stack: [number, number][] = [[x, y]];
    let area = 0;
    let corners = 0;

    while (stack.length) {
      const [curX, curY] = stack.pop()!;
      if (visited[curX][curY]) continue;
      visited[curX][curY] = true;
      corners += countCorners(curX, curY);
      area++;

      for (const [dx, dy] of directions) {
        const newX = curX + dx;
        const newY = curY + dy;
        if (!inMap(newX, newY) || map[newX][newY] !== type) continue;
        if (!visited[newX][newY]) stack.push([newX, newY]);
      }
    }
    return area * corners;
  }

  return map
    .flatMap((r, x) => r.map((c, y) => visited[x][y] ? 0 : dfs(x, y, c)))
    .reduce((acc, cur) => acc + cur);
}
010 1
KiritaniAyaka @ayk.moe · 11/12/2024
Day 11 #AdventOfCode 今天的兩個 Part 是同一個問題,只是數據規模不一樣 一開始用數組模擬,發現性能很差,Part 1 都跑了一秒多,於是從修改原數組改成創建新數組,避免移動元素帶來的損耗(1.7s => 30ms),到這裡性能已經夠用了,但是 Part 2 數組長度超了,一看內存佔用 1.8GB( 想到題目只要求數字的個數,可以不用存具體的數組,直接遞歸計算,參數在棧上,計算完了隨函數立即釋放。但是還是不能解決 Part 2,又想到很多計算是重複的,於是在遞歸函數外加了個緩存,成功解決 Part 2 P1:舊版本,不能過 Part 2 P2:優化版本
export function resolve(input: string, times = 25) {
  let nums = input.split(" ").map((n) => +n);
  function split(num: number): [number, number] {
    const str = `${num}`;
    return [+str.substring(0, str.length / 2), +str.substring(str.length / 2)];
  }

  function blink(nums: number[]) {
    const result: number[] = [];
    for (let i = 0; i < nums.length; i++) {
      if (nums[i] === 0) result.push(1);
      else if (`${nums[i]}`.length % 2 === 0) result.push(...split(nums[i]));
      else result.push(nums[i] * 2024);
    }
    return result;
  }

  for (let i = 0; i < times; i++) nums = blink(nums);
  return nums.length;
}export function solve(input: string, times = 25) {
  const nums = input.split(" ").map((n) => +n);
  const cache = new Map<string, number>();
  function split(num: number): [number, number] {
    const str = `${num}`;
    return [+str.substring(0, str.length / 2), +str.substring(str.length / 2)];
  }

  function blink(nums: number[], depth: number): number {
    if (depth === 0) return nums.length;
    return nums.reduce((acc, cur) => {
      let count = cache.get(`${cur},${depth}`);
      if (count !== undefined) return acc + count;
      cache.set(`${cur},${depth}`, count = blinkOne(cur, depth));
      return acc + count;
    }, 0);
  }

  function blinkOne(num: number, depth: number) {
    if (num === 0) return blink([1], depth - 1);
    else if (`${num}`.length % 2 === 0) return blink(split(num), depth - 1);
    else return blink([num * 2024], depth - 1);
  }

  return blink(nums, times);
}
010 1
KiritaniAyaka @ayk.moe · 10/12/2024
Day 10 #AdventOfCode Part 1 遞歸找地圖上的 9 就好,找到一個就把位置存 Set 裡去重(剛開始理解錯了題目意思,沒有去重,找到的結果是路徑數,結果發現就是 Part 2 的答案) Part 2 做 Part 1 的時候意外做出來了,狂按 Ctrl + Z 就解決了(真的
export function solve(input: string) {
  const map = input.split("\n").map((line) => line.split(""));
  const start: [number, number][] = [];
  for (let x = 0; x < map.length; x++) {
    for (let y = 0; y < map[x].length; y++) {
      if (map[x][y] === "0") start.push([x, y]);
    }
  }
  return start.reduce((acc, cur) => {
    const peaks = new Set<string>();
    find(...cur);
    return acc + peaks.size;

    function find(x: number, y: number) {
      const cur = +map[x][y];
      if (cur === 9) peaks.add(`${x},${y}`);
      if (+map[x + 1]?.[y] === cur + 1) find(x + 1, y);
      if (+map[x - 1]?.[y] === cur + 1) find(x - 1, y);
      if (+map[x]?.[y + 1] === cur + 1) find(x, y + 1);
      if (+map[x]?.[y - 1] === cur + 1) find(x, y - 1);
    }
  }, 0);
}export function solve(input: string) {
  const map = input.split("\n").map((line) => line.split(""));
  const start: [number, number][] = [];
  for (let x = 0; x < map.length; x++) {
    for (let y = 0; y < map[x].length; y++) {
      if (map[x][y] === "0") start.push([x, y]);
    }
  }
  function find(x: number, y: number): number {
    const cur = +map[x][y];
    let count = 0;
    if (cur === 9) return ++count;
    if (+map[x + 1]?.[y] === cur + 1) count += find(x + 1, y);
    if (+map[x - 1]?.[y] === cur + 1) count += find(x - 1, y);
    if (+map[x]?.[y + 1] === cur + 1) count += find(x, y + 1);
    if (+map[x]?.[y - 1] === cur + 1) count += find(x, y - 1);
    return count;
  }

  return start.reduce((acc, cur) => acc + find(...cur), 0);
}
010 1
KiritaniAyaka @ayk.moe · 09/12/2024
Day 9 #AdventOfCode Part 1 原本是想像題目裡那樣轉換成字符串的,結果發現 ID 可以是兩位數,於是只好用一個數組來記錄每一個塊。 然後直接雙指針從兩頭開始計算校驗和(假裝移動),依次從兩頭消費塊的個數 Part 2 這部分似乎直接計算校驗和有點費腦子,於是還是模擬了一下移動… 今天的題材很喜歡,就是咱寫的性能不怎麼樣()
export function solve(input: string) {
  type Block = { id: number; len: number };
  function expand(input: string) {
    let n = 0;
    return input.split("").reduce((acc, cur, i) => {
      return [...acc, { id: i % 2 !== 0 ? -1 : n++, len: +cur }];
    }, [] as Block[]);
  }

  function checkSum(blocks: Block[]) {
    let sum = 0, pos = 0;
    for (let i = 0, j = blocks.length - 1; i <= j;) {
      if (blocks[i].id !== -1) { // is current block not empty
        sum += pos++ * +blocks[i].id;
        blocks[i].len--;
      } else {
        while (blocks[j].id === -1) j--; // skip empty
        while (blocks[j].len && blocks[i].len) { // "moving"
          sum += pos++ * +blocks[j].id;
          blocks[j].len--;
          blocks[i].len--;
        }
      }

      if (blocks[i].len === 0) i++;
      if (blocks[j].len === 0) j--;
    }
    return sum;
  }
  return checkSum(expand(input));
}export function solve(input: string) {
  type Block = { id: number; len: number };
  function expand(input: string) {
    let n = 0;
    return input.split("").reduce((acc, cur, i) => {
      return [...acc, { id: i % 2 !== 0 ? -1 : n++, len: +cur }];
    }, [] as Block[]);
  }

  function move(blocks: Block[]) {
    for (let i = blocks.length - 1; i > 0; i--) {
      while (blocks[i].id === -1) i--;
      const avaliable = blocks.findIndex((b) =>
        b.id === -1 && b.len >= blocks[i].len
      );
      if (avaliable === -1 || avaliable > i) continue;
      // moving
      const lenDiff = blocks[avaliable].len - blocks[i].len;
      blocks[avaliable].id = blocks[i].id;
      blocks[i].id = -1;
      if (lenDiff) {
        blocks[avaliable].len = blocks[i].len;
        blocks.splice(avaliable + 1, 0, { id: -1, len: lenDiff });
      }
    }
    return blocks;
  }

  function checkSum(blocks: Block[]) {
    let sum = 0;
    for (let i = 0, pos = 0; i < blocks.length; ++i) {
      while (blocks[i] && blocks[i].id === -1) pos += blocks[i++].len; // skip empty
      while (blocks[i] && blocks[i].len--) sum += pos++ * blocks[i].id;
    }
    return sum;
  }
  return checkSum(move(expand(input)));
}
020 2
KiritaniAyaka @ayk.moe · 08/12/2024
Day 7 #AdventOfCode Part 1 記錄所有的種類及其位置,然後從中任選兩個遍歷,用了個 JSR 上的包計算所有組合 Part 2 基本一樣,區別是計算波腹的時候需要計算多個距離,可以直接循環整個地圖的大小次數,然後把在地圖外的過濾掉
import { combinations } from "jsr:@elf/combinatorics@^1.0.14/Combinations";

export function solve(input: string) {
  type Point = [number, number];
  const map = new Map<string, Point[]>();
  const lines = input.split("\n");
  for (let x = 0; x < lines.length; x++) {
    for (let y = 0; y < lines[x].length; y++) {
      const c = lines[x][y];
      if (c === ".") continue;
      map.set(c, [...(map.get(c) ?? []), [x, y]]);
    }
  }
  const inMap = ([x, y]: Point) =>
    x >= 0 && x < lines.length && y >= 0 && y < lines[0].length;

  function calcAntinode([ax, ay]: Point, [bx, by]: Point) {
    return ([
      [bx * 2 - ax, by * 2 - ay],
      [ax * 2 - bx, ay * 2 - by],
    ] as Point[]).filter((n) => inMap(n));
  }

  const antinodes = new Set<string>();
  for (const [, v] of map) {
    for (const [a, b] of combinations(v, 2)) {
      calcAntinode(a, b).forEach((n) => antinodes.add(`${n}`));
    }
  }
  return antinodes.size;
}
import { combinations } from "jsr:@elf/combinatorics@^1.0.14/Combinations";

export function solve(input: string) {
  type Point = [number, number];
  const map = new Map<string, Point[]>();
  const lines = input.split("\n");
  for (let x = 0; x < lines.length; x++) {
    for (let y = 0; y < lines[x].length; y++) {
      const c = lines[x][y];
      if (c === ".") continue;
      map.set(c, [...(map.get(c) ?? []), [x, y]]);
    }
  }
  const inMap = ([x, y]: Point) =>
    x >= 0 && x < lines.length && y >= 0 && y < lines[0].length;

  function calcAntinode([ax, ay]: Point, [bx, by]: Point) {
    const result: Point[] = [];
    for (let i = 0; i < lines.length; i++) {
      result.push([bx + i * (bx - ax), by + i * (by - ay)]);
      result.push([ax + i * (ax - bx), ay + i * (ay - by)]);
    }
    return result.filter((n) => inMap(n));
  }

  const antinodes = new Set<string>();
  for (const [, v] of map) {
    for (const [a, b] of combinations(v, 2)) {
      calcAntinode(a, b).forEach((n) => antinodes.add(`${n}`));
    }
  }
  return antinodes.size;
}
010 1
KiritaniAyaka @ayk.moe · 07/12/2024
Day 7 #AdventOfCode Part 1 hummmm雖然不知道貪心是不是最優解但是貪就完了!優先選擇乘法,如果超過目標值再考慮加法。(乘法增長更快) Part 2 同上,但是拼接比乘法增長更快,優先選拼接
export function solve(input: string) {
  const formulas = input.split("\n").map((line) => {
    const r = /(\d+):((?: \d+)+)/g.exec(line);
    return [+r![1], [
      ...r![2].split(" ").filter(Boolean).map(Number),
    ]] as [number, number[]];
  });

  function check(result: number, nums: number[]): boolean {
    if (nums.length < 2) return result === nums[0];
    const mul = nums[0] * nums[1];
    const add = nums[0] + nums[1];
    return (mul <= result && check(result, [mul, ...nums.slice(2)])) ||
      (add <= result && check(result, [add, ...nums.slice(2)]));
  }

  return formulas.reduce(
    (acc, cur) => check(...cur) ? acc + cur[0] : acc,
    0,
  );
}export function solve(input: string) {
  const formulas = input.split("\n").map((line) => {
    const r = /(\d+):((?: \d+)+)/g.exec(line);
    return [+r![1], [
      ...r![2].split(" ").filter(Boolean).map(Number),
    ]] as [number, number[]];
  });

  function check(result: number, nums: number[]): boolean {
    if (nums.length < 2) return result === nums[0];
    const mul = nums[0] * nums[1];
    const add = nums[0] + nums[1];
    const concat = +`${nums[0]}${nums[1]}`;
    return (concat <= result && check(result, [concat, ...nums.slice(2)])) ||
      (mul <= result && check(result, [mul, ...nums.slice(2)])) ||
      (add <= result && check(result, [add, ...nums.slice(2)]));
  }

  return formulas.reduce(
    (acc, [result, nums]) => check(result, nums) ? acc + result : acc,
    0,
  );
}
020 2
KiritaniAyaka @ayk.moe · 06/12/2024
Day 6 #AdventOfCode 模擬還是挺有意思的,除了比較難 debug() Part 1 普普通通模擬,按照題意寫就行 Part 2 思路就是障礙只有可能放在 Part 1 中走過的位置,每走一步就假設一下面前就是障礙繼續模擬嘛,如果走出地圖了表明沒有循環。 判斷是否有循環的關鍵是記錄走過的位置和當時的方向,若再次出現同樣的位置和方向則表明當前在循環中了。 走一步模擬一步有個小坑:有可能會把障礙放在以前走過的地方,然而題目裡障礙必須一開始就放置,因此還要判斷障礙位置是否是以前走過的位置
export function solve(input: string) {
  const map = input.split("\n").map((s) => s.trim());
  const directions = [[-1, 0], [0, 1], [1, 0], [0, -1]] as const;
  const visited = Array.from(
    { length: map.length },
    () => Array.from({ length: map[0].length }, () => false),
  );
  let direction = 0;
  let x = map.findIndex((line) => line.includes("^"));
  let y = map[x].indexOf("^");

  function nextPosition(x: number, y: number, direction: number) {
    const [dx, dy] = directions[direction];
    if (map[x + dx]?.[y + dy] === "#") {
      return nextPosition(x, y, (direction + 1) % directions.length);
    }
    return [x + dx, y + dy, direction];
  }

  while (x >= 0 && y >= 0 && x < map.length && y < map[0].length) {
    visited[x][y] = true;
    [x, y, direction] = nextPosition(x, y, direction);
  }
  return visited.flat().filter(Boolean).length;
}export function solve(input: string) {
  const map = input.split("\n").map((s) => s.trim().split(""));
  const directions = [[-1, 0], [0, 1], [1, 0], [0, -1]] as const;
  const visited = new Set<string>();
  let direction = 0;
  let x = map.findIndex((line) => line.includes("^"));
  let y = map[x].indexOf("^");
  let result = 0;
  const inMap = (x: number, y: number) =>
    x >= 0 && y >= 0 && x < map.length && y < map[0].length;

  function nextPosition(x: number, y: number, direction: number) {
    const [dx, dy] = directions[direction];
    if (map[x + dx]?.[y + dy] === "#") {
      return nextPosition(x, y, (direction + 1) % directions.length);
    }
    return [x + dx, y + dy, direction];
  }

  function checkLoop(x: number, y: number, direction: number) {
    const [obstacleX, obstacleY] = nextPosition(x, y, direction);
    if (!inMap(obstacleX, obstacleY)) return false;
    if (visited.has(`${obstacleX},${obstacleY}`)) return false;

    map[obstacleX][obstacleY] = "#";
    const interVisited = new Set<string>(visited);
    while (inMap(x, y)) {
      [x, y, direction] = nextPosition(x, y, direction);
      if (interVisited.has(`${x},${y},${direction}`)) {
        map[obstacleX][obstacleY] = ".";
        return true;
      }
      interVisited.add(`${x},${y},${direction}`);
    }
    map[obstacleX][obstacleY] = ".";
    return false;
  }

  while (inMap(x, y)) {
    visited.add(`${x},${y}`);
    visited.add(`${x},${y},${direction}`);
    if (checkLoop(x, y, direction)) result++;
    [x, y, direction] = nextPosition(x, y, direction);
  }
  return result;
}
000 0
KiritaniAyaka @ayk.moe · 05/12/2024
Day 5 #AdventOfCode (死去的圖論正在攻擊我)今天的題其實就是有向圖 Part 1 給出的規則本質上是有向圖的邊,轉換成圖後按照給出頁碼的順序遍歷圖,能成功遍歷則說明符合規則 Part 2 在給出的頁碼數組中建立子圖,然後拓撲排序(不太會,問了下 AI)
export function solve(input: string) {
  const [inputRules, inputPages] = input.split("\n\n").map((s) => s.trim());
  const graph = new Map<number, number[]>();
  inputRules.split("\n").map((s) => s.trim()).forEach(
    (cur) => {
      const [before, after] = cur.split("|");
      if (!graph.get(+before)) graph.set(+before, []);
      graph.get(+before)!.push(+after);
    },
  );

  function check(arr: number[]) {
    for (let i = 1; i < arr.length; i++) {
      if (!graph.get(arr[i - 1])?.includes(arr[i])) return false;
    }
    return true;
  }

  return inputPages.split("\n").map((s) => s.trim()).reduce((acc, cur) => {
    const arr = cur.split(",").map((s) => +s.trim());
    if (check(arr)) return acc + arr[arr.length >> 1];
    return acc;
  }, 0);
}export function solve(input: string) {
  // Input parsing and `check` function are same as Part 1, omitted here

  function sort(arr: number[]): number[] {
    const subgraph = new Map<number, number[]>();
    const inDegree = new Map<number, number>();
    for (const node of arr) {
      if (graph.has(node)) {
        subgraph.set(node, graph.get(node)!);
        inDegree.set(node, 0);
      }
    }

    for (const [, neighbors] of subgraph.entries()) {
      for (const neighbor of neighbors) {
        if (arr.includes(neighbor)) {
          if (!inDegree.has(neighbor)) inDegree.set(neighbor, 0);
          inDegree.set(neighbor, inDegree.get(neighbor)! + 1);
        }
      }
    }
    const queue: number[] = [];
    for (const [page, degree] of inDegree.entries()) {
      if (degree === 0) queue.push(page);
    }

    const sorted: number[] = [];
    while (queue.length) {
      const node = queue.shift()!;
      sorted.push(node);
      for (const neighbor of graph.get(node) ?? []) {
        inDegree.set(neighbor, inDegree.get(neighbor)! - 1);
        if (inDegree.get(neighbor) === 0) queue.push(neighbor);
      }
    }
    return sorted;
  }

  return inputPages.split("\n").map((s) => s.trim()).reduce((acc, cur) => {
    const arr = cur.split(",").map((s) => +s.trim());
    if (check(arr)) return acc;
    return acc + sort(arr)[arr.length >> 1];
  }, 0);
}
020 2
KiritaniAyaka @ayk.moe · 04/12/2024
Day4 #AdventOfCode Part1 找到所有的 X 的位置,然後以這個為中心找附近八個方向的字符是否是要找的目標字符 Part2 找到所有的 A 的位置,然後找附近四個方向的字符,是否有相對的方向分別是 M 和 S,有兩對的話則算作找到一個
solve((input) => {
  const xPos: [number, number][] = [];
  const arr: string[] = [];
  input
    .split("\n")
    .map((l) => l.trim())
    .forEach((line, idx) => {
      arr.push(line);
      xPos.push(
        ...[...line.matchAll(/X/g)].map(
          (item) => [idx, item.index] as [number, number],
        ),
      );
    });

  const target = "XMAS";
  const directions: [number, number][] = [
    [0, 1],
    [0, -1],
    [1, 0],
    [-1, 0],
    [1, 1],
    [-1, -1],
    [1, -1],
    [-1, 1],
  ];

  return xPos.reduce((acc, [x, y]) => {
    for (const [dx, dy] of directions) {
      if (
        Array.from({ length: target.length }).every(
          (_, i) => arr[x + dx * i]?.[y + dy * i] === target[i],
        )
      ) acc++;
    }
    return acc;
  }, 0);
});solve((input) => {
  const aPos: [number, number][] = [];
  const arr: string[] = [];
  input
    .split("\n")
    .map((l) => l.trim())
    .forEach((line, idx) => {
      arr.push(line);
      aPos.push(
        ...[...line.matchAll(/A/g)].map(
          (item) => [idx, item.index] as [number, number],
        ),
      );
    });

  const directions: [number, number][] = [
    [1, 1],
    [-1, -1],
    [1, -1],
    [-1, 1],
  ];

  return aPos.reduce((acc, [x, y]) => {
    let c = 0;
    for (const [dx, dy] of directions) {
      if (arr[x + dx]?.[y + dy] === "M" && arr[x - dx]?.[y - dy] === "S") {
        c++;
      }
    }
    return c > 1 ? acc + 1 : acc;
  }, 0);
});
020 2