Merge pull request 'task3' (#2) from task3 into main

Reviewed-on: #2

Wow, alle Beispiele gelöst, sehr schön. Wenn Du konkrete Feedbacks willst, müsstest Du diese einfach stellen.
Was mir gefällt ist die strukturierte Beschrifftung der commits (feat etc.) und dass Du alle Aufgaben modular commitest.
This commit is contained in:
Marco Schmid 2026-03-13 10:11:17 +01:00
commit 7d1e778902
28 changed files with 512 additions and 14 deletions

View File

@ -17,4 +17,15 @@ Repository for CDS-2020 Programming and Promt Engineering II
# Codewars # Codewars
|Title|Source (src/codewars/)|Test (test/codewars/)|URL| |Title|Source (src/codewars/)|Test (test/codewars/)|URL|
|-|-|-|-| |-|-|-|-|
|Find the force of gravity between two objects|kata_force_of_gravity.py|test_force_of_gravity.py|[URL](https://www.codewars.com/kata/5b609ebc8f47bd595e000627/)| |Find the force of gravity between two objects|kata_force_of_gravity.py|test_force_of_gravity.py|[5b609ebc8f47bd595e000627](https://www.codewars.com/kata/5b609ebc8f47bd595e000627)|
|The Lamp: Revisited|kata_the_lamp.py|test_the_lamp.py|[570e6e32de4dc8a8340016dd](https://www.codewars.com/kata/570e6e32de4dc8a8340016dd)|
|OOP: Object Oriented Piracy|kata_object_oriented_piracy.py|test_object_oriented_piracy.py|[54fe05c4762e2e3047000add](https://www.codewars.com/kata/54fe05c4762e2e3047000add)|
|Vigenère Cipher Helper|kata_vigenere_cipher_helper.py|test_vigenere_cipher_helper.py|[52d1bd3694d26f8d6e0000d3](https://www.codewars.com/kata/52d1bd3694d26f8d6e0000d3)|
|Caesar Cipher Helper|kata_ceasar_cipher_helper.py|test_ceasar_cipher_helper.py|[526d42b6526963598d0004db](https://www.codewars.com/kata/526d42b6526963598d0004db)|
|Versions manager|kata_version_mamanger.py|test_version_manager.py|[5bc7bb444be9774f100000c3](https://www.codewars.com/kata/5bc7bb444be9774f100000c3)|
|Thinkful - Object Drills: Quarks|kata_thinkful_quarks.py|test_thinkful_quarks.py|[5882b052bdeafec15e0000e6](https://www.codewars.com/kata/5882b052bdeafec15e0000e6)|
|Thinkful - Object Drills: Vectors|kata_thinkful_vectors.py|test_thinkful_vectors.py|[587f1e1f39d444cee6000ad4](https://www.codewars.com/kata/587f1e1f39d444cee6000ad4)|
|Building blocks|kata_building_blocks.py|test_building_blocks.py|[55b75fcf67e558d3750000a3](https://www.codewars.com/kata/55b75fcf67e558d3750000a3)|
|PaginationHelper|kata_pagination_helper.py|test_pagination_helper.py|[515bb423de843ea99400000a](https://www.codewars.com/kata/515bb423de843ea99400000a)|
|Who has the most money?|kata_who_the_most_money.py|test_who_the_most_money.py|[528d36d7cc451cd7e4000339](https://www.codewars.com/kata/528d36d7cc451cd7e4000339)|
|Next bigger number with the same digits|kata_next_bigger_number_same_digits.py|test_next_bigger_number_same_digits.py|[55983863da40caa2c900004e](https://www.codewars.com/kata/55983863da40caa2c900004e)|

View File

View File

@ -0,0 +1,28 @@
class CaesarCipher(object):
def __init__(self, shift):
self.shift = shift
self.alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode(self, text):
idx_alph = [
self.alphabet.index(x) if x in self.alphabet else x for x in text.upper()
]
idx_shift = [
(x + self.shift) % len(self.alphabet) if isinstance(x, (int)) else x
for x in idx_alph
]
return "".join(
[self.alphabet[x] if isinstance(x, (int)) else x for x in idx_shift]
)
def decode(self, text):
idx_alph = [
self.alphabet.index(x) if x in self.alphabet else x for x in text.upper()
]
idx_shift = [
(x - self.shift) % len(self.alphabet) if isinstance(x, (int)) else x
for x in idx_alph
]
return "".join(
[self.alphabet[x] if isinstance(x, (int)) else x for x in idx_shift]
)

View File

@ -0,0 +1,31 @@
def next_bigger(n):
# get number as digits in list
digits = [int(x) for x in list(str(n))]
# find index of pivot. return -1 if there is no pivot, meaning number is alreadig biggest
pivot = -1
for i in range(1, len(digits)):
if digits[-i] > digits[-i - 1]:
pivot = -i - 1
break
if pivot == -1:
return -1
# find the smallest digit to the right that is bigger than the pivot
right = digits[pivot + 1 :]
swap = right.index(min([x for x in right if x > digits[pivot]]))
# swap pivot with found digit
digits[pivot], digits[len(digits) - len(right) + swap] = (
right[swap],
digits[pivot],
)
# sort right side of new swapped pivot and replace it at the end
right = digits[pivot + 1 :]
right.sort()
digits[pivot + 1 :] = right
# return number
return int("".join([str(x) for x in digits]))

View File

@ -0,0 +1,7 @@
class Ship:
def __init__(self, draft, crew):
self.draft = draft
self.crew = crew
def is_worth_it(self):
return self.draft - self.crew * 1.5 > 20

View File

@ -0,0 +1,46 @@
class PaginationHelper:
# The constructor takes in an array of items and an integer indicating
# how many items fit within a single page
def __init__(self, collection, items_per_page):
self.collection = collection
self.items_per_page = items_per_page
# returns the number of items within the entire collection
def item_count(self):
return len(self.collection)
# returns the number of pages
def page_count(self):
return int(len(self.collection) / self.items_per_page) + (
len(self.collection) % self.items_per_page > 0
)
# returns the number of items on the given page. page_index is zero based
# this method should return -1 for page_index values that are out of range
def page_item_count(self, page_index):
if page_index < 0 or self.page_count() - 1 < page_index:
return -1
if page_index == 0:
return len(self.collection[: self.items_per_page])
return len(
self.collection[
self.items_per_page * page_index : self.items_per_page * page_index
+ self.items_per_page
]
)
# determines what page an item at the given index is on. Zero based indexes.
# this method should return -1 for item_index values that are out of range
def page_index(self, item_index):
try:
self.collection[item_index]
if item_index < 0:
raise IndexError
except IndexError:
return -1
return item_index // self.items_per_page

View File

@ -0,0 +1,13 @@
class Lamp:
def __init__(self, color: str):
self.color = color
self.on = False
def toggle_switch(self):
self.on = not self.on
def state(self):
if self.on:
return "The lamp is on."
return "The lamp is off."

View File

View File

View File

@ -0,0 +1,47 @@
class VersionManager:
def __init__(self, version=None):
if not version:
version = "0.0.1"
parts = version.split(".")[:3]
if not all(p.isdecimal() for p in parts):
raise ValueError("Error occured while parsing version!")
nums = [int(p) for p in parts]
while len(nums) < 3:
nums.append(0)
self.major_v, self.minor_v, self.patch_v = nums
self.history = []
def _save(self):
self.history.append((self.major_v, self.minor_v, self.patch_v))
def major(self):
self._save()
self.major_v += 1
self.minor_v = 0
self.patch_v = 0
return self
def minor(self):
self._save()
self.minor_v += 1
self.patch_v = 0
return self
def patch(self):
self._save()
self.patch_v += 1
return self
def rollback(self):
if not self.history:
raise Exception("Cannot rollback!")
self.major_v, self.minor_v, self.patch_v = self.history.pop()
return self
def release(self):
return f"{self.major_v}.{self.minor_v}.{self.patch_v}"

View File

@ -0,0 +1,40 @@
class VigenereCipher(object):
def __init__(self, key, alphabet):
self.key = key
self.alphabet = alphabet
def encode(self, text):
encoded = []
idx_key = 0
for char in text:
if char in self.alphabet:
text_pos = self.alphabet.index(char)
key_char = self.key[idx_key % len(self.key)]
key_pos = self.alphabet.index(key_char)
pos = (text_pos + key_pos) % len(self.alphabet)
encoded.append(self.alphabet[pos])
else:
encoded.append(char)
idx_key += 1
return "".join(encoded)
def decode(self, text):
decoded = []
idx_key = 0
for char in text:
if char in self.alphabet:
text_pos = self.alphabet.index(char)
key_char = self.key[idx_key % len(self.key)]
key_pos = self.alphabet.index(key_char)
pos = (text_pos - key_pos) % len(self.alphabet)
decoded.append(self.alphabet[pos])
else:
decoded.append(char)
idx_key += 1
return "".join(decoded)

View File

@ -0,0 +1,15 @@
class Student:
def __init__(self, name, fives, tens, twenties):
self.name = name
self.fives = fives
self.tens = tens
self.twenties = twenties
def most_money(students):
student_names = [x.name for x in students]
student_money = [x.fives * 5 + x.tens * 10 + x.twenties * 20 for x in students]
if len(set(student_money)) == 1 and len(student_money) > 1:
return "all"
return max(list(zip(student_names, student_money)), key=lambda x: x[1])[0]

View File

@ -0,0 +1,32 @@
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def speak(slef) -> str:
return "..."
def chorus(self, n: int) -> str:
# Wiederhole den Laut n-mal als eine Zeile"
if n <= 0:
raise ValueError("n muss positiv sein")
return " ".join(self.speak() for _ in range(n))
class Dog(Animal):
def speak(self) -> str:
return "wuff"
class Cat(Animal):
def speak(self) -> str:
return "miau"
class Cow(Animal):
def speak(self) -> str:
return "muh"
cow = Cow()
print(cow.chorus(3))

View File

@ -1,41 +1,88 @@
class Hund: from abc import ABC, abstractmethod
class Hund(ABC):
anzahl_hunde = 0 anzahl_hunde = 0
def __init__(self, name: str, rasse: str, alter: int, gewicht: float): def __init__(self, name: str, alter: int, gewicht: float, bellgeraeusch: str):
self.name = name self.name = name
self.rasse = rasse
self.alter = alter self.alter = alter
self.gewicht = gewicht self.gewicht = gewicht
self.bellgeraeusch = bellgeraeusch
Hund.anzahl_hunde += 1 Hund.anzahl_hunde += 1
def __repr__(self): def __repr__(self) -> str:
return f"Hund(name={self.name!r}, rasse={self.rasse}, alter={self.alter}, gewicht={self.gewicht}" return f"Hund(name={self.name!r}, rasse={self.__class__.__name__}, alter={self.alter}, gewicht={self.gewicht}"
def __str__(self): def __str__(self) -> str:
return f"{self.name} ist ein {self.alter}-jähriger {self.rasse}" return f"{self.name} ist ein {self.alter}-jähriger {self.__class__.__name__}"
def bellen(self, n=1) -> int: @abstractmethod
print(n * "Woof! ") def bellen(self, n: int = 1) -> None:
print(" ".join([self.bellgeraeusch] * n))
def geburtstag(self): def geburtstag(self) -> None:
self.alter += 1 self.alter += 1
print( print(
f"Alles Gute zum Geburtstag, {self.name}! Du bist jetzt {self.alter} Jahre alt." f"Alles Gute zum Geburtstag, {self.name}! Du bist jetzt {self.alter} Jahre alt."
) )
def ist_welpe(self): def ist_welpe(self) -> None:
if self.alter < 2: if self.alter < 2:
print(f"{self.name} ist ein {self.alter}-jähriger Welpe") print(f"{self.name} ist ein {self.alter}-jähriger Welpe")
else: else:
print(f"{self.name} ist ein {self.alter}-jähriger erwachsener Hund") print(f"{self.name} ist ein {self.alter}-jähriger erwachsener Hund")
def __lt__(self, other): # less than: self < other
return self.alter < other.alter
hund1 = Hund(name="Bello", rasse="Pudel", alter=99, gewicht=357) def __le__(self, other): # less equal: self <= other
hund2 = Hund(name="Dewy", rasse="Labrador", alter=-6, gewicht=1) return self.alter <= other.alter
def __gt__(self, other): # greater than: self > other
return self.alter > other.alter
def __ge__(self, other): # greater equal: self >= other
return self.alter >= other.alter
def __eq__(self, other): # equal: self == other
return self.alter == other.alter
class Pudel(Hund):
def __init__(self, name, alter, gewicht):
super().__init__(name, alter, gewicht, "wau")
def bellen(self, n=1):
super().bellen(n)
class Labrador(Hund):
def __init__(self, name, alter, gewicht):
super().__init__(name, alter, gewicht, "wuff")
def bellen(self, n=1):
super().bellen(n)
class Bulldog(Hund):
def __init__(self, name, alter, gewicht):
super().__init__(name, alter, gewicht, "woff")
def bellen(self, n=1):
super().bellen(n)
hund1 = Labrador(name="Bello", alter=33, gewicht=27)
hund2 = Pudel(name="Dewy", alter=6, gewicht=1)
hund3 = Bulldog(name="Stone", alter=15, gewicht=1000)
print(repr(hund1)) print(repr(hund1))
print(hund2) print(hund2)
hund2.bellen(3) hund2.bellen(3)
hund1.bellen(2)
hund1.geburtstag() hund1.geburtstag()
hund2.ist_welpe() hund2.ist_welpe()
hund1.ist_welpe() hund1.ist_welpe()
hund3.bellen(4)
print(hund3 > hund2)

View File

@ -0,0 +1,17 @@
class A:
pass
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
print(D.mro())

View File

@ -0,0 +1,21 @@
class Dog:
def speak(self) -> str:
return "wuff"
class Cat:
def speak(self) -> str:
return "miau"
class Robot:
def speak(self) -> str:
return "bip bup"
def chorus(animals) -> list:
for a in animals:
print(a.speak())
chorus([Dog(), Cat(), Robot()])

View File

@ -0,0 +1,27 @@
from abc import ABC, abstractmethod
class Storage(ABC):
@abstractmethod
def save(self, key: str, value: str) -> None:
pass
@abstractmethod
def load(self, key: str) -> str:
pass
class MemoryStorage(Storage):
def __init__(self) -> None:
self.storage = {}
def save(self, key: str, value: str) -> None:
self.storage[key] = value
def load(self, key):
return self.storage[key]
s = MemoryStorage()
s.save("randomkey", "My Value")
print(s.load("randomkey"))

View File

View File

@ -0,0 +1,8 @@
from src.codewars.kata_ceasar_cipher_helper import CaesarCipher
def test_cipher_helper():
c = CaesarCipher(5)
assert c.encode("Codewars") == "HTIJBFWX"
assert c.decode("HTIJBFWX") == "CODEWARS"

View File

@ -0,0 +1,15 @@
from src.codewars.kata_next_bigger_number_same_digits import next_bigger
def test_next_bigger():
assert next_bigger(12) == 21
assert next_bigger(21) == -1
assert next_bigger(513) == 531
assert next_bigger(2017) == 2071
assert next_bigger(414) == 441
assert next_bigger(144) == 414
assert next_bigger(1234567890) == 1234567908
assert next_bigger(59884848459853) == 59884848483559
assert next_bigger(7600201336) == 7600201363
assert next_bigger(5113455566888) == 5113455568688
assert next_bigger(4769560370633) == 4769560373036

View File

@ -0,0 +1,15 @@
from src.codewars.kata_object_oriented_piracy import Ship
def test_piracy():
empty_ship = Ship(0, 0)
assert not empty_ship.is_worth_it()
boat = Ship(15, 20)
assert not boat.is_worth_it()
worthy_ship = Ship(100, 20)
assert worthy_ship.is_worth_it()
big_boat = Ship(35, 20)
assert not big_boat.is_worth_it()

View File

@ -0,0 +1,27 @@
from src.codewars.kata_pagination_helper import PaginationHelper
def test_pagination():
collection = ["a", "b", "c", "d", "e", "f"]
helper = PaginationHelper(collection, 4)
assert helper.page_count() == 2
assert helper.item_count() == 6
assert helper.page_item_count(0) == 4
assert helper.page_item_count(1) == 2
assert helper.page_item_count(2) == -1
assert helper.page_index(5) == 1
assert helper.page_index(2) == 0
assert helper.page_index(20) == -1
assert helper.page_index(-10) == -1
empty = PaginationHelper([], 10)
assert empty.item_count() == 0
assert empty.page_count() == 0
assert empty.page_index(0) == -1
assert empty.page_index(1) == -1
assert empty.page_index(-1) == -1
assert empty.page_item_count(0) == -1
assert empty.page_item_count(1) == -1
assert empty.page_item_count(-1) == -1

View File

@ -0,0 +1,13 @@
from src.codewars.kata_the_lamp import Lamp
def test_lamp():
my_lamp = Lamp("Blue")
assert my_lamp.color == "Blue"
assert not my_lamp.on
assert my_lamp.state() == "The lamp is off."
my_lamp.toggle_switch()
assert my_lamp.state() == "The lamp is on."
my_lamp.toggle_switch()
assert my_lamp.state() == "The lamp is off."

View File

View File

View File

@ -0,0 +1,7 @@
from src.codewars.kata_version_mamanger import VersionManager
def test_lamp():
v = VersionManager("1.1.1")
assert v.release() == "1.1.1"

View File

@ -0,0 +1,16 @@
from src.codewars.kata_vigenere_cipher_helper import VigenereCipher
def test_cipher_helper():
abc = "abcdefghijklmnopqrstuvwxyz"
key = "password"
c = VigenereCipher(key, abc)
assert c.encode("codewars") == "rovwsoiv"
assert c.decode("rovwsoiv") == "codewars"
assert c.encode("waffles") == "laxxhsj"
assert c.decode("laxxhsj") == "waffles"
assert c.encode("CODEWARS") == "CODEWARS"
assert c.decode("CODEWARS") == "CODEWARS"

View File

@ -0,0 +1,15 @@
from src.codewars.kata_who_the_most_money import Student, most_money
def test_most_money():
phil = Student("Phil", 2, 2, 1)
cam = Student("Cameron", 2, 2, 0)
geoff = Student("Geoff", 0, 3, 0)
assert most_money([cam, geoff, phil]) == "Phil"
phil = Student("Phil", 2, 2, 2)
cam = Student("Cameron", 2, 2, 2)
geoff = Student("Geoff", 2, 2, 2)
assert most_money([cam, geoff, phil]) == "all"