Kistaro Windrider
691c59462a
fun for seeing just how well it behaves on very large boards! it turns out printing the board is the slow part
239 lines
4.8 KiB
Go
239 lines
4.8 KiB
Go
// uckf creates a deeply unsatisfying word search.
|
|
|
|
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
)
|
|
|
|
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)")
|
|
)
|
|
|
|
|
|
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)
|
|
}
|
|
|
|
var evals [][]fuckness
|
|
for i := 0; i < *edgeSize; i++ {
|
|
evals = append(evals, make([]fuckness, *edgeSize))
|
|
}
|
|
|
|
iterations := uint64(0)
|
|
scream := uint64(0)
|
|
for(isStillFucked(board, evals)) {
|
|
if iterations >= scream {
|
|
if (*summarize) {
|
|
emitSummary(evals, iterations)
|
|
} else {
|
|
dump(board, evals, fmt.Sprint("Iteration ", iterations))
|
|
}
|
|
scream = iterations << 1
|
|
}
|
|
iterations++
|
|
fuckUp(board, evals)
|
|
}
|
|
|
|
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)))
|
|
}
|
|
|
|
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[fRow+w*dr][fCol+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, evals, "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, evals [][]fuckness, title string) {
|
|
fmt.Println()
|
|
for i := 0; i < *edgeSize; i++ {
|
|
fmt.Print("--")
|
|
}
|
|
fmt.Println()
|
|
fmt.Println(title)
|
|
fmt.Println()
|
|
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])
|
|
}
|
|
}
|
|
fmt.Println()
|
|
}
|
|
} |