Pertemuan 13 OOP - Abstraksi & Simulasi Fox & Rabit

Nama : Kadek Fajar Pramartha Yasodana

NRP : 5025231185

Kelas : PBO A


Abstraksi & Simulasi Fox & Rabit

1. Tuliskan implementasi dari program kecil Abstract Class Makhluk hidup yang diwariskan kepada manusia, hewan, dan tumbuhan

Source Code : GitHub

Pada soal nomor ini, kita hanya perlu membuat sebuah abstract class makhluk hidup dengan beberapa metode abstrak juga. Setelah itu kita inherit classnya menjadi beberapa subclass Manusia, Hewan, dan Tumbuhan. Terdapat beberapa class seperti

MakhlukHidup.java

public abstract class MakhlukHidup {
protected String name;
public MakhlukHidup(String name)
{
this.name = name;
}

public abstract void doSomething();
public abstract void grow();
public abstract void breathe();
}

Class ini merupakan class yang sebagai template sebuah class makhluk hidup. dengan beberapa method yang harus di override oleh subclassnya yaitu doSomething, grow, dan breathe

Manusia.java

public class Manusia extends MakhlukHidup {
public Manusia(String name)
{
super(name);
}
@Override
public void doSomething() {
System.out.println(name + " sedang berbicara dengan temannya");
}

@Override
public void grow() {
System.out.println(name + " membutuhkan asupan protein dan nutrisi yang cukup");
}

@Override
public void breathe() {
System.out.println(name + " membutuhkan oksigen untuk bernafas");
}
}

Melakukan inheritance dari class MakhlukHidup, dengan override methodnya layaknya seorang manusia.

Hewan.java

public class Hewan extends MakhlukHidup {
public Hewan(String name)
{
super(name);
}

@Override
public void doSomething() {
System.out.println(name + " sedang berlari dengan 4 kaki");
}

@Override
public void breathe() {
System.out.println(name + " membutuhkan oksigen untuk bernapas");
}

@Override
public void grow() {
System.out.println(name + " membutuhkan rumput untuk dimakan");
}
}

Melakukan inheritance dari class MakhlukHidup, dengan override methodnya layaknya seekor hewan.

Tumbuhan.java

public class Tumbuhan extends MakhlukHidup {
public Tumbuhan(String name)
{
super(name);
}
@Override
public void doSomething() {
System.out.println(name + " sedang melakukan fotosintesis");
}

@Override
public void grow() {
System.out.println(name + " membutuhkan siraman air yang cukup dan tanah yang subur");
}

@Override
public void breathe() {
System.out.println(name + " menghirup CO2 pada siang hari untuk melakukan fotosintesis, menghirup O2 pada malam hari untuk merubah glukosa menjadi energi");
}
}

Melakukan inheritance dari class MakhlukHidup, dengan override methodnya layaknya sebuah tumbuhan.

Hasil




2. Pelajari dan baca simulasi Foxes and Rabbit yang ada di buku. Kemudian buat program perubahan dari struktur class umum menjadi bentuk Abstract Class.

Source Code : GitHub
Pada soal ini untuk membuat sebuah graphical interface sebagai penggambaran simulasi, saya menggunakan framework graphics LibGDX . LibGDX dapat mempermudah kita untuk menggambar sprite, melakukan batching, setting proyeksi dengan mudah, yang membuat framework ini cocok untuk digunakan pada soal ini.
Pada simulasi ini terdapat sebuah Field yang nantinya akan digunakan untuk menyimpan objek objek dalam grid 2 dimensi. Objek tersebut bisa berisi GameObject yang dimana class tersebut adalah class parent yang akan kita gunakan untuk inheritance pada subclass lainnya. Class class tersebut antara lain adalah Animal, Food, Fox inherit Animal, Rabbit inherit Animal, Carrot inherit Animal. Objek objek ini nantinya akan dijalankan oleh Simulation dan memanggil fungsi act yang digunakan untuk mengupdate objek objek tersebut. Simulasi akan dijalankan sekali per detik tanpa henti.

Semua class yang digunakan antara lain:

Animal.java
Kelas abstrak ini mewakili hewan dalam simulasi. Kelas ini merupakan turunan dari GameObject dan memiliki atribut seperti alive (menentukan apakah hewan masih hidup), age (umur hewan), maxAge (umur maksimum sebelum mati), dan parameter reproduksi seperti minBreedAge, breedRandom, breedMinAmount, dan breedMaxAmount. Hewan dapat melakukan aksi melalui metode abstrak act(), yang harus diimplementasikan oleh kelas turunannya. Objek ini terkait dengan simulator dan lokasi di dalam medan simulasi.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.graphics.Color;

public abstract class Animal extends GameObject {
protected boolean alive;
protected int age;
protected int maxAge;
protected int minBreedAge;
protected Simulator simulator;
protected boolean breeded = false;
protected int breedRandom;
protected int breedMinAmount;
protected int breedMaxAmount;

public Animal(Simulator simulator, Location location)
{
this.location = location;
this.simulator = simulator;
}
public abstract void act();
}

Carrot.java
Kelas ini mewakili makanan berupa wortel. Sebagai turunan dari Food, Carrot memiliki atribut seperti eaten (status apakah wortel telah dimakan). Warnanya diatur menjadi oranye, sesuai dengan objek wortel dalam simulator. Metode act() diimplementasikan tetapi tidak memiliki logika khusus.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.graphics.Color;

public class Carrot extends Food {
public Carrot(Simulator simulator, Location location)
{
super(simulator, location);
simulatorColor = Color.ORANGE;

eaten = false;
}
@Override
public void act() {

}
}

Field.java
Kelas ini merepresentasikan medan simulasi berbentuk grid dua dimensi yang terdiri dari objek-objek GameObject. Medan ini memiliki fungsi untuk mengelola objek di dalam grid, seperti menambahkan, menghapus, atau mengambil objek berdasarkan koordinat. Selain itu, kelas ini dapat menemukan objek-objek tetangga untuk simulasi interaksi antar objek.
package com.fajar.foxrabbitsimgame;

import java.util.List;

public class Field {
private int width;
private int height;
private GameObject[][] objects;

public Field(int width, int height)
{
objects = new GameObject[width][height];
this.width = width;
this.height = height;
}
public GameObject getObjectAt(int x, int y)
{
return objects[x][y];
}
public boolean withinBounds(int x, int y)
{
return x >= 0 && y >= 0 && x < width && y < height;
}
public boolean setObjectAt(int x, int y, GameObject obj)
{
if(!withinBounds(x, y))
{
return false;
}

if(objects[x][y] == null)
{
objects[x][y] = obj;
return true;
}
else if(obj == null)
{
objects[x][y] = null;
return true;
}

return false;
}
public void getNeighbouringObjects(List<GameObject> outObj, GameObject currentObj, int checkSize)
{
if(currentObj == null)
{
return;
}

int startX = currentObj.location.getCol() - checkSize;
int endX = currentObj.location.getCol() + checkSize;
int startY = currentObj.location.getRow() - checkSize;
int endY = currentObj.location.getRow() + checkSize;

for(int x = startX; x <= endX; x++)
{
for(int y = startY; y <= endY; y++)
{
if(!withinBounds(x, y)) continue;

GameObject obj = getObjectAt(x, y);
if(obj != null && obj != currentObj)
{
outObj.add(obj);
}
}
}
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
public boolean getFreeLocation(Location location)
{
int tries = 100;

if(!withinBounds(location.getCol(), location.getRow()))
{
location.set(0, 0);
}

while (objects[location.getCol()][location.getRow()] != null)
{
tries--;

if(tries <= 0)
{
return false;
}

int colBefore = location.getCol();
int rowBefore = location.getRow();
location.translate(Utility.randInt(-1, 1), Utility.randInt(-1, 1));

if(!withinBounds(location.getCol(), location.getRow()))
{
location.set(colBefore, rowBefore);
}
}

return true;
}
}

Food.java
Kelas abstrak ini mewakili makanan dalam simulasi, yang merupakan turunan dari GameObject. Setiap makanan memiliki atribut eaten, yang menunjukkan apakah makanan telah dimakan, dan metode abstrak act() untuk mengatur logika perilakunya.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.graphics.Color;

public abstract class Food extends GameObject {
protected boolean eaten;
protected Simulator simulator;
public Food(Simulator simulator, Location location)
{
this.simulator = simulator;
this.location = location;
}
public abstract void act();
}

Fox.java
Kelas ini mewakili rubah dalam simulasi dan merupakan turunan dari Animal. Rubah memiliki parameter umur maksimal, kemampuan reproduksi, dan kemampuan untuk berburu kelinci sebagai makanan. Dalam metode act(), rubah dapat bertambah umur, memangsa kelinci, atau berkembang biak jika kondisi tertentu terpenuhi. Jika rubah tidak melakukan apa-apa, ia akan mencoba berpindah ke lokasi lain.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;

import java.util.ArrayList;

public class Fox extends Animal {
private ArrayList<GameObject> searchList = new ArrayList<>();

public Fox(Simulator simulator, Location location)
{
super(simulator, location);
maxAge = 50;

simulatorColor = Color.RED;

alive = true;

breedRandom = 15;
minBreedAge = 20;
breedMinAmount = 1;
breedMaxAmount = 3;
}

@Override
public void act() {
age++;
if(age > maxAge)
{
alive = false;
return;
}

simulator.getField().getNeighbouringObjects(searchList, this, 1);
boolean ate = false;
for(int i = 0; i < searchList.size(); i++)
{
if(searchList.get(i) instanceof Rabbit)
{
Rabbit rabbit = (Rabbit) searchList.get(i);

if(!rabbit.alive) continue;

rabbit.alive = false;
age = MathUtils.clamp(age - 20, 0, 999);
ate = true;
break;
}
else if(age > minBreedAge && searchList.get(i) instanceof Fox && !breeded && Utility.randInt(0, breedRandom) == 0)
{
Fox fox = (Fox) searchList.get(i);

if(!fox.alive) continue;

fox.breeded = true;

int spawnAmount = Utility.randInt(breedMinAmount, breedMaxAmount);

for(int j = 0; j < spawnAmount; j++)
simulator.addSpawnInformation(new Fox(simulator, new Location(location.getCol(), location.getRow())));
}
}
breeded = false;
searchList.clear();

if(!ate && Utility.randInt(0, 2) == 0)
{
setLocationSyncronize(simulator.getField(), location.getCol() + Utility.randInt(-1, 2), location.getRow() + Utility.randInt(-1, 2));
}
}
}

GameObject.java
Kelas abstrak ini menjadi dasar bagi semua objek yang dapat ditempatkan dalam medan simulasi. Setiap objek memiliki atribut lokasi (location) dan warna (simulatorColor). Selain itu, kelas ini menyediakan metode untuk memindahkan objek secara sinkron di medan simulasi.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.graphics.Color;

public abstract class GameObject {
protected Color simulatorColor;
protected Location location;

public Color getSimulatorColor()
{
return simulatorColor;
}
public boolean setLocationSyncronize(Field field, int col, int row)
{
if(!field.withinBounds(col, row))
{
return false;
}

GameObject otherObj = field.getObjectAt(location.getCol(), location.getRow());
if(otherObj == this && field.getObjectAt(col, row) == null)
{
field.setObjectAt(location.getCol(), location.getRow(), null);
field.setObjectAt(col, row, this);

location.set(col, row);

return true;
}

return false;
}
}

Location.java
Kelas ini merepresentasikan lokasi dalam koordinat grid (kolom dan baris). Lokasi dapat diubah atau diterjemahkan melalui metode set() dan translate(). Lokasi ini digunakan oleh GameObject untuk menentukan posisinya di dalam Field.
package com.fajar.foxrabbitsimgame;

public class Location {
private int col;
private int row;

public Location(int col, int row)
{
this.col = col;
this.row = row;
}
public void translate(int col, int row)
{
this.col += col;
this.row += row;
}
public void set(int col, int row)
{
this.col = col;
this.row = row;
}
public int getCol()
{
return col;
}
public int getRow()
{
return row;
}
}

Main.java
Kelas utama untuk menjalankan simulasi menggunakan libGDX. Kelas ini mengatur kamera, viewport, dan elemen visual lain seperti tekstur grid dan objek. Metode seperti create(), render(), dan resize() diimplementasikan untuk mengatur siklus hidup aplikasi.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.*;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.ScreenUtils;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport;
import org.w3c.dom.Text;

import java.awt.*;

/** {@link com.badlogic.gdx.ApplicationListener} implementation shared by all platforms. */
public class Main implements ApplicationListener {
private SpriteBatch batch;
private Viewport viewport;
private OrthographicCamera camera;

private static Main singleton;

//Sprites
private Texture whitePixelTexture;
private Texture gridTexture;

//Others
private Simulator simulator;

@Override
public void create() {
singleton = this;

batch = new SpriteBatch();

camera = new OrthographicCamera(1200f, 1000f);
viewport = new FitViewport(1200f, 1000f, camera);

Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pixmap.setColor(1f, 1f, 1f, 1f);
pixmap.drawPixel(0, 0, new Color(1f, 1f, 1f, 1f).toIntBits());
whitePixelTexture = new Texture(pixmap);

pixmap.dispose();

pixmap = new Pixmap(10, 10, Pixmap.Format.RGBA8888);
pixmap.setColor(0f, 0f, 0f, 1f);
pixmap.fill();
pixmap.setColor(1f, 1f, 1f, 1f);
pixmap.fillRectangle(1, 1, 8, 8);

gridTexture = new Texture(pixmap);

pixmap.dispose();

simulator = new Simulator();
}

public static Texture getWhitePixelTexture()
{
return singleton.whitePixelTexture;
}
public static Texture getGridTexture()
{
return singleton.gridTexture;
}

@Override
public void render() {
ScreenUtils.clear(0f, 0f, 0f, 1f);
viewport.apply();
simulator.setupDraw(camera);
batch.setProjectionMatrix(viewport.getCamera().combined);

batch.begin();

simulator.draw(batch, camera);

batch.end();
}

@Override
public void resize(int width, int height) {
viewport.update(width, height, true);
}

@Override
public void dispose() {
batch.dispose();
whitePixelTexture.dispose();
}

@Override
public void pause() {

}

@Override
public void resume() {

}
}

Rabbit.java
Kelas ini merepresentasikan kelinci dalam simulasi dan merupakan turunan dari Animal. Kelinci dapat bertambah umur, memakan wortel untuk memperpanjang umur, atau berkembang biak jika memenuhi syarat. Dalam metode act(), kelinci akan memeriksa objek di sekitarnya untuk mencari makanan atau pasangan.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.MathUtils;

import java.util.ArrayList;

public class Rabbit extends Animal {
private ArrayList<GameObject> searchList = new ArrayList<>();
public Rabbit(Simulator simulator, Location location)
{
super(simulator, location);
maxAge = 30;

simulatorColor = Color.BLUE;

alive = true;

minBreedAge = 15;
breedRandom = 8;
breedMinAmount = 1;
breedMaxAmount = 5;
}

@Override
public void act() {
age++;
if(age > maxAge)
{
alive = false;
return;
}

simulator.getField().getNeighbouringObjects(searchList, this, 1);
boolean ate = false;
for(int i = 0; i < searchList.size(); i++)
{
if(searchList.get(i) instanceof Carrot)
{
Carrot carrot = (Carrot) searchList.get(i);

if(carrot.eaten) continue;

carrot.eaten = true;
age = MathUtils.clamp(age - 20, 0, 999);
ate = true;
break;
}
else if(age > minBreedAge && searchList.get(i) instanceof Rabbit && !breeded && Utility.randInt(0, breedRandom) == 0)
{
Rabbit rabbit = (Rabbit) searchList.get(i);

if(!rabbit.alive) continue;

rabbit.breeded = true;

int spawnAmount = Utility.randInt(breedMinAmount, breedMaxAmount);

for(int j = 0; j < spawnAmount; j++)
simulator.addSpawnInformation(new Rabbit(simulator, new Location(location.getCol(), location.getRow())));
}
}
searchList.clear();

if(!ate && Utility.randInt(0, 2) == 0)
{
setLocationSyncronize(simulator.getField(), location.getCol() + Utility.randInt(-1, 2), location.getRow() + Utility.randInt(-1, 2));
}
}
}

Simulator.java
Kelas ini mengatur logika utama simulasi, termasuk pembuatan populasi awal, pengelolaan objek-objek seperti hewan dan makanan, serta langkah simulasi. Simulator menggunakan Field untuk memetakan lokasi semua objek, mengupdate status mereka, dan menggambar objek-objek tersebut di layar. Simulasi ini berjalan dalam langkah-langkah yang diatur dengan selang waktu tertentu.
package com.fajar.foxrabbitsimgame;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class Simulator {
private Field field;
private float passedDelta;
private float updateSecond = 1f;

private final float scale = 10f;
private final int width = 100;
private final int height = 80;

private HashSet<Animal> animalList = new HashSet<>();
private HashSet<Food> foodList = new HashSet<>();
private ArrayList<Object> removeObjList = new ArrayList<>();
private ArrayList<SpawnInformation> spawnInformations = new ArrayList<>();

public Simulator()
{
field = new Field(width, height);
new Utility();

spawnIntialPopulation();
}
protected Field getField()
{
return field;
}
private void spawnIntialPopulation()
{
int rabbitAmount = Utility.randInt(50, 70);
int foxAmount = Utility.randInt(30, 40);
int carrotAmount = Utility.randInt(80, 150);

for(int i = 0; i < foxAmount; i++)
{
Fox fox = new Fox(this, new Location(Utility.randInt(0, width - 1), Utility.randInt(0, height - 1)));
SpawnInformation info = new SpawnInformation(fox);

spawnInformations.add(info);
}
for(int i = 0; i < rabbitAmount; i++)
{
Rabbit rabbit = new Rabbit(this, new Location(Utility.randInt(0, width - 1), Utility.randInt(0, height - 1)));
SpawnInformation info = new SpawnInformation(rabbit);

spawnInformations.add(info);
}
for(int i = 0; i < carrotAmount; i++)
{
Carrot carrot = new Carrot(this, new Location(Utility.randInt(0, width - 1), Utility.randInt(0, height - 1)));
SpawnInformation info = new SpawnInformation(carrot);

spawnInformations.add(info);
}
}
private void update()
{
passedDelta += Gdx.graphics.getDeltaTime();

if(passedDelta >= updateSecond)
{
passedDelta -= updateSecond;
simulateOneStep();
}
}
private void simulateOneStep()
{
//Act
for(Animal animal : animalList)
{
if(!animal.alive) continue;

animal.act();
}
for(Food food : foodList)
{
if(food.eaten) continue;

food.act();
}

//Clear
for(Animal animal : animalList)
{
if(!animal.alive)
{
removeObjList.add(animal);
}
}
for(Food food : foodList)
{
if(food.eaten)
{
removeObjList.add(food);
}
}
for(Object obj : removeObjList)
{
GameObject gameObject = (GameObject)obj;

field.setObjectAt(gameObject.location.getCol(), gameObject.location.getRow(), null);

animalList.remove(obj);
foodList.remove(obj);
}
removeObjList.clear();

//Add
for(SpawnInformation spawnInformation : spawnInformations)
{
GameObject obj = spawnInformation.getObject();
if(field.getFreeLocation(obj.location))
{
if(obj instanceof Animal)
{
Animal animal = (Animal) obj;
animalList.add(animal);
}
else if(obj instanceof Food)
{
Food food = (Food) obj;
foodList.add(food);
}

field.setObjectAt(obj.location.getCol(), obj.location.getRow(), obj);
}
}
spawnInformations.clear();

System.out.println("Animals Amount : " + animalList.size());
System.out.println("Food Amount : " + foodList.size());
}
public void addSpawnInformation(GameObject object)
{
spawnInformations.add(new SpawnInformation(object));
}
public void setupDraw(OrthographicCamera camera)
{
camera.position.set(width / 2f * scale - (scale / 2f), height / 2f * scale - (scale / 2f), 0f);
}
public void draw(SpriteBatch batch, OrthographicCamera camera)
{
update();

batch.setColor(0f, 0f, 0f, 1f);
centerDraw(batch, Main.getWhitePixelTexture(), camera.position.x, camera.position.y, camera.viewportWidth, camera.viewportHeight);

batch.setColor(1f, 1f, 1f, 1f);
centerDraw(batch, Main.getGridTexture(), 0f, 0f, 10f, 10f);
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
centerDraw(batch, Main.getGridTexture(), x * scale, y * scale, scale, scale);
}
}
for(int x = 0; x < width; x++)
{
for(int y = 0; y < height; y++)
{
GameObject obj = field.getObjectAt(x, y);
if(obj == null)
{
continue;
}

batch.setColor(obj.getSimulatorColor());
centerDraw(batch, Main.getWhitePixelTexture(), x * scale, y * scale, scale, scale);
}
}
}
public void centerDraw(SpriteBatch batch, Texture texture, float x, float y, float width, float height)
{
x -= width / 2f;
y -= height / 2f;
batch.draw(texture, x, y, width, height);
}
}

SpawnInformation
Kelas ini digunakan untuk merepresentasikan informasi tentang objek yang akan dilakukan spawning.
package com.fajar.foxrabbitsimgame;

public class SpawnInformation {
private GameObject object;

public SpawnInformation(GameObject obj)
{
this.object = obj;
}
public GameObject getObject()
{
return object;
}
}

Utility
Kelas ini menyediakan fungsi utilitas yang berguna untuk simulasi, seperti menghasilkan angka acak dalam rentang tertentu.
package com.fajar.foxrabbitsimgame;

import java.util.Random;

public class Utility {
private static Utility singleton;
private Random randomizer = new Random();

public Utility()
{
singleton = this;
}
public static int randInt(int min, int max)
{
return singleton.randomizer.nextInt(min, max);
}
}

Hasil
Video Simulasi




Comments

Popular posts from this blog

Pertemuan 6 KPPL - Implementasi Web ChatBot Tech Support System, Web & Prototipe & Metodologi

Pertemuan 7 KPPL - Studi Kasus Pembuatan Aplikasi dengan Agile