Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 43 additions & 25 deletions .idea/workspace.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 81 additions & 0 deletions assign3/assign3_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package main

import (
"fmt"
"testing"
)

func TestCompleteTask(t *testing.T) {
tracker := NewTaskTracker()
tracker.AddTask("Read a book")
tracker.AddTask("Play")
tracker.AddTask("Clean")

success, msg := tracker.CompleteTask(2)
if !success {
t.Errorf("Expected task 2 to be completed successfully, but it failed: %s", msg)
}
fmt.Println(msg)
if msg != "Marking task 2 as completed: Play" {
t.Errorf("Incorrect success message. Got: '%s'", msg)
}
if !tracker.tasks[1].Completed {
t.Errorf("Task 2 should be marked as completed")
}

success, msg = tracker.CompleteTask(2)
if success {
t.Errorf("Expected task 2 to not be completed, but it succeeded.")
}
if msg != "Task 2 is already completed." {
t.Errorf("Incorrect already completed message '%s'", msg)
}

success, msg = tracker.CompleteTask(99)
if success {
t.Errorf("Expected task 99 to not be completed, but it succeeded.")
}
if msg != "Task with ID 99 not found." {
t.Errorf("Incorrect not found message. Got: '%s'", msg)
}

if tracker.tasks[0].Completed {
t.Errorf("Task 1 should not be completed")
}
if tracker.tasks[2].Completed {
t.Errorf("Task 3 should not be completed")
}
}

func TestListTasks(t *testing.T) {
tracker := NewTaskTracker()

expected := "Pending Tasks:\nNo pending tasks."
if tracker.ListTasks() != expected {
t.Errorf("Expected '%s', got '%s'", expected, tracker.ListTasks())
}

tracker.AddTask("Task A")
expected = "Pending Tasks:\n1: Task A\n"
if tracker.ListTasks() != expected {
t.Errorf("Expected '%s', got '%s'", expected, tracker.ListTasks())
}

tracker.AddTask("Task B")
expected = "Pending Tasks:\n1: Task A\n2: Task B\n"
if tracker.ListTasks() != expected {
t.Errorf("Expected '%s', got '%s'", expected, tracker.ListTasks())
}

tracker.CompleteTask(1)
expected = "Pending Tasks:\n2: Task B\n"
if tracker.ListTasks() != expected {
t.Errorf("Expected '%s', got '%s'", expected, tracker.ListTasks())
}

tracker.CompleteTask(2)
expected = "Pending Tasks:\nNo pending tasks."
if tracker.ListTasks() != expected {
t.Errorf("Expected '%s', got '%s'", expected, tracker.ListTasks())
}
}
188 changes: 130 additions & 58 deletions assign3/assignment3.go
Original file line number Diff line number Diff line change
@@ -1,78 +1,150 @@
package main

import "fmt"
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)

type BankAccount struct {
Owner string
Balance float64
// Task represents a single task in our tracker.
type Task struct {
ID int
Description string
Completed bool
}

// a value receiver is appropriate here. Changes to 'b' inside this method
// would only affect a copy, not the 'myAccount' variable in main.
func (b BankAccount) DisplayBalance() {
fmt.Printf("%s's current balance: $%.2f\n", b.Owner, b.Balance)
// TaskTracker manages the collection of tasks and generates unique IDs.
type TaskTracker struct {
tasks []Task
nextIDGen func() int
}

// Deposit is a method with a POINTER RECEIVER (b *BankAccount).
// It increases the balance of the account by the given amount.
func (b *BankAccount) Deposit(amount float64) {
if amount <= 0 {
fmt.Printf("Deposit amount must be positive. Skipping deposit of $%.2f.\n", amount)
return
// idGenerator is a closure that generates unique sequential integer IDs.
// It encapsulates the 'id' counter, so it's not a global variable.
func idGenerator() func() int {
id := 0
return func() int {
id++
return id
}
b.Balance += amount // This modifies the original BankAccount's balance
fmt.Printf("Deposited $%.2f. New balance: $%.2f\n", amount, b.Balance)
}

// Withdraw is a method with a POINTER RECEIVER (b *BankAccount).
// It decreases the balance, but only if sufficient funds exist.
func (b *BankAccount) Withdraw(amount float64) {
if amount <= 0 {
fmt.Printf("Withdrawal amount must be positive. Skipping withdrawal of $%.2f.\n", amount)
return
}
if b.Balance >= amount {
b.Balance -= amount // This modifies the original BankAccount's balance
fmt.Printf("Withdrew $%.2f. New balance: $%.2f\n", amount, b.Balance)
} else {
fmt.Printf("Insufficient funds for withdrawal of $%.2f. Current balance: $%.2f.\n", amount, b.Balance)
// NewTaskTracker creates and initializes a new TaskTracker instance.
// It also sets up the unique ID generator.
func NewTaskTracker() *TaskTracker {
return &TaskTracker{
tasks: []Task{},
nextIDGen: idGenerator(),
}
}

// TryToModifyBalance is a standalone function that demonstrates passing a BankAccount by value.
// Any changes to 'b' within this function will only affect a copy, not the original variable passed.
func TryToModifyBalance(b BankAccount, amount float64) {
b.Balance += amount // This only modifies the *copy* of BankAccount passed in
fmt.Printf(" (Inside TryToModifyBalance func): Balance changed to $%.2f\n", b.Balance)
// AddTask adds a new task to the tracker and returns the added Task.
// It uses a pointer receiver (*TaskTracker) because it modifies the TaskTracker's state (its 'tasks' slice).
func (tt *TaskTracker) AddTask(description string) Task {
newID := tt.nextIDGen()
newTask := Task{
ID: newID,
Description: description,
Completed: false,
}
tt.tasks = append(tt.tasks, newTask)
return newTask
}

func main() {
fmt.Println("--- Bank Account System Demonstration ---")
// ListTasks displays all pending tasks.
// It uses a pointer receiver (*TaskTracker) because it operates on the TaskTracker's 'tasks' slice,
// even though it doesn't modify it directly in this function (good practice for methods operating on collections).
func (tt *TaskTracker) ListTasks() string {
s := "Pending Tasks:\n"
foundPending := false
for _, task := range tt.tasks {
if !task.Completed {
s += fmt.Sprintf("%d: %s\n", task.ID, task.Description)
foundPending = true
}
}
if !foundPending {
s += "No pending tasks."
}
return s
}

myAccount := BankAccount{
Owner: "Alice",
Balance: 100.00, // Initial balance
// CompleteTask marks a task as completed given its ID.
// It returns a boolean indicating if the task was found and its completion status was changed,
// and a string message describing the outcome.
// It uses a pointer receiver (*TaskTracker) because it modifies the state of a Task within the tracker's slice.
func (tt *TaskTracker) CompleteTask(id int) (bool, string) {
for i := range tt.tasks {
if tt.tasks[i].ID == id {
if tt.tasks[i].Completed {
return false, fmt.Sprintf("Task %d is already completed.", id)
}
tt.tasks[i].Completed = true
return true, fmt.Sprintf("Marking task %d as completed: %s", id, tt.tasks[i].Description)
}
}
fmt.Println("\n--- Initial State ---")
myAccount.DisplayBalance()
fmt.Println("\n--- Deposit Funds ---")
myAccount.Deposit(50.50)
myAccount.DisplayBalance() // Observe the change
fmt.Println("\n--- Attempting Withdrawals ---")
myAccount.Withdraw(25.00)
myAccount.DisplayBalance()
myAccount.Withdraw(200.00)
myAccount.DisplayBalance()
return false, fmt.Sprintf("Task with ID %d not found.", id)
}

fmt.Println("\n--- Final State ---")
myAccount.DisplayBalance()
fmt.Println("\n--- Demonstrating Value Receiver vs. Pointer Receiver Effect ---")
tempAccount := BankAccount{Owner: "Bob", Balance: 50.00}
fmt.Printf("Bob's balance BEFORE TryToModifyBalance: $%.2f\n", tempAccount.Balance)
TryToModifyBalance(tempAccount, 20.00) // Pass by value
fmt.Printf("Bob's balance AFTER TryToModifyBalance: $%.2f (Did not change externally)\n", tempAccount.Balance)
// displayMenu prints the interactive menu options to the console.
func displayMenu() {
fmt.Println("\n--- Personal Task Tracker ---")
fmt.Println("1. Add a new task")
fmt.Println("2. List all pending tasks")
fmt.Println("3. Mark a task as completed")
fmt.Println("4. Exit")
fmt.Print("Choose an option: ")
}

// getUserInput reads a line of text from the standard input.
func getUserInput() string {
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
return strings.TrimSpace(input)
}

fmt.Printf("Alice's balance BEFORE Deposit (again): $%.2f\n", myAccount.Balance)
myAccount.Deposit(10.00) // This actually modifies myAccount
fmt.Printf("Alice's balance AFTER Deposit (again): $%.2f (Changed externally)\n", myAccount.Balance)
// main function orchestrates the CLI interaction.
func main() {
tracker := NewTaskTracker()

for {
displayMenu()
choiceStr := getUserInput()
choice, err := strconv.Atoi(choiceStr)
if err != nil {
fmt.Println("Invalid choice. Please enter a number between 1 and 4.")
continue
}

switch choice {
case 1:
fmt.Print("Enter task description: ")
description := getUserInput()
if description == "" {
fmt.Println("Task description cannot be empty.")
continue
}
addedTask := tracker.AddTask(description)
fmt.Printf("Task Added: %d - %s\n", addedTask.ID, addedTask.Description)
case 2:
fmt.Println(tracker.ListTasks())
case 3:
fmt.Print("Enter ID of task to mark as completed: ")
idStr := getUserInput()
id, err := strconv.Atoi(idStr)
if err != nil {
fmt.Println("Invalid ID. Please enter a valid number.")
continue
}
_, msg := tracker.CompleteTask(id)
fmt.Println(msg)
case 4:
fmt.Println("Exiting Task Tracker. Goodbye!")
return
default:
fmt.Println("Invalid option. Please choose a number between 1 and 4.")
}
}
}
Loading
Loading