diff --git a/pom.xml b/pom.xml
index f13558a..ab54160 100644
--- a/pom.xml
+++ b/pom.xml
@@ -88,9 +88,9 @@
org.apache.maven.plugins
maven-compiler-plugin
- 8
- 8
-
+ 10
+ 10
+
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/CheckoutAdmin.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/CheckoutAdmin.java
new file mode 100644
index 0000000..7c47b05
--- /dev/null
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/CheckoutAdmin.java
@@ -0,0 +1,79 @@
+package ru.hilariousstartups.javaskills.psplayer;
+
+import lombok.extern.slf4j.Slf4j;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.CheckoutLine;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.Employee;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Управляет кассирами на кассах: следит, кто когда ушел отдыхать,
+ * когда может работать снова, кого именно сажать на кассу.
+ * Используется в стратегии "не увольнять сразу"
+ */
+@Slf4j
+public class CheckoutAdmin {
+
+ private List employees;
+ private List timesheet;
+ private boolean notFailed = false;
+
+ public CheckoutAdmin(List employees) {
+ this.employees = new ArrayList<>(employees);
+ init();
+ }
+
+ private void init() {
+ if (employees == null || employees.isEmpty()) {
+ timesheet = new ArrayList<>();
+ return;
+ }
+ timesheet = new ArrayList<>(employees.size());
+ employees.forEach(it -> timesheet.add(new EmployeeWorkTable(it)));
+ }
+
+ public Employee sendToWorkAny(int currentTime) {
+ var candidate = timesheet.stream()
+ .filter(it -> it.isReadyToWork(currentTime))
+ .findAny().orElse(null);
+ if (candidate == null) {
+ if (notFailed || currentTime % 120 == 0) {
+ log.warn("Not enough employees to work at=" + currentTime + ", " + timesheet);
+ }
+ notFailed = false;
+ return null;
+ }
+ notFailed = true;
+ candidate.startWork(currentTime);
+ return candidate.getEmployee();
+ }
+
+ // TODO add tests
+ // FIXME учесть уволенных
+ public void considerNew(List employeeList,
+ List checkoutLines,
+ Integer tickCount) {
+ var newWorkers = new ArrayList<>(employeeList);
+ newWorkers.removeAll(employees);
+ employees.addAll(newWorkers);
+ for (Employee newbie : newWorkers) {
+ var alreadyAssigned = checkoutLines.stream()
+ .anyMatch(line -> newbie.getId().equals(line.getEmployeeId()));
+ final EmployeeWorkTable newbieWorkTable = new EmployeeWorkTable(newbie);
+ if (alreadyAssigned) {
+ newbieWorkTable.startWork(tickCount);
+ }
+ timesheet.add(newbieWorkTable);
+ log.info(newbieWorkTable.toString());
+ }
+ }
+
+ public List getEmployees() {
+ return employees;
+ }
+
+ public List getTimesheet() {
+ return timesheet;
+ }
+}
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/EmployeeWorkTable.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/EmployeeWorkTable.java
new file mode 100644
index 0000000..145d538
--- /dev/null
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/EmployeeWorkTable.java
@@ -0,0 +1,97 @@
+package ru.hilariousstartups.javaskills.psplayer;
+
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.Employee;
+
+class EmployeeWorkTable {
+ private final static int HOUR_IN_TICKS = 60;
+ private final static int WORK_SHIFT_IN_TICKS = 8 * HOUR_IN_TICKS;
+ private final static int REST_TIME_IN_TICKS = 16 * HOUR_IN_TICKS;
+
+ @NonNull
+ private Employee employee;
+ private Integer startedWork = null;
+
+ public EmployeeWorkTable(Employee employee) {
+ this.employee = employee;
+ }
+
+ // probably change return type
+ public boolean startWork(int currentTime) {
+ if (isReadyToWork(currentTime)) {
+ startedWork = currentTime;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return time (in ticks) when this employee started his last work shift.
+ * Null if it never works.
+ */
+ @Nullable
+ public Integer getStartedWork() {
+ return startedWork;
+ }
+
+ @NonNull
+ public Employee getEmployee() {
+ return employee;
+ }
+
+ /**
+ * @return time (in ticks) when this employee will finish his work shift.
+ * Null if it never works
+ */
+ @Nullable
+ public Integer getFinishedWork() {
+ if (startedWork == null) {
+ return null;
+ }
+ return startedWork + WORK_SHIFT_IN_TICKS;
+ }
+
+ /**
+ * @return time (in ticks) when this employee could start to work.
+ * Null if it never works and could start immediately
+ */
+ @Nullable
+ public Integer getReadyToWork() {
+ if (startedWork == null) {
+ return null;
+ }
+ return getFinishedWork() + REST_TIME_IN_TICKS;
+ }
+
+ public boolean isReadyToWork(int currentTime) {
+ if (startedWork == null) {
+ return true;
+ }
+ return getReadyToWork() <= currentTime;
+ }
+
+ public static int getWorkShiftInTicks() {
+ return WORK_SHIFT_IN_TICKS;
+ }
+
+ public static int getRestTimeInTicks() {
+ return REST_TIME_IN_TICKS;
+ }
+
+ public static int getHourInTicks() {
+ return HOUR_IN_TICKS;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuffer sb = new StringBuffer("EmployeeWorkTable{");
+ sb.append("employee(").append(employee.getId()).append(")");
+ sb.append(" ").append(employee.getFirstName());
+ sb.append(", exp=").append(employee.getExperience());
+ sb.append(", $").append(employee.getSalary());
+ sb.append(", started=").append(startedWork);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/HRDepartment.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/HRDepartment.java
new file mode 100644
index 0000000..e703d6f
--- /dev/null
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/HRDepartment.java
@@ -0,0 +1,49 @@
+package ru.hilariousstartups.javaskills.psplayer;
+
+import lombok.extern.slf4j.Slf4j;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.Employee;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.EmployeeRecruitmentOffer;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.HireEmployeeCommand;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Отвечает за найим и увольнение
+ */
+@Slf4j
+public class HRDepartment {
+
+ private List recruitmentAgency;
+ private List employees;
+ private int checkoutCount; // сколько всего касс
+
+ public HRDepartment(List recruitmentAgency, List employees, int checkoutCount) {
+ this.recruitmentAgency = recruitmentAgency;
+ this.employees = employees;
+ this.checkoutCount = checkoutCount;
+ }
+
+
+ public List getRecruitmentAgency() {
+ return recruitmentAgency;
+ }
+
+ public List firstHire() {
+ List result = new ArrayList<>(recruitmentAgency.size());
+ for (int i = 0; i < 3 && i < recruitmentAgency.size(); i++) {
+ var offer = recruitmentAgency.get(i);
+ result.add(new HireEmployeeCommand()
+ .experience(HireEmployeeCommand.ExperienceEnum.fromValue(offer.getEmployeeType()))
+
+ );
+
+ }
+ // TODO получать реальный id
+ result.get(0).checkoutLineId(1);
+ log.debug("Всего предложений:" + recruitmentAgency.size());
+ log.debug(recruitmentAgency.toString());
+ log.debug(result.toString());
+ return result;
+ }
+}
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/Merchandaizer.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/Merchandaizer.java
new file mode 100644
index 0000000..f625b7e
--- /dev/null
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/Merchandaizer.java
@@ -0,0 +1,147 @@
+package ru.hilariousstartups.javaskills.psplayer;
+
+import lombok.extern.slf4j.Slf4j;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Выкладывает товар на полки, определяет наценки.
+ * Будет ли заниматься заказом товара - пока не знаю
+ */
+@Slf4j
+public class Merchandaizer {
+
+ public enum Margin {
+ DOLLAR_ONE, PERCENT_10, PERCENT_20, PERCENT_50, PERCENT_100;
+
+ public Margin next() {
+ switch (this) {
+ case DOLLAR_ONE:
+ return PERCENT_10;
+ case PERCENT_10:
+ return PERCENT_20;
+ case PERCENT_20:
+ return PERCENT_50;
+ case PERCENT_50:
+ return PERCENT_100;
+ case PERCENT_100:
+ return PERCENT_100;
+ default:
+ return DOLLAR_ONE; // never happens
+ }
+ }
+ }
+
+ private static final Integer DEFAULT_QUANTITY = 100;
+ private List racks;
+ private List stock;
+ private List productsOnRack;
+ private List racksToPutOff;
+
+ public Merchandaizer(List racks, List stock) {
+ this.racks = new ArrayList<>(racks);
+ this.stock = new ArrayList<>(stock);
+ this.stock.sort(Comparator.comparing(Product::getStockPrice));
+ }
+
+ public List removeSold() {
+ var result = racksToPutOff.stream().map(id ->
+ new PutOffRackCellCommand().rackCellId(id)
+ ).collect(Collectors.toList());
+ return result;
+ }
+
+ public List inspectRacks(CurrentWorldResponse world) {
+ racksToPutOff = new ArrayList<>();
+ racks = world.getRackCells();
+ stock = world.getStock();
+ stock.sort(Comparator.comparing(Product::getStockPrice).reversed());
+ productsOnRack = racks.stream()
+ .filter(r -> r.getProductId() != null)
+ .map(RackCell::getProductId)
+ .collect(Collectors.toList());
+
+ List result = new ArrayList<>();
+
+ racks.stream()
+ .filter(rack -> rack.getProductId() == null || rack.getProductQuantity().equals(0))
+ .forEach(rack -> {
+ PutOnRackCellCommand command;
+ if (rack.getProductId() == null) {
+ command = putNextFromStock(rack);
+ } else {
+ command = addSame(rack);
+ }
+ result.add(command);
+ });
+ return result;
+ }
+
+ private PutOnRackCellCommand addSame(RackCell rack) {
+ Product productToPutOnRack = stock.stream()
+ .filter(it -> it.getId().equals(rack.getProductId()))
+ .findAny()
+ .orElseThrow(() -> new IllegalStateException("Product is lost on stock:" + rack.getProductId()));
+
+ final Integer inStock = productToPutOnRack.getInStock();
+ int rackCapacity = rack.getCapacity();
+ double sellPrice = definePrice(productToPutOnRack);
+ return new PutOnRackCellCommand().productId(productToPutOnRack.getId())
+ .rackCellId(rack.getId())
+ .productQuantity(Math.min(inStock, rackCapacity))
+ .sellPrice(sellPrice);
+ }
+
+
+ private PutOnRackCellCommand putNextFromStock(RackCell rack) {
+ Product productToPutOnRack;
+ productToPutOnRack = stock.stream()
+ .filter(product -> !productsOnRack.contains(product.getId())
+ && product.getInStock() > 0)
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("Store is empty"));
+ productsOnRack.add(productToPutOnRack.getId());
+ racksToPutOff.add(rack.getId());
+ final Integer inStock = productToPutOnRack.getInStock();
+ int rackCapacity = rack.getCapacity();
+ double sellPrice = definePrice(productToPutOnRack);
+ return new PutOnRackCellCommand().productId(productToPutOnRack.getId())
+ .rackCellId(rack.getId())
+ .productQuantity(Math.min(inStock, rackCapacity))
+ .sellPrice(sellPrice);
+ }
+
+ private double definePrice(Product product) {
+ if (product.getSellPrice() == null) {
+ return product.getStockPrice() + 1;
+ }
+ return product.getSellPrice() * 1.1; // add 10%
+ }
+
+ public List inspectStore() {
+ List result = new ArrayList<>();
+ stock.stream().filter(it -> it.getInStock().equals(0))
+ .forEach(product -> result.add(new BuyStockCommand()
+ .productId(product.getId())
+ .quantity(DEFAULT_QUANTITY)
+ ));
+ if (result.size() > 5) {
+ return result;
+ } else {
+ return Collections.emptyList();// избегаем мелких закупок
+ }
+ }
+
+ public List initialBuyIn() {
+ List buyStockCommands = new ArrayList<>(50);
+ stock.forEach(it -> buyStockCommands.add(
+ new BuyStockCommand().productId(it.getId()).quantity(DEFAULT_QUANTITY)
+ ));
+ return buyStockCommands;
+ }
+}
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayer.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayer.java
index c50db83..6303e7f 100644
--- a/src/main/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayer.java
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayer.java
@@ -10,8 +10,7 @@
import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.api.PerfectStoreEndpointApi;
import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.*;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
import java.util.stream.Collectors;
@Component
@@ -20,6 +19,13 @@ public class PerfectStorePlayer implements ApplicationListener customersOnCheckline = new HashSet<>();
+ private Set awaitingCustomers = new HashSet<>();
+ private Map basketProducts = new HashMap<>(); // productId->product
+ private HRDepartment hrDepartment;
+ private CheckoutAdmin checkoutAdmin;
+ private Merchandaizer merch;
+
public PerfectStorePlayer(@Value("${rs.endpoint:http://localhost:9080}") String serverUrl) {
this.serverUrl = serverUrl;
}
@@ -31,96 +37,163 @@ public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
PerfectStoreEndpointApi psApiClient = new PerfectStoreEndpointApi(apiClient);
- log.info("Игрок готов. Подключаемся к серверу..");
+ log.debug("Игрок готов. Подключаемся к серверу..");
awaitServer(psApiClient);
log.info("Подключение к серверу успешно. Начинаем игру");
+ CurrentWorldResponse currentWorldResponse = null;
try {
- CurrentWorldResponse currentWorldResponse = null;
int cnt = 0;
do {
cnt += 1;
- if (cnt % 120 == 0) {
+ if (cnt % (60 * 4) == 0) {
log.info("Пройден " + cnt + " тик");
+// log.info("Ответ с предыдущего шага="+currentWorldResponse);
+ }
+
+ CurrentTickRequest request = createNextMove(currentWorldResponse);
+ if (cnt > 1 && cnt <= 3) {
+ log.info(request.getBuyStockCommands().toString());
+ log.info(request.getPutOnRackCellCommands().toString());
}
if (currentWorldResponse == null) {
currentWorldResponse = psApiClient.loadWorld();
+ // logging world
+ log.debug("Total employees=" + currentWorldResponse.getEmployees().size());
+ log.debug("Кассы" + currentWorldResponse.getCheckoutLines());
+ var productAssortment = currentWorldResponse.getStock().size();
+ var productCountList = currentWorldResponse.getStock().
+ stream().sorted(Comparator.comparingDouble(Product::getStockPrice))
+ .map(Product::getInStock).collect(Collectors.toList());
+ log.debug("Видов товаров=" + productAssortment + ", штук=" + productCountList +
+ ", стоит=" + currentWorldResponse.getStockCosts());
+ log.debug("Полок=" + currentWorldResponse.getRackCells().size());
+ // end of logging
+
+ // init state
+ hrDepartment = new HRDepartment(currentWorldResponse.getRecruitmentAgency(),
+ currentWorldResponse.getEmployees(),
+ currentWorldResponse.getCheckoutLines().size()
+ );
+ checkoutAdmin = new CheckoutAdmin(currentWorldResponse.getEmployees());
+ merch = new Merchandaizer(currentWorldResponse.getRackCells(),
+ currentWorldResponse.getStock());
+
+ // generate initial commands
+ request = new CurrentTickRequest();
+ request.hireEmployeeCommands(hrDepartment.firstHire());
+ request.buyStockCommands(merch.initialBuyIn());
}
- CurrentTickRequest request = new CurrentTickRequest();
-
- List hireEmployeeCommands = new ArrayList<>();
- request.setHireEmployeeCommands(hireEmployeeCommands);
- // Смотрим на каких кассах нет кассира (либо не был назначен, либо ушел с кассы отдыхать), нанимаем новых кассиров и ставим на эти кассы.
- // Нанимаем самых опытных!
- // TODO hire only if here are too many customers
- currentWorldResponse.getCheckoutLines().stream().filter(line -> line.getEmployeeId() == null).forEach(line -> {
- HireEmployeeCommand hireEmployeeCommand = new HireEmployeeCommand();
- hireEmployeeCommand.setCheckoutLineId(line.getId());
- hireEmployeeCommand.setExperience(HireEmployeeCommand.ExperienceEnum.SENIOR);
- hireEmployeeCommands.add(hireEmployeeCommand);
- });
- request.setHireEmployeeCommands(hireEmployeeCommands);
-
- // готовимся закупать товар на склад и выставлять его на полки
- ArrayList buyStockCommands = new ArrayList<>();
- request.setBuyStockCommands(buyStockCommands);
-
- ArrayList putOnRackCellCommands = new ArrayList<>();
- request.setPutOnRackCellCommands(putOnRackCellCommands);
-
- List stock = currentWorldResponse.getStock();
- List rackCells = currentWorldResponse.getRackCells();
-
- // Обходим торговый зал и смотрим какие полки пустые. Выставляем на них товар.
- currentWorldResponse.getRackCells().stream().filter(rack -> rack.getProductId() == null || rack.getProductQuantity().equals(0)).forEach(rack -> {
- Product producttoPutOnRack = null;
- if (rack.getProductId() == null) {
- List productsOnRack = rackCells.stream().filter(r -> r.getProductId() != null).map(RackCell::getProductId).collect(Collectors.toList());
- productsOnRack.addAll(putOnRackCellCommands.stream().map(c -> c.getProductId()).collect(Collectors.toList()));
- producttoPutOnRack = stock.stream().filter(product -> !productsOnRack.contains(product.getId())).findFirst().orElse(null);
- }
- else {
- producttoPutOnRack = stock.stream().filter(product -> product.getId().equals(rack.getProductId())).findFirst().orElse(null);
- }
-
- Integer productQuantity = rack.getProductQuantity();
- if (productQuantity == null) {
- productQuantity = 0;
- }
-
- // Вначале закупим товар на склад. Каждый ход закупать товар накладно, но ведь это тестовый игрок.
- Integer orderQuantity = rack.getCapacity() - productQuantity;
- if (producttoPutOnRack.getInStock() < orderQuantity) {
- BuyStockCommand command = new BuyStockCommand();
- command.setProductId(producttoPutOnRack.getId());
- command.setQuantity(100);
- buyStockCommands.add(command);
- }
-
- // Далее разложим на полки. И сформируем цену. Накинем 10 рублей к оптовой цене
- PutOnRackCellCommand command = new PutOnRackCellCommand();
- command.setProductId(producttoPutOnRack.getId());
- command.setRackCellId(rack.getId());
- command.setProductQuantity(orderQuantity);
- if (producttoPutOnRack.getSellPrice() == null) {
- command.setSellPrice(producttoPutOnRack.getStockPrice() + 10);
- }
- putOnRackCellCommands.add(command);
-
- });
-
- currentWorldResponse = psApiClient.tick(request);
+ currentWorldResponse = psApiClient.tick(new CurrentTickRequest());
+ collectDataFromAnswer(currentWorldResponse);
}
while (!currentWorldResponse.isGameOver());
// Если пришел Game Over, значит все время игры закончилось. Пора считать прибыль
log.info("Я заработал " + (currentWorldResponse.getIncome() - currentWorldResponse.getSalaryCosts() - currentWorldResponse.getStockCosts()) + "руб.");
-
- } catch (ApiException e) {
+ log.info(currentWorldResponse.getStock().toString());
+ log.info(currentWorldResponse.getRackCells().toString());
+ log.info("В корзинах:" + basketProducts.values());
+ log.info("Sold products count=" + basketProducts.size());
+ var awaitingCheckoutCustomersCount = currentWorldResponse.getCustomers().stream().
+ filter(it -> it.getMode().equals(Customer.ModeEnum.WAIT_CHECKOUT)).count();
+ var atCheckoutCustomersCount = currentWorldResponse.getCustomers().stream().
+ filter(it -> it.getMode().equals(Customer.ModeEnum.AT_CHECKOUT)).count();
+ var inHallCustomersCount = currentWorldResponse.getCustomers().stream().
+ filter(it -> it.getMode().equals(Customer.ModeEnum.IN_HALL)).count();
+ log.info(String.format("Customers entered to checkline=%s, at checkout=%s, awaiting=%s, in hall=%s, total=%s",
+ customersOnCheckline.size(),
+ awaitingCheckoutCustomersCount,
+ atCheckoutCustomersCount,
+ inHallCustomersCount,
+ currentWorldResponse.getCustomers().size()
+ ));
+
+ } catch (ApiException | RuntimeException e) {
log.error(e.getMessage(), e);
+ closeStore(psApiClient, currentWorldResponse);
+ }
+
+ }
+
+ private void closeStore(PerfectStoreEndpointApi psApiClient, CurrentWorldResponse currentWorldResponse) {
+ CurrentTickRequest request = new CurrentTickRequest();
+ do {
+ try {
+ currentWorldResponse = psApiClient.tick(request);
+ } catch (ApiException e) {
+ log.error("Failed to process empty request: " + e.getMessage());
+ }
+ } while (!currentWorldResponse.isGameOver());
+ }
+
+ private void collectDataFromAnswer(CurrentWorldResponse worldResponse) {
+ var customers = worldResponse.getCustomers();
+ var awaitingCheckoutCustomers = customers.stream().
+ filter(it -> it.getMode().equals(Customer.ModeEnum.WAIT_CHECKOUT)).
+ collect(Collectors.toList());
+ var atCheckoutCustomers = customers.stream().
+ filter(it -> it.getMode().equals(Customer.ModeEnum.AT_CHECKOUT)).
+ collect(Collectors.toList());
+ var newAwaitingCheckoutProducts = awaitingCheckoutCustomers.stream().
+ filter(it -> !awaitingCustomers.contains(it)).
+ map(it -> it.getBasket()).
+ collect(ArrayList::new, List::addAll, List::addAll);
+ for (ProductInBasket product : newAwaitingCheckoutProducts) {
+ if (basketProducts.containsKey(product.getId())) {
+ var countedProduct = basketProducts.get(product.getId());
+ countedProduct.setProductCount(countedProduct.getProductCount() + product.getProductCount());
+ } else {
+ basketProducts.put(product.getId(), product);
+ }
+ }
+ awaitingCustomers.addAll(awaitingCheckoutCustomers);
+ customersOnCheckline.addAll(atCheckoutCustomers);
+
+ }
+
+ private CurrentTickRequest createNextMove(CurrentWorldResponse worldResponse) {
+ if (worldResponse == null) { // первый ход обрабатывается отдельно
+ return null;
+ }
+ CurrentTickRequest request = new CurrentTickRequest();
+
+ checkoutAdmin.considerNew(worldResponse.getEmployees(),
+ worldResponse.getCheckoutLines(),
+ worldResponse.getCurrentTick());
+ inspectChecklines(worldResponse, request);
+
+ // готовимся закупать товар на склад и выставлять его на полки
+ final List putOnRackCellCommands = merch.inspectRacks(worldResponse);
+ final List buyStockCommands = merch.inspectStore();
+ final List putOffRackCellCommands = merch.removeSold();
+ request.setPutOffRackCellCommands(putOffRackCellCommands);
+ request.setPutOnRackCellCommands(putOnRackCellCommands);
+ request.buyStockCommands(buyStockCommands);
+ System.out.println("balbla");
+ return request;
+ }
+
+ /**
+ * Здесь будет вся работа по управлению кассами: слежение за свободными кассами, ротация кассиров
+ *
+ * @param worldResponse
+ * @param request
+ */
+ private void inspectChecklines(CurrentWorldResponse worldResponse, CurrentTickRequest request) {
+ // Стратегия "одна работающая касса", кассиры не увольняются
+ var singleCheckline = worldResponse.getCheckoutLines().get(0);
+ if (singleCheckline.getEmployeeId() == null) {
+ var employee = checkoutAdmin.sendToWorkAny(worldResponse.getCurrentTick());
+ if (employee != null) {
+ request.addSetOnCheckoutLineCommandsItem(
+ new SetOnCheckoutLineCommand().checkoutLineId(singleCheckline.getId()).
+ employeeId(employee.getId())
+ );
+ }
}
}
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/EmployeeRecruitmentOffer.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/EmployeeRecruitmentOffer.java
index 14eca47..c9c70bf 100644
--- a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/EmployeeRecruitmentOffer.java
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/EmployeeRecruitmentOffer.java
@@ -12,15 +12,10 @@
package ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model;
-import java.util.Objects;
-import java.util.Arrays;
-import com.google.gson.TypeAdapter;
-import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
import io.swagger.v3.oas.annotations.media.Schema;
-import java.io.IOException;
+
+import java.util.Objects;
/**
* Кадровое агенство. Справочная информация о том, каких сотрудников можно нанять и по какой ставке
*/
@@ -112,6 +107,16 @@ public int hashCode() {
@Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Type=").append((employeeType));
+ sb.append(", experience: ").append((experience));
+ sb.append(", salary: ").append((salary));
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /*@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class EmployeeRecruitmentOffer {\n");
@@ -121,7 +126,7 @@ public String toString() {
sb.append(" salary: ").append(toIndentedString(salary)).append("\n");
sb.append("}");
return sb.toString();
- }
+ }*/
/**
* Convert the given object to string with each line indented by 4 spaces
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/Product.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/Product.java
index d0de3e3..642fc6c 100644
--- a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/Product.java
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/Product.java
@@ -12,15 +12,10 @@
package ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model;
-import java.util.Objects;
-import java.util.Arrays;
-import com.google.gson.TypeAdapter;
-import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
import io.swagger.v3.oas.annotations.media.Schema;
-import java.io.IOException;
+
+import java.util.Objects;
/**
* Продукт (товар) находится либо на складе либо может быть выложен на полку в магазине. Один продукт может лежать только на одной полке. Продукт на склад можно докупить по закупочной цене. И выставить на полку по любой цене. Чем дороже цена, тем сложнее покупателям купить товар.
*/
@@ -158,14 +153,11 @@ public int hashCode() {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("class Product {\n");
-
- sb.append(" id: ").append(toIndentedString(id)).append("\n");
- sb.append(" name: ").append(toIndentedString(name)).append("\n");
- sb.append(" stockPrice: ").append(toIndentedString(stockPrice)).append("\n");
- sb.append(" inStock: ").append(toIndentedString(inStock)).append("\n");
- sb.append(" sellPrice: ").append(toIndentedString(sellPrice)).append("\n");
- sb.append("}");
+ sb.append("{").append((id)).append("}");
+ sb.append(" ").append((name));
+ sb.append(" stock $").append((stockPrice));
+ sb.append(" inStock=").append((inStock));
+ sb.append(" sell $=").append((sellPrice));
return sb.toString();
}
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/ProductInBasket.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/ProductInBasket.java
index 9e2c663..b9b4e2d 100644
--- a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/ProductInBasket.java
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/ProductInBasket.java
@@ -124,7 +124,7 @@ public boolean equals(java.lang.Object o) {
return Objects.equals(this.id, productInBasket.id) &&
Objects.equals(this.productName, productInBasket.productName) &&
Objects.equals(this.prie, productInBasket.prie) &&
- Objects.equals(this.productCount, productInBasket.productCount);
+ Objects.equals(this.productCount, productInBasket.productCount);
}
@Override
@@ -134,6 +134,17 @@ public int hashCode() {
@Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("product{").append(id).append("}");
+ sb.append(" ").append(productName);
+ sb.append(" $").append(prie);
+ sb.append(" Count=").append(productCount);
+ sb.append(", ");
+ return sb.toString();
+ }
+
+ /*@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class ProductInBasket {\n");
@@ -144,7 +155,7 @@ public String toString() {
sb.append(" productCount: ").append(toIndentedString(productCount)).append("\n");
sb.append("}");
return sb.toString();
- }
+ }*/
/**
* Convert the given object to string with each line indented by 4 spaces
diff --git a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/RackCell.java b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/RackCell.java
index 0a1de7f..4be5cc6 100644
--- a/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/RackCell.java
+++ b/src/main/java/ru/hilariousstartups/javaskills/psplayer/swagger_codegen/model/RackCell.java
@@ -2,25 +2,20 @@
* OpenAPI definition
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
*
- * OpenAPI spec version: v0
- *
+ * OpenAPI spec version=v0
*
- * NOTE: This class is auto generated by the swagger code generator program.
+ *
+ * NOTE=This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
package ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model;
-import java.util.Objects;
-import java.util.Arrays;
-import com.google.gson.TypeAdapter;
-import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
-import com.google.gson.stream.JsonReader;
-import com.google.gson.stream.JsonWriter;
import io.swagger.v3.oas.annotations.media.Schema;
-import java.io.IOException;
+
+import java.util.Objects;
/**
* Продуктовая полка. На полке может находиться товар только одного вида, либо она может быть пустой. Полка может хранить определенное максимальное количество товаров. Также полки разнятся по заметности для покупателя (от 1 - самая незаметная, до 5 - максимально на виду
*/
@@ -180,15 +175,12 @@ public int hashCode() {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("class RackCell {\n");
-
- sb.append(" id: ").append(toIndentedString(id)).append("\n");
- sb.append(" visibility: ").append(toIndentedString(visibility)).append("\n");
- sb.append(" capacity: ").append(toIndentedString(capacity)).append("\n");
- sb.append(" productId: ").append(toIndentedString(productId)).append("\n");
- sb.append(" productName: ").append(toIndentedString(productName)).append("\n");
- sb.append(" productQuantity: ").append(toIndentedString(productQuantity)).append("\n");
- sb.append("}");
+ sb.append("{").append((id)).append("}");
+ sb.append(" visibility=").append((visibility));
+ sb.append(" capacity=").append((capacity));
+ sb.append(" productId=").append((productId));
+ sb.append(" productName=").append((productName));
+ sb.append(" productQuantity=").append((productQuantity));
return sb.toString();
}
diff --git a/src/test/java/ru/hilariousstartups/javaskills/psplayer/CheckoutAdminTest.java b/src/test/java/ru/hilariousstartups/javaskills/psplayer/CheckoutAdminTest.java
new file mode 100644
index 0000000..e27a468
--- /dev/null
+++ b/src/test/java/ru/hilariousstartups/javaskills/psplayer/CheckoutAdminTest.java
@@ -0,0 +1,93 @@
+package ru.hilariousstartups.javaskills.psplayer;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.CheckoutLine;
+import ru.hilariousstartups.javaskills.psplayer.swagger_codegen.model.Employee;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static ru.hilariousstartups.javaskills.psplayer.EmployeeWorkTable.getRestTimeInTicks;
+import static ru.hilariousstartups.javaskills.psplayer.EmployeeWorkTable.getWorkShiftInTicks;
+
+public class CheckoutAdminTest {
+
+ private final static List EMPLOYEE_LIST = Arrays.asList(
+ new Employee().id(1).experience(10).firstName("E1").salary(100),
+ new Employee().id(2).experience(40).firstName("E2").salary(300),
+ new Employee().id(3).experience(70).firstName("E3").salary(560)
+
+ );
+
+ private CheckoutAdmin service = new CheckoutAdmin(EMPLOYEE_LIST);
+
+ @BeforeEach
+ public void setup() {
+ System.out.println("---next test------");
+ }
+
+ @Test
+ public void addNewEmployee() {
+ var newbie = new Employee().id(10).experience(15).firstName("new").salary(250);
+ var employees = new ArrayList<>(EMPLOYEE_LIST);
+ employees.add(newbie);
+ service.considerNew(employees, Collections.emptyList(), 1);
+ final int elementsCount = employees.size();
+ assertEquals(elementsCount, service.getEmployees().size());
+ assertEquals(elementsCount, service.getTimesheet().size());
+ final EmployeeWorkTable actual = service.getTimesheet().get(elementsCount - 1);
+ assertEquals(newbie, actual.getEmployee());
+ assertNull(actual.getStartedWork());
+ assertNull(actual.getFinishedWork());
+ assertNull(actual.getReadyToWork());
+ }
+
+ @Test
+ public void addWorkingEmployee() {
+ final int employeeId = 10;
+ var newbie = new Employee().id(employeeId).experience(15)
+ .firstName("new").salary(250);
+ var employees = new ArrayList<>(EMPLOYEE_LIST);
+ employees.add(newbie);
+ List checkouts = Arrays.asList(new CheckoutLine().employeeId(employeeId));
+ final int hireTime = 1;
+ service.considerNew(employees, checkouts, hireTime);
+ final int elementsCount = employees.size();
+ assertEquals(elementsCount, service.getEmployees().size());
+ assertEquals(elementsCount, service.getTimesheet().size());
+ final EmployeeWorkTable actual = service.getTimesheet().get(elementsCount - 1);
+ assertEquals(newbie, actual.getEmployee());
+ assertEquals(hireTime, actual.getStartedWork());
+ assertEquals(hireTime + getWorkShiftInTicks(), actual.getFinishedWork());
+ assertEquals(hireTime + getWorkShiftInTicks() + getRestTimeInTicks(),
+ actual.getReadyToWork());
+ }
+
+ @Test
+ public void workersPerDay() {
+ int minutesInDay = 60 * 24;
+ assertEquals(minutesInDay, getWorkShiftInTicks() + getRestTimeInTicks());
+ }
+
+ @Test
+ void assignNextEmployeeToWork() {
+ final int firstShift = 2;
+ final int secondShift = firstShift + getWorkShiftInTicks();
+ final int thirdShift = firstShift + 2 * getWorkShiftInTicks();
+ final int fourthShift = firstShift + 3 * getWorkShiftInTicks();
+ var first = service.sendToWorkAny(firstShift);
+ System.out.println("after first " + service.getTimesheet());
+ var second = service.sendToWorkAny(secondShift);
+ System.out.println("after second = " + service.getTimesheet());
+ var third = service.sendToWorkAny(thirdShift);
+ System.out.println("after third = " + service.getTimesheet());
+
+ var fourth = service.sendToWorkAny(fourthShift);
+ assertEquals(first, fourth);
+ }
+}
diff --git a/src/test/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayerApplicationTests.java b/src/test/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayerApplicationTests.java
index 3591699..9cce6a5 100644
--- a/src/test/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayerApplicationTests.java
+++ b/src/test/java/ru/hilariousstartups/javaskills/psplayer/PerfectStorePlayerApplicationTests.java
@@ -1,12 +1,9 @@
package ru.hilariousstartups.javaskills.psplayer;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.SpringBootTest;
-
//@SpringBootTest
class PerfectStorePlayerApplicationTests {
-// @Test
+ // @Test
void contextLoads() {
}