uckf/uckf.go

212 lines
4.1 KiB
Go

// uckf creates a deeply unsatisfying word search.
package main
import (
"flag"
"fmt"
"log"
"math/rand"
)
var edgeSize = flag.Int("n", 4, "Cells per side")
type scanState int
const (
FuckIfIKnow scanState = iota
Fucked
NotFucked
)
type fuckness [3][3]scanState
func main() {
flag.Parse()
if (*edgeSize < 4) {
log.Fatalf("can't fit a fuck into edge size %d", *edgeSize)
}
var board [][]byte
for i := 0; i < *edgeSize; i++ {
fuckingRow := make([]byte, *edgeSize)
for j := 0; j < *edgeSize; j++ {
fuckingRow[j] = byte((i+j)%4)
}
board=append(board, fuckingRow)
}
dump(board, "Starting position")
var evals [][]fuckness
for i := 0; i < *edgeSize; i++ {
evals = append(evals, make([]fuckness, *edgeSize))
}
iterations := uint64(0)
scream := uint64(1)
for(isStillFucked(board, evals)) {
fuckUp(board, evals)
iterations++
if iterations >= scream {
dump(board, fmt.Sprint("Iteration", iterations))
scream = iterations << 1
}
}
dump(board, "Not a single fuck")
}
func onBoard(i, j int) bool {
return i > 0 && j > 0 && i < *edgeSize && j < *edgeSize
}
func charAt(board [][]byte, i, j int) byte {
if !onBoard(i, j) {
return 5
}
return board[i][j]
}
func isStillFucked(board [][]byte, evals[][]fuckness) bool {
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
}
if evals[i][j][dr+1][dc+1] == Fucked {
isFucked = true
continue
}
if evals[i][j][dr+1][dc+1] != FuckIfIKnow {
continue
}
// find theoretical start of fuck
fRow, fCol := i + int(c)*-dr, j + int(c)*-dc
// evaluate fuckness
locallyFucked := true
for w := 0; w < 4; w++ {
if charAt(board, fRow+w*dr, fCol+w*dc) != byte(w) {
locallyFucked = false
break
}
}
if locallyFucked {
// mark every letter involved as fucked
isFucked = true
for w := 0; w < 4; w++ {
evals[i+w*dr][j+w*dc][dr+1][dc+1] = Fucked
}
} else {
evals[i][j][dr+1][dc+1] = NotFucked
}
}
}
}
}
return isFucked
}
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
}
if f[i][j] == NotFucked {
return true
}
}
}
return false
}
func fuckUp(board [][]byte, evals [][]fuckness) {
var todo [][2]int
for i, row := range evals {
for j, e := range row {
if isFucked(e) {
todo = append(todo, [2]int{i, j})
} else if mightBeFucked(e) {
dump(board, "Oh fuck")
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
for head++; head < len(todo); head++ {
item2 := todo[head]
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]
}
}
}
func unevaluate(evals [][]fuckness, i, j int) {
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
}
}
}
}
}
func dump(board [][]byte, title string) {
fmt.Println()
for i := 0; i < *edgeSize; i++ {
fmt.Print("--")
}
fmt.Println()
fmt.Println(title)
fmt.Println()
for _, row := range board {
for _, r := range row {
fmt.Printf("%c ", "fuck"[r])
}
fmt.Println()
}
}