From b44e03910194b2e909237def4d987addffbf3078 Mon Sep 17 00:00:00 2001 From: Caleb Denio Date: Tue, 1 Sep 2026 21:56:48 -0600 Subject: [PATCH] progress --- go.mod | 7 +++- go.sum | 4 ++ main.go | 111 +++++++++++++++++++++++++++++++++++++++++------------ puz/puz.go | 35 +++++++++++++---- 4 files changed, 124 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 574da03..7394634 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,9 @@ module github.com/cjdenio/crossword 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 diff --git a/go.sum b/go.sum index 5a94f53..7283604 100644 --- a/go.sum +++ b/go.sum @@ -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/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= diff --git a/main.go b/main.go index ff26b57..5739847 100644 --- a/main.go +++ b/main.go @@ -6,9 +6,9 @@ import ( "os" "slices" "strings" - "time" "github.com/cjdenio/crossword/puz" + "golang.org/x/term" ) const ( @@ -31,7 +31,7 @@ func RenderPuzzle(puzzle string, width, height int, selectedClueCells []int, sel for range (width * 2) + 1 { b.WriteRune('─') } - b.WriteString("┐\n") + b.WriteString("┐\r\n") for index, char := range puzzle { if index%width == 0 { @@ -57,7 +57,7 @@ func RenderPuzzle(puzzle string, width, height int, selectedClueCells []int, sel b.WriteString(cell) if (index+1)%width == 0 { - b.WriteString(" │\n") + b.WriteString(" │\r\n") } else if char == '.' && puzzle[index+1] == '.' { b.WriteRune(0x2588) } else { @@ -74,19 +74,59 @@ func RenderPuzzle(puzzle string, width, height int, selectedClueCells []int, sel for range (width * 2) + 1 { b.WriteRune('─') } - b.WriteString("┘\n") + b.WriteString("┘\r\n") return b.String() } -func main() { - // fmt.Print("\x1b[?1049h") - // fmt.Print("\x1b[?25l") +type State struct { + Puzzle *puz.Puzzle + SelectedCell int + SelectedClue *puz.Clue + Goodbye bool +} - // defer func() { - // fmt.Print("\x1b[?1049l") - // fmt.Print("\x1b[?25h") - // }() +func RenderUI(state *State) int { + selectedClueCells := []int{} + 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] file, err := os.ReadFile(filename) @@ -99,24 +139,47 @@ func main() { log.Fatal(err) } - fmt.Printf("TITLE: %s\n", puzzle.Title) - fmt.Printf("AUTHOR: %s\n", puzzle.Author) - fmt.Println(RenderPuzzle(puzzle.Solution, puzzle.Width, puzzle.Height, puzzle.Clues[0].Cells, 1)) - fmt.Printf("%s\n", puzzle.Copyright) + state := State{ + Puzzle: puzzle, + SelectedClue: puzzle.Clues[0], + SelectedCell: puzzle.Clues[0].Cells[0], + } - selectedCell := 1 + uiHeight := RenderUI(&state) 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.Printf("TITLE: %s\n", puzzle.Title) - 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++ + uiHeight = RenderUI(&state) } } diff --git a/puz/puz.go b/puz/puz.go index 3b74426..2cd52da 100644 --- a/puz/puz.go +++ b/puz/puz.go @@ -38,13 +38,18 @@ type Puzzle struct { Solution 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] == '.' { return false } + if int(math.Floor(float64(index/width)))+1 == height || puzzle[index+width] == '.' { + return false + } if index < width { return true } @@ -58,6 +63,9 @@ func startOfAcrossClue(puzzle string, width, index int) bool { if puzzle[index] == '.' { return false } + if (index+1)%width == 0 || puzzle[index+1] == '.' { + return false + } if index%width == 0 { return true } @@ -137,7 +145,8 @@ func ParsePuz(file []byte) (*Puzzle, error) { } // 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 clueIndex := 0 @@ -146,23 +155,27 @@ func ParsePuz(file []byte) (*Puzzle, error) { cellGetsNumber := false if startOfAcrossClue(puzzle.Solution, puzzle.Width, i) { cellGetsNumber = true - puzzle.Clues = append(puzzle.Clues, Clue{ + clue := Clue{ Cells: []int{i}, Direction: DirectionAcross, Number: clueNumber, Clue: clues[clueIndex], - }) + } + puzzle.Clues = append(puzzle.Clues, &clue) + puzzle.Cells[i][0] = &clue clueIndex++ } - if startOfDownClue(puzzle.Solution, puzzle.Width, i) { + if startOfDownClue(puzzle.Solution, puzzle.Width, puzzle.Height, i) { cellGetsNumber = true - puzzle.Clues = append(puzzle.Clues, Clue{ + clue := Clue{ Cells: []int{i}, Direction: DirectionDown, Number: clueNumber, Clue: clues[clueIndex], - }) + } + puzzle.Clues = append(puzzle.Clues, &clue) + puzzle.Cells[i][1] = &clue clueIndex++ } @@ -181,6 +194,12 @@ func ParsePuz(file []byte) (*Puzzle, error) { if currentCell != clue.Cells[0] { 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 {