Пројектовање софтвера/К1 2024 — разлика између измена

Извор: SI Wiki
Пређи на навигацију Пређи на претрагу
м (KockaAdmiralac преместио је страницу Пројектовање софтвера/Колоквијум 1 Октобар 2024 на Пројектовање софтвера/К1 2024 без остављања преусмерења: Ispravan naslov)
 
(Нису приказане 24 међуизмене 2 корисника)
Ред 1: Ред 1:
{{tocright}}
{{tocright}}
<!-- Ово ставити уколико НИЈЕДАН задатак није решен, док уколико само неки задаци нису решени на првом месту у њиховој секцији поставити {{делимично решено}}. Уколико се користи било који од ова два шаблона, ОБАВЕЗНО проверити да ли постоји излиставање тих рокова коришћењем {{рокови}} шаблона на страници предмета у одељку за потребну помоћ (како би се знало да нерешени рокови постоје). -->
'''Први колоквијум 2024. године''' одржан је 27. октобра 2024. године. Биле су доступне презентације о свим пројектним обрасцима, као и ''Java'' документација. Време за израду је било 120 минута.
'''К1 2024. године''' Доступне су презентације о свим пројектним обрасцима, као и Java документација. Време за израду је 120 мин. Текстови задатака нису са оригиналног теста већ су препричани. За сваки задатак потребно је доставити Java фајлове и текстуални фајл који садржи опис пројектних узорака и улога њихових учесника.


== 1. задатак ==
== 1. задатак ==
=== Поставка ===
=== Поставка ===
Направити програм за рад са сликама. Слика је дефинисана као низ пиксела, где се сваки пиксел састоји од три целобројне вредности (R, G и B), с тим да су сви бројеви у опсегу од 0 до 255. Имплементација пиксела приказана је у следећој класи:
Посматра се систем за приказ и обраду дигиталних слика. Треба обезбедити то да се на оригиналну слику може применити произвољан број филтера (или ниједан), у произвољном редоследу, при чему остатак система који приказује и користи слику (“клијент”) не треба да види разлику у односу на то да ли су на слику примењени филтери или нису. Свака слика садржи низ пиксела. Пиксел има R, G и B компоненте (интензитет црвене, зелене и плаве боје), где свака има целобројну вредност од 0 до 255. Филтери који могу да се примене у овом систему су за сада (касније могу бити осмишљени и додати нови филтери):
* ''Greyscale'' – сваки пиксел се претвара у одговарајућу нијансу сиве, где се R, G и B компоненте рачунају по следећој формули: <code>R, G, B = 0.3 * R + 0.59 * G + 0.11 * B</code>
* ''Invert'' – R, G и B компоненте сваког пиксела се инвертују по следећој формули: <code>R = 255 – R, G = 255 – G, B = 255 – B</code>
 
Клијент који користи слику може да прочита вредности њених пиксела; уколико су на слику примењени филтери, то су вредности пиксела оригиналне слике измењене дејством додатих филтера (у случају да ниједан филтер није примењен над оригиналном сликом, враћа се неизмењен низ пиксела оригиналне слике). Коришћењем пројектног обрасца Декоратер (Decorator) потребно је имплементирати описани део система и навести расподелу улога из обрасца класама у овом решењу; опис дати у текстуалном фајлу z1.txt.
 
Написати пример коришћења дате сарадње. Направити слику са филтерима Greyscale и Invert, са неким насумичним вредностима пиксела, а затим дохватити пикселе такве слике и исписати их. Имплементација пиксела приказана је у следећој класи:


<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
Ред 46: Ред 51:
</syntaxhighlight>
</syntaxhighlight>


Kорисник може читати пикселе са слике и додавати произвољан број филтера слици, али тако да разлика између обичне слике и слике са примењеним филтерима није видљива кориснику. Потребно је имплементирати два конкретна филтера:
=== Решење ===
==== Сарадња ====
У овом задатку потребно је користити узорак Декоратер, а у изради се користио и узорак Шаблонски метод.


'''Greyscale''': R, G, B = 0.2 * R + 0.5 * G + 0.3 * B
; Декоратер
: Учесници:
:* Компонента (Image)
:* Субјекат (RawImage)
:* Допуна (Filter)
:* КонкретнаДопуна (Greyscale, Inverse)
; Шаблонски метод
: Учесници:
:* АпстрактнаКласа (Filter)
:* КонкретнаКласа (Greyscale, Inverse)


'''Inverse''': R = 255 - R, G = 255 - G, B = 255 - B
==== <code>Image.java</code> ====
<syntaxhighlight lang="java">
package z1;


Потребно је написати пример сарадње ових класа. На произвољну слику треба применити оба филтера, Greyscale и Inverse, и исписати све пикселе након примене филтера.
import java.util.List;


=== Решење ===
public interface Image {
    public List<Pixel> getPixels();
}
</syntaxhighlight>


==== Сарадња ====
==== <code>RawImage.java</code> ====
У овом задатку потребно је користити узорак Декоратер.
<syntaxhighlight lang="java">
package z1;


Декоратер
import java.util.ArrayList;
import java.util.List;


Учесници: Компонента (Picture), Субјекат (RawPicture), Допуна (Filter), КонкретнаДопуна (Greyscale, Inverse).
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;
    }
}
</syntaxhighlight>
 
==== <code>Filter.java</code> ====
<syntaxhighlight lang="java">
package z1;


Учесници: АпстрактнаКласа (Filter), КонкретнаКласа (Greyscale, Inverse).
import java.util.ArrayList;
import java.util.List;


==== Код ====
public abstract class Filter implements Image{
    private Image image; //image that we wrap/decorate


Klasa '''Picture''':
    public Filter(Image image){
<syntaxhighlight lang="java">
        this.image = image;
package zad1;
    }


import java.util.List;
    protected abstract Pixel modifyPixel(Pixel p);


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


    @Override
     public List<Pixel> getPixels() {
     public List<Pixel> getPixels() {
         return pixels;
         return applyFilter(this.image.getPixels());
    }
}
</syntaxhighlight>
 
==== <code>Greyscale.java</code> ====
<syntaxhighlight lang="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
        );
    }
}
}
</syntaxhighlight>
</syntaxhighlight>


Klasa '''RawPicture''':
==== <code>Invert.java</code> ====
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
package zad1;
package z1;


import java.util.List;
public class Invert extends Filter {
    public Invert(Image i) {
        super(i);
    }


public class RawPicture extends Picture {
    private final int invertFactor = 255;


     public RawPicture(List<Pixel> pixels) {
     protected Pixel modifyPixel(Pixel p) {
this.pixels = pixels;
        return new Pixel(
                invertFactor - p.getR(),
                invertFactor - p.getG(),
                invertFactor - p.getB()
        );
     }
     }


Ред 102: Ред 169:
</syntaxhighlight>
</syntaxhighlight>


Klasa '''Filter''':
==== <code>Main.java</code> ====
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
package zad1;
package z1;


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


public abstract class Filter extends Picture {
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());
        }
    }
}
</syntaxhighlight>
 
== 2. задатак ==
=== Поставка ===
Посматра се део система који симулира карташку игру. Пре почетка игре, играчи могу да се пријаве делиоцу карата („крупијеу“) за учествовање у игри и могу да се одјаве након завршене игре. Игра се игра тако да у њој може учествовати различит број играча и током игре играчи не разговарају један са другим. Број играча је увек довољно мали да је са њима прихватљива синхрона комуникација. Након што се игра покрене, сваки играч од крупијеа добија по две насумичне карте (којих је само он свестан). Потом крупије открива једну од преосталих карата и сви играчи се обавештавају о карти која је откривена. На основу ових информација, сваки играч може да одлучи колико ће новца уложити. Неки играчи играју агресивно, а неки штедљиво; начин играња може да се конфигурише за сваког играча различито, при чему се предвиђа осмишљавање нових различитих начина игре, па различити играчи са истим информацијама улажу различиту количину новца. Након што сви играчи уложе свој новац, следи поново откривање нове карте око које се играчи обавештавају и након чега сваки може поново да уложи новац. Ово се понавља пет пута по игри, на крају чега играч са најбољом комбинацијом карата добија сав новац и игра се завршава. Логика одабира победника се такође може мењати од игре до игре, мада конкретна имплементација ове логике није од интереса за овај задатак.


public Filter(Picture picture) {
Дорадити и исправити дату имплементацију система коришћењем одговарајућих пројектних образаца. Навести све примене пројектних образаца у систему са кратким образложењем у текстуалном фајлу z2.txt (једна до две реченице).
pixels = new ArrayList<Pixel>();
for (Pixel pixel : picture.getPixels()) {
pixels.add(applyFilter(pixel));
}
}


public abstract Pixel applyFilter(Pixel pixel);
=== Решење ===
==== Сарадња ====
У овом задатку су искоришћени узорци Уникат, Посматрач и Стратегија.
 
; Уникат
: Учесници:
:* Уникат (Croupier)
; Посматрач
: Учесници:
:* КонкретанСубјекат (Croupier)
:* КонкретанПосматрач (Player)
:* С обзиром на то да се Крупије ослања на познавање Играча, звањем методе за постављање "руке", изабран је нешто запрљанији приступ овог обрасца, тј. да је ово засновано на Посматрачу.
; Стратегија:
: Учесници:
:* Контекст (Player)
:* Стратегија (PlayingStrategy)
:* КонкретнаСтратегија (AggresiveStrategy, ScroogeStrategy)
 
С обзиром на то да ће реалистично играчи стратегије заснивати на основу конкретних карата, сваки пут ће стратегија анализирати све карте на столу. У реалној имплементацији система, вероватно је скупо сваки пут довлачити све карте (крупије вероватно не би био на нашем рачунару), оптималније је чувати све карте код себе.
 
==== <code>Card.java</code> ====
<syntaxhighlight lang="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];
    }
}
}
</syntaxhighlight>
</syntaxhighlight>


Klasa '''Greyscale''':
==== <code>PlayingStrategy.java</code> ====
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
package zad1;
package z2;
 
import java.util.List;
 
public abstract class PlayingStrategy {
    public abstract double playAMove(Card[] myCards, List<Card> tableCards);
}
</syntaxhighlight>


public class Greyscale extends Filter {
==== <code>ScroogeStrategy.java</code> ====
<syntaxhighlight lang="java">
package z2;


public Greyscale(Picture picture) {
import java.util.List;
super(picture);
}


@Override
public class ScroogeStrategy extends PlayingStrategy {
public Pixel applyFilter(Pixel pixel) {
    @Override
int color = (int) (pixel.getR() * 0.2 + pixel.getG() * 0.5 + pixel.getB() * 0.3);
    public double playAMove(Card[] cardsInHand, List<Card> tableCards) {
return new Pixel(color, color, color);
        double bettingMoney;
}
        int numCardsOnTable = tableCards.size();
        bettingMoney = numCardsOnTable * 0.2 + cardsInHand.length * 0.1;
        return bettingMoney;
    }
}
}
</syntaxhighlight>
</syntaxhighlight>


Klasa '''Inverse''':
==== <code>AggresiveStrategy.java</code> ====
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
package zad1;
package z2;


public class Inverse extends Filter {
import java.util.List;
public Inverse(Picture picture) {
super(picture);
}


@Override
public class AggresiveStrategy extends PlayingStrategy {
public Pixel applyFilter(Pixel pixel) {
    @Override
return new Pixel(255 - pixel.getR(), 255 - pixel.getG(), 255 - pixel.getB());
    public double playAMove(Card[] cardsInHand, List<Card> tableCards) {
}
        double bettingMoney;
        int numCardsOnTable = tableCards.size();
        bettingMoney = numCardsOnTable * 0.5 + cardsInHand.length * 0.3;
        return bettingMoney;
    }
}
}
</syntaxhighlight>
</syntaxhighlight>


Klasa '''Main'''
==== <code>Croupier.java</code> ====
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
package zad1;
package z2;


import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
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;
    }


public class Main {
    private Player defineWinner() {
public static void main(String[] args) {
        // TODO: Should be a few declaring winner tactics, not currently important.
List<Pixel> pixels = Arrays.asList(new Pixel(100, 0, 50), new Pixel(255, 255, 0), new Pixel(0, 255, 0));
        return playersInGame.get(randomGenerator.nextInt(playersInGame.size()));
        Picture raw = new RawPicture(pixels);
    }


Picture picture = new Greyscale(new Inverse(raw));
    private Card drawCardFromDeck() {
        return new Card(
for (Pixel pixel : picture.getPixels()) {
                randomGenerator.nextInt(NUMBER_OF_CARDS),
System.out.println("R = " + pixel.getR() + "\tG = " + pixel.getG() + "\tB = " + pixel.getB());
                randomGenerator.nextInt(NUMBER_OF_SUITS)
}
        );
}
    }
}
}
</syntaxhighlight>
</syntaxhighlight>


== 2. задатак ==
==== <code>Player.java</code> ====
=== Поставка ===
<syntaxhighlight lang="java">
{{делимично решено}}
package z2;
Разматра се карташка игра са следећим правилима: пре почетка игре, играчи се могу пријавити, а по завршетку игре могу се одјавити. Играчи не комуницирају међусобно, већ само са крупијеом. Због малог броја играча, синхронизација у комуникацији им не представља проблем. Када игра започне, крупије дели сваком играчу по две карте (које види само тај играч). Затим крупије открива једну карту коју сви играчи виде и, на основу те карте, остали играчи полажу улоге. Неки играчи имају агресивнији приступ, док су други резервисанији. Понашање играча може се прилагођавати.
 
import java.util.ArrayList;
import java.util.List;
 
public class Player {
    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;
    }
 
    public double update(Card newCard) {
        tableCards.add(newCard);
        double bettingMoney;
        bettingMoney = playingStrategy.playAMove(cardsInHand, tableCards);
        bettingMoney = bettingMoney > money ? money : bettingMoney;
        money -= bettingMoney;
        return bettingMoney;
    }


Крупије потом открива још једну карту, а играчи поново улажу. Овај процес се понавља укупно пет пута, након чега се бира победник који узима сав новац. Начин избора победника такође се може мењати, али та логика није релевантна за ову апликацију.
}
=== Решење ===
</syntaxhighlight>


[[Категорија:Рокови]]
[[Категорија:Рокови]]
[[Категорија:Пројектовање софтвера]]
[[Категорија:Пројектовање софтвера]]

Тренутна верзија на датум 2. децембар 2025. у 01:36

Први колоквијум 2024. године одржан је 27. октобра 2024. године. Биле су доступне презентације о свим пројектним обрасцима, као и Java документација. Време за израду је било 120 минута.

1. задатак

Поставка

Посматра се систем за приказ и обраду дигиталних слика. Треба обезбедити то да се на оригиналну слику може применити произвољан број филтера (или ниједан), у произвољном редоследу, при чему остатак система који приказује и користи слику (“клијент”) не треба да види разлику у односу на то да ли су на слику примењени филтери или нису. Свака слика садржи низ пиксела. Пиксел има R, G и B компоненте (интензитет црвене, зелене и плаве боје), где свака има целобројну вредност од 0 до 255. Филтери који могу да се примене у овом систему су за сада (касније могу бити осмишљени и додати нови филтери):

  • Greyscale – сваки пиксел се претвара у одговарајућу нијансу сиве, где се R, G и B компоненте рачунају по следећој формули: R, G, B = 0.3 * R + 0.59 * G + 0.11 * B
  • Invert – R, G и B компоненте сваког пиксела се инвертују по следећој формули: R = 255 – R, G = 255 – G, B = 255 – B

Клијент који користи слику може да прочита вредности њених пиксела; уколико су на слику примењени филтери, то су вредности пиксела оригиналне слике измењене дејством додатих филтера (у случају да ниједан филтер није примењен над оригиналном сликом, враћа се неизмењен низ пиксела оригиналне слике). Коришћењем пројектног обрасца Декоратер (Decorator) потребно је имплементирати описани део система и навести расподелу улога из обрасца класама у овом решењу; опис дати у текстуалном фајлу z1.txt.

Написати пример коришћења дате сарадње. Направити слику са филтерима Greyscale и Invert, са неким насумичним вредностима пиксела, а затим дохватити пикселе такве слике и исписати их. Имплементација пиксела приказана је у следећој класи:

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;
    }
}

Решење

Сарадња

У овом задатку потребно је користити узорак Декоратер, а у изради се користио и узорак Шаблонски метод.

Декоратер
Учесници:
  • Компонента (Image)
  • Субјекат (RawImage)
  • Допуна (Filter)
  • КонкретнаДопуна (Greyscale, Inverse)
Шаблонски метод
Учесници:
  • АпстрактнаКласа (Filter)
  • КонкретнаКласа (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. задатак

Поставка

Посматра се део система који симулира карташку игру. Пре почетка игре, играчи могу да се пријаве делиоцу карата („крупијеу“) за учествовање у игри и могу да се одјаве након завршене игре. Игра се игра тако да у њој може учествовати различит број играча и током игре играчи не разговарају један са другим. Број играча је увек довољно мали да је са њима прихватљива синхрона комуникација. Након што се игра покрене, сваки играч од крупијеа добија по две насумичне карте (којих је само он свестан). Потом крупије открива једну од преосталих карата и сви играчи се обавештавају о карти која је откривена. На основу ових информација, сваки играч може да одлучи колико ће новца уложити. Неки играчи играју агресивно, а неки штедљиво; начин играња може да се конфигурише за сваког играча различито, при чему се предвиђа осмишљавање нових различитих начина игре, па различити играчи са истим информацијама улажу различиту количину новца. Након што сви играчи уложе свој новац, следи поново откривање нове карте око које се играчи обавештавају и након чега сваки може поново да уложи новац. Ово се понавља пет пута по игри, на крају чега играч са најбољом комбинацијом карата добија сав новац и игра се завршава. Логика одабира победника се такође може мењати од игре до игре, мада конкретна имплементација ове логике није од интереса за овај задатак.

Дорадити и исправити дату имплементацију система коришћењем одговарајућих пројектних образаца. Навести све примене пројектних образаца у систему са кратким образложењем у текстуалном фајлу z2.txt (једна до две реченице).

Решење

Сарадња

У овом задатку су искоришћени узорци Уникат, Посматрач и Стратегија.

Уникат
Учесници:
  • Уникат (Croupier)
Посматрач
Учесници:
  • КонкретанСубјекат (Croupier)
  • КонкретанПосматрач (Player)
  • С обзиром на то да се Крупије ослања на познавање Играча, звањем методе за постављање "руке", изабран је нешто запрљанији приступ овог обрасца, тј. да је ово засновано на Посматрачу.
Стратегија
Учесници:
  • Контекст (Player)
  • Стратегија (PlayingStrategy)
  • КонкретнаСтратегија (AggresiveStrategy, ScroogeStrategy)

С обзиром на то да ће реалистично играчи стратегије заснивати на основу конкретних карата, сваки пут ће стратегија анализирати све карте на столу. У реалној имплементацији система, вероватно је скупо сваки пут довлачити све карте (крупије вероватно не би био на нашем рачунару), оптималније је чувати све карте код себе.

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 {
    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;
    }

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

}