8 Commits

Author SHA1 Message Date
34c2ac91c6 Finished AB5 2022-05-07 13:20:59 +02:00
a075544fe2 Refactor BodyLinkedList 2022-05-07 12:01:43 +02:00
391b389063 Refactor BodyForceTreeMap 2022-05-07 12:01:22 +02:00
e7eae474ac Refactor MassiveForceHashMap 2022-05-06 22:45:56 +02:00
6b1f1ecc2a Refactored for AB5 2022-05-06 22:42:28 +02:00
e84bf3bf4a Aufgabenblatt 5 2022-05-02 10:31:10 +00:00
1e789dba34 Aufgabenblatt 5 2022-05-02 12:20:50 +02:00
e94d3daeda Fix AB4 main loop
Signed-off-by: Lorenz Stechauner <lorenz.stechauner@necronda.net>
2022-04-28 21:48:36 +02:00
13 changed files with 954 additions and 166 deletions

102
angabe/Aufgabenblatt5.md Normal file
View File

@ -0,0 +1,102 @@
# Aufgabenblatt 5
## Allgemeine Anmerkungen
Ihre Lösung für dieses Aufgabenblatt ist bis Montag, 9.5. 11h durch `git commit` und `git push`
abzugeben. Mit der Angabe werden die Dateien `Massive.java`, `NamedBody.java`, `MassiveLinkedList.java`,
`MassiveForceHashMap.java`, `Simulation5.java` und `Aufgabe5Test.java` mitgeliefert.
Wenn Sie zusätzlich zu den gefragten Klassen weitere Klassen definieren, achten Sie darauf, dass
die Klassennamen mit `My` beginnen, um Konflikte mit späteren Aufgabenblättern zu vermeiden.
## Ziel
Ziel der Aufgabe ist die Anwendung der Konzepte: Gleichheit und Hash-Werte, Hash-Tabelle
(siehe Skriptum Seite 85-91).
## Beschreibung der gegebenen Dateien
- [Massive](../src/Massive.java) ist ein Interface, das Himmelskörper (als kohärente Massen)
beschreibt. `Massive` ist der gemeinsame Obertyp für verschiedene Klassen von Himmelkörpern. Die
meisten spezifizierten Methoden sind mit einer `default`-Implementierung definiert. Dieser
Programmcode wird ausgeführt, falls die entsprechende Klasse (`Body` oder `NamedBody`) über keine
eigene Definition der Methode verfügt. Verändern Sie diese Datei bitte nicht.
- [NamedBody](../src/NamedBody.java) ist das Gerüst einer Klassendefinition. Die Klasse
repräsentiert Himmelskörper, die einen Namen haben.
- [MassiveLinkedList](../src/MassiveLinkedList.java) ist das Gerüst für eine Implementierung einer
verketteten Liste von `Massive`-Objekten. Die Liste unterscheidet sich von `BodyLinkedList`
dadurch, dass der Elementtyp statt `Body` der Obertyp `Massive` ist.
- [MassiveForceHashMap](../src/MassiveForceHashMap.java) ist das Gerüst für eine Implementierung
einer assoziativen Datenstruktur, die ein `Massive`-Objekt mit der auf das Objekt wirkenden Kraft
assoziiert.
- [Simulation5](../src/Simulation5.java) ist ein Gerüst für eine ausführbare Klasse. Hier soll
die Simulation analog zur Klasse `Simulation` implementiert werden (damit Sie Ihre [ursprüngliche
Datei](../src/Simulation.java) nicht überschreiben müssen).
- [Aufgabe5Test](../src/Aufgabe5Test.java) ist eine vorgegebene Klasse, die Sie zum Testen Ihrer
Implementierung verwenden sollten. Bei einer fehlerfreien Implementierung sollten bei der
Ausführung dieser Klasse keine Exceptions geworfen werden und alle Tests als erfolgreich ("successful")
ausgegeben werden. Entfernen Sie die Kommentarzeichen, um diese Klasse verwenden zu können. Sie
müssen diese Klasse nicht weiter verändern, können aber eigene Testfälle hinzufügen.
## Aufgaben
Ihre Aufgaben sind folgende:
**1. Implementieren Sie `Massive` in den Klassen `Body` und `NamedBody`.**
Passen Sie die bestehende Definition von `Body` so an, dass die Klasse `Massive` implementiert wird.
Vervollständigen Sie auch `NamedBody` so, dass sie `Massive` implementiert und die vorgegebene
Spezifikationen der Methoden erfüllt.
**2. Überschreiben von `equals` und `hashCode` in `NamedBody`:**
Überschreiben Sie in `NamedBody` die Methoden `equals` und `hashCode` gemäß der dort angeführten
Spezifikation. Achten Sie bei der Implementierung darauf, dass die in der Klasse `Object`
beschriebenen Bedingungen für `equals` und `hashCode` eingehalten werden. `equals` und `hashCode`
müssen zusammen passen.
**3. Vervollständigen von `MassiveLinkedList`:**
Definieren Sie `MassiveLinkedList`. Die Klasse ist wie `BodyLinkedList` aufgebaut, mit dem
Unterschied, dass der Elementtyp statt `Body` nun der Typ `Massive` ist. Die Methode `indexOf`
vergleicht Objekte mittels `equals`.
**4. Implementierung von `MassiveForceHashMap`:**
Vervollständigen Sie die Definition der Klasse `MassiveForceHashMap`, die eine Hash-Tabelle
mit Schlüssel vom Typ `Massive` und Wert vom Typ `Vector3` implementiert. Die Klasse ist ähnlich
zur Klasse `BodyForceTreeMap`. Die Unterschiede sind:
- Der Typ des Schlüssels ist der gemeinsame Obertyp von `Body` und `NamedBody` (`Massive`).
Dadurch lassen sich Objekte beider Klassen gemeinsam in der Hash-Tabelle speichern.
- Die Schlüssel-Werte-Paare sind nicht nach Masse sortiert. Stattdessen wird der Hash-Wert zur
Suche benutzt.
- Es gibt eine zusätzliche Methode `keyList()`. Die Methoden `equals` und `hashCode` werden
redefiniert.
**5. Implementierung von `Simulation5`:**
Implementieren Sie die Simulationsschleife unter Verwendung eines Objekts vom Typ
`MassiveForceHashMap`. Die Methode `keyList()` hilft beim Iterieren der Hash-Tabelle.
Kollisionen von Himmelskörpern müssen in dieser Simulation nicht berücksichtigt werden.
### Hinweise:
- Verwenden Sie bei der Implementierung von `MassiveForceHashMap` eine geeignete Kollisionsbehandlung
für gleiche Hash-Werte. Als Vorlage können Sie den Beispielcode aus dem Skriptum nutzen.
### Denkanstöße (ohne Bewertung)
1. Wie könnte man vorgehen, wenn man - wie in früheren Simulationen - Himmelskörper im Fall von
Kollisionen verschmelzen will?
2. Was ändert sich am Verhalten von `MassiveForceHashMap`, wenn man in `Body` die Methoden
`equals` und `hashCode` überschreiben würde?
#### _Punkteaufteilung_
- Implementierung von `Massive` in `NamedBody`: 1 Punkt
- Implementierung von `Massive` in `Body`: 0.5 Punkt
- Implementierung von `MassiveForceHashMap`: 2 Punkte
- Implementierung von `MassiveLinkedList`: 0.5 Punkte
- Implementierung von `Simulation5`: 1 Punkte
- Gesamt: 5 Punkte

81
src/Aufgabe5Test.java Normal file
View File

@ -0,0 +1,81 @@
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class Aufgabe5Test {
@Test
public void testEP2() {
//test classes NamedBody and MassiveForceHashMap
// create 12 named bodies
NamedBody sun1, mercury1, venus1, earth1, moon1, mars1, deimos1, phobos1, vesta1, pallas1, hygiea1, ceres1;
// create a nameless body
Body earth2 = new Body(SolSystem4.EARTH);
// create the same 12 named body-force pairs
sun1 = new NamedBody(SolSystem4.SUN_NAMED);
earth1 = new NamedBody(SolSystem4.EARTH_NAMED);
moon1 = new NamedBody(SolSystem4.MOON_NAMED);
mars1 = new NamedBody(SolSystem4.MARS_NAMED);
deimos1 = new NamedBody(SolSystem4.DEIMOS_NAMED);
phobos1 = new NamedBody(SolSystem4.PHOBOS_NAMED);
mercury1 = new NamedBody(SolSystem4.MERCURY_NAMED);
venus1 = new NamedBody(SolSystem4.VENUS_NAMED);
vesta1 = new NamedBody(SolSystem4.VESTA_NAMED);
pallas1 = new NamedBody(SolSystem4.PALLAS_NAMED);
hygiea1 = new NamedBody(SolSystem4.HYGIEA_NAMED);
ceres1 = new NamedBody(SolSystem4.CERES_NAMED);
NamedBody sun2 = new NamedBody("Sun", 1.9895E30, new Vector3(0.1, 0.0, 0.0), new Vector3(0.0, 0.0, 0.0));
NamedBody earth3 = new NamedBody("Earth", 1, new Vector3(0, 0, 0), new Vector3(0, 0, 0));
assertEquals(sun1, sun2);
assertEquals(sun2.hashCode(), sun1.hashCode());
assertEquals(earth1, earth3);
assertEquals(earth3.hashCode(), earth1.hashCode());
// check basic functions of 'MassiveForceHashMap'
MassiveForceHashMap map = new MassiveForceHashMap();
map.put(sun1, new Vector3(0, 0, 0));
map.put(mercury1, new Vector3(0, 0, 0));
map.put(venus1, new Vector3(0, 0, 0));
map.put(earth1, new Vector3(0, 0, 0));
map.put(moon1, new Vector3(0, 0, 0));
map.put(mars1, new Vector3(0, 0, 0));
map.put(deimos1, new Vector3(0, 0, 0));
map.put(phobos1, new Vector3(0, 0, 0));
map.put(vesta1, new Vector3(0, 0, 0));
map.put(pallas1, new Vector3(0, 0, 0));
map.put(hygiea1, new Vector3(0, 0, 0));
map.put(ceres1, new Vector3(0, 0, 0));
map.put(mars1, new Vector3(0, 0, 0)); // inserted twice
assertEquals(12, map.keyList().size());
assertTrue(map.toString().contains("Mars"));
assertTrue(map.toString().contains("Deimos"));
assertTrue(map.toString().contains("Moon"));
assertTrue(map.toString().contains("Earth"));
MassiveLinkedList bl = map.keyList();
boolean allThere = true;
while (bl.size() > 0) {
allThere &= map.containsKey(bl.pollFirst());
}
assertTrue(allThere);
assertFalse(map.containsKey(new Body(0, new Vector3(0, 0, 0), new Vector3(0, 0, 0))));
assertFalse(map.containsKey(new NamedBody("Omuamua", 0, new Vector3(0, 0, 0), new Vector3(0, 0, 0))));
int hashCode1 = map.hashCode();
Vector3 f = new Vector3(5, 5, 5);
map.put(earth3, f);
assertEquals(f, map.get(earth1));
assertNull(map.get(earth2));
int hashCode2 = map.hashCode();
assertEquals(map, map);
assertEquals(hashCode2, map.hashCode());
assertNotEquals(hashCode1, hashCode2);
}
}

View File

@ -3,7 +3,7 @@ import codedraw.CodeDraw;
/** /**
* This class represents celestial bodies like stars, planets, asteroids, etc... * This class represents celestial bodies like stars, planets, asteroids, etc...
*/ */
public class Body { public class Body implements Massive {
private final double mass; private final double mass;
private Vector3 massCenter; // position of the mass center. private Vector3 massCenter; // position of the mass center.
private Vector3 currentMovement; private Vector3 currentMovement;

View File

@ -4,7 +4,7 @@
*/ */
public class BodyForceTreeMap { public class BodyForceTreeMap {
private int size = 0; private int size = 0;
private BodyForceTreeMapItem root = null; private Item root = null;
/** /**
* Adds a new key-value association to this map. If the key already exists in this map, * Adds a new key-value association to this map. If the key already exists in this map,
@ -13,30 +13,30 @@ public class BodyForceTreeMap {
*/ */
public Vector3 put(Body key, Vector3 value) { public Vector3 put(Body key, Vector3 value) {
if (root == null) { if (root == null) {
root = new BodyForceTreeMapItem(key, value); root = new Item(key, value);
size++; size++;
return null; return null;
} }
BodyForceTreeMapItem item = root; Item item = root;
while (item != null) { while (item != null) {
if (item.key() == key) { if (item.key == key) {
Vector3 old = item.value(); Vector3 old = item.value;
item.setValue(value); item.value = value;
return old; return old;
} else if (item.key().mass() > key.mass()) { } else if (item.key.mass() > key.mass()) {
if (item.left() != null) { if (item.left != null) {
item = item.left(); item = item.left;
} else { } else {
item.setLeft(new BodyForceTreeMapItem(key, value)); item.setLeft(new Item(key, value));
size++; size++;
break; break;
} }
} else { } else {
if (item.right() != null) { if (item.right != null) {
item = item.right(); item = item.right;
} else{ } else{
item.setRight(new BodyForceTreeMapItem(key, value)); item.setRight(new Item(key, value));
size++; size++;
break; break;
} }
@ -52,14 +52,14 @@ public class BodyForceTreeMap {
* Precondition: key != null. * Precondition: key != null.
*/ */
public Vector3 get(Body key) { public Vector3 get(Body key) {
BodyForceTreeMapItem item = root; Item item = root;
while (item != null) { while (item != null) {
if (item.key() == key) { if (item.key == key) {
return item.value(); return item.value;
} else if (item.key().mass() > key.mass()) { } else if (item.key.mass() > key.mass()) {
item = item.left(); item = item.left;
} else { } else {
item = item.right(); item = item.right;
} }
} }
return null; return null;
@ -69,14 +69,14 @@ public class BodyForceTreeMap {
* Returns 'true' if this map contains a mapping for the specified key. * Returns 'true' if this map contains a mapping for the specified key.
*/ */
public boolean containsKey(Body key) { public boolean containsKey(Body key) {
BodyForceTreeMapItem item = root; Item item = root;
while (item != null) { while (item != null) {
if (item.key() == key) { if (item.key == key) {
return true; return true;
} else if (item.key().mass() > key.mass()) { } else if (item.key.mass() > key.mass()) {
item = item.left(); item = item.left;
} else { } else {
item = item.right(); item = item.right;
} }
} }
return false; return false;
@ -86,14 +86,14 @@ public class BodyForceTreeMap {
return this.size; return this.size;
} }
private String toString(BodyForceTreeMapItem item) { private String toString(Item item) {
String s = ""; String s = "";
if (item == null) { if (item == null) {
return s; return s;
} }
s += this.toString(item.right()); s += this.toString(item.right);
s += String.format("{%s: %s}\n", item.key(), item.value()); s += String.format("{%s: %s}\n", item.key, item.value);
s += this.toString(item.left()); s += this.toString(item.left);
return s; return s;
} }
@ -105,51 +105,27 @@ public class BodyForceTreeMap {
public String toString() { public String toString() {
return toString(root); return toString(root);
} }
}
class BodyForceTreeMapItem { private class Item {
private final Body key; private final Body key;
private Vector3 value; private Vector3 value;
private BodyForceTreeMapItem parent; private Item parent;
private BodyForceTreeMapItem left; private Item left;
private BodyForceTreeMapItem right; private Item right;
public BodyForceTreeMapItem(Body key, Vector3 value) { public Item(Body key, Vector3 value) {
this.key = key; this.key = key;
this.value = value; this.value = value;
} }
public Body key() { public void setLeft(Item left) {
return this.key;
}
public void setValue(Vector3 value) {
this.value = value;
}
public Vector3 value() {
return this.value;
}
public BodyForceTreeMapItem left() {
return this.left;
}
public BodyForceTreeMapItem right() {
return this.right;
}
public BodyForceTreeMapItem parent() {
return this.parent;
}
public void setLeft(BodyForceTreeMapItem left) {
this.left = left; this.left = left;
if (left != null) left.parent = this; if (left != null) left.parent = this;
} }
public void setRight(BodyForceTreeMapItem right) { public void setRight(Item right) {
this.right = right; this.right = right;
if (right != null) right.parent = this; if (right != null) right.parent = this;
} }
}
} }

View File

@ -6,8 +6,8 @@ import java.util.Iterator;
*/ */
public class BodyLinkedList implements Iterable<Body> { public class BodyLinkedList implements Iterable<Body> {
private int size = 0; private int size = 0;
private BodyLinkedListItem first; private Item first;
private BodyLinkedListItem last; private Item last;
/** /**
* Initializes 'this' as an empty list. * Initializes 'this' as an empty list.
@ -35,11 +35,11 @@ public class BodyLinkedList implements Iterable<Body> {
*/ */
public void addFirst(Body body) { public void addFirst(Body body) {
if (first == null) { if (first == null) {
first = new BodyLinkedListItem(body); first = new Item(body);
last = first; last = first;
} else { } else {
first.setPrev(new BodyLinkedListItem(body)); first.setPrev(new Item(body));
first = first.prev(); first = first.prev;
} }
size++; size++;
} }
@ -49,11 +49,11 @@ public class BodyLinkedList implements Iterable<Body> {
*/ */
public void addLast(Body body) { public void addLast(Body body) {
if (last == null) { if (last == null) {
last = new BodyLinkedListItem(body); last = new Item(body);
first = last; first = last;
} else { } else {
last.setNext(new BodyLinkedListItem(body)); last.setNext(new Item(body));
last = last.next(); last = last.next;
} }
size++; size++;
} }
@ -63,7 +63,7 @@ public class BodyLinkedList implements Iterable<Body> {
* Returns 'null' if the list is empty. * Returns 'null' if the list is empty.
*/ */
public Body getLast() { public Body getLast() {
return (last != null) ? last.body() : null; return (last != null) ? last.body : null;
} }
/** /**
@ -71,7 +71,7 @@ public class BodyLinkedList implements Iterable<Body> {
* Returns 'null' if the list is empty. * Returns 'null' if the list is empty.
*/ */
public Body getFirst() { public Body getFirst() {
return (first != null) ? first.body() : null; return (first != null) ? first.body : null;
} }
/** /**
@ -82,8 +82,8 @@ public class BodyLinkedList implements Iterable<Body> {
if (first == null) { if (first == null) {
return null; return null;
} }
Body b = first.body(); Body b = first.body;
first = first.next(); first = first.next;
if (first != null) first.setPrev(null); if (first != null) first.setPrev(null);
size--; size--;
return b; return b;
@ -97,8 +97,8 @@ public class BodyLinkedList implements Iterable<Body> {
if (last == null) { if (last == null) {
return null; return null;
} }
Body b = last.body(); Body b = last.body;
last = last.prev(); last = last.prev;
if (last != null) last.setNext(null); if (last != null) last.setNext(null);
size--; size--;
return b; return b;
@ -117,28 +117,28 @@ public class BodyLinkedList implements Iterable<Body> {
return; return;
} }
BodyLinkedListItem item = first; Item item = first;
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
item = item.next(); item = item.next;
} }
item.prev().setNext(new BodyLinkedListItem(body)); item.prev.setNext(new Item(body));
item.setPrev(item.prev().next()); item.setPrev(item.prev.next);
size++; size++;
} }
private Body removeItem(BodyLinkedListItem item) { private Body removeItem(Item item) {
if (item == first) { if (item == first) {
first = item.next(); first = item.next;
if (first != null) first.setPrev(null); if (first != null) first.setPrev(null);
} else if (item == last) { } else if (item == last) {
last = item.prev(); last = item.prev;
if (last != null) last.setNext(null); if (last != null) last.setNext(null);
} else { } else {
item.next().setPrev(item.prev()); item.next.setPrev(item.prev);
} }
size--; size--;
return item.body(); return item.body;
} }
/** /**
@ -146,19 +146,19 @@ public class BodyLinkedList implements Iterable<Body> {
* Precondition: i >= 0 && i < size(). * Precondition: i >= 0 && i < size().
*/ */
public Body get(int i) { public Body get(int i) {
BodyLinkedListItem item; Item item;
if (i < size / 2) { if (i < size / 2) {
item = first; item = first;
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++) {
item = item.next(); item = item.next;
} }
} else { } else {
item = last; item = last;
for (int j = size - 1; j > i; j--) { for (int j = size - 1; j > i; j--) {
item = item.prev(); item = item.prev;
} }
} }
return item.body(); return item.body;
} }
/** /**
@ -170,12 +170,9 @@ public class BodyLinkedList implements Iterable<Body> {
return -1; return -1;
} }
BodyLinkedListItem item = first; Item item = first;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++, item = item.next) {
if (item.body() == body) { if (item.body == body) return i;
return i;
}
item = item.next();
} }
return -1; return -1;
@ -187,8 +184,8 @@ public class BodyLinkedList implements Iterable<Body> {
*/ */
public BodyLinkedList removeCollidingWith(Body body) { public BodyLinkedList removeCollidingWith(Body body) {
BodyLinkedList removed = new BodyLinkedList(); BodyLinkedList removed = new BodyLinkedList();
for (BodyLinkedListItem item = first; item != null; item = item.next()) { for (Item item = first; item != null; item = item.next) {
if (body != item.body() && body.collidesWith(item.body())) { if (body != item.body && body.collidesWith(item.body)) {
removed.addLast(this.removeItem(item)); removed.addLast(this.removeItem(item));
} }
} }
@ -205,57 +202,45 @@ public class BodyLinkedList implements Iterable<Body> {
@Override @Override
public Iterator<Body> iterator() { public Iterator<Body> iterator() {
return new Iterator<>() { return new Iterator<>() {
BodyLinkedListItem ptr = first; Item ptr = first;
boolean yieldedFirst = false; boolean yieldedFirst = false;
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return ptr != null && (!yieldedFirst || ptr.next() != null); return ptr != null && (!yieldedFirst || ptr.next != null);
} }
@Override @Override
public Body next() { public Body next() {
if (!yieldedFirst) { if (!yieldedFirst) {
yieldedFirst = true; yieldedFirst = true;
return ptr.body(); } else {
ptr = ptr.next;
} }
ptr = ptr.next(); return ptr.body;
return ptr.body();
} }
}; };
} }
}
class BodyLinkedListItem { private class Item {
private final Body body; private final Body body;
private BodyLinkedListItem prev; private Item prev;
private BodyLinkedListItem next; private Item next;
public BodyLinkedListItem(Body body) { public Item(Body body) {
this.body = body; this.body = body;
this.prev = null; this.prev = null;
this.next = null; this.next = null;
} }
public Body body() { public void setPrev(Item prev) {
return body;
}
public BodyLinkedListItem prev() {
return prev;
}
public void setPrev(BodyLinkedListItem prev) {
this.prev = prev; this.prev = prev;
if (prev != null) prev.next = this; if (prev != null) prev.next = this;
} }
public BodyLinkedListItem next() { public void setNext(Item next) {
return next;
}
public void setNext(BodyLinkedListItem next) {
this.next = next; this.next = next;
if (next != null) next.prev = this; if (next != null) next.prev = this;
} }
}
} }

73
src/Massive.java Normal file
View File

@ -0,0 +1,73 @@
/**
* Represents a coherent mass with a mass center in 3D space. Has two naming schemes for its
* methods. Please, do not change this interface definition!
*/
public interface Massive extends Drawable {
/**
* Returns the mass.
*/
default double mass() {
return getMass();
}
/**
* Returns the mass center.
*/
default Vector3 massCenter() {
return getMassCenter();
}
/**
* Returns the mass.
*/
default double getMass() {
return mass();
}
/**
* Returns the mass center.
*/
default Vector3 getMassCenter() {
return massCenter();
}
/**
* Returns the approximate radius of 'this', assuming it is a coherent round mass.
* (It is assumed that the radius r is related to the mass m by r = m ^ 0.5,
* where m and r measured in solar units.)
*/
default double getRadius() {
return radius();
}
/**
* Returns the approximate radius of 'this', assuming it is a coherent round mass.
* (It is assumed that the radius r is related to the mass m by r = m ^ 0.5,
* where m and r measured in solar units.)
*/
default double radius() {
return SpaceDraw.massToRadius(mass());
}
/**
* Returns a vector representing the gravitational force exerted by 'b' on this mass.
* The gravitational Force F is calculated by F = G*(m1*m2)/(r*r), with m1 and m2 being the
* masses of the objects interacting, r being the distance between the centers of the masses
* and G being the gravitational constant.
*/
default Vector3 gravitationalForce(Massive b) {
Vector3 direction = b.massCenter().minus(this.massCenter());
double distance = direction.length();
direction.normalize();
double force = Simulation.G * this.mass() * b.mass() / (distance * distance);
return direction.times(force);
}
/**
* Centers this mass at a new position, according to the specified force vector 'force' exerted
* on it, and updates the current velocity vector accordingly.
* (Velocity depends on the mass of 'this', its current velocity and the exerted force.)
*/
void move(Vector3 force);
}

View File

@ -0,0 +1,161 @@
/**
* A hash map that associates a 'Massive'-object with a Vector3 (typically this is the force
* exerted on the object). The number of key-value pairs is not limited.
*/
public class MassiveForceHashMap {
private int size;
private Massive[] keys;
private Vector3[] values;
/**
* Initializes 'this' as an empty map.
*/
public MassiveForceHashMap() {
this(16);
}
public MassiveForceHashMap(int capacity) {
this.size = 0;
this.keys = new Massive[capacity];
this.values = new Vector3[capacity];
}
/**
* Adds a new key-value association to this map. If the key already exists in this map,
* the value is replaced and the old value is returned. Otherwise 'null' is returned.
* Precondition: key != null.
*/
public Vector3 put(Massive key, Vector3 value) {
if (size > keys.length / 2) doubleCapacity();
int idx = ((key.hashCode() % keys.length) + keys.length) % keys.length;
for (int i = 0; i < keys.length; i++) {
int pos = (idx + i) % keys.length;
if (values[pos] == null) {
keys[pos] = key;
values[pos] = value;
size++;
return null;
} else if (keys[pos].equals(key)) {
Vector3 old = values[pos];
values[pos] = value;
return old;
}
}
throw new RuntimeException();
}
private void doubleCapacity() {
Massive[] oldKeys = keys;
Vector3[] oldValues = values;
keys = new Massive[keys.length * 2];
values = new Vector3[values.length * 2];
size = 0;
for (int i = 0; i < oldKeys.length; i++) {
Massive k = oldKeys[i];
Vector3 v = oldValues[i];
if (v != null) put(k, v);
}
}
/**
* Returns the value associated with the specified key, i.e. the method returns the force vector
* associated with the specified key. Returns 'null' if the key is not contained in this map.
* Precondition: key != null.
*/
public Vector3 get(Massive key) {
int idx = ((key.hashCode() % keys.length) + keys.length) % keys.length;
for (int i = 0; i < keys.length; i++) {
int pos = (idx + i) % keys.length;
if (keys[pos] == null) {
break;
} else if (keys[pos].equals(key)) {
return values[pos];
}
}
return null;
}
/**
* Returns 'true' if this map contains a mapping for the specified key.
*/
public boolean containsKey(Massive key) {
return this.get(key) != null;
}
/**
* Returns a readable representation of this map, with all key-value pairs. Their order is not
* defined.
*/
@Override
public String toString() {
if (size == 0) return "{}";
StringBuilder sb = new StringBuilder("{");
for (int i = 0; i < keys.length; i++) {
Massive k = keys[i];
Vector3 v = values[i];
if (k != null && v != null) {
sb.append("\n ");
sb.append(k);
sb.append(": ");
sb.append(v);
sb.append(",");
}
}
sb.deleteCharAt(sb.length() - 1);
sb.append("\n}");
return sb.toString();
}
/**
* Compares `this` with the specified object for equality. Returns `true` if the specified
* `o` is not `null` and is of type `MassiveForceHashMap` and both `this` and `o` have equal
* key-value pairs, i.e. the number of key-value pairs is the same in both maps and every
* key-value pair in `this` equals one key-value pair in `o`. Two key-value pairs are
* equal if the two keys are equal and the two values are equal. Otherwise, `false` is returned.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof MassiveForceHashMap map) || map.size != size)
return false;
for (int i = 0; i < keys.length; i++) {
Massive k1 = keys[i];
Vector3 v1 = values[i];
if (k1 == null || v1 == null) continue;
Vector3 v2 = map.get(k1);
if (!v1.equals(v2)) return false;
}
return true;
}
/**
* Returns the hashCode of `this`.
*/
@Override
public int hashCode() {
int hash = 0;
for (int i = 0; i < keys.length; i++) {
Massive k = keys[i];
Vector3 v = values[i];
if (k == null || v == null) continue;
hash ^= k.hashCode();
hash ^= v.hashCode();
}
return hash;
}
/**
* Returns a list of all the keys in no specified order.
*/
public MassiveLinkedList keyList() {
MassiveLinkedList list = new MassiveLinkedList();
for (int i = 0; i < keys.length; i++) {
if (keys[i] != null && values[i] != null)
list.addLast(keys[i]);
}
return list;
}
}

218
src/MassiveLinkedList.java Normal file
View File

@ -0,0 +1,218 @@
import java.util.Iterator;
/**
* A list of massive objects implemented as a linked list.
* The number of elements of the list is not limited.
*/
public class MassiveLinkedList implements Iterable<Massive> {
private int size = 0;
private Item first;
private Item last;
/**
* Initializes 'this' as an empty list.
*/
public MassiveLinkedList() {
first = null;
last = null;
}
/**
* Initializes 'this' as an independent copy of the specified list 'list'.
* Calling methods of this list will not affect the specified list 'list'
* and vice versa.
* Precondition: list != null.
*/
public MassiveLinkedList(BodyLinkedList list) {
this.size = 0;
for (Body b : list) {
this.addLast(b);
}
}
/**
* Inserts the specified element 'body' at the beginning of this list.
*/
public void addFirst(Massive body) {
if (first == null) {
first = new Item(body);
last = first;
} else {
first.setPrev(new Item(body));
first = first.prev;
}
size++;
}
/**
* Appends the specified element 'body' to the end of this list.
*/
public void addLast(Massive body) {
if (last == null) {
last = new Item(body);
first = last;
} else {
last.setNext(new Item(body));
last = last.next;
}
size++;
}
/**
* Returns the last element in this list.
* Returns 'null' if the list is empty.
*/
public Massive getLast() {
return (last != null) ? last.body : null;
}
/**
* Returns the first element in this list.
* Returns 'null' if the list is empty.
*/
public Massive getFirst() {
return (first != null) ? first.body : null;
}
/**
* Retrieves and removes the first element in this list.
* Returns 'null' if the list is empty.
*/
public Massive pollFirst() {
if (first == null) {
return null;
}
Massive m = first.body;
first = first.next;
if (first != null) first.setPrev(null);
size--;
return m;
}
/**
* Retrieves and removes the last element in this list.
* Returns 'null' if the list is empty.
*/
public Massive pollLast() {
if (last == null) {
return null;
}
Massive m = last.body;
last = last.next;
if (last != null) last.setNext(null);
size--;
return m;
}
/**
* Inserts the specified element at the specified position in this list.
* Precondition: i >= 0 && i <= size().
*/
public void add(int i, Massive m) {
if (first == null || i == 0) {
addFirst(m);
return;
} else if (i == size) {
addLast(m);
return;
}
Item item = first;
for (int j = 0; j < i; j++) {
item = item.next;
}
item.prev.setNext(new Item(m));
item.setPrev(item.prev.next);
size++;
}
/**
* Returns the element at the specified position in this list.
* Precondition: i >= 0 && i < size().
*/
public Massive get(int i) {
Item item;
if (i < size / 2) {
item = first;
for (int j = 0; j < i; j++) {
item = item.next;
}
} else {
item = last;
for (int j = size - 1; j > i; j--) {
item = item.prev;
}
}
return item.body;
}
/**
* Returns the index of the first occurrence of the specified element in this list, or -1 if
* this list does not contain the element.
*/
public int indexOf(Massive m) {
if (first == null) {
return -1;
}
Item item = first;
for (int i = 0; i < size; i++, item = item.next) {
if (item.body.equals(m)) return i;
}
return -1;
}
/**
* Returns the number of elements in this list.
*/
public int size() {
return size;
}
@Override
public Iterator<Massive> iterator() {
return new Iterator<>() {
Item ptr = first;
boolean yieldedFirst = false;
@Override
public boolean hasNext() {
return ptr != null && (!yieldedFirst || ptr.next != null);
}
@Override
public Massive next() {
if (!yieldedFirst) {
yieldedFirst = true;
} else {
ptr = ptr.next;
}
return ptr.body;
}
};
}
private class Item {
private final Massive body;
private Item prev;
private Item next;
public Item(Massive body) {
this.body = body;
this.prev = null;
this.next = null;
}
public void setPrev(Item prev) {
this.prev = prev;
if (prev != null) prev.next = this;
}
public void setNext(Item next) {
this.next = next;
if (next != null) next.prev = this;
}
}
}

79
src/NamedBody.java Normal file
View File

@ -0,0 +1,79 @@
import codedraw.CodeDraw;
public class NamedBody implements Massive {
private final String name;
private final Body body;
/**
* Initializes this with name, mass, current position and movement.
*/
public NamedBody(String name, double mass, Vector3 massCenter, Vector3 currentMovement) {
this(name, new Body(mass, massCenter, currentMovement));
}
public NamedBody(String name, Body body) {
this.name = name;
this.body = body;
}
public NamedBody(NamedBody other) {
this(other.name, new Body(other.body));
}
/**
* Returns the name of the body.
*/
public String getName() {
return name;
}
public Body getBody() {
return body;
}
public Vector3 getMassCenter() {
return body.getMassCenter();
}
public double getMass() {
return body.getMass();
}
/**
* Compares `this` with the specified object. Returns `true` if the specified `o` is not
* `null` and is of type `NamedBody` and both `this` and `o` have equal names.
* Otherwise, `false` is returned.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof NamedBody b)) return false;
return this.name.equals(b.name);
}
/**
* Returns the hashCode of `this`.
*/
@Override
public int hashCode() {
return this.name.hashCode();
}
/**
* Returns a readable representation including the name of this body.
*/
@Override
public String toString() {
return this.getName();
}
@Override
public void move(Vector3 force) {
body.move(force);
}
@Override
public void draw(CodeDraw cd) {
body.draw(cd);
}
}

View File

@ -6,8 +6,7 @@ import codedraw.CodeDraw;
*/ */
public class NamedBodyForcePair implements CosmicSystem { public class NamedBodyForcePair implements CosmicSystem {
private final String name; private final NamedBody body;
private final Body body;
private final Vector3 force = new Vector3(); private final Vector3 force = new Vector3();
/** /**
@ -15,27 +14,30 @@ public class NamedBodyForcePair implements CosmicSystem {
* is initialized with a zero vector. * is initialized with a zero vector.
*/ */
public NamedBodyForcePair(String name, double mass, Vector3 massCenter, Vector3 currentMovement) { public NamedBodyForcePair(String name, double mass, Vector3 massCenter, Vector3 currentMovement) {
this(name, new Body(mass, massCenter, currentMovement)); this(new NamedBody(name, mass, massCenter, currentMovement));
} }
public NamedBodyForcePair(String name, Body b) { public NamedBodyForcePair(String name, Body body) {
this.body = b; this(new NamedBody(name, body));
this.name = name; }
public NamedBodyForcePair(NamedBody body) {
this.body = body;
} }
public NamedBodyForcePair(NamedBodyForcePair other) { public NamedBodyForcePair(NamedBodyForcePair other) {
this(other.name, new Body(other.body)); this(new NamedBody(other.body));
} }
public Body getBody() { public Body getBody() {
return body; return body.getBody();
} }
/** /**
* Returns the name of the body. * Returns the name of the body.
*/ */
public String getName() { public String getName() {
return name; return body.getName();
} }
@Override @Override
@ -70,13 +72,13 @@ public class NamedBodyForcePair implements CosmicSystem {
@Override @Override
public void addForceTo(CosmicSystem cs) { public void addForceTo(CosmicSystem cs) {
cs.addForceFrom(body); cs.addForceFrom(body.getBody());
} }
@Override @Override
public BodyLinkedList getBodies() { public BodyLinkedList getBodies() {
BodyLinkedList list = new BodyLinkedList(); BodyLinkedList list = new BodyLinkedList();
list.addFirst(body); list.addFirst(body.getBody());
return list; return list;
} }

View File

@ -36,9 +36,7 @@ public class Simulation4 {
while (true) { while (true) {
seconds++; seconds++;
for (Body b : sol.getBodies()) { sol.addForceTo(sol);
sol.addForceFrom(b);
}
sol.update(); sol.update();
if ((seconds % 3600) == 0) { if ((seconds % 3600) == 0) {

113
src/Simulation5.java Normal file
View File

@ -0,0 +1,113 @@
import codedraw.CodeDraw;
import java.awt.*;
import java.util.Random;
/**
* Simulates the formation of a massive solar system.
*/
public class Simulation5 {
// gravitational constant
public static final double G = 6.6743e-11;
// one astronomical unit (AU) is the average distance of earth to the sun.
public static final double AU = 150e9; // meters
// one light year
public static final double LY = 9.461e15; // meters
// some further constants needed in the simulation
public static final double SUN_MASS = 1.989e30; // kilograms
public static final double SUN_RADIUS = 696340e3; // meters
public static final double EARTH_MASS = 5.972e24; // kilograms
public static final double EARTH_RADIUS = 6371e3; // meters
// set some system parameters
public static final double SECTION_SIZE = 10 * AU; // the size of the square region in space
public static final int NUMBER_OF_BODIES = 22;
public static final double OVERALL_SYSTEM_MASS = 20 * SUN_MASS; // kilograms
// all quantities are based on units of kilogram respectively second and meter.
/**
* The main simulation method using instances of other classes.
*/
public static void main(String[] args) {
// simulation
CodeDraw cd = new CodeDraw();
// create solar system with 12 bodies
NamedBody sun = new NamedBody(SolSystem4.SUN_NAMED);
NamedBody earth = new NamedBody(SolSystem4.EARTH_NAMED);
NamedBody moon = new NamedBody(SolSystem4.MOON_NAMED);
NamedBody mars = new NamedBody(SolSystem4.MARS_NAMED);
NamedBody deimos = new NamedBody(SolSystem4.DEIMOS_NAMED);
NamedBody phobos = new NamedBody(SolSystem4.PHOBOS_NAMED);
NamedBody mercury = new NamedBody(SolSystem4.MERCURY_NAMED);
NamedBody venus = new NamedBody(SolSystem4.VENUS_NAMED);
NamedBody vesta = new NamedBody(SolSystem4.VESTA_NAMED);
NamedBody pallas = new NamedBody(SolSystem4.PALLAS_NAMED);
NamedBody hygiea = new NamedBody(SolSystem4.HYGIEA_NAMED);
NamedBody ceres = new NamedBody(SolSystem4.CERES_NAMED);
// create some additional bodies
Body[] bodies = new Body[NUMBER_OF_BODIES];
Random random = new Random(2022);
for (int i = 0; i < bodies.length; i++) {
bodies[i] = new Body(
Math.abs(random.nextGaussian()) * OVERALL_SYSTEM_MASS / bodies.length,
new Vector3(0.2 * random.nextGaussian() * AU, 0.2 * random.nextGaussian() * AU, 0.2 * random.nextGaussian() * AU),
new Vector3(0 + random.nextGaussian() * 5e3, 0 + random.nextGaussian() * 5e3, 0 + random.nextGaussian() * 5e3)
);
}
MassiveForceHashMap forceOnBody = new MassiveForceHashMap();
forceOnBody.put(sun, new Vector3());
forceOnBody.put(earth, new Vector3());
forceOnBody.put(moon, new Vector3());
forceOnBody.put(mars, new Vector3());
forceOnBody.put(deimos, new Vector3());
forceOnBody.put(phobos, new Vector3());
forceOnBody.put(mercury, new Vector3());
forceOnBody.put(venus, new Vector3());
forceOnBody.put(vesta, new Vector3());
forceOnBody.put(pallas, new Vector3());
forceOnBody.put(hygiea, new Vector3());
forceOnBody.put(ceres, new Vector3());
for (Body b : bodies) {
forceOnBody.put(b, new Vector3());
}
long seconds = 0;
while (true) {
seconds++;
for (Massive b1 : forceOnBody.keyList()) {
Vector3 force = new Vector3();
for (Massive b2 : forceOnBody.keyList()) {
if (b1 != b2) {
force = force.plus(b1.gravitationalForce(b2));
}
}
forceOnBody.put(b1, force);
}
for (Massive body : forceOnBody.keyList()) {
body.move(forceOnBody.get(body));
}
if ((seconds % 3600) == 0) {
cd.clear(Color.BLACK);
for (Massive body : forceOnBody.keyList()) {
body.draw(cd);
}
cd.show();
}
}
}
}

View File

@ -12,16 +12,16 @@ public class SolSystem4 {
public static final Body HYGIEA = new Body(8.32E19, new Vector3(-3.983943433707043E11, 2.325833000024021E11, -2.233667695713672E10), new Vector3(-6931.864585548552, -15686.8108598699, -690.5791992347208)); public static final Body HYGIEA = new Body(8.32E19, new Vector3(-3.983943433707043E11, 2.325833000024021E11, -2.233667695713672E10), new Vector3(-6931.864585548552, -15686.8108598699, -690.5791992347208));
public static final Body CERES = new Body(9.394E20, new Vector3(3.781372641419032E11, 1.96718960466285E11, -6.366459168068592E10), new Vector3(-8555.324226752316, 14718.33755980907, 2040.230135060142)); public static final Body CERES = new Body(9.394E20, new Vector3(3.781372641419032E11, 1.96718960466285E11, -6.366459168068592E10), new Vector3(-8555.324226752316, 14718.33755980907, 2040.230135060142));
public static final NamedBodyForcePair SUN_NAMED = new NamedBodyForcePair("Sun", SUN); public static final NamedBody SUN_NAMED = new NamedBody("Sun", SUN);
public static final NamedBodyForcePair EARTH_NAMED = new NamedBodyForcePair("Earth", EARTH); public static final NamedBody EARTH_NAMED = new NamedBody("Earth", EARTH);
public static final NamedBodyForcePair MOON_NAMED = new NamedBodyForcePair("Moon", MOON); public static final NamedBody MOON_NAMED = new NamedBody("Moon", MOON);
public static final NamedBodyForcePair MARS_NAMED = new NamedBodyForcePair("Mars", MARS); public static final NamedBody MARS_NAMED = new NamedBody("Mars", MARS);
public static final NamedBodyForcePair DEIMOS_NAMED = new NamedBodyForcePair("Deimos", DEIMOS); public static final NamedBody DEIMOS_NAMED = new NamedBody("Deimos", DEIMOS);
public static final NamedBodyForcePair PHOBOS_NAMED = new NamedBodyForcePair("Phobos", PHOBOS); public static final NamedBody PHOBOS_NAMED = new NamedBody("Phobos", PHOBOS);
public static final NamedBodyForcePair MERCURY_NAMED = new NamedBodyForcePair("Mercury", MERCURY); public static final NamedBody MERCURY_NAMED = new NamedBody("Mercury", MERCURY);
public static final NamedBodyForcePair VENUS_NAMED = new NamedBodyForcePair("Venus", VENUS); public static final NamedBody VENUS_NAMED = new NamedBody("Venus", VENUS);
public static final NamedBodyForcePair VESTA_NAMED = new NamedBodyForcePair("Vesta", VESTA); public static final NamedBody VESTA_NAMED = new NamedBody("Vesta", VESTA);
public static final NamedBodyForcePair PALLAS_NAMED = new NamedBodyForcePair("Pallas", PALLAS); public static final NamedBody PALLAS_NAMED = new NamedBody("Pallas", PALLAS);
public static final NamedBodyForcePair HYGIEA_NAMED = new NamedBodyForcePair("Hygiea", HYGIEA); public static final NamedBody HYGIEA_NAMED = new NamedBody("Hygiea", HYGIEA);
public static final NamedBodyForcePair CERES_NAMED = new NamedBodyForcePair("Ceres", CERES); public static final NamedBody CERES_NAMED = new NamedBody("Ceres", CERES);
} }