Commit 7598e2d9 authored by Anna Maria Eilertsen's avatar Anna Maria Eilertsen
Browse files

initial commit

parents
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
/bin/
bin
tmp
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Binaries
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
*.war
*.ear
*.sar
*.class
# Maven
target/
/target/
# IntelliJ project files
*.iml
*.iws
*.ipr
*.idea/
# Eclipse project files
.settings/
.classpath
.project
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Lab 7
_Dette dokumentet beskriver lab 7 for INF101 - vår 2020._
### Læringsmål
- Abstrakt superklasse
- Generics
- Lage og oppfylle en spesifikasjon
## Klon koden
Klon koden for denne lab-oppgaven fra ditt eget repositorie ved hjelp av URIen:
```
https://retting.ii.uib.no/<brukernavn>/inf101.v20.lab7.git
```
Se på lab-oppgavene fra tidligere uker for detaljert beskrivelse av hvordan. Husk å `add-commit-push` underveis i oppgavene.
## Intro
NHH-studentene fra Smartrobe™ har tatt kontakt igjen. AI-selskapet de var i kontakt med ble kjøpt opp av Amazon før kontrakten gikk i boks. Nå vil de at du skal lage prototypen av en kunstig intelligens i stedet. Den må kunne foreslå daglige outfits fra garderoben, men de skynder seg å utdype at den trenger ikke å være så voldsomt intelligent.
"Vi prøvde oss litt selv, men vi kan jo ikke kode så det ble det ofte 20 gensere og ingen bukser," forteller de oppgitt. Du får beskjed om at en outfit må være ren og fornuftig. Det er opp til deg å finne ut hva det vil si.
Du husker at du ikke var helt fornøyd med koden du skrev, og spør om det er budsjett til å refaktorere den før du setter i gang med utvidelsen. Det får du lov til, sier de. Men det er veldig viktig at du ikke endrer på kode-APIet.
## Om Lab 7
I lab 7 skal du utvide og forbedre kodebasen fra lab 5. Koden du får utlevert er en løsning til lab 5. Du kan også velge å jobbe ut ifra din egen løsning.
Denne oppgaven består av tre deler. De to første innebærer å forbedre den eksisterende koden ved hjelp av [refactoring](https://en.wikipedia.org/wiki/Code_refactoring). Den siste innebærer å bruke eksisterende kode til å implementere ny funksjonalitet.
I del 1 og 2 kan du benytte deg av eksisterende tester: når du gjør endringer vil kanskje noen av testene feile. Da må du finne ut hvorfor og rette opp i feilen. **Del 1 og 2 skal ikke endre funksjonaliteten til koden.**
I del 3 finnes det ikke tester og du har fått litt uklar spesifikasjon av programmet. For å løse denne delen av oppgaven må du *både* finne ut nøyaktig hva koden din skal gjøre, hvordan du kan teste det og skrive koden.
Denne labben inneholder mindre guiding enn på tidligere lab-oppgaver og du vil kanskje syns det er vanskelig å vite når du er ferdig. Derfor er det lurt å diskutere programmet ditt med gruppeledere og medstudenter for å se om det er noe du har oversett. Hvis en medstudentent utvikler gode testfiler eller unit tester kan du gjerne spørre om å låne dem eller bytte dem mot dine egne.
## Del 1 - Introduser \<Generics>
I lab 4 *generaliserte* vi grid-strukturen fra lab 3 til å bruke generics. Her skal du gjøre Storage generisk.
*Tips: Hvis du ikke har gjort lab 4 er det en god idé å gjøre den først.*
👉 Endre interfacet Storage slik at det bruker en generisk type i stedet for Clothing. Du må endre subklassene og alle referanser tilsvarende.
Tips: Drawer, Shelf og LaundryBasket skal *ikke* være generiske. (Hvorfor tror du vi gjør Storage generisk, men ikke disse klassene?)
## Del 2 - Gjenbruk ved abstrakt superklasse
I lab 5 la du kanskje merke til at mye av koden i klassene `Drawer`, `Shelf` og `LaundryBasket` var veldig lik.
👉 Lag en abstrakt superklasse som inneholder koden som disse tre klassene har til felles. Den abstrakte klassen bør *implementere* `Storage`, og `Drawer`, `Shelf` og `LaundryBasket` bør *utvide* den.
Du må selv finne ut hva klassen bør hete, hvilken pakke den bør ligge i, og hvilken del av koden som bør flyttes inn i den.
Tips: For å få nytte av at metodene er felles, kan du gjerne flytte listen som alle klassene har felles inn i denne klassen. Da kan den kanskje ikke være privat lenger? Hvilket nivå av accessibility tror du er lurt å bruke her?
*NB: Du skal ikke trenge å endre på `Storage`, `Wardrobe`, eller noen av testene for å løse del 2.*
## Del 3
*Du kan løse Del 3 uten å endre på eksisterende kode utenfor .outfit-pakkene.*
Du vil kanskje legge til lokale hjelpemetoder eller legge inn typeargumenter. Det er opp til deg.
I denne delen skal du implementere en Bot, eller AI, som benytter seg av garderobe-koden du alt har implementert. Du skal jobbe i de følgende filene:
`Main.java`
`OutfitPicker.java`
`OutfitPickerTest.java`
`Outfit.java`
Boten laster inn klesskapet fra fil og holder det i minnet i løpet av én programkjøring. Hver gang du starter programmet lastes det inn på nytt.
Du kan interagere med boten via kommandolinjen ved hjelp av følgende kommandoer:
- `print`: printer status i klesskap til skjerm
- `suggest`: foreslå en outfit basert på nåværende klesskap
- `use`: registrer alle klesplagg i sist foreslåtte outfit som brukt
- `laundry`: registrer alle klesplagg i skapet som vasket
- `stop`: stopp programmet
`Main.java` inneholder en enkel implementasjon av boten, men funksjonaliteten mangler i OutfitPicker mangler. Du kan fritt endre koden i Main.java, f.eks. ved å utvide støtten for kommandoer eller forbedre formatteringen på uskriftene.
👉 I denne oppgaven skal du:
- **Lage en spesifikasjon av en Outfit.** Hva er kravene for en outfit? Hvilke felter og metoder bør Outfit-type inneholde? Hva kan du teste?
- **Implementere suggest og use i OutfitPicker.**
- **Gjøre ferdig testene for suggest og use i OutfitPickerTest**
Du må teste funksjonaliteten til programmet ditt enten manuelt ved å kjøre Main.java og teste kommandoene selv via kommandolinjen, eller ved hjelp av å lage flere unit tester. Det er opp til deg å bestemme spesifikasjonen, og dermed også når du er ferdig med oppgaven.
Lykke til 🎉
----
### Tips
Noen tips og triks.
#### Hvor starter jeg?
Start med å generere 'dumme' outfits. Se på testene i OutfitPicker: de første testene er enklest å oppfylle.
Du kan også kjøre programmet fra kommandolinja for å se outfit som du genererer.
#### Hvordan tester jeg fra kommandolinja?
Når du får til å lage enkle outfits, kan du teste programmet ved hjelp av sekvensen:
`print`
`suggest`
`use`
`print`
#### Hvordan får jeg tak i verdiene inni Outfit?
Du må selv bestemme hvilke metoder Outfit-klassen skal ha. Da kan det være fristende å legge inn metoder som gjør det lettere for testene å se hva som foregår inni objektet.
Unngå å lage nye metoder *kun* for å forenkle tester. Husk at for hver ny metode du legger til, bør du også legge til tester for den metoden.
#### Hva menes med en spesifikasjon for outfit?
I introen så du for eksempel at outfits bør være rene. Det vil si at alle klærne i en outfit bør være rene, ikke skitne. Et annet krav er at en outfit ikke inneholder duplikasjon av typer, f.eks. to jeans eller to jakker.
Når du bestemmer deg for en spesifkasjon må du også tenke på hva som skal skje dersom metoder blir kalt med argumenter som ikke oppfyller den.
Et mer avansert krav er at outfits ikke skal inneholde klestyper som "overlapper" hverandre. Hvis den allerede inneholder en bukse trenger den kanskje ikke jeans.
Videre må du kanskje tenke på, hvor mange klesplagg trengs for at det skal være en outfit? Holder det med ett?
Skriv spesifikasjonen din ned i javadoc eller i en egen fil, og test at koden din oppfyller den. Du kan teste det både ved hjelp av kommandolinjen og unit-tester.
\ No newline at end of file
# Git og GitLab
I INF101 bruker du som student **Git** til
- klone (laste ned) oppgavene fra en Git-server til din egen datamaskin,
- committe (lagre) så ofte som mulig mens du jobber med en oppgave,
- pulle (laste ned de siste endringene fra serveren til maskinen din) og
- pushe (laste opp de siste endringene fra maskinen til serveren).
- ++
Eclipse er i stand til å gjøre mange av Git-operasjonene inne i programmet, men vi anbefaler at du først lærer deg å bruke Git ved hjelp av terminalen for å få en bedre forståelse av hvordan det fungerer. Hvis ikke kan alt virke "magisk", og når noe går galt (merk **når**) vet man ikke hva man skal gjøre.
Applikasjonen vi bruker til å håndtere alle Git-repositoriene heter **GitLab**, og kjører på en UiB-server på URLen Retting.ii.uib.no. (Eksempler på andre slike servere er github.com, gitlab.com, bitbucket.com, etc.)
## 1. Tilgang til GitLab
Alle studentene som er oppmeldt til INF101 og har logget inn på Mitt UiB skal automatisk ha fått en konto på GitLab-instansen på Retting.ii.uib.no. Merk at når vi snakker om GitLab i INF101 så er det alltid på denne URLen. Brukernavnet ditt er det samme som student-epostadressen din, f.eks. bruce.wayne@student.uib.no. Dersom du er usikker på hva som er din studentepostadresse finner du dette oppe til høyre inne på din Webmail. For å sette et passord velger du "Forgot your password?" på framsiden -> skriver inn eposten -> åpner eposten fra UiB -> Reset Password -> Lag et passord.
Bruk gjerne et minutt eller to til å gjøre deg kjent på nettsiden etter du har logget inn, ettersom du kommer til å måtte navigere deg rundt mange ganger. Søkevinduet oppe til høyre er nyttig når du vet hvilket repositorie du vil gå til.
## 2. Legg inn opplysninger i Git
Bruk de to terminalkommandoene
`> git config --global user.name "FIRST_NAME LAST_NAME"`
`> git config --global user.email "MY_NAME@example.com"`
til å lagre navn og epost (bruk student-eposten din her) i Git-konfigurasjonen på maskinen din. Hvis du ikke gjør dette kommer Git til å klage når du prøver å committe (lagre) endringer, for Git er nemlig opptatt av _hvem_ som gjorde endringene slik at de potensielt senere kan krediteres (eller skyldes på...).
## 3. Clone
På GitLab kommer du til å få tilgang til en del **repositorier** (prosjekt-mapper). Når du skal kjøre eller utvikle videre på et repositorie må du først hente det ned på din egen maskin ved hjelp av Git, i Git-verdenen kalles dette å **klone** repositoriet. Navnet kommer av at du oppretter en kopi av repositoriet på maskinen din.
Noen av repositoriene kommer til å være offentlige slik at alle har tilgang til de, og noen kommer til å være private som f.eks. ukesoppgavene og innleveringene. Dine medstudenter har med andre ord ingen tilgang til dine oppgaver. For å kunne klone et _privat_ prosjekt kommer Git til å spørre deg om brukernavn og passord. Dersom du etterhvert blir lei dette kan du bruke dette svaret på Stack Overflow til å lagre passordet permanent: https://stackoverflow.com/a/5343146. Da lar du Git bruke et eksternt program til lagre passordene, f.eks. _osxkeychain_ som er preinstallert på mac.
Når du skal klone et repositorie går du inn på framsiden til repositorie på GitLab. Merk at du kan ha tilgang til to repositorier med samme navn, et offentlig (under f.eks inf101.v20.oppgaver) som du ikke har redigeringstilgang til og et privat - i så fall ønsker du å klone det _private_ repositoriet. Trykk på den blå Clone-knappen oppe til høyre og kopier URLen for _HTTPS_ (i skrivende stund er SSH stengt). Gå så til terminalen din og naviger deg til den mappen du vil klone repositoriet til. Klon med kommandoen
`git clone <URL her>`
f.eks
`git clone https://retting.ii.uib.no/inf101/inf101.v20/inf101.v20.lab1.git`
Git skal så laste ned repositoriet til den mappen du er i.
## 4. Commit
_Kommer ..._
## 5. Push
_Kommer ..._
## 6. Pull
_Kommer ..._
## 7. Flere resurser
Sel om GIT er et verktøy du kan bruke og teste på lokal maskin er det ikke alltid like lett å følge utfallet av komandoene. Dersom du ønsker å lære GIT er det mange gode videoer og
ressurser online. Under er en liste som kan være til hjelp:
- [LearnGitBranching](https://learngitbranching.js.org/) er et online "spill" hvor du kan prøve ut og øve på GIT kommandoene
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>INF101</groupId>
<artifactId>inf101.v20.lab7.losning</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.jupiter.version>5.5.2</junit.jupiter.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>13</source>
<target>13</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M4</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
package inf101.v20.lab7;
import java.io.File;
import java.util.Scanner;
import inf101.v20.lab7.furniture.Wardrobe;
import inf101.v20.lab7.outfit.Outfit;
import inf101.v20.lab7.outfit.OutfitPicker;
import inf101.v20.lab7.utils.generator.Generator;
public class Main {
public static void main(String[] args) {
//Load example file(s)
Wardrobe wardrobe = Generator.wardrobeFromFile(new File(Main.class.getClassLoader().getResource("hers").getFile()));
Outfit outfit = null;
//Welcome screen
System.out.println("Welcome to Smartrobe outfit assisant.");
listCommands();
//Set up user interaction
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
//Loop as long as there is input
while(!input.toLowerCase().contains("stop")) {
switch(input.toLowerCase().trim()) {
case "stop": break;
case "print": print(wardrobe); break;
case "suggest": outfit = suggest(wardrobe); break;
case "use" : use(wardrobe, outfit); break;
case "laundry" : doLaundry(wardrobe); break;
default: listCommands();
}
input = sc.nextLine();
}
//program is stopping
sc.close();
}
private static void listCommands() {
System.out.println("Use the following commands:");
System.out.println("print - prints the preset wardrobes");
System.out.println("suggest - suggests an outfit based on the preloaded wardrobe");
System.out.println("use - use the previously suggested outfit");
System.out.println("laundry - do laundry");
System.out.println("stop - stops the program and clear the memory.");
}
private static void use(Wardrobe wardrobe, Outfit outfit) {
try{
if(OutfitPicker.useOutfit(outfit, wardrobe)) {
System.out.println("Registered this outfit as used: \n" + outfit);
return;
}
}catch(NullPointerException e) {
}
System.out.println("Failed to use outfit\n" + outfit);
}
private static void print(Wardrobe wardrobe) {
System.out.println(wardrobe);
}
private static void doLaundry(Wardrobe wardrobe) {
wardrobe.doLaundry();
System.out.println("Did laundry");
}
private static Outfit suggest(Wardrobe wardrobe) {
Outfit outfit = OutfitPicker.suggestOutfit(wardrobe);
System.out.println("Suggested \n" + outfit);
return outfit;
}
}
package inf101.v20.lab7.datastructure;
import java.util.Iterator;
public class NestedIterator<T> implements Iterator<T> {
Iterator<? extends Iterable<T>> outer;
Iterator<T> inner;
public NestedIterator(Iterable<? extends Iterable<T>> elements) {
this(elements.iterator());
}
public NestedIterator(Iterator<? extends Iterable<T>> outer) {
this.outer = outer;
getNextInner();
}
private void getNextInner() {
while(outer.hasNext() && (inner ==null || !inner.hasNext()))
inner = outer.next().iterator();
}
@Override
public boolean hasNext() {
getNextInner();
return inner.hasNext();
}
@Override
public T next() {
getNextInner();
return inner.next();
}
}
package inf101.v20.lab7.datastructure;
import java.util.NoSuchElementException;
import inf101.v20.lab7.item.Clothing;
/**
* This interface represents a datastructure for clothing items.
*
* @author mva021, Anna Eilertsen
*/
public interface Storage<T> extends Iterable<T>{
/**
* Places a clothing item in this storage space if possible
*
* @param item the item to place in the storage
* @return true. If this method returns, it will always return true. If the item was not added, the method will throw an exception
* @throws IllegalStateException if the storage is full and the item can not be added
* @throws IllegalArgumentException if the item does not fulfill the requirements for being added
* @throws NullPointerException if the specified element is null and the storage does not permit null elements
*/
public boolean add(T item) throws IllegalStateException, IllegalArgumentException, NullPointerException;
/**
* Checks if a clothing item fulfills the requirements for being added to this storages at the current time
*
* @param item a Clothing item
* @return true if the clothing can be added at the current time, false otherwise
*/
public boolean canAdd(T item);
/**
* Tries to remove an item of clothing from this storage
*
* @param item The clothing item to remove
* @return true if the item was found and removed, false otherwise.
* @throws NoSuchElementException if this storage was empty
*/
public boolean remove(T item) throws NoSuchElementException;
/**
* Checks if the storage contains the argument item using {@link inf101.v20.lab7.item.Clothing#equals(Object) equals}
*
* @param item the desired clothing item
* @return true if the storage contains the clothing item, false otherwise
*/
public boolean has(T item);
}
package inf101.v20.lab7.furniture;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import inf101.v20.lab7.datastructure.NestedIterator;
import inf101.v20.lab7.datastructure.Storage;
import inf101.v20.lab7.furniture.component.Drawer;
import inf101.v20.lab7.furniture.component.LaundryBasket;
import inf101.v20.lab7.furniture.component.Shelf;
import inf101.v20.lab7.item.Clothing;
/**
* This type holds storage units for {@link inf101.v20.lab7.item.Clothing Clothing}
*
* @author anna
*
*/
public class Wardrobe implements Iterable<Clothing>, Iterator<Storage<Clothing>> {
private List<Storage<Clothing>> storage;
private Iterator<Storage<Clothing>> iter;
/**
* Creates a new wardrobe with the indicated number of shelves, drawers and laundry size
* @param numberOfShelves The number of shelves in this wardrobe
* @param numberOfDrawers The number of drawers in this wardrobe
* @param laundrySize the number of items in the laundry before it is full and will be cleaned
*/
public Wardrobe(int numberOfShelves, int numberOfDrawers, int laundrySize) {
storage = new ArrayList<>();
for(int i=0; i<numberOfShelves; i++) {
storage.add(new Shelf());
}
for(int i=0; i<numberOfDrawers; i++) {
storage.add(new Drawer());
}
storage.add(new LaundryBasket(laundrySize));
reset();
}
/**
* Adds an item of clothing to this wardrobe
* @param item the clothing to add
* @return true if there was a space for this item and this item was successfully added, false otherwise
*/
public boolean add(Clothing item){
for(Storage<Clothing> place : storage) {
if(place.canAdd(item) && place.add(item)) {
return true;
}
}
return false;
}
/**
* Uses a desired item from the wardrobe.
*
* The item becomes dirty and is added to the laundry during the execution of this method.
*
* If the item was not found in the wardrobe, or was already dirty, or the laundry is full,
* this method will not succeed.
*
* @param item the desired item
* @return true if item was found and used, false otherwise
* @throws NullPointerException if the item is null
* @throws IllegalArgumentException if the item is dirty
* @throws IllegalStateException if the laundry is full
*/
public boolean use(Clothing item) {
if(item==null)
throw new NullPointerException("Null item");
if(item.isDirty()) {
throw new IllegalArgumentException("Dirty item");
}
Storage<Clothing> laundry = laundryBin();
for(Storage<Clothing> place : storage) {
if(place.has(item) && place.remove(item)) {
if(laundry.canAdd(item.makeDirty()) && laundry.add(item))
return true;
else {
//Revert state
item.makeClean();
place.add(item);
throw new IllegalStateException("Must do laundry before this item can be used.");
}
}
}
return false;
}
private LaundryBasket laundryBin() {
for(Storage<Clothing> place : storage) {
if(place instanceof LaundryBasket) {
return (LaundryBasket) place;
}
}
return null;
}
/**
* Does laundry
*
* After this method succeeds everything in this wardrobe is clean and the laundry is empty
*/
public void doLaundry() {
LaundryBasket laundry = laundryBin();
laundry.washEverything().forEach(item -> add(item));
}
@Override
public Iterator<Clothing> iterator() {
return new NestedIterator<Clothing>(storage);
}
@Override
public boolean hasNext() {
return iter.hasNext();
}
@Override
public Storage<Clothing> next() {
return iter.next();
}
@Override
public void remove() {
iter.remove();
}
public void reset() {