308 lines
8.7 KiB
Go
308 lines
8.7 KiB
Go
package field
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
// META STRUCTURE //
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
|
|
type Properties struct {
|
|
Rows int
|
|
Columns int
|
|
BlockRows int
|
|
BlockColumns int
|
|
BlockSizeRow int
|
|
BlockSizeColumn int
|
|
Rating float64
|
|
}
|
|
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
// FIELD STRUCTURE //
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
|
|
type Field struct {
|
|
props *Properties
|
|
cells [][]Cell
|
|
changes []Change
|
|
}
|
|
|
|
func New(props Properties, cells [][]Cell) *Field {
|
|
field := Field{
|
|
props: &props,
|
|
cells: cells,
|
|
}
|
|
return &field
|
|
}
|
|
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
// GETTER //
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
|
|
func (f *Field) GetRow(r int) (*Row, error) {
|
|
if r >= f.props.Rows || r < 0 {
|
|
return nil, outOfBound
|
|
}
|
|
|
|
cellPtrs := make([]*Cell, len(f.cells[r]))
|
|
for i := range f.cells[r] {
|
|
cellPtrs[i] = &f.cells[r][i]
|
|
}
|
|
|
|
return &Row{
|
|
Line: Line{
|
|
cells: cellPtrs,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (f *Field) GetColumn(c int) (*Column, error) {
|
|
if c >= f.props.Columns || c < 0 {
|
|
return nil, outOfBound
|
|
}
|
|
|
|
// Performance
|
|
result := &Column{
|
|
Line: Line{
|
|
cells: make([]*Cell, 0, f.props.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.props.BlockRows || c < 0 || c >= f.props.BlockColumns {
|
|
return nil, outOfBound
|
|
}
|
|
|
|
startRow := r * f.props.BlockSizeRow
|
|
startCol := c * f.props.BlockSizeColumn
|
|
|
|
block := &Block{
|
|
cells: make([][]*Cell, f.props.BlockSizeRow),
|
|
}
|
|
|
|
for row := range block.cells {
|
|
// Effizientes Kopieren der Zeile
|
|
block.cells[row] = make([]*Cell, f.props.BlockSizeColumn)
|
|
|
|
for column := range block.cells[row] {
|
|
block.cells[row][column] = &f.cells[startRow+row][startCol+column]
|
|
}
|
|
}
|
|
return block, nil
|
|
}
|
|
|
|
func (f *Field) GetEachPartAtPos(pos *Position) []Part {
|
|
row, err := f.GetRow(pos.row)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
column, err := f.GetColumn(pos.column)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
block, err := f.GetBlock(pos.blockRow, pos.blockColumn)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
return append([]Part{}, row, column, block)
|
|
}
|
|
|
|
func (f *Field) GetCell(r, c int) (*Cell, error) {
|
|
if r > f.props.Rows || c > f.props.Columns {
|
|
return nil, outOfBound
|
|
}
|
|
return &f.cells[r][c], nil
|
|
}
|
|
|
|
func (f *Field) GetRating() float64 {
|
|
return f.props.Rating
|
|
}
|
|
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
// FOREACH FUNCTIONS //
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
|
|
func (f *Field) ForEachPart(fn func(part Part)) {
|
|
f.ForEachRow(func(row *Row) {
|
|
fn(row)
|
|
})
|
|
|
|
f.ForEachColumn(func(column *Column) {
|
|
fn(column)
|
|
})
|
|
|
|
f.ForEachBlock(func(block *Block) {
|
|
fn(block)
|
|
})
|
|
}
|
|
|
|
func (f *Field) ForEachPartAtPos(pos *Position, fn func(part Part)) {
|
|
parts := f.GetEachPartAtPos(pos)
|
|
for _, part := range parts {
|
|
fn(part)
|
|
}
|
|
}
|
|
|
|
func (f *Field) ForEachRow(fn func(row *Row)) {
|
|
for i := range f.props.Rows {
|
|
row, err := f.GetRow(i)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
return
|
|
}
|
|
fn(row)
|
|
}
|
|
}
|
|
|
|
func (f *Field) ForEachColumn(fn func(column *Column)) {
|
|
for i := range f.props.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.props.BlockRows {
|
|
for c := range f.props.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(eChange *ExternalChange) {
|
|
change := Change{
|
|
Cell: eChange.Cell,
|
|
marks: eChange.Marks,
|
|
action: eChange.Action,
|
|
value: eChange.Value,
|
|
from: eChange.From,
|
|
triggerdBy: eChange.TriggerdBy,
|
|
}
|
|
|
|
change.do()
|
|
|
|
f.changes = append(f.changes, change)
|
|
}
|
|
|
|
func (f *Field) SetRating(rating float64) {
|
|
f.props.Rating = rating
|
|
}
|
|
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
// STATE //
|
|
// ────────────────────────────────────────────────────────────────────────────── //
|
|
|
|
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) StringNotesForNumber(n int) string {
|
|
return "StringNotesForNumber is not implementet"
|
|
}
|
|
|
|
func (f *Field) StringNotes() string {
|
|
var str strings.Builder
|
|
fmt.Fprintf(&str, "Notes:\n")
|
|
f.ForEachCell(func(cell *Cell) {
|
|
fmt.Fprintf(&str, "Pos: %d/%d - Notes: %v\n", cell.Pos.row, cell.Pos.column, cell.Notes.numbers)
|
|
})
|
|
return str.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 {
|
|
//In jedem Part gibt es jede Zahl max ein mal
|
|
//Die Zahl kommt nicht in intersecting parts vor
|
|
return false
|
|
}
|