Commit 99c4f724 authored by Yngve Sekse Kristiansen's avatar Yngve Sekse Kristiansen
Browse files

New files

parent 84e1c90a
File added
# Authors
* Eivind Jahren, UiB (original cellular automaton code)
* Anya Helene Bagge, UiB (graphics, maintenance)
* Lars Jaffke, UiB (Langton's Ant)
* *you*, UiB (the rest!)
\ No newline at end of file
# Lab 3: Langton's Ant & Git Merge Requests
## Læringsmål
* Kunne håndtere *rejected* feil fra *Git push på* egenhånd.
* Kjenne til vanlig Git arbeidsflyt og kunne bruke *merge request*.
* Få mer erfaring med grid-datastruktur og vanlig programmering
*(Resten av oppgaven er på engelsk. Siden det er en del bakgrunnstekst også, er seksjonene med ting du skal gjøre merket med “(todo!)”.)*
## Part I: Git
(Do Lab 2 first!)
[Go to Part I](
## Part II: Langton's Ant
(Do Lab 2 and Part I first!)
[Go to Part II](
# Lab 3 Part II
[Lab Overview](
# Part I: Git
[Go to Part I](
# Part II: Langton's Ant
**You must complete [Part I]( before doing this part!**
This week's Lab-exercise deals with another type of cellular automata, [Langton's Ant]('s_ant). This automaton was conceptualized in 1986 by Chris Langton and proven to be [Turing-complete]( 2000. In other words, any algorithm can be simulated by Langton's Ant.
The idea of the automaton is simple: An ant moves across the grid formed by the cells of the automaton and each cell has one of *n* states (or colors). The automaton is equipped with a set of rules, telling for each color *i* = 1,...,*n* whether the ant should turn left or right when stepping on a cell with color *i*. After the ant left the cell, the color is shifted in a cyclic fashion, i.e. a cell with color *i* is set to color (*i*+1) mod *n*.
The standard Langton's Ant has two states, say black and white and the following rules:
- White: Turn right.
- Black: Turn left.
An example execution of a few steps looks like this:
<p><a href=""><img src="" alt="LangtonsAntAnimated.gif" height="353" width="353"></a><br>By <a href="//" title="User:Krwawobrody">Krwawobrody</a> - <span class="int-own-work" lang="en">Own work</span>, Public Domain, <a href="">Link</a></p>
The goal of this exercise is to implement a generalisation of Langton's Ant to an (arbitrary) number of colors. In particular, a rule for a Langton's Ant can be encoded as a string of L's and R's in the following way. As an example, the rule 'LLRRRL' means that the ant should move left when encountering color 1, 2 or 6 and right when encountering color 3, 4 or 5.
## 2.0
If you have successfully completed [Part I](, you should find your Lab 3 project at
* Clone it / import it into Eclipse (or your preferred IDE)
* See previous labs or [Hvordan hente og levere oppgaver]( for help
## 2.1 Overview of the code
The code in this exercise is based on the code of Lab 2 with the following changes/additions.
- *Cellstate* is not an `enum` anymore but a class with a single integer value, indicating the color of the cell. The constants ALIVE, DYING and DEAD are there to ensure that the code of the old exercises still works.
- *Direction* is an `enum` holding the values `NORTH`, `EAST`, `SOUTH`, `WEST` which is used to determine the direction the ant is facing in the grid.
- *Ant* is a class that captures the position in integer (x, y) coordinates as well as the direction it is facing.
- *LangtonsAnt* is a stub of a class that contains most of the logic. As before, it implements the *ICellAutomaton* interface and all logic should be implemented in the corresponding methods.
## 2.2 Complete the ant *(todo!)*
In the *Ant* class, the implementations of the *turnLeft()* and *turnRight()* methods are missing. This should be done as follows. Consider *turnLeft()* (*turnRight()* works symmetrically) and suppose the ant is in position (x, y) and faces NORTH. Then a left turn means
- Decreasing the ant's *x*-value by 1.
- Making the ant face *WEST*.
Analogous rules have to be devised for the other cases and implemented accordingly.
## 2.3 Complete the implementation of LangtonsAnt
Throughout the following, when referring to *color i*, we mean a *CellState* object with value i.
### 2.3.1 Check the input rule
The *LangtonsAnt* class contains a method to verify whether a given input rule is valid or not. A rule (given as a string) is invalid if one of the following holds.
- The length of the string exceeds the value of the constant `MAX_RULE_LENGTH`.
- The string contains characters other than 'L' and 'R'.
### 2.3.2 Implement *initializeGeneration()* *(todo!)*
Initialize the first generation of the automaton by doing the following.
- Give all fields color 0.
- Initialize an ant that sits in the middle of the grid, facing NORTH.
- Initialize the *seenState* field to color 0.
### 2.3.3 Implement *stepAutomaton()* *(todo!)*
In the automaton's step method, the ant moves according to the previously read color (stored in the field *seenState*) and shifts the color of the field it is leaving.
Note that a new *Ant* object *nextAnt* has been initialized and that everything that concerns the future position of the ant in the grid have to be executed on that object.
The following things need to be done.
- Fill in the calls of *turnLeft()* and *turnRight()* in the corresponding cases.
- Check if the calculated position is a valid one (i.e. the ant is not thrown off the grid) and if not, reset the position in a way of your choosing.
- Update the color (/state) of the field the ant is *leaving* (note that this concerns the value of the *ant* field, not the *nextAnt* variable). The next color should be `(seenState.getValue() + 1) % rule.length`
- Update the *seenState* field, i.e. get the color of the next cell the ant is moving to and store it.
- Move the ant to the new position in the grid, using the *nextAnt* field.
- Update the *ant* field variable by setting it to *nextAnt*
### 2.3.4 Have fun! *(todo!)*
Replace the *ICellAutomaton* object *ca* in the *Main* with a *LangtonsAnt* object with a rule of your choosing and watch the ant draw patterns.
* If you'd like it do run faster, you can modify `inf101.v19.cell.gui.CellAutomataGUI`. Line 72 makes the automaton do one step every 1/20 second: `timer = new javax.swing.Timer(1000/20, this);` – you can set the timer delay to, e.g., `1000/60`, or `10` (ten milliseconds).
## 2.4 Have more fun! *(todo!)*
There's a lot of other things you can do with cellular automata, including further extensions of Langton's Ant.
* [Turmites]( extend the ant with state, and should be possible to implement with a modification to the `Ant` class.
* There's also [Langton's Loops](
* [Paterson's worms]( is another automaton, but uses a triangular grid (doesn't work so well with our framework).
* There are standard rules for [Elementry cellular automata](, including the one seen on the sea snail shell in Lab 2. See the [WikiPedia page on Cellular Automata]( for more inspiration.
* You can also try to simulate simple physical phenomena, such as fire spreading or snow fall. For example:
* A snow flake (an `ALIVE` cell) will move one step down per iteration if the cell below is free (e.g., if the cell below is `DEAD`, it becomes `ALIVE` and the current cell becomes `DEAD`). For this case, the order in which you process the grid in `stepAutomaton()` becomes important (you should start at the bottom and go up).
* A fire would need multiple states; e.g., `WATER`, `WOOD`, `IGNITING`, `BURNING`, `BURNT` and then rules to move between them.
* Even simple liquid flow can be simulated with automata or something close to it (e.g., use the CellState with integers to keep track of the amount of water in each square, and build rules around pressure and momentum) – though it can require a bit of physics and math.
## Sample output
Multi-color Langton's Ant pattern `"RRLLLRLLLRRR"`. [See more examples at WikiPedia.]('s_ant#Extension_to_multiple_ants)
<img src="img/RRLLLRLLLRRR-1.png" width="300" alt="Ant Screenshot title="After a few minutes" />
<img src="img/RRLLLRLLLRRR-2.png" width="300" alt="Ant Screenshot title="After around ~2 hours" />
<img src="img/RRLLLRLLLRRR-3.png" width="300" alt="Ant Screenshot title="Running overnight / ~10 hours" />
# Lab 3 Part I
[Lab Overview](
## Part I: Git
### 1.0. More Git Background
Git is a [distributed version control system](
* A [version control system]( stores all previously committed changes to a project. (This lets you undo mistakes, maintain multiple versions of the same piece of software at the same time, and easily track the history of changes when you're trying to find out why something went wrong.)
* Modern version control systems are collaborative, and let multiple people work independently on their own copy of the files, before the changes are integrated / synchronised. Since all the changes are recorded in the history it's easy for the version control system to see what each developer has done, and merge changes from multiple developers more or less automatically. Typically, this synchronisation happens when a developer *pulls* from or *pushes* to the server (sometimes called *update* and *check in*). (In [really]( [old]( systems, there was little or no support for multiple developers, and you might be required to *lock* the files you were editing, so that no one else could modify them at the same time. If you then went on vacation without unlocking, your co-workers would be very upset.)
* A *distributed* version control system like Git also supports decentralised collaboration: there isn't really a central server that everyone synchronises with – instead each developer has a complete *clone* of the entire project, and developers can exchange their commits peer-to-peer, without any server. For practical reasons (e.g., backups and easier interaction), we typically want a server anyway and this is the role of our GitLab-installation at Your local clone of your INF101 labs are just as good and complete as those on the server, but the server also does backups, lets your teachers look at and help you with your code, has a nice web interface and does automatic integration testing whenever you push to it.
Collaboration and distribution has two consequences that we will deal with now:
* Someone else can have made changes while you were working, and these changes must be integrated with yours. (Step 1.1 below, and [Lab 1, oppg. 7](
* We change change what we consider to be the “server” (upstream) or even have multiple ones. (Step 1.3 below.)
### 1.1. Always *pull* before *push* *(todo!)*
As seen in [Lab 1, oppg 4]( you should check the response you get whenever you *push*, because it can actually go wrong.
If you see the message *rejected – non-fast-forward*, someone else has made and pushed changes before you. This could be someone yourself, if you're using multiple computers or if you've made multiple copies of your Git files on your computer – but in real life it's often another developer on the same project (less likely for INF101 students, but sometimes Anya or someone else will push to your repository on
So, if you go and make some changes to your *Lab 2* solution, then select *Team → Commit...*, fill in the commit form or staging view form and hit *Commit and Push*...
![](img/git-staging.png) should see a message like this (but with a different repository name, of course):
* **Go and do this now**, with your `inf101.v19.lab2` project. If you don't have any changes to commit or push, just do a tiny change to any of your java files (insert an extra line, for example).
* You get the *rejected* message because the server's copy has changes that you haven't seen (mainly just a few corrections in the exercise text). It's your responsibility to integrate those changes from the server into your local repository (this is called *merging*), before you can push the combined changes back to the server.
* First, right-click on your Lab 2 project, and select *Team → Pull* from the menu. You should get an OK message similar to the one below (possibly after entering your password again):
* The list will include all the changes that have been integrated into your project. In your case it should be the top three commits from the [Lab 2 commits log](
* If the message says *Failed*, some of our changes are in conflict with some of your changes (unlikely unless you've changed the text of the file). You'll have to resolve this as seen in [Lab 1](
If you're working on the command line, the message will look like this:
! [rejected] master -> master (fetch first)
error: failed to push some refs to ''
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
and it's solved by running `git pull`, and then `git push` again.
**General rule:** when you're working with Git, you will normally have to do *pull* before you can do *push*. So, your sequence of steps should be:
* *Commit* your work (remember to include all relevant files and a nice commit message).
* *Pull* to see if anyone else has made any changes.
* *Push* to send the changes to the server.
If you're mostly working alone (as in INF101), you can normally skip the *pull* step, and then just do it if your push is rejected (this will fix 95% of push problems; ask for help with the rest).
### 1.2. Checking status of edits and commits
If you're wondering if all your changes have been committed and pushed, you can see this in the Eclipse package explorer.
Here, “↑1” indicates that you have made one commit that hasn't been pushed yet. You may also have “↓N” (much less likely), which means that you have fetched N commits from the server, but those haven't been merged yet.
A “>” indicates that a file has been edited but not committed yet (or that a folder (transitively) contains such a file):
If you have added a file (made a new class, for instance), it will have a small question mark in the corner of its icon:
You should add such files to Git, either with *Team → Add to index...* or in the commit dialog – except if it's an irrelevant file for some reason (not really part of the project, or it's generated automatically), in which case you should do *Team → Ignore* on it. As you see on ``, files that are in Git have a little yellow cylinder in the corner of their icon.
On the command line, you can get the same information with `git status`.
### 1.3. Merge Requests and Forks
#### Background
A very common situation in [online collaborative software development]( is doing minor (or even major) work on a project that isn't owned by you and where you might not be (or want to be) an official part of the development team. For instance, if you're contributing a bugfix or have implemented an extra feature for an open source project (this can be both a good way to learn, and a good way to develop your CV). You might also be working on a project that has a slightly more formalised workflow, where changes should be reviewed by the other developers before they are accepted into the project. This is the sort of situation distributed version control systems like Git are made for – Git in particular was created for the development of the [Linux kernel](, which has more than 12000 contributors and a somewhat decentralised [development process](
The [workflow]( is as follows (you don't have to do any of this now; most of the relevant bits have been done automatically):
* You want to contribute to a project, and create a fork of the project ([]( supports this for projects hosted here; but all the major open hosting services like [](, []( and []( all support forking). This is your own personal copy of the project which you can develop and edit without interfering with other developers. This is also how all your INF101 exercises are set up – they are forks of the main exercise repository, so students don't interfere with each other.
* You continue development until you have changes you want integrated into the *upstream* (the project you forked from).
* You use the hosting service ([GitLab at]( in our case) to send a *merge request* (also called a *pull request*) to the upstream. The system will look at all changes you have done, and all changes done to the main repository (the “upstream”), and try and see if they still fit together (if they don't, you may have to manually merge them, otherwise Git will merge automatically). The merge request is handled a bit like a bug report; the upstream developers will be alerted and can discuss the requests in the comment section – perhaps asking you questions or tell you to fix stuff they don't like.
* If your merge request is accepted, the upstream developers will *merge* it into the upstream project, and your changes will become part of the upstream repository (i.e., the commits you made to your repository will also be part of the history of the upstream repository).
* You might now delete your fork, if you don't intend to do further development, or you may want to merge the upstream (if they had other changes) back into your own fork and continue working on it.
#### Our Situation
[Part II]( of this exercise (the Java programming bits) is based on the same cellular automata framework used in [Lab 2](, with a few changes. Also, you will (hopefully) have done some changes to your own fork of Lab 2, such as fixing the `MyGrid` implementation. If you haven't, you should do so now – otherwise you won't be able to finish Lab 3.
This situation is similar to the fork/merge workflow commonly used with Git: there's an upstream (the originally handed-out Lab 2 code from `/inf101.v19.oppgaver/inf101.v19.lab2`), which has been changed (into the Lab 3 hand-out from `/inf101.v19.oppgaver/inf101.v19.lab3`) – while you have been modifying your fork of Lab 2 (fixing `MyGrid` and implementing Brian's Brain). Before you can start implementing Lab 3, you must *merge* your Lab 2 solution (`/USERNAME/inf101.v19.lab2`) into your Lab 3 code (`/USERNAME/inf101.v19.lab3`) – both of them are based on the same original upstream repository, so you can handle this by sending yourself a *merge request*. (It's not exactly the same situation as above, since you're not actually merging with the upstream, but only with another fork of the upstream – but the technique is the same, and very useful in a collaborative setting.)
#### Your Task *(todo!)*
(See below for step-by-step screenshots.)
* First – finish [Lab 2]( You'll need at least a correctly working `MyGrid`.
* Commit and push your Lab 2 solution (see also step 1.2 of this Lab).
* Go to the project page for *Lab 2* at `` (*not* the Lab 3 page). Check that your commits appear on the *Commits* sub-page.
* On the overview page, click *(+) Create New... → New merge request*
* Select *USERNAME/inf101.v19.lab2* and *master* as the source branch and *USERNAME/inf101.v19.lab3* and *master* as the target branch, and click *Compare branches and continue*.
* Pick a title for your merge request (or use the default); you can also write a description if you'd like (you should do this in real life, but in this case you're the only one who would read it). Click *Submit merge request*.
* On the next page, you can review the merge request. If everything is OK, there will be a green checkmark, and a *Merge* button. Press it to complete the merge (or explore the interface a bit further, and have a little online discussion with yourself first).
* If everything went well, you are now done with Part I, and can proceed to [Part II – Langton's Ant](
* You might also get a merge conflict. This should only happen if you've modified the exercise text, `` or something else we didn't expect you to change. If so, you can read the [GitLab documentation on merge requests]( and [how to resolve conflicts in the GitLab UI]( **If you get stuck, ask for help!**
* If everything goes horribly wrong, you can also manually copy your Lab 2 files to Lab 3 (in particular, ``).
## Screenshots
### Step 1: Start
![Screenshot: Starting a new Merge Request](img/1-new-merge-request.png)
### Step 2: Pick projects and branches
![Screenshot: Creating the Merge Request](img/2-creating-new-merge-request.png)
### Step 3: Submit
![Screenshot: Submitting the Merge Request](img/3-submit-merge-request.png)
### Step 4: Merge
![Screenshot: Merging the Merge Request](img/4-merge-merge-request.png)
### Step 5 (done!)
![Screenshot: All done](img/5-done.png)
## Part II: Langton's Ant
[Go to Part II](
MIT License
Copyright (c) 2015-2018 University of Bergen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Lab 2: Celle-automater
# INF101
## Læringsmål
Dette prosjektet inneholder [Labøvelse 3]( Du kan også [lese oppgaven online]( (kan evt. ha små oppdateringer i oppgaveteksten som ikke er med i din private kopi).
* Bli kjent med `interface` (grensesnitt)
* Lage klasser som implementerer interface / grensesnitt
* Lage egne datastrukturer / ADTer
## Om oppgaven
Lab-oppgavene denne uken handler om cellemaskiner.
En cellemaskin består av et sett med celler, og hver celle har en tilstand. I
begynnelsen har hver celle en starttilstand, dette kaller vi generasjon 0. Så
lager maskinen en ny generasjon med celletilstander ifølge en fast regel. Når
alle cellene har fått en ny tilstand (generasjon 1) så fortsetter maskinen å
lage enda en ny generasjon ved igjen å følge de faste reglene. Slik fortsetter
maskinen så lenge vi ønsker.
Disse maskinene har blitt studert siden 1940-tallet, men ble berømt da
[Conway's Game of Life](,
den mest kjente cellemaskinen, ble oppfunnet på 1970-tallet. Kanskje det mest
interessante med cellemaskiner er hvor komplekse strukterer som kan oppstå ved
hjelp av veldig enkle regler.
Vi kjører cellemaskiner på et todimensjonalt brett som viser én og én generasjon fortløpende over tid.
Under vises cellemaskinen kalt Conway's Game Of Life:
![game-of-life]( "Game Of Life")
I game of life har cellene to mulige tilstander: levende eller død. En levende
celle dør hvis den har færre enn 2 naboer eller mer enn 3 naboer. Derimot hvis
en død celle har nøyaktig 2 naboer så blir cellen levende. Ideen bak spillet er
å modelere overpopulasjon, underpopulasjon og reprodusering. Både vertikale,
horisontale og diagonale celler er naboer, som vist i bildet under:
![moore-nabolag]( "Moore nabolag")
Over er alle røde celler nabo til den blå cellen.
En av pioneerene for denne typen cellemaskiner var [John von
Neumann]( Han var interessert i
om ikke disse maskinene kunne produsere ikke bare seg selv (reprodusering), men
en hvilken som helst annen konstruksjon en skulle trenge. Under er bilde av en
von Neumann cellemaskin som gjør akkurat det. Den bruker hele 29 forskjellige
celletilstander og nok regler til å fylle et par a4 sider.
![von-neumann-maskin]( "Von Neumann maskin")
# Steg 0: Gjør ferdig forrige oppgave
Laboppgavene bør gjøres i rekkefølge, som om du ikke er helt ferdig med den forrige, gjør den ferdig først.
# Steg 1: Hent Oppgaven fra git
Som før skal du ha et repository for oppgaven på Se [Hvordan hente og levere oppgaver]( for mer informasjon og instrukser. Oppgaven skal dere kunne finne i
repositoriet med den følgende urien:<brukernavn>/inf101.v19.lab2.git
Hvor `<brukernavn>` skal byttes ut med brukernavnet ditt på
Merk at det er nødvendig å bruke https (ssh vil ikke fungere).
Som vist i forelesningene, for å få dette prosjektet inn i eclipse, velg
`import` fra fil menyen. Velg så `Git` -> `Projects from git` -> `Clone URI`.
Fyll in URIen som du finner over. (Eclipse fyller så ut *host* og *repository
path* med `` og `/<brukernavn>/inf101.v19.lab2.git`.
Skriv inn ditt brukernavnet og GitLab-passord, og trykk `next`. Nå velger du hvilken
branch du skal ha (`master` og `clear`). Trykk på `next`. Neste ting er å velge en mappe å
putte repositoriet under `Directory`, og la resten være slik det er (initial
branch: `master`, Remote name: `origin`), `next` -> `import existing project`
-> `next` -> `finish`.
Nå skal du ha et prosjekt i eclipse som heter `inf101.v19.lab2`.
Merk at innlevering skal skje til dette repositoriet. For å levere bruker du
`Team` -> `Push` (husk at du må bruke `commit` først), eventuelt `Team` ->
`Commit` og så `Commit and Push`. *Husk* at hvis du har opprettet nye
klasser/filer, må du krysse av for disse i commit-dialogen for at de skal bli
med i opplastingen. *Sjekk kvitteringssiden som kommer opp når du pusher, i
tilfelle det skjer feil!*
Vi anbefaler at du gjør commit hver dag, eller hver gang du er ferdig med en
større endring. Da går det alltid an å lete seg tilbake i historien til en
tidligere versjon hvis du har havnet på villspor (commit-dialogen inneholder
også en praktisk "Commit and push"-knapp, som pusher endringene til GitLab med
en gang).
# Oversikt over koden
Koden kommer med [Game of Life](
allerede. Senere skal vi utvide programmet slik at det også kan vise en annen
cellemaskin kalt [Brian's Brain](
Et eksempel på kjøringen av denne er under.
![brians-brain]( "Brian's Brain")
I denne maskinen har hver celle 3 mulige tilstand, levende, død, og døende.
Livet går sin vanlige gang: levende blir døende, døende blir døde, og hvis det
er nøyaktig to levende naboer lager de en liten krabat, men da må det være
plass (ingen levende eller døende i midten).
Vi skal først gjøre ferdig noen av klassene, slik at vi kan se at Game of Life
fungerer, og så skal vi implementere Brian's Brain.
# Pakker og Package Explorer
Større Java-prosjekter er organisert i *packages*. Det gjelder også koden dere
får utlevert. Du finner pakkene i `src`-mappen i Eclipse-prosjektet. Du er
denne gangen bare interessert i det som ligger i `inf101.v19.cell` og
![Package Explorer](img/packages.png)
Du kan, om du vil, få Eclipse sin Package Explorer til å vise oversikten
hierarkisk og ikke flat (som over). Anya synes det er mest praktisk, særlig for
store prosjekter. Du kan justere dette i den lille menyen du finner under
trekanten øverst i Package Explorer:
![Package Explorer View Menu](img/viewmenu.png)
Velg *Package Presentation → Hierarchical*.
# Steg 2: IGrid og MyGrid
Vi trenger å kunne holde rede på en todimensjonal brett av celletilstander – et
*grid*. Dette kunne vi gjort med en array, eller evt en array av arrays, men
det er best å lage en egen klasse for dette, slik at vi er uavhengige av
hvordan dataene er lagret – den delen av programmet som håndterer
celleautomater skal ikke trenge å være avhengig av hvilken løsning vi har valgt
for å lagre todimensjonale data:
* Objekter som bruker grid må vite *hva* metodene gjør og hvordan de brukes, men trenger ikke bry seg om *hvordan* de er implementert.
* Bare implementasjonen av grid trenger å vite hvordan dataene er lagret og hvordan de hentes/oppdateres.
## Grensesnitt / Interface
I likhet med en klasse, er et *interface* eller *grensesnitt* noe som beskriver hvilke metoder et objekt skal ha – men, i motsetning til klasser, sier grensesnittet ikke noe om hvordan metodene er implementert eller hvilke feltvariabler et objekt skal ha. Dvs.:
* **Objekter:** Et objekt har tilstand og oppførsel, og består av feltvariabler (data) og et sett med metoder (kode) som implementerer oppførsel og lar oss observere og manipulere tilstanden.
* **Implementasjon av objekter:** En klasse definerer datastruktur og metoder til objekter – det er en oppskrift på hvordan man lager objekter. Hvert objekt er en *instans* av en klasse, og har feltvariablene og metodene som er beskrevet i klassen. Feltvariablene og koden er *innkapslet*, slik at andre deler av programmet vårt ikke trenger (eller har lov til) å forholde seg til detaljene. Dvs. at vi kan samle all kode som er avhengig av feltvariablene i samme `.java`-fil.
* **Bruk av objekter:** Både klasser og grensesnitt er *typer* og beskriver hvordan objekter kan brukes – hvilke variabler de kan lagres i ("alle objekter av klassen Duck kan lagres i en variabel av typen Duck"), og hvilke metoder man kan kalle ("alle Duck-objekter har en quack()-metode, så hvis jeg har et Duck-objekt, kan jeg kalle quack()-metoden").
* Hvert objekt kan passe sammen med flere typer – f.eks. kan alle objekter lagres i en variabel av typen `Object`.
* Vi kan si at en klasse C implementerer et grensesnitt I (`C implements I`) – da krever vi at alle metodene i grensesnittet er implementert i klassen, og vi sier at det er OK å putte et `C`-objekt i en `I`-variabel: `I var = new C();`.
* Hvis en variabel har en grensesnitt type (som `I var` over), så kan du kalle/bruke bare metodene som er definert i grensesnittet.
* Du kan tenke på et grensesnitt som en spesifikasjon eller sett med krav til hva en klasse må inneholde – og som en oppskrift på hvordan du kan bruke objektene etterpå. Ofte vil vi sørge for at grensesnittet inneholder all nødvendig dokumentasjon for bruk..
Vi skal jobbe ganske mye med grensesnitt i løpet av INF101 – dette er bare en første introduksjon. Det er særlig to typer bruk vi støter på til å begynne med:
* Dere får utdelt et grensesnitt i en oppgave, og så skal dere lage en klasse som implementerer det. I dette tilfelle er grensesnittet ca. som en klasse uten feltvariabler og med `// TODO` i hver variabel – rett og slett bare oppskriften på hva du skal implementere.
* I tilfeller hvor vi har flere typer objekter som skal ha forskjellig oppførsel, men fremdeles kunne brukes på samme måte. F.eks. at vi vil kunne ha både ender, fisker, frosker og vannliljer i en andedam.
## Grid Interface
Vi har laget et grensesnitt
som inneholder metodene vi er interessert å bruke på brett. For denne oppgaven skal IGrid håndtere tilstanden
til en Celle, altså en `CellState`, men i en nyere oppgave skal vi oppdatere MyGrid sånn at den kan lagre alle
slags data. IGrid
har følgende metoder:
public interface IGrid {
int getHeight(); // returner høyden
int getWidth(); // returner bredden
void set(int x, int y, CellState element); // sett elemenetet på x,y til element
CellState get(int x, int y); // hent elementet på x,y
IGrid copy(); // returner en kopi av brettet
IGrid er ment å fungere liknende til en flerdimensjonal array, men er litt annerledes i bruk. F.eks. vil `grid.set(x, y, DEAD)` svare til `grid[x][y] = DEAD`.
## Steg 2.1: Gjør ferdig implementasjon av `MyGrid implements IGrid`
Du skal nå gjøre ferdig en implementasjon av IGrid. Heldigvis har vi begynt på jobben allerede :)
* Åpne filene [](
og [](
i Eclipse (du finner dem i `src/inf101/v19/datastructures/`). Les
dokumentasjonen til metodene i IGrid.
* Legg merke til `implements IGrid` øverst i – dette betyr at MyGrid skal implementere IGrid. Klassen må ha kode for alle metodene i IGrid, og alle grid-objekter (laget med `new MyGrid(...)`) kan brukes som beskrevet i IGrid (med `get`, `set` osv).
* I klassen som følger med oppgaven har vi lagt til noen feltvariabler. Du står
fritt til å gjøre andre valg om du vil.
* Vi har brukt `IList` (grensesnitt) og `MyList` (implementasjon av
`IList` til å lagre elementene – Her kan man evt bruke `List` og
`ArrayList` som følger med i Java. Se dokumentasjon i IList for hvordan denne brukes. Du er antakelig interessert i metodene `add`, `set` og `get`.
* Du *kan* bruke en vanlig array/tabell, `CellState[]` – men senere i
kurset kommer vi til å unngå vanlige arrays for å kunne lage *generiske*
* Konstruktøren er allerede ferdig skrevet. Legg merke til at både IList/MyList
og List/ArrayList starter som helt tomme – i motsetning til en array, som har
et fast antall elementer som alle er satt til `null` til å begynne med.
Derfor bruker vi `add` i konstruktøren for å legge til det antallet elementer
vi trenger.
* Fyll inn getHeight() og getWidth()
* For `set` og `get` må vi finne ut hvordan vi skal konvertere mellom (x,y)
koordinater og indekser i en endimensjonal liste. En grei løsning er f.eks.
at *indeks = x + (width * y)* – det vil si at dataene ligger lagret etter
hverandre i listen, rad for rad (hver rad er *width* lang). *Fyll inn kode
som lagrer / henter elementer fra listen basert på x,y-koordinatene.*
* Den siste metoden, `copy()` er allerede ferdig.
## Steg 2.2: Test MyGrid
* Det følger med en klasse `GridTest`. Høyreklikk på denne, og kjør *Run as →
JUnit Test*. Alle fire test casene skal virke. Hvis testen feiler, er det noe
galt med ``:
* Hvis du har feil i `outOfBoundsTest()` har du antakelig fjernet
argumentsjekkene fra `get` og `set`.
* Hvis du har feil i en av `setGetTest1`/`2` eller `copyTest` har du
antakelig enten ikke implementert både `get` og `set`, eller du har feil
i oversetting mellom (x,y)-koordinater og listeindekser.
* Hvis du får IndexOutOfBoundsException i `setGetTest1`/`2` har du
antakelig feil i oversetting mellom (x,y)-koordinater og listeindekser.
* Fiks eventuelle problemer med MyGrid
## Steg 2.3: Lag noen flere tester
Lag noen nye testmetoder i `GridTest` etter samme mønster som de som er der fra
før. Hver testmetode skal være `public void` og ha `@Test` foran seg. Bruk
`assertEquals(forventet verdi, verdi)` for å se om en verdi er den samme som
forventet. Sjekk f.eks.:
* getHeight() gir samme verdi som `MyGrid` ble konstruert med.
* getWidth() gir samme verdi som `MyGrid` ble konstruert med.
* Negative indekser, og for store indekser til `get()`/`set()`resulterer i `IndexOutOfBoundsException`. Se `outOfBoundsTest()` for et eksempel som tester for store indekser til `set()`.
* Hva skjer om du lager et grid med høyde eller bredde lik 0? (Hva synes du burde skje?)
## Steg 2.4: Test testene...
Det er ikke så lett å lage gode tester:
* Endre `MyGrid` midlertidig slik at `set` *alltid* setter elementet på
posisjon 0 i listen, og `get` alltid returnerer elementet på posisjon 0 i
listen. Dvs. i stedet for å lagre et todimensjonalt brett, lagrer vi bare én
eneste verdi – en oppførsel som er helt forskjellig fra hva vi opprinnelig
har tenkt.
* Kjør `GridTest` en gang til. Virker alle eller de fleste av testene?
* Fiks `MyGrid` så den fungerer som normalt igjen.
Selv om vi skriver tester, kan alvorlige feil likevel slippe gjennom. Vi skal etterhvert lære bedre teknikker for å lage grundigere tester.
Steg 3: Kjør Game of Life
I `Main` (`inf101.v19.cell.Main`) klassen finner du `main()`-metoden. Der ser du at det først oprettes
et `GameOfLife` objekt, så blir den gitt til `CellAutomataGUI` som håndterer
det grafiske grensesnittet.
Det er kommentert ut en linje i `main()`-metoden der det istedenfor oprettes
et `BriansBrain` objekt. Når du har implementert `BriansBrain` kan du
istedenfor gi `CellAutomataGUI` et `BriansBrain` objekt, så vil
`CellAutomataGUI` vise det istedenfor.
`CellAutomataGUI` tar imot et objekt som implementerer `ICellAutomaton`, så det
er dette grensesnittet som `BriansBrain` også skal implementere. (Her ser du et tilfelle av at grenesnitt brukes til å la flere forskjellige ting brukes på samme måte – vi kan ha flere forskjellige celleautomater som lar seg kjøre fra samme GUI/meny-systemet.)
* Høyreklikk på `Main`, velg *Run as → Java Application*.
* Prøv ut programmet. Det skal se ca. slik ut:
![Game of life](img/cell.png)
Steg 4: Brian's Brain
Nå er vi klar for `BriansBrain`. I store trekk vil klassen ligne veldig på
`GameOfLife`. Det kan være lurt å ta utganspunkt i den klassen (copy/paste ...). `BriansBrain`
vil også implementere `ICellAutomaton` grensesnittet så det er lurt å sette
deg inn i hva grensesnittet består i. Siden `BriansBrain` har celler med tre mulige
tilstander kommer den til å trenge en egen `enum` for å holde styr på
tilstanden til alle cellene – du kan bruke den vedlagte `CellState` som også blir brukt av `GameOfLife` (som bare bruke tilstandene `ALIVE` og `DEAD`):
public enum CellState {
Hvis du er litt usikker på hvordan `enum` fungerer kan du ta en titt [her](
Steg 4.1
* Lag en ny klasse `BriansBrain implements ICellAutomaton`.
* Skriv konstruktøren for `BriansBrain`, og metodene `getHeight()` og `getWidth()`. Konstruktøren
burde lage et `MyGrid` som holder den nåværende generasjonen. Når du lager et
nytt `MyGrid` kan du oppgi f.eks. `CellState.DEAD` som initielt element til
alle cellene. `getHeight()` og `getWidth()` refererer til størrelsen på den
nåværende generasjonen.
Steg 4.2
* Skriv `initializeGeneration()` og `getColorInCurrentGeneration()`.
* `initializeGeneration()` gir en tilfeldig verdi til alle cellene i den
nåværende generasjonen (bruk
f.eks. `CellState.ALIVE` hvis `random.nextBoolean()` er `true` og
`CellState.DEAD` ellers), og
* `getColorInCurrentGeneration()`
skal fortelle det grafiske grensesnittet hvilken farge som cellen skal farges med. Her
kan du feks. bruke
* `Color.white` for døde celler,
* `` for døende celler
* og `` for levende celler.
Steg 4.3
Skriv `stepAutomata()`. Denne metoden oppdaterer generasjonen i `Grid` ved å anvende
reglene for Brian's Brain. Husk:
* Levende celler blir Døende.
* Døende celler blir Døde.
* Døde celler blir levende hvis den har nøyaktig to levende naboer.