2024-02-06 01:41:20 +00:00
|
|
|
// uckf creates a deeply unsatisfying word search.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
2024-02-06 02:10:29 +00:00
|
|
|
"log"
|
2024-02-06 02:21:52 +00:00
|
|
|
"math/rand"
|
2024-02-06 01:41:20 +00:00
|
|
|
)
|
|
|
|
|
2024-02-06 03:48:45 +00:00
|
|
|
var (
|
|
|
|
edgeSize = flag.Int("n", 15, "Cells per side")
|
|
|
|
summarize = flag.Bool("s", false, "Summarize when iterating")
|
|
|
|
showResult = flag.Bool("r", true, "Show final result (default true)")
|
|
|
|
)
|
|
|
|
|
2024-02-06 01:41:20 +00:00
|
|
|
|
|
|
|
type scanState int
|
|
|
|
const (
|
|
|
|
FuckIfIKnow scanState = iota
|
|
|
|
Fucked
|
|
|
|
NotFucked
|
|
|
|
)
|
|
|
|
|
|
|
|
type fuckness [3][3]scanState
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
flag.Parse()
|
2024-02-06 02:10:29 +00:00
|
|
|
if (*edgeSize < 4) {
|
|
|
|
log.Fatalf("can't fit a fuck into edge size %d", *edgeSize)
|
|
|
|
}
|
2024-02-06 01:41:20 +00:00
|
|
|
var board [][]byte
|
|
|
|
for i := 0; i < *edgeSize; i++ {
|
|
|
|
fuckingRow := make([]byte, *edgeSize)
|
|
|
|
for j := 0; j < *edgeSize; j++ {
|
2024-02-06 02:21:52 +00:00
|
|
|
fuckingRow[j] = byte((i+j)%4)
|
2024-02-06 01:41:20 +00:00
|
|
|
}
|
|
|
|
board=append(board, fuckingRow)
|
|
|
|
}
|
|
|
|
|
2024-02-06 02:21:52 +00:00
|
|
|
var evals [][]fuckness
|
2024-02-06 01:41:20 +00:00
|
|
|
for i := 0; i < *edgeSize; i++ {
|
|
|
|
evals = append(evals, make([]fuckness, *edgeSize))
|
|
|
|
}
|
|
|
|
|
|
|
|
iterations := uint64(0)
|
2024-02-06 02:48:34 +00:00
|
|
|
scream := uint64(0)
|
2024-02-06 01:41:20 +00:00
|
|
|
for(isStillFucked(board, evals)) {
|
2024-02-06 02:21:52 +00:00
|
|
|
if iterations >= scream {
|
2024-02-06 03:48:45 +00:00
|
|
|
if (*summarize) {
|
|
|
|
emitSummary(evals, iterations)
|
|
|
|
} else {
|
|
|
|
dump(board, evals, fmt.Sprint("Iteration ", iterations))
|
|
|
|
}
|
2024-02-06 02:21:52 +00:00
|
|
|
scream = iterations << 1
|
2024-02-06 01:41:20 +00:00
|
|
|
}
|
2024-02-06 02:45:25 +00:00
|
|
|
iterations++
|
|
|
|
fuckUp(board, evals)
|
2024-02-06 01:41:20 +00:00
|
|
|
}
|
|
|
|
|
2024-02-06 03:48:45 +00:00
|
|
|
if (*showResult) {
|
|
|
|
dump(board, evals, fmt.Sprintf("Not a single fuck (%d iterations)", iterations))
|
|
|
|
} else {
|
|
|
|
emitSummary(evals, iterations)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func emitSummary(evals [][]fuckness, iterations uint64) {
|
|
|
|
nFucked := uint64(0)
|
|
|
|
for _, row := range evals {
|
|
|
|
for _, e := range row {
|
|
|
|
if isFucked(e) {
|
|
|
|
nFucked++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Printf("Iteration %d: %d fucked (%.2f%%)\n", iterations, nFucked, 100 * float64(nFucked)/(float64(*edgeSize * *edgeSize)))
|
2024-02-06 01:41:20 +00:00
|
|
|
}
|
|
|
|
|
2024-02-06 02:21:52 +00:00
|
|
|
func onBoard(i, j int) bool {
|
2024-02-06 02:40:19 +00:00
|
|
|
return i >= 0 && j >= 0 && i < *edgeSize && j < *edgeSize
|
2024-02-06 02:10:29 +00:00
|
|
|
}
|
2024-02-06 01:41:20 +00:00
|
|
|
func charAt(board [][]byte, i, j int) byte {
|
2024-02-06 02:10:29 +00:00
|
|
|
if !onBoard(i, j) {
|
2024-02-06 01:41:20 +00:00
|
|
|
return 5
|
|
|
|
}
|
|
|
|
return board[i][j]
|
|
|
|
}
|
|
|
|
|
2024-02-06 02:21:52 +00:00
|
|
|
func isStillFucked(board [][]byte, evals[][]fuckness) bool {
|
2024-02-06 01:41:20 +00:00
|
|
|
isFucked := false
|
|
|
|
for i, row := range board {
|
|
|
|
for j, c := range row {
|
|
|
|
for dr := -1; dr <= 1; dr++ {
|
|
|
|
for dc := -1; dc <= 1; dc++ {
|
|
|
|
if dr == 0 && dc == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2024-02-06 02:21:52 +00:00
|
|
|
if evals[i][j][dr+1][dc+1] == Fucked {
|
|
|
|
isFucked = true
|
|
|
|
continue
|
|
|
|
}
|
2024-02-06 01:41:20 +00:00
|
|
|
if evals[i][j][dr+1][dc+1] != FuckIfIKnow {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// find theoretical start of fuck
|
2024-02-06 02:21:52 +00:00
|
|
|
fRow, fCol := i + int(c)*-dr, j + int(c)*-dc
|
2024-02-06 01:41:20 +00:00
|
|
|
// evaluate fuckness
|
|
|
|
locallyFucked := true
|
|
|
|
for w := 0; w < 4; w++ {
|
2024-02-06 02:21:52 +00:00
|
|
|
if charAt(board, fRow+w*dr, fCol+w*dc) != byte(w) {
|
2024-02-06 01:41:20 +00:00
|
|
|
locallyFucked = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if locallyFucked {
|
|
|
|
// mark every letter involved as fucked
|
|
|
|
isFucked = true
|
|
|
|
for w := 0; w < 4; w++ {
|
2024-02-06 02:30:08 +00:00
|
|
|
evals[fRow+w*dr][fCol+w*dc][dr+1][dc+1] = Fucked
|
2024-02-06 01:41:20 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
evals[i][j][dr+1][dc+1] = NotFucked
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return isFucked
|
|
|
|
}
|
|
|
|
|
2024-02-06 01:46:09 +00:00
|
|
|
func isFucked(f fuckness) bool {
|
|
|
|
for i := 0; i <3; i++ {
|
|
|
|
for j := 0; j < 3; j++ {
|
|
|
|
if i == 1 && j == 1 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if f[i][j] == Fucked {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func mightBeFucked(f fuckness) bool {
|
|
|
|
for i := 0; i <3; i++ {
|
|
|
|
for j := 0; j < 3; j++ {
|
|
|
|
if i == 1 && j == 1 {
|
|
|
|
continue
|
|
|
|
}
|
2024-02-06 02:41:32 +00:00
|
|
|
if f[i][j] != NotFucked {
|
2024-02-06 01:46:09 +00:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-02-06 01:41:20 +00:00
|
|
|
func fuckUp(board [][]byte, evals [][]fuckness) {
|
2024-02-06 01:46:09 +00:00
|
|
|
var todo [][2]int
|
|
|
|
for i, row := range evals {
|
|
|
|
for j, e := range row {
|
2024-02-06 02:10:29 +00:00
|
|
|
if isFucked(e) {
|
|
|
|
todo = append(todo, [2]int{i, j})
|
|
|
|
} else if mightBeFucked(e) {
|
2024-02-06 02:45:25 +00:00
|
|
|
dump(board, evals, "Oh fuck")
|
2024-02-06 02:10:29 +00:00
|
|
|
log.Fatalf("unevaluated fuckness at %d, %d: %v", i, j, e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rand.Shuffle(len(todo), func(i, j int) {
|
|
|
|
todo[j], todo[i] = todo[i], todo[j]
|
|
|
|
})
|
|
|
|
|
|
|
|
// note: "head" moves frequently within this loop
|
|
|
|
for head := 0; head < len(todo); head++ {
|
|
|
|
item := todo[head]
|
|
|
|
i, j := item[0], item[1]
|
|
|
|
if(!isFucked(evals[i][j])) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
i2, j2 := -1, -1
|
2024-02-06 02:21:52 +00:00
|
|
|
for head++; head < len(todo); head++ {
|
|
|
|
item2 := todo[head]
|
2024-02-06 02:10:29 +00:00
|
|
|
itmp, jtmp := item2[0], item2[1]
|
|
|
|
if(isFucked(evals[itmp][jtmp])) {
|
|
|
|
i2, j2 = itmp, jtmp
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if i2 == -1 {
|
|
|
|
// fuck it, swap randomly
|
|
|
|
i2 = rand.Intn(*edgeSize)
|
|
|
|
j2 = rand.Intn(*edgeSize)
|
|
|
|
}
|
|
|
|
if board[i][j] != board[i2][j2] {
|
|
|
|
unevaluate(evals, i, j)
|
|
|
|
unevaluate(evals, i2, j2)
|
|
|
|
board[i][j], board[i2][j2] = board[i2][j2], board[i][j]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-02-06 01:46:09 +00:00
|
|
|
|
2024-02-06 02:21:52 +00:00
|
|
|
func unevaluate(evals [][]fuckness, i, j int) {
|
2024-02-06 02:10:29 +00:00
|
|
|
for dr := -1; dr <= 1; dr++ {
|
|
|
|
for dc := -1; dc <= 1; dc++ {
|
|
|
|
for x := -3; x < 4; x++ {
|
|
|
|
r := i + dr * x
|
|
|
|
c := j + dc * x
|
|
|
|
if onBoard(r, c) {
|
|
|
|
evals[r][c][dr+1][dc+1] = FuckIfIKnow
|
|
|
|
}
|
|
|
|
}
|
2024-02-06 01:46:09 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-06 01:41:20 +00:00
|
|
|
}
|
|
|
|
|
2024-02-06 02:45:25 +00:00
|
|
|
func dump(board [][]byte, evals [][]fuckness, title string) {
|
2024-02-06 01:41:20 +00:00
|
|
|
fmt.Println()
|
|
|
|
for i := 0; i < *edgeSize; i++ {
|
|
|
|
fmt.Print("--")
|
|
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
fmt.Println(title)
|
|
|
|
fmt.Println()
|
2024-02-06 02:45:25 +00:00
|
|
|
for i, row := range board {
|
|
|
|
for j, r := range row {
|
|
|
|
if isFucked(evals[i][j]) {
|
|
|
|
fmt.Printf("%c ", "FUCK"[r])
|
|
|
|
} else {
|
|
|
|
fmt.Printf("%c ", "fuck"[r])
|
|
|
|
}
|
2024-02-06 01:41:20 +00:00
|
|
|
}
|
|
|
|
fmt.Println()
|
|
|
|
}
|
|
|
|
}
|