Kursnotater for tema som er nye i denne laben:

  • En lab er en samling med én eller flere oppgaver.
  • Hver lab kan gi opp til 25 poeng.
    • Oppvarmingsfeltet gir 1 poeng per oppgave, maksimalt 5 poeng.
    • Hovedfeltet gir 15 poeng dersom du består alle oppgavene, og ellers 0.
    • Ekstrafeltet gir 1 poeng per oppgave, maksimalt 5 poeng.
  • Alle oppgaver rettes automatisk. For å bestå en oppgave, må du bestå alle testene for oppgaven.
  • De automatiske testene for grafikk-oppgaver sjekker bare at programmet ditt faktisk kjører, og så laster den opp bildet du lager i galleriet – den sjekker ikke om du faktisk har løst riktig oppgave. Vi gjør derfor en rask manuell sjekk på de grafiske oppgavene.
  • 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 etter 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.

Innlevering av lab’er foregår via verktøyet Codegrade som du kommer til via mitt.uib.

  • Gå til «Oppgåver» i menyen til venstre
  • Klikk deg videre til CodeGrade ved å klikke på «Last lab i eit nytt vindauge» Innlevering
  • Last opp filene du har laget for lab’en og trykk på «Submit.»

Fremdeles problemer? Se

(merk at videoen er litt gammel, så det kan være noen små forskjeller i hvordan det ser ut nå.)

Ser du ikke knappen du skal trykke på i mitt.uib?

  • Dobbeltsjekk at du er innlogget på mitt.uib.
  • Prøv å laste siden i nettleseren på nytt.
  • Prøv en «force refresh» (F5 eller Ctrl+F5 eller Ctrl+Shift+F5 eller Ctrl+Shift+R eller Command+R eller Command+Shift+R avhengig av nettleser og operativsystem).
  • Prøv å tøm cache i nettleseren din (se refreshyourcache.com for hvordan du gjør dette) og prøv på nytt.
  • Prøv en annen nettleser.
Suppe

Programmet under tegner én boks med suppe. Kopier det inn i filen soup.py.

from uib_inf100_graphics.simple import canvas, display

def draw_soup_can(canvas, x, y, bg_color, text_color):
    canvas.create_rectangle(x, y+10, x+100, y+130, fill=bg_color)
    canvas.create_oval(x, y, x+100, y+20, fill='gray')
    canvas.create_oval(x, y+120, x+100, y+140, fill='gray')
    canvas.create_text(x+50, y+50, text='SOUP',
                       fill=text_color, font=('Arial', 16, 'bold'))

draw_soup_can(canvas, 50, 30, 'red', 'yellow')
# TODO: make three more calls to draw_soup_can with different arguments

display(canvas)

Skriv tre nye linjer med kode hvor du kaller funksjonen draw_soup_can med andre argumenter, slik at du får tegnet fire bokser med suppe som vist her:

Fire kanner med suppe
Marilyn

Dette programmet tegner en vakker kvinne fire ganger. Kopier det inn i filen marilyn.py.

from uib_inf100_graphics.simple import canvas, display

def draw_marilyn(canvas, x, y):
    # Background
    canvas.create_rectangle(x, y, x+100, y+100, fill='red', outline='')

    # Face
    canvas.create_oval(x+20, y+20, x+80, y+80, outline='yellow', width=2)

    # Eyes
    canvas.create_oval(x+35, y+40, x+45, y+50, fill='yellow', outline='')
    canvas.create_oval(x+55, y+40, x+65, y+50, fill='yellow', outline='')

    # Mouth
    canvas.create_arc(x+35, y+50, x+65, y+70, start=180, extent=180,
                      fill='yellow', outline='')

# Drawing four faces
draw_marilyn(canvas, 50, 50)
draw_marilyn(canvas, 250, 50)
draw_marilyn(canvas, 50, 250)
draw_marilyn(canvas, 250, 250)

display(canvas)

For å gjøre tegningen litt mer interessant, endre draw_marilyn -funksjonen slik at den i tillegg til x og y også tar inn to nye parametre background_color og detail_color. Oppdater funksjonskroppen (kodelinjene inne i funksjonen) slik at den benytter disse fargene i stedet for hardkodete farger når den tegner

I de fire funksjonskallene som utfører tegningen, velg ulike farger etter eget ønske. Det endelige resultatet kan for eksempel se slik ut:

Marily tegnet i fire ulike farger
Strekmenn

Dette programmet (som du kjenner igjen fra lab1) tegner en strekmann. Kopier koden til stickmen2.py

from uib_inf100_graphics.simple import canvas, display

canvas.create_oval(60, 80, 140, 160)   # Head
canvas.create_line(100, 160, 100, 280) # Body
canvas.create_line(50, 180, 150, 180)  # Arms
canvas.create_line(100, 280, 50, 330)  # Left leg
canvas.create_line(100, 280, 150, 330) # Right leg

display(canvas)

Oppgaven er å endre koden slik at tegningen av strekmannen skjer i en funksjon draw_stickman som tar inn tre parametre canvas, x og y. Tegn strekmannen i funksjonen slik at den som kaller på metoden enkelt kan flytte hele strekmannen rundt på skjermen ved å gi ulike argumenter for x og y.

Kall funksjonen to ganger med ulike argumenter for å tegne to strekmenn ved siden av hverandre, som vist her:

To strekmenn

Den resulterende koden din kan for eksempel se ca slik ut:

from uib_inf100_graphics.simple import canvas, display

def draw_stickman(canvas, x, y):
    # TODO: Kode som tegner strekmannen her
    # Koden kan være en kopi av den originale
    # sterk-mannen, men pluss på x på alle
    # x-koordinater, og pluss på y på alle
    # y-koordinater.

draw_stickman(canvas, 0, 0)
draw_stickman(canvas, 200, 0)

display(canvas)
Avstand

I denne oppgaven skal vi bli kjent med funksjoner med returverdi. I en fil distance.py, skriv en funksjon distance(x1, y1, x2, y2) som regner ut og returnerer avstanden mellom to punkter \((x_1, y_1)\) og \((x_2, y_2)\). Under ser du avstandsformelen hvor d er avstanden mellom punktene:

Illustrasjon av avstandsformelen d=sqrt((x1-x2)^2 + (y1-y2)^2)

For å teste koden, kan du legge til denne linjen nederst i filen:

def almost_equals(a, b):
    return abs(a - b) < 0.00000001

def test_distance():
    print('Tester distance... ', end='')
    assert almost_equals(3, distance(103, 42, 100, 42))
    assert almost_equals(4, distance(2, 100, 2, 104))
    assert almost_equals(5, distance(0, 3, 4, 0))
    assert almost_equals(1.414213562373, distance(0, 0, 1, 1))
    assert almost_equals(1.414213562373, distance(3, 4, 2, 3))
    print('OK')

test_distance()
  • Operatoren for eksponentiering er **, f. eks. vil 3**2 evaluere til 9.

  • Å ta kvadratroten av et tall er det samme som å opphøye tallet i 0.5. For eksempel, 9**0.5 vil evaluere til 3.0.

Joker

I denne oppgaven skal vi skrive en kunstig intelligens for å spille Joker fra Norsk Tipping. I dette spillet får man på forhånd vite fem grunntall; for hvert av grunntallene må man velge om man skal gå «opp» eller «ned» før et hemmelig vippetall avsløres. Dersom man valgte «opp» og det avslørte vippetallet er høyere eller lik grunntallet, vinner man en større premie. Det samme skjer dersom man velger «ned» og vippetallet er lavere eller lik grunntallet. Vi antar at de hemmelige vippetallene er tilfeldig valgt mellom 0 og 9.

Strategien vi skal implementere er å si «opp» dersom grunntallet er 4 eller lavere, og si «ned» ellers.

I filen joker.py, skriv et program som ber brukeren om 5 tall. Disse representerer grunntallene vi får oppgitt når vi begynner å spille Joker. Deretter skal programmet skrive ut enten «opp» eller «ned», for hvert av de fem grunntallene. En kjøring av programmet kan se slik ut:

tall1 = 
3
tall2 =
4
tall3 =
5
tall4 =
6
tall5 =
1
opp
opp
ned
ned
opp
Grisete samarbeid

Teamet ditt med utviklere skal tegne en fin gris, og dere har fordelt arbeidet som følger:

  • Ola tegner grisens hode med en funksjon draw_head i filen pig_head.py.
  • Kari tegner grisens kropp med en funksjon draw_body i filen pig_body.py.
  • Din jobb er å skrive hovedfilen pig_main.py som binder sammen tegningene og som faktisk tegner en gris når programmet kjører.

I denne oppgaven skal du ikke endre på filene pig_head.py eller pig_body.py, du skal bare laste dem ned.

Ola og Kari er ferdig med arbeidet sitt; last ned filene de har laget (høyreklikk på linkene og velg «lagre» eller lignende). Opprett deretter en fil pig_main.py som du legger i samme mappe som filene fra Ola og Kari.

  • Øverst i pig_main.py, importer funksjonene draw_head og draw_body fra henholdsvis pig_head og pig_body -filene.
  • Du må også importere canvas og display fra uib_inf100_graphics.simple slik du pleier. På slutten av filen må du kalle på display(canvas), slik vi også pleier når vi jobber med grafikk.
# pig_main.py
from uib_inf100_graphics.simple import canvas, display
from pig_head import draw_head
from pig_body import draw_body

# TODO: kall på funksjonene draw_body og draw_head
...

display(canvas)

På de første linjene importerer du fra ulike kilder: uib_inf100_graphics.simple er et eksternt bibliotek du har installert, mens pig_head og pig_body er .py -filer som ligger i samme mappe som din egen fil. Det ser veldig likt ut i koden fordi det er veldig likt: det eksterne biblioteket er en .py -fil det også (bare at den ligger i en skjult mappe knyttet til Python-fortolkeren din).

For å gjøre ferdig oppgaven, må du utføre kall til draw_head og draw_body -funksjonene du nettopp har importert. Du må gjøre deg kjent med hvilke parametre disse funksjonene forventer ved å lese litt i filene til Ola og Kari. Når du er ferdig, skal du kunne kjøre pig_main.py og få et vindu som ser omtrent slik ut:

Eksempelkjøring 1
Belgisk flagg

I denne oppgaven skal vi lage en funksjon som tegner et belgisk flagg.

Opprett filen belgian_flag.py med en funksjon draw_belgian_flag som har fem parametere: canvas, x1, y1, x2, y2. Funksjonen skal tegne et belgisk flagg på canvas med øvre venstre hjørne i punktet \((x_1, y_1)\) og nedre høyre hjørne i punktet \((x_2, y_2)\). Funksjonen trenger ikke å ha noen retursetning.

# belgian_flag.py

def draw_belgian_flag(canvas, x1, y1, x2, y2):
    # TOOD: replace this function body with code to draw the Belgian flag
    ...

Merk: du skal ikke importere noe i belgian_flag.py. Når du kjører filen forventer vi heller ikke at noe skjer, siden programmet ikke inneholder noen kall til funksjonen. For å teste at funksjonen din fungerer, kan du i stedet opprette en ny fil belgian_flag_test.py i samme mappe og kopiere inn dette programmet:

# belgian_flag_test.py
from uib_inf100_graphics.simple import canvas, display
from belgian_flag import draw_belgian_flag

draw_belgian_flag(canvas, 125, 135, 275, 265)
draw_belgian_flag(canvas, 10, 10, 40, 36)
draw_belgian_flag(canvas, 10, 340, 390, 360)

display(canvas)

Om du har gjort alt riktig i belgian_flag.py og deretter kjører belgian_flag_test.py, skal det tegnes tre belgiske flagg som vist under:

Eksempelkjøring 1
Innramming

Opprett filen frame.py og kopier inn følgende kode:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def frame(text):
    if text.startswith('#'):
        text = '#' + text
    else:
        text = '# ' + text

    if text.endswith('#'):
        return text + ' #'
    return text + ' #'


print('Testing frame... ', end='')
s = 'Carpe diem'
s = frame(s)
assert '## Carpe diem ##' == frame(s)
print('OK')

Om du kjører koden, vil du oppdage at programmet krasjer på linje 15 med en AssertionError. Vi skjønner at noe er galt, men vi vet ikke hva. Vi skal nå benytte debuggeren for å se hvilken returverdi vi faktisk får i funksjonskallene på linje 15.

  • Sett et breakpoint på linje 12 eller 13 og kjør programmet i debug-modus. Bruk step over -knappen for å gå noen steg, helt til neste linje som skal utføres (den markerte linjen) er linje 15.
  • Undersøk variabel-området i debuggeren. Legg merke til hva s er nå.
  • Siden alt ser bra ut, fortsetter vi: trykk på step into for å gå inn i funksjonskallet til frame.
  • Her kunne du trykket på «step over» for å gå steg for steg gjennom setningene i funksjonskroppen; men akkurat nå er vi er mest interessert i selve returverdien, så trykk i stedet på step out.
  • Undersøk variabel-området i debuggeren på nytt. Legg merke til at det er en «variabel» der som heter (return) frame. Denne viser til returverdien fra funksjonskallet vi nettopp gikk ut fra. Hva er det som er feil?

Ta et skjermbilde frame_debug.png av debuggeren som viser variabel-området i debuggeren og hvilken verdi (return) frame har. Last opp dette bildet som besvarelse på denne oppgaven.

PS: For å kunne se returverdien må du benytte deg av «step out» -knappen. Om du benytter en av de andre knappene når du forlater funksjonskroppen kan det være du utløser assert-krasjen.

Vi forventer at skjermbildet du leverer ser omtrent slik ut (men selvfølgelig uten sladden):

Eksempel på skjermbilde som løser oppgaven

Forklar for deg selv: hva er forskjellen på «step over» og «step into»?

Bonus: hvilken return-setning ble utført? Start debuggeren på nytt og gå steg for steg gjennom funksjonskroppen helt til du ser hvilken return-setning som utføres. Feilen ligger kanskje her? Fiks feilen slik at testen passerer.

Paritet

I filen parity.py, skriv en funksjon parity(x) som har en parameter x, og som returnerer Partall hvis x er et partall og Oddetall hvis x er et oddetall. Du kan anta i funksjonen at x er et heltall med typen int.

def parity(x):
    # Skriv koden din her

For å teste funksjonen din kan du legge til dette på slutten av filen:

print('Tester parity... ', end='')
assert 'Partall' == parity(0)
assert 'Oddetall' == parity(1) 
assert 'Partall' == parity(42)
assert 'Oddetall' == parity(99)
print('OK')
  • Benytt modulo (%) for å avgjøre om et tall er partall eller oddetall. Mer informasjon om modulo finner i kursnotatene om operatorer

Korteste ord

I filen shortest_words.py, skriv kode som leser inn 3 ord og skriver ut det korteste ordet. Om flere ord har den korteste lengden skal programmet skrive ut alle ordene.

Kjøring av programmet ditt skal se ut slik som følgende eksempelkjøringer:

Skriv et ord:
Game   
Skriv et annet ord:
Action
Skriv et siste ord:
Champion

Game
Skriv et ord:
pineapple   
Skriv et annet ord:
apple
Skriv et siste ord:
grape

apple
grape
Skriv et ord:
Four   
Skriv et annet ord:
Five
Skriv et siste ord:
Nine

Four
Five
Nine
Lengste ord

I filen longest_word.py skal du lage et program som leser inn 3 ord og skriver ut det lengste ordet. Men om flere ord har den lengste lengden skal programmet bare skrive ut det første ordet som har en lengst lengde.

Eksempelkjøringer:

Skriv et ord:
Game   
Skriv et annet ord:
Action
Skriv et siste ord:
Champion

Champion
Skriv et ord:
pear   
Skriv et annet ord:
apple
Skriv et siste ord:
grape

apple
Skriv et ord:
Four   
Skriv et annet ord:
Five
Skriv et siste ord:
Nine

Four
Overlappende sirkler

I denne oppgaven skal du skrive et program som avgjør hvorvidt to sirkler overlapper. En sirkel kan beskrives ved hjelp av et koordinat for sirkelens sentrum \((x, y)\), samt en radius \(r\).

Illustrasjon av sirkel

Opprett en funksjon circles_overlap(x1, y1, r1, x2, y2, r2) i filen circles_overlap.py. La funksjonen returnere True dersom to sirkler beskrevet med henholdsvis x1, y1, r1 og x2, y2, r2 overlapper, og False hvis ikke.

To sirkler overlapper dersom avstanden mellom sirklenes sentrum er mindre enn eller lik summen av sirklenes radiuser.

For å teste funksjonen du har skrevet kan du legge til disse linjene nederst i circles_overlap.py:

def test_circles_overlap():
    print('Testing circles_overlap...', end='')

    # Sirkel1 med sentrum (0, 0) og radius 1
    # Sirkel2 med sentrum (1, 1) og radius 1
    # Overlapper
    assert circles_overlap(0, 0, 1, 1, 1, 1) is True

    # Sirkel1 med sentrum (0, 0) og radius 2
    # Sirkel2 med sentrum (4, 1) og radius 2
    # Overlapper ikke
    assert circles_overlap(0, 0, 2, 4, 1, 2) is False

    # Sirkel1 med sentrum (0, 0) og radius 3
    # Sirkel2 med sentrum (5, 0) og radius 2
    # De overlapper hverandre i et enkelt punkt
    assert circles_overlap(0, 0, 3, 5, 0, 2) is True
    print('OK')

test_circles_overlap()

Illustrasjon av testene:

Illustrasjon av sirkler/test-caser
Pop art

I denne oppgaven skal du lage din egen pop art i filen pop_art.py.

Pop art er en kunststil som ble populær på 1950-tallet. Pop art-kunstnere brukte ofte fargerike bilder av kjente personer, produkter og reklame, og benyttet seg mye av repetisjon av samme bilde i ulike farger. Den fremste eksponenten av pop art var Andy Warhol fra nydelige Pittsburgh PA, som blant annet lagde bilder av Marilyn Monroe og Campbell’s suppebokser.

Programmet ditt skal benytte en funksjon for å tegne et motiv, og funksjonen må kalles minst to ganger for å tegne variasjoner av det samme motivet ulike steder på lerretet. Du må benytte uib_inf100_graphics.simple for å lage tegningen. Du kan velge motivet ditt helt selv. Når du leverer oppgaven på CodeGrade, vil bildet ditt automatisk lastes opp i galleriet, hvor du også kan se hva dine medstudenter har laget. Noen eksempler på hva du kan lage:

image
image
image
Punkt i rektangel

Et hyperrektangel er et rektangel hvor sidene er vannrette og loddrette (ikke rotert). Vi kan representere et hyperrektangel med to koordinater \((x_1, y_1)\) og \((x_2, y_2)\) som representerer to diagonalt motsatte hjørner (du kan ikke anta noe om hvilken rekkefølge disse punktene kommer i, eller hvilke to motsatte hjørner i rektangelet de beskriver). I denne oppgaven skal du avgjøre hvorvidt et tredje punkt \((x_p, y_p)\) befinner seg innenfor et slikt rektangel eller ikke.

I en fil point_in_rectangle.py, skriv en funksjon point_in_rectangle som har seks parametre x1, y1, x2, y2, xp, yp, hvor de fire første parametrene representerer et hyperrektangel, og de to siste representerer et vilkårlig punkt. La metoden returnere True dersom punktet er innenfor rektangelet, og False hvis ikke. Dersom punktet befinner seg akkurat på linjen, regner vi det som at den er innenfor.

Illustrasjon av punkt i rektangel

Test koden din (husk å at du også må legge inn et funksjonskall til denne test-funksjonen nederst i filen for å faktisk utføre testene):

def test_point_in_rectangle():
    print('Tester point_in_rectangle... ', end='')
    assert point_in_rectangle(0, 0, 5, 5, 3, 3) is True # Midt i
    assert point_in_rectangle(0, 5, 5, 0, 5, 3) is True # På kanten
    assert point_in_rectangle(0, 0, 5, 5, 6, 3) is False # Utenfor
    print('OK')
  • Omregn rektangelet slik at du vet hva som er høyre og venstre, top og bunn. For eksempel, opprett variabler x_left = min(x1, x2) og x_right = max(x1, x2). Tilsvarende for topp og bunn med y-aksen.

  • Sjekk at punktet \((x_p, y_p)\) både befinner seg mellom venstre- og høyresiden, og også mellom topp og bunn.

  • For eksempel: punktet \((x_p, y_p)\) er mellom høyre- og venstre siden dersom både x_left er mindre eller lik xp og xp er mindre eller lik x_right.

Hundeår

Vanligvis sier man at et menneskeår tilsvarer 7 hundeår. Dette tar ikke hensyn til at hunder blir voksne når de er ca 2 år. Derfor kan det være bedre å regne begge de første 2 menneskeårene som 10.5 hundeår hver, og etter det regne hvert menneskeår som 4 hundeår.

I filen dog_years.py skal du opprette funksjonen human_to_dog_years med én parameter som representerer antall menneskeår. La funksjonen returnere hvor mange hundeår dette tilsvarer.

Test koden din (husk at du også må legge inn et funksjonskall til test-funksjonen for at testene faktisk skal kjøres):

def almost_equals(a, b):
    return abs(a - b) < 0.00000001

def test_human_to_dog_years():
    print('Tester human_to_dog_years... ', end='')
    assert almost_equals(15.75, human_to_dog_years(1.5))
    assert almost_equals(21.00, human_to_dog_years(2))
    assert almost_equals(57.00, human_to_dog_years(11))
    print('OK')
Skuddår

Regelen for å beregne om et år er et skuddår eller ikke er som følger:

  • Vanligvis er et år som er delelig med 4 et skuddår (for eksempel 1996 var et skuddår);
  • bortsett fra år som også er delelige med 100 (for eksempel 1900 er ikke skuddår);
  • men hvis året som er delelige med 100 også er delelig med 400, da er det et skuddår likevel (for eksempel er 2000 et skuddår).

I filen leapyear.py opprett en funksjon is_leap_year(year) som tar inn et årstall og returnerer True dersom det er et skuddår, og False hvis ikke.

Test koden din (husk at testene bare kjøres dersom du i tillegg legger inn et funksjonskall til test_is_leap_year i hovedprogrammet):

def test_is_leap_year():
    print('Tester is_leap_year... ', end='')
    assert is_leap_year(2022) is False # Ikke delelig med 4
    assert is_leap_year(1996) is True  # Normalt skuddår
    assert is_leap_year(1900) is False # Delbart med 100
    assert is_leap_year(2000) is True  # Delbart med 400
    print('OK')
  • Benytt modulo-operatøren (%) for å avgjøre om et heltall er delelig med et annet.

PS: Hvis alt er som det skal, vil programmet skrive ut

Tester is_leap_year... OK

når det kjøres

Kollisjonsdeteksjon

Denne oppgaven består av to deler. Skriv funksjoner til begge deloppgaver (A-B) i én felles fil, collision_detection.py.

Del A

I filen collision_detection.py, skriv en funksjon rectangles_overlap som har åtte parametre x1, y1, x2, y2, x3, y3, x4, y4, hvor de fire første parametrene ett hyperrektangel, og de fire siste representerer et annet (et hyperrektangel representeres av to motsatte hjørner, men vi kan ikke gjøre noen ytterligere antakelser om hvilke hjørner). La metoden returnere True dersom rektanglene overlapper hverandre, og False hvis ikke. Vi sier at rektanglene overlapper selv om de kun deler ett enkelt punkt.

Illustrasjon av overlappende rektangler

Test koden din:

def test_rectangles_overlap():
    print('Tester rectangles_overlap... ', end='')
    assert rectangles_overlap(0, 0, 5, 5, 2, 2, 6, 6) is True # Delvis overlapp
    assert rectangles_overlap(0, 5, 5, 0, 1, 1, 4, 4) is True # Fullstendig overlapp
    assert rectangles_overlap(0, 1, 7, 2, 1, 0, 2, 7) is True # Kryssende rektangler
    assert rectangles_overlap(0, 5, 5, 0, 5, 5, 7, 7) is True # Deler et hjørne
    assert rectangles_overlap(0, 0, 5, 5, 3, 6, 5, 8) is False # Utenfor
    print('OK')
  • Omregn begge rektangler slik at du vet hva som er høyre og venstre, top og bunn (se hint for oppgaven Hyperrektangel).
  • Dersom høyresiden til ett rektangel er til venstre for venstresiden av det andre, blir svaret false (tilsvarende logikk med topp og bunn). Husk å sjekke begge retninger.

Illustrasjon av testcasene i assert’ene over:

Illustrasjon av testcasene over

Del B

I filen collision_detection.py, skriv en funksjon circle_overlaps_rectangle som har syv parametre x1, y1, x2, y2, xc, yc, rc, hvor de fire første parametrene representerer to motstående hjørner i et hyperrektangel, og de tre siste representerer en sirkel sentrert i \((x_c, y_c)\) med radius \(r_c\). La metoden returnere True dersom sirkelen overlapper rektangelet, og False hvis ikke. Dersom sirkelen og rektangelet deler kun ett enkelt punkt regnes det fremdeles som at de er overlappende.

Illustrasjon av sirkel og rektangel som ikke overlapper

Test koden din:

def test_circle_overlaps_rectangle():
    print('Tester circle_overlaps_rectangle... ', end='')
    assert circle_overlaps_rectangle(0, 0, 5, 5, 2.5, 2.5, 2) is True # på midten
    assert circle_overlaps_rectangle(0, 5, 5, 0, 8, 3, 2) is False # langt utenfor
    assert circle_overlaps_rectangle(0, 0, 5, 5, 2.5, 7, 2.01) is True # på kanten
    assert circle_overlaps_rectangle(0, 5, 5, 0, 5.1, 5.1, 1) is True # på hjørnet
    assert circle_overlaps_rectangle(0, 0, 5, 5, 8, 8.99, 5) is True # på hjørnet
    assert circle_overlaps_rectangle(0, 0, 5, 5, 8, 9.01, 5) is False # bare nesten
    print('OK')
  • Dersom sirkelens sentrum er inne i rektangelet, er svaret True. Bruk funksjonen du skrev i oppgaven om punkt i rektangel for å sjekke dette.
    • Du kan importere funksjonen ved å legge til from point_in_rectangle import point_in_rectangle øverst i collision_detection.py.
  • La det minste x-koordinatet av x1 og x2 kalles x_left, og la det største kalles x_right. På samme måte, slutt å tenke på punktene y1 og y2, og regn i stedet ut punktene y_top og y_bottom.
  • «Utvid» rektangelet med sirkelens radius i alle retninger. Hvis sirkelens sentrum er utenfor dette utvidede rektangelet (bruk funksjonen point_in_rectangle igjen), er det garantert ikke noe overlapp.
  • I de gjenstående tilfellene befinner sirkelen sitt sentrum seg i rammen rundt rektangelet som er tegnet med stiplet linje i illustrasjonen over.
    • Dersom sirkelens x-koordinat befinner seg mellom x-koordinatene til det opprinnelige rektangelet, er det overlapp.
    • Tilsvarende for y-aksen.
    • Hvis sirkelens sentrum ikke tilfredsstiller noen av de to punktene over, befinner det seg i et av hjørnene. Dersom sirkelens sentrum har større avstand enn \(r_c\) til samtlige hjørner i det opprinnelige rektangelet, er det ingen overlapp (f. eks. slik som på figuren over). For å finne avstanden, bruk funksjonen distance som du kan importere fra en tidligere oppgave (from distance import distance).

Illustrasjon av testcasene oppgitt over (en sirkel per testcase):

Illustrasjon av testcasene

Flytdiagram for circle_overlaps_rectangle

if point_in_rectangle(...):  # sirkelens sentrum inne i rektangel?
    return True
elif not point_in_rectange(...): # sentrum utenfor utvidet rektangel?
    return False
elif ... # punkt er mellom venstre og høyresiden til rektangel (x-aksen)
    return True
elif ... # punkt er mellom topp og bunn til rektangel (y-aksen)
    return True
elif distance(...) # sirkelen overlapper hjørnet oppe til venstre
    return True
elif ... # sirkelen overlapper hjørnet oppe til høyre
    return True
...
Synlig lys

Våre øyne oppfatter elektromagnetisk stråling med en bølgelengde fra 380 til 740 nanometer, eller med en frekvens fra 405 til 790 terahertz. Dette område er kalt synlig lys eller bare lys. elektromagnetisk stråling i synlig lys omdannes til farger i hjernen hos mennesker og dyr. Tabellen nedenfor viser hvor de ulike fargene av synlig lys ligger i spekteret. Wikipedia.

ColorWavelength (nm)Frequency (THz)
Violet380 - 450670 - 790
Blue450 - 485620 - 670
Cyan485 - 500600 - 620
Green500 - 565530 - 600
Yellow565 - 590510 - 530
Orange590 - 625480 - 510
Red625 - 750400 - 480

Forholdet mellom bølgelengde og frekvens er gitt ved formelen: $$ \lambda = \frac{c}{f} $$ hvor \(\lambda\) er bølgelengde i meter, \(f\) er frekvens i Hz, og \(c = 3\cdot 10^8\text{ m/s}\) er lysets hastighet. Husk også på at vi har følgende forhold mellom enheter: $$ 1\text{ m} = 10^{9}\text{ nm} $$ $$ 1\text{ Hz} = 10^{-12}\text{ THz} $$ PS: Hz er det samme som 1/s.

I filen visible_light.py skal du skrive et program som spør brukeren om en enhet, enten nanometer (nm) eller terahertz (THz), og siden en verdi (et heltall). Programmet skal skrive ut hvilken farge i synlig lys den enheten og verdien tillhører. Om du får en enhet som ikke er nm eller THz skal programmet ditt informere brukeren at enheten må være enten nm eller THz, og programmet skal avslutte kjøringen (se eksempler under for nøyaktig ordlyd). Om brukeren skriver inn en bølgelengde eller frekvens som er utenfor spektrumet, skal det gis melding om dette også (se eksempler). Om du får en verdi som er akkurat på grensen mellom to farger skal du velge fargen med kortest bølgelengde (høyest frekvens) av de to.

Eksempelkjøringer:

Angi enhet (nm eller THz):
nm   
Angi verdi i nm:
520

Green
Angi enhet (nm eller THz):
THz
Angi verdi i THz:
680

Violet
Angi enhet (nm eller THz):
nm
Angi verdi i nm:
320

320 nm er utenfor det synlige spekteret.
Angi enhet (nm eller THz):
THz
angi verdi i THz:
800

800 THz er utenfor det synlige spekteret.
Angi enhet (nm eller THz):
foo

Enheten må være i nm eller THz, det kan ikke være foo.
Forslag til programflyt

Det er mange ulike måter å løse denne oppgaven på. Programflyten over er et forslag som kan hjelpe deg å bryte ned programkoden i mer overkommelige deler. I skjemaet over er det to større oppgaver vi ikke har løst for deg:

  • La \(f\) være frekvensen i antall Hz. Husk at verdien brukeren har gitt oss har enheten THz, så vi må først regne om. 1 THz er det samme som 10**12 Hz.
  • Lysets hastighet er \(c = 3\cdot 10^8\text{ m/s}\).
  • Plugg verdiene inn i formelen \(\lambda = c/f\) for å regne ut bølgelengden.
  • Regn svaret om fra meter til nanometer.
  • Det kan være lurt å benytte en if-elif -sekvens, hvor betingelsene sjekker verdien til bølgelengden opp mot grensene gitt i tabellen i oppgaveteksten.
  • Ikke overskriv den enhet og verdi brukeren opprinnlig gav som input, men ta vare på dem i variabler slik at du kan skrive ut en feilmelding basert på disse hvis det trengs.