Projektovanje softvera/K1 2024

Izvor: SI Wiki
Pređi na navigaciju Pređi na pretragu

Prvi kolokvijum 2024. godine održan je 27. oktobra 2024. godine. Bile su dostupne prezentacije o svim projektnim obrascima, kao i Java dokumentacija. Vreme za izradu je bilo 120 minuta.

1. zadatak

Postavka

Posmatra se sistem za prikaz i obradu digitalnih slika. Treba obezbediti to da se na originalnu sliku može primeniti proizvoljan broj filtera (ili nijedan), u proizvoljnom redosledu, pri čemu ostatak sistema koji prikazuje i koristi sliku (“klijent”) ne treba da vidi razliku u odnosu na to da li su na sliku primenjeni filteri ili nisu. Svaka slika sadrži niz piksela. Piksel ima R, G i B komponente (intenzitet crvene, zelene i plave boje), gde svaka ima celobrojnu vrednost od 0 do 255. Filteri koji mogu da se primene u ovom sistemu su za sada (kasnije mogu biti osmišljeni i dodati novi filteri):

  • Greyscale – svaki piksel se pretvara u odgovarajuću nijansu sive, gde se R, G i B komponente računaju po sledećoj formuli: R, G, B = 0.3 * R + 0.59 * G + 0.11 * B
  • Invert – R, G i B komponente svakog piksela se invertuju po sledećoj formuli: R = 255 – R, G = 255 – G, B = 255 – B

Klijent koji koristi sliku može da pročita vrednosti njenih piksela; ukoliko su na sliku primenjeni filteri, to su vrednosti piksela originalne slike izmenjene dejstvom dodatih filtera (u slučaju da nijedan filter nije primenjen nad originalnom slikom, vraća se neizmenjen niz piksela originalne slike). Korišćenjem projektnog obrasca Dekorater (Decorator) potrebno je implementirati opisani deo sistema i navesti raspodelu uloga iz obrasca klasama u ovom rešenju; opis dati u tekstualnom fajlu z1.txt.

Napisati primer korišćenja date saradnje. Napraviti sliku sa filterima Greyscale i Invert, sa nekim nasumičnim vrednostima piksela, a zatim dohvatiti piksele takve slike i ispisati ih. Implementacija piksela prikazana je u sledećoj klasi:

package zad1;

public class Pixel {
    private int R, G, B;

    public Pixel(int R, int G, int B) {
        this.R = R;
        this.G = G;
        this.B = B;
    }

    public int getR() {
        return R;
    }

    public void setR(int R) {
        this.R = R;
    }

    public int getG() {
        return G;
    }

    public void setG(int G) {
        this.G = G;
    }

    public int getB() {
        return B;
    }

    public void setB(int B) {
        this.B = B;
    }
}

Rešenje

Saradnja

U ovom zadatku potrebno je koristiti uzorak Dekorater, a u izradi se koristio i uzorak Šablonski metod.

Dekorater
Učesnici:
  • Komponenta (Image)
  • Subjekat (RawImage)
  • Dopuna (Filter)
  • KonkretnaDopuna (Greyscale, Inverse)
Šablonski metod
Učesnici:
  • ApstraktnaKlasa (Filter)
  • KonkretnaKlasa (Greyscale, Inverse)

Image.java

package z1;

import java.util.List;

public interface Image {
    public List<Pixel> getPixels();
}

RawImage.java

package z1;

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

public class RawImage implements Image {
    protected List<Pixel> pixels = new ArrayList<Pixel>();

    public RawImage(List<Pixel> pixels) {
        this.pixels = pixels;
    }

    @Override
    public List<Pixel> getPixels() {
        return pixels;
    }
}

Filter.java

package z1;

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

public abstract class Filter implements Image{
    private Image image; //image that we wrap/decorate

    public Filter(Image image){
        this.image = image;
    }

    protected abstract Pixel modifyPixel(Pixel p);

    private List<Pixel> applyFilter(List<Pixel> pixels){
        List<Pixel> modifiedPixels = new ArrayList<Pixel>();
        for (Pixel pixel : pixels){
            modifiedPixels.add(modifyPixel(pixel));
        }
        return modifiedPixels;
    }

    @Override
    public List<Pixel> getPixels() {
        return applyFilter(this.image.getPixels());
    }
}

Greyscale.java

package z1;

public class Greyscale extends Filter{
    public Greyscale(Image i) {
        super(i);
    }

    @Override
    protected Pixel modifyPixel(Pixel p){
        int grey = (int) (p.getR()*0.3) +  (int) (p.getG()*0.59) + (int) (p.getB()*0.11);
        return new Pixel(grey, grey, grey
        );
    }
}

Invert.java

package z1;

public class Invert extends Filter {
    public Invert(Image i) {
        super(i);
    }

    private final int invertFactor = 255;

    protected Pixel modifyPixel(Pixel p) {
        return new Pixel(
                invertFactor - p.getR(),
                invertFactor - p.getG(),
                invertFactor - p.getB()
        );
    }

}

Main.java

package z1;

import java.util.Arrays;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        List<Pixel> pixels = Arrays.asList(new Pixel(2, 2, 2), new Pixel(2, 3, 4), new Pixel(100, 200, 255));
        Image myImage = new RawImage(pixels);
        for (Pixel p : myImage.getPixels()) {
            System.out.println(p.toString());
        }
        Image myFilteredImage = new Invert(new Greyscale(myImage));
        for  (Pixel p : myFilteredImage.getPixels()) {
            System.out.println(p.toString());
        }
    }
}

2. zadatak

Ovaj zadatak nije rešen. Pomozite SI Wiki tako što ćete ga rešiti.

Postavka

Posmatra se deo sistema koji simulira kartašku igru. Pre početka igre, igrači mogu da se prijave deliocu karata („krupijeu“) za učestvovanje u igri i mogu da se odjave nakon završene igre. Igra se igra tako da u njoj može učestvovati različit broj igrača i tokom igre igrači ne razgovaraju jedan sa drugim. Broj igrača je uvek dovoljno mali da je sa njima prihvatljiva sinhrona komunikacija. Nakon što se igra pokrene, svaki igrač od krupijea dobija po dve nasumične karte (kojih je samo on svestan). Potom krupije otkriva jednu od preostalih karata i svi igrači se obaveštavaju o karti koja je otkrivena. Na osnovu ovih informacija, svaki igrač može da odluči koliko će novca uložiti. Neki igrači igraju agresivno, a neki štedljivo; način igranja može da se konfiguriše za svakog igrača različito, pri čemu se predviđa osmišljavanje novih različitih načina igre, pa različiti igrači sa istim informacijama ulažu različitu količinu novca. Nakon što svi igrači ulože svoj novac, sledi ponovo otkrivanje nove karte oko koje se igrači obaveštavaju i nakon čega svaki može ponovo da uloži novac. Ovo se ponavlja pet puta po igri, na kraju čega igrač sa najboljom kombinacijom karata dobija sav novac i igra se završava. Logika odabira pobednika se takođe može menjati od igre do igre, mada konkretna implementacija ove logike nije od interesa za ovaj zadatak.

Doraditi i ispraviti datu implementaciju sistema korišćenjem odgovarajućih projektnih obrazaca. Navesti sve primene projektnih obrazaca u sistemu sa kratkim obrazloženjem u tekstualnom fajlu z2.txt (jedna do dve rečenice).

Rešenje

Saradnja

U ovom zadatku su iskorišćeni uzorci Unikat, Posmatrač i Strategija.

Unikat
Učesnici:
  • Unikat (Croupier)
Posmatrač
Učesnici:
  • KonkretanSubjekat (Croupier)
  • KonkretanPosmatrač (Player)
Strategija
Učesnici:
  • Kontekst (Player)
  • Strategija (PlayingStrategy)
  • KonkretnaStrategija (AggresiveStrategy, ScroogeStrategy)

Observer.java

package z2;

public interface Observer {
    double update(Card newCard);
    void resetGame();
}

Card.java

package z2;

public class Card {

    public enum CardSuit {DIAMOND, CLUB, HEART, SPADE};
    public static final int NUMBER_OF_CARDS = 14;
    public static final int NUMBER_OF_SUITS = 4;
    private int number;
    private CardSuit cardSuit;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public CardSuit getCardSuit() {
        return cardSuit;
    }

    public void setCardSuit(CardSuit cardSuit) {
        this.cardSuit = cardSuit;
    }

    public Card(int number, int cardSuitIndex) {
        this.number = number;
        this.cardSuit = CardSuit.values()[cardSuitIndex];
    }
}

PlayingStrategy.java

package z2;

import java.util.List;

public abstract class PlayingStrategy {
    public abstract double playAMove(Card[] myCards, List<Card> tableCards);
}

ScroogeStrategy.java

package z2;

import java.util.List;

public class ScroogeStrategy extends PlayingStrategy {
    @Override
    public double playAMove(Card[] cardsInHand, List<Card> tableCards) {
        double bettingMoney;
        int numCardsOnTable = tableCards.size();
        bettingMoney = numCardsOnTable * 0.2 + cardsInHand.length * 0.1;
        return bettingMoney;
    }
}

AggresiveStrategy.java

package z2;

import java.util.List;

public class AggresiveStrategy extends PlayingStrategy {
    @Override
    public double playAMove(Card[] cardsInHand, List<Card> tableCards) {
        double bettingMoney;
        int numCardsOnTable = tableCards.size();
        bettingMoney = numCardsOnTable * 0.5 + cardsInHand.length * 0.3;
        return bettingMoney;
    }
}

Croupier.java

package z2;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static z2.Card.*;

public class Croupier {

    private static final int NUMBER_OF_TURNS = 5;
    private boolean isGameInProgress = false;

    private Random randomGenerator = new Random();
    private List<Player> playersInGame = new ArrayList<>();
    private List<Card> cardsOnTable = new ArrayList<>();

    private double moneyPot = 0;

    private Croupier() {}

    private volatile static Croupier instance = null;

    public static Croupier getInstance() {
        //Thread safe variant with minimal critical section
        if (instance == null) {
            synchronized (Croupier.class) {
                if (instance == null) {
                    instance = new Croupier();
                }
            }
        }
        return instance;
    }

    /* join and leave are subscription methods for concrete subject in Observer */
    public void join(Player player) {
        if(!isGameInProgress) {
            this.playersInGame.add(player);
        }
    }

    public void leave(Player player) {
        if(!isGameInProgress) {
            this.playersInGame.remove(player);
        }
    }

    public List<Card> getTable(){
        return cardsOnTable;
    }

    public void simulateGame() {
        isGameInProgress = true;
        for (Player player: playersInGame) {
            player.setCardsInHand(
                    new Card[]{
                            drawCardFromDeck(),
                            drawCardFromDeck()
                    }
            );
        }
        cardsOnTable.clear();
        moneyPot = 0;
        for(int i = 0; i < NUMBER_OF_TURNS; i++) {
            Card newCard = drawCardFromDeck();
            cardsOnTable.add(newCard);
            for(Player player: playersInGame) {
                moneyPot += player.update(newCard);
            }
        }
        Player player = defineWinner();
        player.addMoney(moneyPot);
        System.out.println(
                "Winning player with ID: " + player.getPlayer_id() +
                ", currently has: " + player.getMoney());
        isGameInProgress = false;
    }

    private Player defineWinner() {
        // TODO: Should be a few declaring winner tactics, not currently important.
        return playersInGame.get(randomGenerator.nextInt(playersInGame.size()));
    }

    private Card drawCardFromDeck() {
        return new Card(
                randomGenerator.nextInt(NUMBER_OF_CARDS),
                randomGenerator.nextInt(NUMBER_OF_SUITS)
        );
    }
}

Player.java

package z2;

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

public class Player implements Observer {
    private Croupier croupier;
    private PlayingStrategy playingStrategy;
    public Player(Croupier c, PlayingStrategy playingStrategy) {
        croupier = c;
        this.playingStrategy = playingStrategy;
    }

    private static final double STARTING_MONEY = 10;
    private Card cardsInHand[];
    private double money = STARTING_MONEY;
    private boolean aggressive = true;
    private static int ID = 0;
    private int player_id = ID++;

    private List<Card> tableCards = new ArrayList<Card>();


    public int getPlayer_id() {
        return player_id;
    }

    public Card[] getCardsInHand() {
        return cardsInHand;
    }

    public void setCardsInHand(Card[] cardsInHand) {
        this.cardsInHand = cardsInHand;
    }

    public double getMoney() {
        return money;
    }

    public void addMoney(double money) {
        this.money += money;
    }

    public boolean isAggressive() {
        return aggressive;
    }

    public void setAggressive(boolean aggressive) {
        this.aggressive = aggressive;
    }

    @Override
    public double update(Card newCard) {
        tableCards.add(newCard);
        double bettingMoney;
        bettingMoney = playingStrategy.playAMove(cardsInHand, tableCards);
        bettingMoney = bettingMoney > money ? money : bettingMoney;
        money -= bettingMoney;
        return bettingMoney;
    }

    @Override
    public void resetGame() {
        this.tableCards.clear();
        this.cardsInHand = null;
    }
}