mirror of
https://github.com/cjdenio/crossword.git
synced 2026-10-03 11:06:19 +00:00
support ipuz files
This commit is contained in:
7 files changed
+263
-52
No files matched your search
+154
@@ -0,0 +1,154 @@
|
||||
package puz
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type iPuzDimensions struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
}
|
||||
type iPuzClue struct {
|
||||
Number int `json:"number"`
|
||||
Clue string `json:"clue"`
|
||||
}
|
||||
type iPuzFile struct {
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
Copyright string `json:"copyright"`
|
||||
Puzzle [][]any `json:"puzzle"`
|
||||
Solution [][]string `json:"solution"`
|
||||
Dimensions iPuzDimensions `json:"dimensions"`
|
||||
Clues map[string][]iPuzClue `json:"clues"`
|
||||
}
|
||||
|
||||
func ParseIPuz(file []byte) (*Puzzle, error) {
|
||||
var parsed iPuzFile
|
||||
err := json.Unmarshal(file, &parsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
puzzle := new(Puzzle)
|
||||
|
||||
puzzle.Title = parsed.Title
|
||||
puzzle.Author = parsed.Author
|
||||
puzzle.Copyright = parsed.Copyright
|
||||
|
||||
puzzle.Width = parsed.Dimensions.Width
|
||||
puzzle.Height = parsed.Dimensions.Height
|
||||
|
||||
puzzle.ClueCount = len(parsed.Clues["Across"]) + len(parsed.Clues["Down"])
|
||||
|
||||
clueToCellMap := make(map[int][2]int)
|
||||
|
||||
state := strings.Builder{}
|
||||
for y, row := range parsed.Puzzle {
|
||||
for x, cell := range row {
|
||||
switch c := cell.(type) {
|
||||
case string:
|
||||
if c == "#" {
|
||||
state.WriteRune('.')
|
||||
} else {
|
||||
if i, err := strconv.Atoi(c); err == nil {
|
||||
clueToCellMap[i] = [2]int{x, y}
|
||||
}
|
||||
state.WriteRune('-')
|
||||
}
|
||||
case float64:
|
||||
clueToCellMap[int(c)] = [2]int{x, y}
|
||||
state.WriteRune('-')
|
||||
default:
|
||||
state.WriteRune('-')
|
||||
}
|
||||
}
|
||||
}
|
||||
puzzle.State = state.String()
|
||||
|
||||
solution := strings.Builder{}
|
||||
for _, row := range parsed.Solution {
|
||||
for _, cell := range row {
|
||||
if cell == "#" {
|
||||
solution.WriteRune('.')
|
||||
} else if len(cell) > 1 {
|
||||
solution.WriteByte(cell[0])
|
||||
puzzle.HasRebus = true
|
||||
} else if len(cell) == 1 {
|
||||
solution.WriteString(cell)
|
||||
} else {
|
||||
return nil, errors.New("invalid ipuz file")
|
||||
}
|
||||
}
|
||||
}
|
||||
puzzle.Solution = solution.String()
|
||||
|
||||
puzzle.Clues = make([]*Clue, 0, puzzle.ClueCount)
|
||||
puzzle.Cells = make([][2]*Clue, puzzle.Width*puzzle.Height)
|
||||
|
||||
for _, clue := range parsed.Clues["Across"] {
|
||||
cellCoords := clueToCellMap[clue.Number]
|
||||
|
||||
clueCells := make([]int, 0)
|
||||
clueSolution := strings.Builder{}
|
||||
|
||||
clueStruct := &Clue{
|
||||
Clue: clue.Clue,
|
||||
Direction: DirectionAcross,
|
||||
Number: clue.Number,
|
||||
}
|
||||
|
||||
for i := cellCoords[0]; i < puzzle.Width; i++ {
|
||||
cell := parsed.Solution[cellCoords[1]][i]
|
||||
if cell == "#" {
|
||||
break
|
||||
}
|
||||
|
||||
cellIdx := cellCoords[1]*puzzle.Width + i
|
||||
|
||||
clueCells = append(clueCells, cellIdx)
|
||||
puzzle.Cells[cellIdx][0] = clueStruct
|
||||
clueSolution.WriteByte(cell[0])
|
||||
}
|
||||
|
||||
clueStruct.Solution = clueSolution.String()
|
||||
clueStruct.Cells = clueCells
|
||||
|
||||
puzzle.Clues = append(puzzle.Clues, clueStruct)
|
||||
}
|
||||
|
||||
for _, clue := range parsed.Clues["Down"] {
|
||||
cellCoords := clueToCellMap[clue.Number]
|
||||
|
||||
clueCells := make([]int, 0)
|
||||
clueSolution := strings.Builder{}
|
||||
|
||||
clueStruct := &Clue{
|
||||
Clue: clue.Clue,
|
||||
Direction: DirectionDown,
|
||||
Number: clue.Number,
|
||||
}
|
||||
|
||||
for i := cellCoords[1]; i < puzzle.Height; i++ {
|
||||
cell := parsed.Solution[i][cellCoords[0]]
|
||||
if cell == "#" {
|
||||
break
|
||||
}
|
||||
|
||||
cellIdx := i*puzzle.Width + cellCoords[0]
|
||||
|
||||
clueCells = append(clueCells, cellIdx)
|
||||
puzzle.Cells[cellIdx][1] = clueStruct
|
||||
clueSolution.WriteByte(cell[0])
|
||||
}
|
||||
|
||||
clueStruct.Solution = clueSolution.String()
|
||||
clueStruct.Cells = clueCells
|
||||
|
||||
puzzle.Clues = append(puzzle.Clues, clueStruct)
|
||||
}
|
||||
|
||||
return puzzle, nil
|
||||
}
|
||||
-32
@@ -11,38 +11,6 @@ import (
|
||||
"golang.org/x/text/encoding/charmap"
|
||||
)
|
||||
|
||||
type Direction int
|
||||
|
||||
const (
|
||||
DirectionAcross Direction = iota
|
||||
DirectionDown
|
||||
)
|
||||
|
||||
type Clue struct {
|
||||
Clue string
|
||||
Solution string
|
||||
Cells []int
|
||||
Direction Direction
|
||||
Number int
|
||||
}
|
||||
|
||||
type Puzzle struct {
|
||||
ClueCount int
|
||||
Width int
|
||||
Height int
|
||||
|
||||
Title string
|
||||
Author string
|
||||
Copyright string
|
||||
|
||||
Solution string
|
||||
State string
|
||||
|
||||
Clues []*Clue
|
||||
|
||||
Cells [][2]*Clue
|
||||
}
|
||||
|
||||
func startOfDownClue(puzzle string, width, height, index int) bool {
|
||||
if puzzle[index] == '.' {
|
||||
return false
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package puz
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Direction int
|
||||
|
||||
const (
|
||||
DirectionAcross Direction = iota
|
||||
DirectionDown
|
||||
)
|
||||
|
||||
type Clue struct {
|
||||
Clue string
|
||||
Solution string
|
||||
Cells []int
|
||||
Direction Direction
|
||||
Number int
|
||||
}
|
||||
|
||||
type Puzzle struct {
|
||||
ClueCount int
|
||||
Width int
|
||||
Height int
|
||||
|
||||
Title string
|
||||
Author string
|
||||
Copyright string
|
||||
|
||||
Solution string
|
||||
State string
|
||||
|
||||
Clues []*Clue
|
||||
|
||||
Cells [][2]*Clue
|
||||
|
||||
HasRebus bool
|
||||
}
|
||||
|
||||
func LoadPuzzle(r io.Reader) (*Puzzle, error) {
|
||||
bufReader := bufio.NewReader(r)
|
||||
header, err := bufReader.Peek(16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if string(header[2:14]) == "ACROSS&DOWN\x00" {
|
||||
file, err := io.ReadAll(bufReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParsePuz(file)
|
||||
} else if header[0] == '{' {
|
||||
file, err := io.ReadAll(bufReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseIPuz(file)
|
||||
} else {
|
||||
return nil, errors.New("could not detect file type")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user