Initial commit
This commit is contained in:
+100
@@ -0,0 +1,100 @@
|
||||
package board
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// NOTES STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Notes struct {
|
||||
numbers []int
|
||||
}
|
||||
|
||||
func (n *Notes) Add(field *Field, cell *Cell, note int, trigger string, marks []Mark) {
|
||||
for _, existing := range n.numbers {
|
||||
if existing == note {
|
||||
return // Note already exists
|
||||
}
|
||||
}
|
||||
field.changes = append(field.changes, Change{
|
||||
Cell: cell,
|
||||
marks: marks,
|
||||
action: ActionSetNote,
|
||||
value: note,
|
||||
from: 0,
|
||||
triggerdBy: trigger,
|
||||
})
|
||||
n.numbers = append(n.numbers, note)
|
||||
}
|
||||
|
||||
func (n *Notes) Remove(field *Field, cell *Cell, note int, trigger string, marks []Mark) {
|
||||
for i, existing := range n.numbers {
|
||||
if existing == note {
|
||||
field.changes = append(field.changes, Change{
|
||||
Cell: cell,
|
||||
marks: marks,
|
||||
action: ActionRemoveNote,
|
||||
value: 0,
|
||||
from: note,
|
||||
triggerdBy: trigger,
|
||||
})
|
||||
|
||||
n.numbers = append(n.numbers[:i], n.numbers[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// CELL STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Cell struct {
|
||||
number int
|
||||
Notes *Notes
|
||||
Pos *Position
|
||||
}
|
||||
|
||||
func (c *Cell) SetNumber(field *Field, n int, trigger string, marks []Mark) {
|
||||
if c.number == n {
|
||||
return
|
||||
}
|
||||
|
||||
if field == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if n <= 0 || n > field.rows || n > field.columns {
|
||||
return
|
||||
}
|
||||
|
||||
field.changes = append(field.changes, Change{
|
||||
Cell: c,
|
||||
marks: marks,
|
||||
action: ActionSetNumber,
|
||||
value: n,
|
||||
from: c.number,
|
||||
triggerdBy: trigger,
|
||||
})
|
||||
|
||||
c.number = n
|
||||
}
|
||||
|
||||
func (c *Cell) RemoveNumber(field *Field, trigger string, marks []Mark) {
|
||||
if c.number == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if field == nil {
|
||||
return
|
||||
}
|
||||
|
||||
field.changes = append(field.changes, Change{
|
||||
action: ActionSetNumber,
|
||||
Cell: c,
|
||||
value: 0,
|
||||
from: c.number,
|
||||
marks: marks,
|
||||
triggerdBy: trigger,
|
||||
})
|
||||
|
||||
c.number = 0
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package board
|
||||
|
||||
import "image/color"
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// MARK STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Mark struct {
|
||||
Cell *Cell
|
||||
Change *Change
|
||||
color color.Color
|
||||
}
|
||||
|
||||
func (m *Mark) GetColor() color.Color {
|
||||
return m.color
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// CHANGE STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type ChangeAction int
|
||||
|
||||
const (
|
||||
ActionSetNumber = iota
|
||||
ActionSetNote
|
||||
ActionRemoveNumber
|
||||
ActionRemoveNote
|
||||
)
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// CHANGE STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Change struct {
|
||||
Cell *Cell
|
||||
marks []Mark
|
||||
action ChangeAction
|
||||
value int
|
||||
from int
|
||||
triggerdBy string //strategy or manuell
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// GETTER //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
func (c *Change) GetMarks() []Mark {
|
||||
return c.marks
|
||||
}
|
||||
|
||||
func (c *Change) GetAction() ChangeAction {
|
||||
return c.action
|
||||
}
|
||||
|
||||
func (c *Change) GetTo() int {
|
||||
return c.value
|
||||
}
|
||||
|
||||
func (c *Change) GetFrom() int {
|
||||
return c.from
|
||||
}
|
||||
|
||||
func (c *Change) GetTriggeredBy() string {
|
||||
return c.triggerdBy
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// ??? //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
func (c *Change) do() {
|
||||
switch c.action {
|
||||
|
||||
case ActionSetNumber:
|
||||
c.Cell.number = c.value
|
||||
|
||||
case ActionSetNote:
|
||||
c.Cell.Notes.numbers = append(c.Cell.Notes.numbers, c.value)
|
||||
|
||||
case ActionRemoveNumber:
|
||||
c.Cell.number = 0
|
||||
|
||||
case ActionRemoveNote:
|
||||
for i, existing := range c.Cell.Notes.numbers {
|
||||
if existing == c.from {
|
||||
c.Cell.Notes.numbers = append(c.Cell.Notes.numbers[:i], c.Cell.Notes.numbers[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package board
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
outOfBound = errors.New("invalid coordinates")
|
||||
invalidField = errors.New("can not parse Field")
|
||||
)
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// FIELD STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Field struct {
|
||||
rows int
|
||||
columns int
|
||||
blockRows int
|
||||
blockColumns int
|
||||
blockSizeRow int
|
||||
blockSizeColumn int
|
||||
cells [][]Cell
|
||||
changes []Change
|
||||
sha1 []byte
|
||||
rating float64
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// GETTER //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
func (f *Field) GetRow(r int) (*Line, error) {
|
||||
if r >= f.rows || r < 0 {
|
||||
return nil, outOfBound
|
||||
}
|
||||
|
||||
return &Line{cells: append([]Cell(nil), f.cells[r]...)}, nil
|
||||
}
|
||||
|
||||
func (f *Field) GetColumn(c int) (*Line, error) {
|
||||
if c >= f.columns || c < 0 {
|
||||
return nil, outOfBound
|
||||
}
|
||||
|
||||
// Performance
|
||||
result := &Line{
|
||||
cells: make([]Cell, 0, f.rows),
|
||||
}
|
||||
|
||||
// Copying
|
||||
for _, row := range f.cells {
|
||||
result.cells = append(result.cells, row[c])
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (f *Field) GetBlock(r, c int) (*Block, error) {
|
||||
if r < 0 || r >= f.blockRows || c < 0 || c >= f.blockColumns {
|
||||
return nil, outOfBound
|
||||
}
|
||||
|
||||
startRow := r * f.blockSizeRow
|
||||
startCol := c * f.blockSizeColumn
|
||||
|
||||
block := &Block{
|
||||
cells: make([][]Cell, f.blockSizeRow),
|
||||
}
|
||||
|
||||
for row := range block.cells {
|
||||
// Effizientes Kopieren der Zeile
|
||||
block.cells[row] = make([]Cell, f.blockSizeColumn)
|
||||
copy(block.cells[row], f.cells[startRow+row][startCol:startCol+f.blockSizeColumn])
|
||||
}
|
||||
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (f *Field) GetCell(r, c int) (*Cell, error) {
|
||||
if r > f.rows || c > f.columns {
|
||||
return nil, outOfBound
|
||||
}
|
||||
return &f.cells[r][c], nil
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// FOREACH FUNCTIONS //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
func (f *Field) ForEachPart(fn func(part Part)) {
|
||||
f.ForEachRow(func(line *Line) {
|
||||
fn(line)
|
||||
})
|
||||
|
||||
f.ForEachColumn(func(line *Line) {
|
||||
fn(line)
|
||||
})
|
||||
|
||||
f.ForEachBlock(func(block *Block) {
|
||||
fn(block)
|
||||
})
|
||||
}
|
||||
|
||||
func (f *Field) ForEachRow(fn func(line *Line)) {
|
||||
for i := range f.rows {
|
||||
line, err := f.GetRow(i)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fn(line)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Field) ForEachColumn(fn func(line *Line)) {
|
||||
for i := range f.columns {
|
||||
column, err := f.GetColumn(i)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fn(column)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (f *Field) ForEachBlock(fn func(block *Block)) {
|
||||
for r := range f.blockRows {
|
||||
for c := range f.blockColumns {
|
||||
block, err := f.GetBlock(r, c)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fn(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Field) ForEachCell(fn func(cell *Cell)) {
|
||||
for _, row := range f.cells {
|
||||
for _, cell := range row {
|
||||
fn(&cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// MODIFIER //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
func (f *Field) AddChange(cell *Cell, action ChangeAction, value, from int, trigger string, marks []Mark) {
|
||||
change := Change{
|
||||
Cell: cell,
|
||||
marks: marks,
|
||||
action: action,
|
||||
value: value,
|
||||
from: from,
|
||||
triggerdBy: trigger,
|
||||
}
|
||||
|
||||
change.do()
|
||||
|
||||
f.changes = append(f.changes, change)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package board
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Parser interface {
|
||||
parse(data []byte) error
|
||||
getField(i int) *Field
|
||||
getAllFields() []Field
|
||||
}
|
||||
|
||||
type parserHelper struct {
|
||||
fields []Field
|
||||
}
|
||||
|
||||
func (ph *parserHelper) getField(i int) *Field {
|
||||
if i < 0 || i >= len(ph.fields) {
|
||||
return &Field{}
|
||||
}
|
||||
return &ph.fields[i]
|
||||
}
|
||||
|
||||
func (ph *parserHelper) getAllFields() []Field {
|
||||
if len(ph.fields) == 0 {
|
||||
return nil
|
||||
}
|
||||
return ph.fields
|
||||
}
|
||||
|
||||
// https://github.com/grantm/sudoku-exchange-puzzle-bank/tree/master
|
||||
type PuzzleBank struct {
|
||||
parserHelper
|
||||
}
|
||||
|
||||
func (pb *PuzzleBank) parse(data []byte) error {
|
||||
|
||||
lines := bytes.Split(data, []byte("\n"))
|
||||
for _, line := range lines {
|
||||
if len(line) == 0 { // Überspringe leere Zeile
|
||||
continue
|
||||
}
|
||||
|
||||
if len(line) != 99 {
|
||||
return fmt.Errorf("Zeile hat die falsche länge. soll: 100, ist: %d", len(line))
|
||||
}
|
||||
|
||||
//siehe Readme in github repo
|
||||
sha1Hash := bytes.TrimSpace(line[0:12])
|
||||
sudokuStr := string(line[13:94])
|
||||
ratingStr := string(line[96:99])
|
||||
//fmt.Println(sudokuStr)
|
||||
|
||||
var rating float64
|
||||
_, err := fmt.Sscanf(ratingStr, "%f", &rating)
|
||||
if err != nil {
|
||||
return errors.New("can nor parse raiting")
|
||||
}
|
||||
|
||||
field := Field{
|
||||
rows: 9,
|
||||
columns: 9,
|
||||
blockRows: 3,
|
||||
blockColumns: 3,
|
||||
blockSizeRow: 3,
|
||||
blockSizeColumn: 3,
|
||||
rating: rating,
|
||||
sha1: sha1Hash,
|
||||
}
|
||||
|
||||
field.cells = make([][]Cell, field.rows)
|
||||
for i := range field.cells {
|
||||
field.cells[i] = make([]Cell, field.columns)
|
||||
for j := range field.cells[i] {
|
||||
number, err := strconv.Atoi(string(sudokuStr[i*9+j]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Kann Zeichen nicht in Zahl konvertieren")
|
||||
}
|
||||
field.cells[i][j].number = number
|
||||
field.cells[i][j].Pos = &Position{
|
||||
row: i,
|
||||
column: j,
|
||||
blockRow: i / field.blockRows,
|
||||
blockColumn: j / field.blockColumns,
|
||||
inBlockRow: i % field.blockSizeRow,
|
||||
inBlockColumn: j % field.blockSizeColumn,
|
||||
}
|
||||
}
|
||||
}
|
||||
//fmt.Println(field.String())
|
||||
pb.fields = append(pb.fields, field)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package board
|
||||
|
||||
type Part interface {
|
||||
IsSolved() bool
|
||||
ForEachCell(fn func(cell *Cell))
|
||||
GetMissingNumbers() []int
|
||||
RemoveNote(field *Field, note int, trigger string, marks []Mark)
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// LINE STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Line struct {
|
||||
cells []Cell
|
||||
}
|
||||
|
||||
func (l *Line) IsSolved() bool {
|
||||
//TODO
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *Line) ForEachCell(fn func(cell *Cell)) {
|
||||
for _, cell := range l.cells {
|
||||
fn(&cell)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Line) GetMissingNumbers() []int {
|
||||
return getMissingNumbersHelper(len(l.cells), l.ForEachCell)
|
||||
}
|
||||
|
||||
func (l *Line) RemoveNote(field *Field, note int, trigger string, marks []Mark) {
|
||||
l.ForEachCell(func(cell *Cell) {
|
||||
cell.Notes.Remove(field, cell, note, trigger, marks)
|
||||
})
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// BLOCK STRUCTURE //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
type Block struct {
|
||||
cells [][]Cell
|
||||
}
|
||||
|
||||
func (b *Block) IsSolved() bool {
|
||||
//TODO
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Block) ForEachCell(fn func(cell *Cell)) {
|
||||
for _, row := range b.cells {
|
||||
for _, cell := range row {
|
||||
fn(&cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) GetMissingNumbers() []int {
|
||||
max := len(b.cells) * len(b.cells[0])
|
||||
return getMissingNumbersHelper(max, b.ForEachCell)
|
||||
}
|
||||
|
||||
func (b *Block) RemoveNote(field *Field, note int, trigger string, marks []Mark) {
|
||||
b.ForEachCell(func(cell *Cell) {
|
||||
cell.Notes.Remove(field, cell, note, trigger, marks)
|
||||
})
|
||||
}
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
// HELPER //
|
||||
// ────────────────────────────────────────────────────────────────────────────── //
|
||||
|
||||
func getMissingNumbersHelper(max int, iterate func(fn func(cell *Cell))) []int {
|
||||
present := make(map[int]bool, max)
|
||||
|
||||
iterate(func(cell *Cell) {
|
||||
if cell.number != 0 {
|
||||
present[cell.number] = true
|
||||
}
|
||||
})
|
||||
|
||||
var missing []int
|
||||
for i := 1; i <= max; i++ {
|
||||
if !present[i] {
|
||||
missing = append(missing, i)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package board
|
||||
|
||||
type Position struct {
|
||||
row int
|
||||
column int
|
||||
blockRow int
|
||||
blockColumn int
|
||||
inBlockRow int
|
||||
inBlockColumn int
|
||||
}
|
||||
|
||||
func (p *Position) GetRow() int {
|
||||
return p.row
|
||||
}
|
||||
|
||||
func (p *Position) GetColumn() int {
|
||||
return p.column
|
||||
}
|
||||
|
||||
func (p *Position) GetBlockRow() int {
|
||||
return p.blockRow
|
||||
}
|
||||
|
||||
func (p *Position) GetBlockColumn() int {
|
||||
return p.blockColumn
|
||||
}
|
||||
|
||||
func (p *Position) GetInBlockRow() int {
|
||||
return p.inBlockRow
|
||||
}
|
||||
|
||||
func (p *Position) GetInBlockColumn() int {
|
||||
return p.inBlockColumn
|
||||
}
|
||||
|
||||
func (p *Position) GetCoords() (int, int) {
|
||||
return p.row, p.column
|
||||
}
|
||||
|
||||
func (p *Position) GetBlockCoords() (int, int) {
|
||||
return p.blockRow, p.blockColumn
|
||||
}
|
||||
|
||||
func (p *Position) GetInBlockCoords() (int, int) {
|
||||
return p.inBlockRow, p.inBlockColumn
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package board
|
||||
|
||||
import "strings"
|
||||
|
||||
func (f *Field) String() string {
|
||||
//TODO: auf beliebige größen anpassen
|
||||
var sb strings.Builder
|
||||
|
||||
// Oberer Rahmen
|
||||
sb.WriteString("╔═══════╤═══════╤═══════╗\n")
|
||||
|
||||
for i := range f.cells {
|
||||
sb.WriteString("║ ") // Linke Rahmenseite
|
||||
|
||||
for j, cell := range f.cells[i] {
|
||||
// Wert ausgeben oder Punkt für 0
|
||||
val := cell.number
|
||||
if val == 0 {
|
||||
sb.WriteString("·")
|
||||
} else {
|
||||
sb.WriteString(string('0' + val))
|
||||
}
|
||||
|
||||
// Trennlinien zwischen Blöcken und Zellen
|
||||
if (j+1)%3 == 0 {
|
||||
if j < 8 {
|
||||
sb.WriteString(" │ ")
|
||||
} else {
|
||||
sb.WriteString(" ║\n") // Rechte Rahmenseite + Zeilenumbruch
|
||||
}
|
||||
} else {
|
||||
sb.WriteString(" ")
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontale Trennlinien nach jeder 3. Zeile
|
||||
if (i+1)%3 == 0 && i < 8 {
|
||||
sb.WriteString("╟───────┼───────┼───────╢\n")
|
||||
}
|
||||
}
|
||||
|
||||
// Unterer Rahmen
|
||||
sb.WriteString("╚═══════╧═══════╧═══════╝")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (f *Field) IsSolved() bool {
|
||||
result := true
|
||||
f.ForEachCell(func(cell *Cell) {
|
||||
if cell.number == 0 {
|
||||
result = false
|
||||
}
|
||||
})
|
||||
|
||||
/* TODO:
|
||||
if !f.isValid() {
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *Field) IsValid() bool {
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user