The quadchecker program identifies which "quad" pattern function was used to create an ASCII art rectangle. It reads ASCII art from input and determines which of the 5 quad functions (quadA, quadB, quadC, quadD, quadE) produced that pattern.
Think of it like a fingerprint detector:
- Someone gives you a piece of ASCII art (like a box made with characters)
- The program knows 5 different ways to make boxes (the 5 quad functions)
- It generates all 5 patterns with the same size as the input
- It compares the input with each pattern to find which one(s) match
- It tells you which pattern(s) matched and the dimensions
- Read Input: The program reads ASCII art from stdin (what you pipe into it)
- Remove Newline: It cleans up the final newline character
- Extract Dimensions: It figures out how wide and tall the rectangle is
- Validate: It checks that all lines are the same width (a valid rectangle)
- Generate Patterns: It creates all 5 quad patterns with the same dimensions
- Compare: It compares the input with each generated pattern
- Sort Results: It lists all matches alphabetically
- Output: It prints the results in the format:
[quadX] [width] [height]
Each quad function creates a rectangle border using different characters.
Uses: o (corners), - (top/bottom), | (left/right)
Example (5×3):
o---o
| |
o---o
How it works:
- Corners get
o - Top and bottom edges get
- - Left and right edges get
| - Inside gets space
Uses: /, \ (corners), * (other edges)
Example (5×3):
/---\
* *
\---/
How it works:
- Top-left and bottom-right corners get
/ - Top-right and bottom-left corners get
\ - All other edges get
* - Inside gets space
Uses: A (top corners), C (bottom corners), B (other edges)
Example (5×3):
AAAAA
BBBBB
CCCCC
How it works:
- Top corners get
A - Bottom corners get
C - All other edges get
B - Inside gets space
Uses: A (top-left and bottom-left), C (top-right and bottom-right), B (other edges)
Example (5×3):
ACCCA
BBBBB
ACCCA
How it works:
- Top-left and bottom-left corners get
A - Top-right and bottom-right corners get
C - All other edges get
B - Inside gets space
Uses: A (top-left and bottom-right), C (top-right and bottom-left), B (other edges)
Example (5×3):
ACCCA
BBBBB
CAAAC
How it works:
- Top-left and bottom-right corners get
A - Top-right and bottom-left corners get
C - All other edges get
B - Inside gets space
The test files (quadA, quadB, quadC, quadD, quadE) are executable programs that generate the quad patterns. You only need to create them once to test the checker.
The quadchecker program needs a way to generate test input. Instead of typing out rectangles manually, we have these small programs that do it for us. They take two numbers (width and height) and output the corresponding quad pattern.
In the test files, the function names are different from the main.go file:
- In
main.go: functions are namedquadA,quadB,quadC,quadD,quadE(lowercase 'quad') - In test files: functions are named
genA,genB,genC,genD,genE(using 'gen' prefix)
Why the difference?
The main.go functions (quadA, quadB, etc.) are the "checker" functions - they generate patterns to compare against the input. They must have the exact same logic as the quad functions.
The test files use genA, genB, etc. because:
- They're separate executable programs (not part of the main program)
- Using different names makes it clear these are generator functions
- When we build them into executables (quadA, quadB, etc.), the executable name is what matters, not the function name
- It avoids confusion - the executable is named
quadA(which comes from the main package), and it has a functiongenAinside it
Step 1: Create a file named quadA.go with this code:
package main
import (
"fmt"
"os"
"strconv"
)
// genA generates the quadA pattern
func genA(w, h int) string {
if w <= 0 || h <= 0 {
return ""
}
b := make([]byte, 0, (w+1)*h)
for r := 0; r < h; r++ {
for c := 0; c < w; c++ {
switch {
case r == 0 && c == 0, r == 0 && c == w-1, r == h-1 && c == 0, r == h-1 && c == w-1:
b = append(b, 'o')
case r == 0 || r == h-1:
b = append(b, '-')
case c == 0 || c == w-1:
b = append(b, '|')
default:
b = append(b, ' ')
}
}
b = append(b, '\n')
}
return string(b)
}
func main() {
if len(os.Args) < 3 {
return
}
w, _ := strconv.Atoi(os.Args[1])
h, _ := strconv.Atoi(os.Args[2])
fmt.Print(genA(w, h))
}Step 2: Compile it into an executable:
go build -o quadA quadA.goStep 3: Test it:
./quadA 5 3You should see:
o---o
| |
o---o
Run these commands in the /home/mightpush/test-go/quadchecker directory:
cat > quadB.go << 'EOF'
package main
import (
"fmt"
"os"
"strconv"
)
func genB(w, h int) string {
if w <= 0 || h <= 0 {
return ""
}
b := make([]byte, 0, (w+1)*h)
for r := 0; r < h; r++ {
for c := 0; c < w; c++ {
switch {
case r == 0 && c == 0 || r == h-1 && c == w-1:
b = append(b, '/')
case r == 0 && c == w-1 || r == h-1 && c == 0:
b = append(b, '\\')
case r == 0 || r == h-1 || c == 0 || c == w-1:
b = append(b, '*')
default:
b = append(b, ' ')
}
}
b = append(b, '\n')
}
return string(b)
}
func main() {
if len(os.Args) < 3 {
return
}
w, _ := strconv.Atoi(os.Args[1])
h, _ := strconv.Atoi(os.Args[2])
fmt.Print(genB(w, h))
}
EOF
go build -o quadB quadB.gocat > quadC.go << 'EOF'
package main
import (
"fmt"
"os"
"strconv"
)
func genC(w, h int) string {
if w <= 0 || h <= 0 {
return ""
}
b := make([]byte, 0, (w+1)*h)
for r := 0; r < h; r++ {
for c := 0; c < w; c++ {
switch {
case r == 0 && c == 0 || r == 0 && c == w-1:
b = append(b, 'A')
case r == h-1 && c == 0 || r == h-1 && c == w-1:
b = append(b, 'C')
case r == 0 || r == h-1 || c == 0 || c == w-1:
b = append(b, 'B')
default:
b = append(b, ' ')
}
}
b = append(b, '\n')
}
return string(b)
}
func main() {
if len(os.Args) < 3 {
return
}
w, _ := strconv.Atoi(os.Args[1])
h, _ := strconv.Atoi(os.Args[2])
fmt.Print(genC(w, h))
}
EOF
go build -o quadC quadC.gocat > quadD.go << 'EOF'
package main
import (
"fmt"
"os"
"strconv"
)
func genD(w, h int) string {
if w <= 0 || h <= 0 {
return ""
}
b := make([]byte, 0, (w+1)*h)
for r := 0; r < h; r++ {
for c := 0; c < w; c++ {
switch {
case r == 0 && c == 0 || r == h-1 && c == 0:
b = append(b, 'A')
case r == 0 && c == w-1 || r == h-1 && c == w-1:
b = append(b, 'C')
case r == 0 || r == h-1 || c == 0 || c == w-1:
b = append(b, 'B')
default:
b = append(b, ' ')
}
}
b = append(b, '\n')
}
return string(b)
}
func main() {
if len(os.Args) < 3 {
return
}
w, _ := strconv.Atoi(os.Args[1])
h, _ := strconv.Atoi(os.Args[2])
fmt.Print(genD(w, h))
}
EOF
go build -o quadD quadD.gocat > quadE.go << 'EOF'
package main
import (
"fmt"
"os"
"strconv"
)
func genE(w, h int) string {
if w <= 0 || h <= 0 {
return ""
}
b := make([]byte, 0, (w+1)*h)
for r := 0; r < h; r++ {
for c := 0; c < w; c++ {
switch {
case r == 0 && c == 0 || r == h-1 && c == w-1:
b = append(b, 'A')
case r == 0 && c == w-1 || r == h-1 && c == 0:
b = append(b, 'C')
case r == 0 || r == h-1 || c == 0 || c == w-1:
b = append(b, 'B')
default:
b = append(b, ' ')
}
}
b = append(b, '\n')
}
return string(b)
}
func main() {
if len(os.Args) < 3 {
return
}
w, _ := strconv.Atoi(os.Args[1])
h, _ := strconv.Atoi(os.Args[2])
fmt.Print(genE(w, h))
}
EOF
go build -o quadE quadE.gogo build -o quadchecker main.go./quadA 3 3 | go run main.goExpected output:
[quadA] [3] [3]
./quadC 1 1 | go run main.goExpected output:
[quadC] [1] [1] || [quadD] [1] [1] || [quadE] [1] [1]
echo "0 0" | go run main.goExpected output:
Not a quad function
echo -n "o--o"$'\n'"|"$'\n'"o" | go run main.goExpected output:
Not a quad function
-
The main.go has the checker logic - It contains
quadA,quadB, etc. functions that generate patterns for comparison -
The executable files (quadA, quadB, etc.) are generators - They contain
genA,genB, etc. functions that are used for testing -
Function naming matters - Use
gen*in executable files to avoid confusion with the checker functions -
The logic must match - The
quadAfunction in main.go must produce the exact same output as thegenAfunction in quadA.go, otherwise tests will fail -
You can delete the .go files - After building the executables, you can safely delete the source files (quadA.go, quadB.go, etc.). The compiled executables don't need them
-
Two different packages - main.go is one program, while quadA.go, quadB.go, etc. are separate programs. They both use
package mainbecause each is its own executable
- quadchecker (main.go): Reads input and identifies which quad pattern it matches
- quadA, quadB, quadC, quadD, quadE: Generate test patterns for the checker
- Function names differ: checker uses
quadA, generators usegenA - All outputs must match: The logic in both places must produce identical rectangles
- Fully commented code: main.go includes detailed comments explaining each step
Good luck with your testing!