Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
implement-cowsay/.venv

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing this line is left over from a previous exercise!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you’re right! This line is leftover from the previous cowsay exercise. I’m keeping it intentionally because the virtual environment is still needed there. I’ve added a comment in .gitignore to make this clear.

80 changes: 80 additions & 0 deletions laptop_allocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from dataclasses import dataclass
from enum import Enum
from typing import Dict, List

# Define available operating systems
class OperatingSystem(Enum):
MACOS = "macOS"
ARCH = "Arch Linux"
UBUNTU = "Ubuntu"


@dataclass(frozen=True)
class Person:
name: str
age: int
# Sorted in order of preference, most preferred is first.
preferred_operating_system: List[OperatingSystem]


@dataclass(frozen=True)
class Laptop:
id: int
manufacturer: str
model: str
screen_size_in_inches: float
operating_system: OperatingSystem

# List of available laptops
laptops = [
Laptop(id=1, manufacturer="Dell", model="XPS", screen_size_in_inches=13, operating_system=OperatingSystem.ARCH),
Laptop(id=2, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU),
Laptop(id=3, manufacturer="Dell", model="XPS", screen_size_in_inches=15, operating_system=OperatingSystem.UBUNTU),
Laptop(id=4, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS),
Laptop(id=5, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system=OperatingSystem.MACOS),
Laptop(id=6, manufacturer="Apple", model="macBook", screen_size_in_inches=13, operating_system=OperatingSystem.ARCH),
]

# List of people to allocate laptops to
people = [
Person(name="Imran", age=22, preferred_operating_system=[OperatingSystem.ARCH,OperatingSystem.UBUNTU]),
Person(name="Eliza", age=34, preferred_operating_system=[OperatingSystem.ARCH,OperatingSystem.MACOS,OperatingSystem.UBUNTU]),
Person(name="Leila", age=45, preferred_operating_system=[OperatingSystem.MACOS,OperatingSystem.UBUNTU,OperatingSystem.ARCH]),
Person(name="Mary", age=35, preferred_operating_system=[OperatingSystem.MACOS,OperatingSystem.ARCH]),
Person(name="Sara", age=28, preferred_operating_system=[OperatingSystem.MACOS])
]

# Global sadness counter
sadness=0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a global variable is normally a bad idea. This could just as easily be created inside the allocate_laptops function and returned as part of a multi-value return from the function. As it is if you call the function twice the second time will include the sadness from the first time in its sadness score.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your feedback.
I fixed it.


# Allocate laptops to people to minimize total sadness
def allocate_laptops(people: List[Person], laptops: List[Laptop]) -> Dict[Person, Laptop]:
sorted_people_OS_count=sorted(people,key=lambda p:len(p.preferred_operating_system))
allocated_history : Dict[Person,Laptop] ={}
global sadness
for person in sorted_people_OS_count :
allocated_flag=False
for i in range(len(person.preferred_operating_system)) :
for laptop in laptops :
if person.preferred_operating_system[i] == laptop.operating_system :
allocated_history[person.name]=laptop # assign laptop
sadness += i # increment sadness by preference index
laptops.remove(laptop)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The laptops argument is passed to allocate_laptops by reference. So this remove function is modifying the original list of laptops. Now this may be intentional, in which case it should be made obvious somewhere. Alternatively make a copy of the list at the start of the function and modify that inside the function. I can see arguments for both approaches being desirable, but it would be good to make it obvious.

Copy link
Author

@sheida-shab sheida-shab Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yhank you for your comment.
Laptops list is modified on purpose.
After allocation, it shows the laptops that are still left.I fixed it.
I added a comment for clarify this.

allocated_flag=True
break
if allocated_flag :
break

if not allocated_flag : # assign any remaining laptop if preferred OS not found
allocated_history[person.name]=laptops[0]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both here and above you're using person.name as they key in the dictionary. On line 53 you declared the key as Person. One or the other needs correcting.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed it.

laptops.remove(laptops[0])
sadness +=100 # high sadness for non-preferred OS

return allocated_history

def print_final_allocation(allocated_history:dict[Person,Laptop]) :
for name , laptop in allocated_history.items() :
print(f"{name:<10} : Laptop Id {laptop.id:<3} - OS({laptop.operating_system.name}) ")
print(f"Total sadness is : {sadness}")

print_final_allocation(allocate_laptops(people,laptops))