Lab8
Poeng:
Endelig er den her: semesterets siste lab! Denne lab’en er basert på poeng, og hver oppgave gir litt poeng. Det er maksimalt mulig å oppnå 25 poeng; så selv om du løser oppgaver som summerer til enda mer, får du likevel ikke bare 25 poeng på laben.
I hovedoppgaven i denne lab’en skal du generere dine egne QR-koder! Selv om stegene bygger på hverandre, kan de faktisk løses helt isolert fra hverandre, og de scores isolert fra hverandre på CodeGrade.
- Alle poenggivende oppgaver rettes automatisk. Hvis du ikke består de automatiske testene, får du ikke poeng.
- Vi vil trekke poeng manuelt dersom vi oppdager at oppgaven åpenbart ikke er løst, eller man forsøker å trikse seg gjennom de automatiske testene.
- Hver lab kan leveres så mange ganger du vil; det er siste innlevering som teller. Du kan umiddelbart se resultatene på de automatiske testene og hvor mange poeng du har fått etter hver innlevering.
Hver lab utgjør 2.5% av den endelige karakteren din i emnet. Du må få til sammen 100 poeng eller mer på labene for å kunne ta eksamen.
Det er veldig viktig at du siterer alle kilder og eventuelle samarbeid. Se mitt.uib for mer informasjon om vår policy på plagiat og samarbeid.
Det er lurt å levere inn mange ganger underveis. Du får umiddelbart en automatisk tilbakemelding på oppgavene, og du kan prøve igjen hvis noe ikke virker.
Lister vs oppslagsverk
Denne oppgaven består av to deler. Skriv funksjoner til begge deloppgaver i én felles fil, list_vs_dictionary.py.
I denne oppgaven skal vi undersøke slektskapet mellom oppslagsverk og lister. En nøkkel spiller på mange måter samme rolle for et oppslagsverk som en indeks gjør for en liste.
Del A
Skriv funksjonen key_value_getter(d)
som tar inn en dictionary d
, og skriver ut til skjermen nøklene, verdiene og nøkkel/verdi-par.
Eksempelkjøring:
key_value_getter({
"monday": 0,
"tuesday": 0.7,
"wednesday": 0,
"thursday": 4.7,
"friday": 10
})
skal gi utskriften:
Dictionary keys:
monday
tuesday
wednesday
thursday
friday
Dictionary values:
0
0.7
0
4.7
10
Dictionary keys/value:
monday 0
tuesday 0.7
wednesday 0
thursday 4.7
friday 10
Del B
Skriv funksjonen index_value_getter(a)
som tar inn en liste a
og skriver ut til skjermen indeksene, verdiene og indeks/verdi -parene.
Eksempelkjøring:
index_value_getter([7.0, 8.0, 10.0, 9.0, 10.0])
skal gi utskriften:
List indices:
0
1
2
3
4
List values:
7.0
8.0
10.0
9.0
10.0
List indices/value:
0 7.0
1 8.0
2 10.0
3 9.0
4 10.0
PS: Det skal ikke komme noen utskrift i terminalen dersom noen importerer filen din som en modul. Hvis du vil teste funksjonene dine lokalt på egen maskin kan du legge til dine egne kall til funksjonene nederst i filen din under if __name__ == "__main__":
.
Collatz-sekvensen
Collatz-sekvensen er definert som følger:
- Start med et tall \(n\)
- Hvis \(n\) er jevnt så er neste tall \(\frac{n}{2}\), ellers så er neste tall \(3n +1\)
- Repeter steg \(2\) med det nye tallet helt til du får \(1\). Da er du ferdig.
Her er en funksjon som beregner Collatz-sekvensen gitt en en startverdi \(n\):
def collatz_sequence(n):
sequence = [n]
while n > 1:
if n % 2 == 0:
n = n // 2
else:
n = 3 * n + 1
sequence.append(n)
return sequence
I filen collatz.py, skriv en funksjon som heter collect_collatz(a, b)
som bruker collatz_sequence
-funksjonen gitt over til å beregne Collatz-sekvensen for alle tall fra og med \(a\) og opp til (men ikke inkludert) \(b\). Sekvensene skal returneres i form av et oppslagsverk hvor startverdiene er nøkler.
Test koden din ved å legge til disse linjene nederst i filen:
def test_collect_collatz():
print('Tester collect_collatz... ', end='')
# Test 1
expected = {
1: [1],
2: [2, 1],
3: [3, 10, 5, 16, 8, 4, 2, 1],
}
actual = collect_collatz(1, 4)
assert expected == actual
# Test 2
expected = {
3: [3, 10, 5, 16, 8, 4, 2, 1],
4: [4, 2, 1],
5: [5, 16, 8, 4, 2, 1],
}
actual = collect_collatz(3, 6)
assert expected == actual
print('OK')
if __name__ == '__main__':
test_collect_collatz()
Begynn med å opprette et tomt oppslagsverk (som du skal returnere på slutten av funksjonen, når det er ferdig fylt opp med nøkler og verdier).
Bruk en løkke for å gå gjennom alle tall fra og med \(a\) opp til men ikke inkludert \(b\).
Inne i løkken: gjør et kall til
collatz_sequence
-metoden med iteranden som argument. Oppdater oppslagsverket slik at iteranden blir en ny nøkkel med returverdien fra kallet tilcollatz_sequence
som verdi.
Filter for høye temperaturer
I filen filter_high_temperatures.py, skriv en funksjon som heter filter_high_temperatures
med parametre:
path_input
, en filsti til en eksisterende fil hvor hver linje inneholder først en dag, deretter et mellomrom, og så en temperatur. For eksempel, temperatures.txt.path_output
, en filsti til en fil som skal opprettes, ogthreshold_temp
, et flyttall som representerer en temperatur.
La funksjonen åpne filen path_input
, gå gjennom linjene i filen og lager en ny fil path_output
hvor kun de linjene der temperaturen er minst threshold_temp
er inkludert. Om ingen dager har en temperatur som er minst threshold_temp
så skal path_output
være en tom fil.
For å teste programmet ditt, legg til nederst i filen:
def test_filter_high_temperatures():
print('Tester filter_high_temperatures... ', end='')
filter_high_temperatures('temperatures.txt', 'high_temps.txt', 23.5)
expected = (
'Monday 23.5\n'
'Wednesday 24.0\n'
'Thursday 23.9\n'
'Sunday 23.9\n'
)
with open('high_temps.txt', 'rt', encoding='utf-8') as file:
actual = file.read()
assert expected.strip() == actual.strip()
print('OK')
if __name__ == '__main__':
test_filter_high_temperatures()
Kolonnesum
I filen sum_of_column.py, skriv en funsjon sum_of_column(path, col)
som returnerer summen av verdiene fra den oppgitte kolonnen i csv-filen med filsti path
. Ikke inkluderer verdier i summen som ikke er flyttall; hvis det ikke finnes noen tallverdier i oppgitt kolonne, skal funksjonen returnere 0. Kolonne 0
er den første kolonnen fra venstre i csv-filen. Du kan anta at csv-filen benytter komma (,
) som skilletegn og dobbel rett apostrof ("
) som anførselstegn/grupperingssymbol («quotechar»).
Eksempelkjøringer med foo.csv, Statistikk_Tilsyn_ar.csv og airport-codes.csv
def test_sum_of_column():
print('Tester sum_of_column... ', end='')
assert(42.0 == sum_of_column('foo.csv', 0))
assert(95.0 == sum_of_column('foo.csv', 1))
assert(0.0 == sum_of_column('foo.csv', 2))
assert(76363.0 == sum_of_column('Statistikk_Tilsyn_ar.csv', 1))
assert(46007.0 == sum_of_column('Statistikk_Tilsyn_ar.csv', 2))
assert(5024518.0 == sum_of_column('airport-codes.csv', 3))
print('OK')
if __name__ == '__main__':
test_sum_of_column()
Lønnsberegning
RandomFirma AS trenger et program for å beregne hvor mye de skal betale sine timeansatte. Arbeidsmiljøloven krever at ansatte får lønn for 1,5 time for alle timer over 40 som de jobber i løpet av en enkelt uke. For eksempel, hvis en ansatt jobber 45 timer, får de 5 timer overtid, til 1,5 ganger grunnlønnen. Regjeringen har innført minstelønn i bransjen dette firmaet operer i, og minstlønnen er 200 kr per time. RandomFirma AS krever også at en ansatt ikke jobber mer enn 60 timer i en uke.
Her er regler oppsummert:
- En ansatt får betalt (arbeidstimer) × (grunnlønn), for hver time inntil 40 timer.
- For hver time over 40 får de overtid = (grunnlønn) × 1,5.
- Grunnlønnen må ikke være lavere enn minstelønnen (200 i timen).
- Antall timer kan ikke være større enn 60.
I filen salary.py skriv en funksjon weekly_pay(hourly_rate, hours)
som tar grunnlønn og antall timer en anstatt har jobbet som parametere, og returnerer enten totallønnen som en tallverdi, eller streng med en feilmelding. Feilmeldingene er 'Minstelønnskravet er ikke oppfylt'
eller 'En ansatt jobber mer enn 60 timer'
. Dersom begge reglene brytes, skal det returneres 'Minstelønnskravet er ikke oppfylt'
.
def test_weekly_pay():
print('Tester weekly_pay... ', end='')
assert 2_000 == weekly_pay(200, 10)
assert 40_000 == weekly_pay(1000, 40)
assert 20_000 == weekly_pay(500, 40)
assert 41_500 == weekly_pay(1000, 41)
assert 70_000 == weekly_pay(1000, 60)
assert 'En ansatt jobber mer enn 60 timer' == weekly_pay(1000, 61)
assert 'Minstelønnskravet er ikke oppfylt' == weekly_pay(199, 40)
assert 'Minstelønnskravet er ikke oppfylt' == weekly_pay(100, 100)
print('OK')
if __name__ == '__main__':
test_weekly_pay()
Prikkprodukt
I filen dot_product.py skriv funksjonen dot_product(a, b)
som regner ut prikkprodukuktet for to like lange lister med tall a
og b
. Prikkproduktet er definert som summen av produktene av verdiene som har lik posisjon i de to listene; altså (a[0] * b[0] + a[1] * b[1] + …)
def test_dot_product():
print('Tester dot_product... ', end='')
assert 36 == dot_product([1, 2, 3, 4], [4, 5, 6, 1])
assert 12 == dot_product([0, 6, 1], [400, 1, 6])
assert 651 == dot_product([43, 6], [15, 1])
print('OK')
if __name__ == '__main__':
test_dot_product()
Introduksjon
I denne oppgaven skal vi lære hvordan vi genererer en QR-kode! Når vi er helt ferdige skal programmet vårt fungere som følger:
- Input: en nettadresse, for eksempel strengen
'https://inf100.ii.uib.no'
- Resultat: en QR-kode vises på skjermen. Om vi scanner QR-koden med mobilen kommer vi til nettadressen oppgitt som input.
Før du vi begynner, anbefaler vi sterkt at du ser igjennom denne videon som forklarer hvordan QR-koder fungerer (det er greit om du ikke klarer å følge med på de aller mest tekniske detaljene om Reed-Solomon -enkoding):
Prosessen med å lage en QR-kode går igjennom følgende steg:
Selv om prosessen begynner med å konvertere en streng til en liste med bits (1’ere og 0’ere), vil oppgavene i denne lab’en være organisert «baklengs», slik at vi begynner med å skrive funksjonen for å tegne QR-koden. Grunnen til dette er rett og slett at du får prøve at koden din fungerer etter hvert ved å scanne QR-kodene du produserer!
Kom i gang
Last ned disse filene til arbeidsmappen din:
qr_dummies.py | Inneholder placeholder -varianter for funksjonene vi skal skrive, samt noen ekstra funksjoner som gir oss dummy-data for å hjelpe oss å sjekke underveis at vi er på riktig vei. Når vi er helt ferdige trenger vi ikke denne filen lengre. |
qrv2_layout.json | Informasjon om layouten for en QR-kode i versjon 2. Denne informasjonen benytter vi flere steder. Hvis vi er flinke til å skrive koden vår basert på verdiene vi finner her, kan vi senere bytte ut denne filen med f. eks. qrv3_layout.json og umiddelbart være i stand til å generere QR-koder i versjon 3. |
Opprett også en fil qr0_main.py og lim inn følgende kode:
import json
from pathlib import Path
from qr1_draw import display
# from qr3_masking import get_refined_matrix
from qr_dummies import get_refined_matrix
# from qr4_zigzag import bit_list_to_raw_matrix
from qr_dummies import bit_list_to_raw_matrix
# from qr5_bit_list import string_to_bit_list
from qr_dummies import string_to_bit_list
def get_qr_matrix(content_string):
qr_layout = json.loads(Path('qrv2_layout.json').read_text(encoding='utf-8'))
bit_list, err_corr = string_to_bit_list(content_string, qr_layout)
raw_matrix = bit_list_to_raw_matrix(bit_list, qr_layout)
matrix = get_refined_matrix(raw_matrix, err_corr, qr_layout)
return matrix
if __name__ == '__main__':
url = 'https://inf100.ii.uib.no'
qr_matrix = get_qr_matrix(url)
display(qr_matrix)
Æsj, programmet krasjer allerede. Kan det være fordi vi ikke er ferdige med qr1_draw.py enda? Gjennomfør neste steg og finn ut!
1: Tegning
I denne oppgaven skal vi skrive et program som tegner en QR-kode til skjermen, gitt at vi får en matrise (2D-liste) med hvilke felter som skal være svarte og hvilke som skal være hvite i et rutenett. I denne deloppgaven skal vi opprette filen qr1_draw.py.
draw_qr
I filen qr1_draw.py skriven funksjon draw_qr med fem parametre:
canvas
: et lerret QR-koden skal tegnes på;x_left
ogy_top
: koordinatene til hjørnet øverst til venstre;size
: bredden og høyden til QR-koden som skal tegnes (tegningen skal være like høy som den er bred); ogqr
: en kvadratisk 2D-dimensjonell liste som inneholder 1’ere og 0’ere. En 1’er betyr at ruten skal være svart, mens en 0’er betyr at ruten skal være hvit.
For å teste programmet ditt kan du legge til disse linjene nederst i filen qr1_draw.py. Når du kjører programmet, skal bildet under vises.
def display(matrix):
from uib_inf100_graphics.simple import canvas, display as dsp
canvas.create_rectangle(0, 0, 400, 400, fill='white', outline='')
draw_qr(canvas, 25, 25, 350, matrix)
dsp(canvas)
if __name__ == '__main__':
sample_grid = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1],
[0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0],
[0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
display(sample_grid)
Ta utgangspunkt i kode du har skrevet tidligere for Rutenett (lab4) eller Snake (lab5) og gjør noen små tilpasninger slik at funksjonen din for å tegne et rutenett passer med de nye parametrene.
Husk: både bredde og høyde er lik
size
.Husk: høyresidens x-koordinat er lik venstresidens x-koordinat pluss bredde. Tilsvarende er bunnen lik topp pluss høyde.
Sjekk deg selv
Gå tilbake til qr0_main.py fra introduksjonssteget og kjør den nå. Om du har gjort alt riktig, skal det vises en QR-kode. Scan QR-koden med mobilen og se hvor du kommer… forhåpentligvis fant du en spennende nettside. Dessverre er det ikke riktig nettside, siden vi ikke ender opp på hovedsiden til https://inf100.ii.uib.no/ slik vi forventer ut i fra å lese koden i qr0_main.py. Det viser seg nemlig at get_refined_matrix
-funksjonen som ligger i qr_dummies.py ikke bryr seg om argumene den får, men alltid gir samme matrise tilbake uansett. Dette skal vi rette opp i løpet av de neste to stegene i oppgaven.
2: Faste mønstre og meta-felter
Det finnes mange ulike typer QR-koder. Den mest fremtredende forskjellen er kanskje dimensjonen til QR-koden, som avgjør hvor mange piksler det er i den. Jo større QR-kode, jo mer informasjon (f. eks. lengre nettadresser) kan den inneholde. I denne oppgaven skal vi for enkelhets skyld begrense oss til å kun jobbe med versjon 2 (QRv2), noe som innebærer at rutenettet vi skal lage er en matrise (2D-liste) med størrelse 25x25.
Matrisen for en QRv2 -kode er delt inn i følgende hovedkomponenter:
- En fast komponent som er lik for alle QRv2 -koder (markert i blått/lyseblått under). Disse feltene hjelper en QR-scanner å finne i hvilken del av bildet QR-koden befinner seg og hvordan den er vinklet og orientert. For oss som ikke er i businessen av å scanne QR-koder men istedet bare produsere dem, er ikke dette noe vi graver oss videre ned i utover at vi selvfølgelig må følge reglene og alltid lage koder som følger dette faste mønsteret.
- En datakomponent som inneholder selve data’en som er lagret (i svart/hvitt under). Dette datafeltet inneholder også underkomponenter for feilretting (error correction), men det kommer vi tilbake til senere.
- En komponent for metadata som sier noe om hvordan datafeltet er «maskert» og hvilket feilrettingsnivå koden benytter seg av i datafeltet (i rødt/rosa under).
I filen qrv2_layout.json (som du allerede har lastet ned til arbeidsmappen din, se introduksjon) finner du en datastruktur som forteller oss hvilke posisjoner i matrisen som hører til den faste komponenten og hvilke verdier de skal ha, samt hvilke posisjoner i matrisen som hører til metadata og hvilke verdier disse posisjonene kan ha ved ulike konfigurasjoner. Ta en kikk på filen den og prøv å forstå hvilken informasjon den inneholder.
En QRv2 -kode kan variere på følgende måter:
- Selve dataen som er lagret i QR-koden kan selvfølgelig variere. Ofte er dette en nettadresse, men det kan i prinsippet være hvilken som helst data. I denne oppgaven begrenser vi oss til å kun jobbe med strenger som data. Nettadresser er en form for streng, så vi vil kunne bruke programmet vårt til å lage QR-koder som fører oss til en nettside.
- Alle QR-koder har en viss kapasitet til å rette opp feil i avlesningen på egen hånd; men hvor stor andel av QR-koden som er feilaktig tolket før lesingen feiler kan variere. Du kan velge mellom fire nivåer av feilretting: L, M, Q og H. Disse ulike nivåene gjør at du kan hente ut korrekt informasjon selv om respektivt 7%, 15%, 25% eller 30% av matrisen blir feiltolket av programmet som leser bildet fra kameraet.
- Alle QR-koder har gått igjennom en prosess som heter «maskering», slik at bildet får en jevn fordeling av svarte og hvite piksler uavhengig av hvilken data bildet representerer. Prosessen innebærer å «flippe» enkelte ruter i matrisen etter et bestemt mønster. Det finnes 8 ulike mønstre man kan velge mellom, og ideelt sett velger vi mønsteret som gir den jevnest mulige fordeling av hvite og svarte ruter.
Når vi oppretter matrisen for QR-koden, gjøres det i fire hovedsteg:
Råversjon | Maskering | Metadata | Faste komponenter |
Vi skal nå fokusere på de to siste stegene i denne helheten, nemlig fra vi har en ferdig maskert matrise til vi får en komplett QR-kode. Vi begynner med det siste steget først: å lime inn de faste komponentene. Deretter lager vi en funksjon for å lime inn feltene for metadata.
Begge oppgavene på denne siden skal skrives i filen qr2_matrix_completion.py.
set_fixed_fields
Opprett funksjonen set_fixed_fields som har to parametere
matrix
en kvadratisk matrise (2D-liste), ogqr_layout
et oppslagsverk på samme format du ser i qrv2_layout.json.
Funksjonen skal (destruktivt) endre 2D-listen matrix
slik at de faste feltene får riktig verdi i henhold til qr_layout.
def test_set_fixed_fields():
print('Testing set_fixed_fields...', end='')
matrix = [
[1, 0, 1, 0, 1],
[0, 1, 0, 1, 0],
[1, 0, 1, 0, 1],
[0, 1, 0, 1, 0],
[1, 0, 1, 0, 1],
]
sample_layout = {
'about': 'A fake and incomplete QR layout for testing only',
'side_length': 5,
'fixed_positions': {
'zeros': [
[3, 2], [3, 3], [3, 4], [4, 2], [4, 3], [4, 4]
],
'ones': [
[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]
]
}
# skipping keys 'byte_capacity', 'meta_positions' and 'meta_patterns'
# since they are irrelevant here
}
set_fixed_fields(matrix, sample_layout)
assert matrix == [
[1, 1, 1, 0, 1],
[1, 1, 1, 1, 0],
[1, 0, 1, 0, 1],
[0, 1, 0, 0, 0],
[1, 0, 0, 0, 0],
]
print(' OK')
Benytt en løkke over punktene i qr_layout['fixed_positions']['zeros']
. Iteranden i en slik løkke blir [rad, kolonne] for et sted i matrisen som skal settes til 0. Tilsvarende for «ones».
set_meta_fields
Metadata-feltet varierer basert på to parametre:
- Feilrettingsnivå (L, M, Q eller H), og
- Maskenummer (angitt ved et tall mellom 0 og 7)
Opprett funksjonen set_meta_fields som har fire parametere:
matrix
en kvadratisk matrise (2D-liste),err_corr
en streng på ett tegn som indikerer feilrettingsnivå (enten L, M, Q eller H),mask_no
et tall 0-7 som angir hvilken maske som er benyttet, ogqr_layout
et oppslagsverk på samme format du ser i qrv2_layout.json.
Husk, vi skal ikke gjøre noe med feilrettingskoder eller maskering akkurat nå; vi kan anta at dette allerede er tatt hånd om. Vi skal bare gjøre informasjon om valgene som er gjort tilgjengelig i QR-kodens metafelter.
Funksjonen skal (destruktivt) endre 2D-listen matrix
slik at metafeltene får riktig verdi i henhold til qr_layout. Merk at for å vite hvilket mønster som skal brukes, må du slå opp i «meta_patterns» i qr_layout i henhold til parametrene err_corr og mask_no; og for å vite posisjonene hvor dette mønsteret skal plasseres, må du slå opp i «meta_positions». Studer også testen for et forenklet eksempel.
def test_set_meta_fields():
print('Testing set_meta_fields...', end='')
# For easier visualization the test uses a matrix of strings rather
# than 0's and 1's, but ultimately 1's and 0's should also work
matrix = [
['-', '|', '-', '|', '-'],
['|', '-', '|', '-', '|'],
['-', '|', '-', '|', '-'],
['|', '-', '|', '-', '|'],
['-', '|', '-', '|', '-'],
]
sample_layout = {
'about': 'A fake and incomplete QR layout for testing only',
'side_length': 5,
# skipping key 'fixed_positions' since it is irrelevant here
'meta_positions': {
'first': [
[0, 0], [0, 1], [0, 2]
],
'second': [
[0, 4], [4, 4], [3, 1]
]
},
'meta_patterns': {
'L': [
['A', 'B', 'C'], # mask_no = 0
['a', 'b', 'c'] # mask_no = 1
],
'Q': [
['Q', 'R', 'S'], # mask_no = 0
['q', 'r', 's'] # mask_no = 1
],
}
}
err_corr = 'L'
mask_no = 0
set_meta_fields(matrix, err_corr, mask_no, sample_layout)
assert matrix == [
['A', 'B', 'C', '|', 'A'],
['|', '-', '|', '-', '|'],
['-', '|', '-', '|', '-'],
['|', 'C', '|', '-', '|'],
['-', '|', '-', '|', 'B'],
]
err_corr = 'Q'
mask_no = 1
set_meta_fields(matrix, err_corr, mask_no, sample_layout)
assert matrix == [
['q', 'r', 's', '|', 'q'],
['|', '-', '|', '-', '|'],
['-', '|', '-', '|', '-'],
['|', 's', '|', '-', '|'],
['-', '|', '-', '|', 'r'],
]
print(' OK')
Begynn med å finne hvilket bit-mønster du skal bruke (qr_layout['meta_patterns'][err_corr][mask_no]
) og hvor det skal plasseres (qr_layout['meta_positions']['first'/'second']
). Legg merke til at disse listene er like lange, og derfor har de samme indeksene. Bruk en løkke over indeksene (for ... in range(len(...))
). For hver indeks i
, hent ut [rad, kolonne] fra plassering-listene og hent ut riktig verdi fra bit-mønster-listen; sett så riktig verdi i matrisen på disse posisjonene.
Sjekk deg selv
Legg til denne snutten nederst i qr2_matrix_completion.py. Om du har gjort alt rett skal du kunne besøke en nettside med en hyggelig melding når du scanner QR-koden som vises.
if __name__ == '__main__':
from qr_dummies import sample_masked_matrix
from qr1_draw import display
from pathlib import Path
import json
# Retrieve sample of premasked matrix without meta/fixed fields
# and getting the matching config for it.
matrix, error_correction_level, mask_no = sample_masked_matrix()
qr_layout = json.loads(Path('qrv2_layout.json').read_text(encoding='utf-8'))
# Try the functions we have created here
set_meta_fields(matrix, error_correction_level, mask_no, qr_layout)
set_fixed_fields(matrix, qr_layout)
# Draw the picture
display(matrix)
3: Maskering
I dette steget skal vi maskere en matrise. Hensikten med å maskere matrisen er å få en jevn fordeling av svarte og hvite ruter i QR-koden, slik at den blir lettere å lese for en QR-scanner. Dette innebærer å «flippe» noen av verdiene i en matrise i henhold til et bestemt mønster. QR -standarden spesifiserer 8 ulike masker man kan velge mellom. Gitt et radnummer row og et kolonnenummer col så skal en posisjon i matrisen flippes dersom en gitt betingelse er oppfylt:
Maske nr | Betingelse for flipping | Illustrasjon |
0 | (row + col) % 2 == 0 | |
1 | row % 2 == 0 | |
2 | col % 3 == 0 | |
3 | (row + col) % 3 == 0 | |
4 | (row//2 + col//3) % 2 == 0 | |
5 | (row*col) % 2 + (row*col) % 3 == 0 | |
6 | ((row*col) % 2 + (row*col) % 3) % 2 == 0 | |
7 | ((row+col) % 2 + (row*col) % 3) % 2 == 0 |
I illustrasjonene er de svarte rutene posisjoner hvor betingelsen er oppfylt, og rutene skal flippes.
Funksjonen beskrevet på denne siden skal skrives i filen qr3_masking.py.
should_flip
En funksjon should_flip som skal avgjøre hvorvidt en posisjon skal flippes eller ikke. La funksjonen ha parametre:
row
hvilken rad posisjonen er i,col
hvilken kolonne posisjonen er i, ogmask_no
hvilken maske som skal benyttes.
La funksjonen returnere True dersom verdien i cellen skal flippes i henhold til gitt maske, og False hvis ikke. Denne funksjonen kan du benytte som en hjelpefunksjon for get_masked_matrix.
get_masked_matrix
En ikke-destruktiv funksjon get_masked_matrix med parametre:
matrix
en matrise (2D-liste) hvor alle verdiene er 0 eller 1, ogmask_no
et nummer som indikerer hvilken maske som skal benyttes.
Funksjonen skal returnere en ny matrise (2D-liste) med samme dimensjoner som matrix
. Verdiene skal også være de samme som i matrix, bortsett fra at alle 1’ere er omgjort til 0’ere og alle 0’ere er omgjort til 1’ere i de posisjonene hvor verdien skal flippes i henhold til maskenummeret.
Legg merke til: det er oppgitt at funksjonen skal være «ikke-destruktiv». Det betyr at funksjonen ikke skal mutere
matrix
.
def test_get_masked_matrix():
print('Testing get_masked_matrix...', end='')
blank_5x5_matrix = [[0] * 5 for _ in range(5)]
actual = get_masked_matrix(blank_5x5_matrix, 0)
expected = [
[1, 0, 1, 0, 1],
[0, 1, 0, 1, 0],
[1, 0, 1, 0, 1],
[0, 1, 0, 1, 0],
[1, 0, 1, 0, 1],
]
assert expected == actual
assert blank_5x5_matrix == [[0] * 5 for _ in range(5)]
actual = get_masked_matrix(expected, 1)
expected = [
[0, 1, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 1, 0],
[0, 1, 0, 1, 0],
]
assert expected == actual
print(' OK')
score_matrix
En ikke-destruktiv funksjon score_matrix med én parameter:
matrix
, en matrise (2D-liste) med 1’ere og 0’ere.
Funksjonen skal returnere absolutt-forskjellen på antall enere og nullere i matrisen.
PS: Dette er en forenkling; hvis du er interessert i å skrive en ordentlig score-funksjon, kan du lese deg opp hos Thonky sin QR-tutorial. Men: benytt forenklingen vår når du leverer på CodeGrade, siden testene våre forventer det slik.
def test_score_matrix():
arg = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 0]
]
assert 5 == score_matrix(arg)
arg = [
[1, 1, 0],
[1, 1, 0],
[0, 0, 1]
]
assert 1 == score_matrix(arg)
get_refined_matrix
En ikke-destruktiv funksjon get_refined_matrix med tre parametere:
raw_matrix
, en matrise (2D-liste) med 1’ere og 0’ere som representerer en «rå» matrise med umaskert data;error_correction_level
, enten L, M, Q eller H som indikerer hvilket nivå av feilretting som ligger i den rå matrisen;qr_layout
et oppslagsverk på formatet gitt i qrv2_layout.json som angir detaljer om hvor de faste kompontentene og meta-komponentene i matrisen befinner seg og hvordan de skal fylles ut.
Funksjonen skal returnere en komplett ferdig matrise som er både maskert og fylt med riktige verdier i de faste komponentene og i meta-komponentene. Importer og benytt funksjoner fra tidligere steg som nødvendig. Funksjonen skal velge det maskenummeret som gir lavest score (og hvis det er to masker som gir samme score, skal den med lavest maskenummer blant dem velges). De faste komponentene og meta-feltene skal legges til matrisen før den scores.
I denne funksjonen skal du «binde sammen» alt du har gjort så langt.
Merk: Dersom du på forhånd vet hvilket maskenummer du skal bruke, kan du fullføre oppgaven ved å bruke (1) get_masked_matrix, (2) set_meta_positions og (3) set_fixed_positions. Tenk på disse tre stegene som å «bygge matrisen».
Det jobben get_refined_matrix skal gjøre, er å prøve ut alle muligheter for maskenummer (bruk en for-løkke). For hvert maskenummer, bygg matrisen og se hvordan den scorer (etter at matrisen er ferdig bygget). Ta vare på matrisen med best score etter hver iterasjon av løkken. Til slutt returnerer du den beste matrisen du fant etter at løkken er ferdig.
Sjekk deg selv
Gå tilbake til qr0_main.py fra introduksjonssteget og endre importene slik at get_refined_matrix
importeres fra qr3_masking og ikke fra qr_dummies. Kjør programmet og scan QR-koden som vises. Om du har gjort alt riktig, skal du kunne lese en hyggelig melding.
4: Sikksakk
Det neste steget er å skape en «råmatrise» fra en streng. Denne prosessen vil foregå i to hovedsteg:
- først konverteres strengen til en (1-dimensjonell) liste med «bits» (1’ere og 0’ere), og
- så plasseres den 1-dimensjonelle listen av bits som en slange gjennom en matrise.
Vi skal fokusere på det siste først.
Når vi skal legge listen med bits inn i slangen, følges det et spesielt mønster som slanger seg gjennom matrisen. Vi begynner nederst til høyre og legger bitene som en slange som går opp og ned gjennom matrisen mot venstre. Det som gjør det litt komplisert er at man går «to og to» kolonner om gangen i et sikksakk -mønster. Se illustrasjonen under.
Noen steder (i nærheten av de faste komponentene) ser det ut som mønster av og til avviker fra det vanlige; men det som er kult, er at vi faktisk kan følge det vanlige mønsteret hele tiden, bare at vi hopper over de posisjonene som tilhører faste komponententer og meta-komponentene.
get_next_pos
I filen qr4_zigzag.py skriv en funksjon get_next_pos med parametre:
row
ogcolumn
, heltall som beskriver en posisjon i en matrise (2D-liste), ogsize
et heltall som beskriver sidelengden i en kvadratisk matrise.
Funksjonen skal returnere en tuple (next_row, next_column) i henhold til slange-sikksakk-mønsteret illustrert over. Du skal ikke ta hensyn til at noen deler av matrisen er forbudt, men la mønsteret fylle hele matrisen.
Du kan anta at size én mer enn delelig på fire, altså at size % 4
er 1 (dette er sant for alle QR-størrelser).
Hvis man er i en partallskolonne, flytter man seg ett steg til venstre. Unntaket er hvis man er i kolonne 0, da flytter man seg et steg opp i stedet.
Hvis man er i en oddetallskolonne som er én mer enn delelig med fire, er man på vei nedover. Man flytter seg da én ned og én til høyre. Unntaket er hvis man allerede er nederst, da flytter man seg et steg til venstre.
Ellers er man i en oddetallskolonne som er tre større enn delelig med fire. Da er man på vei oppover. Man flytter seg da én opp og én til høyre. Unntaket er hvis man allerede er øverst, da flytter man til venstre.
def test_get_next_pos_basic():
print('Testing get_next_pos (basic)...', end='')
size = 25
# De første flyttene
assert (24, 23) == get_next_pos(24, 24, size)
assert (23, 24) == get_next_pos(24, 23, size)
assert (23, 23) == get_next_pos(23, 24, size)
...
# Når vi har sikksakket helt til topps
assert (0, 23) == get_next_pos(0, 24, size)
assert (0, 22) == get_next_pos(0, 23, size)
assert (0, 21) == get_next_pos(0, 22, size)
assert (1, 22) == get_next_pos(0, 21, size)
assert (1, 21) == get_next_pos(1, 22, size)
assert (2, 22) == get_next_pos(1, 21, size)
...
# Når vi har ned helt til bunns igjen
assert (24, 20) == get_next_pos(24, 21, size)
assert (24, 19) == get_next_pos(24, 20, size)
...
# Siste kolonne
assert (24, 0) == get_next_pos(24, 1, size)
assert (23, 0) == get_next_pos(24, 0, size)
assert (22, 0) == get_next_pos(23, 0, size)
print(' OK')
def test_get_next_pos_5x5():
print('Testing get_next_pos (5x5)...', end='')
size = 5
expected = [
[24, 11, 10, 9, 8],
[23, 13, 12, 7, 6],
[22, 15, 14, 5, 4],
[21, 17, 16, 3, 2],
[20, 19, 18, 1, 0]
]
actual = [[-1] * size for _ in range(size)]
row, col = 4, 4
for i in range(size * size):
actual[row][col] = i
row, col = get_next_pos(row, col, size)
assert expected == actual
print(' OK')
def test_get_next_pos_9x9():
print('Testing get_next_pos (9x9)...', end='')
size = 9
expected = [
[80, 55, 54, 53, 52, 19, 18, 17, 16],
[79, 57, 56, 51, 50, 21, 20, 15, 14],
[78, 59, 58, 49, 48, 23, 22, 13, 12],
[77, 61, 60, 47, 46, 25, 24, 11, 10],
[76, 63, 62, 45, 44, 27, 26, 9, 8],
[75, 65, 64, 43, 42, 29, 28, 7, 6],
[74, 67, 66, 41, 40, 31, 30, 5, 4],
[73, 69, 68, 39, 38, 33, 32, 3, 2],
[72, 71, 70, 37, 36, 35, 34, 1, 0]
]
actual = [[-1] * size for _ in range(size)]
row, col = 8, 8
for i in range(size * size):
actual[row][col] = i
row, col = get_next_pos(row, col, size)
assert expected == actual
print(' OK')
bit_list_to_raw_matrix
I filen qr4_zigzag.py skriv en funksjon bit_list_to_raw_matrix med parametre:
bit_list
en liste med bits (1’ere og 0’ere) som skal limes inn i en matriseqr_layout
et oppslagsverk med formatet du finner i qrv2_layout.json.
Funksjonen skal opprette en kvadratisk matrise som i utgangspunktet består av kun 0’ere (størrelsen på matrisen som opprettes er angitt i qr_layout). Du skal deretter legge inn elementene fra bit_list: begynn i hjørnet nederst til høyre og benytt get_next_pos -funksjonen for å finne neste posisjon. Hopp over alle posisjoner som er tilhører en fast komponent eller en meta-komponent (informasjon om hvilke posisjoner dette er finnes i qr_layout).
- Hvis det ikke er plass til alle elementene i bit_list, bør programmet krasje.
- Hvis det er plass til overs etter at bitene fra bit_list er plassert, bare la det være 0 i de siste posisjonene.
def test_bit_list_to_raw_matrix():
print('Testing bit_list_to_raw_matrix...', end='')
# To make the test easier to read, bit_list contain distinct elements here
# (in actual applications, bit_list would only have 0's and 1's)
arg_bit_list = list(range(1, 72))
arg_qr_layout = {
'about': 'A fake and incomplete QR layout for testing only',
'side_length': 9,
'fixed_positions': {
'ones': [
[1, 3], [1, 4],
],
'zeros': [
[2, 3], [2, 4],
]
},
'meta_positions': {
'first': [
[5, 2], [5, 3]
],
'second': [
[6, 2], [6, 3]
]
}
# key 'meta_patterns' skipped, since it is irrelevant for this task
}
expected = [
[ 0, 50, 49, 48, 47, 20, 19, 18, 17],
[ 0, 52, 51, 0, 0, 22, 21, 16, 15],
[71, 54, 53, 0, 0, 24, 23, 14, 13],
[70, 56, 55, 46, 45, 26, 25, 12, 11],
[69, 58, 57, 44, 43, 28, 27, 10, 9],
[68, 59, 0, 0, 42, 30, 29, 8, 7],
[67, 60, 0, 0, 41, 32, 31, 6, 5],
[66, 62, 61, 40, 39, 34, 33, 4, 3],
[65, 64, 63, 38, 37, 36, 35, 2, 1]
]
actual = bit_list_to_raw_matrix(arg_bit_list, arg_qr_layout)
assert expected == actual
print(' OK')
Det kan være lurt å samle alle de ulovlige posisjonene i én datastruktur (aller mest effektivt med tanke på kjøretid er det å bruke en mengde av tupler (rad, kolonne) til dette, men en vanlig liste med [rad, kolonne] -elementer fungerer også helt fint med så små matriser vi jobber med nå).
Sjekk deg selv
Gå tilbake til qr0_main.py fra introduksjonssteget og endre importene slik at bit_list_to_raw_matrix
importeres fra qr4_zigzag og ikke fra qr_dummies. Kjør programmet og scan QR-koden som vises. Om du har gjort alt riktig, skal du kunne lese en hyggelig melding.
5: Feilretting
I denne siste delen skal vi se nærmere på hvordan vi skal representere en streng som en liste av bits (1’ere og 0’ere). Du husker kanskje fra kursnotatene om unicode at en streng kan representeres som en sekvens av enere og nullere. Det skal vi ser nærmere på her.
Listen med bits i en QR-kode er delt inn i seks deler:
- Modus er fire bits som forteller hvordan resten av bit-listen skal tolkes. For våre formål skal denne verdien alltid være 0100, som betyr at data’en er lagret som «binær» data; de fleste QR-scannere tolker dette i praksis som at dataen representerer en streng i latin-1 (eller ASCII) enkoding (andre mulige verdier som vi altså ikke skal bruke er: 0001 numerisk modus, 0010 alfanumerisk modus, 1000 japansk kanji, og 0111 ECI-modus).
- Lengde er et tall (i totallsystemet) som indikerer hvor mange bytes dataen består av. Åtte bits utgjør én byte, så vi kan gange tallet med 8 for å finne ut for mange bits som befinner seg i data-seksjonen.
- Data er selve dataen. For våre formål et dette en ASCII-enkodet streng.
- Terminator er fire nuller som følger etter dataen (disse brukes egentlig ikke til noen ting i binærmodus, men må være til stede for å fylle opp plassen).
- Padding er noen faste mønstre som gjentar seg. Hensikten er å bruke opp resten av plassen før feilrettingen begynner. Hvor mye padding det er kommer an på hvor mye data det er og hvor mye plass feilrettingen tar (paddingen kan også være ingenting).
- Feilretting er spesielle bits som gjør det mulig å gjenopprette den originale dataen selv om noen bits av en eller annen grunn har blitt flippet. Denne delen regnes ut på bakgrunn av de foregående delene av bit-listen.
Funksjonene på denne siden skal opprettes i filen qr5_bit_list.py.
string_to_data
Opprett en funksjon string_to_data med én parameter:
content_string
en streng du kan anta består av ASCII -symboler.
Funksjonen skal returnere en liste av bits som representerer strengen i ASCII-enkoding.
def test_string_to_data():
print('Testing string_to_data...', end='')
assert [0,1,0,0,0,0,0,1] == string_to_data('A')
assert [0,1,0,0,0,0,0,1,0,1,0,0,0,0,1,1] == string_to_data('AC')
foo = [0,1,1,0,0,1,1,0,0,1,1,0,1,1,1,1,0,1,1,0,1,1,1,1]
assert foo == string_to_data('foo')
print(' OK')
Denne koden konverterer ett symbol til sin ASCII-enkoding:
c = 'A'
bit_list = [int(x) for x in f'{ord(c):08b}']
Hva skjer her da?
ord
-funksjonen gir oss ordinalen (tallverdien) for symbolet.b
-en inne i krøllparantesene betyr at tallet skal skrives i totall-systemet i strengen som opprettes.08
i krøllparantesene betyr at tallet skal skrives med minst åtte siffer, og bruk ledende 0’er om nødvendig. F-strengen evaluerer altså til strengen ‘01000001’. For-løkken går gjennom alle symbolene x i strengen ogint
konverterer hvert symbol til et heltall. Det blir da 1 eller 0 alt etter som. Verdiene legges til i en liste ved bruk av listeinklusjon.
For å løse oppgaven, bruk en løkke og gjenta operasjonen over for hvert symbol c
i strengen. La bit-listen du finner utvide en resultat-liste du har opprettet før løkken begynner.
# la 'result' være er en tom liste
# for hvert symbol i content_string:
# finn bit-listen for symbolet
# utvid (extend) 'result' med bit-listen
# returner resultatet
get_core_bit_list
Opprett en funksjon get_core_bit_list med én parameter:
content_string
en streng du kan anta består av ASCII -symboler.
Funksjonen skal returnere de fire første delene av en bit-liste: modus, lengde, data og terminator for den gitte strengen
def test_get_core_bit_list():
print('Testing get_core_bit_list...', end='')
arg = 'hei'
expected_head = [0, 1, 0, 0]
expected_len = [0, 0, 0, 0, 0, 0, 1, 1] # 3 in binary
expected_data = [int(x) for x in '011010000110010101101001']
expected_term = [0, 0, 0, 0]
expected = expected_head + expected_len + expected_data + expected_term
actual = get_core_bit_list(arg)
assert expected == actual
print(' OK')
my_len = 42
len_bit_list = [int(x) for x in f'{my_len:08b}']
pad_bit_list
Opprett en funksjon pad_bit_list med to parameter:
core_bit_list
en liste med bitspad_to_bytes
et tall som sier hvor mange bytes den ferdig paddeded bit-listen skal inneholde
Funksjonen skal destruktivt legge til padding-mønstre på slutten av core_bit_list slik at den til sammen blir pad_to_bytes lang. Husk at én byte er åtte bit, så lengden på den endelige listen skal være 8 * pad_to_bytes
.
Paddingen som legges til skal veksle mellom [1, 1, 1, 0, 1, 1, 0, 0] og [0, 0, 0, 1, 0, 0, 0, 1] annenhver gang helt til listen er tilstrekkelig lang.
def test_pad_bit_list():
print('Testing pad_bit_list...', end='')
PAD1 = (1, 1, 1, 0, 1, 1, 0, 0)
PAD2 = (0, 0, 0, 1, 0, 0, 0, 1)
arg = [1, 1, 1, 1, 1, 1, 1, 1]
expected = arg + list(PAD1) + list(PAD2) + list(PAD1)
pad_bit_list(arg, 4)
assert expected == arg
arg = [1, 1, 1, 1, 1, 1, 1, 1]
expected = arg + list(PAD1) + list(PAD2) + list(PAD1) + list(PAD2)
pad_bit_list(arg, 5)
assert expected == arg
arg = [1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1]
expected = arg + list(PAD1) + list(PAD2) + list(PAD1) + list(PAD2)
pad_bit_list(arg, 6)
assert expected == arg
print(' OK')
string_to_bit_list
Opprett en funksjon string_to_bit_list med to parametere:
content_string
, en streng du kan anta består av ASCII -symboler, ogqr_layout
, et oppslagsverk på formatet du finner i qrv2_layout.json.
Funksjonen skal returnere to ting i en tuple:
- en komplett bit-liste, inkludert modus, lengde, data, terminator, padding, og feilretting, og
- en streng på ett tegn: ‘L’, ‘M’, ‘Q’ eller ‘H’ som indikerer hvilket feilrettingsnivå som brukes i bit-listen.
Før vi begynner:
En QRv2-kode har 25x25 piksler, og representerer dermed 625 bit med informasjon. Mye av dette brukes imidlertid til faste komponenter og meta-komponenter, så i praksis er det bare 352 bits (44 bytes) vi har til rådighet (nøkkelen ‘byte_capacity’ i qr_layout forteller oss dette). Denne plassen skal fordeles mellom feltene for modus, lengde, data, terminator, padding og feilrettingskode.
En QR-kode kan ha fire ulike nivåer av feilretting: L, M, Q eller H som gir bedre og bedre nivåer av feilretting. Disse nivåene krever henholdsvis 80, 128, 176 og 224 bits (eller 10, 16, 22 og 28 bytes om du vil). Jo bedre feilretting vi ønsker oss, jo mer plass krever altså feilrettings-komponenten. Konsekvensen av dette er at vi bare kan bruke et høyt nivå av feilretting dersom dataen vi lagrer er tilstrekkelig liten.
Når vi velger nivå av feilretting, gjør vi som følger:
- Vi finner først bit-listen for modus, lengde, data og terminator og ser hvor stor plass dette tar.
- Vi velger deretter nivå av feilretting L, M, Q eller H så høyt som mulig, men slik at det likevel er plass til begge deler (hvis det ikke er plass til laveste feilrettingsnivå, krasj programmet og gi en hensiktsmessig feilmelding).
- Hvis vi ser at det vil bli plass til overs, legger vi til padding før vi regner ut selve feilrettingsmønsteret.
Husk å holde tungen rett i munnen med tanke på bytes og bits. 1 byte = 8 bits.
Forresten, en funksjon for å regne ut selve feilrettingsmønsteret kan du laste ned her: qr6_error_correction.py (du må også installere den eksterne pakken reedsolo
. Prøv å skrive python3 -m pip install reedsolo
i terminalen eller se notatene om eksterne pakker for eksempler på hvordan man installerer eksterne pakker).
Vil du lære å regne ut feilrettingsmønsteret selv? Sjekk ut INF243.
Ferdigstilling
Gå tilbake til qr0_main.py fra introduksjonssteget og endre importene slik at string_to_bit_list
importeres fra qr5_bit_list og ikke fra qr_dummies. Kjør programmet og scan QR-koden som vises. Om du har gjort alt riktig, programmet nå fungerer. Yay!
Akvakulturregisteret
I denne oppgaven skal vi bruke datafilen Akvakulturregisteret.csv.
PS: akvakulturregisteret er dessverre ikke lagret i utf-8. Prøv deg frem til du finner riktig encoding (se notater om tekstkoding).
Del A (1 poeng)
I filen aquaculture_a.py skriv en funksjon count_facilities_by_species(path)
som tar som input en filsti til akvakulturregisteret, og som så skriver ut til terminalen en unicode-alfabetisk liste over antall oppdrettsanlegg for hver art. Med «unicode-alfabetisk» menes den rekkefølgen man får dersom man sorterer en liste med artsnavn med sorted
-funksjonen innebygget i Python. Du kan ta utgangspunkt i følgende kode:
def count_facilities_by_species(path):
... # din kode her
if __name__ == '__main__':
count_facilities_by_species('Akvakulturregisteret.csv')
Eksempel på utskrift når programmet kjøres:
Abbor: 8
Acartia tonsa **(oppdrett): 4
Akkar: 1
Amerikansk hummer: 2
Arctic sea ice amphipod *: 1
Arktisk knurrulke: 1
Berggylt: 58
...
Les innholdet i filen til en egnet datastruktur ved å benytte csv -biblioteket.
Bruk et oppslagsverk (dict) for å telle hver art; bruk en løkke over hver rad i filen.
Første gangen du ser en art, opprett en ny nøkkel med artsnavnet i oppslagsverket og gi den verdien 1
Dersom nøkkelen derimot var i oppslagsverket fra før, øk verdien dens med 1
For å finne artene i ‘alfabetisk’ rekkefølge, bruk
sorted
-funksjonen og gi listen med nøklene fra oppslagsverket som argument.
Del B (1 poeng, rettes manuelt)
I filen aquaculture_bc.py, skriv et program som plotter alle oppdrettsanleggene i akvakulturregisteret på et kart. Bruk et scatterplot fra matplotlib for å plotte punktene. Posisjonene til oppdrettsanleggene er gitt i kolonnene som kalles Ø_GEOWGS84
og N_GEOWGS84
i csv-filen. Det er fint om punktene tegnes med en viss gjennomsiktighet.
Når du kjører programmet skal omtrent følgende vises:
Del C (1 poeng, rettes manuelt)
I filen aquaculture_bc.py, endre programmet slik at punktene får ulik farge avhengig av om det er et oppdrettsanlegg i sjø eller på land.
Når du kjører programmet skal omtrent følgende vises:
Endre farge på prikk med piltaster
I denne oppgaven skal vi bruke uib_inf100_graphics.event_app
for å lage et program hvor brukeren kan forandre fargen på en prikk ved å trykke på piltastene. Denne oppgaven rettes manuelt (det er ingen automatiske tester på CodeGrade).
Les deg gjerne opp på farger i kursnotatene om grafikk før du setter i gang.
En farge er i RGB-systemet representert av tre tall som beskriver lysintensiteten til rødt, grønt og blått lys. Hvert tall er mellom 0 og 255. I programmet vi skal lage i denne oppgaven, skal du tegne en prikk midt på skjermen. Brukeren skal kunne trykke på tastaturet for å endre fargen på prikken
- trykker brukeren på pil opp, økes mengden rød,
- trykker brukeren på pil ned reduseres mengden rød,
- trykker brukeren på pil høyre økes mengden grønn,
- trykker brukeren på pil venstre reduserers mengden grønn,
- trykker brukeren på
a
økes mengden blå, - trykker brukeren på
z
reduseres mengden blå.
Ingen farge-verdier skal kunne være mindre enn 0 eller høyere enn 255.
Det ferdige programmet skal skrives i filen colorful_dot.py og skal se omtrent slik ut:
def rgb_to_hex(r, g, b):
return f'#{r:02x}{g:02x}{b:02x}'
# Example usage
hex_string = rgb_to_hex(255, 0, 128)
print(hex_string) # Output: #ff0080
La modellen (app) består av fire variabler: r, g, b (tallverdier) og message (en streng). Initialiser dem i app_started.
La redraw all tegne teksten og en runding midt på skjermen. La fargen være bestemt av r, g og b fra app.
La key_pressed håndtere tastetrykk som beskrevet i oppgaveteksten. I videoen over endres verdien med 10 for hvert tastetrykk.
Bonus:
- Ha ulike modus som bestemmer hvor store steg du tar hver gang. Endre modus ved å trykke på space.
- Ha ulike prikker, der du kan klikke på hvilken prikk du nå skal endre fargen til. Vis hvilken prikk som er valgt f. eks. ved å la den ha outline.