uckf/uckf.go

239 lines
4.8 KiB
Go
Raw Permalink Normal View History

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
)
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 {
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
}
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()
}
}