Refactor Sudoku field and solver implementation
This commit is contained in:
+37
-44
@@ -2,73 +2,66 @@ package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.kleiax.de/homepage/field"
|
||||
)
|
||||
|
||||
// https://github.com/grantm/sudoku-exchange-puzzle-bank/tree/master
|
||||
type PuzzleBank struct {
|
||||
ParserHelper
|
||||
}
|
||||
|
||||
func (pb *PuzzleBank) Parse(data []byte) error {
|
||||
if pb == nil {
|
||||
return errors.New("nil puzzle bank parser")
|
||||
}
|
||||
|
||||
lines := bytes.Split(data, []byte("\n"))
|
||||
for _, line := range lines {
|
||||
if len(line) == 0 { // Überspringe leere Zeile
|
||||
var parsed []*field.Field
|
||||
for index, rawLine := range bytes.Split(data, []byte("\n")) {
|
||||
lineNumber := index + 1
|
||||
line := strings.TrimSpace(string(rawLine))
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(line) != 99 {
|
||||
return fmt.Errorf("Zeile hat die falsche länge. soll: 100, ist: %d", len(line))
|
||||
columns := strings.Fields(line)
|
||||
if len(columns) != 3 {
|
||||
return fmt.Errorf("line %d: %w: expected hash, puzzle and rating", lineNumber, ErrInvalidInput)
|
||||
}
|
||||
if len(columns[0]) != 12 {
|
||||
return fmt.Errorf("line %d: %w: hash has %d characters, expected 12", lineNumber, ErrInvalidInput, len(columns[0]))
|
||||
}
|
||||
if _, err := hex.DecodeString(columns[0]); err != nil {
|
||||
return fmt.Errorf("line %d: %w: invalid hash: %v", lineNumber, ErrInvalidInput, err)
|
||||
}
|
||||
digest := sha1.Sum([]byte(columns[1]))
|
||||
if columns[0] != hex.EncodeToString(digest[:])[:12] {
|
||||
return fmt.Errorf("line %d: %w: hash does not match puzzle", lineNumber, ErrInvalidInput)
|
||||
}
|
||||
|
||||
//siehe Readme in github repo
|
||||
//sha1Hash := bytes.TrimSpace(line[0:12]) // Wird aktuell nicht gebraucht, später zum sudoku vergleichen
|
||||
sudokuStr := string(line[13:94])
|
||||
ratingStr := string(line[96:99])
|
||||
//fmt.Println(sudokuStr)
|
||||
|
||||
var rating float64
|
||||
_, err := fmt.Sscanf(ratingStr, "%f", &rating)
|
||||
rating, err := strconv.ParseFloat(columns[2], 64)
|
||||
if err != nil {
|
||||
return errors.New("can nor parse raiting")
|
||||
return fmt.Errorf("line %d: %w: invalid rating %q: %v", lineNumber, ErrInvalidInput, columns[2], err)
|
||||
}
|
||||
|
||||
props := field.Properties{
|
||||
Rows: 9,
|
||||
Columns: 9,
|
||||
BlockRows: 3,
|
||||
BlockColumns: 3,
|
||||
BlockSizeRow: 3,
|
||||
BlockSizeColumn: 3,
|
||||
Rating: rating,
|
||||
if rating < 0 || math.IsNaN(rating) || math.IsInf(rating, 0) {
|
||||
return fmt.Errorf("line %d: %w: rating must be a finite non-negative number", lineNumber, ErrInvalidInput)
|
||||
}
|
||||
|
||||
cells := make([][]field.Cell, props.Rows)
|
||||
for i := range cells {
|
||||
cells[i] = make([]field.Cell, props.Columns)
|
||||
for j := range cells[i] {
|
||||
number, err := strconv.Atoi(string(sudokuStr[i*9+j]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Kann Zeichen nicht in Zahl konvertieren")
|
||||
}
|
||||
pos := field.NewPosition(
|
||||
i,
|
||||
j,
|
||||
i/props.BlockRows,
|
||||
j/props.BlockColumns,
|
||||
i%props.BlockRows,
|
||||
j%props.BlockColumns)
|
||||
|
||||
cells[i][j] = *field.NewCell(number, pos)
|
||||
}
|
||||
parsedField, err := classicField(columns[1], rating)
|
||||
if err != nil {
|
||||
return fmt.Errorf("line %d: %w", lineNumber, err)
|
||||
}
|
||||
pb.fields = append(pb.fields, *field.New(props, cells))
|
||||
parsed = append(parsed, parsedField)
|
||||
}
|
||||
|
||||
if len(parsed) == 0 {
|
||||
return ErrNoPuzzles
|
||||
}
|
||||
pb.fields = parsed
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user