package main import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "testing" "hashi/hashisolver" ) // runBridgenCommand runs the bridgen.c program and returns the generated puzzle func runBridgenCommand(rows, cols int) (string, error) { // Compile bridgen.c if not already compiled if _, err := os.Stat("./bridgen"); os.IsNotExist(err) { cmd := exec.Command("gcc", "-o", "bridgen", "c_src/bridgen.c") if err := cmd.Run(); err != nil { return "", fmt.Errorf("failed to compile bridgen: %v", err) } } // Run bridgen to generate a puzzle cmd := exec.Command("./bridgen", fmt.Sprintf("%d", rows), fmt.Sprintf("%d", cols)) output, err := cmd.Output() if err != nil { return "", fmt.Errorf("failed to run bridgen: %v", err) } return string(output), nil } // runBridgecheckCommand runs the bridgecheck.c program to validate a solution func runBridgecheckCommand(puzzleFile, solutionFile string) (bool, string, error) { // Compile bridgecheck.c if not already compiled if _, err := os.Stat("./bridgecheck"); os.IsNotExist(err) { cmd := exec.Command("gcc", "-o", "bridgecheck", "c_src/bridgecheck.c") if err := cmd.Run(); err != nil { return false, "", fmt.Errorf("failed to compile bridgecheck: %v", err) } } // Run bridgecheck to validate the solution cmd := exec.Command("./bridgecheck", puzzleFile) cmd.Stdin, _ = os.Open(solutionFile) output, err := cmd.CombinedOutput() if err != nil { return false, string(output), fmt.Errorf("bridgecheck failed: %v", err) } // Check if the solution is valid return bytes.Contains(output, []byte("Valid Solution")), string(output), nil } // TestSolverWithBridgen tests the hashi solver against puzzles generated by bridgen func TestSolverWithBridgen(t *testing.T) { // Create temporary directory for test files tempDir, err := ioutil.TempDir("", "hashi_test") if err != nil { t.Fatalf("Failed to create temp directory: %v", err) } defer os.RemoveAll(tempDir) // Test with different board sizes sizes := []struct { rows int cols int debug bool }{ {3, 3, false}, // Small {5, 5, false}, // Small-medium {8, 8, true}, // Medium with debug {10, 10, false}, // Medium-large {15, 15, false}, // Large {20, 20, false}, // Very large } for _, size := range sizes { t.Run(fmt.Sprintf("%dx%d", size.rows, size.cols), func(t *testing.T) { // Generate puzzle using bridgen puzzle, err := runBridgenCommand(size.rows, size.cols) if err != nil { t.Fatalf("Failed to generate puzzle: %v", err) } // Save puzzle to file puzzleFile := filepath.Join(tempDir, fmt.Sprintf("puzzle_%dx%d.txt", size.rows, size.cols)) if err := ioutil.WriteFile(puzzleFile, []byte(puzzle), 0644); err != nil { t.Fatalf("Failed to write puzzle file: %v", err) } // Solve puzzle using our Go implementation file, err := os.Open(puzzleFile) if err != nil { t.Fatalf("Failed to open puzzle file: %v", err) } defer file.Close() p, err := hashisolver.Solve(file, size.debug) if err != nil { t.Logf("Solver failed for %dx%d puzzle: %v", size.rows, size.cols, err) if p != nil && size.debug { t.Logf("Progress: %d/%d bridges placed (%.1f%%)", p.BuiltBridges, p.FullBridges, float64(p.BuiltBridges)/float64(p.FullBridges)*100) } t.Fail() return } // Capture the solution var buf bytes.Buffer oldStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w hashisolver.PrintMap(p) w.Close() os.Stdout = oldStdout _, _ = buf.ReadFrom(r) solution := buf.String() // Save solution to file solutionFile := filepath.Join(tempDir, fmt.Sprintf("solution_%dx%d.txt", size.rows, size.cols)) if err := ioutil.WriteFile(solutionFile, []byte(solution), 0644); err != nil { t.Fatalf("Failed to write solution file: %v", err) } // Validate solution using bridgecheck isValid, output, err := runBridgecheckCommand(puzzleFile, solutionFile) if err != nil { t.Logf("Failed to validate solution: %v", err) t.Logf("Bridgecheck output: %s", output) t.Fail() return } if !isValid { t.Logf("Solution for %dx%d puzzle is invalid", size.rows, size.cols) t.Logf("Bridgecheck output: %s", output) t.Fail() } else { t.Logf("Solution for %dx%d puzzle is valid", size.rows, size.cols) } }) } } // TestSolverWithKnownPuzzles tests the hashi solver against known puzzles func TestSolverWithKnownPuzzles(t *testing.T) { // Generate a simple 3x3 puzzle using bridgen puzzle, err := runBridgenCommand(3, 3) if err != nil { t.Fatalf("Failed to generate puzzle: %v", err) } // Create a temporary directory for test files tempDir, err := ioutil.TempDir("", "hashi_known_test") if err != nil { t.Fatalf("Failed to create temp directory: %v", err) } defer os.RemoveAll(tempDir) // Save the puzzle to a file puzzleFile := filepath.Join(tempDir, "simple_puzzle.txt") if err := ioutil.WriteFile(puzzleFile, []byte(puzzle), 0644); err != nil { t.Fatalf("Failed to write puzzle file: %v", err) } // Solve the puzzle file, err := os.Open(puzzleFile) if err != nil { t.Fatalf("Failed to open puzzle file: %v", err) } defer file.Close() p, err := hashisolver.Solve(file, true) if err != nil { t.Fatalf("Failed to solve simple puzzle: %v", err) } // Capture and save the solution var buf bytes.Buffer oldStdout := os.Stdout r, w, _ := os.Pipe() os.Stdout = w hashisolver.PrintMap(p) w.Close() os.Stdout = oldStdout _, _ = buf.ReadFrom(r) solution := buf.String() solutionFile := filepath.Join(tempDir, "simple_solution.txt") if err := ioutil.WriteFile(solutionFile, []byte(solution), 0644); err != nil { t.Fatalf("Failed to write solution file: %v", err) } // Validate the solution isValid, output, err := runBridgecheckCommand(puzzleFile, solutionFile) if err != nil { t.Logf("Failed to validate solution: %v", err) t.Logf("Bridgecheck output: %s", output) t.Fail() return } if !isValid { t.Logf("Solution for simple puzzle is invalid") t.Logf("Bridgecheck output: %s", output) t.Fail() } else { t.Logf("Solution for simple puzzle is valid") } }