This commit is contained in:
cjdenio committed 2026-09-01 21:56:48 -06:00
1 parent 47003d1fe7
commit b44e039101
4 files changed
+124 -33

No files matched your search

+6 -1
View File
@@ -2,4 +2,9 @@ module github.com/cjdenio/crossword
go 1.25.0 go 1.25.0
require golang.org/x/text v0.41.0 require (
golang.org/x/term v0.45.0
golang.org/x/text v0.41.0
)
require golang.org/x/sys v0.47.0 // indirect
+4
View File
@@ -1,2 +1,6 @@
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
+87 -24
View File
@@ -6,9 +6,9 @@ import (
"os" "os"
"slices" "slices"
"strings" "strings"
"time"
"github.com/cjdenio/crossword/puz" "github.com/cjdenio/crossword/puz"
"golang.org/x/term"
) )
const ( const (
@@ -31,7 +31,7 @@ func RenderPuzzle(puzzle string, width, height int, selectedClueCells []int, sel
for range (width * 2) + 1 { for range (width * 2) + 1 {
b.WriteRune('─') b.WriteRune('─')
} }
b.WriteString("┐\n") b.WriteString("┐\r\n")
for index, char := range puzzle { for index, char := range puzzle {
if index%width == 0 { if index%width == 0 {
@@ -57,7 +57,7 @@ func RenderPuzzle(puzzle string, width, height int, selectedClueCells []int, sel
b.WriteString(cell) b.WriteString(cell)
if (index+1)%width == 0 { if (index+1)%width == 0 {
b.WriteString(" │\n") b.WriteString(" │\r\n")
} else if char == '.' && puzzle[index+1] == '.' { } else if char == '.' && puzzle[index+1] == '.' {
b.WriteRune(0x2588) b.WriteRune(0x2588)
} else { } else {
@@ -74,19 +74,59 @@ func RenderPuzzle(puzzle string, width, height int, selectedClueCells []int, sel
for range (width * 2) + 1 { for range (width * 2) + 1 {
b.WriteRune('─') b.WriteRune('─')
} }
b.WriteString("┘\n") b.WriteString("┘\r\n")
return b.String() return b.String()
} }
func main() { type State struct {
// fmt.Print("\x1b[?1049h") Puzzle *puz.Puzzle
// fmt.Print("\x1b[?25l") SelectedCell int
SelectedClue *puz.Clue
Goodbye bool
}
// defer func() { func RenderUI(state *State) int {
// fmt.Print("\x1b[?1049l") selectedClueCells := []int{}
// fmt.Print("\x1b[?25h") if state.SelectedClue != nil {
// }() selectedClueCells = state.SelectedClue.Cells
}
uiHeight := 0
fmt.Printf("\r\nTITLE: %s\r\n", state.Puzzle.Title)
uiHeight += 2
fmt.Printf("AUTHOR: %s\r\n", state.Puzzle.Author)
uiHeight += 1
fmt.Print(RenderPuzzle(state.Puzzle.State, state.Puzzle.Width, state.Puzzle.Height, selectedClueCells, state.SelectedCell) + "\r\n")
uiHeight += state.Puzzle.Height + 3
if state.SelectedClue != nil {
fmt.Printf("%d: %s\r\n\r\n", state.SelectedClue.Number, state.SelectedClue.Clue)
uiHeight += 2
}
fmt.Printf("%s\r\n", state.Puzzle.Copyright)
uiHeight += 1
if state.Goodbye {
fmt.Print("\r\nsee ya\r\n")
uiHeight += 2
}
return uiHeight
}
func main() {
fmt.Print("\x1b[?25l")
defer func() {
fmt.Print("\x1b[?25h")
}()
termState, err := term.MakeRaw(int(os.Stdin.Fd()))
if err != nil {
log.Fatal(err)
}
defer term.Restore(int(os.Stdin.Fd()), termState)
filename := os.Args[1] filename := os.Args[1]
file, err := os.ReadFile(filename) file, err := os.ReadFile(filename)
@@ -99,24 +139,47 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
fmt.Printf("TITLE: %s\n", puzzle.Title) state := State{
fmt.Printf("AUTHOR: %s\n", puzzle.Author) Puzzle: puzzle,
fmt.Println(RenderPuzzle(puzzle.Solution, puzzle.Width, puzzle.Height, puzzle.Clues[0].Cells, 1)) SelectedClue: puzzle.Clues[0],
fmt.Printf("%s\n", puzzle.Copyright) SelectedCell: puzzle.Clues[0].Cells[0],
}
selectedCell := 1 uiHeight := RenderUI(&state)
for { for {
time.Sleep(500 * time.Millisecond) buffer := make([]byte, 3)
_, err = os.Stdin.Read(buffer)
if err != nil {
log.Fatal(err)
}
fmt.Printf("\x1b[%dA", puzzle.Height+6) if buffer[0] == 3 {
state.SelectedClue = nil
state.SelectedCell = -1
state.Goodbye = true
fmt.Printf("\r\x1b[%dA", uiHeight)
fmt.Print("\x1b[J")
RenderUI(&state)
return
}
switch buffer[2] {
case 68:
state.SelectedCell--
case 67:
state.SelectedCell++
case 65:
state.SelectedCell -= puzzle.Width
case 66:
state.SelectedCell += puzzle.Width
}
state.SelectedClue = state.Puzzle.Cells[state.SelectedCell][0]
fmt.Printf("\r\x1b[%dA", uiHeight)
fmt.Print("\x1b[J") fmt.Print("\x1b[J")
fmt.Printf("TITLE: %s\n", puzzle.Title) uiHeight = RenderUI(&state)
fmt.Printf("AUTHOR: %s\n", puzzle.Author)
fmt.Println(RenderPuzzle(puzzle.Solution, puzzle.Width, puzzle.Height, puzzle.Clues[selectedCell].Cells, 1))
fmt.Printf("%s\n", puzzle.Copyright)
selectedCell++
} }
} }
+27 -8
View File
@@ -38,13 +38,18 @@ type Puzzle struct {
Solution string Solution string
State string State string
Clues []Clue Clues []*Clue
Cells [][2]*Clue
} }
func startOfDownClue(puzzle string, width, index int) bool { func startOfDownClue(puzzle string, width, height, index int) bool {
if puzzle[index] == '.' { if puzzle[index] == '.' {
return false return false
} }
if int(math.Floor(float64(index/width)))+1 == height || puzzle[index+width] == '.' {
return false
}
if index < width { if index < width {
return true return true
} }
@@ -58,6 +63,9 @@ func startOfAcrossClue(puzzle string, width, index int) bool {
if puzzle[index] == '.' { if puzzle[index] == '.' {
return false return false
} }
if (index+1)%width == 0 || puzzle[index+1] == '.' {
return false
}
if index%width == 0 { if index%width == 0 {
return true return true
} }
@@ -137,7 +145,8 @@ func ParsePuz(file []byte) (*Puzzle, error) {
} }
// assign clue numbers // assign clue numbers
puzzle.Clues = make([]Clue, 0, puzzle.ClueCount) puzzle.Clues = make([]*Clue, 0, puzzle.ClueCount)
puzzle.Cells = make([][2]*Clue, len(puzzle.Solution))
clueNumber := 1 clueNumber := 1
clueIndex := 0 clueIndex := 0
@@ -146,23 +155,27 @@ func ParsePuz(file []byte) (*Puzzle, error) {
cellGetsNumber := false cellGetsNumber := false
if startOfAcrossClue(puzzle.Solution, puzzle.Width, i) { if startOfAcrossClue(puzzle.Solution, puzzle.Width, i) {
cellGetsNumber = true cellGetsNumber = true
puzzle.Clues = append(puzzle.Clues, Clue{ clue := Clue{
Cells: []int{i}, Cells: []int{i},
Direction: DirectionAcross, Direction: DirectionAcross,
Number: clueNumber, Number: clueNumber,
Clue: clues[clueIndex], Clue: clues[clueIndex],
}) }
puzzle.Clues = append(puzzle.Clues, &clue)
puzzle.Cells[i][0] = &clue
clueIndex++ clueIndex++
} }
if startOfDownClue(puzzle.Solution, puzzle.Width, i) { if startOfDownClue(puzzle.Solution, puzzle.Width, puzzle.Height, i) {
cellGetsNumber = true cellGetsNumber = true
puzzle.Clues = append(puzzle.Clues, Clue{ clue := Clue{
Cells: []int{i}, Cells: []int{i},
Direction: DirectionDown, Direction: DirectionDown,
Number: clueNumber, Number: clueNumber,
Clue: clues[clueIndex], Clue: clues[clueIndex],
}) }
puzzle.Clues = append(puzzle.Clues, &clue)
puzzle.Cells[i][1] = &clue
clueIndex++ clueIndex++
} }
@@ -181,6 +194,12 @@ func ParsePuz(file []byte) (*Puzzle, error) {
if currentCell != clue.Cells[0] { if currentCell != clue.Cells[0] {
clue.Cells = append(clue.Cells, currentCell) clue.Cells = append(clue.Cells, currentCell)
switch clue.Direction {
case DirectionAcross:
puzzle.Cells[currentCell][0] = clue
case DirectionDown:
puzzle.Cells[currentCell][1] = clue
}
} }
if clue.Direction == DirectionAcross { if clue.Direction == DirectionAcross {