diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml
index 8c507688..d9a5bdfc 100644
--- a/.github/workflows/pr-tests.yml
+++ b/.github/workflows/pr-tests.yml
@@ -36,13 +36,99 @@ jobs:
- name: Copy test files
run: |
- echo "Copying test data"
- mkdir -p src/test/resources
+ echo "Copying hidden tests"
+ mkdir -p src/test/java src/test/resources
+ cp -R hidden-tests/src/test/java/. src/test/java/
cp -R hidden-tests/src/test/resources/. src/test/resources/
echo ""
echo "Скопированы файлы:"
- find src/test/resources -type f | sort | while read f; do echo " $f"; done
+ find src/test/java src/test/resources -type f | sort | while read f; do echo " $f"; done
rm -rf hidden-tests
-
+
+ - name: Select test pattern from commit message
+ id: select_tests
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const pr = context.payload.pull_request;
+ if (!pr) {
+ core.setOutput('pattern', '');
+ return;
+ }
+ const repo = { owner: context.repo.owner, repo: context.repo.repo };
+ const { data: commit } = await github.rest.repos.getCommit({
+ ...repo,
+ ref: pr.head.sha
+ });
+ const msg = (commit.commit.message || '').toLowerCase();
+
+ const patterns = [];
+ const add = (p) => { if (!patterns.includes(p)) patterns.push(p); };
+
+ if (msg.startsWith('atm:')) add('**/atm/*Test');
+ if (msg.startsWith('html:')) add('**/html/*Test');
+ if (msg.startsWith('randomset:')) add('**/randomSet/*Test');
+ if (msg.startsWith('cube:')) add('**/CubeSimpleTest');
+
+ core.setOutput('pattern', patterns.length ? patterns.join(',') : '');
+
+ - name: Run honors no-collections test
+ id: honors_test
+ continue-on-error: true
+ if: steps.select_tests.outputs.pattern == '**/randomSet/*Test'
+ run: mvn -B -Dtest=hse.java.lectures.lecture3.practice.randomSet.RandomSetBytecodeTest test
+
+ - name: Comment on PR for honors solution
+ if: steps.honors_test.outcome == 'success'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const pr = context.payload.pull_request;
+ if (!pr) {
+ core.info('No PR context, skipping comment.');
+ return;
+ }
+ const body = [
+ 'Поздравляем! 🎉',
+ '',
+ 'Вы решили сложную версию: решение прошло проверку «без коллекций».'
+ ].join('\n');
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ body
+ });
+
- name: Run tests
- run: mvn -B test
+ run: |
+ if [ -n "${{ steps.select_tests.outputs.pattern }}" ]; then
+ echo "Running selected tests: ${{ steps.select_tests.outputs.pattern }}"
+ mvn -B -Dtest='${{ steps.select_tests.outputs.pattern }},!**/RandomSetBytecodeTest' test
+ else
+ echo "No task prefix found. Failing."
+ exit 1
+ fi
+
+ - name: Comment on PR when no task prefix
+ if: steps.select_tests.outputs.pattern == ''
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const pr = context.payload.pull_request;
+ if (!pr) {
+ core.info('No PR context, skipping comment.');
+ return;
+ }
+ const body = [
+ 'Тесты не запускались.',
+ '',
+ 'Укажите задачу в начале сообщения коммита:',
+ 'atm: ..., html: ..., randomset: ...'
+ ].join('\n');
+ await github.rest.issues.createComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: pr.number,
+ body
+ });
diff --git a/README-commit-format.md b/README-commit-format.md
new file mode 100644
index 00000000..24c170a4
--- /dev/null
+++ b/README-commit-format.md
@@ -0,0 +1,20 @@
+# Формат коммитов для запуска тестов
+
+## Зачем это нужно
+CI запускает тесты выборочно. Чтобы он знал, какие тесты запускать, сообщение коммита должно начинаться с названия задачи.
+
+## Формат
+Используйте префикс в начале сообщения коммита:
+
+- `atm: ...`
+- `html: ...`
+- `randomset: ...`
+
+Тег задачи указывается в описании каждой задачи
+
+Пример:
+```
+randomset: implement getRandom and contains
+```
+
+Если префикс не указан, CI **не запускает тесты** и оставляет комментарий в PR.
diff --git a/commander/commander.iml b/commander/commander.iml
deleted file mode 100644
index 68a9707e..00000000
--- a/commander/commander.iml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index ade43fe2..648f8788 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,13 @@
${junit.version}
test
+
+
+ org.ow2.asm
+ asm
+ 9.8
+ test
+
com.fasterxml.jackson.core
@@ -65,4 +72,4 @@
-
\ No newline at end of file
+
diff --git a/src/main/java/hse/java/lectures/lecture3/examples/Box.java b/src/main/java/hse/java/lectures/lecture3/examples/Box.java
new file mode 100644
index 00000000..6c3ccf57
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/examples/Box.java
@@ -0,0 +1,14 @@
+package hse.java.lectures.lecture3.examples;
+
+public class Box {
+
+ private T item;
+
+ public void put(T item) {
+ this.item = item;
+ }
+
+ public T get() {
+ return item;
+ }
+}
diff --git a/src/main/java/hse/java/lectures/lecture3/examples/Generics.java b/src/main/java/hse/java/lectures/lecture3/examples/Generics.java
new file mode 100644
index 00000000..4fe8a270
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/examples/Generics.java
@@ -0,0 +1,43 @@
+package hse.java.lectures.lecture3.examples;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Generics {
+
+
+
+ public T getType(T type) {
+ System.out.println(type.getClass());
+ return type;
+ }
+
+ public static void write(List list, T value) {
+ list.add(value);
+ }
+
+ public static > T get(T value) {
+ return value;
+ }
+
+ public static void testGenerics() {
+ // get(1.).compareTo()
+ write(List.of(1), 1);
+ write(List.of(), 1.2);
+ List ints = new ArrayList<>();
+ ints.add(1);
+ ints.add(2);
+ // PECS
+ List extends Number> nums = ints;
+ List super Integer> si = ints;
+ si.add(1);
+ si.get(1);
+ }
+
+ public static void main(String[] args) {
+ List il = new ArrayList<>();
+ write(il, 5);
+ System.out.println(il);
+ }
+
+}
diff --git a/src/main/java/hse/java/lectures/lecture3/examples/Main.java b/src/main/java/hse/java/lectures/lecture3/examples/Main.java
new file mode 100644
index 00000000..116ff543
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/examples/Main.java
@@ -0,0 +1,19 @@
+package hse.java.lectures.lecture3.examples;
+
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+
+public class Main {
+ public static void main(String[] args) {
+ Box box = new Box<>();
+ Box dBox = new Box<>();
+ Callable callable;
+
+ int x = new Integer(3);
+ Integer y = 5;
+
+ Box extends Number> nBox = box;
+ new Methods().get(new ArrayList()).add(1);
+
+ }
+}
diff --git a/src/main/java/hse/java/lectures/lecture3/examples/Methods.java b/src/main/java/hse/java/lectures/lecture3/examples/Methods.java
new file mode 100644
index 00000000..cac365d7
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/examples/Methods.java
@@ -0,0 +1,10 @@
+package hse.java.lectures.lecture3.examples;
+
+public class Methods {
+
+
+ public T get(T type) {
+ return type;
+ }
+
+}
diff --git a/src/main/java/hse/java/lectures/lecture3/examples/TryCatchThrows.java b/src/main/java/hse/java/lectures/lecture3/examples/TryCatchThrows.java
new file mode 100644
index 00000000..0d972ac3
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/examples/TryCatchThrows.java
@@ -0,0 +1,79 @@
+package hse.java.lectures.lecture3.examples;
+
+import java.io.*;
+
+public class TryCatchThrows {
+
+ public void testRuntime() throws RuntimeException {
+ throw new RuntimeException("Test runtime");
+ }
+
+ public void testException() throws Exception {
+ throw new Exception("test exception");
+ }
+
+ public void testError() throws Error {
+ throw new Error("test error");
+ }
+
+ public void testTryRuntime() {
+ try {
+ testRuntime();
+ } catch (RuntimeException e) {
+ System.out.println("Catch " + e.getMessage());
+ } finally {
+ System.out.println("finally");
+ }
+ }
+
+ public void testTryException() {
+ try {
+ testException();
+ } catch (Exception e) {
+ System.out.println("Catch " + e.getMessage());
+ } finally {
+ System.out.println("finally");
+ }
+ }
+
+ public void testTryError() {
+ // testError();
+ try {
+ testError();
+ } catch (Error e) {
+ System.out.println("Catch " + e.getMessage());
+ } finally {
+ System.out.println("finally");
+ }
+ }
+
+ public static void foo() {
+ throw new RuntimeException("My runtime");
+ }
+
+ public static void bar() throws Exception {
+ throw new Exception("My exception");
+ }
+
+ public static void main(String[] args) {
+ // foo();
+// try {
+// bar();
+// } catch (Exception e) {
+// System.err.println("Hello exception");
+// } finally {
+//
+// }
+
+// try (BufferedReader br = new BufferedReader(new FileReader(""))) {
+//
+// } catch (IOException e) {
+// throw new RuntimeException(e);
+// }
+
+ // int x = 1/ 0;
+ int[] a = new int[1];
+ System.out.println(a[2]);
+ }
+
+}
diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/EmptySetException.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/EmptySetException.java
new file mode 100644
index 00000000..c7820ac5
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/EmptySetException.java
@@ -0,0 +1,7 @@
+package hse.java.lectures.lecture3.practice.randomSet;
+
+public class EmptySetException extends RuntimeException {
+ public EmptySetException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java
new file mode 100644
index 00000000..8af477b5
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java
@@ -0,0 +1,21 @@
+package hse.java.lectures.lecture3.practice.randomSet;
+
+public class RandomSet {
+
+ public boolean insert(T value) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public boolean remove(T value) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public boolean contains(T value) {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ public T getRandom() {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+}
diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/task.md b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/task.md
new file mode 100644
index 00000000..fb987780
--- /dev/null
+++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/task.md
@@ -0,0 +1,52 @@
+# Случайное множество (RandomSet)
+
+Tag: randomset
+
+## Условие
+
+Реализовать класс `RandomSet`, который хранит множество целых чисел и поддерживает операции вставки, удаления и получения случайного элемента.
+
+## Требования к асимптотике
+
+- `insert(x)` — **O(log n) or O(1)**
+- `remove(x)` — **O(log n) or O(1)**
+- `contains(x)` — **(log n) or O(1)**
+- `getRandom()` — **O(1)**
+
+Не допускается использование стандартных коллекций Java.
+
+## Публичный API
+
+1. `RandomSet()` — создаёт пустое множество.
+1. `boolean insert(T value)` — добавляет `value` в множество.
+ - Возвращает `true`, если элемент добавлен.
+ - Возвращает `false`, если элемент уже был в множестве.
+1. `boolean remove(T value)` — удаляет `value` из множества.
+ - Возвращает `true`, если элемент был удалён.
+ - Возвращает `false`, если элемента не было.
+1. `boolean contains(T value)` — проверяет наличие `value` в множестве.
+ - Возвращает `true`, если элемент есть.
+ - Возвращает `false`, если элемента нет.
+1. `T getRandom()` — возвращает случайный элемент из множества.
+ - Если множество пустое, выбрасывает `EmptySetException`.
+
+## Исключения
+
+- `EmptySetException` — попытка получить случайный элемент из пустого множества.
+
+## Пример
+
+```java
+RandomSet set = new RandomSet<>();
+set.insert(10); // true
+set.insert(20); // true
+set.insert(10); // false
+
+int x = set.getRandom(); // 10 или 20
+
+set.remove(10); // true
+set.remove(10); // false
+
+set.contains(20); // true
+set.contains(30); // false
+```
diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java
index 3fa91909..08f551e4 100644
--- a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java
+++ b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java
@@ -1,14 +1,9 @@
package hse.java.lectures.lecture3.tasks.atm;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.*;
public class Atm {
- private enum Denomination {
+ public enum Denomination {
D50(50),
D100(100),
D500(500),
@@ -24,6 +19,12 @@ private enum Denomination {
int value() {
return value;
}
+
+ public static Denomination fromInt(int value) {
+ return Arrays.stream(values()).filter(v -> v.value == value)
+ .findFirst()
+ .orElse(null);
+ }
}
private final Map banknotes = new EnumMap<>(Denomination.class);
@@ -31,9 +32,9 @@ int value() {
public Atm() {
}
- public void deposit(Map banknotes){}
+ public void deposit(Map banknotes){}
- public Map withdraw(int amount) {
+ public Map withdraw(int amount) {
return Map.of();
}
diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/atm/task.md b/src/main/java/hse/java/lectures/lecture3/tasks/atm/task.md
index 3845672e..6d574e19 100644
--- a/src/main/java/hse/java/lectures/lecture3/tasks/atm/task.md
+++ b/src/main/java/hse/java/lectures/lecture3/tasks/atm/task.md
@@ -1,5 +1,7 @@
# Банкомат (ATM)
+Tag: atm
+
## Условие
Реализовать класс `Atm`, который хранит купюры фиксированных номиналов и выполняет операции пополнения и выдачи наличных. При ошибках операции должны завершаться выбросом исключения без изменения состояния банкомата.
diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/html/task.md b/src/main/java/hse/java/lectures/lecture3/tasks/html/task.md
index 4ec71b18..2160175f 100644
--- a/src/main/java/hse/java/lectures/lecture3/tasks/html/task.md
+++ b/src/main/java/hse/java/lectures/lecture3/tasks/html/task.md
@@ -1,5 +1,7 @@
# Валидация HTML-документа
+Tag: html
+
## Условие
Реализовать класс `HtmlDocument`, который читает HTML-текст из файла и проверяет корректность структуры. Если документ невалиден, при создании объекта выбрасывается исключение.
diff --git a/src/main/java/hse/java/practice/task1/RubiksCube.java b/src/main/java/hse/java/practice/task1/RubiksCube.java
index 2091b657..160a2406 100644
--- a/src/main/java/hse/java/practice/task1/RubiksCube.java
+++ b/src/main/java/hse/java/practice/task1/RubiksCube.java
@@ -2,23 +2,12 @@
import java.util.Arrays;
-/**
- * Необходимо реализовать интерфейс Cube
- * При повороте передней грани, меняются верх низ право и лево
- */
-public class RubiksCube {
+public class RubiksCube implements Cube{
private static final int EDGES_COUNT = 6;
private final Edge[] edges = new Edge[EDGES_COUNT];
- /**
- * Создать валидный собранный кубик
- * грани разместить по ордеру в енуме цветов
- * грань 0 -> цвет 0
- * грань 1 -> цвет 1
- * ...
- */
public RubiksCube() {
CubeColor[] colors = CubeColor.values();
for (int i = 0; i < 6; i++) {
@@ -26,10 +15,186 @@ public RubiksCube() {
}
}
+ @Override
+ public void up(RotateDirection direction) {
+ Edge[] edges_copy = CopyArray();
+ rotateFace(edges[0], edges_copy[0], direction);
+
+ switch (direction) {
+ case CLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[2].getParts()[0][i] = edges_copy[4].getParts()[0][i];
+ edges[5].getParts()[0][i] = edges_copy[2].getParts()[0][i];
+ edges[3].getParts()[0][i] = edges_copy[5].getParts()[0][i];
+ edges[4].getParts()[0][i] = edges_copy[3].getParts()[0][i];
+ }
+ break;
+
+ case COUNTERCLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[4].getParts()[0][i] = edges_copy[2].getParts()[0][i];
+ edges[2].getParts()[0][i] = edges_copy[5].getParts()[0][i];
+ edges[5].getParts()[0][i] = edges_copy[3].getParts()[0][i];
+ edges[3].getParts()[0][i] = edges_copy[4].getParts()[0][i];
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void down(RotateDirection direction) {
+ Edge[] edges_copy = CopyArray();
+ rotateFace(edges[1], edges_copy[1], direction);
+
+ switch (direction) {
+ case CLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[4].getParts()[2][i] = edges_copy[2].getParts()[2][i];
+ edges[3].getParts()[2][i] = edges_copy[4].getParts()[2][i];
+ edges[5].getParts()[2][i] = edges_copy[3].getParts()[2][i];
+ edges[2].getParts()[2][i] = edges_copy[5].getParts()[2][i];
+ }
+ break;
+
+ case COUNTERCLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[2].getParts()[2][i] = edges_copy[4].getParts()[2][i];
+ edges[4].getParts()[2][i] = edges_copy[3].getParts()[2][i];
+ edges[3].getParts()[2][i] = edges_copy[5].getParts()[2][i];
+ edges[5].getParts()[2][i] = edges_copy[2].getParts()[2][i];
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void left(RotateDirection direction) {
+ Edge[] edges_copy = CopyArray();
+ rotateFace(edges[2], edges_copy[2], direction);
+
+ switch (direction) {
+ case CLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[4].getParts()[i][0] = edges_copy[0].getParts()[i][0];
+ edges[1].getParts()[i][0] = edges_copy[4].getParts()[i][0];
+ edges[5].getParts()[2 - i][2] = edges_copy[1].getParts()[i][0];
+ edges[0].getParts()[i][0] = edges_copy[5].getParts()[2 - i][2];
+ }
+ break;
+
+ case COUNTERCLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[0].getParts()[i][0] = edges_copy[4].getParts()[i][0];
+ edges[4].getParts()[i][0] = edges_copy[1].getParts()[i][0];
+ edges[1].getParts()[i][0] = edges_copy[5].getParts()[2 - i][2];
+ edges[5].getParts()[2 - i][2] = edges_copy[0].getParts()[i][0];
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void right(RotateDirection direction) {
+ Edge[] edges_copy = CopyArray();
+ rotateFace(edges[3], edges_copy[3], direction);
+
+ switch (direction) {
+ case CLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[5].getParts()[2 - i][0] = edges_copy[0].getParts()[i][2];
+ edges[1].getParts()[i][2] = edges_copy[5].getParts()[2 - i][0];
+ edges[4].getParts()[i][2] = edges_copy[1].getParts()[i][2];
+ edges[0].getParts()[i][2] = edges_copy[4].getParts()[i][2];
+ }
+ break;
+
+ case COUNTERCLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[0].getParts()[i][2] = edges_copy[5].getParts()[2 - i][0];
+ edges[5].getParts()[2 - i][0] = edges_copy[1].getParts()[i][2];
+ edges[1].getParts()[i][2] = edges_copy[4].getParts()[i][2];
+ edges[4].getParts()[i][2] = edges_copy[0].getParts()[i][2];
+ }
+ break;
+ }
+ }
+
+ @Override
public void front(RotateDirection direction) {
+ Edge[] edges_copy = CopyArray();
+ rotateFace(edges[4], edges_copy[4], direction);
+ switch (direction) {
+ case CLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[0].getParts()[2][i] = edges_copy[2].getParts()[i][2];
+ edges[1].getParts()[0][i] = edges_copy[3].getParts()[i][0];
+ edges[2].getParts()[i][2] = edges_copy[1].getParts()[0][i];
+ edges[3].getParts()[i][0] = edges_copy[0].getParts()[2][i];
+ }
+ break;
+
+ case COUNTERCLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[2].getParts()[i][2] = edges_copy[0].getParts()[2][i];
+ edges[3].getParts()[i][0] = edges_copy[1].getParts()[0][i];
+ edges[1].getParts()[0][i] = edges_copy[2].getParts()[i][2];
+ edges[0].getParts()[2][i] = edges_copy[3].getParts()[i][0];
+ }
+ break;
+ }
+ }
+
+ @Override
+ public void back(RotateDirection direction) {
+ Edge[] edges_copy = CopyArray();
+ rotateFace(edges[5], edges_copy[5], direction);
+
+ switch (direction) {
+ case CLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[0].getParts()[0][i] = edges_copy[3].getParts()[i][2];
+ edges[2].getParts()[2 - i][0] = edges_copy[0].getParts()[0][i];
+ edges[1].getParts()[2][i] = edges_copy[2].getParts()[i][0];
+ edges[3].getParts()[2 - i][2] = edges_copy[1].getParts()[2][i];
+ }
+ break;
+ case COUNTERCLOCKWISE:
+ for (int i = 0; i < 3; i++) {
+ edges[3].getParts()[i][2] = edges_copy[0].getParts()[0][i];
+ edges[0].getParts()[0][i] = edges_copy[2].getParts()[2 - i][0];
+ edges[2].getParts()[i][0] = edges_copy[1].getParts()[2][i];
+ edges[1].getParts()[2][i] = edges_copy[3].getParts()[2 - i][2];
+ }
+ break;
+ }
+ }
+
+ public void rotateFace(Edge Orig, Edge Copy, RotateDirection direction) {
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ if (direction == RotateDirection.CLOCKWISE) {
+ Orig.getParts()[j][2 - i] = Copy.getParts()[i][j];
+ } else {
+ Orig.getParts()[2 - j][i] = Copy.getParts()[i][j];
+ }
+ }
+ }
+ }
+
+ public Edge[] CopyArray() {
+ Edge[] edges_copy = new Edge[edges.length];
+ for (int i = 0; i < 6; i++) {
+ CubeColor[][] parts = new CubeColor[3][3];
+ for (int j = 0; j < 3; j++) {
+ parts[j] = Arrays.copyOf(edges[i].getParts()[j], 3);
+ }
+ edges_copy[i] = new Edge(parts);
+ }
+ return edges_copy;
}
-
+
+
public Edge[] getEdges() {
return edges;
}
@@ -38,4 +203,4 @@ public Edge[] getEdges() {
public String toString() {
return Arrays.toString(edges);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/hse/java/practice/task1/task.md b/src/main/java/hse/java/practice/task1/task.md
index 235cc660..60dc4533 100644
--- a/src/main/java/hse/java/practice/task1/task.md
+++ b/src/main/java/hse/java/practice/task1/task.md
@@ -1,5 +1,7 @@
Необходимо реализовать методы поворота кубика Рубика.
+Tag: cube
+
Кубик представляет собой классический куб 3×3×3:
- 6 граней;
- каждая грань состоит из 9 элементов (стикеров);
diff --git a/src/test/java/hse/java/lectures/lecture3/practice/randomSet/RandomSetBaseTest.java b/src/test/java/hse/java/lectures/lecture3/practice/randomSet/RandomSetBaseTest.java
new file mode 100644
index 00000000..37c38ab1
--- /dev/null
+++ b/src/test/java/hse/java/lectures/lecture3/practice/randomSet/RandomSetBaseTest.java
@@ -0,0 +1,28 @@
+package hse.java.lectures.lecture3.practice.randomSet;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class RandomSetBaseTest {
+
+ @Test
+ void insertRemoveContains() {
+ RandomSet set = new RandomSet<>();
+
+ assertTrue(set.insert(10));
+ assertTrue(set.insert(20));
+ assertFalse(set.insert(10));
+
+ assertTrue(set.contains(10));
+ assertTrue(set.contains(20));
+ assertFalse(set.contains(30));
+
+ assertTrue(set.remove(10));
+ assertFalse(set.remove(10));
+
+ assertFalse(set.contains(10));
+ assertTrue(set.contains(20));
+ }
+
+}
diff --git a/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java b/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java
index 9e3994a6..5e83fcae 100644
--- a/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java
+++ b/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java
@@ -15,6 +15,7 @@
import java.util.Map;
import java.util.stream.Stream;
+import static hse.java.lectures.lecture3.tasks.atm.Atm.Denomination.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -30,18 +31,11 @@ void initialBalanceIsZero() {
@Test
void depositIncreasesBalance() {
Atm atm = new Atm();
- atm.deposit(Map.of(100, 10, 500, 5));
+ atm.deposit(Map.of(D100, 10, D500, 5));
assertEquals(3500, atm.getBalance());
}
- @Test
- void depositRejectsInvalidDenominationAndKeepsState() {
- Atm atm = new Atm();
- atm.deposit(Map.of(100, 1));
- assertThrows(InvalidDepositException.class, () -> atm.deposit(Map.of(30, 1)));
- assertEquals(100, atm.getBalance());
- }
-
+
@Test
void depositRejectsNullMap() {
Atm atm = new Atm();
@@ -51,8 +45,8 @@ void depositRejectsNullMap() {
@Test
void depositRejectsNonPositiveCountAndKeepsState() {
Atm atm = new Atm();
- atm.deposit(Map.of(100, 1));
- assertThrows(InvalidDepositException.class, () -> atm.deposit(Map.of(100, 0)));
+ atm.deposit(Map.of(D100, 1));
+ assertThrows(InvalidDepositException.class, () -> atm.deposit(Map.of(D100, 0)));
assertEquals(100, atm.getBalance());
}
@@ -60,11 +54,11 @@ void depositRejectsNonPositiveCountAndKeepsState() {
void withdrawGreedyAndUpdatesBalance() {
Atm atm = new Atm();
// 2000
- atm.deposit(Map.of(1000, 1, 500, 1, 100, 5));
+ atm.deposit(Map.of(D1000, 1, D500, 1, D100, 5));
- Map result = atm.withdraw(1700);
+ Map result = atm.withdraw(1700);
- assertEquals(Map.of(1000, 1, 500, 1, 100, 2), result);
+ assertEquals(Map.of(D1000, 1, D500, 1, D100, 2), result);
assertEquals(300, atm.getBalance());
}
@@ -78,14 +72,14 @@ void withdrawRejectsInvalidAmount() {
@Test
void withdrawRejectsInsufficientFunds() {
Atm atm = new Atm();
- atm.deposit(Map.of(100, 2));
+ atm.deposit(Map.of(D100, 2));
assertThrows(InsufficientFundsException.class, () -> atm.withdraw(300));
}
@Test
void withdrawRejectsUnmakeableAmountAndKeepsState() {
Atm atm = new Atm();
- atm.deposit(Map.of(500, 1, 100, 1));
+ atm.deposit(Map.of(D500, 1, D100, 1));
assertThrows(CannotDispenseException.class, () -> atm.withdraw(150));
assertEquals(600, atm.getBalance());
}
@@ -96,7 +90,7 @@ void additionalTests(AtmCase atmCase) {
Atm atm = new Atm();
for (Map deposit : atmCase.deposits) {
- atm.deposit(toIntMap(deposit));
+ atm.deposit(toMap(deposit));
}
if (atmCase.expect.exception != null) {
@@ -105,8 +99,8 @@ void additionalTests(AtmCase atmCase) {
"Case: " + atmCase.name);
assertEquals(atmCase.expect.balance, atm.getBalance(), "Case: " + atmCase.name);
} else {
- Map result = atm.withdraw(atmCase.withdraw);
- assertEquals(toIntMap(atmCase.expect.dispense), result, "Case: " + atmCase.name);
+ Map result = atm.withdraw(atmCase.withdraw);
+ assertEquals(toMap(atmCase.expect.dispense), result, "Case: " + atmCase.name);
assertEquals(atmCase.expect.balance, atm.getBalance(), "Case: " + atmCase.name);
}
}
@@ -126,10 +120,10 @@ private static List loadCases() throws IOException {
}
}
- private Map toIntMap(Map source) {
- Map result = new HashMap<>();
+ private Map toMap(Map source) {
+ Map result = new HashMap<>();
for (Map.Entry entry : source.entrySet()) {
- result.put(Integer.parseInt(entry.getKey()), entry.getValue());
+ result.put(Atm.Denomination.fromInt(Integer.parseInt(entry.getKey())), entry.getValue());
}
return result;
}
diff --git a/src/test/java/hse/java/practice/task1/CubeSimpleTest.java b/src/test/java/hse/java/practice/task1/CubeSimpleTest.java
new file mode 100644
index 00000000..b13c4af7
--- /dev/null
+++ b/src/test/java/hse/java/practice/task1/CubeSimpleTest.java
@@ -0,0 +1,277 @@
+package hse.java.practice.task1;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+public class CubeSimpleTest {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ @Test
+ void printTest() {
+ RubiksCube cube = new RubiksCube();
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(color, colors[i]);
+ }
+ }
+ }
+ }
+
+ @Test
+ void frontClockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.front(RotateDirection.CLOCKWISE);
+
+ CubeColor[][][] state = readStateFromFile("frontClockwieseState.json");
+
+ CubeColor[][][] actuallyState = Arrays.stream(cube.getEdges())
+ .map(Edge::getParts)
+ .toArray(CubeColor[][][]::new);
+
+ Assertions.assertArrayEquals(state, actuallyState);
+ }
+
+ @Test
+ void frontCounterclockwise() {
+ RubiksCube cube = new RubiksCube();
+ // Поворот против часовой стрелки = 3 поворота по часовой стрелке
+ cube.front(RotateDirection.COUNTERCLOCKWISE);
+
+ // Проверяем, что 4 поворота против часовой возвращают в исходное состояние
+ cube.front(RotateDirection.COUNTERCLOCKWISE);
+ cube.front(RotateDirection.COUNTERCLOCKWISE);
+ cube.front(RotateDirection.COUNTERCLOCKWISE);
+
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(colors[i], color);
+ }
+ }
+ }
+ }
+
+ @Test
+ void upClockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.up(RotateDirection.CLOCKWISE);
+
+ CubeColor[][][] state = readStateFromFile("upClockwiseState.json");
+
+ CubeColor[][][] actuallyState = Arrays.stream(cube.getEdges())
+ .map(Edge::getParts)
+ .toArray(CubeColor[][][]::new);
+
+ Assertions.assertArrayEquals(state, actuallyState);
+ }
+
+ @Test
+ void upCounterclockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.up(RotateDirection.COUNTERCLOCKWISE);
+ cube.up(RotateDirection.COUNTERCLOCKWISE);
+ cube.up(RotateDirection.COUNTERCLOCKWISE);
+ cube.up(RotateDirection.COUNTERCLOCKWISE);
+
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(colors[i], color);
+ }
+ }
+ }
+ }
+
+ @Test
+ void downClockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.down(RotateDirection.CLOCKWISE);
+
+ CubeColor[][][] state = readStateFromFile("downClockwiseState.json");
+
+ CubeColor[][][] actuallyState = Arrays.stream(cube.getEdges())
+ .map(Edge::getParts)
+ .toArray(CubeColor[][][]::new);
+
+ Assertions.assertArrayEquals(state, actuallyState);
+ }
+
+ @Test
+ void downCounterclockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.down(RotateDirection.COUNTERCLOCKWISE);
+ cube.down(RotateDirection.COUNTERCLOCKWISE);
+ cube.down(RotateDirection.COUNTERCLOCKWISE);
+ cube.down(RotateDirection.COUNTERCLOCKWISE);
+
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(colors[i], color);
+ }
+ }
+ }
+ }
+
+ @Test
+ void leftClockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.left(RotateDirection.CLOCKWISE);
+
+ CubeColor[][][] state = readStateFromFile("leftClockwiseState.json");
+
+ CubeColor[][][] actuallyState = Arrays.stream(cube.getEdges())
+ .map(Edge::getParts)
+ .toArray(CubeColor[][][]::new);
+
+ Assertions.assertArrayEquals(state, actuallyState);
+ }
+
+ @Test
+ void leftCounterclockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.left(RotateDirection.COUNTERCLOCKWISE);
+ cube.left(RotateDirection.COUNTERCLOCKWISE);
+ cube.left(RotateDirection.COUNTERCLOCKWISE);
+ cube.left(RotateDirection.COUNTERCLOCKWISE);
+
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(colors[i], color);
+ }
+ }
+ }
+ }
+
+ @Test
+ void rightClockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.right(RotateDirection.CLOCKWISE);
+
+ CubeColor[][][] state = readStateFromFile("rightClockwiseState.json");
+
+ CubeColor[][][] actuallyState = Arrays.stream(cube.getEdges())
+ .map(Edge::getParts)
+ .toArray(CubeColor[][][]::new);
+
+ Assertions.assertArrayEquals(state, actuallyState);
+ }
+
+ @Test
+ void rightCounterclockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.right(RotateDirection.COUNTERCLOCKWISE);
+ cube.right(RotateDirection.COUNTERCLOCKWISE);
+ cube.right(RotateDirection.COUNTERCLOCKWISE);
+ cube.right(RotateDirection.COUNTERCLOCKWISE);
+
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(colors[i], color);
+ }
+ }
+ }
+ }
+
+ @Test
+ void backClockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.back(RotateDirection.CLOCKWISE);
+
+ CubeColor[][][] state = readStateFromFile("backClockwiseState.json");
+
+ CubeColor[][][] actuallyState = Arrays.stream(cube.getEdges())
+ .map(Edge::getParts)
+ .toArray(CubeColor[][][]::new);
+
+ Assertions.assertArrayEquals(state, actuallyState);
+ }
+
+ @Test
+ void backCounterclockwise() {
+ RubiksCube cube = new RubiksCube();
+ cube.back(RotateDirection.COUNTERCLOCKWISE);
+ cube.back(RotateDirection.COUNTERCLOCKWISE);
+ cube.back(RotateDirection.COUNTERCLOCKWISE);
+ cube.back(RotateDirection.COUNTERCLOCKWISE);
+
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(colors[i], color);
+ }
+ }
+ }
+ }
+
+ @Test
+ void combinedRotations() {
+ RubiksCube cube = new RubiksCube();
+
+ // Выполняем последовательность поворотов
+ cube.front(RotateDirection.CLOCKWISE);
+ cube.right(RotateDirection.CLOCKWISE);
+ cube.up(RotateDirection.CLOCKWISE);
+
+ // Выполняем обратную последовательность
+ cube.up(RotateDirection.COUNTERCLOCKWISE);
+ cube.right(RotateDirection.COUNTERCLOCKWISE);
+ cube.front(RotateDirection.COUNTERCLOCKWISE);
+
+ // Должны вернуться в исходное состояние
+ CubeColor[] colors = CubeColor.values();
+ for (int i = 0; i < 6; i++) {
+ Edge edge = cube.getEdges()[i];
+ CubeColor[][] edgeColors = edge.getParts();
+ for (CubeColor[] row : edgeColors) {
+ for (CubeColor color : row) {
+ Assertions.assertEquals(colors[i], color);
+ }
+ }
+ }
+ }
+
+ private CubeColor[][][] readStateFromFile(String fileName) {
+ String resourcePath = "hse/java/practice/task1/" + fileName;
+ try (InputStream is = CubeSimpleTest.class.getClassLoader().getResourceAsStream(resourcePath)) {
+ if (is == null) {
+ throw new IllegalArgumentException("Resource not found: " + resourcePath);
+ }
+ String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);
+ return MAPPER.readValue(json, CubeColor[][][].class);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to read/parse state file: " + fileName, e);
+ }
+ }
+}