summaryrefslogtreecommitdiff
path: root/scripts/p66-target-report.fsx
blob: e360775684058c48c84afaa2ed393fa693642598 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// P66(每日任务目标具体化)证据报告:按 hash 在可达 NPC/瓦片内定点的命中数字 + 同 seed 复现断言。
// 运行:dotnet fsi scripts/p66-target-report.fsx > docs/evidence/p66-analysis.txt
// 前置:已 dotnet build LivingVillage.sln -c Release。
#r "../src/LivingVillage.Kernel/bin/Release/net8.0/LivingVillage.Kernel.dll"
#r "../src/LivingVillage.Desktop/bin/Release/net8.0/LivingVillage.Desktop.dll"

open System
open LivingVillage.Kernel
open LivingVillage.Kernel.Sim
open LivingVillage.Desktop

let seed = 42UL
let world = WorldBootstrap.initialWorldWithOccupation false seed Sim.npcCount None
let reachable = WorldBootstrap.reachableTargets world

let npcLabel (NpcId id) = string id
let tileLabel (x, y) = sprintf "(%d,%d)" x y

let stateAt (kind: Occupation.Kind) (day: int64) =
    WorldBootstrap.occupationStateForWorld seed kind (day * Sim.ticksPerDay + 10L) world

printfn "P66 每日任务目标具体化 — 证据报告"
printfn "seed=%d npcs=%d reachable_npcs=%d reachable_tiles=%d (%s)"
    (int seed) world.Npcs.Length reachable.Npcs.Length reachable.Tiles.Length
    (reachable.Tiles |> List.map tileLabel |> String.concat ",")
printfn ""

let mutable totalTasks = 0
let mutable npcTargeted = 0
let mutable tileTargeted = 0
let mutable wildcard = 0
let mutable membershipOk = true
let mutable reproduceOk = true
let mutable concreteSamples = 0

for kind in Occupation.all do
    let mutable hits = 0
    for day in 0L .. 59L do
        let state = stateAt kind day
        match state.Today with
        | None -> ()
        | Some task ->
            totalTasks <- totalTasks + 1
            let again = (stateAt kind day).Today.Value
            if again <> task then reproduceOk <- false
            match task.TemplateId with
            | Occupation.HelpWork ->
                npcTargeted <- npcTargeted + 1
                hits <- hits + 1
                match task.TargetNpc with
                | Some npc when List.contains npc reachable.Npcs ->
                    concreteSamples <- concreteSamples + 1
                    if concreteSamples <= 12 then
                        printfn "  %-4s day=%2d HelpWork TargetNpc=%s" (Occupation.saveToken kind) day (npcLabel npc)
                | _ -> membershipOk <- false
            | Occupation.TillSoil ->
                tileTargeted <- tileTargeted + 1
                hits <- hits + 1
                match task.TargetTile with
                | Some tile when List.contains tile reachable.Tiles ->
                    concreteSamples <- concreteSamples + 1
                    if concreteSamples <= 12 then
                        printfn "  %-4s day=%2d TillSoil TargetTile=%s" (Occupation.saveToken kind) day (tileLabel tile)
                | _ -> membershipOk <- false
            | _ ->
                wildcard <- wildcard + 1
                if task.TargetNpc.IsSome || task.TargetTile.IsSome then membershipOk <- false
    printfn "  %-8s 60 天内定点模板命中=%d" (Occupation.nameOf kind) hits

printfn ""
printfn "统计(4 职业 × 60 天 = %d 条任务)" totalTasks
printfn "  定点 NPC(帮工)  = %d" npcTargeted
printfn "  定点瓦片(翻土)  = %d" tileTargeted
printfn "  保持通配(其余)  = %d" wildcard
printfn "  目标 ∈ 可达集合   = %b" membershipOk
printfn "  同输入两次逐字节一致 = %b" reproduceOk
printfn ""

// 空候选 → None(保留历史通配口径)。
let legacyNone =
    (Occupation.dailyTaskOf seed (Occupation.occupationSeedOf Occupation.Farmer) 3L Occupation.Farmer).Value
printfn "历史口径 dailyTaskOf(空候选):TargetNpc=%b TargetTile=%b(应全 false)"
    legacyNone.TargetNpc.IsSome legacyNone.TargetTile.IsSome

let verdict = if membershipOk && reproduceOk then "PASS" else "FAIL"
printfn ""
printfn "verdict: %s" verdict