feat: 初始化文字马赛克生成器

This commit is contained in:
necobot
2026-07-25 16:19:26 +08:00
parent 0001f72635
commit c580d069b1
12 changed files with 1654 additions and 3 deletions
+111
View File
@@ -0,0 +1,111 @@
import { describe, expect, it } from 'bun:test'
import {
generateMosaic,
selectKeywords,
type KeywordCandidate,
} from './mosaic'
const SOURCE =
'我一直以为记忆会忠实地保存一切,后来才发现,每一次回想都在重新编造过去。'
function retain(
candidates: KeywordCandidate[],
words: string[],
): KeywordCandidate[] {
return candidates.map((candidate) => ({
...candidate,
selected: words.includes(candidate.text),
}))
}
describe('selectKeywords', () => {
it('优先提供中文实词候选并排除常见停用词', () => {
const candidates = selectKeywords(SOURCE, 14, 1)
const words = candidates.map((candidate) => candidate.text)
expect(words).toContain('记忆')
expect(words).toContain('保存')
expect(words).toContain('回想')
expect(words).toContain('编造')
expect(words).not.toContain('一直')
expect(words).not.toContain('后来')
})
it('在有候选词时至少自动保留一个', () => {
const candidates = selectKeywords('风穿过废弃车站', 5, 1)
expect(candidates.some((candidate) => candidate.selected)).toBe(true)
})
})
describe('generateMosaic', () => {
it('保留指定词语并彻底替换原标点', () => {
const candidates = retain(selectKeywords(SOURCE, 14, 1), [
'记忆',
'保存',
'回想',
'编造',
'过去',
])
const result = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
expect(result.output).toContain('记忆')
expect(result.output).toContain('保存')
expect(result.output).toContain('回想')
expect(result.output).toContain('编造')
expect(result.output).toContain('过去')
expect(result.output).not.toMatch(/[]/u)
})
it('将总可见长度维持在设定体量附近', () => {
const candidates = selectKeywords(SOURCE, 14, 1)
const result = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
const ratio = result.outputLength / result.inputLength
expect(ratio).toBeGreaterThanOrEqual(0.97)
expect(ratio).toBeLessThanOrEqual(1.03)
})
it('相同种子可复现,不同种子会重新侵蚀', () => {
const candidates = selectKeywords(SOURCE, 14, 1)
const first = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
const repeated = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 42,
})
const rerolled = generateMosaic(SOURCE, candidates, {
volume: 100,
disorder: 58,
seed: 43,
})
expect(repeated.output).toBe(first.output)
expect(rerolled.output).not.toBe(first.output)
})
it('保留换行但替换每一行中的隐藏内容', () => {
const source = '记忆,在这里。\n过去,在那里。'
const candidates = retain(selectKeywords(source, 20, 1), ['记忆', '过去'])
const result = generateMosaic(source, candidates, {
volume: 100,
disorder: 30,
seed: 7,
})
expect(result.output.split('\n')).toHaveLength(2)
expect(result.output).not.toMatch(/[]/u)
})
})