School Commit Init
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Let's write a menu-driven application. What does the menu look like?
|
||||
1. Generate (random) person names
|
||||
2. Exit
|
||||
"""
|
||||
import random
|
||||
|
||||
|
||||
def sort_names(name_list):
|
||||
pass
|
||||
|
||||
|
||||
def generate_names():
|
||||
count = int(input("How many names to generate? ")) # int() converts to integer
|
||||
|
||||
family_names = ["Albu", "Pop", "Gheorghe", "Morar", "Negrea", "Bodnar"] # list()
|
||||
given_names = ["Anca", "Elisa", "Ionut", "Vasile", "Ioana", "Rares"]
|
||||
result = []
|
||||
|
||||
for i in range(count):
|
||||
family_name = random.randint(0, len(family_names) - 1)
|
||||
given_name = random.randint(0, len(given_names) - 1)
|
||||
name = family_names[family_name] + " " + given_names[given_name]
|
||||
result.append(name)
|
||||
|
||||
# Let's print out the names
|
||||
print(result)
|
||||
return result
|
||||
|
||||
|
||||
def start():
|
||||
print("Welcome to seminar 2!")
|
||||
|
||||
while True:
|
||||
print("1. Generate (random) person names")
|
||||
print("2. Sort list of names")
|
||||
print("0. Exit")
|
||||
|
||||
opt = input(">")
|
||||
|
||||
names_list = []
|
||||
if opt == "1":
|
||||
names_list = generate_names()
|
||||
elif opt == "2":
|
||||
sort_names(names_list)
|
||||
elif opt == "0":
|
||||
return # break would have also been acceptable
|
||||
else:
|
||||
print("Bad command or file name")
|
||||
|
||||
|
||||
start()
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
"""
|
||||
1. Determine the time complexity of the following algorithms as a function of n.
|
||||
source: https://complex-systems-ai.com/en/algorithmic/corrected-exercises-time-complexity/
|
||||
"""
|
||||
|
||||
|
||||
# Overall
|
||||
# T(n) = 10 * n * 1 = 10 * n, so O(n) is n
|
||||
# O(n) is also any function growing faster than "n"
|
||||
# O(n) is also n*log(n), n^2, n^3 ,... , 2^n and so on
|
||||
# what about a low bound for f_1 ?
|
||||
# Theta(n) = n, and T(n) = n is both high-bound and low-bound
|
||||
def f_1(n: int):
|
||||
# T(n) = 1, for loop does not depend on n's value, O(n) = 1
|
||||
for i in range(10): # <=> range(0,10) -> from 0 to 9
|
||||
# for loop depends on n's value, T(n) = n, so complexity O(n)
|
||||
for j in range(n): # <=> range(0,n) -> from 0 to n - 1
|
||||
# print does not depend on n's value, so complexity is O(1)
|
||||
# 1 is like f(n) = 1
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# Overall
|
||||
# T(n) = 10 * n * 1, so O(n) = n, Theta(n) = n
|
||||
def f_2(n: int):
|
||||
# does not actually depend on n's value
|
||||
for i in range(n, n + 10): # loops between n .. n + 9 (10 values)
|
||||
# inner loop depends on n linearly, so its O(n)
|
||||
for j in range(n):
|
||||
# print takes constant time to run, so O(1)
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# T(n) = n * n * 1 => O(n) = n^2
|
||||
def f_3(n: int):
|
||||
# T(n) = n for the outer loop (linear time)
|
||||
for i in range(1, n):
|
||||
# Inner loop depends on n, starts with for j in range(1, n)
|
||||
# inner loop calculation:
|
||||
# 1 + 2 + ... + n - 1
|
||||
# 2 + ... + n - 1
|
||||
# ...
|
||||
# ... n - 1
|
||||
for j in range(i, n):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# Overall
|
||||
# T(n) = n * n * 1 => O(n) is n^2
|
||||
def f_4(n: int):
|
||||
# outer loop is O(n), with T(n) = n * inner loop
|
||||
for i in range(n):
|
||||
# j depends on n, final iteration is between 0 and 2 * (n-1) + 1
|
||||
for j in range(2 * i + 1):
|
||||
print("Hello World") # O(1) as usual :)
|
||||
|
||||
|
||||
# T(n) = n^2 * n^2 * 1
|
||||
# O(n) is n^4
|
||||
#
|
||||
# Question:
|
||||
# let's say for n = 1000, it takes 1 ms
|
||||
# how long do we expect it to run for n = 2000?
|
||||
# answer: we doubled the size of the input => 2 ^ 4 times more => 16 ms.
|
||||
# if n = 3000
|
||||
# 3 ^ 4 => 81 ms.
|
||||
def f_5(n: int):
|
||||
# T(n) = n^2 for outer loop
|
||||
for i in range(n ** 2):
|
||||
# T(n) = n^2 for inner loop
|
||||
for j in range(i):
|
||||
print("Hello World") # O(1)
|
||||
|
||||
|
||||
def f_6(n: int):
|
||||
for i in range(n):
|
||||
# How many times can we multiply by 2 in order to go from 1 to n?
|
||||
# Answer is log(n) times
|
||||
t = 1
|
||||
while t < n:
|
||||
print("Hello World")
|
||||
t *= 2 # log is base 2 as we multiply by 2
|
||||
|
||||
|
||||
"""
|
||||
1. Time complexity in both "n" and "m"
|
||||
"""
|
||||
|
||||
|
||||
# overall ??
|
||||
# T(n,m) = n + m, so its O(n+m)
|
||||
# another example -- when merging arrays of lengths n and m, O(n+m) = n + m
|
||||
# as we look at each merged element exactly one time
|
||||
def f_7(n, m: int):
|
||||
# T(n) = n, so for the loop O(n) = n
|
||||
for i in range(0, n):
|
||||
print("Hello World")
|
||||
# T(n) = m, so for the loop O(n) = m
|
||||
for j in range(0, m):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# T(n) = n + n = 2*n
|
||||
# O(n) = n
|
||||
def f_8(n, m: int):
|
||||
for i in range(0, n):
|
||||
print("Hello World")
|
||||
for j in range(0, n):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# O(n) is n^2
|
||||
def f_9(n: int):
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
print("Hello World")
|
||||
for k in range(n):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# O(n) is n^2
|
||||
def f_10(n: int):
|
||||
for i in range(n):
|
||||
for j in range(n, i, -1):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
"""
|
||||
Analyze the time and space complexity
|
||||
"""
|
||||
|
||||
|
||||
# O(n) is n * log(n) (log is base 3)
|
||||
# extra space complexity is O(1)
|
||||
def f_11(data: list):
|
||||
data_sum = 0
|
||||
for el in data:
|
||||
j = len(data)
|
||||
while j > 1:
|
||||
data_sum += el * j
|
||||
j = j // 3 # we get log base 3 of n (j always stars at n)
|
||||
return data_sum
|
||||
|
||||
|
||||
"""
|
||||
time complexity
|
||||
T(n) = 1, if n <= 1
|
||||
T(n) = 2 * T(n/2) + 1
|
||||
|
||||
T(n) = 2 * T(n/2) + 1
|
||||
T(n/2) = 2 * T(n/4) + 1
|
||||
T(n/4) = 2 * T(n/8) + 1
|
||||
|
||||
T(n) = 2 * T(n/2) + 1 = 2 * [2 * T(n/4) + 1] + 1 = 4 * T(n/4) + 2 + 1 =
|
||||
4 * [2 * T(n/8) + 1] + 2 + 1 = 8 * T(n/8) + 4 + 2 + 1
|
||||
|
||||
T(n) = 8 * T(n/8) + 4 + 2 + 1 ... and so on
|
||||
we know T(0) = T(1) = 1
|
||||
let's say we have k natural number so that 2 ^ k = n, k = log(n)
|
||||
|
||||
T(n) = k * T(1) + sum of the powers of 2 (2^0 + 2^1 + ... + 2^(k-1))
|
||||
T(n) = k * 1 + 2^k - 1 = log(n) + n => O(n) is n
|
||||
|
||||
extra space complexity
|
||||
T(1) = 1
|
||||
T(n) = 2 * T(n/2)
|
||||
"""
|
||||
|
||||
|
||||
def f_12(data: list):
|
||||
if len(data) == 0:
|
||||
return 0
|
||||
if len(data) == 1:
|
||||
return data[0]
|
||||
m = len(data) // 2
|
||||
return f_12(data[:m]) + f_12(data[m:]) # T(n/2) + T(n/2)
|
||||
|
||||
|
||||
def f_13(n: int):
|
||||
s = 0
|
||||
for i in range(1, n ** 2):
|
||||
j = i
|
||||
while j != 0:
|
||||
s = s + j - 10 * j // 10
|
||||
j //= 10
|
||||
return s
|
||||
|
||||
|
||||
def f_14(n, i: int):
|
||||
if n > 1:
|
||||
i *= 2
|
||||
m = n // 2
|
||||
f_14(m, i - 2)
|
||||
f_14(m, i - 1)
|
||||
f_14(m, i + 2)
|
||||
f_14(m, i + 1)
|
||||
else:
|
||||
print(i)
|
||||
|
||||
|
||||
"""
|
||||
Analyze the algorithm's time complexity. Write an equivalent algorithm with
|
||||
a strictly better time complexity
|
||||
"""
|
||||
|
||||
|
||||
def f_15(data: list):
|
||||
i = 0
|
||||
j = 0
|
||||
m = 0
|
||||
c = 0
|
||||
while i < len(data):
|
||||
if data[i] == data[j]:
|
||||
c += 1
|
||||
j += 1
|
||||
if j >= len(data):
|
||||
if c > m:
|
||||
m = c
|
||||
c = 0
|
||||
i += 1
|
||||
j = i
|
||||
return m
|
||||
|
||||
|
||||
"""
|
||||
What is the time complexity when the following algorithm is implemented via linear exponentiation. How can this be
|
||||
optimized and how will that improve the complexity?
|
||||
"""
|
||||
|
||||
|
||||
def f_16(x, n: int):
|
||||
"""
|
||||
The algorithms returns x ** n
|
||||
:param x:
|
||||
:param n:
|
||||
:return:
|
||||
"""
|
||||
# TODO Implement me
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Implement and discuss the complexity of merge sort
|
||||
"""
|
||||
|
||||
|
||||
def merge_sort(data: list):
|
||||
# TODO Implement me
|
||||
pass
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
"""
|
||||
Divide & Conquer
|
||||
|
||||
1. Divide - the problem into smaller subproblems (not overlapping)
|
||||
2. Conquer - solve the "small" problems directly (no d&c)
|
||||
3. Combine - "small" problem results into the solution of the original one
|
||||
|
||||
Binary search
|
||||
1. Divide => decide in which half of the array to continue
|
||||
2. Conquer => find the element/run out of list (left >= right)
|
||||
3. Combine => nothing to do here
|
||||
|
||||
Merge Sort
|
||||
1. Divide => split the array into two halves
|
||||
2. Conquer => single-element arrays are already sorted
|
||||
3. Combine => merge arrays until we get to the large, sorted one
|
||||
|
||||
Quick Sort
|
||||
1. Divide => select a pivot, partition the array around it
|
||||
2. Conquer => continue for the subarrays left and right of pivot
|
||||
3. Combine => nothing to do here
|
||||
"""
|
||||
|
||||
"""
|
||||
1. Find the smallest number in a list (chip & conquer, divide in halves, recursive vs non-recursive)
|
||||
a. Chip & conquer, recursive
|
||||
b. Divide in halves, non-recursive
|
||||
c. Divide in halves, recursive
|
||||
"""
|
||||
import random
|
||||
|
||||
|
||||
def _find_min_impl(array: list, index: int):
|
||||
"""
|
||||
Return the smallest element in the array.
|
||||
:param index:
|
||||
:param array:
|
||||
:return: Smallest element in array. None if array is empty.
|
||||
"""
|
||||
if len(array) - 1 == index:
|
||||
return array[index]
|
||||
return min(array[index], _find_min_impl(array, index + 1))
|
||||
|
||||
|
||||
def find_min(array: list):
|
||||
if len(array) == 0:
|
||||
return None
|
||||
return _find_min_impl(array, 0)
|
||||
|
||||
|
||||
def test_find_min():
|
||||
# length of random list
|
||||
n = random.randint(0, 50)
|
||||
data = []
|
||||
for i in range(n):
|
||||
data.append(random.randint(1, 10))
|
||||
|
||||
# insert the known minimal element in a random, but known position
|
||||
index = random.randint(0, len(data) - 1)
|
||||
data.insert(index, -1)
|
||||
|
||||
print(data)
|
||||
result = find_min(data)
|
||||
assert result == -1, (result, index)
|
||||
|
||||
|
||||
# test_find_min()
|
||||
|
||||
"""
|
||||
2. Exponential search
|
||||
a. Generate a pseudo-random array of increasing elements
|
||||
b. Implement exponential search
|
||||
c. Implement binary search
|
||||
d. Driver & test functions
|
||||
"""
|
||||
|
||||
|
||||
def random_number_gen():
|
||||
n = random.randint(50, 1000)
|
||||
x = random.randint(0, 100)
|
||||
array = [x]
|
||||
for i in range(1, n):
|
||||
array.append(x + i)
|
||||
return array
|
||||
|
||||
|
||||
def random_number_gen_v2():
|
||||
array = [random.randint(0, 10)]
|
||||
for i in range(random.randint(5, 10)):
|
||||
array.append(array[-1] + random.randint(0, 3))
|
||||
return array
|
||||
|
||||
|
||||
# print(random_number_gen())
|
||||
# print(random_number_gen_v2())
|
||||
|
||||
|
||||
# TODO Improve this for cases when the searched element is beyond the (min, max)
|
||||
# of the list
|
||||
def exponential_search(data: list, n: int):
|
||||
if data[0] == n:
|
||||
return 0, 0
|
||||
i = 1
|
||||
# Python will not check the second term of the expression if the first
|
||||
# is false
|
||||
while i < len(data) and data[i] < n:
|
||||
if data[i] == n:
|
||||
return i, i
|
||||
else:
|
||||
i *= 2
|
||||
return i // 2, min(i, len(data) - 1)
|
||||
|
||||
|
||||
def exponential_search_v2(data: list, n: int):
|
||||
"""
|
||||
Search element n in list
|
||||
:param data:
|
||||
:param n:
|
||||
:return: The position of element n, -1 if not found
|
||||
"""
|
||||
if data[0] == n:
|
||||
return 0
|
||||
i = 1
|
||||
while i < len(data) and data[i] < n:
|
||||
i *= 2
|
||||
|
||||
for idx in range(i // 2, min(i + 1, len(data) - 1)):
|
||||
if data[idx] == n:
|
||||
return idx
|
||||
return -1
|
||||
|
||||
# TODO Start with linear search
|
||||
# return i // 2, min(i, len(data) - 1)
|
||||
|
||||
|
||||
# data = random_number_gen_v2()
|
||||
# FIXME does not work in this case
|
||||
data = [0, 0, 2, 2, 3, 6, 7, 10]
|
||||
print(data)
|
||||
print(exponential_search_v2(data, 10))
|
||||
|
||||
"""
|
||||
3. Calculate the r-th root of a given number x with a given precision p
|
||||
"""
|
||||
|
||||
"""
|
||||
4. Calculate the maximum subarray sum (subarray = elements having continuous indices)
|
||||
a. Naive implementation
|
||||
b. Divide & conquer implementation
|
||||
|
||||
e.g.
|
||||
for data = [-2, -5, 6, -2, -3, 1, 5, -6], maximum subarray sum is 7.
|
||||
"""
|
||||
|
||||
"""
|
||||
Backtracking
|
||||
"""
|
||||
|
||||
"""
|
||||
5. Recursive implementation for permutations
|
||||
"""
|
||||
|
||||
|
||||
def consistent(x):
|
||||
"""
|
||||
Determines whether the current partial array can lead to a solution
|
||||
"""
|
||||
return len(set(x)) == len(x)
|
||||
|
||||
|
||||
def solution(x, n):
|
||||
"""
|
||||
Determines whether we have a solution
|
||||
"""
|
||||
return len(x) == n
|
||||
|
||||
|
||||
def solution_found(x):
|
||||
"""
|
||||
What to do when a solution is found
|
||||
"""
|
||||
print("Solution: ", x)
|
||||
|
||||
|
||||
def bkt_rec(x, n):
|
||||
"""
|
||||
Backtracking algorithm for permutations problem, recursive implementation
|
||||
"""
|
||||
x.append(0)
|
||||
for i in range(0, n):
|
||||
x[len(x) - 1] = i
|
||||
if consistent(x):
|
||||
if solution(x, n):
|
||||
solution_found(x)
|
||||
else:
|
||||
bkt_rec(x[:], n)
|
||||
|
||||
|
||||
# bkt_rec([], 4)
|
||||
|
||||
"""
|
||||
6. Change the code for generating the permutation above to work for the n-Queen problem
|
||||
"""
|
||||
|
||||
"""
|
||||
A Latin square is an n × n square filled with n different symbols, each occurring exactly once in each row and exactly
|
||||
once in each column
|
||||
|
||||
7. Generate all the N x N Latin squares for a given number N.
|
||||
|
||||
8. Generate all reduced N x N Latin squares for a given number N. In a reduced Latin square, the elements of the first
|
||||
row and column are sorted.
|
||||
"""
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
Dynamic programming
|
||||
"""
|
||||
|
||||
"""
|
||||
1. Calculate the maximum subarray sum (subarray = elements having
|
||||
continuous indices)
|
||||
|
||||
e.g.
|
||||
for data = [-2, -5, 6, -2, -3, 1, 5, -6], maximum subarray sum is 7.
|
||||
"""
|
||||
import sys
|
||||
|
||||
|
||||
def max_crossing_sum(array: list, left, mid, right: int):
|
||||
left_max = -sys.maxsize
|
||||
left_sum = 0
|
||||
|
||||
for i in range(mid, left, -1):
|
||||
left_sum += array[i]
|
||||
left_max = max(left_max, left_sum)
|
||||
|
||||
right_max = -sys.maxsize
|
||||
right_sum = 0
|
||||
|
||||
for i in range(mid + 1, right):
|
||||
right_sum += array[i]
|
||||
right_max = max(right_max, right_sum)
|
||||
|
||||
return left_max + right_max
|
||||
|
||||
|
||||
def max_subarray_dc(array: list, left, right: int):
|
||||
if left >= right:
|
||||
return array[left]
|
||||
|
||||
mid = (left + right) // 2
|
||||
|
||||
# T(n) = 2 * T(n/2) + n (looks a lot like merge sort)
|
||||
return max(max_subarray_dc(array, left, mid),
|
||||
max_subarray_dc(array, mid + 1, right),
|
||||
max_crossing_sum(array, left, mid, right))
|
||||
|
||||
|
||||
array = [-2, -5, 6, -2, -3, 1, 5, -6]
|
||||
|
||||
|
||||
# print(max_subarray_dc(array, 0, len(array) - 1))
|
||||
|
||||
# import array as arr
|
||||
|
||||
|
||||
def max_subarray_dp(array: list):
|
||||
s = 0
|
||||
smax = -9999999999999
|
||||
init = 1
|
||||
|
||||
# v = arr.array('i', [-2, -5, 6, -2, -3, 1, 5, -6])
|
||||
for i in range(0, len(array)):
|
||||
# choose whether we extend subarray or start a new one
|
||||
# max_here = max(array[i], max_here + array[i])
|
||||
# check for new global maximum
|
||||
# max_global = max(max_global,max_here)
|
||||
|
||||
s += array[i]
|
||||
# check that we have a new maximum
|
||||
if s > smax:
|
||||
smax = s
|
||||
# don't carry over <0 values
|
||||
if s < 0:
|
||||
s = 0
|
||||
init = i + 1
|
||||
return smax
|
||||
|
||||
|
||||
# print(max_subarray_dp(array))
|
||||
|
||||
"""
|
||||
2. Count in how many ways we can provide change to a given sum of money (N), when provided infinite
|
||||
supply of given coin denominations.
|
||||
|
||||
e.g. Let's say N = 10, and we have coins of values (1, 5, 10); we can give change in 4 ways (10, 5 + 5, 5 + 1 + ...
|
||||
and 1 + ... + 1)
|
||||
"""
|
||||
|
||||
"""
|
||||
3. 0-1 Knapsack problem. Given the weights and values of N items, put them
|
||||
in a knapsack having capacity W so that you
|
||||
maximize the value of the stored items. Items cannot be broken up
|
||||
(0-1 property)
|
||||
"""
|
||||
|
||||
|
||||
def knapsack_01(W: int, weights, values: list, current: int):
|
||||
if current < 0:
|
||||
return 0
|
||||
|
||||
value_include = 0
|
||||
if W - weights[current] >= 0:
|
||||
value_include = values[current] + knapsack_01(W - weights[current], weights, values, current - 1)
|
||||
value_exclude = knapsack_01(W, weights, values, current - 1)
|
||||
|
||||
# T(n) = 2 * T(n-1) + 1
|
||||
return max(value_include, value_exclude)
|
||||
|
||||
|
||||
W = 10
|
||||
weights = [1, 2, 3, 5, 7]
|
||||
values = [2, 4, 8, 7, 3]
|
||||
print(knapsack_01(W, weights, values, len(weights) - 1))
|
||||
|
||||
"""
|
||||
4. Gold mine problem (a.k.a checkerboard problem)
|
||||
https://www.geeksforgeeks.org/gold-mine-problem
|
||||
"""
|
||||
+258
@@ -0,0 +1,258 @@
|
||||
"""
|
||||
Write an application that manages a list of circles.
|
||||
Each circle has a unique center (x,y - ints) and a positive radius (int).
|
||||
The application will have a menu-driven user interface and will provide the following features:
|
||||
|
||||
1. Add a circle
|
||||
- adds the given circle to the list.
|
||||
- error if circle with given center already exists, the center
|
||||
or radius not given, empty or radius < 0
|
||||
|
||||
2. Delete a circle
|
||||
- deletes the circle with the given center
|
||||
- error if non-existing center is given
|
||||
|
||||
3. Show all circles
|
||||
- shows all circles in descending order of their radius
|
||||
|
||||
4. Show circles that intersect a given one
|
||||
- select a circle from the list of existing circles
|
||||
- print those which intersect it
|
||||
(bonus: sort printed circles by descending order of radius)
|
||||
|
||||
5. exit
|
||||
- exit the program
|
||||
|
||||
Observations:
|
||||
- Add 10 random circles at program startup
|
||||
- Write specifications for non-UI functions
|
||||
- Each function does one thing only, and communicates via parameters and return value
|
||||
- The program reports errors to the user. It must also report errors from non-UI functions!
|
||||
- Make sure you understand the circle's representation
|
||||
- Try to reuse functions across functionalities (Less code to write and test)
|
||||
- Don't use global variables!
|
||||
"""
|
||||
import random
|
||||
|
||||
|
||||
#
|
||||
# Write the implementation for Seminar 06 in this file
|
||||
#
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# Functions to deal with circles -- list representation
|
||||
# -> There should be no print or input statements in this section
|
||||
# -> Each function should do one thing only
|
||||
# -> Functions communicate using input parameters and their return values
|
||||
#
|
||||
|
||||
# circle centered at (1,2) with radius 3 => [1, 2, 3]
|
||||
def new_circle(x, y, radius: int):
|
||||
return [x, y, radius]
|
||||
|
||||
|
||||
def get_center(circle):
|
||||
return [circle[0], circle[1]]
|
||||
|
||||
|
||||
def get_radius(circle):
|
||||
return circle[2]
|
||||
|
||||
|
||||
def to_str(circle):
|
||||
"""
|
||||
Return the circle's representation as a string
|
||||
:param circle: The circle
|
||||
:return: A string; for circle centered (1,2), radius 4,
|
||||
return "circle at (1,2) radius 4"
|
||||
"""
|
||||
return "circle at (" + str(circle[0]) + "," + str(circle[1]) \
|
||||
+ ") radius " + str(circle[2])
|
||||
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# Functions to deal with circles -- dict representation
|
||||
# -> There should be no print or input statements in this section
|
||||
# -> Each function should do one thing only
|
||||
# -> Functions communicate using input parameters and their return values
|
||||
#
|
||||
# TODO Copy function signatures from list representation and implement them
|
||||
|
||||
# circle centered at (1,2) with radius 3 => {"x": 1,"y": 2,"radius": 3}
|
||||
# def new_circle(x, y, radius: int):
|
||||
# return {"x": x, "y": y, "radius": radius}
|
||||
#
|
||||
#
|
||||
# def get_center(circle):
|
||||
# return circle.pop("radius")
|
||||
#
|
||||
#
|
||||
# def get_radius(circle):
|
||||
# return circle["radius"]
|
||||
#
|
||||
#
|
||||
# def to_str(circle):
|
||||
# """
|
||||
# Return the circle's representation as a string
|
||||
# :param circle: The circle
|
||||
# :return: A string; for circle centered (1,2), radius 4,
|
||||
# return "circle at (1,2) radius 4"
|
||||
# """
|
||||
# return "circle at (" + str(circle["x"]) + "," + str(circle["y"]) \
|
||||
# + ") radius " + str(circle["radius"])
|
||||
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# Functions that deal with the required functionalities properties
|
||||
# -> There should be no print or input statements in this section
|
||||
# -> Each function should do one thing only
|
||||
# -> Functions communicate using input parameters and their return values
|
||||
#
|
||||
def make_random_circles(count: int):
|
||||
"""
|
||||
Create count random circles
|
||||
:return: The list of newly created circles
|
||||
"""
|
||||
assert count < 40 ** 2
|
||||
|
||||
circles_list = []
|
||||
centers_x = list(range(-20, 20))
|
||||
centers_y = list(range(-20, 20))
|
||||
|
||||
while count > 0:
|
||||
x = random.choice(centers_x)
|
||||
y = random.choice(centers_y)
|
||||
centers_x.remove(x)
|
||||
centers_y.remove(y)
|
||||
radius = random.randint(1, 20)
|
||||
circles_list.append(new_circle(x, y, radius))
|
||||
count -= 1
|
||||
return circles_list
|
||||
|
||||
|
||||
def add_circle(circles_list: list, new_circle):
|
||||
"""
|
||||
Adds the new_circle to the list of circles
|
||||
:param circles_list: List of circles maintained by the program
|
||||
:param new_circle: The new circle to add
|
||||
:return: 0 on success, 1 if circle with given center already exists
|
||||
"""
|
||||
if new_circle in circles_list:
|
||||
return 1
|
||||
circles_list.append(new_circle)
|
||||
return 0
|
||||
|
||||
|
||||
def delete_circle(circles_list: list, circle):
|
||||
"""
|
||||
Deletes a circle from the list of circles
|
||||
:param circles_list: List of circles maintained by the program
|
||||
:param circle: The circle to delete
|
||||
:return: 0 on success, 1 if the circle does not exist
|
||||
"""
|
||||
if circle not in circles_list:
|
||||
return 1
|
||||
circles_list.remove(circle)
|
||||
return 0
|
||||
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# UI section
|
||||
# Write all functions that have input or print statements here
|
||||
# Ideally, this section should not contain any calculations relevant to program functionalities
|
||||
#
|
||||
def read_circle(circles_list: list):
|
||||
"""
|
||||
Reads a circle from the console; Circle center must be int X,Y coordinates,
|
||||
radius must be > 0 integer (keep reading until true)
|
||||
:param circles_list: List of circles maintained by the program.
|
||||
:return: The new circle.
|
||||
"""
|
||||
while True:
|
||||
print()
|
||||
|
||||
x = input("Enter new X coordinate: ")
|
||||
if not x.lstrip('-').isdigit():
|
||||
print("X must be an integer.")
|
||||
continue
|
||||
x = int(x)
|
||||
|
||||
y = input("Enter new Y coordinate: ")
|
||||
if not y.lstrip('-').isdigit():
|
||||
print("X must be an integer.")
|
||||
continue
|
||||
y = int(y)
|
||||
|
||||
radius = input("Enter radius (type 0 to stop reading...): ")
|
||||
if radius == "0":
|
||||
print("Done reading.")
|
||||
break
|
||||
if not radius.isdigit():
|
||||
print("Radius must be an integer greater than 0!")
|
||||
continue
|
||||
radius = int(radius)
|
||||
|
||||
return new_circle(x, y, radius)
|
||||
|
||||
|
||||
def show_circles(circles_list):
|
||||
sorted_list = sorted(circles_list, key=lambda c: get_radius(c), reverse=True)
|
||||
print("Current list of circles:\n" + ",\n".join(map(to_str, sorted_list)))
|
||||
|
||||
|
||||
def start():
|
||||
# TODO this is the program's entry point
|
||||
# What do to here !!!???
|
||||
# 1. Print out main menu in a loop
|
||||
# 2. Keep the list of circles
|
||||
# 3. Call the function corresponding to user choice
|
||||
# 4. Print out error messages coming from functions
|
||||
circles_list = make_random_circles(10)
|
||||
while True:
|
||||
print()
|
||||
print("Welcome to Circle Manager 9000.")
|
||||
print("Please type the number of the operation to execute.")
|
||||
print()
|
||||
print("1. Add a bunch of circles.")
|
||||
print("2. Delete a circle.")
|
||||
print()
|
||||
print("3. Show a list of circles in descending order of radius.")
|
||||
print("4. Show a list of circles which intersect another given circle.")
|
||||
print()
|
||||
print("5. Exit")
|
||||
operation = input()
|
||||
if operation == "1":
|
||||
circle = read_circle(circles_list)
|
||||
exists = add_circle(circles_list, circle)
|
||||
if exists:
|
||||
print("Circle already exists in the list!")
|
||||
else:
|
||||
print("OK")
|
||||
elif operation == "2":
|
||||
circle = read_circle(circles_list)
|
||||
not_exists = delete_circle(circles_list, circle)
|
||||
if not_exists:
|
||||
print("Circle does not exist in the list!")
|
||||
else:
|
||||
print("OK")
|
||||
elif operation == "3":
|
||||
show_circles(circles_list)
|
||||
elif operation == "4":
|
||||
# TODO
|
||||
pass
|
||||
elif operation == "5":
|
||||
print("Goodbye!")
|
||||
return
|
||||
else:
|
||||
print("Unknown operation", operation, "- please try again.")
|
||||
# print()
|
||||
# print("Press Enter to continue...")
|
||||
# input()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
start()
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
Tic Tac Toe board game
|
||||
- Human vs. computer player
|
||||
- Human plays first
|
||||
- Computer always plays valid moves
|
||||
|
||||
Command-driven UI !!!???
|
||||
play 1,1 # (1,1) is center square on the 3x3 board
|
||||
takeback # takes back human player's last move
|
||||
ragequit
|
||||
|
||||
What to look out for today:
|
||||
1. Modular programming
|
||||
2. Test-Driven Development (first examples)
|
||||
3. Using Exceptions
|
||||
"""
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
def create_board():
|
||||
"""
|
||||
Create the Tic Tac Toe board
|
||||
:return: The empty game board
|
||||
"""
|
||||
board = []
|
||||
for i in range(3):
|
||||
board.append([None, None, None])
|
||||
return board
|
||||
|
||||
|
||||
def get_position_on_board(board, row, col: int):
|
||||
"""
|
||||
Return the symbol at (row,col)
|
||||
:param row:
|
||||
:param col:
|
||||
:return: X, O or None
|
||||
Raise ValueError if (row,col) outside of board
|
||||
"""
|
||||
if row not in [0, 1, 2] or col not in [0, 1, 2]:
|
||||
raise ValueError("Position not on board - (" + str(row) + "," + str(col) + ")")
|
||||
return board[row][col]
|
||||
|
||||
|
||||
def str_position(board, row, col):
|
||||
symbol = get_position_on_board(board, row, col)
|
||||
|
||||
# This is <=> to what is below
|
||||
# return ' ' if symbol is None else symbol
|
||||
if symbol is not None:
|
||||
return symbol
|
||||
else:
|
||||
return ' '
|
||||
|
||||
|
||||
def str_board(board):
|
||||
"""
|
||||
Return the board's str representation
|
||||
:param board: The game board
|
||||
:return: In str form
|
||||
"""
|
||||
gp = str_position
|
||||
result = "-----\n"
|
||||
for i in range(3):
|
||||
result += gp(board, i, 0) + "|" + gp(board, i, 1) + "|" + gp(board, i, 2) + "\n"
|
||||
result += "-----\n"
|
||||
return result
|
||||
|
||||
|
||||
def make_move_on_board(board, symbol, row, column):
|
||||
"""
|
||||
Represent a move on the board
|
||||
:param board: The game board
|
||||
:param symbol: One of ['X', 'O']
|
||||
:param row, column: Position to play
|
||||
:return: None
|
||||
Raise ValueError if invalid symbol, already occupied square or play
|
||||
outside of board
|
||||
"""
|
||||
if symbol not in ['X', 'O']:
|
||||
raise ValueError("Invalid symbol")
|
||||
if row not in [0, 1, 2] or column not in [0, 1, 2]:
|
||||
raise ValueError("Move outside board")
|
||||
if get_position_on_board(board, row, column) is not None:
|
||||
raise ValueError("Cannot overwrite board at (" + str(row) + "," + str(column) + ")")
|
||||
# This works as a setter function
|
||||
board[row][column] = symbol
|
||||
|
||||
|
||||
def test_board():
|
||||
board = create_board()
|
||||
# Check that the board is empty
|
||||
for i in range(3):
|
||||
for j in range(3):
|
||||
assert get_position_on_board(board, i, j) is None
|
||||
# Make some moves on the board
|
||||
make_move_on_board(board, 'X', 1, 1)
|
||||
assert get_position_on_board(board, 1, 1) == 'X'
|
||||
|
||||
make_move_on_board(board, 'O', 0, 0)
|
||||
assert get_position_on_board(board, 0, 0) == 'O'
|
||||
|
||||
# Check that moving outside of board raises a ValueError
|
||||
try:
|
||||
make_move_on_board(board, 'X', 3, 3)
|
||||
assert False
|
||||
except ValueError:
|
||||
assert True
|
||||
|
||||
# Check that we cannot overwrite a square
|
||||
try:
|
||||
make_move_on_board(board, 'O', 0, 0)
|
||||
assert False
|
||||
except ValueError:
|
||||
assert True
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import board
|
||||
|
||||
|
||||
def human_move(game_board, row, col):
|
||||
"""
|
||||
Record the human's move on the board
|
||||
:param game_board:
|
||||
:param row:
|
||||
:param col:
|
||||
:return:
|
||||
"""
|
||||
board.make_move_on_board(game_board, 'X', row, col)
|
||||
|
||||
|
||||
def computer_move(game_board):
|
||||
"""
|
||||
Determine where the computer plays and make the move
|
||||
:param game_board:
|
||||
:return: The position where computer moved
|
||||
"""
|
||||
for row in [0, 1, 2]:
|
||||
for col in [0, 1, 2]:
|
||||
if board.get_position_on_board(game_board, row, col) is None:
|
||||
board.make_move_on_board(game_board, 'O', row, col)
|
||||
return row, col
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import board
|
||||
import game
|
||||
|
||||
|
||||
def start_game():
|
||||
game_board = board.create_board()
|
||||
humans_turn = True
|
||||
|
||||
while True:
|
||||
print(board.str_board(game_board))
|
||||
|
||||
if humans_turn:
|
||||
user_input = input(">")
|
||||
command, params = user_input.split(" ", maxsplit=1)
|
||||
if command == 'play':
|
||||
params = params.split(",")
|
||||
try:
|
||||
row = int(params[0])
|
||||
col = int(params[1])
|
||||
game.human_move(game_board, row, col)
|
||||
except ValueError as ve:
|
||||
# TODO Allow user to attempt to make a move
|
||||
print(ve)
|
||||
elif command == 'takeback':
|
||||
# Take back human's last move, if possible
|
||||
pass
|
||||
elif command == 'ragequit':
|
||||
print("Computer wins!")
|
||||
return
|
||||
else:
|
||||
print("Invalid command")
|
||||
else:
|
||||
# Computer player's turn
|
||||
row, col = game.computer_move(game_board)
|
||||
print("Computer moved at (" + str(row) + "," + str(col) + ")")
|
||||
humans_turn = not humans_turn
|
||||
|
||||
|
||||
start_game()
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
"""
|
||||
Turning seminar 7 game into object-oriented representation
|
||||
|
||||
1. Let's turn the board module functions into the Board class
|
||||
How can we protect class attributes from outside changes?
|
||||
|
||||
protecting class attributes from outside change:
|
||||
C#, Java, Kotlin, C++ -> public, protected, private (keywords)
|
||||
public -> anyone can access and change
|
||||
private -> only class functions can access and change
|
||||
protected -> only class functions and derived classes can access and change
|
||||
(default)
|
||||
|
||||
Python
|
||||
<var. name> -> public
|
||||
_<var. name> -> private (convention)
|
||||
__<var. name> -> private (convention!?, name mangling)
|
||||
|
||||
a. Created the game_board class
|
||||
b. changed create_board to __init__
|
||||
c. changed str_board to __str__
|
||||
d. added remaining methods to class
|
||||
e. shortened their names as it makes more sense
|
||||
(e.g., make_move_on_board -> move)
|
||||
f. rewrote test_board to work with class
|
||||
|
||||
2. Let's do the same to the game module
|
||||
a. Create the game class and add the board as a constructor parameter
|
||||
b. Add the *_move methods into the class
|
||||
c. Update them to use the private __board attribute
|
||||
|
||||
3. Update the UI module
|
||||
4. Added the skynet_level_2 random move strategy :)
|
||||
"""
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
class game_board:
|
||||
def __init__(self):
|
||||
"""
|
||||
Create the Tic Tac Toe board
|
||||
:return: The empty game board
|
||||
"""
|
||||
# board is a local variable in __init__
|
||||
# board = []
|
||||
# Let's make the game_board object "remember it"
|
||||
self.__board = []
|
||||
|
||||
for i in range(3):
|
||||
self.__board.append([None, None, None])
|
||||
# The interpreter returns the object reference in __init__
|
||||
# return board
|
||||
|
||||
def __str_position(self, row, col):
|
||||
symbol = self.get_position(row, col)
|
||||
|
||||
# This is <=> to what is below
|
||||
# return ' ' if symbol is None else symbol
|
||||
if symbol is not None:
|
||||
return symbol
|
||||
else:
|
||||
return ' '
|
||||
|
||||
def get_position(self, row, col: int):
|
||||
"""
|
||||
Return the symbol at (row,col)
|
||||
:param row:
|
||||
:param col:
|
||||
:return: X, O or None
|
||||
Raise ValueError if (row,col) outside of board
|
||||
"""
|
||||
if row not in [0, 1, 2] or col not in [0, 1, 2]:
|
||||
raise ValueError("Position not on board - (" + str(row) + "," + str(col) + ")")
|
||||
return self.__board[row][col]
|
||||
|
||||
def move(self, symbol, row, column):
|
||||
"""
|
||||
Represent a move on the board
|
||||
:param board: The game board
|
||||
:param symbol: One of ['X', 'O']
|
||||
:param row, column: Position to play
|
||||
:return: None
|
||||
Raise ValueError if invalid symbol, already occupied square or play
|
||||
outside of board
|
||||
"""
|
||||
if symbol not in ['X', 'O']:
|
||||
raise ValueError("Invalid symbol")
|
||||
if row not in [0, 1, 2] or column not in [0, 1, 2]:
|
||||
raise ValueError("Move outside board")
|
||||
if self.get_position(row, column) is not None:
|
||||
raise ValueError("Cannot overwrite board at (" + str(row) + "," + str(column) + ")")
|
||||
# This works as a setter function
|
||||
self.__board[row][column] = symbol
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Return the board's str representation
|
||||
:param board: The game board
|
||||
:return: In str form
|
||||
"""
|
||||
gp = self.__str_position
|
||||
result = "-----\n"
|
||||
for i in range(3):
|
||||
result += gp(i, 0) + "|" + gp(i, 1) + "|" + gp(i, 2) + "\n"
|
||||
result += "-----\n"
|
||||
return result
|
||||
|
||||
|
||||
def test_board():
|
||||
board = game_board()
|
||||
# Check that the board is empty
|
||||
for i in range(3):
|
||||
for j in range(3):
|
||||
assert board.get_position(i, j) is None
|
||||
# Make some moves on the board
|
||||
board.move('X', 1, 1)
|
||||
assert board.get_position(1, 1) == 'X'
|
||||
|
||||
board.move('O', 0, 0)
|
||||
assert board.get_position(0, 0) == 'O'
|
||||
|
||||
# Check that moving outside of board raises a ValueError
|
||||
try:
|
||||
board.move('X', 3, 3)
|
||||
assert False
|
||||
except ValueError:
|
||||
assert True
|
||||
|
||||
# Check that we cannot overwrite a square
|
||||
try:
|
||||
board.move('O', 0, 0)
|
||||
assert False
|
||||
except ValueError:
|
||||
assert True
|
||||
|
||||
|
||||
gb = game_board()
|
||||
|
||||
# data = list() # []
|
||||
# data.append(1)
|
||||
# list.append(data, 1)
|
||||
# print(str(data))
|
||||
|
||||
# gb = game_board()
|
||||
# print(game_board.str_board(gb)) # gb is self from str_board
|
||||
# print(gb.str_board()) # gb is passed as self implicitely by the interpreter
|
||||
# print(str(gb)) # how do we tell the interpreter what to call here?
|
||||
|
||||
# gb.__board[1][1] = '1234'
|
||||
# print(gb.__dict__)
|
||||
# print(gb.__board)
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
# import board
|
||||
from board import game_board
|
||||
from random import choice
|
||||
|
||||
|
||||
class skynet_level_1():
|
||||
def __init__(self, board):
|
||||
self.__board = board
|
||||
|
||||
def computer_move(self):
|
||||
"""
|
||||
Determine where the computer plays and make the move
|
||||
:param game_board:
|
||||
:return: The position where computer moved
|
||||
"""
|
||||
for row in [0, 1, 2]:
|
||||
for col in [0, 1, 2]:
|
||||
if self.__board.get_position(row, col) is None:
|
||||
self.__board.move('O', row, col)
|
||||
return row, col
|
||||
|
||||
|
||||
class skynet_level_2():
|
||||
def __init__(self, board):
|
||||
self.__board = board
|
||||
|
||||
def computer_move(self):
|
||||
"""
|
||||
Determine where the computer plays and make the move
|
||||
:param game_board:
|
||||
:return: The position where computer moved
|
||||
"""
|
||||
empty_pos = []
|
||||
for row in [0, 1, 2]:
|
||||
for col in [0, 1, 2]:
|
||||
if self.__board.get_position(row, col) is None:
|
||||
empty_pos.append((row, col))
|
||||
row, col = choice(empty_pos)
|
||||
self.__board.move('O', row, col)
|
||||
return row, col
|
||||
|
||||
|
||||
class game:
|
||||
def __init__(self, board: game_board, computer_player):
|
||||
self.__board = board
|
||||
self.__computer_player = computer_player
|
||||
|
||||
def human_move(self, row, col):
|
||||
"""
|
||||
Record the human's move on the board
|
||||
:param game_board:
|
||||
:param row:
|
||||
:param col:
|
||||
:return:
|
||||
"""
|
||||
self.__board.move('X', row, col)
|
||||
# board.make_move_on_board(game_board, )
|
||||
|
||||
def computer_move(self):
|
||||
"""
|
||||
Determine where the computer plays and make the move
|
||||
:param game_board:
|
||||
:return: The position where computer moved
|
||||
"""
|
||||
return self.__computer_player.computer_move()
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
from board import game_board
|
||||
from game import game, skynet_level_1, skynet_level_2
|
||||
|
||||
|
||||
def start_game():
|
||||
board = game_board()
|
||||
# Strategy design pattern - https://en.wikipedia.org/wiki/Strategy_pattern
|
||||
# computer_player = skynet_level_1(board)
|
||||
computer_player = skynet_level_2(board)
|
||||
ttt_game = game(board, computer_player)
|
||||
humans_turn = True
|
||||
|
||||
while True:
|
||||
# print(board.str_board(game_board))
|
||||
print(str(board))
|
||||
|
||||
if humans_turn:
|
||||
user_input = input(">")
|
||||
command, params = user_input.split(" ", maxsplit=1)
|
||||
if command == 'play':
|
||||
params = params.split(",")
|
||||
try:
|
||||
row = int(params[0])
|
||||
col = int(params[1])
|
||||
ttt_game.human_move(row, col)
|
||||
except ValueError as ve:
|
||||
# TODO Allow user to attempt to make a move
|
||||
print(ve)
|
||||
elif command == 'takeback':
|
||||
# Take back human's last move, if possible
|
||||
pass
|
||||
elif command == 'ragequit':
|
||||
print("Computer wins!")
|
||||
return
|
||||
else:
|
||||
print("Invalid command")
|
||||
else:
|
||||
# Computer player's turn
|
||||
row, col = ttt_game.computer_move()
|
||||
print("Computer moved at (" + str(row) + "," + str(col) + ")")
|
||||
humans_turn = not humans_turn
|
||||
|
||||
|
||||
start_game()
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Create an application for a car rental business using a console based user interface.
|
||||
The application must allow keeping records of the company’s list of clients, existing car pool and rental history.
|
||||
The application must allow its users to manage clients, cars and rentals in the following ways:
|
||||
Clients
|
||||
Add a new client. Each client is a physical person having a unique ID (driver license series), name, age.
|
||||
Update the data for any client.
|
||||
Remove a client from active clients. Note that removing a client must not remove existing car rental statistics.
|
||||
Search for clients based on ID and name [both at the same time]
|
||||
All client operations must undergo proper validation!
|
||||
Cars
|
||||
Add a new car to the car pool. Each car must have a valid license plate number, a make and model taken from a
|
||||
list of makes and models. In addition, each car will have a color.
|
||||
Remove a car from the car pool.
|
||||
Search for cars based on license number, make, model and color.
|
||||
[make = VW, model = Polo, CJ01ABC], [make = VW, model = Polo, CJ01XYZ]
|
||||
All car operations must undergo proper validation!
|
||||
Rentals
|
||||
An existing client can rent one or several cars from the car pool for a determined period. When rented, a car
|
||||
becomes unavailable for further renting.
|
||||
When a car is returned, it becomes available for renting once again.
|
||||
Search the rental history of a given client, car, or all rentals during any given period.
|
||||
Statistics
|
||||
The list of all cars in the car pool sorted by number of days they were rented.
|
||||
The list of clients sorted descending by the number of cars they have rented.
|
||||
|
||||
The application must have support for unlimited undo/redo with cascading.
|
||||
"""
|
||||
|
||||
"""
|
||||
week 7 - Tic Tac Toe -- modular programming
|
||||
week 8 - Tic Tac Toe with classes & objects
|
||||
week 9-11 - moar classes & objects
|
||||
-> writing domain classes using properties
|
||||
-> intro to layered architecture
|
||||
-> intro to the repository layer
|
||||
-> working with text and binary files
|
||||
-> intro to inheritance
|
||||
-> writing our own exception classes !?
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
class car:
|
||||
"""
|
||||
Add a new car to the car pool. Each car must have
|
||||
-> a valid license plate number,
|
||||
-> a make and model taken from a list of makes and models.
|
||||
-> each car will have a color.
|
||||
"""
|
||||
|
||||
def __init__(self, car_id: str, make: str, model: str, color: str):
|
||||
self.__car_id = car_id
|
||||
self.__make = make
|
||||
self.__model = model
|
||||
self.__color = color
|
||||
|
||||
@property
|
||||
def car_id(self):
|
||||
return self.__car_id
|
||||
|
||||
@car_id.setter
|
||||
def car_id(self, new_value):
|
||||
self.__car_id = new_value
|
||||
|
||||
@property
|
||||
def make(self):
|
||||
return self.__make
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
return self.__model
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
return self.__color
|
||||
|
||||
@color.setter
|
||||
def color(self, new_value):
|
||||
self.__color = new_value
|
||||
|
||||
def __str__(self):
|
||||
return self.car_id + " -> " + self.make + " " + self.model + ", " + self.color
|
||||
|
||||
|
||||
def test_car():
|
||||
new_car = car("CJ 01 ABC", "Dacia", "Sandero", "red")
|
||||
# assert new_car.get_id() == "CJ 01 ABC"
|
||||
assert new_car.car_id == "CJ 01 ABC"
|
||||
assert new_car.make == "Dacia"
|
||||
assert new_car.model == "Sandero"
|
||||
assert new_car.color == "red"
|
||||
assert str(new_car) == "CJ 01 ABC -> Dacia Sandero, red"
|
||||
|
||||
# repaint it
|
||||
# new_car.set_color("blue")
|
||||
new_car.color = "blue"
|
||||
assert new_car.color == "blue"
|
||||
assert str(new_car) == "CJ 01 ABC -> Dacia Sandero, blue"
|
||||
|
||||
# change license plates
|
||||
new_car.car_id = "CJ 99 XYZ"
|
||||
assert str(new_car) == "CJ 99 XYZ -> Dacia Sandero, blue"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_car()
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
from seminar.group_911.seminar_09.domain.car import car
|
||||
from random import choice, randint
|
||||
import pickle
|
||||
|
||||
|
||||
# RepoException inherits from Python's builtin Exception class
|
||||
# RepoException "IS AN" exception
|
||||
class RepoException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class car_repo(object):
|
||||
def __init__(self):
|
||||
# keys are car license numbers, values are car objects
|
||||
self._data = {}
|
||||
|
||||
def add(self, new_car: car):
|
||||
if new_car.car_id in self._data:
|
||||
raise RepoException("Car already in repo")
|
||||
self._data[new_car.car_id] = new_car
|
||||
|
||||
def get(self, car_id: str):
|
||||
# If car cannot be found in repo, catch the dict's KeyError and
|
||||
# re-raise it as RepoException
|
||||
try:
|
||||
return self._data[car_id]
|
||||
except KeyError:
|
||||
raise RepoException("Car is not in repo")
|
||||
|
||||
def get_all(self):
|
||||
return list(self._data.values())
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
|
||||
class car_repo_bin_file(car_repo):
|
||||
def __init__(self, file_name="cars.bin"):
|
||||
# call superclass constructor
|
||||
super(car_repo_bin_file, self).__init__()
|
||||
# remember the name of the file we're working with
|
||||
self._file_name = file_name
|
||||
# load the cars from the file
|
||||
self._load_file()
|
||||
|
||||
def add(self, new_car: car):
|
||||
# call the add() method on the super class
|
||||
# we want to do everything the superclass add() already does
|
||||
super().add(new_car)
|
||||
# we also want to save all cars to a text file
|
||||
self._save_file()
|
||||
|
||||
def _load_file(self):
|
||||
# r - read, b - binary
|
||||
fin = open(self._file_name, "rb")
|
||||
obj = pickle.load(fin)
|
||||
|
||||
for c in obj:
|
||||
super().add(c)
|
||||
fin.close()
|
||||
|
||||
def _save_file(self):
|
||||
# w - write mode (overwrite), b - binary mode
|
||||
fout = open(self._file_name, "wb")
|
||||
pickle.dump(self.get_all(), fout)
|
||||
# NOTE Don't forget to close the file!
|
||||
fout.close()
|
||||
|
||||
|
||||
# just a plain old regular class :)
|
||||
class car_repo_text_file(car_repo):
|
||||
# this class inherits from car_repo
|
||||
# => has all the mathods and attributes in car_repo
|
||||
|
||||
def __init__(self, file_name="cars.txt"):
|
||||
# call superclass constructor
|
||||
super(car_repo_text_file, self).__init__()
|
||||
# remember the name of the file we're working with
|
||||
self._file_name = file_name
|
||||
# load the cars from the file
|
||||
self._load_file()
|
||||
|
||||
def _load_file(self):
|
||||
"""
|
||||
Load the cars from a text file
|
||||
"""
|
||||
# open a text file for reading
|
||||
# t - text file mode, r - reading
|
||||
lines = []
|
||||
|
||||
try:
|
||||
fin = open(self._file_name, "rt")
|
||||
# each car should be on its own line
|
||||
lines = fin.readlines()
|
||||
# close the file when done reading
|
||||
fin.close()
|
||||
except IOError:
|
||||
# It's ok if we don't find the input file
|
||||
pass
|
||||
|
||||
for line in lines:
|
||||
current_line = line.split(",")
|
||||
new_car = car(current_line[0].strip(), current_line[1].strip(), current_line[2].strip(),
|
||||
current_line[3].strip())
|
||||
# NOTE call super() so that we don't write the file we're reading from
|
||||
super().add(new_car)
|
||||
|
||||
def _save_file(self):
|
||||
"""
|
||||
Save all cars to a text file
|
||||
"""
|
||||
# open a text file for writing
|
||||
# t - text file mode, w - writing (rewrite the file every time)
|
||||
fout = open(self._file_name, "wt")
|
||||
|
||||
# writes car_string into the text file
|
||||
# fout.write(car_string)
|
||||
for car in self.get_all():
|
||||
car_string = str(car.car_id) + "," + str(car.make) + "," + str(car.model) + "," + str(car.color) + "\n"
|
||||
fout.write(car_string)
|
||||
|
||||
# call close when done writing
|
||||
fout.close()
|
||||
|
||||
def add(self, new_car: car):
|
||||
# call the add() method on the super class
|
||||
# we want to do everything the superclass add() already does
|
||||
super().add(new_car)
|
||||
# we also want to save all cars to a text file
|
||||
self._save_file()
|
||||
|
||||
|
||||
#
|
||||
# def test_car_repo():
|
||||
# repo = car_repo()
|
||||
# # car repository is empty
|
||||
# assert len(repo) == 0
|
||||
#
|
||||
# # add cars to the repo
|
||||
# c1 = car("CJ 01 ABC", "Dacia", "Sandero", "red")
|
||||
# repo.add(c1)
|
||||
# c2 = car("CJ 01 XYZ", "Dacia", "Logdy", "white")
|
||||
# repo.add(c2)
|
||||
# assert len(repo) == 2
|
||||
#
|
||||
# # try to add the same car again
|
||||
# try:
|
||||
# repo.add(c1)
|
||||
# assert False
|
||||
# except RepoException:
|
||||
# assert True
|
||||
#
|
||||
# # retrieve cars from repo
|
||||
# assert repo.get("CJ 01 ABC") == c1
|
||||
#
|
||||
# # TODO Try to implement repo["CJ 01 XYZ"] == c2
|
||||
# assert repo.get("CJ 01 XYZ") == c2
|
||||
#
|
||||
# # try to retrieve a non-existing car
|
||||
# try:
|
||||
# repo.get("SJ 04 RTY")
|
||||
# assert False
|
||||
# except RepoException:
|
||||
# assert True
|
||||
|
||||
|
||||
def generate_cars(n: int):
|
||||
"""
|
||||
Generates n car instances
|
||||
:return: A list of n cars
|
||||
"""
|
||||
counties = ["AB", "SJ", "VS", "CJ", "B", "TL", "TR", "GL", "GR", "IS"]
|
||||
make_model = {"Dacia": ["Logan", "Sandero", "Lodgy"], "Toyota": ["Corolla", "RAV-4", "Yaris"]}
|
||||
colors = ["red", "blue", "green", "black"]
|
||||
|
||||
result = []
|
||||
while n > 0:
|
||||
letters = ""
|
||||
for i in [0, 1, 2]:
|
||||
letters += chr(randint(65, 90)) # A -> Z
|
||||
car_id = choice(counties) + " " + str(randint(10, 99)) + " " + letters
|
||||
make = choice(list(make_model.keys()))
|
||||
model = choice(make_model[make])
|
||||
color = choice(colors)
|
||||
result.append(car(car_id, make, model, color))
|
||||
n -= 1
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# repo = car_repo()
|
||||
# repo_text = car_repo_text_file()
|
||||
# # NOTE Save the generated cars to the file
|
||||
# for c in generate_cars(10):
|
||||
# print(str(c))
|
||||
# # repo.add(c)
|
||||
# repo_text.add(c)
|
||||
|
||||
# read the cars.bin input file
|
||||
car_repo_bin = car_repo_bin_file()
|
||||
print("Cars saved in cars.bin")
|
||||
for c in car_repo_bin.get_all():
|
||||
print(str(c))
|
||||
|
||||
# read the cars.txt file
|
||||
car_repo_text = car_repo_text_file()
|
||||
print("\n\nCars saved in cars.txt")
|
||||
for c in car_repo_text.get_all():
|
||||
print(str(c))
|
||||
|
||||
# NOTE Load the cars and display them again
|
||||
# new_car_repo = car_repo_text_file()
|
||||
# for c in new_car_repo.get_all():
|
||||
# print(str(c))
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
VS 65 HNV,Dacia,Logan,blue
|
||||
TL 51 QQP,Dacia,Lodgy,red
|
||||
IS 97 EJG,Toyota,Yaris,red
|
||||
TR 91 KTU,Dacia,Sandero,blue
|
||||
TL 89 SSI,Dacia,Sandero,black
|
||||
TL 23 LTR,Dacia,Sandero,green
|
||||
TR 24 UVD,Toyota,RAV-4,red
|
||||
CJ 92 TRD,Dacia,Logan,green
|
||||
CJ 36 ZIA,Dacia,Logan,red
|
||||
IS 66 DJX,Dacia,Sandero,blue
|
||||
CJ 51 HCN,Dacia,Lodgy,red
|
||||
VS 23 GWQ,Toyota,Yaris,red
|
||||
IS 59 WCC,Toyota,Corolla,red
|
||||
CJ 92 YDU,Toyota,Yaris,green
|
||||
AB 19 FIN,Dacia,Lodgy,blue
|
||||
TL 70 GYH,Toyota,Corolla,red
|
||||
TR 64 QBZ,Dacia,Logan,blue
|
||||
TL 79 NEB,Dacia,Logan,black
|
||||
GL 42 RKZ,Dacia,Lodgy,green
|
||||
CJ 56 KNZ,Toyota,RAV-4,green
|
||||
GR 29 DUQ,Toyota,RAV-4,green
|
||||
AB 65 OJD,Dacia,Sandero,red
|
||||
TL 53 KYY,Dacia,Lodgy,green
|
||||
GL 58 ETL,Dacia,Logan,black
|
||||
IS 73 YZI,Dacia,Lodgy,black
|
||||
IS 61 VWR,Dacia,Sandero,green
|
||||
CJ 57 FTB,Toyota,Corolla,black
|
||||
B 36 VEF,Toyota,Yaris,blue
|
||||
GR 17 ERN,Toyota,RAV-4,green
|
||||
AB 40 ZXT,Dacia,Sandero,green
|
||||
GR 95 USA,Toyota,Yaris,green
|
||||
SJ 39 JTW,Dacia,Logan,blue
|
||||
AB 93 WVL,Dacia,Sandero,black
|
||||
TL 62 DZS,Dacia,Lodgy,green
|
||||
IS 23 CUK,Dacia,Logan,blue
|
||||
TR 99 JYB,Toyota,Corolla,green
|
||||
TL 97 TRC,Toyota,Yaris,green
|
||||
AB 19 SMK,Dacia,Logan,green
|
||||
B 54 FNU,Dacia,Sandero,black
|
||||
IS 27 WBE,Toyota,Yaris,green
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
class Car:
|
||||
"""
|
||||
Add a new car to the car pool. Each car must have
|
||||
-> a valid license plate number,
|
||||
-> a make and model taken from a list of makes and models.
|
||||
-> each car will have a color.
|
||||
"""
|
||||
|
||||
def __init__(self, car_id: str, make: str, model: str, color: str):
|
||||
self.__car_id = car_id
|
||||
self.__make = make
|
||||
self.__model = model
|
||||
self.__color = color
|
||||
|
||||
@property
|
||||
def car_id(self):
|
||||
return self.__car_id
|
||||
|
||||
@car_id.setter
|
||||
def car_id(self, new_value):
|
||||
self.__car_id = new_value
|
||||
|
||||
@property
|
||||
def make(self):
|
||||
return self.__make
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
return self.__model
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
return self.__color
|
||||
|
||||
@color.setter
|
||||
def color(self, new_value):
|
||||
self.__color = new_value
|
||||
|
||||
def __str__(self):
|
||||
return self.car_id + " -> " + self.make + " " + self.model + ", " + self.color
|
||||
|
||||
|
||||
def test_car():
|
||||
new_car = Car("CJ 01 ABC", "Dacia", "Sandero", "red")
|
||||
# assert new_car.get_id() == "CJ 01 ABC"
|
||||
assert new_car.car_id == "CJ 01 ABC"
|
||||
assert new_car.make == "Dacia"
|
||||
assert new_car.model == "Sandero"
|
||||
assert new_car.color == "red"
|
||||
assert str(new_car) == "CJ 01 ABC -> Dacia Sandero, red"
|
||||
|
||||
# repaint it
|
||||
# new_car.set_color("blue")
|
||||
new_car.color = "blue"
|
||||
assert new_car.color == "blue"
|
||||
assert str(new_car) == "CJ 01 ABC -> Dacia Sandero, blue"
|
||||
|
||||
# change license plates
|
||||
new_car.car_id = "CJ 99 XYZ"
|
||||
assert str(new_car) == "CJ 99 XYZ -> Dacia Sandero, blue"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_car()
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
from seminar.group_911.seminar_11.domain.exceptions import CarValidationException
|
||||
|
||||
|
||||
class CarValidatorRO:
|
||||
@staticmethod
|
||||
def _is_license_valid(license):
|
||||
# TODO Implement full validation
|
||||
"""
|
||||
Implement Romanian license plate validation
|
||||
@param license:
|
||||
@return: ...
|
||||
"""
|
||||
return len(license) > 2
|
||||
|
||||
# FIXME Duplicated code across validators, use inheritance to remove it
|
||||
def validate(self, car):
|
||||
errors = []
|
||||
# V1 - All properties are non-empty
|
||||
if not CarValidatorRO._is_license_valid(car.license_plate):
|
||||
errors.append('Invalid license plate')
|
||||
if len(car.make) < 2:
|
||||
errors.append('Car make should have at least 3 letters')
|
||||
if len(car.model) < 2:
|
||||
errors.append('Car model should have at least 3 letters')
|
||||
|
||||
if len(errors) > 0:
|
||||
raise CarValidationException(errors)
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
class Client:
|
||||
def __init__(self, client_id, cnp, name):
|
||||
self._client_id = client_id
|
||||
self._cnp = cnp
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._client_id
|
||||
|
||||
@property
|
||||
def cnp(self):
|
||||
return self._cnp
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
self._name = value
|
||||
|
||||
def __eq__(self, z):
|
||||
"""
|
||||
Two Clients are equal if they have the same id
|
||||
:param z:
|
||||
:return:
|
||||
"""
|
||||
if type(z) != Client:
|
||||
return False
|
||||
return self.id == z.id
|
||||
|
||||
def __str__(self):
|
||||
return "Id=" + str(self.id) + ", Name=" + str(self.name)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
c1 = Client(100, "280122334506070", "Pop Maria")
|
||||
c2 = Client(101, "2334506070", "Pop Maria")
|
||||
print(id(c1), id(c2))
|
||||
# Note fun with == __eq__
|
||||
print(c1 == [])
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class ClientValidator:
|
||||
# TODO Implement
|
||||
|
||||
def validate(self, client):
|
||||
return True
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
class ValidatorException(Exception):
|
||||
def __init__(self, message_list="Validation error!"):
|
||||
self._message_list = message_list
|
||||
|
||||
@property
|
||||
def messages(self):
|
||||
return self._message_list
|
||||
|
||||
def __str__(self):
|
||||
result = ""
|
||||
for message in self.messages:
|
||||
result += message
|
||||
result += "\n"
|
||||
return result
|
||||
|
||||
|
||||
class CarException(Exception):
|
||||
def __init__(self, msg):
|
||||
self._msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return self._msg
|
||||
|
||||
|
||||
class CarValidationException(CarException):
|
||||
def __init__(self, error_list):
|
||||
self._errors = error_list
|
||||
|
||||
def __str__(self):
|
||||
result = ''
|
||||
|
||||
for er in self._errors:
|
||||
result += er
|
||||
result += '\n'
|
||||
return result
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
from datetime import date
|
||||
|
||||
from seminar.group_911.seminar_11.domain.car import Car
|
||||
from seminar.group_911.seminar_11.domain.client import Client
|
||||
|
||||
|
||||
class Rental:
|
||||
def __init__(self, rental_id: int, start: date, end: date, client: Client, car: Car):
|
||||
self._rentalId = rental_id
|
||||
self._client = client
|
||||
self._car = car
|
||||
self._start = start
|
||||
self._end = end
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._rentalId
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
return self._client
|
||||
|
||||
@client.setter
|
||||
def client(self, client):
|
||||
self._client = client
|
||||
|
||||
@property
|
||||
def car(self):
|
||||
return self._car
|
||||
|
||||
@car.setter
|
||||
def car(self, car):
|
||||
self._car = car
|
||||
|
||||
@property
|
||||
def start(self):
|
||||
return self._start
|
||||
|
||||
@start.setter
|
||||
def start(self, start):
|
||||
self._start = start
|
||||
|
||||
@property
|
||||
def end(self):
|
||||
return self._end
|
||||
|
||||
@end.setter
|
||||
def end(self, end):
|
||||
self._end = end
|
||||
|
||||
# len(rental)
|
||||
def __len__(self):
|
||||
if self._end is not None:
|
||||
return (self._end - self._start).days + 1
|
||||
today = date.today()
|
||||
return (today - self._start).days + 1
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
return "Rental: " + str(self.id) + "\nCar: " + str(self.car) + "\nClient: " + str(
|
||||
self.client) + "\nPeriod: " + self._start.strftime("%Y-%m-%d") + " to " + self._end.strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
car = Car("CJ 01 ABC", "Dacia", "Sandero", "red")
|
||||
client = Client(100, "280122334506070", "Pop Maria")
|
||||
|
||||
r = Rental(100, date(2022, 10, 15), date(2022, 11, 20), client, car)
|
||||
print(len(r))
|
||||
|
||||
r = Rental(100, date(2022, 10, 15), None, client, car)
|
||||
print(len(r))
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
from datetime import date
|
||||
|
||||
from seminar.group_911.seminar_11.domain.exceptions import ValidatorException
|
||||
from seminar.group_911.seminar_11.domain.rental import Rental
|
||||
|
||||
|
||||
class RentalValidator:
|
||||
def validate(self, rental):
|
||||
if isinstance(rental, Rental) is False:
|
||||
raise TypeError("Not a Rental")
|
||||
|
||||
_errorList = []
|
||||
now = date(2000, 1, 1)
|
||||
if rental.start < now:
|
||||
_errorList.append("Rental starts in past;")
|
||||
if len(rental) < 1:
|
||||
_errorList.append("Rental must be at least 1 day;")
|
||||
if len(_errorList) > 0:
|
||||
raise ValidatorException(_errorList)
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
from seminar.group_911.seminar_11.domain.car import Car
|
||||
from random import choice, randint
|
||||
import pickle
|
||||
|
||||
|
||||
# RepoException inherits from Python's builtin Exception class
|
||||
# RepoException "IS AN" exception
|
||||
class RepoException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class car_repo(object):
|
||||
def __init__(self):
|
||||
# keys are car license numbers, values are car objects
|
||||
self._data = {}
|
||||
|
||||
def add(self, new_car: Car):
|
||||
if new_car.car_id in self._data:
|
||||
raise RepoException("Car already in repo")
|
||||
self._data[new_car.car_id] = new_car
|
||||
|
||||
def get(self, car_id: str):
|
||||
# If car cannot be found in repo, catch the dict's KeyError and
|
||||
# re-raise it as RepoException
|
||||
try:
|
||||
return self._data[car_id]
|
||||
except KeyError:
|
||||
raise RepoException("Car is not in repo")
|
||||
|
||||
def get_all(self):
|
||||
return list(self._data.values())
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
|
||||
class car_repo_bin_file(car_repo):
|
||||
def __init__(self, file_name="cars.bin"):
|
||||
# call superclass constructor
|
||||
super(car_repo_bin_file, self).__init__()
|
||||
# remember the name of the file we're working with
|
||||
self._file_name = file_name
|
||||
# load the cars from the file
|
||||
self._load_file()
|
||||
|
||||
def add(self, new_car: Car):
|
||||
# call the add() method on the super class
|
||||
# we want to do everything the superclass add() already does
|
||||
super().add(new_car)
|
||||
# we also want to save all cars to a text file
|
||||
self._save_file()
|
||||
|
||||
def _load_file(self):
|
||||
# r - read, b - binary
|
||||
fin = open(self._file_name, "rb")
|
||||
obj = pickle.load(fin)
|
||||
|
||||
for c in obj:
|
||||
super().add(c)
|
||||
fin.close()
|
||||
|
||||
def _save_file(self):
|
||||
# w - write mode (overwrite), b - binary mode
|
||||
fout = open(self._file_name, "wb")
|
||||
pickle.dump(self.get_all(), fout)
|
||||
# NOTE Don't forget to close the file!
|
||||
fout.close()
|
||||
|
||||
|
||||
# just a plain old regular class :)
|
||||
class car_repo_text_file(car_repo):
|
||||
# this class inherits from car_repo
|
||||
# => has all the mathods and attributes in car_repo
|
||||
|
||||
def __init__(self, file_name="cars.txt"):
|
||||
# call superclass constructor
|
||||
super(car_repo_text_file, self).__init__()
|
||||
# remember the name of the file we're working with
|
||||
self._file_name = file_name
|
||||
# load the cars from the file
|
||||
self._load_file()
|
||||
|
||||
def _load_file(self):
|
||||
"""
|
||||
Load the cars from a text file
|
||||
"""
|
||||
# open a text file for reading
|
||||
# t - text file mode, r - reading
|
||||
lines = []
|
||||
|
||||
try:
|
||||
fin = open(self._file_name, "rt")
|
||||
# each car should be on its own line
|
||||
lines = fin.readlines()
|
||||
# close the file when done reading
|
||||
fin.close()
|
||||
except IOError:
|
||||
# It's ok if we don't find the input file
|
||||
pass
|
||||
|
||||
for line in lines:
|
||||
current_line = line.split(",")
|
||||
new_car = Car(current_line[0].strip(), current_line[1].strip(), current_line[2].strip(),
|
||||
current_line[3].strip())
|
||||
# NOTE call super() so that we don't write the file we're reading from
|
||||
super().add(new_car)
|
||||
|
||||
def _save_file(self):
|
||||
"""
|
||||
Save all cars to a text file
|
||||
"""
|
||||
# open a text file for writing
|
||||
# t - text file mode, w - writing (rewrite the file every time)
|
||||
fout = open(self._file_name, "wt")
|
||||
|
||||
# writes car_string into the text file
|
||||
# fout.write(car_string)
|
||||
for car in self.get_all():
|
||||
car_string = str(car.car_id) + "," + str(car.make) + "," + str(car.model) + "," + str(car.color) + "\n"
|
||||
fout.write(car_string)
|
||||
|
||||
# call close when done writing
|
||||
fout.close()
|
||||
|
||||
def add(self, new_car: Car):
|
||||
# call the add() method on the super class
|
||||
# we want to do everything the superclass add() already does
|
||||
super().add(new_car)
|
||||
# we also want to save all cars to a text file
|
||||
self._save_file()
|
||||
|
||||
|
||||
#
|
||||
# def test_car_repo():
|
||||
# repo = car_repo()
|
||||
# # car repository is empty
|
||||
# assert len(repo) == 0
|
||||
#
|
||||
# # add cars to the repo
|
||||
# c1 = car("CJ 01 ABC", "Dacia", "Sandero", "red")
|
||||
# repo.add(c1)
|
||||
# c2 = car("CJ 01 XYZ", "Dacia", "Logdy", "white")
|
||||
# repo.add(c2)
|
||||
# assert len(repo) == 2
|
||||
#
|
||||
# # try to add the same car again
|
||||
# try:
|
||||
# repo.add(c1)
|
||||
# assert False
|
||||
# except RepoException:
|
||||
# assert True
|
||||
#
|
||||
# # retrieve cars from repo
|
||||
# assert repo.get("CJ 01 ABC") == c1
|
||||
#
|
||||
# # TODO Try to implement repo["CJ 01 XYZ"] == c2
|
||||
# assert repo.get("CJ 01 XYZ") == c2
|
||||
#
|
||||
# # try to retrieve a non-existing car
|
||||
# try:
|
||||
# repo.get("SJ 04 RTY")
|
||||
# assert False
|
||||
# except RepoException:
|
||||
# assert True
|
||||
|
||||
|
||||
def generate_cars(n: int):
|
||||
"""
|
||||
Generates n car instances
|
||||
:return: A list of n cars
|
||||
"""
|
||||
counties = ["AB", "SJ", "VS", "CJ", "B", "TL", "TR", "GL", "GR", "IS"]
|
||||
make_model = {"Dacia": ["Logan", "Sandero", "Lodgy"], "Toyota": ["Corolla", "RAV-4", "Yaris"]}
|
||||
colors = ["red", "blue", "green", "black"]
|
||||
|
||||
result = []
|
||||
while n > 0:
|
||||
letters = ""
|
||||
for i in [0, 1, 2]:
|
||||
letters += chr(randint(65, 90)) # A -> Z
|
||||
car_id = choice(counties) + " " + str(randint(10, 99)) + " " + letters
|
||||
make = choice(list(make_model.keys()))
|
||||
model = choice(make_model[make])
|
||||
color = choice(colors)
|
||||
result.append(Car(car_id, make, model, color))
|
||||
n -= 1
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# repo = car_repo()
|
||||
# repo_text = car_repo_text_file()
|
||||
# # NOTE Save the generated cars to the file
|
||||
# for c in generate_cars(10):
|
||||
# print(str(c))
|
||||
# # repo.add(c)
|
||||
# repo_text.add(c)
|
||||
|
||||
# read the cars.bin input file
|
||||
car_repo_bin = car_repo_bin_file()
|
||||
print("Cars saved in cars.bin")
|
||||
for c in car_repo_bin.get_all():
|
||||
print(str(c))
|
||||
|
||||
# read the cars.txt file
|
||||
car_repo_text = car_repo_text_file()
|
||||
print("\n\nCars saved in cars.txt")
|
||||
for c in car_repo_text.get_all():
|
||||
print(str(c))
|
||||
|
||||
# NOTE Load the cars and display them again
|
||||
# new_car_repo = car_repo_text_file()
|
||||
# for c in new_car_repo.get_all():
|
||||
# print(str(c))
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
from seminar.group_911.seminar_11.domain.client import Client
|
||||
from seminar.group_911.seminar_11.repository.repository_exception import RepositoryException
|
||||
|
||||
|
||||
class ClientRepo:
|
||||
# TODO Finish implementation
|
||||
def __init__(self):
|
||||
self._clients = {}
|
||||
|
||||
def add(self, client):
|
||||
if client.id in self._clients.keys():
|
||||
raise RepositoryException("Duplicate Client id")
|
||||
self._clients[client.id] = client
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import datetime
|
||||
|
||||
from seminar.group_911.seminar_11.repository.repository_exception import RepositoryException
|
||||
|
||||
|
||||
class RentalRepository:
|
||||
# TODO Finish implementation
|
||||
def __init__(self):
|
||||
self._data = {}
|
||||
|
||||
def add(self, rental):
|
||||
if rental.id in self._data.keys():
|
||||
raise RepositoryException("Duplicate Rental ID")
|
||||
self._data[rental.id] = rental
|
||||
|
||||
def remove(self, rental_id):
|
||||
if rental_id in self._data.keys():
|
||||
del self.data[rental_id]
|
||||
else:
|
||||
raise RepositoryException("Rental was not found")
|
||||
|
||||
def get_all(self):
|
||||
return list(self._data.values())
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
class RepositoryException(Exception):
|
||||
def __init__(self, message):
|
||||
self._message = message
|
||||
|
||||
@property
|
||||
def message(self):
|
||||
return self._message
|
||||
|
||||
def __str__(self):
|
||||
return self._message
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
from seminar.group_911.seminar_11.domain.car import Car
|
||||
|
||||
|
||||
class CarService:
|
||||
def __init__(self, repo, validator):
|
||||
# self._repo = CarRepo()
|
||||
# NOTE Taking parameters in ctor allows you to change them
|
||||
self._repo = repo
|
||||
self._validator = validator
|
||||
|
||||
def add_car(self, car_id: str, car_make: str, car_model: str, color: str):
|
||||
"""
|
||||
Add a new car
|
||||
"""
|
||||
# 1. Build Car instance
|
||||
car = Car(car_id, car_make, car_model, color)
|
||||
# 2. Validate Car instance
|
||||
self._validator.validate(car)
|
||||
# 3. Add car to repo
|
||||
self._repo.add(car)
|
||||
|
||||
def get_all(self):
|
||||
return self._repo.get_all()
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
class ClientService:
|
||||
pass
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
from datetime import date
|
||||
|
||||
from seminar.group_911.seminar_11.domain.car import Car
|
||||
from seminar.group_911.seminar_11.domain.client import Client
|
||||
from seminar.group_911.seminar_11.domain.rental import Rental
|
||||
from seminar.group_911.seminar_11.repository.rental_repo import RentalRepository
|
||||
from seminar.group_911.seminar_11.services.car_service import CarService
|
||||
|
||||
|
||||
class CarsRentalsDTO:
|
||||
"""
|
||||
Data transfer object for car rental statistic
|
||||
Holds the number of total rental days for one specific car
|
||||
"""
|
||||
|
||||
def __init__(self, car: Car, rental_days: int):
|
||||
self._car = car
|
||||
self._rental_days = rental_days
|
||||
|
||||
@property
|
||||
def days(self):
|
||||
return self._rental_days
|
||||
|
||||
@days.setter
|
||||
def days(self, new_value):
|
||||
self._rental_days = new_value
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.days) + " days for -> " + str(self._car)
|
||||
|
||||
|
||||
class RentalService:
|
||||
def __init__(self, repo: RentalRepository, car_service: CarService, validator):
|
||||
self._repo = repo
|
||||
# NOTE all *Service classes are the same layer so they can know about each other
|
||||
self._car_service = car_service
|
||||
self._validator = validator
|
||||
|
||||
def add_rental(self, rental_start: date, rental_end: date, client: Client, car: Car):
|
||||
# 1. Build Rental instance
|
||||
# TODO rental ID!
|
||||
rent = Rental(100, rental_start, rental_end, client, car)
|
||||
# 2. Validate it
|
||||
self._validator.validate(rent)
|
||||
# 3. Add to repo
|
||||
self._repo.add(rent)
|
||||
|
||||
# The list of all cars in the car pool sorted by number of days they
|
||||
# were rented.
|
||||
def cars_sorted_by_rental_days(self):
|
||||
# let's print all cars
|
||||
cars = self._car_service.get_all()
|
||||
# print(cars)
|
||||
# let's print all rentals
|
||||
rentals = self._repo.get_all()
|
||||
# print(rentals)
|
||||
|
||||
# NOTE license plates are keys, DTO instances are values
|
||||
rental_dict = {}
|
||||
|
||||
for rental in self._repo.get_all():
|
||||
license_plate = rental.car.car_id
|
||||
if license_plate in rental_dict:
|
||||
rental_dict[license_plate].days += len(rental)
|
||||
else:
|
||||
rental_dict[license_plate] = CarsRentalsDTO(rental.car, len(rental))
|
||||
|
||||
# move data from the {} to []
|
||||
result = list(rental_dict.values())
|
||||
result.sort(key=lambda x: x.days, reverse=True)
|
||||
|
||||
return result
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
Create an application for a car rental business using a console based user interface.
|
||||
The application must allow keeping records of the company’s list of clients, existing car pool and rental history.
|
||||
The application must allow its users to manage clients, cars and rentals in the following ways:
|
||||
Clients
|
||||
Add a new client. Each client is a physical person having a unique ID (driver license series), name, age.
|
||||
Update the data for any client.
|
||||
Remove a client from active clients. Note that removing a client must not remove existing car rental statistics.
|
||||
Search for clients based on ID and name [both at the same time]
|
||||
All client operations must undergo proper validation!
|
||||
Cars
|
||||
Add a new car to the car pool. Each car must have a valid license plate number, a make and model taken from a
|
||||
list of makes and models. In addition, each car will have a color.
|
||||
Remove a car from the car pool.
|
||||
Search for cars based on license number, make and model and color.
|
||||
[search with no parameters displays everything, omitting a param disregards it]
|
||||
[make = VW, model = Polo, CJ01ABC], [make = VW, model = Polo, CJ01XYZ]
|
||||
All car operations must undergo proper validation!
|
||||
Rentals
|
||||
An existing client can rent one or several cars from the car pool for a determined period. When rented, a car
|
||||
becomes unavailable for further renting.
|
||||
When a car is returned, it becomes available for renting once again.
|
||||
Search the rental history of a given client, car, or all rentals during any given period.
|
||||
Statistics
|
||||
The list of all cars in the car pool sorted by number of days they were rented.
|
||||
The list of clients sorted descending by the number of cars they have rented.
|
||||
|
||||
The application must have support for unlimited undo/redo with cascading.
|
||||
"""
|
||||
import random
|
||||
from datetime import date, timedelta
|
||||
|
||||
from seminar.group_911.seminar_09.repository.repo_memory import car_repo_bin_file
|
||||
from seminar.group_911.seminar_11.domain.car import Car
|
||||
from seminar.group_911.seminar_11.domain.car_validators import CarValidatorRO
|
||||
from seminar.group_911.seminar_11.domain.client import Client
|
||||
from seminar.group_911.seminar_11.domain.client_validators import ClientValidator
|
||||
from seminar.group_911.seminar_11.domain.rental import Rental
|
||||
from seminar.group_911.seminar_11.domain.rental_validators import RentalValidator
|
||||
from seminar.group_911.seminar_11.repository.car_repo import car_repo_text_file
|
||||
|
||||
from seminar.group_911.seminar_11.repository.client_repo import ClientRepo
|
||||
from seminar.group_911.seminar_11.repository.rental_repo import RentalRepository
|
||||
|
||||
# Initialize repositories
|
||||
from seminar.group_911.seminar_11.services.car_service import CarService
|
||||
from seminar.group_911.seminar_11.services.client_service import ClientService
|
||||
from seminar.group_911.seminar_11.services.rental_service import RentalService
|
||||
from seminar.group_911.seminar_11.ui.ui import UI
|
||||
|
||||
car_repo = car_repo_text_file()
|
||||
|
||||
|
||||
# for c in car_repo.get_all():
|
||||
# print(c)
|
||||
|
||||
|
||||
def generate_rentals(n: int):
|
||||
car_repo = car_repo_text_file()
|
||||
client = Client(100, "290010203445566", "Pop Maria")
|
||||
|
||||
# TODO Generate n rentals
|
||||
# all rentals have the same client (Pop Maria)
|
||||
# rented car may vary
|
||||
# Rental - id, client, car, start_date, end_date
|
||||
# TODO - generate a random start date and end date
|
||||
# select a car from the list of existing cars
|
||||
# have a unique rental_id (start from 1000 and +1 for each instance)
|
||||
# return the list of rentals
|
||||
|
||||
rental_id = 1000
|
||||
rentals = []
|
||||
while n > 0:
|
||||
rd = random.randint
|
||||
start_date = date(rd(2021, 2022), rd(1, 12), rd(1, 28))
|
||||
day_count = timedelta(days=rd(1, 10))
|
||||
end_here = start_date + day_count
|
||||
rentals.append(Rental(rental_id, start_date, end_here, client, random.choice(car_repo.get_all())))
|
||||
rental_id += 1
|
||||
n -= 1
|
||||
return rentals
|
||||
|
||||
|
||||
# rentals = generate_rentals(10)
|
||||
# print(rentals)
|
||||
|
||||
# NOTE you should be able to change the types of repos, services etc.
|
||||
# car_repo = car_repo_bin_file()
|
||||
client_repo = ClientRepo()
|
||||
|
||||
rent_repo = RentalRepository()
|
||||
for rental in generate_rentals(100):
|
||||
rent_repo.add(rental)
|
||||
|
||||
# Start up services layer
|
||||
# NOTE dependency injection of car repository for car service
|
||||
car_service = CarService(car_repo, CarValidatorRO())
|
||||
|
||||
# client_service = ClientService(client_repo, ClientValidator())
|
||||
rent_service = RentalService(rent_repo, car_service, RentalValidator())
|
||||
|
||||
# TODO Move this code to the UI
|
||||
for r in rent_service.cars_sorted_by_rental_days():
|
||||
print(r)
|
||||
"""
|
||||
ui = UI(car_service, client_service, rent_service)
|
||||
ui.start()
|
||||
"""
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
class UI:
|
||||
def __init__(self, car_service, rent_service, client_service):
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Layered architecture
|
||||
ui -> user interface
|
||||
-> all print/input (or all GUI windows/dialogs/menus etc)
|
||||
-> in our case we catch exception and display them here
|
||||
services
|
||||
-> below the UI layer (UI calls functions in services)
|
||||
-> does not know about the UI
|
||||
-> forward calls to repo, implement functionalities (undo/redo, statistics, search, etc.)
|
||||
repository
|
||||
-> stores everything (preferably using files/SQL/noSQL)
|
||||
-> does not know about UI, services
|
||||
domain
|
||||
-> classes that we find in the problem statement (cars, expense, client, student, book, etc.)
|
||||
-> does not know about any layer
|
||||
|
||||
function call direction:
|
||||
ui -> services -> repository
|
||||
|
||||
Dependency injection
|
||||
-> e.g., services need a repo to work, but you can vary the repo implementation
|
||||
|
||||
Statistics
|
||||
The list of all cars in the car pool sorted by number of days they
|
||||
were rented.
|
||||
The list of clients sorted descending by the number of cars they have rented.
|
||||
|
||||
"""
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
from seminar.group_911.seminar_12.domain.validator_exception import ValidatorException
|
||||
|
||||
|
||||
class Car:
|
||||
def __init__(self, _id, license_plate, make, model):
|
||||
self._id = _id
|
||||
self._license = license_plate
|
||||
self._make = make
|
||||
self._model = model
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def license(self):
|
||||
return self._license
|
||||
|
||||
@property
|
||||
def make(self):
|
||||
return self._make
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
return self._model
|
||||
|
||||
def __eq__(self, z):
|
||||
if not isinstance(z, Car):
|
||||
return False
|
||||
return self.id == z.id
|
||||
|
||||
def __str__(self):
|
||||
return "Id: " + str(self.id) + ", License: " + self.license + ", Car type: " + self.make + ", " + self.model
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class CarValidator:
|
||||
|
||||
def __init__(self):
|
||||
# and so on...
|
||||
self.__counties = ["AB", "B", "CJ"]
|
||||
self._errors = ""
|
||||
|
||||
def _license_valid(self, plate):
|
||||
token = str(plate).split(' ')
|
||||
if len(token) != 3:
|
||||
return False
|
||||
if token[0] not in self.__counties:
|
||||
return False
|
||||
try:
|
||||
n = int(token[1])
|
||||
if len(token[1]) < 2 or len(token[1]) > 3:
|
||||
return False
|
||||
if n < 1 or n > 999:
|
||||
return False
|
||||
if n > 99 and token[0] != "B":
|
||||
return False
|
||||
except TypeError:
|
||||
return False
|
||||
if len(token[2]) != 3:
|
||||
return False
|
||||
tu = str(token[2]).upper()
|
||||
if tu[0] in ['I', 'O']:
|
||||
return False
|
||||
for x in tu:
|
||||
if x < 'A' or x > 'Z':
|
||||
return False
|
||||
if x == 'Q':
|
||||
return False
|
||||
return True
|
||||
|
||||
def validate(self, car):
|
||||
"""
|
||||
Validate if provided Car instance is valid
|
||||
car - Instance of Car type
|
||||
Return List of validation errors. An empty list if instance is valid.
|
||||
"""
|
||||
if isinstance(car, Car) == False:
|
||||
raise TypeError("Can only validate Car objects!")
|
||||
_errors = []
|
||||
if len(car.make) == 0:
|
||||
_errors.append("Car must have x make")
|
||||
if len(car.model) == 0:
|
||||
_errors.append("Car must have x model;")
|
||||
if self._license_valid(car.license) is False:
|
||||
_errors.append("Bad license plate number;")
|
||||
if len(_errors) > 0:
|
||||
raise ValidatorException(_errors)
|
||||
return True
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
class CarRentalException(Exception):
|
||||
def __init__(self, msg):
|
||||
self._message = msg
|
||||
|
||||
def getMessage(self):
|
||||
return self._message
|
||||
|
||||
def __str__(self):
|
||||
return self._message
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
class Client:
|
||||
def __init__(self, _id, cnp, name):
|
||||
self._id = _id
|
||||
self._cnp = cnp
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def cnp(self):
|
||||
return self._cnp
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def __eq__(self, z):
|
||||
if isinstance(z, Client) is False:
|
||||
return False
|
||||
return self.id == z.id
|
||||
|
||||
def __str__(self):
|
||||
return "Id=" + str(self.id) + ", Name=" + str(self.name)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class ClientValidator:
|
||||
def _is_cnp_valid(self, cnp):
|
||||
# SAALLZZJJNNNC
|
||||
if len(cnp) != 13:
|
||||
# This is not x full CNP validation
|
||||
return False
|
||||
for x in cnp:
|
||||
if x < '0' or x > '9':
|
||||
return False
|
||||
return True
|
||||
|
||||
def validate(self, client):
|
||||
"""
|
||||
Validate if provided Client instance is valid
|
||||
client - Instance of Client type
|
||||
Return List of validation errors. An empty list if instance is valid.
|
||||
"""
|
||||
if isinstance(client, Client) is False:
|
||||
raise TypeError("Not x Client")
|
||||
_errors = []
|
||||
if self._is_cnp_valid(client.cnp) is False:
|
||||
_errors.append("CNP not valid.;")
|
||||
if len(client.name) == 0:
|
||||
_errors.append("Name not valid.")
|
||||
if len(_errors) != 0:
|
||||
raise ValueError(_errors)
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
from datetime import date
|
||||
|
||||
from seminar.group_911.seminar_12.domain.validator_exception import ValidatorException
|
||||
|
||||
|
||||
class Rental:
|
||||
def __init__(self, _id, start, end, client, car):
|
||||
self._id = _id
|
||||
self._client = client
|
||||
self._car = car
|
||||
self._start = start
|
||||
self._end = end
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
return self._client
|
||||
|
||||
@property
|
||||
def car(self):
|
||||
return self._car
|
||||
|
||||
@property
|
||||
def start(self):
|
||||
return self._start
|
||||
|
||||
@property
|
||||
def end(self):
|
||||
return self._end
|
||||
|
||||
def __len__(self):
|
||||
return (self._end - self._start).days + 1
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
return "Rental: " + str(self.id) + "\nCar: " + str(self.car) + "\nClient: " + str(
|
||||
self.client) + "\nPeriod: " + self.start.strftime("%Y-%m-%d") + " to " + self.end.strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
class RentalValidator:
|
||||
|
||||
def validate(self, rental):
|
||||
if isinstance(rental, Rental) is False:
|
||||
raise TypeError("Not a Rental")
|
||||
_errorList = []
|
||||
now = date(2000, 1, 1)
|
||||
if rental.start < now:
|
||||
_errorList.append("Rental starts in past;")
|
||||
if len(rental) < 1:
|
||||
_errorList.append("Rental must be at least 1 day;")
|
||||
if len(_errorList) > 0:
|
||||
raise ValidatorException(_errorList)
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
class ValidatorException(Exception):
|
||||
def __init__(self, messageList):
|
||||
self._messageList = messageList
|
||||
|
||||
def getMessage(self):
|
||||
return self._messageList
|
||||
|
||||
def __str__(self):
|
||||
result = ""
|
||||
for message in self.getMessage():
|
||||
result += message
|
||||
result += "\n"
|
||||
return result
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
from seminar.group_911.seminar_12.repository.repository_exception import RepositoryException
|
||||
|
||||
|
||||
class Repository:
|
||||
"""
|
||||
Repository for storing IDObject instances
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._objects = []
|
||||
|
||||
def store(self, obj):
|
||||
if self.find(obj.id) is not None:
|
||||
raise RepositoryException("Element having id=" + str(obj.id) + " already stored!")
|
||||
self._objects.append(obj)
|
||||
|
||||
def update(self, object):
|
||||
"""
|
||||
Update the instance given as parameter. The provided instance replaces the one having the same ID
|
||||
object - The object that will be updated
|
||||
Raises RepositoryException in case the object is not contained within the repository
|
||||
"""
|
||||
el = self.find(object.id)
|
||||
if el is None:
|
||||
raise RepositoryException("Element not found!")
|
||||
idx = self._objects.index(el)
|
||||
self._objects.remove(el)
|
||||
self._objects.insert(idx, object)
|
||||
|
||||
def find(self, objectId):
|
||||
for e in self._objects:
|
||||
if objectId == e.id:
|
||||
return e
|
||||
return None
|
||||
|
||||
def delete(self, objectId):
|
||||
"""
|
||||
Remove the object with given objectId from repository
|
||||
objectId - The objectId that will be removed
|
||||
Returns the object that was removed
|
||||
Raises RepositoryException if object with given objectId is not contained in the repository
|
||||
"""
|
||||
object = self.find(objectId)
|
||||
if object is None:
|
||||
raise RepositoryException("Element not in repository!")
|
||||
self._objects.remove(object)
|
||||
return object
|
||||
|
||||
def get_all(self):
|
||||
return self._objects
|
||||
|
||||
def __len__(self):
|
||||
return len(self._objects)
|
||||
|
||||
def __str__(self):
|
||||
r = ""
|
||||
for e in self._objects:
|
||||
r += str(e)
|
||||
r += "\n"
|
||||
return r
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
class RepositoryException(Exception):
|
||||
def __init__(self, message):
|
||||
self._message = message
|
||||
|
||||
def get_message(self):
|
||||
return self._message
|
||||
|
||||
def __str__(self):
|
||||
return self._message
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class CarRentalException(Exception):
|
||||
def __init__(self, msg):
|
||||
self.__msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return str(self.__msg)
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
from seminar.group_911.seminar_12.domain.car import Car
|
||||
from seminar.group_911.seminar_12.service.undo_service import call, operation
|
||||
|
||||
|
||||
class CarService:
|
||||
def __init__(self, undo_service, rental_service, validator, repository):
|
||||
self._validator = validator
|
||||
self._repository = repository
|
||||
self._rental_service = rental_service
|
||||
self._undo_service = undo_service
|
||||
|
||||
def create(self, car_id, license_plate, car_make, car_model):
|
||||
car = Car(car_id, license_plate, car_make, car_model)
|
||||
self._validator.validate(car)
|
||||
self._repository.store(car)
|
||||
return car
|
||||
|
||||
def delete(self, car_id):
|
||||
"""
|
||||
1. Delete the car from the repository
|
||||
"""
|
||||
car = self._repository.delete(car_id)
|
||||
|
||||
# undo/redo support for car deletion
|
||||
redo = call(self.delete, car_id)
|
||||
undo = call(self.create, car.id, car.license, car.make, car.model)
|
||||
self._undo_service.record_for_undo(operation(undo, redo))
|
||||
|
||||
'''
|
||||
2. Delete its rentals
|
||||
NB! This implementation is not transactional, i.e. the two delete operations are performed separately
|
||||
'''
|
||||
# NOTE also add rentals to undo/redo support
|
||||
rentals = self._rental_service.filter_rentals(None, car)
|
||||
for rent in rentals:
|
||||
self._rental_service.delete_rental(rent.id)
|
||||
return car
|
||||
|
||||
def update(self, car):
|
||||
"""
|
||||
NB! Undo/redo is also needed here
|
||||
"""
|
||||
# TODO Implement later...
|
||||
pass
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
from seminar.group_911.seminar_12.domain.client import Client
|
||||
from seminar.group_911.seminar_12.service.undo_service import call, operation
|
||||
|
||||
|
||||
class ClientService:
|
||||
def __init__(self, undo_service, rental_service, validator, repository):
|
||||
self._validator = validator
|
||||
self._repository = repository
|
||||
self._rental_service = rental_service
|
||||
self._undo_service = undo_service
|
||||
|
||||
def create(self, client_id, client_cnp, client_name):
|
||||
client = Client(client_id, client_cnp, client_name)
|
||||
self._validator.validate(client)
|
||||
self._repository.store(client)
|
||||
return client
|
||||
|
||||
def delete(self, client_id):
|
||||
"""
|
||||
1. Delete the client
|
||||
"""
|
||||
client = self._repository.delete(client_id)
|
||||
|
||||
# NOTE Record client deletion for undo/redo
|
||||
undo = call(self.create, client.id, client.cnp, client.name)
|
||||
redo = call(self.delete, client.id)
|
||||
# Operations go on the undo/redo stack
|
||||
self._undo_service.record_for_undo(operation(undo, redo))
|
||||
|
||||
'''
|
||||
2. Delete their rentals
|
||||
NB! This implementation is not transactional, i.e. the two delete operations are performed separately
|
||||
'''
|
||||
|
||||
# TODO Add undo/redo support for the client's rentals
|
||||
rentals = self._rental_service.filter_rentals(client, None)
|
||||
for rent in rentals:
|
||||
self._rental_service.delete_rental(rent.getId(), False)
|
||||
|
||||
return client
|
||||
|
||||
def get_client_count(self):
|
||||
return len(self._repository)
|
||||
|
||||
def update(self, car):
|
||||
"""
|
||||
NB! Undo/redo is also needed here
|
||||
"""
|
||||
pass
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
from seminar.group_911.seminar_12.domain.car_rental_exception import CarRentalException
|
||||
from seminar.group_911.seminar_12.domain.rental import Rental
|
||||
|
||||
|
||||
class RentalService:
|
||||
"""
|
||||
Service for rental operations
|
||||
"""
|
||||
|
||||
def __init__(self, undo_service, validator, rental_repo, car_repo, client_repo):
|
||||
self._validator = validator
|
||||
self._carRepo = car_repo
|
||||
self._cliRepo = client_repo
|
||||
self._repository = rental_repo
|
||||
|
||||
self._undoController = undo_service
|
||||
|
||||
def create_rental(self, rental_id, client, car, start, end):
|
||||
rental = Rental(rental_id, start, end, client, car)
|
||||
self._validator.validate(rental)
|
||||
|
||||
'''
|
||||
Check the car's availability for the given period
|
||||
'''
|
||||
if self.is_car_available(rental.car, rental.start, rental.end) is False:
|
||||
raise CarRentalException("Car is not available during that time!")
|
||||
|
||||
self._repository.store(rental)
|
||||
return rental
|
||||
|
||||
def is_car_available(self, car, start, end):
|
||||
"""
|
||||
Check the availability of the given car to be rented in the provided time period
|
||||
car - The availability of this car is verified
|
||||
start, end - The time span. The car is available if it is not rented in this time span
|
||||
Return True if the car is available, False otherwise
|
||||
"""
|
||||
rentals = self.filter_rentals(None, car)
|
||||
for rent in rentals:
|
||||
if start > rent.end or end < rent.start:
|
||||
continue
|
||||
return False
|
||||
return True
|
||||
|
||||
def filter_rentals(self, client, car):
|
||||
"""
|
||||
Return a list of rentals performed by the provided client for the provided car
|
||||
client - The client performing the rental. None means all clients
|
||||
cars - The rented car. None means all cars
|
||||
"""
|
||||
result = []
|
||||
for rental in self._repository.get_all():
|
||||
if client is not None and rental.client != client:
|
||||
continue
|
||||
if car is not None and rental.car != car:
|
||||
continue
|
||||
result.append(rental)
|
||||
return result
|
||||
|
||||
def delete_rental(self, rental_id):
|
||||
rental = self._repository.delete(rental_id)
|
||||
return rental
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
Ways to implement undo/redo
|
||||
|
||||
1. remember the program's state before each operation
|
||||
(deep copy list/dict/repository)
|
||||
Memento design pattern -- https://refactoring.guru/design-patterns/memento
|
||||
|
||||
2. carry out the opposite of each operation (undo) or redo the operation
|
||||
itself (redo)
|
||||
Command design pattern -- https://refactoring.guru/design-patterns/command
|
||||
|
||||
3. state-diffing
|
||||
What are the differences in the program before/after the operation?
|
||||
"""
|
||||
|
||||
|
||||
class call():
|
||||
def __init__(self, func_name, *func_params):
|
||||
self._func_name = func_name
|
||||
self._func_params = func_params
|
||||
|
||||
def call(self):
|
||||
return self._func_name(*self._func_params)
|
||||
|
||||
|
||||
class operation():
|
||||
def __init__(self, undo: call, redo: call):
|
||||
self._undo = undo
|
||||
self._redo = redo
|
||||
|
||||
def undo(self):
|
||||
self._undo.call()
|
||||
|
||||
def redo(self):
|
||||
self._redo.call()
|
||||
|
||||
|
||||
class cascaded_operation():
|
||||
def __init__(self, *operations):
|
||||
self._operations = operations
|
||||
|
||||
def undo(self):
|
||||
for oper in self._operations:
|
||||
oper.undo()
|
||||
|
||||
def redo(self):
|
||||
for oper in self._operations:
|
||||
oper.redo()
|
||||
|
||||
|
||||
class UndoRedoError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UndoService:
|
||||
def __init__(self):
|
||||
self._operations = []
|
||||
self._index = 0
|
||||
# flag == true means operation is not from undo_service
|
||||
# flag == false means don't record for undo/redo
|
||||
self._undo_flag = True
|
||||
|
||||
def record_for_undo(self, op: operation):
|
||||
# this is a callback from undo_service so it should not be recorded
|
||||
if self._undo_flag is False:
|
||||
return
|
||||
# NOTE this isn't actually complete
|
||||
self._operations.append(op)
|
||||
# update the undo/redo index to the latest value
|
||||
self._index = len(self._operations)
|
||||
|
||||
def undo(self):
|
||||
if self._index == 0:
|
||||
raise UndoRedoError("No more undos")
|
||||
|
||||
self._undo_flag = False
|
||||
self._operations[self._index - 1].undo()
|
||||
self._undo_flag = True
|
||||
self._index -= 1
|
||||
|
||||
def redo(self):
|
||||
if self._index >= len(self._operations):
|
||||
raise UndoRedoError("No more redos")
|
||||
self._undo_flag = False
|
||||
self._operations[self._index].redo()
|
||||
self._undo_flag = True
|
||||
self._index += 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def a(x, y, z, t):
|
||||
return x + y + z + t
|
||||
|
||||
|
||||
def b(x):
|
||||
return x ** 2
|
||||
|
||||
|
||||
call_b = call(b, 11)
|
||||
call_a = call(a, 1, 2, 3, 4)
|
||||
# ... other things ...
|
||||
print(call_a.call())
|
||||
print(call_b.call())
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Created on Nov 17, 2018
|
||||
|
||||
@author: Arthur
|
||||
"""
|
||||
from seminar.group_911.seminar_12.domain.car import CarValidator
|
||||
from seminar.group_911.seminar_12.domain.client import ClientValidator
|
||||
from seminar.group_911.seminar_12.domain.rental import RentalValidator
|
||||
from seminar.group_911.seminar_12.repository.repository import Repository
|
||||
from seminar.group_911.seminar_12.service.car_service import CarService
|
||||
from seminar.group_911.seminar_12.service.client_service import ClientService
|
||||
from seminar.group_911.seminar_12.service.rental_service import RentalService
|
||||
from seminar.group_911.seminar_12.service.undo_service import UndoService
|
||||
from seminar.group_911.seminar_12.util import print_repos_with_message
|
||||
|
||||
|
||||
def undo_example_hard():
|
||||
undo_service = UndoService()
|
||||
client_repo = Repository()
|
||||
car_repo = Repository()
|
||||
|
||||
'''
|
||||
Start rental Service
|
||||
'''
|
||||
rent_repo = Repository()
|
||||
rent_validator = RentalValidator()
|
||||
rent_service = RentalService(undo_service, rent_validator, rent_repo, car_repo, client_repo)
|
||||
|
||||
'''
|
||||
Start client Service
|
||||
'''
|
||||
client_validator = ClientValidator()
|
||||
client_service = ClientService(undo_service, rent_service, client_validator, client_repo)
|
||||
|
||||
'''
|
||||
Start car Service
|
||||
'''
|
||||
car_validator = CarValidator()
|
||||
car_service = CarService(undo_service, rent_service, car_validator, car_repo)
|
||||
|
||||
'''
|
||||
We add 2 clients
|
||||
'''
|
||||
sophia = client_service.create(103, "2990511035588", "Sophia")
|
||||
carol = client_service.create(104, "2670511035588", "Carol")
|
||||
|
||||
'''
|
||||
We add 2 cars
|
||||
'''
|
||||
hyundai_tucson = car_service.create(201, "CJ 02 TWD", "Hyundai", "Tucson")
|
||||
toyota_corolla = car_service.create(202, "CJ 02 FWD", "Toyota", "Corolla")
|
||||
print_repos_with_message("Added 2 clients and 2 cars", client_repo, car_repo, None)
|
||||
|
||||
'''
|
||||
We delete 1 client and 1 car
|
||||
'''
|
||||
client_service.delete(103)
|
||||
car_service.delete(202)
|
||||
print_repos_with_message("Deleted Sophia and the Corolla", client_repo, car_repo, None)
|
||||
|
||||
'''
|
||||
We undo twice
|
||||
'''
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo, the Corolla is back", client_repo, car_repo, None)
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo, Sophia is back", client_repo, car_repo, None)
|
||||
|
||||
'''
|
||||
Redo twice
|
||||
'''
|
||||
undo_service.redo()
|
||||
undo_service.redo()
|
||||
print_repos_with_message("2 redos, Sophia and the Corolla are again deleted", client_repo, car_repo, None)
|
||||
|
||||
'''
|
||||
Last redo
|
||||
'''
|
||||
# undo_service.redo()
|
||||
print_repos_with_message("1 redo - but there are no more redos", client_repo, car_repo, None)
|
||||
|
||||
|
||||
undo_example_hard()
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
from datetime import date
|
||||
|
||||
from seminar.group_911.seminar_12.domain.car import CarValidator
|
||||
from seminar.group_911.seminar_12.domain.client import ClientValidator
|
||||
from seminar.group_911.seminar_12.domain.rental import RentalValidator
|
||||
from seminar.group_911.seminar_12.repository.repository import Repository
|
||||
from seminar.group_911.seminar_12.service.car_service import CarService
|
||||
from seminar.group_911.seminar_12.service.client_service import ClientService
|
||||
from seminar.group_911.seminar_12.service.rental_service import RentalService
|
||||
from seminar.group_911.seminar_12.service.undo_service import UndoService
|
||||
from seminar.group_911.seminar_12.util import print_repos_with_message
|
||||
|
||||
|
||||
def undo_example_hardest():
|
||||
"""
|
||||
An example for doing multiple undo operations.
|
||||
This is a bit more difficult due to the fact that there are now several controllers,
|
||||
and each of them can perform operations that require undo support.
|
||||
|
||||
Follow the code below and figure out how it works!
|
||||
"""
|
||||
undo_service = UndoService()
|
||||
client_repo = Repository()
|
||||
car_repo = Repository()
|
||||
|
||||
'''
|
||||
Start rental Service
|
||||
'''
|
||||
rent_repo = Repository()
|
||||
rent_validator = RentalValidator()
|
||||
rent_service = RentalService(undo_service, rent_validator, rent_repo, car_repo, client_repo)
|
||||
|
||||
'''
|
||||
Start client Service
|
||||
'''
|
||||
client_validator = ClientValidator()
|
||||
client_service = ClientService(undo_service, rent_service, client_validator, client_repo)
|
||||
|
||||
'''
|
||||
Start car Service
|
||||
'''
|
||||
car_validator = CarValidator()
|
||||
car_service = CarService(undo_service, rent_service, car_validator, car_repo)
|
||||
|
||||
'''
|
||||
We add 1 client, 1 car and 2 rentals
|
||||
'''
|
||||
sophia = client_service.create(103, "2990511035588", "Sophia")
|
||||
hyundai_tucson = car_service.create(201, "CJ 02 TWD", "Hyundai", "Tucson")
|
||||
rent_service.create_rental(301, sophia, hyundai_tucson, date(2016, 11, 1), date(2016, 11, 30))
|
||||
rent_service.create_rental(302, sophia, hyundai_tucson, date(2016, 12, 1), date(2016, 12, 31))
|
||||
|
||||
print_repos_with_message("We added Sophia, the Hyundai and its 2 rentals", client_repo, car_repo, rent_repo)
|
||||
|
||||
car_service.delete(201)
|
||||
print_repos_with_message("Delete the Hyundai (also deletes its rentals)", client_repo, car_repo, rent_repo)
|
||||
|
||||
'''
|
||||
Now undo the performed operations, one by one
|
||||
'''
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo, the Hyundai and its rentals are back", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo deletes the second rental", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo deletes the first rental", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo deletes the Hyundai", client_repo, car_repo, rent_repo)
|
||||
|
||||
'''
|
||||
After 5 undos, all repos should be empty, as we did 5 operations in total
|
||||
'''
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo deletes Sophia (no more undos)", client_repo, car_repo, rent_repo)
|
||||
|
||||
'''
|
||||
Redos start here
|
||||
'''
|
||||
undo_service.redo()
|
||||
print_repos_with_message("1 redo and Sophia is added", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.redo()
|
||||
print_repos_with_message("1 redo adds the Hyundai", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.redo()
|
||||
print_repos_with_message("1 redo adds the first rental", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.redo()
|
||||
print_repos_with_message("1 redo adds the second rental", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.redo()
|
||||
print_repos_with_message("1 redo deletes the Hyundai and its rentals (again)", client_repo, car_repo, rent_repo)
|
||||
|
||||
'''
|
||||
Let's do a few undos again...
|
||||
'''
|
||||
undo_service.undo()
|
||||
undo_service.undo()
|
||||
undo_service.undo()
|
||||
|
||||
print_repos_with_message("3 undos later, we have Sophia and the Hyundai", client_repo, car_repo, rent_repo)
|
||||
|
||||
'''
|
||||
Now we try something new - let's add another car!
|
||||
|
||||
NB!
|
||||
A new operation must invalidate the history for redo() operations
|
||||
'''
|
||||
car_service.create(202, "CJ 02 SSE", "Dacia", "Sandero")
|
||||
print("\n Do we have a redo? -", undo_service.redo(), "\n")
|
||||
|
||||
'''
|
||||
Now we should have 2 cars
|
||||
'''
|
||||
print_repos_with_message("After adding the Dacia, there is no redo ", client_repo, car_repo, rent_repo)
|
||||
|
||||
'''
|
||||
However, undos is still available !
|
||||
'''
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo deletes the Dacia", client_repo, car_repo, rent_repo)
|
||||
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo deletes the Hyundai", client_repo, car_repo, rent_repo)
|
||||
|
||||
|
||||
undo_example_hardest()
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Created on Nov 17, 2018
|
||||
|
||||
@author: Arthur
|
||||
"""
|
||||
from seminar.group_911.seminar_12.domain.car import CarValidator
|
||||
from seminar.group_911.seminar_12.domain.client import ClientValidator
|
||||
from seminar.group_911.seminar_12.domain.rental import RentalValidator
|
||||
from seminar.group_911.seminar_12.repository.repository import Repository
|
||||
from seminar.group_911.seminar_12.service.car_service import CarService
|
||||
from seminar.group_911.seminar_12.service.client_service import ClientService
|
||||
from seminar.group_911.seminar_12.service.rental_service import RentalService
|
||||
from seminar.group_911.seminar_12.service.undo_service import UndoService
|
||||
from seminar.group_911.seminar_12.util import print_repos_with_message
|
||||
|
||||
|
||||
def undo_example_medium():
|
||||
undo_service = UndoService()
|
||||
client_repo = Repository()
|
||||
car_repo = Repository()
|
||||
|
||||
'''
|
||||
Start rental Controller
|
||||
'''
|
||||
rent_repo = Repository()
|
||||
rent_validator = RentalValidator()
|
||||
rent_service = RentalService(undo_service, rent_validator, rent_repo, car_repo, client_repo)
|
||||
|
||||
'''
|
||||
Start client Controller
|
||||
'''
|
||||
client_validator = ClientValidator()
|
||||
client_service = ClientService(undo_service, rent_service, client_validator, client_repo)
|
||||
|
||||
'''
|
||||
Start car Controller
|
||||
'''
|
||||
car_validator = CarValidator()
|
||||
car_service = CarService(undo_service, rent_service, car_validator, car_repo)
|
||||
|
||||
'''
|
||||
We add 3 clients
|
||||
'''
|
||||
sophia = client_service.create(103, "2990511035588", "Sophia")
|
||||
carol = client_service.create(104, "2670511035588", "Carol")
|
||||
bob = client_service.create(105, "2590411035588", "Bob")
|
||||
print_repos_with_message("We added 3 clients", client_repo, None, None)
|
||||
|
||||
'''
|
||||
We delete 2 of the clients
|
||||
'''
|
||||
client_service.delete(103)
|
||||
client_service.delete(105)
|
||||
print_repos_with_message("Deleted Sophia and Bob", client_repo, None, None)
|
||||
|
||||
'''
|
||||
We undo twice
|
||||
'''
|
||||
undo_service.undo()
|
||||
print_repos_with_message("1 undo, so Bob is back", client_repo, None, None)
|
||||
undo_service.undo()
|
||||
print_repos_with_message("Another undo, so Sophia is back too", client_repo, None, None)
|
||||
|
||||
'''
|
||||
We redo once
|
||||
'''
|
||||
undo_service.redo()
|
||||
print_repos_with_message("1 redo, so Sophia is again deleted", client_repo, None, None)
|
||||
|
||||
|
||||
undo_example_medium()
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
def print_repos_with_message(msg, client_repo, car_repo, rent_repo):
|
||||
print("-" * 15 + msg + "-" * 15)
|
||||
if client_repo is not None:
|
||||
print("Clients:\n" + str(client_repo))
|
||||
if car_repo is not None:
|
||||
print("Cars:\n" + str(car_repo))
|
||||
if rent_repo is not None:
|
||||
print("Rentals:\n" + str(rent_repo))
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import random
|
||||
|
||||
from texttable import Texttable
|
||||
|
||||
|
||||
class MineField():
|
||||
def __init__(self, rows, cols, mines: int):
|
||||
self._rows = rows
|
||||
self._cols = cols
|
||||
self._mines = mines
|
||||
# we always want to create the mine field when initializing
|
||||
self._data = []
|
||||
|
||||
"""
|
||||
Meaning of values in self._data:
|
||||
0 - 8 => number of adjacent mines (square is not mined)
|
||||
9 => square is mined
|
||||
+10 => square is revealed
|
||||
+100 => square is flaged
|
||||
"""
|
||||
for row in range(self._rows):
|
||||
self._data.append([0] * self._cols)
|
||||
self._lay_mines()
|
||||
|
||||
def _lay_mines(self):
|
||||
# 9 means the square is mined
|
||||
my_mines = [9] * self._mines
|
||||
my_mines += [0] * (self._rows * self._cols - self._mines)
|
||||
random.shuffle(my_mines)
|
||||
|
||||
for row in range(self._rows):
|
||||
for col in range(self._cols):
|
||||
if my_mines[row * self._cols + col] == 9:
|
||||
# lay the mine
|
||||
self._data[row][col] = my_mines[row * self._cols + col]
|
||||
# update mine adjacency
|
||||
for i in [-1, 0, 1]:
|
||||
for j in [-1, 0, 1]:
|
||||
if i == 0 and j == 0:
|
||||
continue
|
||||
if (0 > row + i) or (row + i >= self._rows):
|
||||
continue
|
||||
if (0 > col + j) or (col + j >= self._cols):
|
||||
continue
|
||||
if self._data[row + i][col + j] != 9:
|
||||
self._data[row + i][col + j] += 1
|
||||
|
||||
def __str__(self):
|
||||
t = Texttable()
|
||||
|
||||
header = ['X']
|
||||
for ascii in range(self._cols):
|
||||
header.append(chr(65 + ascii))
|
||||
|
||||
t.header(header)
|
||||
for r in range(self._rows):
|
||||
t.add_row([r + 1] + self._data[r])
|
||||
|
||||
return t.draw()
|
||||
|
||||
|
||||
f = MineField(8, 10, 50)
|
||||
print(f)
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
RO650,Cluj,05:45,Bucuresti,06:40
|
||||
0B3302,Cluj,07:15,Bucuresti,08:15
|
||||
SLD322,Timisoara,09:05,Cluj,10:00
|
||||
RO643,Bucuresti,10:15,Cluj,11:10
|
||||
RO734,Timisoara,10:45,Iasi,12:25
|
||||
KL2710,Timisoara,14:25,Bucuresti,15:40
|
||||
RO745,Cluj,12:50,Iasi,14:05
|
||||
RO746,Iasi,14:30,Cluj,15:50
|
||||
RO647,Bucuresti,18:05,Cluj,19:00
|
||||
KL2706,Bucuresti,18:10,Timisoara,19:05
|
||||
RO733,Iasi,08:30,Timisoara,10:20
|
||||
SLD363,Cluj,19:35,Timisoara,20:30
|
||||
RO649,Bucuresti,21:55,Cluj,22:50
|
||||
0B3101,Bucuresti,22:55,Cluj,23:55
|
||||
RP621,Bucuresti,07:30,Oradea,08:55
|
||||
RO622,Oradea,09:20,Bucuresti,10:40
|
||||
RO627,Bucuresti,17:55,Oradea,19:20
|
||||
RO628,Oradea,19:45,Bucuresti,21:05
|
||||
XL897,TgMures,08:55,Oradea,09:30
|
||||
XL898,Oradea,20:45,TgMures,21:25
|
||||
LH012,Iasi,14:35,Oradea,15:35
|
||||
LH013,Oradea,17:00,Iasi,18:10
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
Write a console-based application to help air traffic control (ATC) monitor all domestic flights taking place during one day (00:00 – 23:59). The application must include the following features:
|
||||
1. Flight information is kept in a text file, using the format in the example below. When the program starts, flight information is read from the file [1p]. Each modification is persisted to the text file [1p].
|
||||
2. Add a new flight. Each flight has an identifier, a departure city and time, and an arrival city and time [1p]. Flight identifiers are unique; flight times are between 15 and 90 minutes; an airport can handle a single operation (departure or arrival) during each minute [1p].
|
||||
3. Delete a flight. The user provides the flight identifier. If it does not exist, an error message is displayed [1p].
|
||||
4. List the airports, in decreasing order of activity (number of departures and arrivals during the day) [1p].
|
||||
5. List the time intervals during which no flights are taking place, in decreasing order of length. [1.5p].
|
||||
6. The tracking radar suffers a failure. The backup radar can be used, but it can only track a single flight at a time. Determine the maximum number of flights that can proceed as planned. List them using the format below [1.5p]:
|
||||
05:45 | 06:40 | RO650 | Cluj - Bucuresti
|
||||
|
||||
Non-functional requirements:
|
||||
• Implement an object-oriented, layered architecture solution using the Python language.
|
||||
• Provide specification and unit tests for Repository/Controller functions related with the second functionality. In case specification or tests are missing, the functionality is graded at 50%.
|
||||
|
||||
Observations!
|
||||
• The day starts at 00:00 and ends at 23:59.
|
||||
• Default 1p.
|
||||
|
||||
## TODO
|
||||
-= Iteration 1 =-
|
||||
1. Write the Flight class
|
||||
-> what are the attributes?
|
||||
-> ! use properties for each of them
|
||||
|
||||
2. Write a FlightTextRepo class
|
||||
-> read the flights on program start (__init__)
|
||||
# -> save the flights text file (def save_file(...))
|
||||
# -> write method add_flight
|
||||
|
||||
3. Write the FlightServices class
|
||||
-> get_all_flights() # return all flights from repo
|
||||
|
||||
4. Write the UI class
|
||||
-> write the __init__
|
||||
-> display the main menu:
|
||||
a. Display all flights
|
||||
x. Exit
|
||||
-> displays all flights at menu (a)
|
||||
|
||||
-= Iteration 2 =-
|
||||
0. Create /tests directory and add PyUnit tests
|
||||
|
||||
1. Write FlightValidation class
|
||||
-> is_valid (return True iff flight is valid)
|
||||
|
||||
2. class FlightTextRepo
|
||||
-> write method add_flight
|
||||
-> save the flights text file (def save_file(...))
|
||||
|
||||
3. Write the FlightServices class
|
||||
-> get_all_flights() # return all flights from repo
|
||||
-> add_flight(...)
|
||||
|
||||
4. Write the UI class
|
||||
-> display the main menu:
|
||||
a. Display all flights
|
||||
b. Add flight
|
||||
x. Exit
|
||||
-> displays all flights at menu (a)
|
||||
-> read flight data -> save it via services/repo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
from traceback import print_exc
|
||||
import random
|
||||
import math
|
||||
|
||||
"""
|
||||
Let's create a menu-driven application. This is the menu:
|
||||
1. Generate rational numbers
|
||||
2. Sort the list of numbers
|
||||
0. Exit
|
||||
"""
|
||||
|
||||
"""
|
||||
Functions for Q numbers
|
||||
"""
|
||||
|
||||
|
||||
def create_q(num, denom=1):
|
||||
# TODO What about 1/0 !!
|
||||
gcd = math.gcd(num, denom)
|
||||
return [num // gcd, denom // gcd]
|
||||
|
||||
|
||||
def generate_rationals_v2():
|
||||
"""Function used in order to generate rational numbers"""
|
||||
numbers_list = []
|
||||
|
||||
nr_numbers = input("Please input how many numbers you'd like to store.")
|
||||
nr_numbers = int(nr_numbers)
|
||||
|
||||
for iterator in range(0, nr_numbers):
|
||||
num = random.randint(-10, 10)
|
||||
denom = random.randint(1, 20)
|
||||
numbers_list.append(create_q(num, denom))
|
||||
|
||||
for q in numbers_list:
|
||||
print(tranform_to_string(q))
|
||||
return numbers_list
|
||||
|
||||
|
||||
def tranform_to_string(rational_nr_list):
|
||||
"""
|
||||
Function used in order to represent the rational number as a string
|
||||
|
||||
:param rational_nr_list: a list of num and denom
|
||||
:return: a string
|
||||
"""
|
||||
num, denom = rational_nr_list
|
||||
result = num / denom
|
||||
int_result = int(result)
|
||||
|
||||
if result == int_result:
|
||||
string_to_be_returned = str(result)
|
||||
else:
|
||||
string_to_be_returned = f"{num} / {denom}"
|
||||
|
||||
return string_to_be_returned
|
||||
|
||||
|
||||
def start():
|
||||
while True:
|
||||
print("1. Generate rational numbers")
|
||||
print("2. Sort the list of numbers")
|
||||
print("0. Exit")
|
||||
|
||||
opt = input(">") # by default reads str
|
||||
|
||||
# print(type(opt))
|
||||
if opt == "1":
|
||||
generate_rationals_v2()
|
||||
elif opt == "2":
|
||||
pass
|
||||
elif opt == "0":
|
||||
return # or break
|
||||
else:
|
||||
print("Bad command or file name :)")
|
||||
|
||||
|
||||
start()
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
"""
|
||||
1. Determine the time complexity of the following algorithms as a function of n.
|
||||
source: https://complex-systems-ai.com/en/algorithmic/corrected-exercises-time-complexity/
|
||||
"""
|
||||
|
||||
"""
|
||||
n -> size of the algorithm's input
|
||||
T(n) -> number of operations made by program
|
||||
T(n) = 1 -> constant time (a single operation)
|
||||
O notation -> O(1) is also O(n) is also O(n^2) ...
|
||||
"""
|
||||
|
||||
|
||||
# T(n) = 10 * n * 1 => O(n)
|
||||
def f_1(n: int):
|
||||
# does not depend on n
|
||||
for i in range(10):
|
||||
# depends on n linearly -> O(n)
|
||||
for j in range(n):
|
||||
# assume print is constant time -> T(1)
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# T(n) = 10 * n * 1 => O(n)
|
||||
def f_2(n: int):
|
||||
# does not actually depend on n
|
||||
for i in range(n, n + 10):
|
||||
# depends on n linearly -> O(n)
|
||||
for j in range(n):
|
||||
# assume print is constant time -> T(1)
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# T(n) = (n-1) * n (simplified)
|
||||
# T(n) = (n-1) + (n-2) + ... + 1 => O(n^2)
|
||||
def f_3(n: int):
|
||||
# depends on n linearly
|
||||
for i in range(1, n):
|
||||
# depends on n linearly
|
||||
for j in range(i, n):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
def f_4(n: int):
|
||||
for i in range(n):
|
||||
for j in range(2 * i + 1):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# time complexity O(n^4)
|
||||
|
||||
# 1 + 2 + ... + (n^2-2) = ((n^2 - 2)*(n^2-1))/2
|
||||
# (n^2 - 1) * (n^2 - 1)
|
||||
# complexity is O(n^4)
|
||||
# if for n = 10, runtime is 1ms, what's the runtime for n = 20 ?
|
||||
def f_5(n: int):
|
||||
# i ranges between 0 and n^2 => first loop is O(n^2)
|
||||
for i in range(n ** 2):
|
||||
# j depends on i which loops to n^2 => second loop is O(n^2)
|
||||
# for j in range(i): # probably faster, as i starts with lower values
|
||||
for j in range(1, n ** 2):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# O(n * log(n))
|
||||
def f_6(n: int):
|
||||
# outer loop is O(n)
|
||||
for i in range(n):
|
||||
t = 1
|
||||
# how many times can we multiply by 2 until we reach n?
|
||||
# inner loop is O(log(n))
|
||||
while t < n:
|
||||
print("Hello World")
|
||||
t *= 2
|
||||
|
||||
|
||||
"""
|
||||
1. Time complexity in both "n" and "m"
|
||||
"""
|
||||
|
||||
|
||||
# complexity is O(n + m)
|
||||
# e.g., merging an array with n elements with an array having m elements
|
||||
def f_7(n, m: int):
|
||||
# O(n)
|
||||
for i in range(0, n):
|
||||
print("Hello World")
|
||||
# O(m)
|
||||
for j in range(0, m):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# time complexity is O(n)
|
||||
def f_8(n, m: int):
|
||||
for i in range(0, n):
|
||||
print("Hello World")
|
||||
for j in range(0, n):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# O(n^2)
|
||||
def f_9(n: int):
|
||||
for i in range(n):
|
||||
for j in range(n):
|
||||
print("Hello World")
|
||||
for k in range(n):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
# O(n^2)
|
||||
def f_10(n: int):
|
||||
for i in range(n):
|
||||
# depends on n => O(n) for inner loop
|
||||
for j in range(n, i, -1):
|
||||
print("Hello World")
|
||||
|
||||
|
||||
"""
|
||||
Analyze the time and space complexity
|
||||
"""
|
||||
|
||||
|
||||
# n = len(data)
|
||||
# time complexity is O(n * log_3(n))
|
||||
# space complexity is O(1)
|
||||
def f_11(data: list):
|
||||
data_sum = 0
|
||||
for el in data:
|
||||
j = len(data)
|
||||
while j > 1:
|
||||
data_sum += el * j
|
||||
j = j // 3
|
||||
return data_sum
|
||||
|
||||
|
||||
"""
|
||||
1. Time comlexity
|
||||
T(n) = 1, n <= 1
|
||||
T(n) = 2 * T(n /2) + 1
|
||||
|
||||
T(n/2) = 2 * T(n/4) + 1
|
||||
T(n/4) = 2 * T(n/8) + 1
|
||||
|
||||
T(n) = 2 * T(n /2) + 1 = 2 * [2 * T(n/4) + 1] + 1 = 4 * T(n/4) + 2 + 1
|
||||
= 4 * [2 * T(n/8) + 1] + 2 + 1 = 8 * T(n/8) + 4 + 2 + 1
|
||||
|
||||
T(n) = 8 * T(n/8) + 4 + 2 + 1
|
||||
assume 2^k = n, k = log_2(n)
|
||||
|
||||
T(n) = n * T(1) + 2^(k-1) + ... + 2^0 = n + 2^k - 1 = n => O(n)
|
||||
|
||||
2. Space complexity
|
||||
T(n) = 1, n <= 1
|
||||
T(n) = 2 * T(n /2) + n
|
||||
T(n/2) = 2 * T(n/4) + n/2
|
||||
T(n/4) = 2 * T(n/8) + n/4
|
||||
|
||||
T(n) = 2 * T(n /2) + n = 2 * [2 * T(n/4) + n/2] + n = ...
|
||||
like previously, only with n instead of 1 as final term
|
||||
"""
|
||||
|
||||
|
||||
def f_12(data: list):
|
||||
if len(data) == 0:
|
||||
return 0
|
||||
if len(data) == 1:
|
||||
return data[0]
|
||||
m = len(data) // 2
|
||||
return f_12(data[:m]) + f_12(data[m:])
|
||||
|
||||
|
||||
# O(n^2 * log_10(n))
|
||||
def f_13(n: int):
|
||||
s = 0
|
||||
for i in range(1, n ** 2): # n^2 loop
|
||||
j = i
|
||||
while j != 0: # log_10(i) < log_10(n^2), if n large
|
||||
s = s + j - 10 * j // 10
|
||||
j //= 10
|
||||
return s
|
||||
|
||||
|
||||
"""
|
||||
T(n) = 1, n <= 1
|
||||
T(n) = 4 * T(n/2) + 1, n > 1
|
||||
|
||||
T(n/2) = 4 * T(n/4) + 1
|
||||
T(n/4) = 4 * T(n/8) + 1
|
||||
|
||||
T(n) = 4 * [4 * T(n/4) + 1] + 1 = 16 * T(n/4) + 4 + 1 =
|
||||
= 16 * [4 * T(n/8) + 1] + 4 + 1 =
|
||||
2^k = n
|
||||
|
||||
T(n) = (2^k)^2 * T(1) + 4^(k-1) + ... + 4^0 = n^2 + => O(n^2)
|
||||
"""
|
||||
|
||||
|
||||
def f_14(n, i: int):
|
||||
if n > 1:
|
||||
i *= 2
|
||||
m = n // 2
|
||||
f_14(m, i - 2)
|
||||
f_14(m, i - 1)
|
||||
f_14(m, i + 2)
|
||||
f_14(m, i + 1)
|
||||
else:
|
||||
print(i)
|
||||
|
||||
|
||||
"""
|
||||
Analyze the algorithm's time complexity. Write an equivalent algorithm with
|
||||
a strictly better time complexity
|
||||
|
||||
1. What's the time complexity?
|
||||
2. What does it do?
|
||||
3. Find a better O(n) to do it in...
|
||||
"""
|
||||
|
||||
|
||||
def f_15(data: list):
|
||||
i = 0
|
||||
j = 0
|
||||
m = 0
|
||||
c = 0
|
||||
while i < len(data):
|
||||
if data[i] == data[j]:
|
||||
c += 1
|
||||
j += 1
|
||||
if j >= len(data):
|
||||
if c > m:
|
||||
m = c
|
||||
c = 0
|
||||
i += 1
|
||||
j = i
|
||||
return m
|
||||
|
||||
|
||||
# TODO Time complexity
|
||||
def f_15_better(data: list):
|
||||
freq_dict = {}
|
||||
|
||||
max_freq = 1
|
||||
for el in list:
|
||||
if el not in freq_dict:
|
||||
freq_dict[el] = 1
|
||||
else:
|
||||
freq_dict[el] += 1
|
||||
max_freq = max(freq_dict[el], max_freq)
|
||||
return max_freq
|
||||
|
||||
|
||||
"""
|
||||
What is the time complexity when the following algorithm is implemented via linear exponentiation. How can this be
|
||||
optimized and how will that improve the complexity?
|
||||
"""
|
||||
|
||||
|
||||
def f_16(x, n: int):
|
||||
"""
|
||||
The algorithms returns x ** n
|
||||
:param x:
|
||||
:param n:
|
||||
:return:
|
||||
"""
|
||||
# TODO Implement me
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
Implement and discuss the complexity of merge sort
|
||||
"""
|
||||
|
||||
|
||||
def merge_sort(data: list):
|
||||
# TODO Implement me
|
||||
pass
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
Divide & Conquer + Combine (the results of the small, already solved subproblems
|
||||
"""
|
||||
import random
|
||||
|
||||
"""
|
||||
1. Find the smallest number in a list (chip & conquer, divide in halves, recursive vs non-recursive). Return None for
|
||||
an empty list
|
||||
a. Chip & conquer, recursive
|
||||
b. Divide in halves, non-recursive
|
||||
c. Divide in halves, recursive
|
||||
"""
|
||||
|
||||
|
||||
# a. Chip & conquer, recursive
|
||||
def array_min_impl(array: list, start_index: int):
|
||||
if start_index == len(array) - 1:
|
||||
return array[start_index]
|
||||
return min(array_min_impl(array, start_index + 1), array[start_index])
|
||||
|
||||
|
||||
def array_min(array: list):
|
||||
if len(array) == 0:
|
||||
return None
|
||||
return array_min_impl(array, 0)
|
||||
|
||||
|
||||
# b. Divide in halves, non-recursive
|
||||
def divide_in_halves_iter(my_list: list):
|
||||
"""Returns the smallest number from a list"""
|
||||
if not len(my_list):
|
||||
return None
|
||||
|
||||
min_found = my_list[0]
|
||||
stack = [(0, len(my_list) - 1)]
|
||||
|
||||
# As long as we have elements in the stack
|
||||
while len(stack):
|
||||
left, right = stack.pop()
|
||||
if left == right:
|
||||
min_found = min(my_list[left], min_found)
|
||||
|
||||
# Then we continue with the next item from the stack
|
||||
continue
|
||||
|
||||
# If we got to this point, it means left != right
|
||||
mid = (left + right) // 2
|
||||
|
||||
# We look in the first half
|
||||
stack.append((left, mid))
|
||||
|
||||
# We look in the second half
|
||||
stack.append((mid + 1, right))
|
||||
|
||||
return min_found
|
||||
|
||||
|
||||
# c. Divide in halves, recursive
|
||||
def calc_array_min_impl(array: list, left, right: int):
|
||||
if right == left:
|
||||
return array[left]
|
||||
mid = (left + right) // 2
|
||||
return min(calc_array_min_impl(array, left, mid), calc_array_min_impl(array, mid + 1, right))
|
||||
|
||||
|
||||
def calc_array_min(array: list):
|
||||
if len(array) == 0:
|
||||
return None
|
||||
return calc_array_min_impl(array, 0, len(array) - 1)
|
||||
|
||||
|
||||
# def test_divide():
|
||||
# for count in range(1000):
|
||||
# length = random.randint(1, 100)
|
||||
# array = []
|
||||
# for i in range(length):
|
||||
# array.append(random.randint(-100, 100))
|
||||
# assert calc_array_min(array) == min(array), (calc_array_min(array), array)
|
||||
# assert array_min(array) == min(array), (array_min(array), array)
|
||||
# assert divide_in_halves_iter(array) == min(array), (divide_in_halves_iter(array), array)
|
||||
# # special case - empty array
|
||||
# assert calc_array_min([]) is None
|
||||
# assert array_min([]) is None
|
||||
# assert divide_in_halves_iter([]) is None
|
||||
#
|
||||
#
|
||||
# test_divide()
|
||||
|
||||
"""
|
||||
2. Exponential search
|
||||
a. Generate a pseudo-random array of increasing elements
|
||||
b. Implement exponential search
|
||||
c. Implement binary search
|
||||
d. Driver & test functions
|
||||
"""
|
||||
|
||||
|
||||
def generate_random_increasing_array():
|
||||
# n=length of array
|
||||
n = random.randint(0, 100)
|
||||
array = [random.randint(0, 100)]
|
||||
for i_ul in range(1, n):
|
||||
array.append(array[i_ul - 1] + random.randint(0, 2))
|
||||
return array
|
||||
|
||||
|
||||
def exponential_search(array: list, key: int):
|
||||
poz = 1
|
||||
while poz <= len(array) - 1 and array[poz] < key:
|
||||
poz *= 2
|
||||
|
||||
# TODO Replace the linear search with binary search
|
||||
for i in range(poz // 2, min(poz + 1, len(array))):
|
||||
if array[i] == key:
|
||||
return i
|
||||
return -1
|
||||
|
||||
|
||||
# def test_exponential_search():
|
||||
# for i in range(1000):
|
||||
# array = generate_random_increasing_array()
|
||||
# pos = random.randint(0, len(array) - 1)
|
||||
#
|
||||
# # array[...] as array the array is not strictly increasing
|
||||
# assert array[exponential_search(array, array[pos])] == array[pos], (pos, array, array[pos])
|
||||
|
||||
|
||||
# test_exponential_search()
|
||||
|
||||
"""
|
||||
3. Calculate the r-th root of a given number x with a given precision p
|
||||
"""
|
||||
|
||||
"""
|
||||
4. Calculate the maximum subarray sum (subarray = elements having continuous indices)
|
||||
a. Naive implementation
|
||||
b. Divide & conquer implementation
|
||||
|
||||
e.g.
|
||||
for data = [-2, -5, 6, -2, -3, 1, 5, -6], maximum subarray sum is 7.
|
||||
"""
|
||||
|
||||
"""
|
||||
Backtracking
|
||||
"""
|
||||
|
||||
"""
|
||||
5. Recursive implementation for permutations
|
||||
"""
|
||||
|
||||
|
||||
def consistent(x):
|
||||
"""
|
||||
Determines whether the current partial array can lead to a solution
|
||||
"""
|
||||
return len(set(x)) == len(x)
|
||||
|
||||
|
||||
def solution(x, n):
|
||||
"""
|
||||
Determines whether we have a solution
|
||||
"""
|
||||
return len(x) == n
|
||||
|
||||
|
||||
def solution_found(x):
|
||||
"""
|
||||
What to do when a solution is found
|
||||
"""
|
||||
print("Solution: ", x)
|
||||
|
||||
|
||||
def bkt_rec(x, n):
|
||||
"""
|
||||
Backtracking algorithm for permutations problem, recursive implementation
|
||||
"""
|
||||
x.append(0)
|
||||
for i in range(0, n):
|
||||
x[len(x) - 1] = i
|
||||
if consistent(x):
|
||||
if solution(x, n):
|
||||
solution_found(x)
|
||||
else:
|
||||
bkt_rec(x[:], n)
|
||||
|
||||
|
||||
bkt_rec([], 4)
|
||||
|
||||
"""
|
||||
6. Change the code for generating the permutation above to work for the n-Queen problem
|
||||
"""
|
||||
|
||||
"""
|
||||
A Latin square is an n × n square filled with n different symbols, each occurring exactly once in each row and exactly
|
||||
once in each column
|
||||
|
||||
7. Generate all the N x N Latin squares for a given number N.
|
||||
|
||||
8. Generate all reduced N x N Latin squares for a given number N. In a reduced Latin square, the elements of the first
|
||||
row and column are sorted.
|
||||
"""
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
"""
|
||||
Dynamic programming
|
||||
"""
|
||||
|
||||
"""
|
||||
1. Calculate the maximum subarray sum (subarray = elements having continuous
|
||||
indices)
|
||||
|
||||
e.g.
|
||||
for data = [-2, -5, 6, -2, -3, 1, 5, -6], maximum subarray sum is 7.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
# sys.maxsize
|
||||
|
||||
# Ineficient solution-Dobocan Raul
|
||||
# An O(n^2) implementation
|
||||
def max_subarray_sum(array: list):
|
||||
if len(array) == 0:
|
||||
return None
|
||||
|
||||
maxx = array[0]
|
||||
for i in range(0, len(array)):
|
||||
sum = 0
|
||||
for j in range(i, len(array)):
|
||||
sum += array[j]
|
||||
if sum >= maxx:
|
||||
maxx = sum
|
||||
return maxx
|
||||
|
||||
|
||||
# l = [-2, -5, 6, -2, -3, 1, 5, -2]
|
||||
# print(max_subarray_sum(l))
|
||||
|
||||
# Solution with dinamic programming - Enache Vlad
|
||||
def max_subarray_sum_DP(arr: list):
|
||||
if len(arr) == 0:
|
||||
return None
|
||||
max_ending_here = arr[0] # initialising the sum with the first element
|
||||
max_global = arr[0]
|
||||
for i in range(1, len(arr)):
|
||||
if arr[i] + max_ending_here < arr[i]: # the case when we start the sum again
|
||||
max_ending_here = arr[i]
|
||||
else: # the case when we continue the sum
|
||||
max_ending_here += arr[i]
|
||||
max_global = max(max_global, max_ending_here)
|
||||
|
||||
return max_global
|
||||
|
||||
|
||||
# print([-2, -5, 6, -2, -3, 1, 5, -2])
|
||||
|
||||
"""
|
||||
Maximum subarray divide and conquer - Cirlea Mihai Alexandru
|
||||
|
||||
e.g.
|
||||
for data = [-2, -5, 6, -2, -3, 1, 5, -6], maximum subarray sum is 7.
|
||||
"""
|
||||
|
||||
|
||||
# TODO Check this for bugs
|
||||
def max_subarray_div_conq(arr: list):
|
||||
"""Uses div and conquer iterative technique to get the max subarray."""
|
||||
# Checking for empty arrays
|
||||
if not arr:
|
||||
return
|
||||
|
||||
stack = [(0, len(arr) - 1)]
|
||||
max_sum = arr[0]
|
||||
|
||||
while stack:
|
||||
left, right = stack.pop()
|
||||
if left == right:
|
||||
max_sum = max(arr[left], max_sum)
|
||||
|
||||
# Continue with the next item from the stack
|
||||
continue
|
||||
|
||||
middle = (left + right) // 2
|
||||
|
||||
max_left, max_right = arr[left], arr[middle + 1]
|
||||
sum_left, sum_right = 0, 0
|
||||
|
||||
# Creating the maximum sum in the left part
|
||||
for iterator in range(left, middle + 1):
|
||||
if arr[iterator] > sum_left:
|
||||
sum_left = arr[iterator]
|
||||
|
||||
else:
|
||||
sum_left += arr[iterator]
|
||||
|
||||
max_left = sum_left
|
||||
|
||||
# Creating the maximum sum in the right part
|
||||
for iterator in range(middle + 1, right + 1):
|
||||
sum_right += arr[iterator]
|
||||
|
||||
if sum_right > max_right:
|
||||
max_right = sum_right
|
||||
|
||||
max_sum = max(max_right + max_left, max_sum)
|
||||
|
||||
stack.append((left, middle))
|
||||
stack.append((middle + 1, right))
|
||||
|
||||
return max_sum
|
||||
|
||||
|
||||
"""
|
||||
2. Count in how many ways we can provide change to a given sum of money (N), when provided infinite
|
||||
supply of given coin denominations.
|
||||
|
||||
e.g. Let's say N = 10, and we have coins of values (1, 5, 10); we can give change in 4 ways (10, 5 + 5, 5 + 1 + ...
|
||||
and 1 + ... + 1)
|
||||
"""
|
||||
|
||||
"""
|
||||
3. 0-1 Knapsack problem. Given the weights and values of N items, put them
|
||||
in a knapsack having capacity W so that you
|
||||
maximize the value of the stored items. Items cannot be broken up
|
||||
(0-1 property)
|
||||
"""
|
||||
W = 11
|
||||
weights = [1, 2, 3, 4, 7]
|
||||
values = [1, 5, 8, 3, 2]
|
||||
|
||||
|
||||
def knapsack_01(W: int, weights, values: list, current: int):
|
||||
if current == len(weights):
|
||||
return 0
|
||||
|
||||
value_include = 0
|
||||
if W - weights[current] >= 0:
|
||||
value_include = values[current] + knapsack_01(W - weights[current], weights, values, current + 1)
|
||||
value_exclude = knapsack_01(W, weights, values, current + 1)
|
||||
|
||||
return max(value_include, value_exclude)
|
||||
|
||||
|
||||
print(knapsack_01(W, weights, values, 0))
|
||||
|
||||
"""
|
||||
4. Gold mine problem (a.k.a checkerboard problem)
|
||||
https://www.geeksforgeeks.org/gold-mine-problem
|
||||
"""
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
"""
|
||||
Write an application that manages a list of rectangles.
|
||||
Each rectangle is represented using its two opposite corners (x1, y1) and (x2, y2)
|
||||
The application will have a menu-driven user interface and will provide the following features:
|
||||
|
||||
1. Add a rectangle
|
||||
- adds the given rectangle to the list.
|
||||
- error if the given rectangle already exists, x1 <= x2 or y1 <= y2
|
||||
|
||||
2. Delete a rectangle
|
||||
- deletes the rectangle with the given corners
|
||||
- error if non-existing rectangle is given
|
||||
|
||||
3. Show all rectangles
|
||||
- shows all rectangles in descending order of their area
|
||||
|
||||
4. Show rectangles that intersect a given one
|
||||
- select a rectangle from the list of existing rectangle
|
||||
- print those which intersect it by descending order of area
|
||||
|
||||
5. exit
|
||||
- exit the program
|
||||
|
||||
Observations:
|
||||
- Add 10 random rectangles at program startup
|
||||
- Write specifications for non-UI functions
|
||||
- Each function does one thing only, and communicates via parameters and return value
|
||||
- The program reports errors to the user. It must also report errors from non-UI functions!
|
||||
- Make sure you understand the rectangle's representation
|
||||
- Try to reuse functions across functionalities (Less code to write and test)
|
||||
- Don't use global variables!
|
||||
"""
|
||||
import random
|
||||
|
||||
|
||||
#
|
||||
# Write the implementation for Seminar 06 in this file
|
||||
#
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# Functions to deal with rectangles -- list representation
|
||||
# -> There should be no print or input statements in this section
|
||||
# -> Each function should do one thing only
|
||||
# -> Functions communicate using input parameters and their return values
|
||||
#
|
||||
# def create_rect(x1, y1, x2, y2: int):
|
||||
# """
|
||||
# Create a rectangle with corners (x1, y1) and (x2, y2).
|
||||
# :param x1:
|
||||
# :param y1:
|
||||
# :param x2:
|
||||
# :param y2:
|
||||
# :return: The newly created rectangle
|
||||
# """
|
||||
# pass
|
||||
|
||||
|
||||
# def rect_equal(rect1, rect2):
|
||||
# """
|
||||
# Return True iff the two rectangles are equal (have the same corners)
|
||||
# :param rect1:
|
||||
# :param rect2:
|
||||
# :return:
|
||||
# """
|
||||
# pass
|
||||
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# Functions to deal with rectangles -- dict representation
|
||||
# -> There should be no print or input statements in this section
|
||||
# -> Each function should do one thing only
|
||||
# -> Functions communicate using input parameters and their return values
|
||||
#
|
||||
# Dumitrana Mihnea dictionary repres.
|
||||
def create_rect(x1, y1, x2, y2):
|
||||
if x1 == x2 or y1 == y2:
|
||||
return None
|
||||
|
||||
rect = {}
|
||||
rect['low_left'] = (min(x1, x2), min(y1, y2))
|
||||
rect['up_right'] = (max(x1, x2), max(y1, y2))
|
||||
return rect
|
||||
|
||||
|
||||
def get_x1(rect):
|
||||
return rect["low_left"][0]
|
||||
|
||||
|
||||
def get_y1(rect):
|
||||
return rect["low_left"][1]
|
||||
|
||||
|
||||
def get_x2(rect):
|
||||
return rect["up_right"][0]
|
||||
|
||||
|
||||
def get_y2(rect):
|
||||
return rect["up_right"][1]
|
||||
|
||||
|
||||
def rect_equal(rect1, rect2: dict):
|
||||
return get_x1(rect1) == get_x1(rect2) and get_x2(rect1) == get_x2(rect2) and get_y1(rect1) == get_y1(
|
||||
rect2) and get_y2(rect1) == get_y2(rect2)
|
||||
|
||||
|
||||
def to_str(rect):
|
||||
return "{} {} | {} {}".format(get_x1(rect), get_y1(rect), get_x2(rect), get_y2(rect))
|
||||
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# Functions that deal with the required functionalities properties
|
||||
# -> There should be no print or input statements in this section
|
||||
# -> Each function should do one thing only
|
||||
# -> Functions communicate using input parameters and their return values
|
||||
#
|
||||
def gen_rectangles(count: int):
|
||||
result = []
|
||||
while count > 0:
|
||||
x1 = random.randint(-20, 20)
|
||||
y1 = random.randint(-20, 20)
|
||||
x2 = x1 + random.randint(1, 10)
|
||||
y2 = y1 + random.randint(1, 10)
|
||||
|
||||
new_rect = create_rect(x1, y1, x2, y2)
|
||||
rect_ok_flag = True
|
||||
for rect in result:
|
||||
if rect_equal(new_rect, rect):
|
||||
rect_ok_flag = False
|
||||
break
|
||||
if rect_ok_flag:
|
||||
result.append(new_rect)
|
||||
count -= 1
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# rects = gen_rectangles(5)
|
||||
# print(rects)
|
||||
|
||||
def add_rectangle(rectangles: list, new_rect):
|
||||
"""
|
||||
:param rectangles: list
|
||||
:param x1,x2,y1,y2: int
|
||||
:return:
|
||||
None if rectangle already exists or is invalid
|
||||
updated list if rectangle is ok
|
||||
"""
|
||||
# newRectangle = [x1, y1, x2, y2]
|
||||
# newRectangle = create_rect()
|
||||
# if x1 > x2 or y1 > y2:
|
||||
# return None
|
||||
for rectangle in rectangles:
|
||||
if rect_equal(rectangle, new_rect):
|
||||
return None
|
||||
|
||||
rectangles.append(new_rect)
|
||||
return rectangles
|
||||
|
||||
|
||||
#
|
||||
# Write below this comment
|
||||
# UI section
|
||||
# Write all functions that have input or print statements here
|
||||
# Ideally, this section should not contain any calculations relevant to program functionalities
|
||||
#
|
||||
|
||||
def show_all_rect(rect_list: list):
|
||||
print("Rectangle list:")
|
||||
for rect in rect_list:
|
||||
print(to_str(rect))
|
||||
|
||||
|
||||
# Chisleac Remus
|
||||
def read_rect_ui():
|
||||
x1 = int(input("x1="))
|
||||
y1 = int(input("y1="))
|
||||
x2 = int(input("x2="))
|
||||
y2 = int(input("y2="))
|
||||
|
||||
return create_rect(x1, y1, x2, y2)
|
||||
|
||||
|
||||
def add_rectangle_ui(rectangles: list):
|
||||
new_rect = read_rect_ui()
|
||||
if new_rect is None:
|
||||
print("Invalid rectangle. Cannot be added")
|
||||
return
|
||||
|
||||
if add_rectangle(rectangles, new_rect) is None:
|
||||
print("Overlapping rectangles. Rectangle was not added.")
|
||||
|
||||
|
||||
def start_menu():
|
||||
rectangles = gen_rectangles(1)
|
||||
while True:
|
||||
show_all_rect(rectangles)
|
||||
print(rectangles)
|
||||
print("1. Add a rectangle:\n",
|
||||
"2. Delete a rectangle:\n",
|
||||
"3. Show all rectangles:\n",
|
||||
"4. Show rectangles that intersect a given one:\n",
|
||||
"5. exit\n")
|
||||
opt = input('>')
|
||||
if opt == "1":
|
||||
add_rectangle_ui(rectangles)
|
||||
elif opt == "2":
|
||||
pass
|
||||
elif opt == "3":
|
||||
pass
|
||||
elif opt == "4":
|
||||
pass
|
||||
elif opt == "5":
|
||||
return
|
||||
else:
|
||||
print("Choose a valid option")
|
||||
# Dumitrescu David
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
start_menu()
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
1. Modular programming
|
||||
2. Test-driven development
|
||||
3. Exceptions
|
||||
4. command-driven UI
|
||||
|
||||
Tic Tac Toe - human vs. computer
|
||||
play 5 (5th square on the board)
|
||||
play top middle
|
||||
play 1,1
|
||||
play B2
|
||||
"""
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Board module
|
||||
"""
|
||||
|
||||
|
||||
def create_board():
|
||||
"""
|
||||
Create the game board
|
||||
:return: the board
|
||||
"""
|
||||
board = []
|
||||
for i in [0, 1, 2]:
|
||||
board.append([' ', ' ', ' '])
|
||||
return board
|
||||
|
||||
|
||||
def get_symbol(game_board, row, col):
|
||||
# TODO Missing exceptions
|
||||
symbol = game_board[row][col]
|
||||
return symbol if symbol is not ' ' else None
|
||||
|
||||
|
||||
def move_on_board(game_board, symbol, row, col):
|
||||
"""
|
||||
Play a move on the board
|
||||
:param game_board: The game board
|
||||
:param symbol: one of 'X' or 'O'
|
||||
:param row: one of 0,1,2
|
||||
:param col: one of 0,1,2
|
||||
:return: None
|
||||
Raise ValueError if (row,col) outside board, symbol not one of (X,O) and
|
||||
if square already taken
|
||||
"""
|
||||
if row not in [0, 1, 2] or col not in [0, 1, 2]:
|
||||
raise ValueError("Move outside the board")
|
||||
if symbol not in ['X', 'O']:
|
||||
raise ValueError("Invalid symbol")
|
||||
if get_symbol(game_board, row, col) is not None:
|
||||
raise ValueError("Square already taken")
|
||||
game_board[row][col] = symbol
|
||||
|
||||
|
||||
#
|
||||
# def test_move_on_board():
|
||||
# b = create_board()
|
||||
# # Test empty board
|
||||
# for row in [0, 1, 2]:
|
||||
# for col in [0, 1, 2]:
|
||||
# assert get_symbol(b, row, col) is None
|
||||
#
|
||||
# # check for placing moves on the board
|
||||
# move_on_board(b, 'X', 1, 1)
|
||||
# assert get_symbol(b, 1, 1) == 'X'
|
||||
# move_on_board(b, 'X', 0, 0)
|
||||
# assert get_symbol(b, 0, 0) == 'X'
|
||||
# move_on_board(b, 'O', 2, 2)
|
||||
# assert get_symbol(b, 2, 2) == 'O'
|
||||
#
|
||||
# # check error handling
|
||||
# try:
|
||||
# move_on_board(b, 'X', 1, 1)
|
||||
# assert False
|
||||
# except ValueError:
|
||||
# assert True
|
||||
|
||||
|
||||
def str_board(game_board):
|
||||
result = ""
|
||||
gb = game_board
|
||||
|
||||
for row in [0, 1]:
|
||||
result += gb[row][0] + " | " + gb[row][1] + " | " + gb[row][2] + "\n"
|
||||
result += '--+---+--\n'
|
||||
result += gb[2][0] + " | " + gb[2][1] + " | " + gb[2][2] + "\n"
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
b = create_board()
|
||||
move_on_board(b, 'X', 1, 1)
|
||||
move_on_board(b, 'O', 0, 0)
|
||||
move_on_board(b, 'X', 2, 2)
|
||||
print(str_board(b))
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
Human and computer moves
|
||||
"""
|
||||
import board
|
||||
from random import choice
|
||||
|
||||
|
||||
def human_move(game_board, row, col):
|
||||
board.move_on_board(game_board, 'X', row, col)
|
||||
|
||||
|
||||
def computer_move(game_board):
|
||||
positions = []
|
||||
for row in [0, 1, 2]:
|
||||
for col in [0, 1, 2]:
|
||||
if board.get_symbol(game_board, row, col) is None:
|
||||
positions.append((row, col))
|
||||
pos = choice(positions)
|
||||
board.move_on_board(game_board, 'O', pos[0], pos[1])
|
||||
return pos
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
UI module for game
|
||||
|
||||
user commands:
|
||||
play 1,1 # plays in the center of the board
|
||||
takeback # undo the user's last move
|
||||
exit
|
||||
"""
|
||||
import board
|
||||
import game
|
||||
|
||||
|
||||
def help_user():
|
||||
print(
|
||||
"""
|
||||
play 1, 1 # plays in the center of the board
|
||||
takeback # undo the user's last move
|
||||
exit
|
||||
""")
|
||||
|
||||
|
||||
def process_user_command(user_command):
|
||||
"""
|
||||
Return user's command and its parameters
|
||||
:param user_command:
|
||||
:return:
|
||||
"""
|
||||
user_command = user_command.strip()
|
||||
tokens = user_command.split(" ", maxsplit=1)
|
||||
command = tokens[0]
|
||||
|
||||
if len(tokens) == 1:
|
||||
return command, ""
|
||||
|
||||
tokens = tokens[1].split(",")
|
||||
for i in range(len(tokens)):
|
||||
tokens[i] = tokens[i].strip()
|
||||
|
||||
return command, tokens
|
||||
|
||||
|
||||
def start():
|
||||
game_board = board.create_board()
|
||||
help_user()
|
||||
|
||||
while True:
|
||||
print(board.str_board(game_board))
|
||||
user_command = input(">")
|
||||
command, params = process_user_command(user_command)
|
||||
|
||||
if command == 'play':
|
||||
try:
|
||||
row = int(params[0])
|
||||
col = int(params[1])
|
||||
game.human_move(game_board, row, col)
|
||||
except ValueError as ve:
|
||||
print(str(ve))
|
||||
else:
|
||||
pos = game.computer_move(game_board)
|
||||
print("Computer moved at " + str(pos))
|
||||
elif command == 'takeback':
|
||||
pass
|
||||
elif command == 'exit':
|
||||
return
|
||||
else:
|
||||
print("Valid commands are:")
|
||||
help_user()
|
||||
|
||||
|
||||
# print(process_user_command("play 1,1"))
|
||||
# print(process_user_command(" play 1, 1 "))
|
||||
|
||||
start()
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
"""
|
||||
Board module
|
||||
"""
|
||||
|
||||
|
||||
# This class is a subclass of Python's Exception class
|
||||
# That allows us to raise and catch it
|
||||
class BoardFullException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class GameWonException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Board():
|
||||
"""
|
||||
this is Java
|
||||
public Board() { ... }
|
||||
Python's self is kind of like C++'s this
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Create the game board
|
||||
:return: the board
|
||||
"""
|
||||
# board is a local var in the __init__ method
|
||||
# board = []
|
||||
# self.board is an attribute of the Board class -> will be visible
|
||||
# across all Board methods
|
||||
|
||||
"""
|
||||
How do we protect self.board from being changed outside the class?
|
||||
|
||||
C++/JAVA/C#
|
||||
private -> field/methods accessible only from within the class
|
||||
public -> field/methods accessible from everywhere
|
||||
protected -> field/methods accessible from within the class and derived classes
|
||||
(and in the same package in Java)
|
||||
|
||||
Python -> private, etc keywords aren't used
|
||||
<name> -> public (e.g., self.board)
|
||||
_<name> -> private (by convention) (e.g., self._board)
|
||||
__<name> -> private (using name mangling) (e.g., self.__board)
|
||||
"""
|
||||
self.__board = []
|
||||
self.__free_squares = 9
|
||||
for i in [0, 1, 2]:
|
||||
self.__board.append([' ', ' ', ' '])
|
||||
|
||||
def get_symbol(self, row, col):
|
||||
# TODO Missing exceptions
|
||||
symbol = self.__board[row][col]
|
||||
if symbol != ' ':
|
||||
return symbol
|
||||
return None
|
||||
# return symbol if symbol =! ' ' else None
|
||||
|
||||
def move(self, symbol, row, col):
|
||||
"""
|
||||
Play a move on the board
|
||||
:param game_board: The game board
|
||||
:param symbol: one of 'X' or 'O'
|
||||
:param row: one of 0,1,2
|
||||
:param col: one of 0,1,2
|
||||
:return: None
|
||||
Raise ValueError if (row,col) outside board, symbol not one of (X,O) and
|
||||
if square already taken
|
||||
|
||||
Raise BoardFullException if the board is full but it's not won
|
||||
Raise GameWonException if the game was won
|
||||
"""
|
||||
if row not in [0, 1, 2] or col not in [0, 1, 2]:
|
||||
raise ValueError("Move outside the board")
|
||||
if symbol not in ['X', 'O']:
|
||||
raise ValueError("Invalid symbol")
|
||||
if self.get_symbol(row, col) is not None:
|
||||
raise ValueError("Square already taken")
|
||||
self.__board[row][col] = symbol
|
||||
self.__free_squares -= 1
|
||||
|
||||
if self._is_won():
|
||||
raise GameWonException()
|
||||
if self._is_full():
|
||||
raise BoardFullException()
|
||||
|
||||
def _is_won(self):
|
||||
gb = self.__board
|
||||
# checking for wins on rows
|
||||
for row in [0, 1, 2]:
|
||||
if gb[row][0] == ' ':
|
||||
# jump to the next iteration in the innermost loop
|
||||
continue
|
||||
if gb[row][0] == gb[row][1] == gb[row][2]:
|
||||
return True
|
||||
# checking for wins on columns
|
||||
for col in [0, 1, 2]:
|
||||
if gb[0][col] == ' ':
|
||||
# jump to the next iteration in the innermost loop
|
||||
continue
|
||||
if gb[0][col] == gb[1][col] == gb[2][col]:
|
||||
return True
|
||||
# checking on diagonals
|
||||
if gb[1][1] == ' ':
|
||||
return False
|
||||
if gb[0][0] == gb[1][1] == gb[2][2]:
|
||||
return True
|
||||
if gb[2][0] == gb[1][1] == gb[0][2]:
|
||||
return True
|
||||
|
||||
def _is_full(self):
|
||||
return self.__free_squares == 0
|
||||
|
||||
def __str__(self):
|
||||
result = ""
|
||||
gb = self.__board
|
||||
|
||||
for row in [0, 1]:
|
||||
result += gb[row][0] + " | " + gb[row][1] + " | " + gb[row][2] + "\n"
|
||||
result += '--+---+--\n'
|
||||
result += gb[2][0] + " | " + gb[2][1] + " | " + gb[2][2] + "\n"
|
||||
return result
|
||||
|
||||
|
||||
#
|
||||
# def test_move_on_board():
|
||||
# game_board = Board()
|
||||
# # Test empty board
|
||||
# for row in [0, 1, 2]:
|
||||
# for col in [0, 1, 2]:
|
||||
# assert game_board.get_symbol(row, col) is None
|
||||
#
|
||||
# # check for placing moves on the board
|
||||
# game_board.move('X', 1, 1)
|
||||
# assert game_board.get_symbol(1, 1) == 'X'
|
||||
# game_board.move('X', 0, 0)
|
||||
# assert game_board.get_symbol(0, 0) == 'X'
|
||||
# game_board.move('O', 2, 2)
|
||||
# assert game_board.get_symbol(2, 2) == 'O'
|
||||
#
|
||||
# # check error handling
|
||||
# try:
|
||||
# game_board.move('X', 1, 1)
|
||||
# assert False
|
||||
# except ValueError:
|
||||
# assert True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Board.__init__ is called here implicitly
|
||||
# __init__ must return a reference to the new object --> handled by the
|
||||
# Python runtime
|
||||
b = Board()
|
||||
b.move('X', 1, 1)
|
||||
b.move('O', 2, 2)
|
||||
|
||||
# we can use this but it's not very Pythonic :)
|
||||
# print(b.str_board())
|
||||
# print(str(b))
|
||||
# print(b)
|
||||
|
||||
# each Board object (e.g., b1, b2) has its own independent copy of
|
||||
# self.__board
|
||||
b1 = Board()
|
||||
b2 = Board()
|
||||
b1.move('O', 1, 1)
|
||||
b2.move('X', 1, 1)
|
||||
|
||||
print(b1)
|
||||
print(b2)
|
||||
|
||||
# b plays the role of self implicitly
|
||||
# print(b.get_symbol(1, 1))
|
||||
# in this version of the call, b plays the role of self explicitly
|
||||
# print(Board.get_symbol(b, 1, 1))
|
||||
|
||||
# print(b.__board)
|
||||
# b.__board[1][1] = 'X'
|
||||
# print(b.__board)
|
||||
# Python is called a dict programming language :)
|
||||
# print(b.__dict__)
|
||||
|
||||
# print(type(b))
|
||||
# print(type([]))
|
||||
# print(type(str_board))
|
||||
|
||||
# b = create_board()
|
||||
# move_on_board(b, 'X', 1, 1)
|
||||
# move_on_board(b, 'O', 0, 0)
|
||||
# move_on_board(b, 'X', 2, 2)
|
||||
# print(str_board(b))
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
"""
|
||||
Human and computer moves
|
||||
"""
|
||||
from board import Board
|
||||
from random import choice
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self):
|
||||
# private field of a Game class instance
|
||||
self.__board = Board()
|
||||
|
||||
def get_board(self):
|
||||
return self.__board
|
||||
|
||||
def human_move(self, row, col):
|
||||
self.__board.move('X', row, col)
|
||||
|
||||
def computer_move(self):
|
||||
positions = []
|
||||
for row in [0, 1, 2]:
|
||||
for col in [0, 1, 2]:
|
||||
if self.__board.get_symbol(row, col) is None:
|
||||
positions.append((row, col))
|
||||
pos = choice(positions)
|
||||
self.__board.move('O', pos[0], pos[1])
|
||||
return pos
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
UI module for game
|
||||
|
||||
user commands:
|
||||
play 1,1 # plays in the center of the board
|
||||
takeback # undo the user's last move
|
||||
exit
|
||||
"""
|
||||
from board import Board, GameWonException, BoardFullException
|
||||
from game import Game
|
||||
|
||||
|
||||
class UI:
|
||||
|
||||
def help_user(self):
|
||||
print(
|
||||
"""
|
||||
play 1, 1 # plays in the center of the board
|
||||
takeback # undo the user's last move
|
||||
exit
|
||||
""")
|
||||
|
||||
def _process_user_command(self, user_command):
|
||||
"""
|
||||
Return user's command and its parameters
|
||||
:param user_command:
|
||||
:return:
|
||||
"""
|
||||
user_command = user_command.strip()
|
||||
tokens = user_command.split(" ", maxsplit=1)
|
||||
command = tokens[0]
|
||||
|
||||
if len(tokens) == 1:
|
||||
return command, ""
|
||||
|
||||
tokens = tokens[1].split(",")
|
||||
for i in range(len(tokens)):
|
||||
tokens[i] = tokens[i].strip()
|
||||
|
||||
return command, tokens
|
||||
|
||||
def start(self):
|
||||
game = Game()
|
||||
self.help_user()
|
||||
|
||||
while True:
|
||||
print(game.get_board())
|
||||
user_command = input(">")
|
||||
command, params = self._process_user_command(user_command)
|
||||
|
||||
if command == 'play':
|
||||
try:
|
||||
row = int(params[0])
|
||||
col = int(params[1])
|
||||
game.human_move(row, col)
|
||||
except ValueError as ve:
|
||||
print(str(ve))
|
||||
except BoardFullException:
|
||||
print("-= Game Over. It's a draw! =-")
|
||||
print(game.get_board())
|
||||
return
|
||||
except GameWonException:
|
||||
print("-= Congratulations! =-")
|
||||
print(game.get_board())
|
||||
return
|
||||
else:
|
||||
try:
|
||||
pos = game.computer_move()
|
||||
print("Computer moved at " + str(pos))
|
||||
except GameWonException:
|
||||
print("-= Comiserations! =-")
|
||||
print(game.get_board())
|
||||
return
|
||||
elif command == 'takeback':
|
||||
pass
|
||||
elif command == 'exit':
|
||||
return
|
||||
else:
|
||||
print("Valid commands are:")
|
||||
self.help_user()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
ui = UI()
|
||||
ui.start()
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Create an application for a car rental business using a console based user interface.
|
||||
The application must allow keeping records of the company’s list of clients, existing car pool and rental history.
|
||||
The application must allow its users to manage clients, cars and rentals in the following ways:
|
||||
Clients
|
||||
Add a new client. Each client is a physical person having a unique ID (driver license series), name, age.
|
||||
Update the data for any client.
|
||||
Remove a client from active clients. Note that removing a client must not remove existing car rental statistics.
|
||||
Search for clients based on ID and name [both at the same time]
|
||||
All client operations must undergo proper validation!
|
||||
Cars
|
||||
Add a new car to the car pool. Each car must have a valid license plate number, a make and model taken from a
|
||||
list of makes and models. In addition, each car will have a color.
|
||||
Remove a car from the car pool.
|
||||
Search for cars based on license number, make, model and color.
|
||||
[make = VW, model = Polo, CJ01ABC], [make = VW, model = Polo, CJ01XYZ]
|
||||
All car operations must undergo proper validation!
|
||||
Rentals
|
||||
An existing client can rent one or several cars from the car pool for a determined period. When rented, a car
|
||||
becomes unavailable for further renting.
|
||||
When a car is returned, it becomes available for renting once again.
|
||||
Search the rental history of a given client, car, or all rentals during any given period.
|
||||
Statistics
|
||||
The list of all cars in the car pool sorted by number of days they were rented.
|
||||
The list of clients sorted descending by the number of cars they have rented.
|
||||
|
||||
The application must have support for unlimited undo/redo with cascading.
|
||||
"""
|
||||
|
||||
"""
|
||||
1. Write a Car class so that:
|
||||
- it has fields for license plate, make, model and color (all are str)
|
||||
- class has properties for all fields
|
||||
- all properties are set in the class constructor (__init__)
|
||||
- property for license plates is read-only
|
||||
- remaining properties are read/write
|
||||
- override __str__ so cars are displayed nicely on console :)
|
||||
|
||||
2. Write a function that generates n cars (n - input parameter)
|
||||
- make sure license plates are Ro, make, model color are real
|
||||
(e.g., -> CJ 01 ABC, VW Polo, red)
|
||||
- have lists of counties, makes, models, colors and randomly pick :)
|
||||
|
||||
3. Write a CarRepo class:
|
||||
- keeps a list or dict of cars (protected field -> _data)
|
||||
- methods to add a car, delete a car, get a car by license plate, get all cars
|
||||
- override __len__ to return how many cars are in the repo
|
||||
- define a RepoException class that inherits from Exception
|
||||
- RepoException is in the same module as CarRepo
|
||||
- raise RepoException when:
|
||||
-> trying to add a car with existing license plates in repo
|
||||
-> calling get on a car that's not in the repo
|
||||
- NB! -> make the CarRepo iterable -> __iter__, __next__ ?
|
||||
|
||||
4. Write CarRepoTextFile class:
|
||||
- CarRepoTextFile is derived from CarRepo class
|
||||
- it adds a _load_file and a _save_file method
|
||||
- cars are kept in a CSV file (CSV - comma separated values)
|
||||
- cars are loaded from file when the constructor is called
|
||||
- cars are saved after every operation
|
||||
"""
|
||||
|
||||
# _load_file
|
||||
# r - read, t - text
|
||||
# fin = open(file_name, "rt")
|
||||
# read all the lines in the file into a list
|
||||
# each car should be represented on its own line
|
||||
# list_of_lines = fin.readlines()
|
||||
# split lines by "," -> add them to repo
|
||||
|
||||
# _save_file
|
||||
# fout = open(file_name, "wt")
|
||||
# turn each Car object into a one-line string -> CJ 01 ABC, Toyota, Yaris, red
|
||||
# in a for loop :)
|
||||
# fout.write(car_as_string)
|
||||
# fout.close()
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
class Car:
|
||||
def __init__(self, license_plate: str, make: str, model: str, color: str):
|
||||
# self.__license_plate is a private field
|
||||
self.__license_plate = license_plate
|
||||
# self.make is a property setter
|
||||
self.make = make
|
||||
self.model = model
|
||||
self.color = color
|
||||
|
||||
@property
|
||||
def license(self):
|
||||
return self.__license_plate
|
||||
|
||||
@property
|
||||
def make(self):
|
||||
return self.__make
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
return self.__model
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
return self.__color
|
||||
|
||||
@make.setter
|
||||
def make(self, new_make: str):
|
||||
self.__make = new_make
|
||||
|
||||
@model.setter
|
||||
def model(self, new_model: str):
|
||||
self.__model = new_model
|
||||
|
||||
@color.setter
|
||||
def color(self, new_color: str):
|
||||
self.__color = new_color
|
||||
|
||||
# repr is the str representation of the object
|
||||
# called by data structures when str()-ing them
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
display_str = f"{self.__license_plate}, {self.make} {self.model}," \
|
||||
f" {self.color}"
|
||||
|
||||
return display_str
|
||||
|
||||
if __name__ == "__main__":
|
||||
car = Car("CJ 01 ABC", "Audi", "A1", "blue")
|
||||
print(car)
|
||||
print(car.license)
|
||||
car.make = "Toyota"
|
||||
print(car)
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
from random import randint
|
||||
|
||||
from seminar.group_912.seminar_10.car import Car
|
||||
|
||||
|
||||
# This class inherits from Exception
|
||||
class RepoException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CarRepoIterator():
|
||||
def __init__(self, car_repo):
|
||||
self._repo = car_repo
|
||||
# start iteration at the beginning of the list
|
||||
self._index = -1
|
||||
|
||||
def __next__(self):
|
||||
if self._index == len(self._repo) - 1:
|
||||
raise StopIteration()
|
||||
|
||||
self._index += 1
|
||||
return self._repo._cars[self._index]
|
||||
|
||||
|
||||
class CarRepo:
|
||||
def __init__(self):
|
||||
self._cars = []
|
||||
|
||||
def add_car(self, new_car: Car):
|
||||
for car in self._cars:
|
||||
if new_car.license == car.license:
|
||||
raise RepoException("Duplicate license plates")
|
||||
self._cars.append(new_car)
|
||||
|
||||
def get_all_cars(self):
|
||||
return self._cars
|
||||
|
||||
def get_car_by_license_plate(self, license_plate: str):
|
||||
for car in self._cars:
|
||||
if car.get_license_plate() == license_plate:
|
||||
return car
|
||||
raise RepoException("Car not found!")
|
||||
|
||||
def remove_car(self, license_plate: str):
|
||||
car = self.get_car_by_license_plate(license_plate)
|
||||
self._cars.remove(car)
|
||||
|
||||
def update_car(self, license_plate: str, model: str, color: str, make: str):
|
||||
car = self.get_car_by_license_plate(license_plate)
|
||||
car.set_model(model)
|
||||
car.set_color(color)
|
||||
car.set_make(make)
|
||||
|
||||
def __iter__(self):
|
||||
return CarRepoIterator(self)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._cars)
|
||||
|
||||
def __str__(self) -> str:
|
||||
string = ""
|
||||
for car in self._cars:
|
||||
string += str(car) + "\n"
|
||||
return string
|
||||
|
||||
|
||||
def gen_cars(n):
|
||||
car_list = []
|
||||
|
||||
counties = ["CJ", "HD", "MM", 'SV', "TM"]
|
||||
make_model = [["VW", "Golf", "Polo", "Passat"], ["BMW", "E36", "M5 CS", "1 Series"],
|
||||
["Renault", "Laguna", "Megane", "Clio"], ["Mercedes", "C63 AMG", "GLE 550", "E220"]]
|
||||
colors = ["Red", "Green", "Grey", "Black", "Magenta", "Blue", "Light Pink"]
|
||||
|
||||
for i in range(n):
|
||||
car_nr = randint(0, 3)
|
||||
# 65 is the ASCII code for A
|
||||
plate = f"{counties[randint(0, 4)]} {randint(0, 9)}{randint(1, 9)} {chr(randint(65, 90))}{chr(randint(65, 90))}{chr(randint(65, 90))}"
|
||||
make = make_model[car_nr][0]
|
||||
model = make_model[car_nr][randint(1, 3)]
|
||||
color = colors[randint(0, 6)]
|
||||
car = Car(plate, make, model, color)
|
||||
car_list.append(car)
|
||||
|
||||
return car_list
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
cars = gen_cars(10)
|
||||
repo = CarRepo()
|
||||
for car in cars:
|
||||
repo.add_car(car)
|
||||
|
||||
# print(len(repo))
|
||||
# print(repo)
|
||||
for car in repo:
|
||||
print(car)
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
VS 65 HNV,Dacia,Logan,blue
|
||||
TL 51 QQP,Dacia,Lodgy,red
|
||||
IS 97 EJG,Toyota,Yaris,red
|
||||
TR 91 KTU,Dacia,Sandero,blue
|
||||
TL 89 SSI,Dacia,Sandero,black
|
||||
TL 23 LTR,Dacia,Sandero,green
|
||||
TR 24 UVD,Toyota,RAV-4,red
|
||||
CJ 92 TRD,Dacia,Logan,green
|
||||
CJ 36 ZIA,Dacia,Logan,red
|
||||
IS 66 DJX,Dacia,Sandero,blue
|
||||
CJ 51 HCN,Dacia,Lodgy,red
|
||||
VS 23 GWQ,Toyota,Yaris,red
|
||||
IS 59 WCC,Toyota,Corolla,red
|
||||
CJ 92 YDU,Toyota,Yaris,green
|
||||
AB 19 FIN,Dacia,Lodgy,blue
|
||||
TL 70 GYH,Toyota,Corolla,red
|
||||
TR 64 QBZ,Dacia,Logan,blue
|
||||
TL 79 NEB,Dacia,Logan,black
|
||||
GL 42 RKZ,Dacia,Lodgy,green
|
||||
CJ 56 KNZ,Toyota,RAV-4,green
|
||||
GR 29 DUQ,Toyota,RAV-4,green
|
||||
AB 65 OJD,Dacia,Sandero,red
|
||||
TL 53 KYY,Dacia,Lodgy,green
|
||||
GL 58 ETL,Dacia,Logan,black
|
||||
IS 73 YZI,Dacia,Lodgy,black
|
||||
IS 61 VWR,Dacia,Sandero,green
|
||||
CJ 57 FTB,Toyota,Corolla,black
|
||||
B 36 VEF,Toyota,Yaris,blue
|
||||
GR 17 ERN,Toyota,RAV-4,green
|
||||
AB 40 ZXT,Dacia,Sandero,green
|
||||
GR 95 USA,Toyota,Yaris,green
|
||||
SJ 39 JTW,Dacia,Logan,blue
|
||||
AB 93 WVL,Dacia,Sandero,black
|
||||
TL 62 DZS,Dacia,Lodgy,green
|
||||
IS 23 CUK,Dacia,Logan,blue
|
||||
TR 99 JYB,Toyota,Corolla,green
|
||||
TL 97 TRC,Toyota,Yaris,green
|
||||
AB 19 SMK,Dacia,Logan,green
|
||||
B 54 FNU,Dacia,Sandero,black
|
||||
IS 27 WBE,Toyota,Yaris,green
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
class car:
|
||||
"""
|
||||
Add a new car to the car pool. Each car must have
|
||||
-> a valid license plate number,
|
||||
-> a make and model taken from a list of makes and models.
|
||||
-> each car will have a color.
|
||||
"""
|
||||
|
||||
def __init__(self, car_id: str, make: str, model: str, color: str):
|
||||
self.__car_id = car_id
|
||||
self.__make = make
|
||||
self.__model = model
|
||||
self.__color = color
|
||||
|
||||
@property
|
||||
def car_id(self):
|
||||
return self.__car_id
|
||||
|
||||
@car_id.setter
|
||||
def car_id(self, new_value):
|
||||
self.__car_id = new_value
|
||||
|
||||
@property
|
||||
def make(self):
|
||||
return self.__make
|
||||
|
||||
@property
|
||||
def model(self):
|
||||
return self.__model
|
||||
|
||||
@property
|
||||
def color(self):
|
||||
return self.__color
|
||||
|
||||
@color.setter
|
||||
def color(self, new_value):
|
||||
self.__color = new_value
|
||||
|
||||
def __str__(self):
|
||||
return self.car_id + " -> " + self.make + " " + self.model + ", " + self.color
|
||||
|
||||
|
||||
def test_car():
|
||||
new_car = car("CJ 01 ABC", "Dacia", "Sandero", "red")
|
||||
# assert new_car.get_id() == "CJ 01 ABC"
|
||||
assert new_car.car_id == "CJ 01 ABC"
|
||||
assert new_car.make == "Dacia"
|
||||
assert new_car.model == "Sandero"
|
||||
assert new_car.color == "red"
|
||||
assert str(new_car) == "CJ 01 ABC -> Dacia Sandero, red"
|
||||
|
||||
# repaint it
|
||||
# new_car.set_color("blue")
|
||||
new_car.color = "blue"
|
||||
assert new_car.color == "blue"
|
||||
assert str(new_car) == "CJ 01 ABC -> Dacia Sandero, blue"
|
||||
|
||||
# change license plates
|
||||
new_car.car_id = "CJ 99 XYZ"
|
||||
assert str(new_car) == "CJ 99 XYZ -> Dacia Sandero, blue"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_car()
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
from seminar.group_912.seminar_11.domain.exceptions import CarValidationException
|
||||
|
||||
|
||||
class CarValidatorRO:
|
||||
@staticmethod
|
||||
def _is_license_valid(license):
|
||||
# TODO Implement full validation
|
||||
"""
|
||||
Implement Romanian license plate validation
|
||||
@param license:
|
||||
@return: ...
|
||||
"""
|
||||
return len(license) > 2
|
||||
|
||||
# FIXME Duplicated code across validators, use inheritance to remove it
|
||||
def validate(self, car):
|
||||
errors = []
|
||||
# V1 - All properties are non-empty
|
||||
if not CarValidatorRO._is_license_valid(car.license_plate):
|
||||
errors.append('Invalid license plate')
|
||||
if len(car.make) < 2:
|
||||
errors.append('Car make should have at least 3 letters')
|
||||
if len(car.model) < 2:
|
||||
errors.append('Car model should have at least 3 letters')
|
||||
|
||||
if len(errors) > 0:
|
||||
raise CarValidationException(errors)
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
class Client(object):
|
||||
def __init__(self, client_id, cnp, name):
|
||||
self._client_id = client_id
|
||||
self._cnp = cnp
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._client_id
|
||||
|
||||
@property
|
||||
def cnp(self):
|
||||
return self._cnp
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
self._name = value
|
||||
|
||||
def __eq__(self, z):
|
||||
# don't compare apples to oranges
|
||||
if type(z) != Client:
|
||||
return False
|
||||
# just look at the id field
|
||||
return self.id == z.id
|
||||
|
||||
def __str__(self):
|
||||
return "Id=" + str(self.id) + ", Name=" + str(self.name)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
c1 = Client(1000, "290010203445566", "Popescu Ana")
|
||||
c2 = Client(1001, "290010203445566", "Popescu Ana")
|
||||
# print(c1 == c2)
|
||||
# print(repr(c1))
|
||||
print({1000: c1, 1001: c2})
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class ClientValidator:
|
||||
# TODO Implement
|
||||
|
||||
def validate(self, client):
|
||||
return True
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
class ValidatorException(Exception):
|
||||
def __init__(self, message_list="Validation error!"):
|
||||
self._message_list = message_list
|
||||
|
||||
@property
|
||||
def messages(self):
|
||||
return self._message_list
|
||||
|
||||
def __str__(self):
|
||||
result = ""
|
||||
for message in self.messages:
|
||||
result += message
|
||||
result += "\n"
|
||||
return result
|
||||
|
||||
|
||||
class CarException(Exception):
|
||||
def __init__(self, msg):
|
||||
self._msg = msg
|
||||
|
||||
def __str__(self):
|
||||
return self._msg
|
||||
|
||||
|
||||
class CarValidationException(CarException):
|
||||
def __init__(self, error_list):
|
||||
self._errors = error_list
|
||||
|
||||
def __str__(self):
|
||||
result = ''
|
||||
|
||||
for er in self._errors:
|
||||
result += er
|
||||
result += '\n'
|
||||
return result
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
from datetime import date, timedelta
|
||||
|
||||
|
||||
class Rental:
|
||||
def __init__(self, rental_id: int, start: date, end: date, client, car):
|
||||
self._rentalId = rental_id
|
||||
self._client = client
|
||||
self._car = car
|
||||
self._start = start
|
||||
self._end = end
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._rentalId
|
||||
|
||||
@property
|
||||
def client(self):
|
||||
return self._client
|
||||
|
||||
@client.setter
|
||||
def client(self, client):
|
||||
self._client = client
|
||||
|
||||
@property
|
||||
def car(self):
|
||||
return self._car
|
||||
|
||||
@car.setter
|
||||
def car(self, car):
|
||||
self._car = car
|
||||
|
||||
@property
|
||||
def start(self):
|
||||
return self._start
|
||||
|
||||
@start.setter
|
||||
def start(self, start):
|
||||
self._start = start
|
||||
|
||||
@property
|
||||
def end(self):
|
||||
return self._end
|
||||
|
||||
@end.setter
|
||||
def end(self, end):
|
||||
self._end = end
|
||||
|
||||
# len(rental)
|
||||
def __len__(self):
|
||||
if self._end is not None:
|
||||
return (self._end - self._start).days + 1
|
||||
today = date.today()
|
||||
return (today - self._start).days + 1
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
def __str__(self):
|
||||
return "Rental: " + str(self.id) + "\nCar: " + str(self.car) + "\nClient: " + str(
|
||||
self.client) + "\nPeriod: " + self._start.strftime("%Y-%m-%d") + " to " + self._end.strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
d1 = date(2020, 12, 15)
|
||||
d2 = date(2022, 12, 15)
|
||||
|
||||
td = timedelta(days=3)
|
||||
|
||||
print(type(d2 + td))
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
from datetime import date
|
||||
|
||||
from seminar.group_912.seminar_11.domain.exceptions import ValidatorException
|
||||
from seminar.group_912.seminar_11.domain.rental import Rental
|
||||
|
||||
|
||||
class RentalValidator:
|
||||
def validate(self, rental):
|
||||
if isinstance(rental, Rental) is False:
|
||||
raise TypeError("Not a Rental")
|
||||
|
||||
_errorList = []
|
||||
now = date(2000, 1, 1)
|
||||
if rental.start < now:
|
||||
_errorList.append("Rental starts in past;")
|
||||
if len(rental) < 1:
|
||||
_errorList.append("Rental must be at least 1 day;")
|
||||
if len(_errorList) > 0:
|
||||
raise ValidatorException(_errorList)
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
from seminar.group_912.seminar_11.domain.car import car
|
||||
from random import choice, randint
|
||||
import pickle
|
||||
|
||||
|
||||
# RepoException inherits from Python's builtin Exception class
|
||||
# RepoException "IS AN" exception
|
||||
class RepoException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class car_repo(object):
|
||||
def __init__(self):
|
||||
# keys are car license numbers, values are car objects
|
||||
self._data = {}
|
||||
|
||||
def add(self, new_car: car):
|
||||
if new_car.car_id in self._data:
|
||||
raise RepoException("Car already in repo")
|
||||
self._data[new_car.car_id] = new_car
|
||||
|
||||
def get(self, car_id: str):
|
||||
# If car cannot be found in repo, catch the dict's KeyError and
|
||||
# re-raise it as RepoException
|
||||
try:
|
||||
return self._data[car_id]
|
||||
except KeyError:
|
||||
raise RepoException("Car is not in repo")
|
||||
|
||||
def get_all(self):
|
||||
return list(self._data.values())
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
|
||||
class car_repo_bin_file(car_repo):
|
||||
def __init__(self, file_name="cars.bin"):
|
||||
# call superclass constructor
|
||||
super().__init__()
|
||||
# remember the name of the file we're working with
|
||||
self._file_name = file_name
|
||||
# load the cars from the file
|
||||
self._load_file()
|
||||
|
||||
def add(self, new_car: car):
|
||||
# call the add() method on the super class
|
||||
# we want to do everything the superclass add() already does
|
||||
super().add(new_car)
|
||||
# we also want to save all cars to a text file
|
||||
self._save_file()
|
||||
|
||||
def _load_file(self):
|
||||
# r - read, b - binary
|
||||
obj = []
|
||||
try:
|
||||
fin = open(self._file_name, "rb")
|
||||
obj = pickle.load(fin)
|
||||
fin.close()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
for c in obj:
|
||||
super().add(c)
|
||||
|
||||
def _save_file(self):
|
||||
# w - write mode (overwrite), b - binary mode
|
||||
fout = open(self._file_name, "wb")
|
||||
pickle.dump(self.get_all(), fout)
|
||||
# NOTE Don't forget to close the file!
|
||||
fout.close()
|
||||
|
||||
|
||||
# just a plain old regular class :)
|
||||
class car_repo_text_file(car_repo):
|
||||
# this class inherits from car_repo
|
||||
# => has all the mathods and attributes in car_repo
|
||||
|
||||
def __init__(self, file_name="cars.txt"):
|
||||
# call superclass constructor
|
||||
super().__init__()
|
||||
# remember the name of the file we're working with
|
||||
self._file_name = file_name
|
||||
# load the cars from the file
|
||||
self._load_file()
|
||||
|
||||
def _load_file(self):
|
||||
"""
|
||||
Load the cars from a text file
|
||||
"""
|
||||
# open a text file for reading
|
||||
# t - text file mode, r - reading
|
||||
lines = []
|
||||
|
||||
try:
|
||||
fin = open(self._file_name, "rt")
|
||||
# each car should be on its own line
|
||||
lines = fin.readlines()
|
||||
# close the file when done reading
|
||||
fin.close()
|
||||
except IOError:
|
||||
# It's ok if we don't find the input file
|
||||
pass
|
||||
|
||||
for line in lines:
|
||||
current_line = line.split(",")
|
||||
new_car = car(current_line[0].strip(), current_line[1].strip(), current_line[2].strip(),
|
||||
current_line[3].strip())
|
||||
# NOTE call super() so that we don't write the file we're reading from
|
||||
super().add(new_car)
|
||||
|
||||
def _save_file(self):
|
||||
"""
|
||||
Save all cars to a text file
|
||||
"""
|
||||
# open a text file for writing
|
||||
# t - text file mode, w - writing (rewrite the file every time)
|
||||
fout = open(self._file_name, "wt")
|
||||
|
||||
# writes car_string into the text file
|
||||
# fout.write(car_string)
|
||||
for car in self.get_all():
|
||||
car_string = str(car.car_id) + "," + str(car.make) + "," + str(car.model) + "," + str(car.color) + "\n"
|
||||
fout.write(car_string)
|
||||
|
||||
# call close when done writing
|
||||
fout.close()
|
||||
|
||||
def add(self, new_car: car):
|
||||
# call the add() method on the super class
|
||||
# we want to do everything the superclass add() already does
|
||||
super().add(new_car)
|
||||
# we also want to save all cars to a text file
|
||||
self._save_file()
|
||||
|
||||
|
||||
#
|
||||
# def test_car_repo():
|
||||
# repo = car_repo()
|
||||
# # car repository is empty
|
||||
# assert len(repo) == 0
|
||||
#
|
||||
# # add cars to the repo
|
||||
# c1 = car("CJ 01 ABC", "Dacia", "Sandero", "red")
|
||||
# repo.add(c1)
|
||||
# c2 = car("CJ 01 XYZ", "Dacia", "Logdy", "white")
|
||||
# repo.add(c2)
|
||||
# assert len(repo) == 2
|
||||
#
|
||||
# # try to add the same car again
|
||||
# try:
|
||||
# repo.add(c1)
|
||||
# assert False
|
||||
# except RepoException:
|
||||
# assert True
|
||||
#
|
||||
# # retrieve cars from repo
|
||||
# assert repo.get("CJ 01 ABC") == c1
|
||||
#
|
||||
# # TODO Try to implement repo["CJ 01 XYZ"] == c2
|
||||
# assert repo.get("CJ 01 XYZ") == c2
|
||||
#
|
||||
# # try to retrieve a non-existing car
|
||||
# try:
|
||||
# repo.get("SJ 04 RTY")
|
||||
# assert False
|
||||
# except RepoException:
|
||||
# assert True
|
||||
|
||||
|
||||
def generate_cars(n: int):
|
||||
"""
|
||||
Generates n car instances
|
||||
:return: A list of n cars
|
||||
"""
|
||||
counties = ["AB", "SJ", "VS", "CJ", "B", "TL", "TR", "GL", "GR", "IS"]
|
||||
make_model = {"Dacia": ["Logan", "Sandero", "Lodgy"], "Toyota": ["Corolla", "RAV-4", "Yaris"]}
|
||||
colors = ["red", "blue", "green", "black"]
|
||||
|
||||
result = []
|
||||
while n > 0:
|
||||
letters = ""
|
||||
for i in [0, 1, 2]:
|
||||
letters += chr(randint(65, 90)) # A -> Z
|
||||
car_id = choice(counties) + " " + str(randint(10, 99)) + " " + letters
|
||||
make = choice(list(make_model.keys()))
|
||||
model = choice(make_model[make])
|
||||
color = choice(colors)
|
||||
result.append(car(car_id, make, model, color))
|
||||
n -= 1
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# repo = car_repo()
|
||||
# repo_text = car_repo_text_file()
|
||||
# # NOTE Save the generated cars to the file
|
||||
# for c in generate_cars(10):
|
||||
# print(str(c))
|
||||
# # repo.add(c)
|
||||
# repo_text.add(c)
|
||||
|
||||
# read the cars.bin input file
|
||||
car_repo_bin = car_repo_bin_file()
|
||||
car_list = generate_cars(20)
|
||||
for c in car_list:
|
||||
car_repo_bin.add(c)
|
||||
|
||||
print("Cars saved in cars.bin")
|
||||
for c in car_repo_bin.get_all():
|
||||
print(str(c))
|
||||
|
||||
# read the cars.txt file
|
||||
# car_repo_text = car_repo_text_file()
|
||||
# print("\n\nCars saved in cars.txt")
|
||||
# for c in car_repo_text.get_all():
|
||||
# print(str(c))
|
||||
|
||||
# NOTE Load the cars and display them again
|
||||
# new_car_repo = car_repo_text_file()
|
||||
# for c in new_car_repo.get_all():
|
||||
# print(str(c))
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
from seminar.group_912.seminar_11.domain.client import Client
|
||||
from seminar.group_912.seminar_11.repository.repository_exception import RepositoryException
|
||||
|
||||
|
||||
class ClientRepo:
|
||||
# TODO Finish implementation
|
||||
def __init__(self):
|
||||
self._clients = {}
|
||||
|
||||
def add(self, client):
|
||||
if client.id in self._clients.keys():
|
||||
raise RepositoryException("Duplicate Client id")
|
||||
self._clients[client.id] = client
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import datetime
|
||||
|
||||
from seminar.group_912.seminar_11.repository.repository_exception import RepositoryException
|
||||
|
||||
|
||||
class RentalRepository:
|
||||
# TODO Finish implementation
|
||||
def __init__(self):
|
||||
self._data = {}
|
||||
|
||||
def add(self, rental):
|
||||
if rental.id in self._data.keys():
|
||||
raise RepositoryException("Duplicate Rental ID")
|
||||
self._data[rental.id] = rental
|
||||
|
||||
def remove(self, rental_id):
|
||||
if rental_id in self._data.keys():
|
||||
del self._data[rental_id]
|
||||
else:
|
||||
raise RepositoryException("Rental was not found")
|
||||
|
||||
def get_all(self):
|
||||
return list(self._data.values())
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user