Skip to content

Go if-else — Practice Tasks

Task List

  • Task 1: Grade Calculator (Beginner)
  • Task 2: FizzBuzz (Beginner)
  • Task 3: Login Validator (Beginner)
  • Task 4: BMI Calculator (Beginner)
  • Task 5: Leap Year Checker (Beginner)
  • Task 6: Password Strength Checker (Intermediate)
  • Task 7: Triangle Classifier (Intermediate)
  • Task 8: Shopping Cart Discount (Intermediate)
  • Task 9: Network Packet Router (Intermediate)
  • Task 10: Multi-level Access Control (Advanced)
  • Task 11: Loan Eligibility Checker (Advanced)
  • Task 12: Rate Limiter with Tiers (Advanced)

Task 1: Grade Calculator

Difficulty: Beginner Goal: Convert numeric scores to letter grades using if-else

Requirements: - 90-100 → "A" - 80-89 → "B" - 70-79 → "C" - 60-69 → "D" - Below 60 → "F" - Score < 0 or > 100 → "Invalid"

Starter Code:

package main

import "fmt"

func calculateGrade(score int) string {
    // TODO: implement grade calculation
    // Hint: check invalid range first (guard clause)
    return ""
}

func main() {
    tests := []int{95, 85, 75, 65, 55, -1, 101, 100, 0, 60}
    for _, score := range tests {
        fmt.Printf("Score %3d -> %s\n", score, calculateGrade(score))
    }
}

// Expected output:
// Score  95 -> A
// Score  85 -> B
// Score  75 -> C
// Score  65 -> D
// Score  55 -> F
// Score  -1 -> Invalid
// Score 101 -> Invalid
// Score 100 -> A
// Score   0 -> F
// Score  60 -> D

Task 2: FizzBuzz

Difficulty: Beginner Goal: Classic FizzBuzz using if-else

Requirements: - Divisible by 3 AND 5 → "FizzBuzz" - Divisible by 3 → "Fizz" - Divisible by 5 → "Buzz" - Otherwise → the number as string

Starter Code:

package main

import (
    "fmt"
    "strconv"
)

func fizzBuzz(n int) string {
    // TODO: implement FizzBuzz
    // IMPORTANT: check divisible by BOTH first!
    return strconv.Itoa(n)
}

func main() {
    for i := 1; i <= 20; i++ {
        fmt.Printf("%2d: %s\n", i, fizzBuzz(i))
    }
}

// Expected for 1-20:
// 1: 1, 2: 2, 3: Fizz, 4: 4, 5: Buzz
// 6: Fizz, 7: 7, 8: 8, 9: Fizz, 10: Buzz
// 11: 11, 12: Fizz, 13: 13, 14: 14, 15: FizzBuzz
// 16: 16, 17: 17, 18: Fizz, 19: 19, 20: Buzz

Task 3: Login Validator

Difficulty: Beginner Goal: Validate login credentials with specific error messages

Requirements: - Username cannot be empty - Password must be at least 8 characters - Username must be at least 3 characters - Return specific error message for each failure - Return "Login successful" if all valid

Starter Code:

package main

import (
    "fmt"
    "strings"
)

func validateLogin(username, password string) string {
    // TODO: implement validation
    // Use strings.TrimSpace to handle whitespace
    return ""
}

func main() {
    tests := []struct {
        user, pass, expected string
    }{
        {"alice", "password123", "Login successful"},
        {"", "password123", "Username cannot be empty"},
        {"ab", "password123", "Username too short"},
        {"alice", "short", "Password too short (min 8 chars)"},
        {"  ", "password123", "Username cannot be empty"},
    }

    for _, tt := range tests {
        result := validateLogin(tt.user, tt.pass)
        status := "PASS"
        if result != tt.expected {
            status = "FAIL"
        }
        fmt.Printf("[%s] user=%q pass=%q\n  got: %q\n  want: %q\n",
            status, tt.user, tt.pass, result, tt.expected)
    }
}

Task 4: BMI Calculator

Difficulty: Beginner Goal: Calculate BMI and return the category

Requirements: - BMI = weight(kg) / height(m)^2 - < 18.5 → "Underweight" - 18.5 – 24.9 → "Normal weight" - 25.0 – 29.9 → "Overweight" - ≥ 30.0 → "Obese" - Invalid input (weight ≤ 0 or height ≤ 0) → "Invalid input"

Starter Code:

package main

import (
    "fmt"
    "math"
)

func bmiCategory(weightKg, heightM float64) string {
    // TODO: check invalid inputs first
    // TODO: calculate BMI
    // TODO: return category
    return ""
}

func main() {
    cases := []struct {
        weight, height float64
        name           string
    }{
        {50, 1.75, "Underweight"},
        {70, 1.75, "Normal"},
        {85, 1.75, "Overweight"},
        {100, 1.75, "Obese"},
        {0, 1.75, "Invalid"},
        {70, -1, "Invalid"},
    }
    for _, c := range cases {
        result := bmiCategory(c.weight, c.height)
        fmt.Printf("%.0fkg/%.2fm -> %s\n", c.weight, c.height, result)
    }
    _ = math.Pow // hint: use math.Pow(x, 2) or x*x
}

Task 5: Leap Year Checker

Difficulty: Beginner Goal: Determine if a year is a leap year

Rules: - Divisible by 400 → leap year - Divisible by 100 but not 400 → NOT a leap year - Divisible by 4 but not 100 → leap year - Otherwise → NOT a leap year

Starter Code:

package main

import "fmt"

func isLeapYear(year int) bool {
    // TODO: implement the leap year logic
    // Order matters! Check 400 first, then 100, then 4
    return false
}

func main() {
    cases := []struct {
        year int
        want bool
    }{
        {2000, true},  // divisible by 400
        {1900, false}, // divisible by 100 but not 400
        {2024, true},  // divisible by 4 but not 100
        {2023, false}, // not divisible by 4
        {2100, false}, // divisible by 100 but not 400
        {2400, true},  // divisible by 400
    }

    for _, c := range cases {
        result := isLeapYear(c.year)
        status := "✓"
        if result != c.want {
            status = "✗"
        }
        fmt.Printf("%s Year %d: got %v, want %v\n", status, c.year, result, c.want)
    }
}

Task 6: Password Strength Checker

Difficulty: Intermediate Goal: Rate password strength based on multiple criteria

Requirements: - "Very Weak": length < 6 - "Weak": length 6-7 - "Fair": length ≥ 8 but only one character class - "Strong": length ≥ 8, has letters AND digits - "Very Strong": length ≥ 12, has lowercase + uppercase + digits + special chars

Starter Code:

package main

import (
    "fmt"
    "unicode"
)

func checkPasswordStrength(password string) string {
    length := len(password)

    // Count character classes
    var hasLower, hasUpper, hasDigit, hasSpecial bool
    for _, ch := range password {
        switch {
        case unicode.IsLower(ch):
            hasLower = true
        case unicode.IsUpper(ch):
            hasUpper = true
        case unicode.IsDigit(ch):
            hasDigit = true
        default:
            hasSpecial = true
        }
    }

    // TODO: implement strength classification
    // Use the variables: length, hasLower, hasUpper, hasDigit, hasSpecial
    _ = hasLower  // remove when used
    _ = hasUpper
    _ = hasDigit
    _ = hasSpecial

    return "Unknown"
}

func main() {
    passwords := []struct {
        password string
        expected string
    }{
        {"abc", "Very Weak"},
        {"abcdef", "Weak"},
        {"abcdefgh", "Fair"},
        {"abcdef12", "Strong"},
        {"Abcdef12!@#", "Very Strong"},
        {"ALLCAPS123!!", "Very Strong"},
        {"short1!", "Weak"},
    }

    for _, p := range passwords {
        result := checkPasswordStrength(p.password)
        status := "PASS"
        if result != p.expected {
            status = "FAIL"
        }
        fmt.Printf("[%s] %q -> %q (expected: %q)\n",
            status, p.password, result, p.expected)
    }
}

Task 7: Triangle Classifier

Difficulty: Intermediate Goal: Classify a triangle by its sides and angles

Requirements: - Validate the triangle inequality (a + b > c for all permutations) - If all sides equal → "Equilateral" - If two sides equal → "Isosceles" - Otherwise → "Scalene" - Also determine if right triangle (a² + b² == c²)

Starter Code:

package main

import (
    "fmt"
    "math"
)

type Triangle struct {
    A, B, C float64  // side lengths
}

func (t Triangle) IsValid() bool {
    // TODO: check triangle inequality
    return false
}

func (t Triangle) Type() string {
    if !t.IsValid() {
        return "Invalid"
    }
    // TODO: return "Equilateral", "Isosceles", or "Scalene"
    return ""
}

func (t Triangle) IsRight() bool {
    if !t.IsValid() {
        return false
    }
    // TODO: check if it's a right triangle
    // Hint: sort sides and check a² + b² == c²
    // Use math.Abs(x - y) < 1e-9 for float comparison
    _ = math.Abs
    return false
}

func main() {
    triangles := []Triangle{
        {3, 3, 3},   // equilateral
        {3, 3, 4},   // isosceles
        {3, 4, 5},   // scalene + right
        {5, 12, 13}, // scalene + right
        {1, 1, 10},  // invalid
        {6, 8, 10},  // scalene + right
    }

    for _, tri := range triangles {
        fmt.Printf("(%g, %g, %g): valid=%v, type=%s, right=%v\n",
            tri.A, tri.B, tri.C,
            tri.IsValid(), tri.Type(), tri.IsRight())
    }
}

Task 8: Shopping Cart Discount

Difficulty: Intermediate Goal: Apply discount rules to a shopping cart

Discount Rules: - VIP users always get 20% off - Orders over $200 get 15% off (unless VIP — VIP gets 20%) - Orders over $100 get 10% off - Members with coupon "SAVE10" get 10% off (can stack with VIP) - Discounts don't stack (take the highest, except VIP+coupon stacks)

Starter Code:

package main

import "fmt"

type Cart struct {
    Subtotal float64
    IsVIP    bool
    IsMember bool
    Coupon   string
}

func (c Cart) FinalPrice() float64 {
    discount := 0.0

    // TODO: implement discount logic
    // Remember: VIP + coupon can stack (VIP 20% + coupon 10% = 30%)
    // But other discounts don't stack

    return c.Subtotal * (1 - discount)
}

func main() {
    carts := []Cart{
        {150, false, false, ""},          // 10% off ($135)
        {250, false, false, ""},          // 15% off ($212.50)
        {100, true, false, ""},           // VIP 20% ($80)
        {100, true, false, "SAVE10"},     // VIP+coupon 30% ($70)
        {100, false, true, "SAVE10"},     // coupon 10% ($90)
        {50, false, false, ""},           // no discount ($50)
    }

    for _, c := range carts {
        fmt.Printf("$%.2f (vip=%v, coupon=%q) -> $%.2f\n",
            c.Subtotal, c.IsVIP, c.Coupon, c.FinalPrice())
    }
}

Task 9: Network Packet Router

Difficulty: Intermediate Goal: Route network packets based on IP ranges and protocols

Requirements: - Private IPs (10.x.x.x, 192.168.x.x, 172.16-31.x.x) → "internal" - Protocol TCP, port 80/443 → "web" - Protocol TCP, port 22 → "ssh" - Protocol UDP, port 53 → "dns" - Otherwise → "unknown"

Starter Code:

package main

import (
    "fmt"
    "strings"
)

type Packet struct {
    SrcIP    string
    DstIP    string
    Protocol string // "TCP" or "UDP"
    Port     int
}

func isPrivateIP(ip string) bool {
    // TODO: check if IP is in private ranges
    // Hint: use strings.HasPrefix
    _ = strings.HasPrefix
    return false
}

func routePacket(p Packet) string {
    // TODO: implement routing logic
    // Order: check destination IP first, then protocol+port
    return "unknown"
}

func main() {
    packets := []Packet{
        {"1.2.3.4", "10.0.0.1", "TCP", 80},
        {"10.0.0.1", "8.8.8.8", "UDP", 53},
        {"192.168.1.1", "10.0.0.2", "TCP", 22},
        {"172.16.1.1", "1.1.1.1", "TCP", 443},
        {"1.2.3.4", "5.6.7.8", "TCP", 8080},
    }

    for _, p := range packets {
        route := routePacket(p)
        fmt.Printf("%s -> %s:%d/%s -> %s\n",
            p.SrcIP, p.DstIP, p.Port, p.Protocol, route)
    }
}

Task 10: Multi-level Access Control

Difficulty: Advanced Goal: Implement RBAC (Role-Based Access Control) with if-else

Requirements: - Roles: "guest", "user", "moderator", "admin", "superadmin" - Resources: "public", "profile", "content", "settings", "system" - Actions: "read", "write", "delete", "admin" - Return true/false based on permission matrix

Starter Code:

package main

import "fmt"

type Permission struct {
    Role     string
    Resource string
    Action   string
}

func hasPermission(p Permission) bool {
    // TODO: implement RBAC logic
    //
    // Rules:
    // superadmin: can do anything
    // admin: can do anything except system delete
    // moderator: can read/write/delete content; read/write profile; read settings
    // user: can read public/content, read/write own profile
    // guest: can only read public

    switch p.Role {
    case "superadmin":
        return true
    case "admin":
        // TODO
    case "moderator":
        // TODO
    case "user":
        // TODO
    case "guest":
        return p.Resource == "public" && p.Action == "read"
    }
    return false
}

func main() {
    tests := []struct {
        p    Permission
        want bool
    }{
        {Permission{"guest", "public", "read"}, true},
        {Permission{"guest", "content", "read"}, false},
        {Permission{"user", "public", "read"}, true},
        {Permission{"user", "profile", "write"}, true},
        {Permission{"user", "settings", "read"}, false},
        {Permission{"moderator", "content", "delete"}, true},
        {Permission{"moderator", "system", "read"}, false},
        {Permission{"admin", "system", "admin"}, true},
        {Permission{"admin", "system", "delete"}, false},
        {Permission{"superadmin", "system", "delete"}, true},
    }

    for _, tt := range tests {
        result := hasPermission(tt.p)
        status := "PASS"
        if result != tt.want {
            status = "FAIL"
        }
        fmt.Printf("[%s] %s can %s %s: got %v, want %v\n",
            status, tt.p.Role, tt.p.Action, tt.p.Resource, result, tt.want)
    }
}

Task 11: Loan Eligibility Checker

Difficulty: Advanced Goal: Determine loan eligibility and amount based on multiple criteria

Requirements: - Minimum age: 21 - Minimum income: $25,000/year - Minimum credit score: 620 - No outstanding defaults - Loan amount = 5x annual income (max $500,000) - Credit score >= 750: additional 20% bump - Credit score 700-749: additional 10% bump

Starter Code:

package main

import "fmt"

type Applicant struct {
    Age          int
    AnnualIncome float64
    CreditScore  int
    HasDefault   bool
}

type LoanDecision struct {
    Approved    bool
    MaxAmount   float64
    Reason      string
}

func evaluateLoan(a Applicant) LoanDecision {
    // TODO: implement loan eligibility logic
    // Use guard clauses for each rejection reason
    // Calculate loan amount for approved applicants

    return LoanDecision{}
}

func main() {
    applicants := []struct {
        a    Applicant
        name string
    }{
        {Applicant{25, 50000, 720, false}, "Alice"},
        {Applicant{20, 50000, 720, false}, "Bob (too young)"},
        {Applicant{25, 20000, 720, false}, "Carol (low income)"},
        {Applicant{25, 50000, 600, false}, "Dave (low credit)"},
        {Applicant{25, 50000, 720, true}, "Eve (has default)"},
        {Applicant{30, 80000, 760, false}, "Frank (excellent)"},
        {Applicant{40, 200000, 800, false}, "Grace (capped)"},
    }

    for _, item := range applicants {
        d := evaluateLoan(item.a)
        if d.Approved {
            fmt.Printf("%s: APPROVED for $%.0f\n", item.name, d.MaxAmount)
        } else {
            fmt.Printf("%s: DENIED (%s)\n", item.name, d.Reason)
        }
    }
}

Task 12: Rate Limiter with Tiers

Difficulty: Advanced Goal: Build a tiered rate limiter using if-else logic

Requirements: - Free tier: 10 requests/minute - Basic tier: 100 requests/minute - Pro tier: 1000 requests/minute - Enterprise: unlimited - Return: whether request is allowed AND current usage info

Starter Code:

package main

import (
    "fmt"
    "sync"
    "time"
)

type Tier string

const (
    Free       Tier = "free"
    Basic      Tier = "basic"
    Pro        Tier = "pro"
    Enterprise Tier = "enterprise"
)

type RateLimitResult struct {
    Allowed   bool
    Remaining int
    ResetIn   time.Duration
}

type TieredLimiter struct {
    mu      sync.Mutex
    clients map[string]*clientState
}

type clientState struct {
    tier      Tier
    requests  []time.Time
    windowSz  time.Duration
    limit     int
}

func NewTieredLimiter() *TieredLimiter {
    return &TieredLimiter{clients: make(map[string]*clientState)}
}

func (l *TieredLimiter) limitForTier(tier Tier) int {
    // TODO: return the limit for each tier
    // Enterprise: -1 (unlimited)
    return 0
}

func (l *TieredLimiter) Allow(clientID string, tier Tier) RateLimitResult {
    l.mu.Lock()
    defer l.mu.Unlock()

    // TODO: implement rate limiting logic
    // 1. Get or create client state
    // 2. If enterprise tier, always allow
    // 3. Filter out old requests (outside window)
    // 4. Check if under limit
    // 5. Record this request if allowed
    // 6. Return result with remaining count

    return RateLimitResult{Allowed: true}
}

func main() {
    limiter := NewTieredLimiter()

    // Test free tier
    for i := 0; i < 12; i++ {
        result := limiter.Allow("client-free", Free)
        fmt.Printf("Free request %2d: allowed=%v, remaining=%d\n",
            i+1, result.Allowed, result.Remaining)
    }
}