Created
February 25, 2021 14:15
-
-
Save majelbstoat/f29ccece5088060c0177bd5db0068d71 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This code scans a text file and performs some basic manipulations on its contents to unearth amulets, | |
as defined by Robin Sloan. | |
Learn more about them at https://text.bargains | |
USAGE: go run . | |
Expects a filename called shakespeare.txt in the same directory. | |
I used a version from the Gutenberg Project, with gratitude: | |
https://ocw.mit.edu/ans7870/6/6.006/s08/lecturenotes/files/t8.shakespeare.txt | |
Example results here: https://twitter.com/majelbstoat/status/1364936594445189122 | |
Yes, you could make it more efficient, or do other text manipulations! Why not try? :) | |
Released under CC0 https://creativecommons.org/publicdomain/zero/1.0/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"crypto/sha256" | |
"fmt" | |
"log" | |
"math" | |
"os" | |
"strings" | |
) | |
var results int | |
var longest int | |
var best map[int][]string | |
var cache map[string]bool | |
func main() { | |
// return | |
file, err := os.Open("./shakespeare.txt") | |
if err != nil { | |
log.Fatal(err) | |
} | |
defer file.Close() | |
cache = make(map[string]bool) | |
best = make(map[int][]string) | |
for i := 4; i <= 10; i++ { | |
best[i] = make([]string, 0) | |
} | |
scanner := bufio.NewScanner(file) | |
for scanner.Scan() { | |
line := scanner.Text() | |
checkCandidate(line, "RAW") | |
line = strings.Trim(line, " ") | |
checkCandidate(line, "TRIMMED") | |
for _, punc := range []string{",", ".", ";", "-"} { | |
l := strings.ReplaceAll(line, punc, "") | |
if l != line { | |
checkCandidate(line, "DEPUNC") | |
line = l | |
} | |
} | |
words := strings.Split(line, " ") | |
spaces := len(words) - 1 | |
if spaces > 4 { | |
// cap the max permutations to 16 so it finishes in a reasonable time. | |
continue | |
} | |
permutations := int(math.Pow(2, float64(spaces))) | |
for i := 0; i < permutations; i++ { | |
s := "" | |
for j := 0; j < spaces; j++ { | |
o := " " | |
if hasBit(i, uint(j)) { | |
o = "\n" | |
} | |
s = fmt.Sprintf("%s%s%s", s, words[j], o) | |
} | |
s = fmt.Sprintf("%s%s", s, words[spaces]) | |
checkCandidate(s, fmt.Sprintf("LINED %d", i)) | |
} | |
} | |
if err := scanner.Err(); err != nil { | |
log.Fatal(err) | |
} | |
fmt.Printf("\nFOUND %d amulets\n", results) | |
for l, b := range best { | |
if len(best[l]) > 0 { | |
fmt.Printf("%d:\n\n", l) | |
} | |
for _, o := range b { | |
fmt.Printf("%s", o) | |
} | |
fmt.Printf("\n") | |
} | |
} | |
func checkCandidate(line, munging string) { | |
h := sha256.New() | |
h.Write([]byte(line)) | |
candidate := fmt.Sprintf("%x", h.Sum(nil)) | |
if strings.Contains(candidate, "8888") { | |
if _, ok := cache[line]; ok { | |
return | |
} | |
cache[line] = true | |
max := 0 | |
consecutive := 0 | |
for _, c := range candidate { | |
if c == '8' { | |
consecutive++ | |
} else { | |
if consecutive > max { | |
max = consecutive | |
} | |
consecutive = 0 | |
} | |
} | |
if consecutive > max { | |
max = consecutive | |
} | |
if max > longest { | |
longest = max | |
} | |
output := fmt.Sprintf("%s: (%8s) \"%s\"\n", candidate, munging, line) | |
best[max] = append(best[max], output) | |
results++ | |
} | |
} | |
func hasBit(n int, pos uint) bool { | |
val := n & (1 << pos) | |
return (val > 0) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment