(Feat): Initial Commit, Termdoku
This commit is contained in:
220
internal/generator/benchmark.go
Normal file
220
internal/generator/benchmark.go
Normal file
@@ -0,0 +1,220 @@
|
||||
package generator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BenchmarkResult contains statistics from puzzle generation benchmarking.
|
||||
type BenchmarkResult struct {
|
||||
Difficulty Difficulty
|
||||
TotalAttempts int
|
||||
SuccessfulPuzzles int
|
||||
FailedPuzzles int
|
||||
AverageTime time.Duration
|
||||
MinTime time.Duration
|
||||
MaxTime time.Duration
|
||||
AverageRating float64
|
||||
Ratings []int
|
||||
}
|
||||
|
||||
// BenchmarkGeneration tests puzzle generation performance for a given difficulty.
|
||||
func BenchmarkGeneration(d Difficulty, attempts int) BenchmarkResult {
|
||||
result := BenchmarkResult{
|
||||
Difficulty: d,
|
||||
TotalAttempts: attempts,
|
||||
MinTime: time.Hour, // Start with a large value
|
||||
Ratings: make([]int, 0, attempts),
|
||||
}
|
||||
|
||||
var totalDuration time.Duration
|
||||
|
||||
for i := 0; i < attempts; i++ {
|
||||
seed := fmt.Sprintf("benchmark-%d-%d", time.Now().UnixNano(), i)
|
||||
start := time.Now()
|
||||
|
||||
puzzle, err := Generate(d, seed)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
result.FailedPuzzles++
|
||||
continue
|
||||
}
|
||||
|
||||
result.SuccessfulPuzzles++
|
||||
totalDuration += elapsed
|
||||
|
||||
if elapsed < result.MinTime {
|
||||
result.MinTime = elapsed
|
||||
}
|
||||
if elapsed > result.MaxTime {
|
||||
result.MaxTime = elapsed
|
||||
}
|
||||
|
||||
rating := RatePuzzle(puzzle)
|
||||
result.Ratings = append(result.Ratings, rating)
|
||||
}
|
||||
|
||||
if result.SuccessfulPuzzles > 0 {
|
||||
result.AverageTime = totalDuration / time.Duration(result.SuccessfulPuzzles)
|
||||
|
||||
var totalRating int
|
||||
for _, r := range result.Ratings {
|
||||
totalRating += r
|
||||
}
|
||||
result.AverageRating = float64(totalRating) / float64(len(result.Ratings))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// String returns a formatted string representation of the benchmark result.
|
||||
func (br BenchmarkResult) String() string {
|
||||
successRate := float64(br.SuccessfulPuzzles) / float64(br.TotalAttempts) * 100
|
||||
|
||||
return fmt.Sprintf(`Benchmark Results for %s:
|
||||
Total Attempts: %d
|
||||
Successful: %d (%.1f%%)
|
||||
Failed: %d
|
||||
Average Time: %v
|
||||
Min Time: %v
|
||||
Max Time: %v
|
||||
Average Rating: %.1f/100
|
||||
`,
|
||||
br.Difficulty.String(),
|
||||
br.TotalAttempts,
|
||||
br.SuccessfulPuzzles,
|
||||
successRate,
|
||||
br.FailedPuzzles,
|
||||
br.AverageTime,
|
||||
br.MinTime,
|
||||
br.MaxTime,
|
||||
br.AverageRating,
|
||||
)
|
||||
}
|
||||
|
||||
// CompareGenerationMethods compares standard vs symmetric generation.
|
||||
func CompareGenerationMethods(d Difficulty, attempts int) (standard, symmetric BenchmarkResult) {
|
||||
// Benchmark standard generation
|
||||
standard = BenchmarkGeneration(d, attempts)
|
||||
|
||||
// Benchmark symmetric generation
|
||||
symmetric = BenchmarkResult{
|
||||
Difficulty: d,
|
||||
TotalAttempts: attempts,
|
||||
MinTime: time.Hour,
|
||||
Ratings: make([]int, 0, attempts),
|
||||
}
|
||||
|
||||
var totalDuration time.Duration
|
||||
|
||||
for i := 0; i < attempts; i++ {
|
||||
seed := fmt.Sprintf("symmetric-benchmark-%d-%d", time.Now().UnixNano(), i)
|
||||
start := time.Now()
|
||||
|
||||
puzzle, err := GenerateWithSymmetry(d, seed, SymmetryRotational180)
|
||||
elapsed := time.Since(start)
|
||||
|
||||
if err != nil {
|
||||
symmetric.FailedPuzzles++
|
||||
continue
|
||||
}
|
||||
|
||||
symmetric.SuccessfulPuzzles++
|
||||
totalDuration += elapsed
|
||||
|
||||
if elapsed < symmetric.MinTime {
|
||||
symmetric.MinTime = elapsed
|
||||
}
|
||||
if elapsed > symmetric.MaxTime {
|
||||
symmetric.MaxTime = elapsed
|
||||
}
|
||||
|
||||
rating := RatePuzzle(puzzle)
|
||||
symmetric.Ratings = append(symmetric.Ratings, rating)
|
||||
}
|
||||
|
||||
if symmetric.SuccessfulPuzzles > 0 {
|
||||
symmetric.AverageTime = totalDuration / time.Duration(symmetric.SuccessfulPuzzles)
|
||||
|
||||
var totalRating int
|
||||
for _, r := range symmetric.Ratings {
|
||||
totalRating += r
|
||||
}
|
||||
symmetric.AverageRating = float64(totalRating) / float64(len(symmetric.Ratings))
|
||||
}
|
||||
|
||||
return standard, symmetric
|
||||
}
|
||||
|
||||
// PuzzleStatistics provides detailed statistics about a generated puzzle.
|
||||
type PuzzleStatistics struct {
|
||||
Grid Grid
|
||||
Analysis PuzzleAnalysis
|
||||
Rating int
|
||||
EstimatedSolveTime time.Duration
|
||||
ComplexityScore float64
|
||||
}
|
||||
|
||||
// GetPuzzleStatistics performs comprehensive analysis on a puzzle.
|
||||
func GetPuzzleStatistics(g Grid) PuzzleStatistics {
|
||||
analysis := g.Analyze()
|
||||
rating := RatePuzzle(g)
|
||||
|
||||
// Estimate solve time based on difficulty (rough approximation)
|
||||
var estimatedTime time.Duration
|
||||
switch analysis.EstimatedDifficulty {
|
||||
case Easy:
|
||||
estimatedTime = 3 * time.Minute
|
||||
case Normal:
|
||||
estimatedTime = 8 * time.Minute
|
||||
case Hard:
|
||||
estimatedTime = 15 * time.Minute
|
||||
case Expert:
|
||||
estimatedTime = 25 * time.Minute
|
||||
case Lunatic:
|
||||
estimatedTime = 45 * time.Minute
|
||||
}
|
||||
|
||||
// Calculate complexity score (0-1)
|
||||
complexityScore := float64(rating) / 100.0
|
||||
|
||||
return PuzzleStatistics{
|
||||
Grid: g,
|
||||
Analysis: analysis,
|
||||
Rating: rating,
|
||||
EstimatedSolveTime: estimatedTime,
|
||||
ComplexityScore: complexityScore,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a formatted string of puzzle statistics.
|
||||
func (ps PuzzleStatistics) String() string {
|
||||
return fmt.Sprintf(`Puzzle Statistics:
|
||||
Difficulty: %s
|
||||
Rating: %d/100
|
||||
Filled Cells: %d
|
||||
Empty Cells: %d
|
||||
Min Candidates: %d
|
||||
Max Candidates: %d
|
||||
Avg Candidates: %.2f
|
||||
Unique Solution: %v
|
||||
Symmetry: %s
|
||||
Techniques: %v
|
||||
Complexity Score: %.2f
|
||||
Est. Solve Time: %v
|
||||
`,
|
||||
ps.Analysis.EstimatedDifficulty.String(),
|
||||
ps.Rating,
|
||||
ps.Analysis.FilledCells,
|
||||
ps.Analysis.EmptyCells,
|
||||
ps.Analysis.MinCandidates,
|
||||
ps.Analysis.MaxCandidates,
|
||||
ps.Analysis.AvgCandidates,
|
||||
ps.Analysis.HasUniqueSolution,
|
||||
ps.Analysis.SymmetryType.String(),
|
||||
ps.Analysis.SolvingTechniques,
|
||||
ps.ComplexityScore,
|
||||
ps.EstimatedSolveTime,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user