Uppgift 5 - Sokoban
- Bakgrund
- Uppgiftsbeskrivning
- Deluppgiften - Ramen Sokoban
- Deluppgiften - Enumerationen GridType
- Deluppgiften - Spellogiken
- Deluppgiften - Klassen GamePanel
- Deluppgiften - Spelets status
- Deluppgiften - Lyssnandet av kommandona
- Deluppgiften - Förslag till bonusuppgifter
- Deluppgiften - Till slut
- Returnering
- Bedömning
Bakgrund
I den här uppgiften implementerar vi, med hjälp av Scalas Swing-klassbibliotek, ett spel som heter Sokoban (känt också med namnet Boxxle). I spelet rör man sig i en vinkelrät labyrint, sett från ett fågelperspektiv, och skuffar omkring lådor med avsikten att antingen placera alla lådor på sina rätta ställen eller (som är fallet i vårt spel) att ta spelaren till en målruta.
Det finns ganska många deluppgifter, men bortsett från deluppgift 3 är de ganska korta. Det lönar sig att läsa varje deluppgift i sin helhet före man börjar med implementationen. Kom också ihåg att kompilera och testa programmet efter att du tillsatt nya funktioner.
Uppgiftsbeskrivning
Efter den här uppgiften vet du hur man:
- Skapar ett simpelt program med fönster med hjälp av Scalas Swing-klassbibliotek.
- Hanterar olika Swing-komponenter.
- Skapar en enumerationsklass (Enumeration) och hanterar dess värden.
- Läser och behandlar information ur textfiler.
- Orsakar och hanterar undantag (Exception).
- Skapar en menypanel till ditt fönsterprogram och hur man tillägger funktioner till menyn.
Du kan ladda ner alla klasser som används i denna uppgift i det här paketet: luokat.zip.
1. Deluppgiften - Ramen Sokoban
Vi börjar med att definiera ett objekt med namnet Sokoban som ärver av SimpleSwingApplication. Det lättaste sättet att skapa ett simpelt program med ett grafiskt användargränssnitt är att ärva av SimpleSwingApplication-klassen. Genom att implementera top-metoden kan vi skapa ett huvudfönster åt programmet. top-metodens implementation skall vara sådan, att den returnerar ett MainFrame-objekt. Du kan se hur det görs i View.scala-filen som du använde i omgång 3.
I top-metoden skall du dessutom:
- Definiera en titel åt fönstret (title)
- Ge fönstret en rekommenderad storlek av (300, 375) pixlar.
- Befalla fönstret att öppna sig i mitten av rutan.
- Skapa ett BorderPanel-objekt, som kommer att kontrollera fönstrets innehåll (Det lönar sig att spara objektet i en variabel, så att du kan komma åt det senare.)
- I det här skedet ger vi ännu inget innehåll åt panelen, utan sätter enbart dess rekommenderade storlek till (300, 300) pixlar.
- Tillsätt det nyss skapade BorderPanel-objektet till fönstret.
Till slut skapar vi ännu en menypanel åt fönstret. Menypaneler som är konstruerade med Swing består av tre olika typers objekt. Den första, MenuBar, fungerar som en container åt alla menyer, som representeras av Menu-objekt. De här Menu-objekten kan innehålla flera kommandon, som representeras av MenuItem-objekt. Skapa alltså en menypanel åt ditt fönster, dit du tillsätter en meny som heter "Spelet", och in i menyn två kommandon som heter "Börja om" och "Sluta". I senare deluppgifter länkar vi funktioner till kommandona.
I det här skedet har du redan:
- Initialiserat klassen Sokoban, som ärver av SimpleSwingApplication.
- Skapat till klassen Sokoban en metod top, som returnerar ett MainFrame-objekt.
- Gett huvudfönstret ett namn, fått fönstret att öppnas i mitten, gett en rekommenderad storlek åt fönstret och gett fönstret ett tomt BorderPanel-objekt, som också har en rekommenderad storlek.
- Skapat en menypanel med en meny, som innehåller två kommandon.
Kom ihåg att försöka köra ditt program!
2. Deluppgiften - Enumerationen GridType
I den här deluppgiften bekantar vi oss med en speciell klass — enumerationen. En enumeration är en uppräcknad datatyp, en slags lista av alla element av någon specifik grupp. Man kan t.ex. skapa en enumeration som reflekterar veckodagarna genom att inkludera alla veckodagar (måndag, tisdag, onsdag, torsdag, fredag, lördag och söndag) som element samt möjligtvis metoder som man kan använda dem med. I en enumeration definierar man alltså på förhand alla möjliga instanser av klassen. Detta kan användas då man har en begränsad mängd möjliga alternativ och man vet alternativen på förhand.
Implementera en klass, GridType, som beskriver rutorna på spelplanet genom att ärva från Enumeration-klassen. Skriv din enumeration i en separat fil. Du får hjälp med att definiera typerna genom att läsa dokumentationen av Enumeration. Typerna som du behöver i den här uppgiften är:
- PLAYER: Representerar rutan som spelaren befinner sig på.
- WALL: Representerar en vägg, dvs. en ruta som spelaren inte kan flytta till och som inte kan flyttas.
- OBSTACLE: Representerar ett flyttbart hinder. Hindret kan röra på sig om spelaren flyttar sig till samma ruta som hindret.
- GOAL: Representerar målet dit spelaren skall flytta sig för att vinna spelet.
- EMPTY: Representerar en tom ruta, dit spelaren fritt kan röra sig.
I det här skedet skapar vi en hjälpmetod åt vår enumeration, som låter oss färgsätta spelplanet genom att associera varje värde i enumerationen med en färg. Skriv alltså en metod getColor, som tar emot ett värde av enumerationen GridType, och som returnerar ett Color-objekt. Det lättaste och bästa sättet att åstadkomma detta är genom att använda match-strukturen. Du kan själv välja färgerna för rutorna på spelplanet.
Efter den här deluppgiften har du:
- Skapat klassen GridType, som ärver av Enumeration.
- Definierat fem värden åt GridType.
- Skapat en hjälpmetod getColor, som returnerar en färg för varje värde av enumerationen.
3. Deluppgiften - Spellogiken
Nu skall vi bygga logikmaskineriet, som utgör grunden för spelet och som upprätthåller spelplanets tillstånd, dvs. väggarnas och de flyttbara hindrens plats samt spelarens och målets läge. För spellogiken skapar vi en egen klass, som inte är beroende av något visst användargränssnitt, utan som med ett passligt gränssnitt (dvs. med kompatibla metoder) skulle kunna användas med många olika grafiska- och icke-grafiska gränssnitt. I princip skulle du alltså kunna ta en spellogikklass av din vän och den skulle fungera ihop med din grafiska implementation. Det är vanligtvis (om det inte är frågan om ett väldigt simpelt program) en bra idé att separera programlogiken och användargränssnittet från varandra — då kan man göra ändringar i den ena ändan utan att behöva anpassa till dem i den andra ändan.
Spelets logiska kärna är en klass med namnet Game. Den ärver endast av Object (dvs. den är inte en subklass av någon av Swing bibliotekets klasser). Klassen representerar spelplanet som en tvådimensionell tabell vars element är instanser av den nyss skapade enumerationsklassen GridType.
Klassens konstruktor tar som parameter en fil (java.io.File) på basen av vilken den skapar spelplanet. Filen bör innehålla speciellt formulerade textrader, som läses in med hjälp av java.util.Scanner-klassen och som sedan tyds och översätts till ett spelplan. Ett exempel på ett simpelt spelplan kan du hitta i filen kentta1.txt. Filerna skall följa följande format:
- På första raden skall det finnas ett positivt heltal, som anger spelplanets bredd.
- På andra raden skall det finnas ett positivt heltal, som anger spelplanets höjd.
- Resten av raderna definierar spelplanets struktur på följande sätt:
- "@" - Spelarens position i början.
- "#" - En oflyttbar vägg.
- "$" - Ett flyttbart hinder.
- "G" - Målet.
- " " - En tom ruta (mellanslag).
- Märk, att man i filen inte nödvändigtvis behöver märka spelplanets yttre väggar, för att utrymmet som man kan röra sig i begränsas av spelplanets dimensioner. Om filen innehåller märken som inte kan tolkas, skall de tolkas som väggar.
Ladda ner som underlag åt din klass: Game.scala.

Märk, att det används ett konstigt try-catch -trick i början av klassens implementation. try-catch-finally är en struktur som används i Scala och som man kan hantera undantag (alltså instanser av klassen Exception, som du också kommer att få använda snart) med. try-blocket innehåller kod som kan kasta undantag, medan dessa möjliga undantag kan fångas och hanteras i catch-blocket.
I catch-blocket kan man hantera en eller flera olika typer av undantag. Märk dock att om den första och/eller enda undantagstypen är Exception, kommer alla undantag att fastna i blocket. catch-blockets interna struktur baserar sig på fall som är bestämda av case-satser (på samma sätt som i match-strukturen). Man kan utöver de två redan nämnda blocken använda ett finally-block, vilket exekveras oavsett om ett undantag uppstod eller inte. Du kan läsa en noggrannare förklaring med exempel här: http://www.tutorialspoint.com/scala/scala_exception_handling.htm.
När du skriver dina egna try-catch -block skall du komma ihåg att inkludera all sådan kod, som beror på resultatet av något som kan kasta ett undantag, i samma try-block så att det inte går som för hunden i bilden ovan. Det lönar sig inte alltid heller att försöka vara programmerarnas Ash Ketchum och försöka ta fast alla undantag. Det finns fall då programmets exekvering bör sluta vid ett undantag.
Och sedan flyttar vi oss till lite mera seriösa saker, dvs. implementationen av denna deluppgift.
Du bör implementera åtminstone följande metoder i klassen Game:
- private def initGame(f:File) : Unit
- I den här metoden använder vi Java-bibliotekets Scanner-klass för att läsa filen (till skillnad från implementationen av View-klassen i omgång 3). Börja med att skapa en instans, som du sedan använder för att läsa den relevanta informationen. Ge filen som parameter åt Scannerns konstruktor. Märk, att skapandet av ett Scanner-objekt kan kasta ett FileNotFoundException. Hantera undantaget genom att skriva ut ett felmeddelande till felströmmen (System.err).
- Sedan processeras spelplanets storlek. Läs följande två rader och tolka deras innehåll som heltal. Ifall någondera av numren (eller raderna) inte är heltal kommer NumberFormatException att kastas. Hantera undantaget genom att kasta det vidare via nyckelordet throw. Använd undantagsklassens konstruktor som tar emot parametrar och ge ett passligt meddelande åt undantaget. Ett annat möjligt undantag är NoSuchElementExeption, som kastas om någotdera värden för spelplanets storlek saknas. Hantera undantaget på samma sätt som det föregående.
- Efter att du har läst spelplanets storlek kan du skapa en tvådimensionell tabell som container för spelplanets status. Tabellen skall bestå av GridType-objekt och ha samma dimensioner som nyss lästes från filen.
- Sedan fyller vi tabellen med hjälp av translateToGridType-metoden. Läs högst så många märken som höjden och bredden av tabellen tillåter. Spara varje märke tolkat som ett GridType-objekt till den rätta positionen i tabellen.
- Kolla ännu till slut att spelplanet har exakt en spelare och ett mål.
- I samband med den här metodens implementation lönar det sig att fundera vilka värden det lönar sig att ha som fält i klassen, och vilka som borde ha offentliga getter-metoder.
- private def translateToGridType(s:String, x:Int, y:Int) : GridType.Value
- Metoden returnerar objekt av typ GridType på basen av den givna strängen s.
- Som parametrar tas också märkets position på planet så att spelarens och målets position kan sparas och deras mängder räknas.
- def getGridType(x:Int, y:Int) : GridType.Value
- Metoden returnerar ett GridType-objekt från den givna positionen.
- def hasGameFinished() : Boolean
- En enkel hjälpmetod som returnerar sant om spelet har slutat, dvs. om spelaren står på målet.
- private def canMoveTo(x:Int, y:Int) : Boolean
- Metoden kollar om spelaren kan flyttas till den valda positionen.
- Flyttandet av spelaren lyckas, om:
- Den valda rutan är inom spelområdet.
- Den valda rutan kan nås av spelaren genom att flytta ett steg vertikalt eller horisontalt — diagonalt kan man inte röra sig.
- Den valda rutan är tom...
- ...eller om det är ett hinder med en tom ruta bakom, vart hindret kan skuffas.
- Den valda rutan är målet..
- Man kan alltså inte flytta:
- Utanför spelområdet..
- Mera än en ruta vertikalt eller horisontalt, och inte alls diagonalt.
- In i väggar eller ovanpå hinder som inte samtidigt kan flyttas.
- def move(x:Int, y:Int) : Boolean
- Metoden kollar om spelaren kan flyttas till de önskade koordinaterna och om det går, så flyttar metoden spelaren dit. Metoden uppdaterar alltså spelplanet, spelarens position och flyttar möjligtvis också på hinder.
- def startOver() : Unit
- Börjar om spelet med spelplanet återställt till sitt ursprungliga läge.
- def startOver(f:File) : Unit
- Börjar om spelet med ett nytt spelplan, vilket definjeras i den som parameter givna filen.
- Denna metod behöver inte implementeras i det här skedet -- implementationen krävs endast i en av bonusuppgifterna.
När din Game-klass är klar kan du testa dess funktionalitet med följande testklass: SikobanTest.scala. Den använder rutnätet i ett simpelt märkesbaserat Sokobanspel, som kan spelas genom att alltid ge rutans koordinater som man vill att spelaren skall flytta sig till näst.
Det börjar bli dags att knyta spellogiken som du skapat ihop med det grafiska användargränssnittet, som du tidigare började på.
Efter den här deluppgiften har du:
- Skapat en Game-klass som fungerar som logikklass i spelet.
- Implementerat en struktur, som baserar sig på att läsa filer, och som kan skapa spelplan till Sokoban-spelet.
- Implementerat hanteringen av undantag som kan förekomma under skapandet av ett spelplan.
- Implementerat en mängd hjälpmetoder som man kan bl.a. få reda på om spelet har slutat och börja om spelet.
- Testat och försäkrat, att spellogiken du skapat fungerar korrekt.
4. Deluppgiften - Klassen GamePanel
Nu skapar vi en panel, som representerar ett spelplan av rutor. Klassen GamePanel skall ärva av GridPanel. På grund av att GridPanel-klassens konstruktor vill veta antalet på raderna och kolumnerna i panelen måste vi definiera en liknande konstruktor även åt vår GamePanel-klass. Utöver de nyss nämnda parametrarna tar vår konstruktor även en instans av den nyss skapade Game-klassen.
I det här skedet kan du snabbt återvända till Sokoban-klassen och integrera GamePanel-klassen som en del av användargränssnittet. Placera GamePanel i den tidigare skapade BorderPanel-containern. Lägg till ett try-catch-block, på samma sätt som i Game-klassen, där du försöker börja ett nytt spel med den givna kentta1.txt-filen. Här kan man bara reagera på undantag på ett sätt: Printa ut felets meddelande.
Vi använder Graphics2D-objektet, som introducerades i ett PBL-fall, till att rita rutnätet i GamePanel-klassen. Vi överlagrar den skyddade (protected, finns inte i Scala API:n pga. synligheten) metoden paintComponent, som tar som parameter Graphics2D-objektet. Metoden skall inte returnera något. Scalas Swing ser till att paintComponent-metoden kallas automatiskt alltid då dess förra invokering har avslutat.
Vår implementation av paintComponent-metoden fungerar så att varje ruta i Game-instansen ritas med en färg som bestäms av GridType-enumerationen. Sätt storleken av rutorna till (30, 30) pixlar och använd Graphics2D-objektet på samma sätt som i PBL-fallet.
Därefter implementerar vi interaktionen mellan spelaren och spelplanet. Du kan själv välja om du vill att man ska spela genom att flytta på musen eller genom att klicka på planet (Lyssnandet av tangentbordet lämnas till en bonusuppgift). Gör alltså så att GamePanel lyssnar på de valda funktionerna av musen genom att använda listenTo-metoden.
Definiera sedan reaktionerna till musens händelser (antingen MouseMoved eller MouseClicked) genom att modifiera GamePanel-klassens reactions-fält. Från händelsen som musen orsakat kan du få reda på var musens position var då händelsen genererades: Med hjälp av den kan du sedan be Game-instansen att flytta spelaren till den motsvarande rutan. Implementera lyssnandet av musen så att det inte längre går att röra spelaren efter att spelet har blivit slutfört.
Pröva också grafiskt att spelaren rör sig ordentligt.
Efter den här uppgiften har du:
- Skapat klassen GamePanel, som ärver av klassen GridPanel.
- Integrerat GamePanel- och Game-klasserna till en del av spelets implementation.
- Överlagrat paintComponent-metoden, som tar som parameter ett Graphics2D-objekt, som det sedan använder för att rita spelplanet som en tabell av rutor.
- Implementerat lyssnandet av användarens mus, så att man kan röra på spelaren med hjälp av musen.
- Testat för första gången ditt fungerande Sokoban spel grafiskt.
5. Deluppgiften - Spelets status
I den här deluppgiften implementerar vi en mekanism som visar information om spelets ställning. Skapa ett Label-objekt, som du modifierar i början av spelet så att den indikerar att spelet är igång. Kom ihåg att ansluta den som en del av användargränssnittet, under spelplanet.
Skapa också en offentlig metod, som tar som parameter en sträng, och som ändrar Labelns text till den givna strängen.
Nu kan du ändra GamePanel-klassens reaktion till att spelet slutar med att ändra Labelns text.
Efter den här uppgiften har du:
- Skapat en mekanism, som man kan använda för att informera spelaren om spelets status.
- Gjort så att texten under spelplanet ändras då spelet slutar.
6. Deluppgiften - Lyssnandet av kommandona
I den här deluppgiften skapar vi lyssnare åt kommandona i menyn som du implementerade i den första deluppgiften.
Sluta-kommandot
Modifiera skapandet av kommandot så att det tar ett nytt Action-objekt som parameter. Det här objektet tar som parameter namnet på kommandot. Bygg ett klassblock åt denna instans där du implementerar apply-metoden och sätter en genvägstangentkombination åt kommandot. Här är lite hjälpmedel som får dig att komma igång:
contents += new MenuItem(new Action("valikon nimi") { def apply() { /* apply metodin sisältö */ } accelerator = Some( /* näppäinkomento, johon valikko reagoi */ ) })Vi definierar alltså en ny, anonym klass som vi använder enbart här. Modifiera apply-metoden så att den slutar programmet. Då du väljer genvägstangentkombinationen får du använda eget omdöme, men ett vanligt val i det här fallet kunde vara t.ex. alt+F4. Du kommer att behöva Java Swing -klassbibliotekets KeyStroke-klass för att implementera genvägarna. Det lättaste sättet är att använda konstruktorn som tar en sträng som parameter.
Börja om -kommandot
Att börja om spelet implementeras på samma sätt som i föregående fall, men förstås med en annan implementation av apply-metoden och med en annan genvägstangentkombination. apply-metoden borde uppdatera texten i fönstrets undre del så att den visar att spelet har börjats om och också se till att spelplanet återställs till sitt börjantillstånd. Använd här startOver-metoden som du implementerade i spellogikklassen Game.
Du kan igen själv välja vad för tangentkombination du vill använda som genväg, men ett typiskt val är F2-knappen.
Efter den här uppgiften har du:
- Gjort ett kommando som avslutar spelet och kan användas via en genvägstangentkombination.
- Gjort ett kommando som börjar om spelet och kan användas via en genvägstangentkombination.
7. Deluppgiften - Förslag till bonusuppgifter
I det här skedet borde du ha ett fungerande Sokoban-spel som fungerar som sådant.. Den är dock kanske inte så mångsidig eller snygg. Du kan utveckla ditt spel vidare med några bonusuppgifter (krävs inte för fulla poäng), varav några introduceras i denna deluppgift. Du kan också alltid utveckla dina egna idéer.
Snyggare grafik
I luokat.zip-paketet som du laddade ner fanns det några bilder, som har tillsatts med tanke på spelets utseende. Du kan använda dessa bilder, eller skapa dina egna, för att få mera liv i spelet. Du kan använda bilderna för att representera spelaren (svinet) och hinder (tjocka svinet).


Du kan använda samma Graphics2D-objekt som du använde i paintComponent-metoden för att rita bilder på rutan.
Byte av spelplan
Lägg till i menyn ett nytt kommando som byter filen för spelplanet. Implementera en lyssnare åt kommandot på samma sätt som i 6. deluppgiften. Du skall implementera en filväljare (FileChooser), som accepterar textfiler. Du kan begränsa filtyperna med hjälp av FileFilter-klassen.
Skapa en ny metod i Game-klassen, som överlagrar startOver-metoden genom att ta som parameter ett File-objekt, som sedan används för att börja ett nytt spel.
Generering av spelplan (Svår: värd 6p)
Skapa en mekanism till spelet där det genereras ett nytt spelplan (som går att vinna) för varje spelomgång.
8. Deluppgiften - Till slut
Testa ännu alla klasser du implementerat och se till att allt fungerar så som du vill. Kommentera senast i det här skedet de delar av din kod som kan vara svårförståeliga. Packa hela lösningen så att assistenten inte behöver ladda ner något extra för att få programmet att fungera. Inkludera alltså i paketet som du returnerar åtminstone följande filer: Game.scala, GamePanel.scala, GridType.scala, SikobanTest.scala, Sokoban.scala, kentta1.txt och eventuella bildfiler som du kanske använt. Inkludera ännu readme.txt, vars underlag du kan kopiera nedan:
# ME-2120 Höst 2013 # # Omgång 5: Sokoban Studentnumret: Tid i timmar som har använts till de obligatoriska uppgifterna (uppskattning): Tid i timmar som har använts till bonusuppgifterna (uppskattning): # Märk de delar av omgången som du har implementerat med ordet 'gjort' # Du kan också märka de delar som du har börjat på, men inte slutfört med ordet 'försökt' # Du kan också nämna varför du inte lyckades med att slutföra dessa delar. D1 Ramen Sokoban: inte gjort Fönstret skapas och den har rätt storlek: inte gjort Fönstret öppnas i mitten av rutan: inte gjort Fönstret har en menyrad: inte gjort D2 Enumerationen GridType: inte gjort Enumerationen innehåller de krävda typerna: inte gjort Enumerationen har den krävda hjälpmetoden: inte gjort D3 Spellogiken: inte gjort Spelplanet tyds rätt: inte gjort Spelaren rör sig enligt reglerna på planet: inte gjort Spelet kan ta slut: inte gjort D4 Klassen GamePanel: inte gjort Spelplansfilen tyds grafiskt rätt: inte gjort Spelet reagerar till användarens kommandon: inte gjort D5 Spelets status: inte gjort Spelets status uppdateras vid behov: inte gjort D6 Lyssnandet av menykommandon: inte gjort Kommandot som avslutar spelet fungerar: inte gjort Kommandot som börjar om spelet fungerar: inte gjort # Gjorde du (av de givna, eller egna) bonusuppgifter? (Berätta vad du gjorde och eventuellt hur man använder dem ifall funktionaliteten skiljer sig avsevärt från det som tidigare implementerats.) # Blev det kvar fel, eller ställen som inte fungerar korrekt i ditt program? (Lista alla problem som du känner till. Nämn, om möjligt, din egen uppskattning om deras orsaker och vad du har försökt för att fixa dem.) # Fria kommentarer gällande den här uppgiften? (Du kan fritt kommentera uppgiften, men kom ihåg att ge den egentliga feedbacken via feedbackblanketten.)Fyll i readme.txt och inkludera den i paketet som du returnerar. Namnge paketet enligt formen studentnr_kierros5.zip. (Och inga skämtare den här gången, tack — sätt ditt studentnummer istället för studentnr :).
Returnering
Returnera uppgiften till Rubyric-systemet senast lördagen den 23. november kl. 18.00. Om du försenar dig med returneringen så bestraffas du med 2 poäng för varje 24-timmars period som har hunnit börja efter deadlinen. Om du t.ex. returnerar ditt arbete på söndagen kl. 18.01, så bestraffas du med 2*2=4 poäng. Returnering av paket med fel filformat (de ända tillåtna formaten är .jar och .zip) bestraffas med ett poäng. Om en .scala-fil saknas bestraffas du med ett poäng, och om readme.txt-filen saknas bestraffas du också med ett poäng.
Efter returneringen skall du ännu fylla i feedbackblanketten här: http://www.cs.hut.fi/cgi-bin/teekysely.pl?action=showform&id=studio1-scala5-2013&lang=FIN
Bedömning
Programmeringsuppgifterna bedöms enligt de nedan givna kriterierna. Uppgiftens maximipoäng är 60 poäng och det krävs åtminstone 30 poäng för att få godkänt.
Om den returnerade uppgiften är tydligt halvfärdig och den får under den godkända mängden poäng, kan den returneras till studenten för förbättring (som en sk. bumerang). Uppgiften godkänns först då när den har blivit tillräckligt omfattande och djupgående. En bumerang kan godkännas högst med minimipoängmängden 30.
Det lönar sig att lägga märke till kodens indentering och klarhet; om assistenten kan läsa din tankegång tydligt från koden, hjälper det betydligt med uppskattningen av kodens funktionalitet och felens allvarlighet. Du bör också försäkra dig om att den returnerade koden verkligen kompilerar och att du returnerar den senaste versionen av din lösning.
- Uppfyllandet av uppgiftsbeskrivningens krav och korrektheten - 40 poäng
- Om det saknas funktionalitet som beskrivs i uppgiftsbeskrivningen, kan det inte ges fulla poäng. Grovt uppskattat, kunde man säga att för att få 50% av poängen i den här delen skall man ha implementerat hälften av den krävda funktionaliteten, och en 90% korrekt gjord upggift är värd sådär 36 poäng.
- Uppgiftsbeskrivningen behöver man ändå inte följa till pricken - det är tillåtet att avvika från anvisningarna om man lyckas motivera det.
- För klarhetens skull har korrektheten inkluderats i den här delen. Det räcker inte enbart med att man har implementerat funktionaliteten, utan den måste också fungera korrekt.
- Förnuftighet och klarhet - 10 poäng
- Det går ofta att implementera en sak på flera olika sätt, och ett sätt att göra någonting är inte nödvändigtvis bättre än något annat. Var gärna kreativa, men försök också sträva efter en slags elegans - gör väl övervägda och lättförståeliga lösningar och undvik onödiga rader kod om de inte gör koden klarare.
- Stil - 5 poäng
- Hit hör bl.a. systematisk och korrekt indentering, ändamålsenligt namngivande av egna variabler och metoder samt användning av kommentarer på sådana ställen där det gör det lättare att förstå koden.
- Allmänt intryck - 5 poäng
- Utöver de tidigare punkterna kan man få ännu upp till fem extra poäng. Fulla poäng i den här kategorin kan man få om arbetet är perfekt, eller nästan perfekt. Tre till fyra poäng får man om arbetet är "helt ok", dvs. det finns några enstaka brister. Ett till två poäng kräver redan en hel del misstag och noll poäng ges om det finns mycket stora brister i arbetets innehåll eller form.
Det är dessutom möjligt att för bonuspoäng för prestationer som överskrider uppgiftsbeskrivningen.