diff --git a/app/instructor/exercises/[id]/ExerciseManager.tsx b/app/instructor/exercises/[id]/ExerciseManager.tsx index 1658a3c..184eb9a 100644 --- a/app/instructor/exercises/[id]/ExerciseManager.tsx +++ b/app/instructor/exercises/[id]/ExerciseManager.tsx @@ -56,9 +56,11 @@ export default function ExerciseManager({ exercise: initial, sessions, assignedU } async function toggleAssignment(userId: string, currentlyAssigned: boolean) { - const newIds = currentlyAssigned - ? assignedUsers.filter((u) => u.id !== userId).map((u) => u.id) - : [...assignedUsers.map((u) => u.id), userId]; + const newIds = userId === '__all__' + ? allParticipants.map((u) => u.id) + : currentlyAssigned + ? assignedUsers.filter((u) => u.id !== userId).map((u) => u.id) + : [...assignedUsers.map((u) => u.id), userId]; setSaving(true); try { const res = await fetch(`/api/instructor/exercises/${exercise.id}`, { @@ -139,7 +141,18 @@ export default function ExerciseManager({ exercise: initial, sessions, assignedU
Participants - {assignedUsers.length} assigned +
+ {assignedUsers.length} assigned + {assignedUsers.length < allParticipants.length && ( + + )} +
{allParticipants.length === 0 ? (

No participant accounts exist yet.

diff --git a/docs/go-reloaded/coding-go-reloaded.md b/docs/go-reloaded/coding-go-reloaded.md index 88e54c6..6ca754d 100644 --- a/docs/go-reloaded/coding-go-reloaded.md +++ b/docs/go-reloaded/coding-go-reloaded.md @@ -1,179 +1,254 @@ -# go-reloaded — Scenario-Based Re-Coding Questions +# go-reloaded — Hands-On Coding Drills > Derived strictly from the go-reloaded project brief -> Format: Scenario-based | Tests: Implementation decisions, Debugging & problem-solving, Design thinking -> Rules: Answer each question in your own words. No AI-generated responses. +> Format: Small focused coding tasks, build up to full implementation +> Rules: Write every function yourself. Test each drill before moving to the next. +> Standard Go packages only — no external libraries. --- -## 🔧 Implementation Decisions +## Drill 1 — Read Input and Write Output Files -*Why you built it this way.* +Write a Go program that: ---- +```go +func readFile(path string) (string, error) +func writeFile(path string, content string) error +``` -### Question 1 +- `readFile` reads the entire file at `path` and returns its content as a string +- `writeFile` writes `content` to the file at `path`, creating it if it doesn't exist +- Both return a meaningful error if the operation fails -Your program receives two arguments: an input filename and an output filename. You read the input file, process it, and write to the output. +**Usage:** +```bash +go run . input.txt output.txt +``` -A classmate reads the entire file into memory as one string and processes it all at once. You consider reading it line by line instead. +**Test it:** +```bash +echo "hello world" > input.txt +go run . input.txt output.txt +cat output.txt +# → hello world +``` -**Which approach did you choose — and why? What are the trade-offs between whole-file-in-memory vs line-by-line for a text transformation tool like this? What breaks in each approach when the input file is very large?** +**Edge cases:** +- Input file does not exist → print error and exit with non-zero code +- Output path is not writable → print error and exit --- -### Question 2 - -You need to handle `(up)`, `(low)`, and `(cap)` — each with an optional number modifier like `(up, 3)`. +## Drill 2 — Convert `(hex)` and `(bin)` Tags -**Walk through your implementation decision: did you handle the simple case `(up)` and the numbered case `(up, 3)` in the same code path or separately? What made that the right call? What would break if you tried to force both into a single handler?** +Write a Go function: ---- +```go +func convertBases(text string) string +``` -### Question 3 +- Finds every `(hex)` tag and replaces the word immediately before it with its decimal value +- Finds every `(bin)` tag and replaces the word immediately before it with its decimal value +- Removes the tag itself from the output -The spec says `(hex)` and `(bin)` should replace the word **before** them with its decimal conversion. The word before is always guaranteed to be a valid hex or binary number. +**Test cases:** +``` +"1E (hex) files were added" → "30 files were added" +"It has been 10 (bin) years" → "It has been 2 years" +"Simply add 42 (hex) and 10 (bin) and you will see the result is 68." → "Simply add 66 and 2 and you will see the result is 68." +``` -**How did you locate "the word before" in your implementation? Did you split the string, use an index, scan backwards? Walk through your exact approach and explain why it's correct — and where it would fail if the guarantee about valid input was removed.** +**What you need:** +- `strconv.ParseInt(s, 16, 64)` for hex +- `strconv.ParseInt(s, 2, 64)` for binary +- `strconv.FormatInt(n, 10)` to convert back to decimal string --- -### Question 4 +## Drill 3 — Single-Word Case Modifiers: `(up)`, `(low)`, `(cap)` -Rule 8 says: convert `a` to `an` if the next word starts with a vowel or `h`. +Write a Go function: -But the rule says *every instance of `a`* — not just standalone `a`. So `"a amazing"` → `"an amazing"`, but what about `"a hard-boiled egg"` or `"a hour"`? - -**How did you define "a" in your implementation — exact match only, or something broader? What edge cases did you have to consciously decide to include or exclude, and how does your code enforce that decision?** - ---- +```go +func applySingleCaseModifiers(text string) string +``` -### Question 5 +- `(up)` → converts the word immediately before it to UPPERCASE +- `(low)` → converts the word immediately before it to lowercase +- `(cap)` → capitalizes the first letter of the word immediately before it -Your program must handle punctuation spacing for `.`, `,`, `!`, `?`, `:`, `;` — close to the previous word, space before the next. But grouped punctuation like `...` or `!?` must stay together and be treated as a single unit. +**Test cases:** +``` +"Ready, set, go (up) !" → "Ready, set, GO !" +"I should stop SHOUTING (low)" → "I should stop shouting" +"Welcome to the Brooklyn bridge (cap)" → "Welcome to the Brooklyn Bridge" +``` -**This is two different rules that look similar. How did you implement both without the grouped-punctuation rule accidentally triggering the single-punctuation rule — or vice versa? What was the order of operations in your processing pipeline, and why does order matter here?** +**What you need:** +- `strings.ToUpper`, `strings.ToLower` +- Manual capitalize: `strings.ToUpper(s[:1]) + strings.ToLower(s[1:])` --- -## 🐛 Debugging & Problem-Solving +## Drill 4 — Multi-Word Case Modifiers: `(up, N)`, `(low, N)`, `(cap, N)` -*What broke and how you fixed it.* +Extend your solution from Drill 3 to handle the numbered variant: ---- - -### Question 6 +```go +func applyCaseModifiers(text string) string +``` -You run your program on the sample input: +- `(up, 3)` → converts the 3 words before the tag to uppercase +- `(low, 2)` → converts the 2 words before the tag to lowercase +- `(cap, 6)` → capitalizes the first letter of the 6 words before the tag +- Single `(up)` / `(low)` / `(cap)` still works (defaults to 1 word) +**Test cases:** ``` -it (cap) was the best of times, it was the worst of times (up) , it was the age of foolishness (cap, 6) +"This is so exciting (up, 2)" → "This is SO EXCITING" +"it was the age of foolishness (cap, 6)" → "It Was The Age Of Foolishness" +"IT WAS THE (low, 3) winter" → "it was the winter" ``` -Your output has `(cap, 6)` correctly uppercasing 6 words — but it's counting the comma as one of the 6 words. - -**What went wrong in your word-counting logic? Walk through the bug: where in your code did punctuation get treated as a word, and what specific fix resolves it without breaking the single-word `(cap)` case?** +**Important:** count only actual words — punctuation attached to a word counts as part of that word, not a separate word. --- -### Question 7 +## Drill 5 — Punctuation Spacing -You implement the single-quote rule: `' awesome '` → `'awesome'`. It works on the sample input. Then your auditor tests it with: +Write a Go function: +```go +func fixPunctuation(text string) string ``` -"As Elton John said: ' I am the most well-known homosexual in the world '" -``` - -Your output incorrectly puts the closing `'` after `world'` with a space before it. - -**Trace the bug. What assumption did your implementation make about single quotes that broke on multi-word input? How do you fix it — and how do you test that the fix works for both the one-word and multi-word cases?** ---- - -### Question 8 - -You process `(hex)` conversion and it works for most inputs. Then your auditor runs: +- Single punctuation marks (`.`, `,`, `!`, `?`, `:`, `;`) must be placed directly after the previous word with no space, and have a space before the next word +- Grouped punctuation (`...`, `!?`, `!!`, etc.) must be treated as a single unit — same rule applies +**Test cases:** ``` -1E (hex) files were added +"I was sitting over there ,and then BAMM !!" → "I was sitting over there, and then BAMM!!" +"Punctuation tests are ... kinda boring ,what do you think ?" → "Punctuation tests are... kinda boring, what do you think?" +"Ready, set, GO !" → "Ready, set, GO!" ``` -Your program crashes with a parsing error. - -**What went wrong? Walk through what `strconv.ParseInt("1E", 16, 64)` actually requires — and what your code likely missed about case sensitivity in hex input. How do you fix it, and what other hex edge cases should you now test for?** +**Order matters:** process grouped punctuation before single punctuation, otherwise `...` gets broken apart. --- -### Question 9 - -Your program passes all your own tests. During the audit, your auditor feeds it a file where a `(low, 5)` modifier appears near the beginning of a sentence — and there are fewer than 5 words before it. - -**What does your program do in this case — crash, silently process fewer words, or something else? Walk through what the correct behavior should be according to the spec, and how your implementation handles (or should handle) the boundary condition of `n > available words`.** +## Drill 6 — Single-Quote Formatting ---- +Write a Go function: -### Question 10 +```go +func fixSingleQuotes(text string) string +``` -You process punctuation spacing as a final pass after all other transformations. Your auditor runs: +- Pairs of `'` marks should have no space between them and the words they wrap +- Works for both single-word and multi-word content between the quotes +**Test cases:** ``` -Ready, set, go (up) ! -``` +"I am exactly how they describe me: ' awesome '" +→ "I am exactly how they describe me: 'awesome'" -Your output is `Ready, set, GO !` — the space before `!` wasn't removed. +"As Elton John said: ' I am the most well-known homosexual in the world '" +→ "As Elton John said: 'I am the most well-known homosexual in the world'" +``` -**Why did the final punctuation pass miss this? What does this reveal about the order in which your transformations run — and what would you need to change in your pipeline so that punctuation cleanup always fires after case transformations, not before?** +**Hint:** find the first `'`, strip the space after it; find the matching closing `'`, strip the space before it. --- -## 🏗️ Design Thinking +## Drill 7 — `a` → `an` Rule -*Architecture, trade-offs, pattern choices.* +Write a Go function: ---- - -### Question 11 +```go +func fixArticles(text string) string +``` -You have 8 transformation rules to implement. Two approaches are on the table: +- Every standalone `a` or `A` followed by a word starting with a vowel (`a`, `e`, `i`, `o`, `u`) or `h` should become `an` / `An` -- **Approach A:** One large function that scans the text and handles every rule in a single pass. -- **Approach B:** A pipeline — separate functions for each rule, applied in sequence. +**Test cases:** +``` +"There it was. A amazing rock!" → "There it was. An amazing rock!" +"There is no greater agony than bearing a untold story inside you." → "There is no greater agony than bearing an untold story inside you." +"I need a hero" → "I need a hero" ← h rule +"She ate a apple" → "She ate an apple" +``` -**Which did you choose — and what are the real trade-offs? Think specifically about: what happens when two rules interact (e.g., `(up)` fires on a word that also needs punctuation cleanup), how easy it is to add a 9th rule later, and how you'd write unit tests for each approach.** +**Important:** only match exact standalone `a` — not `a` inside another word like `cat` or `amazing`. --- -### Question 12 +## Drill 8 — Wire Everything Together -The spec says to use only standard Go packages. You need to do string manipulation, file I/O, number base conversion, and pattern matching. +Build the full pipeline in `main`: -**Map out which standard Go packages you used for which rules — and for at least one rule, explain why you chose that package's approach over writing the logic manually. What does the standard library give you that a hand-rolled solution might get wrong?** +1. Read args: `go run . input.txt output.txt` +2. Read input file +3. Apply transformations in this order: + - `convertBases` (hex/bin) + - `applyCaseModifiers` (up/low/cap with optional N) + - `fixPunctuation` + - `fixSingleQuotes` + - `fixArticles` +4. Write result to output file ---- +**Test with all spec examples:** -### Question 13 +```bash +# Test 1 +echo "it (cap) was the best of times, it was the worst of times (up) , it was the age of wisdom, it was the age of foolishness (cap, 6) , it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of darkness, it was the spring of hope, IT WAS THE (low, 3) winter of despair." > sample.txt +go run . sample.txt result.txt +cat result.txt +# Expected: It was the best of times, it was the worst of TIMES, it was the age of wisdom, It Was The Age Of Foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of darkness, it was the spring of hope, it was the winter of despair. -Your auditor is another student. The spec says: *"We advise you to create your own tests for yourself and for when you will correct your auditees."* +# Test 2 +echo "Simply add 42 (hex) and 10 (bin) and you will see the result is 68." > sample.txt +go run . sample.txt result.txt +cat result.txt +# Expected: Simply add 66 and 2 and you will see the result is 68. -You need to write tests that are genuinely useful — not just the four sample inputs from the spec. +# Test 3 +echo "There is no greater agony than bearing a untold story inside you." > sample.txt +go run . sample.txt result.txt +cat result.txt +# Expected: There is no greater agony than bearing an untold story inside you. -**Design a test suite for go-reloaded. What categories of input would you test beyond the given samples? List at least 6 specific test cases — including at least two that target interactions between rules (e.g., a word that needs both `(cap)` and punctuation cleanup) — and explain what each one is designed to catch.** +# Test 4 +echo "Punctuation tests are ... kinda boring ,what do you think ?" > sample.txt +go run . sample.txt result.txt +cat result.txt +# Expected: Punctuation tests are... kinda boring, what do you think? +``` --- -### Question 14 +## Drill 9 — Edge Case Gauntlet -The spec guarantees that when `(hex)` or `(bin)` is used, the word before it will always be a valid number in that base. But in a real production tool, you can't make that guarantee. +Write a test file `transform_test.go` covering: -**Without changing the spec's required behavior for valid inputs, how would you design your error handling for invalid inputs? What should the program output, exit with, or log — and how does Go's idiomatic error handling pattern (`if err != nil`) shape the way you structure this decision across your codebase?** - ---- - -## Reflection Prompt (Bonus) +```go +func TestHexConversion(t *testing.T) // "1E (hex)" → "30" +func TestBinConversion(t *testing.T) // "10 (bin)" → "2" +func TestUpSingle(t *testing.T) // "go (up)" → "GO" +func TestCapMultiple(t *testing.T) // "(cap, 6)" on 6 words +func TestLowFewerWordsThanN(t *testing.T) // (low, 5) with only 2 words before → apply to available words +func TestGroupedPunctuation(t *testing.T) // "..." stays together +func TestSingleQuoteMultiWord(t *testing.T)// ' I am great ' → 'I am great' +func TestArticleH(t *testing.T) // "a hero" → "a hero" (h rule) +func TestArticleVowel(t *testing.T) // "a apple" → "an apple" +func TestCombinedRules(t *testing.T) // (up) + punctuation in same sentence +``` -After completing go-reloaded, you look back at your code. You notice that the section handling `(low, n)`, `(up, n)`, and `(cap, n)` is almost identical three times — just with a different string transformation applied. +Run with: +```bash +go test ./... +``` -**What refactoring would eliminate the duplication? Write the signature of a Go function that could replace all three, and explain what you'd pass as an argument to make it work for all cases. What does this refactoring reveal about the design principle your original implementation missed?** +All tests must pass before you consider the project complete. --- -*All answers must be written in your own words. Responses identified as AI-generated will be rejected.* +*Write every function yourself. Do not copy. Test locally before moving to the next drill.* diff --git a/docs/go-reloaded/question0.md b/docs/go-reloaded/question0.md index f33dde2..7c30616 100644 --- a/docs/go-reloaded/question0.md +++ b/docs/go-reloaded/question0.md @@ -1,7 +1,46 @@ -### Question 1 +## Drill 1 — Read Input Text from Stdin -Your program receives two arguments: an input filename and an output filename. You read the input file, process it, and write to the output. +Write a function that reads the entire input from stdin and returns it as a string: -A classmate reads the entire file into memory as one string and processes it all at once. You consider reading it line by line instead. +```go +func readInput() string +``` -**Which approach did you choose — and why? What are the trade-offs between whole-file-in-memory vs line-by-line for a text transformation tool like this? What breaks in each approach when the input file is very large?** +**Requirements:** +- Read all lines from stdin +- Join them back with newlines +- Return the full text as a single string + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + // TODO: implement + return "" +} + +func main() { + text := readInput() + fmt.Print(text) +} +``` + +**Stdin (paste into the stdin box):** +``` +hello world +this is a test +``` + +**Expected output:** +``` +hello world +this is a test +``` diff --git a/docs/go-reloaded/question1.md b/docs/go-reloaded/question1.md index 74e43e8..5d57eab 100644 --- a/docs/go-reloaded/question1.md +++ b/docs/go-reloaded/question1.md @@ -1,5 +1,56 @@ -### Question 2 +## Drill 2 — Convert `(hex)` Tags -You need to handle `(up)`, `(low)`, and `(cap)` — each with an optional number modifier like `(up, 3)`. +Write a function that finds every `(hex)` tag and replaces the word immediately before it with its decimal value: -**Walk through your implementation decision: did you handle the simple case `(up)` and the numbered case `(up, 3)` in the same code path or separately? What made that the right call? What would break if you tried to force both into a single handler?** +```go +func convertHex(text string) string +``` + +**Requirements:** +- The word before `(hex)` is always a valid hexadecimal number +- Replace that word with its decimal equivalent +- Remove the `(hex)` tag from the output + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func convertHex(text string) string { + // TODO: implement + return text +} + +func main() { + text := readInput() + fmt.Println(convertHex(text)) +} +``` + +**Stdin:** +``` +1E (hex) files were added +``` + +**Expected output:** +``` +30 files were added +``` + +**Hint:** Use `strconv.ParseInt(s, 16, 64)` to parse hex, and `strconv.FormatInt(n, 10)` to convert back to decimal. diff --git a/docs/go-reloaded/question10.md b/docs/go-reloaded/question10.md index 932aa97..5675365 100644 --- a/docs/go-reloaded/question10.md +++ b/docs/go-reloaded/question10.md @@ -1,8 +1,41 @@ -### Question 11 +## Drill 11 — Test Punctuation Cleanup -You have 8 transformation rules to implement. Two approaches are on the table: +Use your full pipeline. Verify punctuation spacing works correctly including grouped punctuation. -- **Approach A:** One large function that scans the text and handles every rule in a single pass. -- **Approach B:** A pipeline — separate functions for each rule, applied in sequence. +**Starter:** +```go +package main -**Which did you choose — and what are the real trade-offs? Think specifically about: what happens when two rules interact (e.g., `(up)` fires on a word that also needs punctuation cleanup), how easy it is to add a 9th rule later, and how you'd write unit tests for each approach.** +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +// TODO: paste your full transform pipeline here + +func main() { + text := readInput() + fmt.Print(transform(text)) +} +``` + +**Stdin:** +``` +I was sitting over there ,and then BAMM !! +``` + +**Expected output:** +``` +I was sitting over there, and then BAMM!! +``` diff --git a/docs/go-reloaded/question11.md b/docs/go-reloaded/question11.md index 87715bc..61e3a41 100644 --- a/docs/go-reloaded/question11.md +++ b/docs/go-reloaded/question11.md @@ -1,5 +1,41 @@ -### Question 12 +## Drill 12 — Test Single-Quote Formatting -The spec says to use only standard Go packages. You need to do string manipulation, file I/O, number base conversion, and pattern matching. +Use your full pipeline. Verify single-quote pairs are formatted correctly for both single-word and multi-word cases. -**Map out which standard Go packages you used for which rules — and for at least one rule, explain why you chose that package's approach over writing the logic manually. What does the standard library give you that a hand-rolled solution might get wrong?** +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +// TODO: paste your full transform pipeline here + +func main() { + text := readInput() + fmt.Print(transform(text)) +} +``` + +**Stdin:** +``` +I am exactly how they describe me: ' awesome ' +``` + +**Expected output:** +``` +I am exactly how they describe me: 'awesome' +``` diff --git a/docs/go-reloaded/question12.md b/docs/go-reloaded/question12.md index 8eb3dfb..a6d0977 100644 --- a/docs/go-reloaded/question12.md +++ b/docs/go-reloaded/question12.md @@ -1,7 +1,41 @@ -### Question 13 +## Drill 13 — Test the Article Rule -Your auditor is another student. The spec says: *"We advise you to create your own tests for yourself and for when you will correct your auditees."* +Use your full pipeline. Verify `a` → `an` works for vowels and `h`. -You need to write tests that are genuinely useful — not just the four sample inputs from the spec. +**Starter:** +```go +package main -**Design a test suite for go-reloaded. What categories of input would you test beyond the given samples? List at least 6 specific test cases — including at least two that target interactions between rules (e.g., a word that needs both `(cap)` and punctuation cleanup) — and explain what each one is designed to catch.** +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +// TODO: paste your full transform pipeline here + +func main() { + text := readInput() + fmt.Print(transform(text)) +} +``` + +**Stdin:** +``` +There it was. A amazing rock! +``` + +**Expected output:** +``` +There it was. An amazing rock! +``` diff --git a/docs/go-reloaded/question13.md b/docs/go-reloaded/question13.md index 2bb757b..5429f31 100644 --- a/docs/go-reloaded/question13.md +++ b/docs/go-reloaded/question13.md @@ -1,5 +1,41 @@ -### Question 14 +## Drill 14 — Combined Rules -The spec guarantees that when `(hex)` or `(bin)` is used, the word before it will always be a valid number in that base. But in a real production tool, you can't make that guarantee. +Use your full pipeline. This input exercises multiple rules at once — case modifiers, punctuation, and the article rule all interact. -**Without changing the spec's required behavior for valid inputs, how would you design your error handling for invalid inputs? What should the program output, exit with, or log — and how does Go's idiomatic error handling pattern (`if err != nil`) shape the way you structure this decision across your codebase?** +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +// TODO: paste your full transform pipeline here + +func main() { + text := readInput() + fmt.Print(transform(text)) +} +``` + +**Stdin:** +``` +it (cap) was the best of times, it was the worst of times (up) , it was the age of wisdom, it was the age of foolishness (cap, 6) , it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of darkness, it was the spring of hope, IT WAS THE (low, 3) winter of despair. +``` + +**Expected output:** +``` +It was the best of times, it was the worst of TIMES, it was the age of wisdom, It Was The Age Of Foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of darkness, it was the spring of hope, it was the winter of despair. +``` diff --git a/docs/go-reloaded/question14.md b/docs/go-reloaded/question14.md index 10811db..b4dd87b 100644 --- a/docs/go-reloaded/question14.md +++ b/docs/go-reloaded/question14.md @@ -1,5 +1,52 @@ -## Reflection Prompt (Bonus) +## Drill 15 — Edge Cases -After completing go-reloaded, you look back at your code. You notice that the section handling `(low, n)`, `(up, n)`, and `(cap, n)` is almost identical three times — just with a different string transformation applied. +Use your full pipeline. This input combines all rules and tests boundary conditions — make sure nothing breaks. -**What refactoring would eliminate the duplication? Write the signature of a Go function that could replace all three, and explain what you'd pass as an argument to make it work for all cases. What does this refactoring reveal about the design principle your original implementation missed?** +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +// TODO: paste your full transform pipeline here + +func main() { + text := readInput() + fmt.Print(transform(text)) +} +``` + +**Stdin:** +``` +Simply add 42 (hex) and 10 (bin) and you will see the result is 68. +``` + +**Expected output:** +``` +Simply add 66 and 2 and you will see the result is 68. +``` + +**Also test with:** +``` +Ready, set, go (up) ! +``` +**Expected:** +``` +Ready, set, GO! +``` + +Try both inputs — your pipeline should handle them without any changes to the code. diff --git a/docs/go-reloaded/question2.md b/docs/go-reloaded/question2.md index b887964..6e378ae 100644 --- a/docs/go-reloaded/question2.md +++ b/docs/go-reloaded/question2.md @@ -1,5 +1,56 @@ -### Question 3 +## Drill 3 — Convert `(bin)` Tags -The spec says `(hex)` and `(bin)` should replace the word **before** them with its decimal conversion. The word before is always guaranteed to be a valid hex or binary number. +Write a function that finds every `(bin)` tag and replaces the word immediately before it with its decimal value: -**How did you locate "the word before" in your implementation? Did you split the string, use an index, scan backwards? Walk through your exact approach and explain why it's correct — and where it would fail if the guarantee about valid input was removed.** +```go +func convertBin(text string) string +``` + +**Requirements:** +- The word before `(bin)` is always a valid binary number +- Replace that word with its decimal equivalent +- Remove the `(bin)` tag from the output + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func convertBin(text string) string { + // TODO: implement + return text +} + +func main() { + text := readInput() + fmt.Println(convertBin(text)) +} +``` + +**Stdin:** +``` +It has been 10 (bin) years +``` + +**Expected output:** +``` +It has been 2 years +``` + +**Hint:** Use `strconv.ParseInt(s, 2, 64)` to parse binary. diff --git a/docs/go-reloaded/question3.md b/docs/go-reloaded/question3.md index d76dda2..48f733a 100644 --- a/docs/go-reloaded/question3.md +++ b/docs/go-reloaded/question3.md @@ -1,7 +1,56 @@ -### Question 4 +## Drill 4 — Single-Word `(up)`, `(low)`, `(cap)` -Rule 8 says: convert `a` to `an` if the next word starts with a vowel or `h`. +Write a function that handles single-word case modifiers: -But the rule says *every instance of `a`* — not just standalone `a`. So `"a amazing"` → `"an amazing"`, but what about `"a hard-boiled egg"` or `"a hour"`? +```go +func applySingleCaseModifiers(text string) string +``` -**How did you define "a" in your implementation — exact match only, or something broader? What edge cases did you have to consciously decide to include or exclude, and how does your code enforce that decision?** +**Requirements:** +- `(up)` → converts the word immediately before it to UPPERCASE +- `(low)` → converts the word immediately before it to lowercase +- `(cap)` → capitalizes the first letter of the word immediately before it (rest lowercase) +- Remove the tag from the output + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func applySingleCaseModifiers(text string) string { + // TODO: implement + return text +} + +func main() { + text := readInput() + fmt.Println(applySingleCaseModifiers(text)) +} +``` + +**Stdin:** +``` +Ready, set, go (up) ! +``` + +**Expected output:** +``` +Ready, set, GO ! +``` + +**Hint:** To capitalize: `strings.ToUpper(s[:1]) + strings.ToLower(s[1:])` diff --git a/docs/go-reloaded/question4.md b/docs/go-reloaded/question4.md index fc0ac05..2c89184 100644 --- a/docs/go-reloaded/question4.md +++ b/docs/go-reloaded/question4.md @@ -1,5 +1,55 @@ -### Question 5 +## Drill 5 — Multi-Word `(up, N)`, `(low, N)`, `(cap, N)` -Your program must handle punctuation spacing for `.`, `,`, `!`, `?`, `:`, `;` — close to the previous word, space before the next. But grouped punctuation like `...` or `!?` must stay together and be treated as a single unit. +Extend your case modifier to handle the numbered variant: -**This is two different rules that look similar. How did you implement both without the grouped-punctuation rule accidentally triggering the single-punctuation rule — or vice versa? What was the order of operations in your processing pipeline, and why does order matter here?** +```go +func applyCaseModifiers(text string) string +``` + +**Requirements:** +- `(up, 3)` → converts the 3 words before the tag to uppercase +- `(low, 2)` → converts the 2 words before the tag to lowercase +- `(cap, 6)` → capitalizes the first letter of the 6 words before the tag +- Single `(up)` / `(low)` / `(cap)` still works (defaults to 1 word) +- If N is greater than the number of available words, apply to all available words + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func applyCaseModifiers(text string) string { + // TODO: implement + return text +} + +func main() { + text := readInput() + fmt.Println(applyCaseModifiers(text)) +} +``` + +**Stdin:** +``` +it was the age of foolishness (cap, 6) +``` + +**Expected output:** +``` +It Was The Age Of Foolishness +``` diff --git a/docs/go-reloaded/question5.md b/docs/go-reloaded/question5.md index 924bc83..965269d 100644 --- a/docs/go-reloaded/question5.md +++ b/docs/go-reloaded/question5.md @@ -1,11 +1,53 @@ -### Question 6 +## Drill 6 — Fix Punctuation Spacing -You run your program on the sample input: +Write a function that corrects spacing around punctuation marks: +```go +func fixPunctuation(text string) string ``` -it (cap) was the best of times, it was the worst of times (up) , it was the age of foolishness (cap, 6) + +**Requirements:** +- Single punctuation marks (`.`, `,`, `!`, `?`, `:`, `;`) must sit directly after the previous word with no space before them +- Grouped punctuation (`...`, `!?`, `!!`, etc.) must be treated as a single unit — same rule applies +- Process grouped punctuation before single punctuation to avoid breaking groups apart + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func fixPunctuation(text string) string { + // TODO: implement + return text +} + +func main() { + text := readInput() + fmt.Println(fixPunctuation(text)) +} ``` -Your output has `(cap, 6)` correctly uppercasing 6 words — but it's counting the comma as one of the 6 words. +**Stdin:** +``` +Punctuation tests are ... kinda boring ,what do you think ? +``` -**What went wrong in your word-counting logic? Walk through the bug: where in your code did punctuation get treated as a word, and what specific fix resolves it without breaking the single-word `(cap)` case?** +**Expected output:** +``` +Punctuation tests are... kinda boring, what do you think? +``` diff --git a/docs/go-reloaded/question6.md b/docs/go-reloaded/question6.md index 2963828..7ffcd5d 100644 --- a/docs/go-reloaded/question6.md +++ b/docs/go-reloaded/question6.md @@ -1,11 +1,54 @@ -### Question 7 +## Drill 7 — Fix Single-Quote Formatting -You implement the single-quote rule: `' awesome '` → `'awesome'`. It works on the sample input. Then your auditor tests it with: +Write a function that removes spaces between single-quote marks and the words they wrap: +```go +func fixSingleQuotes(text string) string ``` -"As Elton John said: ' I am the most well-known homosexual in the world '" + +**Requirements:** +- Find pairs of `'` marks +- Remove any space immediately after the opening `'` +- Remove any space immediately before the closing `'` +- Works for both single-word and multi-word content between the quotes + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func fixSingleQuotes(text string) string { + // TODO: implement + return text +} + +func main() { + text := readInput() + fmt.Println(fixSingleQuotes(text)) +} ``` -Your output incorrectly puts the closing `'` after `world'` with a space before it. +**Stdin:** +``` +As Elton John said: ' I am the most well-known homosexual in the world ' +``` -**Trace the bug. What assumption did your implementation make about single quotes that broke on multi-word input? How do you fix it — and how do you test that the fix works for both the one-word and multi-word cases?** +**Expected output:** +``` +As Elton John said: 'I am the most well-known homosexual in the world' +``` diff --git a/docs/go-reloaded/question7.md b/docs/go-reloaded/question7.md index 8c9bb87..f3c9961 100644 --- a/docs/go-reloaded/question7.md +++ b/docs/go-reloaded/question7.md @@ -1,11 +1,52 @@ -### Question 8 +## Drill 8 — Fix `a` → `an` Article Rule -You process `(hex)` conversion and it works for most inputs. Then your auditor runs: +Write a function that converts `a` to `an` when the next word starts with a vowel or `h`: +```go +func fixArticles(text string) string ``` -1E (hex) files were added + +**Requirements:** +- Every standalone `a` or `A` followed by a word starting with `a`, `e`, `i`, `o`, `u`, or `h` (case-insensitive) becomes `an` / `An` +- Only match exact standalone `a` — not `a` inside another word like `cat` or `amazing` + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func fixArticles(text string) string { + // TODO: implement + return text +} + +func main() { + text := readInput() + fmt.Println(fixArticles(text)) +} ``` -Your program crashes with a parsing error. +**Stdin:** +``` +There is no greater agony than bearing a untold story inside you. +``` -**What went wrong? Walk through what `strconv.ParseInt("1E", 16, 64)` actually requires — and what your code likely missed about case sensitivity in hex input. How do you fix it, and what other hex edge cases should you now test for?** +**Expected output:** +``` +There is no greater agony than bearing an untold story inside you. +``` diff --git a/docs/go-reloaded/question8.md b/docs/go-reloaded/question8.md index 0451f9e..93c4c60 100644 --- a/docs/go-reloaded/question8.md +++ b/docs/go-reloaded/question8.md @@ -1,5 +1,67 @@ -### Question 9 +## Drill 9 — Wire the Full Pipeline -Your program passes all your own tests. During the audit, your auditor feeds it a file where a `(low, 5)` modifier appears near the beginning of a sentence — and there are fewer than 5 words before it. +Combine all your functions into a complete transformation pipeline: -**What does your program do in this case — crash, silently process fewer words, or something else? Walk through what the correct behavior should be according to the spec, and how your implementation handles (or should handle) the boundary condition of `n > available words`.** +```go +func transform(text string) string +``` + +Apply transformations in this exact order: +1. `convertHex` + `convertBin` +2. `applyCaseModifiers` +3. `fixPunctuation` +4. `fixSingleQuotes` +5. `fixArticles` + +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +func convertHex(text string) string { return text } // TODO: copy from Drill 2 +func convertBin(text string) string { return text } // TODO: copy from Drill 3 +func applyCaseModifiers(text string) string { return text } // TODO: copy from Drill 5 +func fixPunctuation(text string) string { return text } // TODO: copy from Drill 6 +func fixSingleQuotes(text string) string { return text } // TODO: copy from Drill 7 +func fixArticles(text string) string { return text } // TODO: copy from Drill 8 + +func transform(text string) string { + text = convertHex(text) + text = convertBin(text) + text = applyCaseModifiers(text) + text = fixPunctuation(text) + text = fixSingleQuotes(text) + text = fixArticles(text) + return text +} + +func main() { + text := readInput() + fmt.Print(transform(text)) +} +``` + +**Stdin:** +``` +it (cap) was the best of times, it was the worst of times (up) , it was the age of wisdom, it was the age of foolishness (cap, 6) , it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of darkness, it was the spring of hope, IT WAS THE (low, 3) winter of despair. +``` + +**Expected output:** +``` +It was the best of times, it was the worst of TIMES, it was the age of wisdom, It Was The Age Of Foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of darkness, it was the spring of hope, it was the winter of despair. +``` diff --git a/docs/go-reloaded/question9.md b/docs/go-reloaded/question9.md index 69a1704..a1a8f3f 100644 --- a/docs/go-reloaded/question9.md +++ b/docs/go-reloaded/question9.md @@ -1,11 +1,41 @@ -### Question 10 +## Drill 10 — Test Hex and Bin Together -You process punctuation spacing as a final pass after all other transformations. Your auditor runs: +Use your full pipeline from Drill 9. Test that hex and bin conversions work together in the same input. -``` -Ready, set, go (up) ! +**Starter:** +```go +package main + +import ( + "bufio" + "fmt" + "os" + "strings" +) + +func readInput() string { + var lines []string + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return strings.Join(lines, "\n") +} + +// TODO: paste your full transform pipeline here + +func main() { + text := readInput() + fmt.Print(transform(text)) +} ``` -Your output is `Ready, set, GO !` — the space before `!` wasn't removed. +**Stdin:** +``` +Simply add 42 (hex) and 10 (bin) and you will see the result is 68. +``` -**Why did the final punctuation pass miss this? What does this reveal about the order in which your transformations run — and what would you need to change in your pipeline so that punctuation cleanup always fires after case transformations, not before?** +**Expected output:** +``` +Simply add 66 and 2 and you will see the result is 68. +``` diff --git a/lib/questions.ts b/lib/questions.ts index 8be57ca..3015bbc 100644 --- a/lib/questions.ts +++ b/lib/questions.ts @@ -28,7 +28,7 @@ function resolveExerciseDir(slug: string): string { return path.join(base, slug); } -const CODE_EXERCISE_SLUGS = new Set(['ascii-art', 'ascii-art-web']); +const CODE_EXERCISE_SLUGS = new Set(['ascii-art', 'ascii-art-web', 'go-reloaded']); export function loadExercise(slug: string): ExerciseContent { const dir = resolveExerciseDir(slug);