okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
8679812
main
1package tui 2 3import ( 4"fmt" 5"time" 6) 7 8// A mood is a class of moment in a turn. Each class owns its own faces, so a 9// face always means something: what albedo is doing, or how a turn ended. 10type mood string 11 12const ( 13moodPreparing mood = "preparing" 14moodThinking mood = "thinking" 15moodResponding mood = "responding" 16moodWorking mood = "working" 17moodCompacting mood = "compacting" 18moodStopping mood = "stopping" 19moodConnecting mood = "connecting" 20 21moodDone mood = "done" 22moodQuick mood = "quick" 23moodLong mood = "long" 24moodTired mood = "tired" 25moodFailed mood = "failed" 26moodStopped mood = "stopped" 27) 28 29// Faces avoid combining marks and right-to-left letters, which terminals 30// disagree on. 31// 32// A phase owns a pool of animations and each turn plays one of them. A face 33// keeps its parentheses in place across frames: only eyes, mouth, and what 34// follows the face move. 35var animations = map [mood ][][]string { 36moodPreparing : {{"(・・ )" ,"( ・・)" }}, 37moodThinking : { 38 {"( ˘ω˘ )" ,"( ˘ω˘ )." ,"( ˘ω˘ ).." ,"( ˘ω˘ )..." }, 39 {"(・ω・ )" ,"( ・ω・)" ,"(・ω・ )?" ,"( ・ω・)?" }, 40 {"(´-ω-`)" ,"(´・ω・`)" ,"(´-ω-`)" ,"(´・ω・`)!" }, 41 }, 42moodResponding : { 43 {"(ˊᗜˋ )" ,"(ˊ-ˋ )" ,"( ˊᗜˋ)" ,"( ˊ-ˋ)" }, 44 {"(˶ᵔ ᗜ ᵔ˶)" ,"(˶ᵔ ᵕ ᵔ˶)" }, 45 {"(・ᗜ・)" ,"(・-・)" ,"(・ᗜ・)ノ" ,"(・-・)ノ" }, 46 }, 47moodWorking : { 48 {"(˶•ᴗ•)カタ" ,"(˶•ᴗ•)カタカタ" ,"(˶•ᴗ•)カタカタカタ" }, 49 {"(・_・ )" ,"( ・_・)" ,"(・ᴗ・ )✧" }, 50 {"(っ•ᴗ•)っ・" ,"(っ•ᴗ•)っ ・" ,"(っ•ᴗ•)っ ・" }, 51 {"ヽ(・ω・)ノ" ,"ヾ(・ω・)ノ" }, 52 {"(๑•ᴗ•)" ,"(๑•ᴗ•)✧" ,"(๑•ᴗ•) ✧" }, 53 }, 54moodCompacting : {{"(>_<)" ,"(>.<)" }}, 55moodStopping : {{"(・_・;)" }}, 56moodConnecting : { 57 {"(・ω・)ノ" ,"(・ω・)ノ ・" ,"(・ω・)ノ ・・" ,"(・ω・)ノ ・・・" }, 58 {"( ˘ω˘ )~" ,"( ˘ω˘ )~~" ,"( ˘ω˘ )~~~" }, 59 {"(っ•ᴗ•)っ" ,"(っ•ᴗ•)っ ・" ,"(っ•ᴗ•)っ ・゚" ,"(っ•ᴗ•)っ ・゚✧" }, 60 }, 61} 62 63// An outcome owns a pool of faces and each turn shows one of them. 64// Additional faces come from https://wikileaks.org/ciav7p1/cms/page_17760284.html. 65var faces = map [mood ][]string { 66moodDone : {"(˶ᵔ ᵕ ᵔ˶)" ,"(๑˃ᴗ˂)" ,"ヽ(・∀・)ノ" ,"(ᵔᴥᵔ)" ,"(っ˘ω˘ς)" ,"(◕‿◕)" ,"(✿◠‿◠)" ,"(o´ω`o)" ,"☻_☻" ,"ᶘ ᵒᴥᵒᶅ" }, 67moodQuick : {"(・ω・)ノ" ,"(`・ω・´)" ,"(^_−)☆" ,"(^▽^)" }, 68moodLong : {"(ง ˃ᴗ˂)ง" ,"(๑˃ᴗ˂)✧" ,"(ˊᗜˋ*)✧" ,"o(≧∀≦)o" ,"(ノ◕ヮ◕)ノ*:・゚✧" }, 69moodTired : {"( ̄ー ̄;)ゞ" ,"(・ω・;)ゞ" ,"( ˘ᴗ˘ )フゥ" ,"(´-ω-`)zzZ" ,"_(:3 」∠)_" ,"(︶ω︶)" ,"(-_- )ノ" ,"(n˘v˘•)¬" }, 70moodFailed : {"(╥﹏╥)" ,"(。•︿•。)" ,"(っ- ‸ – ς)" ,"(ಥ﹏ಥ)" ,"(╥_╥)" ,"☹_☹" ,"(ಠ~ಠ)" ,"(ಡ_ಡ)" ,"(ತಎತ)" ,"(ತ_ತ)" ,"(ಥдಥ)" }, 71moodStopped : {"(・_・;)" ,"(°ロ°)" ,"( ̄□ ̄;)" ,"(ಠ_ಠ)" ,"(°Д°)" }, 72} 73 74// pick chooses from n by seed, salted by the mood so one turn's picks for 75// different moods do not move in step. 76func (m mood )pick (seed int64 ,n int )int { 77return int (fnv1a (uint64 (seed ),string (m ))% uint64 (n )) 78} 79 80// face picks a stable face for seed, so a rebuilt transcript keeps its faces. 81func (m mood )face (seed int64 )string { 82set := faces [m ] 83return set [m .pick (seed ,len (set ))] 84} 85 86// faceInterval is how long each frame of an animation shows. 87const faceInterval = 500 * time .Millisecond 88 89// frame is the turn's animation at tick, one frame per faceInterval. 90func (m mood )frame (seed int64 ,tick int )string { 91pool := animations [m ] 92set := pool [m .pick (seed ,len (pool ))] 93return set [tick % len (set )] 94} 95 96// reachingText is what waiting on a host reads as, by its label: connecting, 97// or copying albedo's kernel over (step staging), which the first visit to a 98// host, and the first after albedo's python changed, waits for. 99func reachingText (label ,step string )string { 100if step == "staging" { 101return "copying the kernel to " + label + "…" 102 } 103return "connecting to " + label + "…" 104} 105 106// connectingFace is the animation for reaching a host, by its label, at 107// tick: the same one for a host wherever it shows. 108func connectingFace (host string ,tick int )string { 109return moodConnecting .frame (int64 (fnv1a (fnvOffset64 ,host )),tick ) 110} 111 112// outcome classes a finished turn by how it ended and how long it took. 113func outcome (failed ,stopped bool ,elapsedMs int64 )mood { 114switch { 115case stopped : 116return moodStopped 117case failed : 118return moodFailed 119case elapsedMs > 10 * 60_000 : 120return moodTired 121case elapsedMs >= 3 * 60_000 : 122return moodLong 123case elapsedMs > 0 && elapsedMs < 4_000 : 124return moodQuick 125 } 126return moodDone 127} 128 129func formatElapsed (ms int64 )string { 130s := ms / 1000 131switch { 132case s < 60 : 133return fmt .Sprintf ("%ds" ,s ) 134case s < 3600 : 135return fmt .Sprintf ("%dm %ds" ,s / 60 ,s % 60 ) 136 } 137return fmt .Sprintf ("%dh %dm" ,s / 3600 ,s / 60 % 60 ) 138} 139 140// formatGap is how long you were away, in the largest whole unit. 141func formatGap (ms int64 )string { 142m := ms / 60_000 143switch { 144case m < 60 : 145return fmt .Sprintf ("%dm later" ,m ) 146case m < 48 * 60 : 147return fmt .Sprintf ("%dh later" ,m / 60 ) 148 } 149return fmt .Sprintf ("%dd later" ,m / (24 * 60 )) 150}