From 8a17ffbbc7cf325c8c34f76cd4c867c055d2037f Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 26 Jun 2023 20:27:01 +0200 Subject: [PATCH 001/137] start working on module 3 --- .../OperatorsOverloading/CMakeLists.txt | 16 ++++++++++++++++ .../OperatorsOverloading/src/operators.cpp | 9 +++++++++ .../OperatorsOverloading/src/point.cpp | 19 +++++++++++++++++++ .../OperatorsOverloading/task-info.yaml | 11 +++++++++++ .../OperatorsOverloading/task.md | 0 .../OperatorsOverloading/test/test.cpp | 11 +++++++++++ .../ClassesAndObjects/lesson-info.yaml | 4 ++++ ObjectOrientedProgramming/section-info.yaml | 3 +++ 8 files changed, 73 insertions(+) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml create mode 100644 ObjectOrientedProgramming/section-info.yaml diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt new file mode 100644 index 0000000..0bb1270 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-OperatorsOverloading) + +set(TASK + src/operators.cpp) + +set(SRC + ${TASK} src/point.cpp) + +set(TEST + test/test.cpp) + +include_directories(${CMAKE_SOURCE_DIR}/include/) + +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp new file mode 100644 index 0000000..49e01ad --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp @@ -0,0 +1,9 @@ +#include "scene.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp new file mode 100644 index 0000000..6e55434 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp @@ -0,0 +1,19 @@ +#include "scene.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml new file mode 100644 index 0000000..34d5a5f --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml @@ -0,0 +1,11 @@ +type: edu +custom_name: Operators Overloading +files: +- name: CMakeLists.txt + visible: false +- name: src/operators.cpp + visible: true +- name: src/point.cpp + visible: true +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md new file mode 100644 index 0000000..e69de29 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp new file mode 100644 index 0000000..734038d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp @@ -0,0 +1,11 @@ +#include + +// Headers of objects that student should implement: +int sum(int a, int b); + + +// Tests: +// todo: replace this with an actual test +TEST(SumTest, Simple) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class + ASSERT_EQ(sum(1, 2), 3); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml new file mode 100644 index 0000000..19f536c --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -0,0 +1,4 @@ +type: framework +custom_name: Classes And Objects +content: +- OperatorsOverloading diff --git a/ObjectOrientedProgramming/section-info.yaml b/ObjectOrientedProgramming/section-info.yaml new file mode 100644 index 0000000..56082a6 --- /dev/null +++ b/ObjectOrientedProgramming/section-info.yaml @@ -0,0 +1,3 @@ +custom_name: Object Oriented Programming +content: +- ClassesAndObjects From 86208a5020ad096d05c2ea453d471d070c6d50c7 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 10 Jul 2023 21:12:46 +0200 Subject: [PATCH 002/137] operators overloading task Signed-off-by: Evgenii Moiseenko --- .../OperatorsOverloading/src/operators.cpp | 8 + .../OperatorsOverloading/task-info.yaml | 13 ++ .../OperatorsOverloading/task.md | 53 ++++++ .../OperatorsOverloading/test/test.cpp | 152 +++++++++++++++++- course-info.yaml | 1 + include/operators.hpp | 8 + 6 files changed, 229 insertions(+), 6 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp index 49e01ad..824187c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp @@ -4,6 +4,14 @@ Point2D operator+(Point2D a, Point2D b) { return add(a, b); } +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + Point2D operator*(float s, Point2D a) { return mul(s, a); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml index 34d5a5f..d710281 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml @@ -5,6 +5,19 @@ files: visible: false - name: src/operators.cpp visible: true + placeholders: + - offset: 68 + length: 17 + placeholder_text: "return { 0.0f, 0.0f };" + - offset: 124 + length: 22 + placeholder_text: "return { 0.0f, 0.0f, };" + - offset: 196 + length: 18 + placeholder_text: "return { 0.0f, 0.0f, };" + - offset: 262 + length: 17 + placeholder_text: "return { 0.0f, 0.0f, };" - name: src/point.cpp visible: true - name: test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md index e69de29..e3abb1c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md @@ -0,0 +1,53 @@ +Before we dive into object-oriented programming, +we will learn about another useful feature of C++ language +that can help to make your code easier to read and understand. +This feature is __operator overloading,__ and it allows you to define various operators, +like arithmetic operators `+`, `-`, `*`, +for your custom data type. + +Recall the function `move` you implemented before. +At first, your task was to implement it to move an object along `x` axis. +Your code, probably, looked like this: + +```c++ +float move(float position, float velocity, float delta) { + return position + delta * velocity; +} +``` + +At the next stage, your task was to re-implement `move`, +this time to move an object along both `x` and `y` axis, +using our custom data type `Point2D` and two functions +`add` and `mul` defined for it: + +```c++ +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} +``` + +As you can see, it is much easier to grasp the meaning of the first code. +Fortunately, with operator overloading, it is possible to make the second +code fragment look just like the first one! + +```c++ +Point2D move(Point2D position, Point2D velocity, float delta) { + return position + delta * velocity; +} +``` + +In fact, to enable this syntax, it is enough just to define +a special function with `operator` prefix in its name: + +```c++ +Point2D operator+(Point2D a, Point2D b) { + // your code here +} +``` + +We have already prepared for you a template for several operator functions, +that would make our work with `Point2D` data type much more pleasant. +Your job is to provide an implementation for these functions. +It is allowed to use `add` and `mul` functions implemented on previous steps. + +[//]: # (TODO: hint about IO streams overloads) \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp index 734038d..0ee372b 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp @@ -1,11 +1,151 @@ #include -// Headers of objects that student should implement: -int sum(int a, int b); +#include "operators.hpp" +#include "testing.hpp" +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); -// Tests: -// todo: replace this with an actual test -TEST(SumTest, Simple) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class - ASSERT_EQ(sum(1, 2), 3); +const float MIN = -10e3; +const float MAX = 10e3; + +namespace expected { + Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; + } + + Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; + } +} + +std::string plus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " c = a + b" << "\n"; + stream << "Test data:\n" + << " a = " << a << "\n" + << " b = " << b << "\n"; + stream << "Expected result:\n" + << " c = " << expected << "\n"; + stream << "Actual result:\n" + << " c = " << actual << "\n"; + return stream.str(); +} + +TEST(PlusTest, PlusTest) { + property_test( + [] () { + Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + Point2D b = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + return std::make_tuple(a, b); + }, + [] (std::tuple data) { + Point2D a, b; + std::tie(a, b) = data; + Point2D expected = expected::add(a, b); + Point2D actual = a + b; + ASSERT_FLOAT_EQ(expected.x, actual.x) << plus_error_msg(a, b, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << plus_error_msg(a, b, expected, actual); + } + ); +} + +std::string unary_minus_error_msg(Point2D a, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " b = -a" << "\n"; + stream << "Test data:\n" + << " a = " << a << "\n"; + stream << "Expected result:\n" + << " b = " << expected << "\n"; + stream << "Actual result:\n" + << " b = " << actual << "\n"; + return stream.str(); +} + +TEST(UnaryMinusTest, UnaryMinusTest) { + property_test( + [] () { + Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + return a; + }, + [] (Point2D a) { + Point2D expected = { -a.x, -a.y }; + Point2D actual = -a; + ASSERT_FLOAT_EQ(expected.x, actual.x) << unary_minus_error_msg(a, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << unary_minus_error_msg(a, expected, actual); + } + ); +} + +std::string minus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " c = a - b" << "\n"; + stream << "Test data:\n" + << " a = " << a << "\n" + << " b = " << b << "\n"; + stream << "Expected result:\n" + << " c = " << expected << "\n"; + stream << "Actual result:\n" + << " c = " << actual << "\n"; + return stream.str(); +} + +TEST(MinusTest, MinusTest) { + property_test( + [] () { + Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + Point2D b = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + return std::make_tuple(a, b); + }, + [] (std::tuple data) { + Point2D a, b; + std::tie(a, b) = data; + Point2D expected = expected::add(a, { -b.x, -b.y }); + Point2D actual = a - b; + ASSERT_FLOAT_EQ(expected.x, actual.x) << minus_error_msg(a, b, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << minus_error_msg(a, b, expected, actual); + } + ); +} + +std::string scalar_mul_error_msg(float s, Point2D a, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " c = s * a" << "\n"; + stream << "Test data:\n" + << " s = " << s << "\n" + << " a = " << a << "\n"; + stream << "Expected result:\n" + << " c = " << expected << "\n"; + stream << "Actual result:\n" + << " c = " << actual << "\n"; + return stream.str(); +} + +TEST(ScalarProdTest, ScalarProdTest) { + property_test( + [] () { + float s = genFloat(MIN, MAX); + Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + return std::make_tuple(s, a); + }, + [] (std::tuple data) { + float s; + Point2D a; + std::tie(s, a) = data; + Point2D expected = expected::mul(s, a); + Point2D actual = s * a; + ASSERT_FLOAT_EQ(expected.x, actual.x) << scalar_mul_error_msg(s, a, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << scalar_mul_error_msg(s, a, expected, actual); + } + ); } \ No newline at end of file diff --git a/course-info.yaml b/course-info.yaml index 8715478..942fe63 100644 --- a/course-info.yaml +++ b/course-info.yaml @@ -61,3 +61,4 @@ environment: GoogleTest content: - WarmUp - MemoryManagement +- ObjectOrientedProgramming diff --git a/include/operators.hpp b/include/operators.hpp index 1e458f8..f248f5a 100644 --- a/include/operators.hpp +++ b/include/operators.hpp @@ -32,4 +32,12 @@ inline std::ostream& operator<<(std::ostream& os, Direction direction) { return os << to_string(direction); } +Point2D operator+(Point2D a, Point2D b); + +Point2D operator-(Point2D a); + +Point2D operator-(Point2D a, Point2D b); + +Point2D operator*(float s, Point2D a); + #endif // CPPBASICS_OPERATORS_HPP From 536d25ab614fe0da474f5ce239ebd9430b427140 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Wed, 12 Jul 2023 19:24:20 +0200 Subject: [PATCH 003/137] rename `scene.hpp` into `game.hpp`, split it into several files Signed-off-by: Evgenii Moiseenko --- .../LinkedList/LinkedList/src/approaching.cpp | 2 +- .../LinkedList/LinkedList/src/borders.cpp | 2 +- .../LinkedList/LinkedList/src/collision.cpp | 2 +- .../LinkedList/LinkedList/src/direction.cpp | 2 +- .../LinkedList/LinkedList/src/generate.cpp | 2 +- .../LinkedList/LinkedList/src/loop.cpp | 2 +- .../LinkedList/LinkedList/src/main.cpp | 2 +- .../LinkedList/LinkedList/src/point.cpp | 2 +- WarmUp/HelloWorld/Welcome/task-info.yaml | 2 +- .../ALittleSpontaneity/src/borders.cpp | 2 +- .../ALittleSpontaneity/src/collision.cpp | 2 +- .../ALittleSpontaneity/src/direction.cpp | 2 +- .../ALittleSpontaneity/src/generate.cpp | 2 +- .../MovingOn/ALittleSpontaneity/src/main.cpp | 2 +- .../MovingOn/ALittleSpontaneity/src/point.cpp | 2 +- .../ALittleSpontaneity/task-info.yaml | 150 ++++++++--------- .../MovingOn/ALittleSpontaneity/test/test.cpp | 2 +- .../MovingOn/ConsumeAnObject/src/borders.cpp | 2 +- .../ConsumeAnObject/src/collision.cpp | 2 +- .../ConsumeAnObject/src/direction.cpp | 2 +- WarmUp/MovingOn/ConsumeAnObject/src/main.cpp | 2 +- WarmUp/MovingOn/ConsumeAnObject/src/point.cpp | 2 +- .../MovingOn/ConsumeAnObject/task-info.yaml | 46 +++--- WarmUp/MovingOn/ConsumeAnObject/test/test.cpp | 2 +- WarmUp/MovingOn/DrawALine/src/borders.cpp | 2 +- WarmUp/MovingOn/DrawALine/src/direction.cpp | 2 +- WarmUp/MovingOn/DrawALine/src/main.cpp | 2 +- WarmUp/MovingOn/DrawALine/src/point.cpp | 2 +- WarmUp/MovingOn/DrawALine/task-info.yaml | 34 ++-- WarmUp/MovingOn/DrawALine/test/test.cpp | 2 +- .../GivingDirection/src/direction.cpp | 2 +- WarmUp/MovingOn/GivingDirection/src/main.cpp | 2 +- WarmUp/MovingOn/GivingDirection/src/point.cpp | 2 +- .../MovingOn/GivingDirection/task-info.yaml | 32 ++-- WarmUp/MovingOn/GivingDirection/test/test.cpp | 2 +- WarmUp/MovingOn/HavingFun/src/main.cpp | 2 +- WarmUp/MovingOn/HavingFun/task-info.yaml | 5 +- .../MovingOn/HuntingBugs/src/approaching.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/src/borders.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/src/collision.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/src/direction.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/src/generate.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/src/loop.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/src/main.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/src/point.cpp | 2 +- WarmUp/MovingOn/HuntingBugs/task-info.yaml | 139 ++++++++-------- WarmUp/MovingOn/KeepSpinning/src/borders.cpp | 2 +- .../MovingOn/KeepSpinning/src/collision.cpp | 2 +- .../MovingOn/KeepSpinning/src/direction.cpp | 2 +- WarmUp/MovingOn/KeepSpinning/src/generate.cpp | 2 +- WarmUp/MovingOn/KeepSpinning/src/loop.cpp | 2 +- WarmUp/MovingOn/KeepSpinning/src/main.cpp | 2 +- WarmUp/MovingOn/KeepSpinning/src/point.cpp | 2 +- WarmUp/MovingOn/KeepSpinning/task-info.yaml | 121 +++++++------- .../MovingOn/LimitsOfPossible/src/borders.cpp | 2 +- .../LimitsOfPossible/src/collision.cpp | 2 +- .../LimitsOfPossible/src/direction.cpp | 2 +- .../LimitsOfPossible/src/generate.cpp | 2 +- WarmUp/MovingOn/LimitsOfPossible/src/loop.cpp | 2 +- WarmUp/MovingOn/LimitsOfPossible/src/main.cpp | 2 +- .../MovingOn/LimitsOfPossible/src/point.cpp | 2 +- .../MovingOn/LimitsOfPossible/task-info.yaml | 143 ++++++++-------- WarmUp/MovingOn/LookAround/src/main.cpp | 2 +- WarmUp/MovingOn/LookAround/task-info.yaml | 5 +- WarmUp/MovingOn/MoveAnObject/src/main.cpp | 2 +- WarmUp/MovingOn/MoveAnObject/task-info.yaml | 4 +- WarmUp/MovingOn/MoveAnObject/test/test.cpp | 2 +- WarmUp/MovingOn/MovingUp/src/main.cpp | 2 +- WarmUp/MovingOn/MovingUp/src/point.cpp | 2 +- WarmUp/MovingOn/MovingUp/task-info.yaml | 30 ++-- WarmUp/MovingOn/MovingUp/test/test.cpp | 2 +- .../MovingOn/PlayAround/src/approaching.cpp | 2 +- WarmUp/MovingOn/PlayAround/src/borders.cpp | 2 +- WarmUp/MovingOn/PlayAround/src/collision.cpp | 2 +- WarmUp/MovingOn/PlayAround/src/direction.cpp | 2 +- WarmUp/MovingOn/PlayAround/src/generate.cpp | 2 +- WarmUp/MovingOn/PlayAround/src/loop.cpp | 2 +- WarmUp/MovingOn/PlayAround/src/main.cpp | 2 +- WarmUp/MovingOn/PlayAround/src/point.cpp | 2 +- WarmUp/MovingOn/PlayAround/task-info.yaml | 148 ++++++++--------- .../MovingOn/RunningInALoop/src/borders.cpp | 2 +- .../MovingOn/RunningInALoop/src/collision.cpp | 2 +- .../MovingOn/RunningInALoop/src/direction.cpp | 2 +- .../MovingOn/RunningInALoop/src/generate.cpp | 2 +- WarmUp/MovingOn/RunningInALoop/src/loop.cpp | 2 +- WarmUp/MovingOn/RunningInALoop/src/main.cpp | 2 +- WarmUp/MovingOn/RunningInALoop/src/point.cpp | 2 +- WarmUp/MovingOn/RunningInALoop/task-info.yaml | 156 +++++++++--------- WarmUp/MovingOn/RunningInALoop/test/test.cpp | 2 +- WarmUp/MovingOn/StartTheGame/src/main.cpp | 2 +- WarmUp/MovingOn/StartTheGame/task-info.yaml | 6 +- include/circle.hpp | 11 ++ include/constants.hpp | 24 +++ include/direction.hpp | 36 ++++ include/{scene.hpp => game.hpp} | 58 +------ 95 files changed, 664 insertions(+), 638 deletions(-) create mode 100644 include/circle.hpp create mode 100644 include/constants.hpp create mode 100644 include/direction.hpp rename include/{scene.hpp => game.hpp} (70%) diff --git a/MemoryManagement/LinkedList/LinkedList/src/approaching.cpp b/MemoryManagement/LinkedList/LinkedList/src/approaching.cpp index de9627a..d44ade4 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/approaching.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/approaching.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void approachingLoop(Circle player, Circle consumable[], bool concerned[], int size) { for (int i = 0; i < size; ++i) { diff --git a/MemoryManagement/LinkedList/LinkedList/src/borders.cpp b/MemoryManagement/LinkedList/LinkedList/src/borders.cpp index b5ca906..c9df954 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/borders.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/MemoryManagement/LinkedList/LinkedList/src/collision.cpp b/MemoryManagement/LinkedList/LinkedList/src/collision.cpp index 639aad3..449016a 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/collision.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/MemoryManagement/LinkedList/LinkedList/src/direction.cpp b/MemoryManagement/LinkedList/LinkedList/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/direction.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/MemoryManagement/LinkedList/LinkedList/src/generate.cpp b/MemoryManagement/LinkedList/LinkedList/src/generate.cpp index b08977b..180912d 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/generate.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/generate.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" #include diff --git a/MemoryManagement/LinkedList/LinkedList/src/loop.cpp b/MemoryManagement/LinkedList/LinkedList/src/loop.cpp index 5fe6ce3..8148f4a 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/loop.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/loop.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void collisionLoop(Circle player, Circle consumable[], bool consumed[], int size) { for (int i = 0; i < size; ++i) { diff --git a/MemoryManagement/LinkedList/LinkedList/src/main.cpp b/MemoryManagement/LinkedList/LinkedList/src/main.cpp index 830d150..7113e21 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/main.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "dllist.hpp" const int MAX_CONSUMABLES_COUNT = 8; diff --git a/MemoryManagement/LinkedList/LinkedList/src/point.cpp b/MemoryManagement/LinkedList/LinkedList/src/point.cpp index 6e55434..5c74f69 100644 --- a/MemoryManagement/LinkedList/LinkedList/src/point.cpp +++ b/MemoryManagement/LinkedList/LinkedList/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/HelloWorld/Welcome/task-info.yaml b/WarmUp/HelloWorld/Welcome/task-info.yaml index 4318276..0f0ebb8 100644 --- a/WarmUp/HelloWorld/Welcome/task-info.yaml +++ b/WarmUp/HelloWorld/Welcome/task-info.yaml @@ -5,4 +5,4 @@ files: visible: false - name: src/main.cpp visible: true -feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Hello+World!/Welcome \ No newline at end of file +feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Hello+World!/Welcome diff --git a/WarmUp/MovingOn/ALittleSpontaneity/src/borders.cpp b/WarmUp/MovingOn/ALittleSpontaneity/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/src/borders.cpp +++ b/WarmUp/MovingOn/ALittleSpontaneity/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/ALittleSpontaneity/src/collision.cpp b/WarmUp/MovingOn/ALittleSpontaneity/src/collision.cpp index 639aad3..449016a 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/src/collision.cpp +++ b/WarmUp/MovingOn/ALittleSpontaneity/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/WarmUp/MovingOn/ALittleSpontaneity/src/direction.cpp b/WarmUp/MovingOn/ALittleSpontaneity/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/src/direction.cpp +++ b/WarmUp/MovingOn/ALittleSpontaneity/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/ALittleSpontaneity/src/generate.cpp b/WarmUp/MovingOn/ALittleSpontaneity/src/generate.cpp index 8b854eb..bc95378 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/src/generate.cpp +++ b/WarmUp/MovingOn/ALittleSpontaneity/src/generate.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" #include diff --git a/WarmUp/MovingOn/ALittleSpontaneity/src/main.cpp b/WarmUp/MovingOn/ALittleSpontaneity/src/main.cpp index 0a7a693..27e36f3 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/src/main.cpp +++ b/WarmUp/MovingOn/ALittleSpontaneity/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/ALittleSpontaneity/src/point.cpp b/WarmUp/MovingOn/ALittleSpontaneity/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/src/point.cpp +++ b/WarmUp/MovingOn/ALittleSpontaneity/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/ALittleSpontaneity/task-info.yaml b/WarmUp/MovingOn/ALittleSpontaneity/task-info.yaml index 0fdbc5e..99d8e53 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/task-info.yaml +++ b/WarmUp/MovingOn/ALittleSpontaneity/task-info.yaml @@ -4,52 +4,52 @@ files: - name: src/generate.cpp visible: true placeholders: - - offset: 121 + - offset: 120 length: 197 placeholder_text: /* TODO */ - - offset: 394 + - offset: 393 length: 49 placeholder_text: return min; - name: src/collision.cpp visible: true placeholders: - - offset: 83 - length: 83 - placeholder_text: return 0.0f; - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/collision.cpp - placeholder: 1 - is_visible: false - - offset: 223 - length: 82 - placeholder_text: return false; - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/collision.cpp - placeholder: 2 - is_visible: false + - offset: 82 + length: 83 + placeholder_text: return 0.0f; + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/collision.cpp + placeholder: 1 + is_visible: false + - offset: 222 + length: 82 + placeholder_text: return false; + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/collision.cpp + placeholder: 2 + is_visible: false - name: src/borders.cpp visible: true placeholders: - - offset: 101 - length: 359 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/borders.cpp - placeholder: 1 - is_visible: false + - offset: 100 + length: 359 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/borders.cpp + placeholder: 1 + is_visible: false - name: src/direction.cpp visible: true placeholders: - - offset: 70 + - offset: 69 length: 304 placeholder_text: "return { 0.0f, 0.0f };" dependency: @@ -62,53 +62,53 @@ files: - name: src/point.cpp visible: true placeholders: - - offset: 88 - length: 37 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/point.cpp - placeholder: 1 - is_visible: false - - offset: 207 - length: 33 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/point.cpp - placeholder: 2 - is_visible: false - - offset: 333 - length: 35 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/point.cpp - placeholder: 3 - is_visible: false + - offset: 87 + length: 37 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/point.cpp + placeholder: 1 + is_visible: false + - offset: 206 + length: 33 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/point.cpp + placeholder: 2 + is_visible: false + - offset: 332 + length: 35 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/point.cpp + placeholder: 3 + is_visible: false - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/move.cpp + placeholder: 1 + is_visible: false +- name: test/test.cpp + visible: false - name: src/main.cpp visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/A+Little+Spontaneity diff --git a/WarmUp/MovingOn/ALittleSpontaneity/test/test.cpp b/WarmUp/MovingOn/ALittleSpontaneity/test/test.cpp index af50514..4f7a151 100644 --- a/WarmUp/MovingOn/ALittleSpontaneity/test/test.cpp +++ b/WarmUp/MovingOn/ALittleSpontaneity/test/test.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "operators.hpp" #include "testing.hpp" diff --git a/WarmUp/MovingOn/ConsumeAnObject/src/borders.cpp b/WarmUp/MovingOn/ConsumeAnObject/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/ConsumeAnObject/src/borders.cpp +++ b/WarmUp/MovingOn/ConsumeAnObject/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/ConsumeAnObject/src/collision.cpp b/WarmUp/MovingOn/ConsumeAnObject/src/collision.cpp index 639aad3..449016a 100644 --- a/WarmUp/MovingOn/ConsumeAnObject/src/collision.cpp +++ b/WarmUp/MovingOn/ConsumeAnObject/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/WarmUp/MovingOn/ConsumeAnObject/src/direction.cpp b/WarmUp/MovingOn/ConsumeAnObject/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/ConsumeAnObject/src/direction.cpp +++ b/WarmUp/MovingOn/ConsumeAnObject/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/ConsumeAnObject/src/main.cpp b/WarmUp/MovingOn/ConsumeAnObject/src/main.cpp index dfa57c6..083ffd1 100644 --- a/WarmUp/MovingOn/ConsumeAnObject/src/main.cpp +++ b/WarmUp/MovingOn/ConsumeAnObject/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/ConsumeAnObject/src/point.cpp b/WarmUp/MovingOn/ConsumeAnObject/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/ConsumeAnObject/src/point.cpp +++ b/WarmUp/MovingOn/ConsumeAnObject/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/ConsumeAnObject/task-info.yaml b/WarmUp/MovingOn/ConsumeAnObject/task-info.yaml index b5fd3c6..4177a4f 100644 --- a/WarmUp/MovingOn/ConsumeAnObject/task-info.yaml +++ b/WarmUp/MovingOn/ConsumeAnObject/task-info.yaml @@ -4,16 +4,16 @@ files: - name: src/collision.cpp visible: true placeholders: - - offset: 83 - length: 83 - placeholder_text: return 0.0f; - - offset: 223 - length: 82 - placeholder_text: return false; + - offset: 82 + length: 83 + placeholder_text: return 0.0f; + - offset: 222 + length: 82 + placeholder_text: return false; - name: src/borders.cpp visible: true placeholders: - - offset: 101 + - offset: 100 length: 359 placeholder_text: /* TODO */ dependency: @@ -26,7 +26,7 @@ files: - name: src/direction.cpp visible: true placeholders: - - offset: 70 + - offset: 69 length: 304 placeholder_text: "return { 0.0f, 0.0f };" dependency: @@ -39,7 +39,7 @@ files: - name: src/point.cpp visible: true placeholders: - - offset: 88 + - offset: 87 length: 37 placeholder_text: /* TODO */ dependency: @@ -49,7 +49,7 @@ files: file: src/point.cpp placeholder: 1 is_visible: false - - offset: 207 + - offset: 206 length: 33 placeholder_text: /* TODO */ dependency: @@ -59,7 +59,7 @@ files: file: src/point.cpp placeholder: 2 is_visible: false - - offset: 333 + - offset: 332 length: 35 placeholder_text: position dependency: @@ -72,20 +72,20 @@ files: - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: DrawALine - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: DrawALine + file: src/move.cpp + placeholder: 1 + is_visible: false +- name: test/test.cpp + visible: false - name: src/main.cpp visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Consume+an+Object diff --git a/WarmUp/MovingOn/ConsumeAnObject/test/test.cpp b/WarmUp/MovingOn/ConsumeAnObject/test/test.cpp index c7ca914..9ac85eb 100644 --- a/WarmUp/MovingOn/ConsumeAnObject/test/test.cpp +++ b/WarmUp/MovingOn/ConsumeAnObject/test/test.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "operators.hpp" #include "testing.hpp" diff --git a/WarmUp/MovingOn/DrawALine/src/borders.cpp b/WarmUp/MovingOn/DrawALine/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/DrawALine/src/borders.cpp +++ b/WarmUp/MovingOn/DrawALine/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/DrawALine/src/direction.cpp b/WarmUp/MovingOn/DrawALine/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/DrawALine/src/direction.cpp +++ b/WarmUp/MovingOn/DrawALine/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/DrawALine/src/main.cpp b/WarmUp/MovingOn/DrawALine/src/main.cpp index 20c558f..b434a92 100644 --- a/WarmUp/MovingOn/DrawALine/src/main.cpp +++ b/WarmUp/MovingOn/DrawALine/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/DrawALine/src/point.cpp b/WarmUp/MovingOn/DrawALine/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/DrawALine/src/point.cpp +++ b/WarmUp/MovingOn/DrawALine/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/DrawALine/task-info.yaml b/WarmUp/MovingOn/DrawALine/task-info.yaml index a42e11d..b937bdd 100644 --- a/WarmUp/MovingOn/DrawALine/task-info.yaml +++ b/WarmUp/MovingOn/DrawALine/task-info.yaml @@ -4,13 +4,13 @@ files: - name: src/borders.cpp visible: true placeholders: - - offset: 101 + - offset: 100 length: 359 placeholder_text: /* TODO */ - name: src/direction.cpp visible: true placeholders: - - offset: 70 + - offset: 69 length: 304 placeholder_text: "return { 0.0f, 0.0f };" dependency: @@ -23,7 +23,7 @@ files: - name: src/point.cpp visible: true placeholders: - - offset: 88 + - offset: 87 length: 37 placeholder_text: /* TODO */ dependency: @@ -33,7 +33,7 @@ files: file: src/point.cpp placeholder: 1 is_visible: false - - offset: 207 + - offset: 206 length: 33 placeholder_text: /* TODO */ dependency: @@ -43,7 +43,7 @@ files: file: src/point.cpp placeholder: 2 is_visible: false - - offset: 333 + - offset: 332 length: 35 placeholder_text: position dependency: @@ -56,20 +56,20 @@ files: - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: GivingDirection - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: GivingDirection + file: src/move.cpp + placeholder: 1 + is_visible: false +- name: test/test.cpp + visible: false - name: src/main.cpp visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Draw+a+Line diff --git a/WarmUp/MovingOn/DrawALine/test/test.cpp b/WarmUp/MovingOn/DrawALine/test/test.cpp index bef66e9..5358582 100644 --- a/WarmUp/MovingOn/DrawALine/test/test.cpp +++ b/WarmUp/MovingOn/DrawALine/test/test.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "operators.hpp" #include "testing.hpp" diff --git a/WarmUp/MovingOn/GivingDirection/src/direction.cpp b/WarmUp/MovingOn/GivingDirection/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/GivingDirection/src/direction.cpp +++ b/WarmUp/MovingOn/GivingDirection/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/GivingDirection/src/main.cpp b/WarmUp/MovingOn/GivingDirection/src/main.cpp index ec02486..f621b03 100644 --- a/WarmUp/MovingOn/GivingDirection/src/main.cpp +++ b/WarmUp/MovingOn/GivingDirection/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/GivingDirection/src/point.cpp b/WarmUp/MovingOn/GivingDirection/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/GivingDirection/src/point.cpp +++ b/WarmUp/MovingOn/GivingDirection/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/GivingDirection/task-info.yaml b/WarmUp/MovingOn/GivingDirection/task-info.yaml index ef79e56..d8b77bf 100644 --- a/WarmUp/MovingOn/GivingDirection/task-info.yaml +++ b/WarmUp/MovingOn/GivingDirection/task-info.yaml @@ -4,13 +4,13 @@ files: - name: src/direction.cpp visible: true placeholders: - - offset: 70 + - offset: 69 length: 304 placeholder_text: "return { 0.0f, 0.0f };" - name: src/point.cpp visible: true placeholders: - - offset: 88 + - offset: 87 length: 37 placeholder_text: /* TODO */ dependency: @@ -20,7 +20,7 @@ files: file: src/point.cpp placeholder: 1 is_visible: false - - offset: 207 + - offset: 206 length: 33 placeholder_text: /* TODO */ dependency: @@ -30,7 +30,7 @@ files: file: src/point.cpp placeholder: 2 is_visible: false - - offset: 333 + - offset: 332 length: 35 placeholder_text: position dependency: @@ -43,20 +43,20 @@ files: - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: MovingUp - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: MovingUp + file: src/move.cpp + placeholder: 1 + is_visible: false +- name: test/test.cpp + visible: false - name: src/main.cpp visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Giving+Direction diff --git a/WarmUp/MovingOn/GivingDirection/test/test.cpp b/WarmUp/MovingOn/GivingDirection/test/test.cpp index 63d463c..85246d8 100644 --- a/WarmUp/MovingOn/GivingDirection/test/test.cpp +++ b/WarmUp/MovingOn/GivingDirection/test/test.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "operators.hpp" #include "testing.hpp" diff --git a/WarmUp/MovingOn/HavingFun/src/main.cpp b/WarmUp/MovingOn/HavingFun/src/main.cpp index af46561..df459f4 100644 --- a/WarmUp/MovingOn/HavingFun/src/main.cpp +++ b/WarmUp/MovingOn/HavingFun/src/main.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) diff --git a/WarmUp/MovingOn/HavingFun/task-info.yaml b/WarmUp/MovingOn/HavingFun/task-info.yaml index a9fd962..197c1bb 100644 --- a/WarmUp/MovingOn/HavingFun/task-info.yaml +++ b/WarmUp/MovingOn/HavingFun/task-info.yaml @@ -14,9 +14,8 @@ files: file: src/move.cpp placeholder: 1 is_visible: false -- name: src/main.cpp - visible: true - editable: true - name: CMakeLists.txt visible: false +- name: src/main.cpp + visible: true feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Having+Fun diff --git a/WarmUp/MovingOn/HuntingBugs/src/approaching.cpp b/WarmUp/MovingOn/HuntingBugs/src/approaching.cpp index ba0307c..12404e0 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/approaching.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/approaching.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void approachingLoop(Circle player, Circle consumable[], bool concerned[], int size) { for (int i = 0; i < size; ++i) { diff --git a/WarmUp/MovingOn/HuntingBugs/src/borders.cpp b/WarmUp/MovingOn/HuntingBugs/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/borders.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/HuntingBugs/src/collision.cpp b/WarmUp/MovingOn/HuntingBugs/src/collision.cpp index 639aad3..449016a 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/collision.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/WarmUp/MovingOn/HuntingBugs/src/direction.cpp b/WarmUp/MovingOn/HuntingBugs/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/direction.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/HuntingBugs/src/generate.cpp b/WarmUp/MovingOn/HuntingBugs/src/generate.cpp index 8b854eb..bc95378 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/generate.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/generate.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" #include diff --git a/WarmUp/MovingOn/HuntingBugs/src/loop.cpp b/WarmUp/MovingOn/HuntingBugs/src/loop.cpp index 5fe6ce3..8148f4a 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/loop.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/loop.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void collisionLoop(Circle player, Circle consumable[], bool consumed[], int size) { for (int i = 0; i < size; ++i) { diff --git a/WarmUp/MovingOn/HuntingBugs/src/main.cpp b/WarmUp/MovingOn/HuntingBugs/src/main.cpp index 2906421..aa8d82c 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/main.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/HuntingBugs/src/point.cpp b/WarmUp/MovingOn/HuntingBugs/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/HuntingBugs/src/point.cpp +++ b/WarmUp/MovingOn/HuntingBugs/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/HuntingBugs/task-info.yaml b/WarmUp/MovingOn/HuntingBugs/task-info.yaml index 96d1bfe..ee18256 100644 --- a/WarmUp/MovingOn/HuntingBugs/task-info.yaml +++ b/WarmUp/MovingOn/HuntingBugs/task-info.yaml @@ -3,6 +3,7 @@ custom_name: Hunting Bugs files: - name: src/approaching.cpp visible: true +<<<<<<< HEAD placeholders: - offset: 113 length: 215 @@ -14,23 +15,25 @@ files: concerned[i] = dist < warnDist; } } +======= +>>>>>>> ffb8328 (rename `scene.hpp` into `game.hpp`, split it into several files) - name: src/loop.cpp visible: true placeholders: - - offset: 110 - length: 136 - placeholder_text: return; - dependency: - section: WarmUp - lesson: MovingOn - task: LimitsOfPossible - file: src/loop.cpp - placeholder: 1 - is_visible: false + - offset: 109 + length: 136 + placeholder_text: return; + dependency: + section: WarmUp + lesson: MovingOn + task: LimitsOfPossible + file: src/loop.cpp + placeholder: 1 + is_visible: false - name: src/generate.cpp visible: true placeholders: - - offset: 121 + - offset: 120 length: 197 placeholder_text: /* TODO */ dependency: @@ -40,7 +43,7 @@ files: file: src/generate.cpp placeholder: 1 is_visible: false - - offset: 394 + - offset: 393 length: 49 placeholder_text: return min; dependency: @@ -53,7 +56,7 @@ files: - name: src/collision.cpp visible: true placeholders: - - offset: 83 + - offset: 82 length: 83 placeholder_text: return 0.0f; dependency: @@ -63,7 +66,7 @@ files: file: src/collision.cpp placeholder: 1 is_visible: false - - offset: 223 + - offset: 222 length: 82 placeholder_text: return false; dependency: @@ -76,7 +79,7 @@ files: - name: src/borders.cpp visible: true placeholders: - - offset: 101 + - offset: 100 length: 359 placeholder_text: /* TODO */ dependency: @@ -89,66 +92,68 @@ files: - name: src/direction.cpp visible: true placeholders: - - offset: 70 - length: 304 - placeholder_text: "return { 0.0f, 0.0f };" - dependency: - section: WarmUp - lesson: MovingOn - task: LimitsOfPossible - file: src/direction.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 304 + placeholder_text: "return { 0.0f, 0.0f };" + dependency: + section: WarmUp + lesson: MovingOn + task: LimitsOfPossible + file: src/direction.cpp + placeholder: 1 + is_visible: false - name: src/point.cpp visible: true placeholders: - - offset: 88 - length: 37 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: LimitsOfPossible - file: src/point.cpp - placeholder: 1 - is_visible: false - - offset: 207 - length: 33 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: LimitsOfPossible - file: src/point.cpp - placeholder: 2 - is_visible: false - - offset: 333 - length: 35 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: LimitsOfPossible - file: src/point.cpp - placeholder: 3 - is_visible: false + - offset: 87 + length: 37 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: LimitsOfPossible + file: src/point.cpp + placeholder: 1 + is_visible: false + - offset: 206 + length: 33 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: LimitsOfPossible + file: src/point.cpp + placeholder: 2 + is_visible: false + - offset: 332 + length: 35 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: LimitsOfPossible + file: src/point.cpp + placeholder: 3 + is_visible: false - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: LimitsOfPossible - file: src/move.cpp - placeholder: 1 - is_visible: false -- name: CMakeLists.txt - visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: LimitsOfPossible + file: src/move.cpp + placeholder: 1 + is_visible: false - name: src/main.cpp visible: true +<<<<<<< HEAD - name: test/test.cpp +======= +- name: CMakeLists.txt +>>>>>>> ffb8328 (rename `scene.hpp` into `game.hpp`, split it into several files) visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Hunting+Bugs diff --git a/WarmUp/MovingOn/KeepSpinning/src/borders.cpp b/WarmUp/MovingOn/KeepSpinning/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/KeepSpinning/src/borders.cpp +++ b/WarmUp/MovingOn/KeepSpinning/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/KeepSpinning/src/collision.cpp b/WarmUp/MovingOn/KeepSpinning/src/collision.cpp index 639aad3..449016a 100644 --- a/WarmUp/MovingOn/KeepSpinning/src/collision.cpp +++ b/WarmUp/MovingOn/KeepSpinning/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/WarmUp/MovingOn/KeepSpinning/src/direction.cpp b/WarmUp/MovingOn/KeepSpinning/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/KeepSpinning/src/direction.cpp +++ b/WarmUp/MovingOn/KeepSpinning/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/KeepSpinning/src/generate.cpp b/WarmUp/MovingOn/KeepSpinning/src/generate.cpp index 8b854eb..bc95378 100644 --- a/WarmUp/MovingOn/KeepSpinning/src/generate.cpp +++ b/WarmUp/MovingOn/KeepSpinning/src/generate.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" #include diff --git a/WarmUp/MovingOn/KeepSpinning/src/loop.cpp b/WarmUp/MovingOn/KeepSpinning/src/loop.cpp index 5fe6ce3..8148f4a 100644 --- a/WarmUp/MovingOn/KeepSpinning/src/loop.cpp +++ b/WarmUp/MovingOn/KeepSpinning/src/loop.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void collisionLoop(Circle player, Circle consumable[], bool consumed[], int size) { for (int i = 0; i < size; ++i) { diff --git a/WarmUp/MovingOn/KeepSpinning/src/main.cpp b/WarmUp/MovingOn/KeepSpinning/src/main.cpp index 6bdc8bb..b208448 100644 --- a/WarmUp/MovingOn/KeepSpinning/src/main.cpp +++ b/WarmUp/MovingOn/KeepSpinning/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/KeepSpinning/src/point.cpp b/WarmUp/MovingOn/KeepSpinning/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/KeepSpinning/src/point.cpp +++ b/WarmUp/MovingOn/KeepSpinning/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/KeepSpinning/task-info.yaml b/WarmUp/MovingOn/KeepSpinning/task-info.yaml index 94cac8a..2602205 100644 --- a/WarmUp/MovingOn/KeepSpinning/task-info.yaml +++ b/WarmUp/MovingOn/KeepSpinning/task-info.yaml @@ -4,79 +4,79 @@ files: - name: src/loop.cpp visible: true placeholders: - - offset: 110 - length: 136 - placeholder_text: return; - dependency: - section: WarmUp - lesson: MovingOn - task: RunningInALoop - file: src/loop.cpp - placeholder: 1 - is_visible: false -- name: src/generate.cpp - visible: true - placeholders: - - offset: 121 - length: 197 - placeholder_text: /* TODO */ + - offset: 109 + length: 136 + placeholder_text: return; dependency: section: WarmUp lesson: MovingOn task: RunningInALoop - file: src/generate.cpp + file: src/loop.cpp placeholder: 1 is_visible: false - - offset: 394 - length: 49 - placeholder_text: return min; - dependency: - section: WarmUp - lesson: MovingOn - task: RunningInALoop - file: src/generate.cpp - placeholder: 2 - is_visible: false -- name: src/collision.cpp +- name: src/generate.cpp visible: true placeholders: - - offset: 83 - length: 83 - placeholder_text: return 0.0f; + - offset: 120 + length: 197 + placeholder_text: /* TODO */ dependency: section: WarmUp lesson: MovingOn task: RunningInALoop - file: src/collision.cpp + file: src/generate.cpp placeholder: 1 is_visible: false - - offset: 223 - length: 82 - placeholder_text: return false; + - offset: 393 + length: 49 + placeholder_text: return min; dependency: section: WarmUp lesson: MovingOn task: RunningInALoop - file: src/collision.cpp + file: src/generate.cpp placeholder: 2 is_visible: false +- name: src/collision.cpp + visible: true + placeholders: + - offset: 82 + length: 83 + placeholder_text: return 0.0f; + dependency: + section: WarmUp + lesson: MovingOn + task: RunningInALoop + file: src/collision.cpp + placeholder: 1 + is_visible: false + - offset: 222 + length: 82 + placeholder_text: return false; + dependency: + section: WarmUp + lesson: MovingOn + task: RunningInALoop + file: src/collision.cpp + placeholder: 2 + is_visible: false - name: src/borders.cpp visible: true placeholders: - - offset: 101 - length: 359 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: RunningInALoop - file: src/borders.cpp - placeholder: 1 - is_visible: false + - offset: 100 + length: 359 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: RunningInALoop + file: src/borders.cpp + placeholder: 1 + is_visible: false - name: src/direction.cpp visible: true placeholders: - - offset: 70 + - offset: 69 length: 304 placeholder_text: "return { 0.0f, 0.0f };" dependency: @@ -89,7 +89,7 @@ files: - name: src/point.cpp visible: true placeholders: - - offset: 88 + - offset: 87 length: 37 placeholder_text: /* TODO */ dependency: @@ -99,7 +99,7 @@ files: file: src/point.cpp placeholder: 1 is_visible: false - - offset: 207 + - offset: 206 length: 33 placeholder_text: /* TODO */ dependency: @@ -109,7 +109,7 @@ files: file: src/point.cpp placeholder: 2 is_visible: false - - offset: 333 + - offset: 332 length: 35 placeholder_text: position dependency: @@ -122,19 +122,18 @@ files: - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: RunningInALoop - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: RunningInALoop + file: src/move.cpp + placeholder: 1 + is_visible: false - name: src/main.cpp visible: true - editable: true - name: CMakeLists.txt visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Keep+Spinning diff --git a/WarmUp/MovingOn/LimitsOfPossible/src/borders.cpp b/WarmUp/MovingOn/LimitsOfPossible/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/src/borders.cpp +++ b/WarmUp/MovingOn/LimitsOfPossible/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/LimitsOfPossible/src/collision.cpp b/WarmUp/MovingOn/LimitsOfPossible/src/collision.cpp index 639aad3..449016a 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/src/collision.cpp +++ b/WarmUp/MovingOn/LimitsOfPossible/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/WarmUp/MovingOn/LimitsOfPossible/src/direction.cpp b/WarmUp/MovingOn/LimitsOfPossible/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/src/direction.cpp +++ b/WarmUp/MovingOn/LimitsOfPossible/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/LimitsOfPossible/src/generate.cpp b/WarmUp/MovingOn/LimitsOfPossible/src/generate.cpp index 8b854eb..bc95378 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/src/generate.cpp +++ b/WarmUp/MovingOn/LimitsOfPossible/src/generate.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" #include diff --git a/WarmUp/MovingOn/LimitsOfPossible/src/loop.cpp b/WarmUp/MovingOn/LimitsOfPossible/src/loop.cpp index 5fe6ce3..8148f4a 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/src/loop.cpp +++ b/WarmUp/MovingOn/LimitsOfPossible/src/loop.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void collisionLoop(Circle player, Circle consumable[], bool consumed[], int size) { for (int i = 0; i < size; ++i) { diff --git a/WarmUp/MovingOn/LimitsOfPossible/src/main.cpp b/WarmUp/MovingOn/LimitsOfPossible/src/main.cpp index 6bdc8bb..b208448 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/src/main.cpp +++ b/WarmUp/MovingOn/LimitsOfPossible/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/LimitsOfPossible/src/point.cpp b/WarmUp/MovingOn/LimitsOfPossible/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/src/point.cpp +++ b/WarmUp/MovingOn/LimitsOfPossible/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/LimitsOfPossible/task-info.yaml b/WarmUp/MovingOn/LimitsOfPossible/task-info.yaml index 736d24b..41393bf 100644 --- a/WarmUp/MovingOn/LimitsOfPossible/task-info.yaml +++ b/WarmUp/MovingOn/LimitsOfPossible/task-info.yaml @@ -4,7 +4,7 @@ files: - name: src/loop.cpp visible: true placeholders: - - offset: 110 + - offset: 109 length: 136 placeholder_text: return; dependency: @@ -17,7 +17,7 @@ files: - name: src/generate.cpp visible: true placeholders: - - offset: 121 + - offset: 120 length: 197 placeholder_text: /* TODO */ dependency: @@ -27,7 +27,7 @@ files: file: src/generate.cpp placeholder: 1 is_visible: false - - offset: 394 + - offset: 393 length: 49 placeholder_text: return min; dependency: @@ -40,30 +40,43 @@ files: - name: src/collision.cpp visible: true placeholders: - - offset: 83 - length: 83 - placeholder_text: return 0.0f; + - offset: 82 + length: 83 + placeholder_text: return 0.0f; + dependency: + section: WarmUp + lesson: MovingOn + task: KeepSpinning + file: src/collision.cpp + placeholder: 1 + is_visible: false + - offset: 222 + length: 82 + placeholder_text: return false; + dependency: + section: WarmUp + lesson: MovingOn + task: KeepSpinning + file: src/collision.cpp + placeholder: 2 + is_visible: false +- name: src/borders.cpp + visible: true + placeholders: + - offset: 100 + length: 359 + placeholder_text: /* TODO */ dependency: section: WarmUp lesson: MovingOn task: KeepSpinning - file: src/collision.cpp + file: src/borders.cpp placeholder: 1 is_visible: false - - offset: 223 - length: 82 - placeholder_text: return false; - dependency: - section: WarmUp - lesson: MovingOn - task: KeepSpinning - file: src/collision.cpp - placeholder: 2 - is_visible: false - name: src/direction.cpp visible: true placeholders: - - offset: 70 + - offset: 69 length: 304 placeholder_text: "return { 0.0f, 0.0f };" dependency: @@ -73,68 +86,54 @@ files: file: src/direction.cpp placeholder: 1 is_visible: false -- name: src/borders.cpp - visible: true - placeholders: - - offset: 101 - length: 359 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: KeepSpinning - file: src/borders.cpp - placeholder: 1 - is_visible: false - name: src/point.cpp visible: true placeholders: - - offset: 88 - length: 37 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: KeepSpinning - file: src/point.cpp - placeholder: 1 - is_visible: false - - offset: 207 - length: 33 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: KeepSpinning - file: src/point.cpp - placeholder: 2 - is_visible: false - - offset: 333 - length: 35 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: KeepSpinning - file: src/point.cpp - placeholder: 3 - is_visible: false + - offset: 87 + length: 37 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: KeepSpinning + file: src/point.cpp + placeholder: 1 + is_visible: false + - offset: 206 + length: 33 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: KeepSpinning + file: src/point.cpp + placeholder: 2 + is_visible: false + - offset: 332 + length: 35 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: KeepSpinning + file: src/point.cpp + placeholder: 3 + is_visible: false - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: KeepSpinning - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: KeepSpinning + file: src/move.cpp + placeholder: 1 + is_visible: false - name: src/main.cpp visible: true - editable: true - name: CMakeLists.txt visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Limits+of+Possible diff --git a/WarmUp/MovingOn/LookAround/src/main.cpp b/WarmUp/MovingOn/LookAround/src/main.cpp index 691a5c4..215e90f 100644 --- a/WarmUp/MovingOn/LookAround/src/main.cpp +++ b/WarmUp/MovingOn/LookAround/src/main.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/LookAround/task-info.yaml b/WarmUp/MovingOn/LookAround/task-info.yaml index a9916e4..5ef9982 100644 --- a/WarmUp/MovingOn/LookAround/task-info.yaml +++ b/WarmUp/MovingOn/LookAround/task-info.yaml @@ -1,9 +1,6 @@ type: theory custom_name: Look Around files: -- name: src/main.cpp - visible: true - editable: true - name: CMakeLists.txt visible: false - name: src/move.cpp @@ -19,4 +16,6 @@ files: file: src/move.cpp placeholder: 1 is_visible: false +- name: src/main.cpp + visible: true feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Look+Around diff --git a/WarmUp/MovingOn/MoveAnObject/src/main.cpp b/WarmUp/MovingOn/MoveAnObject/src/main.cpp index 691a5c4..215e90f 100644 --- a/WarmUp/MovingOn/MoveAnObject/src/main.cpp +++ b/WarmUp/MovingOn/MoveAnObject/src/main.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/MoveAnObject/task-info.yaml b/WarmUp/MovingOn/MoveAnObject/task-info.yaml index 00c7ab7..c3112a8 100644 --- a/WarmUp/MovingOn/MoveAnObject/task-info.yaml +++ b/WarmUp/MovingOn/MoveAnObject/task-info.yaml @@ -3,8 +3,6 @@ custom_name: Move an Object files: - name: CMakeLists.txt visible: false -- name: src/main.cpp - visible: false - name: src/move.cpp visible: true placeholders: @@ -13,4 +11,6 @@ files: placeholder_text: position - name: test/test.cpp visible: false +- name: src/main.cpp + visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Move+an+Object diff --git a/WarmUp/MovingOn/MoveAnObject/test/test.cpp b/WarmUp/MovingOn/MoveAnObject/test/test.cpp index ee64744..a9ba049 100644 --- a/WarmUp/MovingOn/MoveAnObject/test/test.cpp +++ b/WarmUp/MovingOn/MoveAnObject/test/test.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "testing.hpp" testing::Environment* const env = diff --git a/WarmUp/MovingOn/MovingUp/src/main.cpp b/WarmUp/MovingOn/MovingUp/src/main.cpp index 5f26629..a96ff52 100644 --- a/WarmUp/MovingOn/MovingUp/src/main.cpp +++ b/WarmUp/MovingOn/MovingUp/src/main.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/MovingUp/src/point.cpp b/WarmUp/MovingOn/MovingUp/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/MovingUp/src/point.cpp +++ b/WarmUp/MovingOn/MovingUp/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/MovingUp/task-info.yaml b/WarmUp/MovingOn/MovingUp/task-info.yaml index 7da5ec1..c0c53de 100644 --- a/WarmUp/MovingOn/MovingUp/task-info.yaml +++ b/WarmUp/MovingOn/MovingUp/task-info.yaml @@ -4,32 +4,32 @@ files: - name: src/point.cpp visible: true placeholders: - - offset: 88 + - offset: 87 length: 37 placeholder_text: /* TODO */ - - offset: 207 + - offset: 206 length: 33 placeholder_text: /* TODO */ - - offset: 333 + - offset: 332 length: 35 placeholder_text: position - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: HavingFun - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: HavingFun + file: src/move.cpp + placeholder: 1 + is_visible: false +- name: test/test.cpp + visible: false - name: src/main.cpp visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Moving+Up diff --git a/WarmUp/MovingOn/MovingUp/test/test.cpp b/WarmUp/MovingOn/MovingUp/test/test.cpp index ec471cc..7b265b0 100644 --- a/WarmUp/MovingOn/MovingUp/test/test.cpp +++ b/WarmUp/MovingOn/MovingUp/test/test.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "operators.hpp" #include "testing.hpp" diff --git a/WarmUp/MovingOn/PlayAround/src/approaching.cpp b/WarmUp/MovingOn/PlayAround/src/approaching.cpp index ba0307c..12404e0 100644 --- a/WarmUp/MovingOn/PlayAround/src/approaching.cpp +++ b/WarmUp/MovingOn/PlayAround/src/approaching.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void approachingLoop(Circle player, Circle consumable[], bool concerned[], int size) { for (int i = 0; i < size; ++i) { diff --git a/WarmUp/MovingOn/PlayAround/src/borders.cpp b/WarmUp/MovingOn/PlayAround/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/PlayAround/src/borders.cpp +++ b/WarmUp/MovingOn/PlayAround/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/PlayAround/src/collision.cpp b/WarmUp/MovingOn/PlayAround/src/collision.cpp index 639aad3..449016a 100644 --- a/WarmUp/MovingOn/PlayAround/src/collision.cpp +++ b/WarmUp/MovingOn/PlayAround/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/WarmUp/MovingOn/PlayAround/src/direction.cpp b/WarmUp/MovingOn/PlayAround/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/PlayAround/src/direction.cpp +++ b/WarmUp/MovingOn/PlayAround/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/PlayAround/src/generate.cpp b/WarmUp/MovingOn/PlayAround/src/generate.cpp index 8b854eb..bc95378 100644 --- a/WarmUp/MovingOn/PlayAround/src/generate.cpp +++ b/WarmUp/MovingOn/PlayAround/src/generate.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" #include diff --git a/WarmUp/MovingOn/PlayAround/src/loop.cpp b/WarmUp/MovingOn/PlayAround/src/loop.cpp index 5fe6ce3..8148f4a 100644 --- a/WarmUp/MovingOn/PlayAround/src/loop.cpp +++ b/WarmUp/MovingOn/PlayAround/src/loop.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void collisionLoop(Circle player, Circle consumable[], bool consumed[], int size) { for (int i = 0; i < size; ++i) { diff --git a/WarmUp/MovingOn/PlayAround/src/main.cpp b/WarmUp/MovingOn/PlayAround/src/main.cpp index 2906421..aa8d82c 100644 --- a/WarmUp/MovingOn/PlayAround/src/main.cpp +++ b/WarmUp/MovingOn/PlayAround/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/PlayAround/src/point.cpp b/WarmUp/MovingOn/PlayAround/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/PlayAround/src/point.cpp +++ b/WarmUp/MovingOn/PlayAround/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/PlayAround/task-info.yaml b/WarmUp/MovingOn/PlayAround/task-info.yaml index 1dea63f..7e39269 100644 --- a/WarmUp/MovingOn/PlayAround/task-info.yaml +++ b/WarmUp/MovingOn/PlayAround/task-info.yaml @@ -24,7 +24,7 @@ files: - name: src/loop.cpp visible: true placeholders: - - offset: 110 + - offset: 109 length: 136 placeholder_text: return; dependency: @@ -37,7 +37,7 @@ files: - name: src/generate.cpp visible: true placeholders: - - offset: 121 + - offset: 120 length: 197 placeholder_text: /* TODO */ dependency: @@ -47,7 +47,7 @@ files: file: src/generate.cpp placeholder: 1 is_visible: false - - offset: 394 + - offset: 393 length: 49 placeholder_text: return min; dependency: @@ -60,43 +60,43 @@ files: - name: src/collision.cpp visible: true placeholders: - - offset: 83 - length: 83 - placeholder_text: return 0.0f; - dependency: - section: WarmUp - lesson: MovingOn - task: HuntingBugs - file: src/collision.cpp - placeholder: 1 - is_visible: false - - offset: 223 - length: 82 - placeholder_text: return false; - dependency: - section: WarmUp - lesson: MovingOn - task: HuntingBugs - file: src/collision.cpp - placeholder: 2 - is_visible: false + - offset: 82 + length: 83 + placeholder_text: return 0.0f; + dependency: + section: WarmUp + lesson: MovingOn + task: HuntingBugs + file: src/collision.cpp + placeholder: 1 + is_visible: false + - offset: 222 + length: 82 + placeholder_text: return false; + dependency: + section: WarmUp + lesson: MovingOn + task: HuntingBugs + file: src/collision.cpp + placeholder: 2 + is_visible: false - name: src/borders.cpp visible: true placeholders: - - offset: 101 - length: 359 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: HuntingBugs - file: src/borders.cpp - placeholder: 1 - is_visible: false + - offset: 100 + length: 359 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: HuntingBugs + file: src/borders.cpp + placeholder: 1 + is_visible: false - name: src/direction.cpp visible: true placeholders: - - offset: 70 + - offset: 69 length: 304 placeholder_text: "return { 0.0f, 0.0f };" dependency: @@ -109,49 +109,49 @@ files: - name: src/point.cpp visible: true placeholders: - - offset: 88 - length: 37 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: HuntingBugs - file: src/point.cpp - placeholder: 1 - is_visible: false - - offset: 207 - length: 33 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: HuntingBugs - file: src/point.cpp - placeholder: 2 - is_visible: false - - offset: 333 - length: 35 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: HuntingBugs - file: src/point.cpp - placeholder: 3 - is_visible: false + - offset: 87 + length: 37 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: HuntingBugs + file: src/point.cpp + placeholder: 1 + is_visible: false + - offset: 206 + length: 33 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: HuntingBugs + file: src/point.cpp + placeholder: 2 + is_visible: false + - offset: 332 + length: 35 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: HuntingBugs + file: src/point.cpp + placeholder: 3 + is_visible: false - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: HuntingBugs - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: HuntingBugs + file: src/move.cpp + placeholder: 1 + is_visible: false - name: src/main.cpp visible: true - name: CMakeLists.txt diff --git a/WarmUp/MovingOn/RunningInALoop/src/borders.cpp b/WarmUp/MovingOn/RunningInALoop/src/borders.cpp index b5ca906..c9df954 100644 --- a/WarmUp/MovingOn/RunningInALoop/src/borders.cpp +++ b/WarmUp/MovingOn/RunningInALoop/src/borders.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D adjustToBorders(Point2D position) { Point2D result = position; diff --git a/WarmUp/MovingOn/RunningInALoop/src/collision.cpp b/WarmUp/MovingOn/RunningInALoop/src/collision.cpp index 639aad3..449016a 100644 --- a/WarmUp/MovingOn/RunningInALoop/src/collision.cpp +++ b/WarmUp/MovingOn/RunningInALoop/src/collision.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" float distance(Point2D a, Point2D b) { float dx = a.x - b.x; diff --git a/WarmUp/MovingOn/RunningInALoop/src/direction.cpp b/WarmUp/MovingOn/RunningInALoop/src/direction.cpp index 2195184..ad7c2bd 100644 --- a/WarmUp/MovingOn/RunningInALoop/src/direction.cpp +++ b/WarmUp/MovingOn/RunningInALoop/src/direction.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D getDirection(Direction direction) { switch (direction) { diff --git a/WarmUp/MovingOn/RunningInALoop/src/generate.cpp b/WarmUp/MovingOn/RunningInALoop/src/generate.cpp index 8b854eb..bc95378 100644 --- a/WarmUp/MovingOn/RunningInALoop/src/generate.cpp +++ b/WarmUp/MovingOn/RunningInALoop/src/generate.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" #include diff --git a/WarmUp/MovingOn/RunningInALoop/src/loop.cpp b/WarmUp/MovingOn/RunningInALoop/src/loop.cpp index 5fe6ce3..8148f4a 100644 --- a/WarmUp/MovingOn/RunningInALoop/src/loop.cpp +++ b/WarmUp/MovingOn/RunningInALoop/src/loop.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" void collisionLoop(Circle player, Circle consumable[], bool consumed[], int size) { for (int i = 0; i < size; ++i) { diff --git a/WarmUp/MovingOn/RunningInALoop/src/main.cpp b/WarmUp/MovingOn/RunningInALoop/src/main.cpp index 6bdc8bb..b208448 100644 --- a/WarmUp/MovingOn/RunningInALoop/src/main.cpp +++ b/WarmUp/MovingOn/RunningInALoop/src/main.cpp @@ -1,6 +1,6 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/RunningInALoop/src/point.cpp b/WarmUp/MovingOn/RunningInALoop/src/point.cpp index 6e55434..5c74f69 100644 --- a/WarmUp/MovingOn/RunningInALoop/src/point.cpp +++ b/WarmUp/MovingOn/RunningInALoop/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/WarmUp/MovingOn/RunningInALoop/task-info.yaml b/WarmUp/MovingOn/RunningInALoop/task-info.yaml index 9f27e50..3237e04 100644 --- a/WarmUp/MovingOn/RunningInALoop/task-info.yaml +++ b/WarmUp/MovingOn/RunningInALoop/task-info.yaml @@ -4,36 +4,36 @@ files: - name: src/loop.cpp visible: true placeholders: - - offset: 110 - length: 136 - placeholder_text: return; + - offset: 109 + length: 136 + placeholder_text: return; - name: src/generate.cpp visible: true placeholders: - - offset: 121 - length: 197 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: ALittleSpontaneity - file: src/generate.cpp - placeholder: 1 - is_visible: false - - offset: 394 - length: 49 - placeholder_text: return min; - dependency: - section: WarmUp - lesson: MovingOn - task: ALittleSpontaneity - file: src/generate.cpp - placeholder: 2 - is_visible: false + - offset: 120 + length: 197 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: ALittleSpontaneity + file: src/generate.cpp + placeholder: 1 + is_visible: false + - offset: 393 + length: 49 + placeholder_text: return min; + dependency: + section: WarmUp + lesson: MovingOn + task: ALittleSpontaneity + file: src/generate.cpp + placeholder: 2 + is_visible: false - name: src/collision.cpp visible: true placeholders: - - offset: 83 + - offset: 82 length: 83 placeholder_text: return 0.0f; dependency: @@ -43,7 +43,7 @@ files: file: src/collision.cpp placeholder: 1 is_visible: false - - offset: 223 + - offset: 222 length: 82 placeholder_text: return false; dependency: @@ -56,7 +56,7 @@ files: - name: src/borders.cpp visible: true placeholders: - - offset: 101 + - offset: 100 length: 359 placeholder_text: /* TODO */ dependency: @@ -69,66 +69,66 @@ files: - name: src/direction.cpp visible: true placeholders: - - offset: 70 - length: 304 - placeholder_text: "return { 0.0f, 0.0f };" - dependency: - section: WarmUp - lesson: MovingOn - task: ConsumeAnObject - file: src/direction.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 304 + placeholder_text: "return { 0.0f, 0.0f };" + dependency: + section: WarmUp + lesson: MovingOn + task: ConsumeAnObject + file: src/direction.cpp + placeholder: 1 + is_visible: false - name: src/point.cpp visible: true placeholders: - - offset: 88 - length: 37 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: ALittleSpontaneity - file: src/point.cpp - placeholder: 1 - is_visible: false - - offset: 207 - length: 33 - placeholder_text: /* TODO */ - dependency: - section: WarmUp - lesson: MovingOn - task: ALittleSpontaneity - file: src/point.cpp - placeholder: 2 - is_visible: false - - offset: 333 - length: 35 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: ALittleSpontaneity - file: src/point.cpp - placeholder: 3 - is_visible: false + - offset: 87 + length: 37 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: ALittleSpontaneity + file: src/point.cpp + placeholder: 1 + is_visible: false + - offset: 206 + length: 33 + placeholder_text: /* TODO */ + dependency: + section: WarmUp + lesson: MovingOn + task: ALittleSpontaneity + file: src/point.cpp + placeholder: 2 + is_visible: false + - offset: 332 + length: 35 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: ALittleSpontaneity + file: src/point.cpp + placeholder: 3 + is_visible: false - name: src/move.cpp visible: true placeholders: - - offset: 69 - length: 27 - placeholder_text: position - dependency: - section: WarmUp - lesson: MovingOn - task: ALittleSpontaneity - file: src/move.cpp - placeholder: 1 - is_visible: false + - offset: 69 + length: 27 + placeholder_text: position + dependency: + section: WarmUp + lesson: MovingOn + task: ALittleSpontaneity + file: src/move.cpp + placeholder: 1 + is_visible: false +- name: test/test.cpp + visible: false - name: src/main.cpp visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Running+a+Loop diff --git a/WarmUp/MovingOn/RunningInALoop/test/test.cpp b/WarmUp/MovingOn/RunningInALoop/test/test.cpp index 4cc932f..217ffe6 100644 --- a/WarmUp/MovingOn/RunningInALoop/test/test.cpp +++ b/WarmUp/MovingOn/RunningInALoop/test/test.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "operators.hpp" #include "testing.hpp" diff --git a/WarmUp/MovingOn/StartTheGame/src/main.cpp b/WarmUp/MovingOn/StartTheGame/src/main.cpp index f9ea279..6a0303b 100644 --- a/WarmUp/MovingOn/StartTheGame/src/main.cpp +++ b/WarmUp/MovingOn/StartTheGame/src/main.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" void processEvent(sf::RenderWindow& window, const sf::Event& event) { switch (event.type) { diff --git a/WarmUp/MovingOn/StartTheGame/task-info.yaml b/WarmUp/MovingOn/StartTheGame/task-info.yaml index 3b1a8f1..313123d 100644 --- a/WarmUp/MovingOn/StartTheGame/task-info.yaml +++ b/WarmUp/MovingOn/StartTheGame/task-info.yaml @@ -4,8 +4,8 @@ files: - name: CMakeLists.txt visible: false editable: false -- name: src/main.cpp - visible: false - name: src/empty.cpp visible: true -feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Start+the+Game \ No newline at end of file +- name: src/main.cpp + visible: false +feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Start+the+Game diff --git a/include/circle.hpp b/include/circle.hpp new file mode 100644 index 0000000..80c186f --- /dev/null +++ b/include/circle.hpp @@ -0,0 +1,11 @@ +#ifndef CPPBASICS_CIRCLE_HPP +#define CPPBASICS_CIRCLE_HPP + +#include "point.hpp" + +struct Circle { + Point2D center; + float radius; +}; + +#endif //CPPBASICS_CIRCLE_HPP diff --git a/include/constants.hpp b/include/constants.hpp new file mode 100644 index 0000000..8384995 --- /dev/null +++ b/include/constants.hpp @@ -0,0 +1,24 @@ +#ifndef CPPBASICS_CONSTANTS_HPP +#define CPPBASICS_CONSTANTS_HPP + +const float RADIUS = 40.0f; +const float CONSUMABLE_RADIUS = 20.0f; + +const float SPEED = 150.0f; + +const float SCENE_WIDTH = 800.0f; +const float SCENE_HEIGHT = 600.0f; + +const float NORTH_BORDER = 0.0f; +const float WEST_BORDER = 0.0f; +const float EAST_BORDER = WEST_BORDER + SCENE_WIDTH; +const float SOUTH_BORDER = NORTH_BORDER + SCENE_HEIGHT; + +const float PLAYER_START_X = 400.0f; +const float PLAYER_START_Y = 300.0f; + +const float CONSUMABLE_START_X = 600.0f; +const float CONSUMABLE_START_Y = 150.0f; + + +#endif // CPPBASICS_CONSTANTS_HPP diff --git a/include/direction.hpp b/include/direction.hpp new file mode 100644 index 0000000..bccd4b2 --- /dev/null +++ b/include/direction.hpp @@ -0,0 +1,36 @@ +#ifndef CPPBASICS_DIRECTION_HPP +#define CPPBASICS_DIRECTION_HPP + +#include + +#include "point.hpp" +#include "constants.hpp" + +enum Direction { + North, + East, + South, + West, +}; + +Point2D getDirection(Direction direction); + +inline Point2D calculateVelocity() { + Point2D velocity = { 0.0f, 0.0f }; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = add(velocity, getDirection(North)); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = add(velocity, getDirection(East)); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = add(velocity, getDirection(South)); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = add(velocity, getDirection(West)); + } + velocity = mul(SPEED, velocity); + return velocity; +} + +#endif // CPPBASICS_DIRECTION_HPP diff --git a/include/scene.hpp b/include/game.hpp similarity index 70% rename from include/scene.hpp rename to include/game.hpp index c2698d7..b780484 100644 --- a/include/scene.hpp +++ b/include/game.hpp @@ -1,43 +1,19 @@ -#ifndef CPPBASICS_SCENE_HPP -#define CPPBASICS_SCENE_HPP +#ifndef CPPBASICS_GAME_HPP +#define CPPBASICS_GAME_HPP #include #include #include "point.hpp" - -const float RADIUS = 40.0f; -const float CONSUMABLE_RADIUS = 20.0f; +#include "circle.hpp" +#include "direction.hpp" +#include "constants.hpp" float move(float position, float velocity, float delta); -enum Direction { - North, - East, - South, - West, -}; - -Point2D getDirection(Direction direction); - -const float SPEED = 150.0f; - -const float SCENE_WIDTH = 800.0f; -const float SCENE_HEIGHT = 600.0f; - -const float NORTH_BORDER = 0.0f; -const float WEST_BORDER = 0.0f; -const float EAST_BORDER = WEST_BORDER + SCENE_WIDTH; -const float SOUTH_BORDER = NORTH_BORDER + SCENE_HEIGHT; - Point2D adjustToBorders(Point2D position); -struct Circle { - Point2D center; - float radius; -}; - float distance(Point2D a, Point2D b); bool collision(Circle circle1, Circle circle2); @@ -71,8 +47,6 @@ inline int initBackrground(sf::Sprite& sprite, sf::Texture& texture) { return 0; } -const float PLAYER_START_X = 400.0f; -const float PLAYER_START_Y = 300.0f; inline int initPlayer(sf::CircleShape& shape, sf::Texture& texture) { shape.setRadius(RADIUS); @@ -85,9 +59,6 @@ inline int initPlayer(sf::CircleShape& shape, sf::Texture& texture) { return 0; } -const float CONSUMABLE_START_X = 600.0f; -const float CONSUMABLE_START_Y = 150.0f; - inline int initConsumableTexture(sf::Texture& texture) { std::string filename = "resources/star.png"; if (!texture.loadFromFile(filename)) { @@ -141,22 +112,5 @@ inline int initConsumablesRandom(sf::CircleShape* shapes, int count, const sf::T return 0; } -inline Point2D calculateVelocity() { - Point2D velocity = { 0.0f, 0.0f }; - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { - velocity = add(velocity, getDirection(North)); - } - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { - velocity = add(velocity, getDirection(East)); - } - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { - velocity = add(velocity, getDirection(South)); - } - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { - velocity = add(velocity, getDirection(West)); - } - velocity = mul(SPEED, velocity); - return velocity; -} -#endif // CPPBASICS_SCENE_HPP +#endif // CPPBASICS_GAME_HPP From 1376a51cc97400d4aa4cd59ab951b333e272e8da Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Wed, 12 Jul 2023 22:19:53 +0200 Subject: [PATCH 004/137] working on framework lesson project implementation Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 16 +++++ .../IntroducingObjects/src/cgobject.cpp | 18 ++++++ .../IntroducingObjects/src/gobject.cpp | 5 ++ .../IntroducingObjects/src/operators.cpp | 17 ++++++ .../IntroducingObjects/src/point.cpp | 19 ++++++ .../IntroducingObjects/src/scene.cpp | 3 + .../IntroducingObjects/task-info.yaml | 58 +++++++++++++++++++ .../IntroducingObjects/task.md | 48 +++++++++++++++ .../IntroducingObjects/test/test.cpp | 11 ++++ .../OperatorsOverloading/src/operators.cpp | 2 +- .../OperatorsOverloading/src/point.cpp | 2 +- .../OperatorsOverloading/task-info.yaml | 28 ++++----- .../ClassesAndObjects/lesson-info.yaml | 1 + include/cgobject.hpp | 14 +++++ include/gevent.hpp | 6 ++ include/gobject.hpp | 30 ++++++++++ include/rectangle.hpp | 24 ++++++++ include/scene.hpp | 34 +++++++++++ 18 files changed, 320 insertions(+), 16 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp create mode 100644 include/cgobject.hpp create mode 100644 include/gevent.hpp create mode 100644 include/gobject.hpp create mode 100644 include/rectangle.hpp create mode 100644 include/scene.hpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt new file mode 100644 index 0000000..be3cf55 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-IntroducingObjects) + +set(TASK + src/gobject.cpp) + +set(SRC + ${TASK} src/operators.cpp src/point.cpp) + +set(TEST + test/test.cpp) + +include_directories(${CMAKE_SOURCE_DIR}/include/) + +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp new file mode 100644 index 0000000..f8df39f --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -0,0 +1,18 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +Point2D CircleGameObject::getPosition() const { + return shape.center; +} + +Point2D CircleGameObject::setPosition(Point2D position) { + shape.center = position; +} + +Rectangle CircleGameObject::boundingBox() const { + Point2D offset = { shape.radius, shape.radius }; + Point2D p1 = shape.center - offset; + Point2D p2 = shape.center + offset; + return createRectangle(p1, p2); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp new file mode 100644 index 0000000..e81f7af --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp @@ -0,0 +1,5 @@ +#include "gobject.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp new file mode 100644 index 0000000..75a298d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp @@ -0,0 +1,17 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp new file mode 100644 index 0000000..2299409 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -0,0 +1,3 @@ +// +// Created by eupp on 12.07.23. +// diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml new file mode 100644 index 0000000..6d682da --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -0,0 +1,58 @@ +type: edu +custom_name: Introducing Objects +files: +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false +- name: src/gobject.cpp + visible: true +- name: src/operators.cpp + visible: true + placeholders: + - offset: 67 + length: 17 + placeholder_text: "return { 0.0f, 0.0f };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 1 + is_visible: false + - offset: 123 + length: 22 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 2 + is_visible: false + - offset: 195 + length: 18 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 3 + is_visible: false + - offset: 261 + length: 17 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 4 + is_visible: false +- name: src/point.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/cgobject.cpp + visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md new file mode 100644 index 0000000..fa7499b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md @@ -0,0 +1,48 @@ + +This is a task description file. +Its content will be displayed to a learner +in the **Task Description** window. + +It supports both Markdown and HTML. +To toggle the format, you can rename **task.md** +to **task.html**, or vice versa. +The default task description format can be changed +in **Preferences | Tools | Education**, +but this will not affect any existing task description files. + +The following features are available in +**task.md/task.html** which are specific to the JetBrains Academy plugin: + +- Hints can be added anywhere in the task text. + Type "hint" and press Tab. + Hints should be added to an empty line in the task text. + In hints you can use both HTML and Markdown. +
+ +Text of your hint + +
+ +- You may need to refer your learners to a particular lesson, +task, or file. To achieve this, you can use the in-course links. +Specify the path using the `[link_text](course://lesson1/task1/file1)` format. + +- You can insert shortcuts in the task description. +While **task.html/task.md** is open, right-click anywhere +on the **Editor** tab and choose the **Insert shortcut** option +from the context menu. +For example: &shortcut:FileStructurePopup;. + +- Insert the %`IDE_NAME`% macro, +which will be replaced by the actual IDE name. +For example, **%IDE_NAME%**. + +- Insert PSI elements, by using links like +`[element_description](psi_element://link.to.element)`. +To get such a link, right-click the class or method +and select **Copy Reference**. +Then press &shortcut:EditorPaste; to insert the link where appropriate. +For example, a [link to the "contains" method](psi_element://java.lang.String#contains). + +- You can add link to file using **full path** like this: + `[file_link](file://lesson1/task1/file.txt)`. \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp new file mode 100644 index 0000000..734038d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp @@ -0,0 +1,11 @@ +#include + +// Headers of objects that student should implement: +int sum(int a, int b); + + +// Tests: +// todo: replace this with an actual test +TEST(SumTest, Simple) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class + ASSERT_EQ(sum(1, 2), 3); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp index 824187c..75a298d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D operator+(Point2D a, Point2D b) { return add(a, b); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp index 6e55434..5c74f69 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/point.cpp @@ -1,4 +1,4 @@ -#include "scene.hpp" +#include "game.hpp" Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml index d710281..d2944ca 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml @@ -3,22 +3,22 @@ custom_name: Operators Overloading files: - name: CMakeLists.txt visible: false +- name: test/test.cpp + visible: false - name: src/operators.cpp visible: true placeholders: - - offset: 68 - length: 17 - placeholder_text: "return { 0.0f, 0.0f };" - - offset: 124 - length: 22 - placeholder_text: "return { 0.0f, 0.0f, };" - - offset: 196 - length: 18 - placeholder_text: "return { 0.0f, 0.0f, };" - - offset: 262 - length: 17 - placeholder_text: "return { 0.0f, 0.0f, };" + - offset: 67 + length: 17 + placeholder_text: "return { 0.0f, 0.0f };" + - offset: 123 + length: 22 + placeholder_text: "return { 0.0f, 0.0f, };" + - offset: 195 + length: 18 + placeholder_text: "return { 0.0f, 0.0f, };" + - offset: 261 + length: 17 + placeholder_text: "return { 0.0f, 0.0f, };" - name: src/point.cpp visible: true -- name: test/test.cpp - visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 19f536c..4de47f2 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -2,3 +2,4 @@ type: framework custom_name: Classes And Objects content: - OperatorsOverloading +- IntroducingObjects diff --git a/include/cgobject.hpp b/include/cgobject.hpp new file mode 100644 index 0000000..71dd5f0 --- /dev/null +++ b/include/cgobject.hpp @@ -0,0 +1,14 @@ +#ifndef CPPBASICS_CGOBJECT_HPP +#define CPPBASICS_CGOBJECT_HPP + +#include "gobject.hpp" +#include "circle.hpp" + +class CircleGameObject : public GameObject { +public: + +private: + Circle shape; +}; + +#endif // CPPBASICS_CGOBJECT_HPP diff --git a/include/gevent.hpp b/include/gevent.hpp new file mode 100644 index 0000000..9e77cac --- /dev/null +++ b/include/gevent.hpp @@ -0,0 +1,6 @@ +#ifndef CPPBASICS_GEVENT_HPP +#define CPPBASICS_GEVENT_HPP + +class GameEvent {}; + +#endif //CPPBASICS_GEVENT_HPP diff --git a/include/gobject.hpp b/include/gobject.hpp new file mode 100644 index 0000000..5f60924 --- /dev/null +++ b/include/gobject.hpp @@ -0,0 +1,30 @@ +#ifndef CPPBASICS_GOBJECT_HPP +#define CPPBASICS_GOBJECT_HPP + +#include + +#include "point.hpp" +#include "rectangle.hpp" +#include "operators.hpp" +#include "gevent.hpp" + +class GameObject { +public: + + void move(Point2D vector); + + virtual Point2D getPosition() const = 0; + + virtual void setPosition(Point2D position) = 0; + + virtual Rectangle boundingBox() const = 0; + + virtual void draw(sf::RenderWindow& window) const = 0; + + virtual void processEvent(const GameEvent& event) = 0; + + virtual ~GameObject() {} + +}; + +#endif // CPPBASICS_GOBJECT_HPP diff --git a/include/rectangle.hpp b/include/rectangle.hpp new file mode 100644 index 0000000..812fed1 --- /dev/null +++ b/include/rectangle.hpp @@ -0,0 +1,24 @@ +#ifndef CPPBASICS_RECTANGLE_HPP +#define CPPBASICS_RECTANGLE_HPP + +#include "point.hpp" + +struct Rectangle { + Point2D topLeft; + Point2D botRight; +}; + +inline bool isEmpty(const Rectangle& rect) { + return (rect.topLeft.x == rect.botRight.x) || (rect.topLeft.y == rect.botRight.y); +} + +inline Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect); + +#endif // CPPBASICS_RECTANGLE_HPP diff --git a/include/scene.hpp b/include/scene.hpp new file mode 100644 index 0000000..92126a6 --- /dev/null +++ b/include/scene.hpp @@ -0,0 +1,34 @@ +#ifndef CPPBASICS_SCENE_HPP +#define CPPBASICS_SCENE_HPP + +#include + +#include "point.hpp" +#include "gobject.hpp" + +class Scene { +public: + + virtual void processEvent(const sf::Event& event) = 0; + + virtual void update(sf::Time delta) = 0; + + virtual void render() = 0; + + virtual ~Scene() {} + + static Scene* create(); + +protected: + void move(GameObject& object, Point2D vector); + +private: + void fitInto(GameObject& object); + + float width; + float height; + + sf::RenderWindow window; +}; + +#endif // CPPBASICS_SCENE_HPP From d86491cd9243a8aaa0e93a74cc9164e928b99763 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Sun, 16 Jul 2023 23:02:18 +0200 Subject: [PATCH 005/137] working on framework lesson project implementation Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 16 ++++++-- .../IntroducingObjects/src/cgobject.cpp | 35 +++++++++++++++--- .../IntroducingObjects/src/collision.cpp | 29 +++++++++++++++ .../IntroducingObjects/src/consumable.cpp | 7 ++++ .../IntroducingObjects/src/enemy.cpp | 5 +++ .../IntroducingObjects/src/gobject.cpp | 2 + .../IntroducingObjects/src/player.cpp | 5 +++ .../IntroducingObjects/src/rectangle.cpp | 24 ++++++++++++ .../IntroducingObjects/src/scene.cpp | 37 +++++++++++++++++-- .../IntroducingObjects/src/simplscene.cpp | 36 ++++++++++++++++++ .../IntroducingObjects/task-info.yaml | 16 +++++++- include/cgobject.hpp | 21 ++++++++++- include/collision.hpp | 19 ++++++++++ include/consumable.hpp | 11 ++++++ include/enemy.hpp | 11 ++++++ include/game.hpp | 3 +- include/gobject.hpp | 13 +++++-- include/player.hpp | 11 ++++++ include/rectangle.hpp | 14 +++++++ include/scene.hpp | 13 +++++-- include/utils.hpp | 8 ++++ 21 files changed, 310 insertions(+), 26 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp create mode 100644 include/collision.hpp create mode 100644 include/consumable.hpp create mode 100644 include/enemy.hpp create mode 100644 include/player.hpp create mode 100644 include/utils.hpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index be3cf55..f6ada7a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -3,14 +3,22 @@ cmake_minimum_required(VERSION 3.25) project(ObjectOrientedProgramming-ClassesAndObjects-IntroducingObjects) set(TASK - src/gobject.cpp) + src/cgobject.cpp) set(SRC - ${TASK} src/operators.cpp src/point.cpp) + ${TASK} + src/scene.cpp src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) set(TEST test/test.cpp) -include_directories(${CMAKE_SOURCE_DIR}/include/) +add_executable(${PROJECT_NAME}-run ${SRC}) -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index f8df39f..eb4a709 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -2,17 +2,40 @@ #include "operators.hpp" +CircleGameObject::CircleGameObject(Circle circle, sf::Texture *texture) + : circle(circle) + , state(LIVE) + , texture(texture) +{} + Point2D CircleGameObject::getPosition() const { - return shape.center; + return circle.center; } -Point2D CircleGameObject::setPosition(Point2D position) { - shape.center = position; +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; } Rectangle CircleGameObject::boundingBox() const { - Point2D offset = { shape.radius, shape.radius }; - Point2D p1 = shape.center - offset; - Point2D p2 = shape.center + offset; + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; return createRectangle(p1, p2); } + +GameObjectState CircleGameObject::getState() const { + return state; +} + +void CircleGameObject::setState(GameObjectState newState) { + state = newState; +} + +void CircleGameObject::draw(sf::RenderWindow &window) const { + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.center.x, circle.center.y); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp new file mode 100644 index 0000000..b64ba97 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp @@ -0,0 +1,29 @@ +#include + +#include "collision.hpp" +#include "operators.hpp" +#include "utils.hpp" + +CollisionData collision(const Circle& circle1, const Circle& circle2) { + float dx = circle2.center.x - circle1.center.x; + float dy = circle2.center.y - circle1.center.y; + float dist = sqrt(dx * dx + dy * dy); + Point2D d = { dx, dy }; + CollisionData collisionData; + collisionData.collide = (dist < circle1.radius + circle2.radius); + collisionData.distance = dist; + collisionData.normalVector = (1 / dist) * d; + return collisionData; +} + +bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionData& collisionData) { + return collisionData.distance < 3 * circle1.radius + circle2.radius; +} + +Point2D calculateResolutionVector(const Circle& circle1, const Circle& circle2, + const CollisionData& collisionData, float resolutionFactor) { + Point2D border1 = circle1.center + circle1.radius * collisionData.normalVector; + Point2D border2 = circle2.center - circle2.radius * collisionData.normalVector; + float dist = distance(border1, border2); + return -(dist * resolutionFactor) * collisionData.normalVector; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp new file mode 100644 index 0000000..9af832d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -0,0 +1,7 @@ +#include "consumable.hpp" + +void ConsumableObject::onCollision(const GameObject &object, const CollisionData &collisionData) { + if (collisionData.collide) { + CircleGameObject::setState(DEAD); + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp new file mode 100644 index 0000000..6bef822 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -0,0 +1,5 @@ +#include "enemy.hpp" + +void EnemyObject::onCollision(const GameObject &object, const CollisionData &collisionData) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp index e81f7af..a673ef5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobject.cpp @@ -1,5 +1,7 @@ #include "gobject.hpp" +#include "operators.hpp" + void GameObject::move(Point2D vector) { setPosition(getPosition() + vector); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp new file mode 100644 index 0000000..83410ef --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -0,0 +1,5 @@ +#include "player.hpp" + +void PlayerObject::onCollision(const GameObject &object, const CollisionData &collisionData) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp new file mode 100644 index 0000000..e2a28bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp @@ -0,0 +1,24 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x -= rect.botRight.x - intoRect.botRight.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y -= rect.botRight.y - intoRect.botRight.y; + } + // TODO: implement operators for Rectangle (and Circle) ? + return { rect.topLeft + vector, rect.botRight + vector }; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 2299409..bb70195 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -1,3 +1,34 @@ -// -// Created by eupp on 12.07.23. -// +#include "scene.hpp" + +#include "constants.hpp" + +Scene::Scene() + : width(SCENE_WIDTH) + , height(SCENE_HEIGHT) +{ + window.create(sf::VideoMode(width, height), "Space Game"); + window.setFramerateLimit(60); +} + +Rectangle Scene::boundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::move(GameObject &object, Point2D vector) { + object.move(vector); + fitInto(object); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.boundingBox(), boundingBox()); + object.setPosition(center(rect)); +} + +void Scene::draw(const GameObject &object) { + if (object.getState() == LIVE) { + object.draw(window); + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp new file mode 100644 index 0000000..664d66b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp @@ -0,0 +1,36 @@ +#include "scene.hpp" + +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" + +class SimpleScene : public Scene { +public: + + void processEvent(const sf::Event &event) override { + switch (event.type) { + case sf::Event::Closed: + window.close(); + break; + default: + break; + } + } + + void update(sf::Time delta) override { + Point2D playerVelocity = calculateVelocity(); + move(player, playerVelocity); + } + + void render() override { + draw(player); + draw(consumable); + draw(enemy); + window.display(); + } + +private: + PlayerObject player; + ConsumableObject consumable; + EnemyObject enemy; +}; \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 6d682da..e235c4d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -5,8 +5,6 @@ files: visible: false - name: test/test.cpp visible: false -- name: src/gobject.cpp - visible: true - name: src/operators.cpp visible: true placeholders: @@ -56,3 +54,17 @@ files: visible: true - name: src/cgobject.cpp visible: true +- name: src/gobject.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/simplscene.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/enemy.cpp + visible: true diff --git a/include/cgobject.hpp b/include/cgobject.hpp index 71dd5f0..745b379 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -1,14 +1,31 @@ #ifndef CPPBASICS_CGOBJECT_HPP #define CPPBASICS_CGOBJECT_HPP +#include + #include "gobject.hpp" #include "circle.hpp" class CircleGameObject : public GameObject { public: + CircleGameObject(Circle circle, sf::Texture* texture); + + Point2D getPosition() const override; + + void setPosition(Point2D position) override; + + Rectangle boundingBox() const override; + + GameObjectState getState() const override; + + void setState(GameObjectState newState) override; + + void draw(sf::RenderWindow &window) const override; -private: - Circle shape; +protected: + Circle circle; + GameObjectState state; + sf::Texture* texture; }; #endif // CPPBASICS_CGOBJECT_HPP diff --git a/include/collision.hpp b/include/collision.hpp new file mode 100644 index 0000000..4cf2bd9 --- /dev/null +++ b/include/collision.hpp @@ -0,0 +1,19 @@ +#ifndef CPPBASICS_COLLISION_HPP +#define CPPBASICS_COLLISION_HPP + +#include "circle.hpp" + +struct CollisionData { + bool collide; + float distance; + Point2D normalVector; +}; + +CollisionData collision(const Circle& circle1, const Circle& circle2); + +bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionData& collisionData); + +Point2D calculateResolutionVector(const Circle& circle1, const Circle& circle2, + const CollisionData& collisionData, float resolutionFactor = 1.0f); + +#endif // CPPBASICS_COLLISION_HPP diff --git a/include/consumable.hpp b/include/consumable.hpp new file mode 100644 index 0000000..8926a0e --- /dev/null +++ b/include/consumable.hpp @@ -0,0 +1,11 @@ +#ifndef CPPBASICS_CONSUMABLE_HPP +#define CPPBASICS_CONSUMABLE_HPP + +#include "cgobject.hpp" + +class ConsumableObject : public CircleGameObject { +public: + void onCollision(const GameObject &object, const CollisionData &collisionData) override; +}; + +#endif // CPPBASICS_CONSUMABLE_HPP diff --git a/include/enemy.hpp b/include/enemy.hpp new file mode 100644 index 0000000..8f11950 --- /dev/null +++ b/include/enemy.hpp @@ -0,0 +1,11 @@ +#ifndef CPPBASICS_ENEMY_HPP +#define CPPBASICS_ENEMY_HPP + +#include "cgobject.hpp" + +class EnemyObject : public CircleGameObject { +public: + void onCollision(const GameObject &object, const CollisionData &collisionData) override; +}; + +#endif // CPPBASICS_ENEMY_HPP diff --git a/include/game.hpp b/include/game.hpp index b780484..5fd78c9 100644 --- a/include/game.hpp +++ b/include/game.hpp @@ -8,14 +8,13 @@ #include "point.hpp" #include "circle.hpp" #include "direction.hpp" +#include "utils.hpp" #include "constants.hpp" float move(float position, float velocity, float delta); Point2D adjustToBorders(Point2D position); -float distance(Point2D a, Point2D b); - bool collision(Circle circle1, Circle circle2); void collisionLoop(Circle player, Circle consumable[], bool consumed[], int size); void approachingLoop(Circle player, Circle consumable[], bool warned[], int size); diff --git a/include/gobject.hpp b/include/gobject.hpp index 5f60924..18f87ad 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -5,8 +5,11 @@ #include "point.hpp" #include "rectangle.hpp" -#include "operators.hpp" -#include "gevent.hpp" +#include "collision.hpp" + +enum GameObjectState { + LIVE, DEAD +}; class GameObject { public: @@ -19,9 +22,13 @@ class GameObject { virtual Rectangle boundingBox() const = 0; + virtual GameObjectState getState() const = 0; + + virtual void setState(GameObjectState newState) = 0; + virtual void draw(sf::RenderWindow& window) const = 0; - virtual void processEvent(const GameEvent& event) = 0; + virtual void onCollision(const GameObject& object, const CollisionData& collisionData) = 0; virtual ~GameObject() {} diff --git a/include/player.hpp b/include/player.hpp new file mode 100644 index 0000000..97e4bcd --- /dev/null +++ b/include/player.hpp @@ -0,0 +1,11 @@ +#ifndef CPPBASICS_PLAYER_HPP +#define CPPBASICS_PLAYER_HPP + +#include "cgobject.hpp" + +class PlayerObject : public CircleGameObject { +public: + void onCollision(const GameObject &object, const CollisionData &collisionData) override; +}; + +#endif // CPPBASICS_PLAYER_HPP diff --git a/include/rectangle.hpp b/include/rectangle.hpp index 812fed1..6fd8fdd 100644 --- a/include/rectangle.hpp +++ b/include/rectangle.hpp @@ -2,6 +2,7 @@ #define CPPBASICS_RECTANGLE_HPP #include "point.hpp" +#include "operators.hpp" struct Rectangle { Point2D topLeft; @@ -12,6 +13,19 @@ inline bool isEmpty(const Rectangle& rect) { return (rect.topLeft.x == rect.botRight.x) || (rect.topLeft.y == rect.botRight.y); } +inline float width(const Rectangle& rect) { + return rect.botRight.x - rect.topLeft.x; +} + +inline float height(const Rectangle& rect) { + return rect.botRight.y - rect.topLeft.y; +} + +inline Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return 0.5f * Point2D { width(rect), height(rect) }; +} + inline Rectangle createRectangle(Point2D p1, Point2D p2) { Rectangle rect; rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; diff --git a/include/scene.hpp b/include/scene.hpp index 92126a6..4ff04ba 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -9,26 +9,31 @@ class Scene { public: + Scene(); + + virtual ~Scene() {} + virtual void processEvent(const sf::Event& event) = 0; virtual void update(sf::Time delta) = 0; virtual void render() = 0; - virtual ~Scene() {} - static Scene* create(); protected: + Rectangle boundingBox() const; + void move(GameObject& object, Point2D vector); + void draw(const GameObject& object); + + sf::RenderWindow window; private: void fitInto(GameObject& object); float width; float height; - - sf::RenderWindow window; }; #endif // CPPBASICS_SCENE_HPP diff --git a/include/utils.hpp b/include/utils.hpp new file mode 100644 index 0000000..319af03 --- /dev/null +++ b/include/utils.hpp @@ -0,0 +1,8 @@ +#ifndef CPPBASICS_UTILS_HPP +#define CPPBASICS_UTILS_HPP + +#include "point.hpp" + +float distance(Point2D a, Point2D b); + +#endif // CPPBASICS_UTILS_HPP From 0f1845aa022cd2c4d7f2edc22086e68855ce9139 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Tue, 8 Aug 2023 11:04:29 +0200 Subject: [PATCH 006/137] working on framework lesson project implementation Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 5 ++-- .../IntroducingObjects/src/cgobject.cpp | 4 +++ .../IntroducingObjects/src/collision.cpp | 20 +++++++++++--- .../IntroducingObjects/src/consumable.cpp | 10 ++++++- .../IntroducingObjects/src/direction.cpp | 16 +++++++++++ .../IntroducingObjects/src/enemy.cpp | 10 ++++++- .../IntroducingObjects/src/player.cpp | 27 ++++++++++++++++++- .../IntroducingObjects/src/scene.cpp | 6 +++++ .../IntroducingObjects/src/simplscene.cpp | 6 +++-- .../IntroducingObjects/task-info.yaml | 10 ++++--- include/cgobject.hpp | 2 ++ include/collision.hpp | 11 +++++--- include/consumable.hpp | 6 ++++- include/enemy.hpp | 6 ++++- include/gobject.hpp | 4 ++- include/player.hpp | 6 ++++- include/scene.hpp | 2 ++ include/textures.hpp | 15 +++++++++++ 18 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/direction.cpp create mode 100644 include/textures.hpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index f6ada7a..76405c6 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -7,8 +7,9 @@ set(TASK set(SRC ${TASK} - src/scene.cpp src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp - src/collision.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) + src/scene.cpp src/simplscene.cpp + src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) set(TEST test/test.cpp) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index eb4a709..d5eca1a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -23,6 +23,10 @@ Rectangle CircleGameObject::boundingBox() const { return createRectangle(p1, p2); } +Circle CircleGameObject::getCircle() const { + return circle; +} + GameObjectState CircleGameObject::getState() const { return state; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp index b64ba97..da72e71 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp @@ -1,29 +1,41 @@ #include +#include #include "collision.hpp" #include "operators.hpp" +#include "cgobject.hpp" #include "utils.hpp" -CollisionData collision(const Circle& circle1, const Circle& circle2) { +CollisionInfo collision(const Circle& circle1, const Circle& circle2) { float dx = circle2.center.x - circle1.center.x; float dy = circle2.center.y - circle1.center.y; float dist = sqrt(dx * dx + dy * dy); Point2D d = { dx, dy }; - CollisionData collisionData; + CollisionInfo collisionData; collisionData.collide = (dist < circle1.radius + circle2.radius); collisionData.distance = dist; collisionData.normalVector = (1 / dist) * d; return collisionData; } -bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionData& collisionData) { +bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionInfo& collisionData) { return collisionData.distance < 3 * circle1.radius + circle2.radius; } Point2D calculateResolutionVector(const Circle& circle1, const Circle& circle2, - const CollisionData& collisionData, float resolutionFactor) { + const CollisionInfo& collisionData, float resolutionFactor) { Point2D border1 = circle1.center + circle1.radius * collisionData.normalVector; Point2D border2 = circle2.center - circle2.radius * collisionData.normalVector; float dist = distance(border1, border2); return -(dist * resolutionFactor) * collisionData.normalVector; } + +CollisionInfo collision(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collision(circleObject1->getCircle(), circleObject2->getCircle()); + } + // TODO + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index 9af832d..ccefbf5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -1,6 +1,14 @@ #include "consumable.hpp" -void ConsumableObject::onCollision(const GameObject &object, const CollisionData &collisionData) { +ConsumableObject::ConsumableObject(Circle circle, sf::Texture *texture) + : CircleGameObject(circle, texture) +{} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { if (collisionData.collide) { CircleGameObject::setState(DEAD); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 6bef822..812ba20 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -1,5 +1,13 @@ #include "enemy.hpp" -void EnemyObject::onCollision(const GameObject &object, const CollisionData &collisionData) { +EnemyObject::EnemyObject(Circle circle, sf::Texture *texture) + : CircleGameObject(circle, texture) +{} + +Point2D EnemyObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { return; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index 83410ef..d4e5ac3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -1,5 +1,30 @@ #include "player.hpp" -void PlayerObject::onCollision(const GameObject &object, const CollisionData &collisionData) { +#include "direction.hpp" +#include "constants.hpp" + +PlayerObject::PlayerObject(Circle circle, sf::Texture *texture) + : CircleGameObject(circle, texture) +{} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { return; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index bb70195..5734235 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -27,6 +27,12 @@ void Scene::fitInto(GameObject &object) { object.setPosition(center(rect)); } +void Scene::detectCollision(GameObject &object1, GameObject &object2) { + CollisionInfo info = collision(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + void Scene::draw(const GameObject &object) { if (object.getState() == LIVE) { object.draw(window); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp index 664d66b..ea39d0e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp @@ -18,8 +18,10 @@ class SimpleScene : public Scene { } void update(sf::Time delta) override { - Point2D playerVelocity = calculateVelocity(); - move(player, playerVelocity); + move(player, 0.001f * delta.asMilliseconds() * player.getVelocity()); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); } void render() override { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index e235c4d..06f3ad9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -50,8 +50,6 @@ files: is_visible: false - name: src/point.cpp visible: true -- name: src/scene.cpp - visible: true - name: src/cgobject.cpp visible: true - name: src/gobject.cpp @@ -60,11 +58,15 @@ files: visible: true - name: src/rectangle.cpp visible: true -- name: src/simplscene.cpp - visible: true - name: src/consumable.cpp visible: true - name: src/player.cpp visible: true - name: src/enemy.cpp visible: true +- name: src/scene.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/simplscene.cpp + visible: true diff --git a/include/cgobject.hpp b/include/cgobject.hpp index 745b379..51def16 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -16,6 +16,8 @@ class CircleGameObject : public GameObject { Rectangle boundingBox() const override; + Circle getCircle() const; + GameObjectState getState() const override; void setState(GameObjectState newState) override; diff --git a/include/collision.hpp b/include/collision.hpp index 4cf2bd9..14b5f57 100644 --- a/include/collision.hpp +++ b/include/collision.hpp @@ -2,18 +2,21 @@ #define CPPBASICS_COLLISION_HPP #include "circle.hpp" +#include "gobject.hpp" -struct CollisionData { +struct CollisionInfo { bool collide; float distance; Point2D normalVector; }; -CollisionData collision(const Circle& circle1, const Circle& circle2); +CollisionInfo collision(const Circle& circle1, const Circle& circle2); -bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionData& collisionData); +bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionInfo& collisionData); Point2D calculateResolutionVector(const Circle& circle1, const Circle& circle2, - const CollisionData& collisionData, float resolutionFactor = 1.0f); + const CollisionInfo& collisionData, float resolutionFactor = 1.0f); + +CollisionInfo collision(const GameObject& object1, const GameObject& object2); #endif // CPPBASICS_COLLISION_HPP diff --git a/include/consumable.hpp b/include/consumable.hpp index 8926a0e..3098cf9 100644 --- a/include/consumable.hpp +++ b/include/consumable.hpp @@ -5,7 +5,11 @@ class ConsumableObject : public CircleGameObject { public: - void onCollision(const GameObject &object, const CollisionData &collisionData) override; + ConsumableObject(Circle circle, sf::Texture *texture); + + Point2D getVelocity() const override; + + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; #endif // CPPBASICS_CONSUMABLE_HPP diff --git a/include/enemy.hpp b/include/enemy.hpp index 8f11950..9640d66 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -5,7 +5,11 @@ class EnemyObject : public CircleGameObject { public: - void onCollision(const GameObject &object, const CollisionData &collisionData) override; + EnemyObject(Circle circle, sf::Texture* texture); + + Point2D getVelocity() const override; + + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; #endif // CPPBASICS_ENEMY_HPP diff --git a/include/gobject.hpp b/include/gobject.hpp index 18f87ad..cd00782 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -20,6 +20,8 @@ class GameObject { virtual void setPosition(Point2D position) = 0; + virtual Point2D getVelocity() const = 0; + virtual Rectangle boundingBox() const = 0; virtual GameObjectState getState() const = 0; @@ -28,7 +30,7 @@ class GameObject { virtual void draw(sf::RenderWindow& window) const = 0; - virtual void onCollision(const GameObject& object, const CollisionData& collisionData) = 0; + virtual void onCollision(const GameObject& object, const CollisionInfo& collisionData) = 0; virtual ~GameObject() {} diff --git a/include/player.hpp b/include/player.hpp index 97e4bcd..55cb1a3 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -5,7 +5,11 @@ class PlayerObject : public CircleGameObject { public: - void onCollision(const GameObject &object, const CollisionData &collisionData) override; + PlayerObject(Circle circle, sf::Texture* texture); + + Point2D getVelocity() const override; + + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; #endif // CPPBASICS_PLAYER_HPP diff --git a/include/scene.hpp b/include/scene.hpp index 4ff04ba..932e472 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -26,6 +26,8 @@ class Scene { void move(GameObject& object, Point2D vector); + void detectCollision(GameObject& object1, GameObject& object2); + void draw(const GameObject& object); sf::RenderWindow window; diff --git a/include/textures.hpp b/include/textures.hpp new file mode 100644 index 0000000..051e16d --- /dev/null +++ b/include/textures.hpp @@ -0,0 +1,15 @@ +#ifndef CPPBASICS_TEXTURES_HPP +#define CPPBASICS_TEXTURES_HPP + +#include + +enum GameTexture { + BACKGROUND_T +}; + +class TextureManager { +public: + sf::Texture* getTexture(); +}; + +#endif // CPPBASICS_TEXTURES_HPP From 3c79f86c4a13cc4e305db079512875a1a9ea4a59 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Thu, 10 Aug 2023 10:23:27 +0200 Subject: [PATCH 007/137] working on framework lesson project implementation Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 2 +- .../IntroducingObjects/src/scene.cpp | 2 +- .../IntroducingObjects/src/textures.cpp | 38 +++++++++++++++++++ .../IntroducingObjects/task-info.yaml | 2 + include/scene.hpp | 7 +++- include/textures.hpp | 19 ++++++++-- 6 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index 76405c6..6e2eabc 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -7,7 +7,7 @@ set(TASK set(SRC ${TASK} - src/scene.cpp src/simplscene.cpp + src/scene.cpp src/simplscene.cpp src/textures.cpp src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 5734235..6ea0016 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -27,7 +27,7 @@ void Scene::fitInto(GameObject &object) { object.setPosition(center(rect)); } -void Scene::detectCollision(GameObject &object1, GameObject &object2) { +void Scene::detectCollision(GameObject& object1, GameObject& object2) { CollisionInfo info = collision(object1, object2); object1.onCollision(object2, info); object2.onCollision(object1, info); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp new file mode 100644 index 0000000..e7080a7 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp @@ -0,0 +1,38 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::BLACK_HOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 06f3ad9..2ae2747 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -70,3 +70,5 @@ files: visible: true - name: src/simplscene.cpp visible: true +- name: src/textures.cpp + visible: true diff --git a/include/scene.hpp b/include/scene.hpp index 932e472..cf339b5 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -5,6 +5,7 @@ #include "point.hpp" #include "gobject.hpp" +#include "textures.hpp" class Scene { public: @@ -30,10 +31,12 @@ class Scene { void draw(const GameObject& object); - sf::RenderWindow window; -private: void fitInto(GameObject& object); +protected: + sf::RenderWindow window; + TextureManager textureManager; +private: float width; float height; }; diff --git a/include/textures.hpp b/include/textures.hpp index 051e16d..301810a 100644 --- a/include/textures.hpp +++ b/include/textures.hpp @@ -1,15 +1,28 @@ #ifndef CPPBASICS_TEXTURES_HPP #define CPPBASICS_TEXTURES_HPP +#include + #include -enum GameTexture { - BACKGROUND_T +enum class GameTextureID { + SPACE, + PLANET, + STAR, + BLACK_HOLE, + SIZE }; class TextureManager { public: - sf::Texture* getTexture(); + bool initialize(); + + const sf::Texture* getTexture(GameTextureID id); +private: + static const size_t SIZE = static_cast(GameTextureID::SIZE); + + sf::Texture textures[SIZE]; + }; #endif // CPPBASICS_TEXTURES_HPP From 2c85d401c79db1e0c9bfead43d8ca622ddd2076d Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Thu, 10 Aug 2023 10:23:48 +0200 Subject: [PATCH 008/137] add black hole texture Signed-off-by: Evgenii Moiseenko --- resources/balckhole.png | Bin 0 -> 15345 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/balckhole.png diff --git a/resources/balckhole.png b/resources/balckhole.png new file mode 100644 index 0000000000000000000000000000000000000000..64d103a3720c212d9b82344dbf307d67303ebef8 GIT binary patch literal 15345 zcmb_jWmlX{kR9A%aEAc~cXziLf?JRfoIsEO!F6y*(7_=%1b2rZ!2`kFf_re+Ebs1L z*bn{qb53`iK6P(Z-HOswSH{7lzytsQI8ca!_UrlAe*+!$^%ylkg7SL8aE2Iu1^}=K z{u>BWs{u~{00;n8kk$42cI@w;MD^BvuiA03j-$HRTEZY)MCSz{C&$MrXOUC*m$f>z z<^xi>%R0y3aGoN8_ITig2o~?T=&vLg@}@{hGB5>Mj4l1I+ybx}f*aah^>I@O%^=tY zv5B`&D~@;JyJPusN>Q!U%Hv>r^IG}g!^=KR=0nzWu}xX|A77K}fYU#or~bEks38a{ zNi6u$^-QZU0!AS*75M925FE^?qL2%QMiJn%K(+G~kpI6Qn@tx|p1lkIQ%MQH1_%Q5 z02DCt2OnY?Jede7Tv+mrTfOj{Qc9#kkt%Wm_mxuzsu_N_1gw-uu>!FMpWT=(!Rr3z>VpHO+n=`A5Pq9{41EXw=KQzHp`}zB-Dlbc}W>fIqt4plocSWrwja!r$V+w+Ly9 z?kr^^p}IQ{K!sX@6rB*SY{7><|LiC1h#Xp`3kL{%V&CRx;6*z&D{F8!+=grY(Hb3N zz0iA+CYZ`rLwK~%x)1@%MOpW`S~h+fqqP{5E=Kh(F&JYVjdPu*b3Di^cKkqeI-EXA zE*53yilVTQSuJ7?G+KkI#5`yZzfD8s=%&7J77!0Y~H7gMWXAA!i!-ygp{)X7&uXAY>^KF}j; zEop~&y>CCzrIjmd*!OL$v

6d!y@=U`ev!XupYsYMs)Lp*bzVgayLz6(>P$E+VhB- z5!Zf-XMf?NhA=6@-~4~101<_|s9ed_zwv@+KIq5hnBDI?j6EY|C`l|CG19%g!Rc#} zY$8Yq-BK3F+=qH8Kx;dIp4N}hT=>?fUs8k zE}%klZ6Tg4vu&q#S>yMYWaOFMUckfg!R`=C#s2bF=SNSbO@@hf_ki?eU5sNFw%w0l zI^uCyw5!>h9kx*BE@G>F5Qc@kk`j?qm3oK^+$vuR^^nEP^yp%Z+kV&b}Js5PDZ3T z{01X$k!MM11DuC8Co3m7!p!j}w>mhOq@bHx6rFmfCX5(vz<3N*+ z80b^=uI-5ln}SJH2;$;41Oc(K5M-Q_ge5$xp4Gf@%t+vtmzhtqldMShO@gSPqbS8n z;&cH(3aOMSJON@tQiCY#w>I`I6ztZ~yoFZwbJ&mX=-qb|I>x!BX*JHxCGW^Rp)d^b zZ(o_BP$nnm5WbN*lfOYm6DHiW$QL4p&>qtUuMS1aITYE;S*M4-Xh z2;xJRwqyLr$ZLt!K$oFQ1wP73h+Z7|gqahhYH%qSRJEIN6r9HD?-hi+K1#hx7 zl(X+KAZj?wANO^q7QyQ5fz*bA*lE0^{zY`-d~zv&9x4;hk(IlIWp5 z4}0OfJN;pspCf0o_~$HTQD3aTSYQ19cOwgA{!Oq?kDDc~I*0`PI7xu%g*tpHwUF&J zHu&R%g#v3RIGfbtzTPp+XbS2)&?eBrd{bNWMZ=JR#*?jU_a3>@9j#E|aC)sZVp%6M z)@zw!t1}8%RI0m7kh?*H zuFXfMn;i46YH|fEteLakM1gT~GTB1KTua=9ZSDWuk0{e)rSS5E#i$A-U4Eydgf;410O&RWEX5a^Ut6IDdtvF479+dITdd)?b$-7iCD{+`@?wfuG6S`4^l1*SxA`j2 zaLtD|yuT1+&%I`JJCF z?4|fHTP5HX&$+?fBv!M^+!W^VC^oM4E5c%K+&(Kt_4BRg5G3gli1+*8BJnv+L_X{= z;}9fKvxLFR1c$}>@#Be2>HN89{H>*u?Sm8Oq42jAaVWx~B0|5^51gk#)P|kh;$xpC zEUh?oORy(?^AoZW2$IBNNq*!e5q8Q&b9ifMuH{xyc&m` zeH-aBvj@*)@alyt+>YmFi-yHbb=DC3`KeW`$v8TK{)naVRLBGG5&=$ed3;Opm!;;# zdUC-QaT6{$Tr{lThA3%eQdqYa@+1kkNip<7b3*qp?J9}&Fvq2kZ^1PQNU{P&{x;FZ zh>oW->WN~7Vx>3ov~oRvwWLO_4E3tS@w(p}JTmw+zrg-f{_}{JPk^`(KDWyhB5c3w z!?sH#MWvQsqwGLZ*K5cT3*dM~C!tbUI|u_2F#F?)YWN8qkaFQHK$4=(Qgol7jDX9y zhBj#qBT{FkJ?j+*@~VitQF(X!G)rk@owM{swh_7pI0N&iU56rsX})x<{5=qQC4Ut6 z7)=Ow)Gg4@T$Z40TSQeU8uAL_P-$+-|qx0Nd>={DoFG( zVa(dedAN$(p%u%{XseIPgr=oIhcSXMhJ}?bmu6Tj)Zt##;Zkjl86zY)1=FlNJF$C4 zSXS;qJP5|lN0UMpP7m@sdK>EDfId}8mC!pJK=rksJ1Oj~Z+0TaJ(V=xx?ZH+!6cHa zhoWryzrxYZo@*}ovL5d$RBL(p!%lt|Xnx!YmENT{)XvR;8(T~aqEWTPTLKAINU>!`0jZ`t69}Z$2eRL0#6c21mJKJ zu})~@F=YDZ~6w#5$;BgLUCu?7>4K3%52CK@1PB(!#JneU}k2+|SMJCXko< zg!+)WN74QYWid?IrH-eL_zEI;o>D7}m?YT8LvE+la@$v8fL7wZ(btW{{q!S>MIz3Z zhPpEHd%uqm!ttRjDrMbpQVI(b_*bf{b_a>)Jh3{;KqJJ-uV~ZHbhr=AY-3jQIY2n#b|^VvOlT? z;-_#T`-$4bt7hj+P+@Y$XAw@VS4w^w$G@PIG#`{iub<(e77A`tl@mfSPZhRYNpYj6 zKsJTS*zAdysNGH<1pXSaKSJvt?#;5m?ST{D^DR%03eqoe4j*LsNrzH2px(z;lBpf` z*|&c36$4sX<`?RJirg;K0wsE<4I77$MHAfS&_ky3EYli$gX@s_ZCX{~zq1XAw@(d% zmj6E4JcyGn`;#s4={)bmr_-l`RPz(M8YNf#qzk|F7zC&JmLBN!7GiXjtA7)UXs|}P z@GlFXG-c7QKfS!Bw;F7!Yxvr2&B4TIk^nw;M32%qiNADF{;1tL2YTAll zDkv6P(mlhi`rzq_DwsJ^PxscJ3C@%l4;G@nEkZZm@Er}kMacGG4Im!_+V;g{u@TIK zQxsx05%TIY`?%ff#OwbkNBEZ46*7av1m!9#ifHhiR!=JpJX;waUf^hqR+uV;z-|9< z@0*XxptdlEsSReaDy5jWmrzHnGevp|7$d#govfAB`svZ#u`4;lu6D~I4@O==rAvMQ zjOlY-V$G%%J?h-!BQ9C=I!*~{lUPr>NJX?pg%C{@qQlQZvR|Ei#%s*^gwQSY+@&z= zz7UW1M`e+Va58`#B;i0>jDXxb+eUYQIf10-(flMPoT=mWiRr!aGYCt+wI}5>Fs2@72iE+`90YM4QNjzHHtMkoJ z$i)P^;L24=riH`{`5xS`0{#JcqCV-eC6CRezQ0m$sWg!J5%n(|4rbeg5XbfHT;fuv zf+U|kmOOOXh%2>A4dT5KdX|1mB}w&NHseoEo5st}e=i_+H)n%;g%I#kv~5fzJY!1? zm)bMRdgkA_Kf_HD>EbppoaW>Y8_v5Ju=+;Wc08!A}#1{bxAO>FnL$*ZwL!!FZex@jN>QM`3aywuwK!p{2)O)Bn@U71@M0 zEqQRFE0J*Y8yeOKN}M+g`I(!TU->nD2>@h>UVY@cfIT?$Y+e#T2@b(!_W}+A9{aZ7 zQU5I3N=f>0^rG;@1M4SIGNS;y1?@g7Rxp z@1?a(sKUqAiL%JBQOC@9Y*!v*d~;sXm8*J&uPf)@Ul2;y{Y~G_6zHKsJwp`pkJ6s2 z8;@Y_^n^{{d;DEgV{eaXWMV!{thbk}zti`Hvx>%mx^ zcU7JpM~Ade>u76K*r=5a+h^U{FR?}uJ;(2HTbXdE#3;;38KLSQ7!*4z!LO+xPQ@=JzXZ~F z)Q65g;|ZSL@6kT*|4t|EJPVW7uAUd>w<&SL)sxse|&WYvs{;K{7LdrR;U;PIR}e6R60(`Rpl zORD^V;`+VXwAr#yER|@maOX+ayW3;ar=D454;rz{v*TOpTwiIq)Paxu-b3Epz^18H zbUTN%d?6Yp3`>$C6(ELve4aH()A+R)ov=Pz(oEBQL~_G<2qP zmE*3Cmh+f$Arlg>uJ3j=99V0|60m;{Sf9im@$Ii;?79C%aQ{2vG^KeD&vWGDbF>GR zts_9{j{_zM{?HHQr+w+jyXuuanXx>|d&$7ee6+&kR+hsW>xrmTgX4nsg9Hm%{8imxL_BRQ(JJD)%Od$~Vip7+e@+IbrG&K&P$h>OgPY;6^t`6!t7 zJ<5o43Nb)ZJNm4RnY!xakF+aVshr^UH`}pdYCQ!91pEjvHF$w_b}vAv-sL9Y-DLyY z^@8&UhSr2bk?laVbrD?E>As~UqX|X;tX|l$XZLbU(qjXK^v~0TJf6QuDk|aCB=UM# ze3iDz2UOf=Sx--*Pyu&eU)cfLS+@kBpxdA^=kCO`&^ zeUpmW8jLk{zS#bSXW}-g*ztzD<7&RQhXR;g1sote3Xa244O2 z^dmO)o;2P3r6B>+(a4DgqXRg&xeN5m&8-U4sa*WdK99FZ8y|Ha0!l@*EgkW={g!Wx>9P^8M;QsMuaDa-#;S^ZaJkFkDFZ>(eK z)?#^}%H+MEBt@L`i`UL>=BMnoqpJ3sYo880{qk9*uC;&NW;{a@o6|prCsyxWMPv3Y zjO-0w5wYqiij=zL-^uB_=9(IqWwk$s)boLc@>)sm+G#Hjwn*$&c|eET^v*XgJYZHE z(t*x_%pzfUcUZ*%ACj~&vffrti%_sp(0)$FT58ZM$snZCCm?qPhBo;Y&u+!PajtBz zF3WZOfz`TeTEEkl7%=ZCmZghq?zhcA`*TqGl|N zt@DQf3DwS&1@~8Q-1@>^b@%w6Csq#ZA2;#b9>Smb*w}|`e)ag={p;zbJ|}`w=$}LC zOIFyv^BdsQI-wDMzpcV6&b=p`#!V6z zKlUU~tMvx0NG8B~f*U8{1_Sn?t8ZV?;=c`%Y257e+>`m|>ib*CyY-MvFZG1$=IW}d zo+ih&)0=m9DdTcZ{{Dn3(4Qf0Mjm4{g-6wIgRMjfayOctm_E4e?~9|OsIP1dH1-93 zl!c+|Hqa>zsXl76B)Wf6HNfeyJdvxO?`?8ip3>jznJiE?c?nT3MjEMeUhhT#$-hga z9m4)7Z&a+rA(^aVP1C)(JDOP?aNEA-pS=k{j(^{OWwAGwW!vb-Sz8`Sj$NjjB;VYm z`LGT-T$adFb`alShnDEnmnAO>sY$EHbnZ+B7SjYiT9ZOB&#hK3{Eo%xMPd`bvzdA^ z*ZocV@qDxVkKa_gRQIn0_>Dosry^}$wqzAFImV{>`T2jp2NBYiKS_I?nN7T{iu`%otHP?@`XfXNZ~3g9u1_wJOg8J} zZNSdkxmWDPm@Iu8Z;DDV%R5BzSV0jmKG&b+wTb`f>{r-zqw$3)`!ZQd$Us)Uu-pI1|0By5!iPx}c8$9x`m7SIUtP zvGWF{v=epcTY%m3p!D6P^xZ5SulklMerEqJhEqA=p2eZCgmi8FnB_D`1-?xk`eAyq za7)69z6pG9$tkAfIKYpw{rp=gx8lN*eZuL>pRBIEnXZS74q+g+*g$}K131Lr!Q({@ zQyB;q5c6=Y8vnjURoQ;>v(i&3BSTWV53}X>ElQ|Y3?T#fT|xY>qSxq9B=f`8XTV9z z=~YHG_}67KWq87lM}Vh#&!g(l-!scunqWxJuiLC%P`2HqPpz>Rj)~jQ;JB(VVh|Fp0|ez0uGr;p4){r>T9OB*1V^{b;HYU zR8=bs9f`_)GWmT;^ssm^gLk2TTJOrcn@~zDEG&jO27ri=2!eG&a!PAHaGx(GvF+Ey z;YQ-xMMpqD0F920PUjlq&rOSU&`Dp*D#ZC1J;|FxGkR&K93?s){6)`Db}H(6L~-Xw zjBUPwTQonZ%agi|dr4UGd2-^K2x_a1f|`25NJ;V6=rkOEftYC;O@qM=om);?%5P+? z%|?~s?q>#R>FF3)d7c(T=HM0~bSBp&EG>kt951kzW}(yQXFMHAawVceTbDxg-Wzc{ zGSrlml&8)wVI97=sE4=04lj%IGF)|tp-aQwy)#z6T83;0*q0gUZ9`6rLC*0f;Yn1! zAo?=SjDoAzY`~U1^J(jRwZuqW&rA^nSp+}m&R%kAOgTLF=VX51=fz+Oy2WDz+PL;j z|KFVTQM5gBXYQU#B(aE&usS!eL0pe}bhm)oP3+)6Pmi3L0ct3P*_rfkMuN8(ZQ)Uc zxf=~(A))IBhsIw1b^G`uuXp>`K0Mf#Ely0h-@kvKuoXZo8agcf^l$dBD0e`}X)3ab z(?zRNtzCLQj8-Am8Pco%0mXp$otrb?3+KxTgyW&Z_)4uUj~pML$=h&kv|s_M>$D?`7iFn!V``3~Ef)jnIBY?IxDTb3Y8(m6z(VyT<$bd0x zE&DXo-FD^lTnzA|c8!b492&+a)kDuGZnP&Z59uX!J19zhc{tx1tYpsivS`Z{Gik~7 z`I$aCZQMTF{O)f*PCCc9_|SHYKX7tIsIc%>Cr2>;fwVzEh%*T^q&w1++=Rwt z^zBOO1P79EFtP2C>*oVp)EEn2=)eF9xrzsQcA}9FBnZDlF4bjgJYH@NJpJ~0>^||; z^}9U~=G-B_#$iKfu+w|_2wZa~C$4kPuBQi6sYh$>yV~Q`+W^ar$K6!CtdpE=EAJTN z?7L}=?4SG^#AnNRGET>4Jssw82dycVRfq)){4ntwIrwY>W{ac81WMvc{t`?R%_}Lh zx8x%a3M;~x))+V7F6B>Umz0gWA~1~kP(IjUv2KU`lmC&i=~*`t-nT~oM-PX!PRYT; z;M&(qvhbkBF^Yi0Z=~jqf4q0y{(%9%pSPw>14lCBIz7vC3`ICQ@2=jy5ErCC34bbV zbX{h|m0ZU=q;LQLl&Sw|0mLA!wSm~yk2*oo&17^dG=k4OL}4ru=9Q)A-`)2~U$ zJ0v^Y6ZuGw#za%Z^~BednV0IG3EoKuy3WSJI`Yfr-3J3bt6<%6@zWpNfp@*MQo?xB zsz$oydJRUL@BC*qa<138MNC6@0{%XZ9iYvn z9Jf0tPXWqGPcM_4`E{0ljKSb0dTk@6A~BShS|GkZ=SuMEU!k2SjcT8xXR0(np* zHMy#N+i|FuX}MiSf#t9~Kp1yp8ZdwPFJF0FO$LRMS5r%V=!8t; zAuL?p{HgpTMdebR$^r<;c^R_|v)9a`hOA{0@6k+IiR8T9rxJ#&9_q*%eZ$-e=D;{a z9?wVnFxe_P$6o5xq=FNOoUSkZT8egxc$e6d7DDz@jz7lldVAIMse?JQ<-FFDCSqmG z96bIPFI&`y*57mMpVWCjd%)?h-Oh+HiD6Uji-+*7!MLurNr=T87LwVI#HZflzGG7Y z$9Q*C?uFd6Qtq??1)fJQ|E})Z<6h2dH8#hJ@wQCW-UZe0oQB}o!no3$bvT_a{Eg-Fj|<8wKzcmL2>au ze}CoziRxUBVa7PV6nxW>+=d4K{9K)%dE$(Y ze423|uAtOHPU1J`_P9B1aq^M4vMM)_C>e!&?%N&o1TV^1W<2b4eR>+4S(T@wTjmg9D;RaRGj z!Y%81EuK$&mcm|_0GI;KEFwQNDV&ZtXb9VXNl${3zIA-azPMdGq-S+EKXTk(HHd$l zHU48i#NEGFDZ*w(28H=CV#LXH{w_+LgPmnAfW8l^$xV{nHyI6gz!oy@l@I>O`!ojt z`8Jx7XyNf0!u^hzB~HZoy(p0riDHAFld;$N~CGo=xM2*O-EsOiJamJ^iGnf9=bzgLm8&u_9Se{#x_j{Rl_3zU@Zl{+QFd$c z&99#)5e9k4s4SzfFnO(Zm;|fcR&;kD3hGrDt2Tf_H9uZ@bQy+<6IS3ShWlN1eK zsfP3Is;Bhzf@NGk6VzA0KTdYf*BQ<1WTxFc6O|6hb(|QcdIm%YfqAX<2E`&$E;@7x zTa!USZnZ3q_$LYhu=wzK9b|Wl1lp8p&N-~ zL%9XqnadVA@kl^;o+6trQvPLqs~d(K0Zc3lDdAus*vpe?`!Xv`wXg%*oY)w_nYK^J zH?Y|A=o7&Ccd=+M#FnD_2cF)gU(a_`rRZDoNESFi^iv`bm0((8u^yyUq}X9RZe5M5 zPixyyyH4Zy2W{Q*s+q7r1Wkbe5Ti`%fA#z}t&y@yu&RBdi~GfKf$>p-w=|emdsPNF=A@a%e%#R*PhgK7{iriNCeErr=Vh+A-i@CoF zP{`>o8FFYs+{j)cbD;29@kg1QaiV(PendYDw1X$d)DX7 zejWV*?RtEe?+V!Miy~|}>%lTUIy(9^Mn{*8M5mSp>OJ%H;f8@#BA(wX0Aovdysz}Z zvN-v8x(NHgQxC%Yw;yhJ*`9rdMbTWnyMb4+{E1+o#!*3LaJtR_B7uGS$o(b?Kr96 z{9sxdaQi!o|5p0>xp--?%iV1!FK(4kR0anXiTRMigiD7Ss;gEuORo^2&G?rWkm{Wi zcJkh0^z`dUT)Xi(eV+^x@?XAW;pXHP5VnFZ_Ts3k9in!&WiK3>g}WGlIK=g1 zpLtLZsY8zMohR>wimPVWe#=TwNXmMZ9JWlX>)3g#dO;l2x)yEaYX~p^PSV&5OWN*a zsO5T#%7^&&?q1fDduz=9QgcVRFhJNvh{RErNAjS~@RMks1$PBa5fW*WWLmx$0)1!~ zQfnA$@R+}x4RNpZY7ae z2{Hx~z-1z;lK1D)B2|<0u@GIRfA)};9KYx$96ia%SRpjUj|Z;Ht%_b?EM#5-Y7VQ5 zbkZKC6Z~64bgvtg^k`i+mxPav7FLkt6|ieeJKq-s^4~Qx-XhUIFZ>)QCNWyqf}m8y<4P#S0gz^`Imt5DMWtudYC z@MO7XZ@%qDNk_z!TEt`mRmEPItdgq5Z0n4itV#+HSk>y1woq<4IS*o>kRIv}OmDVC z7vf(`U|^F4dgkjFVu)Q*#L2K@ZL-*mHyMZ&3#Bn);~57FNWMeuFPCYti!p`ViI$Eu zO6hSj!F%1k?tNpkdWoe8*Yi;ucXvr7SI>ZZ#@z zCYkMP5wrdg6kyDg(O)YcXyfYUvn$*RS@-CBe{2p>fHwsm=FH@RL}uYuOf9LS@G9zh zf?!iv4w85SccS}WubKtl#Vnu=i>Q0j^dF6n-6|QQGMJBWz+{iH@ ztpftI_rVi#?3AtnM!L+em9A|ACT^&P0Em$ZnmyGwZ6FxoP=O&DrJ2pKj8Ps3r-%7z z)%!HR$Dzj@{UDMM3@x;ruF1jh4l67PH#^{S;pg*SGC|hr-1RKX9>HzN!Kf3L#K3E(6*2!z-}(L_bPN)9|#%Z>O#@ITItARp`cDQVC!9< zIA2sIKos}4xwm$ zwcD*|_r^A_%G~>&S5X8>W$7ZSxr&Wl6X89<0E&-C1RZslI6ZyUy?A0QD)l8j^v}e8 z+K^X52H4Rvc(b8a*DvlUZ))F3W=tmqa(s4d7Er4wOANQ#thuYXM`xJOd2>7i!gzik zIa3DkK9E!}pJh+^qH<^uGE+N}5W^F`cZ;3(wX#M${%}?{(w#0Z%Y-*^eBZ*I`Av?< zxH~}_f`zgNc7k0oJB9V#&fa$qtwl+K%Zj<*a$ePyFd#mqD=u)3P$cB!v-aE=Qf=h_z3n$7@5>*5bZhBH9EI+*K6$1q^-KCL5omUn}#8C&S z9UaGAycxJjAt4e?A^Zm|_2{2LG5QpRFYU-?jAm6m@KNEauVaix5o!i{C%5(tgc*d@ zE(-(d{fKh~xMTX&tH3UXbN;WO-&CQ{dvwEJUev7vw~5=fvK4FVd$o9SKHy|TA?oVf zg5}>lESW2Ez%xS)BC*_%&r5PK442mN2aXg|(%)?%<$vkV#L!f$-St&;`5EXGskOgjsKBI}SDd4sZfn3IaR zuy`-R?^lHJ)&phxL=h}<2JM$libnLJxo}Vw&^$oz4>9TI;-)VrK#AStWLe5tfU{EA z>5p|)j%bP}%eoZ0fUO+5K?UlzcPFdN^7ju!)`4A zQXZ1BdRZrrob*8WCz73Rgx9K^v4LETOV3 z_5u`$H(;I zJVtw+xVg#S`2^u`crmOF!OD21FE_wqy6f>Gp1uukNkORy=UF=Ozv~HpY}xqi?f!1H zeuS-1VWagIVrW@!`VlkY#q$h^Iw%!zr;u%xx0el`R3Zr#GwGE%b_lu=jOO?Jg2BbC z9~wrB$$;?_c9nA%-r2l*r1Q6e*ay81GcT^}GrnMt2lMtr%)1bjt$fMVrw@FY@6pWZ zQNf07J~tgGhF=PenqDbFkAjUE1r>E;gKvvD)6S?(R`|OM9!rs2LRPo zR$JuH>x%BO z8|bD)$DG!>SVN2OMD75|q#yvgIgsD&&zgvkYYXk6&t^XDj3R<$7+jtHDO8y3r_V#} zX{OudBVB~grIaHr@=Ky@?3wFv4DZK_zpA0en~F2vBD9cLhzn)c&-|}E&~#R@&+*;% zL1S$2Rw+Nxl^^wxr~Zh=##Pd&x)G=V0puwS1-j)lPD#x##BaKg_Z;koCY8!MjrFM4 z$e79`&1b#O!0;^Tr|1jy6sq`}{~9;&18ZHX!>?)q-CF>uFOk%(L|uC3zU?gTcECKg zJl`Nqv?fsVrm<2dfx_{dQh(#<+63fCHKLo8^2KFQfsmhhq-ESzZZr;oWNZ*kpr&M3nM_Yv`~@Ry;w!e!!J;`WDnL7(LD}&U z2Ig#s@)2Fg^~<84IEYBWCV#M7{i(M^V1xU9T<03R%Ud#wNMU~(F>+*P=)uNQcRxka z(4jE={5!RVuNA&D^!qh&WQmH^!vs8@2l6VZE~;=7(8ZdPW81@m7LKYK-5QQfYXG??j8u^M!3U^KQhYi#_yM%0CFox&&0z?;U%Yb zJT7oRFaEC}s;~izkF%@ool1^Bf3T1J2nqNKrFGY(E_c&X&F%dF?Mjs{leMC@c)5@R z53)fpCkJ6^gxj5~4YWz3=$aEN_VV$e8|R4?atmYPDD#WK6I=5& z6kEitH`+vJSA)oomEAtm^CkAfGI{&~rlEhY2A_uUcyo*)wO{yIlgx&C(B^Oy3Cygr zry}-MWj8pyXWoYGWAmS-VJDMj|4J%xQ^B&Vi9Wy}B@cs;ITavTytbxc@cdQ9iH-%eCMuE# zd9PydWT!DBi5dX=;>pF;z8)3k2*h9G=!w`+Qj;D!e6p!fZ%4=u8}0Hl=fMtu*B{!&6| zwpL2P`2Lnzy*pKYd7)IeDRRqY!%%~Odq~$-#x3AI?TW%tAqr;}=*4Qn6pbtm zU@B?Gyv6L@OUmf?8cPJ@c}2n>wWm4GkU#!GmPJqb9&otFwgPR*B66=I;-}jS8HWY~ z7@-y+kyA3x`B70aaYG#yB{`_U&lL9YxUw&5)DIgC$C<>=lffoYQ3>+a@|3C`hLxUOaajFARanc2r*Z&bz`q+8C>m_Fk3R{jN-SkGn z!Q>kS0mOU>jO_YvN{f>~mU2+|xAg=z8TFs3ZL0I4`cZmv`Cw@5Srh`2tv@tO5|5K7zG65fMGxDKjX7uqkn}5X_@vdkdP-k7l5Hb=)b-Zph@d?M#e Date: Fri, 11 Aug 2023 14:12:27 +0200 Subject: [PATCH 009/137] working on framework lesson project implementation Signed-off-by: Evgenii Moiseenko --- .../CStringConcat/task-info.yaml | 4 +- .../IntroducingObjects/CMakeLists.txt | 1 + .../IntroducingObjects/src/cgobject.cpp | 10 ++-- .../IntroducingObjects/src/collision.cpp | 7 +++ .../IntroducingObjects/src/consumable.cpp | 19 +++++-- .../IntroducingObjects/src/enemy.cpp | 12 +++-- .../IntroducingObjects/src/main.cpp | 6 +++ .../IntroducingObjects/src/player.cpp | 10 ++-- .../IntroducingObjects/src/scene.cpp | 35 ++++++++++-- .../IntroducingObjects/src/simplscene.cpp | 54 +++++++------------ .../IntroducingObjects/task-info.yaml | 12 +++-- include/cgobject.hpp | 5 +- include/collision.hpp | 3 -- include/constants.hpp | 3 ++ include/consumable.hpp | 4 +- include/enemy.hpp | 4 +- include/gobject.hpp | 8 ++- include/operators.hpp | 2 + include/player.hpp | 4 +- include/scene.hpp | 16 ++++-- include/simplscene.hpp | 26 +++++++++ 21 files changed, 173 insertions(+), 72 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp create mode 100644 include/simplscene.hpp diff --git a/MemoryManagement/TypeCastsAndCStrings/CStringConcat/task-info.yaml b/MemoryManagement/TypeCastsAndCStrings/CStringConcat/task-info.yaml index e7e08d6..4ab59e8 100644 --- a/MemoryManagement/TypeCastsAndCStrings/CStringConcat/task-info.yaml +++ b/MemoryManagement/TypeCastsAndCStrings/CStringConcat/task-info.yaml @@ -3,11 +3,11 @@ custom_name: C Style Strings Concatenation files: - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/task.cpp visible: true placeholders: - offset: 92 length: 201 placeholder_text: return nullptr; +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index 6e2eabc..cde881f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -7,6 +7,7 @@ set(TASK set(SRC ${TASK} + src/main.cpp src/scene.cpp src/simplscene.cpp src/textures.cpp src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index d5eca1a..2325fd5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -2,10 +2,9 @@ #include "operators.hpp" -CircleGameObject::CircleGameObject(Circle circle, sf::Texture *texture) - : circle(circle) +CircleGameObject::CircleGameObject() + : circle({ { 0.0f, 0.0f }, 0.0f }) , state(LIVE) - , texture(texture) {} Point2D CircleGameObject::getPosition() const { @@ -35,7 +34,10 @@ void CircleGameObject::setState(GameObjectState newState) { state = newState; } -void CircleGameObject::draw(sf::RenderWindow &window) const { +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; sf::CircleShape shape; shape.setPosition(circle.center.x, circle.center.y); shape.setOrigin(circle.center.x, circle.center.y); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp index da72e71..cbef14e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp @@ -6,6 +6,13 @@ #include "cgobject.hpp" #include "utils.hpp" +// TODO: move to utils.cpp (?) +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + CollisionInfo collision(const Circle& circle1, const Circle& circle2) { float dx = circle2.center.x - circle1.center.x; float dy = circle2.center.y - circle1.center.y; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index ccefbf5..3f82c4e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -1,13 +1,26 @@ #include "consumable.hpp" -ConsumableObject::ConsumableObject(Circle circle, sf::Texture *texture) - : CircleGameObject(circle, texture) -{} +#include "constants.hpp" + +ConsumableObject::ConsumableObject() { + circle = { { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }; +} Point2D ConsumableObject::getVelocity() const { return { 0.0f, 0.0f }; } +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (state) { + case LIVE: + return textureManager.getTexture(GameTextureID::STAR); + // case DEAD: + // // TODO: return empty texture instead (?) + // return nullptr; + } + return nullptr; +} + void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { if (collisionData.collide) { CircleGameObject::setState(DEAD); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 812ba20..9e37167 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -1,13 +1,19 @@ #include "enemy.hpp" -EnemyObject::EnemyObject(Circle circle, sf::Texture *texture) - : CircleGameObject(circle, texture) -{} +#include "constants.hpp" + +EnemyObject::EnemyObject() { + circle = { { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }; +} Point2D EnemyObject::getVelocity() const { return { 0.0f, 0.0f }; } +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + return textureManager.getTexture(GameTextureID::BLACK_HOLE); +} + void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { return; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp new file mode 100644 index 0000000..3807280 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp @@ -0,0 +1,6 @@ +#include "scene.hpp" + +int main() { + Scene::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index d4e5ac3..582c750 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -3,9 +3,9 @@ #include "direction.hpp" #include "constants.hpp" -PlayerObject::PlayerObject(Circle circle, sf::Texture *texture) - : CircleGameObject(circle, texture) -{} +PlayerObject::PlayerObject() { + circle = { { PLAYER_START_X, PLAYER_START_Y }, RADIUS }; +} Point2D PlayerObject::getVelocity() const { Point2D velocity = { 0.0f, 0.0f }; @@ -25,6 +25,10 @@ Point2D PlayerObject::getVelocity() const { return velocity; } +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + return textureManager.getTexture(GameTextureID::PLANET); +} + void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { return; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 6ea0016..e136318 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -1,13 +1,44 @@ #include "scene.hpp" #include "constants.hpp" +#include "simplscene.hpp" Scene::Scene() : width(SCENE_WIDTH) , height(SCENE_HEIGHT) + , active(true) { window.create(sf::VideoMode(width, height), "Space Game"); window.setFramerateLimit(60); + active &= textureManager.initialize(); +} + +Scene* Scene::create() { + static SimpleScene scene; + return &scene; +} + +void Scene::run() { + sf::Clock clock; + while (active && window.isOpen()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + window.display(); + } +} + +void Scene::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void Scene::close() { + active = false; + window.close(); } Rectangle Scene::boundingBox() const { @@ -34,7 +65,5 @@ void Scene::detectCollision(GameObject& object1, GameObject& object2) { } void Scene::draw(const GameObject &object) { - if (object.getState() == LIVE) { - object.draw(window); - } + object.draw(window, textureManager); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp index ea39d0e..7ff2417 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp @@ -1,38 +1,24 @@ -#include "scene.hpp" +#include "simplscene.hpp" -#include "player.hpp" -#include "consumable.hpp" -#include "enemy.hpp" - -class SimpleScene : public Scene { -public: - - void processEvent(const sf::Event &event) override { - switch (event.type) { - case sf::Event::Closed: - window.close(); - break; - default: - break; - } +void SimpleScene::processEvent(const sf::Event& event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + break; } +} - void update(sf::Time delta) override { - move(player, 0.001f * delta.asMilliseconds() * player.getVelocity()); - detectCollision(player, consumable); - detectCollision(enemy, player); - detectCollision(enemy, consumable); - } - - void render() override { - draw(player); - draw(consumable); - draw(enemy); - window.display(); - } +void SimpleScene::update(sf::Time delta) { + move(player, 0.001f * delta.asMilliseconds() * player.getVelocity()); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} -private: - PlayerObject player; - ConsumableObject consumable; - EnemyObject enemy; -}; \ No newline at end of file +void SimpleScene::render() { + draw(player); + draw(consumable); + draw(enemy); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 2ae2747..3f7c5f9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -58,17 +58,19 @@ files: visible: true - name: src/rectangle.cpp visible: true -- name: src/consumable.cpp - visible: true - name: src/player.cpp visible: true - name: src/enemy.cpp visible: true +- name: src/direction.cpp + visible: true +- name: src/textures.cpp + visible: true - name: src/scene.cpp visible: true -- name: src/direction.cpp +- name: src/main.cpp visible: true -- name: src/simplscene.cpp +- name: src/consumable.cpp visible: true -- name: src/textures.cpp +- name: src/simplscene.cpp visible: true diff --git a/include/cgobject.hpp b/include/cgobject.hpp index 51def16..71cfec0 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -8,7 +8,7 @@ class CircleGameObject : public GameObject { public: - CircleGameObject(Circle circle, sf::Texture* texture); + CircleGameObject(); Point2D getPosition() const override; @@ -22,12 +22,11 @@ class CircleGameObject : public GameObject { void setState(GameObjectState newState) override; - void draw(sf::RenderWindow &window) const override; + void draw(sf::RenderWindow &window, TextureManager& textureManager) const override; protected: Circle circle; GameObjectState state; - sf::Texture* texture; }; #endif // CPPBASICS_CGOBJECT_HPP diff --git a/include/collision.hpp b/include/collision.hpp index 14b5f57..22c1eeb 100644 --- a/include/collision.hpp +++ b/include/collision.hpp @@ -2,7 +2,6 @@ #define CPPBASICS_COLLISION_HPP #include "circle.hpp" -#include "gobject.hpp" struct CollisionInfo { bool collide; @@ -17,6 +16,4 @@ bool areApproaching(const Circle& circle1, const Circle& circle2, const Collisio Point2D calculateResolutionVector(const Circle& circle1, const Circle& circle2, const CollisionInfo& collisionData, float resolutionFactor = 1.0f); -CollisionInfo collision(const GameObject& object1, const GameObject& object2); - #endif // CPPBASICS_COLLISION_HPP diff --git a/include/constants.hpp b/include/constants.hpp index 8384995..7511800 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -3,6 +3,7 @@ const float RADIUS = 40.0f; const float CONSUMABLE_RADIUS = 20.0f; +const float ENEMY_RADIUS = 40.0f; const float SPEED = 150.0f; @@ -20,5 +21,7 @@ const float PLAYER_START_Y = 300.0f; const float CONSUMABLE_START_X = 600.0f; const float CONSUMABLE_START_Y = 150.0f; +const float ENEMY_START_X = 100.0f; +const float ENEMY_START_Y = 450.0f; #endif // CPPBASICS_CONSTANTS_HPP diff --git a/include/consumable.hpp b/include/consumable.hpp index 3098cf9..26b6f3e 100644 --- a/include/consumable.hpp +++ b/include/consumable.hpp @@ -5,10 +5,12 @@ class ConsumableObject : public CircleGameObject { public: - ConsumableObject(Circle circle, sf::Texture *texture); + ConsumableObject(); Point2D getVelocity() const override; + const sf::Texture* getTexture(TextureManager& textureManager) const override; + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; diff --git a/include/enemy.hpp b/include/enemy.hpp index 9640d66..63b2a9b 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -5,10 +5,12 @@ class EnemyObject : public CircleGameObject { public: - EnemyObject(Circle circle, sf::Texture* texture); + EnemyObject(); Point2D getVelocity() const override; + const sf::Texture* getTexture(TextureManager& textureManager) const override; + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; diff --git a/include/gobject.hpp b/include/gobject.hpp index cd00782..409e858 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -6,7 +6,9 @@ #include "point.hpp" #include "rectangle.hpp" #include "collision.hpp" +#include "textures.hpp" +// TODO: use enum class (?) enum GameObjectState { LIVE, DEAD }; @@ -28,7 +30,9 @@ class GameObject { virtual void setState(GameObjectState newState) = 0; - virtual void draw(sf::RenderWindow& window) const = 0; + virtual const sf::Texture* getTexture(TextureManager& textureManager) const = 0; + + virtual void draw(sf::RenderWindow& window, TextureManager& textureManager) const = 0; virtual void onCollision(const GameObject& object, const CollisionInfo& collisionData) = 0; @@ -36,4 +40,6 @@ class GameObject { }; +CollisionInfo collision(const GameObject& object1, const GameObject& object2); + #endif // CPPBASICS_GOBJECT_HPP diff --git a/include/operators.hpp b/include/operators.hpp index f248f5a..87c4bba 100644 --- a/include/operators.hpp +++ b/include/operators.hpp @@ -4,6 +4,8 @@ #include #include "point.hpp" +#include "circle.hpp" +#include "direction.hpp" inline std::ostream& operator<<(std::ostream& os, const Point2D& p) { return os << "(" << p.x << ", " << p.y << ")"; diff --git a/include/player.hpp b/include/player.hpp index 55cb1a3..a945753 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -5,10 +5,12 @@ class PlayerObject : public CircleGameObject { public: - PlayerObject(Circle circle, sf::Texture* texture); + PlayerObject(); Point2D getVelocity() const override; + const sf::Texture* getTexture(TextureManager& textureManager) const override; + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; diff --git a/include/scene.hpp b/include/scene.hpp index cf339b5..a52ec7e 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -14,15 +14,22 @@ class Scene { virtual ~Scene() {} + void run(); + + static Scene* create(); + +protected: + + void processInput(); + virtual void processEvent(const sf::Event& event) = 0; virtual void update(sf::Time delta) = 0; virtual void render() = 0; - static Scene* create(); + void close(); -protected: Rectangle boundingBox() const; void move(GameObject& object, Point2D vector); @@ -32,13 +39,12 @@ class Scene { void draw(const GameObject& object); void fitInto(GameObject& object); - -protected: +private: sf::RenderWindow window; TextureManager textureManager; -private: float width; float height; + bool active; }; #endif // CPPBASICS_SCENE_HPP diff --git a/include/simplscene.hpp b/include/simplscene.hpp new file mode 100644 index 0000000..9ba60b1 --- /dev/null +++ b/include/simplscene.hpp @@ -0,0 +1,26 @@ +#ifndef CPPBASICS_SIMPLSCENE_HPP +#define CPPBASICS_SIMPLSCENE_HPP + +#include + +#include "scene.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" + +class SimpleScene : public Scene { +public: + + void processEvent(const sf::Event &event) override; + + void update(sf::Time delta) override; + + void render() override; + +private: + PlayerObject player; + ConsumableObject consumable; + EnemyObject enemy; +}; + +#endif // CPPBASICS_SIMPLSCENE_HPP From 621b163e6b9687d8faa7a54b0140f938b97edd9b Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 11 Aug 2023 14:12:45 +0200 Subject: [PATCH 010/137] fix texture file name Signed-off-by: Evgenii Moiseenko --- resources/{balckhole.png => blackhole.png} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename resources/{balckhole.png => blackhole.png} (100%) diff --git a/resources/balckhole.png b/resources/blackhole.png similarity index 100% rename from resources/balckhole.png rename to resources/blackhole.png From 455e92c05a28aef79b319ef88dc284c9fff353a2 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 11 Aug 2023 15:01:44 +0200 Subject: [PATCH 011/137] bugfixes and minor refactorings in framework lesson project implementation Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/cgobject.cpp | 2 +- .../IntroducingObjects/src/scene.cpp | 15 ++++++++++++++- .../IntroducingObjects/src/simplscene.cpp | 8 ++++---- .../IntroducingObjects/src/textures.cpp | 2 +- include/rectangle.hpp | 2 +- include/scene.hpp | 9 +++++++-- include/simplscene.hpp | 2 +- include/textures.hpp | 3 +-- 8 files changed, 30 insertions(+), 13 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index 2325fd5..ab091c3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -40,7 +40,7 @@ void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureMan return; sf::CircleShape shape; shape.setPosition(circle.center.x, circle.center.y); - shape.setOrigin(circle.center.x, circle.center.y); + // shape.setOrigin(circle.center.x, circle.center.y); shape.setRadius(circle.radius); shape.setTexture(texture); window.draw(shape); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index e136318..237a182 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -25,7 +25,6 @@ void Scene::run() { processInput(); update(delta); render(); - window.display(); } } @@ -36,6 +35,13 @@ void Scene::processInput() { } } +void Scene::render() { + window.clear(sf::Color::White); + window.draw(background()); + draw(); + window.display(); +} + void Scene::close() { active = false; window.close(); @@ -64,6 +70,13 @@ void Scene::detectCollision(GameObject& object1, GameObject& object2) { object2.onCollision(object1, info); } +sf::Sprite Scene::background() const { + sf::Sprite background; + background.setTexture(*textureManager.getTexture(GameTextureID::SPACE)); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + return background; +} + void Scene::draw(const GameObject &object) { object.draw(window, textureManager); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp index 7ff2417..d53e08b 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp @@ -17,8 +17,8 @@ void SimpleScene::update(sf::Time delta) { detectCollision(enemy, consumable); } -void SimpleScene::render() { - draw(player); - draw(consumable); - draw(enemy); +void SimpleScene::draw() { + Scene::draw(player); + Scene::draw(consumable); + Scene::draw(enemy); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp index e7080a7..e3c931d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp @@ -33,6 +33,6 @@ bool TextureManager::initialize() { return true; } -const sf::Texture* TextureManager::getTexture(GameTextureID id) { +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { return &textures[static_cast(id)]; } \ No newline at end of file diff --git a/include/rectangle.hpp b/include/rectangle.hpp index 6fd8fdd..d9b95eb 100644 --- a/include/rectangle.hpp +++ b/include/rectangle.hpp @@ -23,7 +23,7 @@ inline float height(const Rectangle& rect) { inline Point2D center(const Rectangle& rect) { // TODO: explain this C++ initialization syntax - return 0.5f * Point2D { width(rect), height(rect) }; + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; } inline Rectangle createRectangle(Point2D p1, Point2D p2) { diff --git a/include/scene.hpp b/include/scene.hpp index a52ec7e..5319c1b 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -26,7 +26,9 @@ class Scene { virtual void update(sf::Time delta) = 0; - virtual void render() = 0; + virtual void draw() = 0; + + void render(); void close(); @@ -34,11 +36,14 @@ class Scene { void move(GameObject& object, Point2D vector); + void fitInto(GameObject& object); + void detectCollision(GameObject& object1, GameObject& object2); + sf::Sprite background() const; + void draw(const GameObject& object); - void fitInto(GameObject& object); private: sf::RenderWindow window; TextureManager textureManager; diff --git a/include/simplscene.hpp b/include/simplscene.hpp index 9ba60b1..a522d0d 100644 --- a/include/simplscene.hpp +++ b/include/simplscene.hpp @@ -15,7 +15,7 @@ class SimpleScene : public Scene { void update(sf::Time delta) override; - void render() override; + void draw() override; private: PlayerObject player; diff --git a/include/textures.hpp b/include/textures.hpp index 301810a..e8bad7b 100644 --- a/include/textures.hpp +++ b/include/textures.hpp @@ -17,12 +17,11 @@ class TextureManager { public: bool initialize(); - const sf::Texture* getTexture(GameTextureID id); + const sf::Texture* getTexture(GameTextureID id) const; private: static const size_t SIZE = static_cast(GameTextureID::SIZE); sf::Texture textures[SIZE]; - }; #endif // CPPBASICS_TEXTURES_HPP From 1abda8833fab6229ba5bf206aacffb0b59502820 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 11 Aug 2023 15:31:57 +0200 Subject: [PATCH 012/137] implement enemy movement Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 2 +- .../IntroducingObjects/src/cgobject.cpp | 2 +- .../IntroducingObjects/src/consumable.cpp | 9 +++++++-- .../IntroducingObjects/src/enemy.cpp | 15 +++++++++++++-- .../IntroducingObjects/src/player.cpp | 17 ++++++++++++++++- .../IntroducingObjects/src/simplscene.cpp | 1 + .../IntroducingObjects/src/textures.cpp | 4 +++- .../IntroducingObjects/src/utils.cpp | 13 +++++++++++++ .../IntroducingObjects/task-info.yaml | 2 ++ include/cgobject.hpp | 1 + include/constants.hpp | 2 +- include/consumable.hpp | 2 ++ include/enemy.hpp | 2 ++ include/gobject.hpp | 8 +++++++- include/player.hpp | 2 ++ include/textures.hpp | 3 ++- include/utils.hpp | 4 ++++ resources/planetDead.png | Bin 0 -> 13198 bytes 18 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp create mode 100644 resources/planetDead.png diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index cde881f..a80879a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -7,7 +7,7 @@ set(TASK set(SRC ${TASK} - src/main.cpp + src/main.cpp src/utils.cpp src/scene.cpp src/simplscene.cpp src/textures.cpp src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index ab091c3..0f2d315 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -4,7 +4,7 @@ CircleGameObject::CircleGameObject() : circle({ { 0.0f, 0.0f }, 0.0f }) - , state(LIVE) + , state(GameObjectState::LIVE) {} Point2D CircleGameObject::getPosition() const { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index 3f82c4e..f8df0e7 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -12,7 +12,7 @@ Point2D ConsumableObject::getVelocity() const { const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { switch (state) { - case LIVE: + case GameObjectState::LIVE: return textureManager.getTexture(GameTextureID::STAR); // case DEAD: // // TODO: return empty texture instead (?) @@ -21,8 +21,13 @@ const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) return nullptr; } +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + + void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { if (collisionData.collide) { - CircleGameObject::setState(DEAD); + CircleGameObject::setState(GameObjectState::DEAD); } } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 9e37167..c83a35d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -1,5 +1,6 @@ #include "enemy.hpp" +#include "utils.hpp" #include "constants.hpp" EnemyObject::EnemyObject() { @@ -7,11 +8,21 @@ EnemyObject::EnemyObject() { } Point2D EnemyObject::getVelocity() const { - return { 0.0f, 0.0f }; + Point2D velocity = { 0.0f, 0.0f }; + if (state == GameObjectState::DEAD) + return velocity; + velocity.x = generateBool() ? 1.0f : -1.0f; + velocity.y = generateBool() ? 1.0f : -1.0f; + velocity = SPEED * velocity; + return velocity; +} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; } const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { - return textureManager.getTexture(GameTextureID::BLACK_HOLE); + return textureManager.getTexture(GameTextureID::BLACKHOLE); } void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index 582c750..245fa14 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -9,6 +9,8 @@ PlayerObject::PlayerObject() { Point2D PlayerObject::getVelocity() const { Point2D velocity = { 0.0f, 0.0f }; + if (state == GameObjectState::DEAD) + return velocity; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { velocity = velocity + getDirection(North); } @@ -25,10 +27,23 @@ Point2D PlayerObject::getVelocity() const { return velocity; } +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { - return textureManager.getTexture(GameTextureID::PLANET); + switch (state) { + case GameObjectState::LIVE: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectState::DEAD: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + } + return nullptr; } void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { + if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { + CircleGameObject::setState(GameObjectState::DEAD); + } return; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp index d53e08b..86d4d45 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp @@ -12,6 +12,7 @@ void SimpleScene::processEvent(const sf::Event& event) { void SimpleScene::update(sf::Time delta) { move(player, 0.001f * delta.asMilliseconds() * player.getVelocity()); + move(enemy, 0.001f * delta.asMilliseconds() * enemy.getVelocity()); detectCollision(player, consumable); detectCollision(enemy, player); detectCollision(enemy, consumable); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp index e3c931d..5bde26f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp @@ -8,9 +8,11 @@ const char* getTextureFilename(GameTextureID id) { return "resources/space.png"; case GameTextureID::PLANET: return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; case GameTextureID::STAR: return "resources/star.png"; - case GameTextureID::BLACK_HOLE: + case GameTextureID::BLACKHOLE: return "resources/blackhole.png"; default: return ""; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp new file mode 100644 index 0000000..1054bb7 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp @@ -0,0 +1,13 @@ +#include "utils.hpp" + +#include + +// TODO: move `distance` here + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 3f7c5f9..abd8c8c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -74,3 +74,5 @@ files: visible: true - name: src/simplscene.cpp visible: true +- name: src/utils.cpp + visible: true diff --git a/include/cgobject.hpp b/include/cgobject.hpp index 71cfec0..2b54708 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -24,6 +24,7 @@ class CircleGameObject : public GameObject { void draw(sf::RenderWindow &window, TextureManager& textureManager) const override; +// TODO: make private protected: Circle circle; GameObjectState state; diff --git a/include/constants.hpp b/include/constants.hpp index 7511800..0a514af 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -3,7 +3,7 @@ const float RADIUS = 40.0f; const float CONSUMABLE_RADIUS = 20.0f; -const float ENEMY_RADIUS = 40.0f; +const float ENEMY_RADIUS = 60.0f; const float SPEED = 150.0f; diff --git a/include/consumable.hpp b/include/consumable.hpp index 26b6f3e..6b97064 100644 --- a/include/consumable.hpp +++ b/include/consumable.hpp @@ -9,6 +9,8 @@ class ConsumableObject : public CircleGameObject { Point2D getVelocity() const override; + GameObjectKind getKind() const override; + const sf::Texture* getTexture(TextureManager& textureManager) const override; void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; diff --git a/include/enemy.hpp b/include/enemy.hpp index 63b2a9b..ef72743 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -9,6 +9,8 @@ class EnemyObject : public CircleGameObject { Point2D getVelocity() const override; + GameObjectKind getKind() const override; + const sf::Texture* getTexture(TextureManager& textureManager) const override; void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; diff --git a/include/gobject.hpp b/include/gobject.hpp index 409e858..fee2902 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -9,10 +9,14 @@ #include "textures.hpp" // TODO: use enum class (?) -enum GameObjectState { +enum class GameObjectState { LIVE, DEAD }; +enum class GameObjectKind { + PLAYER, CONSUMABLE, ENEMY +}; + class GameObject { public: @@ -30,6 +34,8 @@ class GameObject { virtual void setState(GameObjectState newState) = 0; + virtual GameObjectKind getKind() const = 0; + virtual const sf::Texture* getTexture(TextureManager& textureManager) const = 0; virtual void draw(sf::RenderWindow& window, TextureManager& textureManager) const = 0; diff --git a/include/player.hpp b/include/player.hpp index a945753..5f0c98f 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -9,6 +9,8 @@ class PlayerObject : public CircleGameObject { Point2D getVelocity() const override; + GameObjectKind getKind() const override; + const sf::Texture* getTexture(TextureManager& textureManager) const override; void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; diff --git a/include/textures.hpp b/include/textures.hpp index e8bad7b..fa86950 100644 --- a/include/textures.hpp +++ b/include/textures.hpp @@ -8,8 +8,9 @@ enum class GameTextureID { SPACE, PLANET, + PLANET_DEAD, STAR, - BLACK_HOLE, + BLACKHOLE, SIZE }; diff --git a/include/utils.hpp b/include/utils.hpp index 319af03..807c43c 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -5,4 +5,8 @@ float distance(Point2D a, Point2D b); +float generateFloat(float min, float max); + +bool generateBool(float prob = 0.5f); + #endif // CPPBASICS_UTILS_HPP diff --git a/resources/planetDead.png b/resources/planetDead.png new file mode 100644 index 0000000000000000000000000000000000000000..a439b91cd0e93ba490d4e65295426328b6daae23 GIT binary patch literal 13198 zcmW+-WmFq&6AcoIOK_(&P^?&SEffmDEx5b8yBD|OUYy`kyg0?ZNO5*M zwnV~f8Md;7+J9R_&YV5;pSO9}GuFo@{`*A7<9w2qa`yPze*MpHJ+H-cV(ZqRzaei)8HlEMlbryjO(k+9ioyK5D;vT78w(#F7Uu#($?#du zN{SeVWfN+7RwA`X8Z0<$h|RNk5;%${;c8aT60)m|mE3ehK}@HWKW5*FO4Z}eBdj1h zUiV94{6d2W!7U-|ejyuwxS@wq&I(sq)&siz*n3&>aDT<(Cv`WGL=Q!h_+Pvmt!CL8 zhb4Upq$ZX~w^?v#;j|e94W@$CW9lsZ2IAQ2gQ>OLlYiG6L29u#$kJuqSxnMRq(Bqa zzV(aDXoG5seLmg_fi-B|dXm4?C4-p=#8ZUyNd~?RtV96T&XAo&v_*|d{to& zp2~OVaNjMe7tykvRv4PLLU$y0!rm!@qA4_#Y15ckkHRAlbN65k1OzKSm*{~NQaLPA zsbHxf3Vkk2q>pdNe!2F&ti{n96*3X;2IIwXX~j`M;%NChNETejTyr+`@dK&VO>Ln& z!8IqANxMS{{pdqAh@Nmuhu;v?$lY;0`g~CC)|MBOW%&(63MB3xa;`bRfFOO%1*_xC zaVHDuq!7YQ_=7aJ8*B&F9BDBmw#7MdX?0&z9sHUJmc{U-8uKPl0kGgd>DK;GMR(s7 zZ!iQf%dt|K6Q3w0!JpT~u=IDGV^x7ThZd17Y~Qp8y>iLt63SjrAyDCbAYI=WSOE~x z`0JKTiq+SxDl)fn^UF#%@yD?~Hx|-XVD|8u_%%J!5@0i!|$3Zme zXDf&?aDSA~sYWHln6rYjI%NWuy!6aO2dV5SHOp?%$S~BMHVMe+ib;Ys8rjR%6AQGye z>EwZeZ2)nHx|xRg+(U4L9)mLa+hK$sI-oGYA2Me(c|1>K7-*ErpR(HP z?nj!5i|{*`qsF2_0u>BAnD)!fKGt{G$w|27Vh*2EzoNP{(-PVnm9%HtwAd7Cr$0hbm7VD#p^{xU)6~>4 zh{Z$sm_RnMa8h?`5|m$D^p&d@xFvMxi?E@^l!4x@bdJ^TFoj9?E_VVno6=-jRyvP@ zoiCt@Xb-IJ1E~OC0H8&$hM?+}gJ8nL+dZ(|Uj`z-_cip#d+ug+b}NTdh9rmgHO85K z$VdrGJ}1Hi+695jH!^tr<0%ZS6x-^Vcl2>eL^g7~w!uYJA#+4PTlI_-%7w*|aHsJ= zTf|x6y@!(#S_1sCEb}5T!gt09aUI8%zF3i4(d)pFAHI>UL zWJ5&)4r`KX%J)XzJ!%cga`;%Q5`BhfNVIz6>>Nhy(Zy&??z=ssVMERn+E!ce1*46) z8ZHJ_nyvOGmD6cycXI=}dIKEfcpdA;S%w$ZoR}IGCO|v(MOKoY%~5a6S~Jy!!=yLf z2a##8c_tJ`(Uvc*B^ifYv2N5f?dn+>S3#>)80$kl#5gZ6dAAPcN ziGI+&9Znp>v5$_6y9GTC7xN4d!ju>lOAbHZa}=3+657@Ow8;&EWvv*@2kH5R7-@ln=vxI?B%Zu zjtxi%q!j*o2hT@dycWuidEnHmW87QS$m}Dl^uv0d)T$^`{P$8qDJ90H)*fYqUopW$ zMuTN;-c3b4y!Yw$8!GJ<_(dkrvh&|etyTth%pmn+LR)pyWx~uejx@Gvj1u%QnM7!! z{D!a8K#_&$vwD~t5HNPs@0UYRYuox{;h_UQj}X;#int)Mczgh zAu02_IdPU!rdsqD>z~%^9o)>F@X#M{?|~=jntA2}O^H0=gZCp}piL$j7&-cQm&4B5 zimCJO-3dA9F-|`ziTZ*`k&he-s{h0>7`hdyM17fP8VdFiA)%hd68gD5;Hp7>VaW@3 z5A@8dLqZwIGlyCmVn)ArT^bm#1rz*iVwt4rBj#^5b$-dPEul|lNfJI9`zPh_3zNo* zWY0bPz9E-j)^sLR*4{eLfwwZU1gCMxN3=tvOSEsklEQMH?yBS74^?vgFn<64G9=q^!lirCg1feacBM(jNypP zQQuvXD*F?EMAdwp!pYDgg*c~S@51q`q z9m3b&Y^|;)ZPOZ|V+ipfUc!+HW$Bg~HN@XK88!$AyaM=8{|329AlNB0sKr_6&OR~? z0~HR;qQEkLqpY@)QXX#IAqH#b1?K6JaW4vVTJD*@ofLj|iB{LCJBlE2e!pdo@!Z@W zoY4i!FPuV#z00?Hk7X#K!2q!3+a>62xY5>(KU?E+pfLpuPu_$3(WBW6ZB}^v;R)mI zFgK0AFK9UljMsSE^yMNyOL!=Le^9j$*^kR(ItkfEje9jKEpO;J0#QAlL98R9;!OJb zH4{)7qxKyg5l$~k@7e+Ac95vmdeCT+W{;xjy1g7PyGMT8&@>2mG6Fi9N2cmtt1Y9c z3u)`2KOt*7q>bkdnE?U0%%xZ|#jWcCs zoIuj}CpGM#N4%m?=RIp&@EefcXn`^RLl&Z$Xd(ZM<9>!6>fU4amq zu$bi=^v-|MgdW!D?gYbU24oAE+2>^~9qR){u;puf)3A93e%66%mg0GPu_I=G1GAcU z92HyD6}oHZOJA|_w(h9Hfv3BBvy2I^nhY`bq>H{g2yK@=x>3|l<*C;m* z)3t=-8Me?xM>62?H`p0jH!OpGiGk%3kDn*Wncs4}gvD&`t68+SEW@BikL;s_08r1Hy^MP6e)F0saPjjPUqBw$*g$1@t-5n7K1`OBpe)nBz=NhyFlFkdgjj$PH zw9$Wz9N2~X+$;pMjoUm_nE>DIM3sZ;Qv^Qpk_GOAZ#tNdn`1#A3o_O4o6UfQfQOkH zuR{BD&v;4Xkx)E1Q14RJ+R$lH!rHnfVqsTkF&z<#&LVJ3g)=kp26wOEU~icW{H5JP zHIjkAN1=<^hMCSF!MQ>1QtM1tCoCi0K{ZE``<`;Qy!$e`C^(sHS?aV}E1-+Iz58=s zPiu{5$IP{S87rk=^qg#rpH=TJnP4XCL|uRt5=)kBdSTCT^3_O6_W)^_&Prx$iqJy9 z>e>0w%LrEbr|8uvP(X0QwN(YB#wfkz_n9{3#6;{utxB$Bh=%db`hLf-xSx&?y;?wG zJ0iOc#P}aA9^{@fz^W|pWF*c;sXw+j-JX|u9Q!}#H8V|M#3?8A?troLa$891kKfgO? za?T5u)`~#nt4?322hVLpAtgna{oopyn{nJ>#~L{~&QYpcN>0v1qU)#W*EBYBdtn_AuVEe!+_%LHS(;L$m$O#dxsy9c4(6 zd;hb49$|zx6#KmzW!i1KSvlU{poI;@6?HmnOQJW9Nwhxmeuc2D0mfP359i(^ z1$X4tf~!n4BBXy0SoA(1YJGDWA3BLGC!g^3OES#Kccdl!o7*UfAlhhO?KGazTuOPc zGr@)VvH7=+%G62hDD1@J-5|!kptYd^b>!o}pe=$mzj2QIygUCs8|(-Fps#+`=8NV% z8dVMT^iMBN4($>Yo_A=VCZ?W;W}8w14L`bJw4C>upe)wiVM;pvbV0|5A3S_^Nct;m zm|Xvc7F1aWX%2AHZ(zb-=+OeU%pf2r?g`G0E(&;aPK^iF_aipJpbG6-=3g@E#cY=u z)Y0SUS|zdyeSpbV&*ZkdtGb7b%%}S0hmj1B9D4UXNp*9Y&{+b0sn4dX-s1~$Go4Nc zrstZ7xP5_7N5Xmcm5ltCDFzo1R$zciNs9akdV787ocE9T71y6!IuiC?+8@Q2)e9{b z-K&y0CSUtrTtDFBr$1C>H7b3iPLUYIQDAHD$vc9yr+OtEY9LKu3W$Z6&q0Hrxj+2_ zC?xMXrke#Cuc=jo%yrH%p7MM#wy;7}{EnYwb*<8_Bi-`7fH z=KT6k8~c?0=X0vt#x}Qr<59%Ur*VGQx$Zv&689PoLSOS3@;;)1rC1)b z-qZIo5KTMa2qx~n!@*%+odapjM{!2MP1)X)z5a|Jrdx5|(G-!A!-fFZVe%0z@y^IO z;~7f2WHmrx-12(J8%ixgy*QIvIooiF*X!BE`S=`_vXDD)chcQGFXwG$zc1!FiWDM3HGZ+lyDoh*tW%F1|Tse;cZf2(=HpLD$r_3E1V1+ctL zeB~ITdogiSluesk`ih6pZosUhG0OZYhxmyAu)Xn9HJV4+e>+xjTlA%djjoi4Vi z(+vtpFnSr}QeA3dyhS{XkF0ADfX!SZi~;bYuBM)$LESiskoyeIB%V=~qj&ksDnCe? zW95|CIK_vVzuy_&L<2~<+k95^CGWaDG8kOOnZK+$7m5tH)aVLR=GV-3(IC1^W!YR0 zI8VFSyCp`r#}Dl`f7J37ml*b*#>3y58d!- zoXFL29bgDykDr(PdHN?quEU-Pnn%dFC)7%l{1!CRzx?3+>hzxvpm*jiJv5Qph^Hr(2AW z!5NmrNam51<$S@1=gl|$aLV{%sG;w}^Shv*PFk~pYC|E*qTEX^sZ{7d$?71y{rYz7 z&1FWOR!`SIwlt^a8x)c2rH}Xhs%)QYIvdWtG+tcsL?gs-5p%Y{0{(TYvXc03F(nA< z8Ge_hO%-GVv(t-QGd{3w5fL*UAZwPgWq6CE-!(8ep@O*QH=<*IWa}tI@ad#ZQN8|n zJ@|UTTS>B8_0#QXv#RtV_+Jt7+Y12z_+2vhles5IJDI=xEu`oaDjCei`eoR4`ii7` zRSOdtpH80mxR!<`ojR2UIhVofl&X_n$d@>~gOIeGr)x31m z*hyJQsHF58iT8JH`l9x)s5YWcv6{$lySge@+m*#6ye^BqvKY=O{HR|BpG6VbDa24a zj#G#u7{CX)-8c0ghX2S_vzy7m1n64{F8G?a#-lk_mG!7+Gv+U(sHir4OTU)qs@Cm& z7_nJ@)wRV?3B`3&ztB$RLKBDA4hv5TRMiU!L}mmLEqO)TTHFbxWS)KOCw|2G_vMWX zSok0H=53`$O#r3VqSd77ht{{5!Cwm-5{k*Vej)+V|BdFwofuz+&2jv!wW}}R96RKH z>Nn^J^?7|11puhZ6^*TFZPDYZdniMkzD>dvfO_bsNeQk+QGPD3=lyQ2Cpl~Bi zLbXz)f7qoKz$seF=^IIP(+1jYn{&RfxkPk0*JZ9QC#4^2eiiJzx>B9`i>jlPqa_;wMI1B&kkq1@O;qXb*Y)7AZ9CL(0|(ANDGeQCr^{z zKm#6amCkcnqKi>uy2G0&j2HOTVXde$( z7-oSjRm0%4+Z&l@{0^(Nczu7M0}s`JY-;F)>NjlVPVaqHrGR^%?|+!vnrhd-Pg^(; zc3`5)UYpAkyo49$RtO2fE7umLYnM0Qk#q3z>JrkweA8D;uw03w1-8BSpASXsDFHv2 z=k^}a*vam>niWM!tv@rejK8n~HazBY`})S$n+NtfE?A{*kOrLSv~`hav?;+h1yfiU zd`BE!xM2=Pq*KDa0`=`Ft~~s#o`0rl|KQYcZ$7ql41A{X{+{sgeeCXq^JU_zKQ+Ln z;q|)8OxN1uQG)XRC%Fm2M>bczoy-&jC-g8YP#89u$iEqoqz2TV{{7sqREDja%+FI8 zlmU2g2OMa;S=n^6UVo;<+)&ptbq{J&HJTcxo-OFAibNmI6kGR{_19i#OR9R_z5IS_ z12Tmi7&1oFK~x}$(F{9Ze1MYoWFw)leV*m@WQyQvywdz2Uvw6d2V@=N$*hZ1O)a>FU7rW?HKoxZpdp!0Mm{Q`gG zgfDFB^AfG&z~5qLibU{^c~jN|`UJMJv7dG01T%B^b?}O^j!3#;=)nVTf`0?8Vlq5m zZ8E=p#S61*a*McPRTM}BQF_G*KfbLpm!#1tsFnZ4qEPlP`$)?;R) z-#6vY2I;iMB=@sfy7}#bysGDWyOCFU?wva-2H@yTyWKn31aVj+s`v$H_w==y=dTMW zGwFaVc1YFdL_yvOZ6ns^!Lt%Ay@Ul(Z|~KDLns-H(SI)YeIGu6dP*sm6uLbSn&@z0 zopZmtQ~0ptEIIa-=ux2^4LWEJ4L26p8E#k~$I;03CY>9x%I42mJDAFtb(h#b=iloAgkxb9+nJa4zD#&Ts?KJAN8=# zN%*4&sd6SwsdnH<^k{yq!z!~n;w!W@-m)7D|3NW@CX{8}LR)$8Z-7q-bplAc;6Npx z0AVHi8rcCy+G7vP-(rNmS%F0n|7VqSbtd-DP7}${6{vX}qV!u7Lsq+7otpb(SKB00 z%Cvs5)s3`+h-%XFj=4X)74;Pn{HwDk5)#vwLq@-0$X=B;iXj-)Nd7px zz05wNUn1U9<)LbWJ2dB=)sD7I+l9=KVSmQHAt;BYRXr()O>=4hNPW@nv|%)E*9pWA z{WZ5w8Orx@+jnnB#}nQsa_>%)pmHO*5X29{XHFvWY(5tLsf*L~CQ`cBp+IIl6R)uM^q{p>Mu0KmRtl3olOY8|z5fyKTmRr5v`KZ<8&JDSytn1hh38}a2<18xK z`;@;?hnp+LD0Xb*?{70{E44B>#)MR+%(pAw>URa{K&aAFMhS1c-azZ2VzsmNYKCJN zYim3ukvhISwU{LCCQI<@_uD)EvZ_iXj%5|k3s#LxUc==5UDYllhubFF*1fXpVAXa! zjL{JaGh4^1Q_?q=IDoI-z-R{}hv0i$BCSi>pJxag({ z`z=VWs}SPO)C-6v+M_}{if_G-6NZ_O{_EB{$z z0}vs)BXws<0(d(I3OEuAY8duk z(6kaj5Nmnjqfe=ndFO}^9pCvpKedv_OJiu*>RmNM(dtKY}&Xq4wxAwp8V!EyFLb^2GgZrOy8kuchQ zF<}qpQWNoDZg$@nKX^kPLtJnZ-;e*Xu^LOdOT)T>c6m4mBZulJ+^SSV5HFP0vn<$f zJz1+bw7h8+FH8i{hfw!^xOqFYQ6uLo8X-2CUD9VpkS%=)1@dMmoNA1L~w z`*Ey{GfN8PX?+3WrSh%oRV51tVjCU4iN#!tT1(Ru;xg4DR)p-)_0JrjweYCKXFB@-TjBGld zW0%vFaWtt*uKkumz9&z&z!t^D>|iw%oP$e8*(e!w{78PZ(LV6{4l0XiY9nvUEfE>( zGBd3FQBG+3Y@pgtDF`IZO2JWm=zb;&^LHAxK>QZbJIEmQL0IV5IKT|3&(6Ln=CZEC z>zddfC42V1Lj-U_pWl;SS%L^jvuR-RY0OYd3%4P#&32Y^O$v0#X14z#FRMD4vEa9& z4%T1~s9P0G0uHk5rns98#)_n|1a|E{hp(3kB##1Xw~UEYgBp7SiHAzaEBc_%Y^NUY z+P!V_Xa)UBOePeVORU(i+sOTRJrCSj{97@*(c_#V16JzB>7+pU=P#3?V1Vw6zxD?j zF3|CKOrr!|#ZA=dc>`ZD`9FfS9rKxORIcmPK?f%#T$J%Ec$f6Q!A-m3jGTVP)CK=e zeK)p7yo}@d>Xm>pcN6Lh z@Luum^I5m8)XJYj$Q;_@x%!reh}0i3NdFx@1GY_eQYqca7V%qOB>5j8R9cC7k)WcdK*!cF-z=~KWXSG$n z5#OI-xZq*Q#MMIL{V-g36Zy757rE0iZC{)ya8@X>YEp zZL%&l-IiY`v#e&7hMs-Dkz3alF3x_MpyW#6#<4ZRrZr=^Ccb`wvmCE;TG>t&x=C+W7H$*xrtw19=xffTDNwd=Yo7H$4Z$$+Sk{bP_=u5$fD?(hW2$~E_c!o{KikK@i&^@x33RfQ1QR^2R% zB^0e1Tcv^NzJ7Qmj90eIdCLM?;_Yo1OzW_Si0h>TI%CIZaX|5wRj%fPN`8dm)|&ez zVD-gtlS-$86}~V`xY?LHirr?0JlRu5z083>eYH~gEiYX<@N zhnJq8cTp47;-e(>b4s#-Tbe#y;Cta)yISSYr)7*VefvDh5;CO1JG(lJShOz!k`}RY zE7~HvYFj+p^nKrvpGkO-0Fjkpz&;SZfITC%knLV7*YLUdjoC@oJM_Zaursk~bNU3L zjgmacyE%U8N^ZJK=QqW1qY-ez@BVX3Mk&U|H<*^X{^DFUwE-+f!~1hngyI&rj))`@ ztS-P;$EtyQFPW`~X-WFl^at?(PE5y3xvUJO0aN58b$CAVz;41>2v<))wPfYd-Zo8K z?LsiqiGF{bpN7ekz3Iwccuaxo$` z{qxA0uJkN~rABy!HG^fE2>9q&yVWJ!e-TG(@MBIquZ>$7*h9rxK+cN!^pbQK_&n6W5DcU&UATS{NQlSTP~K{?6#!f{Ih4q;k%oD}7Wpwb6t~;AcbbJ2a#@i!XoH ziT*?ZE_wBYsju(e1>x)ra+?@d2*j?;8=DWJh;hin9n*%|%bcQ$kk}Rq7wI$~R|ZeM z`RBU=GCz&jkX#WmGX0)T_n}@X797cCr=8zdX`fDJ{A;mJ+IWDz+`J97!+0nZ_7xOH z-AehlU74ergF43;mrV@dfkU=Vw~{RJ7{2}d0vGC>1VzOQ30(+(Km2iZWOnsgi6=e^ zMvRYYd!Z!v(Jk==oF8IoK%gICA_@4!Zqmjqi@}Fs$UGln9O&W319Lu@z!&LjWd1n5 z(Gp~|>U?>RDYtk$kA<&z?t$=kkV%Lhy4oz}tYI#(oI>aOckm5BQ&&9|-G9}Ye2{x} z&i4-F zK!Kq~YNd8)q*T>>LO%Y%2d(~h;Z^}TP&YMsd_Cn)ygMOka`^F9w$bx)8V##KaYJ9s z*U_zXi^xsocrO%I`>SamI@L$(1=^!f(Z+Uh5rmA>@6pD)i%OPn5-AlNJ1i4GTYi$k z2ncSop~m&v6NPO-9(S&f&)y{8y+y1j3E;ZAXM!}LfiQYNsiFVvQ}Ex_miSnB{1{px zl=dJuQO7@TL~MK*d0?bdo3(DMxUus6Z+b6Q`&!X0@tIe7e@s*iUz1MQdbZ%d3E||D zT4oZ7)m!+GgJc0y&P6^6d95uOPfWe;U40LI(@bw3yXook%0l+#$PM{0_N@dMA+Q*z zi*R9F@J1?>ca-3k9^TARg@r?)@j``S9PkR|m-{T-?-Tu4v~|QLZ9#|Cdp@&vqbll; zui;-7eJCW-;VWLN_P0-OpMsscERh2>5>A(7FQi1#Z>iXa3bWo`99J(PlrOQmd< zx!q@0xC&rAN|2}^%N|yGqm`D*U|2!>-Wlf_peKk1LTUXeA4wRh1Ns&|p8lk$6?V~2 zk_iUXHU@G78a|6o11_8b=>awK7~GpYtXelIaFq);q8H(8|3{n*Ao@FXCCpFERA=hJ zNPC=tTZyR*fAMm^($_|L%7XI6YukT*YwO1fX+%gL$@xqt7 zfYmqfHEkjBns@;(?gYM@J(0+e%!SjO?7KO&;Q4wjVv%FsA((k!zhoax(ZhsRtck{q z?}XT5<%plST^dH_ehCSpBYL>LQg}6EaoU_!bX+>HoiT^Q;z*i)`a*l>ivXu!_j@Xst=-WPB4qNB6Le;+GV3;=m z&`$o%v6j{6rJ8Ny1ZTHr<^A-3r}vLpJQd83)x^zk{SZkUHLOziq>$V(+&lRGL!#Tf z%88okQl=uE4_x_T%M4me`gRwWNI3qF#&h!li^cqACnFKLHqse~bU`tAiXC%|qJ$m6ee zeKYZ7chJ%*Xg*-zwQAiLtlwuLUWt?*FK9(Vobe4A09S+KZ0wF1SOR*rf1hI?nj`;Q zg9|p@W>da@HpGo!G0EIqRwzL0URHl4R3wAF?hw?TBk*N$iv?y&{|6MB$QNO{`nCXu zgd3Y{`^ZX~Mb$WmS-!GZjh!F1!|G)4{UQ-g#_rwv1j!YM?2%sDDZzhXgv~ z0a4Fe=o%|8H=A(RJQvt8V~$S3w}uz+fF1g(Q+UXN+dvA|f(uI$h1WUKYSGbXct9$e z>UGL%Nl4+&)X%hnkcTqBpZ-ONmyy%2nc3B$KVEDbR`ecmNiaBAx3K7q_2t901_m0j zMP&|koCq-S|!c4k>5 zaP}Pu@Tx&H*G~BFYEG2s)g>y8iZl%VpAHEKl2j1Gc4pi)<$PsE5v%wc>>8;J;ZQDw zy?BCu=f!ikxyWHj$b4_n6_bnXb|t%)m1{Gd-G17qmu2m}2)bUM``;=S)s#N$a47RZ z0PS(k_&_3x=-@O4Gf*39ADse-r!e_kAaH;dp4{P*<$Q{if3%x~ZTX1{oV0NScH%oK zkoc&X1Z0LL#(K<)rrKnV>lBGMALu#l)a8OzD53Mi9Pu&9$D?uMFk6So;;?*WBJ$lZ z5t}p-G5;U7$Otm#5KjV*7AO&1uD>s+kX?Xg>!zQm-9vhudy3G)St+8f&hP;exJ@~Q z^nCp&9|^qia>w&Q%R&&F??H8Dt9ma-UQCZ>ILJ6U1PLp)h3AzqM+}9ar(a3$p{w(J zg$wdpZHER1U5Vqjjaz(@SzjY5r)a_1#GH=g**S1$?&c zbn`AtjFjeqfOJ9-@=?U?+1p_n%4cZpgB0UTyRu`MwD^#0QF|pSEe616r-?2SNs^>2 zFKRsMFHV7-`VN=sl#k&~OU69@Uf5>{s9XQBHYVn-Fqt%J*feCK{JCBX`KWRiZ(dhg zopJYJ3f8m91KteRxCL5FQ@6NQ=q{gk@$oO9d|%5D0UL5#a&a6 zS(Jh?3Ad7V2THN+Sq$g^mn*qPoG}Y>lBu^!}?OqCt zlq2xLl}*q96j=r>p(Q*XP&#Q-_}eD z{W^DaC8SUpO>D}Y8XqD{7;d5nqJrmr#6^R_ME1KL Date: Fri, 11 Aug 2023 15:44:46 +0200 Subject: [PATCH 013/137] fix scene borders adjustment Signed-off-by: Evgenii Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/cgobject.cpp | 2 +- .../IntroducingObjects/src/rectangle.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index 0f2d315..8e75eb6 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -40,7 +40,7 @@ void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureMan return; sf::CircleShape shape; shape.setPosition(circle.center.x, circle.center.y); - // shape.setOrigin(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); shape.setRadius(circle.radius); shape.setTexture(texture); window.draw(shape); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp index e2a28bc..2d820a2 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp @@ -10,14 +10,14 @@ Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { if (rect.topLeft.x < intoRect.topLeft.x) { vector.x += intoRect.topLeft.x - rect.topLeft.x; } - if (rect.botRight.x > intoRect.botRight.x) { - vector.x -= rect.botRight.x - intoRect.botRight.x; - } if (rect.topLeft.y < intoRect.topLeft.y) { vector.y += intoRect.topLeft.y - rect.topLeft.y; } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } if (rect.botRight.y > intoRect.botRight.y) { - vector.y -= rect.botRight.y - intoRect.botRight.y; + vector.y += intoRect.botRight.y - rect.botRight.y; } // TODO: implement operators for Rectangle (and Circle) ? return { rect.topLeft + vector, rect.botRight + vector }; From 36c37db52a97679d710630d9e86eb37747eb5ec8 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 11 Aug 2023 16:59:29 +0200 Subject: [PATCH 014/137] minor refactorings Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/cgobject.cpp | 4 ++-- .../IntroducingObjects/src/consumable.cpp | 14 ++++++-------- .../IntroducingObjects/src/enemy.cpp | 8 ++++---- .../IntroducingObjects/src/player.cpp | 13 +++++++------ include/cgobject.hpp | 5 ++--- include/gevent.hpp | 6 ------ 6 files changed, 21 insertions(+), 29 deletions(-) delete mode 100644 include/gevent.hpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index 8e75eb6..66541b7 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -2,8 +2,8 @@ #include "operators.hpp" -CircleGameObject::CircleGameObject() - : circle({ { 0.0f, 0.0f }, 0.0f }) +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) , state(GameObjectState::LIVE) {} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index f8df0e7..fde1c9f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -2,23 +2,21 @@ #include "constants.hpp" -ConsumableObject::ConsumableObject() { - circle = { { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }; -} +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} Point2D ConsumableObject::getVelocity() const { return { 0.0f, 0.0f }; } const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { - switch (state) { + switch (CircleGameObject::getState()) { case GameObjectState::LIVE: return textureManager.getTexture(GameTextureID::STAR); - // case DEAD: - // // TODO: return empty texture instead (?) - // return nullptr; + default: + return nullptr; } - return nullptr; } GameObjectKind ConsumableObject::getKind() const { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index c83a35d..2d00042 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -3,13 +3,13 @@ #include "utils.hpp" #include "constants.hpp" -EnemyObject::EnemyObject() { - circle = { { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }; -} +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} Point2D EnemyObject::getVelocity() const { Point2D velocity = { 0.0f, 0.0f }; - if (state == GameObjectState::DEAD) + if (CircleGameObject::getState() == GameObjectState::DEAD) return velocity; velocity.x = generateBool() ? 1.0f : -1.0f; velocity.y = generateBool() ? 1.0f : -1.0f; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index 245fa14..023ee46 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -3,13 +3,13 @@ #include "direction.hpp" #include "constants.hpp" -PlayerObject::PlayerObject() { - circle = { { PLAYER_START_X, PLAYER_START_Y }, RADIUS }; -} +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} Point2D PlayerObject::getVelocity() const { Point2D velocity = { 0.0f, 0.0f }; - if (state == GameObjectState::DEAD) + if (CircleGameObject::getState() == GameObjectState::DEAD) return velocity; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { velocity = velocity + getDirection(North); @@ -32,13 +32,14 @@ GameObjectKind PlayerObject::getKind() const { } const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { - switch (state) { + switch (CircleGameObject::getState()) { case GameObjectState::LIVE: return textureManager.getTexture(GameTextureID::PLANET); case GameObjectState::DEAD: return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; } - return nullptr; } void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { diff --git a/include/cgobject.hpp b/include/cgobject.hpp index 2b54708..4774afe 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -8,7 +8,7 @@ class CircleGameObject : public GameObject { public: - CircleGameObject(); + CircleGameObject(Circle circle); Point2D getPosition() const override; @@ -24,8 +24,7 @@ class CircleGameObject : public GameObject { void draw(sf::RenderWindow &window, TextureManager& textureManager) const override; -// TODO: make private -protected: +private: Circle circle; GameObjectState state; }; diff --git a/include/gevent.hpp b/include/gevent.hpp deleted file mode 100644 index 9e77cac..0000000 --- a/include/gevent.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef CPPBASICS_GEVENT_HPP -#define CPPBASICS_GEVENT_HPP - -class GameEvent {}; - -#endif //CPPBASICS_GEVENT_HPP From 10835e2033c08ac1ce72a25283d6988f0a823540 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 11 Aug 2023 17:28:48 +0200 Subject: [PATCH 015/137] implement consumable object approaching logic Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/cgobject.cpp | 2 +- .../IntroducingObjects/src/consumable.cpp | 20 ++++++++++++++++++- .../IntroducingObjects/src/enemy.cpp | 4 ++++ .../IntroducingObjects/src/player.cpp | 6 +++++- .../IntroducingObjects/src/scene.cpp | 4 ++++ .../IntroducingObjects/src/simplscene.cpp | 8 ++++++-- .../IntroducingObjects/src/textures.cpp | 2 ++ include/consumable.hpp | 2 ++ include/enemy.hpp | 2 ++ include/gobject.hpp | 4 +++- include/player.hpp | 2 ++ include/scene.hpp | 2 ++ include/textures.hpp | 1 + 13 files changed, 53 insertions(+), 6 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index 66541b7..ec216d9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -4,7 +4,7 @@ CircleGameObject::CircleGameObject(Circle circle) : circle(circle) - , state(GameObjectState::LIVE) + , state(GameObjectState::NORMAL) {} Point2D CircleGameObject::getPosition() const { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index fde1c9f..ef5f1fd 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -12,8 +12,10 @@ Point2D ConsumableObject::getVelocity() const { const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { switch (CircleGameObject::getState()) { - case GameObjectState::LIVE: + case GameObjectState::NORMAL: return textureManager.getTexture(GameTextureID::STAR); + case GameObjectState::CONCERNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); default: return nullptr; } @@ -23,9 +25,25 @@ GameObjectKind ConsumableObject::getKind() const { return GameObjectKind::CONSUMABLE; } +void ConsumableObject::update(sf::Time delta) { + if (CircleGameObject::getState() != GameObjectState::DEAD) { + CircleGameObject::setState(GameObjectState::NORMAL); + } +} void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { + if (CircleGameObject::getState() == GameObjectState::DEAD) { + return; + } if (collisionData.collide) { CircleGameObject::setState(GameObjectState::DEAD); + return; + } + if (CircleGameObject::getState() == GameObjectState::CONCERNED) { + return; + } + if (collisionData.distance < 6 * getCircle().radius) { + CircleGameObject::setState(GameObjectState::CONCERNED); + return; } } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 2d00042..80fb6ba 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -25,6 +25,10 @@ const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const return textureManager.getTexture(GameTextureID::BLACKHOLE); } +void EnemyObject::update(sf::Time delta) { + return; +} + void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { return; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index 023ee46..5b044fe 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -33,7 +33,7 @@ GameObjectKind PlayerObject::getKind() const { const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { switch (CircleGameObject::getState()) { - case GameObjectState::LIVE: + case GameObjectState::NORMAL: return textureManager.getTexture(GameTextureID::PLANET); case GameObjectState::DEAD: return textureManager.getTexture(GameTextureID::PLANET_DEAD); @@ -42,6 +42,10 @@ const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) cons } } +void PlayerObject::update(sf::Time delta) { + return; +} + void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { CircleGameObject::setState(GameObjectState::DEAD); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 237a182..d283ff3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -54,6 +54,10 @@ Rectangle Scene::boundingBox() const { return box; } +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + void Scene::move(GameObject &object, Point2D vector) { object.move(vector); fitInto(object); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp index 86d4d45..548f538 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp @@ -11,8 +11,12 @@ void SimpleScene::processEvent(const sf::Event& event) { } void SimpleScene::update(sf::Time delta) { - move(player, 0.001f * delta.asMilliseconds() * player.getVelocity()); - move(enemy, 0.001f * delta.asMilliseconds() * enemy.getVelocity()); + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(enemy, delta); + move(consumable, delta); detectCollision(player, consumable); detectCollision(enemy, player); detectCollision(enemy, consumable); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp index 5bde26f..2b5abcc 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/textures.cpp @@ -12,6 +12,8 @@ const char* getTextureFilename(GameTextureID id) { return "resources/planetDead.png"; case GameTextureID::STAR: return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; case GameTextureID::BLACKHOLE: return "resources/blackhole.png"; default: diff --git a/include/consumable.hpp b/include/consumable.hpp index 6b97064..e16e772 100644 --- a/include/consumable.hpp +++ b/include/consumable.hpp @@ -13,6 +13,8 @@ class ConsumableObject : public CircleGameObject { const sf::Texture* getTexture(TextureManager& textureManager) const override; + void update(sf::Time delta) override; + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; diff --git a/include/enemy.hpp b/include/enemy.hpp index ef72743..b1dac68 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -13,6 +13,8 @@ class EnemyObject : public CircleGameObject { const sf::Texture* getTexture(TextureManager& textureManager) const override; + void update(sf::Time delta) override; + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; diff --git a/include/gobject.hpp b/include/gobject.hpp index fee2902..d69b117 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -10,7 +10,7 @@ // TODO: use enum class (?) enum class GameObjectState { - LIVE, DEAD + NORMAL, CONCERNED, DEAD, }; enum class GameObjectKind { @@ -38,6 +38,8 @@ class GameObject { virtual const sf::Texture* getTexture(TextureManager& textureManager) const = 0; + virtual void update(sf::Time delta) = 0; + virtual void draw(sf::RenderWindow& window, TextureManager& textureManager) const = 0; virtual void onCollision(const GameObject& object, const CollisionInfo& collisionData) = 0; diff --git a/include/player.hpp b/include/player.hpp index 5f0c98f..ec2682d 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -13,6 +13,8 @@ class PlayerObject : public CircleGameObject { const sf::Texture* getTexture(TextureManager& textureManager) const override; + void update(sf::Time delta) override; + void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; }; diff --git a/include/scene.hpp b/include/scene.hpp index 5319c1b..076a4bb 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -34,6 +34,8 @@ class Scene { Rectangle boundingBox() const; + void move(GameObject& object, sf::Time delta); + void move(GameObject& object, Point2D vector); void fitInto(GameObject& object); diff --git a/include/textures.hpp b/include/textures.hpp index fa86950..ec0827d 100644 --- a/include/textures.hpp +++ b/include/textures.hpp @@ -10,6 +10,7 @@ enum class GameTextureID { PLANET, PLANET_DEAD, STAR, + STAR_CONCERNED, BLACKHOLE, SIZE }; From 80c80dba335ca4fc19d819f61e797cf8e78685ac Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 11 Aug 2023 18:04:37 +0200 Subject: [PATCH 016/137] change enemy movement update logic Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/enemy.cpp | 17 ++++++++++------- include/enemy.hpp | 4 ++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 80fb6ba..15a9693 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -8,12 +8,6 @@ EnemyObject::EnemyObject() {} Point2D EnemyObject::getVelocity() const { - Point2D velocity = { 0.0f, 0.0f }; - if (CircleGameObject::getState() == GameObjectState::DEAD) - return velocity; - velocity.x = generateBool() ? 1.0f : -1.0f; - velocity.y = generateBool() ? 1.0f : -1.0f; - velocity = SPEED * velocity; return velocity; } @@ -26,7 +20,16 @@ const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const } void EnemyObject::update(sf::Time delta) { - return; + if (CircleGameObject::getState() == GameObjectState::DEAD) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + velocity = { 0.0f, 0.0f }; + velocity.x = generateBool() ? 1.0f : -1.0f; + velocity.y = generateBool() ? 1.0f : -1.0f; + velocity = SPEED * velocity; } void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { diff --git a/include/enemy.hpp b/include/enemy.hpp index b1dac68..6a335c5 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -16,6 +16,10 @@ class EnemyObject : public CircleGameObject { void update(sf::Time delta) override; void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; + +private: + Point2D velocity; + sf::Time updateTimer; }; #endif // CPPBASICS_ENEMY_HPP From 5a49201413c91fe75092b2ba982daf223ed6b944 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Thu, 24 Aug 2023 17:08:58 +0200 Subject: [PATCH 017/137] working on framework lesson project implementation (dynamic scene) Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 3 +- .../IntroducingObjects/src/dynscene.cpp | 74 +++++++++++++++++++ .../IntroducingObjects/src/gobjectlist.cpp | 3 + .../IntroducingObjects/src/scene.cpp | 5 ++ .../IntroducingObjects/src/utils.cpp | 25 +++++++ .../IntroducingObjects/task-info.yaml | 4 + include/dynscene.hpp | 31 ++++++++ include/gobjectlist.hpp | 66 +++++++++++++++++ include/scene.hpp | 2 + include/utils.hpp | 6 ++ 10 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp create mode 100644 include/dynscene.hpp create mode 100644 include/gobjectlist.hpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index a80879a..aec3507 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -8,8 +8,9 @@ set(TASK set(SRC ${TASK} src/main.cpp src/utils.cpp - src/scene.cpp src/simplscene.cpp src/textures.cpp + src/scene.cpp src/simplscene.cpp src/dynscene.cpp src/textures.cpp src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp + src/gobjectlist.cpp src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) set(TEST diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp new file mode 100644 index 0000000..7daf8ce --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -0,0 +1,74 @@ +#include "dynscene.hpp" + +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +void DynamicScene::processEvent(const sf::Event& event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + break; + } +} + +void DynamicScene::update(sf::Time delta) { + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + detectCollision(object, other); + }); + }); +} + +std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setPosition(*object, generatePoint(boundingBox())); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide = collide || collision(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void DynamicScene::draw() { + objects.foreach([this] (const GameObject& object) { + Scene::draw(object); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp new file mode 100644 index 0000000..52a7a59 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp @@ -0,0 +1,3 @@ +// +// Created by eupp on 24.08.23. +// diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index d283ff3..17a8487 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -54,6 +54,11 @@ Rectangle Scene::boundingBox() const { return box; } +void Scene::setPosition(GameObject& object, Point2D position) { + object.setPosition(position); + fitInto(object); +} + void Scene::move(GameObject& object, sf::Time delta) { move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp index 1054bb7..d5fecb3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp @@ -10,4 +10,29 @@ float generateFloat(float min, float max) { bool generateBool(float prob) { return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + // TODO: replace with rectangle arithmetics operators? + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index abd8c8c..28ea0ad 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -76,3 +76,7 @@ files: visible: true - name: src/utils.cpp visible: true +- name: src/gobjectlist.cpp + visible: true +- name: src/dynscene.cpp + visible: true diff --git a/include/dynscene.hpp b/include/dynscene.hpp new file mode 100644 index 0000000..bf5ec50 --- /dev/null +++ b/include/dynscene.hpp @@ -0,0 +1,31 @@ +#ifndef CPPBASICS_DYNSCENE_HPP +#define CPPBASICS_DYNSCENE_HPP + +#include + +#include + +#include "scene.hpp" +#include "gobjectlist.hpp" +#include "player.hpp" + +class DynamicScene : public Scene { +public: + + void processEvent(const sf::Event &event) override; + + void update(sf::Time delta) override; + + void draw() override; + +protected: + + std::shared_ptr addNewGameObject(GameObjectKind kind); + +private: + GameObjectList objects; + PlayerObject* player; +}; + + +#endif // CPPBASICS_DYNSCENE_HPP diff --git a/include/gobjectlist.hpp b/include/gobjectlist.hpp new file mode 100644 index 0000000..0b70f8c --- /dev/null +++ b/include/gobjectlist.hpp @@ -0,0 +1,66 @@ +#ifndef CPPBASICS_GOBJECTLIST_HPP +#define CPPBASICS_GOBJECTLIST_HPP + +#include +#include + +#include "gobject.hpp" + +class GameObjectList { +public: + + GameObjectList() { + + } + + void insert(const std::shared_ptr& object) { + if (!object) { + return; + } + std::unique_ptr node = std::make_unique(); + node->object = object; + link(head.get(), std::move(node)); + } + + void remove(const std::function& pred) { + Node* curr = head->next.get(); + while (curr) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } + } + + void foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr) { + apply(*curr->object); + curr = curr->next.get(); + } + } + +private: + struct Node { + Node* prev = nullptr; + std::unique_ptr next; + std::shared_ptr object; + }; + + static void link(Node* cursor, std::unique_ptr&& node) { + cursor->next->prev = node.get(); + node->next = std::move(cursor->next); + node->prev = cursor; + cursor->next = std::move(node); + } + + static void unlink(Node* node) { + node->next->prev = node->prev; + node->prev->next = std::move(node->next); + } + + std::unique_ptr head; +}; + +#endif // CPPBASICS_GOBJECTLIST_HPP diff --git a/include/scene.hpp b/include/scene.hpp index 076a4bb..73871e3 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -34,6 +34,8 @@ class Scene { Rectangle boundingBox() const; + void setPosition(GameObject& object, Point2D position); + void move(GameObject& object, sf::Time delta); void move(GameObject& object, Point2D vector); diff --git a/include/utils.hpp b/include/utils.hpp index 807c43c..936784c 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -2,6 +2,8 @@ #define CPPBASICS_UTILS_HPP #include "point.hpp" +#include "rectangle.hpp" +#include "circle.hpp" float distance(Point2D a, Point2D b); @@ -9,4 +11,8 @@ float generateFloat(float min, float max); bool generateBool(float prob = 0.5f); +Point2D generatePoint(const Rectangle& boundingBox); + +Circle generateCircle(float radius, const Rectangle& boundingBox); + #endif // CPPBASICS_UTILS_HPP From 74ad0dbb0afba4f4952479cf189cdec3ed391353 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Thu, 24 Aug 2023 19:11:47 +0200 Subject: [PATCH 018/137] working prototype for the dynamic scene Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/dynscene.cpp | 52 ++++++++++++++++++- .../IntroducingObjects/src/scene.cpp | 5 +- .../IntroducingObjects/src/simplscene.cpp | 6 +++ include/dynscene.hpp | 5 +- include/gobjectlist.hpp | 10 ++-- include/scene.hpp | 6 ++- include/simplscene.hpp | 2 + 7 files changed, 77 insertions(+), 9 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index 7daf8ce..eefa7d5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -5,6 +5,16 @@ #include "enemy.hpp" #include "utils.hpp" +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +void DynamicScene::initialize() { + addNewGameObject(GameObjectKind::PLAYER); +} + void DynamicScene::processEvent(const sf::Event& event) { switch (event.type) { case sf::Event::Closed: @@ -24,9 +34,45 @@ void DynamicScene::update(sf::Time delta) { }); objects.foreach([this] (GameObject& object) { objects.foreach([this, &object] (GameObject& other) { - detectCollision(object, other); + if (&object != &other) { + detectCollision(object, other); + } }); }); + updateObjectsList(); +} + +void DynamicScene::updateObjectsList() { + objects.remove([] (const GameObject& object) { + return (object.getState() == GameObjectState::DEAD) + && (object.getKind() != GameObjectKind::PLAYER); + }); + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } } std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) { @@ -51,11 +97,13 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) if (kind == GameObjectKind::CONSUMABLE || kind == GameObjectKind::ENEMY) { setPosition(*object, generatePoint(boundingBox())); + } else { + fitInto(*object); } // check that object does not collide with existing objects bool collide = false; objects.foreach([&collide, &object](GameObject& other) { - collide = collide || collision(*object, other).collide; + collide |= collision(*object, other).collide; }); // reset a colliding object if (collide) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 17a8487..38d05d7 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -2,6 +2,7 @@ #include "constants.hpp" #include "simplscene.hpp" +#include "dynscene.hpp" Scene::Scene() : width(SCENE_WIDTH) @@ -14,11 +15,13 @@ Scene::Scene() } Scene* Scene::create() { - static SimpleScene scene; + // static SimpleScene scene; + static DynamicScene scene; return &scene; } void Scene::run() { + initialize(); sf::Clock clock; while (active && window.isOpen()) { sf::Time delta = clock.restart(); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp index 548f538..0ce08af 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp @@ -1,5 +1,11 @@ #include "simplscene.hpp" +void SimpleScene::initialize() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + void SimpleScene::processEvent(const sf::Event& event) { switch (event.type) { case sf::Event::Closed: diff --git a/include/dynscene.hpp b/include/dynscene.hpp index bf5ec50..1372503 100644 --- a/include/dynscene.hpp +++ b/include/dynscene.hpp @@ -12,6 +12,8 @@ class DynamicScene : public Scene { public: + void initialize() override; + void processEvent(const sf::Event &event) override; void update(sf::Time delta) override; @@ -20,11 +22,12 @@ class DynamicScene : public Scene { protected: + void updateObjectsList(); + std::shared_ptr addNewGameObject(GameObjectKind kind); private: GameObjectList objects; - PlayerObject* player; }; diff --git a/include/gobjectlist.hpp b/include/gobjectlist.hpp index 0b70f8c..6b5dc84 100644 --- a/include/gobjectlist.hpp +++ b/include/gobjectlist.hpp @@ -10,7 +10,10 @@ class GameObjectList { public: GameObjectList() { - + head = std::make_unique(); + head->next = std::make_unique(); + tail = head->next.get(); + tail->prev = head.get(); } void insert(const std::shared_ptr& object) { @@ -24,7 +27,7 @@ class GameObjectList { void remove(const std::function& pred) { Node* curr = head->next.get(); - while (curr) { + while (curr != tail) { Node* next = curr->next.get(); if (pred(*curr->object)) { unlink(curr); @@ -35,7 +38,7 @@ class GameObjectList { void foreach(const std::function& apply) { Node* curr = head->next.get(); - while (curr) { + while (curr != tail) { apply(*curr->object); curr = curr->next.get(); } @@ -61,6 +64,7 @@ class GameObjectList { } std::unique_ptr head; + Node* tail; }; #endif // CPPBASICS_GOBJECTLIST_HPP diff --git a/include/scene.hpp b/include/scene.hpp index 73871e3..286f2ed 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -20,16 +20,18 @@ class Scene { protected: + virtual void initialize() = 0; + void processInput(); virtual void processEvent(const sf::Event& event) = 0; virtual void update(sf::Time delta) = 0; - virtual void draw() = 0; - void render(); + virtual void draw() = 0; + void close(); Rectangle boundingBox() const; diff --git a/include/simplscene.hpp b/include/simplscene.hpp index a522d0d..f8f6b60 100644 --- a/include/simplscene.hpp +++ b/include/simplscene.hpp @@ -11,6 +11,8 @@ class SimpleScene : public Scene { public: + void initialize() override; + void processEvent(const sf::Event &event) override; void update(sf::Time delta) override; From 5401e4ecfeef4a1c3e060707322469371387b56f Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Thu, 24 Aug 2023 19:27:13 +0200 Subject: [PATCH 019/137] rename `SimpleScene` to `StaticScene` Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 2 +- .../ClassesAndObjects/IntroducingObjects/src/scene.cpp | 6 +++--- .../src/{simplscene.cpp => statscene.cpp} | 10 +++++----- .../IntroducingObjects/task-info.yaml | 8 ++++---- include/{simplscene.hpp => statscene.hpp} | 8 ++++---- 5 files changed, 17 insertions(+), 17 deletions(-) rename ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/{simplscene.cpp => statscene.cpp} (75%) rename include/{simplscene.hpp => statscene.hpp} (73%) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index aec3507..326a5b8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -8,7 +8,7 @@ set(TASK set(SRC ${TASK} src/main.cpp src/utils.cpp - src/scene.cpp src/simplscene.cpp src/dynscene.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp src/textures.cpp src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp src/gobjectlist.cpp src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 38d05d7..82a7e68 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -1,7 +1,7 @@ #include "scene.hpp" #include "constants.hpp" -#include "simplscene.hpp" +#include "statscene.hpp" #include "dynscene.hpp" Scene::Scene() @@ -15,8 +15,8 @@ Scene::Scene() } Scene* Scene::create() { - // static SimpleScene scene; - static DynamicScene scene; + static StaticScene scene; + // static DynamicScene scene; return &scene; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp similarity index 75% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp rename to ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp index 0ce08af..58c8c23 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/simplscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp @@ -1,12 +1,12 @@ -#include "simplscene.hpp" +#include "statscene.hpp" -void SimpleScene::initialize() { +void StaticScene::initialize() { fitInto(player); fitInto(consumable); fitInto(enemy); } -void SimpleScene::processEvent(const sf::Event& event) { +void StaticScene::processEvent(const sf::Event& event) { switch (event.type) { case sf::Event::Closed: close(); @@ -16,7 +16,7 @@ void SimpleScene::processEvent(const sf::Event& event) { } } -void SimpleScene::update(sf::Time delta) { +void StaticScene::update(sf::Time delta) { player.update(delta); consumable.update(delta); enemy.update(delta); @@ -28,7 +28,7 @@ void SimpleScene::update(sf::Time delta) { detectCollision(enemy, consumable); } -void SimpleScene::draw() { +void StaticScene::draw() { Scene::draw(player); Scene::draw(consumable); Scene::draw(enemy); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 28ea0ad..8fb2c6d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -66,17 +66,17 @@ files: visible: true - name: src/textures.cpp visible: true -- name: src/scene.cpp - visible: true - name: src/main.cpp visible: true - name: src/consumable.cpp visible: true -- name: src/simplscene.cpp - visible: true - name: src/utils.cpp visible: true - name: src/gobjectlist.cpp visible: true - name: src/dynscene.cpp visible: true +- name: src/statscene.cpp + visible: true +- name: src/scene.cpp + visible: true diff --git a/include/simplscene.hpp b/include/statscene.hpp similarity index 73% rename from include/simplscene.hpp rename to include/statscene.hpp index f8f6b60..e0224e9 100644 --- a/include/simplscene.hpp +++ b/include/statscene.hpp @@ -1,5 +1,5 @@ -#ifndef CPPBASICS_SIMPLSCENE_HPP -#define CPPBASICS_SIMPLSCENE_HPP +#ifndef CPPBASICS_STATSCENE_HPP +#define CPPBASICS_STATSCENE_HPP #include @@ -8,7 +8,7 @@ #include "consumable.hpp" #include "enemy.hpp" -class SimpleScene : public Scene { +class StaticScene : public Scene { public: void initialize() override; @@ -25,4 +25,4 @@ class SimpleScene : public Scene { EnemyObject enemy; }; -#endif // CPPBASICS_SIMPLSCENE_HPP +#endif // CPPBASICS_STATSCENE_HPP From c7844443690e17250677942649c35908590a6941 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Thu, 24 Aug 2023 20:09:31 +0200 Subject: [PATCH 020/137] add copy-and-swap idiom to GameObjectList Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/scene.cpp | 4 +-- include/gobjectlist.hpp | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 82a7e68..79c4b15 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -15,8 +15,8 @@ Scene::Scene() } Scene* Scene::create() { - static StaticScene scene; - // static DynamicScene scene; + // static StaticScene scene; + static DynamicScene scene; return &scene; } diff --git a/include/gobjectlist.hpp b/include/gobjectlist.hpp index 6b5dc84..4a44699 100644 --- a/include/gobjectlist.hpp +++ b/include/gobjectlist.hpp @@ -16,6 +16,32 @@ class GameObjectList { tail->prev = head.get(); } + GameObjectList(const GameObjectList& other) : GameObjectList() { + Node* cursor = head.get(); + Node* curr = other.head->next.get(); + while (curr != other.tail) { + link(cursor, std::make_unique()); + cursor = cursor->next.get(); + cursor->object = curr->object; + curr = curr->next.get(); + } + } + + GameObjectList(GameObjectList&& other) noexcept : GameObjectList() { + swap(*this, other); + } + + GameObjectList& operator=(GameObjectList other) { + swap(*this, other); + return *this; + } + + friend void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + swap(first.head, second.head); + swap(first.tail, second.tail); + } + void insert(const std::shared_ptr& object) { if (!object) { return; From c0a3ed11cd3e4d70da1ad435d85a32e9bcf9c52a Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 13:56:57 +0200 Subject: [PATCH 021/137] rename GameObjectState::DEAD intro GameObjectState::DESTROYED Signed-off-by: Evgenii Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/consumable.cpp | 6 +++--- .../ClassesAndObjects/IntroducingObjects/src/dynscene.cpp | 2 +- .../ClassesAndObjects/IntroducingObjects/src/enemy.cpp | 2 +- .../ClassesAndObjects/IntroducingObjects/src/player.cpp | 6 +++--- include/gobject.hpp | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index ef5f1fd..03e2307 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -26,17 +26,17 @@ GameObjectKind ConsumableObject::getKind() const { } void ConsumableObject::update(sf::Time delta) { - if (CircleGameObject::getState() != GameObjectState::DEAD) { + if (CircleGameObject::getState() != GameObjectState::DESTROYED) { CircleGameObject::setState(GameObjectState::NORMAL); } } void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { - if (CircleGameObject::getState() == GameObjectState::DEAD) { + if (CircleGameObject::getState() == GameObjectState::DESTROYED) { return; } if (collisionData.collide) { - CircleGameObject::setState(GameObjectState::DEAD); + CircleGameObject::setState(GameObjectState::DESTROYED); return; } if (CircleGameObject::getState() == GameObjectState::CONCERNED) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index eefa7d5..b303842 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -44,7 +44,7 @@ void DynamicScene::update(sf::Time delta) { void DynamicScene::updateObjectsList() { objects.remove([] (const GameObject& object) { - return (object.getState() == GameObjectState::DEAD) + return (object.getState() == GameObjectState::DESTROYED) && (object.getKind() != GameObjectKind::PLAYER); }); int consumableCount = 0; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 15a9693..04b2811 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -20,7 +20,7 @@ const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const } void EnemyObject::update(sf::Time delta) { - if (CircleGameObject::getState() == GameObjectState::DEAD) + if (CircleGameObject::getState() == GameObjectState::DESTROYED) return; updateTimer += delta; if (updateTimer < sf::seconds(1.0f)) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index 5b044fe..c88b4e8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -9,7 +9,7 @@ PlayerObject::PlayerObject() Point2D PlayerObject::getVelocity() const { Point2D velocity = { 0.0f, 0.0f }; - if (CircleGameObject::getState() == GameObjectState::DEAD) + if (CircleGameObject::getState() == GameObjectState::DESTROYED) return velocity; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { velocity = velocity + getDirection(North); @@ -35,7 +35,7 @@ const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) cons switch (CircleGameObject::getState()) { case GameObjectState::NORMAL: return textureManager.getTexture(GameTextureID::PLANET); - case GameObjectState::DEAD: + case GameObjectState::DESTROYED: return textureManager.getTexture(GameTextureID::PLANET_DEAD); default: return nullptr; @@ -48,7 +48,7 @@ void PlayerObject::update(sf::Time delta) { void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { - CircleGameObject::setState(GameObjectState::DEAD); + CircleGameObject::setState(GameObjectState::DESTROYED); } return; } \ No newline at end of file diff --git a/include/gobject.hpp b/include/gobject.hpp index d69b117..ad0daef 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -10,7 +10,7 @@ // TODO: use enum class (?) enum class GameObjectState { - NORMAL, CONCERNED, DEAD, + NORMAL, CONCERNED, DESTROYED, }; enum class GameObjectKind { From c98fe213c3a1052e963bff8b7e74e716365b6575 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 14:16:25 +0200 Subject: [PATCH 022/137] minor rewrite in OperatorsOverloading task description Signed-off-by: Evgenii Moiseenko --- .../ClassesAndObjects/OperatorsOverloading/task.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md index e3abb1c..7fcd1f9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md @@ -26,9 +26,10 @@ Point2D move(Point2D position, Point2D velocity, float delta) { } ``` -As you can see, it is much easier to grasp the meaning of the first code. -Fortunately, with operator overloading, it is possible to make the second -code fragment look just like the first one! +As you may notice, the meaning of the latter code fragment is less evident +and harder to grasp at the first sight. +Fortunately, with operator overloading, it is possible to make +the two versions of the function look identical! ```c++ Point2D move(Point2D position, Point2D velocity, float delta) { @@ -36,7 +37,7 @@ Point2D move(Point2D position, Point2D velocity, float delta) { } ``` -In fact, to enable this syntax, it is enough just to define +To enable this syntax, it is sufficient to define a special function with `operator` prefix in its name: ```c++ From 3ef829577c1f598cc912a67b1937bd68cfb09749 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 15:02:57 +0200 Subject: [PATCH 023/137] add documentation for GameObject class Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/cgobject.cpp | 2 +- .../IntroducingObjects/src/scene.cpp | 2 +- include/cgobject.hpp | 2 +- include/gobject.hpp | 90 +++++++++++++++---- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index ec216d9..05a2f5d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -15,7 +15,7 @@ void CircleGameObject::setPosition(Point2D position) { circle.center = position; } -Rectangle CircleGameObject::boundingBox() const { +Rectangle CircleGameObject::getBoundingBox() const { Point2D offset = { circle.radius, circle.radius }; Point2D p1 = circle.center - offset; Point2D p2 = circle.center + offset; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 79c4b15..8ad3b3a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -72,7 +72,7 @@ void Scene::move(GameObject &object, Point2D vector) { } void Scene::fitInto(GameObject &object) { - Rectangle rect = ::fitInto(object.boundingBox(), boundingBox()); + Rectangle rect = ::fitInto(object.getBoundingBox(), boundingBox()); object.setPosition(center(rect)); } diff --git a/include/cgobject.hpp b/include/cgobject.hpp index 4774afe..d8e06a5 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -14,7 +14,7 @@ class CircleGameObject : public GameObject { void setPosition(Point2D position) override; - Rectangle boundingBox() const override; + Rectangle getBoundingBox() const override; Circle getCircle() const; diff --git a/include/gobject.hpp b/include/gobject.hpp index ad0daef..0f77bae 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -7,47 +7,103 @@ #include "rectangle.hpp" #include "collision.hpp" #include "textures.hpp" +#include "enums.hpp" -// TODO: use enum class (?) -enum class GameObjectState { - NORMAL, CONCERNED, DESTROYED, -}; - -enum class GameObjectKind { - PLAYER, CONSUMABLE, ENEMY -}; +/** + * GameObject is a base class for game objects in a game engine. + * It defines functions for getting and setting object properties, + * updating object state, and drawing the object on a screen. + */ class GameObject { public: - void move(Point2D vector); - + /** + * Returns the current position of the object. + */ virtual Point2D getPosition() const = 0; + /** + * Changes the current position of the object. + */ virtual void setPosition(Point2D position) = 0; - virtual Point2D getVelocity() const = 0; - - virtual Rectangle boundingBox() const = 0; - + /** + * Returns the current state of the object. + */ virtual GameObjectState getState() const = 0; + /** + * Changes the current state of the object. + */ virtual void setState(GameObjectState newState) = 0; + /** + * Returns the kind of the object. + */ virtual GameObjectKind getKind() const = 0; - virtual const sf::Texture* getTexture(TextureManager& textureManager) const = 0; + /** + * Returns the current velocity of the object. + */ + virtual Point2D getVelocity() const = 0; + + /** + * Returns the bounding box of the object. + * A bounding box is the smallest rectangle that entirely encompasses an object. + */ + virtual Rectangle getBoundingBox() const = 0; + /** + * Moves an object in a direction given by the passed vector. + */ + void move(Point2D vector); + + /** + * Updates the object based on the elapsed time. + * + * @param delta The time elapsed since last update. + */ virtual void update(sf::Time delta) = 0; + /** + * Handler function called when a collision occurs between this object and another object. + * The derived class implementing this function will define the behavior for handling the collision. + * + * @param object The collided object. + * @param collisionData The information about the collision. + */ + virtual void onCollision(const GameObject& object, const CollisionInfo& collisionData) = 0; + + /** + * This function is used to render the object on a SFML window passed as an argument. + * + * @param window The window on which the object should be drawn. + * @param textureManager The texture manager object used to retrieve the required textures for drawing. + */ virtual void draw(sf::RenderWindow& window, TextureManager& textureManager) const = 0; - virtual void onCollision(const GameObject& object, const CollisionInfo& collisionData) = 0; + /** + * Retrieves the texture associated with the object. + * + * @param textureManager The texture manager object used to retrieve the texture. + * @return A constant pointer to the texture. If the object should not be drawn in the current state, + * the pointer should be null. + */ + virtual const sf::Texture* getTexture(TextureManager& textureManager) const = 0; - virtual ~GameObject() {} + /** + * Destructor of a game object. + */ + virtual ~GameObject() = default; }; + +/** + * This function takes in two game objects as parameters and checks whether they are colliding. + * It returns a CollisionInfo structure that contains information about the collision. + */ CollisionInfo collision(const GameObject& object1, const GameObject& object2); #endif // CPPBASICS_GOBJECT_HPP From 6e6df9604dc356e4deef8afd7bf1546c6eed35e1 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 16:43:59 +0200 Subject: [PATCH 024/137] add text of OOP task1 Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task1.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md new file mode 100644 index 0000000..01fb0d7 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md @@ -0,0 +1,58 @@ +Let us finally meet with the object-oriented programming paradigm. + +At the core of this paradigm lies the concept of the _object_. +An object groups together some data, called object's _state_, +with a set of functions operating on this data. +These functions are also called the _methods_ of the object. +In this way, objects are similar to structures, but unlike plain structures, +they also allow to add functions into their definition. + +Objects are grouped into _classes_. +Class defines a blueprint after which the objects are created. +In other words, a _class_ is just a type of objects. + +For example, let us consider the `GameObject` class. +The objects of this class represent entities appearing on the game scene, +such as the planet objects controlled by the player, +consumable star objects, and others that we will add later. +Instances of this class will store data related to a game object, +such as its position on the scene, and some functions to manipulate an object. + +Let us have a look at the `GameObject` class definition. +First note that in C++ new class is defined with the help of the `class` keyword. +Next comes the keyword `public` --- we will describe its meaning in the later steps. +After that come the methods of the class. +There are plenty of them, you can get the meaning of each method +by consulting its _documentation_ given as a docstring comment in front of the method declaration. + +The `GameObject` class itself does not define any data fields, only the methods. +It, however, implicitly defines a bunch of _properties_ of an object, for example, its position. +A value of a property can be requested using its _getter_ method (e.g. `getPosition`), +and it can be changed using its _setter_ method (e.g. `setPosition`). +Note that some properties of an object have both _getter_ and _setter_ methods, +like aforementioned `getPosition` and `setPosition` methods, +while others have only _getter_, for example `getVelocity`. +This is for a reason — some properties are derivatives of the current objects' state, +and they cannot be directly changed from the outside. + +Another piece of unfamiliar syntax here is the `const` keyword coming after the arguments of a methods. +It denotes the _constant methods_ --- these methods cannot change the state of the object. + +Finally, keyword `virtual` denotes the _virtual_ methods — these are the methods +that can be _overridden_ by the inheritors of the class +(we will delve back to inheritance in the next task). +The `= 0` syntax at the end of the virtual method indicates that this +method is not implemented for the given class — it is just a stub for an actual method implementation. + +The classes that do declare any data fields and contain unimplemented `virtual` methods are also called _interfaces_. +In some sense, interfaces just provide a description of the objects' behavior, +without actually specifying their internal state. +These leaves a programmer an opportunity to define several _subclasses_ +of an interface that provide different implementations of its behavior. +For example, in the following steps of this lesson, you will have to implement +a subclass of playable objects, consumable objects, and others. + +But before moving to the following step, let us complete small programming exercise. +Note that one of the methods of the `GameObject` class --- the `move` method --- is actually not `virtual`. +It is because it can be implemented in terms of other methods of this class, namely `getPosition` and `setPosition`. +To finish the programming assignment, please provide an implementation of this method. \ No newline at end of file From 89d0dfefade61faf80ea2e29f32b04865e8b4316 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 16:44:33 +0200 Subject: [PATCH 025/137] add missing file Signed-off-by: Evgenii Moiseenko --- include/enums.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 include/enums.hpp diff --git a/include/enums.hpp b/include/enums.hpp new file mode 100644 index 0000000..a654c4a --- /dev/null +++ b/include/enums.hpp @@ -0,0 +1,12 @@ +#ifndef CPPBASICS_ENUMS_HPP +#define CPPBASICS_ENUMS_HPP + +enum class GameObjectState { + NORMAL, CONCERNED, DESTROYED, +}; + +enum class GameObjectKind { + PLAYER, CONSUMABLE, ENEMY +}; + +#endif // CPPBASICS_ENUMS_HPP From 98330a14116828ca55f3ac36e844613d378c15aa Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 17:06:13 +0200 Subject: [PATCH 026/137] rename GameObjectState into GameObjectStatus to avoid collision with object state concept Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/cgobject.cpp | 6 +++--- .../IntroducingObjects/src/consumable.cpp | 18 +++++++++--------- .../IntroducingObjects/src/dynscene.cpp | 2 +- .../IntroducingObjects/src/enemy.cpp | 2 +- .../IntroducingObjects/src/player.cpp | 10 +++++----- include/cgobject.hpp | 6 +++--- include/enums.hpp | 2 +- include/gobject.hpp | 6 +++--- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index 05a2f5d..c78f296 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -4,7 +4,7 @@ CircleGameObject::CircleGameObject(Circle circle) : circle(circle) - , state(GameObjectState::NORMAL) + , state(GameObjectStatus::NORMAL) {} Point2D CircleGameObject::getPosition() const { @@ -26,11 +26,11 @@ Circle CircleGameObject::getCircle() const { return circle; } -GameObjectState CircleGameObject::getState() const { +GameObjectStatus CircleGameObject::getStatus() const { return state; } -void CircleGameObject::setState(GameObjectState newState) { +void CircleGameObject::setStatus(GameObjectStatus newState) { state = newState; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index 03e2307..fc95ec3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -11,10 +11,10 @@ Point2D ConsumableObject::getVelocity() const { } const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { - switch (CircleGameObject::getState()) { - case GameObjectState::NORMAL: + switch (CircleGameObject::getStatus()) { + case GameObjectStatus::NORMAL: return textureManager.getTexture(GameTextureID::STAR); - case GameObjectState::CONCERNED: + case GameObjectStatus::CONCERNED: return textureManager.getTexture(GameTextureID::STAR_CONCERNED); default: return nullptr; @@ -26,24 +26,24 @@ GameObjectKind ConsumableObject::getKind() const { } void ConsumableObject::update(sf::Time delta) { - if (CircleGameObject::getState() != GameObjectState::DESTROYED) { - CircleGameObject::setState(GameObjectState::NORMAL); + if (CircleGameObject::getStatus() != GameObjectStatus::DESTROYED) { + CircleGameObject::setStatus(GameObjectStatus::NORMAL); } } void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { - if (CircleGameObject::getState() == GameObjectState::DESTROYED) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) { return; } if (collisionData.collide) { - CircleGameObject::setState(GameObjectState::DESTROYED); + CircleGameObject::setStatus(GameObjectStatus::DESTROYED); return; } - if (CircleGameObject::getState() == GameObjectState::CONCERNED) { + if (CircleGameObject::getStatus() == GameObjectStatus::CONCERNED) { return; } if (collisionData.distance < 6 * getCircle().radius) { - CircleGameObject::setState(GameObjectState::CONCERNED); + CircleGameObject::setStatus(GameObjectStatus::CONCERNED); return; } } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index b303842..aab6cd7 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -44,7 +44,7 @@ void DynamicScene::update(sf::Time delta) { void DynamicScene::updateObjectsList() { objects.remove([] (const GameObject& object) { - return (object.getState() == GameObjectState::DESTROYED) + return (object.getStatus() == GameObjectStatus::DESTROYED) && (object.getKind() != GameObjectKind::PLAYER); }); int consumableCount = 0; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 04b2811..39b80d0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -20,7 +20,7 @@ const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const } void EnemyObject::update(sf::Time delta) { - if (CircleGameObject::getState() == GameObjectState::DESTROYED) + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) return; updateTimer += delta; if (updateTimer < sf::seconds(1.0f)) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index c88b4e8..af37891 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -9,7 +9,7 @@ PlayerObject::PlayerObject() Point2D PlayerObject::getVelocity() const { Point2D velocity = { 0.0f, 0.0f }; - if (CircleGameObject::getState() == GameObjectState::DESTROYED) + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) return velocity; if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { velocity = velocity + getDirection(North); @@ -32,10 +32,10 @@ GameObjectKind PlayerObject::getKind() const { } const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { - switch (CircleGameObject::getState()) { - case GameObjectState::NORMAL: + switch (CircleGameObject::getStatus()) { + case GameObjectStatus::NORMAL: return textureManager.getTexture(GameTextureID::PLANET); - case GameObjectState::DESTROYED: + case GameObjectStatus::DESTROYED: return textureManager.getTexture(GameTextureID::PLANET_DEAD); default: return nullptr; @@ -48,7 +48,7 @@ void PlayerObject::update(sf::Time delta) { void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { - CircleGameObject::setState(GameObjectState::DESTROYED); + CircleGameObject::setStatus(GameObjectStatus::DESTROYED); } return; } \ No newline at end of file diff --git a/include/cgobject.hpp b/include/cgobject.hpp index d8e06a5..74bf187 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -18,15 +18,15 @@ class CircleGameObject : public GameObject { Circle getCircle() const; - GameObjectState getState() const override; + GameObjectStatus getStatus() const override; - void setState(GameObjectState newState) override; + void setStatus(GameObjectStatus newState) override; void draw(sf::RenderWindow &window, TextureManager& textureManager) const override; private: Circle circle; - GameObjectState state; + GameObjectStatus state; }; #endif // CPPBASICS_CGOBJECT_HPP diff --git a/include/enums.hpp b/include/enums.hpp index a654c4a..ac948b0 100644 --- a/include/enums.hpp +++ b/include/enums.hpp @@ -1,7 +1,7 @@ #ifndef CPPBASICS_ENUMS_HPP #define CPPBASICS_ENUMS_HPP -enum class GameObjectState { +enum class GameObjectStatus { NORMAL, CONCERNED, DESTROYED, }; diff --git a/include/gobject.hpp b/include/gobject.hpp index 0f77bae..f932e14 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -31,12 +31,12 @@ class GameObject { /** * Returns the current state of the object. */ - virtual GameObjectState getState() const = 0; + virtual GameObjectStatus getStatus() const = 0; /** * Changes the current state of the object. */ - virtual void setState(GameObjectState newState) = 0; + virtual void setStatus(GameObjectStatus status) = 0; /** * Returns the kind of the object. @@ -60,7 +60,7 @@ class GameObject { void move(Point2D vector); /** - * Updates the object based on the elapsed time. + * Updates the state of an object based on the elapsed time. * * @param delta The time elapsed since last update. */ From 2193907f2397cea54595990ce4def1d377419b37 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 18:53:57 +0200 Subject: [PATCH 027/137] dd text of OOP task2 Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task1.md | 38 +++++--- .../IntroducingObjects/task2.md | 86 +++++++++++++++++++ 2 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md index 01fb0d7..1b5c0c4 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md @@ -25,34 +25,48 @@ After that come the methods of the class. There are plenty of them, you can get the meaning of each method by consulting its _documentation_ given as a docstring comment in front of the method declaration. +[//]: # (TODO: add links to docstring format) + The `GameObject` class itself does not define any data fields, only the methods. It, however, implicitly defines a bunch of _properties_ of an object, for example, its position. A value of a property can be requested using its _getter_ method (e.g. `getPosition`), -and it can be changed using its _setter_ method (e.g. `setPosition`). +and it can be changed using its _setter_ method (e.g. `setObjectPosition`). Note that some properties of an object have both _getter_ and _setter_ methods, -like aforementioned `getPosition` and `setPosition` methods, +like aforementioned `getPosition` and `setObjectPosition` methods, while others have only _getter_, for example `getVelocity`. -This is for a reason — some properties are derivatives of the current objects' state, +This is for a reason — some properties are derivatives of the current objects' status, and they cannot be directly changed from the outside. Another piece of unfamiliar syntax here is the `const` keyword coming after the arguments of a methods. -It denotes the _constant methods_ --- these methods cannot change the state of the object. +It denotes the _constant methods_ --- these methods cannot change the status of the object. Finally, keyword `virtual` denotes the _virtual_ methods — these are the methods that can be _overridden_ by the inheritors of the class (we will delve back to inheritance in the next task). -The `= 0` syntax at the end of the virtual method indicates that this -method is not implemented for the given class — it is just a stub for an actual method implementation. +The `= 0` syntax at the end of the virtual method indicates that +it is a _pure virtual_ method. +Such method is not implemented for the given class — +it is just a stub for an actual method implementation. -The classes that do declare any data fields and contain unimplemented `virtual` methods are also called _interfaces_. +The classes that do declare any data fields and contain pure virtual methods are also called _interfaces_. In some sense, interfaces just provide a description of the objects' behavior, -without actually specifying their internal state. +without actually specifying their internal status. These leaves a programmer an opportunity to define several _subclasses_ of an interface that provide different implementations of its behavior. For example, in the following steps of this lesson, you will have to implement a subclass of playable objects, consumable objects, and others. -But before moving to the following step, let us complete small programming exercise. -Note that one of the methods of the `GameObject` class --- the `move` method --- is actually not `virtual`. -It is because it can be implemented in terms of other methods of this class, namely `getPosition` and `setPosition`. -To finish the programming assignment, please provide an implementation of this method. \ No newline at end of file +But before moving to the following step, please complete a small programming exercise. +One of the methods of the `GameObject` class --- the `move` method --- is actually not `virtual`. +It is because it can be implemented in terms of other methods of this class, namely `getPosition` and `setObjectPosition`. +To finish the programming assignment, please provide an implementation of this method. + +The implementation of the method should be put into `gobject.cpp` file. +Note that methods' definition given in this file contains both +name of the class, and the name of the method, separated by the `::`. + +```c++ +void GameObject::move(Point2D vector) { + ... +} +``` diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md new file mode 100644 index 0000000..c0be354 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md @@ -0,0 +1,86 @@ +As we have mentioned, `GameObject` class on itself +does not specify the status of game objects — it just describes their behavior. +We need another class that extends `GameObject` with an actual status. + +Fortunately, object-oriented programming has a suitable concept for this job — +it is called class _inheritance_. +Inheritance mechanism allows extending an existing class +and providing concrete implementations for its virtual functions. +A _derived_ class, also called a _subclass_, +inherits all the method and fields of the _base_ class, +and can add its own new methods and fields. + +Giving back to our problem, let us define +a `CircleGameObject` subclass of `GameObject` class. +Instances of `CircleGameObject` class represent +game objects of circular shape — like the planet object controlled by the player. + +Have a look at the `CircleGameObject` class definition. +The semicolon syntax: + +``` +class CircleGameObject : public GameObject +``` + +indicates that `CircleGameObject` is a subclass of `GameObject`. + +For a time being let us again ignore the `public` and `private` keywords +used in the `CircleGameObject` class. + +Instead, let us note that `CircleGameObject` declares not only methods, but also two fields: +* `circle` field stores its shape data; +* `status` field stores its current status. + +[//]: # (Note that `CircleGameObject` has two sections: ) +[//]: # (`public` containing its various methods, and `private` containing its data fields.) +[//]: # (All the fields and methods declared inside `public` section are visible ) +[//]: # (and can be used freely outside the class.) +[//]: # (However, all the fields and methods declared inside `private` section ) +[//]: # (can only be used from within the class itself --- they are invisible outside.) + +The very first method of the `CircleGameObject` is a special method called the _constructor_. +Constructor methods have the same name as the class itself, +and it takes single argument `circle`: `CircleGameObject(Circle circle)`. +The constructor is called to create an instance of an object and initialize its state. +For now, you can omit the `explicit` keyword put at the constructor --- we will get back to it later. + +[//]: # (TODO: explain explicit constructors) + +The definition of the `CircleGameObject` constructor body contains some new interesting syntax: + +```c++ +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} +``` + +After the arguments' list comes the semicolon `:`, followed by the list of the object's fields. +The value, provided in the brackets next to the field's name, is used to initialize the corresponding field. +Please note that the order of the fields in the _constructor initializer list_ is important, +it should match the order in which the fields are declared in the class. +After the constructor initializer list comes the constructor body `{}` (empty in this case). +Similarly, as regular methods, it can contain arbitrary C++ statements. + +A constructor has its counterpart — the _destructor_ method, +which should have the same name as a class prefixed with `~`. +It is a method called automatically before destruction of the object +in order to perform some clean-up routines. +A class can have several constructors taking different arguments, +but there could only one destructor taking no arguments. + +In fact, you may have already seen the destructor on the previous step: +a class `GameObject` has a virtual destructor `~GameObject()`. +The `= default` syntax at the end of its definition indicates that +this destructor has default auto-generated implementation. + +As we will see later in the course, constructors and destructor have a pivotal role in C++. + +Going back to the `CircleGameObject` class, consider its methods. +Some of them, like `getPosition` and `setObjectPosition`, are just re-declared methods of the `GameObject` class. +The keyword `override` at the end of the methods' declarations indicates this fact. + +However, unlike the `GameObject` class, the `CircleGameObject` class actually defines the behavior of these methods. +To be precise, it is your task to implement some of them, +namely `getPosition`, `setObjectPosition`, `getStatus`, `setStatus`, and `getCircle`. +Keep in mind that the position of the `CircleGameObject` is a position of its circle's center. \ No newline at end of file From f615a41fdd0f35d4ddf30161064ff4e818b8d1fc Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 18:54:37 +0200 Subject: [PATCH 028/137] minor changes in the task Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/cgobject.cpp | 24 +++++++------- .../IntroducingObjects/src/consumable.cpp | 32 +++++++++---------- .../IntroducingObjects/src/dynscene.cpp | 2 +- .../IntroducingObjects/src/enemy.cpp | 14 ++++---- .../IntroducingObjects/src/player.cpp | 28 ++++++++-------- .../IntroducingObjects/src/scene.cpp | 2 +- include/cgobject.hpp | 12 +++---- include/gobject.hpp | 12 +++---- include/scene.hpp | 22 ++++++------- 9 files changed, 74 insertions(+), 74 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index c78f296..1d49ba3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -4,7 +4,7 @@ CircleGameObject::CircleGameObject(Circle circle) : circle(circle) - , state(GameObjectStatus::NORMAL) + , status(GameObjectStatus::NORMAL) {} Point2D CircleGameObject::getPosition() const { @@ -15,23 +15,23 @@ void CircleGameObject::setPosition(Point2D position) { circle.center = position; } -Rectangle CircleGameObject::getBoundingBox() const { - Point2D offset = { circle.radius, circle.radius }; - Point2D p1 = circle.center - offset; - Point2D p2 = circle.center + offset; - return createRectangle(p1, p2); +GameObjectStatus CircleGameObject::getStatus() const { + return status; } -Circle CircleGameObject::getCircle() const { - return circle; +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; } -GameObjectStatus CircleGameObject::getStatus() const { - return state; +Circle CircleGameObject::getCircle() const { + return circle; } -void CircleGameObject::setStatus(GameObjectStatus newState) { - state = newState; +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); } void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index fc95ec3..8738992 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -6,25 +6,14 @@ ConsumableObject::ConsumableObject() : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) {} -Point2D ConsumableObject::getVelocity() const { - return { 0.0f, 0.0f }; -} - -const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { - switch (CircleGameObject::getStatus()) { - case GameObjectStatus::NORMAL: - return textureManager.getTexture(GameTextureID::STAR); - case GameObjectStatus::CONCERNED: - return textureManager.getTexture(GameTextureID::STAR_CONCERNED); - default: - return nullptr; - } -} - GameObjectKind ConsumableObject::getKind() const { return GameObjectKind::CONSUMABLE; } +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + void ConsumableObject::update(sf::Time delta) { if (CircleGameObject::getStatus() != GameObjectStatus::DESTROYED) { CircleGameObject::setStatus(GameObjectStatus::NORMAL); @@ -46,4 +35,15 @@ void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo CircleGameObject::setStatus(GameObjectStatus::CONCERNED); return; } -} \ No newline at end of file +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::CONCERNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index aab6cd7..4ec3d56 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -96,7 +96,7 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) // set random position for consumable and enemy objects if (kind == GameObjectKind::CONSUMABLE || kind == GameObjectKind::ENEMY) { - setPosition(*object, generatePoint(boundingBox())); + setObjectPosition(*object, generatePoint(boundingBox())); } else { fitInto(*object); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 39b80d0..3e8f928 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -7,16 +7,12 @@ EnemyObject::EnemyObject() : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) {} -Point2D EnemyObject::getVelocity() const { - return velocity; -} - GameObjectKind EnemyObject::getKind() const { return GameObjectKind::ENEMY; } -const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { - return textureManager.getTexture(GameTextureID::BLACKHOLE); +Point2D EnemyObject::getVelocity() const { + return velocity; } void EnemyObject::update(sf::Time delta) { @@ -34,4 +30,8 @@ void EnemyObject::update(sf::Time delta) { void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { return; -} \ No newline at end of file +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + return textureManager.getTexture(GameTextureID::BLACKHOLE); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index af37891..e7b8eae 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -7,6 +7,10 @@ PlayerObject::PlayerObject() : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) {} +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + Point2D PlayerObject::getVelocity() const { Point2D velocity = { 0.0f, 0.0f }; if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) @@ -27,12 +31,19 @@ Point2D PlayerObject::getVelocity() const { return velocity; } -GameObjectKind PlayerObject::getKind() const { - return GameObjectKind::PLAYER; +void PlayerObject::update(sf::Time delta) { + return; +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { + if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { + CircleGameObject::setStatus(GameObjectStatus::DESTROYED); + } + return; } const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { - switch (CircleGameObject::getStatus()) { + switch (getStatus()) { case GameObjectStatus::NORMAL: return textureManager.getTexture(GameTextureID::PLANET); case GameObjectStatus::DESTROYED: @@ -40,15 +51,4 @@ const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) cons default: return nullptr; } -} - -void PlayerObject::update(sf::Time delta) { - return; -} - -void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { - if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { - CircleGameObject::setStatus(GameObjectStatus::DESTROYED); - } - return; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 8ad3b3a..1751d63 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -57,7 +57,7 @@ Rectangle Scene::boundingBox() const { return box; } -void Scene::setPosition(GameObject& object, Point2D position) { +void Scene::setObjectPosition(GameObject& object, Point2D position) { object.setPosition(position); fitInto(object); } diff --git a/include/cgobject.hpp b/include/cgobject.hpp index 74bf187..a88701f 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -8,25 +8,25 @@ class CircleGameObject : public GameObject { public: - CircleGameObject(Circle circle); + explicit CircleGameObject(Circle circle); Point2D getPosition() const override; void setPosition(Point2D position) override; - Rectangle getBoundingBox() const override; + GameObjectStatus getStatus() const override; - Circle getCircle() const; + void setStatus(GameObjectStatus newStatus) override; - GameObjectStatus getStatus() const override; + Circle getCircle() const; - void setStatus(GameObjectStatus newState) override; + Rectangle getBoundingBox() const override; void draw(sf::RenderWindow &window, TextureManager& textureManager) const override; private: Circle circle; - GameObjectStatus state; + GameObjectStatus status; }; #endif // CPPBASICS_CGOBJECT_HPP diff --git a/include/gobject.hpp b/include/gobject.hpp index f932e14..61cb8f4 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -13,7 +13,7 @@ /** * GameObject is a base class for game objects in a game engine. * It defines functions for getting and setting object properties, - * updating object state, and drawing the object on a screen. + * updating object status, and drawing the object on a screen. */ class GameObject { public: @@ -29,14 +29,14 @@ class GameObject { virtual void setPosition(Point2D position) = 0; /** - * Returns the current state of the object. + * Returns the current status of the object. */ virtual GameObjectStatus getStatus() const = 0; /** - * Changes the current state of the object. + * Changes the current status of the object. */ - virtual void setStatus(GameObjectStatus status) = 0; + virtual void setStatus(GameObjectStatus newStatus) = 0; /** * Returns the kind of the object. @@ -60,7 +60,7 @@ class GameObject { void move(Point2D vector); /** - * Updates the state of an object based on the elapsed time. + * Updates the status of an object based on the elapsed time. * * @param delta The time elapsed since last update. */ @@ -87,7 +87,7 @@ class GameObject { * Retrieves the texture associated with the object. * * @param textureManager The texture manager object used to retrieve the texture. - * @return A constant pointer to the texture. If the object should not be drawn in the current state, + * @return A constant pointer to the texture. If the object should not be drawn in the current status, * the pointer should be null. */ virtual const sf::Texture* getTexture(TextureManager& textureManager) const = 0; diff --git a/include/scene.hpp b/include/scene.hpp index 286f2ed..fcafff5 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -22,34 +22,34 @@ class Scene { virtual void initialize() = 0; + void close(); + void processInput(); virtual void processEvent(const sf::Event& event) = 0; - virtual void update(sf::Time delta) = 0; - - void render(); - - virtual void draw() = 0; - - void close(); + void setObjectPosition(GameObject& object, Point2D position); Rectangle boundingBox() const; - void setPosition(GameObject& object, Point2D position); + void fitInto(GameObject& object); void move(GameObject& object, sf::Time delta); void move(GameObject& object, Point2D vector); - void fitInto(GameObject& object); - void detectCollision(GameObject& object1, GameObject& object2); - sf::Sprite background() const; + virtual void update(sf::Time delta) = 0; + + void render(); void draw(const GameObject& object); + virtual void draw() = 0; + + sf::Sprite background() const; + private: sf::RenderWindow window; TextureManager textureManager; From 8644ae58238267887169b868a4c53fd7659767ac Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 19:21:25 +0200 Subject: [PATCH 029/137] add classes documentation Signed-off-by: Evgenii Moiseenko --- include/cgobject.hpp | 30 ++++++++++++++++++++++++++++++ include/consumable.hpp | 33 ++++++++++++++++++++++++++++++--- include/gobject.hpp | 2 +- include/player.hpp | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 91 insertions(+), 7 deletions(-) diff --git a/include/cgobject.hpp b/include/cgobject.hpp index a88701f..a247646 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -6,22 +6,52 @@ #include "gobject.hpp" #include "circle.hpp" +/** + * A class that represents a game object with a circular shape. + */ class CircleGameObject : public GameObject { public: + + /** + * Constructor initializing the object with the provided circle shape. + */ explicit CircleGameObject(Circle circle); + /** + * Returns position of the circle's center. + */ Point2D getPosition() const override; + + /** + * Changes position of the circle's center. + */ void setPosition(Point2D position) override; + + /** + * Returns the current status of the object. + */ GameObjectStatus getStatus() const override; + /** + * Changes the current status of the object. + */ void setStatus(GameObjectStatus newStatus) override; + /** + * Returns the current shape of the object. + */ Circle getCircle() const; + /** + * Returns the bounding box of the circle. + */ Rectangle getBoundingBox() const override; + /** + * Render the object on a window. + */ void draw(sf::RenderWindow &window, TextureManager& textureManager) const override; private: diff --git a/include/consumable.hpp b/include/consumable.hpp index e16e772..17dac25 100644 --- a/include/consumable.hpp +++ b/include/consumable.hpp @@ -3,19 +3,46 @@ #include "cgobject.hpp" +/** + * A class representing a consumable object in the game. + */ class ConsumableObject : public CircleGameObject { public: - ConsumableObject(); - Point2D getVelocity() const override; + /** + * Constructor that initializes consumable's shape and position. + */ + ConsumableObject(); + /** + * Returns the `CONSUMABLE` kind. + */ GameObjectKind getKind() const override; - const sf::Texture* getTexture(TextureManager& textureManager) const override; + /** + * Returns the current velocity of the object. + * Consumable objects are static and thus their velocity is always zero. + */ + Point2D getVelocity() const override; + /** + * Updates the state of an object based on the elapsed time. + * Always resets consumable status back to `NORMAL`, unless it was `DESTROYED`. + */ void update(sf::Time delta) override; + /** + * Handler the collision of consumable object with another object. + * Sets the status to `CONCERNED` if some other object is approaching the consumable. + * Sets the status to `DESTROYED` in case of a collision. + */ void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; + + /** + * Retrieves the texture associated with the consumable object based on its current status. + */ + const sf::Texture* getTexture(TextureManager& textureManager) const override; + }; #endif // CPPBASICS_CONSUMABLE_HPP diff --git a/include/gobject.hpp b/include/gobject.hpp index 61cb8f4..6736233 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -60,7 +60,7 @@ class GameObject { void move(Point2D vector); /** - * Updates the status of an object based on the elapsed time. + * Updates the state of an object based on the elapsed time. * * @param delta The time elapsed since last update. */ diff --git a/include/player.hpp b/include/player.hpp index ec2682d..61b2706 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -3,19 +3,46 @@ #include "cgobject.hpp" + +/** + * The PlayerObject class represents a player in the game. + * It provides additional behavior and properties specific to players. + */ class PlayerObject : public CircleGameObject { public: - PlayerObject(); - Point2D getVelocity() const override; + /** + * Constructor that initializes player's shape and position. + */ + PlayerObject(); + /** + * Returns the `PLAYER` kind. + */ GameObjectKind getKind() const override; - const sf::Texture* getTexture(TextureManager& textureManager) const override; + /** + * Returns the current velocity of the object. + * The object's velocity depends on the control keys pressed by the player at the moment. + */ + Point2D getVelocity() const override; + /** + * Idle for the player object. + */ void update(sf::Time delta) override; + /** + * Handler the collision of player object with another object. + * In particular, if player collided with an enemy object, sets its status to `DESTROYED`. + */ void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; + + /** + * Retrieves the texture associated with the player object based on its current status. + */ + const sf::Texture* getTexture(TextureManager& textureManager) const override; + }; #endif // CPPBASICS_PLAYER_HPP From 4523dd87ba7cf096f2e1560f089578e2184dee8f Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Fri, 8 Sep 2023 20:03:49 +0200 Subject: [PATCH 030/137] add text of OOP task3 Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task3.md | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md new file mode 100644 index 0000000..3094696 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md @@ -0,0 +1,44 @@ +Despite class `CircleGameObject` adds some data fields to the `GameObject` class, +it still leaves some of `GameObject` virtual methods unimplemented. +Thus this class is still an _abstract class_ --- it cannot be instantiated. + +Let us introduce two concrete subclasses of the `CircleGameObject` class --- +the `PlayerObject` class and the `ConsumableObject` classes, +representing object controlled by the player, and consumable objects respectively. +At last, both of these classes implement all the functionality required by the `GameObject` class. + +Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. +There are no new syntactic constructs here, so you should be able to understand the code in these files. + +The implementations of these classes can be found in the files `player.cpp` and `consumable.cpp`. +Note that the full implementation of some methods is already provided. +For example, the `getVelocity` method of the `PlayerObject` computes +the current velocity vector by calling the `SFML` functions +to determine which keys are pressed by the player at the moment. + +Your task is to implement the `getTexture` methods of both classes. +These methods should return the current texture of an object to be displayed, +depending on the current status of the object. +Although, we have not yet implemented the methods that actually update +the status of the objects, implementing the `getTexture` methods first +will give you a good opportunity to practice and learn the method call syntax. + +The `getTexture` methods takes by reference one argument — object of the `TextureManager` class. +It is another predefined by us class --- it is responsible for loading the textures required by the game. +A pointer to a texture can be requested by calling the `getTexture` method of the `TextureManager` class. +It takes as argument the ID of the textures — these IDs are represented by the `GameTextureID` enum. + +Please implement the `getTexture` methods of the `PlayerObject` and `ConsumableObject` +with the following logic: +* under `NORMAL` status, the player object should have `PLANET` texture; +* under `DESTROYED` status, the player object should have `PLANET_DEAD` texture; +* under `NORMAL` status, the consumable object should have `STAR` texture; +* under `CONCERNED` status, the consumable object should have `STAR_CONCERNED` texture; +* under `DESTROYED` status, the consumable object should not be displayed. + +
+ +If you have troubles implementing the last case, +consult the documentation of the `GameObject`'s `getTexture` method. + +
\ No newline at end of file From 086edd5b495991822f4558646f2536a73b4824dd Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 11 Sep 2023 13:36:17 +0200 Subject: [PATCH 031/137] add text of OOP task4 Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task2.md | 7 -- .../IntroducingObjects/task3.md | 21 ++++-- .../IntroducingObjects/task4.md | 71 +++++++++++++++++++ 3 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md index c0be354..97219c3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md @@ -31,13 +31,6 @@ Instead, let us note that `CircleGameObject` declares not only methods, but also * `circle` field stores its shape data; * `status` field stores its current status. -[//]: # (Note that `CircleGameObject` has two sections: ) -[//]: # (`public` containing its various methods, and `private` containing its data fields.) -[//]: # (All the fields and methods declared inside `public` section are visible ) -[//]: # (and can be used freely outside the class.) -[//]: # (However, all the fields and methods declared inside `private` section ) -[//]: # (can only be used from within the class itself --- they are invisible outside.) - The very first method of the `CircleGameObject` is a special method called the _constructor_. Constructor methods have the same name as the class itself, and it takes single argument `circle`: `CircleGameObject(Circle circle)`. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md index 3094696..a463ea5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md @@ -1,10 +1,10 @@ Despite class `CircleGameObject` adds some data fields to the `GameObject` class, it still leaves some of `GameObject` virtual methods unimplemented. -Thus this class is still an _abstract class_ --- it cannot be instantiated. +Therefore, this class is still an _abstract class_ --- it cannot be instantiated. Let us introduce two concrete subclasses of the `CircleGameObject` class --- the `PlayerObject` class and the `ConsumableObject` classes, -representing object controlled by the player, and consumable objects respectively. +representing the object controlled by the player, and consumable objects respectively. At last, both of these classes implement all the functionality required by the `GameObject` class. Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. @@ -24,7 +24,7 @@ the status of the objects, implementing the `getTexture` methods first will give you a good opportunity to practice and learn the method call syntax. The `getTexture` methods takes by reference one argument — object of the `TextureManager` class. -It is another predefined by us class --- it is responsible for loading the textures required by the game. +It is another predefined by us class — it is responsible for loading the textures required by the game. A pointer to a texture can be requested by calling the `getTexture` method of the `TextureManager` class. It takes as argument the ID of the textures — these IDs are represented by the `GameTextureID` enum. @@ -38,7 +38,16 @@ with the following logic:
-If you have troubles implementing the last case, -consult the documentation of the `GameObject`'s `getTexture` method. +If you have troubles implementing the last case, +consult the documentation of the `GameObject`'s `getTexture` method. -
\ No newline at end of file + + +In order to implement this method, you will have to call +the `getTexture` method of the `TextureManager` class. +In order to do that, use the dot syntax `.` — the same syntax as the one used +to access fields of a structure: + +```c++ +const sf::Texture* texture = textureManaged.getTexture(id); +``` \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md new file mode 100644 index 0000000..07a36ac --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md @@ -0,0 +1,71 @@ +Let us now consider another essential class of the game — the `Scene` class. +Game scene object is responsible for handling user input, +keeping the game objects and managing their updates, +drawing the graphics on the window, and other activities. +In essence, it is a working horse of our simple game engine. + +Let us have a look at the declaration of the `Scene` class. +This class is pretty packed — it includes both regular and virtual methods +as well as some data fields. +For the details on the meaning of these class' methods, we refer to their documentation. + +What is more interesting — the `Scene` class groups its members into three sections: +`public`, `protected`, and `private`. +Let us finally decipher their meaning! + +With the help of these _visibility modifiers_, +the class can govern how its clients interact with the objects of this class: + +* all the fields and methods declared inside `public` section + are visible and can be used freely outside the class; +* all the fields and methods declared inside `protected` section + are visible and can be used in the class itself and inside its ancestors; +* all the fields and methods declared inside `private` section + are visible and can be used only in the class itself, and in no other place. + +The publicly available fields and methods of the class are also called its _public interface_ +(not to be confused with the term _interface_ denoting classes containing pure virtual functions and having no state). +The public interface of a class defines how the objects of this class are visible from the outside, +what fields and methods can the clients of the class access. + +You might be wondering what is the point of hiding some of +the class' field or methods — after all, they can be useful outside. +However, the ability to hide some of the object's _implementation details_ +gains the objects an ultimate control over their internal state. + +This principle is known under the name _encapsulation_. +Encapsulation allows the developer of a class to maintain the _invariants_ on object's data, +ensuring that objects of this class always remain in some valid state. + +Let us explain this on the example of the `Scene` class. +Among other things, this class is responsible for keeping on the game objects appearing on the scene. +One useful invariant that `Scene` class may enforce is that all of its game objects are lay within the scene's borders. +But if the `GameObject` class provides a `public` method to change object's position (i.e. `setPosition`), +then the `Scene` object has no means to guarantee this property. +Any other class or function may change the position of an object and put it outside the visible area of the scene. +However, if the only way for a user class to change position of an object is through a call +to a scene's method (for example, its `move` method) — then the implementation of this method may +take some additional actions in order to guarantee that the object remains within the scene. +This way, by controlling the visibility of objects' fields and methods, +the developer of a class may enforce various useful invariants on the state of a program. + +Mastering the invariants of classes and controlling the visibility of their members is +a skill that comes with the experience. The more complex applications you will architect and develop, +the better you will become at designing classes, their invariants. + +To consolidate the material of this step, please +implement the following two methods of the `Scene` class. + +```c++ +void setObjectPosition(GameObject& object, Point2D position); +void move(GameObject& object, Point2D vector); +``` + +You need to guarantee the invariant of the `Scene` class we discussed above — +the objects of the scene should remain within its borders. + +In order to implement these methods, you have to use +corresponding methods of the `GameObject` class, +as well as another method the `Scene` class — the `fitInto` method. +This method adjusts the position of an object to fit into the `Scene` borders. + From f57d47bff6b446dca58fb100fa6a3df6378e425d Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 11 Sep 2023 13:37:31 +0200 Subject: [PATCH 032/137] minor updates in framework lesson code Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/consumable.cpp | 3 --- .../IntroducingObjects/src/dynscene.cpp | 2 +- .../IntroducingObjects/src/player.cpp | 1 - .../IntroducingObjects/src/scene.cpp | 24 +++++++++---------- include/scene.hpp | 10 ++++---- include/textures.hpp | 1 - 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index 8738992..c82507f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -28,9 +28,6 @@ void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo CircleGameObject::setStatus(GameObjectStatus::DESTROYED); return; } - if (CircleGameObject::getStatus() == GameObjectStatus::CONCERNED) { - return; - } if (collisionData.distance < 6 * getCircle().radius) { CircleGameObject::setStatus(GameObjectStatus::CONCERNED); return; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index 4ec3d56..80056b5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -96,7 +96,7 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) // set random position for consumable and enemy objects if (kind == GameObjectKind::CONSUMABLE || kind == GameObjectKind::ENEMY) { - setObjectPosition(*object, generatePoint(boundingBox())); + setObjectPosition(*object, generatePoint(getBoundingBox())); } else { fitInto(*object); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index e7b8eae..acdbb04 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -39,7 +39,6 @@ void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &co if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { CircleGameObject::setStatus(GameObjectStatus::DESTROYED); } - return; } const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 1751d63..332e7a4 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -50,32 +50,32 @@ void Scene::close() { window.close(); } -Rectangle Scene::boundingBox() const { - Rectangle box; - box.topLeft = { 0.0f, 0.0f }; - box.botRight = { width, height }; - return box; -} - void Scene::setObjectPosition(GameObject& object, Point2D position) { object.setPosition(position); fitInto(object); } -void Scene::move(GameObject& object, sf::Time delta) { - move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); -} - void Scene::move(GameObject &object, Point2D vector) { object.move(vector); fitInto(object); } +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + void Scene::fitInto(GameObject &object) { - Rectangle rect = ::fitInto(object.getBoundingBox(), boundingBox()); + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); object.setPosition(center(rect)); } +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + void Scene::detectCollision(GameObject& object1, GameObject& object2) { CollisionInfo info = collision(object1, object2); object1.onCollision(object2, info); diff --git a/include/scene.hpp b/include/scene.hpp index fcafff5..456e574 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -12,7 +12,7 @@ class Scene { Scene(); - virtual ~Scene() {} + virtual ~Scene() = default; void run(); @@ -30,13 +30,13 @@ class Scene { void setObjectPosition(GameObject& object, Point2D position); - Rectangle boundingBox() const; - - void fitInto(GameObject& object); + void move(GameObject& object, Point2D vector); void move(GameObject& object, sf::Time delta); - void move(GameObject& object, Point2D vector); + void fitInto(GameObject& object); + + Rectangle getBoundingBox() const; void detectCollision(GameObject& object1, GameObject& object2); diff --git a/include/textures.hpp b/include/textures.hpp index ec0827d..ef768b9 100644 --- a/include/textures.hpp +++ b/include/textures.hpp @@ -22,7 +22,6 @@ class TextureManager { const sf::Texture* getTexture(GameTextureID id) const; private: static const size_t SIZE = static_cast(GameTextureID::SIZE); - sf::Texture textures[SIZE]; }; From c6fc9a863500d367bbb2c8c9289ed0c238ba6614 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 11 Sep 2023 15:20:55 +0200 Subject: [PATCH 033/137] declare Scene class as a friend of GameObject Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task4.md | 12 +++++++++++- include/cgobject.hpp | 12 +++++++----- include/gobject.hpp | 18 ++++++++---------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md index 07a36ac..d2538bc 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md @@ -21,7 +21,9 @@ the class can govern how its clients interact with the objects of this class: * all the fields and methods declared inside `protected` section are visible and can be used in the class itself and inside its ancestors; * all the fields and methods declared inside `private` section - are visible and can be used only in the class itself, and in no other place. + are visible and can be used only in the class itself, and in no other place; +* there is a single exception to the previous rule — + a class marked as a `friend` of a given class can access its protected and private members. The publicly available fields and methods of the class are also called its _public interface_ (not to be confused with the term _interface_ denoting classes containing pure virtual functions and having no state). @@ -69,3 +71,11 @@ corresponding methods of the `GameObject` class, as well as another method the `Scene` class — the `fitInto` method. This method adjusts the position of an object to fit into the `Scene` borders. +
+ +Note that `Scene` class is declared as a `friend` of `GameObject` class +(see the class declaration in the `gobject.hpp` file). +Thus, the `Scene` class can access `setPosition` method of the `GameObject` class, +even though it is declared as a private method. + +
diff --git a/include/cgobject.hpp b/include/cgobject.hpp index a247646..d22f9d0 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -34,11 +34,6 @@ class CircleGameObject : public GameObject { */ GameObjectStatus getStatus() const override; - /** - * Changes the current status of the object. - */ - void setStatus(GameObjectStatus newStatus) override; - /** * Returns the current shape of the object. */ @@ -54,6 +49,13 @@ class CircleGameObject : public GameObject { */ void draw(sf::RenderWindow &window, TextureManager& textureManager) const override; +protected: + + /** + * Changes the current status of the object. + */ + void setStatus(GameObjectStatus newStatus); + private: Circle circle; GameObjectStatus status; diff --git a/include/gobject.hpp b/include/gobject.hpp index 6736233..ed0ef38 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -23,21 +23,11 @@ class GameObject { */ virtual Point2D getPosition() const = 0; - /** - * Changes the current position of the object. - */ - virtual void setPosition(Point2D position) = 0; - /** * Returns the current status of the object. */ virtual GameObjectStatus getStatus() const = 0; - /** - * Changes the current status of the object. - */ - virtual void setStatus(GameObjectStatus newStatus) = 0; - /** * Returns the kind of the object. */ @@ -97,6 +87,14 @@ class GameObject { */ virtual ~GameObject() = default; +private: + friend class Scene; + + /** + * Changes the current position of the object. + */ + virtual void setPosition(Point2D position) = 0; + }; From 291d61b12d2b58d4c645f6b6f667e63db845e722 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 11 Sep 2023 19:05:32 +0200 Subject: [PATCH 034/137] add text of OOP task4 (static modifier) Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task.md | 102 +++++++++++------- .../IntroducingObjects/task1.md | 10 +- .../IntroducingObjects/task2.md | 4 +- .../IntroducingObjects/task3.md | 2 + .../IntroducingObjects/task5.md | 72 +++++++++++++ 5 files changed, 144 insertions(+), 46 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md index fa7499b..1ef4d24 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md @@ -1,48 +1,72 @@ +The `Scene` class is an _abstract class_ too — +it has pure virtual methods and thus cannot be instantiated. +This gives us the flexibility of having different implementations of the `Scene` class. -This is a task description file. -Its content will be displayed to a learner -in the **Task Description** window. +One such implementation is given by the `StaticScene` subclass +(see files `statscene.hpp` and `statscene.cpp`). +This scene implementation is called static because it contains only +static predefined number of game objects: single player object and single consumable object. +It is certainly a downgrade from our previous version of the game, +when we learned how to create objects dynamically with the help of the linked lists. +Do not worry, we will restore this feature soon. +But for now, let us work with the static scene. -It supports both Markdown and HTML. -To toggle the format, you can rename **task.md** -to **task.html**, or vice versa. -The default task description format can be changed -in **Preferences | Tools | Education**, -but this will not affect any existing task description files. +The `Scene` class has a certain peculiarity compared to the `GameObject` class — +there could exist single unique `Scene` per one game instance. +We can express this in the code with the help of another C++ feature: `static` modifier. -The following features are available in -**task.md/task.html** which are specific to the JetBrains Academy plugin: +First, note that the `Scene` class has one method that stands out from the others: +it is `create()` method that has `static` modifier in front of it. +The `static` modifier, applied to a class member (either field or method), +turns this member into a _static_ member. + +Static members are not associated with the objects, instead they are associated with the class itself. +This means that in order to access a static member, you do not need an instance of the class at hand. +Instead, static members are accessed through the class name: + +```c++ +// obtains a scene instance by calling static method `create` +Scene* scene = Scene::create(); +``` + +Static members provide a convenient way to associate some methods or data fields with the class itself. +For example, the `create` method shown above provides an ability to instantiate +the scene object without revealing the actual implementation to the user of the method +(note that it returns `Scene*` instead of `StaticScene*`). +Moreover, it gives us a way to ensure that only one scene is created per each game run. +How we can achieve that — well, with the help of the `static` modifier again. + +When applied to the declaration of local variables inside functions, +`static` modifier has a different meaning. +It allows creating a _static variable_ that survives and preserves its value between the function calls. +Such variables are actually stored inside the static memory region of the program, +instead of the stack memory region where the other local function's variables reside, +hence the name _static_. + +```c++ +int foo() { + static int x = 0; + return ++x; +} + +// prints 1 +std::cout << foo() << std::endl; +// prints 2 +std::cout << foo() << std::endl; +``` + +With the help of the `static` modifier, it becomes possible to +declare static `StaticScene` variable inside `Scene::create` method +and return pointer to this variable. -- Hints can be added anywhere in the task text. - Type "hint" and press Tab. - Hints should be added to an empty line in the task text. - In hints you can use both HTML and Markdown.
-Text of your hint +Note that in this case, the address escape error does not occur. +Because the `static` variable resides in the static memory region, it lives thought the whole program execution time. +Thus, it is safe to return the address of this variable from the function.
-- You may need to refer your learners to a particular lesson, -task, or file. To achieve this, you can use the in-course links. -Specify the path using the `[link_text](course://lesson1/task1/file1)` format. - -- You can insert shortcuts in the task description. -While **task.html/task.md** is open, right-click anywhere -on the **Editor** tab and choose the **Insert shortcut** option -from the context menu. -For example: &shortcut:FileStructurePopup;. - -- Insert the %`IDE_NAME`% macro, -which will be replaced by the actual IDE name. -For example, **%IDE_NAME%**. - -- Insert PSI elements, by using links like -`[element_description](psi_element://link.to.element)`. -To get such a link, right-click the class or method -and select **Copy Reference**. -Then press &shortcut:EditorPaste; to insert the link where appropriate. -For example, a [link to the "contains" method](psi_element://java.lang.String#contains). - -- You can add link to file using **full path** like this: - `[file_link](file://lesson1/task1/file.txt)`. \ No newline at end of file +Please implement the `create` method as described above. +If you do this correctly, you will be finally able to run the refactored game application +and see the planet and star objects on the screen. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md index 1b5c0c4..fc1f48d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md @@ -13,10 +13,10 @@ In other words, a _class_ is just a type of objects. For example, let us consider the `GameObject` class. The objects of this class represent entities appearing on the game scene, -such as the planet objects controlled by the player, +such as the planet object controlled by the player, consumable star objects, and others that we will add later. Instances of this class will store data related to a game object, -such as its position on the scene, and some functions to manipulate an object. +such as its position on the scene, and some methods to manipulate the object. Let us have a look at the `GameObject` class definition. First note that in C++ new class is defined with the help of the `class` keyword. @@ -30,9 +30,9 @@ by consulting its _documentation_ given as a docstring comment in front of the m The `GameObject` class itself does not define any data fields, only the methods. It, however, implicitly defines a bunch of _properties_ of an object, for example, its position. A value of a property can be requested using its _getter_ method (e.g. `getPosition`), -and it can be changed using its _setter_ method (e.g. `setObjectPosition`). +and it can be changed using its _setter_ method (e.g. `setPosition`). Note that some properties of an object have both _getter_ and _setter_ methods, -like aforementioned `getPosition` and `setObjectPosition` methods, +like aforementioned `getPosition` and `setPosition` methods, while others have only _getter_, for example `getVelocity`. This is for a reason — some properties are derivatives of the current objects' status, and they cannot be directly changed from the outside. @@ -45,7 +45,7 @@ that can be _overridden_ by the inheritors of the class (we will delve back to inheritance in the next task). The `= 0` syntax at the end of the virtual method indicates that it is a _pure virtual_ method. -Such method is not implemented for the given class — +Such a method is not implemented for the given class — it is just a stub for an actual method implementation. The classes that do declare any data fields and contain pure virtual methods are also called _interfaces_. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md index 97219c3..e50a300 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md @@ -70,10 +70,10 @@ this destructor has default auto-generated implementation. As we will see later in the course, constructors and destructor have a pivotal role in C++. Going back to the `CircleGameObject` class, consider its methods. -Some of them, like `getPosition` and `setObjectPosition`, are just re-declared methods of the `GameObject` class. +Some of them, like `getPosition` and `setPosition`, are just re-declared methods of the `GameObject` class. The keyword `override` at the end of the methods' declarations indicates this fact. However, unlike the `GameObject` class, the `CircleGameObject` class actually defines the behavior of these methods. To be precise, it is your task to implement some of them, -namely `getPosition`, `setObjectPosition`, `getStatus`, `setStatus`, and `getCircle`. +namely `getPosition`, `setOPosition`, `getStatus`, `setStatus`, and `getCircle`. Keep in mind that the position of the `CircleGameObject` is a position of its circle's center. \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md index a463ea5..89c2e71 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md @@ -10,6 +10,8 @@ At last, both of these classes implement all the functionality required by the ` Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. There are no new syntactic constructs here, so you should be able to understand the code in these files. +[//]: # (add here a paragraph about the polymorphism) + The implementations of these classes can be found in the files `player.cpp` and `consumable.cpp`. Note that the full implementation of some methods is already provided. For example, the `getVelocity` method of the `PlayerObject` computes diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md new file mode 100644 index 0000000..9131a85 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md @@ -0,0 +1,72 @@ +The `Scene` class is an _abstract class_ too — +it has pure virtual methods and thus cannot be instantiated. +This gives us the flexibility of having different implementations of the `Scene` class. + +One such implementation is given by the `StaticScene` subclass +(see files `statscene.hpp` and `statscene.cpp`). +This scene implementation is called static because it contains only +static predefined number of game objects: single player object and single consumable object. +It is certainly a downgrade from our previous version implementation of the game, +when we learned how to create objects dynamically with the help of the linked lists. +Do not worry, we will restore this feature soon. +But for now, let us work with the static scene. + +The `Scene` class has a certain peculiarity compared to the `GameObject` class — +there could exist single unique `Scene` per one game instance. +We can express this in the code with the help of another C++ feature: `static` modifier. + +First, note that the `Scene` class has one method that stands out from the others: +it is `create()` method that has `static` modifier in front of it. +The `static` modifier, applied to a class member (either field or method), +turns this member into a _static_ member. + +Static members are not associated with the objects, instead they are associated with the class itself. +This means that in order to access a static member, you do not need an instance of the class at hand. +Instead, static members are accessed through the class name: + +```c++ +// obtains a scene instance by calling static method `create` +Scene* scene = Scene::create(); +``` + +Static members provide a convenient way to associate some methods or data fields with the class itself. +For example, the `create` method shown above provides an ability to instantiate +the scene object, without revealing the actual implementation to the user of the method +(note that it returns `Scene*` instead of `StaticScene*`). +Moreover, it gives us a way to ensure that only one scene is created per each game run. +How we can achieve that — well, with the help of the `static` modifier again. + +When applied to the declaration of local variables inside functions, +`static` modifier has a different meaning. +It allows creating a _static variable_ that survives and preserves its value between the function calls. +Such variables are actually stored inside the static memory region of the program, +instead of the stack memory region where the other local function's variables reside, +hence the name _static_. + +```c++ +int foo() { + static int x = 0; + return ++x; +} + +// prints 1 +std::cout << foo() << std::endl; +// prints 2 +std::cout << foo() << std::endl; +``` + +With the help of the `static` modifier, it becomes possible to +declare static `StaticScene` variable inside `Scene::create` method +and return pointer to this variable. + +
+ +Note that in this case, the address escape error does not occur. +Because the `static` variable resides in the static memory region, it lives thought the whole program execution time. +Thus, it is safe to return the address of this variable from the function. + +
+ +Please implement the `create` method as described above. +If you do this correctly, you will be finally able to run the refactored game application +and see the planet and star objects on the screen. From e37e8decfeba940f2377b686760680ee0c5d2696 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 11 Sep 2023 20:30:38 +0200 Subject: [PATCH 035/137] add text of OOP task6 (objects vs POD) Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task4.md | 2 + .../IntroducingObjects/task6.md | 62 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md index d2538bc..3c841e1 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md @@ -30,6 +30,8 @@ The publicly available fields and methods of the class are also called its _publ The public interface of a class defines how the objects of this class are visible from the outside, what fields and methods can the clients of the class access. +[//]: # (TODO: add a note about visibility-inheritance modifier) + You might be wondering what is the point of hiding some of the class' field or methods — after all, they can be useful outside. However, the ability to hide some of the object's _implementation details_ diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md new file mode 100644 index 0000000..1a32812 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md @@ -0,0 +1,62 @@ +Now let us restore the collision detection functionality in our refactored game. + +We have already refactored the collision detection function. + +```c++ +CollisionInfo collision(const Circle& circle1, const Circle& circle2); +``` + +Now it takes two circle shapes by constant references. +Instead of boolean flag indicating whether the collision occurred, +it returns the `CollisionInfo` structure: + +```c++ +struct CollisionInfo { + bool collide; + float distance; +}; +``` + +This design will give us flexibility to compute various information +about possible collision between two objects inside `collision` function, +and leave the opportunity to decide what to do with this information to other modules of our game. +For now, we will store the boolean flag `collide`, indicating whether the collision happened, +and the computed `distance` between two objects — but later you +will have an opportunity to extend this structure +with additional information required to implement new features in our game. + +You might wonder why we decided to model `CollisionInfo` as a structure, +instead of using the fancy objects we learned in this lesson. +In fact, we did this on purposes to illustrate the following point. +Structures, declared via `struct`, and objects/classes, declared via `class`, +are not incompatible concepts in C++, +and often instances of both can be found in the same codebase. + +* Objects are used to tie in together data (fields) and behavior (methods). + Objects provide _encapsulation_ and _polymorphism_. + The state of objects can satisfy various invariants, + maintained by carefully controlling the visibility of class' members. + +* Structures are used as simple containers of data. + They have predictable memory layout and predictable behavior — + there are no associated virtual methods dispatched at runtime. + +In C++, structures are also sometime referred +to as [_POD types_]((https://en.wikipedia.org/wiki/Passive_data_structure)), +where POD stands for _plain old data_. + +
+ +Technically, in C++ there is no big difference between `class` and `struct` keywords. +For example, one can declare classes using the `struct` keyword, and vice versa. +The only real difference is that : +* in `struct` members by default have `public` visibility; +* in `class` members by default have `private` visibility. + +However, a prevalent convention among the C++ developers is +to use `class` keyword to declare actual classes in the object-oriented programming sense, +while the `struct` keyword is used to declare POD types. + +
+ +[//]: # (TODO: add task) \ No newline at end of file From 89f03e4c7e5d38748463fba5463d59a23ce3aee5 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 13:19:00 +0200 Subject: [PATCH 036/137] minor Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/consumable.cpp | 10 +++++----- .../IntroducingObjects/src/player.cpp | 2 +- include/enemy.hpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index c82507f..9a7a91b 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -15,21 +15,21 @@ Point2D ConsumableObject::getVelocity() const { } void ConsumableObject::update(sf::Time delta) { - if (CircleGameObject::getStatus() != GameObjectStatus::DESTROYED) { - CircleGameObject::setStatus(GameObjectStatus::NORMAL); + if (getStatus() != GameObjectStatus::DESTROYED) { + setStatus(GameObjectStatus::NORMAL); } } void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { - if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) { + if (getStatus() == GameObjectStatus::DESTROYED) { return; } if (collisionData.collide) { - CircleGameObject::setStatus(GameObjectStatus::DESTROYED); + setStatus(GameObjectStatus::DESTROYED); return; } if (collisionData.distance < 6 * getCircle().radius) { - CircleGameObject::setStatus(GameObjectStatus::CONCERNED); + setStatus(GameObjectStatus::CONCERNED); return; } } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index acdbb04..d78bba9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -37,7 +37,7 @@ void PlayerObject::update(sf::Time delta) { void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { - CircleGameObject::setStatus(GameObjectStatus::DESTROYED); + setStatus(GameObjectStatus::DESTROYED); } } diff --git a/include/enemy.hpp b/include/enemy.hpp index 6a335c5..dd166a3 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -7,16 +7,16 @@ class EnemyObject : public CircleGameObject { public: EnemyObject(); - Point2D getVelocity() const override; - GameObjectKind getKind() const override; - const sf::Texture* getTexture(TextureManager& textureManager) const override; + Point2D getVelocity() const override; void update(sf::Time delta) override; void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; + const sf::Texture* getTexture(TextureManager& textureManager) const override; + private: Point2D velocity; sf::Time updateTimer; From f6e6bdee9210c951065f3814f0405a308c3d1b10 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 13:20:07 +0200 Subject: [PATCH 037/137] add description of a task about enemy objects Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task1.md | 2 +- .../IntroducingObjects/task7.md | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md index fc1f48d..5730c48 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md @@ -38,7 +38,7 @@ This is for a reason — some properties are derivatives of the current objects' and they cannot be directly changed from the outside. Another piece of unfamiliar syntax here is the `const` keyword coming after the arguments of a methods. -It denotes the _constant methods_ --- these methods cannot change the status of the object. +It denotes the _constant methods_ --- these methods cannot change the state of the object. Finally, keyword `virtual` denotes the _virtual_ methods — these are the methods that can be _overridden_ by the inheritors of the class diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md new file mode 100644 index 0000000..b5a0f4c --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md @@ -0,0 +1,59 @@ +Now let us apply the newly acquired knowledge +to extend the mechanics of our game by adding a new kind of game objects — +enemy objects visualized as black holes. +As you will see, with the help of object-oriented programming tricks, +this task can be accomplished quite easily. + +The enemy object should behave as follows: +- it should move on a scene in a random direction, changing it periodically; +- it should consume the star objects when colliding with them; +- star objects should change their status into `CONCERNED` when + the enemy object is approaching them + (similarly as they do when the player object is approaching); +- when the enemy object collides with the player object, the latter + should change its status into `DESTROYED`, becoming effectively immovable. + +Let us proceed to implementation of this behavior. +First, take a look at the declaration of the `EnemyObject` class. +It again inherits from the `CircleGameObject` class, so it already +has a corresponding circle shape data and behavior attached to it. +Moreover, the `getKind()` method of the `EnemyObject` class +distinguish the enemy objects from other kind of objects (player and consumable) +by returning the `GameObjectKind::ENEMY` value. + +We have also already implemented for you the movement behavior of the `EnemyObject`. +To do so, we have added two new fields to the objects of this class: +- `velocity` field is vector storing the direction and speed of the current velocity of the object; +- `updateTimer` field stores the time elapsed since the last update of the object's velocity. + +The method `getVelocity()` simply returns the value of `velocity` field. +The method `update(sf::Time delta)` is responsible for periodically updating the velocity of the object. +It takes as an argument the amount of time elapsed since the last update. +The implementation simply checks if the overall amount of elapsed time +is greater than the predefined time period (1 second), +and if so, it resets the velocity to a new randomly generated one. + +Now your task is to implement the rest of enemy objects functionality. + +The easy part is to implement the `getTexture` method, +that should return a new special texture for the enemy objects. +This texture has a corresponding id --- `GameTextureID::BLACKHOLE`. + +The harder part is to implement the collision behavior. +When an enemy object collides with the player object, the player object should become inactive. +This can be achieved by setting the status of the player object to `DESTROYED`. +However, an enemy object does not have direct access to the `setStatus` method of the player object. +So in order to implement the desired behavior, you actually need to +modify the `onCollision` method of the `PlayerObject` class, not the `EnemyObject` class! + +
+ +In the implementation of the `PlayerObject::onCollision` method, +do not forget to check that the collision occurred with the enemy object, +not an object of some other kind! + +
+ +Notice that from the point of view of the consumable objects, +the player and the enemy objects behave similarly, +so you do not need to modify their behavior. \ No newline at end of file From 2832105b0b03cbd074e5add3a8b9d453f85ea7a3 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 15:14:53 +0200 Subject: [PATCH 038/137] add task description for collision handling, minor renaming: `collision` function -> `collisionInfo` Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/collision.cpp | 29 +-- .../IntroducingObjects/src/consumable.cpp | 2 +- .../IntroducingObjects/task-info.yaml | 172 ++++++++++-------- .../IntroducingObjects/task6.md | 36 +++- include/collision.hpp | 8 +- include/constants.hpp | 2 + include/consumable.hpp | 4 +- include/gobject.hpp | 5 +- include/player.hpp | 2 +- 9 files changed, 141 insertions(+), 119 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp index cbef14e..41af854 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp @@ -13,35 +13,18 @@ float distance(Point2D a, Point2D b) { return sqrt(dx * dx + dy * dy); } -CollisionInfo collision(const Circle& circle1, const Circle& circle2) { - float dx = circle2.center.x - circle1.center.x; - float dy = circle2.center.y - circle1.center.y; - float dist = sqrt(dx * dx + dy * dy); - Point2D d = { dx, dy }; - CollisionInfo collisionData; - collisionData.collide = (dist < circle1.radius + circle2.radius); - collisionData.distance = dist; - collisionData.normalVector = (1 / dist) * d; - return collisionData; -} - -bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionInfo& collisionData) { - return collisionData.distance < 3 * circle1.radius + circle2.radius; -} - -Point2D calculateResolutionVector(const Circle& circle1, const Circle& circle2, - const CollisionInfo& collisionData, float resolutionFactor) { - Point2D border1 = circle1.center + circle1.radius * collisionData.normalVector; - Point2D border2 = circle2.center - circle2.radius * collisionData.normalVector; - float dist = distance(border1, border2); - return -(dist * resolutionFactor) * collisionData.normalVector; +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; } CollisionInfo collision(const GameObject& object1, const GameObject& object2) { const CircleGameObject* circleObject1 = dynamic_cast(&object1); const CircleGameObject* circleObject2 = dynamic_cast(&object2); if (circleObject1 && circleObject2) { - return collision(circleObject1->getCircle(), circleObject2->getCircle()); + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); } // TODO assert(false); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index 9a7a91b..5e8a897 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -28,7 +28,7 @@ void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo setStatus(GameObjectStatus::DESTROYED); return; } - if (collisionData.distance < 6 * getCircle().radius) { + if (collisionData.distance < CONSUMABLE_CONCERNED_MULTIPLIER * getCircle().radius) { setStatus(GameObjectStatus::CONCERNED); return; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 8fb2c6d..1ce1c8a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -1,82 +1,96 @@ type: edu custom_name: Introducing Objects files: -- name: CMakeLists.txt - visible: false -- name: test/test.cpp - visible: false -- name: src/operators.cpp - visible: true - placeholders: - - offset: 67 - length: 17 - placeholder_text: "return { 0.0f, 0.0f };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 1 - is_visible: false - - offset: 123 - length: 22 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 2 - is_visible: false - - offset: 195 - length: 18 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 3 - is_visible: false - - offset: 261 - length: 17 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 4 - is_visible: false -- name: src/point.cpp - visible: true -- name: src/cgobject.cpp - visible: true -- name: src/gobject.cpp - visible: true -- name: src/collision.cpp - visible: true -- name: src/rectangle.cpp - visible: true -- name: src/player.cpp - visible: true -- name: src/enemy.cpp - visible: true -- name: src/direction.cpp - visible: true -- name: src/textures.cpp - visible: true -- name: src/main.cpp - visible: true -- name: src/consumable.cpp - visible: true -- name: src/utils.cpp - visible: true -- name: src/gobjectlist.cpp - visible: true -- name: src/dynscene.cpp - visible: true -- name: src/statscene.cpp - visible: true -- name: src/scene.cpp - visible: true + - name: CMakeLists.txt + visible: false + - name: test/test.cpp + visible: false + - name: src/operators.cpp + visible: true + placeholders: + - offset: 67 + length: 17 + placeholder_text: "return { 0.0f, 0.0f };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 1 + is_visible: false + - offset: 123 + length: 22 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 2 + is_visible: false + - offset: 195 + length: 18 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 3 + is_visible: false + - offset: 261 + length: 17 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 4 + is_visible: false + - name: src/point.cpp + visible: true + - name: src/rectangle.cpp + visible: true + - name: src/direction.cpp + visible: true + - name: src/textures.cpp + visible: true + - name: src/main.cpp + visible: true + - name: src/utils.cpp + visible: true + - name: src/dynscene.cpp + visible: true + - name: task1.md + visible: true + - name: task2.md + visible: true + - name: task3.md + visible: true + - name: task4.md + visible: true + - name: src/statscene.cpp + visible: true + - name: src/collision.cpp + visible: true + - name: src/enemy.cpp + visible: true + - name: src/cgobject.cpp + visible: true + - name: src/scene.cpp + visible: true + - name: src/gobject.cpp + visible: true + - name: task5.md + visible: true + - name: task6.md + visible: true + - name: src/player.cpp + visible: true + - name: src/consumable.cpp + visible: true + - name: src/gobjectlist.cpp + visible: true + - name: task7.md + visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md index 1a32812..98e35b8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md @@ -3,7 +3,7 @@ Now let us restore the collision detection functionality in our refactored game. We have already refactored the collision detection function. ```c++ -CollisionInfo collision(const Circle& circle1, const Circle& circle2); +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2); ``` Now it takes two circle shapes by constant references. @@ -18,7 +18,7 @@ struct CollisionInfo { ``` This design will give us flexibility to compute various information -about possible collision between two objects inside `collision` function, +about possible collision between two objects inside `collisionInfo` function, and leave the opportunity to decide what to do with this information to other modules of our game. For now, we will store the boolean flag `collide`, indicating whether the collision happened, and the computed `distance` between two objects — but later you @@ -59,4 +59,34 @@ while the `struct` keyword is used to declare POD types. -[//]: # (TODO: add task) \ No newline at end of file +Now, with the help of the new collision detection function, +your task is to re-implement the behavior of consumable objects. +- Upon collision with another object, the consumable should change its status into `DESTROYED`. +- When another object is approaching the consumable, it should change its status into `CONCERNED`. + This should happen whenever the distance between another object and consumable is less than `C * r`, where + - `r` is the radius of consumable object, + - `C` is a special multiplier constant + +```c++ +const float CONSUMABLE_CONCERNED_MULTIPLIER = 6.0f; +``` + +To do so, please implement the `onCollision` method of `ConsumableObject` class. +This method is called periodically from the `Scene` class, notifying the object +about its potential collisions with other objects. +The function takes as a first argument another object, +and as a second argument the `CollisionInfo` structure, +containing the information about the distance between objects +and whether they actually collided. +It is up to the method's implementation to decide what to do with this information. + +Note that when the consumable object becomes `CONCERNED`, it should eventually +change its status back to `NORMAL` when other objects left its nearby area. +One way to achieve this is by also modifying the implementation of `update` method. +This method is also periodically called from the `Scene` class to give +the object an opportunity to update its internal state. +This function takes as a single argument the amount of time elapsed since the last update, +although you will not need this information in the current task. +Simply reset the status of the alive (that is --- not `DESTROYED`!) consumable back to `NORMAL`. +If there are some objects nearby, they will be detected again during `onCollision` call, +otherwise the consumable object will remain in `NORMAL` status. diff --git a/include/collision.hpp b/include/collision.hpp index 22c1eeb..b79a9c1 100644 --- a/include/collision.hpp +++ b/include/collision.hpp @@ -6,14 +6,8 @@ struct CollisionInfo { bool collide; float distance; - Point2D normalVector; }; -CollisionInfo collision(const Circle& circle1, const Circle& circle2); - -bool areApproaching(const Circle& circle1, const Circle& circle2, const CollisionInfo& collisionData); - -Point2D calculateResolutionVector(const Circle& circle1, const Circle& circle2, - const CollisionInfo& collisionData, float resolutionFactor = 1.0f); +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2); #endif // CPPBASICS_COLLISION_HPP diff --git a/include/constants.hpp b/include/constants.hpp index 0a514af..ceca800 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -24,4 +24,6 @@ const float CONSUMABLE_START_Y = 150.0f; const float ENEMY_START_X = 100.0f; const float ENEMY_START_Y = 450.0f; +const float CONSUMABLE_CONCERNED_MULTIPLIER = 6.0f; + #endif // CPPBASICS_CONSTANTS_HPP diff --git a/include/consumable.hpp b/include/consumable.hpp index 17dac25..97217b7 100644 --- a/include/consumable.hpp +++ b/include/consumable.hpp @@ -32,9 +32,9 @@ class ConsumableObject : public CircleGameObject { void update(sf::Time delta) override; /** - * Handler the collision of consumable object with another object. + * Handles the potential collision of consumable object with another object. * Sets the status to `CONCERNED` if some other object is approaching the consumable. - * Sets the status to `DESTROYED` in case of a collision. + * Sets the status to `DESTROYED` in case of an actual collision. */ void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; diff --git a/include/gobject.hpp b/include/gobject.hpp index ed0ef38..6549f78 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -57,11 +57,10 @@ class GameObject { virtual void update(sf::Time delta) = 0; /** - * Handler function called when a collision occurs between this object and another object. - * The derived class implementing this function will define the behavior for handling the collision. + * Handler function called when a potential collision occurs between this object and another object. * * @param object The collided object. - * @param collisionData The information about the collision. + * @param collisionData The information about the potential collision. */ virtual void onCollision(const GameObject& object, const CollisionInfo& collisionData) = 0; diff --git a/include/player.hpp b/include/player.hpp index 61b2706..a9505aa 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -33,7 +33,7 @@ class PlayerObject : public CircleGameObject { void update(sf::Time delta) override; /** - * Handler the collision of player object with another object. + * Handler a potential collision of player object with another object. * In particular, if player collided with an enemy object, sets its status to `DESTROYED`. */ void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; From d6852026053affd3695a6e37de9e93bb5e70bc23 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 15:24:12 +0200 Subject: [PATCH 039/137] minor renaming Signed-off-by: Evgenii Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/collision.cpp | 2 +- .../ClassesAndObjects/IntroducingObjects/src/consumable.cpp | 6 +++--- .../ClassesAndObjects/IntroducingObjects/src/dynscene.cpp | 2 +- .../ClassesAndObjects/IntroducingObjects/src/enemy.cpp | 2 +- .../ClassesAndObjects/IntroducingObjects/src/player.cpp | 4 ++-- .../ClassesAndObjects/IntroducingObjects/src/scene.cpp | 2 +- include/consumable.hpp | 2 +- include/enemy.hpp | 2 +- include/gobject.hpp | 6 +++--- include/player.hpp | 4 ++-- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp index 41af854..a41a0e9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp @@ -20,7 +20,7 @@ CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { return info; } -CollisionInfo collision(const GameObject& object1, const GameObject& object2) { +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { const CircleGameObject* circleObject1 = dynamic_cast(&object1); const CircleGameObject* circleObject2 = dynamic_cast(&object2); if (circleObject1 && circleObject2) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index 5e8a897..de1c448 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -20,15 +20,15 @@ void ConsumableObject::update(sf::Time delta) { } } -void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { if (getStatus() == GameObjectStatus::DESTROYED) { return; } - if (collisionData.collide) { + if (info.collide) { setStatus(GameObjectStatus::DESTROYED); return; } - if (collisionData.distance < CONSUMABLE_CONCERNED_MULTIPLIER * getCircle().radius) { + if (info.distance < CONSUMABLE_CONCERNED_MULTIPLIER * getCircle().radius) { setStatus(GameObjectStatus::CONCERNED); return; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index 80056b5..305c81f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -103,7 +103,7 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) // check that object does not collide with existing objects bool collide = false; objects.foreach([&collide, &object](GameObject& other) { - collide |= collision(*object, other).collide; + collide |= collisionInfo(*object, other).collide; }); // reset a colliding object if (collide) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 3e8f928..9f61d85 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -28,7 +28,7 @@ void EnemyObject::update(sf::Time delta) { velocity = SPEED * velocity; } -void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { return; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index d78bba9..cab1c5a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -35,8 +35,8 @@ void PlayerObject::update(sf::Time delta) { return; } -void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &collisionData) { - if (collisionData.collide && object.getKind() == GameObjectKind::ENEMY) { +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (info.collide && object.getKind() == GameObjectKind::ENEMY) { setStatus(GameObjectStatus::DESTROYED); } } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 332e7a4..6d52a4d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -77,7 +77,7 @@ Rectangle Scene::getBoundingBox() const { } void Scene::detectCollision(GameObject& object1, GameObject& object2) { - CollisionInfo info = collision(object1, object2); + CollisionInfo info = collisionInfo(object1, object2); object1.onCollision(object2, info); object2.onCollision(object1, info); } diff --git a/include/consumable.hpp b/include/consumable.hpp index 97217b7..78c680d 100644 --- a/include/consumable.hpp +++ b/include/consumable.hpp @@ -36,7 +36,7 @@ class ConsumableObject : public CircleGameObject { * Sets the status to `CONCERNED` if some other object is approaching the consumable. * Sets the status to `DESTROYED` in case of an actual collision. */ - void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; + void onCollision(const GameObject &object, const CollisionInfo &info) override; /** * Retrieves the texture associated with the consumable object based on its current status. diff --git a/include/enemy.hpp b/include/enemy.hpp index dd166a3..e8ce9e2 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -13,7 +13,7 @@ class EnemyObject : public CircleGameObject { void update(sf::Time delta) override; - void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; + void onCollision(const GameObject &object, const CollisionInfo &info) override; const sf::Texture* getTexture(TextureManager& textureManager) const override; diff --git a/include/gobject.hpp b/include/gobject.hpp index 6549f78..18d6522 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -60,9 +60,9 @@ class GameObject { * Handler function called when a potential collision occurs between this object and another object. * * @param object The collided object. - * @param collisionData The information about the potential collision. + * @param info The information about the potential collision. */ - virtual void onCollision(const GameObject& object, const CollisionInfo& collisionData) = 0; + virtual void onCollision(const GameObject& object, const CollisionInfo& info) = 0; /** * This function is used to render the object on a SFML window passed as an argument. @@ -101,6 +101,6 @@ class GameObject { * This function takes in two game objects as parameters and checks whether they are colliding. * It returns a CollisionInfo structure that contains information about the collision. */ -CollisionInfo collision(const GameObject& object1, const GameObject& object2); +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2); #endif // CPPBASICS_GOBJECT_HPP diff --git a/include/player.hpp b/include/player.hpp index a9505aa..1dccd97 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -33,10 +33,10 @@ class PlayerObject : public CircleGameObject { void update(sf::Time delta) override; /** - * Handler a potential collision of player object with another object. + * Handles a potential collision of player object with another object. * In particular, if player collided with an enemy object, sets its status to `DESTROYED`. */ - void onCollision(const GameObject &object, const CollisionInfo &collisionData) override; + void onCollision(const GameObject &object, const CollisionInfo &info) override; /** * Retrieves the texture associated with the player object based on its current status. From 03481c15c5eb5f432cd05e077d124589255a7a26 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 16:40:12 +0200 Subject: [PATCH 040/137] add OOP introductory task description Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task-info.yaml | 2 ++ .../IntroducingObjects/task0.md | 23 +++++++++++++++++++ .../IntroducingObjects/task2.md | 2 ++ .../IntroducingObjects/task3.md | 2 +- 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 1ce1c8a..109d785 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -94,3 +94,5 @@ files: visible: true - name: task7.md visible: true + - name: task0.md + visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md new file mode 100644 index 0000000..2b95e9b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md @@ -0,0 +1,23 @@ +In the next few lessons, you will learn about the object-oriented programming paradigm +in the context of the C++ language. As you will see, this paradigm helps +to organize the code into independent subcomponents, manage the complexity +of the applications, and streamline the addition of new functionality. + +We will re-implement our game using the features of object-oriented programming. +This effort will help us to easily extend the game by adding new kinds of objects. +In particular, we will add enemy objects that will make our little game a bit more challenging. +There is a lot of fun coming, so get ready for your journey into the world of objects! + +Here is a list of specific topics that are going to be covered: + +* Operators overloading. +* Classes and objects. +* Abstract classes and interfaces. +* Constructors and destructors. +* Inheritance and subtyping. +* Subtype polymorphism. +* Virtual methods. +* Visibility modifiers: `public`, `protected`, and `private`. +* Encapsulation and class invariants. +* Static methods, fields, and variables. +* Objects vs structures. \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md index e50a300..ff28046 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md @@ -10,6 +10,8 @@ A _derived_ class, also called a _subclass_, inherits all the method and fields of the _base_ class, and can add its own new methods and fields. +[//]: # (TODO: also mention term `subtyping`) + Giving back to our problem, let us define a `CircleGameObject` subclass of `GameObject` class. Instances of `CircleGameObject` class represent diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md index 89c2e71..9ba062c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md @@ -10,7 +10,7 @@ At last, both of these classes implement all the functionality required by the ` Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. There are no new syntactic constructs here, so you should be able to understand the code in these files. -[//]: # (add here a paragraph about the polymorphism) +[//]: # (TODO: add here a paragraph about the polymorphism) The implementations of these classes can be found in the files `player.cpp` and `consumable.cpp`. Note that the full implementation of some methods is already provided. From da96719a243ab23cd30e44000e4058ced70cff5a Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 18:56:45 +0200 Subject: [PATCH 041/137] linked lists task description Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task8.md | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md new file mode 100644 index 0000000..3e00a3c --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md @@ -0,0 +1,144 @@ +Next, we need to restore the dynamic behavior of our game — the ability +to add and remove objects dynamically during the play. +To do so, we will re-implement the doubly linked list data structure +in terms of object-oriented programming. +Before proceeding with this task, please first finish the `Ownership` module, +as we will need some concepts taught there. + +First of all, instead of completely rewriting the `StaticScene` class, +we will just a new class --- `DynamicScene`, where we will implement the new dynamic functionality. + +[//]: # (Then switching between the two `Scene` implementations will be quite easy --- ) +[//]: # (TODO: describe the actual scene-switching logic once it will be settled.) + +Please have a look at the declaration of the `DynamicScene` class (file `dynscene.hpp`), +and its definition (file `dynscene.cpp`). You can find the brief description of its methods +in the documentation comments. + +The `DynamicScene` class has a field `objects` of `GameObjectList` class. +This is the main class we are going to work with in this task — it will implement the doubly linked list +(its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively). + +This time, using the object-oriented programming and ownership model, +we will implement a list that will own its nodes. +The nodes will be destructed and deallocated automatically upon destruction of the list itself, +and copying of the list will result in all of its nodes being copied too. + +The nodes of the list are represented by the `Node` structure, +which implement the ownership semantics --- each node is owning its successor. +Let us have a closer look on the fields of the `Node` structure. + +- `prev` field is a plain pointer `Node*`, storing the pointer to a previous node. + It is a non-owning pointer, because if it was made an owning pointer, then it would + result into ownership relation cycle. + Indeed, giving a node `x`, `x.prev` can point to a node `y`, such that `y.next` points to `x` --- + this is clearly a cycle. +- `next` field is an owning pointer `std::unique_ptr`. + Whenever a node is destructed, all its succeeding nodes will also be destructed, + due to the ownership semantics. +- `object` field is a shared pointer `std::shared_ptr` to a game object stored in the node. + It has the shared ownership semantics, so that the shared pointers to game objects + can be safely returned from the methods of the `GameObjectList` class. + +The static methods `link` and `unlink` implement the linking and unlinking of nodes respectively. +You have already seen them in the `LinkedList` task. Here is a reminder of their semantics: + +- Links together the `cursor` and `node` nodes, putting `node` right after `cursor`. +```c++ +static void link(Node* cursor, std::unique_ptr&& node); +``` + +- Unlinks the node from its neighboring nodes. +```c++ +static void unlink(Node* node); +``` + +Please implement these methods. +Pay attention to the different ownership semantics of `next`, `prev`, and `object` pointers, +and use `std::move` to manage the ownership. +Remember that `std::unique_ptr` transitions into `nullptr` state after ownership transfer, +and so the order of `std::move` and pointer dereferences becomes important! + +Next, consider the fields of the `GameObjectList` class. + +- `head` is an owning pointer `std::unique_ptr` to the first node of the list. +- `tail` is a non-owning pointer `Node*` to the last node of the list. + It is a non-owning pointer because the pointed-to node is actually owned + by its predecessor via its `next` pointer. + +Because the `head` pointer of the list is owning one, the whole sequence of nodes +belonging to the list will be destroyed automatically upon destruction of the list itself. +This is the reason why we left the default implementation of the class' destructor: + +```c++ +~GameObjectList() = default; +``` + +
+ +Indeed, the destructor of the `GameObjectList` will call +the `~std::unique_ptr()` destructor of the `head` field. +In turn, it will call the destructor of the `Node`, +which will call the destructor `~std::unique_ptr()` of its `next` field, +and so on, until all nodes will be destructed. + +
+ +Note that in the previous implementation of the list, in the `LinkedList` task, +we use a single sentinel node to simplify the implementation of some list operating functions. +Moreover, under the hood the list was organized into a cyclic list: +the `next` field of the last node was pointing the first (sentinel) node. +This time we cannot reuse this trick, since a cyclic list would result into ownership cycle. +Therefore, we would need two sentinel nodes — one as a first node, and second as a last node. + +Please take a look at the pre-defined methods `foreach` and `remove` of the list +that utilize this list representation: +- `foreach` applies the function given as an argument to every game object stored in the list; +- `remove` unlinks nodes (effectively removing them), whose game object satisfies predicate given as an argument. + +
+ +You might find the type `std::function<...>` unfamiliar. +In essence, it is just an object-oriented counterpart of a function pointer. +We will have a closer look at this type in the later modules of the course. + +
+ +Now, please implement the method inserting a game object into the beginning of the list: + +```c++ +void insert(const std::shared_ptr& object); +``` + +Keep in mind that: +- the first and the last nodes should remain sentinel nodes; +- only the sentinel nodes can store null pointer `nullptr` inside `object` field. + +In order to complete this task, also finish the implementation of the ownership semantics of +the `GameObjectList` class, following the rule-of-file and copy-and-swap idiom. +In particular, please implement: + +- default constructor +```c++ +GameObjectList(); +``` + +- copy constructor +```c++ +GameObjectList(const GameObjectList& other); +``` + +- moving constructor +```c++ +GameObjectList(GameObjectList&& other) noexcept; +``` + +- assignment operator +```c++ +GameObjectList& operator=(GameObjectList other); +``` + +- swap function +```c++ +friend void swap(GameObjectList& first, GameObjectList& second); +``` \ No newline at end of file From cd604a4eb826e8acfe7a55658fe311c8c0aae9e4 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 18:58:06 +0200 Subject: [PATCH 042/137] add documentation to `DynamicScene` class, minor fixes Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/src/dynscene.cpp | 8 ++++++++ .../IntroducingObjects/task-info.yaml | 2 ++ include/dynscene.hpp | 17 +++++++++++++++++ include/gobjectlist.hpp | 2 ++ 4 files changed, 29 insertions(+) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index 305c81f..ff74248 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -26,12 +26,15 @@ void DynamicScene::processEvent(const sf::Event& event) { } void DynamicScene::update(sf::Time delta) { + // update the objects' state objects.foreach([delta] (GameObject& object) { object.update(delta); }); + // move objects according to their velocity and elapsed time objects.foreach([this, delta] (GameObject& object) { move(object, delta); }); + // compute collision information for each pair of objects objects.foreach([this] (GameObject& object) { objects.foreach([this, &object] (GameObject& other) { if (&object != &other) { @@ -39,14 +42,17 @@ void DynamicScene::update(sf::Time delta) { } }); }); + // update the list of objects updateObjectsList(); } void DynamicScene::updateObjectsList() { + // remove destroyed objects from the list objects.remove([] (const GameObject& object) { return (object.getStatus() == GameObjectStatus::DESTROYED) && (object.getKind() != GameObjectKind::PLAYER); }); + // count the number of the different kinds of objects present on the scene int consumableCount = 0; int enemyCount = 0; objects.foreach([&] (const GameObject& object) { @@ -61,6 +67,7 @@ void DynamicScene::updateObjectsList() { break; } }); + // add new objects of randomly chosen kind if there is enough room for them on the scene int dynamicObjectsCount = consumableCount + enemyCount; if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { int r = rand() % 100; @@ -115,6 +122,7 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) } void DynamicScene::draw() { + // draw all objects on the scene objects.foreach([this] (const GameObject& object) { Scene::draw(object); }); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 109d785..c1365f4 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -96,3 +96,5 @@ files: visible: true - name: task0.md visible: true + - name: task8.md + visible: true diff --git a/include/dynscene.hpp b/include/dynscene.hpp index 1372503..ea79b5f 100644 --- a/include/dynscene.hpp +++ b/include/dynscene.hpp @@ -14,16 +14,33 @@ class DynamicScene : public Scene { void initialize() override; + /** + * Process the given event. + */ void processEvent(const sf::Event &event) override; + /** + * Updates the object's state based on the elapsed time since the last update. + */ void update(sf::Time delta) override; + /** + * Draws the scene with all its game objects. + */ void draw() override; protected: + /** + * Update the list of objects present on the scene. In particular: + * - removes all destroyed objects; + * - creates new objects if there is enough room for them. + */ void updateObjectsList(); + /** + * Adds to the scene new object of the given kind. + */ std::shared_ptr addNewGameObject(GameObjectKind kind); private: diff --git a/include/gobjectlist.hpp b/include/gobjectlist.hpp index 4a44699..f14bea5 100644 --- a/include/gobjectlist.hpp +++ b/include/gobjectlist.hpp @@ -36,6 +36,8 @@ class GameObjectList { return *this; } + ~GameObjectList() = default; + friend void swap(GameObjectList& first, GameObjectList& second) { using std::swap; swap(first.head, second.head); From 4fccb43ce5f2374fa66c4d0712b7aa25798444ee Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 18:58:25 +0200 Subject: [PATCH 043/137] minor grammar fixes Signed-off-by: Evgenii Moiseenko --- MemoryManagement/LinkedList/LinkedList/task.md | 4 ++-- MemoryManagement/TypeCastsAndCStrings/CStyle/task.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MemoryManagement/LinkedList/LinkedList/task.md b/MemoryManagement/LinkedList/LinkedList/task.md index b1e65c9..caf1de9 100644 --- a/MemoryManagement/LinkedList/LinkedList/task.md +++ b/MemoryManagement/LinkedList/LinkedList/task.md @@ -55,7 +55,7 @@ void link(Node* cursor, Node* node); It should link together the `cursor` and `node` nodes, putting `node` right after `cursor`. Make sure to properly update the `next` and `prev` fields of all relevant nodes, that is, the nodes pointed by the `cursor`, `cursor->next`, and `node` pointers. -You might assume that all nodes reachable from `cursor` through `next` and `prev` +You might assume that all nodes which are reachable from `cursor` through `next` and `prev` are valid nodes and none of their `next` or `prev` pointers are null (we will see why this is true for our intended list implementation later). @@ -91,7 +91,7 @@ struct List { For this scheme to work properly, we also have to initialize the `next` and `prev` fields of the `sentry` node. We can make them both point to the `sentry` node. -Therefore, in our encoding, an empty list is modelled as +Therefore, in our encoding, an empty list is modeled as a list consisting of a single sentinel node whose `next` and `prev` pointers form a cycle. Write code for the function `initList` implementing this idea (set the `data` field of the `sentry` node to `nullptr`): diff --git a/MemoryManagement/TypeCastsAndCStrings/CStyle/task.md b/MemoryManagement/TypeCastsAndCStrings/CStyle/task.md index 5fe3111..bd2fa8c 100644 --- a/MemoryManagement/TypeCastsAndCStrings/CStyle/task.md +++ b/MemoryManagement/TypeCastsAndCStrings/CStyle/task.md @@ -8,7 +8,7 @@ However, in general, C is not a strict subset of C++, meaning that there are a lot of various [incompatibilities](https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B) between these languages. Besides that, there are a lot of language features, idioms, and patterns that differ in C and C++. -These difference give rise to the mentioned C and C++ styles of doing things. +These differences give rise to the mentioned C and C++ styles of doing things. It is important to learn to read C-style code, even if you plan to follow purely the C++ style in the future. As of today, the C language has become a "cross-platform assembly language", From 357747e399bb26f40032988d45dc2252f6a78af6 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 19:12:54 +0200 Subject: [PATCH 044/137] minor Signed-off-by: Evgenii Moiseenko --- .../IntroducingObjects/task.md | 72 ---------------- .../IntroducingObjects/task8.md | 86 ++++++++++--------- 2 files changed, 47 insertions(+), 111 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md index 1ef4d24..e69de29 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md @@ -1,72 +0,0 @@ -The `Scene` class is an _abstract class_ too — -it has pure virtual methods and thus cannot be instantiated. -This gives us the flexibility of having different implementations of the `Scene` class. - -One such implementation is given by the `StaticScene` subclass -(see files `statscene.hpp` and `statscene.cpp`). -This scene implementation is called static because it contains only -static predefined number of game objects: single player object and single consumable object. -It is certainly a downgrade from our previous version of the game, -when we learned how to create objects dynamically with the help of the linked lists. -Do not worry, we will restore this feature soon. -But for now, let us work with the static scene. - -The `Scene` class has a certain peculiarity compared to the `GameObject` class — -there could exist single unique `Scene` per one game instance. -We can express this in the code with the help of another C++ feature: `static` modifier. - -First, note that the `Scene` class has one method that stands out from the others: -it is `create()` method that has `static` modifier in front of it. -The `static` modifier, applied to a class member (either field or method), -turns this member into a _static_ member. - -Static members are not associated with the objects, instead they are associated with the class itself. -This means that in order to access a static member, you do not need an instance of the class at hand. -Instead, static members are accessed through the class name: - -```c++ -// obtains a scene instance by calling static method `create` -Scene* scene = Scene::create(); -``` - -Static members provide a convenient way to associate some methods or data fields with the class itself. -For example, the `create` method shown above provides an ability to instantiate -the scene object without revealing the actual implementation to the user of the method -(note that it returns `Scene*` instead of `StaticScene*`). -Moreover, it gives us a way to ensure that only one scene is created per each game run. -How we can achieve that — well, with the help of the `static` modifier again. - -When applied to the declaration of local variables inside functions, -`static` modifier has a different meaning. -It allows creating a _static variable_ that survives and preserves its value between the function calls. -Such variables are actually stored inside the static memory region of the program, -instead of the stack memory region where the other local function's variables reside, -hence the name _static_. - -```c++ -int foo() { - static int x = 0; - return ++x; -} - -// prints 1 -std::cout << foo() << std::endl; -// prints 2 -std::cout << foo() << std::endl; -``` - -With the help of the `static` modifier, it becomes possible to -declare static `StaticScene` variable inside `Scene::create` method -and return pointer to this variable. - -
- -Note that in this case, the address escape error does not occur. -Because the `static` variable resides in the static memory region, it lives thought the whole program execution time. -Thus, it is safe to return the address of this variable from the function. - -
- -Please implement the `create` method as described above. -If you do this correctly, you will be finally able to run the refactored game application -and see the planet and star objects on the screen. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md index 3e00a3c..c0aec87 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md @@ -1,59 +1,65 @@ -Next, we need to restore the dynamic behavior of our game — the ability +Next, we need to restore the dynamic behavior of our game — the ability to add and remove objects dynamically during the play. To do so, we will re-implement the doubly linked list data structure in terms of object-oriented programming. -Before proceeding with this task, please first finish the `Ownership` module, +Before proceeding with this task, please first finish the `Ownership` module, as we will need some concepts taught there. -First of all, instead of completely rewriting the `StaticScene` class, -we will just a new class --- `DynamicScene`, where we will implement the new dynamic functionality. +First of all, instead of completely rewriting the `StaticScene` class, +we will just add a new class --- `DynamicScene`, where we will implement the new dynamic functionality. [//]: # (Then switching between the two `Scene` implementations will be quite easy --- ) [//]: # (TODO: describe the actual scene-switching logic once it will be settled.) -Please have a look at the declaration of the `DynamicScene` class (file `dynscene.hpp`), -and its definition (file `dynscene.cpp`). You can find the brief description of its methods -in the documentation comments. +Please have a look at the declaration of the `DynamicScene` class (file `dynscene.hpp`), +and its definition (file `dynscene.cpp`). +You can find the brief description of its methods in the documentation comments. The `DynamicScene` class has a field `objects` of `GameObjectList` class. This is the main class we are going to work with in this task — it will implement the doubly linked list (its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively). -This time, using the object-oriented programming and ownership model, -we will implement a list that will own its nodes. +This time, using the object-oriented programming and ownership model, +we will implement a list that will own its nodes. The nodes will be destructed and deallocated automatically upon destruction of the list itself, and copying of the list will result in all of its nodes being copied too. -The nodes of the list are represented by the `Node` structure, -which implement the ownership semantics --- each node is owning its successor. +The nodes of the list are represented by the `Node` structure, +which implement the ownership semantics — each node is owning its successor. Let us have a closer look on the fields of the `Node` structure. - `prev` field is a plain pointer `Node*`, storing the pointer to a previous node. - It is a non-owning pointer, because if it was made an owning pointer, then it would - result into ownership relation cycle. - Indeed, giving a node `x`, `x.prev` can point to a node `y`, such that `y.next` points to `x` --- - this is clearly a cycle. -- `next` field is an owning pointer `std::unique_ptr`. - Whenever a node is destructed, all its succeeding nodes will also be destructed, - due to the ownership semantics. + It is a non-owning pointer, because if it was made an owning pointer, then it would + result into ownership cycle. + Indeed, giving a node `x`, `x.prev` can point to a node `y`, such that `y.next` points to `x` --- + this is clearly a cycle. +- `next` field is an owning pointer `std::unique_ptr`. + Whenever a node is destructed, all its succeeding nodes will also be destructed, + due to its ownership semantics. - `object` field is a shared pointer `std::shared_ptr` to a game object stored in the node. - It has the shared ownership semantics, so that the shared pointers to game objects - can be safely returned from the methods of the `GameObjectList` class. + It has the shared ownership semantics, so that the shared pointers to game objects + can be safely returned from the methods of the `GameObjectList` class. + Also, the shared ownership of game objects will allow us to implement + the copying of a list — a copy would simply store copies of shared pointers. + The static methods `link` and `unlink` implement the linking and unlinking of nodes respectively. -You have already seen them in the `LinkedList` task. Here is a reminder of their semantics: +You have already seen these functions in the `LinkedList` task. +Here is a reminder of their semantics: -- Links together the `cursor` and `node` nodes, putting `node` right after `cursor`. ```c++ static void link(Node* cursor, std::unique_ptr&& node); ``` -- Unlinks the node from its neighboring nodes. +- Links together the `cursor` and `node` nodes, putting `node` right after `cursor`. + ```c++ static void unlink(Node* node); ``` -Please implement these methods. +- Unlinks the node from its neighboring nodes. + +Please implement these methods. Pay attention to the different ownership semantics of `next`, `prev`, and `object` pointers, and use `std::move` to manage the ownership. Remember that `std::unique_ptr` transitions into `nullptr` state after ownership transfer, @@ -63,10 +69,10 @@ Next, consider the fields of the `GameObjectList` class. - `head` is an owning pointer `std::unique_ptr` to the first node of the list. - `tail` is a non-owning pointer `Node*` to the last node of the list. - It is a non-owning pointer because the pointed-to node is actually owned - by its predecessor via its `next` pointer. + It is a non-owning pointer because the pointed-to node is actually owned + by its predecessor via its `next` pointer. -Because the `head` pointer of the list is owning one, the whole sequence of nodes +Because the `head` pointer of the list is owning one, the whole sequence of nodes belonging to the list will be destroyed automatically upon destruction of the list itself. This is the reason why we left the default implementation of the class' destructor: @@ -76,25 +82,25 @@ This is the reason why we left the default implementation of the class' destruct
-Indeed, the destructor of the `GameObjectList` will call -the `~std::unique_ptr()` destructor of the `head` field. +Indeed, the destructor of the `GameObjectList` will call +the `~std::unique_ptr()` destructor of the `head` field. In turn, it will call the destructor of the `Node`, -which will call the destructor `~std::unique_ptr()` of its `next` field, -and so on, until all nodes will be destructed. +which will call the destructor `~std::unique_ptr()` of its `next` field, +and so on, until all nodes will be destructed.
-Note that in the previous implementation of the list, in the `LinkedList` task, -we use a single sentinel node to simplify the implementation of some list operating functions. +Note that in the previous implementation of the list (in the `LinkedList` task), +we used a single sentinel node to simplify the implementation of some list operating functions. Moreover, under the hood the list was organized into a cyclic list: the `next` field of the last node was pointing the first (sentinel) node. This time we cannot reuse this trick, since a cyclic list would result into ownership cycle. Therefore, we would need two sentinel nodes — one as a first node, and second as a last node. -Please take a look at the pre-defined methods `foreach` and `remove` of the list +Please take a look at the pre-defined methods `foreach` and `remove` of the list that utilize this list representation: - `foreach` applies the function given as an argument to every game object stored in the list; -- `remove` unlinks nodes (effectively removing them), whose game object satisfies predicate given as an argument. +- `remove` unlinks nodes (effectively removing them), whose game object satisfies predicate given as an argument.
@@ -104,7 +110,7 @@ We will have a closer look at this type in the later modules of the course.
-Now, please implement the method inserting a game object into the beginning of the list: +Now, please implement the method inserting a game object into the beginning of the list: ```c++ void insert(const std::shared_ptr& object); @@ -114,9 +120,9 @@ Keep in mind that: - the first and the last nodes should remain sentinel nodes; - only the sentinel nodes can store null pointer `nullptr` inside `object` field. -In order to complete this task, also finish the implementation of the ownership semantics of +In order to complete this task, also finish the implementation of the ownership semantics of the `GameObjectList` class, following the rule-of-file and copy-and-swap idiom. -In particular, please implement: +In particular, please implement: - default constructor ```c++ @@ -141,4 +147,6 @@ GameObjectList& operator=(GameObjectList other); - swap function ```c++ friend void swap(GameObjectList& first, GameObjectList& second); -``` \ No newline at end of file +``` + +Once you do this, you should be able to run the instance of the game with the new dynamic scene! From e793119c38bfe83a62035407dbf26d65820e3ed1 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 19:13:25 +0200 Subject: [PATCH 045/137] mark the lesson as non-template-based Signed-off-by: Evgenii Moiseenko --- ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 4de47f2..2d7600f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -1,5 +1,6 @@ type: framework custom_name: Classes And Objects content: -- OperatorsOverloading -- IntroducingObjects + - OperatorsOverloading + - IntroducingObjects +is_template_based: false From a8bbd1c9e15e20e289032d0dba6554bd4a30a899 Mon Sep 17 00:00:00 2001 From: Evgenii Moiseenko Date: Mon, 16 Oct 2023 19:20:00 +0200 Subject: [PATCH 046/137] make `GameObject::move` protected Signed-off-by: Evgenii Moiseenko --- include/gobject.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/gobject.hpp b/include/gobject.hpp index 18d6522..9bb3887 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -44,11 +44,6 @@ class GameObject { */ virtual Rectangle getBoundingBox() const = 0; - /** - * Moves an object in a direction given by the passed vector. - */ - void move(Point2D vector); - /** * Updates the state of an object based on the elapsed time. * @@ -94,6 +89,11 @@ class GameObject { */ virtual void setPosition(Point2D position) = 0; + /** + * Moves an object in a direction given by the passed vector. + */ + void move(Point2D vector); + }; From a89fff8122fc322829112a71969502ff9cf2ac24 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 17:12:11 +0100 Subject: [PATCH 047/137] improve documentation for the game's classes Signed-off-by: Evgeniy Moiseenko --- include/cgobject.hpp | 12 ++--- include/collision.hpp | 7 +++ include/dynscene.hpp | 7 +++ include/enemy.hpp | 25 +++++++++ include/enums.hpp | 19 +++++-- include/gobject.hpp | 8 +-- include/gobjectlist.hpp | 70 ++++++++++++++++++++++++- include/player.hpp | 3 +- include/scene.hpp | 111 ++++++++++++++++++++++++++++++++++++++++ include/statscene.hpp | 17 ++++++ include/textures.hpp | 28 +++++++++- 11 files changed, 289 insertions(+), 18 deletions(-) diff --git a/include/cgobject.hpp b/include/cgobject.hpp index d22f9d0..ad66341 100644 --- a/include/cgobject.hpp +++ b/include/cgobject.hpp @@ -22,13 +22,6 @@ class CircleGameObject : public GameObject { */ Point2D getPosition() const override; - - /** - * Changes position of the circle's center. - */ - void setPosition(Point2D position) override; - - /** * Returns the current status of the object. */ @@ -51,6 +44,11 @@ class CircleGameObject : public GameObject { protected: + /** + * Changes position of the circle's center. + */ + void setPosition(Point2D position) override; + /** * Changes the current status of the object. */ diff --git a/include/collision.hpp b/include/collision.hpp index b79a9c1..1c15465 100644 --- a/include/collision.hpp +++ b/include/collision.hpp @@ -3,11 +3,18 @@ #include "circle.hpp" +/** + * This structure provides a way to store information about a collision, + * such as whether a collision occurred and the distance between colliding objects. + */ struct CollisionInfo { bool collide; float distance; }; +/** + * This function takes in two Circle objects and calculates the collision information between them. + */ CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2); #endif // CPPBASICS_COLLISION_HPP diff --git a/include/dynscene.hpp b/include/dynscene.hpp index ea79b5f..4847ea0 100644 --- a/include/dynscene.hpp +++ b/include/dynscene.hpp @@ -9,9 +9,16 @@ #include "gobjectlist.hpp" #include "player.hpp" +/** + * DynamicScene is a scene capable of managing and updating the list of game objects in the scene: + * objects might be added to or removed from the scene dynamically. + */ class DynamicScene : public Scene { public: + /** + * Initializes the scene. + */ void initialize() override; /** diff --git a/include/enemy.hpp b/include/enemy.hpp index e8ce9e2..7e057b0 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -3,18 +3,43 @@ #include "cgobject.hpp" + +/** + * A class representing an enemy object in the game. + */ class EnemyObject : public CircleGameObject { public: + + /** + * Constructor that initializes enemy's shape and position. + */ EnemyObject(); + /** + * Returns the `ENEMY` kind. + */ GameObjectKind getKind() const override; + /** + * Returns the current velocity of the object. + * Enemy objects change their velocity randomly on every fixed period of time. + */ Point2D getVelocity() const override; + /** + * Updates the state of an object based on the elapsed time. + * Changes the velocity on every fixed period of time. + */ void update(sf::Time delta) override; + /** + * Handles the potential collision of enemy object with another object. + */ void onCollision(const GameObject &object, const CollisionInfo &info) override; + /** + * Retrieves the texture associated with the enemy object based on its current status. + */ const sf::Texture* getTexture(TextureManager& textureManager) const override; private: diff --git a/include/enums.hpp b/include/enums.hpp index ac948b0..29599ed 100644 --- a/include/enums.hpp +++ b/include/enums.hpp @@ -1,12 +1,25 @@ #ifndef CPPBASICS_ENUMS_HPP #define CPPBASICS_ENUMS_HPP -enum class GameObjectStatus { - NORMAL, CONCERNED, DESTROYED, -}; +/** + * This enumeration is used to categorize different kinds of game objects. + * Each game object can have one of the following kinds - PLAYER, CONSUMABLE, or ENEMY. + * The kind of a game object can be used to determine its behavior or interaction with the game. + */ enum class GameObjectKind { PLAYER, CONSUMABLE, ENEMY }; +/** + * Represents the status of a game object. + * - NORMAL - represents a status of an alive object in its normal state. + * - CONCERNED - represents a status of an alive object when it is in some dangerous situation, + * the exact meaning of which depends on the particular kind of the object. + * - DESTROYED - represents a status of a destroyed object. + */ + enum class GameObjectStatus { + NORMAL, CONCERNED, DESTROYED, +}; + #endif // CPPBASICS_ENUMS_HPP diff --git a/include/gobject.hpp b/include/gobject.hpp index 9bb3887..704d3e6 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -54,7 +54,7 @@ class GameObject { /** * Handler function called when a potential collision occurs between this object and another object. * - * @param object The collided object. + * @param object The potentially collided object. * @param info The information about the potential collision. */ virtual void onCollision(const GameObject& object, const CollisionInfo& info) = 0; @@ -81,14 +81,16 @@ class GameObject { */ virtual ~GameObject() = default; -private: - friend class Scene; +protected: /** * Changes the current position of the object. */ virtual void setPosition(Point2D position) = 0; +private: + friend class Scene; + /** * Moves an object in a direction given by the passed vector. */ diff --git a/include/gobjectlist.hpp b/include/gobjectlist.hpp index f14bea5..ad795e8 100644 --- a/include/gobjectlist.hpp +++ b/include/gobjectlist.hpp @@ -6,9 +6,19 @@ #include "gobject.hpp" + +/** + * @class GameObjectList + * @brief Represents a list of game objects. + * + * The class provides functionality to insert, remove, and iterate over game objects. + */ class GameObjectList { public: + /** + * Constructs an empty list. + */ GameObjectList() { head = std::make_unique(); head->next = std::make_unique(); @@ -16,6 +26,10 @@ class GameObjectList { tail->prev = head.get(); } + /** + * Constructs a copy of the given list. + * The game objects itself are not copied, but shared by the original list and its copy. + */ GameObjectList(const GameObjectList& other) : GameObjectList() { Node* cursor = head.get(); Node* curr = other.head->next.get(); @@ -27,23 +41,39 @@ class GameObjectList { } } + /** + * Moves game objects from another list. + * The list passed as the argument becomes empty. + */ GameObjectList(GameObjectList&& other) noexcept : GameObjectList() { swap(*this, other); } + /** + * Re-assigns the list (either by copying or by moving elements from another list). + */ GameObjectList& operator=(GameObjectList other) { swap(*this, other); return *this; } + /** + * Destructs the list. + */ ~GameObjectList() = default; - friend void swap(GameObjectList& first, GameObjectList& second) { + /** + * Swaps the contents of two game object lists. + */ + inline friend void swap(GameObjectList& first, GameObjectList& second) { using std::swap; swap(first.head, second.head); swap(first.tail, second.tail); } + /** + * Inserts a game object passed as a std::shared_ptr into the list. + */ void insert(const std::shared_ptr& object) { if (!object) { return; @@ -53,6 +83,11 @@ class GameObjectList { link(head.get(), std::move(node)); } + /** + * Removes objects from the collection for which the given predicate evaluates to true. + * + * @param pred The predicate function used to determine whether an object should be removed. + */ void remove(const std::function& pred) { Node* curr = head->next.get(); while (curr != tail) { @@ -64,6 +99,12 @@ class GameObjectList { } } + + /** + * Applies a given function to each game object inside the list. + * + * @param apply A function that accept a single game object reference parameter. + */ void foreach(const std::function& apply) { Node* curr = head->next.get(); while (curr != tail) { @@ -73,12 +114,26 @@ class GameObjectList { } private: - struct Node { + + /** + * A node of the doubly-linked list of game objects. + */ + struct Node { Node* prev = nullptr; std::unique_ptr next; std::shared_ptr object; }; + + /** + * Links a new node into the list after the given cursor node. + * + * @param cursor A pointer to the cursor node. + * @param node A unique pointer to the new node to be linked. + * + * @note The ownership of the new node is transferred to the cursor. + * @note The cursor and node should not be null pointers, otherwise this function will have undefined behavior. + */ static void link(Node* cursor, std::unique_ptr&& node) { cursor->next->prev = node.get(); node->next = std::move(cursor->next); @@ -86,6 +141,17 @@ class GameObjectList { cursor->next = std::move(node); } + /** + * Unlinks a given node from a linked list it currently belongs to. + * It does so by updating the pointers of adjacent nodes. + * + * @param node The node to be unlinked from the linked list. + * + * @note The provided node should be a valid node in the linked list and should + * not be the head or tail of the list. + * + * @warning This function does not free the memory occupied by the unlinked node. + */ static void unlink(Node* node) { node->next->prev = node->prev; node->prev->next = std::move(node->next); diff --git a/include/player.hpp b/include/player.hpp index 1dccd97..c839fae 100644 --- a/include/player.hpp +++ b/include/player.hpp @@ -5,8 +5,7 @@ /** - * The PlayerObject class represents a player in the game. - * It provides additional behavior and properties specific to players. + * A class represents a player in the game. */ class PlayerObject : public CircleGameObject { public: diff --git a/include/scene.hpp b/include/scene.hpp index 456e574..f03c733 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -7,47 +7,158 @@ #include "gobject.hpp" #include "textures.hpp" +/** + * The Scene class provides functionalities for managing and rendering game scenes. + */ class Scene { public: + /** + * Constructs the scene. + */ Scene(); + /** + * Destructs the scene. + */ virtual ~Scene() = default; + /** + * The run function implements the scene management logic. + * It initializes the scene, and while scene is active, it continues to run the main loop of the scene. + * In each iteration of the loop, it processes any possible input, + * updates the scene according to the delta time, and finally renders the scene. + */ void run(); + /** + * Creates a new scene. + * + * @return A pointer to the newly created scene. + * + * @note This function does not transfer ownership of the scene to the caller. + * The lifetime of the scene is the entire duration of the program, and thus there is no need + * to manually delete the returned pointer. + */ static Scene* create(); protected: + /** + * Initializes the scene. + * + * @note This function must be implemented in any derived scene class. + */ virtual void initialize() = 0; + /** + * Closes the scene, forcing it to exit its running loop. + */ void close(); + /** + * Processes all the input events accumulated in the event queue by passing them to the Scene::processEvent method. + */ void processInput(); + /** + * Processes single input event, such as player pressing some button, resizing the game window, etc. + * + * @param event the processing event. + * + * @note This function must be implemented in any derived scene class. + */ virtual void processEvent(const sf::Event& event) = 0; + /** + * Set the position of a game object. + * Ensures that the object fits within the bounds of the scene. + * + * @param object the game object which position to be set. + * @param position the new position of the object. + */ void setObjectPosition(GameObject& object, Point2D position); + /** + * Moves a game object on the scene by the given vector. + * Ensures that the object fits within the bounds of the scene. + * + * @param object The game object to be moved. + * @param vector The vector specifying the movement. + */ void move(GameObject& object, Point2D vector); + /** + * Moves a game object on the scene based on a given time delta and object's velocity. + * + * @param object The game object to be moved. + * @param delta The time delta. + */ void move(GameObject& object, sf::Time delta); + /** + * @brief Fits the game object into the bound of the scene. + * The fitting operation may involve adjusting object's position. + * + * @param object the object to be fit into the scene. + */ void fitInto(GameObject& object); + /** + * Returns the bounding box of the scene. + * The bounding box defines the smallest rectangle that completely encloses the scene. + * + * @return The bounding box of the scene as a rectangle. + */ Rectangle getBoundingBox() const; + /** + * Checks for potential collision between two game objects and notifies the objects about the result of the check + * by calling their GameObject::onCollision methods and provided with the CollisionInfo structure. + * + * @param object1 The first game object to check for collision. + * @param object2 The second game object to check for collision. + */ void detectCollision(GameObject& object1, GameObject& object2); + /** + * Updates the scene state based on time elapsed since the last update. + * + * @param delta The time elapsed since the last update. + * + * @note This function must be implemented in any derived scene class. + */ virtual void update(sf::Time delta) = 0; + /** + * Renders the whole scene on the window: resets the window, draws the background and then + * delegated to the Scene::draw method to draw all game objects belonging to the scene. + */ void render(); + /** + * Renders the given game object on the window. + * + * @param object The game object to be rendered. + */ void draw(const GameObject& object); + /** + * Draws all the game objects belonging to the scene on the window. + * + * @note This function must be implemented in any derived scene class. + */ virtual void draw() = 0; + /** + * Returns the sprite to draw on the background of the scene. + * + * This method returns the background details of the current instance. + * The background information may vary depending on the implementation and context. + * + * @return A constant reference to the background information. + */ + sf::Sprite background() const; private: diff --git a/include/statscene.hpp b/include/statscene.hpp index e0224e9..83a6fb7 100644 --- a/include/statscene.hpp +++ b/include/statscene.hpp @@ -8,15 +8,32 @@ #include "consumable.hpp" #include "enemy.hpp" + +/** + * StaticScene is a scene operating a static fixed collection of game objects, + * namely single player, single consumable, and single enemy objects. + */ class StaticScene : public Scene { public: + /** + * Initializes the scene. + */ void initialize() override; + /** + * Process the given event. + */ void processEvent(const sf::Event &event) override; + /** + * Updates the object's state based on the elapsed time since the last update. + */ void update(sf::Time delta) override; + /** + * Draws the scene with all its game objects. + */ void draw() override; private: diff --git a/include/textures.hpp b/include/textures.hpp index ef768b9..15b40b6 100644 --- a/include/textures.hpp +++ b/include/textures.hpp @@ -5,7 +5,12 @@ #include -enum class GameTextureID { + +/** + * The enumeration represents the IDs of textures used in the game. + * These IDs are used to identify specific textures when loading or rendering game graphics. + */ + enum class GameTextureID { SPACE, PLANET, PLANET_DEAD, @@ -15,11 +20,32 @@ enum class GameTextureID { SIZE }; + +/** + * The TextureManager class manages the loading and retrieval of textures for the game. + */ class TextureManager { public: + + /** + * Initializes the texture manager. + * + * @return true if the initialization was successful, false otherwise. + */ bool initialize(); + + /** + * Get the texture associated with the specified game texture ID. + * + * @param id The ID of the game texture to retrieve. + * @return The texture associated with the specified game texture ID. + * + * @note this method does not transfer the ownership of the texture object, + * since the texture manager itself is the owner of all the textures. + */ const sf::Texture* getTexture(GameTextureID id) const; + private: static const size_t SIZE = static_cast(GameTextureID::SIZE); sf::Texture textures[SIZE]; From 49aab7f09b8cf46ce36487a7ce498db994d4a7b8 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 18:58:41 +0100 Subject: [PATCH 048/137] rename CONCERNED to WARNED Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/consumable.cpp | 6 +++--- .../ClassesAndObjects/IntroducingObjects/task3.md | 2 +- .../ClassesAndObjects/IntroducingObjects/task6.md | 6 +++--- .../ClassesAndObjects/IntroducingObjects/task7.md | 2 +- include/constants.hpp | 2 +- include/enums.hpp | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index de1c448..488e9ce 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -28,8 +28,8 @@ void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo setStatus(GameObjectStatus::DESTROYED); return; } - if (info.distance < CONSUMABLE_CONCERNED_MULTIPLIER * getCircle().radius) { - setStatus(GameObjectStatus::CONCERNED); + if (info.distance < CONSUMABLE_WARNED_MULTIPLIER * getCircle().radius) { + setStatus(GameObjectStatus::WARNED); return; } } @@ -38,7 +38,7 @@ const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) switch (getStatus()) { case GameObjectStatus::NORMAL: return textureManager.getTexture(GameTextureID::STAR); - case GameObjectStatus::CONCERNED: + case GameObjectStatus::WARNED: return textureManager.getTexture(GameTextureID::STAR_CONCERNED); case GameObjectStatus::DESTROYED: return nullptr; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md index 9ba062c..b4edc9f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md @@ -35,7 +35,7 @@ with the following logic: * under `NORMAL` status, the player object should have `PLANET` texture; * under `DESTROYED` status, the player object should have `PLANET_DEAD` texture; * under `NORMAL` status, the consumable object should have `STAR` texture; -* under `CONCERNED` status, the consumable object should have `STAR_CONCERNED` texture; +* under `WARNED` status, the consumable object should have `STAR_CONCERNED` texture; * under `DESTROYED` status, the consumable object should not be displayed.
diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md index 98e35b8..4517556 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md @@ -62,13 +62,13 @@ while the `struct` keyword is used to declare POD types. Now, with the help of the new collision detection function, your task is to re-implement the behavior of consumable objects. - Upon collision with another object, the consumable should change its status into `DESTROYED`. -- When another object is approaching the consumable, it should change its status into `CONCERNED`. +- When another object is approaching the consumable, it should change its status into `WARNED`. This should happen whenever the distance between another object and consumable is less than `C * r`, where - `r` is the radius of consumable object, - `C` is a special multiplier constant ```c++ -const float CONSUMABLE_CONCERNED_MULTIPLIER = 6.0f; +const float CONSUMABLE_WARNED_MULTIPLIER = 6.0f; ``` To do so, please implement the `onCollision` method of `ConsumableObject` class. @@ -80,7 +80,7 @@ containing the information about the distance between objects and whether they actually collided. It is up to the method's implementation to decide what to do with this information. -Note that when the consumable object becomes `CONCERNED`, it should eventually +Note that when the consumable object becomes `WARNED`, it should eventually change its status back to `NORMAL` when other objects left its nearby area. One way to achieve this is by also modifying the implementation of `update` method. This method is also periodically called from the `Scene` class to give diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md index b5a0f4c..1171286 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md @@ -7,7 +7,7 @@ this task can be accomplished quite easily. The enemy object should behave as follows: - it should move on a scene in a random direction, changing it periodically; - it should consume the star objects when colliding with them; -- star objects should change their status into `CONCERNED` when +- star objects should change their status into `WARNED` when the enemy object is approaching them (similarly as they do when the player object is approaching); - when the enemy object collides with the player object, the latter diff --git a/include/constants.hpp b/include/constants.hpp index ceca800..58da132 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -24,6 +24,6 @@ const float CONSUMABLE_START_Y = 150.0f; const float ENEMY_START_X = 100.0f; const float ENEMY_START_Y = 450.0f; -const float CONSUMABLE_CONCERNED_MULTIPLIER = 6.0f; +const float CONSUMABLE_WARNED_MULTIPLIER = 6.0f; #endif // CPPBASICS_CONSTANTS_HPP diff --git a/include/enums.hpp b/include/enums.hpp index 29599ed..d16b002 100644 --- a/include/enums.hpp +++ b/include/enums.hpp @@ -14,12 +14,12 @@ enum class GameObjectKind { /** * Represents the status of a game object. * - NORMAL - represents a status of an alive object in its normal state. - * - CONCERNED - represents a status of an alive object when it is in some dangerous situation, + * - WARNED - represents a status of an alive object when it is in some dangerous situation, * the exact meaning of which depends on the particular kind of the object. * - DESTROYED - represents a status of a destroyed object. */ enum class GameObjectStatus { - NORMAL, CONCERNED, DESTROYED, + NORMAL, WARNED, DESTROYED, }; #endif // CPPBASICS_ENUMS_HPP From 34dcb5160096c95e5410bb979c03212620a6816f Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 19:16:10 +0100 Subject: [PATCH 049/137] fix the concerned behavior of consumable objects Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/consumable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index 488e9ce..d03f046 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -21,7 +21,7 @@ void ConsumableObject::update(sf::Time delta) { } void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { - if (getStatus() == GameObjectStatus::DESTROYED) { + if (getStatus() == GameObjectStatus::DESTROYED || object.getKind() == GameObjectKind::CONSUMABLE) { return; } if (info.collide) { From c2c2ca9a592b7275fb257326696655a00a365449 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 19:38:44 +0100 Subject: [PATCH 050/137] fix the moving behavior of enemy objects Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/enemy.cpp | 10 ++++++---- .../ClassesAndObjects/IntroducingObjects/src/utils.cpp | 4 ++++ .../ClassesAndObjects/IntroducingObjects/task7.md | 2 +- include/utils.hpp | 2 ++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 9f61d85..20e3619 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -22,10 +22,12 @@ void EnemyObject::update(sf::Time delta) { if (updateTimer < sf::seconds(1.0f)) return; updateTimer = sf::milliseconds(0.0f); - velocity = { 0.0f, 0.0f }; - velocity.x = generateBool() ? 1.0f : -1.0f; - velocity.y = generateBool() ? 1.0f : -1.0f; - velocity = SPEED * velocity; + Direction direction1 = static_cast(generateInt(0, 3)); + Direction direction2 = static_cast(generateInt(0, 3)); + Point2D directionVector = (direction1 == direction2) + ? getDirection(direction1) + : (getDirection(direction1) + getDirection(direction2)); + velocity = SPEED * directionVector; } void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp index d5fecb3..8a2cfbd 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp @@ -8,6 +8,10 @@ float generateFloat(float min, float max) { return min + (rand() / (RAND_MAX / (max - min))); } +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + bool generateBool(float prob) { return generateFloat(0.0f, 1.0f) < prob; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md index 1171286..8c70287 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md @@ -13,7 +13,7 @@ The enemy object should behave as follows: - when the enemy object collides with the player object, the latter should change its status into `DESTROYED`, becoming effectively immovable. -Let us proceed to implementation of this behavior. +Let us proceed to the implementation of this behavior. First, take a look at the declaration of the `EnemyObject` class. It again inherits from the `CircleGameObject` class, so it already has a corresponding circle shape data and behavior attached to it. diff --git a/include/utils.hpp b/include/utils.hpp index 936784c..a83613e 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -9,6 +9,8 @@ float distance(Point2D a, Point2D b); float generateFloat(float min, float max); +int generateInt(int min, int max); + bool generateBool(float prob = 0.5f); Point2D generatePoint(const Rectangle& boundingBox); From c76f5cb4bb97a7252a00b8d8689e6c4b1c8ec6b9 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 19:53:36 +0100 Subject: [PATCH 051/137] split game object list class into header/source files Signed-off-by: Evgeniy Moiseenko --- .../IntroducingObjects/src/gobjectlist.cpp | 77 ++++++++++++++++++- include/gobjectlist.hpp | 75 +++--------------- 2 files changed, 84 insertions(+), 68 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp index 52a7a59..ea0fd08 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp @@ -1,3 +1,74 @@ -// -// Created by eupp on 24.08.23. -// +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + cursor->next->prev = node.get(); + node->next = std::move(cursor->next); + node->prev = cursor; + cursor->next = std::move(node); +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + node->next->prev = node->prev; + node->prev->next = std::move(node->next); +} + +void GameObjectList::insert(const std::shared_ptr &object) { + if (!object) { + return; + } + std::unique_ptr node = std::make_unique(); + node->object = object; + link(head.get(), std::move(node)); +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + head = std::make_unique(); + head->next = std::make_unique(); + tail = head->next.get(); + tail->prev = head.get(); +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + Node* cursor = head.get(); + Node* curr = other.head->next.get(); + while (curr != other.tail) { + link(cursor, std::make_unique()); + cursor = cursor->next.get(); + cursor->object = curr->object; + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + swap(*this, other); +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + swap(*this, other); + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + swap(first.head, second.head); + swap(first.tail, second.tail); +} diff --git a/include/gobjectlist.hpp b/include/gobjectlist.hpp index ad795e8..a28bf98 100644 --- a/include/gobjectlist.hpp +++ b/include/gobjectlist.hpp @@ -19,43 +19,24 @@ class GameObjectList { /** * Constructs an empty list. */ - GameObjectList() { - head = std::make_unique(); - head->next = std::make_unique(); - tail = head->next.get(); - tail->prev = head.get(); - } + GameObjectList(); /** * Constructs a copy of the given list. * The game objects itself are not copied, but shared by the original list and its copy. */ - GameObjectList(const GameObjectList& other) : GameObjectList() { - Node* cursor = head.get(); - Node* curr = other.head->next.get(); - while (curr != other.tail) { - link(cursor, std::make_unique()); - cursor = cursor->next.get(); - cursor->object = curr->object; - curr = curr->next.get(); - } - } + GameObjectList(const GameObjectList& other); /** * Moves game objects from another list. * The list passed as the argument becomes empty. */ - GameObjectList(GameObjectList&& other) noexcept : GameObjectList() { - swap(*this, other); - } + GameObjectList(GameObjectList&& other) noexcept; /** * Re-assigns the list (either by copying or by moving elements from another list). */ - GameObjectList& operator=(GameObjectList other) { - swap(*this, other); - return *this; - } + GameObjectList& operator=(GameObjectList other); /** * Destructs the list. @@ -65,53 +46,26 @@ class GameObjectList { /** * Swaps the contents of two game object lists. */ - inline friend void swap(GameObjectList& first, GameObjectList& second) { - using std::swap; - swap(first.head, second.head); - swap(first.tail, second.tail); - } + friend void swap(GameObjectList& first, GameObjectList& second); /** * Inserts a game object passed as a std::shared_ptr into the list. */ - void insert(const std::shared_ptr& object) { - if (!object) { - return; - } - std::unique_ptr node = std::make_unique(); - node->object = object; - link(head.get(), std::move(node)); - } + void insert(const std::shared_ptr& object); /** * Removes objects from the collection for which the given predicate evaluates to true. * * @param pred The predicate function used to determine whether an object should be removed. */ - void remove(const std::function& pred) { - Node* curr = head->next.get(); - while (curr != tail) { - Node* next = curr->next.get(); - if (pred(*curr->object)) { - unlink(curr); - } - curr = next; - } - } - + void remove(const std::function& pred); /** * Applies a given function to each game object inside the list. * * @param apply A function that accept a single game object reference parameter. */ - void foreach(const std::function& apply) { - Node* curr = head->next.get(); - while (curr != tail) { - apply(*curr->object); - curr = curr->next.get(); - } - } + void foreach(const std::function& apply); private: @@ -124,7 +78,6 @@ class GameObjectList { std::shared_ptr object; }; - /** * Links a new node into the list after the given cursor node. * @@ -134,12 +87,7 @@ class GameObjectList { * @note The ownership of the new node is transferred to the cursor. * @note The cursor and node should not be null pointers, otherwise this function will have undefined behavior. */ - static void link(Node* cursor, std::unique_ptr&& node) { - cursor->next->prev = node.get(); - node->next = std::move(cursor->next); - node->prev = cursor; - cursor->next = std::move(node); - } + static void link(Node* cursor, std::unique_ptr&& node); /** * Unlinks a given node from a linked list it currently belongs to. @@ -152,10 +100,7 @@ class GameObjectList { * * @warning This function does not free the memory occupied by the unlinked node. */ - static void unlink(Node* node) { - node->next->prev = node->prev; - node->prev->next = std::move(node->next); - } + static void unlink(Node* node); std::unique_ptr head; Node* tail; From a1af1ed8f731c79f8d1b5005c50518398cf01b7e Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 20:57:27 +0100 Subject: [PATCH 052/137] expand the operators overloading task Signed-off-by: Evgeniy Moiseenko --- .../IntroducingObjects/src/operators.cpp | 25 ++++++++ .../IntroducingObjects/src/rectangle.cpp | 12 ++++ .../OperatorsOverloading/task.md | 63 ++++++++++++++++--- include/operators.hpp | 12 +++- include/rectangle.hpp | 13 +--- 5 files changed, 104 insertions(+), 21 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp index 75a298d..19fa160 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp @@ -14,4 +14,29 @@ Point2D operator-(Point2D a, Point2D b) { Point2D operator*(float s, Point2D a) { return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp index 2d820a2..c25492c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp @@ -2,6 +2,18 @@ #include "operators.hpp" +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { return rect; diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md index 7fcd1f9..9591758 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md @@ -1,5 +1,5 @@ Before we dive into object-oriented programming, -we will learn about another useful feature of C++ language +we will learn about another useful feature of the C++ language that can help to make your code easier to read and understand. This feature is __operator overloading,__ and it allows you to define various operators, like arithmetic operators `+`, `-`, `*`, @@ -26,10 +26,10 @@ Point2D move(Point2D position, Point2D velocity, float delta) { } ``` -As you may notice, the meaning of the latter code fragment is less evident -and harder to grasp at the first sight. +As you may notice, just by looking at the code, +it is much harder to grasp what is happening in the second code fragment. Fortunately, with operator overloading, it is possible to make -the two versions of the function look identical! +the two versions of the `move` function look identical! ```c++ Point2D move(Point2D position, Point2D velocity, float delta) { @@ -46,9 +46,58 @@ Point2D operator+(Point2D a, Point2D b) { } ``` -We have already prepared for you a template for several operator functions, -that would make our work with `Point2D` data type much more pleasant. -Your job is to provide an implementation for these functions. +The C++ language allows overloading a [large number of operators](https://en.cppreference.com/w/cpp/language/operators). +Please remember, just like any other feature, it is possible to abuse the operator overloading feature. +It is recommended to overload the operators only if the corresponding notation +has a natural interpretation for your custom data type! + +Your next task is to implement several overloaded operators for the data types used in our game. + +Let us start with the familiar operations on `Point2D` data type. +Please overload the arithmetic operators `+`, `-`, `*` for this data type +(their signatures are already given in the task template). It is allowed to use `add` and `mul` functions implemented on previous steps. +Note the difference between subtraction operator and unary minus operator — +the former subtracts the coordinates of one point from another, +while the latter just changes the sign of both coordinates of a single point. + +Arithmetic operators also do have natural interpretation for shape data types, such as `Circle` and `Rectangle`. + +
+ +Please note that the rectangle is defined by two points — its top-left and bottom-right corners: + +```c++ +struct Rectangle { + Point2D topLeft; + Point2D botRight; +}; +``` + +
+ +If we are adding a point to a shape, then the point is interpreted as a vector and +the shape should be moved in the direction of this vector. +* For `Circle` shape it is sufficient to add the point to the center of the circle. +* For `Rectangle`, it is required to add the point to both corners of the rectangle. + +Multiplying a shape by a scalar should perform the scaling operation. +* For `Circle`, it is sufficient to multiply the radius to the scalar. +* For `Rectangle` the implementation is a bit trickier. + It is required to scale the width and height of the rectangle and then recompute its bottom-right corner. + You might use the pre-defined functions `width` and `height` to get + the corresponding properties of the rectangle (defined in the file `rectangle.hpp`). + +```c++ +float width(const Rectangle& rect) { + return rect.botRight.x - rect.topLeft.x; +} + +float height(const Rectangle& rect) { + return rect.botRight.y - rect.topLeft.y; +} +``` + +Finally, it would be convenient to overload equality comparison operators for all the data types mentioned above. [//]: # (TODO: hint about IO streams overloads) \ No newline at end of file diff --git a/include/operators.hpp b/include/operators.hpp index 87c4bba..b167919 100644 --- a/include/operators.hpp +++ b/include/operators.hpp @@ -5,6 +5,7 @@ #include "point.hpp" #include "circle.hpp" +#include "rectangle.hpp" #include "direction.hpp" inline std::ostream& operator<<(std::ostream& os, const Point2D& p) { @@ -35,11 +36,16 @@ inline std::ostream& operator<<(std::ostream& os, Direction direction) { } Point2D operator+(Point2D a, Point2D b); - +Point2D operator-(Point2D a, Point2D b); Point2D operator-(Point2D a); +Point2D operator*(float s, Point2D a); -Point2D operator-(Point2D a, Point2D b); +Circle operator+(Circle c, Point2D v); +Circle operator-(Circle c, Point2D v); +Circle operator*(float s, Circle c); -Point2D operator*(float s, Point2D a); +Rectangle operator+(Rectangle r, Point2D v); +Rectangle operator-(Rectangle r, Point2D v); +Rectangle operator*(float s, Rectangle r); #endif // CPPBASICS_OPERATORS_HPP diff --git a/include/rectangle.hpp b/include/rectangle.hpp index d9b95eb..6355839 100644 --- a/include/rectangle.hpp +++ b/include/rectangle.hpp @@ -2,7 +2,6 @@ #define CPPBASICS_RECTANGLE_HPP #include "point.hpp" -#include "operators.hpp" struct Rectangle { Point2D topLeft; @@ -21,17 +20,9 @@ inline float height(const Rectangle& rect) { return rect.botRight.y - rect.topLeft.y; } -inline Point2D center(const Rectangle& rect) { - // TODO: explain this C++ initialization syntax - return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; -} +Point2D center(const Rectangle& rect); -inline Rectangle createRectangle(Point2D p1, Point2D p2) { - Rectangle rect; - rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; - rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; - return rect; -} +Rectangle createRectangle(Point2D p1, Point2D p2); Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect); From f7c6a42f77fd3c81171ddf540d05ef763ea7cd25 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 21:15:02 +0100 Subject: [PATCH 053/137] add additional info about input/output operator overloads Signed-off-by: Evgeniy Moiseenko --- .../OperatorsOverloading/task.md | 37 +++++++++++++++- include/operators.hpp | 42 +++++++++++++------ 2 files changed, 65 insertions(+), 14 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md index 9591758..1511a8b 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md @@ -100,4 +100,39 @@ float height(const Rectangle& rect) { Finally, it would be convenient to overload equality comparison operators for all the data types mentioned above. -[//]: # (TODO: hint about IO streams overloads) \ No newline at end of file +
+ +In the C++ language, it is also possible to overload the input/output operators +for reading from or writing to the terminal. +Here is an example of corresponding overloads for the `Point2D` data type: + +```c++ +// output operator overload +std::ostream& operator<<(std::ostream& os, const Point2D& p) { + return os << "(" << p.x << ", " << p.y << ")"; +} + +// input operator overload +std::istream& operator>>(std::istream& is, Point2D& p) { + return is >> p.x >> p.y; +} +``` + +Notice the arguments of type `std::ostream` and `std::istream` — those are +output and input streams respectively. +We have not yet covered them in this course, that would be a topic of the next lessons. +For now, however, it is sufficient to know that `std::cout` and `std::cin` +(which we have seen in previous lessons!) are particular objects of these classes. + +Therefore, with the help of the overloads given above, it becomes possible to write the following code: +```c++ +Point2D point; +std::cout << "Please enter coordinates of point (x, y)" << std::endl; +std::cin >> point; +std::cout << "Your point is " << point << std::endl; +``` + +You might find the overloads of the input/output operators +for the other data types used in our game in the file `operators.hpp`. + +
diff --git a/include/operators.hpp b/include/operators.hpp index b167919..fd00c0f 100644 --- a/include/operators.hpp +++ b/include/operators.hpp @@ -8,14 +8,43 @@ #include "rectangle.hpp" #include "direction.hpp" +Point2D operator+(Point2D a, Point2D b); +Point2D operator-(Point2D a, Point2D b); +Point2D operator-(Point2D a); +Point2D operator*(float s, Point2D a); + +Circle operator+(Circle c, Point2D v); +Circle operator-(Circle c, Point2D v); +Circle operator*(float s, Circle c); + +Rectangle operator+(Rectangle r, Point2D v); +Rectangle operator-(Rectangle r, Point2D v); +Rectangle operator*(float s, Rectangle r); + inline std::ostream& operator<<(std::ostream& os, const Point2D& p) { return os << "(" << p.x << ", " << p.y << ")"; } +inline std::istream& operator>>(std::istream& is, Point2D& p) { + return is >> p.x >> p.y; +} + inline std::ostream& operator<<(std::ostream& os, const Circle& c) { return os << "{ " << "center: " << c.center << "; radius: " << c.radius << " }"; } +inline std::istream& operator>>(std::istream& is, Circle& c) { + return is >> c.center >> c.radius; +} + +inline std::ostream& operator<<(std::ostream& os, const Rectangle& r) { + return os << "{ " << "top-left: " << r.topLeft << "; bottom-right: " << r.botRight << " }"; +} + +inline std::istream& operator>>(std::istream& is, Rectangle& r) { + return is >> r.topLeft >> r.botRight; +} + inline std::string to_string(Direction direction) { switch (direction) { case North: @@ -35,17 +64,4 @@ inline std::ostream& operator<<(std::ostream& os, Direction direction) { return os << to_string(direction); } -Point2D operator+(Point2D a, Point2D b); -Point2D operator-(Point2D a, Point2D b); -Point2D operator-(Point2D a); -Point2D operator*(float s, Point2D a); - -Circle operator+(Circle c, Point2D v); -Circle operator-(Circle c, Point2D v); -Circle operator*(float s, Circle c); - -Rectangle operator+(Rectangle r, Point2D v); -Rectangle operator-(Rectangle r, Point2D v); -Rectangle operator*(float s, Rectangle r); - #endif // CPPBASICS_OPERATORS_HPP From f2d8b0d616a2d8d2703b76f02b9e9353e21af80c Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 1 Dec 2023 21:22:26 +0100 Subject: [PATCH 054/137] fix operator overload includes Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/enemy.cpp | 3 ++- .../ClassesAndObjects/IntroducingObjects/src/player.cpp | 3 ++- .../ClassesAndObjects/IntroducingObjects/src/rectangle.cpp | 3 +-- .../ClassesAndObjects/IntroducingObjects/src/scene.cpp | 1 + .../ClassesAndObjects/IntroducingObjects/src/utils.cpp | 1 + 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 20e3619..8aaa716 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -1,7 +1,8 @@ #include "enemy.hpp" -#include "utils.hpp" #include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" EnemyObject::EnemyObject() : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index cab1c5a..fcadfff 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -1,7 +1,8 @@ #include "player.hpp" -#include "direction.hpp" #include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" PlayerObject::PlayerObject() : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp index c25492c..2b5a1ec 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/rectangle.cpp @@ -31,6 +31,5 @@ Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { if (rect.botRight.y > intoRect.botRight.y) { vector.y += intoRect.botRight.y - rect.botRight.y; } - // TODO: implement operators for Rectangle (and Circle) ? - return { rect.topLeft + vector, rect.botRight + vector }; + return rect + vector; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 6d52a4d..5d041e8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -1,6 +1,7 @@ #include "scene.hpp" #include "constants.hpp" +#include "operators.hpp" #include "statscene.hpp" #include "dynscene.hpp" diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp index 8a2cfbd..82ce84e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp @@ -1,6 +1,7 @@ #include "utils.hpp" #include +#include // TODO: move `distance` here From 0ca4f02fac283fce51776551af3c8c8a99ecd004 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Sat, 2 Dec 2023 00:59:54 +0100 Subject: [PATCH 055/137] refactor the game: add game engine class, add possibility to change between scenes (will be used in the following modules) Signed-off-by: Evgeniy Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 7 +- .../IntroducingObjects/src/dynscene.cpp | 31 ++- .../IntroducingObjects/src/engine.cpp | 79 +++++++ .../IntroducingObjects/src/scene.cpp | 74 ++----- .../IntroducingObjects/src/scenes.cpp | 14 ++ .../IntroducingObjects/src/statscene.cpp | 31 +-- .../IntroducingObjects/task-info.yaml | 198 +++++++++--------- WarmUp/HelloWorld/TypesOfVariables/task.md | 4 +- include/dynscene.hpp | 21 +- include/engine.hpp | 90 ++++++++ include/enums.hpp | 18 ++ include/scene.hpp | 124 +++++------ include/scenes.hpp | 43 ++++ include/statscene.hpp | 21 +- include/textures.hpp | 17 +- 15 files changed, 491 insertions(+), 281 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp create mode 100644 include/engine.hpp create mode 100644 include/scenes.hpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index 326a5b8..ec55bac 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -8,9 +8,10 @@ set(TASK set(SRC ${TASK} src/main.cpp src/utils.cpp - src/scene.cpp src/statscene.cpp src/dynscene.cpp src/textures.cpp - src/gobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp - src/gobjectlist.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) set(TEST diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index ff74248..4b65ace 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -11,18 +11,27 @@ const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; const int NEW_DYNAMIC_OBJECT_PROB = 1; const int NEW_ENEMY_OBJECT_PROB = 10; -void DynamicScene::initialize() { +void DynamicScene::activate() { addNewGameObject(GameObjectKind::PLAYER); } +void DynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID DynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID DynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + void DynamicScene::processEvent(const sf::Event& event) { - switch (event.type) { - case sf::Event::Closed: - close(); - break; - default: - break; - } + return; } void DynamicScene::update(sf::Time delta) { @@ -121,10 +130,10 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) return object; } -void DynamicScene::draw() { +void DynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { // draw all objects on the scene - objects.foreach([this] (const GameObject& object) { - Scene::draw(object); + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); }); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp new file mode 100644 index 0000000..8bd3de7 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp @@ -0,0 +1,79 @@ +#include "engine.hpp" + +GameEngine *GameEngine::create() { + static GameEngine engine; + return &engine; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + scene = sceneManager.getCurrentScene(); + Rectangle sceneBox = scene->getBoundingBox(); + window.create(sf::VideoMode(width(sceneBox), height(sceneBox)), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (active && window.isOpen()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} + +void GameEngine::close() { + active = false; + window.close(); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 5d041e8..1457361 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -1,54 +1,17 @@ #include "scene.hpp" -#include "constants.hpp" #include "operators.hpp" -#include "statscene.hpp" -#include "dynscene.hpp" -Scene::Scene() - : width(SCENE_WIDTH) - , height(SCENE_HEIGHT) - , active(true) -{ - window.create(sf::VideoMode(width, height), "Space Game"); - window.setFramerateLimit(60); - active &= textureManager.initialize(); -} - -Scene* Scene::create() { - // static StaticScene scene; - static DynamicScene scene; - return &scene; -} - -void Scene::run() { - initialize(); - sf::Clock clock; - while (active && window.isOpen()) { - sf::Time delta = clock.restart(); - processInput(); - update(delta); - render(); - } -} - -void Scene::processInput() { - sf::Event event; - while (window.pollEvent(event)) { - processEvent(event); - } -} - -void Scene::render() { - window.clear(sf::Color::White); - window.draw(background()); - draw(); - window.display(); -} +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} -void Scene::close() { - active = false; - window.close(); +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; } void Scene::setObjectPosition(GameObject& object, Point2D position) { @@ -70,26 +33,15 @@ void Scene::fitInto(GameObject &object) { object.setPosition(center(rect)); } -Rectangle Scene::getBoundingBox() const { - Rectangle box; - box.topLeft = { 0.0f, 0.0f }; - box.botRight = { width, height }; - return box; -} - void Scene::detectCollision(GameObject& object1, GameObject& object2) { CollisionInfo info = collisionInfo(object1, object2); object1.onCollision(object2, info); object2.onCollision(object1, info); } -sf::Sprite Scene::background() const { +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { sf::Sprite background; - background.setTexture(*textureManager.getTexture(GameTextureID::SPACE)); + background.setTexture(*texture); background.setTextureRect(sf::IntRect(0, 0, width, height)); - return background; -} - -void Scene::draw(const GameObject &object) { - object.draw(window, textureManager); -} + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp new file mode 100644 index 0000000..9b58d9c --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + dynamicScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &dynamicScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp index 58c8c23..177a9fc 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp @@ -1,19 +1,25 @@ #include "statscene.hpp" -void StaticScene::initialize() { +void StaticScene::activate() { fitInto(player); fitInto(consumable); fitInto(enemy); } +void StaticScene::deactivate() { + return; +} + +SceneID StaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID StaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + void StaticScene::processEvent(const sf::Event& event) { - switch (event.type) { - case sf::Event::Closed: - close(); - break; - default: - break; - } + return; } void StaticScene::update(sf::Time delta) { @@ -28,8 +34,9 @@ void StaticScene::update(sf::Time delta) { detectCollision(enemy, consumable); } -void StaticScene::draw() { - Scene::draw(player); - Scene::draw(consumable); - Scene::draw(enemy); +void StaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index c1365f4..e70214d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -1,100 +1,104 @@ type: edu custom_name: Introducing Objects files: - - name: CMakeLists.txt - visible: false - - name: test/test.cpp - visible: false - - name: src/operators.cpp - visible: true - placeholders: - - offset: 67 - length: 17 - placeholder_text: "return { 0.0f, 0.0f };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 1 - is_visible: false - - offset: 123 - length: 22 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 2 - is_visible: false - - offset: 195 - length: 18 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 3 - is_visible: false - - offset: 261 - length: 17 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 4 - is_visible: false - - name: src/point.cpp - visible: true - - name: src/rectangle.cpp - visible: true - - name: src/direction.cpp - visible: true - - name: src/textures.cpp - visible: true - - name: src/main.cpp - visible: true - - name: src/utils.cpp - visible: true - - name: src/dynscene.cpp - visible: true - - name: task1.md - visible: true - - name: task2.md - visible: true - - name: task3.md - visible: true - - name: task4.md - visible: true - - name: src/statscene.cpp - visible: true - - name: src/collision.cpp - visible: true - - name: src/enemy.cpp - visible: true - - name: src/cgobject.cpp - visible: true - - name: src/scene.cpp - visible: true - - name: src/gobject.cpp - visible: true - - name: task5.md - visible: true - - name: task6.md - visible: true - - name: src/player.cpp - visible: true - - name: src/consumable.cpp - visible: true - - name: src/gobjectlist.cpp - visible: true - - name: task7.md - visible: true - - name: task0.md - visible: true - - name: task8.md - visible: true +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false +- name: src/operators.cpp + visible: true + placeholders: + - offset: 67 + length: 17 + placeholder_text: "return { 0.0f, 0.0f };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 1 + is_visible: false + - offset: 123 + length: 22 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 2 + is_visible: false + - offset: 195 + length: 18 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 3 + is_visible: false + - offset: 261 + length: 17 + placeholder_text: "return { 0.0f, 0.0f, };" + dependency: + section: ObjectOrientedProgramming + lesson: ClassesAndObjects + task: OperatorsOverloading + file: src/operators.cpp + placeholder: 4 + is_visible: false +- name: src/point.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/main.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: task1.md + visible: true +- name: task2.md + visible: true +- name: task3.md + visible: true +- name: task4.md + visible: true +- name: src/collision.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: task5.md + visible: true +- name: task6.md + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: task7.md + visible: true +- name: task0.md + visible: true +- name: task8.md + visible: true +- name: src/engine.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/scenes.cpp + visible: true diff --git a/WarmUp/HelloWorld/TypesOfVariables/task.md b/WarmUp/HelloWorld/TypesOfVariables/task.md index af80e60..5abb1e2 100644 --- a/WarmUp/HelloWorld/TypesOfVariables/task.md +++ b/WarmUp/HelloWorld/TypesOfVariables/task.md @@ -33,7 +33,7 @@ If the variable is uninitialized, then its value before the point of its first assignment is *undefined*. Uninitialized variables are unpleasant errors (also known as *bugs*) in programs. -Thus, it is very **important** to **always** initialize variables +Thus, it is very **important** to **always** activate variables (there might be some rare exceptions to this rule, which we are not going to discuss now). @@ -57,7 +57,7 @@ of some types are actually initialized by default to some predefined value. An example of such type is `std::string`. Variables of this type are initialized with an empty string `""` automatically upon declaration, and thus they do not require -manual initialization, except if you want to initialize them +manual initialization, except if you want to activate them with some other value. We will learn more about types in C++ later in this course. diff --git a/include/dynscene.hpp b/include/dynscene.hpp index 4847ea0..d0c7c8c 100644 --- a/include/dynscene.hpp +++ b/include/dynscene.hpp @@ -17,9 +17,24 @@ class DynamicScene : public Scene { public: /** - * Initializes the scene. + * Activates the scene. */ - void initialize() override; + void activate() override; + + /** + * Deactivates the scene. + */ + void deactivate() override; + + /** + * Returns the ID of the scene. + */ + SceneID getID() const override; + + /** + * Returns the ID of the next scene to transition into. + */ + SceneID getNextSceneID() const override; /** * Process the given event. @@ -34,7 +49,7 @@ class DynamicScene : public Scene { /** * Draws the scene with all its game objects. */ - void draw() override; + void draw(sf::RenderWindow &window, TextureManager& textureManager) override; protected: diff --git a/include/engine.hpp b/include/engine.hpp new file mode 100644 index 0000000..6a69040 --- /dev/null +++ b/include/engine.hpp @@ -0,0 +1,90 @@ +#ifndef CPPBASICS_ENGINE_HPP +#define CPPBASICS_ENGINE_HPP + +#include + +#include "scenes.hpp" +#include "textures.hpp" + +class GameEngine { +public: + + /** + * Returns the game engine instance. + * + * @return A pointer to the game engine instance. + * + * @note This function does not transfer ownership of the game engine to the caller. + * The lifetime of the game engine is the entire duration of the program, and thus there is no need + * to manually delete the returned pointer. + */ + static GameEngine* create(); + + /** + * The run function implements the main loop of the game. + * + * While game is active, on each iteration of the loop, it processes any possible input, + * updates the scene according to the delta time, renders the scene, + * and, finally, performs a transition to a new scene if required. + */ + void run(); + +private: + + /** + * Constructs the game engine. + */ + GameEngine(); + + /** + * Processes all the input events accumulated in the event queue by passing them + * to the GameEngine::processEvent method. + */ + void processInput(); + + /** + * Processes single input event. + * + * Window-related events, such as user resizing or closing a window, are processed by + * the engine itself, while all other kinds of events are delegated to the scene. + * + * @param event the processing event. + */ + void processEvent(const sf::Event& event); + + + /** + * Updates the state of the current scene. + * + * @param delta The time elapsed since the last update. + */ + void update(sf::Time delta); + + /** + * Renders the current scene on the window. + */ + void render(); + + /** + * Performs a scene transition in the game. + */ + void sceneTransition(); + + /** + * Resizes the game window to fit the current scene into it. + */ + void resizeWindow(); + + /** + * Closes the game, forcing it to exit its running loop. + */ + void close(); + + sf::RenderWindow window; + SceneManager sceneManager; + TextureManager textureManager; + Scene* scene; + bool active; +}; + +#endif //CPPBASICS_ENGINE_HPP diff --git a/include/enums.hpp b/include/enums.hpp index d16b002..ba85bc7 100644 --- a/include/enums.hpp +++ b/include/enums.hpp @@ -1,6 +1,24 @@ #ifndef CPPBASICS_ENUMS_HPP #define CPPBASICS_ENUMS_HPP +enum class SceneID { + STATIC_GAME_FIELD, + DYNAMIC_GAME_FIELD, +}; + +/** +* The enumeration represents the IDs of textures used in the game. +* These IDs are used to identify specific textures when loading or rendering game graphics. +*/ +enum class GameTextureID { + SPACE, + PLANET, + PLANET_DEAD, + STAR, + STAR_CONCERNED, + BLACKHOLE, + SIZE +}; /** * This enumeration is used to categorize different kinds of game objects. diff --git a/include/scene.hpp b/include/scene.hpp index f03c733..6361d8b 100644 --- a/include/scene.hpp +++ b/include/scene.hpp @@ -8,7 +8,7 @@ #include "textures.hpp" /** - * The Scene class provides functionalities for managing and rendering game scenes. + * The Scene class provides functionalities for rendering game scenes. */ class Scene { public: @@ -16,7 +16,7 @@ class Scene { /** * Constructs the scene. */ - Scene(); + Scene(float width, float height); /** * Destructs the scene. @@ -24,51 +24,72 @@ class Scene { virtual ~Scene() = default; /** - * The run function implements the scene management logic. - * It initializes the scene, and while scene is active, it continues to run the main loop of the scene. - * In each iteration of the loop, it processes any possible input, - * updates the scene according to the delta time, and finally renders the scene. + * Activates the scene. + * + * Scene is activated by the game engine when it sets it as a the current scene. + * Activation should initialize the state of the scene, including all the game objects belonging to it. + * + * @note This method must be implemented in any derived scene class. */ - void run(); + virtual void activate() = 0; /** - * Creates a new scene. + * Deactivates the scene. * - * @return A pointer to the newly created scene. + * Scene is deactivated by the game engine when it performs a transition to another scene. + * Deactivation should reset the state of the scene. * - * @note This function does not transfer ownership of the scene to the caller. - * The lifetime of the scene is the entire duration of the program, and thus there is no need - * to manually delete the returned pointer. + * @note This method must be implemented in any derived scene class. */ - static Scene* create(); + virtual void deactivate() = 0; -protected: + /** + * Returns the ID of the scene. + */ + virtual SceneID getID() const = 0; /** - * Initializes the scene. - * - * @note This function must be implemented in any derived scene class. + * Returns the ID of the next scene to transition into. */ - virtual void initialize() = 0; + virtual SceneID getNextSceneID() const = 0; /** - * Closes the scene, forcing it to exit its running loop. + * Processes scene-related input event, such as player pressing some button, etc. + * + * @param event the processing event. + * + * @note This method must be implemented in any derived scene class. */ - void close(); + virtual void processEvent(const sf::Event& event) = 0; /** - * Processes all the input events accumulated in the event queue by passing them to the Scene::processEvent method. + * Updates the scene state based on time elapsed since the last update. + * + * @param delta The time elapsed since the last update. + * + * @note This method must be implemented in any derived scene class. */ - void processInput(); + virtual void update(sf::Time delta) = 0; /** - * Processes single input event, such as player pressing some button, resizing the game window, etc. + * Draws all the game objects belonging to the scene on the window. * - * @param event the processing event. + * @param window the window to draw on. + * @param textureManager the texture manager. * - * @note This function must be implemented in any derived scene class. + * @note This method must be implemented in any derived scene class. */ - virtual void processEvent(const sf::Event& event) = 0; + virtual void draw(sf::RenderWindow &window, TextureManager& textureManager) = 0; + + /** + * Returns the bounding box of the scene. + * The bounding box defines the smallest rectangle that completely encloses the scene. + * + * @return The bounding box of the scene as a rectangle. + */ + Rectangle getBoundingBox() const; + +protected: /** * Set the position of a game object. @@ -104,14 +125,6 @@ class Scene { */ void fitInto(GameObject& object); - /** - * Returns the bounding box of the scene. - * The bounding box defines the smallest rectangle that completely encloses the scene. - * - * @return The bounding box of the scene as a rectangle. - */ - Rectangle getBoundingBox() const; - /** * Checks for potential collision between two game objects and notifies the objects about the result of the check * by calling their GameObject::onCollision methods and provided with the CollisionInfo structure. @@ -122,51 +135,16 @@ class Scene { void detectCollision(GameObject& object1, GameObject& object2); /** - * Updates the scene state based on time elapsed since the last update. + * Draws the background, filling it with the provided texture. * - * @param delta The time elapsed since the last update. - * - * @note This function must be implemented in any derived scene class. + * @param window the window to draw on. + * @param texture the background texture. */ - virtual void update(sf::Time delta) = 0; - - /** - * Renders the whole scene on the window: resets the window, draws the background and then - * delegated to the Scene::draw method to draw all game objects belonging to the scene. - */ - void render(); - - /** - * Renders the given game object on the window. - * - * @param object The game object to be rendered. - */ - void draw(const GameObject& object); - - /** - * Draws all the game objects belonging to the scene on the window. - * - * @note This function must be implemented in any derived scene class. - */ - virtual void draw() = 0; - - /** - * Returns the sprite to draw on the background of the scene. - * - * This method returns the background details of the current instance. - * The background information may vary depending on the implementation and context. - * - * @return A constant reference to the background information. - */ - - sf::Sprite background() const; + void drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const; private: - sf::RenderWindow window; - TextureManager textureManager; float width; float height; - bool active; }; #endif // CPPBASICS_SCENE_HPP diff --git a/include/scenes.hpp b/include/scenes.hpp new file mode 100644 index 0000000..9c2c40c --- /dev/null +++ b/include/scenes.hpp @@ -0,0 +1,43 @@ +#ifndef CPPBASICS_SCENES_HPP +#define CPPBASICS_SCENES_HPP + +#include "scene.hpp" +#include "statscene.hpp" +#include "dynscene.hpp" + +/** + * The SceneManager class is responsible for loading and managing the different scenes in a game. + */ +class SceneManager { +public: + + /** + * Initializes the scene manager. + * + * @return true if the initialization was successful, false otherwise. + */ + bool initialize(); + + /** + * Retrieves the current scene of the game. + * + * @return The pointer to the current scene. + * + * @note This method does not transfer ownership of the scene to the caller. + * The lifetime of the scenes is managed by the scene manager itself. + */ + Scene* getCurrentScene(); + + /** + * This function checks if the transition to a new scene is necessary by querying the current scene + * and performs this transition if required. + */ + void transitionScene(SceneID id); + +private: + // StaticScene staticScene; + DynamicScene dynamicScene; +}; + + +#endif //CPPBASICS_SCENES_HPP diff --git a/include/statscene.hpp b/include/statscene.hpp index 83a6fb7..26aaee5 100644 --- a/include/statscene.hpp +++ b/include/statscene.hpp @@ -17,9 +17,24 @@ class StaticScene : public Scene { public: /** - * Initializes the scene. + * Activates the scene. */ - void initialize() override; + void activate() override; + + /** + * Deactivates the scene. + */ + void deactivate() override; + + /** + * Returns the ID of the scene. + */ + SceneID getID() const override; + + /** + * Returns the ID of the next scene to transition into. + */ + SceneID getNextSceneID() const override; /** * Process the given event. @@ -34,7 +49,7 @@ class StaticScene : public Scene { /** * Draws the scene with all its game objects. */ - void draw() override; + void draw(sf::RenderWindow &window, TextureManager& textureManager) override; private: PlayerObject player; diff --git a/include/textures.hpp b/include/textures.hpp index 15b40b6..5a79404 100644 --- a/include/textures.hpp +++ b/include/textures.hpp @@ -5,21 +5,7 @@ #include - -/** - * The enumeration represents the IDs of textures used in the game. - * These IDs are used to identify specific textures when loading or rendering game graphics. - */ - enum class GameTextureID { - SPACE, - PLANET, - PLANET_DEAD, - STAR, - STAR_CONCERNED, - BLACKHOLE, - SIZE -}; - +#include "enums.hpp" /** * The TextureManager class manages the loading and retrieval of textures for the game. @@ -34,7 +20,6 @@ class TextureManager { */ bool initialize(); - /** * Get the texture associated with the specified game texture ID. * From 4ddf970f73fe6b9f9a107fba2171a8d8b294cf19 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Sat, 2 Dec 2023 01:05:29 +0100 Subject: [PATCH 056/137] minor bugfixes after refactoring Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/dynscene.cpp | 5 +++++ .../ClassesAndObjects/IntroducingObjects/src/main.cpp | 4 ++-- .../ClassesAndObjects/IntroducingObjects/src/statscene.cpp | 4 ++++ include/dynscene.hpp | 5 +++++ include/statscene.hpp | 5 +++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index 4b65ace..c24d65a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -1,5 +1,6 @@ #include "dynscene.hpp" +#include "constants.hpp" #include "player.hpp" #include "consumable.hpp" #include "enemy.hpp" @@ -11,6 +12,8 @@ const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; const int NEW_DYNAMIC_OBJECT_PROB = 1; const int NEW_ENEMY_OBJECT_PROB = 10; +DynamicScene::DynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + void DynamicScene::activate() { addNewGameObject(GameObjectKind::PLAYER); } @@ -131,6 +134,8 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) } void DynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); // draw all objects on the scene objects.foreach([&] (const GameObject& object) { object.draw(window, textureManager); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp index 3807280..3608feb 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp @@ -1,6 +1,6 @@ -#include "scene.hpp" +#include "engine.hpp" int main() { - Scene::create()->run(); + GameEngine::create()->run(); return 0; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp index 177a9fc..20eae1d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp @@ -1,5 +1,9 @@ #include "statscene.hpp" +#include "constants.hpp" + +StaticScene::StaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + void StaticScene::activate() { fitInto(player); fitInto(consumable); diff --git a/include/dynscene.hpp b/include/dynscene.hpp index d0c7c8c..4ff2cdf 100644 --- a/include/dynscene.hpp +++ b/include/dynscene.hpp @@ -16,6 +16,11 @@ class DynamicScene : public Scene { public: + /** + * Constructs the scene. + */ + DynamicScene(); + /** * Activates the scene. */ diff --git a/include/statscene.hpp b/include/statscene.hpp index 26aaee5..9d2d19d 100644 --- a/include/statscene.hpp +++ b/include/statscene.hpp @@ -16,6 +16,11 @@ class StaticScene : public Scene { public: + /** + * Constructs the scene. + */ + StaticScene(); + /** * Activates the scene. */ From c6fc6225984f56fb4aa08fa5ecf7fa34834a13a8 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Sun, 3 Dec 2023 16:02:09 +0100 Subject: [PATCH 057/137] initialize random number generator Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/engine.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp index 8bd3de7..f0b7e23 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp @@ -9,12 +9,17 @@ GameEngine::GameEngine() : scene(nullptr) , active(true) { + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers active &= sceneManager.initialize(); active &= textureManager.initialize(); if (!active) { return; } + // set the current scene scene = sceneManager.getCurrentScene(); + // initialize the application window Rectangle sceneBox = scene->getBoundingBox(); window.create(sf::VideoMode(width(sceneBox), height(sceneBox)), "Space Game"); window.setFramerateLimit(60); From eaa6ea8b97290db352c2da9647827b07a255102b Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Sun, 3 Dec 2023 16:38:48 +0100 Subject: [PATCH 058/137] split window size and scene size Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/engine.cpp | 5 +++-- include/constants.hpp | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp index f0b7e23..3f5a6e8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp @@ -1,5 +1,7 @@ #include "engine.hpp" +#include "constants.hpp" + GameEngine *GameEngine::create() { static GameEngine engine; return &engine; @@ -20,8 +22,7 @@ GameEngine::GameEngine() // set the current scene scene = sceneManager.getCurrentScene(); // initialize the application window - Rectangle sceneBox = scene->getBoundingBox(); - window.create(sf::VideoMode(width(sceneBox), height(sceneBox)), "Space Game"); + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); window.setFramerateLimit(60); } diff --git a/include/constants.hpp b/include/constants.hpp index 58da132..516f304 100644 --- a/include/constants.hpp +++ b/include/constants.hpp @@ -7,6 +7,9 @@ const float ENEMY_RADIUS = 60.0f; const float SPEED = 150.0f; +const float WINDOW_WIDTH = 800.0f; +const float WINDOW_HEIGHT = 600.0f; + const float SCENE_WIDTH = 800.0f; const float SCENE_HEIGHT = 600.0f; From fde866ba9d3152db854bb4e5a1bff1e87bc02ae1 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Sun, 3 Dec 2023 19:28:00 +0100 Subject: [PATCH 059/137] extract `updateVelocity()` method of enemy object Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/IntroducingObjects/src/enemy.cpp | 4 ++++ include/enemy.hpp | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 8aaa716..083d4d1 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -23,6 +23,10 @@ void EnemyObject::update(sf::Time delta) { if (updateTimer < sf::seconds(1.0f)) return; updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { Direction direction1 = static_cast(generateInt(0, 3)); Direction direction2 = static_cast(generateInt(0, 3)); Point2D directionVector = (direction1 == direction2) diff --git a/include/enemy.hpp b/include/enemy.hpp index 7e057b0..f5f73bd 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -42,6 +42,13 @@ class EnemyObject : public CircleGameObject { */ const sf::Texture* getTexture(TextureManager& textureManager) const override; +protected: + + /** + * Updates the velocity of an enemy object. + */ + void updateVelocity(); + private: Point2D velocity; sf::Time updateTimer; From f31f0dc49687f9787b71286a4cb468ac82b673ff Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 4 Dec 2023 15:04:01 +0100 Subject: [PATCH 060/137] prepare placeholders for the student solution Signed-off-by: Evgeniy Moiseenko --- .../IntroducingObjects/src/dynscene.cpp | 20 +-- .../IntroducingObjects/src/scenes.cpp | 4 +- .../IntroducingObjects/src/statscene.cpp | 16 +- .../IntroducingObjects/task-info.yaml | 100 ++++++------ .../IntroducingObjects/task5.md | 6 +- .../IntroducingObjects/task8.md | 8 +- .../Introduction/CMakeLists.txt | 31 ++++ .../Introduction/src/cgobject.cpp | 47 ++++++ .../Introduction/src/collision.cpp | 21 +++ .../Introduction/src/consumable.cpp | 27 ++++ .../Introduction/src/direction.cpp | 16 ++ .../Introduction/src/dynscene.cpp | 144 ++++++++++++++++++ .../Introduction/src/enemy.cpp | 40 +++++ .../Introduction/src/engine.cpp | 85 +++++++++++ .../Introduction/src/gobject.cpp | 7 + .../Introduction/src/gobjectlist.cpp | 54 +++++++ .../Introduction/src/main.cpp | 13 ++ .../Introduction/src/operators.cpp | 42 +++++ .../Introduction/src/player.cpp | 46 ++++++ .../Introduction/src/point.cpp | 19 +++ .../Introduction/src/rectangle.cpp | 35 +++++ .../Introduction/src/scene.cpp | 45 ++++++ .../Introduction/src/scenes.cpp | 14 ++ .../Introduction/src/statscene.cpp | 46 ++++++ .../Introduction/src/textures.cpp | 42 +++++ .../Introduction/src/utils.cpp | 48 ++++++ .../Introduction/task-info.yaml | 43 ++++++ .../ClassesAndObjects/Introduction/task.md | 23 +++ .../ClassesAndObjects/lesson-info.yaml | 5 +- include/dynscene.hpp | 4 +- include/scenes.hpp | 3 +- include/statscene.hpp | 4 +- 32 files changed, 973 insertions(+), 85 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp index c24d65a..1dbcf2e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/dynscene.cpp @@ -12,32 +12,32 @@ const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; const int NEW_DYNAMIC_OBJECT_PROB = 1; const int NEW_ENEMY_OBJECT_PROB = 10; -DynamicScene::DynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} -void DynamicScene::activate() { +void GameplayDynamicScene::activate() { addNewGameObject(GameObjectKind::PLAYER); } -void DynamicScene::deactivate() { +void GameplayDynamicScene::deactivate() { // remove all the objects from the list objects.remove([&] (const GameObject& object) { return true; }); } -SceneID DynamicScene::getID() const { +SceneID GameplayDynamicScene::getID() const { return SceneID::DYNAMIC_GAME_FIELD; } -SceneID DynamicScene::getNextSceneID() const { +SceneID GameplayDynamicScene::getNextSceneID() const { return SceneID::DYNAMIC_GAME_FIELD; } -void DynamicScene::processEvent(const sf::Event& event) { +void GameplayDynamicScene::processEvent(const sf::Event& event) { return; } -void DynamicScene::update(sf::Time delta) { +void GameplayDynamicScene::update(sf::Time delta) { // update the objects' state objects.foreach([delta] (GameObject& object) { object.update(delta); @@ -58,7 +58,7 @@ void DynamicScene::update(sf::Time delta) { updateObjectsList(); } -void DynamicScene::updateObjectsList() { +void GameplayDynamicScene::updateObjectsList() { // remove destroyed objects from the list objects.remove([] (const GameObject& object) { return (object.getStatus() == GameObjectStatus::DESTROYED) @@ -94,7 +94,7 @@ void DynamicScene::updateObjectsList() { } } -std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) { +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { std::shared_ptr object; while (!object) { // create an object with default position @@ -133,7 +133,7 @@ std::shared_ptr DynamicScene::addNewGameObject(GameObjectKind kind) return object; } -void DynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { // draw background drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); // draw all objects on the scene diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp index 9b58d9c..7125bb8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scenes.cpp @@ -1,12 +1,12 @@ #include "scenes.hpp" bool SceneManager::initialize() { - dynamicScene.activate(); + staticScene.activate(); return true; } Scene* SceneManager::getCurrentScene() { - return &dynamicScene; + return &staticScene; } void SceneManager::transitionScene(SceneID id) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp index 20eae1d..07c0bd0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp @@ -2,31 +2,31 @@ #include "constants.hpp" -StaticScene::StaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} -void StaticScene::activate() { +void GameplayStaticScene::activate() { fitInto(player); fitInto(consumable); fitInto(enemy); } -void StaticScene::deactivate() { +void GameplayStaticScene::deactivate() { return; } -SceneID StaticScene::getID() const { +SceneID GameplayStaticScene::getID() const { return SceneID::STATIC_GAME_FIELD; } -SceneID StaticScene::getNextSceneID() const { +SceneID GameplayStaticScene::getNextSceneID() const { return SceneID::STATIC_GAME_FIELD; } -void StaticScene::processEvent(const sf::Event& event) { +void GameplayStaticScene::processEvent(const sf::Event& event) { return; } -void StaticScene::update(sf::Time delta) { +void GameplayStaticScene::update(sf::Time delta) { player.update(delta); consumable.update(delta); enemy.update(delta); @@ -38,7 +38,7 @@ void StaticScene::update(sf::Time delta) { detectCollision(enemy, consumable); } -void StaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); player.draw(window, textureManager); consumable.draw(window, textureManager); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index e70214d..b4002d1 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -3,6 +3,56 @@ custom_name: Introducing Objects files: - name: CMakeLists.txt visible: false +- name: src/rectangle.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/main.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: task1.md + visible: true +- name: task2.md + visible: true +- name: task3.md + visible: true +- name: task4.md + visible: true +- name: src/collision.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: task5.md + visible: true +- name: task6.md + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: task7.md + visible: true +- name: task0.md + visible: true +- name: task8.md + visible: true +- name: src/engine.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/point.cpp + visible: true - name: test/test.cpp visible: false - name: src/operators.cpp @@ -48,57 +98,7 @@ files: file: src/operators.cpp placeholder: 4 is_visible: false -- name: src/point.cpp - visible: true -- name: src/rectangle.cpp - visible: true -- name: src/direction.cpp - visible: true -- name: src/main.cpp - visible: true -- name: src/utils.cpp - visible: true -- name: task1.md - visible: true -- name: task2.md - visible: true -- name: task3.md - visible: true -- name: task4.md - visible: true -- name: src/collision.cpp - visible: true -- name: src/gobject.cpp - visible: true -- name: task5.md - visible: true -- name: task6.md - visible: true -- name: src/gobjectlist.cpp - visible: true -- name: task7.md - visible: true -- name: task0.md - visible: true -- name: task8.md - visible: true -- name: src/engine.cpp - visible: true -- name: src/consumable.cpp - visible: true -- name: src/cgobject.cpp - visible: true -- name: src/player.cpp - visible: true -- name: src/statscene.cpp - visible: true - name: src/enemy.cpp visible: true - name: src/scene.cpp visible: true -- name: src/textures.cpp - visible: true -- name: src/dynscene.cpp - visible: true -- name: src/scenes.cpp - visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md index 9131a85..5c51289 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md @@ -2,7 +2,7 @@ The `Scene` class is an _abstract class_ too — it has pure virtual methods and thus cannot be instantiated. This gives us the flexibility of having different implementations of the `Scene` class. -One such implementation is given by the `StaticScene` subclass +One such implementation is given by the `GameplayStaticScene` subclass (see files `statscene.hpp` and `statscene.cpp`). This scene implementation is called static because it contains only static predefined number of game objects: single player object and single consumable object. @@ -32,7 +32,7 @@ Scene* scene = Scene::create(); Static members provide a convenient way to associate some methods or data fields with the class itself. For example, the `create` method shown above provides an ability to instantiate the scene object, without revealing the actual implementation to the user of the method -(note that it returns `Scene*` instead of `StaticScene*`). +(note that it returns `Scene*` instead of `GameplayStaticScene*`). Moreover, it gives us a way to ensure that only one scene is created per each game run. How we can achieve that — well, with the help of the `static` modifier again. @@ -56,7 +56,7 @@ std::cout << foo() << std::endl; ``` With the help of the `static` modifier, it becomes possible to -declare static `StaticScene` variable inside `Scene::create` method +declare static `GameplayStaticScene` variable inside `Scene::create` method and return pointer to this variable.
diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md index c0aec87..8fe5726 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md @@ -5,17 +5,17 @@ in terms of object-oriented programming. Before proceeding with this task, please first finish the `Ownership` module, as we will need some concepts taught there. -First of all, instead of completely rewriting the `StaticScene` class, -we will just add a new class --- `DynamicScene`, where we will implement the new dynamic functionality. +First of all, instead of completely rewriting the `GameplayStaticScene` class, +we will just add a new class --- `GameplayDynamicScene`, where we will implement the new dynamic functionality. [//]: # (Then switching between the two `Scene` implementations will be quite easy --- ) [//]: # (TODO: describe the actual scene-switching logic once it will be settled.) -Please have a look at the declaration of the `DynamicScene` class (file `dynscene.hpp`), +Please have a look at the declaration of the `GameplayDynamicScene` class (file `dynscene.hpp`), and its definition (file `dynscene.cpp`). You can find the brief description of its methods in the documentation comments. -The `DynamicScene` class has a field `objects` of `GameObjectList` class. +The `GameplayDynamicScene` class has a field `objects` of `GameObjectList` class. This is the main class we are going to work with in this task — it will implement the doubly linked list (its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively). diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt new file mode 100644 index 0000000..92d30df --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-Introduction) + +set(TASK + src/cgobject.cpp) + +set(SRC + ${TASK} + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/cgobject.cpp new file mode 100644 index 0000000..6b24599 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + // TODO: write your solution here +} + +void CircleGameObject::setPosition(Point2D position) { + // TODO: write your solution here +} + +GameObjectStatus CircleGameObject::getStatus() const { + // TODO: write your solution here +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + // TODO: write your solution here +} + +Circle CircleGameObject::getCircle() const { + // TODO: write your solution here +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/consumable.cpp new file mode 100644 index 0000000..7bd2eaa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/consumable.cpp @@ -0,0 +1,27 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + // TODO: write your solution here +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp new file mode 100644 index 0000000..48a52bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp @@ -0,0 +1,40 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + // TODO: write your solution here +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp new file mode 100644 index 0000000..9f8b2b3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp @@ -0,0 +1,85 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + // TODO: write your solution here + return nullptr; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (active && window.isOpen()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} + +void GameEngine::close() { + active = false; + window.close(); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobject.cpp new file mode 100644 index 0000000..b1abc1e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/player.cpp new file mode 100644 index 0000000..e335cf0 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/player.cpp @@ -0,0 +1,46 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scene.cpp new file mode 100644 index 0000000..81a4470 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scene.cpp @@ -0,0 +1,45 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + // TODO: write your solution here +} + +void Scene::move(GameObject &object, Point2D vector) { + // TODO: write your solution here +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp new file mode 100644 index 0000000..9b58d9c --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + dynamicScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &dynamicScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp new file mode 100644 index 0000000..71c9658 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp @@ -0,0 +1,48 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + // TODO: replace with rectangle arithmetics operators? + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml new file mode 100644 index 0000000..5ff2ffa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml @@ -0,0 +1,43 @@ +type: theory +custom_name: Introduction +files: +- name: src/main.cpp + visible: true +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md new file mode 100644 index 0000000..ebe7b73 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md @@ -0,0 +1,23 @@ +In the next few lessons, you will learn about the object-oriented programming paradigm +in the context of the C++ language. As you will see, this paradigm helps +to organize the code into independent subcomponents, manage the complexity +of the applications, and streamline the addition of new functionality. + +We will re-implement our game using the features of object-oriented programming. +This effort will help us to easily extend the game by adding new kinds of objects. +In particular, we will add enemy objects that will make our little game a bit more challenging. +There is a lot of fun coming, so get ready for your journey into the world of objects! + +Here is a list of specific topics that are going to be covered: + +* Operators overloading. +* Classes and objects. +* Abstract classes and interfaces. +* Constructors and destructors. +* Inheritance and subtyping. +* Subtype polymorphism. +* Virtual methods. +* Visibility modifiers: `public`, `protected`, and `private`. +* Encapsulation and class invariants. +* Static methods, fields, and variables. +* Objects vs structures. \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 2d7600f..12151a1 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -1,6 +1,7 @@ type: framework custom_name: Classes And Objects content: - - OperatorsOverloading - - IntroducingObjects +- Introduction +- OperatorsOverloading +- IntroducingObjects is_template_based: false diff --git a/include/dynscene.hpp b/include/dynscene.hpp index 4ff2cdf..e08b4af 100644 --- a/include/dynscene.hpp +++ b/include/dynscene.hpp @@ -13,13 +13,13 @@ * DynamicScene is a scene capable of managing and updating the list of game objects in the scene: * objects might be added to or removed from the scene dynamically. */ -class DynamicScene : public Scene { +class GameplayDynamicScene : public Scene { public: /** * Constructs the scene. */ - DynamicScene(); + GameplayDynamicScene(); /** * Activates the scene. diff --git a/include/scenes.hpp b/include/scenes.hpp index 9c2c40c..e3f39f7 100644 --- a/include/scenes.hpp +++ b/include/scenes.hpp @@ -35,8 +35,7 @@ class SceneManager { void transitionScene(SceneID id); private: - // StaticScene staticScene; - DynamicScene dynamicScene; + GameplayStaticScene staticScene; }; diff --git a/include/statscene.hpp b/include/statscene.hpp index 9d2d19d..82ae782 100644 --- a/include/statscene.hpp +++ b/include/statscene.hpp @@ -13,13 +13,13 @@ * StaticScene is a scene operating a static fixed collection of game objects, * namely single player, single consumable, and single enemy objects. */ -class StaticScene : public Scene { +class GameplayStaticScene : public Scene { public: /** * Constructs the scene. */ - StaticScene(); + GameplayStaticScene(); /** * Activates the scene. From 53ad17f658e833536a744ccbeda4cf6b45d33ea4 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 4 Dec 2023 15:04:29 +0100 Subject: [PATCH 061/137] make `main.cpp` non-editable Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/Introduction/task-info.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml index 5ff2ffa..2860b12 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml @@ -3,6 +3,7 @@ custom_name: Introduction files: - name: src/main.cpp visible: true + editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp From f21a401dcb33d3b75e8712531a8f96dd440f6ea8 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 4 Dec 2023 20:58:03 +0100 Subject: [PATCH 062/137] add GameEngine::isActive function (for testing purposes) Signed-off-by: Evgeniy Moiseenko --- .../Introduction/src/engine.cpp | 16 ++++++++++------ .../Introduction/src/scenes.cpp | 4 ++-- include/engine.hpp | 17 ++++++++++++----- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp index 9f8b2b3..c506cdf 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/engine.cpp @@ -28,7 +28,7 @@ GameEngine::GameEngine() void GameEngine::run() { sf::Clock clock; - while (active && window.isOpen()) { + while (isActive()) { sf::Time delta = clock.restart(); processInput(); update(delta); @@ -37,6 +37,15 @@ void GameEngine::run() { } } +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + void GameEngine::processInput() { sf::Event event; while (window.pollEvent(event)) { @@ -77,9 +86,4 @@ void GameEngine::resizeWindow() { Rectangle sceneBox = scene->getBoundingBox(); sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); window.setSize(sceneSize); -} - -void GameEngine::close() { - active = false; - window.close(); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp index 9b58d9c..7125bb8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/scenes.cpp @@ -1,12 +1,12 @@ #include "scenes.hpp" bool SceneManager::initialize() { - dynamicScene.activate(); + staticScene.activate(); return true; } Scene* SceneManager::getCurrentScene() { - return &dynamicScene; + return &staticScene; } void SceneManager::transitionScene(SceneID id) { diff --git a/include/engine.hpp b/include/engine.hpp index 6a69040..319984e 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -29,6 +29,18 @@ class GameEngine { */ void run(); + /** + * Checks if the game engine is active and the application window is open. + * + * @return true if engine is active, false otherwise. + */ + bool isActive() const; + + /** + * Closes the game engine, forcing it to exit its running loop and close the application window. + */ + void close(); + private: /** @@ -75,11 +87,6 @@ class GameEngine { */ void resizeWindow(); - /** - * Closes the game, forcing it to exit its running loop. - */ - void close(); - sf::RenderWindow window; SceneManager sceneManager; TextureManager textureManager; From 385b56de30afd0e83d0e758fe762eefeb8e097f8 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 4 Dec 2023 22:10:13 +0100 Subject: [PATCH 063/137] student template solution for operators.cpp Signed-off-by: Evgeniy Moiseenko --- .../Introduction/src/operators.cpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp index 19fa160..3582868 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp @@ -1,23 +1,23 @@ #include "game.hpp" Point2D operator+(Point2D a, Point2D b) { - return add(a, b); + // TODO: write your solution here } Point2D operator-(Point2D a) { - return { -a.x, -a.y }; + // TODO: write your solution here } Point2D operator-(Point2D a, Point2D b) { - return add(a, -b); + // TODO: write your solution here } Point2D operator*(float s, Point2D a) { - return mul(s, a); + // TODO: write your solution here } Circle operator+(Circle c, Point2D v) { - return { c.center + v, c.radius }; + // TODO: write your solution here } Circle operator-(Circle c, Point2D v) { @@ -25,18 +25,17 @@ Circle operator-(Circle c, Point2D v) { } Rectangle operator+(Rectangle r, Point2D v) { - return { r.topLeft + v, r.botRight + v }; + // TODO: write your solution here } Rectangle operator-(Rectangle r, Point2D v) { - return { r.topLeft - v, r.botRight - v }; + // TODO: write your solution here } Circle operator*(float s, Circle c) { - return { c.center, s * c.radius }; + // TODO: write your solution here } Rectangle operator*(float s, Rectangle r) { - Point2D v = { width(r), height(r) }; - return r + s * v; + // TODO: write your solution here } \ No newline at end of file From 8edc7798ea9ab4aefebaef07c2b2d251c6139465 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 4 Dec 2023 22:11:03 +0100 Subject: [PATCH 064/137] WIP: operators task Signed-off-by: Evgeniy Moiseenko --- .../Introduction/CMakeLists.txt | 6 - .../OperatorsOverloading/CMakeLists.txt | 16 +- .../OperatorsOverloading/src/cgobject.cpp | 47 ++++++ .../OperatorsOverloading/src/collision.cpp | 21 +++ .../OperatorsOverloading/src/consumable.cpp | 27 ++++ .../OperatorsOverloading/src/direction.cpp | 16 ++ .../OperatorsOverloading/src/dynscene.cpp | 144 ++++++++++++++++++ .../OperatorsOverloading/src/enemy.cpp | 40 +++++ .../OperatorsOverloading/src/engine.cpp | 89 +++++++++++ .../OperatorsOverloading/src/gobject.cpp | 7 + .../OperatorsOverloading/src/gobjectlist.cpp | 54 +++++++ .../OperatorsOverloading/src/main.cpp | 13 ++ .../OperatorsOverloading/src/player.cpp | 46 ++++++ .../OperatorsOverloading/src/rectangle.cpp | 35 +++++ .../OperatorsOverloading/src/scene.cpp | 45 ++++++ .../OperatorsOverloading/src/scenes.cpp | 14 ++ .../OperatorsOverloading/src/statscene.cpp | 46 ++++++ .../OperatorsOverloading/src/textures.cpp | 42 +++++ .../OperatorsOverloading/src/utils.cpp | 48 ++++++ .../OperatorsOverloading/task-info.yaml | 56 ++++--- 20 files changed, 785 insertions(+), 27 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt index 92d30df..5c00214 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt @@ -2,11 +2,7 @@ cmake_minimum_required(VERSION 3.25) project(ObjectOrientedProgramming-ClassesAndObjects-Introduction) -set(TASK - src/cgobject.cpp) - set(SRC - ${TASK} src/main.cpp src/engine.cpp src/scenes.cpp src/textures.cpp src/scene.cpp src/statscene.cpp src/dynscene.cpp @@ -22,8 +18,6 @@ set(TEST add_executable(${PROJECT_NAME}-run ${SRC}) -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) - prepare_sfml_framework_lesson_task( "${CMAKE_CURRENT_SOURCE_DIR}/.." ${PROJECT_NAME}-run diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt index 0bb1270..5a241de 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt @@ -2,15 +2,23 @@ cmake_minimum_required(VERSION 3.25) project(ObjectOrientedProgramming-ClassesAndObjects-OperatorsOverloading) +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + set(TASK src/operators.cpp) -set(SRC - ${TASK} src/point.cpp) - set(TEST test/test.cpp) include_directories(${CMAKE_SOURCE_DIR}/include/) -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/cgobject.cpp new file mode 100644 index 0000000..6b24599 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + // TODO: write your solution here +} + +void CircleGameObject::setPosition(Point2D position) { + // TODO: write your solution here +} + +GameObjectStatus CircleGameObject::getStatus() const { + // TODO: write your solution here +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + // TODO: write your solution here +} + +Circle CircleGameObject::getCircle() const { + // TODO: write your solution here +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/consumable.cpp new file mode 100644 index 0000000..7bd2eaa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/consumable.cpp @@ -0,0 +1,27 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + // TODO: write your solution here +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp new file mode 100644 index 0000000..48a52bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp @@ -0,0 +1,40 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + // TODO: write your solution here +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/engine.cpp new file mode 100644 index 0000000..c506cdf --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + // TODO: write your solution here + return nullptr; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobject.cpp new file mode 100644 index 0000000..b1abc1e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/player.cpp new file mode 100644 index 0000000..e335cf0 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/player.cpp @@ -0,0 +1,46 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scene.cpp new file mode 100644 index 0000000..81a4470 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scene.cpp @@ -0,0 +1,45 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + // TODO: write your solution here +} + +void Scene::move(GameObject &object, Point2D vector) { + // TODO: write your solution here +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp new file mode 100644 index 0000000..71c9658 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp @@ -0,0 +1,48 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + // TODO: replace with rectangle arithmetics operators? + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml index d2944ca..f130cb5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml @@ -1,24 +1,46 @@ type: edu custom_name: Operators Overloading files: +- name: src/main.cpp + visible: true + editable: false +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true - name: CMakeLists.txt visible: false - name: test/test.cpp visible: false -- name: src/operators.cpp - visible: true - placeholders: - - offset: 67 - length: 17 - placeholder_text: "return { 0.0f, 0.0f };" - - offset: 123 - length: 22 - placeholder_text: "return { 0.0f, 0.0f, };" - - offset: 195 - length: 18 - placeholder_text: "return { 0.0f, 0.0f, };" - - offset: 261 - length: 17 - placeholder_text: "return { 0.0f, 0.0f, };" -- name: src/point.cpp - visible: true From e4d2f2ec71d3e825362b4179961a3b28d30e7070 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 6 Dec 2023 16:11:52 +0100 Subject: [PATCH 065/137] operators task --- add more tests Signed-off-by: Evgeniy Moiseenko --- .../Introduction/src/utils.cpp | 5 +- .../OperatorsOverloading/src/operators.cpp | 25 ++ .../OperatorsOverloading/src/utils.cpp | 5 +- .../OperatorsOverloading/test/test.cpp | 262 ++++++++++++++++-- include/testing.hpp | 1 + include/utils.hpp | 2 + 6 files changed, 276 insertions(+), 24 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp index 71c9658..783fc7a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/utils.cpp @@ -34,7 +34,6 @@ Circle generateCircle(float radius, const Rectangle& boundingBox) { if (radius > std::min(width(boundingBox), height(boundingBox))) { return circle; } - // TODO: replace with rectangle arithmetics operators? circle.center.x = generateFloat( boundingBox.topLeft.x + radius, boundingBox.botRight.x - radius @@ -45,4 +44,8 @@ Circle generateCircle(float radius, const Rectangle& boundingBox) { ); circle.radius = radius; return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp index 75a298d..19fa160 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp @@ -14,4 +14,29 @@ Point2D operator-(Point2D a, Point2D b) { Point2D operator*(float s, Point2D a) { return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp index 71c9658..783fc7a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/utils.cpp @@ -34,7 +34,6 @@ Circle generateCircle(float radius, const Rectangle& boundingBox) { if (radius > std::min(width(boundingBox), height(boundingBox))) { return circle; } - // TODO: replace with rectangle arithmetics operators? circle.center.x = generateFloat( boundingBox.topLeft.x + radius, boundingBox.botRight.x - radius @@ -45,4 +44,8 @@ Circle generateCircle(float radius, const Rectangle& boundingBox) { ); circle.radius = radius; return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp index 0ee372b..efd387a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/test/test.cpp @@ -9,6 +9,8 @@ testing::Environment* const env = const float MIN = -10e3; const float MAX = 10e3; +const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; + namespace expected { Point2D add(Point2D a, Point2D b) { Point2D c = { 0, 0 }; @@ -25,7 +27,7 @@ namespace expected { } } -std::string plus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actual) { +std::string point_plus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actual) { std::ostringstream stream; stream << "Testing expression:\n" << " c = a + b" << "\n"; @@ -39,11 +41,11 @@ std::string plus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actua return stream.str(); } -TEST(PlusTest, PlusTest) { +TEST(PointPlusTest, PointPlusTest) { property_test( [] () { - Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; - Point2D b = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + Point2D a = generatePoint(generateArea); + Point2D b = generatePoint(generateArea); return std::make_tuple(a, b); }, [] (std::tuple data) { @@ -51,13 +53,13 @@ TEST(PlusTest, PlusTest) { std::tie(a, b) = data; Point2D expected = expected::add(a, b); Point2D actual = a + b; - ASSERT_FLOAT_EQ(expected.x, actual.x) << plus_error_msg(a, b, expected, actual); - ASSERT_FLOAT_EQ(expected.y, actual.y) << plus_error_msg(a, b, expected, actual); + ASSERT_FLOAT_EQ(expected.x, actual.x) << point_plus_error_msg(a, b, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << point_plus_error_msg(a, b, expected, actual); } ); } -std::string unary_minus_error_msg(Point2D a, Point2D expected, Point2D actual) { +std::string point_unary_minus_error_msg(Point2D a, Point2D expected, Point2D actual) { std::ostringstream stream; stream << "Testing expression:\n" << " b = -a" << "\n"; @@ -70,22 +72,22 @@ std::string unary_minus_error_msg(Point2D a, Point2D expected, Point2D actual) { return stream.str(); } -TEST(UnaryMinusTest, UnaryMinusTest) { +TEST(PointUnaryMinusTest, PointUnaryMinusTest) { property_test( [] () { - Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + Point2D a = generatePoint(generateArea); return a; }, [] (Point2D a) { Point2D expected = { -a.x, -a.y }; Point2D actual = -a; - ASSERT_FLOAT_EQ(expected.x, actual.x) << unary_minus_error_msg(a, expected, actual); - ASSERT_FLOAT_EQ(expected.y, actual.y) << unary_minus_error_msg(a, expected, actual); + ASSERT_FLOAT_EQ(expected.x, actual.x) << point_unary_minus_error_msg(a, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << point_unary_minus_error_msg(a, expected, actual); } ); } -std::string minus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actual) { +std::string point_minus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actual) { std::ostringstream stream; stream << "Testing expression:\n" << " c = a - b" << "\n"; @@ -99,11 +101,11 @@ std::string minus_error_msg(Point2D a, Point2D b, Point2D expected, Point2D actu return stream.str(); } -TEST(MinusTest, MinusTest) { +TEST(PointMinusTest, PointMinusTest) { property_test( [] () { - Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; - Point2D b = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + Point2D a = generatePoint(generateArea); + Point2D b = generatePoint(generateArea); return std::make_tuple(a, b); }, [] (std::tuple data) { @@ -111,13 +113,13 @@ TEST(MinusTest, MinusTest) { std::tie(a, b) = data; Point2D expected = expected::add(a, { -b.x, -b.y }); Point2D actual = a - b; - ASSERT_FLOAT_EQ(expected.x, actual.x) << minus_error_msg(a, b, expected, actual); - ASSERT_FLOAT_EQ(expected.y, actual.y) << minus_error_msg(a, b, expected, actual); + ASSERT_FLOAT_EQ(expected.x, actual.x) << point_minus_error_msg(a, b, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << point_minus_error_msg(a, b, expected, actual); } ); } -std::string scalar_mul_error_msg(float s, Point2D a, Point2D expected, Point2D actual) { +std::string point_scalar_mul_error_msg(float s, Point2D a, Point2D expected, Point2D actual) { std::ostringstream stream; stream << "Testing expression:\n" << " c = s * a" << "\n"; @@ -134,8 +136,8 @@ std::string scalar_mul_error_msg(float s, Point2D a, Point2D expected, Point2D a TEST(ScalarProdTest, ScalarProdTest) { property_test( [] () { - float s = genFloat(MIN, MAX); - Point2D a = { genFloat(MIN, MAX), genFloat(MIN, MAX) }; + float s = generateFloat(MIN, MAX); + Point2D a = generatePoint(generateArea); return std::make_tuple(s, a); }, [] (std::tuple data) { @@ -144,8 +146,224 @@ TEST(ScalarProdTest, ScalarProdTest) { std::tie(s, a) = data; Point2D expected = expected::mul(s, a); Point2D actual = s * a; - ASSERT_FLOAT_EQ(expected.x, actual.x) << scalar_mul_error_msg(s, a, expected, actual); - ASSERT_FLOAT_EQ(expected.y, actual.y) << scalar_mul_error_msg(s, a, expected, actual); + ASSERT_FLOAT_EQ(expected.x, actual.x) << point_scalar_mul_error_msg(s, a, expected, actual); + ASSERT_FLOAT_EQ(expected.y, actual.y) << point_scalar_mul_error_msg(s, a, expected, actual); + } + ); +} + +std::string circle_plus_error_msg(Circle c, Point2D v, Circle expected, Circle actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " c' = c + v" << "\n"; + stream << "Test data:\n" + << " c = " << c << "\n" + << " v = " << v << "\n"; + stream << "Expected result:\n" + << " c' = " << expected << "\n"; + stream << "Actual result:\n" + << " c' = " << actual << "\n"; + return stream.str(); +} + +TEST(CirclePlusTest, CirclePlusTest) { + property_test( + [] () { + Circle c = generateCircle(generateFloat(MIN, MAX), generateArea); + Point2D v = generatePoint(generateArea); + return std::make_tuple(c, v); + }, + [] (std::tuple data) { + Circle c; + Point2D v; + std::tie(c, v) = data; + Circle expected = Circle { expected::add(c.center, v) , c.radius }; + Circle actual = c + v; + ASSERT_FLOAT_EQ(expected.center.x, actual.center.x) << circle_plus_error_msg(c, v, expected, actual); + ASSERT_FLOAT_EQ(expected.center.y, actual.center.y) << circle_plus_error_msg(c, v, expected, actual); + ASSERT_FLOAT_EQ(expected.radius, actual.radius) << circle_plus_error_msg(c, v, expected, actual); + } + ); +} + +std::string circle_minus_error_msg(Circle c, Point2D v, Circle expected, Circle actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " c' = c - v" << "\n"; + stream << "Test data:\n" + << " c = " << c << "\n" + << " v = " << v << "\n"; + stream << "Expected result:\n" + << " c' = " << expected << "\n"; + stream << "Actual result:\n" + << " c' = " << actual << "\n"; + return stream.str(); +} + +TEST(CircleMinusTest, CircleMinusTest) { + property_test( + [] () { + Circle c = generateCircle(generateFloat(MIN, MAX), generateArea); + Point2D v = generatePoint(generateArea); + return std::make_tuple(c, v); + }, + [] (std::tuple data) { + Circle c; + Point2D v; + std::tie(c, v) = data; + Circle expected = Circle { expected::add(c.center, Point2D { -v.x, -v.y }) , c.radius }; + Circle actual = c - v; + ASSERT_FLOAT_EQ(expected.center.x, actual.center.x) << circle_minus_error_msg(c, v, expected, actual); + ASSERT_FLOAT_EQ(expected.center.y, actual.center.y) << circle_minus_error_msg(c, v, expected, actual); + ASSERT_FLOAT_EQ(expected.radius, actual.radius) << circle_minus_error_msg(c, v, expected, actual); + } + ); +} + +std::string rectangle_plus_error_msg(Rectangle r, Point2D v, Rectangle expected, Rectangle actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " r' = r + v" << "\n"; + stream << "Test data:\n" + << " r = " << r << "\n" + << " v = " << v << "\n"; + stream << "Expected result:\n" + << " r' = " << expected << "\n"; + stream << "Actual result:\n" + << " r' = " << actual << "\n"; + return stream.str(); +} + +TEST(RectanglePlusTest, RectanglePlusTest) { + property_test( + [] () { + Rectangle r = generateRectangle(generateArea); + Point2D v = generatePoint(generateArea); + return std::make_tuple(r, v); + }, + [] (std::tuple data) { + Rectangle r; + Point2D v; + std::tie(r, v) = data; + Rectangle expected = Rectangle { + expected::add(r.topLeft, v), + expected::add(r.botRight, v) + }; + Rectangle actual = r + v; + ASSERT_FLOAT_EQ(expected.topLeft.x, actual.topLeft.x) << rectangle_plus_error_msg(r, v, expected, actual); + ASSERT_FLOAT_EQ(expected.topLeft.y, actual.topLeft.y) << rectangle_plus_error_msg(r, v, expected, actual); + ASSERT_FLOAT_EQ(expected.botRight.x, actual.botRight.x) << rectangle_plus_error_msg(r, v, expected, actual); + ASSERT_FLOAT_EQ(expected.botRight.y, actual.botRight.y) << rectangle_plus_error_msg(r, v, expected, actual); + } + ); +} + +std::string rectangle_minus_error_msg(Rectangle r, Point2D v, Rectangle expected, Rectangle actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " r' = r - v" << "\n"; + stream << "Test data:\n" + << " r = " << r << "\n" + << " v = " << v << "\n"; + stream << "Expected result:\n" + << " r' = " << expected << "\n"; + stream << "Actual result:\n" + << " r' = " << actual << "\n"; + return stream.str(); +} + +TEST(RectangleMinusTest, RectangleMinusTest) { + property_test( + [] () { + Rectangle r = generateRectangle(generateArea); + Point2D v = generatePoint(generateArea); + return std::make_tuple(r, v); + }, + [] (std::tuple data) { + Rectangle r; + Point2D v; + std::tie(r, v) = data; + Rectangle expected = Rectangle { + expected::add(r.topLeft, { -v.x, -v.y }), + expected::add(r.botRight, { -v.x, -v.y }) + }; + Rectangle actual = r - v; + ASSERT_FLOAT_EQ(expected.topLeft.x, actual.topLeft.x) << rectangle_minus_error_msg(r, v, expected, actual); + ASSERT_FLOAT_EQ(expected.topLeft.y, actual.topLeft.y) << rectangle_minus_error_msg(r, v, expected, actual); + ASSERT_FLOAT_EQ(expected.botRight.x, actual.botRight.x) << rectangle_minus_error_msg(r, v, expected, actual); + ASSERT_FLOAT_EQ(expected.botRight.y, actual.botRight.y) << rectangle_minus_error_msg(r, v, expected, actual); + } + ); +} + +std::string circle_scalar_mul_error_msg(float s, Circle c, Circle expected, Circle actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " c' = s * c" << "\n"; + stream << "Test data:\n" + << " s = " << s << "\n" + << " c = " << c << "\n"; + stream << "Expected result:\n" + << " c' = " << expected << "\n"; + stream << "Actual result:\n" + << " c' = " << actual << "\n"; + return stream.str(); +} + +TEST(CircleMulTest, CircleMulTest) { + property_test( + [] () { + float s = generateFloat(MIN, MAX); + Circle c = generateCircle(generateFloat(MIN, MAX), generateArea); + return std::make_tuple(s, c); + }, + [] (std::tuple data) { + float s; + Circle c; + std::tie(s, c) = data; + Circle expected = Circle { c.center, s * c.radius }; + Circle actual = s * c; + ASSERT_FLOAT_EQ(expected.center.x, actual.center.x) << circle_scalar_mul_error_msg(s, c, expected, actual); + ASSERT_FLOAT_EQ(expected.center.y, actual.center.y) << circle_scalar_mul_error_msg(s, c, expected, actual); + ASSERT_FLOAT_EQ(expected.radius, actual.radius) << circle_scalar_mul_error_msg(s, c, expected, actual); + } + ); +} + +std::string rectangle_scalar_mul_error_msg(float s, Rectangle r, Rectangle expected, Rectangle actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " r' = s * r" << "\n"; + stream << "Test data:\n" + << " s = " << s << "\n" + << " r = " << r << "\n"; + stream << "Expected result:\n" + << " r' = " << expected << "\n"; + stream << "Actual result:\n" + << " r' = " << actual << "\n"; + return stream.str(); +} + +TEST(RectangleScalarMulTest, RectangleScalarMulTest) { + property_test( + [] () { + float s = generateFloat(MIN, MAX); + Rectangle r = generateRectangle(generateArea); + return std::make_tuple(s, r); + }, + [] (std::tuple data) { + float s; + Rectangle r; + std::tie(s, r) = data; + Rectangle expected = Rectangle { + r.topLeft, + expected::add(r.topLeft, expected::mul(s, { width(r), height(r) })) + }; + Rectangle actual = s * r; + ASSERT_FLOAT_EQ(expected.topLeft.x, actual.topLeft.x) << rectangle_scalar_mul_error_msg(s, r, expected, actual); + ASSERT_FLOAT_EQ(expected.topLeft.y, actual.topLeft.y) << rectangle_scalar_mul_error_msg(s, r, expected, actual); + ASSERT_FLOAT_EQ(expected.botRight.x, actual.botRight.x) << rectangle_scalar_mul_error_msg(s, r, expected, actual); + ASSERT_FLOAT_EQ(expected.botRight.y, actual.botRight.y) << rectangle_scalar_mul_error_msg(s, r, expected, actual); } ); } \ No newline at end of file diff --git a/include/testing.hpp b/include/testing.hpp index 221e035..5295091 100644 --- a/include/testing.hpp +++ b/include/testing.hpp @@ -9,6 +9,7 @@ #include #include "point.hpp" +#include "utils.hpp" inline void initGen() { // fix seed to generate test data deterministically diff --git a/include/utils.hpp b/include/utils.hpp index a83613e..56b5b65 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -17,4 +17,6 @@ Point2D generatePoint(const Rectangle& boundingBox); Circle generateCircle(float radius, const Rectangle& boundingBox); +Rectangle generateRectangle(const Rectangle& boundingBox); + #endif // CPPBASICS_UTILS_HPP From 10d782f86b3c38bd78a941a2def0fde794755377 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 6 Dec 2023 18:10:29 +0100 Subject: [PATCH 066/137] game object task (framework lesson stuff + tests) Signed-off-by: Evgeniy Moiseenko --- .../IntroducingObjects/src/cgobject.cpp | 10 +- .../IntroducingObjects/src/collision.cpp | 10 -- .../IntroducingObjects/src/consumable.cpp | 25 +-- .../IntroducingObjects/src/enemy.cpp | 10 +- .../IntroducingObjects/src/engine.cpp | 20 ++- .../IntroducingObjects/src/gobjectlist.cpp | 36 +---- .../IntroducingObjects/src/main.cpp | 7 + .../IntroducingObjects/src/player.cpp | 18 +-- .../IntroducingObjects/src/scene.cpp | 6 +- .../IntroducingObjects/src/statscene.cpp | 2 +- .../IntroducingObjects/src/utils.cpp | 12 +- .../IntroducingObjects/task-info.yaml | 94 +++--------- .../IntroducingObjects/task.md | 72 +++++++++ .../IntroducingObjects/task0.md | 23 --- .../IntroducingObjects/test/test.cpp | 84 +++++++++- .../ClassesAndObjects/Introduction/task0.md | 0 .../task1.md | 0 .../task2.md | 23 +-- .../task3.md | 8 +- .../task4.md | 15 +- .../task5.md | 2 +- .../task6.md | 15 +- .../task7.md | 4 +- .../task8.md | 7 +- .../solutions/src/cgobject.cpp | 47 ++++++ .../solutions/src/collision.cpp | 31 ++++ .../solutions/src/consumable.cpp | 46 ++++++ .../solutions/src/direction.cpp | 16 ++ .../solutions/src/dynscene.cpp | 144 ++++++++++++++++++ .../ClassesAndObjects/solutions/src/enemy.cpp | 44 ++++++ .../solutions/src/engine.cpp | 85 +++++++++++ .../solutions/src/gobject.cpp | 7 + .../solutions/src/gobjectlist.cpp | 74 +++++++++ .../ClassesAndObjects/solutions/src/main.cpp | 6 + .../solutions/src/operators.cpp | 42 +++++ .../solutions/src/player.cpp | 54 +++++++ .../ClassesAndObjects/solutions/src/point.cpp | 19 +++ .../solutions/src/rectangle.cpp | 35 +++++ .../ClassesAndObjects/solutions/src/scene.cpp | 47 ++++++ .../solutions/src/scenes.cpp | 14 ++ .../solutions/src/statscene.cpp | 46 ++++++ .../solutions/src/textures.cpp | 42 +++++ .../ClassesAndObjects/solutions/src/utils.cpp | 43 ++++++ include/gobject.hpp | 5 +- include/testscene.hpp | 78 ++++++++++ 45 files changed, 1182 insertions(+), 246 deletions(-) delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task0.md rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task1.md (100%) rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task2.md (87%) rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task3.md (90%) rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task4.md (87%) rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task5.md (98%) rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task6.md (87%) rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task7.md (95%) rename ObjectOrientedProgramming/ClassesAndObjects/{IntroducingObjects => Introduction}/task8.md (94%) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp create mode 100644 include/testscene.hpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp index 1d49ba3..6b24599 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/cgobject.cpp @@ -8,23 +8,23 @@ CircleGameObject::CircleGameObject(Circle circle) {} Point2D CircleGameObject::getPosition() const { - return circle.center; + // TODO: write your solution here } void CircleGameObject::setPosition(Point2D position) { - circle.center = position; + // TODO: write your solution here } GameObjectStatus CircleGameObject::getStatus() const { - return status; + // TODO: write your solution here } void CircleGameObject::setStatus(GameObjectStatus newStatus) { - status = newStatus; + // TODO: write your solution here } Circle CircleGameObject::getCircle() const { - return circle; + // TODO: write your solution here } Rectangle CircleGameObject::getBoundingBox() const { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp index a41a0e9..9b56990 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/collision.cpp @@ -1,18 +1,9 @@ -#include #include #include "collision.hpp" -#include "operators.hpp" #include "cgobject.hpp" #include "utils.hpp" -// TODO: move to utils.cpp (?) -float distance(Point2D a, Point2D b) { - float dx = a.x - b.x; - float dy = a.y - b.y; - return sqrt(dx * dx + dy * dy); -} - CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { CollisionInfo info; info.distance = distance(circle1.center, circle2.center); @@ -26,6 +17,5 @@ CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2 if (circleObject1 && circleObject2) { return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); } - // TODO assert(false); } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp index d03f046..7bd2eaa 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/consumable.cpp @@ -15,32 +15,13 @@ Point2D ConsumableObject::getVelocity() const { } void ConsumableObject::update(sf::Time delta) { - if (getStatus() != GameObjectStatus::DESTROYED) { - setStatus(GameObjectStatus::NORMAL); - } + // TODO: write your solution here } void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { - if (getStatus() == GameObjectStatus::DESTROYED || object.getKind() == GameObjectKind::CONSUMABLE) { - return; - } - if (info.collide) { - setStatus(GameObjectStatus::DESTROYED); - return; - } - if (info.distance < CONSUMABLE_WARNED_MULTIPLIER * getCircle().radius) { - setStatus(GameObjectStatus::WARNED); - return; - } + // TODO: write your solution here } const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { - switch (getStatus()) { - case GameObjectStatus::NORMAL: - return textureManager.getTexture(GameTextureID::STAR); - case GameObjectStatus::WARNED: - return textureManager.getTexture(GameTextureID::STAR_CONCERNED); - case GameObjectStatus::DESTROYED: - return nullptr; - } + // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 083d4d1..48a52bc 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -27,12 +27,7 @@ void EnemyObject::update(sf::Time delta) { } void EnemyObject::updateVelocity() { - Direction direction1 = static_cast(generateInt(0, 3)); - Direction direction2 = static_cast(generateInt(0, 3)); - Point2D directionVector = (direction1 == direction2) - ? getDirection(direction1) - : (getDirection(direction1) + getDirection(direction2)); - velocity = SPEED * directionVector; + // TODO: write your solution here } void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { @@ -40,5 +35,6 @@ void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &inf } const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { - return textureManager.getTexture(GameTextureID::BLACKHOLE); + // TODO: write your solution here + return nullptr; } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp index 3f5a6e8..c506cdf 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/engine.cpp @@ -3,8 +3,8 @@ #include "constants.hpp" GameEngine *GameEngine::create() { - static GameEngine engine; - return &engine; + // TODO: write your solution here + return nullptr; } GameEngine::GameEngine() @@ -28,7 +28,7 @@ GameEngine::GameEngine() void GameEngine::run() { sf::Clock clock; - while (active && window.isOpen()) { + while (isActive()) { sf::Time delta = clock.restart(); processInput(); update(delta); @@ -37,6 +37,15 @@ void GameEngine::run() { } } +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + void GameEngine::processInput() { sf::Event event; while (window.pollEvent(event)) { @@ -77,9 +86,4 @@ void GameEngine::resizeWindow() { Rectangle sceneBox = scene->getBoundingBox(); sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); window.setSize(sceneSize); -} - -void GameEngine::close() { - active = false; - window.close(); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp index ea0fd08..b4724f4 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp @@ -1,24 +1,15 @@ #include "gobjectlist.hpp" void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { - cursor->next->prev = node.get(); - node->next = std::move(cursor->next); - node->prev = cursor; - cursor->next = std::move(node); + // TODO: write your solution here } void GameObjectList::unlink(GameObjectList::Node *node) { - node->next->prev = node->prev; - node->prev->next = std::move(node->next); + // TODO: write your solution here } void GameObjectList::insert(const std::shared_ptr &object) { - if (!object) { - return; - } - std::unique_ptr node = std::make_unique(); - node->object = object; - link(head.get(), std::move(node)); + // TODO: write your solution here } void GameObjectList::remove(const std::function &pred) { @@ -41,34 +32,23 @@ void GameObjectList::foreach(const std::function& apply) { } GameObjectList::GameObjectList() { - head = std::make_unique(); - head->next = std::make_unique(); - tail = head->next.get(); - tail->prev = head.get(); + // TODO: write your solution here } GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { - Node* cursor = head.get(); - Node* curr = other.head->next.get(); - while (curr != other.tail) { - link(cursor, std::make_unique()); - cursor = cursor->next.get(); - cursor->object = curr->object; - curr = curr->next.get(); - } + // TODO: write your solution here } GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { - swap(*this, other); + // TODO: write your solution here } GameObjectList &GameObjectList::operator=(GameObjectList other) { - swap(*this, other); + // TODO: write your solution here return *this; } void swap(GameObjectList& first, GameObjectList& second) { using std::swap; - swap(first.head, second.head); - swap(first.tail, second.tail); + // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp index 3608feb..b192818 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/main.cpp @@ -1,6 +1,13 @@ #include "engine.hpp" +#include + int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } GameEngine::create()->run(); return 0; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp index fcadfff..e335cf0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/player.cpp @@ -36,19 +36,11 @@ void PlayerObject::update(sf::Time delta) { return; } -void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { - if (info.collide && object.getKind() == GameObjectKind::ENEMY) { - setStatus(GameObjectStatus::DESTROYED); - } +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; } -const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { - switch (getStatus()) { - case GameObjectStatus::NORMAL: - return textureManager.getTexture(GameTextureID::PLANET); - case GameObjectStatus::DESTROYED: - return textureManager.getTexture(GameTextureID::PLANET_DEAD); - default: - return nullptr; - } +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp index 1457361..81a4470 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/scene.cpp @@ -15,13 +15,11 @@ Rectangle Scene::getBoundingBox() const { } void Scene::setObjectPosition(GameObject& object, Point2D position) { - object.setPosition(position); - fitInto(object); + // TODO: write your solution here } void Scene::move(GameObject &object, Point2D vector) { - object.move(vector); - fitInto(object); + // TODO: write your solution here } void Scene::move(GameObject& object, sf::Time delta) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp index 07c0bd0..a6abdfa 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/statscene.cpp @@ -31,8 +31,8 @@ void GameplayStaticScene::update(sf::Time delta) { consumable.update(delta); enemy.update(delta); move(player, delta); - move(enemy, delta); move(consumable, delta); + move(enemy, delta); detectCollision(player, consumable); detectCollision(enemy, player); detectCollision(enemy, consumable); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp index 82ce84e..783fc7a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/utils.cpp @@ -1,9 +1,14 @@ #include "utils.hpp" #include +#include #include -// TODO: move `distance` here +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} float generateFloat(float min, float max) { return min + (rand() / (RAND_MAX / (max - min))); @@ -29,7 +34,6 @@ Circle generateCircle(float radius, const Rectangle& boundingBox) { if (radius > std::min(width(boundingBox), height(boundingBox))) { return circle; } - // TODO: replace with rectangle arithmetics operators? circle.center.x = generateFloat( boundingBox.topLeft.x + radius, boundingBox.botRight.x - radius @@ -40,4 +44,8 @@ Circle generateCircle(float radius, const Rectangle& boundingBox) { ); circle.radius = radius; return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index b4002d1..141b469 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -1,104 +1,46 @@ type: edu custom_name: Introducing Objects files: -- name: CMakeLists.txt - visible: false -- name: src/rectangle.cpp - visible: true -- name: src/direction.cpp - visible: true - name: src/main.cpp visible: true -- name: src/utils.cpp + editable: false +- name: src/engine.cpp visible: true -- name: task1.md +- name: src/scenes.cpp visible: true -- name: task2.md +- name: src/textures.cpp visible: true -- name: task3.md +- name: src/scene.cpp visible: true -- name: task4.md +- name: src/statscene.cpp visible: true -- name: src/collision.cpp +- name: src/dynscene.cpp visible: true - name: src/gobject.cpp visible: true -- name: task5.md - visible: true -- name: task6.md - visible: true - name: src/gobjectlist.cpp visible: true -- name: task7.md - visible: true -- name: task0.md - visible: true -- name: task8.md - visible: true -- name: src/engine.cpp - visible: true -- name: src/consumable.cpp - visible: true - name: src/cgobject.cpp visible: true - name: src/player.cpp visible: true -- name: src/statscene.cpp +- name: src/consumable.cpp visible: true -- name: src/textures.cpp +- name: src/enemy.cpp visible: true -- name: src/dynscene.cpp +- name: src/collision.cpp visible: true -- name: src/scenes.cpp +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp visible: true - name: src/point.cpp visible: true -- name: test/test.cpp - visible: false - name: src/operators.cpp visible: true - placeholders: - - offset: 67 - length: 17 - placeholder_text: "return { 0.0f, 0.0f };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 1 - is_visible: false - - offset: 123 - length: 22 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 2 - is_visible: false - - offset: 195 - length: 18 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 3 - is_visible: false - - offset: 261 - length: 17 - placeholder_text: "return { 0.0f, 0.0f, };" - dependency: - section: ObjectOrientedProgramming - lesson: ClassesAndObjects - task: OperatorsOverloading - file: src/operators.cpp - placeholder: 4 - is_visible: false -- name: src/enemy.cpp - visible: true -- name: src/scene.cpp +- name: src/utils.cpp visible: true +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md index e69de29..e6d67a8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md @@ -0,0 +1,72 @@ +Let us finally meet with the object-oriented programming paradigm. + +At the core of this paradigm lies the concept of the _object_. +An object groups together some data, called object's _state_, +with a set of functions operating on this data. +These functions are also called the _methods_ of the object. +In this way, objects are similar to structures, but unlike plain structures, +they also allow to add functions into their definition. + +Objects are grouped into _classes_. +Class defines a blueprint after which the objects are created. +In other words, a _class_ is just a type of objects. + +For example, let us consider the `GameObject` class. +The objects of this class represent entities appearing on the game scene, +such as the planet object controlled by the player, +consumable star objects, and others that we will add later. +Instances of this class will store data related to a game object, +such as its position on the scene, and some methods to manipulate the object. + +Let us have a look at the `GameObject` class definition. +First note that in C++ new class is defined with the help of the `class` keyword. +Next comes the keyword `public` --- we will describe its meaning in the later steps. +After that come the methods of the class. +There are plenty of them, you can get the meaning of each method +by consulting its _documentation_ given as a docstring comment in front of the method declaration. + +[//]: # (TODO: add links to docstring format) + +The `GameObject` class itself does not define any data fields, only the methods. +It, however, implicitly defines a bunch of _properties_ of an object, for example, its position. +A value of a property can be requested using its _getter_ method (e.g. `getPosition`), +and it can be changed using its _setter_ method (e.g. `setPosition`). +Note that some properties of an object have both _getter_ and _setter_ methods, +like aforementioned `getPosition` and `setPosition` methods, +while others have only _getter_, for example `getVelocity`. +This is for a reason — some properties are derivatives of the current objects' status, +and they cannot be directly changed from the outside. + +Another piece of unfamiliar syntax here is the `const` keyword coming after the arguments of a methods. +It denotes the _constant methods_ --- these methods cannot change the state of the object. + +Finally, keyword `virtual` denotes the _virtual_ methods — these are the methods +that can be _overridden_ by the inheritors of the class +(we will delve back to inheritance in the next task). +The `= 0` syntax at the end of the virtual method indicates that +it is a _pure virtual_ method. +Such a method is not implemented for the given class — +it is just a stub for an actual method implementation. + +The classes that do declare any data fields and contain pure virtual methods are also called _interfaces_. +In some sense, interfaces just provide a description of the objects' behavior, +without actually specifying their internal status. +This leaves a programmer an opportunity to define several _subclasses_ +of an interface that provide different implementations of its behavior. +For example, in the following steps of this lesson, you will have to implement +a subclass of playable objects, consumable objects, and others. + +But before moving to the following step, please complete a small programming exercise. +One of the methods of the `GameObject` class --- the `move` method --- is actually not `virtual`. +It is because it can be implemented in terms of other methods of this class, namely `getPosition` and `setObjectPosition`. +To finish the programming assignment, please provide an implementation of this method. + +The implementation of the method should be put into `gobject.cpp` file. +Note that methods' definition given in this file contains both +name of the class, and the name of the method, separated by the `::`. + +```c++ +void GameObject::move(Point2D vector) { + ... +} +``` diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md deleted file mode 100644 index 2b95e9b..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task0.md +++ /dev/null @@ -1,23 +0,0 @@ -In the next few lessons, you will learn about the object-oriented programming paradigm -in the context of the C++ language. As you will see, this paradigm helps -to organize the code into independent subcomponents, manage the complexity -of the applications, and streamline the addition of new functionality. - -We will re-implement our game using the features of object-oriented programming. -This effort will help us to easily extend the game by adding new kinds of objects. -In particular, we will add enemy objects that will make our little game a bit more challenging. -There is a lot of fun coming, so get ready for your journey into the world of objects! - -Here is a list of specific topics that are going to be covered: - -* Operators overloading. -* Classes and objects. -* Abstract classes and interfaces. -* Constructors and destructors. -* Inheritance and subtyping. -* Subtype polymorphism. -* Virtual methods. -* Visibility modifiers: `public`, `protected`, and `private`. -* Encapsulation and class invariants. -* Static methods, fields, and variables. -* Objects vs structures. \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp index 734038d..7c9aa1a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp @@ -1,11 +1,83 @@ #include -// Headers of objects that student should implement: -int sum(int a, int b); +#include +#include "gobject.hpp" +#include "operators.hpp" -// Tests: -// todo: replace this with an actual test -TEST(SumTest, Simple) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class - ASSERT_EQ(sum(1, 2), 3); +#include "testscene.hpp" +#include "testing.hpp" + +const float MIN = -10e3; +const float MAX = 10e3; + +const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; + + +std::string move_error_msg(GameObject* object, Point2D vector, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.move(vector)" << "\n"; + stream << "Test data:" << "\n" + << " object.getPosition() = " << object->getPosition() << "\n" + << " vector = " << vector << "\n"; + stream << "Expected result:\n" + << " object.getPosition() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getPosition() = " << actual << "\n"; + return stream.str(); +} + +TEST(MoveTest, MoveTest0) { + property_test( + [] () { + Point2D position = generatePoint(generateArea); + Point2D velocity = Point2D { 0.0f, 0.0f }; + TestGameObject object = TestGameObject(position, velocity, + GameObjectStatus::NORMAL, + GameObjectKind::CONSUMABLE + ); + return std::make_tuple(object); + }, + [] (std::tuple data) { + TestGameObject object; + std::tie(object) = data; + TestGameObject expected = object; + TestGameObject actual = object; + Point2D vector0 = Point2D { 0.0f, 0.0f }; + object.performMove(vector0); + ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) + << move_error_msg(&object, vector0, expected.getPosition(), actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) + << move_error_msg(&object, vector0, expected.getPosition(), actual.getPosition()); + } + ); +} + +TEST(MoveTest, MoveTestRandom) { + property_test( + [] () { + Point2D position = generatePoint(generateArea); + Point2D velocity = Point2D { 0.0f, 0.0f }; + TestGameObject object = TestGameObject(position, velocity, + GameObjectStatus::NORMAL, + GameObjectKind::CONSUMABLE + ); + Point2D vector = generatePoint(generateArea); + return std::make_tuple(object, vector); + }, + [] (std::tuple data) { + TestGameObject object; + Point2D vector; + std::tie(object, vector) = data; + TestGameObject expected = object; + expected.setPosition(expected.getPosition() + vector); + TestGameObject actual = object; + object.performMove(vector); + ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) + << move_error_msg(&object, vector, expected.getPosition(), actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) + << move_error_msg(&object, vector, expected.getPosition(), actual.getPosition()); + } + ); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task0.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task0.md new file mode 100644 index 0000000..e69de29 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task1.md similarity index 100% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task1.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task1.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task2.md similarity index 87% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task2.md index ff28046..166e9e2 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task2.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task2.md @@ -1,6 +1,6 @@ As we have mentioned, `GameObject` class on itself -does not specify the status of game objects — it just describes their behavior. -We need another class that extends `GameObject` with an actual status. +does not specify the state of game objects — it just describes their behavior. +We need another class that extends `GameObject` with an actual state. Fortunately, object-oriented programming has a suitable concept for this job — it is called class _inheritance_. @@ -52,15 +52,14 @@ CircleGameObject::CircleGameObject(Circle circle) After the arguments' list comes the semicolon `:`, followed by the list of the object's fields. The value, provided in the brackets next to the field's name, is used to initialize the corresponding field. -Please note that the order of the fields in the _constructor initializer list_ is important, -it should match the order in which the fields are declared in the class. +Please note that the order of the fields in the _constructor initializer list_ is important. +It should match the order in which the fields are declared in the class. After the constructor initializer list comes the constructor body `{}` (empty in this case). Similarly, as regular methods, it can contain arbitrary C++ statements. A constructor has its counterpart — the _destructor_ method, which should have the same name as a class prefixed with `~`. -It is a method called automatically before destruction of the object -in order to perform some clean-up routines. +It is a method called automatically before destruction of the object to perform some clean-up routines. A class can have several constructors taking different arguments, but there could only one destructor taking no arguments. @@ -69,13 +68,19 @@ a class `GameObject` has a virtual destructor `~GameObject()`. The `= default` syntax at the end of its definition indicates that this destructor has default auto-generated implementation. -As we will see later in the course, constructors and destructor have a pivotal role in C++. +As we will see later in the course, constructors and destructors have a pivotal role in C++. Going back to the `CircleGameObject` class, consider its methods. Some of them, like `getPosition` and `setPosition`, are just re-declared methods of the `GameObject` class. The keyword `override` at the end of the methods' declarations indicates this fact. -However, unlike the `GameObject` class, the `CircleGameObject` class actually defines the behavior of these methods. +However, unlike the `GameObject` class, the `CircleGameObject` class can actually define the behavior of these methods. To be precise, it is your task to implement some of them, namely `getPosition`, `setOPosition`, `getStatus`, `setStatus`, and `getCircle`. -Keep in mind that the position of the `CircleGameObject` is a position of its circle's center. \ No newline at end of file + +
+ +Note that the position of the `CircleGameObject` is a position of its circle's center. + +
+ diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task3.md similarity index 90% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task3.md index b4edc9f..cf0a5f5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task3.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task3.md @@ -30,6 +30,8 @@ It is another predefined by us class — it is responsible for loading the textu A pointer to a texture can be requested by calling the `getTexture` method of the `TextureManager` class. It takes as argument the ID of the textures — these IDs are represented by the `GameTextureID` enum. +[//]: # (TODO: explain difference between `enum` and `enum class`) + Please implement the `getTexture` methods of the `PlayerObject` and `ConsumableObject` with the following logic: * under `NORMAL` status, the player object should have `PLANET` texture; @@ -45,10 +47,8 @@ consult the documentation of the `GameObject`'s `getTexture` method.
-In order to implement this method, you will have to call -the `getTexture` method of the `TextureManager` class. -In order to do that, use the dot syntax `.` — the same syntax as the one used -to access fields of a structure: +To implement this method, you will have to call the `getTexture` method of the `TextureManager` class. +To do so use the dot syntax `.` — the same syntax as the one used to access fields of a structure: ```c++ const sf::Texture* texture = textureManaged.getTexture(id); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task4.md similarity index 87% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task4.md index 3c841e1..3b53799 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task4.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task4.md @@ -7,10 +7,9 @@ In essence, it is a working horse of our simple game engine. Let us have a look at the declaration of the `Scene` class. This class is pretty packed — it includes both regular and virtual methods as well as some data fields. -For the details on the meaning of these class' methods, we refer to their documentation. +For the details on the meaning of these methods, we refer to their documentation. -What is more interesting — the `Scene` class groups its members into three sections: -`public`, `protected`, and `private`. +The `Scene` class groups its members into three sections: `public`, `protected`, and `private`. Let us finally decipher their meaning! With the help of these _visibility modifiers_, @@ -32,8 +31,8 @@ what fields and methods can the clients of the class access. [//]: # (TODO: add a note about visibility-inheritance modifier) -You might be wondering what is the point of hiding some of -the class' field or methods — after all, they can be useful outside. +You might be wondering what is the point of hiding some fields or methods of the class — +after all, they can be useful outside. However, the ability to hide some of the object's _implementation details_ gains the objects an ultimate control over their internal state. @@ -42,7 +41,7 @@ Encapsulation allows the developer of a class to maintain the _invariants_ on ob ensuring that objects of this class always remain in some valid state. Let us explain this on the example of the `Scene` class. -Among other things, this class is responsible for keeping on the game objects appearing on the scene. +Among other things, this class is responsible for storing the game objects appearing on the scene. One useful invariant that `Scene` class may enforce is that all of its game objects are lay within the scene's borders. But if the `GameObject` class provides a `public` method to change object's position (i.e. `setPosition`), then the `Scene` object has no means to guarantee this property. @@ -55,7 +54,7 @@ the developer of a class may enforce various useful invariants on the state of a Mastering the invariants of classes and controlling the visibility of their members is a skill that comes with the experience. The more complex applications you will architect and develop, -the better you will become at designing classes, their invariants. +the better you will become at designing classes and their invariants. To consolidate the material of this step, please implement the following two methods of the `Scene` class. @@ -68,7 +67,7 @@ void move(GameObject& object, Point2D vector); You need to guarantee the invariant of the `Scene` class we discussed above — the objects of the scene should remain within its borders. -In order to implement these methods, you have to use +To implement these methods, you have to use corresponding methods of the `GameObject` class, as well as another method the `Scene` class — the `fitInto` method. This method adjusts the position of an object to fit into the `Scene` borders. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task5.md similarity index 98% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task5.md index 5c51289..3344be6 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task5.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task5.md @@ -57,7 +57,7 @@ std::cout << foo() << std::endl; With the help of the `static` modifier, it becomes possible to declare static `GameplayStaticScene` variable inside `Scene::create` method -and return pointer to this variable. +and return a pointer to this variable.
diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task6.md similarity index 87% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task6.md index 4517556..69ecd66 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task6.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task6.md @@ -1,6 +1,6 @@ Now let us restore the collision detection functionality in our refactored game. -We have already refactored the collision detection function. +We have already changed the signature of the collision detection function. ```c++ CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2); @@ -27,12 +27,12 @@ with additional information required to implement new features in our game. You might wonder why we decided to model `CollisionInfo` as a structure, instead of using the fancy objects we learned in this lesson. -In fact, we did this on purposes to illustrate the following point. +In fact, we did this on purpose to illustrate the following point. Structures, declared via `struct`, and objects/classes, declared via `class`, are not incompatible concepts in C++, and often instances of both can be found in the same codebase. -* Objects are used to tie in together data (fields) and behavior (methods). +* Objects are used to tie together data (fields) and behavior (methods). Objects provide _encapsulation_ and _polymorphism_. The state of objects can satisfy various invariants, maintained by carefully controlling the visibility of class' members. @@ -41,8 +41,7 @@ and often instances of both can be found in the same codebase. They have predictable memory layout and predictable behavior — there are no associated virtual methods dispatched at runtime. -In C++, structures are also sometime referred -to as [_POD types_]((https://en.wikipedia.org/wiki/Passive_data_structure)), +In C++, structures are also sometimes referred to as [_POD types_]((https://en.wikipedia.org/wiki/Passive_data_structure)), where POD stands for _plain old data_.
@@ -64,7 +63,7 @@ your task is to re-implement the behavior of consumable objects. - Upon collision with another object, the consumable should change its status into `DESTROYED`. - When another object is approaching the consumable, it should change its status into `WARNED`. This should happen whenever the distance between another object and consumable is less than `C * r`, where - - `r` is the radius of consumable object, + - `r` is the radius of the consumable object, - `C` is a special multiplier constant ```c++ @@ -87,6 +86,6 @@ This method is also periodically called from the `Scene` class to give the object an opportunity to update its internal state. This function takes as a single argument the amount of time elapsed since the last update, although you will not need this information in the current task. -Simply reset the status of the alive (that is --- not `DESTROYED`!) consumable back to `NORMAL`. +Reset the status of the alive (that is --- not `DESTROYED`!) consumable back to `NORMAL`. If there are some objects nearby, they will be detected again during `onCollision` call, -otherwise the consumable object will remain in `NORMAL` status. +otherwise the consumable object will remain in the `NORMAL` status. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task7.md similarity index 95% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task7.md index 8c70287..a436757 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task7.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task7.md @@ -43,13 +43,13 @@ The harder part is to implement the collision behavior. When an enemy object collides with the player object, the player object should become inactive. This can be achieved by setting the status of the player object to `DESTROYED`. However, an enemy object does not have direct access to the `setStatus` method of the player object. -So in order to implement the desired behavior, you actually need to +To implement the desired behavior, you actually need to modify the `onCollision` method of the `PlayerObject` class, not the `EnemyObject` class!
In the implementation of the `PlayerObject::onCollision` method, -do not forget to check that the collision occurred with the enemy object, +remember to check that the collision occurred with the enemy object, not an object of some other kind!
diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task8.md similarity index 94% rename from ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md rename to ObjectOrientedProgramming/ClassesAndObjects/Introduction/task8.md index 8fe5726..f50bcdc 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task8.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task8.md @@ -8,9 +8,6 @@ as we will need some concepts taught there. First of all, instead of completely rewriting the `GameplayStaticScene` class, we will just add a new class --- `GameplayDynamicScene`, where we will implement the new dynamic functionality. -[//]: # (Then switching between the two `Scene` implementations will be quite easy --- ) -[//]: # (TODO: describe the actual scene-switching logic once it will be settled.) - Please have a look at the declaration of the `GameplayDynamicScene` class (file `dynscene.hpp`), and its definition (file `dynscene.cpp`). You can find the brief description of its methods in the documentation comments. @@ -95,7 +92,7 @@ we used a single sentinel node to simplify the implementation of some list opera Moreover, under the hood the list was organized into a cyclic list: the `next` field of the last node was pointing the first (sentinel) node. This time we cannot reuse this trick, since a cyclic list would result into ownership cycle. -Therefore, we would need two sentinel nodes — one as a first node, and second as a last node. +Therefore, we would need two sentinel nodes — one as the first node, and the second as a last node. Please take a look at the pre-defined methods `foreach` and `remove` of the list that utilize this list representation: @@ -110,7 +107,7 @@ We will have a closer look at this type in the later modules of the course.
-Now, please implement the method inserting a game object into the beginning of the list: +Now, please implement the method for inserting a game object into the beginning of the list: ```c++ void insert(const std::shared_ptr& object); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp new file mode 100644 index 0000000..a41a0e9 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp @@ -0,0 +1,31 @@ +#include +#include + +#include "collision.hpp" +#include "operators.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +// TODO: move to utils.cpp (?) +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + // TODO + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp new file mode 100644 index 0000000..d03f046 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp @@ -0,0 +1,46 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + if (getStatus() != GameObjectStatus::DESTROYED) { + setStatus(GameObjectStatus::NORMAL); + } +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (getStatus() == GameObjectStatus::DESTROYED || object.getKind() == GameObjectKind::CONSUMABLE) { + return; + } + if (info.collide) { + setStatus(GameObjectStatus::DESTROYED); + return; + } + if (info.distance < CONSUMABLE_WARNED_MULTIPLIER * getCircle().radius) { + setStatus(GameObjectStatus::WARNED); + return; + } +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp new file mode 100644 index 0000000..083d4d1 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp @@ -0,0 +1,44 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + Direction direction1 = static_cast(generateInt(0, 3)); + Direction direction2 = static_cast(generateInt(0, 3)); + Point2D directionVector = (direction1 == direction2) + ? getDirection(direction1) + : (getDirection(direction1) + getDirection(direction2)); + velocity = SPEED * directionVector; +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + return textureManager.getTexture(GameTextureID::BLACKHOLE); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp new file mode 100644 index 0000000..3f5a6e8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp @@ -0,0 +1,85 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + static GameEngine engine; + return &engine; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (active && window.isOpen()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} + +void GameEngine::close() { + active = false; + window.close(); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp new file mode 100644 index 0000000..ea0fd08 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp @@ -0,0 +1,74 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + cursor->next->prev = node.get(); + node->next = std::move(cursor->next); + node->prev = cursor; + cursor->next = std::move(node); +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + node->next->prev = node->prev; + node->prev->next = std::move(node->next); +} + +void GameObjectList::insert(const std::shared_ptr &object) { + if (!object) { + return; + } + std::unique_ptr node = std::make_unique(); + node->object = object; + link(head.get(), std::move(node)); +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + head = std::make_unique(); + head->next = std::make_unique(); + tail = head->next.get(); + tail->prev = head.get(); +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + Node* cursor = head.get(); + Node* curr = other.head->next.get(); + while (curr != other.tail) { + link(cursor, std::make_unique()); + cursor = cursor->next.get(); + cursor->object = curr->object; + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + swap(*this, other); +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + swap(*this, other); + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + swap(first.head, second.head); + swap(first.tail, second.tail); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp new file mode 100644 index 0000000..3608feb --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp @@ -0,0 +1,6 @@ +#include "engine.hpp" + +int main() { + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp new file mode 100644 index 0000000..fcadfff --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp @@ -0,0 +1,54 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (info.collide && object.getKind() == GameObjectKind::ENEMY) { + setStatus(GameObjectStatus::DESTROYED); + } +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::DESTROYED: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp new file mode 100644 index 0000000..1457361 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp @@ -0,0 +1,47 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + object.setPosition(position); + fitInto(object); +} + +void Scene::move(GameObject &object, Point2D vector) { + object.move(vector); + fitInto(object); +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp new file mode 100644 index 0000000..07c0bd0 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(enemy, delta); + move(consumable, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp new file mode 100644 index 0000000..82ce84e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp @@ -0,0 +1,43 @@ +#include "utils.hpp" + +#include +#include + +// TODO: move `distance` here + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + // TODO: replace with rectangle arithmetics operators? + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} \ No newline at end of file diff --git a/include/gobject.hpp b/include/gobject.hpp index 704d3e6..84ec76a 100644 --- a/include/gobject.hpp +++ b/include/gobject.hpp @@ -82,20 +82,17 @@ class GameObject { virtual ~GameObject() = default; protected: + friend class Scene; /** * Changes the current position of the object. */ virtual void setPosition(Point2D position) = 0; -private: - friend class Scene; - /** * Moves an object in a direction given by the passed vector. */ void move(Point2D vector); - }; diff --git a/include/testscene.hpp b/include/testscene.hpp new file mode 100644 index 0000000..accfa26 --- /dev/null +++ b/include/testscene.hpp @@ -0,0 +1,78 @@ +#ifndef CPPBASICS_TESTSCENE_HPP +#define CPPBASICS_TESTSCENE_HPP + +#include "gobject.hpp" +#include "scene.hpp" + +class TestGameObject : public GameObject { +public: + + inline TestGameObject() + : position(Point2D { 0.0f, 0.0f }) + , velocity(Point2D { 0.0f, 0.0f }) + , status(GameObjectStatus::NORMAL) + , kind(GameObjectKind::CONSUMABLE) + {} + + inline TestGameObject(Point2D position, Point2D velocity, GameObjectStatus status, GameObjectKind kind) + : position(position) + , velocity(velocity) + , status(status) + , kind(kind) + {} + + TestGameObject(const TestGameObject& other) = default; + TestGameObject& operator=(const TestGameObject& other) = default; + + inline Point2D getPosition() const override { + return position; + } + + inline void setPosition(Point2D position) override { + this->position = position; + } + + inline GameObjectStatus getStatus() const override { + return status; + } + + inline GameObjectKind getKind() const override { + return kind; + } + + inline Point2D getVelocity() const override { + return velocity; + } + + inline Rectangle getBoundingBox() const override { + return Rectangle { position, position }; + } + + inline void update(sf::Time delta) override { + return; + } + + inline void onCollision(const GameObject& object, const CollisionInfo& info) override { + return; + } + + inline void draw(sf::RenderWindow& window, TextureManager& textureManager) const override { + return; + } + + inline const sf::Texture* getTexture(TextureManager& textureManager) const override { + return nullptr; + } + + void performMove(Point2D vector) { + move(vector); + } + +private: + Point2D position; + Point2D velocity; + GameObjectStatus status; + GameObjectKind kind; +}; + +#endif //CPPBASICS_TESTSCENE_HPP From 756a2772430649aaa117e1ef66e79f7ca0d7fb6a Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 6 Dec 2023 19:20:31 +0100 Subject: [PATCH 067/137] fixing framework lesson stuff (task-info.yaml and tests configuration) Signed-off-by: Evgeniy Moiseenko --- .../IntroducingObjects/CMakeLists.txt | 15 +++++++-------- .../IntroducingObjects/task-info.yaml | 4 ++-- .../IntroducingObjects/test/test.cpp | 8 +++++--- .../Introduction/src/operators.cpp | 2 +- .../OperatorsOverloading/task-info.yaml | 4 ++-- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt index ec55bac..c675121 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/CMakeLists.txt @@ -2,24 +2,23 @@ cmake_minimum_required(VERSION 3.25) project(ObjectOrientedProgramming-ClassesAndObjects-IntroducingObjects) -set(TASK - src/cgobject.cpp) - set(SRC - ${TASK} - src/main.cpp src/utils.cpp + src/main.cpp src/engine.cpp src/scenes.cpp src/textures.cpp src/scene.cpp src/statscene.cpp src/dynscene.cpp - src/gobject.cpp src/gobjectlist.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp src/player.cpp src/consumable.cpp src/enemy.cpp - src/collision.cpp src/direction.cpp src/operators.cpp src/rectangle.cpp src/point.cpp) + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) set(TEST test/test.cpp) add_executable(${PROJECT_NAME}-run ${SRC}) -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) prepare_sfml_framework_lesson_task( "${CMAKE_CURRENT_SOURCE_DIR}/.." diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 141b469..1413645 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -1,6 +1,8 @@ type: edu custom_name: Introducing Objects files: +- name: src/gobject.cpp + visible: true - name: src/main.cpp visible: true editable: false @@ -16,8 +18,6 @@ files: visible: true - name: src/dynscene.cpp visible: true -- name: src/gobject.cpp - visible: true - name: src/gobjectlist.cpp visible: true - name: src/cgobject.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp index 7c9aa1a..c084b46 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/test/test.cpp @@ -8,12 +8,14 @@ #include "testscene.hpp" #include "testing.hpp" +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + const float MIN = -10e3; const float MAX = 10e3; const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; - std::string move_error_msg(GameObject* object, Point2D vector, Point2D expected, Point2D actual) { std::ostringstream stream; stream << "Testing expression:\n" @@ -45,7 +47,7 @@ TEST(MoveTest, MoveTest0) { TestGameObject expected = object; TestGameObject actual = object; Point2D vector0 = Point2D { 0.0f, 0.0f }; - object.performMove(vector0); + actual.performMove(vector0); ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) << move_error_msg(&object, vector0, expected.getPosition(), actual.getPosition()); ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) @@ -73,7 +75,7 @@ TEST(MoveTest, MoveTestRandom) { TestGameObject expected = object; expected.setPosition(expected.getPosition() + vector); TestGameObject actual = object; - object.performMove(vector); + actual.performMove(vector); ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) << move_error_msg(&object, vector, expected.getPosition(), actual.getPosition()); ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp index 3582868..e746b55 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/operators.cpp @@ -21,7 +21,7 @@ Circle operator+(Circle c, Point2D v) { } Circle operator-(Circle c, Point2D v) { - return { c.center - v, c.radius }; + // TODO: write your solution here } Rectangle operator+(Rectangle r, Point2D v) { diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml index f130cb5..28d1e7d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml @@ -1,6 +1,8 @@ type: edu custom_name: Operators Overloading files: +- name: src/operators.cpp + visible: true - name: src/main.cpp visible: true editable: false @@ -36,8 +38,6 @@ files: visible: true - name: src/point.cpp visible: true -- name: src/operators.cpp - visible: true - name: src/utils.cpp visible: true - name: CMakeLists.txt From 8d40dfc3748dbd3ce66af2b4a6333c03b868f012 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 6 Dec 2023 22:21:30 +0100 Subject: [PATCH 068/137] add Inheritance task to the framework lesson Signed-off-by: Evgeniy Moiseenko --- .../Inheritance/CMakeLists.txt | 27 ++++ .../Inheritance/src/cgobject.cpp | 47 ++++++ .../Inheritance/src/collision.cpp | 21 +++ .../Inheritance/src/consumable.cpp | 27 ++++ .../Inheritance/src/direction.cpp | 16 ++ .../Inheritance/src/dynscene.cpp | 144 ++++++++++++++++++ .../Inheritance/src/enemy.cpp | 40 +++++ .../Inheritance/src/engine.cpp | 89 +++++++++++ .../Inheritance/src/gobject.cpp | 7 + .../Inheritance/src/gobjectlist.cpp | 54 +++++++ .../Inheritance/src/main.cpp | 13 ++ .../Inheritance/src/operators.cpp | 42 +++++ .../Inheritance/src/player.cpp | 46 ++++++ .../Inheritance/src/point.cpp | 19 +++ .../Inheritance/src/rectangle.cpp | 35 +++++ .../Inheritance/src/scene.cpp | 45 ++++++ .../Inheritance/src/scenes.cpp | 14 ++ .../Inheritance/src/statscene.cpp | 46 ++++++ .../Inheritance/src/textures.cpp | 42 +++++ .../Inheritance/src/utils.cpp | 51 +++++++ .../Inheritance/task-info.yaml | 45 ++++++ .../ClassesAndObjects/Inheritance/task.md | 86 +++++++++++ .../Inheritance/test/test.cpp | 144 ++++++++++++++++++ .../ClassesAndObjects/lesson-info.yaml | 1 + include/operators.hpp | 18 +++ include/testscene.hpp | 52 +++++++ 26 files changed, 1171 insertions(+) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/CMakeLists.txt new file mode 100644 index 0000000..db78be6 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-Inheritance) + +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/consumable.cpp new file mode 100644 index 0000000..7bd2eaa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/consumable.cpp @@ -0,0 +1,27 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + // TODO: write your solution here +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp new file mode 100644 index 0000000..48a52bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp @@ -0,0 +1,40 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + // TODO: write your solution here +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/engine.cpp new file mode 100644 index 0000000..c506cdf --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + // TODO: write your solution here + return nullptr; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/player.cpp new file mode 100644 index 0000000..e335cf0 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/player.cpp @@ -0,0 +1,46 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scene.cpp new file mode 100644 index 0000000..81a4470 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scene.cpp @@ -0,0 +1,45 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + // TODO: write your solution here +} + +void Scene::move(GameObject &object, Point2D vector) { + // TODO: write your solution here +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/utils.cpp new file mode 100644 index 0000000..783fc7a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml new file mode 100644 index 0000000..433bd4c --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml @@ -0,0 +1,45 @@ +type: edu +files: +- name: src/main.cpp + visible: true + editable: false +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false +- name: src/gobject.cpp + visible: true +- name: test/test.cpp + visible: false +- name: src/cgobject.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md new file mode 100644 index 0000000..9b3db24 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md @@ -0,0 +1,86 @@ +As we have mentioned, `GameObject` class on itself +does not specify the state of game objects — it just describes their behavior. +We need another class that extends `GameObject` with an actual state. + +Fortunately, object-oriented programming has a suitable concept for this job — +it is called class _inheritance_. +Inheritance mechanism allows extending an existing class +and providing concrete implementations for its virtual functions. +A _derived_ class, also called a _subclass_, +inherits all the method and fields of the _base_ class, +and can add its own new methods and fields. + +[//]: # (TODO: also mention term `subtyping`) + +Giving back to our problem, let us define +a `CircleGameObject` subclass of `GameObject` class. +Instances of `CircleGameObject` class represent +game objects of circular shape — like the planet object controlled by the player. + +Have a look at the `CircleGameObject` class definition. +The semicolon syntax: + +``` +class CircleGameObject : public GameObject +``` + +indicates that `CircleGameObject` is a subclass of `GameObject`. + +For a time being let us again ignore the `public` and `private` keywords +used in the `CircleGameObject` class. + +Instead, let us note that `CircleGameObject` declares not only methods, but also two fields: +* `circle` field stores its shape data; +* `status` field stores its current status. + +The very first method of the `CircleGameObject` is a special method called the _constructor_. +Constructor methods have the same name as the class itself, +and it takes single argument `circle`: `CircleGameObject(Circle circle)`. +The constructor is called to create an instance of an object and initialize its state. +For now, you can omit the `explicit` keyword put at the constructor --- we will get back to it later. + +[//]: # (TODO: explain explicit constructors) + +The definition of the `CircleGameObject` constructor body contains some new interesting syntax: + +```c++ +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} +``` + +After the arguments' list comes the semicolon `:`, followed by the list of the object's fields. +The value, provided in the brackets next to the field's name, is used to initialize the corresponding field. +Please note that the order of the fields in the _constructor initializer list_ is important. +It should match the order in which the fields are declared in the class. +After the constructor initializer list comes the constructor body `{}` (empty in this case). +Similarly, as regular methods, it can contain arbitrary C++ statements. + +A constructor has its counterpart — the _destructor_ method, +which should have the same name as a class prefixed with `~`. +It is a method called automatically before destruction of the object to perform some clean-up routines. +A class can have several constructors taking different arguments, +but there could only one destructor taking no arguments. + +In fact, you may have already seen the destructor on the previous step: +a class `GameObject` has a virtual destructor `~GameObject()`. +The `= default` syntax at the end of its definition indicates that +this destructor has default auto-generated implementation. + +As we will see later in the course, constructors and destructors have a pivotal role in C++. + +Going back to the `CircleGameObject` class, consider its methods. +Some of them, like `getPosition` and `setPosition`, are just re-declared methods of the `GameObject` class. +The keyword `override` at the end of the methods' declarations indicates this fact. + +However, unlike the `GameObject` class, the `CircleGameObject` class can actually define the behavior of these methods. +To be precise, it is your task to implement some of them, +namely `getPosition`, `setPosition`, `getStatus`, `setStatus`, and `getCircle`. + +
+ +Note that the position of the `CircleGameObject` is a position of its circle's center. + +
+ diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/test/test.cpp new file mode 100644 index 0000000..ce201e3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/test/test.cpp @@ -0,0 +1,144 @@ +#include + +#include + +#include "gobject.hpp" +#include "operators.hpp" + +#include "testscene.hpp" +#include "testing.hpp" + +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + +const float MIN = -10e3; +const float MAX = 10e3; + +const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; + +std::string getPosition_error_msg(CircleGameObject* object, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.getPosition()" << "\n"; + stream << "Expected result:\n" + << " object.getPosition() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getPosition() = " << actual << "\n"; + return stream.str(); +} + +TEST(CircleGameObjectTest, GetPositionTest) { + Point2D center = { 10.0f, 10.0f }; + float radius = 30.0f; + TestCircleGameObject object = TestCircleGameObject( + Circle { center, radius }, + Point2D { 0.0f, 0.0f }, + GameObjectKind::CONSUMABLE + ); + ASSERT_FLOAT_EQ(center.x, object.getPosition().x) + << getPosition_error_msg(&object, center, object.getPosition()); + ASSERT_FLOAT_EQ(center.y, object.getPosition().y) + << getPosition_error_msg(&object, center, object.getPosition()); +} + +std::string setPosition_error_msg(CircleGameObject* object, Point2D position, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.setPosition(p)" << "\n"; + stream << "Test data:" << "\n" + << " object.getPosition() = " << object->getPosition() << "\n" + << " p = " << position << "\n"; + stream << "Expected result:\n" + << " object.getPosition() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getPosition() = " << actual << "\n"; + return stream.str(); +} + +TEST(CircleGameObjectTest, SetPositionTest) { + Point2D position = { 10.0f, 10.0f }; + TestCircleGameObject object = TestCircleGameObject(); + object.performSetPosition(position); + ASSERT_FLOAT_EQ(position.x, object.getPosition().x) + << setPosition_error_msg(&object, position, position, object.getPosition()); + ASSERT_FLOAT_EQ(position.y, object.getPosition().y) + << setPosition_error_msg(&object, position, position, object.getPosition()); +} + +std::string getStatus_error_msg(CircleGameObject* object, GameObjectStatus expected, GameObjectStatus actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.getStatus()" << "\n"; + stream << "Expected result:\n" + << " object.getStatus() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getStatus() = " << actual << "\n"; + return stream.str(); +} + +TEST(CircleGameObjectTest, GetStatusTest) { + TestCircleGameObject object = TestCircleGameObject(); + ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) + << getStatus_error_msg(&object, GameObjectStatus::NORMAL, object.getStatus()); +} + +std::string setStatus_error_msg(CircleGameObject* object, GameObjectStatus status, GameObjectStatus expected, GameObjectStatus actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.setStatus(status)" << "\n"; + stream << "Test data:" << "\n" + << " object.getStatus() = " << object->getStatus() << "\n" + << " status = " << status << "\n"; + stream << "Expected result:\n" + << " object.getStatus() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getStatus() = " << actual << "\n"; + return stream.str(); +} + +TEST(CircleGameObjectTest, SetStatusTest) { + TestCircleGameObject object = TestCircleGameObject(); + + TestCircleGameObject actual1 = TestCircleGameObject(); + actual1.performSetStatus(GameObjectStatus::NORMAL); + ASSERT_EQ(GameObjectStatus::NORMAL, actual1.getStatus()) + << setStatus_error_msg(&object, GameObjectStatus::NORMAL, GameObjectStatus::NORMAL, actual1.getStatus()); + + TestCircleGameObject actual2 = TestCircleGameObject(); + actual2.performSetStatus(GameObjectStatus::WARNED); + ASSERT_EQ(GameObjectStatus::WARNED, actual2.getStatus()) + << setStatus_error_msg(&object, GameObjectStatus::WARNED, GameObjectStatus::WARNED, actual2.getStatus()); + + TestCircleGameObject actual3 = TestCircleGameObject(); + actual3.performSetStatus(GameObjectStatus::DESTROYED); + ASSERT_EQ(GameObjectStatus::DESTROYED, actual3.getStatus()) + << setStatus_error_msg(&object, GameObjectStatus::DESTROYED, GameObjectStatus::DESTROYED, actual3.getStatus()); +} + +std::string getCircle_error_msg(CircleGameObject* object, Point2D position, Circle expected, Circle actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.setPosition(p)" << "\n"; + stream << "Test data:" << "\n" + << " object.getCircle() = " << object->getCircle() << "\n" + << " p = " << position << "\n"; + stream << "Expected result:\n" + << " object.getCircle() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getCircle() = " << actual << "\n"; + return stream.str(); +} + +TEST(CircleGameObjectTest, GetCircleTest) { + TestCircleGameObject object = TestCircleGameObject(); + Point2D position = Point2D { 150.0f, 300.0f }; + Circle expected = Circle { position, CONSUMABLE_RADIUS }; + TestCircleGameObject actual = TestCircleGameObject(); + actual.performSetPosition(position); + ASSERT_FLOAT_EQ(expected.center.x, actual.getCircle().center.x) + << getCircle_error_msg(&object, position, expected, actual.getCircle()); + ASSERT_FLOAT_EQ(expected.center.y, actual.getCircle().center.y) + << getCircle_error_msg(&object, position, expected, actual.getCircle()); + ASSERT_FLOAT_EQ(expected.radius, actual.getCircle().radius) + << getCircle_error_msg(&object, position, expected, actual.getCircle()); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 12151a1..ba3e495 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -4,4 +4,5 @@ content: - Introduction - OperatorsOverloading - IntroducingObjects +- Inheritance is_template_based: false diff --git a/include/operators.hpp b/include/operators.hpp index fd00c0f..2e5643d 100644 --- a/include/operators.hpp +++ b/include/operators.hpp @@ -7,6 +7,7 @@ #include "circle.hpp" #include "rectangle.hpp" #include "direction.hpp" +#include "enums.hpp" Point2D operator+(Point2D a, Point2D b); Point2D operator-(Point2D a, Point2D b); @@ -64,4 +65,21 @@ inline std::ostream& operator<<(std::ostream& os, Direction direction) { return os << to_string(direction); } +inline std::string to_string(GameObjectStatus status) { + switch (status) { + case GameObjectStatus::NORMAL: + return "GameObjectStatus::NORMAL"; + case GameObjectStatus::WARNED: + return "GameObjectStatus::WARNED"; + case GameObjectStatus::DESTROYED: + return "GameObjectStatus::DESTROYED"; + default: + return ""; + } +} + +inline std::ostream& operator<<(std::ostream& os, GameObjectStatus status) { + return os << to_string(status); +} + #endif // CPPBASICS_OPERATORS_HPP diff --git a/include/testscene.hpp b/include/testscene.hpp index accfa26..c77daae 100644 --- a/include/testscene.hpp +++ b/include/testscene.hpp @@ -2,6 +2,7 @@ #define CPPBASICS_TESTSCENE_HPP #include "gobject.hpp" +#include "cgobject.hpp" #include "scene.hpp" class TestGameObject : public GameObject { @@ -75,4 +76,55 @@ class TestGameObject : public GameObject { GameObjectKind kind; }; +class TestCircleGameObject : public CircleGameObject { +public: + + inline TestCircleGameObject() + : CircleGameObject(Circle { Point2D { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) + , velocity(Point2D { 0.0f, 0.0f }) + , kind(GameObjectKind::CONSUMABLE) + {} + + inline TestCircleGameObject(Circle circle, Point2D velocity, GameObjectKind kind) + : CircleGameObject(circle) + , velocity(velocity) + , kind(kind) + {} + + TestCircleGameObject(const TestCircleGameObject& other) = default; + TestCircleGameObject& operator=(const TestCircleGameObject& other) = default; + + inline GameObjectKind getKind() const override { + return kind; + } + + inline Point2D getVelocity() const override { + return velocity; + } + + inline void update(sf::Time delta) override { + return; + } + + inline void onCollision(const GameObject& object, const CollisionInfo& info) override { + return; + } + + inline const sf::Texture* getTexture(TextureManager& textureManager) const override { + return nullptr; + } + + void performSetPosition(Point2D position) { + setPosition(position); + } + + void performSetStatus(GameObjectStatus status) { + setStatus(status); + } + +private: + Point2D velocity; + GameObjectKind kind; +}; + #endif //CPPBASICS_TESTSCENE_HPP From e5f590f9e997446475ae550428c44e5f25934309 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 6 Dec 2023 22:22:49 +0100 Subject: [PATCH 069/137] fix SFML linking to test targets Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/Introduction/CMakeLists.txt | 2 +- cmake/utils.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt index 5c00214..86cf263 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt @@ -21,5 +21,5 @@ add_executable(${PROJECT_NAME}-run ${SRC}) prepare_sfml_framework_lesson_task( "${CMAKE_CURRENT_SOURCE_DIR}/.." ${PROJECT_NAME}-run - ${PROJECT_NAME}-test + "" ) diff --git a/cmake/utils.cmake b/cmake/utils.cmake index f6cc963..04ce333 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -76,7 +76,7 @@ macro(prepare_sfml_framework_lesson_task _lesson_path _target_name _test_name) include_directories(${CMAKE_SOURCE_DIR}/include/) target_copy_resources(${_target_name}) target_link_sfml(${_target_name}) - if (${_test_name}) + if (NOT "${_test_name}" STREQUAL "") target_link_sfml(${_test_name}) endif() endmacro() \ No newline at end of file From 19b2f474415da58afb2c85b7d8b999e38493dbbf Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 6 Dec 2023 22:24:22 +0100 Subject: [PATCH 070/137] fix task-info.yaml Signed-off-by: Evgeniy Moiseenko --- .../Inheritance/task-info.yaml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml index 433bd4c..72788eb 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml @@ -1,5 +1,7 @@ type: edu files: +- name: src/cgobject.cpp + visible: true - name: src/main.cpp visible: true editable: false @@ -13,12 +15,20 @@ files: visible: true - name: src/statscene.cpp visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true - name: src/player.cpp visible: true - name: src/consumable.cpp visible: true - name: src/enemy.cpp visible: true +- name: src/collision.cpp + visible: true - name: src/direction.cpp visible: true - name: src/rectangle.cpp @@ -31,15 +41,5 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: src/gobject.cpp - visible: true - name: test/test.cpp - visible: false -- name: src/cgobject.cpp - visible: true -- name: src/dynscene.cpp - visible: true -- name: src/collision.cpp - visible: true -- name: src/gobjectlist.cpp - visible: true + visible: false \ No newline at end of file From e5a6b5fff0cb5a603724a7419fcc80da8535d73d Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 7 Dec 2023 00:10:40 +0100 Subject: [PATCH 071/137] add Polymorphism task to the framework lesson Signed-off-by: Evgeniy Moiseenko --- .../Inheritance/task-info.yaml | 2 +- .../Polymorphism/CMakeLists.txt | 27 ++++ .../Polymorphism/src/cgobject.cpp | 47 ++++++ .../Polymorphism/src/collision.cpp | 21 +++ .../Polymorphism/src/consumable.cpp | 34 +++++ .../Polymorphism/src/direction.cpp | 16 ++ .../Polymorphism/src/dynscene.cpp | 144 ++++++++++++++++++ .../Polymorphism/src/enemy.cpp | 40 +++++ .../Polymorphism/src/engine.cpp | 89 +++++++++++ .../Polymorphism/src/gobject.cpp | 7 + .../Polymorphism/src/gobjectlist.cpp | 54 +++++++ .../Polymorphism/src/main.cpp | 13 ++ .../Polymorphism/src/operators.cpp | 42 +++++ .../Polymorphism/src/player.cpp | 54 +++++++ .../Polymorphism/src/point.cpp | 19 +++ .../Polymorphism/src/rectangle.cpp | 35 +++++ .../Polymorphism/src/scene.cpp | 45 ++++++ .../Polymorphism/src/scenes.cpp | 14 ++ .../Polymorphism/src/statscene.cpp | 46 ++++++ .../Polymorphism/src/textures.cpp | 42 +++++ .../Polymorphism/src/utils.cpp | 51 +++++++ .../Polymorphism/task-info.yaml | 45 ++++++ .../ClassesAndObjects/Polymorphism/task.md | 55 +++++++ .../Polymorphism/test/test.cpp | 64 ++++++++ .../ClassesAndObjects/lesson-info.yaml | 1 + cmake/utils.cmake | 1 + include/testscene.hpp | 36 +++++ 27 files changed, 1043 insertions(+), 1 deletion(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml index 72788eb..443192d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml @@ -42,4 +42,4 @@ files: - name: CMakeLists.txt visible: false - name: test/test.cpp - visible: false \ No newline at end of file + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/CMakeLists.txt new file mode 100644 index 0000000..8444dac --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-Polymorphism) + +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/consumable.cpp new file mode 100644 index 0000000..2d371e4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/consumable.cpp @@ -0,0 +1,34 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + // TODO: write your solution here +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp new file mode 100644 index 0000000..48a52bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp @@ -0,0 +1,40 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + // TODO: write your solution here +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/engine.cpp new file mode 100644 index 0000000..c506cdf --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + // TODO: write your solution here + return nullptr; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/player.cpp new file mode 100644 index 0000000..452577b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/player.cpp @@ -0,0 +1,54 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::DESTROYED: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; + } +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scene.cpp new file mode 100644 index 0000000..81a4470 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scene.cpp @@ -0,0 +1,45 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + // TODO: write your solution here +} + +void Scene::move(GameObject &object, Point2D vector) { + // TODO: write your solution here +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/utils.cpp new file mode 100644 index 0000000..783fc7a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml new file mode 100644 index 0000000..25a88bb --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml @@ -0,0 +1,45 @@ +type: edu +files: +- name: src/player.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/main.cpp + visible: true + editable: false +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md new file mode 100644 index 0000000..ac10b97 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md @@ -0,0 +1,55 @@ +Despite class `CircleGameObject` adds some data fields to the `GameObject` class, +it still leaves some of `GameObject` virtual methods unimplemented. +Therefore, this class is still an _abstract class_ --- it cannot be instantiated. + +Let us introduce two concrete subclasses of the `CircleGameObject` class --- +the `PlayerObject` class and the `ConsumableObject` classes, +representing the object controlled by the player, and consumable objects respectively. +At last, both of these classes implement all the functionality required by the `GameObject` class. + +Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. +There are no new syntactic constructs here, so you should be able to understand the code in these files. + +[//]: # (TODO: add here a paragraph about the polymorphism) + +The implementations of these classes can be found in the files `player.cpp` and `consumable.cpp`. +Note that the full implementation of some methods is already provided. +For example, the `getVelocity` method of the `PlayerObject` computes +the current velocity vector by calling the `SFML` functions +to determine which keys are pressed by the player at the moment. + +Your task is to implement the `getTexture` methods of both classes. +These methods should return the current texture of an object to be displayed, +depending on the current status of the object. +Although, we have not yet implemented the methods that actually update +the status of the objects, implementing the `getTexture` methods first +will give you a good opportunity to practice and learn the method call syntax. + +The `getTexture` methods takes by reference one argument — object of the `TextureManager` class. +It is another predefined by us class — it is responsible for loading the textures required by the game. +A pointer to a texture can be requested by calling the `getTexture` method of the `TextureManager` class. +It takes as argument the ID of the textures — these IDs are represented by the `GameTextureID` enum. + +[//]: # (TODO: explain difference between `enum` and `enum class`) + +Please implement the `getTexture` methods of the `PlayerObject` and `ConsumableObject` +with the following logic: +* under `NORMAL` or `WARNED` status, the player object should have `PLANET` texture; +* under `DESTROYED` status, the player object should have `PLANET_DEAD` texture; +* under `NORMAL` status, the consumable object should have `STAR` texture; +* under `WARNED` status, the consumable object should have `STAR_CONCERNED` texture; +* under `DESTROYED` status, the consumable object should not be displayed. + +
+ +If you have troubles implementing the last case, +consult the documentation of the `GameObject`'s `getTexture` method. + +
+ +To implement this method, you will have to call the `getTexture` method of the `TextureManager` class. +To do so use the dot syntax `.` — the same syntax as the one used to access fields of a structure: + +```c++ +const sf::Texture* texture = textureManaged.getTexture(id); +``` \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp new file mode 100644 index 0000000..a1edaad --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp @@ -0,0 +1,64 @@ +#include + +#include + +#include "gobject.hpp" +#include "textures.hpp" +#include "operators.hpp" + +#include "testscene.hpp" +#include "testing.hpp" + +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + +const float MIN = -10e3; +const float MAX = 10e3; + +const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; + +TEST(PlayerObjectTest, GetTextureTest) { + TextureManager textureManager = TextureManager(); + bool init = textureManager.initialize(); + ASSERT_TRUE(init) << "Internal error: failed to initialize texture manager"; + + TestPlayerObject object = TestPlayerObject(); + + object.performSetStatus(GameObjectStatus::NORMAL); + const sf::Texture* normalTexture = object.getTexture(textureManager); + ASSERT_EQ(textureManager.getTexture(GameTextureID::PLANET), normalTexture) + << "Texture of player object in GameObjectStatus::NORMAL status is not equal to GameTextureID::PLANET"; + + object.performSetStatus(GameObjectStatus::WARNED); + const sf::Texture* warnedTexture = object.getTexture(textureManager); + ASSERT_EQ(textureManager.getTexture(GameTextureID::PLANET), warnedTexture) + << "Texture of player object in GameObjectStatus::WARNED status is not equal to GameTextureID::PLANET"; + + object.performSetStatus(GameObjectStatus::DESTROYED); + const sf::Texture* destroyedTexture = object.getTexture(textureManager); + ASSERT_EQ(textureManager.getTexture(GameTextureID::PLANET_DEAD), destroyedTexture) + << "Texture of player object in GameObjectStatus::DESTROYED status is not equal to GameTextureID::PLANET_DEAD"; +} + +TEST(ConsumableObjectTest, GetTextureTest) { + TextureManager textureManager = TextureManager(); + bool init = textureManager.initialize(); + ASSERT_TRUE(init) << "Internal error: failed to initialize texture manager"; + + TestConsumableObject object = TestConsumableObject(); + + object.performSetStatus(GameObjectStatus::NORMAL); + const sf::Texture* normalTexture = object.getTexture(textureManager); + ASSERT_EQ(textureManager.getTexture(GameTextureID::STAR), normalTexture) + << "Texture of consumable object in GameObjectStatus::NORMAL status is not equal to GameTextureID::STAR"; + + object.performSetStatus(GameObjectStatus::WARNED); + const sf::Texture* warnedTexture = object.getTexture(textureManager); + ASSERT_EQ(textureManager.getTexture(GameTextureID::STAR_CONCERNED), warnedTexture) + << "Texture of consumable object in GameObjectStatus::WARNED status is not equal to GameTextureID::STAR_CONCERNED"; + + object.performSetStatus(GameObjectStatus::DESTROYED); + const sf::Texture* destroyedTexture = object.getTexture(textureManager); + ASSERT_EQ(nullptr, destroyedTexture) + << "Texture of consumable object in GameObjectStatus::DESTROYED status is not equal to nullptr"; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index ba3e495..0ea9f4f 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -5,4 +5,5 @@ content: - OperatorsOverloading - IntroducingObjects - Inheritance +- Polymorphism is_template_based: false diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 04ce333..4e72b16 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -77,6 +77,7 @@ macro(prepare_sfml_framework_lesson_task _lesson_path _target_name _test_name) target_copy_resources(${_target_name}) target_link_sfml(${_target_name}) if (NOT "${_test_name}" STREQUAL "") + target_copy_resources(${_test_name}) target_link_sfml(${_test_name}) endif() endmacro() \ No newline at end of file diff --git a/include/testscene.hpp b/include/testscene.hpp index c77daae..f6d87d3 100644 --- a/include/testscene.hpp +++ b/include/testscene.hpp @@ -3,6 +3,8 @@ #include "gobject.hpp" #include "cgobject.hpp" +#include "player.hpp" +#include "consumable.hpp" #include "scene.hpp" class TestGameObject : public GameObject { @@ -127,4 +129,38 @@ class TestCircleGameObject : public CircleGameObject { GameObjectKind kind; }; +class TestPlayerObject : public PlayerObject { +public: + + inline TestPlayerObject() : PlayerObject() {} + + TestPlayerObject(const TestPlayerObject& other) = default; + TestPlayerObject& operator=(const TestPlayerObject& other) = default; + + void performSetPosition(Point2D position) { + setPosition(position); + } + + void performSetStatus(GameObjectStatus status) { + setStatus(status); + } +}; + +class TestConsumableObject : public ConsumableObject { +public: + + inline TestConsumableObject() : ConsumableObject() {} + + TestConsumableObject(const TestConsumableObject& other) = default; + TestConsumableObject& operator=(const TestConsumableObject& other) = default; + + void performSetPosition(Point2D position) { + setPosition(position); + } + + void performSetStatus(GameObjectStatus status) { + setStatus(status); + } +}; + #endif //CPPBASICS_TESTSCENE_HPP From f89a8b55b86773586914aa5511ee2992f359b800 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Tue, 12 Dec 2023 15:30:40 +0100 Subject: [PATCH 072/137] add Encapsulation task to the framework lesson Signed-off-by: Evgeniy Moiseenko --- .../Encapsulation/CMakeLists.txt | 27 +++ .../Encapsulation/src/cgobject.cpp | 47 ++++ .../Encapsulation/src/collision.cpp | 21 ++ .../Encapsulation/src/consumable.cpp | 34 +++ .../Encapsulation/src/direction.cpp | 16 ++ .../Encapsulation/src/dynscene.cpp | 144 ++++++++++++ .../Encapsulation/src/enemy.cpp | 40 ++++ .../Encapsulation/src/engine.cpp | 89 +++++++ .../Encapsulation/src/gobject.cpp | 7 + .../Encapsulation/src/gobjectlist.cpp | 54 +++++ .../Encapsulation/src/main.cpp | 13 ++ .../Encapsulation/src/operators.cpp | 42 ++++ .../Encapsulation/src/player.cpp | 54 +++++ .../Encapsulation/src/point.cpp | 19 ++ .../Encapsulation/src/rectangle.cpp | 35 +++ .../Encapsulation/src/scene.cpp | 47 ++++ .../Encapsulation/src/scenes.cpp | 14 ++ .../Encapsulation/src/statscene.cpp | 46 ++++ .../Encapsulation/src/textures.cpp | 42 ++++ .../Encapsulation/src/utils.cpp | 51 ++++ .../Encapsulation/task-info.yaml | 45 ++++ .../ClassesAndObjects/Encapsulation/task.md | 82 +++++++ .../Encapsulation/test/test.cpp | 218 ++++++++++++++++++ .../Polymorphism/test/test.cpp | 1 - .../ClassesAndObjects/lesson-info.yaml | 1 + include/rectangle.hpp | 5 + include/testscene.hpp | 39 +++- 27 files changed, 1228 insertions(+), 5 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/CMakeLists.txt new file mode 100644 index 0000000..8a7f54e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-Encapsulation) + +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/consumable.cpp new file mode 100644 index 0000000..2d371e4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/consumable.cpp @@ -0,0 +1,34 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + // TODO: write your solution here +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp new file mode 100644 index 0000000..48a52bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp @@ -0,0 +1,40 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + // TODO: write your solution here +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/engine.cpp new file mode 100644 index 0000000..c506cdf --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + // TODO: write your solution here + return nullptr; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/player.cpp new file mode 100644 index 0000000..452577b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/player.cpp @@ -0,0 +1,54 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::DESTROYED: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; + } +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scene.cpp new file mode 100644 index 0000000..1457361 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scene.cpp @@ -0,0 +1,47 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + object.setPosition(position); + fitInto(object); +} + +void Scene::move(GameObject &object, Point2D vector) { + object.move(vector); + fitInto(object); +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/utils.cpp new file mode 100644 index 0000000..783fc7a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml new file mode 100644 index 0000000..90e5ea9 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml @@ -0,0 +1,45 @@ +type: edu +files: +- name: src/consumable.cpp + visible: true +- name: src/main.cpp + visible: true + editable: false +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false +- name: src/player.cpp + visible: true +- name: test/test.cpp + visible: false +- name: src/gobjectlist.cpp + visible: true +- name: src/rectangle.cpp + visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md new file mode 100644 index 0000000..8e15e69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md @@ -0,0 +1,82 @@ +Let us now consider another essential class of the game — the `Scene` class. +Game scene object is responsible for handling user input, +keeping the game objects and managing their updates, +drawing the graphics on the window, and other activities. +In essence, it is a working horse of our simple game engine. + +Let us have a look at the declaration of the `Scene` class. +This class is pretty packed — it includes both regular and virtual methods +as well as some data fields. +For the details on the meaning of these methods, we refer to their documentation. + +The `Scene` class groups its members into three sections: `public`, `protected`, and `private`. +Let us finally decipher their meaning! + +With the help of these _visibility modifiers_, +the class can govern how its clients interact with the objects of this class: + +* all the fields and methods declared inside `public` section + are visible and can be used freely outside the class; +* all the fields and methods declared inside `protected` section + are visible and can be used in the class itself and inside its ancestors; +* all the fields and methods declared inside `private` section + are visible and can be used only in the class itself, and in no other place; +* there is a single exception to the previous rule — + a class marked as a `friend` of a given class can access its protected and private members. + +The publicly available fields and methods of the class are also called its _public interface_ +(not to be confused with the term _interface_ denoting classes containing pure virtual functions and having no state). +The public interface of a class defines how the objects of this class are visible from the outside, +what fields and methods can the clients of the class access. + +[//]: # (TODO: add a note about visibility-inheritance modifier) + +You might be wondering what is the point of hiding some fields or methods of the class — +after all, they can be useful outside. +However, the ability to hide some of the object's _implementation details_ +gains the objects an ultimate control over their internal state. + +This principle is known under the name _encapsulation_. +Encapsulation allows the developer of a class to maintain the _invariants_ on object's data, +ensuring that objects of this class always remain in some valid state. + +Let us explain this on the example of the `Scene` class. +Among other things, this class is responsible for storing the game objects appearing on the scene. +One useful invariant that `Scene` class may enforce is that all of its game objects are lay within the scene's borders. +But if the `GameObject` class provides a `public` method to change object's position (i.e. `setPosition`), +then the `Scene` object has no means to guarantee this property. +Any other class or function may change the position of an object and put it outside the visible area of the scene. +However, if the only way for a user class to change position of an object is through a call +to a scene's method (for example, its `move` method) — then the implementation of this method may +take some additional actions in order to guarantee that the object remains within the scene. +This way, by controlling the visibility of objects' fields and methods, +the developer of a class may enforce various useful invariants on the state of a program. + +Mastering the invariants of classes and controlling the visibility of their members is +a skill that comes with the experience. The more complex applications you will architect and develop, +the better you will become at designing classes and their invariants. + +To consolidate the material of this step, please +implement the following two methods of the `Scene` class. + +```c++ +void setObjectPosition(GameObject& object, Point2D position); +void move(GameObject& object, Point2D vector); +``` + +You need to guarantee the invariant of the `Scene` class we discussed above — +the objects of the scene should remain within its borders. + +To implement these methods, you have to use +corresponding methods of the `GameObject` class, +as well as another method the `Scene` class — the `fitInto` method. +This method adjusts the position of an object to fit into the `Scene` borders. + +
+ +Note that `Scene` class is declared as a `friend` of `GameObject` class +(see the class declaration in the `gobject.hpp` file). +Thus, the `Scene` class can access `setPosition` method of the `GameObject` class, +even though it is declared as a private method. + +
diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/test/test.cpp new file mode 100644 index 0000000..e052d1a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/test/test.cpp @@ -0,0 +1,218 @@ +#include + +#include + +#include "scene.hpp" +#include "operators.hpp" +#include "constants.hpp" + +#include "testscene.hpp" +#include "testing.hpp" + +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + +std::string setObjectPosition_error_msg(Scene* scene, GameObject* object, Point2D position, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " scene.setObjectPosition(object, position)" << "\n"; + stream << "Test data:" << "\n" + << " scene.getBoundingBox() = " << scene->getBoundingBox() << "\n" + << " object.getPosition() = " << object->getPosition() << "\n" + << " position = " << position << "\n"; + stream << "Expected result:\n" + << " object.getPosition() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getPosition() = " << actual << "\n"; + return stream.str(); +} + +std::string setObjectPositionInRectangle_error_msg(Scene* scene, GameObject* object, Point2D position, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " scene.setObjectPosition(object, position)" << "\n"; + stream << "Test data:" << "\n" + << " scene.getBoundingBox() = " << scene->getBoundingBox() << "\n" + << " object.getPosition() = " << object->getPosition() << "\n" + << " position = " << position << "\n"; + stream << "Actual result:\n" + << " object.getPosition() = " << actual << "\n"; + stream << "The result is not withing the scene bounds!\n"; + return stream.str(); +} + +TEST(SceneTest, SetObjectPositionInBoundsTest) { + TestScene scene = TestScene(SCENE_WIDTH, SCENE_HEIGHT); + property_test( + [] () { + const float radius = 20.0f; + Point2D offset = { radius, radius }; + Rectangle positionArea = { + Point2D { 0.0f, 0.0f } + offset, + Point2D { SCENE_WIDTH, SCENE_HEIGHT } - offset + }; + Point2D position = generatePoint(positionArea); + Circle circle = Circle { position, radius }; + Point2D velocity = Point2D { 0.0f, 0.0f }; + TestCircleGameObject object = TestCircleGameObject(circle, velocity, GameObjectKind::CONSUMABLE); + Rectangle newPositionArea = positionArea; + Point2D newPosition = generatePoint(newPositionArea); + return std::make_tuple(object, newPosition); + }, + [&scene] (std::tuple data) { + TestCircleGameObject object; + Point2D newPosition; + std::tie(object, newPosition) = data; + TestCircleGameObject expected = object; + expected.performSetPosition(newPosition); + TestCircleGameObject actual = object; + scene.performSetObjectPosition(actual, newPosition); + ASSERT_TRUE(isInRectangle(actual.getPosition(), scene.getBoundingBox())) + << setObjectPositionInRectangle_error_msg(&scene, &object, newPosition, actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) + << setObjectPosition_error_msg(&scene, &object, newPosition, expected.getPosition(), actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) + << setObjectPosition_error_msg(&scene, &object, newPosition, expected.getPosition(), actual.getPosition()); + } + ); +} + +TEST(SceneTest, SetObjectPositionOutOfBoundsTest) { + TestScene scene = TestScene(SCENE_WIDTH, SCENE_HEIGHT); + property_test( + [&scene] () { + const float radius = 20.0f; + Point2D offset = { radius, radius }; + Rectangle positionArea = { + Point2D { 0.0f, 0.0f } + offset, + Point2D { SCENE_WIDTH, SCENE_HEIGHT } - offset + }; + Point2D position = generatePoint(positionArea); + Circle circle = Circle { position, radius }; + Point2D velocity = Point2D { 0.0f, 0.0f }; + TestCircleGameObject object = TestCircleGameObject(circle, velocity, GameObjectKind::CONSUMABLE); + Rectangle newPositionArea = { + { - 2 * SCENE_WIDTH, -2 * SCENE_HEIGHT }, + { 2 * SCENE_WIDTH, 2 * SCENE_HEIGHT } + }; + Point2D newPosition; + do { + newPosition = generatePoint(newPositionArea); + } while (isInRectangle(newPosition, scene.getBoundingBox())); + return std::make_tuple(object, newPosition); + }, + [&scene] (std::tuple data) { + TestCircleGameObject object; + Point2D newPosition; + std::tie(object, newPosition) = data; + TestCircleGameObject expected = object; + expected.performSetPosition(newPosition); + Rectangle rect = fitInto(expected.getBoundingBox(), scene.getBoundingBox()); + expected.performSetPosition(center(rect)); + TestCircleGameObject actual = object; + scene.performSetObjectPosition(actual, newPosition); + ASSERT_TRUE(isInRectangle(actual.getPosition(), scene.getBoundingBox())) + << setObjectPositionInRectangle_error_msg(&scene, &object, newPosition, actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) + << setObjectPosition_error_msg(&scene, &object, newPosition, expected.getPosition(), actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) + << setObjectPosition_error_msg(&scene, &object, newPosition, expected.getPosition(), actual.getPosition()); + } + ); +} + +std::string moveObject_error_msg(Scene* scene, GameObject* object, Point2D vector, Point2D expected, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " scene.move(object, vector)" << "\n"; + stream << "Test data:" << "\n" + << " scene.getBoundingBox() = " << scene->getBoundingBox() << "\n" + << " object.getPosition() = " << object->getPosition() << "\n" + << " vector = " << vector << "\n"; + stream << "Expected result:\n" + << " object.getPosition() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getPosition() = " << actual << "\n"; + return stream.str(); +} + +std::string moveObjectInRectangle_error_msg(Scene* scene, GameObject* object, Point2D vector, Point2D actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " scene.move(object, vector)" << "\n"; + stream << "Test data:" << "\n" + << " scene.getBoundingBox() = " << scene->getBoundingBox() << "\n" + << " object.getPosition() = " << object->getPosition() << "\n" + << " vector = " << vector << "\n"; + stream << "Actual result:\n" + << " object.getPosition() = " << actual << "\n"; + stream << "The result is not withing the scene bounds!\n"; + return stream.str(); +} + +TEST(SceneTest, MoveInBoundsTest) { + TestScene scene = TestScene(SCENE_WIDTH, SCENE_HEIGHT); + property_test( + [] () { + const float radius = 20.0f; + Point2D position = { SCENE_WIDTH / 2, SCENE_HEIGHT / 2 }; + Circle circle = Circle { position, radius }; + Point2D velocity = Point2D { 0.0f, 0.0f }; + TestCircleGameObject object = TestCircleGameObject(circle, velocity, GameObjectKind::CONSUMABLE); + Rectangle vectorArea = Rectangle { { 0.0f, 0.0f }, { 2 * radius, 2 * radius } }; + Point2D vector = generatePoint(vectorArea); + return std::make_tuple(object, vector); + }, + [&scene] (std::tuple data) { + TestCircleGameObject object; + Point2D vector; + std::tie(object, vector) = data; + TestCircleGameObject expected = object; + expected.performSetPosition(expected.getPosition() + vector); + TestCircleGameObject actual = object; + scene.performMove(actual, vector); + ASSERT_TRUE(isInRectangle(actual.getPosition(), scene.getBoundingBox())) + << moveObjectInRectangle_error_msg(&scene, &object, vector, actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) + << moveObject_error_msg(&scene, &object, vector, expected.getPosition(), actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) + << moveObject_error_msg(&scene, &object, vector, expected.getPosition(), actual.getPosition()); + } + ); +} + +TEST(SceneTest, MoveOutOfBoundsTest) { + TestScene scene = TestScene(SCENE_WIDTH, SCENE_HEIGHT); + property_test( + [] () { + const float radius = 20.0f; + Point2D position = { SCENE_WIDTH / 2, SCENE_HEIGHT / 2 }; + Circle circle = Circle { position, radius }; + Point2D velocity = Point2D { 0.0f, 0.0f }; + TestCircleGameObject object = TestCircleGameObject(circle, velocity, GameObjectKind::CONSUMABLE); + Rectangle vectorArea = Rectangle { + { SCENE_WIDTH, SCENE_HEIGHT }, + { 4 * SCENE_WIDTH, 4 * SCENE_HEIGHT }, + }; + Point2D vector = generatePoint(vectorArea); + return std::make_tuple(object, vector); + }, + [&scene] (std::tuple data) { + TestCircleGameObject object; + Point2D vector; + std::tie(object, vector) = data; + TestCircleGameObject expected = object; + expected.performSetPosition(expected.getPosition() + vector); + Rectangle rect = fitInto(expected.getBoundingBox(), scene.getBoundingBox()); + expected.performSetPosition(center(rect)); + TestCircleGameObject actual = object; + scene.performMove(actual, vector); + ASSERT_TRUE(isInRectangle(actual.getPosition(), scene.getBoundingBox())) + << moveObjectInRectangle_error_msg(&scene, &object, vector, actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().x, actual.getPosition().x) + << moveObject_error_msg(&scene, &object, vector, expected.getPosition(), actual.getPosition()); + ASSERT_FLOAT_EQ(expected.getPosition().y, actual.getPosition().y) + << moveObject_error_msg(&scene, &object, vector, expected.getPosition(), actual.getPosition()); + } + ); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp index a1edaad..6572680 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/test/test.cpp @@ -4,7 +4,6 @@ #include "gobject.hpp" #include "textures.hpp" -#include "operators.hpp" #include "testscene.hpp" #include "testing.hpp" diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 0ea9f4f..8ff1d72 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -6,4 +6,5 @@ content: - IntroducingObjects - Inheritance - Polymorphism +- Encapsulation is_template_based: false diff --git a/include/rectangle.hpp b/include/rectangle.hpp index 6355839..5ff197f 100644 --- a/include/rectangle.hpp +++ b/include/rectangle.hpp @@ -20,6 +20,11 @@ inline float height(const Rectangle& rect) { return rect.botRight.y - rect.topLeft.y; } +inline bool isInRectangle(Point2D p, const Rectangle& rect) { + return (rect.topLeft.x <= p.x) && (rect.topLeft.y <= p.y) && + (p.x <= rect.botRight.x) && (p.y <= rect.botRight.y); +} + Point2D center(const Rectangle& rect); Rectangle createRectangle(Point2D p1, Point2D p2); diff --git a/include/testscene.hpp b/include/testscene.hpp index f6d87d3..1f0527b 100644 --- a/include/testscene.hpp +++ b/include/testscene.hpp @@ -137,11 +137,11 @@ class TestPlayerObject : public PlayerObject { TestPlayerObject(const TestPlayerObject& other) = default; TestPlayerObject& operator=(const TestPlayerObject& other) = default; - void performSetPosition(Point2D position) { + inline void performSetPosition(Point2D position) { setPosition(position); } - void performSetStatus(GameObjectStatus status) { + inline void performSetStatus(GameObjectStatus status) { setStatus(status); } }; @@ -154,13 +154,44 @@ class TestConsumableObject : public ConsumableObject { TestConsumableObject(const TestConsumableObject& other) = default; TestConsumableObject& operator=(const TestConsumableObject& other) = default; - void performSetPosition(Point2D position) { + inline void performSetPosition(Point2D position) { setPosition(position); } - void performSetStatus(GameObjectStatus status) { + inline void performSetStatus(GameObjectStatus status) { setStatus(status); } }; +class TestScene : public Scene { +public: + + inline TestScene(float width, float height) : Scene(width, height) {} + + inline void activate() override {} + inline void deactivate() override {} + + inline SceneID getID() const override { + return SceneID::DYNAMIC_GAME_FIELD; + } + + inline SceneID getNextSceneID() const override { + return SceneID::DYNAMIC_GAME_FIELD; + } + + inline void processEvent(const sf::Event& event) override {} + + inline void update(sf::Time delta) override {} + + inline void draw(sf::RenderWindow &window, TextureManager& textureManager) override {} + + inline void performSetObjectPosition(GameObject& object, Point2D position) { + setObjectPosition(object, position); + } + + inline void performMove(GameObject& object, Point2D vector) { + move(object, vector); + } +}; + #endif //CPPBASICS_TESTSCENE_HPP From feaee5de459e07bbfa64915b04857d71f8db4c6f Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Tue, 12 Dec 2023 15:35:22 +0100 Subject: [PATCH 073/137] minor Signed-off-by: Evgeniy Moiseenko --- .../Encapsulation/task-info.yaml | 21 ++++++++++--------- .../Inheritance/task-info.yaml | 1 + .../Polymorphism/task-info.yaml | 1 + 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml index 90e5ea9..b08adbf 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml @@ -1,6 +1,7 @@ type: edu +custom_name: Encapsulation files: -- name: src/consumable.cpp +- name: src/scene.cpp visible: true - name: src/main.cpp visible: true @@ -11,22 +12,28 @@ files: visible: true - name: src/textures.cpp visible: true -- name: src/scene.cpp - visible: true - name: src/statscene.cpp visible: true - name: src/dynscene.cpp visible: true - name: src/gobject.cpp visible: true +- name: src/gobjectlist.cpp + visible: true - name: src/cgobject.cpp visible: true +- name: src/player.cpp + visible: true +- name: src/consumable.cpp + visible: true - name: src/enemy.cpp visible: true - name: src/collision.cpp visible: true - name: src/direction.cpp visible: true +- name: src/rectangle.cpp + visible: true - name: src/point.cpp visible: true - name: src/operators.cpp @@ -35,11 +42,5 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: src/player.cpp - visible: true - name: test/test.cpp - visible: false -- name: src/gobjectlist.cpp - visible: true -- name: src/rectangle.cpp - visible: true + visible: false \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml index 443192d..f3b3ee0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml @@ -1,4 +1,5 @@ type: edu +custom_name: Inheritance files: - name: src/cgobject.cpp visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml index 25a88bb..bc82cee 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml @@ -1,4 +1,5 @@ type: edu +custom_name: Polymorphism files: - name: src/player.cpp visible: true From b2ddb728c935a380f8f39000261463d07b9250a2 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 13 Dec 2023 12:07:57 +0100 Subject: [PATCH 074/137] add Static Members task to the framework lesson Signed-off-by: Evgeniy Moiseenko --- .../StaticMembers/CMakeLists.txt | 27 ++++ .../StaticMembers/src/cgobject.cpp | 47 ++++++ .../StaticMembers/src/collision.cpp | 21 +++ .../StaticMembers/src/consumable.cpp | 34 +++++ .../StaticMembers/src/direction.cpp | 16 ++ .../StaticMembers/src/dynscene.cpp | 144 ++++++++++++++++++ .../StaticMembers/src/enemy.cpp | 40 +++++ .../StaticMembers/src/engine.cpp | 89 +++++++++++ .../StaticMembers/src/gobject.cpp | 7 + .../StaticMembers/src/gobjectlist.cpp | 54 +++++++ .../StaticMembers/src/main.cpp | 13 ++ .../StaticMembers/src/operators.cpp | 42 +++++ .../StaticMembers/src/player.cpp | 54 +++++++ .../StaticMembers/src/point.cpp | 19 +++ .../StaticMembers/src/rectangle.cpp | 35 +++++ .../StaticMembers/src/scene.cpp | 47 ++++++ .../StaticMembers/src/scenes.cpp | 14 ++ .../StaticMembers/src/statscene.cpp | 46 ++++++ .../StaticMembers/src/textures.cpp | 42 +++++ .../StaticMembers/src/utils.cpp | 51 +++++++ .../StaticMembers/task-info.yaml | 46 ++++++ .../ClassesAndObjects/StaticMembers/task.md | 72 +++++++++ .../StaticMembers/test/test.cpp | 23 +++ include/engine.hpp | 1 - 24 files changed, 983 insertions(+), 1 deletion(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/CMakeLists.txt new file mode 100644 index 0000000..0be52bb --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-StaticMembers) + +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/consumable.cpp new file mode 100644 index 0000000..2d371e4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/consumable.cpp @@ -0,0 +1,34 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + // TODO: write your solution here +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp new file mode 100644 index 0000000..48a52bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp @@ -0,0 +1,40 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + // TODO: write your solution here +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/engine.cpp new file mode 100644 index 0000000..32ca01d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + static GameEngine engine; + return &engine; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/player.cpp new file mode 100644 index 0000000..452577b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/player.cpp @@ -0,0 +1,54 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::DESTROYED: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; + } +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scene.cpp new file mode 100644 index 0000000..1457361 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scene.cpp @@ -0,0 +1,47 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + object.setPosition(position); + fitInto(object); +} + +void Scene::move(GameObject &object, Point2D vector) { + object.move(vector); + fitInto(object); +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/utils.cpp new file mode 100644 index 0000000..783fc7a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-info.yaml new file mode 100644 index 0000000..7d93bfb --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-info.yaml @@ -0,0 +1,46 @@ +type: edu +custom_name: Static Members +files: +- name: src/main.cpp + visible: true + editable: false +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false +- name: src/engine.cpp + visible: true +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md new file mode 100644 index 0000000..389b480 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md @@ -0,0 +1,72 @@ +The `Scene` class is an _abstract class_ too — +it has pure virtual methods and thus cannot be instantiated. +This gives us the flexibility of having different implementations of the `Scene` class. + +One such implementation is given by the `GameplayStaticScene` subclass +(see files `statscene.hpp` and `statscene.cpp`). +This scene implementation is called static because it contains only +static predefined number of game objects: single player object and single consumable object. +It is certainly a downgrade from our previous version implementation of the game, +when we learned how to create objects dynamically with the help of the linked lists. +Do not worry, we will restore this feature soon. +But for now, let us work with the static scene. + +The `Scene` class has a certain peculiarity compared to the `GameObject` class — +there could exist single unique `Scene` per one game instance. +We can express this in the code with the help of another C++ feature: `static` modifier. + +First, note that the `Scene` class has one method that stands out from the others: +it is `create()` method that has `static` modifier in front of it. +The `static` modifier, applied to a class member (either field or method), +turns this member into a _static_ member. + +Static members are not associated with the objects, instead they are associated with the class itself. +This means that in order to access a static member, you do not need an instance of the class at hand. +Instead, static members are accessed through the class name: + +```c++ +// obtains a scene instance by calling static method `create` +Scene* scene = Scene::create(); +``` + +Static members provide a convenient way to associate some methods or data fields with the class itself. +For example, the `create` method shown above provides an ability to instantiate +the scene object, without revealing the actual implementation to the user of the method +(note that it returns `Scene*` instead of `GameplayStaticScene*`). +Moreover, it gives us a way to ensure that only one scene is created per each game run. +How we can achieve that — well, with the help of the `static` modifier again. + +When applied to the declaration of local variables inside functions, +`static` modifier has a different meaning. +It allows creating a _static variable_ that survives and preserves its value between the function calls. +Such variables are actually stored inside the static memory region of the program, +instead of the stack memory region where the other local function's variables reside, +hence the name _static_. + +```c++ +int foo() { + static int x = 0; + return ++x; +} + +// prints 1 +std::cout << foo() << std::endl; +// prints 2 +std::cout << foo() << std::endl; +``` + +With the help of the `static` modifier, it becomes possible to +declare static `GameplayStaticScene` variable inside `Scene::create` method +and return a pointer to this variable. + +
+ +Note that in this case, the address escape error does not occur. +Because the `static` variable resides in the static memory region, it lives thought the whole program execution time. +Thus, it is safe to return the address of this variable from the function. + +
+ +Please implement the `create` method as described above. +If you do this correctly, you will be finally able to run the refactored game application +and see the planet and star objects on the screen. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/test/test.cpp new file mode 100644 index 0000000..fc4de50 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/test/test.cpp @@ -0,0 +1,23 @@ +#include + +#include + +#include "engine.hpp" + +#include "testing.hpp" + +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + +TEST(EngineTest, CreateTest) { + GameEngine* engine = GameEngine::create(); + + ASSERT_TRUE(engine != nullptr) + << "GameEngine::create() returns nullptr!"; + + ASSERT_TRUE(engine == GameEngine::create()) + << "GameEngine::create() returns pointer to non-static memory!"; + + ASSERT_TRUE(engine->isActive()) + << "GameEngine::create() returns pointer to a non-initialized engine object!"; +} \ No newline at end of file diff --git a/include/engine.hpp b/include/engine.hpp index 319984e..74b887b 100644 --- a/include/engine.hpp +++ b/include/engine.hpp @@ -64,7 +64,6 @@ class GameEngine { */ void processEvent(const sf::Event& event); - /** * Updates the state of the current scene. * From 5e8845a9cb171c45b2450f44a34596f7794c5a33 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 15 Dec 2023 15:07:00 +0100 Subject: [PATCH 075/137] add Collisions Revisited task to the framework lesson Signed-off-by: Evgeniy Moiseenko --- .../CollisionsRevisited/CMakeLists.txt | 27 ++ .../CollisionsRevisited/src/cgobject.cpp | 47 +++ .../CollisionsRevisited/src/collision.cpp | 21 ++ .../CollisionsRevisited/src/consumable.cpp | 46 +++ .../CollisionsRevisited/src/direction.cpp | 16 + .../CollisionsRevisited/src/dynscene.cpp | 144 +++++++++ .../CollisionsRevisited/src/enemy.cpp | 40 +++ .../CollisionsRevisited/src/engine.cpp | 89 ++++++ .../CollisionsRevisited/src/gobject.cpp | 7 + .../CollisionsRevisited/src/gobjectlist.cpp | 54 ++++ .../CollisionsRevisited/src/main.cpp | 13 + .../CollisionsRevisited/src/operators.cpp | 42 +++ .../CollisionsRevisited/src/player.cpp | 54 ++++ .../CollisionsRevisited/src/point.cpp | 19 ++ .../CollisionsRevisited/src/rectangle.cpp | 35 +++ .../CollisionsRevisited/src/scene.cpp | 47 +++ .../CollisionsRevisited/src/scenes.cpp | 14 + .../CollisionsRevisited/src/statscene.cpp | 46 +++ .../CollisionsRevisited/src/textures.cpp | 42 +++ .../CollisionsRevisited/src/utils.cpp | 51 ++++ .../CollisionsRevisited/task-info.yaml | 46 +++ .../CollisionsRevisited/task.md | 91 ++++++ .../CollisionsRevisited/test/test.cpp | 288 ++++++++++++++++++ .../ClassesAndObjects/lesson-info.yaml | 2 + include/operators.hpp | 22 ++ include/testscene.hpp | 11 +- 26 files changed, 1313 insertions(+), 1 deletion(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/CMakeLists.txt new file mode 100644 index 0000000..eaeca57 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-CollisionsRevisited) + +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/consumable.cpp new file mode 100644 index 0000000..d03f046 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/consumable.cpp @@ -0,0 +1,46 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + if (getStatus() != GameObjectStatus::DESTROYED) { + setStatus(GameObjectStatus::NORMAL); + } +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (getStatus() == GameObjectStatus::DESTROYED || object.getKind() == GameObjectKind::CONSUMABLE) { + return; + } + if (info.collide) { + setStatus(GameObjectStatus::DESTROYED); + return; + } + if (info.distance < CONSUMABLE_WARNED_MULTIPLIER * getCircle().radius) { + setStatus(GameObjectStatus::WARNED); + return; + } +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp new file mode 100644 index 0000000..48a52bc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp @@ -0,0 +1,40 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::updateVelocity() { + // TODO: write your solution here +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + // TODO: write your solution here + return nullptr; +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/engine.cpp new file mode 100644 index 0000000..32ca01d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + static GameEngine engine; + return &engine; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/player.cpp new file mode 100644 index 0000000..452577b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/player.cpp @@ -0,0 +1,54 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::DESTROYED: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; + } +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + // TODO: write your solution here +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scene.cpp new file mode 100644 index 0000000..1457361 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scene.cpp @@ -0,0 +1,47 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + object.setPosition(position); + fitInto(object); +} + +void Scene::move(GameObject &object, Point2D vector) { + object.move(vector); + fitInto(object); +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/utils.cpp new file mode 100644 index 0000000..783fc7a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml new file mode 100644 index 0000000..f8f450d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml @@ -0,0 +1,46 @@ +type: edu +custom_name: Collisions Revisited +files: +- name: src/consumable.cpp + visible: true +- name: src/main.cpp + visible: true + editable: false +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md new file mode 100644 index 0000000..a9d53da --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md @@ -0,0 +1,91 @@ +Now let us restore the collision detection functionality in our refactored game. + +We have already changed the signature of the collision detection function. + +```c++ +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2); +``` + +Now it takes two circle shapes by constant references. +Instead of boolean flag indicating whether the collision occurred, +it returns the `CollisionInfo` structure: + +```c++ +struct CollisionInfo { + bool collide; + float distance; +}; +``` + +This design will give us flexibility to compute various information +about possible collision between two objects inside `collisionInfo` function, +and leave the opportunity to decide what to do with this information to other modules of our game. +For now, we will store the boolean flag `collide`, indicating whether the collision happened, +and the computed `distance` between two objects — but later you +will have an opportunity to extend this structure +with additional information required to implement new features in our game. + +You might wonder why we decided to model `CollisionInfo` as a structure, +instead of using the fancy objects we learned in this lesson. +In fact, we did this on purpose to illustrate the following point. +Structures, declared via `struct`, and objects/classes, declared via `class`, +are not incompatible concepts in C++, +and often instances of both can be found in the same codebase. + +* Objects are used to tie together data (fields) and behavior (methods). + Objects provide _encapsulation_ and _polymorphism_. + The state of objects can satisfy various invariants, + maintained by carefully controlling the visibility of class' members. + +* Structures are used as simple containers of data. + They have predictable memory layout and predictable behavior — + there are no associated virtual methods dispatched at runtime. + +In C++, structures are also sometimes referred to as [_POD types_]((https://en.wikipedia.org/wiki/Passive_data_structure)), +where POD stands for _plain old data_. + +
+ +Technically, in C++ there is no big difference between `class` and `struct` keywords. +For example, one can declare classes using the `struct` keyword, and vice versa. +The only real difference is that : +* in `struct` members by default have `public` visibility; +* in `class` members by default have `private` visibility. + +However, a prevalent convention among the C++ developers is +to use `class` keyword to declare actual classes in the object-oriented programming sense, +while the `struct` keyword is used to declare POD types. + +
+ +Now, with the help of the new collision detection function, +your task is to re-implement the behavior of consumable objects. +- Upon collision with another object, the consumable should change its status into `DESTROYED`. +- When another object is approaching the consumable, it should change its status into `WARNED`. + This should happen whenever the distance between another object and consumable is less than `C * r`, where + - `r` is the radius of the consumable object, + - `C` is a special multiplier constant + +```c++ +const float CONSUMABLE_WARNED_MULTIPLIER = 6.0f; +``` + +To do so, please implement the `onCollision` method of `ConsumableObject` class. +This method is called periodically from the `Scene` class, notifying the object +about its potential collisions with other objects. +The function takes as a first argument another object, +and as a second argument the `CollisionInfo` structure, +containing the information about the distance between objects +and whether they actually collided. +It is up to the method's implementation to decide what to do with this information. + +Note that when the consumable object becomes `WARNED`, it should eventually +change its status back to `NORMAL` when other objects left its nearby area. +One way to achieve this is by also modifying the implementation of `update` method. +This method is also periodically called from the `Scene` class to give +the object an opportunity to update its internal state. +This function takes as a single argument the amount of time elapsed since the last update, +although you will not need this information in the current task. +Reset the status of the alive (that is --- not `DESTROYED`!) consumable back to `NORMAL`. +If there are some objects nearby, they will be detected again during `onCollision` call, +otherwise the consumable object will remain in the `NORMAL` status. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp new file mode 100644 index 0000000..5b72ebe --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp @@ -0,0 +1,288 @@ +#include + +#include "consumable.hpp" +#include "operators.hpp" + +#include "testscene.hpp" +#include "testing.hpp" + +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + +const float MIN = -10e3; +const float MAX = 10e3; + +const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; + +TEST(ConsumableOjectTest, OnCollisionDestroyTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::PLAYER); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { true, 0.0f }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of consumable object after a collision with another non-consumable object should become GameObjectStatus::DESTROYED"; +} + +TEST(ConsumableOjectTest, OnCollisionDestroyWarnedTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::WARNED); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::PLAYER); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { true, 0.0f }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::WARNED << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of consumable object after a collision with another non-consumable object should become GameObjectStatus::DESTROYED"; +} + +TEST(ConsumableOjectTest, OnCollisionWarnTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::PLAYER); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { false, object.getCircle().radius }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::WARNED, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::WARNED << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of consumable object after another non-consumable object approached it should become GameObjectStatus::WARNED"; +} + +TEST(ConsumableOjectTest, OnCollisionConsumableTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::CONSUMABLE); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { true, 0.0f }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of consumable object after a collision with another consumable object should not change"; +} + +TEST(ConsumableOjectTest, OnCollisionConsumableWarningTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::CONSUMABLE); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { false, object.getCircle().radius }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of consumable object after another consumable object approached it should not change"; +} + +TEST(ConsumableOjectTest, OnCollisionDestroyedTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::DESTROYED); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::PLAYER); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { true, 0.0f }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of destroyed consumable object after a collision should remain GameObjectStatus::DESTROYED"; +} + +TEST(ConsumableOjectTest, OnCollisionDestroyedWarningTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::DESTROYED); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::PLAYER); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { false, object.getCircle().radius }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of destroyed consumable object after a another object approached it should remain GameObjectStatus::DESTROYED"; +} + +TEST(ConsumableOjectTest, OnCollisionDestroyedNoCollisionTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::DESTROYED); + + TestGameObject other = TestGameObject(); + other.setKind(GameObjectKind::PLAYER); + other.setStatus(GameObjectStatus::NORMAL); + + CollisionInfo info = CollisionInfo { false, 2 * CONSUMABLE_WARNED_MULTIPLIER * object.getCircle().radius }; + object.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) + << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" + << " other.getKind() = " << other.getKind() << "\n" + << " other.getStatus() = " << other.getStatus() << "\n" + << " info = " << info << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of destroyed consumable object should remain GameObjectStatus::DESTROYED"; +} + +TEST(ConsumableOjectTest, UpdateNormalTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + + object.update(sf::milliseconds(10)); + + ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) + << "Testing expression:\n" + << " object.update(delta)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of consumable object after an update should remain GameObjectStatus::NORMAL"; +} + +TEST(ConsumableOjectTest, UpdateWarnedTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::WARNED); + + object.update(sf::milliseconds(10)); + + ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) + << "Testing expression:\n" + << " object.update(delta)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::WARNED << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of warned consumable object after an update should become GameObjectStatus::NORMAL"; +} + +TEST(ConsumableOjectTest, UpdateDestroyedTest) { + TestConsumableObject object = TestConsumableObject(); + object.performSetStatus(GameObjectStatus::DESTROYED); + + object.update(sf::milliseconds(10)); + + ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) + << "Testing expression:\n" + << " object.update(delta)" << "\n" + << "Test data:" << "\n" + << " object.getKind() = " << object.getKind() << "\n" + << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" + << "Expected result:\n" + << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" + << "Actual result:\n" + << " object.getStatus() = " << object.getStatus() << "\n" + << "The status of destroyed consumable object after an update should remain GameObjectStatus::DESTROYED"; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 8ff1d72..6d76865 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -7,4 +7,6 @@ content: - Inheritance - Polymorphism - Encapsulation +- StaticMembers +- CollisionsRevisited is_template_based: false diff --git a/include/operators.hpp b/include/operators.hpp index 2e5643d..a43f422 100644 --- a/include/operators.hpp +++ b/include/operators.hpp @@ -6,6 +6,7 @@ #include "point.hpp" #include "circle.hpp" #include "rectangle.hpp" +#include "collision.hpp" #include "direction.hpp" #include "enums.hpp" @@ -65,6 +66,23 @@ inline std::ostream& operator<<(std::ostream& os, Direction direction) { return os << to_string(direction); } +inline std::string to_string(GameObjectKind kind) { + switch (kind) { + case GameObjectKind::PLAYER: + return "GameObjectKind::PLAYER"; + case GameObjectKind::CONSUMABLE: + return "GameObjectKind::CONSUMABLE"; + case GameObjectKind::ENEMY: + return "GameObjectKind::ENEMY"; + default: + return ""; + } +} + +inline std::ostream& operator<<(std::ostream& os, GameObjectKind kind) { + return os << to_string(kind); +} + inline std::string to_string(GameObjectStatus status) { switch (status) { case GameObjectStatus::NORMAL: @@ -82,4 +100,8 @@ inline std::ostream& operator<<(std::ostream& os, GameObjectStatus status) { return os << to_string(status); } +inline std::ostream& operator<<(std::ostream& os, const CollisionInfo& info) { + return os << "CollisionInfo = { " << "collide: " << info.collide << "; distance: " << info.distance << " }"; +} + #endif // CPPBASICS_OPERATORS_HPP diff --git a/include/testscene.hpp b/include/testscene.hpp index 1f0527b..7fd960c 100644 --- a/include/testscene.hpp +++ b/include/testscene.hpp @@ -1,11 +1,12 @@ #ifndef CPPBASICS_TESTSCENE_HPP #define CPPBASICS_TESTSCENE_HPP +#include "scene.hpp" #include "gobject.hpp" #include "cgobject.hpp" #include "player.hpp" #include "consumable.hpp" -#include "scene.hpp" +#include "constants.hpp" class TestGameObject : public GameObject { public: @@ -39,10 +40,18 @@ class TestGameObject : public GameObject { return status; } + inline void setStatus(GameObjectStatus status) { + this->status = status; + } + inline GameObjectKind getKind() const override { return kind; } + inline void setKind(GameObjectKind kind) { + this->kind = kind; + } + inline Point2D getVelocity() const override { return velocity; } From 5dbcbb2622dfeafc4abdf6e8a8865d5138b223e7 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 15 Dec 2023 19:20:29 +0100 Subject: [PATCH 076/137] add New Challenge task to the framework lesson Signed-off-by: Evgeniy Moiseenko --- .../CollisionsRevisited/src/enemy.cpp | 4 + .../CollisionsRevisited/test/test.cpp | 271 +++++++----------- .../Encapsulation/src/enemy.cpp | 4 + .../Inheritance/src/enemy.cpp | 4 + .../IntroducingObjects/src/enemy.cpp | 4 + .../Introduction/src/enemy.cpp | 4 + .../NewChallenge/CMakeLists.txt | 27 ++ .../NewChallenge/src/cgobject.cpp | 47 +++ .../NewChallenge/src/collision.cpp | 21 ++ .../NewChallenge/src/consumable.cpp | 46 +++ .../NewChallenge/src/direction.cpp | 16 ++ .../NewChallenge/src/dynscene.cpp | 144 ++++++++++ .../NewChallenge/src/enemy.cpp | 48 ++++ .../NewChallenge/src/engine.cpp | 89 ++++++ .../NewChallenge/src/gobject.cpp | 7 + .../NewChallenge/src/gobjectlist.cpp | 54 ++++ .../NewChallenge/src/main.cpp | 13 + .../NewChallenge/src/operators.cpp | 42 +++ .../NewChallenge/src/player.cpp | 56 ++++ .../NewChallenge/src/point.cpp | 19 ++ .../NewChallenge/src/rectangle.cpp | 35 +++ .../NewChallenge/src/scene.cpp | 47 +++ .../NewChallenge/src/scenes.cpp | 14 + .../NewChallenge/src/statscene.cpp | 46 +++ .../NewChallenge/src/textures.cpp | 42 +++ .../NewChallenge/src/utils.cpp | 51 ++++ .../NewChallenge/task-info.yaml | 46 +++ .../ClassesAndObjects/NewChallenge/task.md | 59 ++++ .../NewChallenge/test/test.cpp | 184 ++++++++++++ .../OperatorsOverloading/src/enemy.cpp | 4 + .../Polymorphism/src/enemy.cpp | 4 + .../StaticMembers/src/enemy.cpp | 4 + include/enemy.hpp | 5 + include/testscene.hpp | 42 ++- 34 files changed, 1325 insertions(+), 178 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp index 5b72ebe..3ecde6d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/test/test.cpp @@ -14,7 +14,25 @@ const float MAX = 10e3; const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; -TEST(ConsumableOjectTest, OnCollisionDestroyTest) { +std::string onCollision_error_msg(GameObject* object, GameObject* other, const CollisionInfo& info, + GameObjectStatus expected, GameObjectStatus actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n"; + stream << "Test data:" << "\n" + << " object.getKind() = " << object->getKind() << "\n" + << " object.getStatus() = " << object->getStatus() << "\n" + << " other.getKind() = " << other->getKind() << "\n" + << " other.getStatus() = " << other->getStatus() << "\n" + << " info = " << info << "\n"; + stream << "Expected result:\n" + << " object.getStatus() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getStatus() = " << actual << "\n"; + return stream.str(); +} + +TEST(ConsumableObjectTest, OnCollisionDestroyTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::NORMAL); @@ -23,25 +41,15 @@ TEST(ConsumableOjectTest, OnCollisionDestroyTest) { other.setStatus(GameObjectStatus::NORMAL); CollisionInfo info = CollisionInfo { true, 0.0f }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::DESTROYED, actual.getStatus()) << "The status of consumable object after a collision with another non-consumable object should become GameObjectStatus::DESTROYED"; } -TEST(ConsumableOjectTest, OnCollisionDestroyWarnedTest) { +TEST(ConsumableObjectTest, OnCollisionDestroyWarnedTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::WARNED); @@ -50,25 +58,15 @@ TEST(ConsumableOjectTest, OnCollisionDestroyWarnedTest) { other.setStatus(GameObjectStatus::NORMAL); CollisionInfo info = CollisionInfo { true, 0.0f }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::WARNED << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::DESTROYED, actual.getStatus()) << "The status of consumable object after a collision with another non-consumable object should become GameObjectStatus::DESTROYED"; } -TEST(ConsumableOjectTest, OnCollisionWarnTest) { +TEST(ConsumableObjectTest, OnCollisionWarnTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::NORMAL); @@ -76,26 +74,16 @@ TEST(ConsumableOjectTest, OnCollisionWarnTest) { other.setKind(GameObjectKind::PLAYER); other.setStatus(GameObjectStatus::NORMAL); - CollisionInfo info = CollisionInfo { false, object.getCircle().radius }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::WARNED, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::WARNED << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + CollisionInfo info = CollisionInfo { false, object.getCircle().radius + 10.0f }; + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::WARNED, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::WARNED, actual.getStatus()) << "The status of consumable object after another non-consumable object approached it should become GameObjectStatus::WARNED"; } -TEST(ConsumableOjectTest, OnCollisionConsumableTest) { +TEST(ConsumableObjectTest, OnCollisionConsumableTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::NORMAL); @@ -104,25 +92,15 @@ TEST(ConsumableOjectTest, OnCollisionConsumableTest) { other.setStatus(GameObjectStatus::NORMAL); CollisionInfo info = CollisionInfo { true, 0.0f }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::NORMAL, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::NORMAL, actual.getStatus()) << "The status of consumable object after a collision with another consumable object should not change"; } -TEST(ConsumableOjectTest, OnCollisionConsumableWarningTest) { +TEST(ConsumableObjectTest, OnCollisionConsumableWarningTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::NORMAL); @@ -130,26 +108,16 @@ TEST(ConsumableOjectTest, OnCollisionConsumableWarningTest) { other.setKind(GameObjectKind::CONSUMABLE); other.setStatus(GameObjectStatus::NORMAL); - CollisionInfo info = CollisionInfo { false, object.getCircle().radius }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + CollisionInfo info = CollisionInfo { false, object.getCircle().radius + 10.0f }; + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::NORMAL, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::NORMAL, actual.getStatus()) << "The status of consumable object after another consumable object approached it should not change"; } -TEST(ConsumableOjectTest, OnCollisionDestroyedTest) { +TEST(ConsumableObjectTest, OnCollisionDestroyedTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::DESTROYED); @@ -158,25 +126,15 @@ TEST(ConsumableOjectTest, OnCollisionDestroyedTest) { other.setStatus(GameObjectStatus::NORMAL); CollisionInfo info = CollisionInfo { true, 0.0f }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::DESTROYED, actual.getStatus()) << "The status of destroyed consumable object after a collision should remain GameObjectStatus::DESTROYED"; } -TEST(ConsumableOjectTest, OnCollisionDestroyedWarningTest) { +TEST(ConsumableObjectTest, OnCollisionDestroyedWarningTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::DESTROYED); @@ -184,26 +142,16 @@ TEST(ConsumableOjectTest, OnCollisionDestroyedWarningTest) { other.setKind(GameObjectKind::PLAYER); other.setStatus(GameObjectStatus::NORMAL); - CollisionInfo info = CollisionInfo { false, object.getCircle().radius }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + CollisionInfo info = CollisionInfo { false, object.getCircle().radius + 10.0f }; + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::DESTROYED, actual.getStatus()) << "The status of destroyed consumable object after a another object approached it should remain GameObjectStatus::DESTROYED"; } -TEST(ConsumableOjectTest, OnCollisionDestroyedNoCollisionTest) { +TEST(ConsumableObjectTest, OnCollisionDestroyedNoCollisionTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::DESTROYED); @@ -212,77 +160,60 @@ TEST(ConsumableOjectTest, OnCollisionDestroyedNoCollisionTest) { other.setStatus(GameObjectStatus::NORMAL); CollisionInfo info = CollisionInfo { false, 2 * CONSUMABLE_WARNED_MULTIPLIER * object.getCircle().radius }; - object.onCollision(other, info); - - ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) - << "Testing expression:\n" - << " object.onCollision(other, info)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" - << " other.getKind() = " << other.getKind() << "\n" - << " other.getStatus() = " << other.getStatus() << "\n" - << " info = " << info << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.onCollision(other, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << onCollision_error_msg(&object, &other, info, GameObjectStatus::DESTROYED, actual.getStatus()) << "The status of destroyed consumable object should remain GameObjectStatus::DESTROYED"; } -TEST(ConsumableOjectTest, UpdateNormalTest) { +std::string update_error_msg(GameObject* object, GameObjectStatus expected, GameObjectStatus actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.update(delta)" << "\n"; + stream << "Test data:" << "\n" + << " object.getKind() = " << object->getKind() << "\n" + << " object.getStatus() = " << object->getStatus() << "\n"; + stream << "Expected result:\n" + << " object.getStatus() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getStatus() = " << actual << "\n"; + return stream.str(); +} + +TEST(ConsumableObjectTest, UpdateNormalTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::NORMAL); - object.update(sf::milliseconds(10)); - - ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) - << "Testing expression:\n" - << " object.update(delta)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.update(sf::milliseconds(10)); + + ASSERT_EQ(GameObjectStatus::NORMAL, actual.getStatus()) + << update_error_msg(&object, GameObjectStatus::NORMAL, actual.getStatus()) << "The status of consumable object after an update should remain GameObjectStatus::NORMAL"; } -TEST(ConsumableOjectTest, UpdateWarnedTest) { +TEST(ConsumableObjectTest, UpdateWarnedTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::WARNED); - object.update(sf::milliseconds(10)); - - ASSERT_EQ(GameObjectStatus::NORMAL, object.getStatus()) - << "Testing expression:\n" - << " object.update(delta)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::WARNED << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::NORMAL << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.update(sf::milliseconds(10)); + + ASSERT_EQ(GameObjectStatus::NORMAL, actual.getStatus()) + << update_error_msg(&object, GameObjectStatus::NORMAL, actual.getStatus()) << "The status of warned consumable object after an update should become GameObjectStatus::NORMAL"; } -TEST(ConsumableOjectTest, UpdateDestroyedTest) { +TEST(ConsumableObjectTest, UpdateDestroyedTest) { TestConsumableObject object = TestConsumableObject(); object.performSetStatus(GameObjectStatus::DESTROYED); - object.update(sf::milliseconds(10)); - - ASSERT_EQ(GameObjectStatus::DESTROYED, object.getStatus()) - << "Testing expression:\n" - << " object.update(delta)" << "\n" - << "Test data:" << "\n" - << " object.getKind() = " << object.getKind() << "\n" - << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" - << "Expected result:\n" - << " object.getStatus() = " << GameObjectStatus::DESTROYED << "\n" - << "Actual result:\n" - << " object.getStatus() = " << object.getStatus() << "\n" + TestConsumableObject actual = object; + actual.update(sf::milliseconds(10)); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << update_error_msg(&object, GameObjectStatus::DESTROYED, actual.getStatus()) << "The status of destroyed consumable object after an update should remain GameObjectStatus::DESTROYED"; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/CMakeLists.txt new file mode 100644 index 0000000..c688d7f --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-NewChallenge) + +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/consumable.cpp new file mode 100644 index 0000000..d03f046 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/consumable.cpp @@ -0,0 +1,46 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + if (getStatus() != GameObjectStatus::DESTROYED) { + setStatus(GameObjectStatus::NORMAL); + } +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (getStatus() == GameObjectStatus::DESTROYED || object.getKind() == GameObjectKind::CONSUMABLE) { + return; + } + if (info.collide) { + setStatus(GameObjectStatus::DESTROYED); + return; + } + if (info.distance < CONSUMABLE_WARNED_MULTIPLIER * getCircle().radius) { + setStatus(GameObjectStatus::WARNED); + return; + } +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/enemy.cpp new file mode 100644 index 0000000..ce5ba50 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/enemy.cpp @@ -0,0 +1,48 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + +void EnemyObject::updateVelocity() { + Direction direction1 = static_cast(generateInt(0, 3)); + Direction direction2 = static_cast(generateInt(0, 3)); + Point2D directionVector = (direction1 == direction2) + ? getDirection(direction1) + : (getDirection(direction1) + getDirection(direction2)); + velocity = SPEED * directionVector; +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + return textureManager.getTexture(GameTextureID::BLACKHOLE); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/engine.cpp new file mode 100644 index 0000000..32ca01d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + static GameEngine engine; + return &engine; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp new file mode 100644 index 0000000..b4724f4 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp @@ -0,0 +1,54 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + // TODO: write your solution here +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + // TODO: write your solution here +} + +void GameObjectList::insert(const std::shared_ptr &object) { + // TODO: write your solution here +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + // TODO: write your solution here +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + // TODO: write your solution here + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + // TODO: write your solution here +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/player.cpp new file mode 100644 index 0000000..2908e92 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/player.cpp @@ -0,0 +1,56 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::DESTROYED: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; + } +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (info.collide && object.getKind() == GameObjectKind::ENEMY) { + setStatus(GameObjectStatus::DESTROYED); + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scene.cpp new file mode 100644 index 0000000..1457361 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scene.cpp @@ -0,0 +1,47 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + object.setPosition(position); + fitInto(object); +} + +void Scene::move(GameObject &object, Point2D vector) { + object.move(vector); + fitInto(object); +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/utils.cpp new file mode 100644 index 0000000..783fc7a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml new file mode 100644 index 0000000..db6ce31 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml @@ -0,0 +1,46 @@ +type: edu +custom_name: New Challenge +files: +- name: src/enemy.cpp + visible: true +- name: src/main.cpp + visible: true + editable: false +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/gobjectlist.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false +- name: src/consumable.cpp + visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md new file mode 100644 index 0000000..def6477 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md @@ -0,0 +1,59 @@ +Now let us apply the newly acquired knowledge +to extend the mechanics of our game by adding a new kind of game objects — +enemy objects visualized as black holes. +As you will see, with the help of object-oriented programming tricks, +this task can be accomplished quite easily. + +The enemy object should behave as follows: +- it should move on a scene in a random direction, changing it periodically; +- it should consume the star objects when colliding with them; +- star objects should change their status into `WARNED` when + the enemy object is approaching them + (similarly as they do when the player object is approaching); +- when the enemy object collides with the player object, the latter + should change its status into `DESTROYED`, becoming effectively immovable. + +Let us proceed to the implementation of this behavior. +First, take a look at the declaration of the `EnemyObject` class. +It again inherits from the `CircleGameObject` class, so it already +has a corresponding circle shape data and behavior attached to it. +Moreover, the `getKind()` method of the `EnemyObject` class +distinguish the enemy objects from other kind of objects (player and consumable) +by returning the `GameObjectKind::ENEMY` value. + +We have also already implemented for you the movement behavior of the `EnemyObject`. +To do so, we have added two new fields to the objects of this class: +- `velocity` field is vector storing the direction and speed of the current velocity of the object; +- `updateTimer` field stores the time elapsed since the last update of the object's velocity. + +The method `getVelocity()` simply returns the value of `velocity` field. +The method `update(sf::Time delta)` is responsible for periodically updating the velocity of the object. +It takes as an argument the amount of time elapsed since the last update. +The implementation simply checks if the overall amount of elapsed time +is greater than the predefined time period (1 second), +and if so, it resets the velocity to a new randomly generated one. + +Now your task is to implement the rest of enemy objects functionality. + +The easy part is to implement the `getTexture` method, +that should return a new special texture for the enemy objects. +This texture has a corresponding id --- `GameTextureID::BLACKHOLE`. + +The harder part is to implement the collision behavior. +When an enemy object collides with the player object, the player object should become inactive. +This can be achieved by setting the status of the player object to `DESTROYED`. +However, an enemy object does not have direct access to the `setStatus` method of the player object. +To implement the desired behavior, you actually need to +modify the `onCollision` method of the `PlayerObject` class, not the `EnemyObject` class! + +
+ +In the implementation of the `PlayerObject::onCollision` method, +remember to check that the collision occurred with the enemy object, +not an object of some other kind! + +
+ +Notice that from the point of view of the consumable objects, +the player and the enemy objects behave similarly, +so you do not need to modify their behavior. \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp new file mode 100644 index 0000000..3b795bf --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp @@ -0,0 +1,184 @@ +#include + +#include "enemy.hpp" +#include "operators.hpp" + +#include "testscene.hpp" +#include "testing.hpp" + +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + +const float MIN = -10e3; +const float MAX = 10e3; + +const Rectangle generateArea = { { MIN, MIN }, { MAX, MAX } }; + +std::string updateVelocity_error_msg(GameObject* object, Point2D velocity) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.updateVelocity()" << "\n"; + stream << "Test data:" << "\n" + << " object.getKind() = " << object->getKind() << "\n" + << " object.getStatus() = " << object->getStatus() << "\n" + << " object.getVelocity() = " << object->getVelocity() << "\n"; + stream << "Result:\n" + << " object.getVelocity() = " << velocity << "\n"; + return stream.str(); +} + +TEST(EnemyObjectTest, UpdateVelocityChangedTest) { + TestEnemyObject object = TestEnemyObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + object.performSetVelocity(Point2D { 0.0f, 0.0f }); + + TestEnemyObject actual = object; + actual.performUpdateVelocity(); + + Point2D velocity = actual.getVelocity(); + ASSERT_FALSE(velocity.x == 0.0f && velocity.y == 0.0f) + << updateVelocity_error_msg(&object, actual.getVelocity()) + << "The velocity has not changed!"; +} + +TEST(EnemyObjectTest, UpdateVelocityInBoundsTest) { + property_test( + [] () { + TestEnemyObject object = TestEnemyObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + object.performSetVelocity(Point2D { 0.0f, 0.0f }); + return std::make_tuple(object); + }, + [] (std::tuple data) { + TestEnemyObject object; + std::tie(object) = data; + TestEnemyObject actual = object; + actual.performUpdateVelocity(); + Point2D velocity = actual.getVelocity(); + ASSERT_TRUE(abs(velocity.x) < EPS || abs(abs(velocity.x) - SPEED) < EPS) + << updateVelocity_error_msg(&object, actual.getVelocity()) + << "The speed of velocity along x-axis is invalid!"; + ASSERT_TRUE(abs(velocity.y) < EPS || abs(abs(velocity.y) - SPEED) < EPS) + << updateVelocity_error_msg(&object, actual.getVelocity()) + << "The speed of velocity along y-axis is invalid!"; + } + ); +} + +TEST(EnemyObjectTest, UpdateVelocityRandomnessTest) { + randomness_test( + [] () { + TestEnemyObject object = TestEnemyObject(); + object.performSetStatus(GameObjectStatus::NORMAL); + object.performSetVelocity(Point2D { 0.0f, 0.0f }); + object.performUpdateVelocity(); + return object; + }, + [] (const TestEnemyObject& object1, const TestEnemyObject& object2) { + Point2D velocity1 = object1.getVelocity(); + Point2D velocity2 = object2.getVelocity(); + return (abs(velocity1.x - velocity2.x) < EPS) && (abs(velocity1.y - velocity2.y) < EPS); + }, + [] (const TestEnemyObject& object) { + std::stringstream stream; + stream << "object.getVelocity() = " << object.getVelocity(); + return stream.str(); + } + ); +} + +TEST(EnemyObjectTest, GetTextureTest) { + TextureManager textureManager = TextureManager(); + bool init = textureManager.initialize(); + ASSERT_TRUE(init) << "Internal error: failed to initialize texture manager"; + + TestEnemyObject object = TestEnemyObject(); + + object.performSetStatus(GameObjectStatus::NORMAL); + const sf::Texture* normalTexture = object.getTexture(textureManager); + ASSERT_EQ(textureManager.getTexture(GameTextureID::BLACKHOLE), normalTexture) + << "Texture of enemy object in GameObjectStatus::NORMAL status is not equal to GameTextureID::BLACKHOLE"; +} + +std::string onCollision_error_msg(GameObject* object, GameObject* other, const CollisionInfo& info, + GameObjectStatus expected, GameObjectStatus actual) { + std::ostringstream stream; + stream << "Testing expression:\n" + << " object.onCollision(other, info)" << "\n"; + stream << "Test data:" << "\n" + << " object.getKind() = " << object->getKind() << "\n" + << " object.getStatus() = " << object->getStatus() << "\n" + << " other.getKind() = " << other->getKind() << "\n" + << " other.getStatus() = " << other->getStatus() << "\n" + << " info = " << info << "\n"; + stream << "Expected result:\n" + << " object.getStatus() = " << expected << "\n"; + stream << "Actual result:\n" + << " object.getStatus() = " << actual << "\n"; + return stream.str(); +} + +TEST(EnemyObjectTest, OnCollisionPlayerDestroyedTest) { + TestEnemyObject enemy = TestEnemyObject(); + PlayerObject player = PlayerObject(); + + CollisionInfo info = CollisionInfo { true, 0.0f }; + PlayerObject actual = player; + actual.onCollision(enemy, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << onCollision_error_msg(&player, &enemy, info, GameObjectStatus::DESTROYED, actual.getStatus()) + << "The status of player object after a collision with enemy object should become GameObjectStatus::DESTROYED"; +} + +TEST(EnemyObjectTest, OnCollisionPlayerNoCollisionTest) { + TestEnemyObject enemy = TestEnemyObject(); + PlayerObject player = PlayerObject(); + + CollisionInfo info = CollisionInfo { false, player.getCircle().radius + enemy.getCircle().radius + 10.0f }; + PlayerObject actual = player; + actual.onCollision(enemy, info); + + ASSERT_EQ(GameObjectStatus::NORMAL, actual.getStatus()) + << onCollision_error_msg(&player, &enemy, info, GameObjectStatus::NORMAL, actual.getStatus()) + << "The status of player object after should not change in case when enemy approaches it, but does not collide"; +} + +TEST(EnemyObjectTest, OnCollisionConsumableDestroyedTest) { + TestEnemyObject enemy = TestEnemyObject(); + ConsumableObject consumable = ConsumableObject(); + + CollisionInfo info = CollisionInfo { true, 0.0f }; + ConsumableObject actual = consumable; + actual.onCollision(enemy, info); + + ASSERT_EQ(GameObjectStatus::DESTROYED, actual.getStatus()) + << onCollision_error_msg(&consumable, &enemy, info, GameObjectStatus::DESTROYED, actual.getStatus()) + << "The status of consumable object after a collision with enemy object should become GameObjectStatus::DESTROYED"; +} + +TEST(EnemyObjectTest, OnCollisionConsumableWarnedTest) { + TestEnemyObject enemy = TestEnemyObject(); + ConsumableObject consumable = ConsumableObject(); + + CollisionInfo info = CollisionInfo { false, consumable.getCircle().radius + enemy.getCircle().radius + 10.0f }; + ConsumableObject actual = consumable; + actual.onCollision(enemy, info); + + ASSERT_EQ(GameObjectStatus::WARNED, actual.getStatus()) + << onCollision_error_msg(&consumable, &enemy, info, GameObjectStatus::WARNED, actual.getStatus()) + << "The status of consumable object after a enemy object approached it should become GameObjectStatus::WARNED"; +} + +TEST(EnemyObjectTest, OnCollisionConsumableNormalTest) { + TestEnemyObject enemy = TestEnemyObject(); + ConsumableObject consumable = ConsumableObject(); + + CollisionInfo info = CollisionInfo { false, 2 * CONSUMABLE_WARNED_MULTIPLIER * consumable.getCircle().radius }; + ConsumableObject actual = consumable; + actual.onCollision(enemy, info); + + ASSERT_EQ(GameObjectStatus::NORMAL, actual.getStatus()) + << onCollision_error_msg(&consumable, &enemy, info, GameObjectStatus::NORMAL, actual.getStatus()) + << "The status of consumable object should not have changed, because the enemy object is too far away from it"; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp index 48a52bc..3bb34d8 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/enemy.cpp @@ -26,6 +26,10 @@ void EnemyObject::update(sf::Time delta) { updateVelocity(); } +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + void EnemyObject::updateVelocity() { // TODO: write your solution here } diff --git a/include/enemy.hpp b/include/enemy.hpp index f5f73bd..f4363c5 100644 --- a/include/enemy.hpp +++ b/include/enemy.hpp @@ -44,6 +44,11 @@ class EnemyObject : public CircleGameObject { protected: + /** + * Sets the velocity of the enemy object. + */ + void setVelocity(Point2D velocity); + /** * Updates the velocity of an enemy object. */ diff --git a/include/testscene.hpp b/include/testscene.hpp index 7fd960c..c25cbd8 100644 --- a/include/testscene.hpp +++ b/include/testscene.hpp @@ -6,6 +6,7 @@ #include "cgobject.hpp" #include "player.hpp" #include "consumable.hpp" +#include "enemy.hpp" #include "constants.hpp" class TestGameObject : public GameObject { @@ -125,14 +126,14 @@ class TestCircleGameObject : public CircleGameObject { return nullptr; } - void performSetPosition(Point2D position) { - setPosition(position); - } - void performSetStatus(GameObjectStatus status) { setStatus(status); } + void performSetPosition(Point2D position) { + setPosition(position); + } + private: Point2D velocity; GameObjectKind kind; @@ -146,13 +147,13 @@ class TestPlayerObject : public PlayerObject { TestPlayerObject(const TestPlayerObject& other) = default; TestPlayerObject& operator=(const TestPlayerObject& other) = default; - inline void performSetPosition(Point2D position) { - setPosition(position); - } - inline void performSetStatus(GameObjectStatus status) { setStatus(status); } + + inline void performSetPosition(Point2D position) { + setPosition(position); + } }; class TestConsumableObject : public ConsumableObject { @@ -163,13 +164,38 @@ class TestConsumableObject : public ConsumableObject { TestConsumableObject(const TestConsumableObject& other) = default; TestConsumableObject& operator=(const TestConsumableObject& other) = default; + inline void performSetStatus(GameObjectStatus status) { + setStatus(status); + } + inline void performSetPosition(Point2D position) { setPosition(position); } +}; + +class TestEnemyObject : public EnemyObject { +public: + + inline TestEnemyObject() : EnemyObject() {} + + TestEnemyObject(const TestEnemyObject& other) = default; + TestEnemyObject& operator=(const TestEnemyObject& other) = default; inline void performSetStatus(GameObjectStatus status) { setStatus(status); } + + inline void performSetPosition(Point2D position) { + setPosition(position); + } + + inline void performSetVelocity(Point2D velocity) { + setVelocity(velocity); + } + + inline void performUpdateVelocity() { + updateVelocity(); + } }; class TestScene : public Scene { From f41b548b7659652b357c115d79a0d145470a6ceb Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 15 Dec 2023 19:21:51 +0100 Subject: [PATCH 077/137] minor Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/NewChallenge/task-info.yaml | 4 ++-- ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml index db6ce31..9463e2a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml @@ -26,6 +26,8 @@ files: visible: true - name: src/player.cpp visible: true +- name: src/consumable.cpp + visible: true - name: src/collision.cpp visible: true - name: src/direction.cpp @@ -42,5 +44,3 @@ files: visible: false - name: test/test.cpp visible: false -- name: src/consumable.cpp - visible: true diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 6d76865..06c2a27 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -9,4 +9,5 @@ content: - Encapsulation - StaticMembers - CollisionsRevisited +- NewChallenge is_template_based: false From 4046a9cfbfea848c6422eb0a5f090152c371046a Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 15 Dec 2023 22:16:33 +0100 Subject: [PATCH 078/137] add New Dynamics task to the framework lesson Signed-off-by: Evgeniy Moiseenko --- .../CollisionsRevisited/src/gobjectlist.cpp | 8 +- .../Encapsulation/src/gobjectlist.cpp | 8 +- .../Inheritance/src/gobjectlist.cpp | 8 +- .../IntroducingObjects/src/gobjectlist.cpp | 8 +- .../Introduction/src/gobjectlist.cpp | 8 +- .../NewChallenge/src/gobjectlist.cpp | 8 +- .../NewDynamics/CMakeLists.txt | 27 ++ .../NewDynamics/src/cgobject.cpp | 47 ++++ .../NewDynamics/src/collision.cpp | 21 ++ .../NewDynamics/src/consumable.cpp | 46 ++++ .../NewDynamics/src/direction.cpp | 16 ++ .../NewDynamics/src/dynscene.cpp | 144 ++++++++++ .../NewDynamics/src/enemy.cpp | 48 ++++ .../NewDynamics/src/engine.cpp | 89 ++++++ .../NewDynamics/src/gobject.cpp | 7 + .../NewDynamics/src/gobjectlist.cpp | 74 +++++ .../NewDynamics/src/main.cpp | 13 + .../NewDynamics/src/operators.cpp | 42 +++ .../NewDynamics/src/player.cpp | 56 ++++ .../NewDynamics/src/point.cpp | 19 ++ .../NewDynamics/src/rectangle.cpp | 35 +++ .../NewDynamics/src/scene.cpp | 47 ++++ .../NewDynamics/src/scenes.cpp | 14 + .../NewDynamics/src/statscene.cpp | 46 ++++ .../NewDynamics/src/textures.cpp | 42 +++ .../NewDynamics/src/utils.cpp | 51 ++++ .../NewDynamics/task-info.yaml | 46 ++++ .../ClassesAndObjects/NewDynamics/task.md | 150 ++++++++++ .../NewDynamics/test/test.cpp | 260 ++++++++++++++++++ .../OperatorsOverloading/src/gobjectlist.cpp | 8 +- .../Polymorphism/src/gobjectlist.cpp | 8 +- .../Polymorphism/task-info.yaml | 2 +- .../StaticMembers/src/gobjectlist.cpp | 8 +- .../ClassesAndObjects/lesson-info.yaml | 1 + .../solutions/src/gobjectlist.cpp | 2 +- 35 files changed, 1379 insertions(+), 38 deletions(-) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/cgobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/collision.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/consumable.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/direction.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/dynscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/enemy.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/engine.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobject.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobjectlist.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/main.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/player.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/point.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/rectangle.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scenes.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/statscene.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/textures.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/utils.cpp create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/CMakeLists.txt new file mode 100644 index 0000000..a404d81 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.25) + +project(ObjectOrientedProgramming-ClassesAndObjects-NewDynamics) + +set(SRC + src/main.cpp + src/engine.cpp src/scenes.cpp src/textures.cpp + src/scene.cpp src/statscene.cpp src/dynscene.cpp + src/gobject.cpp src/gobjectlist.cpp src/cgobject.cpp + src/player.cpp src/consumable.cpp src/enemy.cpp + src/collision.cpp src/direction.cpp + src/rectangle.cpp src/point.cpp + src/operators.cpp src/utils.cpp +) + +set(TEST + test/test.cpp) + +add_executable(${PROJECT_NAME}-run ${SRC}) + +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/cgobject.cpp new file mode 100644 index 0000000..1d49ba3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/cgobject.cpp @@ -0,0 +1,47 @@ +#include "cgobject.hpp" + +#include "operators.hpp" + +CircleGameObject::CircleGameObject(Circle circle) + : circle(circle) + , status(GameObjectStatus::NORMAL) +{} + +Point2D CircleGameObject::getPosition() const { + return circle.center; +} + +void CircleGameObject::setPosition(Point2D position) { + circle.center = position; +} + +GameObjectStatus CircleGameObject::getStatus() const { + return status; +} + +void CircleGameObject::setStatus(GameObjectStatus newStatus) { + status = newStatus; +} + +Circle CircleGameObject::getCircle() const { + return circle; +} + +Rectangle CircleGameObject::getBoundingBox() const { + Point2D offset = { circle.radius, circle.radius }; + Point2D p1 = circle.center - offset; + Point2D p2 = circle.center + offset; + return createRectangle(p1, p2); +} + +void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { + const sf::Texture* texture = getTexture(textureManager); + if (texture == nullptr) + return; + sf::CircleShape shape; + shape.setPosition(circle.center.x, circle.center.y); + shape.setOrigin(circle.radius, circle.radius); + shape.setRadius(circle.radius); + shape.setTexture(texture); + window.draw(shape); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/collision.cpp new file mode 100644 index 0000000..9b56990 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/collision.cpp @@ -0,0 +1,21 @@ +#include + +#include "collision.hpp" +#include "cgobject.hpp" +#include "utils.hpp" + +CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { + CollisionInfo info; + info.distance = distance(circle1.center, circle2.center); + info.collide = (info.distance < circle1.radius + circle2.radius); + return info; +} + +CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { + const CircleGameObject* circleObject1 = dynamic_cast(&object1); + const CircleGameObject* circleObject2 = dynamic_cast(&object2); + if (circleObject1 && circleObject2) { + return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); + } + assert(false); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/consumable.cpp new file mode 100644 index 0000000..d03f046 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/consumable.cpp @@ -0,0 +1,46 @@ +#include "consumable.hpp" + +#include "constants.hpp" + +ConsumableObject::ConsumableObject() + : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) +{} + +GameObjectKind ConsumableObject::getKind() const { + return GameObjectKind::CONSUMABLE; +} + +Point2D ConsumableObject::getVelocity() const { + return { 0.0f, 0.0f }; +} + +void ConsumableObject::update(sf::Time delta) { + if (getStatus() != GameObjectStatus::DESTROYED) { + setStatus(GameObjectStatus::NORMAL); + } +} + +void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (getStatus() == GameObjectStatus::DESTROYED || object.getKind() == GameObjectKind::CONSUMABLE) { + return; + } + if (info.collide) { + setStatus(GameObjectStatus::DESTROYED); + return; + } + if (info.distance < CONSUMABLE_WARNED_MULTIPLIER * getCircle().radius) { + setStatus(GameObjectStatus::WARNED); + return; + } +} + +const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::STAR); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::STAR_CONCERNED); + case GameObjectStatus::DESTROYED: + return nullptr; + } +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/direction.cpp new file mode 100644 index 0000000..0a2b606 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/direction.cpp @@ -0,0 +1,16 @@ +#include "direction.hpp" + +Point2D getDirection(Direction direction) { + switch (direction) { + case North: + return { 0.0f, -1.0f }; + case East: + return { 1.0f, 0.0f }; + case South: + return { 0.0f, 1.0f }; + case West: + return { -1.0f, 0.0f }; + default: + return { 0.0f, 0.0f }; + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/dynscene.cpp new file mode 100644 index 0000000..1dbcf2e --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/dynscene.cpp @@ -0,0 +1,144 @@ +#include "dynscene.hpp" + +#include "constants.hpp" +#include "player.hpp" +#include "consumable.hpp" +#include "enemy.hpp" +#include "utils.hpp" + +const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; +const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; + +const int NEW_DYNAMIC_OBJECT_PROB = 1; +const int NEW_ENEMY_OBJECT_PROB = 10; + +GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayDynamicScene::activate() { + addNewGameObject(GameObjectKind::PLAYER); +} + +void GameplayDynamicScene::deactivate() { + // remove all the objects from the list + objects.remove([&] (const GameObject& object) { + return true; + }); +} + +SceneID GameplayDynamicScene::getID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +SceneID GameplayDynamicScene::getNextSceneID() const { + return SceneID::DYNAMIC_GAME_FIELD; +} + +void GameplayDynamicScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayDynamicScene::update(sf::Time delta) { + // update the objects' state + objects.foreach([delta] (GameObject& object) { + object.update(delta); + }); + // move objects according to their velocity and elapsed time + objects.foreach([this, delta] (GameObject& object) { + move(object, delta); + }); + // compute collision information for each pair of objects + objects.foreach([this] (GameObject& object) { + objects.foreach([this, &object] (GameObject& other) { + if (&object != &other) { + detectCollision(object, other); + } + }); + }); + // update the list of objects + updateObjectsList(); +} + +void GameplayDynamicScene::updateObjectsList() { + // remove destroyed objects from the list + objects.remove([] (const GameObject& object) { + return (object.getStatus() == GameObjectStatus::DESTROYED) + && (object.getKind() != GameObjectKind::PLAYER); + }); + // count the number of the different kinds of objects present on the scene + int consumableCount = 0; + int enemyCount = 0; + objects.foreach([&] (const GameObject& object) { + switch (object.getKind()) { + case GameObjectKind::CONSUMABLE: + ++consumableCount; + break; + case GameObjectKind::ENEMY: + ++enemyCount; + break; + default: + break; + } + }); + // add new objects of randomly chosen kind if there is enough room for them on the scene + int dynamicObjectsCount = consumableCount + enemyCount; + if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { + int r = rand() % 100; + int k = rand() % 100; + if (r < NEW_DYNAMIC_OBJECT_PROB) { + if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { + addNewGameObject(GameObjectKind::ENEMY); + } else if (k > NEW_ENEMY_OBJECT_PROB) { + addNewGameObject(GameObjectKind::CONSUMABLE); + } + } + } +} + +std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { + std::shared_ptr object; + while (!object) { + // create an object with default position + switch (kind) { + case GameObjectKind::PLAYER: { + object = std::make_shared(); + break; + } + case GameObjectKind::CONSUMABLE: { + object = std::make_shared(); + break; + } + case GameObjectKind::ENEMY: { + object = std::make_shared(); + break; + } + } + // set random position for consumable and enemy objects + if (kind == GameObjectKind::CONSUMABLE || + kind == GameObjectKind::ENEMY) { + setObjectPosition(*object, generatePoint(getBoundingBox())); + } else { + fitInto(*object); + } + // check that object does not collide with existing objects + bool collide = false; + objects.foreach([&collide, &object](GameObject& other) { + collide |= collisionInfo(*object, other).collide; + }); + // reset a colliding object + if (collide) { + object = nullptr; + } + } + objects.insert(object); + return object; +} + +void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + // draw background + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + // draw all objects on the scene + objects.foreach([&] (const GameObject& object) { + object.draw(window, textureManager); + }); +} + diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/enemy.cpp new file mode 100644 index 0000000..ce5ba50 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/enemy.cpp @@ -0,0 +1,48 @@ +#include "enemy.hpp" + +#include "constants.hpp" +#include "operators.hpp" +#include "utils.hpp" + +EnemyObject::EnemyObject() + : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) +{} + +GameObjectKind EnemyObject::getKind() const { + return GameObjectKind::ENEMY; +} + +Point2D EnemyObject::getVelocity() const { + return velocity; +} + +void EnemyObject::update(sf::Time delta) { + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return; + updateTimer += delta; + if (updateTimer < sf::seconds(1.0f)) + return; + updateTimer = sf::milliseconds(0.0f); + updateVelocity(); +} + +void EnemyObject::setVelocity(Point2D velocity) { + this->velocity = velocity; +} + +void EnemyObject::updateVelocity() { + Direction direction1 = static_cast(generateInt(0, 3)); + Direction direction2 = static_cast(generateInt(0, 3)); + Point2D directionVector = (direction1 == direction2) + ? getDirection(direction1) + : (getDirection(direction1) + getDirection(direction2)); + velocity = SPEED * directionVector; +} + +void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { + return; +} + +const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { + return textureManager.getTexture(GameTextureID::BLACKHOLE); +} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/engine.cpp new file mode 100644 index 0000000..32ca01d --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/engine.cpp @@ -0,0 +1,89 @@ +#include "engine.hpp" + +#include "constants.hpp" + +GameEngine *GameEngine::create() { + static GameEngine engine; + return &engine; +} + +GameEngine::GameEngine() + : scene(nullptr) + , active(true) +{ + // initialize random number generator + srand(time(nullptr)); + // initialize resource managers + active &= sceneManager.initialize(); + active &= textureManager.initialize(); + if (!active) { + return; + } + // set the current scene + scene = sceneManager.getCurrentScene(); + // initialize the application window + window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); + window.setFramerateLimit(60); +} + +void GameEngine::run() { + sf::Clock clock; + while (isActive()) { + sf::Time delta = clock.restart(); + processInput(); + update(delta); + render(); + sceneTransition(); + } +} + +bool GameEngine::isActive() const { + return active && window.isOpen(); +} + +void GameEngine::close() { + active = false; + window.close(); +} + +void GameEngine::processInput() { + sf::Event event; + while (window.pollEvent(event)) { + processEvent(event); + } +} + +void GameEngine::processEvent(const sf::Event &event) { + switch (event.type) { + case sf::Event::Closed: + close(); + break; + default: + scene->processEvent(event); + break; + } +} + +void GameEngine::update(sf::Time delta) { + scene->update(delta); +} + +void GameEngine::render() { + window.clear(sf::Color::White); + scene->draw(window, textureManager); + window.display(); +} + +void GameEngine::sceneTransition() { + if (scene->getNextSceneID() != scene->getID()) { + sceneManager.transitionScene(scene->getNextSceneID()); + scene = sceneManager.getCurrentScene(); + resizeWindow(); + } +} + +void GameEngine::resizeWindow() { + Rectangle sceneBox = scene->getBoundingBox(); + sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); + window.setSize(sceneSize); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobject.cpp new file mode 100644 index 0000000..a673ef5 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobject.cpp @@ -0,0 +1,7 @@ +#include "gobject.hpp" + +#include "operators.hpp" + +void GameObject::move(Point2D vector) { + setPosition(getPosition() + vector); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobjectlist.cpp new file mode 100644 index 0000000..d4f4541 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/gobjectlist.cpp @@ -0,0 +1,74 @@ +#include "gobjectlist.hpp" + +void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { + cursor->next->prev = node.get(); + node->next = std::move(cursor->next); + node->prev = cursor; + cursor->next = std::move(node); +} + +void GameObjectList::unlink(GameObjectList::Node *node) { + node->next->prev = node->prev; + node->prev->next = std::move(node->next); +} + +GameObjectList::GameObjectList() { + head = std::make_unique(); + head->next = std::make_unique(); + tail = head->next.get(); + tail->prev = head.get(); +} + +void GameObjectList::insert(const std::shared_ptr &object) { + if (!object) { + return; + } + std::unique_ptr node = std::make_unique(); + node->object = object; + link(tail->prev, std::move(node)); +} + +void GameObjectList::remove(const std::function &pred) { + GameObjectList::Node* curr = head->next.get(); + while (curr != tail) { + Node* next = curr->next.get(); + if (pred(*curr->object)) { + unlink(curr); + } + curr = next; + } +} + +void GameObjectList::foreach(const std::function& apply) { + Node* curr = head->next.get(); + while (curr != tail) { + apply(*curr->object); + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { + Node* cursor = head.get(); + Node* curr = other.head->next.get(); + while (curr != other.tail) { + link(cursor, std::make_unique()); + cursor = cursor->next.get(); + cursor->object = curr->object; + curr = curr->next.get(); + } +} + +GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { + swap(*this, other); +} + +GameObjectList &GameObjectList::operator=(GameObjectList other) { + swap(*this, other); + return *this; +} + +void swap(GameObjectList& first, GameObjectList& second) { + using std::swap; + swap(first.head, second.head); + swap(first.tail, second.tail); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/main.cpp new file mode 100644 index 0000000..b192818 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/main.cpp @@ -0,0 +1,13 @@ +#include "engine.hpp" + +#include + +int main() { + GameEngine* engine = GameEngine::create(); + if (!engine) { + std::cout << "Game engine is not created!\n"; + return 1; + } + GameEngine::create()->run(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp new file mode 100644 index 0000000..19fa160 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp @@ -0,0 +1,42 @@ +#include "game.hpp" + +Point2D operator+(Point2D a, Point2D b) { + return add(a, b); +} + +Point2D operator-(Point2D a) { + return { -a.x, -a.y }; +} + +Point2D operator-(Point2D a, Point2D b) { + return add(a, -b); +} + +Point2D operator*(float s, Point2D a) { + return mul(s, a); +} + +Circle operator+(Circle c, Point2D v) { + return { c.center + v, c.radius }; +} + +Circle operator-(Circle c, Point2D v) { + return { c.center - v, c.radius }; +} + +Rectangle operator+(Rectangle r, Point2D v) { + return { r.topLeft + v, r.botRight + v }; +} + +Rectangle operator-(Rectangle r, Point2D v) { + return { r.topLeft - v, r.botRight - v }; +} + +Circle operator*(float s, Circle c) { + return { c.center, s * c.radius }; +} + +Rectangle operator*(float s, Rectangle r) { + Point2D v = { width(r), height(r) }; + return r + s * v; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/player.cpp new file mode 100644 index 0000000..2908e92 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/player.cpp @@ -0,0 +1,56 @@ +#include "player.hpp" + +#include "constants.hpp" +#include "direction.hpp" +#include "operators.hpp" + +PlayerObject::PlayerObject() + : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) +{} + +GameObjectKind PlayerObject::getKind() const { + return GameObjectKind::PLAYER; +} + +Point2D PlayerObject::getVelocity() const { + Point2D velocity = { 0.0f, 0.0f }; + if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) + return velocity; + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { + velocity = velocity + getDirection(North); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { + velocity = velocity + getDirection(East); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { + velocity = velocity + getDirection(South); + } + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { + velocity = velocity + getDirection(West); + } + velocity = SPEED * velocity; + return velocity; +} + +void PlayerObject::update(sf::Time delta) { + return; +} + +const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { + switch (getStatus()) { + case GameObjectStatus::NORMAL: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::WARNED: + return textureManager.getTexture(GameTextureID::PLANET); + case GameObjectStatus::DESTROYED: + return textureManager.getTexture(GameTextureID::PLANET_DEAD); + default: + return nullptr; + } +} + +void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { + if (info.collide && object.getKind() == GameObjectKind::ENEMY) { + setStatus(GameObjectStatus::DESTROYED); + } +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/point.cpp new file mode 100644 index 0000000..5c74f69 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/point.cpp @@ -0,0 +1,19 @@ +#include "game.hpp" + +Point2D add(Point2D a, Point2D b) { + Point2D c = { 0, 0 }; + c.x = a.x + b.x; + c.y = a.y + b.y; + return c; +} + +Point2D mul(float s, Point2D a) { + Point2D b = { 0, 0 }; + b.x = s * a.x; + b.y = s * a.y; + return b; +} + +Point2D move(Point2D position, Point2D velocity, float delta) { + return add(position, mul(delta, velocity)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/rectangle.cpp new file mode 100644 index 0000000..2b5a1ec --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/rectangle.cpp @@ -0,0 +1,35 @@ +#include "rectangle.hpp" + +#include "operators.hpp" + +Point2D center(const Rectangle& rect) { + // TODO: explain this C++ initialization syntax + return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; +} + +Rectangle createRectangle(Point2D p1, Point2D p2) { + Rectangle rect; + rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; + rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; + return rect; +} + +Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { + if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { + return rect; + } + Point2D vector = { 0.0f, 0.0f }; + if (rect.topLeft.x < intoRect.topLeft.x) { + vector.x += intoRect.topLeft.x - rect.topLeft.x; + } + if (rect.topLeft.y < intoRect.topLeft.y) { + vector.y += intoRect.topLeft.y - rect.topLeft.y; + } + if (rect.botRight.x > intoRect.botRight.x) { + vector.x += intoRect.botRight.x - rect.botRight.x; + } + if (rect.botRight.y > intoRect.botRight.y) { + vector.y += intoRect.botRight.y - rect.botRight.y; + } + return rect + vector; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scene.cpp new file mode 100644 index 0000000..1457361 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scene.cpp @@ -0,0 +1,47 @@ +#include "scene.hpp" + +#include "operators.hpp" + +Scene::Scene(float width, float height) + : width(width) + , height(height) +{} + +Rectangle Scene::getBoundingBox() const { + Rectangle box; + box.topLeft = { 0.0f, 0.0f }; + box.botRight = { width, height }; + return box; +} + +void Scene::setObjectPosition(GameObject& object, Point2D position) { + object.setPosition(position); + fitInto(object); +} + +void Scene::move(GameObject &object, Point2D vector) { + object.move(vector); + fitInto(object); +} + +void Scene::move(GameObject& object, sf::Time delta) { + move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); +} + +void Scene::fitInto(GameObject &object) { + Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); + object.setPosition(center(rect)); +} + +void Scene::detectCollision(GameObject& object1, GameObject& object2) { + CollisionInfo info = collisionInfo(object1, object2); + object1.onCollision(object2, info); + object2.onCollision(object1, info); +} + +void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { + sf::Sprite background; + background.setTexture(*texture); + background.setTextureRect(sf::IntRect(0, 0, width, height)); + window.draw(background); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scenes.cpp new file mode 100644 index 0000000..7125bb8 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/scenes.cpp @@ -0,0 +1,14 @@ +#include "scenes.hpp" + +bool SceneManager::initialize() { + staticScene.activate(); + return true; +} + +Scene* SceneManager::getCurrentScene() { + return &staticScene; +} + +void SceneManager::transitionScene(SceneID id) { + return; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/statscene.cpp new file mode 100644 index 0000000..a6abdfa --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/statscene.cpp @@ -0,0 +1,46 @@ +#include "statscene.hpp" + +#include "constants.hpp" + +GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} + +void GameplayStaticScene::activate() { + fitInto(player); + fitInto(consumable); + fitInto(enemy); +} + +void GameplayStaticScene::deactivate() { + return; +} + +SceneID GameplayStaticScene::getID() const { + return SceneID::STATIC_GAME_FIELD; +} + +SceneID GameplayStaticScene::getNextSceneID() const { + return SceneID::STATIC_GAME_FIELD; +} + +void GameplayStaticScene::processEvent(const sf::Event& event) { + return; +} + +void GameplayStaticScene::update(sf::Time delta) { + player.update(delta); + consumable.update(delta); + enemy.update(delta); + move(player, delta); + move(consumable, delta); + move(enemy, delta); + detectCollision(player, consumable); + detectCollision(enemy, player); + detectCollision(enemy, consumable); +} + +void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { + drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); + player.draw(window, textureManager); + consumable.draw(window, textureManager); + enemy.draw(window, textureManager); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/textures.cpp new file mode 100644 index 0000000..2b5abcc --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/textures.cpp @@ -0,0 +1,42 @@ +#include "textures.hpp" + +#include + +const char* getTextureFilename(GameTextureID id) { + switch (id) { + case GameTextureID::SPACE: + return "resources/space.png"; + case GameTextureID::PLANET: + return "resources/planet.png"; + case GameTextureID::PLANET_DEAD: + return "resources/planetDead.png"; + case GameTextureID::STAR: + return "resources/star.png"; + case GameTextureID::STAR_CONCERNED: + return "resources/starConcerned.png"; + case GameTextureID::BLACKHOLE: + return "resources/blackhole.png"; + default: + return ""; + } +} + +bool TextureManager::initialize() { + for (size_t i = 0; i < SIZE; ++i) { + GameTextureID id = static_cast(i); + const char* filename = getTextureFilename(id); + sf::Texture* texture = &textures[i]; + if (!texture->loadFromFile(filename)) { + std::cerr << "Could not open file " << filename << "\n"; + return false; + } + if (id == GameTextureID::SPACE) { + texture->setRepeated(true); + } + } + return true; +} + +const sf::Texture* TextureManager::getTexture(GameTextureID id) const { + return &textures[static_cast(id)]; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/utils.cpp new file mode 100644 index 0000000..783fc7a --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/utils.cpp @@ -0,0 +1,51 @@ +#include "utils.hpp" + +#include +#include +#include + +float distance(Point2D a, Point2D b) { + float dx = a.x - b.x; + float dy = a.y - b.y; + return sqrt(dx * dx + dy * dy); +} + +float generateFloat(float min, float max) { + return min + (rand() / (RAND_MAX / (max - min))); +} + +int generateInt(int min, int max) { + return min + rand() % (max - min + 1); +} + +bool generateBool(float prob) { + return generateFloat(0.0f, 1.0f) < prob; +} + +Point2D generatePoint(const Rectangle& boundingBox) { + Point2D point = { 0.0f, 0.0f, }; + point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); + point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); + return point; +} + +Circle generateCircle(float radius, const Rectangle& boundingBox) { + Circle circle = { { 0.0f, 0.0f }, 0.0f }; + if (radius > std::min(width(boundingBox), height(boundingBox))) { + return circle; + } + circle.center.x = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.center.y = generateFloat( + boundingBox.topLeft.x + radius, + boundingBox.botRight.x - radius + ); + circle.radius = radius; + return circle; +} + +Rectangle generateRectangle(const Rectangle& boundingBox) { + return createRectangle(generatePoint(boundingBox), generatePoint(boundingBox)); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml new file mode 100644 index 0000000..b81a329 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml @@ -0,0 +1,46 @@ +type: edu +custom_name: New Dynamics +files: +- name: src/gobjectlist.cpp + visible: true +- name: src/main.cpp + visible: true + editable: false +- name: src/engine.cpp + visible: true +- name: src/scenes.cpp + visible: true +- name: src/textures.cpp + visible: true +- name: src/scene.cpp + visible: true +- name: src/statscene.cpp + visible: true +- name: src/dynscene.cpp + visible: true +- name: src/gobject.cpp + visible: true +- name: src/cgobject.cpp + visible: true +- name: src/player.cpp + visible: true +- name: src/consumable.cpp + visible: true +- name: src/enemy.cpp + visible: true +- name: src/collision.cpp + visible: true +- name: src/direction.cpp + visible: true +- name: src/rectangle.cpp + visible: true +- name: src/point.cpp + visible: true +- name: src/operators.cpp + visible: true +- name: src/utils.cpp + visible: true +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md new file mode 100644 index 0000000..5f969d9 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md @@ -0,0 +1,150 @@ +Next, we need to restore the dynamic behavior of our game — the ability +to add and remove objects dynamically during the play. +To do so, we will re-implement the doubly linked list data structure +in terms of object-oriented programming. +Before proceeding with this task, please first finish the `Ownership` module, +as we will need some concepts taught there. + +First of all, instead of completely rewriting the `GameplayStaticScene` class, +we will just add a new class --- `GameplayDynamicScene`, where we will implement the new dynamic functionality. + +Please have a look at the declaration of the `GameplayDynamicScene` class (file `dynscene.hpp`), +and its definition (file `dynscene.cpp`). +You can find the brief description of its methods in the documentation comments. + +The `GameplayDynamicScene` class has a field `objects` of `GameObjectList` class. +This is the main class we are going to work with in this task — it will implement the doubly linked list +(its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively). + +This time, using the object-oriented programming and ownership model, +we will implement a list that will own its nodes. +The nodes will be destructed and deallocated automatically upon destruction of the list itself, +and copying of the list will result in all of its nodes being copied too. + +The nodes of the list are represented by the `Node` structure, +which implement the ownership semantics — each node is owning its successor. +Let us have a closer look on the fields of the `Node` structure. + +- `prev` field is a plain pointer `Node*`, storing the pointer to a previous node. + It is a non-owning pointer, because if it was made an owning pointer, then it would + result into ownership cycle. + Indeed, giving a node `x`, `x.prev` can point to a node `y`, such that `y.next` points to `x` --- + this is clearly a cycle. +- `next` field is an owning pointer `std::unique_ptr`. + Whenever a node is destructed, all its succeeding nodes will also be destructed, + due to its ownership semantics. +- `object` field is a shared pointer `std::shared_ptr` to a game object stored in the node. + It has the shared ownership semantics, so that the shared pointers to game objects + can be safely returned from the methods of the `GameObjectList` class. + Also, the shared ownership of game objects will allow us to implement + the copying of a list — a copy would simply store copies of shared pointers. + + +The static methods `link` and `unlink` implement the linking and unlinking of nodes respectively. +You have already seen these functions in the `LinkedList` task. +Here is a reminder of their semantics: + +```c++ +static void link(Node* cursor, std::unique_ptr&& node); +``` + +- Links together the `cursor` and `node` nodes, putting `node` right after `cursor`. + +```c++ +static void unlink(Node* node); +``` + +- Unlinks the node from its neighboring nodes. + +Please implement these methods. +Pay attention to the different ownership semantics of `next`, `prev`, and `object` pointers, +and use `std::move` to manage the ownership. +Remember that `std::unique_ptr` transitions into `nullptr` state after ownership transfer, +and so the order of `std::move` and pointer dereferences becomes important! + +Next, consider the fields of the `GameObjectList` class. + +- `head` is an owning pointer `std::unique_ptr` to the first node of the list. +- `tail` is a non-owning pointer `Node*` to the last node of the list. + It is a non-owning pointer because the pointed-to node is actually owned + by its predecessor via its `next` pointer. + +Because the `head` pointer of the list is owning one, the whole sequence of nodes +belonging to the list will be destroyed automatically upon destruction of the list itself. +This is the reason why we left the default implementation of the class' destructor: + +```c++ +~GameObjectList() = default; +``` + +
+ +Indeed, the destructor of the `GameObjectList` will call +the `~std::unique_ptr()` destructor of the `head` field. +In turn, it will call the destructor of the `Node`, +which will call the destructor `~std::unique_ptr()` of its `next` field, +and so on, until all nodes will be destructed. + +
+ +Note that in the previous implementation of the list (in the `LinkedList` task), +we used a single sentinel node to simplify the implementation of some list operating functions. +Moreover, under the hood the list was organized into a cyclic list: +the `next` field of the last node was pointing the first (sentinel) node. +This time we cannot reuse this trick, since a cyclic list would result into ownership cycle. +Therefore, we would need two sentinel nodes — one as the first node, and the second as a last node. + +Please take a look at the pre-defined methods `foreach` and `remove` of the list +that utilize this list representation: +- `foreach` applies the function given as an argument to every game object stored in the list; +- `remove` unlinks nodes (effectively removing them), whose game object satisfies predicate given as an argument. + +
+ +You might find the type `std::function<...>` unfamiliar. +In essence, it is just an object-oriented counterpart of a function pointer. +We will have a closer look at this type in the later modules of the course. + +
+ +Now, please implement the method for inserting a game object into the end of the list +(just before the last sentinel node): + +```c++ +void insert(const std::shared_ptr& object); +``` + +Keep in mind that: +- the first and the last nodes should remain sentinel nodes; +- only the sentinel nodes can store null pointer `nullptr` inside `object` field. + +In order to complete this task, also finish the implementation of the ownership semantics of +the `GameObjectList` class, following the rule-of-file and copy-and-swap idiom. +In particular, please implement: + +- default constructor +```c++ +GameObjectList(); +``` + +- copy constructor +```c++ +GameObjectList(const GameObjectList& other); +``` + +- moving constructor +```c++ +GameObjectList(GameObjectList&& other) noexcept; +``` + +- assignment operator +```c++ +GameObjectList& operator=(GameObjectList other); +``` + +- swap function +```c++ +friend void swap(GameObjectList& first, GameObjectList& second); +``` + +Once you do this, you should be able to run the instance of the game with the new dynamic scene! diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/test/test.cpp new file mode 100644 index 0000000..fbc4d51 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/test/test.cpp @@ -0,0 +1,260 @@ +#include + +#include "gobjectlist.hpp" + +#include "testscene.hpp" +#include "testing.hpp" + +testing::Environment* const env = + testing::AddGlobalTestEnvironment(new TestEnvironment); + +TEST(GameObjectListTest, DefaultConstructorTest) { + GameObjectList list; + + size_t counter = 0; + list.foreach([&] (GameObject& it) { + counter++; + }); + ASSERT_EQ(0, counter) + << "The default constructed list is not empty\n"; +} + +TEST(GameObjectListTest, InsertTest) { + GameObjectList list; + std::shared_ptr object = std::make_shared(); + list.insert(object); + + size_t counter = 0; + list.foreach([&] (GameObject& it) { + counter++; + }); + ASSERT_EQ(1, counter) + << "The size of the list is not equal to 1\n"; + + list.foreach([&] (GameObject& it) { + ASSERT_EQ(&it, object.get()) + << "The list contains another object (not the one that was inserted)\n"; + }); +} + +TEST(GameObjectListTest, InsertNullPtrTest) { + GameObjectList list; + std::shared_ptr object; + list.insert(object); + + size_t counter = 0; + list.foreach([&] (GameObject& it) { + counter++; + }); + ASSERT_EQ(0, counter) + << "The size of the list is not equal to 1\n"; +} + +TEST(GameObjectListTest, InsertMultipleTest) { + GameObjectList list; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list.insert(object1); + list.insert(object2); + list.insert(object3); + + std::vector expected { object1.get(), object2.get(), object3.get() }; + std::vector actual; + list.foreach([&] (GameObject& it) { + actual.push_back(&it); + }); + ASSERT_EQ(expected, actual); +} + +TEST(GameObjectListTest, RemoveTest) { + GameObjectList list; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list.insert(object1); + list.insert(object2); + list.insert(object3); + + list.remove([&] (const GameObject& it) { + return &it == object2.get(); + }); + std::vector expected1 { object1.get(), object3.get() }; + std::vector actual1; + list.foreach([&] (GameObject& it) { + actual1.push_back(&it); + }); + ASSERT_EQ(expected1, actual1); + + list.remove([&] (const GameObject& it) { + return &it == object3.get(); + }); + std::vector expected2 { object1.get() }; + std::vector actual2; + list.foreach([&] (GameObject& it) { + actual2.push_back(&it); + }); + ASSERT_EQ(expected2, actual2); + + list.remove([&] (const GameObject& it) { + return &it == object1.get(); + }); + std::vector expected3 {}; + std::vector actual3; + list.foreach([&] (GameObject& it) { + actual3.push_back(&it); + }); + ASSERT_EQ(expected3, actual3); +} + +TEST(GameObjectListTest, RemoveAllTest) { + GameObjectList list; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list.insert(object1); + list.insert(object2); + list.insert(object3); + + list.remove([&] (const GameObject& it) { + return true; + }); + std::vector expected { }; + std::vector actual; + list.foreach([&] (GameObject& it) { + actual.push_back(&it); + }); + ASSERT_EQ(expected, actual); +} + +TEST(GameObjectListTest, CopyConstructorTest) { + GameObjectList list; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list.insert(object1); + list.insert(object2); + list.insert(object3); + + GameObjectList copy(list); + + std::vector expected { object1.get(), object2.get(), object3.get() }; + std::vector actual1; + list.foreach([&] (GameObject& it) { + actual1.push_back(&it); + }); + ASSERT_EQ(expected, actual1); + + std::vector actual2; + copy.foreach([&] (GameObject& it) { + actual2.push_back(&it); + }); + ASSERT_EQ(expected, actual2); +} + +TEST(GameObjectListTest, MoveConstructorTest) { + GameObjectList list; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list.insert(object1); + list.insert(object2); + list.insert(object3); + + GameObjectList copy(std::move(list)); + + std::vector expected1 { object1.get(), object2.get(), object3.get() }; + std::vector actual1; + copy.foreach([&] (GameObject& it) { + actual1.push_back(&it); + }); + ASSERT_EQ(expected1, actual1); + + std::vector expected2; + std::vector actual2; + list.foreach([&] (GameObject& it) { + actual2.push_back(&it); + }); + ASSERT_EQ(expected2, actual2); +} + +TEST(GameObjectListTest, CopyAssignmentTest) { + GameObjectList list; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list.insert(object1); + list.insert(object2); + list.insert(object3); + + GameObjectList copy; + copy = list; + + std::vector expected { object1.get(), object2.get(), object3.get() }; + std::vector actual1; + list.foreach([&] (GameObject& it) { + actual1.push_back(&it); + }); + ASSERT_EQ(expected, actual1); + + std::vector actual2; + copy.foreach([&] (GameObject& it) { + actual2.push_back(&it); + }); + ASSERT_EQ(expected, actual2); +} + +TEST(GameObjectListTest, MoveAssignmentTest) { + GameObjectList list; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list.insert(object1); + list.insert(object2); + list.insert(object3); + + GameObjectList copy; + copy = std::move(list); + + std::vector expected1 { object1.get(), object2.get(), object3.get() }; + std::vector actual1; + copy.foreach([&] (GameObject& it) { + actual1.push_back(&it); + }); + ASSERT_EQ(expected1, actual1); + + std::vector expected2; + std::vector actual2; + list.foreach([&] (GameObject& it) { + actual2.push_back(&it); + }); + ASSERT_EQ(expected2, actual2); +} + +TEST(GameObjectListTest, SwapTest) { + GameObjectList list1; + GameObjectList list2; + std::shared_ptr object1 = std::make_shared(); + std::shared_ptr object2 = std::make_shared(); + std::shared_ptr object3 = std::make_shared(); + list1.insert(object1); + list2.insert(object2); + list2.insert(object3); + + using std::swap; + swap(list1, list2); + + std::vector expected1 { object2.get(), object3.get() }; + std::vector actual1; + list1.foreach([&] (GameObject& it) { + actual1.push_back(&it); + }); + ASSERT_EQ(expected1, actual1); + + std::vector expected2 { object1.get() }; + std::vector actual2; + list2.foreach([&] (GameObject& it) { + actual2.push_back(&it); + }); + ASSERT_EQ(expected2, actual2); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml index bc82cee..654cc05 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml @@ -43,4 +43,4 @@ files: - name: CMakeLists.txt visible: false - name: test/test.cpp - visible: false \ No newline at end of file + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp index b4724f4..5b8b35e 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/gobjectlist.cpp @@ -8,6 +8,10 @@ void GameObjectList::unlink(GameObjectList::Node *node) { // TODO: write your solution here } +GameObjectList::GameObjectList() { + // TODO: write your solution here +} + void GameObjectList::insert(const std::shared_ptr &object) { // TODO: write your solution here } @@ -31,10 +35,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - // TODO: write your solution here -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { // TODO: write your solution here } diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 06c2a27..59cda0a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -10,4 +10,5 @@ content: - StaticMembers - CollisionsRevisited - NewChallenge +- NewDynamics is_template_based: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp index ea0fd08..3be211c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp @@ -18,7 +18,7 @@ void GameObjectList::insert(const std::shared_ptr &object) { } std::unique_ptr node = std::make_unique(); node->object = object; - link(head.get(), std::move(node)); + link(tail->prev, std::move(node)); } void GameObjectList::remove(const std::function &pred) { From 8cd525668f02f49910e918fc22a950328b8a8cf5 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 20 Dec 2023 19:05:35 +0100 Subject: [PATCH 079/137] fix description of enemy object task Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/NewChallenge/task.md | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md index def6477..87034e0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md @@ -21,7 +21,7 @@ Moreover, the `getKind()` method of the `EnemyObject` class distinguish the enemy objects from other kind of objects (player and consumable) by returning the `GameObjectKind::ENEMY` value. -We have also already implemented for you the movement behavior of the `EnemyObject`. +We already partly implemented for you the movement behavior of the `EnemyObject`. To do so, we have added two new fields to the objects of this class: - `velocity` field is vector storing the direction and speed of the current velocity of the object; - `updateTimer` field stores the time elapsed since the last update of the object's velocity. @@ -31,12 +31,42 @@ The method `update(sf::Time delta)` is responsible for periodically updating the It takes as an argument the amount of time elapsed since the last update. The implementation simply checks if the overall amount of elapsed time is greater than the predefined time period (1 second), -and if so, it resets the velocity to a new randomly generated one. +and if so, it resets the velocity to a new randomly generated one +by calling the method `updateVelocity()`. +Your task is to implement this method. -Now your task is to implement the rest of enemy objects functionality. +The method should generate a random direction vector, multiply it to the speed scalar constant, +and assign it to the `velocity` field. +That is, the following formula should be used +``` +v` = S * d +``` +where: +* `` v` `` is the new velocity vector, +* `S` is the speed constant `SPEED`, +* `d` is a direction vector. -The easy part is to implement the `getTexture` method, -that should return a new special texture for the enemy objects. +You have the freedom to define a direction vector as you like, +but it should satisfy the following constraints: +* the `x` and `y` coordinates should either be equal `1`, `0`, or `-1`, and +* the vector should be randomly generated: several consecutive invocations of a method + with a high probability should not result in the same direction generated. + +To generate the random direction vector, you might find useful the following functions: + +```c++ +bool generateBool(float prob = 0.5f); +int generateInt(int min, int max); +float generateFloat(float min, float max); +Point2D generatePoint(const Rectangle& boundingBox); +``` + +A detailed description of these functions can be found in the documentation +put alongside their definition in the `utils.hpp` file. + +Moving forward with the `Enemy` class, +the easy part is to implement the `getTexture` method. +It should return a new special texture for the enemy objects. This texture has a corresponding id --- `GameTextureID::BLACKHOLE`. The harder part is to implement the collision behavior. From 4213e0bba7020469539ae1d7efe026ab1f54164a Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 20 Dec 2023 19:06:32 +0100 Subject: [PATCH 080/137] fix description of object list task Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/NewDynamics/task.md | 30 ++++++++++--------- .../solutions/src/gobjectlist.cpp | 14 ++++----- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md index 5f969d9..d2c75d9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md @@ -1,20 +1,20 @@ Next, we need to restore the dynamic behavior of our game — the ability -to add and remove objects dynamically during the play. +to add and remove objects dynamically during play. To do so, we will re-implement the doubly linked list data structure in terms of object-oriented programming. Before proceeding with this task, please first finish the `Ownership` module, as we will need some concepts taught there. First of all, instead of completely rewriting the `GameplayStaticScene` class, -we will just add a new class --- `GameplayDynamicScene`, where we will implement the new dynamic functionality. +we will just add a new class — `GameplayDynamicScene`, where we will implement the new dynamic functionality. Please have a look at the declaration of the `GameplayDynamicScene` class (file `dynscene.hpp`), and its definition (file `dynscene.cpp`). You can find the brief description of its methods in the documentation comments. The `GameplayDynamicScene` class has a field `objects` of `GameObjectList` class. -This is the main class we are going to work with in this task — it will implement the doubly linked list -(its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively). +This is the main class we are going to work with in this task — it will implement the doubly linked list. +Its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively. This time, using the object-oriented programming and ownership model, we will implement a list that will own its nodes. @@ -25,14 +25,14 @@ The nodes of the list are represented by the `Node` structure, which implement the ownership semantics — each node is owning its successor. Let us have a closer look on the fields of the `Node` structure. +- `next` field is an owning pointer `std::unique_ptr`. + Whenever a node is destructed, all its succeeding nodes will also be destructed, + due to its ownership semantics. - `prev` field is a plain pointer `Node*`, storing the pointer to a previous node. It is a non-owning pointer, because if it was made an owning pointer, then it would result into ownership cycle. Indeed, giving a node `x`, `x.prev` can point to a node `y`, such that `y.next` points to `x` --- this is clearly a cycle. -- `next` field is an owning pointer `std::unique_ptr`. - Whenever a node is destructed, all its succeeding nodes will also be destructed, - due to its ownership semantics. - `object` field is a shared pointer `std::shared_ptr` to a game object stored in the node. It has the shared ownership semantics, so that the shared pointers to game objects can be safely returned from the methods of the `GameObjectList` class. @@ -94,7 +94,7 @@ the `next` field of the last node was pointing the first (sentinel) node. This time we cannot reuse this trick, since a cyclic list would result into ownership cycle. Therefore, we would need two sentinel nodes — one as the first node, and the second as a last node. -Please take a look at the pre-defined methods `foreach` and `remove` of the list +Take a look at the pre-defined methods `foreach` and `remove` of the list that utilize this list representation: - `foreach` applies the function given as an argument to every game object stored in the list; - `remove` unlinks nodes (effectively removing them), whose game object satisfies predicate given as an argument. @@ -107,7 +107,14 @@ We will have a closer look at this type in the later modules of the course.
-Now, please implement the method for inserting a game object into the end of the list +Now please implement the default constructor of the `GameObjectList` class +that should initialize two sentinel nodes of the list: + +```c++ +GameObjectList(); +``` + +Next implement the method for inserting a game object into the end of the list (just before the last sentinel node): ```c++ @@ -122,11 +129,6 @@ In order to complete this task, also finish the implementation of the ownership the `GameObjectList` class, following the rule-of-file and copy-and-swap idiom. In particular, please implement: -- default constructor -```c++ -GameObjectList(); -``` - - copy constructor ```c++ GameObjectList(const GameObjectList& other); diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp index 3be211c..1b67d0b 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp @@ -12,6 +12,13 @@ void GameObjectList::unlink(GameObjectList::Node *node) { node->prev->next = std::move(node->next); } +GameObjectList::GameObjectList() { + head = std::make_unique(); + head->next = std::make_unique(); + tail = head->next.get(); + tail->prev = head.get(); +} + void GameObjectList::insert(const std::shared_ptr &object) { if (!object) { return; @@ -40,13 +47,6 @@ void GameObjectList::foreach(const std::function& apply) { } } -GameObjectList::GameObjectList() { - head = std::make_unique(); - head->next = std::make_unique(); - tail = head->next.get(); - tail->prev = head.get(); -} - GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { Node* cursor = head.get(); Node* curr = other.head->next.get(); From 94dd6a6663e2a42e84cf4d0e55fa773543df2803 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 20 Dec 2023 19:49:30 +0100 Subject: [PATCH 081/137] fix the game engine task description Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/StaticMembers/task.md | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md index 389b480..a3f8459 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md @@ -5,18 +5,43 @@ This gives us the flexibility of having different implementations of the `Scene` One such implementation is given by the `GameplayStaticScene` subclass (see files `statscene.hpp` and `statscene.cpp`). This scene implementation is called static because it contains only -static predefined number of game objects: single player object and single consumable object. +static predefined number of game objects: +one player object, one consumable object, and one enemy object, +which we are going to cover later in this module. It is certainly a downgrade from our previous version implementation of the game, when we learned how to create objects dynamically with the help of the linked lists. Do not worry, we will restore this feature soon. But for now, let us work with the static scene. -The `Scene` class has a certain peculiarity compared to the `GameObject` class — -there could exist single unique `Scene` per one game instance. +Despite the `Scene` class is very important and does a lot of work, +like game objects maintenance and scene drawing, +by itself it is not responsible for managing the game itself. +This is the purpose of another class — `GameEngine` +(see files `engine.hpp` and `engine.cpp`). +This class, in particular, controls the application window and the currently active scene. + +
+ +Another important responsibility of the `GameEngine` class is to perform scene transitions. +As for now, this functionality would not be important for us, +since for some time we would only have one single scene (the gameplay scene). +But later in this course it will become useful, as we will start to implement other types of scenes — +for example, a scene managing the main menu of the game. + +
+ +The most important method of the `GameEngine` class is the `run()` method +which implements the main loop of the game. +In essence, the entry points of our game application is just +a creation of the `GameEngine` object and call to its `run()` method (see file `main.cpp`). + +Speaking of the creation of `GameEngine` object. +The `GameEngine` class has a certain peculiarity compared to the other classes we have seen so far — +there could exist only a single unique `GameEngine` object instance per one game instance. We can express this in the code with the help of another C++ feature: `static` modifier. -First, note that the `Scene` class has one method that stands out from the others: -it is `create()` method that has `static` modifier in front of it. +First, note that the `GameEngine` class has one method that stands out from the others: +it is the `create()` method that has `static` modifier in front of it. The `static` modifier, applied to a class member (either field or method), turns this member into a _static_ member. @@ -25,23 +50,19 @@ This means that in order to access a static member, you do not need an instance Instead, static members are accessed through the class name: ```c++ -// obtains a scene instance by calling static method `create` -Scene* scene = Scene::create(); +// obtains an engine instance by calling static method `create` +GameEngine* engine = GameEngine::create(); ``` Static members provide a convenient way to associate some methods or data fields with the class itself. -For example, the `create` method shown above provides an ability to instantiate -the scene object, without revealing the actual implementation to the user of the method -(note that it returns `Scene*` instead of `GameplayStaticScene*`). -Moreover, it gives us a way to ensure that only one scene is created per each game run. -How we can achieve that — well, with the help of the `static` modifier again. +For example, the `create` method shown above provides an ability to instantiate the game engine object. When applied to the declaration of local variables inside functions, `static` modifier has a different meaning. It allows creating a _static variable_ that survives and preserves its value between the function calls. Such variables are actually stored inside the static memory region of the program, -instead of the stack memory region where the other local function's variables reside, -hence the name _static_. +instead of the stack memory region where reside the other local variables of the function +(hence the name _static_). ```c++ int foo() { @@ -56,8 +77,9 @@ std::cout << foo() << std::endl; ``` With the help of the `static` modifier, it becomes possible to -declare static `GameplayStaticScene` variable inside `Scene::create` method -and return a pointer to this variable. +ensure that only one engine is created per each game run. +For this, it is sufficient to declare static `GameEngine` variable +inside `GameEngine::create` method and return a pointer to this variable.
From fd05a554f3d85c26aa592496eda27f54ab3c276d Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 20 Dec 2023 19:54:51 +0100 Subject: [PATCH 082/137] add documentation to the `utils.hpp` top-level functions Signed-off-by: Evgeniy Moiseenko --- include/utils.hpp | 61 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/include/utils.hpp b/include/utils.hpp index 56b5b65..30bd3ea 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -5,18 +5,73 @@ #include "rectangle.hpp" #include "circle.hpp" + +/** + * Calculates the distance between two 2D points. + * + * @param a The first point. + * @param b The second point. + * @return The Euclidean distance between the two points. + */ float distance(Point2D a, Point2D b); -float generateFloat(float min, float max); +/** + * Generates a random boolean value with a given probability. + * + * @param prob the probability of generating a 'true' value (default is 0.5) + * @return a random boolean value + */ +bool generateBool(float prob = 0.5f); + + +/** + * Generates a random integer between the given minimum and maximum values (inclusive). + * that is between `min` and `max` (inclusive). + * + * @param min The minimum value for the generated integer. + * @param max The maximum value for the generated integer. + * @return A random integer between `min` and `max`. + */ int generateInt(int min, int max); -bool generateBool(float prob = 0.5f); +/** + * Generates a random float between the given minimum and maximum values (inclusive). + * + * @param min The minimum value for the range. + * @param max The maximum value for the range. + * @return float A random float value between the minimum and maximum values. + */ +float generateFloat(float min, float max); + + +/** + * Generates a random 2D point within the given bounding box. + * + * @param boundingBox The rectangle bounding box within which to generate the point. + * @return Point2D The generated 2D point. + */ Point2D generatePoint(const Rectangle& boundingBox); + +/** + * Generates a random circle with the given radius inside the specified bounding box. + * + * @param radius The radius of the circle to be generated. + * @param boundingBox The rectangle representing the bounding box. + * @return Circle The generated circle. + */ Circle generateCircle(float radius, const Rectangle& boundingBox); + +/** + * Generates a random rectangle inside the specified bounding box. + * + * @param boundingBox The bounding box within which the rectangle should be generated. + * @return The generated rectangle within the bounding box. + */ Rectangle generateRectangle(const Rectangle& boundingBox); -#endif // CPPBASICS_UTILS_HPP + +#endif // CPPBASICS_UTILS_HPP \ No newline at end of file From 012e9e90a5a5e2747d48088473b1c2a8d80883c5 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 12:58:47 +0100 Subject: [PATCH 083/137] resolving minor TODOs Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/Inheritance/task.md | 48 +++++++++++++++++-- .../IntroducingObjects/task.md | 17 ++++++- .../ClassesAndObjects/Polymorphism/task.md | 47 +++++++++++++++++- 3 files changed, 105 insertions(+), 7 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md index 9b3db24..eb438a9 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md @@ -6,12 +6,10 @@ Fortunately, object-oriented programming has a suitable concept for this job — it is called class _inheritance_. Inheritance mechanism allows extending an existing class and providing concrete implementations for its virtual functions. -A _derived_ class, also called a _subclass_, -inherits all the method and fields of the _base_ class, +A _derived_ class, also called a _subclass_ (_subtype_), +inherits all the method and fields of the _base class_ (_base type_), and can add its own new methods and fields. -[//]: # (TODO: also mention term `subtyping`) - Giving back to our problem, let us define a `CircleGameObject` subclass of `GameObject` class. Instances of `CircleGameObject` class represent @@ -37,7 +35,47 @@ The very first method of the `CircleGameObject` is a special method called the _ Constructor methods have the same name as the class itself, and it takes single argument `circle`: `CircleGameObject(Circle circle)`. The constructor is called to create an instance of an object and initialize its state. -For now, you can omit the `explicit` keyword put at the constructor --- we will get back to it later. + +
+ +The `explicit` [specifier](https://en.cppreference.com/w/cpp/language/explicit) +before a constructor prevents implicit type casts. +In C++, if a class has a constructor with a single argument, +which is not marked with the `explicit` keyword, +then the compiler can automatically convert the argument type to the class type +when necessary. + +For example, if the constructor `CircleGameObject(Circle circle)` has not been +marked as `explicit`, the following code would be able to compile: + +```c++ +void foo(CircleGameObject object) { /* ... */ } + +int main() { + Circle circle = { { 0.0f, 0.0f, }, 10.0f }; + // `Circle` will be implicitly converted into `CircleGameObject` + // by calling `CircleGameObject` constructor. + foo(circle); +} +``` + +However, with the constructor marked as `explicit` the code fragment above would not compile, +and should be rewritten as follows: + +```c++ +void foo(CircleGameObject object) { /* ... */ } + +int main() { + Circle circle = { { 0.0f, 0.0f, }, 10.0f }; + CircleGameObject object(circle); + foo(object); +} +``` + +In the C++ language, the usage of the `explicit` constructors is generally encouraged, +as it results in more predictable behavior. + +
[//]: # (TODO: explain explicit constructors) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md index e6d67a8..899e226 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md @@ -25,7 +25,22 @@ After that come the methods of the class. There are plenty of them, you can get the meaning of each method by consulting its _documentation_ given as a docstring comment in front of the method declaration. -[//]: # (TODO: add links to docstring format) +
+ +A docstring is a comment in a special format that is used to document a specific segment of code. +This format is widely used in C/C++ libraries and frameworks to document their API. +Dedicated tools, like the [Doxygen](https://www.doxygen.nl/index.html), +can scan the source code of your program and extract these docstring comments +to produce documentation in various formats, such as HTML or PDF. + +The docstring format supports various annotations used to document +specific aspects of a given code block. +For example, `@param` annotation can be used to document arguments of a function, +`@return` annotation can be used to document the return value of a function, etc. +You can learn more about the docstring format by consulting +the dedicated [page](https://www.doxygen.nl/manual/docblocks.html). + +
The `GameObject` class itself does not define any data fields, only the methods. It, however, implicitly defines a bunch of _properties_ of an object, for example, its position. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md index ac10b97..82c6af0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md @@ -30,7 +30,52 @@ It is another predefined by us class — it is responsible for loading the textu A pointer to a texture can be requested by calling the `getTexture` method of the `TextureManager` class. It takes as argument the ID of the textures — these IDs are represented by the `GameTextureID` enum. -[//]: # (TODO: explain difference between `enum` and `enum class`) +
+ +Note that previously we used keyword `enum`, +but the `GameTextureID` type is defined with the two keywords: `enum class`. +So what is the difference between the `enum` and `enum class` declarations? + +In fact, the `enum class` (also known as a _scoped enumeration_) +is a restricted form of the regular `enum`, +that was introduced in the C++ language to overcome +some of its issues. + +Firstly, in case of `enum class`, the names of enumeration values are kept +withing the scope of enumeration name. +This way the enumeration values do not pollute the global scope, +so there is no risk of accidental name clashes. + +Let us see the examples. +In case of regular `enum` the following syntax is used: +```c++ +enum Color { RED, GREEN, BLUE }; +// RED, GREEN, and BLUE are globally accessible names +Color green = GREEN; +``` +while in case of `enum class` the enumeration value can be accessed +only though the enumeration name: +```c++ +enum class Color { RED, GREEN, BLUE }; +// RED, GREEN, and BLUE do not pollute global scope +Color green = Color::GREEN; +``` + +Secondly, the `enum class` does not permit implicit conversion to `int`. +For example, the following code compiles just fine: +```c++ +enum Color { RED, GREEN, BLUE }; +// no compilation error, variable green equals to 1. +int green = GREEN; +``` +However, if `enum class` is used, the code will not compile: +```c++ +enum class Color { RED, GREEN, BLUE }; +// compilation error: no implicit conversion from Color to int. +int green = Color::GREEN; +``` + +
Please implement the `getTexture` methods of the `PlayerObject` and `ConsumableObject` with the following logic: From f088837a3c838c6173e6b0fb5c738bc0c6affbcf Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 14:26:02 +0100 Subject: [PATCH 084/137] resolving minor TODOs Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/Encapsulation/task.md | 24 +++++++++++++++++-- .../ClassesAndObjects/Polymorphism/task.md | 17 +++++++++---- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md index 8e15e69..74c005c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md @@ -24,13 +24,33 @@ the class can govern how its clients interact with the objects of this class: * there is a single exception to the previous rule — a class marked as a `friend` of a given class can access its protected and private members. +
+ +When applied in the context of inheritance, +as, for example, in the declaration of the `CircleGameObject`: +``` +class CircleGameObject : public GameObject +``` +the visibility modifiers have the following meaning: + +* if a class is inherited in `public` mode, + the public members of the base class become public in the derived class, + and the protected members of the base class become protected in the derived class, + private members from the base class are not accessible directly from the derived class; +* if a class is inherited in `protected` mode, + both the public and protected members of the base class become protected in the derived class, + private members from the base class are not accessible directly from the derived class; +* if a class is inherited in `private` mode, + all public and protected members of the base class become private in the derived class, + private members from the base class are not accessible directly from the derived class. + +
+ The publicly available fields and methods of the class are also called its _public interface_ (not to be confused with the term _interface_ denoting classes containing pure virtual functions and having no state). The public interface of a class defines how the objects of this class are visible from the outside, what fields and methods can the clients of the class access. -[//]: # (TODO: add a note about visibility-inheritance modifier) - You might be wondering what is the point of hiding some fields or methods of the class — after all, they can be useful outside. However, the ability to hide some of the object's _implementation details_ diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md index 82c6af0..47d3d48 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md @@ -1,8 +1,17 @@ Despite class `CircleGameObject` adds some data fields to the `GameObject` class, it still leaves some of `GameObject` virtual methods unimplemented. -Therefore, this class is still an _abstract class_ --- it cannot be instantiated. - -Let us introduce two concrete subclasses of the `CircleGameObject` class --- +Therefore, this class is still an _abstract class_ — it cannot be instantiated. + +We need to introduce concrete subclasses implementing the behavior of base class. +The instances of these subclasses can then be used in all places +where an instance of a base class is expected. +By substituting the concrete subclass of an object, +we can change the behavior of the program without changing the code +of the functions which use this object! +This ability to treat objects of different classes implementing different behaviors +as objects of a common base class is known as _subtype polymorphism_. + +Let us introduce two concrete subclasses of the `CircleGameObject` class — the `PlayerObject` class and the `ConsumableObject` classes, representing the object controlled by the player, and consumable objects respectively. At last, both of these classes implement all the functionality required by the `GameObject` class. @@ -10,8 +19,6 @@ At last, both of these classes implement all the functionality required by the ` Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. There are no new syntactic constructs here, so you should be able to understand the code in these files. -[//]: # (TODO: add here a paragraph about the polymorphism) - The implementations of these classes can be found in the files `player.cpp` and `consumable.cpp`. Note that the full implementation of some methods is already provided. For example, the `getVelocity` method of the `PlayerObject` computes From 511fd5ae7f21712ec6ad244616cd8c0e677f336d Mon Sep 17 00:00:00 2001 From: Dmitrii Kotov <87141565+boruno@users.noreply.github.com> Date: Mon, 25 Dec 2023 16:34:10 +0200 Subject: [PATCH 085/137] Module3 ownership (#21) Module3 ownership (#21) --- .gitignore | 1 + .../MoveSemantics/CMakeLists.txt | 20 ++++ .../MoveSemantics/src/task.cpp | 31 +++++ .../MoveSemantics/task-info.yaml | 16 +++ .../MemoryOwnership/MoveSemantics/task.md | 51 +++++++++ .../MoveSemantics/test/test.cpp | 21 ++++ .../NewAndDeleteOperators/CMakeLists.txt | 20 ++++ .../NewAndDeleteOperators/src/task.cpp | 32 ++++++ .../NewAndDeleteOperators/task-info.yaml | 16 +++ .../NewAndDeleteOperators/task.md | 43 +++++++ .../NewAndDeleteOperators/test/test.cpp | 9 ++ .../ObjectLifetime/CMakeLists.txt | 13 +++ .../ObjectLifetime/src/main.cpp | 4 + .../ObjectLifetime/task-info.yaml | 7 ++ .../MemoryOwnership/ObjectLifetime/task.md | 10 ++ .../MemoryOwnership/Ownership/CMakeLists.txt | 13 +++ .../MemoryOwnership/Ownership/src/main.cpp | 4 + .../MemoryOwnership/Ownership/task-info.yaml | 7 ++ .../MemoryOwnership/Ownership/task.md | 48 ++++++++ .../PlacementNew/CMakeLists.txt | 20 ++++ .../MemoryOwnership/PlacementNew/src/task.cpp | 61 ++++++++++ .../PlacementNew/task-info.yaml | 22 ++++ .../MemoryOwnership/PlacementNew/task.md | 46 ++++++++ .../PlacementNew/test/test.cpp | 41 +++++++ .../RAIICopySwapIdiom/CMakeLists.txt | 20 ++++ .../RAIICopySwapIdiom/src/dynarray.h | 35 ++++++ .../RAIICopySwapIdiom/src/task.cpp | 62 ++++++++++ .../RAIICopySwapIdiom/task-info.yaml | 15 +++ .../MemoryOwnership/RAIICopySwapIdiom/task.md | 86 ++++++++++++++ .../RAIICopySwapIdiom/test/test.cpp | 108 ++++++++++++++++++ .../MemoryOwnership/SharedPtr/CMakeLists.txt | 20 ++++ .../MemoryOwnership/SharedPtr/src/task.cpp | 55 +++++++++ .../MemoryOwnership/SharedPtr/task-info.yaml | 9 ++ .../MemoryOwnership/SharedPtr/task.md | 48 ++++++++ .../MemoryOwnership/SharedPtr/test/test.cpp | 57 +++++++++ .../SmartPointersSummary/CMakeLists.txt | 13 +++ .../SmartPointersSummary/src/main.cpp | 4 + .../SmartPointersSummary/task-info.yaml | 7 ++ .../SmartPointersSummary/task.md | 12 ++ .../MemoryOwnership/UniquePtr/CMakeLists.txt | 20 ++++ .../MemoryOwnership/UniquePtr/src/task.cpp | 23 ++++ .../MemoryOwnership/UniquePtr/task-info.yaml | 13 +++ .../MemoryOwnership/UniquePtr/task.md | 49 ++++++++ .../MemoryOwnership/UniquePtr/test/test.cpp | 16 +++ .../MemoryOwnership/WeakPtr/CMakeLists.txt | 13 +++ .../MemoryOwnership/WeakPtr/src/main.cpp | 70 ++++++++++++ .../MemoryOwnership/WeakPtr/task-info.yaml | 7 ++ .../MemoryOwnership/WeakPtr/task.md | 9 ++ .../MemoryOwnership/lesson-info.yaml | 12 ++ ObjectOrientedProgramming/section-info.yaml | 1 + README.md | 3 + 51 files changed, 1343 insertions(+) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Ownership/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Ownership/src/main.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SharedPtr/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/src/main.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/UniquePtr/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml diff --git a/.gitignore b/.gitignore index 3dfc9c3..d0a2d4c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ sfml/* Makefile CMakeCache.txt +.DS_Store diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt new file mode 100644 index 0000000..481b6ed --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-MoveSemantics) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/task.cpp) + +# Files from `./test` directory +set(TEST test/test.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) + +# Running tests +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp new file mode 100644 index 0000000..b956873 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp @@ -0,0 +1,31 @@ +#include +#include +#include + +std::unique_ptr transfer_ownership(std::unique_ptr &ptr) { + return std::unique_ptr(std::move(ptr)); +} + +void swap_ownership(std::unique_ptr& ptr1, std::unique_ptr& ptr2) { + std::unique_ptr temp(ptr1.release()); + ptr1 = std::move(ptr2); + ptr2 = std::move(temp); +} + +int main() { + auto ptr1 = std::make_unique(42); + auto ptr2 = transfer_ownership(ptr1); + if (ptr1 == nullptr) { + std::cout << "Ownership is transferred!" << std::endl; + } + + auto ptr3 = std::make_unique(10); + auto ptr4 = std::make_unique(20); + + swap_ownership(ptr3, ptr4); + + std::cout << "ptr1: " << *ptr3 << std::endl; + std::cout << "ptr2: " << *ptr4 << std::endl; + return 0; +} + diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml new file mode 100644 index 0000000..b253755 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml @@ -0,0 +1,16 @@ +type: edu +custom_name: move semantics +files: + - name: CMakeLists.txt + visible: false + - name: src/task.cpp + visible: true + placeholders: + - offset: 839 + length: 44 + placeholder_text: /* TODO */ + - offset: 969 + length: 98 + placeholder_text: /* TODO */ + - name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md new file mode 100644 index 0000000..bbdb5e9 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md @@ -0,0 +1,51 @@ +[Move semantics](https://en.cppreference.com/w/cpp/language/move_constructor) is a feature of C++ that allows you to efficiently transfer ownership of an object from one variable to another without copying the object. This can be useful for improving performance and avoiding unnecessary memory allocations. Before diving into move semantics, let's briefly understand [value categories](https://en.cppreference.com/w/cpp/language/value_category) concept. + +**lvalue** reference represents an object that has a name or an identifier. It refers to something that exists in memory and typically persists beyond a single expression. It usually stands on the left side of an assignment operator (`=`), hence the name lvalue. + +**rvalue** reference represents a temporary or disposable value. It is usually the result of an expression and might not have a named memory location. Rvalues are often short-lived. + +#### Move Semantics Overview +- Efficient Resource Transfer. Move semantics allows the transfer of ownership from one object to another without copying. This is especially beneficial for large or resource-intensive objects. +- [Rvalue References](https://en.cppreference.com/w/cpp/language/rvalue_reference). Move semantics utilize rvalue references (`&&`), representing temporary objects or objects about to be destroyed. Rvalue references to facilitate the efficient transfer of ownership without unnecessary overhead. +- [`std::move`](https://en.cppreference.com/w/cpp/utility/move) Function. The `std::move` function is used to convert a lvalue (an object with a name) into a rvalue reference. This is a key tool for explicitly indicating that ownership can be moved. + +#### Move Semantics with `std::unique_ptr` +`std::unique_ptr` supports move semantics through its `std::move()` member function. The `std::move()` function transfers ownership of the object from the source `std::unique_ptr` to the destination unique_ptr. The source `std::unique_ptr` is then left in a null state. + +Here is an example of how to use the `std::move()` function to transfer ownership of a `std::unique_ptr` object: + +```cpp +#include + +class MyClass { +public: + MyClass() { + std::cout << "MyClass Constructor\n"; + } + + ~MyClass() { + std::cout << "MyClass Destructor\n"; + } +}; + +int main() { + // Create two unique_ptr objects. + std::unique_ptr p1 = std::make_unique(); + std::unique_ptr p2; + + // Transfer ownership of p1 to p2. + p2 = std::move(p1); + + // p1 is now in a null state. + assert(p1 == nullptr); + + // p2 now owns the MyClass object. + assert(p2 != nullptr); + + return 0; +} +``` + +Move semantics can be helpful for a variety of tasks, such as passing a `std::unique_ptr` object to a function that takes ownership of the object, returning a `std::unique_ptr` object from a function and moving a `std::unique_ptr` object from one container to another. + +Implement functions `transfer_ownership` and `swap_ownership` using `std::move()` function, so that the code in main function compiles and runs successfully. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp new file mode 100644 index 0000000..3240cea --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp @@ -0,0 +1,21 @@ +#include +#include + +std::unique_ptr transfer_ownership(std::unique_ptr &ptr); + +void swap_ownership(std::unique_ptr& ptr1, std::unique_ptr& ptr2); + +TEST(MoveUniquePtrTest, MoveTest) { + std::unique_ptr ptr = std::make_unique(42); + std::unique_ptr new_ptr = transfer_ownership(ptr); + EXPECT_EQ(ptr.get(), nullptr); + EXPECT_EQ(*new_ptr, 42); +} + +TEST(SwapUniquePtrTest, SwapTest) { + std::unique_ptr ptr1 = std::make_unique(10); + std::unique_ptr ptr2 = std::make_unique(20); + swap_ownership(ptr1, ptr2); + EXPECT_EQ(*ptr1, 20); + EXPECT_EQ(*ptr2, 10); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt new file mode 100644 index 0000000..a072c38 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-new_and_delete_operators) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/task.cpp) + +# Files from `./test` directory +set(TEST test/test.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) + +# Running tests +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp new file mode 100644 index 0000000..d96a957 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp @@ -0,0 +1,32 @@ +#include + +struct Book { + std::string name; + + explicit Book(std::string name) : name(std::move(name)) { + std::cout << "Book is open!\n"; + } + + ~Book() { + std::cout << "Book is closed!\n"; + } +}; + +void createAndDeleteBook() { + Book *favourite_book = new Book("Harry Potter"); + delete favourite_book; +} + +void allocateAndFreeBook() { + Book *favourite_book = (Book *) malloc(sizeof(Book)); + free(favourite_book); +} + +int main() { + createAndDeleteBook(); + std::cout << "-------------------\n"; + allocateAndFreeBook(); + std::cout << "Did something come out in the console?\n" + "Nothing came out... :(" << std::endl; + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml new file mode 100644 index 0000000..704b961 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml @@ -0,0 +1,16 @@ +type: edu +custom_name: new and delete operators +files: + - name: CMakeLists.txt + visible: false + - name: src/task.cpp + visible: true + placeholders: + - offset: 266 + length: 75 + placeholder_text: create and delete Book object using new/delete + - offset: 378 + length: 79 + placeholder_text: Do the same using malloc/free + - name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md new file mode 100644 index 0000000..92c86ac --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md @@ -0,0 +1,43 @@ +New and delete operators are used for dynamic memory allocation in C++. The new operator allocates memory on the heap for an object or an array of objects. The delete operator releases the memory back to the heap. + +### new/delete syntax +The syntax for the new operator is + +```cpp +int *ptr = new int; +``` + +After creating a new object, the pointer `ptr` points to the address of the newly created object. +The syntax for the delete operator is + +```cpp +delete ptr; +``` + +The delete operator releases the memory allocated for the object pointed to by `ptr`. + +### new[]/delete[] syntax +The syntax for the new[] operator is + +```cpp +int *ptr = new int[10]; +``` + +The new[] operator allocates memory for an array of 10 integers and returns a pointer to the first element of the array. + +The syntax for the delete[] operator is + +```cpp +delete[] ptr; +``` + +The delete[] operator releases the memory allocated for the array of objects. + +### Difference between new/delete and malloc/free + +The new and delete operators are similar to the malloc() and free() functions in C, but there are some key differences: + +- The `new` and `delete` operators are overloaded, so they can be used to allocate and deallocate memory for different types of objects, including user-defined types. The `malloc()` and `free()` functions can only be used to allocate and deallocate memory for raw memory blocks. +- The `new` operator calls the constructor of the object it allocates. The `malloc()` function does not call any constructors. The same applies to the `delete` operator and the `free()` function. + +Your task is to create a `Book` object using `new`/`delete` syntax in `createAndDeleteBook` function, and then create a `Book` object using `malloc`/`free` syntax in `allocateAndFreeBook` function. The `Book` class is already defined in `main.cpp`. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp new file mode 100644 index 0000000..e27da64 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp @@ -0,0 +1,9 @@ +#include + +void createAndDeleteBook(); +void allocateAndFreeBook(); + +TEST(CreateAndDeleteBookTest, SimpleBookTest) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class + createAndDeleteBook(); + allocateAndFreeBook(); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt new file mode 100644 index 0000000..c5c7dc3 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-Object_Lifetime) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/main.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp new file mode 100644 index 0000000..9b1a251 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp @@ -0,0 +1,4 @@ +int main() { + // Put your code here + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml new file mode 100644 index 0000000..dc25492 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml @@ -0,0 +1,7 @@ +type: theory +custom_name: Object lifetime +files: + - name: CMakeLists.txt + visible: false + - name: src/main.cpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md new file mode 100644 index 0000000..8f192df --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md @@ -0,0 +1,10 @@ +As we remember from the 'Classes and Objects' block, the constructor and destructor are mandatory elements of any class, and they are responsible for creating and destroying an object. + +In this module, we will look at different topics, including memory ownership, the lifetime of an object, and such useful tools as smart pointers. + +### Lifetime of the object + +Each reference and object has a [lifetime](https://en.cppreference.com/w/cpp/language/lifetime) during the execution time of the program, it depends on the time of creation and destruction of this object. The lifetime of an object begins when the program allocates the required amount of memory to the object and initializes it, including default initialization. The lifetime ends at the moment when the object is manually deleted (for example, when calling the destructor), when exiting the scope of a code block (when we meet the `}` closing curly bracket), or when the place given to the object is occupied by another object or is cleared. + +### Object lifetime and storage duration +The concepts of object lifetime and [storage duration](https://en.cppreference.com/w/cpp/language/storage_duration) are related, but they mean different things. Object lifetime refers to the object itself, while storage duration refers to the memory that is allocated for it. The storage duration is the time between the memory allocation of a region and its deallocation, while the lifetime is the time between the construction of an object and its destruction. Object lifetime is equal to or less than the lifetime of its storage. Two objects with the same storage duration can have different lifetimes (especially for objects with dynamic storage duration). \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/Ownership/CMakeLists.txt new file mode 100644 index 0000000..c6b5c46 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-Ownership) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/main.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/Ownership/src/main.cpp new file mode 100644 index 0000000..9b1a251 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/src/main.cpp @@ -0,0 +1,4 @@ +int main() { + // Put your code here + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml new file mode 100644 index 0000000..604ab0d --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml @@ -0,0 +1,7 @@ +type: theory +custom_name: Memory ownership +files: + - name: CMakeLists.txt + visible: false + - name: src/main.cpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md new file mode 100644 index 0000000..6da7533 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md @@ -0,0 +1,48 @@ +In C++, memory ownership refers to the responsibility of managing the allocation and deallocation of memory for objects. Understanding memory ownership is crucial to preventing memory leaks, avoiding double-deletions, and ensuring proper resource management. The ownership model in C++ can be broadly categorized into two types: ownership of sub-objects stored in object fields and ownership of dynamically allocated objects using pointers. + +### Ownership of Sub-objects Stored in Object Fields + +In C++, classes often contain member variables that represent sub-objects. These sub-objects are typically owned by the parent object, and their memory is automatically managed by the parent's lifecycle. When a class instance is created, its member variables are initialized, and when the instance is destroyed, the destructors of its member variables are automatically called. Note that constructors and destructors are called in the reverse order of initialization, so for the sub-object, the destructor is called before the destructor of the parent object. + +For example, consider the following class: +```c++ +class Processor { + // Processor implementation +}; + +class Laptop { +private: + Processor laptopProcessor; // Ownership is automatically managed by Laptop + +public: + // Other methods +}; +``` + +In the example above, the Laptop class owns the Processor object as a sub-object. The Processor object's memory is automatically allocated and deallocated along with the Laptop object. + +### Ownership of dynamically allocated objects using pointers + +Objects stored in pointer-typed fields are, by default, considered non-owned. When a class contains a pointer to an object, the responsibility for memory management lies outside the class. This is crucial to prevent memory leaks and undefined behaviour, as ownership is not automatically transferred with the assignment of pointers. + +```c++ +class Student { +private: + std::string* name; // Non-owned by default + +public: + Student(std::string n) : name(new std::string(std::move(n))) {} + ~Student() { + delete name; // Responsibility for cleanup is within the class + } +}; + +``` + +In the example above, the Student class contains a pointer to a dynamically allocated `std::string`. The ownership responsibility for the memory allocated for the name lies with the Student class, and it needs to explicitly manage the memory by deleting the name in the destructor. + +### Why objects in pointer-typed fields are non-owned by default + +C++ follows a philosophy of "you only pay for what you use", which means that C++ provides flexibility and efficiency, and ownership is not automatically assumed to be transferred with a pointer assignment. Objects in pointer-typed fields are considered non-owned by default to allow for more explicit control over memory management. This approach encourages developers to know their responsibilities regarding memory allocation and deallocation, promoting a safer and more predictable memory management model. + +In scenarios where ownership needs to be transferred, smart pointers such as `std::unique_ptr` and `std::shared_ptr` can be used to express ownership semantics more explicitly, providing automated memory management with reduced risks of memory-related issues. We will discuss smart pointers in more detail in the next lesson. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt new file mode 100644 index 0000000..eb8cb55 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-Placement_new) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/task.cpp) + +# Files from `./test` directory +set(TEST test/test.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) + +# Running tests +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp new file mode 100644 index 0000000..0a5e02a --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp @@ -0,0 +1,61 @@ +#include +#include +#include + +class Cat { +private: + int age; + std::string name; + +public: + Cat(int age, std::string name) : age(age), name(std::move(name)) { + std::cout << "Cat " << this->name << " jumped into the box!\n"; + } + + ~Cat() { + std::cout << "Cat " << this->name << " jumped out of the box!\n"; + } +}; + +class Dog { +private: + int age; + std::string name; + +public: + Dog(int age, std::string name) : age(age), name(std::move(name)) { + std::cout << "Dog " << this->name << " jumped into the box!\n"; + } + + ~Dog() { + std::cout << "Dog " << this->name << " jumped out of the box!\n"; + } +}; + +void placeAnimals() { + std::size_t numberOfAnimals = 3; + char* animalBox = new char[sizeof(Cat) * numberOfAnimals]; + + for (int i = 0; i < numberOfAnimals; ++i) { + new (animalBox + i * sizeof(Cat)) Cat(i + 1, "Cat" + std::to_string(i + 1)); + } + + for (int i = 0; i < numberOfAnimals; ++i) { + reinterpret_cast(animalBox + i * sizeof(Cat))->~Cat(); + } + + for (int i = 0; i < numberOfAnimals; ++i) { + new (animalBox + i * sizeof(Dog)) Dog(i + 1, "Dog" + std::to_string(i + 1)); + } + + for (int i = 0; i < numberOfAnimals; ++i) { + reinterpret_cast(animalBox + i * sizeof(Dog))->~Dog(); + } + + delete[] animalBox; +} + +int main() { + placeAnimals(); + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml new file mode 100644 index 0000000..451e625 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml @@ -0,0 +1,22 @@ +type: edu +custom_name: placement new +files: + - name: CMakeLists.txt + visible: false + - name: src/task.cpp + visible: true + placeholders: + - offset: 805 + length: 134 + placeholder_text: Create cats in pre-allocated memory + - offset: 945 + length: 118 + placeholder_text: Call destructors for each Cat object + - offset: 1069 + length: 134 + placeholder_text: create dogs in the same pre-allocated memory + - offset: 1209 + length: 118 + placeholder_text: Call destructors for each Dog object + - name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md new file mode 100644 index 0000000..3e0585e --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md @@ -0,0 +1,46 @@ +Placement new is a special version of the new operator that allows you to construct an object in a pre-allocated memory region. This can be useful for various reasons, such as reducing memory fragmentation and improving performance. + +To use placement new, you first need to allocate a memory region of the appropriate size. Then, you can pass the memory region to placement new as the second argument. Placement new will then construct an object of the specified type in the memory region. + +When you are done with the object, you must explicitly call the destructor and then free the memory region. When working with arrays, you must call the destructor for each object in the array and then free the memory region. + +The same storage duration can cover several different object lifetimes when using placement new. For example, when you reuse a memory region to construct a new object, the lifetime of the previous object ends, and the lifetime of the new object begins. However, the storage duration of the memory region remains the same. + +Here is an example of how to use placement new to construct an integer object in a pre-allocated memory region: + +```cpp +#include + +int main() { + // Allocate a memory region of the appropriate size. + char buffer[sizeof(int)]; + + // Construct an integer object in the memory region using placement new. + int* p = new (buffer) int(); + + // Assign a value to the object. + *p = 42; + + // Print the value of the object. + std::cout << *p << std::endl; + + // Delete the object. + delete p; + + return 0; +} +``` + +Complete the function using placement new to create `Cat` and `Dog` objects in the same pre-allocated memory regions. Notice that when you are done with the objects, you must explicitly call the destructors for each object and then free the memory region. + +
+ +Remember to multiply the object's size by the loop counter when calculating the offset. + +
+ +
+ +To refer to the destructor of a class, use the following syntax: `reinterpret_cast(/*offset*/)->~Cat();`. + +
diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp new file mode 100644 index 0000000..0678221 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp @@ -0,0 +1,41 @@ +#include + +#include +#include +#include + +class Cat { +private: + int age; + std::string name; + +public: + Cat(int age, std::string name) : age(age), name(std::move(name)) { + std::cout << "Cat " << this->name << " jumped into the box!\n"; + } + + ~Cat() { + std::cout << "Cat " << this->name << " jumped out of the box!\n"; + } +}; + +class Dog { +private: + int age; + std::string name; + +public: + Dog(int age, std::string name) : age(age), name(std::move(name)) { + std::cout << "Dog " << this->name << " jumped into the box!\n"; + } + + ~Dog() { + std::cout << "Dog " << this->name << " jumped out of the box!\n"; + } +}; + +void placeAnimals(); + +TEST(PlacementNew, MovingAnimals) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class + placeAnimals(); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt new file mode 100644 index 0000000..143bfbe --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-RAIICopySwapIdiom) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/task.cpp) + +# Files from `./test` directory +set(TEST test/test.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) + +# Running tests +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h new file mode 100644 index 0000000..f0b5188 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h @@ -0,0 +1,35 @@ +#ifndef CPPBASICS_DYNARRAY_H +#define CPPBASICS_DYNARRAY_H + +#include + +class dynarray { +private: + int *m_data = nullptr; + std::size_t m_size = 0; + +public: + dynarray(); + + dynarray(std::size_t size); + + dynarray(const dynarray &other); + + dynarray(dynarray &&other); + + dynarray &operator=(dynarray other); + + ~dynarray(); + + std::size_t size() const; + + int &operator[](std::size_t i); + + const int &operator[](std::size_t i) const; + + void swap(dynarray &other); + + friend std::ostream &operator<<(std::ostream &os, const dynarray &array); +}; + +#endif //CPPBASICS_DYNARRAY_H diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp new file mode 100644 index 0000000..7612acc --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp @@ -0,0 +1,62 @@ +#include "dynarray.h" + +// implement dynarray here +dynarray::dynarray() : m_data(nullptr), m_size(0) {} + +dynarray::dynarray(const std::size_t size) : m_data(new int[size]), m_size(size) {} + +dynarray::dynarray(const dynarray &other) : m_data(new int[other.m_size]), m_size(other.m_size) { + std::copy(other.m_data, other.m_data + m_size, m_data); +} + +dynarray::dynarray(dynarray &&other) : m_data(other.m_data), m_size(other.m_size) { + other.m_data = nullptr; + other.m_size = 0; +} + +void dynarray::swap(dynarray &other) { + std::swap(m_data, other.m_data); + std::swap(m_size, other.m_size); +} + +dynarray &dynarray::operator=(dynarray other) { + swap(other); + return *this; +} + +dynarray::~dynarray() { + delete[] m_data; +} + +std::size_t dynarray::size() const { + return m_size; +} + +int &dynarray::operator[](const std::size_t i) { + return m_data[i]; +} + +const int &dynarray::operator[](const std::size_t i) const { + return m_data[i]; +} + +std::ostream &operator<<(std::ostream &os, const dynarray &array) { + for (std::size_t i = 0; i < array.size(); ++i) { + os << array[i] << " "; + } + os << std::endl; + return os; +} + +int main() { + dynarray array(5); + dynarray copy_arr = array; + for (int i = 0; i < array.size(); ++i) { + array[i] = i; + } + for (int i = 0; i < copy_arr.size(); ++i) { + copy_arr[i] = i + 1; + } + std::cout << array; + std::cout << copy_arr; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml new file mode 100644 index 0000000..efe576f --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml @@ -0,0 +1,15 @@ +type: edu +custom_name: RAII & Copy-Swap Idiom +files: + - name: CMakeLists.txt + visible: false + - name: test/test.cpp + visible: false + - name: src/dynarray.h + visible: true + - name: src/task.cpp + visible: true + placeholders: + - offset: 50 + length: 1101 + placeholder_text: Use the interface given to you in dynamic_array.h diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md new file mode 100644 index 0000000..ca53062 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md @@ -0,0 +1,86 @@ +The last thing we need to discuss is a few small but fundamental concepts in C++ that will help you work correctly with the objects you create. + +**RAII** stands for *Resource Acquisition Is Initialization* and is pronounced as '*ray*'. It is one of the most essential idioms in modern C++ and is the best way to manage resources safely. It consists of two parts: + +1. The constructor of the class should allocate all the resources that the object will use during its lifetime. If errors occur during the execution of the constructor, the destructor will automatically be called, releasing all the resources allocated at the time of the error. For example, creating a container that contains `1'000'000` elements of the `int` type is possible when creating the same container for `100'000'000'000` elements will always fill all memory, which will throw an exception (for example, [`std::bad_alloc`](https://en.cppreference.com/w/cpp/memory/new/bad_alloc)). Otherwise, it can be formulated as "The constructor is completed if and only if it was possible to ensure the invariant of the object and capture all the necessary resources". +2. The destructor of the class should release all the resources that the object used during its lifetime. For example, if the constructor manually allocates memory, the destructor should free it. It is considered a main part of the idiom and is usually the only part assumed when talking about RAII. + +In this example we will use a [`std::vector`](https://en.cppreference.com/w/cpp/container/vector), which is a dynamic array. We will cover it in the next module, but for now, we will use it as an example of RAII. Let's look at the following code: +```cpp +#include + +int main() { + { + std::vector v(1'000'000); + // Invariant: size of vector is 1'000'000, memory allocation succeeded. + v[999'999] = 123; // OK + } + + try { + std::vector v(100'000'000'000); + // Memory allocation failed, invariant is broken. + v[99'999'999'999] = 123; // won't be executed, because of exception + } catch (std::bad_alloc &) { + std::cout << "caught bad_alloc\n"; + } +} +``` + +**Rule of five** is a rule created for proper work with resources in C++. If you need to write one of the following methods, then you should write all five: +1. Destructor +2. Copy constructor +3. Copy assignment operator +4. Move constructor +5. Move assignment operator + +It expands the RAII idiom and allows you to work correctly with resources in C++. It was previously called **Rule of three** because before the introduction of move semantics in C++11, there was no need to write move constructor and move assignment operator. + +And now, as we covered the first two, let's talk about the **Copy-and-Swap idiom**. It is a technique that utilizes the `std::swap` function to make the copy assignment operator safe. This usually boils down to swapping contents of both operands, then returning an instance of `*this`. It is used to implement the copy assignment operator in the Rule of five. + +The simple implementation of RAII with Copy-and-Swap idiom looks like this: +```cpp +class RAIIexample { +public: + RAIIexample(std::size_t size) : m_size(size) { + m_data = new int[size]; + } + + // Copy constructor + RAIIexample(const RAIIexample & other) : RAIIexample(other.m_size) { + std::copy(other.m_data, other.m_data + m_size, m_data); + } + + // Move constructor + RAIIexample(RAIIexample && other) { + m_data = other.m_data; + other.m_data = nullptr; + } + + // Copy assignment operator + RAIIexample& operator=(const RAIIexample & other) { + return *this = RAIIexample(other); + } + + // Move assignment operator + RAIIexample& operator=(RAIIexample && other) noexcept { + std::swap(m_data, other.m_data); + return *this; + } + + // Destructor + ~RAIIexample() { + delete[] m_data; + } +private: + int* m_data; + std::size_t m_size; +}; +``` + +To consolidate all the knowledge gained in this module, let's write our implementation of an array with a dynamic size. You don't need support size changing after the creation of the array. +Take a look at the `dynarray.h` file. You need to implement all the methods that are declared there. To write an implementation, use `dynarray::` as a prefix for all methods. Please use **RAII**, **Rule of five** and **Copy-and-Swap idiom** in your implementation. Besides two constructors and all methods that should be by Rule of five, you need to implement the following methods: +1. `std::size_t size() const` – returns the number of elements in the array. +2. `int &operator[](std::size_t i)` – returns a reference to the element at position `i` in the array. +3. `const int &operator[](std::size_t i) const` – returns a const reference to the element at position `i` in the array. +4. `void swap(dynarray &other)` – swaps the contents of the array with the contents of `other`. You'll need to implement it for the Copy-and-Swap idiom. +5. `friend std::ostream &operator<<(std::ostream &os, const dynarray &arr)` – prints the contents of the array to the output stream `os`. Let's print the array without any brackets and commas, like this: `1 2 3 4 5`. And don't forget to print a newline character at the end of the output. diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp new file mode 100644 index 0000000..07c14ff --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp @@ -0,0 +1,108 @@ +#include +#include "../src/dynarray.h" + +TEST(DynarrayTest, DefaultConstructor) { + dynarray arr; + EXPECT_EQ(arr.size(), 0); +} + +TEST(DynarrayTest, ConstructorWithSize) { + const std::size_t size = 5; + dynarray arr(size); + EXPECT_EQ(arr.size(), size); +} + +TEST(DynarrayTest, CopyConstructor) { + dynarray arr1(3); + arr1[0] = 1; + arr1[1] = 2; + arr1[2] = 3; + + dynarray arr2(arr1); + EXPECT_EQ(arr2.size(), 3); + EXPECT_EQ(arr2[0], 1); + EXPECT_EQ(arr2[1], 2); + EXPECT_EQ(arr2[2], 3); +} + +TEST(DynarrayTest, MoveConstructor) { + dynarray arr1(3); + arr1[0] = 1; + arr1[1] = 2; + arr1[2] = 3; + + dynarray arr2(std::move(arr1)); + EXPECT_EQ(arr1.size(), 0); // arr1 is moved from, so it should be empty + EXPECT_EQ(arr2.size(), 3); + EXPECT_EQ(arr2[0], 1); + EXPECT_EQ(arr2[1], 2); + EXPECT_EQ(arr2[2], 3); +} + +TEST(DynarrayTest, CopyAssignmentOperator) { + dynarray arr1(3); + arr1[0] = 1; + arr1[1] = 2; + arr1[2] = 3; + + dynarray arr2; + arr2 = arr1; + EXPECT_EQ(arr2.size(), 3); + EXPECT_EQ(arr2[0], 1); + EXPECT_EQ(arr2[1], 2); + EXPECT_EQ(arr2[2], 3); +} + +TEST(DynarrayTest, MoveAssignmentOperator) { + dynarray arr1(3); + arr1[0] = 1; + arr1[1] = 2; + arr1[2] = 3; + + dynarray arr2; + arr2 = std::move(arr1); + EXPECT_EQ(arr1.size(), 0); // arr1 is moved from, so it should be empty + EXPECT_EQ(arr2.size(), 3); + EXPECT_EQ(arr2[0], 1); + EXPECT_EQ(arr2[1], 2); + EXPECT_EQ(arr2[2], 3); +} + +TEST(DynarrayTest, Destructor) { + dynarray *arr = new dynarray(5); + delete arr; // Destructor should be called without memory leaks +} + +TEST(DynarrayTest, Size) { + dynarray arr(4); + EXPECT_EQ(arr.size(), 4); +} + +TEST(DynarrayTest, SubscriptOperatorNonConst) { + dynarray arr(3); + arr[0] = 10; + arr[1] = 20; + arr[2] = 30; + + EXPECT_EQ(arr[0], 10); + EXPECT_EQ(arr[1], 20); + EXPECT_EQ(arr[2], 30); +} + +TEST(DynarrayTest, OutputOperator) { + dynarray arr(3); + arr[0] = 1; + arr[1] = 2; + arr[2] = 3; + + testing::internal::CaptureStdout(); // Redirect std::cout for testing + std::cout << arr; + std::string output = testing::internal::GetCapturedStdout(); + + EXPECT_EQ(output, "1 2 3 \n"); +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/CMakeLists.txt new file mode 100644 index 0000000..0fc777e --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-SharedPtr) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/task.cpp) + +# Files from `./test` directory +set(TEST test/test.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) + +# Running tests +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp new file mode 100644 index 0000000..deb1a0b --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp @@ -0,0 +1,55 @@ +#include +#include +#include + +struct chat{ +private: + std::string name; + +public: + explicit chat(std::string name) : name(std::move(name)) {}; + + ~chat() { + std::cout << "Chat deleted!\n"; + } +}; + +struct user { +private: + std::string name; + int id; + +public: + std::shared_ptr current_chat; + + user(std::string name, int id) : name(std::move(name)), id(id) {}; + + void create_chat(std::string chat_name) { + current_chat = std::make_shared(std::move(chat_name)); + } + + void join_chat_by_invite(user & other_user) { + current_chat = other_user.current_chat; + } + + void leave_chat() { + current_chat = nullptr; + } +}; + +int user_count(std::shared_ptr & sharedPtr) { + return sharedPtr.use_count(); +} + +int main() { + user user1("Tom", 1); + user user2("Bob", 2); + user user3("Alice", 3); + user1.create_chat("Friends"); + user2.join_chat_by_invite(user1); + user3.join_chat_by_invite(user1); + std::cout << user_count(user1.current_chat) << std::endl; + user1.leave_chat(); + user2.leave_chat(); + user3.leave_chat(); +} diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml new file mode 100644 index 0000000..1d0afab --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml @@ -0,0 +1,9 @@ +type: edu +custom_name: shared_ptr +files: + - name: CMakeLists.txt + visible: false + - name: test/test.cpp + visible: false + - name: src/task.cpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md new file mode 100644 index 0000000..2ca140c --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md @@ -0,0 +1,48 @@ +In contrast to `std::unique_ptr`, [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) introduces shared ownership semantics, allowing multiple smart pointers to share control over the same dynamically allocated object. While both smart pointers share some common characteristics, they differ in several ways. In this section, we will explore the shared ownership model and compare it to the exclusive ownership model of `std::unique_ptr`. + +#### Shared Ownership Model and differences between `std::unique_ptr` and `std::shared_ptr` +- Multiple Ownership. `std::shared_ptr` facilitates shared ownership, enabling several smart pointers to collectively manage the lifetime of a dynamically allocated object. This is especially useful when passing pointers to functions or storing them in containers. +- Reference Counting. Under the hood, `std::shared_ptr` implements a reference count to track the number of shared pointers pointing to the same object. The reference count is dynamically adjusted as shared pointers are created or destroyed. When the reference count reaches zero, the managed object is automatically destroyed. +- Ability to copy. While `std::unique_ptr` is movable but not copyable, `std::shared_ptr` is both movable and copyable. Copying a `std::shared_ptr` results in shared ownership and incrementation of the reference counter, and the underlying object is only deallocated when the last shared pointer releases its ownership. +- Automatic Deallocation. The memory associated with the dynamically allocated object is automatically deallocated when the last `std::shared_ptr pointing` to it is destroyed or reset. This ensures proper cleanup without explicit management. + +#### Example of usage of `std::shared_ptr` +```c++ +#include +#include + +class MyClass { +public: + MyClass() { + std::cout << "MyClass Constructor\n"; + } + + ~MyClass() { + std::cout << "MyClass Destructor\n"; + } + + void Display() { + std::cout << "Hello from MyClass!\n"; + } +}; + +int main() { + // Creating a shared pointer + std::shared_ptr sharedPtr = std::make_shared(); + + // Using the pointer + if (sharedPtr) { + sharedPtr->Display(); + } + + // Creating another shared pointer sharing ownership + std::shared_ptr anotherSharedPtr = sharedPtr; + + // Both sharedPtr and anotherSharedPtr share ownership of the same object + std::cout << "sharedPtr.use_count() = " << sharedPtr.use_count() << '\n'; + + return 0; // MyClass Destructor will be called here (shared ownership) +} +``` + +As an exercise, let's develop a system for controlling the number of users in the chat. The user can create a chat (which he automatically enters after creation), join the chat through another user's invitation, and exit the chat. Please complete the implementation of the user class, as well as the implementation of the user_count function, so that the code in the `main` function is executed correctly. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp new file mode 100644 index 0000000..022dae5 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp @@ -0,0 +1,57 @@ +#include +#include +#include +#include + +struct chat{ +private: + std::string name; + +public: + explicit chat(std::string name) : name(std::move(name)) {}; + + ~chat() { + std::cout << "Chat deleted!\n"; + } +}; + +struct user { +private: + std::string name; + int id; + +public: + std::shared_ptr current_chat; + + user(std::string name, int id) : name(std::move(name)), id(id) {}; + + void create_chat(std::string chat_name) { + current_chat = std::make_shared(std::move(chat_name));; + } + + void join_chat_by_invite(user & other_user) { + current_chat = other_user.current_chat; + } + + void leave_chat() { + current_chat = nullptr; + } +}; + +int user_count(std::shared_ptr & sharedPtr); + +TEST(SharedPtrTest, ChatTest) { + user user1("User1", 1); + user user2("User2", 2); + user user3("User3", 3); + user1.create_chat("name"); + user2.join_chat_by_invite(user1); + EXPECT_EQ(user_count(user1.current_chat), 2); + user1.leave_chat(); + EXPECT_EQ(user_count(user2.current_chat), 1); + user3.join_chat_by_invite(user2); + EXPECT_EQ(user_count(user3.current_chat), 2); + user2.leave_chat(); + user3.leave_chat(); + EXPECT_EQ(user1.current_chat, nullptr); +} diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt new file mode 100644 index 0000000..580c7d3 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-Smart_pointers_summary) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/main.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/src/main.cpp new file mode 100644 index 0000000..9b1a251 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/src/main.cpp @@ -0,0 +1,4 @@ +int main() { + // Put your code here + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml new file mode 100644 index 0000000..fc62dcd --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml @@ -0,0 +1,7 @@ +type: theory +custom_name: Smart pointers summary +files: + - name: CMakeLists.txt + visible: false + - name: src/main.cpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md new file mode 100644 index 0000000..a59c16c --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md @@ -0,0 +1,12 @@ +To summarize the differences between the smart pointers, let's take a look on a table: + +| Feature | `T*` (Raw Pointer) | `std::unique_ptr` | `std::shared_ptr` | `std::weak_ptr` | +|---------------------------------|-------------------------------|---------------------------------------------------------------|---------------------------------------------------------------|------------------------------------------------------------| +| Ownership Semantics | Manual ownership management | Unique ownership | Shared ownership | Non-intrusive observer, no ownership | +| Memory Management | Manual deallocation required | Automatic deallocation | Automatic deallocation | No deallocation responsibility | +| Copyability | Copyable (shallow copy) | Movable (transfer ownership) | Copyable (shared ownership) | Copyable (shared ownership) | +| Reference Counting | No | No | Yes | Yes | +| Circular Dependency Resolution | N/A | N/A | Resolved using weak_ptr | Resolved using weak_ptr | +| Use Case | Low-level memory manipulation | Exclusive ownership, non-transferable | Shared ownership, multiple references | Observing shared ownership without affecting lifetime | +| Common Usage | Basic pointer usage | Scoped ownership, avoiding memory leaks | Managing shared resources, avoiding memory leaks | Breaking circular dependencies, observing shared ownership | +| Automatic Cleanup on Scope Exit | No | Yes | Yes | N/A (no ownership) | diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/CMakeLists.txt new file mode 100644 index 0000000..37423d2 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-UniquePtr) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/task.cpp) + +# Files from `./test` directory +set(TEST test/test.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) + +# Running tests +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp new file mode 100644 index 0000000..673ecb6 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp @@ -0,0 +1,23 @@ +#include +#include + +std::unique_ptr *create_unique_ptr_array(int size) { + std::unique_ptr *array = new std::unique_ptr[size]; + for (int i = 0; i < size; i++) { + array[i] = std::make_unique(); + } + return array; +} + +int main() { + std::unique_ptr *array = create_unique_ptr_array(10); + for (int i = 0; i < 10; i++) { + *array[i] = i; + } + for (int i = 0; i < 10; i++) { + std::cout << *array[i] << std::endl; + } + delete[] array; + + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml new file mode 100644 index 0000000..5543b25 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml @@ -0,0 +1,13 @@ +type: edu +custom_name: unique_ptr +files: + - name: CMakeLists.txt + visible: false + - name: src/task.cpp + visible: true + placeholders: + - offset: 549 + length: 166 + placeholder_text: Create an array of unique_ptr pointers + - name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md new file mode 100644 index 0000000..667e3cb --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md @@ -0,0 +1,49 @@ +C++ contains a tool that provides exclusive ownership semantics. [std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr) is a "smart" pointer that is designed to manage the lifetime of dynamically allocated objects. The unique ownership model ensures that at any given time, only one `std::unique_ptr` instance owns a particular dynamically allocated object. When the owning `std::unique_ptr` is destroyed or explicitly reset, it ensures the deallocation of the associated memory. + +#### Main features about std::unique_ptr ownership model +- Exclusive ownership. When a dynamically allocated object comes into the possession of `std::unique_ptr`, no `std::unique_ptr` or other smart pointer (which we will cover further) can share ownership of this object. +- The transfer of ownership occurs through move semantics (they will be discussed in the next step). When a `std::unique_ptr` is moved, the ownership is transferred, and the source `std::unique_ptr` becomes empty (`nullptr`). +- Automatic Deallocation. When a `std::unique_ptr` goes out of scope or is explicitly reset, it automatically releases the memory it owns. +- Support the correct state. `std::unique_ptr` is movable but not copyable. This aligns well with the ownership model, as copying would violate the exclusive ownership semantics. +- Nullability. `std::unique_ptr` can be in a null state (pointing to no object) by default or after a move operation. This feature allows for representing the absence of an object safely and clearly. + +`std::unique_ptr` prevents memory leaks and double deletion. These two problems often happen when a programmer deals with the allocation of memory himself. `std::unique_ptr` ensures that an object will be deleted when it is no longer needed, even if an exception is thrown, and prevents an object from being deleted more than once. In addition, it slightly improves code readability: `std::unique_ptr` makes it clear which pointer owns an object and which is responsible for deleting it. + +#### Example of usage of `std::unique_ptr` + +```c++ +#include +#include + +class Cat { +public: + Cat() { + std::cout << "Cat appeared\n"; + } + + ~Cat() { + std::cout << "Cat disappeared\n"; + } + + void Meow() { + std::cout << "Meow!\n"; + } +}; + +int main() { + // Creating a unique pointer + std::unique_ptr catPtr = std::make_unique(); + + // Using the pointer + if (catPtr) { + catPtr->Meow(); + } + + // Deleting the object manually + // anotherCatPtr.reset(); + + return 0; // MyClass Destructor will be called here +} +``` + +Your task is to finish the implementation of `create_unique_ptr_array` function. It should create a `std::unique_ptr` with an array of `int` of size `size` and return it. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp new file mode 100644 index 0000000..60a3eae --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp @@ -0,0 +1,16 @@ +#include +#include +#include + +std::unique_ptr *create_unique_ptr_array(int size); + +TEST(CreateUniquePtrArrayTest, Simple) { + std::unique_ptr *array = create_unique_ptr_array(10); + for (int i = 0; i < 10; i++) { + *array[i] = i; + } + for (int i = 0; i < 10; i++) { + std::cout << *array[i] << std::endl; + } + delete[] array; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt new file mode 100644 index 0000000..e1c2412 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.26) + +project(ObjectOrientedProgramming-MemoryOwnership-WeakPtr) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/main.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp new file mode 100644 index 0000000..5de8198 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp @@ -0,0 +1,70 @@ +#include +#include + +class Laptop; + +class Student : public std::enable_shared_from_this { +public: + explicit Student(int studentID) : studentID(studentID) {} + + int GetStudentID() const { + return studentID; + } + + void SetLaptop(const std::shared_ptr& laptop_pointer) { + laptop = laptop_pointer; + } + + std::shared_ptr GetLaptop() const { + return laptop; + } + +private: + int studentID; + std::shared_ptr laptop; +}; + +class Laptop { +public: + explicit Laptop(const std::shared_ptr& owner) : owner(owner) {} + + void DisplayInfo() const { + if (auto lockedOwner = owner.lock()) { + std::cout << "Laptop belongs to Student ID: " << lockedOwner->GetStudentID() << std::endl; + } else { + std::cout << "Laptop owner has been released.\n"; + } + } + +private: + std::weak_ptr owner; +}; + +int main() { + auto student1 = std::make_shared(1); + auto student2 = std::make_shared(2); + + // Create a laptop with an owner (student1) + auto laptop = std::make_shared(student1); + + // Set the laptop for student1 + student1->SetLaptop(laptop); + + // Display laptop owner info + std::cout << "Initial Laptop Owner Info:\n"; + laptop->DisplayInfo(); + + // Student2 borrows the laptop + student2->SetLaptop(laptop); + + // Display laptop owner info after borrowing + std::cout << "\nAfter Student2 Borrowed the Laptop:\n"; + laptop->DisplayInfo(); + + // Display laptop owner info after student1 release + student1.reset(); // Release student1 + std::cout << "\nAfter Student1 Released the Laptop:\n"; + laptop->DisplayInfo(); + + return 0; +} diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml new file mode 100644 index 0000000..7b98137 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml @@ -0,0 +1,7 @@ +type: theory +custom_name: weak_ptr +files: + - name: CMakeLists.txt + visible: false + - name: src/main.cpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md new file mode 100644 index 0000000..713c901 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md @@ -0,0 +1,9 @@ +In C++ memory ownership, [std::weak_ptr](https://en.cppreference.com/w/cpp/memory/weak_ptr) stands out as a tool for managing transient ownership without affecting the object's lifetime. While `std::shared_ptr` enables shared ownership, `std::weak_ptr` complements it by providing a non-intrusive observer-like role; this pointer does not contribute to the object's reference count, allowing for the detection of object state without extending its lifetime. + +#### Distinctive Features of std::weak_ptr and common usage: +- Non-Intrusive Observation. `std::weak_ptr` allows for the observation of an object without actively participating in its ownership. Unlike `std::shared_ptr`, it doesn't contribute to the reference count, and its existence doesn't affect the lifetime of the shared object. +- Breaking Circular References. In scenarios where circular dependencies among objects lead to reference cycles, using std::weak_ptr can help break the cycle. This ensures that objects are properly deallocated when they are no longer in use, even in the presence of circular references. As an example, consider a scenario where two objects, `A` and `B`, hold `std::shared_ptr` instances to each other. In this case, the reference count of each object will never reach zero, and they will never be deallocated. However, if one of the objects holds a `std::weak_ptr` to the other, the reference count will reach zero when the other object is destroyed, and the object will be deallocated. +- Locking for Access. To access the shared object, a `std::weak_ptr` must be converted to a `std::shared_ptr` using the `lock()` member function. If the shared object still exists, this operation succeeds, allowing safe access. Otherwise, it returns an empty `std::shared_ptr`. +- Observer Pattern. `std::weak_ptr` is often used in conjunction with `std::shared_ptr` to implement the observer pattern. Objects can register observers using `std::weak_ptr`, and these observers can check the object's state before attempting to access it. + +Code in `main.cpp` demonstrates the use of `std::weak_ptr` to avoid circular references when managing ownership of a `Laptop` by a `Student`. A laptop is created with one student owner and shared with another student. When the original owner is released, the laptop's weak pointer to the owner becomes invalid. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml new file mode 100644 index 0000000..fa07041 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml @@ -0,0 +1,12 @@ +custom_name: Memory Ownership +content: + - ObjectLifetime + - NewAndDeleteOperators + - PlacementNew + - Ownership + - UniquePtr + - MoveSemantics + - SharedPtr + - WeakPtr + - SmartPointersSummary + - RAIICopySwapIdiom diff --git a/ObjectOrientedProgramming/section-info.yaml b/ObjectOrientedProgramming/section-info.yaml index 56082a6..b9778cc 100644 --- a/ObjectOrientedProgramming/section-info.yaml +++ b/ObjectOrientedProgramming/section-info.yaml @@ -1,3 +1,4 @@ custom_name: Object Oriented Programming content: - ClassesAndObjects +- MemoryOwnership diff --git a/README.md b/README.md index c7009d6..c6b5016 100644 --- a/README.md +++ b/README.md @@ -84,4 +84,7 @@ with each module covering specific topics and aspects of the C++ language. * C style strings * __TBA__ ... + +* __Memory ownership__ + * smart pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr` From ae428e7256c6890a96a930ea16ff356ac0e1868f Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 15:56:08 +0100 Subject: [PATCH 086/137] remove obsolete files Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/lesson-info.yaml | 2 +- .../solutions/src/cgobject.cpp | 47 ------ .../solutions/src/collision.cpp | 31 ---- .../solutions/src/consumable.cpp | 46 ------ .../solutions/src/direction.cpp | 16 -- .../solutions/src/dynscene.cpp | 144 ------------------ .../ClassesAndObjects/solutions/src/enemy.cpp | 44 ------ .../solutions/src/engine.cpp | 85 ----------- .../solutions/src/gobject.cpp | 7 - .../solutions/src/gobjectlist.cpp | 74 --------- .../ClassesAndObjects/solutions/src/main.cpp | 6 - .../solutions/src/operators.cpp | 42 ----- .../solutions/src/player.cpp | 54 ------- .../ClassesAndObjects/solutions/src/point.cpp | 19 --- .../solutions/src/rectangle.cpp | 35 ----- .../ClassesAndObjects/solutions/src/scene.cpp | 47 ------ .../solutions/src/scenes.cpp | 14 -- .../solutions/src/statscene.cpp | 46 ------ .../solutions/src/textures.cpp | 42 ----- .../ClassesAndObjects/solutions/src/utils.cpp | 43 ------ 20 files changed, 1 insertion(+), 843 deletions(-) delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml index 59cda0a..2fb38f1 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-info.yaml @@ -11,4 +11,4 @@ content: - CollisionsRevisited - NewChallenge - NewDynamics -is_template_based: false +is_template_based: false \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp deleted file mode 100644 index 1d49ba3..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/cgobject.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "cgobject.hpp" - -#include "operators.hpp" - -CircleGameObject::CircleGameObject(Circle circle) - : circle(circle) - , status(GameObjectStatus::NORMAL) -{} - -Point2D CircleGameObject::getPosition() const { - return circle.center; -} - -void CircleGameObject::setPosition(Point2D position) { - circle.center = position; -} - -GameObjectStatus CircleGameObject::getStatus() const { - return status; -} - -void CircleGameObject::setStatus(GameObjectStatus newStatus) { - status = newStatus; -} - -Circle CircleGameObject::getCircle() const { - return circle; -} - -Rectangle CircleGameObject::getBoundingBox() const { - Point2D offset = { circle.radius, circle.radius }; - Point2D p1 = circle.center - offset; - Point2D p2 = circle.center + offset; - return createRectangle(p1, p2); -} - -void CircleGameObject::draw(sf::RenderWindow &window, TextureManager& textureManager) const { - const sf::Texture* texture = getTexture(textureManager); - if (texture == nullptr) - return; - sf::CircleShape shape; - shape.setPosition(circle.center.x, circle.center.y); - shape.setOrigin(circle.radius, circle.radius); - shape.setRadius(circle.radius); - shape.setTexture(texture); - window.draw(shape); -} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp deleted file mode 100644 index a41a0e9..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/collision.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include - -#include "collision.hpp" -#include "operators.hpp" -#include "cgobject.hpp" -#include "utils.hpp" - -// TODO: move to utils.cpp (?) -float distance(Point2D a, Point2D b) { - float dx = a.x - b.x; - float dy = a.y - b.y; - return sqrt(dx * dx + dy * dy); -} - -CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2) { - CollisionInfo info; - info.distance = distance(circle1.center, circle2.center); - info.collide = (info.distance < circle1.radius + circle2.radius); - return info; -} - -CollisionInfo collisionInfo(const GameObject& object1, const GameObject& object2) { - const CircleGameObject* circleObject1 = dynamic_cast(&object1); - const CircleGameObject* circleObject2 = dynamic_cast(&object2); - if (circleObject1 && circleObject2) { - return collisionInfo(circleObject1->getCircle(), circleObject2->getCircle()); - } - // TODO - assert(false); -} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp deleted file mode 100644 index d03f046..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/consumable.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "consumable.hpp" - -#include "constants.hpp" - -ConsumableObject::ConsumableObject() - : CircleGameObject({ { CONSUMABLE_START_X, CONSUMABLE_START_Y }, CONSUMABLE_RADIUS }) -{} - -GameObjectKind ConsumableObject::getKind() const { - return GameObjectKind::CONSUMABLE; -} - -Point2D ConsumableObject::getVelocity() const { - return { 0.0f, 0.0f }; -} - -void ConsumableObject::update(sf::Time delta) { - if (getStatus() != GameObjectStatus::DESTROYED) { - setStatus(GameObjectStatus::NORMAL); - } -} - -void ConsumableObject::onCollision(const GameObject &object, const CollisionInfo &info) { - if (getStatus() == GameObjectStatus::DESTROYED || object.getKind() == GameObjectKind::CONSUMABLE) { - return; - } - if (info.collide) { - setStatus(GameObjectStatus::DESTROYED); - return; - } - if (info.distance < CONSUMABLE_WARNED_MULTIPLIER * getCircle().radius) { - setStatus(GameObjectStatus::WARNED); - return; - } -} - -const sf::Texture* ConsumableObject::getTexture(TextureManager& textureManager) const { - switch (getStatus()) { - case GameObjectStatus::NORMAL: - return textureManager.getTexture(GameTextureID::STAR); - case GameObjectStatus::WARNED: - return textureManager.getTexture(GameTextureID::STAR_CONCERNED); - case GameObjectStatus::DESTROYED: - return nullptr; - } -} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp deleted file mode 100644 index 0a2b606..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/direction.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "direction.hpp" - -Point2D getDirection(Direction direction) { - switch (direction) { - case North: - return { 0.0f, -1.0f }; - case East: - return { 1.0f, 0.0f }; - case South: - return { 0.0f, 1.0f }; - case West: - return { -1.0f, 0.0f }; - default: - return { 0.0f, 0.0f }; - } -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp deleted file mode 100644 index 1dbcf2e..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/dynscene.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "dynscene.hpp" - -#include "constants.hpp" -#include "player.hpp" -#include "consumable.hpp" -#include "enemy.hpp" -#include "utils.hpp" - -const int MAX_DYNAMIC_OBJECTS_ON_SCENE = 10; -const int MAX_ENEMY_OBJECTS_ON_SCENE = 4; - -const int NEW_DYNAMIC_OBJECT_PROB = 1; -const int NEW_ENEMY_OBJECT_PROB = 10; - -GameplayDynamicScene::GameplayDynamicScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} - -void GameplayDynamicScene::activate() { - addNewGameObject(GameObjectKind::PLAYER); -} - -void GameplayDynamicScene::deactivate() { - // remove all the objects from the list - objects.remove([&] (const GameObject& object) { - return true; - }); -} - -SceneID GameplayDynamicScene::getID() const { - return SceneID::DYNAMIC_GAME_FIELD; -} - -SceneID GameplayDynamicScene::getNextSceneID() const { - return SceneID::DYNAMIC_GAME_FIELD; -} - -void GameplayDynamicScene::processEvent(const sf::Event& event) { - return; -} - -void GameplayDynamicScene::update(sf::Time delta) { - // update the objects' state - objects.foreach([delta] (GameObject& object) { - object.update(delta); - }); - // move objects according to their velocity and elapsed time - objects.foreach([this, delta] (GameObject& object) { - move(object, delta); - }); - // compute collision information for each pair of objects - objects.foreach([this] (GameObject& object) { - objects.foreach([this, &object] (GameObject& other) { - if (&object != &other) { - detectCollision(object, other); - } - }); - }); - // update the list of objects - updateObjectsList(); -} - -void GameplayDynamicScene::updateObjectsList() { - // remove destroyed objects from the list - objects.remove([] (const GameObject& object) { - return (object.getStatus() == GameObjectStatus::DESTROYED) - && (object.getKind() != GameObjectKind::PLAYER); - }); - // count the number of the different kinds of objects present on the scene - int consumableCount = 0; - int enemyCount = 0; - objects.foreach([&] (const GameObject& object) { - switch (object.getKind()) { - case GameObjectKind::CONSUMABLE: - ++consumableCount; - break; - case GameObjectKind::ENEMY: - ++enemyCount; - break; - default: - break; - } - }); - // add new objects of randomly chosen kind if there is enough room for them on the scene - int dynamicObjectsCount = consumableCount + enemyCount; - if (dynamicObjectsCount < MAX_DYNAMIC_OBJECTS_ON_SCENE) { - int r = rand() % 100; - int k = rand() % 100; - if (r < NEW_DYNAMIC_OBJECT_PROB) { - if (k < NEW_ENEMY_OBJECT_PROB && enemyCount < MAX_ENEMY_OBJECTS_ON_SCENE) { - addNewGameObject(GameObjectKind::ENEMY); - } else if (k > NEW_ENEMY_OBJECT_PROB) { - addNewGameObject(GameObjectKind::CONSUMABLE); - } - } - } -} - -std::shared_ptr GameplayDynamicScene::addNewGameObject(GameObjectKind kind) { - std::shared_ptr object; - while (!object) { - // create an object with default position - switch (kind) { - case GameObjectKind::PLAYER: { - object = std::make_shared(); - break; - } - case GameObjectKind::CONSUMABLE: { - object = std::make_shared(); - break; - } - case GameObjectKind::ENEMY: { - object = std::make_shared(); - break; - } - } - // set random position for consumable and enemy objects - if (kind == GameObjectKind::CONSUMABLE || - kind == GameObjectKind::ENEMY) { - setObjectPosition(*object, generatePoint(getBoundingBox())); - } else { - fitInto(*object); - } - // check that object does not collide with existing objects - bool collide = false; - objects.foreach([&collide, &object](GameObject& other) { - collide |= collisionInfo(*object, other).collide; - }); - // reset a colliding object - if (collide) { - object = nullptr; - } - } - objects.insert(object); - return object; -} - -void GameplayDynamicScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { - // draw background - drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); - // draw all objects on the scene - objects.foreach([&] (const GameObject& object) { - object.draw(window, textureManager); - }); -} - diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp deleted file mode 100644 index 083d4d1..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/enemy.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "enemy.hpp" - -#include "constants.hpp" -#include "operators.hpp" -#include "utils.hpp" - -EnemyObject::EnemyObject() - : CircleGameObject({ { ENEMY_START_X, ENEMY_START_Y }, ENEMY_RADIUS }) -{} - -GameObjectKind EnemyObject::getKind() const { - return GameObjectKind::ENEMY; -} - -Point2D EnemyObject::getVelocity() const { - return velocity; -} - -void EnemyObject::update(sf::Time delta) { - if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) - return; - updateTimer += delta; - if (updateTimer < sf::seconds(1.0f)) - return; - updateTimer = sf::milliseconds(0.0f); - updateVelocity(); -} - -void EnemyObject::updateVelocity() { - Direction direction1 = static_cast(generateInt(0, 3)); - Direction direction2 = static_cast(generateInt(0, 3)); - Point2D directionVector = (direction1 == direction2) - ? getDirection(direction1) - : (getDirection(direction1) + getDirection(direction2)); - velocity = SPEED * directionVector; -} - -void EnemyObject::onCollision(const GameObject &object, const CollisionInfo &info) { - return; -} - -const sf::Texture* EnemyObject::getTexture(TextureManager& textureManager) const { - return textureManager.getTexture(GameTextureID::BLACKHOLE); -} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp deleted file mode 100644 index 3f5a6e8..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/engine.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "engine.hpp" - -#include "constants.hpp" - -GameEngine *GameEngine::create() { - static GameEngine engine; - return &engine; -} - -GameEngine::GameEngine() - : scene(nullptr) - , active(true) -{ - // initialize random number generator - srand(time(nullptr)); - // initialize resource managers - active &= sceneManager.initialize(); - active &= textureManager.initialize(); - if (!active) { - return; - } - // set the current scene - scene = sceneManager.getCurrentScene(); - // initialize the application window - window.create(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Space Game"); - window.setFramerateLimit(60); -} - -void GameEngine::run() { - sf::Clock clock; - while (active && window.isOpen()) { - sf::Time delta = clock.restart(); - processInput(); - update(delta); - render(); - sceneTransition(); - } -} - -void GameEngine::processInput() { - sf::Event event; - while (window.pollEvent(event)) { - processEvent(event); - } -} - -void GameEngine::processEvent(const sf::Event &event) { - switch (event.type) { - case sf::Event::Closed: - close(); - break; - default: - scene->processEvent(event); - break; - } -} - -void GameEngine::update(sf::Time delta) { - scene->update(delta); -} - -void GameEngine::render() { - window.clear(sf::Color::White); - scene->draw(window, textureManager); - window.display(); -} - -void GameEngine::sceneTransition() { - if (scene->getNextSceneID() != scene->getID()) { - sceneManager.transitionScene(scene->getNextSceneID()); - scene = sceneManager.getCurrentScene(); - resizeWindow(); - } -} - -void GameEngine::resizeWindow() { - Rectangle sceneBox = scene->getBoundingBox(); - sf::Vector2u sceneSize = sf::Vector2u(width(sceneBox), height(sceneBox)); - window.setSize(sceneSize); -} - -void GameEngine::close() { - active = false; - window.close(); -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp deleted file mode 100644 index a673ef5..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobject.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "gobject.hpp" - -#include "operators.hpp" - -void GameObject::move(Point2D vector) { - setPosition(getPosition() + vector); -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp deleted file mode 100644 index 1b67d0b..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/gobjectlist.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "gobjectlist.hpp" - -void GameObjectList::link(GameObjectList::Node *cursor, std::unique_ptr &&node) { - cursor->next->prev = node.get(); - node->next = std::move(cursor->next); - node->prev = cursor; - cursor->next = std::move(node); -} - -void GameObjectList::unlink(GameObjectList::Node *node) { - node->next->prev = node->prev; - node->prev->next = std::move(node->next); -} - -GameObjectList::GameObjectList() { - head = std::make_unique(); - head->next = std::make_unique(); - tail = head->next.get(); - tail->prev = head.get(); -} - -void GameObjectList::insert(const std::shared_ptr &object) { - if (!object) { - return; - } - std::unique_ptr node = std::make_unique(); - node->object = object; - link(tail->prev, std::move(node)); -} - -void GameObjectList::remove(const std::function &pred) { - GameObjectList::Node* curr = head->next.get(); - while (curr != tail) { - Node* next = curr->next.get(); - if (pred(*curr->object)) { - unlink(curr); - } - curr = next; - } -} - -void GameObjectList::foreach(const std::function& apply) { - Node* curr = head->next.get(); - while (curr != tail) { - apply(*curr->object); - curr = curr->next.get(); - } -} - -GameObjectList::GameObjectList(const GameObjectList &other) : GameObjectList() { - Node* cursor = head.get(); - Node* curr = other.head->next.get(); - while (curr != other.tail) { - link(cursor, std::make_unique()); - cursor = cursor->next.get(); - cursor->object = curr->object; - curr = curr->next.get(); - } -} - -GameObjectList::GameObjectList(GameObjectList &&other) noexcept : GameObjectList() { - swap(*this, other); -} - -GameObjectList &GameObjectList::operator=(GameObjectList other) { - swap(*this, other); - return *this; -} - -void swap(GameObjectList& first, GameObjectList& second) { - using std::swap; - swap(first.head, second.head); - swap(first.tail, second.tail); -} diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp deleted file mode 100644 index 3608feb..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "engine.hpp" - -int main() { - GameEngine::create()->run(); - return 0; -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp deleted file mode 100644 index 19fa160..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/operators.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "game.hpp" - -Point2D operator+(Point2D a, Point2D b) { - return add(a, b); -} - -Point2D operator-(Point2D a) { - return { -a.x, -a.y }; -} - -Point2D operator-(Point2D a, Point2D b) { - return add(a, -b); -} - -Point2D operator*(float s, Point2D a) { - return mul(s, a); -} - -Circle operator+(Circle c, Point2D v) { - return { c.center + v, c.radius }; -} - -Circle operator-(Circle c, Point2D v) { - return { c.center - v, c.radius }; -} - -Rectangle operator+(Rectangle r, Point2D v) { - return { r.topLeft + v, r.botRight + v }; -} - -Rectangle operator-(Rectangle r, Point2D v) { - return { r.topLeft - v, r.botRight - v }; -} - -Circle operator*(float s, Circle c) { - return { c.center, s * c.radius }; -} - -Rectangle operator*(float s, Rectangle r) { - Point2D v = { width(r), height(r) }; - return r + s * v; -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp deleted file mode 100644 index fcadfff..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/player.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "player.hpp" - -#include "constants.hpp" -#include "direction.hpp" -#include "operators.hpp" - -PlayerObject::PlayerObject() - : CircleGameObject({ { PLAYER_START_X, PLAYER_START_Y }, RADIUS }) -{} - -GameObjectKind PlayerObject::getKind() const { - return GameObjectKind::PLAYER; -} - -Point2D PlayerObject::getVelocity() const { - Point2D velocity = { 0.0f, 0.0f }; - if (CircleGameObject::getStatus() == GameObjectStatus::DESTROYED) - return velocity; - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) { - velocity = velocity + getDirection(North); - } - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { - velocity = velocity + getDirection(East); - } - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) { - velocity = velocity + getDirection(South); - } - if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { - velocity = velocity + getDirection(West); - } - velocity = SPEED * velocity; - return velocity; -} - -void PlayerObject::update(sf::Time delta) { - return; -} - -void PlayerObject::onCollision(const GameObject &object, const CollisionInfo &info) { - if (info.collide && object.getKind() == GameObjectKind::ENEMY) { - setStatus(GameObjectStatus::DESTROYED); - } -} - -const sf::Texture* PlayerObject::getTexture(TextureManager& textureManager) const { - switch (getStatus()) { - case GameObjectStatus::NORMAL: - return textureManager.getTexture(GameTextureID::PLANET); - case GameObjectStatus::DESTROYED: - return textureManager.getTexture(GameTextureID::PLANET_DEAD); - default: - return nullptr; - } -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp deleted file mode 100644 index 5c74f69..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/point.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "game.hpp" - -Point2D add(Point2D a, Point2D b) { - Point2D c = { 0, 0 }; - c.x = a.x + b.x; - c.y = a.y + b.y; - return c; -} - -Point2D mul(float s, Point2D a) { - Point2D b = { 0, 0 }; - b.x = s * a.x; - b.y = s * a.y; - return b; -} - -Point2D move(Point2D position, Point2D velocity, float delta) { - return add(position, mul(delta, velocity)); -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp deleted file mode 100644 index 2b5a1ec..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/rectangle.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "rectangle.hpp" - -#include "operators.hpp" - -Point2D center(const Rectangle& rect) { - // TODO: explain this C++ initialization syntax - return rect.topLeft + 0.5f * Point2D { width(rect), height(rect) }; -} - -Rectangle createRectangle(Point2D p1, Point2D p2) { - Rectangle rect; - rect.topLeft = { std::min(p1.x, p2.x), std::min(p1.y, p2.y) }; - rect.botRight = { std::max(p1.x, p2.x), std::max(p1.y, p2.y) }; - return rect; -} - -Rectangle fitInto(const Rectangle& rect, const Rectangle& intoRect) { - if (width(rect) > width(intoRect) || height(rect) > height(intoRect)) { - return rect; - } - Point2D vector = { 0.0f, 0.0f }; - if (rect.topLeft.x < intoRect.topLeft.x) { - vector.x += intoRect.topLeft.x - rect.topLeft.x; - } - if (rect.topLeft.y < intoRect.topLeft.y) { - vector.y += intoRect.topLeft.y - rect.topLeft.y; - } - if (rect.botRight.x > intoRect.botRight.x) { - vector.x += intoRect.botRight.x - rect.botRight.x; - } - if (rect.botRight.y > intoRect.botRight.y) { - vector.y += intoRect.botRight.y - rect.botRight.y; - } - return rect + vector; -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp deleted file mode 100644 index 1457361..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scene.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "scene.hpp" - -#include "operators.hpp" - -Scene::Scene(float width, float height) - : width(width) - , height(height) -{} - -Rectangle Scene::getBoundingBox() const { - Rectangle box; - box.topLeft = { 0.0f, 0.0f }; - box.botRight = { width, height }; - return box; -} - -void Scene::setObjectPosition(GameObject& object, Point2D position) { - object.setPosition(position); - fitInto(object); -} - -void Scene::move(GameObject &object, Point2D vector) { - object.move(vector); - fitInto(object); -} - -void Scene::move(GameObject& object, sf::Time delta) { - move(object, 0.001f * delta.asMilliseconds() * object.getVelocity()); -} - -void Scene::fitInto(GameObject &object) { - Rectangle rect = ::fitInto(object.getBoundingBox(), getBoundingBox()); - object.setPosition(center(rect)); -} - -void Scene::detectCollision(GameObject& object1, GameObject& object2) { - CollisionInfo info = collisionInfo(object1, object2); - object1.onCollision(object2, info); - object2.onCollision(object1, info); -} - -void Scene::drawBackground(sf::RenderWindow &window, const sf::Texture* texture) const { - sf::Sprite background; - background.setTexture(*texture); - background.setTextureRect(sf::IntRect(0, 0, width, height)); - window.draw(background); -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp deleted file mode 100644 index 7125bb8..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/scenes.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "scenes.hpp" - -bool SceneManager::initialize() { - staticScene.activate(); - return true; -} - -Scene* SceneManager::getCurrentScene() { - return &staticScene; -} - -void SceneManager::transitionScene(SceneID id) { - return; -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp deleted file mode 100644 index 07c0bd0..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/statscene.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "statscene.hpp" - -#include "constants.hpp" - -GameplayStaticScene::GameplayStaticScene() : Scene(SCENE_WIDTH, SCENE_HEIGHT) {} - -void GameplayStaticScene::activate() { - fitInto(player); - fitInto(consumable); - fitInto(enemy); -} - -void GameplayStaticScene::deactivate() { - return; -} - -SceneID GameplayStaticScene::getID() const { - return SceneID::STATIC_GAME_FIELD; -} - -SceneID GameplayStaticScene::getNextSceneID() const { - return SceneID::STATIC_GAME_FIELD; -} - -void GameplayStaticScene::processEvent(const sf::Event& event) { - return; -} - -void GameplayStaticScene::update(sf::Time delta) { - player.update(delta); - consumable.update(delta); - enemy.update(delta); - move(player, delta); - move(enemy, delta); - move(consumable, delta); - detectCollision(player, consumable); - detectCollision(enemy, player); - detectCollision(enemy, consumable); -} - -void GameplayStaticScene::draw(sf::RenderWindow &window, TextureManager& textureManager) { - drawBackground(window, textureManager.getTexture(GameTextureID::SPACE)); - player.draw(window, textureManager); - consumable.draw(window, textureManager); - enemy.draw(window, textureManager); -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp deleted file mode 100644 index 2b5abcc..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/textures.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "textures.hpp" - -#include - -const char* getTextureFilename(GameTextureID id) { - switch (id) { - case GameTextureID::SPACE: - return "resources/space.png"; - case GameTextureID::PLANET: - return "resources/planet.png"; - case GameTextureID::PLANET_DEAD: - return "resources/planetDead.png"; - case GameTextureID::STAR: - return "resources/star.png"; - case GameTextureID::STAR_CONCERNED: - return "resources/starConcerned.png"; - case GameTextureID::BLACKHOLE: - return "resources/blackhole.png"; - default: - return ""; - } -} - -bool TextureManager::initialize() { - for (size_t i = 0; i < SIZE; ++i) { - GameTextureID id = static_cast(i); - const char* filename = getTextureFilename(id); - sf::Texture* texture = &textures[i]; - if (!texture->loadFromFile(filename)) { - std::cerr << "Could not open file " << filename << "\n"; - return false; - } - if (id == GameTextureID::SPACE) { - texture->setRepeated(true); - } - } - return true; -} - -const sf::Texture* TextureManager::getTexture(GameTextureID id) const { - return &textures[static_cast(id)]; -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp b/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp deleted file mode 100644 index 82ce84e..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/solutions/src/utils.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "utils.hpp" - -#include -#include - -// TODO: move `distance` here - -float generateFloat(float min, float max) { - return min + (rand() / (RAND_MAX / (max - min))); -} - -int generateInt(int min, int max) { - return min + rand() % (max - min + 1); -} - -bool generateBool(float prob) { - return generateFloat(0.0f, 1.0f) < prob; -} - -Point2D generatePoint(const Rectangle& boundingBox) { - Point2D point = { 0.0f, 0.0f, }; - point.x = generateFloat(boundingBox.topLeft.x, boundingBox.botRight.x); - point.y = generateFloat(boundingBox.topLeft.y, boundingBox.botRight.y); - return point; -} - -Circle generateCircle(float radius, const Rectangle& boundingBox) { - Circle circle = { { 0.0f, 0.0f }, 0.0f }; - if (radius > std::min(width(boundingBox), height(boundingBox))) { - return circle; - } - // TODO: replace with rectangle arithmetics operators? - circle.center.x = generateFloat( - boundingBox.topLeft.x + radius, - boundingBox.botRight.x - radius - ); - circle.center.y = generateFloat( - boundingBox.topLeft.x + radius, - boundingBox.botRight.x - radius - ); - circle.radius = radius; - return circle; -} \ No newline at end of file From 69e05187ead8371f3228b4bd09759b2471a144ac Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 17:03:08 +0100 Subject: [PATCH 087/137] proofreading Ownership module, minor changes and clean-up Signed-off-by: Evgeniy Moiseenko --- .../Introduction/CMakeLists.txt | 13 +++++ .../MemoryOwnership/Introduction/src/main.cpp | 3 ++ .../Introduction/task-info.yaml | 6 +++ .../MemoryOwnership/Introduction/task.md | 16 +++++++ .../NewAndDeleteOperators/include/book.hpp | 29 ++++++++++++ .../NewAndDeleteOperators/src/task.cpp | 23 ++------- .../NewAndDeleteOperators/task-info.yaml | 30 ++++++------ .../NewAndDeleteOperators/task.md | 47 ++++++++++--------- .../NewAndDeleteOperators/test/test.cpp | 14 ++++-- .../ObjectLifetime/src/main.cpp | 1 - .../ObjectLifetime/task-info.yaml | 2 +- .../MemoryOwnership/ObjectLifetime/task.md | 25 ++++++---- .../MemoryOwnership/lesson-info.yaml | 21 +++++---- 13 files changed, 149 insertions(+), 81 deletions(-) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Introduction/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Introduction/src/main.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Introduction/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp diff --git a/ObjectOrientedProgramming/MemoryOwnership/Introduction/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/Introduction/CMakeLists.txt new file mode 100644 index 0000000..ff53e71 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Introduction/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.27) + +project(ObjectOrientedProgramming-MemoryOwnership-Introduction) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/main.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/Introduction/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/Introduction/src/main.cpp new file mode 100644 index 0000000..e9cdae1 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Introduction/src/main.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/Introduction/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task-info.yaml new file mode 100644 index 0000000..17adf1f --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task-info.yaml @@ -0,0 +1,6 @@ +type: theory +files: +- name: CMakeLists.txt + visible: false +- name: src/main.cpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md new file mode 100644 index 0000000..e40bcab --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md @@ -0,0 +1,16 @@ +The next few lessons are dedicated to the important concept of _ownership_, +which defines the rules for managing the lifecycle of resources, +such as allocated memory blocks. +The concept of ownership is a crucial part of modern C++ language, +and mastering it is an important step towards writing safe C++ programs. + +Here we are going to cover the following topics related to ownership: + +* Lifetime of objects. +* `new` and `delete` operators. +* Placement `new`. +* Ownership and move semantics. +* Smart pointers: `unique_ptr`, `shared_ptr`, and `weak_ptr`. +* Resource Acquisition Is Initialization (RAII) idiom. +* Rule-of-five and copy-and-swap idiom. + diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp new file mode 100644 index 0000000..65a580c --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp @@ -0,0 +1,29 @@ +#ifndef CPPBASICS_BOOK_HPP +#define CPPBASICS_BOOK_HPP + +#include +#include +#include + +class Book { +public: + explicit Book(const std::string& name) : name(name) { + std::cout << "Book is open!\n"; + counter++; + } + + ~Book() { + std::cout << "Book is closed!\n"; + } + + static size_t getBooksCounter() { + return counter; + } +private: + std::string name; + static size_t counter; +}; + +size_t Book::counter = 0; + +#endif // CPPBASICS_BOOK_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp index d96a957..25a6e70 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp @@ -1,32 +1,19 @@ #include -struct Book { - std::string name; +#include "../include/book.hpp" - explicit Book(std::string name) : name(std::move(name)) { - std::cout << "Book is open!\n"; - } - - ~Book() { - std::cout << "Book is closed!\n"; - } -}; - -void createAndDeleteBook() { +void newAndDeleteBook() { Book *favourite_book = new Book("Harry Potter"); delete favourite_book; } -void allocateAndFreeBook() { +void mallocAndFreeBook() { Book *favourite_book = (Book *) malloc(sizeof(Book)); free(favourite_book); } int main() { - createAndDeleteBook(); - std::cout << "-------------------\n"; - allocateAndFreeBook(); - std::cout << "Did something come out in the console?\n" - "Nothing came out... :(" << std::endl; + newAndDeleteBook(); + mallocAndFreeBook(); return 0; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml index 704b961..925ecb5 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml @@ -1,16 +1,18 @@ type: edu -custom_name: new and delete operators +custom_name: New and Delete Operators files: - - name: CMakeLists.txt - visible: false - - name: src/task.cpp - visible: true - placeholders: - - offset: 266 - length: 75 - placeholder_text: create and delete Book object using new/delete - - offset: 378 - length: 79 - placeholder_text: Do the same using malloc/free - - name: test/test.cpp - visible: false +- name: CMakeLists.txt + visible: false +- name: src/task.cpp + visible: true + placeholders: + - offset: 83 + length: 75 + placeholder_text: create and delete Book object using new/delete + - offset: 193 + length: 79 + placeholder_text: Do the same using malloc/free +- name: test/test.cpp + visible: false +- name: include/book.hpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md index 92c86ac..64eebc7 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md @@ -1,43 +1,46 @@ -New and delete operators are used for dynamic memory allocation in C++. The new operator allocates memory on the heap for an object or an array of objects. The delete operator releases the memory back to the heap. +Recall that in `Memory Management` module of this course, +we studied the `malloc` and `free` functions which are used +to allocate and deallocate memory. +As we mentioned, these functions implement the C-style memory management, +and C++ has its own tools to manage memory. +It is finally time to meet these tools. -### new/delete syntax -The syntax for the new operator is +The `new` operator allocates memory on the heap for an object or an array of objects: ```cpp -int *ptr = new int; +// allocates memory for one `int` +int* ptr = new int; ``` -After creating a new object, the pointer `ptr` points to the address of the newly created object. -The syntax for the delete operator is +The `delete` operator releases the memory back to the heap: ```cpp delete ptr; ``` -The delete operator releases the memory allocated for the object pointed to by `ptr`. - -### new[]/delete[] syntax -The syntax for the new[] operator is +To allocate an array of a certain type, operator `new[]` is used: ```cpp -int *ptr = new int[10]; +int* array = new int[10]; ``` -The new[] operator allocates memory for an array of 10 integers and returns a pointer to the first element of the array. +As always, this operator returns a pointer to the first element of the array. -The syntax for the delete[] operator is +To deallocate an array, the `delete[]` operator should be used: ```cpp -delete[] ptr; +delete[] array; ``` -The delete[] operator releases the memory allocated for the array of objects. - -### Difference between new/delete and malloc/free - -The new and delete operators are similar to the malloc() and free() functions in C, but there are some key differences: +What is the difference between `malloc/free` and `new/delete`? -- The `new` and `delete` operators are overloaded, so they can be used to allocate and deallocate memory for different types of objects, including user-defined types. The `malloc()` and `free()` functions can only be used to allocate and deallocate memory for raw memory blocks. -- The `new` operator calls the constructor of the object it allocates. The `malloc()` function does not call any constructors. The same applies to the `delete` operator and the `free()` function. +The most important one is that the `new` and `delete` operators +call the constructor and destructor correspondingly. +The `malloc` and `free` function do not call constructors or destructors, +they are used merely to allocate raw memory blocks. -Your task is to create a `Book` object using `new`/`delete` syntax in `createAndDeleteBook` function, and then create a `Book` object using `malloc`/`free` syntax in `allocateAndFreeBook` function. The `Book` class is already defined in `main.cpp`. \ No newline at end of file +To witness the difference between the two, we ask you to complete the following task. +Given the `Book` class, create an object of this class +using `new`/`delete` syntax in `newAndDeleteBook` function, +and then try to create an object +using `malloc`/`free` syntax in `mallocAndFreeBook` function. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp index e27da64..6ee37fd 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/test/test.cpp @@ -1,9 +1,13 @@ #include -void createAndDeleteBook(); -void allocateAndFreeBook(); +#include "../include/book.hpp" -TEST(CreateAndDeleteBookTest, SimpleBookTest) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class - createAndDeleteBook(); - allocateAndFreeBook(); +void newAndDeleteBook(); +void mallocAndFreeBook(); + +TEST(CreateAndDeleteBookTest, SimpleBookTest) { + newAndDeleteBook(); + mallocAndFreeBook(); + ASSERT_EQ(1, Book::getBooksCounter()) + << "Expected constructor of Book() to be called inside `newAndDeleteBook` function."; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp index 9b1a251..e9cdae1 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/src/main.cpp @@ -1,4 +1,3 @@ int main() { - // Put your code here return 0; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml index dc25492..3af5f2a 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-info.yaml @@ -1,5 +1,5 @@ type: theory -custom_name: Object lifetime +custom_name: Object Lifetime files: - name: CMakeLists.txt visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md index 8f192df..68eda6a 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md @@ -1,10 +1,15 @@ -As we remember from the 'Classes and Objects' block, the constructor and destructor are mandatory elements of any class, and they are responsible for creating and destroying an object. - -In this module, we will look at different topics, including memory ownership, the lifetime of an object, and such useful tools as smart pointers. - -### Lifetime of the object - -Each reference and object has a [lifetime](https://en.cppreference.com/w/cpp/language/lifetime) during the execution time of the program, it depends on the time of creation and destruction of this object. The lifetime of an object begins when the program allocates the required amount of memory to the object and initializes it, including default initialization. The lifetime ends at the moment when the object is manually deleted (for example, when calling the destructor), when exiting the scope of a code block (when we meet the `}` closing curly bracket), or when the place given to the object is occupied by another object or is cleared. - -### Object lifetime and storage duration -The concepts of object lifetime and [storage duration](https://en.cppreference.com/w/cpp/language/storage_duration) are related, but they mean different things. Object lifetime refers to the object itself, while storage duration refers to the memory that is allocated for it. The storage duration is the time between the memory allocation of a region and its deallocation, while the lifetime is the time between the construction of an object and its destruction. Object lifetime is equal to or less than the lifetime of its storage. Two objects with the same storage duration can have different lifetimes (especially for objects with dynamic storage duration). \ No newline at end of file +As we remember, the constructor and destructor are mandatory elements of any class, +and they are responsible for creating and destroying an object. +For any object, the period of time between the creation of the object +through one of its constructors and its destruction through destructor +is called the [_lifetime_](https://en.cppreference.com/w/cpp/language/lifetime) of this object. + +The concepts of object's lifetime and +[_storage duration_](https://en.cppreference.com/w/cpp/language/storage_duration) +are related, but they mean different things. +Object lifetime refers to the object itself, while storage duration refers to the memory allocated for it. +The storage duration is the time between the allocation of a memory region and its deallocation, +while the lifetime is the time between the construction of an object and its destruction. +Object lifetime is equal to or less than the lifetime of its storage. +Two objects residing in the memory region with the same storage duration can have +different non-overlapping lifetimes. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml index fa07041..9e0b403 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml @@ -1,12 +1,13 @@ custom_name: Memory Ownership content: - - ObjectLifetime - - NewAndDeleteOperators - - PlacementNew - - Ownership - - UniquePtr - - MoveSemantics - - SharedPtr - - WeakPtr - - SmartPointersSummary - - RAIICopySwapIdiom +- Introduction +- ObjectLifetime +- NewAndDeleteOperators +- PlacementNew +- Ownership +- UniquePtr +- MoveSemantics +- SharedPtr +- WeakPtr +- SmartPointersSummary +- RAIICopySwapIdiom From 54552367c41f0d8c4e510ecbf876a39e8f223d7c Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 17:03:49 +0100 Subject: [PATCH 088/137] remove obsolete files Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/Introduction/task0.md | 0 .../ClassesAndObjects/Introduction/task1.md | 72 --------- .../ClassesAndObjects/Introduction/task2.md | 86 ---------- .../ClassesAndObjects/Introduction/task3.md | 55 ------- .../ClassesAndObjects/Introduction/task4.md | 82 ---------- .../ClassesAndObjects/Introduction/task5.md | 72 --------- .../ClassesAndObjects/Introduction/task6.md | 91 ----------- .../ClassesAndObjects/Introduction/task7.md | 59 ------- .../ClassesAndObjects/Introduction/task8.md | 149 ------------------ 9 files changed, 666 deletions(-) delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task0.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task1.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task2.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task3.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task4.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task5.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task6.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task7.md delete mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task8.md diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task0.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task0.md deleted file mode 100644 index e69de29..0000000 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task1.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task1.md deleted file mode 100644 index 5730c48..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task1.md +++ /dev/null @@ -1,72 +0,0 @@ -Let us finally meet with the object-oriented programming paradigm. - -At the core of this paradigm lies the concept of the _object_. -An object groups together some data, called object's _state_, -with a set of functions operating on this data. -These functions are also called the _methods_ of the object. -In this way, objects are similar to structures, but unlike plain structures, -they also allow to add functions into their definition. - -Objects are grouped into _classes_. -Class defines a blueprint after which the objects are created. -In other words, a _class_ is just a type of objects. - -For example, let us consider the `GameObject` class. -The objects of this class represent entities appearing on the game scene, -such as the planet object controlled by the player, -consumable star objects, and others that we will add later. -Instances of this class will store data related to a game object, -such as its position on the scene, and some methods to manipulate the object. - -Let us have a look at the `GameObject` class definition. -First note that in C++ new class is defined with the help of the `class` keyword. -Next comes the keyword `public` --- we will describe its meaning in the later steps. -After that come the methods of the class. -There are plenty of them, you can get the meaning of each method -by consulting its _documentation_ given as a docstring comment in front of the method declaration. - -[//]: # (TODO: add links to docstring format) - -The `GameObject` class itself does not define any data fields, only the methods. -It, however, implicitly defines a bunch of _properties_ of an object, for example, its position. -A value of a property can be requested using its _getter_ method (e.g. `getPosition`), -and it can be changed using its _setter_ method (e.g. `setPosition`). -Note that some properties of an object have both _getter_ and _setter_ methods, -like aforementioned `getPosition` and `setPosition` methods, -while others have only _getter_, for example `getVelocity`. -This is for a reason — some properties are derivatives of the current objects' status, -and they cannot be directly changed from the outside. - -Another piece of unfamiliar syntax here is the `const` keyword coming after the arguments of a methods. -It denotes the _constant methods_ --- these methods cannot change the state of the object. - -Finally, keyword `virtual` denotes the _virtual_ methods — these are the methods -that can be _overridden_ by the inheritors of the class -(we will delve back to inheritance in the next task). -The `= 0` syntax at the end of the virtual method indicates that -it is a _pure virtual_ method. -Such a method is not implemented for the given class — -it is just a stub for an actual method implementation. - -The classes that do declare any data fields and contain pure virtual methods are also called _interfaces_. -In some sense, interfaces just provide a description of the objects' behavior, -without actually specifying their internal status. -These leaves a programmer an opportunity to define several _subclasses_ -of an interface that provide different implementations of its behavior. -For example, in the following steps of this lesson, you will have to implement -a subclass of playable objects, consumable objects, and others. - -But before moving to the following step, please complete a small programming exercise. -One of the methods of the `GameObject` class --- the `move` method --- is actually not `virtual`. -It is because it can be implemented in terms of other methods of this class, namely `getPosition` and `setObjectPosition`. -To finish the programming assignment, please provide an implementation of this method. - -The implementation of the method should be put into `gobject.cpp` file. -Note that methods' definition given in this file contains both -name of the class, and the name of the method, separated by the `::`. - -```c++ -void GameObject::move(Point2D vector) { - ... -} -``` diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task2.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task2.md deleted file mode 100644 index 166e9e2..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task2.md +++ /dev/null @@ -1,86 +0,0 @@ -As we have mentioned, `GameObject` class on itself -does not specify the state of game objects — it just describes their behavior. -We need another class that extends `GameObject` with an actual state. - -Fortunately, object-oriented programming has a suitable concept for this job — -it is called class _inheritance_. -Inheritance mechanism allows extending an existing class -and providing concrete implementations for its virtual functions. -A _derived_ class, also called a _subclass_, -inherits all the method and fields of the _base_ class, -and can add its own new methods and fields. - -[//]: # (TODO: also mention term `subtyping`) - -Giving back to our problem, let us define -a `CircleGameObject` subclass of `GameObject` class. -Instances of `CircleGameObject` class represent -game objects of circular shape — like the planet object controlled by the player. - -Have a look at the `CircleGameObject` class definition. -The semicolon syntax: - -``` -class CircleGameObject : public GameObject -``` - -indicates that `CircleGameObject` is a subclass of `GameObject`. - -For a time being let us again ignore the `public` and `private` keywords -used in the `CircleGameObject` class. - -Instead, let us note that `CircleGameObject` declares not only methods, but also two fields: -* `circle` field stores its shape data; -* `status` field stores its current status. - -The very first method of the `CircleGameObject` is a special method called the _constructor_. -Constructor methods have the same name as the class itself, -and it takes single argument `circle`: `CircleGameObject(Circle circle)`. -The constructor is called to create an instance of an object and initialize its state. -For now, you can omit the `explicit` keyword put at the constructor --- we will get back to it later. - -[//]: # (TODO: explain explicit constructors) - -The definition of the `CircleGameObject` constructor body contains some new interesting syntax: - -```c++ -CircleGameObject::CircleGameObject(Circle circle) - : circle(circle) - , status(GameObjectStatus::NORMAL) -{} -``` - -After the arguments' list comes the semicolon `:`, followed by the list of the object's fields. -The value, provided in the brackets next to the field's name, is used to initialize the corresponding field. -Please note that the order of the fields in the _constructor initializer list_ is important. -It should match the order in which the fields are declared in the class. -After the constructor initializer list comes the constructor body `{}` (empty in this case). -Similarly, as regular methods, it can contain arbitrary C++ statements. - -A constructor has its counterpart — the _destructor_ method, -which should have the same name as a class prefixed with `~`. -It is a method called automatically before destruction of the object to perform some clean-up routines. -A class can have several constructors taking different arguments, -but there could only one destructor taking no arguments. - -In fact, you may have already seen the destructor on the previous step: -a class `GameObject` has a virtual destructor `~GameObject()`. -The `= default` syntax at the end of its definition indicates that -this destructor has default auto-generated implementation. - -As we will see later in the course, constructors and destructors have a pivotal role in C++. - -Going back to the `CircleGameObject` class, consider its methods. -Some of them, like `getPosition` and `setPosition`, are just re-declared methods of the `GameObject` class. -The keyword `override` at the end of the methods' declarations indicates this fact. - -However, unlike the `GameObject` class, the `CircleGameObject` class can actually define the behavior of these methods. -To be precise, it is your task to implement some of them, -namely `getPosition`, `setOPosition`, `getStatus`, `setStatus`, and `getCircle`. - -
- -Note that the position of the `CircleGameObject` is a position of its circle's center. - -
- diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task3.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task3.md deleted file mode 100644 index cf0a5f5..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task3.md +++ /dev/null @@ -1,55 +0,0 @@ -Despite class `CircleGameObject` adds some data fields to the `GameObject` class, -it still leaves some of `GameObject` virtual methods unimplemented. -Therefore, this class is still an _abstract class_ --- it cannot be instantiated. - -Let us introduce two concrete subclasses of the `CircleGameObject` class --- -the `PlayerObject` class and the `ConsumableObject` classes, -representing the object controlled by the player, and consumable objects respectively. -At last, both of these classes implement all the functionality required by the `GameObject` class. - -Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. -There are no new syntactic constructs here, so you should be able to understand the code in these files. - -[//]: # (TODO: add here a paragraph about the polymorphism) - -The implementations of these classes can be found in the files `player.cpp` and `consumable.cpp`. -Note that the full implementation of some methods is already provided. -For example, the `getVelocity` method of the `PlayerObject` computes -the current velocity vector by calling the `SFML` functions -to determine which keys are pressed by the player at the moment. - -Your task is to implement the `getTexture` methods of both classes. -These methods should return the current texture of an object to be displayed, -depending on the current status of the object. -Although, we have not yet implemented the methods that actually update -the status of the objects, implementing the `getTexture` methods first -will give you a good opportunity to practice and learn the method call syntax. - -The `getTexture` methods takes by reference one argument — object of the `TextureManager` class. -It is another predefined by us class — it is responsible for loading the textures required by the game. -A pointer to a texture can be requested by calling the `getTexture` method of the `TextureManager` class. -It takes as argument the ID of the textures — these IDs are represented by the `GameTextureID` enum. - -[//]: # (TODO: explain difference between `enum` and `enum class`) - -Please implement the `getTexture` methods of the `PlayerObject` and `ConsumableObject` -with the following logic: -* under `NORMAL` status, the player object should have `PLANET` texture; -* under `DESTROYED` status, the player object should have `PLANET_DEAD` texture; -* under `NORMAL` status, the consumable object should have `STAR` texture; -* under `WARNED` status, the consumable object should have `STAR_CONCERNED` texture; -* under `DESTROYED` status, the consumable object should not be displayed. - -
- -If you have troubles implementing the last case, -consult the documentation of the `GameObject`'s `getTexture` method. - -
- -To implement this method, you will have to call the `getTexture` method of the `TextureManager` class. -To do so use the dot syntax `.` — the same syntax as the one used to access fields of a structure: - -```c++ -const sf::Texture* texture = textureManaged.getTexture(id); -``` \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task4.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task4.md deleted file mode 100644 index 3b53799..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task4.md +++ /dev/null @@ -1,82 +0,0 @@ -Let us now consider another essential class of the game — the `Scene` class. -Game scene object is responsible for handling user input, -keeping the game objects and managing their updates, -drawing the graphics on the window, and other activities. -In essence, it is a working horse of our simple game engine. - -Let us have a look at the declaration of the `Scene` class. -This class is pretty packed — it includes both regular and virtual methods -as well as some data fields. -For the details on the meaning of these methods, we refer to their documentation. - -The `Scene` class groups its members into three sections: `public`, `protected`, and `private`. -Let us finally decipher their meaning! - -With the help of these _visibility modifiers_, -the class can govern how its clients interact with the objects of this class: - -* all the fields and methods declared inside `public` section - are visible and can be used freely outside the class; -* all the fields and methods declared inside `protected` section - are visible and can be used in the class itself and inside its ancestors; -* all the fields and methods declared inside `private` section - are visible and can be used only in the class itself, and in no other place; -* there is a single exception to the previous rule — - a class marked as a `friend` of a given class can access its protected and private members. - -The publicly available fields and methods of the class are also called its _public interface_ -(not to be confused with the term _interface_ denoting classes containing pure virtual functions and having no state). -The public interface of a class defines how the objects of this class are visible from the outside, -what fields and methods can the clients of the class access. - -[//]: # (TODO: add a note about visibility-inheritance modifier) - -You might be wondering what is the point of hiding some fields or methods of the class — -after all, they can be useful outside. -However, the ability to hide some of the object's _implementation details_ -gains the objects an ultimate control over their internal state. - -This principle is known under the name _encapsulation_. -Encapsulation allows the developer of a class to maintain the _invariants_ on object's data, -ensuring that objects of this class always remain in some valid state. - -Let us explain this on the example of the `Scene` class. -Among other things, this class is responsible for storing the game objects appearing on the scene. -One useful invariant that `Scene` class may enforce is that all of its game objects are lay within the scene's borders. -But if the `GameObject` class provides a `public` method to change object's position (i.e. `setPosition`), -then the `Scene` object has no means to guarantee this property. -Any other class or function may change the position of an object and put it outside the visible area of the scene. -However, if the only way for a user class to change position of an object is through a call -to a scene's method (for example, its `move` method) — then the implementation of this method may -take some additional actions in order to guarantee that the object remains within the scene. -This way, by controlling the visibility of objects' fields and methods, -the developer of a class may enforce various useful invariants on the state of a program. - -Mastering the invariants of classes and controlling the visibility of their members is -a skill that comes with the experience. The more complex applications you will architect and develop, -the better you will become at designing classes and their invariants. - -To consolidate the material of this step, please -implement the following two methods of the `Scene` class. - -```c++ -void setObjectPosition(GameObject& object, Point2D position); -void move(GameObject& object, Point2D vector); -``` - -You need to guarantee the invariant of the `Scene` class we discussed above — -the objects of the scene should remain within its borders. - -To implement these methods, you have to use -corresponding methods of the `GameObject` class, -as well as another method the `Scene` class — the `fitInto` method. -This method adjusts the position of an object to fit into the `Scene` borders. - -
- -Note that `Scene` class is declared as a `friend` of `GameObject` class -(see the class declaration in the `gobject.hpp` file). -Thus, the `Scene` class can access `setPosition` method of the `GameObject` class, -even though it is declared as a private method. - -
diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task5.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task5.md deleted file mode 100644 index 3344be6..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task5.md +++ /dev/null @@ -1,72 +0,0 @@ -The `Scene` class is an _abstract class_ too — -it has pure virtual methods and thus cannot be instantiated. -This gives us the flexibility of having different implementations of the `Scene` class. - -One such implementation is given by the `GameplayStaticScene` subclass -(see files `statscene.hpp` and `statscene.cpp`). -This scene implementation is called static because it contains only -static predefined number of game objects: single player object and single consumable object. -It is certainly a downgrade from our previous version implementation of the game, -when we learned how to create objects dynamically with the help of the linked lists. -Do not worry, we will restore this feature soon. -But for now, let us work with the static scene. - -The `Scene` class has a certain peculiarity compared to the `GameObject` class — -there could exist single unique `Scene` per one game instance. -We can express this in the code with the help of another C++ feature: `static` modifier. - -First, note that the `Scene` class has one method that stands out from the others: -it is `create()` method that has `static` modifier in front of it. -The `static` modifier, applied to a class member (either field or method), -turns this member into a _static_ member. - -Static members are not associated with the objects, instead they are associated with the class itself. -This means that in order to access a static member, you do not need an instance of the class at hand. -Instead, static members are accessed through the class name: - -```c++ -// obtains a scene instance by calling static method `create` -Scene* scene = Scene::create(); -``` - -Static members provide a convenient way to associate some methods or data fields with the class itself. -For example, the `create` method shown above provides an ability to instantiate -the scene object, without revealing the actual implementation to the user of the method -(note that it returns `Scene*` instead of `GameplayStaticScene*`). -Moreover, it gives us a way to ensure that only one scene is created per each game run. -How we can achieve that — well, with the help of the `static` modifier again. - -When applied to the declaration of local variables inside functions, -`static` modifier has a different meaning. -It allows creating a _static variable_ that survives and preserves its value between the function calls. -Such variables are actually stored inside the static memory region of the program, -instead of the stack memory region where the other local function's variables reside, -hence the name _static_. - -```c++ -int foo() { - static int x = 0; - return ++x; -} - -// prints 1 -std::cout << foo() << std::endl; -// prints 2 -std::cout << foo() << std::endl; -``` - -With the help of the `static` modifier, it becomes possible to -declare static `GameplayStaticScene` variable inside `Scene::create` method -and return a pointer to this variable. - -
- -Note that in this case, the address escape error does not occur. -Because the `static` variable resides in the static memory region, it lives thought the whole program execution time. -Thus, it is safe to return the address of this variable from the function. - -
- -Please implement the `create` method as described above. -If you do this correctly, you will be finally able to run the refactored game application -and see the planet and star objects on the screen. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task6.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task6.md deleted file mode 100644 index 69ecd66..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task6.md +++ /dev/null @@ -1,91 +0,0 @@ -Now let us restore the collision detection functionality in our refactored game. - -We have already changed the signature of the collision detection function. - -```c++ -CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2); -``` - -Now it takes two circle shapes by constant references. -Instead of boolean flag indicating whether the collision occurred, -it returns the `CollisionInfo` structure: - -```c++ -struct CollisionInfo { - bool collide; - float distance; -}; -``` - -This design will give us flexibility to compute various information -about possible collision between two objects inside `collisionInfo` function, -and leave the opportunity to decide what to do with this information to other modules of our game. -For now, we will store the boolean flag `collide`, indicating whether the collision happened, -and the computed `distance` between two objects — but later you -will have an opportunity to extend this structure -with additional information required to implement new features in our game. - -You might wonder why we decided to model `CollisionInfo` as a structure, -instead of using the fancy objects we learned in this lesson. -In fact, we did this on purpose to illustrate the following point. -Structures, declared via `struct`, and objects/classes, declared via `class`, -are not incompatible concepts in C++, -and often instances of both can be found in the same codebase. - -* Objects are used to tie together data (fields) and behavior (methods). - Objects provide _encapsulation_ and _polymorphism_. - The state of objects can satisfy various invariants, - maintained by carefully controlling the visibility of class' members. - -* Structures are used as simple containers of data. - They have predictable memory layout and predictable behavior — - there are no associated virtual methods dispatched at runtime. - -In C++, structures are also sometimes referred to as [_POD types_]((https://en.wikipedia.org/wiki/Passive_data_structure)), -where POD stands for _plain old data_. - -
- -Technically, in C++ there is no big difference between `class` and `struct` keywords. -For example, one can declare classes using the `struct` keyword, and vice versa. -The only real difference is that : -* in `struct` members by default have `public` visibility; -* in `class` members by default have `private` visibility. - -However, a prevalent convention among the C++ developers is -to use `class` keyword to declare actual classes in the object-oriented programming sense, -while the `struct` keyword is used to declare POD types. - -
- -Now, with the help of the new collision detection function, -your task is to re-implement the behavior of consumable objects. -- Upon collision with another object, the consumable should change its status into `DESTROYED`. -- When another object is approaching the consumable, it should change its status into `WARNED`. - This should happen whenever the distance between another object and consumable is less than `C * r`, where - - `r` is the radius of the consumable object, - - `C` is a special multiplier constant - -```c++ -const float CONSUMABLE_WARNED_MULTIPLIER = 6.0f; -``` - -To do so, please implement the `onCollision` method of `ConsumableObject` class. -This method is called periodically from the `Scene` class, notifying the object -about its potential collisions with other objects. -The function takes as a first argument another object, -and as a second argument the `CollisionInfo` structure, -containing the information about the distance between objects -and whether they actually collided. -It is up to the method's implementation to decide what to do with this information. - -Note that when the consumable object becomes `WARNED`, it should eventually -change its status back to `NORMAL` when other objects left its nearby area. -One way to achieve this is by also modifying the implementation of `update` method. -This method is also periodically called from the `Scene` class to give -the object an opportunity to update its internal state. -This function takes as a single argument the amount of time elapsed since the last update, -although you will not need this information in the current task. -Reset the status of the alive (that is --- not `DESTROYED`!) consumable back to `NORMAL`. -If there are some objects nearby, they will be detected again during `onCollision` call, -otherwise the consumable object will remain in the `NORMAL` status. diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task7.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task7.md deleted file mode 100644 index a436757..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task7.md +++ /dev/null @@ -1,59 +0,0 @@ -Now let us apply the newly acquired knowledge -to extend the mechanics of our game by adding a new kind of game objects — -enemy objects visualized as black holes. -As you will see, with the help of object-oriented programming tricks, -this task can be accomplished quite easily. - -The enemy object should behave as follows: -- it should move on a scene in a random direction, changing it periodically; -- it should consume the star objects when colliding with them; -- star objects should change their status into `WARNED` when - the enemy object is approaching them - (similarly as they do when the player object is approaching); -- when the enemy object collides with the player object, the latter - should change its status into `DESTROYED`, becoming effectively immovable. - -Let us proceed to the implementation of this behavior. -First, take a look at the declaration of the `EnemyObject` class. -It again inherits from the `CircleGameObject` class, so it already -has a corresponding circle shape data and behavior attached to it. -Moreover, the `getKind()` method of the `EnemyObject` class -distinguish the enemy objects from other kind of objects (player and consumable) -by returning the `GameObjectKind::ENEMY` value. - -We have also already implemented for you the movement behavior of the `EnemyObject`. -To do so, we have added two new fields to the objects of this class: -- `velocity` field is vector storing the direction and speed of the current velocity of the object; -- `updateTimer` field stores the time elapsed since the last update of the object's velocity. - -The method `getVelocity()` simply returns the value of `velocity` field. -The method `update(sf::Time delta)` is responsible for periodically updating the velocity of the object. -It takes as an argument the amount of time elapsed since the last update. -The implementation simply checks if the overall amount of elapsed time -is greater than the predefined time period (1 second), -and if so, it resets the velocity to a new randomly generated one. - -Now your task is to implement the rest of enemy objects functionality. - -The easy part is to implement the `getTexture` method, -that should return a new special texture for the enemy objects. -This texture has a corresponding id --- `GameTextureID::BLACKHOLE`. - -The harder part is to implement the collision behavior. -When an enemy object collides with the player object, the player object should become inactive. -This can be achieved by setting the status of the player object to `DESTROYED`. -However, an enemy object does not have direct access to the `setStatus` method of the player object. -To implement the desired behavior, you actually need to -modify the `onCollision` method of the `PlayerObject` class, not the `EnemyObject` class! - -
- -In the implementation of the `PlayerObject::onCollision` method, -remember to check that the collision occurred with the enemy object, -not an object of some other kind! - -
- -Notice that from the point of view of the consumable objects, -the player and the enemy objects behave similarly, -so you do not need to modify their behavior. \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task8.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task8.md deleted file mode 100644 index f50bcdc..0000000 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task8.md +++ /dev/null @@ -1,149 +0,0 @@ -Next, we need to restore the dynamic behavior of our game — the ability -to add and remove objects dynamically during the play. -To do so, we will re-implement the doubly linked list data structure -in terms of object-oriented programming. -Before proceeding with this task, please first finish the `Ownership` module, -as we will need some concepts taught there. - -First of all, instead of completely rewriting the `GameplayStaticScene` class, -we will just add a new class --- `GameplayDynamicScene`, where we will implement the new dynamic functionality. - -Please have a look at the declaration of the `GameplayDynamicScene` class (file `dynscene.hpp`), -and its definition (file `dynscene.cpp`). -You can find the brief description of its methods in the documentation comments. - -The `GameplayDynamicScene` class has a field `objects` of `GameObjectList` class. -This is the main class we are going to work with in this task — it will implement the doubly linked list -(its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively). - -This time, using the object-oriented programming and ownership model, -we will implement a list that will own its nodes. -The nodes will be destructed and deallocated automatically upon destruction of the list itself, -and copying of the list will result in all of its nodes being copied too. - -The nodes of the list are represented by the `Node` structure, -which implement the ownership semantics — each node is owning its successor. -Let us have a closer look on the fields of the `Node` structure. - -- `prev` field is a plain pointer `Node*`, storing the pointer to a previous node. - It is a non-owning pointer, because if it was made an owning pointer, then it would - result into ownership cycle. - Indeed, giving a node `x`, `x.prev` can point to a node `y`, such that `y.next` points to `x` --- - this is clearly a cycle. -- `next` field is an owning pointer `std::unique_ptr`. - Whenever a node is destructed, all its succeeding nodes will also be destructed, - due to its ownership semantics. -- `object` field is a shared pointer `std::shared_ptr` to a game object stored in the node. - It has the shared ownership semantics, so that the shared pointers to game objects - can be safely returned from the methods of the `GameObjectList` class. - Also, the shared ownership of game objects will allow us to implement - the copying of a list — a copy would simply store copies of shared pointers. - - -The static methods `link` and `unlink` implement the linking and unlinking of nodes respectively. -You have already seen these functions in the `LinkedList` task. -Here is a reminder of their semantics: - -```c++ -static void link(Node* cursor, std::unique_ptr&& node); -``` - -- Links together the `cursor` and `node` nodes, putting `node` right after `cursor`. - -```c++ -static void unlink(Node* node); -``` - -- Unlinks the node from its neighboring nodes. - -Please implement these methods. -Pay attention to the different ownership semantics of `next`, `prev`, and `object` pointers, -and use `std::move` to manage the ownership. -Remember that `std::unique_ptr` transitions into `nullptr` state after ownership transfer, -and so the order of `std::move` and pointer dereferences becomes important! - -Next, consider the fields of the `GameObjectList` class. - -- `head` is an owning pointer `std::unique_ptr` to the first node of the list. -- `tail` is a non-owning pointer `Node*` to the last node of the list. - It is a non-owning pointer because the pointed-to node is actually owned - by its predecessor via its `next` pointer. - -Because the `head` pointer of the list is owning one, the whole sequence of nodes -belonging to the list will be destroyed automatically upon destruction of the list itself. -This is the reason why we left the default implementation of the class' destructor: - -```c++ -~GameObjectList() = default; -``` - -
- -Indeed, the destructor of the `GameObjectList` will call -the `~std::unique_ptr()` destructor of the `head` field. -In turn, it will call the destructor of the `Node`, -which will call the destructor `~std::unique_ptr()` of its `next` field, -and so on, until all nodes will be destructed. - -
- -Note that in the previous implementation of the list (in the `LinkedList` task), -we used a single sentinel node to simplify the implementation of some list operating functions. -Moreover, under the hood the list was organized into a cyclic list: -the `next` field of the last node was pointing the first (sentinel) node. -This time we cannot reuse this trick, since a cyclic list would result into ownership cycle. -Therefore, we would need two sentinel nodes — one as the first node, and the second as a last node. - -Please take a look at the pre-defined methods `foreach` and `remove` of the list -that utilize this list representation: -- `foreach` applies the function given as an argument to every game object stored in the list; -- `remove` unlinks nodes (effectively removing them), whose game object satisfies predicate given as an argument. - -
- -You might find the type `std::function<...>` unfamiliar. -In essence, it is just an object-oriented counterpart of a function pointer. -We will have a closer look at this type in the later modules of the course. - -
- -Now, please implement the method for inserting a game object into the beginning of the list: - -```c++ -void insert(const std::shared_ptr& object); -``` - -Keep in mind that: -- the first and the last nodes should remain sentinel nodes; -- only the sentinel nodes can store null pointer `nullptr` inside `object` field. - -In order to complete this task, also finish the implementation of the ownership semantics of -the `GameObjectList` class, following the rule-of-file and copy-and-swap idiom. -In particular, please implement: - -- default constructor -```c++ -GameObjectList(); -``` - -- copy constructor -```c++ -GameObjectList(const GameObjectList& other); -``` - -- moving constructor -```c++ -GameObjectList(GameObjectList&& other) noexcept; -``` - -- assignment operator -```c++ -GameObjectList& operator=(GameObjectList other); -``` - -- swap function -```c++ -friend void swap(GameObjectList& first, GameObjectList& second); -``` - -Once you do this, you should be able to run the instance of the game with the new dynamic scene! From 188e3b2ddc3cde95d2481778896cd90962a88546 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 17:13:46 +0100 Subject: [PATCH 089/137] fix NewAndDeleteOperators task Signed-off-by: Evgeniy Moiseenko --- .../NewAndDeleteOperators/CMakeLists.txt | 1 - .../NewAndDeleteOperators/include/book.hpp | 2 -- .../NewAndDeleteOperators/src/task.cpp | 2 ++ .../NewAndDeleteOperators/task-info.yaml | 16 ++++++++-------- .../NewAndDeleteOperators/task.md | 7 +++---- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt index a072c38..442e8d8 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt @@ -10,7 +10,6 @@ set(SRC src/task.cpp) # Files from `./test` directory set(TEST test/test.cpp) - # Running learner side code # Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. add_executable(${PROJECT_NAME}-run ${SRC}) diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp index 65a580c..198ff6f 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/include/book.hpp @@ -24,6 +24,4 @@ class Book { static size_t counter; }; -size_t Book::counter = 0; - #endif // CPPBASICS_BOOK_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp index 25a6e70..a381091 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp @@ -2,6 +2,8 @@ #include "../include/book.hpp" +size_t Book::counter = 0; + void newAndDeleteBook() { Book *favourite_book = new Book("Harry Potter"); delete favourite_book; diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml index 925ecb5..27e5b5e 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml @@ -3,16 +3,16 @@ custom_name: New and Delete Operators files: - name: CMakeLists.txt visible: false +- name: include/book.hpp + visible: true +- name: test/test.cpp + visible: false - name: src/task.cpp visible: true placeholders: - - offset: 83 + - offset: 110 length: 75 - placeholder_text: create and delete Book object using new/delete - - offset: 193 + placeholder_text: // Create and delete Book object using new/delete + - offset: 220 length: 79 - placeholder_text: Do the same using malloc/free -- name: test/test.cpp - visible: false -- name: include/book.hpp - visible: true + placeholder_text: // Do the same using malloc/free diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md index 64eebc7..cfb6806 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md @@ -40,7 +40,6 @@ The `malloc` and `free` function do not call constructors or destructors, they are used merely to allocate raw memory blocks. To witness the difference between the two, we ask you to complete the following task. -Given the `Book` class, create an object of this class -using `new`/`delete` syntax in `newAndDeleteBook` function, -and then try to create an object -using `malloc`/`free` syntax in `mallocAndFreeBook` function. \ No newline at end of file +Given the `Book` class defined in `book.hpp` file, +create an object of this class using `new`/`delete` syntax in `newAndDeleteBook` function, +and then try to create an object using `malloc`/`free` syntax in `mallocAndFreeBook` function. \ No newline at end of file From 16a0cf3a05b9493713b7df5f25b33fa630c7a5a0 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 19:09:48 +0100 Subject: [PATCH 090/137] rework PlacementNew task Signed-off-by: Evgeniy Moiseenko --- .../MemoryLayout/Swap/task-info.yaml | 4 +- .../PlacementNew/CMakeLists.txt | 9 +-- .../PlacementNew/include/animal.hpp | 56 ++++++++++++++ .../PlacementNew/src/animal.cpp | 4 + .../MemoryOwnership/PlacementNew/src/task.cpp | 64 +++------------- .../PlacementNew/task-info.yaml | 47 +++++++----- .../MemoryOwnership/PlacementNew/task.md | 76 +++++++++++++------ .../PlacementNew/test/test.cpp | 70 ++++++++--------- 8 files changed, 188 insertions(+), 142 deletions(-) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/include/animal.hpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/animal.cpp diff --git a/MemoryManagement/MemoryLayout/Swap/task-info.yaml b/MemoryManagement/MemoryLayout/Swap/task-info.yaml index a4871ca..27c3e2c 100644 --- a/MemoryManagement/MemoryLayout/Swap/task-info.yaml +++ b/MemoryManagement/MemoryLayout/Swap/task-info.yaml @@ -2,11 +2,11 @@ type: edu files: - name: CMakeLists.txt visible: false +- name: test/test.cpp + visible: false - name: src/task.cpp visible: true placeholders: - offset: 32 length: 36 placeholder_text: /* TODO */ -- name: test/test.cpp - visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt index eb8cb55..1ea4a14 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/CMakeLists.txt @@ -5,16 +5,11 @@ project(ObjectOrientedProgramming-MemoryOwnership-Placement_new) set(CMAKE_CXX_STANDARD 14) # Files from `./src` directory -set(SRC src/task.cpp) +set(SRC src/task.cpp src/animal.cpp) # Files from `./test` directory set(TEST test/test.cpp) - -# Running learner side code -# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -add_executable(${PROJECT_NAME}-run ${SRC}) - # Running tests # Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/include/animal.hpp b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/include/animal.hpp new file mode 100644 index 0000000..21d9f69 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/include/animal.hpp @@ -0,0 +1,56 @@ +#ifndef CPPBASICS_ANIMAL_HPP +#define CPPBASICS_ANIMAL_HPP + +#include +#include +#include + +class Cat { +public: + explicit Cat(const std::string& name) : name(name) { + std::cout << "Cat " << this->name << " jumped into the box!\n"; + counter++; + } + + ~Cat() { + counter--; + std::cout << "Cat " << this->name << " jumped out of the box!\n"; + } + + std::string getName() const { + return name; + } + + static size_t getCounter() { + return counter; + } +private: + std::string name; + static size_t counter; +}; + +class Mouse { +public: + explicit Mouse(const std::string& name) : name(name) { + std::cout << "Mouse " << this->name << " jumped into the box!\n"; + counter++; + } + + ~Mouse() { + counter--; + std::cout << "Mouse " << this->name << " jumped out of the box!\n"; + } + + std::string getName() const { + return name; + } + + static size_t getCounter() { + return counter; + } +private: + std::string name; + static size_t counter; +}; + +#endif // CPPBASICS_ANIMAL_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/animal.cpp b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/animal.cpp new file mode 100644 index 0000000..08bee44 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/animal.cpp @@ -0,0 +1,4 @@ +#include "../include/animal.hpp" + +size_t Cat::counter = 0; +size_t Mouse::counter = 0; \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp index 0a5e02a..1a516cb 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/src/task.cpp @@ -1,61 +1,19 @@ #include -#include -#include -class Cat { -private: - int age; - std::string name; +#include "../include/animal.hpp" -public: - Cat(int age, std::string name) : age(age), name(std::move(name)) { - std::cout << "Cat " << this->name << " jumped into the box!\n"; - } - - ~Cat() { - std::cout << "Cat " << this->name << " jumped out of the box!\n"; - } -}; - -class Dog { -private: - int age; - std::string name; - -public: - Dog(int age, std::string name) : age(age), name(std::move(name)) { - std::cout << "Dog " << this->name << " jumped into the box!\n"; - } - - ~Dog() { - std::cout << "Dog " << this->name << " jumped out of the box!\n"; - } -}; - -void placeAnimals() { - std::size_t numberOfAnimals = 3; - char* animalBox = new char[sizeof(Cat) * numberOfAnimals]; - - for (int i = 0; i < numberOfAnimals; ++i) { - new (animalBox + i * sizeof(Cat)) Cat(i + 1, "Cat" + std::to_string(i + 1)); - } - - for (int i = 0; i < numberOfAnimals; ++i) { - reinterpret_cast(animalBox + i * sizeof(Cat))->~Cat(); - } - - for (int i = 0; i < numberOfAnimals; ++i) { - new (animalBox + i * sizeof(Dog)) Dog(i + 1, "Dog" + std::to_string(i + 1)); - } +Cat* createCat(char* memory) { + return new (memory) Cat("Tom"); +} - for (int i = 0; i < numberOfAnimals; ++i) { - reinterpret_cast(animalBox + i * sizeof(Dog))->~Dog(); - } +void destroyCat(char* memory) { + reinterpret_cast(memory)->~Cat(); +} - delete[] animalBox; +Mouse* createMouse(char* memory) { + return new (memory) Mouse("Jerry"); } -int main() { - placeAnimals(); - return 0; +void destroyMouse(char* memory) { + reinterpret_cast(memory)->~Mouse(); } \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml index 451e625..7b23e3c 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-info.yaml @@ -1,22 +1,29 @@ type: edu -custom_name: placement new +custom_name: Placement New files: - - name: CMakeLists.txt - visible: false - - name: src/task.cpp - visible: true - placeholders: - - offset: 805 - length: 134 - placeholder_text: Create cats in pre-allocated memory - - offset: 945 - length: 118 - placeholder_text: Call destructors for each Cat object - - offset: 1069 - length: 134 - placeholder_text: create dogs in the same pre-allocated memory - - offset: 1209 - length: 118 - placeholder_text: Call destructors for each Dog object - - name: test/test.cpp - visible: false +- name: CMakeLists.txt + visible: false +- name: src/animal.cpp + visible: true + editable: false +- name: include/animal.hpp + visible: true + editable: false +- name: src/task.cpp + visible: true + placeholders: + - offset: 85 + length: 31 + placeholder_text: // create Cat object with the name Tom in the given memory block + - offset: 156 + length: 39 + placeholder_text: // destroy Cat object stored in the given memory block + - offset: 238 + length: 35 + placeholder_text: // create Mouse object with the name Jerry in the given memory + block + - offset: 315 + length: 43 + placeholder_text: // destroy Mouse object stored in the given memory block +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md index 3e0585e..b6c0775 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md @@ -1,46 +1,76 @@ -Placement new is a special version of the new operator that allows you to construct an object in a pre-allocated memory region. This can be useful for various reasons, such as reducing memory fragmentation and improving performance. +Placement `new` operator is a special version of the `new` operator +that allows to construct an object in a pre-allocated memory region. +This can be useful for various reasons, +such as reducing memory fragmentation and improving performance. -To use placement new, you first need to allocate a memory region of the appropriate size. Then, you can pass the memory region to placement new as the second argument. Placement new will then construct an object of the specified type in the memory region. +To use the placement `new` operator, +one first has to allocate a memory region of the appropriate size. +Then, this memory region needs to be passed into placement `new` operator. +The operator will then construct an object of the specified type in the given memory region. -When you are done with the object, you must explicitly call the destructor and then free the memory region. When working with arrays, you must call the destructor for each object in the array and then free the memory region. +When an object created with the help of the placement `new` operator is no longer needed, +it must be destroyed by explicitly calling the destructor. +When working with arrays, the destructor for each object in the array should be called. -The same storage duration can cover several different object lifetimes when using placement new. For example, when you reuse a memory region to construct a new object, the lifetime of the previous object ends, and the lifetime of the new object begins. However, the storage duration of the memory region remains the same. - -Here is an example of how to use placement new to construct an integer object in a pre-allocated memory region: +Here is an example of how to use the placement `new` operator to construct +an integer object in a pre-allocated memory region: ```cpp #include +class Cat { +public: + explicit Cat(const std::string& name) { /* ... */ } + ~Cat() { ... } + std::string getName() { /* ... */ } + /* ... */ +}; + int main() { // Allocate a memory region of the appropriate size. - char buffer[sizeof(int)]; - - // Construct an integer object in the memory region using placement new. - int* p = new (buffer) int(); + char buffer[sizeof(Cat)]; - // Assign a value to the object. - *p = 42; + // Construct a Cat object in the given memory region. + Cat* cat = new (buffer) Cat("Garfield"); - // Print the value of the object. - std::cout << *p << std::endl; + // Access the object. + std::cout << cat->getName(); << std::endl; - // Delete the object. - delete p; + // Destruct the object. + cat->~Cat(); return 0; } ``` -Complete the function using placement new to create `Cat` and `Dog` objects in the same pre-allocated memory regions. Notice that when you are done with the objects, you must explicitly call the destructors for each object and then free the memory region. +With the help of the placement `new` operator it is possible to +fit into the same storage duration lifetimes of several objects. +For example, when you reuse a memory region to construct a new object, +the lifetime of the previous object ends, and the lifetime of the new object begins. +However, the storage duration of the memory region remains the same. -
+In order to finish this task, please implement the following functions. -Remember to multiply the object's size by the loop counter when calculating the offset. +The `createCat` function should create the `Cat` object with the name `"Tom"` in the given memory block: + +```c++ +Cat* createCat(char* memory); +``` -
+The `destroyCat` function should destroy the `Cat` object residing in the given memory block: -
+```c++ +void destroyCat(char* memory); +``` + +The `createMouse` function should create the `Mouse` object with the name `"Jerry"` in the given memory block: + +```c++ +Mouse* createMouse(char* memory); +``` -To refer to the destructor of a class, use the following syntax: `reinterpret_cast(/*offset*/)->~Cat();`. +The `destroyMouse` function should destroy the `Cat` object residing in the given memory block: -
+```c++ +void destroyMouse(char* memory); +``` \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp index 0678221..8ef6221 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/test/test.cpp @@ -1,41 +1,37 @@ #include +#include #include -#include -#include - -class Cat { -private: - int age; - std::string name; - -public: - Cat(int age, std::string name) : age(age), name(std::move(name)) { - std::cout << "Cat " << this->name << " jumped into the box!\n"; - } - - ~Cat() { - std::cout << "Cat " << this->name << " jumped out of the box!\n"; - } -}; - -class Dog { -private: - int age; - std::string name; - -public: - Dog(int age, std::string name) : age(age), name(std::move(name)) { - std::cout << "Dog " << this->name << " jumped into the box!\n"; - } - - ~Dog() { - std::cout << "Dog " << this->name << " jumped out of the box!\n"; - } -}; - -void placeAnimals(); - -TEST(PlacementNew, MovingAnimals) { // NOLINT(cert-err58-cpp) suppress for initialization static field in generated class - placeAnimals(); + +#include "../include/animal.hpp" + +Cat* createCat(char* memory); +void destroyCat(char* memory); + +Mouse* createMouse(char* memory); +void destroyMouse(char* memory); + +TEST(PlacementNew, PlacementNewTest) { + size_t size = std::max(sizeof(Cat), sizeof(Mouse)); + char* memory = (char*) malloc(size); + + Cat* cat = createCat(memory); + ASSERT_EQ("Tom", cat->getName()); + ASSERT_EQ("Tom", reinterpret_cast(memory)->getName()); + ASSERT_EQ(cat, reinterpret_cast(memory)); + ASSERT_EQ(1, Cat::getCounter()); + + destroyCat(memory); + ASSERT_EQ(0, Cat::getCounter()); + + Mouse* mouse = createMouse(memory); + ASSERT_EQ("Jerry", mouse->getName()); + ASSERT_EQ("Jerry", reinterpret_cast(memory)->getName()); + ASSERT_EQ(mouse, reinterpret_cast(memory)); + ASSERT_EQ(1, Mouse::getCounter()); + + destroyMouse(memory); + ASSERT_EQ(0, Mouse::getCounter()); + + free(memory); } \ No newline at end of file From f1327894baee35a2c6221a31bdb7be9c5ef791c8 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 19:13:17 +0100 Subject: [PATCH 091/137] minor fix in NewAndDeleteOperators task Signed-off-by: Evgeniy Moiseenko --- .../NewAndDeleteOperators/CMakeLists.txt | 8 ++------ .../NewAndDeleteOperators/src/book.cpp | 3 +++ .../NewAndDeleteOperators/src/task.cpp | 2 -- .../NewAndDeleteOperators/task-info.yaml | 16 ++++++++++------ 4 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/book.cpp diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt index 442e8d8..4548bc7 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/CMakeLists.txt @@ -5,15 +5,11 @@ project(ObjectOrientedProgramming-MemoryOwnership-new_and_delete_operators) set(CMAKE_CXX_STANDARD 14) # Files from `./src` directory -set(SRC src/task.cpp) +set(SRC src/task.cpp src/book.cpp) # Files from `./test` directory set(TEST test/test.cpp) -# Running learner side code -# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -add_executable(${PROJECT_NAME}-run ${SRC}) - # Running tests # Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/book.cpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/book.cpp new file mode 100644 index 0000000..155a888 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/book.cpp @@ -0,0 +1,3 @@ +#include "../include/book.hpp" + +size_t Book::counter = 0; \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp index a381091..25a6e70 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/src/task.cpp @@ -2,8 +2,6 @@ #include "../include/book.hpp" -size_t Book::counter = 0; - void newAndDeleteBook() { Book *favourite_book = new Book("Harry Potter"); delete favourite_book; diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml index 27e5b5e..605fe55 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-info.yaml @@ -3,16 +3,20 @@ custom_name: New and Delete Operators files: - name: CMakeLists.txt visible: false -- name: include/book.hpp - visible: true -- name: test/test.cpp - visible: false - name: src/task.cpp visible: true placeholders: - - offset: 110 + - offset: 83 length: 75 placeholder_text: // Create and delete Book object using new/delete - - offset: 220 + - offset: 193 length: 79 placeholder_text: // Do the same using malloc/free +- name: test/test.cpp + visible: false +- name: include/book.hpp + visible: true + editable: false +- name: src/book.cpp + visible: true + editable: false From 5351ff4ae9c3786da2a516018f42fc4786337b16 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 25 Dec 2023 22:06:49 +0100 Subject: [PATCH 092/137] reword Ownership task Signed-off-by: Evgeniy Moiseenko --- .../MemoryOwnership/Ownership/task-info.yaml | 2 +- .../MemoryOwnership/Ownership/task.md | 100 +++++++++++++----- 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml index 604ab0d..e84747f 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-info.yaml @@ -1,5 +1,5 @@ type: theory -custom_name: Memory ownership +custom_name: Ownership files: - name: CMakeLists.txt visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md index 6da7533..f20b3c5 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md @@ -1,48 +1,94 @@ -In C++, memory ownership refers to the responsibility of managing the allocation and deallocation of memory for objects. Understanding memory ownership is crucial to preventing memory leaks, avoiding double-deletions, and ensuring proper resource management. The ownership model in C++ can be broadly categorized into two types: ownership of sub-objects stored in object fields and ownership of dynamically allocated objects using pointers. +In C++, ownership refers to the responsibility of managing +the lifecycle of certain resources, +such as memory, files, network connections, objects, _etc_. +The ownership model in C++ can be broadly categorized into two kinds: +ownership of sub-objects stored in object fields +and ownership of dynamically created objects using pointers. -### Ownership of Sub-objects Stored in Object Fields +In C++, classes often contain member fields that represent sub-objects. +These sub-objects are typically owned by the parent object. +The parent object is responsible for calling their constructors and destructors, +and the memory they occupy is automatically managed by the parent object's lifecycle. -In C++, classes often contain member variables that represent sub-objects. These sub-objects are typically owned by the parent object, and their memory is automatically managed by the parent's lifecycle. When a class instance is created, its member variables are initialized, and when the instance is destroyed, the destructors of its member variables are automatically called. Note that constructors and destructors are called in the reverse order of initialization, so for the sub-object, the destructor is called before the destructor of the parent object. +
+ +Note that constructors and destructors are called in specific order, +and, moreover, the order of destruction is the reverse order of initialization. +For example, consider the following class hierarchy: -For example, consider the following class: ```c++ -class Processor { - // Processor implementation -}; +class Motherboard { /* ... */ }; +class Processor { /* ... */ }; +class RandomAccessMemory { /* ... */ }; -class Laptop { -private: - Processor laptopProcessor; // Ownership is automatically managed by Laptop +class Device { /* ... */ }; +class Laptop : public Device { +private: + // Ownership of objects stored in the fields + // is managed automatically + Motherboard motherboard; + Processor processor; + RandomAccessMemory ram; public: - // Other methods + /* ... */ }; ``` -In the example above, the Laptop class owns the Processor object as a sub-object. The Processor object's memory is automatically allocated and deallocated along with the Laptop object. +The order of construction of a `Laptop` object would be as follows: +1. the base class `Device` constructor would be called first; +2. then the sub-objects' constructors would be called: + * the `Motherboard` constructor for the field `motherboard`; + * the `Processor` constructor for the field `processor`; + * the `RandomAccessMemory` constructor for the field `ram`; +3. finally, the `Laptop` constructor would be called. + +The order of destruction is the opposite: +1. first the derived class destructor `Laptop` would be called; +2. then the sub-objects destructors would be called: + * the `RandomAccessMemory` destructor; + * the `Processor` destructor; + * the `Motherboard` destructor; +3. finally, the base class `Device` destructor is called. -### Ownership of dynamically allocated objects using pointers +
-Objects stored in pointer-typed fields are, by default, considered non-owned. When a class contains a pointer to an object, the responsibility for memory management lies outside the class. This is crucial to prevent memory leaks and undefined behaviour, as ownership is not automatically transferred with the assignment of pointers. +Objects stored in pointer-typed fields are, by default, considered to be non-owned. +When a class contains a pointer to an object, +the responsibility for lifetime and storage duration management lies outside the class. +It is crucial to understand this fact in order to prevent memory leaks and undefined behavior, +as ownership is not automatically transferred with the assignment of pointers. ```c++ class Student { private: - std::string* name; // Non-owned by default - + // Non-owned by default + Laptop* laptop; public: - Student(std::string n) : name(new std::string(std::move(n))) {} - ~Student() { - delete name; // Responsibility for cleanup is within the class - } + explicit Student(Laptop* laptop) + : laptop(laptop) {} + + // the destructor does not destroy the object + // pointed-by laptop field by default + ~Student() = default; }; - ``` -In the example above, the Student class contains a pointer to a dynamically allocated `std::string`. The ownership responsibility for the memory allocated for the name lies with the Student class, and it needs to explicitly manage the memory by deleting the name in the destructor. - -### Why objects in pointer-typed fields are non-owned by default - -C++ follows a philosophy of "you only pay for what you use", which means that C++ provides flexibility and efficiency, and ownership is not automatically assumed to be transferred with a pointer assignment. Objects in pointer-typed fields are considered non-owned by default to allow for more explicit control over memory management. This approach encourages developers to know their responsibilities regarding memory allocation and deallocation, promoting a safer and more predictable memory management model. +In the example above, the Student class contains +a pointer to a dynamically allocated `Laptop` object. +The ownership responsibility for the `Laptop` object +may or may not be assigned to the `Student` class depending on the desired semantics. +If the desired semantics is that the `Student` takes +the ownership of the `laptop` object passed to it in the constructor, +then the developer of this class must ensure that +the object is destroyed manually in the destructor +(for example, by using the `delete` operator). -In scenarios where ownership needs to be transferred, smart pointers such as `std::unique_ptr` and `std::shared_ptr` can be used to express ownership semantics more explicitly, providing automated memory management with reduced risks of memory-related issues. We will discuss smart pointers in more detail in the next lesson. \ No newline at end of file +Objects in pointer-typed fields are considered non-owned by default +to allow for more explicit control over memory management, +providing more flexibility and efficiency. +In the scenarios where ownership needs to be transferred, +smart pointers such as `std::unique_ptr` and `std::shared_ptr` +can be used to express ownership semantics explicitly, +providing automated memory management with reduced risks of memory-related issues. +We will discuss smart pointers in more detail in the next few lessons. \ No newline at end of file From b64f49c7eab47b75b24de9a41f7a09a678d7f742 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Tue, 26 Dec 2023 20:03:53 +0100 Subject: [PATCH 093/137] rework unique_ptr lesson Signed-off-by: Evgeniy Moiseenko --- .../MemoryOwnership/Ownership/task.md | 11 +- .../MemoryOwnership/UniquePtr/src/task.cpp | 27 +-- .../MemoryOwnership/UniquePtr/task-info.yaml | 22 +-- .../MemoryOwnership/UniquePtr/task.md | 183 ++++++++++++++---- .../MemoryOwnership/UniquePtr/test/test.cpp | 23 ++- 5 files changed, 199 insertions(+), 67 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md index f20b3c5..691c23c 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md @@ -87,8 +87,13 @@ the object is destroyed manually in the destructor Objects in pointer-typed fields are considered non-owned by default to allow for more explicit control over memory management, providing more flexibility and efficiency. + In the scenarios where ownership needs to be transferred, -smart pointers such as `std::unique_ptr` and `std::shared_ptr` -can be used to express ownership semantics explicitly, -providing automated memory management with reduced risks of memory-related issues. +[_smart pointers_](https://en.wikipedia.org/wiki/Smart_pointer) +such as `std::unique_ptr` and `std::shared_ptr` can be used, +providing automated memory management with reduced risks of memory-related issues. +These smart pointers are specialized classes defined in the standard library of the C++. +They behave like plain pointers, in a sense that they support the +same set of operations, like the dereferencing, +but in addition they provide specific ownership semantics. We will discuss smart pointers in more detail in the next few lessons. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp index 673ecb6..c87f2d9 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/src/task.cpp @@ -1,23 +1,26 @@ #include #include -std::unique_ptr *create_unique_ptr_array(int size) { - std::unique_ptr *array = new std::unique_ptr[size]; - for (int i = 0; i < size; i++) { - array[i] = std::make_unique(); +std::unique_ptr copy(const int* array, size_t size) { + if (size == 0) { + return nullptr; + } + std::unique_ptr ptr = std::make_unique(size); + for (size_t i = 0; i < size; ++i) { + ptr[i] = array[i]; } - return array; + return ptr; } int main() { - std::unique_ptr *array = create_unique_ptr_array(10); - for (int i = 0; i < 10; i++) { - *array[i] = i; + const size_t size = 10; + int array[size]; + for (int i = 0; i < size; i++) { + array[i] = i + 1; } - for (int i = 0; i < 10; i++) { - std::cout << *array[i] << std::endl; + std::unique_ptr ptr = copy(array, size); + for (int i = 0; i < size; i++) { + std::cout << ptr[i] << std::endl; } - delete[] array; - return 0; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml index 5543b25..4a4fc26 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-info.yaml @@ -1,13 +1,13 @@ type: edu -custom_name: unique_ptr +custom_name: Unique Pointer files: - - name: CMakeLists.txt - visible: false - - name: src/task.cpp - visible: true - placeholders: - - offset: 549 - length: 166 - placeholder_text: Create an array of unique_ptr pointers - - name: test/test.cpp - visible: false +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false +- name: src/task.cpp + visible: true + placeholders: + - offset: 104 + length: 199 + placeholder_text: return nullptr; diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md index 667e3cb..f97e1be 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md @@ -1,49 +1,166 @@ -C++ contains a tool that provides exclusive ownership semantics. [std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr) is a "smart" pointer that is designed to manage the lifetime of dynamically allocated objects. The unique ownership model ensures that at any given time, only one `std::unique_ptr` instance owns a particular dynamically allocated object. When the owning `std::unique_ptr` is destroyed or explicitly reset, it ensures the deallocation of the associated memory. +The first kind of smart pointers in C++ we are going to look at is +the [std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr). -#### Main features about std::unique_ptr ownership model -- Exclusive ownership. When a dynamically allocated object comes into the possession of `std::unique_ptr`, no `std::unique_ptr` or other smart pointer (which we will cover further) can share ownership of this object. -- The transfer of ownership occurs through move semantics (they will be discussed in the next step). When a `std::unique_ptr` is moved, the ownership is transferred, and the source `std::unique_ptr` becomes empty (`nullptr`). -- Automatic Deallocation. When a `std::unique_ptr` goes out of scope or is explicitly reset, it automatically releases the memory it owns. -- Support the correct state. `std::unique_ptr` is movable but not copyable. This aligns well with the ownership model, as copying would violate the exclusive ownership semantics. -- Nullability. `std::unique_ptr` can be in a null state (pointing to no object) by default or after a move operation. This feature allows for representing the absence of an object safely and clearly. +This smart pointer is designed to manage the lifetime of +dynamically allocated objects, and it provides exclusive ownership semantics. +The unique ownership model ensures that at any given time, +only one `std::unique_ptr` instance owns a particular dynamically allocated object. +When the owning `std::unique_ptr` is destroyed or explicitly reset, +the pointed-to object is automatically destroyed and +the associated memory is deallocated. -`std::unique_ptr` prevents memory leaks and double deletion. These two problems often happen when a programmer deals with the allocation of memory himself. `std::unique_ptr` ensures that an object will be deleted when it is no longer needed, even if an exception is thrown, and prevents an object from being deleted more than once. In addition, it slightly improves code readability: `std::unique_ptr` makes it clear which pointer owns an object and which is responsible for deleting it. +Another advantage of `std::unique_ptr` is that it helps to prevent +memory errors, such as memory leaks and double deletion. +The `std::unique_ptr` ensures that an object will be deleted +when it is no longer needed, and that it will be deleted only once. -#### Example of usage of `std::unique_ptr` +In addition, usage of `std::unique_ptr` improves code readability: +by making clear what pointer owns an object and is responsible for deleting it. -```c++ -#include -#include +Let us look at the examples. +Suppose we have a class `Dog` defined as follows. -class Cat { +```c++ +class Dog { public: - Cat() { - std::cout << "Cat appeared\n"; - } + explicit Dog(const std::string& name) : name(name) {} + ~Dog() {} - ~Cat() { - std::cout << "Cat disappeared\n"; - } - - void Meow() { - std::cout << "Meow!\n"; + std::string bark() const { + std::cout << "Woof!\n"; } +private: + std::string name; }; +``` -int main() { - // Creating a unique pointer - std::unique_ptr catPtr = std::make_unique(); +The following code snippet demonstrates how to create a new `Dog` object owned by a `unique_ptr`: - // Using the pointer - if (catPtr) { - catPtr->Meow(); +```c++ +void makeBark() { + // creating a unique pointer + std::unique_ptr dog = std::make_unique("Snoopy"); + // you can test if the unique pointer is a null pointer, + // similarly as you would do with a plain pointer + if (!dog) { + return; } - - // Deleting the object manually - // anotherCatPtr.reset(); + // unique pointer can be used + // similarly to how plain pointers are used + dog->bark(); + // when the function exits, + // the unique_ptr automatically destroys + // the Dog object and deallocated memory +} +``` + +
+ +How the automated destruction of the pointed-to object +by the `std::unique_ptr` is achieved? +In fact, the implementation of the `std::unique_ptr` class +in the standard library simply overrides the destructor of this class. - return 0; // MyClass Destructor will be called here +
+ +Alternatively, one can explicitly reset the pointer, +and thus trigger the deletion of the pointed-to object: + +```c++ +std::unique_ptr dog = std::make_unique("Snoopy"); +dog->bark(); +// at this point, the pointer will be reset to null, +// and the pointed-to object will be destroyed +dog.reset(); +assert(dog == nullptr); } ``` -Your task is to finish the implementation of `create_unique_ptr_array` function. It should create a `std::unique_ptr` with an array of `int` of size `size` and return it. \ No newline at end of file +It is also possible to transfer an existing plain pointer +to a new `std::unique_ptr`: + +```c++ +Dog* dog = new Dog("Snoopy"); +// ownership of the Dog object is taken by the unique_ptr, +// it should not be manually deleted, +// as the unique pointer itself will do that +std::unique_ptr smartDog(dog); +``` + +vice versa, it is possible to take the pointer together with the ownership +out of the `std::unique_ptr` command: + +```c++ +std::unique_ptr smartDog = std::make_unique("Snoopy"); +// Dog object is transferred to the plain pointer, +// it should be manually deleted eventually, +// because the unique pointer would not do that +Dog* dog = smartDog.release(); +// after release, the unique pointer is in the null state +assert(smartDog == nullptr); +``` + +It is possible to obtain a plain pointer without releasing the ownership. +However, the plain pointer should not outlive the unique pointer; +otherwise, it can result in a use-after-free error. + +```c++ +std::unique_ptr smartDog = std::make_unique("Snoopy"); +// now the dog and smartDog point-to the same object; +// the ownership still belongs to the smartDog pointer +Dog* dog = smartDog.get(); +``` + +
+ +The `get()` method is typically used to pass a unique pointer +into a function expecting the plain pointer. +It is often the case when C++ code needs to interact with the C libraries. + +
+ +Note that by converting plain pointers to unique pointers and vice versa, +using the methods given above, you might accidentally create +two unique pointers pointing to the same object, +thus violating the ownership rules of `std::unique_ptr`. +This situation would result in undefined behavior: + +```c++ +std::unique_ptr dog = std::make_unique("Snoopy"); +// the dog and anotherDog point-to the same object, +// and both incorrectly assume unique ownership of the object; +// thus both can attempt to destroy the object, +// leading to undefined behavior. +std::unique_ptr anotherDog(dog.get()); +``` + +This is why you should be extremely careful when converting +between plain and unique pointers. +As a rule of thumb, try to complete avoid these conversions in your code. + +By default, the `std::unique_ptr` attempts to prevent such misuses +and enforce the single ownership rule. +This is why, for example, the copy constructor of the `std::unique_ptr` is disabled: + +```c++ +std::unique_ptr dog = std::make_unique("Snoopy"); +// compilation error +std::unique_ptr anotherDog = dog; +``` + +In order to consolidate the material of this lesson, +finish the implementation of `copy` function. +This function takes as an argument an array of integers, +given as a plain pointer and a size, +and it should return a copied array as a unique pointer. +If the given array size is `0`, +then the function should return the null pointer. + +
+ +To create a unique pointer to an array, use the following syntax: +```c++ +std::make_unique(size) +``` + +
\ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp index 60a3eae..65eb313 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/test/test.cpp @@ -2,15 +2,22 @@ #include #include -std::unique_ptr *create_unique_ptr_array(int size); +std::unique_ptr copy(const int* array, size_t size); -TEST(CreateUniquePtrArrayTest, Simple) { - std::unique_ptr *array = create_unique_ptr_array(10); - for (int i = 0; i < 10; i++) { - *array[i] = i; +TEST(CopyTest, CopyEmpty) { + int a[10]; + ASSERT_EQ(nullptr, copy(a, 0)); +} + +TEST(CopyTest, Copy) { + const size_t SIZE = 10; + int a[SIZE]; + for (int i = 0; i < SIZE; ++i) { + a[i] = rand() % 100; } - for (int i = 0; i < 10; i++) { - std::cout << *array[i] << std::endl; + std::unique_ptr p = copy(a, SIZE); + for (int i = 0; i < SIZE; ++i) { + ASSERT_NE(a + i, p.get() + i); + ASSERT_EQ(a[i], p[i]); } - delete[] array; } \ No newline at end of file From 80d17cc476dda18ef4715ce8b5337dc1a71eacb8 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 27 Dec 2023 11:19:31 +0100 Subject: [PATCH 094/137] reworking move semantics task Signed-off-by: Evgeniy Moiseenko --- .../MoveSemantics/task-info.yaml | 2 +- .../MemoryOwnership/MoveSemantics/task.md | 188 +++++++++++++++--- 2 files changed, 158 insertions(+), 32 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml index b253755..59dd6da 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml @@ -1,5 +1,5 @@ type: edu -custom_name: move semantics +custom_name: Move Semantics files: - name: CMakeLists.txt visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md index bbdb5e9..e76978f 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md @@ -1,51 +1,177 @@ -[Move semantics](https://en.cppreference.com/w/cpp/language/move_constructor) is a feature of C++ that allows you to efficiently transfer ownership of an object from one variable to another without copying the object. This can be useful for improving performance and avoiding unnecessary memory allocations. Before diving into move semantics, let's briefly understand [value categories](https://en.cppreference.com/w/cpp/language/value_category) concept. +[Move semantics](https://en.cppreference.com/w/cpp/language/move_constructor) +is a feature of C++ that allows to efficiently +transfer ownership of an object without copying. +This can be useful for improving performance +and avoiding unnecessary memory allocations. +Before diving into the move semantics details, let us briefly understand the concept of +[value categories](https://en.cppreference.com/w/cpp/language/value_category). -**lvalue** reference represents an object that has a name or an identifier. It refers to something that exists in memory and typically persists beyond a single expression. It usually stands on the left side of an assignment operator (`=`), hence the name lvalue. +* __lvalue__ expression represents an object that has a name or an identifier. + It refers to something that exists in memory and typically persists beyond a single expression. + It usually stands on the left-hand side of an assignment operator (`=`), hence the name __lvalue__. -**rvalue** reference represents a temporary or disposable value. It is usually the result of an expression and might not have a named memory location. Rvalues are often short-lived. +* __rvalue__ expression represents a temporary or disposable value. + It is usually the intermediate result of some computation that might not have a named memory location. + It usually stands on the right-hand side of an assignment operator (`=`), hence the name __rvalue__. -#### Move Semantics Overview -- Efficient Resource Transfer. Move semantics allows the transfer of ownership from one object to another without copying. This is especially beneficial for large or resource-intensive objects. -- [Rvalue References](https://en.cppreference.com/w/cpp/language/rvalue_reference). Move semantics utilize rvalue references (`&&`), representing temporary objects or objects about to be destroyed. Rvalue references to facilitate the efficient transfer of ownership without unnecessary overhead. -- [`std::move`](https://en.cppreference.com/w/cpp/utility/move) Function. The `std::move` function is used to convert a lvalue (an object with a name) into a rvalue reference. This is a key tool for explicitly indicating that ownership can be moved. +For example, below variables `a`, `b`, and `c` are lvalues, +while expression `a + b` is an rvalue. -#### Move Semantics with `std::unique_ptr` -`std::unique_ptr` supports move semantics through its `std::move()` member function. The `std::move()` function transfers ownership of the object from the source `std::unique_ptr` to the destination unique_ptr. The source `std::unique_ptr` is then left in a null state. +```c++ +int a = 2; +int b = 3; +int c = a + b; +``` -Here is an example of how to use the `std::move()` function to transfer ownership of a `std::unique_ptr` object: +Move semantics utilize rvalues, +representing temporary objects or objects about to be destroyed. +When another object wants to copy a soon-to-be disposed rvalue object, +instead of actual copying, the contents of the rvalue object can be moved. -```cpp -#include +For example, recall the `int_array` class, which has a custom copy constructor: -class MyClass { +```c++ +class int_array { public: - MyClass() { - std::cout << "MyClass Constructor\n"; + /* ... */ + int_array(const dynarray& other) + : data(new int[other.size]) + , size(other.size) + { + for (size_t i = 0; i < size; ++i) { + data[i] = other.data[i]; + } + }; + /* ... */ +private: + int *data; + std::size_t size; +}; +``` + +Suppose there is a function that creates an array filled with the given value: + +```c++ +int_array create_array(int value, size_t size) { + if (size == 0) { + return int_array(); + } + int_array array = int_array(size); + for (size_t i = 0; i < size; ++i) { + array[i] = value; } + return array; +} +``` + +And then this function is called as follows: + +```c++ +// the copy constructor is called here (!) +int_array array = create_array(1, 24); +``` + +In the code above, unnecessary copying is performed, +which copies an array from an object returned from the function +into the newly created object. + +However, because the returned object is actually a temporary rvalue +which is going to be disposed anyway, +we can take advantage of that and instead of copying the array, +just _move_ the pointer. + +To do that, in addition to the copy constructor, +one can define the _move constructor_, +which takes as an argument rvalue reference denoted with `&&`: - ~MyClass() { - std::cout << "MyClass Destructor\n"; +```c++ +class int_array { +public: + /* ... */ + int_array(int_array&& other) + : data(other.data) + , size(other.size) + { + other.data = nullptr; + other.size = 0; + }; + /* ... */ +private: + int *data; + std::size_t size; +}; +``` + +Note that in addition to copying the pointer, +the move constructor of `int_array` class also nullifies +the pointer in the original object passed by rvalue reference. +It is necessary because otherwise, once the destructor +of the original object is called, it would deallocate the memory +pointed-to by `data` field. +This way, the given move constructor implementation reflects +the occurring ownership transfer. + +Similarly to copy assignment operator, +one can also define move assignment operator for a class: + +```c++ +class int_array { +public: + /* ... */ + int_array& operator=(int_array&& other) { + // ... } + /* ... */ +private: + int *data; + std::size_t size; }; +``` -int main() { - // Create two unique_ptr objects. - std::unique_ptr p1 = std::make_unique(); - std::unique_ptr p2; +With the help of the move assignment operator and +the special standard function `std::move`, +one can manually transfer the ownership from one object to another: - // Transfer ownership of p1 to p2. - p2 = std::move(p1); +```c++ +int_array a = int_array(); +int_array b = int_array(10); +// ownership transfer +a = std::move(b); +``` - // p1 is now in a null state. - assert(p1 == nullptr); +
- // p2 now owns the MyClass object. - assert(p2 != nullptr); +Technically, the `std::move` function converts an lvalue into an rvalue reference. - return 0; -} +
+ +A custom implementation of the move assignment operator +should ensure that after being moved, +the object remains in a valid state, +usually some kind of null state. +This means that it should be possible to still safely destruct the object, +but other operations such as method calls or operator +uses might lead to undefined behavior. +In general, avoid using an object after it has been moved, +except for assigning it a new value or destroying it. + +The smart pointer `std::unique_ptr` also supports move semantics. +The move semantics of this class transfers ownership of the object +from the source `std::unique_ptr` to the destination pointer. +The source `std::unique_ptr` is then left in a null state. + +```c++ +std::unique_ptr p1 = std::make_unique("Snoopy"); +std::unique_ptr p2; +// transfer ownership from p1 to p2. +p2 = std::move(p1); +// p1 is now in a null state. +assert(p1 == nullptr); +// p2 now owns the Dog object. +assert(p2 != nullptr); ``` -Move semantics can be helpful for a variety of tasks, such as passing a `std::unique_ptr` object to a function that takes ownership of the object, returning a `std::unique_ptr` object from a function and moving a `std::unique_ptr` object from one container to another. +[//]: # (TODO) -Implement functions `transfer_ownership` and `swap_ownership` using `std::move()` function, so that the code in main function compiles and runs successfully. \ No newline at end of file +[//]: # (Implement functions `transfer_ownership` and `swap_ownership` using `std::move()` function, ) +[//]: # (so that the code in main function compiles and runs successfully.) \ No newline at end of file From 790fd56fc24f965b144191f65f47fc5e4e2afc88 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 27 Dec 2023 11:55:42 +0100 Subject: [PATCH 095/137] reworking move semantics task Signed-off-by: Evgeniy Moiseenko --- .../MoveSemantics/src/task.cpp | 28 +++++-------------- .../MoveSemantics/task-info.yaml | 23 +++++++-------- .../MemoryOwnership/MoveSemantics/task.md | 8 ++++-- .../MoveSemantics/test/test.cpp | 26 ++++++----------- 4 files changed, 31 insertions(+), 54 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp index b956873..1dddce0 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp @@ -2,30 +2,16 @@ #include #include -std::unique_ptr transfer_ownership(std::unique_ptr &ptr) { - return std::unique_ptr(std::move(ptr)); -} - -void swap_ownership(std::unique_ptr& ptr1, std::unique_ptr& ptr2) { - std::unique_ptr temp(ptr1.release()); - ptr1 = std::move(ptr2); - ptr2 = std::move(temp); +void transfer(std::unique_ptr& from, std::unique_ptr& to) { + to = std::move(from); } int main() { - auto ptr1 = std::make_unique(42); - auto ptr2 = transfer_ownership(ptr1); - if (ptr1 == nullptr) { + std::unique_ptr p = std::make_unique(8); + std::unique_ptr q = std::make_unique(8); + transfer(p, q); + if (!p && q) { std::cout << "Ownership is transferred!" << std::endl; } - - auto ptr3 = std::make_unique(10); - auto ptr4 = std::make_unique(20); - - swap_ownership(ptr3, ptr4); - - std::cout << "ptr1: " << *ptr3 << std::endl; - std::cout << "ptr2: " << *ptr4 << std::endl; return 0; -} - +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml index 59dd6da..6c6d9ae 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml @@ -1,16 +1,13 @@ type: edu custom_name: Move Semantics files: - - name: CMakeLists.txt - visible: false - - name: src/task.cpp - visible: true - placeholders: - - offset: 839 - length: 44 - placeholder_text: /* TODO */ - - offset: 969 - length: 98 - placeholder_text: /* TODO */ - - name: test/test.cpp - visible: false +- name: CMakeLists.txt + visible: false +- name: src/task.cpp + visible: true + placeholders: + - offset: 136 + length: 21 + placeholder_text: /* TODO */ +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md index e76978f..4b93c36 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md @@ -171,7 +171,9 @@ assert(p1 == nullptr); assert(p2 != nullptr); ``` -[//]: # (TODO) +As a conclusion of this lesson, please implement the `transfer` function, +which should transfer an ownership from one unique pointer into another: -[//]: # (Implement functions `transfer_ownership` and `swap_ownership` using `std::move()` function, ) -[//]: # (so that the code in main function compiles and runs successfully.) \ No newline at end of file +```c++ +void transfer(std::unique_ptr& from, std::unique_ptr& to); +``` \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp index 3240cea..fa8ad10 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp @@ -1,21 +1,13 @@ #include #include -std::unique_ptr transfer_ownership(std::unique_ptr &ptr); - -void swap_ownership(std::unique_ptr& ptr1, std::unique_ptr& ptr2); - -TEST(MoveUniquePtrTest, MoveTest) { - std::unique_ptr ptr = std::make_unique(42); - std::unique_ptr new_ptr = transfer_ownership(ptr); - EXPECT_EQ(ptr.get(), nullptr); - EXPECT_EQ(*new_ptr, 42); -} - -TEST(SwapUniquePtrTest, SwapTest) { - std::unique_ptr ptr1 = std::make_unique(10); - std::unique_ptr ptr2 = std::make_unique(20); - swap_ownership(ptr1, ptr2); - EXPECT_EQ(*ptr1, 20); - EXPECT_EQ(*ptr2, 10); +void transfer(std::unique_ptr& from, std::unique_ptr& to); + +TEST(TransferTest, TransferTest) { + std::unique_ptr p = std::make_unique(8); + std::unique_ptr q = std::make_unique(8); + int* expected = p.get(); + transfer(p, q); + EXPECT_EQ(nullptr, p.get()); + EXPECT_EQ(expected, q.get()); } \ No newline at end of file From cdf356626359dedba80ee1dd850a5ab627a74ca7 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 27 Dec 2023 15:15:05 +0100 Subject: [PATCH 096/137] rework shared pointer task Signed-off-by: Evgeniy Moiseenko --- .../MemoryOwnership/MoveSemantics/task.md | 2 +- .../SharedPtr/include/chat.hpp | 61 +++++++++++ .../MemoryOwnership/SharedPtr/src/task.cpp | 67 ++++-------- .../MemoryOwnership/SharedPtr/task-info.yaml | 26 +++-- .../MemoryOwnership/SharedPtr/task.md | 100 ++++++++++++------ .../MemoryOwnership/SharedPtr/test/test.cpp | 86 +++++++-------- 6 files changed, 203 insertions(+), 139 deletions(-) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SharedPtr/include/chat.hpp diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md index 4b93c36..b552432 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md @@ -161,7 +161,7 @@ from the source `std::unique_ptr` to the destination pointer. The source `std::unique_ptr` is then left in a null state. ```c++ -std::unique_ptr p1 = std::make_unique("Snoopy"); +std::unique_ptr p1 = std::make_unique("Beethoven"); std::unique_ptr p2; // transfer ownership from p1 to p2. p2 = std::move(p1); diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/include/chat.hpp b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/include/chat.hpp new file mode 100644 index 0000000..dfb5f03 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/include/chat.hpp @@ -0,0 +1,61 @@ +#ifndef CPPBASICS_CHAT_HPP +#define CPPBASICS_CHAT_HPP + +#include +#include +#include + +class Chat { +public: + Chat(int id, std::string name) + : id(id) + , name(std::move(name)) + {}; + + inline int getId() const { + return id; + } + + inline std::string getName() const { + return name; + } +private: + int id; + std::string name; +}; + +class User { +public: + + explicit User(std::string name) + : id(nextId++) + , name(std::move(name)) + , chat(nullptr) + {}; + + inline int getId() const { + return id; + } + + inline std::string getName() const { + return name; + } + + inline const std::shared_ptr& getChat() const { + return chat; + } + + void createNewChat(std::string name); + + void joinChatByInvite(const User& user); + + void leaveChat(); +private: + int id; + std::string name; + std::shared_ptr chat; + static int nextId; + static int nextChatId; +}; + +#endif // CPPBASICS_CHAT_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp index deb1a0b..688e60c 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/src/task.cpp @@ -1,55 +1,30 @@ -#include #include -#include -struct chat{ -private: - std::string name; +#include "../include/chat.hpp" -public: - explicit chat(std::string name) : name(std::move(name)) {}; +int User::nextId = 0; +int User::nextChatId = 0; - ~chat() { - std::cout << "Chat deleted!\n"; - } -}; - -struct user { -private: - std::string name; - int id; - -public: - std::shared_ptr current_chat; - - user(std::string name, int id) : name(std::move(name)), id(id) {}; - - void create_chat(std::string chat_name) { - current_chat = std::make_shared(std::move(chat_name)); - } - - void join_chat_by_invite(user & other_user) { - current_chat = other_user.current_chat; - } +void User::createNewChat(std::string name) { + chat = std::make_shared(nextChatId++, std::move(name)); +} - void leave_chat() { - current_chat = nullptr; - } -}; +void User::joinChatByInvite(const User& user) { + chat = user.chat; +} -int user_count(std::shared_ptr & sharedPtr) { - return sharedPtr.use_count(); +void User::leaveChat() { + chat.reset(); } int main() { - user user1("Tom", 1); - user user2("Bob", 2); - user user3("Alice", 3); - user1.create_chat("Friends"); - user2.join_chat_by_invite(user1); - user3.join_chat_by_invite(user1); - std::cout << user_count(user1.current_chat) << std::endl; - user1.leave_chat(); - user2.leave_chat(); - user3.leave_chat(); -} + User bob("Bob"); + User alice("Alice"); + bob.createNewChat("C++ discussion"); + alice.joinChatByInvite(bob); + + std::cout << "Bob is currently in the chat " << bob.getChat()->getName() << "\n"; + std::cout << "Alice is currently in the chat " << alice.getChat()->getName() << "\n"; + + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml index 1d0afab..1a7690c 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-info.yaml @@ -1,9 +1,21 @@ type: edu -custom_name: shared_ptr +custom_name: Shared Pointer files: - - name: CMakeLists.txt - visible: false - - name: test/test.cpp - visible: false - - name: src/task.cpp - visible: true +- name: CMakeLists.txt + visible: false +- name: test/test.cpp + visible: false +- name: src/task.cpp + visible: true + placeholders: + - offset: 151 + length: 61 + placeholder_text: /* TODO */ + - offset: 268 + length: 17 + placeholder_text: /* TODO */ + - offset: 318 + length: 13 + placeholder_text: /* TODO */ +- name: include/chat.hpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md index 2ca140c..84cb758 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md @@ -1,48 +1,78 @@ -In contrast to `std::unique_ptr`, [`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) introduces shared ownership semantics, allowing multiple smart pointers to share control over the same dynamically allocated object. While both smart pointers share some common characteristics, they differ in several ways. In this section, we will explore the shared ownership model and compare it to the exclusive ownership model of `std::unique_ptr`. +In contrast to `std::unique_ptr`, the smart pointer class +[`std::shared_ptr`](https://en.cppreference.com/w/cpp/memory/shared_ptr) introduces +shared ownership semantics, allowing multiple smart pointers to share control +over the same dynamically allocated object. -#### Shared Ownership Model and differences between `std::unique_ptr` and `std::shared_ptr` -- Multiple Ownership. `std::shared_ptr` facilitates shared ownership, enabling several smart pointers to collectively manage the lifetime of a dynamically allocated object. This is especially useful when passing pointers to functions or storing them in containers. -- Reference Counting. Under the hood, `std::shared_ptr` implements a reference count to track the number of shared pointers pointing to the same object. The reference count is dynamically adjusted as shared pointers are created or destroyed. When the reference count reaches zero, the managed object is automatically destroyed. -- Ability to copy. While `std::unique_ptr` is movable but not copyable, `std::shared_ptr` is both movable and copyable. Copying a `std::shared_ptr` results in shared ownership and incrementation of the reference counter, and the underlying object is only deallocated when the last shared pointer releases its ownership. -- Automatic Deallocation. The memory associated with the dynamically allocated object is automatically deallocated when the last `std::shared_ptr pointing` to it is destroyed or reset. This ensures proper cleanup without explicit management. +Under the hood, `std::shared_ptr` implements a reference count to track +the number of shared pointers pointing to the same object. +The reference count is dynamically adjusted as shared pointers are created or destroyed. +When the reference count reaches zero, the managed object is automatically destroyed. + +While `std::unique_ptr` is movable but not copyable, `std::shared_ptr` is both movable and copyable. +Copying a `std::shared_ptr` results in shared ownership. +It increments the reference counter. +The underlying object is only deallocated when +the last shared pointer releases its ownership. + +Let us have a look at the example of `std::shared_ptr` usage: -#### Example of usage of `std::shared_ptr` ```c++ -#include -#include +void test() { + // creating a shared pointer + std::shared_ptr dog = std::make_shared("Pluto"); + // using the pointer + dog->bark(); + // creating a copy of the shared pointer + std::shared_ptr copy = dog; + // both dog and copy share ownership of the same object + std::cout << *dog << " " << *copy << "\n"; + // one can query the count of shared pointers + // pointing-to the given object + std::cout << "dog.use_count() = " << dog.use_count() << "\n"; + // similarly to std::unique_ptr, it is possible to obtain a plain pointer + std::cout << dog.get() << "\n"; + // when the function exits, + // destructors of both shared pointers is called, + // dropping the reference count to 0 and thus + // triggering the deallocation of the pointed-to Dog object +} +``` -class MyClass { -public: - MyClass() { - std::cout << "MyClass Constructor\n"; - } +As an exercise, let iss develop a simple chat system. +It consists of two classes: `Chat` and `User` (see file `include/chat.hpp`). +Each user has a shared pointer to the chat object it is currently logged in. +Your task is to write implementation of the following methods of the `User` class. - ~MyClass() { - std::cout << "MyClass Destructor\n"; - } +```c++ +void createNewChat(std::string name); +``` - void Display() { - std::cout << "Hello from MyClass!\n"; - } -}; +* The `createNewChat` method should create a new chat with the given name + and log in the user inside. -int main() { - // Creating a shared pointer - std::shared_ptr sharedPtr = std::make_shared(); +
- // Using the pointer - if (sharedPtr) { - sharedPtr->Display(); - } +To create a new object pointed-by a shared pointer +use the function `std::make_shared` - // Creating another shared pointer sharing ownership - std::shared_ptr anotherSharedPtr = sharedPtr; +
- // Both sharedPtr and anotherSharedPtr share ownership of the same object - std::cout << "sharedPtr.use_count() = " << sharedPtr.use_count() << '\n'; +
- return 0; // MyClass Destructor will be called here (shared ownership) -} +To assign a unique identifier to the newly create `Chat` object, +use the static field `nextChatId`. + +
+ +```c++ +void joinChatByInvite(const User& user); +``` + +* The `joinChatByInvite` method should log in the user into the chat of another user + (by re-assigning its chat pointer). + +```c++ +void leaveChat(); ``` -As an exercise, let's develop a system for controlling the number of users in the chat. The user can create a chat (which he automatically enters after creation), join the chat through another user's invitation, and exit the chat. Please complete the implementation of the user class, as well as the implementation of the user_count function, so that the code in the `main` function is executed correctly. \ No newline at end of file +* The `leaveChat` method should log out the user from the chat. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp index 022dae5..804312c 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp @@ -3,55 +3,41 @@ #include #include -struct chat{ -private: - std::string name; - -public: - explicit chat(std::string name) : name(std::move(name)) {}; - - ~chat() { - std::cout << "Chat deleted!\n"; - } -}; - -struct user { -private: - std::string name; - int id; - -public: - std::shared_ptr current_chat; - - user(std::string name, int id) : name(std::move(name)), id(id) {}; - - void create_chat(std::string chat_name) { - current_chat = std::make_shared(std::move(chat_name));; - } - - void join_chat_by_invite(user & other_user) { - current_chat = other_user.current_chat; - } - - void leave_chat() { - current_chat = nullptr; - } -}; - -int user_count(std::shared_ptr & sharedPtr); +#include "../include/chat.hpp" + +TEST(UserTest, CreateNewChatTest) { + User bob("Bob"); + bob.createNewChat("Chat"); + ASSERT_NE(nullptr, bob.getChat()); + ASSERT_EQ("Test", bob.getChat()->getName()); + ASSERT_EQ(1, bob.getChat().use_count()); + + std::shared_ptr chat = bob.getChat(); + bob.createNewChat("New Chat"); + ASSERT_NE(nullptr, bob.getChat()); + ASSERT_NE(chat, bob.getChat()); + ASSERT_NE(chat->getId(), bob.getChat()->getId()); + ASSERT_EQ("New Chat", bob.getChat()->getName()); + ASSERT_EQ(1, bob.getChat().use_count()); +} -TEST(SharedPtrTest, ChatTest) { - user user1("User1", 1); - user user2("User2", 2); - user user3("User3", 3); - user1.create_chat("name"); - user2.join_chat_by_invite(user1); - EXPECT_EQ(user_count(user1.current_chat), 2); - user1.leave_chat(); - EXPECT_EQ(user_count(user2.current_chat), 1); - user3.join_chat_by_invite(user2); - EXPECT_EQ(user_count(user3.current_chat), 2); - user2.leave_chat(); - user3.leave_chat(); - EXPECT_EQ(user1.current_chat, nullptr); +TEST(UserTest, JoinChatByInviteTest) { + User bob("Bob"); + User alice("Alice"); + bob.createNewChat("Chat"); + alice.joinChatByInvite(bob); + ASSERT_NE(nullptr, alice.getChat()); + ASSERT_EQ(bob.getChat(), alice.getChat()); + ASSERT_EQ(2, bob.getChat().use_count()); } + +TEST(UserTest, LeaveChatTest) { + User bob("Bob"); + User alice("Alice"); + bob.createNewChat("Chat"); + alice.joinChatByInvite(bob); + bob.leaveChat(); + ASSERT_EQ(nullptr, bob.getChat()); + ASSERT_NE(nullptr, alice.getChat()); + ASSERT_EQ(1, alice.getChat().use_count()); +} \ No newline at end of file From 629efd4f72374782d66238d64ba19b3f6eb0aed7 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Wed, 27 Dec 2023 21:38:17 +0100 Subject: [PATCH 097/137] rework weak pointer task Signed-off-by: Evgeniy Moiseenko --- .../CollisionsRevisited/task-info.yaml | 6 +- .../Inheritance/task-info.yaml | 6 +- .../IntroducingObjects/task-info.yaml | 6 +- .../Introduction/task-info.yaml | 6 +- .../NewChallenge/task-info.yaml | 6 +- .../NewDynamics/task-info.yaml | 6 +- .../OperatorsOverloading/task-info.yaml | 6 +- .../Polymorphism/task-info.yaml | 6 +- .../MemoryOwnership/SharedPtr/test/test.cpp | 2 - .../MemoryOwnership/WeakPtr/CMakeLists.txt | 11 ++- .../MemoryOwnership/WeakPtr/include/chat.hpp | 37 +++++++++ .../MemoryOwnership/WeakPtr/include/user.hpp | 61 ++++++++++++++ .../MemoryOwnership/WeakPtr/src/main.cpp | 70 ---------------- .../MemoryOwnership/WeakPtr/src/task.cpp | 23 ++++++ .../MemoryOwnership/WeakPtr/src/user.cpp | 13 +++ .../MemoryOwnership/WeakPtr/task-info.yaml | 36 +++++++-- .../MemoryOwnership/WeakPtr/task.md | 79 +++++++++++++++++-- .../MemoryOwnership/WeakPtr/test/test.cpp | 26 ++++++ 18 files changed, 294 insertions(+), 112 deletions(-) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/chat.hpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/user.hpp delete mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/task.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/user.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/test/test.cpp diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml index f8f450d..423d8de 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml @@ -3,9 +3,6 @@ custom_name: Collisions Revisited files: - name: src/consumable.cpp visible: true -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -44,3 +41,6 @@ files: visible: false - name: test/test.cpp visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml index f3b3ee0..e6c0982 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml @@ -3,9 +3,6 @@ custom_name: Inheritance files: - name: src/cgobject.cpp visible: true -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -44,3 +41,6 @@ files: visible: false - name: test/test.cpp visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 1413645..329091b 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -3,9 +3,6 @@ custom_name: Introducing Objects files: - name: src/gobject.cpp visible: true -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -44,3 +41,6 @@ files: visible: false - name: test/test.cpp visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml index 2860b12..15c9b54 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-info.yaml @@ -1,9 +1,6 @@ type: theory custom_name: Introduction files: -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -42,3 +39,6 @@ files: visible: true - name: CMakeLists.txt visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml index 9463e2a..68b4c68 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml @@ -3,9 +3,6 @@ custom_name: New Challenge files: - name: src/enemy.cpp visible: true -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -44,3 +41,6 @@ files: visible: false - name: test/test.cpp visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml index b81a329..1ca3e09 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml @@ -3,9 +3,6 @@ custom_name: New Dynamics files: - name: src/gobjectlist.cpp visible: true -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -44,3 +41,6 @@ files: visible: false - name: test/test.cpp visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml index 28d1e7d..d648128 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml @@ -3,9 +3,6 @@ custom_name: Operators Overloading files: - name: src/operators.cpp visible: true -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -44,3 +41,6 @@ files: visible: false - name: test/test.cpp visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml index 654cc05..3f838d5 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml @@ -5,9 +5,6 @@ files: visible: true - name: src/consumable.cpp visible: true -- name: src/main.cpp - visible: true - editable: false - name: src/engine.cpp visible: true - name: src/scenes.cpp @@ -44,3 +41,6 @@ files: visible: false - name: test/test.cpp visible: false +- name: src/main.cpp + visible: true + editable: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp index 804312c..c51ec89 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp @@ -1,7 +1,5 @@ #include #include -#include -#include #include "../include/chat.hpp" diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt index e1c2412..cbef030 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/CMakeLists.txt @@ -5,9 +5,14 @@ project(ObjectOrientedProgramming-MemoryOwnership-WeakPtr) set(CMAKE_CXX_STANDARD 14) # Files from `./src` directory -set(SRC src/main.cpp) +set(SRC src/task.cpp src/user.cpp) +# Files from `./test` directory +set(TEST test/test.cpp) -# Running learner side code # Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file +add_executable(${PROJECT_NAME}-run ${SRC}) + +# Running tests +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/chat.hpp b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/chat.hpp new file mode 100644 index 0000000..008cf3a --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/chat.hpp @@ -0,0 +1,37 @@ +#ifndef CPPBASICS_CHAT_HPP +#define CPPBASICS_CHAT_HPP + +#include +#include +#include + +class User; + +class Chat { +public: + Chat(int id, std::string name, const std::shared_ptr& owner) + : id(id) + , name(std::move(name)) + , host(owner) + {}; + + inline int getId() const { + return id; + } + + inline std::string getName() const { + return name; + } + + inline std::shared_ptr getHost() const { + return host.lock(); + } +private: + int id; + std::string name; + std::weak_ptr host; +}; + +std::shared_ptr createNewChat(std::string name, const std::shared_ptr& host); + +#endif // CPPBASICS_CHAT_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/user.hpp b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/user.hpp new file mode 100644 index 0000000..9926b41 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/include/user.hpp @@ -0,0 +1,61 @@ +#ifndef CPPBASICS_USER_HPP +#define CPPBASICS_USER_HPP + +#include +#include +#include + +#include "../include/chat.hpp" + +class User { +public: + explicit User(std::string name) + : id(nextId++) + , name(std::move(name)) + , chat(nullptr) + { + counter++; + }; + + ~User() { + counter--; + } + + User(const User&) = default; + User(User&&) = default; + + User& operator=(const User&) = default; + User& operator=(User&&) = default; + + inline int getId() const { + return id; + } + + inline std::string getName() const { + return name; + } + + inline const std::shared_ptr& getChat() const { + return chat; + } + + inline static int getUserCount() { + return counter; + } + + void joinChatByInvite(const User& user); + + void leaveChat(); + + friend std::shared_ptr createNewChat(std::string name, const std::shared_ptr& host); +private: + int id; + std::string name; + std::shared_ptr chat; + static int nextId; + static int nextChatId; + static int counter; +}; + + +#endif // CPPBASICS_USER_HPP \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp deleted file mode 100644 index 5de8198..0000000 --- a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/main.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include - -class Laptop; - -class Student : public std::enable_shared_from_this { -public: - explicit Student(int studentID) : studentID(studentID) {} - - int GetStudentID() const { - return studentID; - } - - void SetLaptop(const std::shared_ptr& laptop_pointer) { - laptop = laptop_pointer; - } - - std::shared_ptr GetLaptop() const { - return laptop; - } - -private: - int studentID; - std::shared_ptr laptop; -}; - -class Laptop { -public: - explicit Laptop(const std::shared_ptr& owner) : owner(owner) {} - - void DisplayInfo() const { - if (auto lockedOwner = owner.lock()) { - std::cout << "Laptop belongs to Student ID: " << lockedOwner->GetStudentID() << std::endl; - } else { - std::cout << "Laptop owner has been released.\n"; - } - } - -private: - std::weak_ptr owner; -}; - -int main() { - auto student1 = std::make_shared(1); - auto student2 = std::make_shared(2); - - // Create a laptop with an owner (student1) - auto laptop = std::make_shared(student1); - - // Set the laptop for student1 - student1->SetLaptop(laptop); - - // Display laptop owner info - std::cout << "Initial Laptop Owner Info:\n"; - laptop->DisplayInfo(); - - // Student2 borrows the laptop - student2->SetLaptop(laptop); - - // Display laptop owner info after borrowing - std::cout << "\nAfter Student2 Borrowed the Laptop:\n"; - laptop->DisplayInfo(); - - // Display laptop owner info after student1 release - student1.reset(); // Release student1 - std::cout << "\nAfter Student1 Released the Laptop:\n"; - laptop->DisplayInfo(); - - return 0; -} diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/task.cpp new file mode 100644 index 0000000..cf8cfeb --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/task.cpp @@ -0,0 +1,23 @@ +#include +#include + +#include "../include/chat.hpp" +#include "../include/user.hpp" + +std::shared_ptr createNewChat(std::string name, const std::shared_ptr& host) { + host->chat = std::make_shared(User::nextChatId++, std::move(name), host); + return host->chat; +} + +int main() { + std::shared_ptr bob = std::make_shared("Bob"); + std::shared_ptr alice = std::make_shared("Alice"); + std::shared_ptr chat = createNewChat("C++ discussion", bob); + alice->joinChatByInvite(*bob); + + std::cout << "Bob is currently in the chat " << bob->getChat()->getName() << "\n"; + std::cout << "Alice is currently in the chat " << alice->getChat()->getName() << "\n"; + std::cout << "Host of the chat " << chat->getName() << "is " << chat->getHost() << "\n"; + + return 0; +} diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/user.cpp b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/user.cpp new file mode 100644 index 0000000..6d69f31 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/src/user.cpp @@ -0,0 +1,13 @@ +#include "../include/user.hpp" + +int User::nextId = 0; +int User::nextChatId = 0; +int User::counter = 0; + +void User::joinChatByInvite(const User& user) { + chat = user.chat; +} + +void User::leaveChat() { + chat.reset(); +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml index 7b98137..e8c982f 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-info.yaml @@ -1,7 +1,31 @@ -type: theory -custom_name: weak_ptr +type: edu +custom_name: Weak Pointer files: - - name: CMakeLists.txt - visible: false - - name: src/main.cpp - visible: true +- name: CMakeLists.txt + visible: false +- name: src/user.cpp + visible: true + editable: false +- name: include/user.hpp + visible: false + editable: false +- name: include/chat.hpp + visible: true + placeholders: + - offset: 273 + length: 13 + placeholder_text: ", host(owner)" + - offset: 481 + length: 19 + placeholder_text: return host; + - offset: 554 + length: 25 + placeholder_text: std::shared_ptr host; +- name: src/task.cpp + visible: true + placeholders: + - offset: 197 + length: 102 + placeholder_text: return nullptr; +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md index 713c901..80688ed 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md @@ -1,9 +1,74 @@ -In C++ memory ownership, [std::weak_ptr](https://en.cppreference.com/w/cpp/memory/weak_ptr) stands out as a tool for managing transient ownership without affecting the object's lifetime. While `std::shared_ptr` enables shared ownership, `std::weak_ptr` complements it by providing a non-intrusive observer-like role; this pointer does not contribute to the object's reference count, allowing for the detection of object state without extending its lifetime. +In C++ memory ownership, +[std::weak_ptr](https://en.cppreference.com/w/cpp/memory/weak_ptr) stands out as a tool +for managing transient ownership without affecting the object's lifetime. +While `std::shared_ptr` enables shared ownership, +`std::weak_ptr` complements it by providing a non-intrusive observer-like role. +This pointer does not contribute to the object's reference count, +allowing for the detection of object state without extending its lifetime. -#### Distinctive Features of std::weak_ptr and common usage: -- Non-Intrusive Observation. `std::weak_ptr` allows for the observation of an object without actively participating in its ownership. Unlike `std::shared_ptr`, it doesn't contribute to the reference count, and its existence doesn't affect the lifetime of the shared object. -- Breaking Circular References. In scenarios where circular dependencies among objects lead to reference cycles, using std::weak_ptr can help break the cycle. This ensures that objects are properly deallocated when they are no longer in use, even in the presence of circular references. As an example, consider a scenario where two objects, `A` and `B`, hold `std::shared_ptr` instances to each other. In this case, the reference count of each object will never reach zero, and they will never be deallocated. However, if one of the objects holds a `std::weak_ptr` to the other, the reference count will reach zero when the other object is destroyed, and the object will be deallocated. -- Locking for Access. To access the shared object, a `std::weak_ptr` must be converted to a `std::shared_ptr` using the `lock()` member function. If the shared object still exists, this operation succeeds, allowing safe access. Otherwise, it returns an empty `std::shared_ptr`. -- Observer Pattern. `std::weak_ptr` is often used in conjunction with `std::shared_ptr` to implement the observer pattern. Objects can register observers using `std::weak_ptr`, and these observers can check the object's state before attempting to access it. +The main use case of `std::weak_ptr` is to break the circular reference cycles, +which can lead to memory leaks otherwise. -Code in `main.cpp` demonstrates the use of `std::weak_ptr` to avoid circular references when managing ownership of a `Laptop` by a `Student`. A laptop is created with one student owner and shared with another student. When the original owner is released, the laptop's weak pointer to the owner becomes invalid. \ No newline at end of file +Going back the chat example from the previous lesson, +suppose we want to extend the `Chat` class +and add a possibility to assign a host to the chat. + +For this purpose, we might rework the `Chat` class as follows +(also see `include/chat.hpp` file): + +```c++ +class Chat { +public: + Chat(int id, std::string name, const std::shared_ptr& owner) + : id(id) + , name(std::move(name)) + , host(owner) + {}; + + /* ... */ +private: + int id; + std::string name; + std::shared_ptr host; +}; +``` + +Also, now, instead of `User` method `createNewChat`, +we would declare a function with the same name: + +```c++ +std::shared_ptr createNewChat(std::string name, const std::shared_ptr& host); +``` + +This function should take the name of the chat to be created, +as long as the shared pointer to the user who would become its host. + +Now, suppose this function creates shared pointer `chat`, +pointing to a new `Chat` object, then assigns pointer `chat->host` to `host`, +and `host->chat` to `chat`. +This would result in a reference cycle. +As long as `chat` and `host` store the shared pointers to each other, +both their reference counters cannot drop below `1`. +It means that the object will never be deallocated — +in other words, we got the memory leak! + +In order to avoid this, we need to use `std::weak_ptr` to break the reference cycle. +In particular, the `host` field of the `Chat` object should be declared as a weak pointer. + +Note that the method `getHost` of the `Chat` class should still return +the shared pointer: + +```c++ +inline std::shared_ptr getHost() const; +``` + +To achieve this, you need to use the `lock()` method of the `std::weak_ptr`. +This method creates new `std::shared_ptr` pointing to the same object +as the given `std::weak_ptr`, if it still exists; +otherwise, it returns an empty `std::shared_ptr`. + +To finish this lesson, please fix the implementation of +the `Chat` class (see file `include/chat.hpp`) +by using `std::weak_ptr` instead of the `std::shared_ptr` for the `host` field. +Also, provide an implementation of the `createNewChat` function +(see file `task.cpp` ). \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/test/test.cpp new file mode 100644 index 0000000..ef5134f --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/test/test.cpp @@ -0,0 +1,26 @@ +#include +#include + +#include "../include/chat.hpp" +#include "../include/user.hpp" + +TEST(ChatTest, HostTest) { + std::shared_ptr bob = std::make_shared("Bob"); + std::shared_ptr alice = std::make_shared("Alice"); + std::shared_ptr chat = createNewChat("C++ discussion", bob); + alice->joinChatByInvite(*bob); + + ASSERT_EQ(chat, bob->getChat()); + ASSERT_EQ(chat, alice->getChat()); + + ASSERT_EQ(bob, chat->getHost()); + ASSERT_EQ(2, User::getUserCount()); + + alice.reset(); + ASSERT_EQ(bob, chat->getHost()); + ASSERT_EQ(1, User::getUserCount()); + + bob.reset(); + ASSERT_EQ(nullptr, chat->getHost()); + ASSERT_EQ(0, User::getUserCount()); +} \ No newline at end of file From 7ba2a09bdde72234afa47b1782a1d61edf41fd8a Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 18:27:57 +0100 Subject: [PATCH 098/137] reworking RAII task Signed-off-by: Evgeniy Moiseenko --- .../RAIICopySwapIdiom/src/dynarray.h | 35 --- .../RAIICopySwapIdiom/src/int_array.hpp | 79 +++++++ .../RAIICopySwapIdiom/src/task.cpp | 2 +- .../RAIICopySwapIdiom/task-info.yaml | 24 +- .../MemoryOwnership/RAIICopySwapIdiom/task.md | 220 ++++++++++++------ .../RAIICopySwapIdiom/test/test.cpp | 2 +- 6 files changed, 237 insertions(+), 125 deletions(-) delete mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/int_array.hpp diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h deleted file mode 100644 index f0b5188..0000000 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/dynarray.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef CPPBASICS_DYNARRAY_H -#define CPPBASICS_DYNARRAY_H - -#include - -class dynarray { -private: - int *m_data = nullptr; - std::size_t m_size = 0; - -public: - dynarray(); - - dynarray(std::size_t size); - - dynarray(const dynarray &other); - - dynarray(dynarray &&other); - - dynarray &operator=(dynarray other); - - ~dynarray(); - - std::size_t size() const; - - int &operator[](std::size_t i); - - const int &operator[](std::size_t i) const; - - void swap(dynarray &other); - - friend std::ostream &operator<<(std::ostream &os, const dynarray &array); -}; - -#endif //CPPBASICS_DYNARRAY_H diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/int_array.hpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/int_array.hpp new file mode 100644 index 0000000..0732ef1 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/int_array.hpp @@ -0,0 +1,79 @@ +#ifndef CPPBASICS_INT_ARRAY_HPP +#define CPPBASICS_INT_ARRAY_HPP + +#include +#include + +class int_array { +public: + int_array() + : data_(nullptr) + , size_(0) + {}; + + explicit int_array(std::size_t size) + : data_(new int[size]) + , size_(size) + {}; + + ~int_array() { + delete[] data_; + } + + int_array(const int_array& other) + : data_(new int[other.size_]) + , size_(other.size_) + { + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } + }; + + int_array(int_array&& other) + : data_(other.data_) + , size_(other.size_) + { + other.data_ = nullptr; + other.size_ = 0; + }; + + int_array& operator=(int_array other) { + swap(other); + return *this; + } + + std::size_t size() const { + return size_; + } + + int& operator[](std::size_t i) { + return data_[i]; + } + + const int& operator[](std::size_t i) const { + return data_[i]; + } + + void swap(int_array& other) { + std::swap(data_, other.data_); + std::swap(size_, other.size_); + } + + friend std::ostream& operator<<(std::ostream &os, const int_array& array) { + os << "[ "; + for (std::size_t i = 0; i < array.size(); ++i) { + os << array[i]; + if (i != array.size() - 1) { + os << ", "; + } + } + os << " ]"; + return os; + } + +private: + int* data_; + size_t size_; +}; + +#endif // CPPBASICS_INT_ARRAY_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp index 7612acc..809a6d7 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp @@ -1,4 +1,4 @@ -#include "dynarray.h" +#include "int_array.hpp" // implement dynarray here dynarray::dynarray() : m_data(nullptr), m_size(0) {} diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml index efe576f..99f3a6b 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml @@ -1,15 +1,15 @@ type: edu custom_name: RAII & Copy-Swap Idiom files: - - name: CMakeLists.txt - visible: false - - name: test/test.cpp - visible: false - - name: src/dynarray.h - visible: true - - name: src/task.cpp - visible: true - placeholders: - - offset: 50 - length: 1101 - placeholder_text: Use the interface given to you in dynamic_array.h +- name: CMakeLists.txt + visible: false +- name: src/int_array.hpp + visible: true +- name: src/task.cpp + visible: true + placeholders: + - offset: 50 + length: 1101 + placeholder_text: Use the interface given to you in dynamic_array.h +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md index ca53062..2fc0058 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md @@ -1,86 +1,154 @@ -The last thing we need to discuss is a few small but fundamental concepts in C++ that will help you work correctly with the objects you create. +Concluding the presentation of the ownership model in the C++ language, +we need to discuss several related essential concepts and idioms. -**RAII** stands for *Resource Acquisition Is Initialization* and is pronounced as '*ray*'. It is one of the most essential idioms in modern C++ and is the best way to manage resources safely. It consists of two parts: +[_Resource Acquisition Is Initialization_](https://en.cppreference.com/w/cpp/language/raii), +__RAII__ for short, is a programming technique which involves linking a resource +(such as dynamically allocated memory block, open file, network connection, _etc._) +to the lifetime of an object. +According to this idiom: -1. The constructor of the class should allocate all the resources that the object will use during its lifetime. If errors occur during the execution of the constructor, the destructor will automatically be called, releasing all the resources allocated at the time of the error. For example, creating a container that contains `1'000'000` elements of the `int` type is possible when creating the same container for `100'000'000'000` elements will always fill all memory, which will throw an exception (for example, [`std::bad_alloc`](https://en.cppreference.com/w/cpp/memory/new/bad_alloc)). Otherwise, it can be formulated as "The constructor is completed if and only if it was possible to ensure the invariant of the object and capture all the necessary resources". -2. The destructor of the class should release all the resources that the object used during its lifetime. For example, if the constructor manually allocates memory, the destructor should free it. It is considered a main part of the idiom and is usually the only part assumed when talking about RAII. +1. the constructor of the class should allocate and initialize + all the resources that the object will own during its lifetime; +2. the copy constructor (and copy assignment operator) of the class + should copy the resource, + if the resource is not copyable, then this constructor should be deleted; +3. the move constructor (and move assignment operator) of the class + should transfer the ownership of the resource; +4. the destructor of the class should release all the resources that the object owns. -In this example we will use a [`std::vector`](https://en.cppreference.com/w/cpp/container/vector), which is a dynamic array. We will cover it in the next module, but for now, we will use it as an example of RAII. Let's look at the following code: -```cpp -#include +[//]: # (TODO: mention `= default` and `= delete` syntax) -int main() { - { - std::vector v(1'000'000); - // Invariant: size of vector is 1'000'000, memory allocation succeeded. - v[999'999] = 123; // OK - } - - try { - std::vector v(100'000'000'000); - // Memory allocation failed, invariant is broken. - v[99'999'999'999] = 123; // won't be executed, because of exception - } catch (std::bad_alloc &) { - std::cout << "caught bad_alloc\n"; - } +[**Rule of five**](https://en.cppreference.com/w/cpp/language/rule_of_three) +is a related concept, which states that if a class requires a custom +1. destructor +2. copy constructor +3. copy assignment operator +4. move constructor +5. move assignment operator +then it actually requires all five. + +[//]: # (TODO: mention rule of 3) + +Let us revisit the `int_array` class to see how it fits into RAII and the rule of five +(see complete definition of the class in the file `include/int_array.hpp`). +This class manages a resource — a dynamically allocated array of integers. +The lifecycle of this array is bound to the lifetime of the `int_array` object. + +The default constructor creates an empty array: + +```c++ +int_array() + : data_(nullptr) + , size_(0) +{}; +``` + +Another way to look at the default constructor +is as if it creates an array in special "empty" state — +this point of view will become handy later. + +Another constructor of the class creates an array of the given size +by actually allocating the memory for it: + +```c++ +explicit int_array(std::size_t size) + : data_(new int[size]) + , size_(size) +{}; +``` + +As such, the destructor has to deallocate this memory: + +```c++ +~int_array() { + delete[] data_; } ``` -**Rule of five** is a rule created for proper work with resources in C++. If you need to write one of the following methods, then you should write all five: -1. Destructor -2. Copy constructor -3. Copy assignment operator -4. Move constructor -5. Move assignment operator - -It expands the RAII idiom and allows you to work correctly with resources in C++. It was previously called **Rule of three** because before the introduction of move semantics in C++11, there was no need to write move constructor and move assignment operator. - -And now, as we covered the first two, let's talk about the **Copy-and-Swap idiom**. It is a technique that utilizes the `std::swap` function to make the copy assignment operator safe. This usually boils down to swapping contents of both operands, then returning an instance of `*this`. It is used to implement the copy assignment operator in the Rule of five. - -The simple implementation of RAII with Copy-and-Swap idiom looks like this: -```cpp -class RAIIexample { -public: - RAIIexample(std::size_t size) : m_size(size) { - m_data = new int[size]; - } - - // Copy constructor - RAIIexample(const RAIIexample & other) : RAIIexample(other.m_size) { - std::copy(other.m_data, other.m_data + m_size, m_data); - } - - // Move constructor - RAIIexample(RAIIexample && other) { - m_data = other.m_data; - other.m_data = nullptr; - } - - // Copy assignment operator - RAIIexample& operator=(const RAIIexample & other) { - return *this = RAIIexample(other); - } - - // Move assignment operator - RAIIexample& operator=(RAIIexample && other) noexcept { - std::swap(m_data, other.m_data); - return *this; - } - - // Destructor - ~RAIIexample() { - delete[] m_data; - } -private: - int* m_data; - std::size_t m_size; +
+ Note that we do not check for the null pointer, + as deleting `nullptr` is a safe operation that has no effect. +
+ +Now, the rule of five dictates that the class should also define +custom copy constructor, move constructor, as well as copy and move assignment operators. +We have already seen both of these in the previous lessons: + +```c++ +int_array(const int_array& other) + : data_(new int[other.size_]) + , size_(other.size_) + { + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } + }; + +int_array(int_array&& other) + : data_(other.data_) + , size_(other.size_) +{ + other.data_ = nullptr; + other.size_ = 0; }; ``` -To consolidate all the knowledge gained in this module, let's write our implementation of an array with a dynamic size. You don't need support size changing after the creation of the array. -Take a look at the `dynarray.h` file. You need to implement all the methods that are declared there. To write an implementation, use `dynarray::` as a prefix for all methods. Please use **RAII**, **Rule of five** and **Copy-and-Swap idiom** in your implementation. Besides two constructors and all methods that should be by Rule of five, you need to implement the following methods: -1. `std::size_t size() const` – returns the number of elements in the array. -2. `int &operator[](std::size_t i)` – returns a reference to the element at position `i` in the array. -3. `const int &operator[](std::size_t i) const` – returns a const reference to the element at position `i` in the array. -4. `void swap(dynarray &other)` – swaps the contents of the array with the contents of `other`. You'll need to implement it for the Copy-and-Swap idiom. -5. `friend std::ostream &operator<<(std::ostream &os, const dynarray &arr)` – prints the contents of the array to the output stream `os`. Let's print the array without any brackets and commas, like this: `1 2 3 4 5`. And don't forget to print a newline character at the end of the output. +Notice how the move constructor resets the argument object into an "empty" state. +Calling the destructor on this "empty" object later will effectively have no effect. +Validly so, as the ownership of the resource has been transferred to the new object. + +Next, we need to define the assignment operators. +To avoid code duplication between copy constructor and copy assignment operator +(and similarly between move constructor and move assignment operator) +it is possible to use another clever trick called +the [__Copy-and-Swap idiom__](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap). + +With the help of this idiom, it is sufficient to define +just a single version of assignment operator, +which should take the argument by value: + +```c++ +int_array& operator=(int_array other) { + swap(other); + return *this; +} +``` + +Depending on whether the operator is called with +an lvalue (taken as `const int_array&`) or rvalue (taken as `int_array&&`) +as an argument, the copy constructor or move constructor +will create a local copy (`int_array`) of the data in the `other` variable. +Then, with the help of the swap function (see definition below), +the assignment operator swaps the data of the current object with the data of the local copy. +The temporary local copy then destructs, releasing the old data along the way. + +The `swap` function performs an exchange of the data between two objects, +with the help of the standard function `std::swap` capable of swapping the variables of primitive types: + +```c++ +void swap(int_array& other) { + std::swap(data_, other.data_); + std::swap(size_, other.size_); +} +``` + +This way, the `int_array` class implements the _RAII_ principle, +following the _rule of five_ and the _copy-and-swap_ idiom. + +In conclusion, you might have a look at the implementation of other methods of +the `int_array` class in the `include/int_array.hpp` file. +In particular, you might find interesting: + +```c++ +int& operator[](std::size_t i); +const int& operator[](std::size_t i) const; +``` + +* the two versions of custom array subscript operator `operator[]` + for a mutable and constant `int_array` object; + +```c++ +std::ostream& operator<<(std::ostream &os, const int_array& array) +``` + +* the printing operator `operator<<`. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp index 07c14ff..62b19f9 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp @@ -1,5 +1,5 @@ #include -#include "../src/dynarray.h" +#include "../src/int_array.hpp" TEST(DynarrayTest, DefaultConstructor) { dynarray arr; From 31a38a2f2b105f408aacc27dd439e2ccda555ecb Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 18:29:41 +0100 Subject: [PATCH 099/137] reworking RAII task Signed-off-by: Evgeniy Moiseenko --- .../MemoryOwnership/MoveSemantics/task.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md index b552432..99dbceb 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md @@ -34,18 +34,18 @@ For example, recall the `int_array` class, which has a custom copy constructor: class int_array { public: /* ... */ - int_array(const dynarray& other) - : data(new int[other.size]) - , size(other.size) + int_array(const int_array& other) + : data_(new int[other.size_]) + , size_(other.size_) { - for (size_t i = 0; i < size; ++i) { - data[i] = other.data[i]; + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; } }; /* ... */ private: - int *data; - std::size_t size; + int* data_; + size_t size_; }; ``` @@ -89,16 +89,16 @@ class int_array { public: /* ... */ int_array(int_array&& other) - : data(other.data) - , size(other.size) + : data_(other.data_) + , size_(other.size_) { - other.data = nullptr; - other.size = 0; + other.data_ = nullptr; + other.size_ = 0; }; /* ... */ private: - int *data; - std::size_t size; + int* data_; + size_t size_; }; ``` From d6eec3f791cd4eb46977ab3bad8e7602272e5c07 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 18:55:27 +0100 Subject: [PATCH 100/137] reworking RAII task Signed-off-by: Evgeniy Moiseenko --- .../RAIICopySwapIdiom/CMakeLists.txt | 12 +- .../{src => include}/int_array.hpp | 4 + .../RAIICopySwapIdiom/src/main.cpp | 44 +++++++ .../RAIICopySwapIdiom/src/task.cpp | 62 ---------- .../RAIICopySwapIdiom/task-info.yaml | 15 +-- .../RAIICopySwapIdiom/test/test.cpp | 108 ------------------ 6 files changed, 55 insertions(+), 190 deletions(-) rename ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/{src => include}/int_array.hpp (94%) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp delete mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp delete mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt index 143bfbe..8d1909c 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/CMakeLists.txt @@ -5,16 +5,8 @@ project(ObjectOrientedProgramming-MemoryOwnership-RAIICopySwapIdiom) set(CMAKE_CXX_STANDARD 14) # Files from `./src` directory -set(SRC src/task.cpp) - -# Files from `./test` directory -set(TEST test/test.cpp) - +set(SRC src/main.cpp) # Running learner side code # Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -add_executable(${PROJECT_NAME}-run ${SRC}) - -# Running tests -# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/int_array.hpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/include/int_array.hpp similarity index 94% rename from ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/int_array.hpp rename to ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/include/int_array.hpp index 0732ef1..057b15e 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/int_array.hpp +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/include/int_array.hpp @@ -60,6 +60,10 @@ class int_array { } friend std::ostream& operator<<(std::ostream &os, const int_array& array) { + if (array.size() == 0) { + os << "[]"; + return os; + } os << "[ "; for (std::size_t i = 0; i < array.size(); ++i) { os << array[i]; diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp new file mode 100644 index 0000000..f796911 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp @@ -0,0 +1,44 @@ +#include "../include/int_array.hpp" + +int main() { + + std::cout << "create an empty array 'a':\n"; + int_array a; + std::cout << "a = " << a << "\n"; + std::cout << "\n"; + + std::cout << "create a non-empty array 'b':\n"; + int_array b = int_array(10); + for (size_t i = 0; i < b.size(); ++i) { + b[i] = (int) i + 1; + } + std::cout << "b = " << b << "\n"; + std::cout << "\n"; + + std::cout << "create a copy `c` of array 'b':\n"; + int_array c = b; + std::cout << "c = " << c << "\n"; + std::cout << "\n"; + + std::cout << "modify `c`, check that the original 'b' is left unchanged:\n"; + for (size_t i = 0; i < b.size(); ++i) { + c[i] = -c[i]; + } + std::cout << "b = " << b << "\n"; + std::cout << "c = " << c << "\n"; + std::cout << "\n"; + + std::cout << "move data from `c` to 'b':\n"; + b = std::move(c); + std::cout << "b = " << b << "\n"; + std::cout << "c = " << c << "\n"; + std::cout << "\n"; + + std::cout << "move data from `b` to 'a':\n"; + a = std::move(b); + std::cout << "a = " << a << "\n"; + std::cout << "b = " << b << "\n"; + std::cout << "\n"; + + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp deleted file mode 100644 index 809a6d7..0000000 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/task.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "int_array.hpp" - -// implement dynarray here -dynarray::dynarray() : m_data(nullptr), m_size(0) {} - -dynarray::dynarray(const std::size_t size) : m_data(new int[size]), m_size(size) {} - -dynarray::dynarray(const dynarray &other) : m_data(new int[other.m_size]), m_size(other.m_size) { - std::copy(other.m_data, other.m_data + m_size, m_data); -} - -dynarray::dynarray(dynarray &&other) : m_data(other.m_data), m_size(other.m_size) { - other.m_data = nullptr; - other.m_size = 0; -} - -void dynarray::swap(dynarray &other) { - std::swap(m_data, other.m_data); - std::swap(m_size, other.m_size); -} - -dynarray &dynarray::operator=(dynarray other) { - swap(other); - return *this; -} - -dynarray::~dynarray() { - delete[] m_data; -} - -std::size_t dynarray::size() const { - return m_size; -} - -int &dynarray::operator[](const std::size_t i) { - return m_data[i]; -} - -const int &dynarray::operator[](const std::size_t i) const { - return m_data[i]; -} - -std::ostream &operator<<(std::ostream &os, const dynarray &array) { - for (std::size_t i = 0; i < array.size(); ++i) { - os << array[i] << " "; - } - os << std::endl; - return os; -} - -int main() { - dynarray array(5); - dynarray copy_arr = array; - for (int i = 0; i < array.size(); ++i) { - array[i] = i; - } - for (int i = 0; i < copy_arr.size(); ++i) { - copy_arr[i] = i + 1; - } - std::cout << array; - std::cout << copy_arr; -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml index 99f3a6b..733ada0 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml @@ -1,15 +1,10 @@ -type: edu -custom_name: RAII & Copy-Swap Idiom +type: theory +custom_name: RAII & Copy-and-Swap Idiom files: - name: CMakeLists.txt visible: false -- name: src/int_array.hpp +- name: include/int_array.hpp visible: true -- name: src/task.cpp + editable: false +- name: src/main.cpp visible: true - placeholders: - - offset: 50 - length: 1101 - placeholder_text: Use the interface given to you in dynamic_array.h -- name: test/test.cpp - visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp deleted file mode 100644 index 62b19f9..0000000 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/test/test.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include "../src/int_array.hpp" - -TEST(DynarrayTest, DefaultConstructor) { - dynarray arr; - EXPECT_EQ(arr.size(), 0); -} - -TEST(DynarrayTest, ConstructorWithSize) { - const std::size_t size = 5; - dynarray arr(size); - EXPECT_EQ(arr.size(), size); -} - -TEST(DynarrayTest, CopyConstructor) { - dynarray arr1(3); - arr1[0] = 1; - arr1[1] = 2; - arr1[2] = 3; - - dynarray arr2(arr1); - EXPECT_EQ(arr2.size(), 3); - EXPECT_EQ(arr2[0], 1); - EXPECT_EQ(arr2[1], 2); - EXPECT_EQ(arr2[2], 3); -} - -TEST(DynarrayTest, MoveConstructor) { - dynarray arr1(3); - arr1[0] = 1; - arr1[1] = 2; - arr1[2] = 3; - - dynarray arr2(std::move(arr1)); - EXPECT_EQ(arr1.size(), 0); // arr1 is moved from, so it should be empty - EXPECT_EQ(arr2.size(), 3); - EXPECT_EQ(arr2[0], 1); - EXPECT_EQ(arr2[1], 2); - EXPECT_EQ(arr2[2], 3); -} - -TEST(DynarrayTest, CopyAssignmentOperator) { - dynarray arr1(3); - arr1[0] = 1; - arr1[1] = 2; - arr1[2] = 3; - - dynarray arr2; - arr2 = arr1; - EXPECT_EQ(arr2.size(), 3); - EXPECT_EQ(arr2[0], 1); - EXPECT_EQ(arr2[1], 2); - EXPECT_EQ(arr2[2], 3); -} - -TEST(DynarrayTest, MoveAssignmentOperator) { - dynarray arr1(3); - arr1[0] = 1; - arr1[1] = 2; - arr1[2] = 3; - - dynarray arr2; - arr2 = std::move(arr1); - EXPECT_EQ(arr1.size(), 0); // arr1 is moved from, so it should be empty - EXPECT_EQ(arr2.size(), 3); - EXPECT_EQ(arr2[0], 1); - EXPECT_EQ(arr2[1], 2); - EXPECT_EQ(arr2[2], 3); -} - -TEST(DynarrayTest, Destructor) { - dynarray *arr = new dynarray(5); - delete arr; // Destructor should be called without memory leaks -} - -TEST(DynarrayTest, Size) { - dynarray arr(4); - EXPECT_EQ(arr.size(), 4); -} - -TEST(DynarrayTest, SubscriptOperatorNonConst) { - dynarray arr(3); - arr[0] = 10; - arr[1] = 20; - arr[2] = 30; - - EXPECT_EQ(arr[0], 10); - EXPECT_EQ(arr[1], 20); - EXPECT_EQ(arr[2], 30); -} - -TEST(DynarrayTest, OutputOperator) { - dynarray arr(3); - arr[0] = 1; - arr[1] = 2; - arr[2] = 3; - - testing::internal::CaptureStdout(); // Redirect std::cout for testing - std::cout << arr; - std::string output = testing::internal::GetCapturedStdout(); - - EXPECT_EQ(output, "1 2 3 \n"); -} - -int main(int argc, char **argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} From 0b319707b95d19de45e3a8c71da709d2f9dccb59 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 19:53:56 +0100 Subject: [PATCH 101/137] add copy constructor task Signed-off-by: Evgeniy Moiseenko --- .../CopyConstructor/CMakeLists.txt | 13 ++ .../CopyConstructor/include/int_array.hpp | 78 +++++++++++ .../CopyConstructor/src/main.cpp | 41 ++++++ .../CopyConstructor/task-info.yaml | 10 ++ .../MemoryOwnership/CopyConstructor/task.md | 124 ++++++++++++++++++ .../RAIICopySwapIdiom/src/main.cpp | 2 +- .../MemoryOwnership/RAIICopySwapIdiom/task.md | 27 +--- .../MemoryOwnership/lesson-info.yaml | 1 + 8 files changed, 270 insertions(+), 26 deletions(-) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/CMakeLists.txt create mode 100644 ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/include/int_array.hpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/CMakeLists.txt new file mode 100644 index 0000000..88acb9a --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.27) + +project(ObjectOrientedProgramming-MemoryOwnership-CopyConstructor) + +set(CMAKE_CXX_STANDARD 14) + +# Files from `./src` directory +set(SRC src/main.cpp) + + +# Running learner side code +# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/include/int_array.hpp b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/include/int_array.hpp new file mode 100644 index 0000000..9436052 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/include/int_array.hpp @@ -0,0 +1,78 @@ +#ifndef CPPBASICS_INT_ARRAY_HPP +#define CPPBASICS_INT_ARRAY_HPP + +#include +#include + +class int_array { +public: + int_array() + : data_(nullptr) + , size_(0) + {}; + + explicit int_array(std::size_t size) + : data_(new int[size]) + , size_(size) + {}; + + ~int_array() { + delete[] data_; + } + + int_array(const int_array& other) + : data_(new int[other.size_]) + , size_(other.size_) + { + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } + }; + + int_array& operator=(const int_array& other) { + if (this == &other) { + return *this; + } + delete[] data_; + data_ = new int[other.size_]; + size_ = other.size_; + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } + return *this; + } + + std::size_t size() const { + return size_; + } + + int& operator[](std::size_t i) { + return data_[i]; + } + + const int& operator[](std::size_t i) const { + return data_[i]; + } + + friend std::ostream& operator<<(std::ostream &os, const int_array& array) { + if (array.size() == 0) { + os << "[]"; + return os; + } + os << "[ "; + for (std::size_t i = 0; i < array.size(); ++i) { + os << array[i]; + if (i != array.size() - 1) { + os << ", "; + } + } + os << " ]"; + return os; + } + +private: + int* data_; + size_t size_; +}; + +#endif // CPPBASICS_INT_ARRAY_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp new file mode 100644 index 0000000..87d4e42 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp @@ -0,0 +1,41 @@ +#include "../include/int_array.hpp" + +int main() { + + std::cout << "create a non-empty array 'a':\n"; + int_array a = int_array(10); + for (size_t i = 0; i < a.size(); ++i) { + a[i] = (int) i + 1; + } + std::cout << "a = " << a << "\n"; + std::cout << "\n"; + + std::cout << "create a copy `b` of array 'a':\n"; + int_array b(a); + std::cout << "b = " << b << "\n"; + std::cout << "\n"; + + std::cout << "modify `b`, check that the original 'a' is left unchanged:\n"; + for (size_t i = 0; i < b.size(); ++i) { + b[i] = -b[i]; + } + std::cout << "a = " << a << "\n"; + std::cout << "b = " << b << "\n"; + std::cout << "\n"; + + std::cout << "create another non-empty array 'c':\n"; + int_array c = int_array(4); + for (size_t i = 0; i < c.size(); ++i) { + c[i] = 0; + } + std::cout << "c = " << c << "\n"; + std::cout << "\n"; + + std::cout << "re-assign `c` to the copy of 'a':\n"; + c = a; + std::cout << "a = " << a << "\n"; + std::cout << "c = " << c << "\n"; + std::cout << "\n"; + + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-info.yaml new file mode 100644 index 0000000..8ce2a1b --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-info.yaml @@ -0,0 +1,10 @@ +type: theory +custom_name: Copy Constructor +files: +- name: CMakeLists.txt + visible: false +- name: include/int_array.hpp + visible: true + editable: false +- name: src/main.cpp + visible: true diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md new file mode 100644 index 0000000..ba159c0 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md @@ -0,0 +1,124 @@ + In C++, the class can define a special kind of a constructor, + called the _copy constructor_. + +For example, consider the class `int_array` which represents +a dynamically allocated array of integers +(see complete definition of the class in the file `include/int_array.hpp`): + +This class has two fields: a pointer to the allocated array and its size. +It also defines the default constructor creating an empty array, +and one custom constructor creating a new array of the given size. +The destructor of the class deallocates the array. + +
+ Note that we do not check for the null pointer in the destructor, + as deleting `nullptr` is a safe operation that has no effect. +
+ +To effectively work with an array, the class also +defines `size()` method to query for the size of the array, +overloads the array subscript operators to provide the access to the underlying array, +and overloads the printing operator to display the contents of the array. + +
+ +Note that there are two overloads of array subscript operator: +one for a mutable array, and one for an immutable array. + +
+ +Now, suppose a user of this class has created an array: + +```c++ +int_array a = int_array(10); +for (size_t i = 0; i < a.size(); ++i) { + a[i] = (int) i + 1; +} +``` + +It now might want to create a copy of this array. +Of course, one can do that manually by creating an array of suitable size +and assigning elements in the loop. + +However, C++ provides a more convenient way to do so. +It is possible to define a special _copy constructor_: + +```c++ +int_array(const int_array& other) + : data_(new int[other.size_]) + , size_(other.size_) +{ + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } +}; +``` + +Now, the user of the class can create a copy as simply as follows: + +```c++ +int_array b(a); +``` + +Another possible use-case scenario: given two existing arrays, +a user might want to re-assign one of them, copying the elements of the other. +The C++ language has a tool for that too! +It is called the _copy assignment operator_, and can be declared as follows: + +```c++ +int_array& operator=(const int_array& other) { + if (this == &other) { + return *this; + } + delete[] data_; + data_ = new int[other.size_]; + size_ = other.size_; + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } + return *this; +} +``` + +
+ +The first `if` statement in the implementation of the operator +handles the case of self-assignment — in this case the method simply returns. + +
+ +
+ +Note that the assignment operator returns reference `int_array&` +to the object itself as a result. +It is required to support multiple assignments syntax, +_e.g._: + +```c++ +a = b = c; +``` + +
+ +The copy assignment operator can be used as follows: + +```c++ +int_array c = int_array(4); +for (size_t i = 0; i < a.size(); ++i) { + c[i] = 0; +} +// re-assign c +c = a; +``` + +
+ +Note that when the following syntax is used: + +```c++ +int_array b = a; +``` + +the copy constructor will be called, not the assignment operator! + +
diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp index f796911..4d32995 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp @@ -21,7 +21,7 @@ int main() { std::cout << "\n"; std::cout << "modify `c`, check that the original 'b' is left unchanged:\n"; - for (size_t i = 0; i < b.size(); ++i) { + for (size_t i = 0; i < c.size(); ++i) { c[i] = -c[i]; } std::cout << "b = " << b << "\n"; diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md index 2fc0058..eb43a13 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md @@ -65,11 +65,6 @@ As such, the destructor has to deallocate this memory: } ``` -
- Note that we do not check for the null pointer, - as deleting `nullptr` is a safe operation that has no effect. -
- Now, the rule of five dictates that the class should also define custom copy constructor, move constructor, as well as copy and move assignment operators. We have already seen both of these in the previous lessons: @@ -99,7 +94,7 @@ Validly so, as the ownership of the resource has been transferred to the new obj Next, we need to define the assignment operators. To avoid code duplication between copy constructor and copy assignment operator -(and similarly between move constructor and move assignment operator) +(and similarly between move constructor and move assignment operator), it is possible to use another clever trick called the [__Copy-and-Swap idiom__](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap). @@ -133,22 +128,4 @@ void swap(int_array& other) { ``` This way, the `int_array` class implements the _RAII_ principle, -following the _rule of five_ and the _copy-and-swap_ idiom. - -In conclusion, you might have a look at the implementation of other methods of -the `int_array` class in the `include/int_array.hpp` file. -In particular, you might find interesting: - -```c++ -int& operator[](std::size_t i); -const int& operator[](std::size_t i) const; -``` - -* the two versions of custom array subscript operator `operator[]` - for a mutable and constant `int_array` object; - -```c++ -std::ostream& operator<<(std::ostream &os, const int_array& array) -``` - -* the printing operator `operator<<`. \ No newline at end of file +following the _rule of five_ and the _copy-and-swap_ idiom. \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml index 9e0b403..ec0e64e 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/lesson-info.yaml @@ -4,6 +4,7 @@ content: - ObjectLifetime - NewAndDeleteOperators - PlacementNew +- CopyConstructor - Ownership - UniquePtr - MoveSemantics From 5050365fc581ff846ef0652244ba748511f68d8a Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 20:12:46 +0100 Subject: [PATCH 102/137] reworked move semantics task Signed-off-by: Evgeniy Moiseenko --- .../CopyConstructor/src/main.cpp | 6 +- .../MoveSemantics/CMakeLists.txt | 12 +-- .../MoveSemantics/include/int_array.hpp | 98 +++++++++++++++++++ .../MoveSemantics/src/main.cpp | 35 +++++++ .../MoveSemantics/src/task.cpp | 17 ---- .../MoveSemantics/task-info.yaml | 13 +-- .../MemoryOwnership/MoveSemantics/task.md | 19 ++-- .../MoveSemantics/test/test.cpp | 13 --- .../RAIICopySwapIdiom/src/main.cpp | 8 +- .../RAIICopySwapIdiom/task-info.yaml | 4 +- 10 files changed, 156 insertions(+), 69 deletions(-) create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/include/int_array.hpp create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/main.cpp delete mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp delete mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp index 87d4e42..163c688 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/src/main.cpp @@ -10,12 +10,12 @@ int main() { std::cout << "a = " << a << "\n"; std::cout << "\n"; - std::cout << "create a copy `b` of array 'a':\n"; + std::cout << "create a copy 'b' of array 'a':\n"; int_array b(a); std::cout << "b = " << b << "\n"; std::cout << "\n"; - std::cout << "modify `b`, check that the original 'a' is left unchanged:\n"; + std::cout << "modify 'b', check that the original 'a' is left unchanged:\n"; for (size_t i = 0; i < b.size(); ++i) { b[i] = -b[i]; } @@ -31,7 +31,7 @@ int main() { std::cout << "c = " << c << "\n"; std::cout << "\n"; - std::cout << "re-assign `c` to the copy of 'a':\n"; + std::cout << "re-assign 'c' to the copy of 'a':\n"; c = a; std::cout << "a = " << a << "\n"; std::cout << "c = " << c << "\n"; diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt index 481b6ed..9177132 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/CMakeLists.txt @@ -5,16 +5,8 @@ project(ObjectOrientedProgramming-MemoryOwnership-MoveSemantics) set(CMAKE_CXX_STANDARD 14) # Files from `./src` directory -set(SRC src/task.cpp) - -# Files from `./test` directory -set(TEST test/test.cpp) - +set(SRC src/main.cpp) # Running learner side code # Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -add_executable(${PROJECT_NAME}-run ${SRC}) - -# Running tests -# Use PROJECT_NAME dependent names of targets for the plugin support to work correctly. -configure_test_target(${PROJECT_NAME}-test ${SRC} ${TEST}) \ No newline at end of file +add_executable(${PROJECT_NAME}-run ${SRC}) \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/include/int_array.hpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/include/int_array.hpp new file mode 100644 index 0000000..c74e2a4 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/include/int_array.hpp @@ -0,0 +1,98 @@ +#ifndef CPPBASICS_INT_ARRAY_HPP +#define CPPBASICS_INT_ARRAY_HPP + +#include +#include + +class int_array { +public: + int_array() + : data_(nullptr) + , size_(0) + {}; + + explicit int_array(std::size_t size) + : data_(new int[size]) + , size_(size) + {}; + + ~int_array() { + delete[] data_; + } + + int_array(const int_array& other) + : data_(new int[other.size_]) + , size_(other.size_) + { + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } + }; + + int_array& operator=(const int_array& other) { + if (this == &other) { + return *this; + } + delete[] data_; + data_ = new int[other.size_]; + size_ = other.size_; + for (size_t i = 0; i < size_; ++i) { + data_[i] = other.data_[i]; + } + return *this; + } + + int_array(int_array&& other) + : data_(other.data_) + , size_(other.size_) + { + other.data_ = nullptr; + other.size_ = 0; + }; + + int_array& operator=(int_array&& other) { + if (this == &other) { + return *this; + } + delete[] data_; + data_ = other.data_; + size_ = other.size_; + other.data_ = nullptr; + other.size_ = 0; + return *this; + } + + std::size_t size() const { + return size_; + } + + int& operator[](std::size_t i) { + return data_[i]; + } + + const int& operator[](std::size_t i) const { + return data_[i]; + } + + friend std::ostream& operator<<(std::ostream &os, const int_array& array) { + if (array.size() == 0) { + os << "[]"; + return os; + } + os << "[ "; + for (std::size_t i = 0; i < array.size(); ++i) { + os << array[i]; + if (i != array.size() - 1) { + os << ", "; + } + } + os << " ]"; + return os; + } + +private: + int* data_; + size_t size_; +}; + +#endif // CPPBASICS_INT_ARRAY_HPP diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/main.cpp new file mode 100644 index 0000000..8d487c4 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/main.cpp @@ -0,0 +1,35 @@ +#include "../include/int_array.hpp" + +int_array create_array(int value, size_t size) { + if (size == 0) { + return int_array(); + } + int_array array = int_array(size); + for (size_t i = 0; i < size; ++i) { + array[i] = value; + } + return array; +} + +int main() { + + std::cout << "create an array 'a' filled with value '1':\n"; + int_array a = create_array(0, 4); + std::cout << "a = " << a << "\n"; + std::cout << "\n"; + + std::cout << "create non-empty arrays 'b' and 'c':\n"; + int_array b = create_array(1, 4); + int_array c = create_array(2, 4); + std::cout << "b = " << b << "\n"; + std::cout << "c = " << c << "\n"; + std::cout << "\n"; + + std::cout << "transfer ownership from 'c' to 'b':\n"; + b = std::move(c); + std::cout << "b = " << b << "\n"; + std::cout << "c = " << c << "\n"; + std::cout << "\n"; + + return 0; +} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp deleted file mode 100644 index 1dddce0..0000000 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/src/task.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include -#include - -void transfer(std::unique_ptr& from, std::unique_ptr& to) { - to = std::move(from); -} - -int main() { - std::unique_ptr p = std::make_unique(8); - std::unique_ptr q = std::make_unique(8); - transfer(p, q); - if (!p && q) { - std::cout << "Ownership is transferred!" << std::endl; - } - return 0; -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml index 6c6d9ae..6f4ed8b 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-info.yaml @@ -1,13 +1,10 @@ -type: edu +type: theory custom_name: Move Semantics files: - name: CMakeLists.txt visible: false -- name: src/task.cpp +- name: src/main.cpp visible: true - placeholders: - - offset: 136 - length: 21 - placeholder_text: /* TODO */ -- name: test/test.cpp - visible: false +- name: include/int_array.hpp + visible: true + editable: false \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md index 99dbceb..9620421 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md @@ -28,7 +28,9 @@ representing temporary objects or objects about to be destroyed. When another object wants to copy a soon-to-be disposed rvalue object, instead of actual copying, the contents of the rvalue object can be moved. -For example, recall the `int_array` class, which has a custom copy constructor: +For example, recall the `int_array` class, +which has a custom copy constructor +(see file `include/int_array.hpp`): ```c++ class int_array { @@ -68,7 +70,7 @@ And then this function is called as follows: ```c++ // the copy constructor is called here (!) -int_array array = create_array(1, 24); +int_array a = create_array(0, 4); ``` In the code above, unnecessary copying is performed, @@ -133,10 +135,10 @@ the special standard function `std::move`, one can manually transfer the ownership from one object to another: ```c++ -int_array a = int_array(); -int_array b = int_array(10); +int_array b = create_array(1, 4); +int_array c = create_array(2, 4); // ownership transfer -a = std::move(b); +b = std::move(c); ```
@@ -169,11 +171,4 @@ p2 = std::move(p1); assert(p1 == nullptr); // p2 now owns the Dog object. assert(p2 != nullptr); -``` - -As a conclusion of this lesson, please implement the `transfer` function, -which should transfer an ownership from one unique pointer into another: - -```c++ -void transfer(std::unique_ptr& from, std::unique_ptr& to); ``` \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp deleted file mode 100644 index fa8ad10..0000000 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/test/test.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -void transfer(std::unique_ptr& from, std::unique_ptr& to); - -TEST(TransferTest, TransferTest) { - std::unique_ptr p = std::make_unique(8); - std::unique_ptr q = std::make_unique(8); - int* expected = p.get(); - transfer(p, q); - EXPECT_EQ(nullptr, p.get()); - EXPECT_EQ(expected, q.get()); -} \ No newline at end of file diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp index 4d32995..d2d6aa7 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/src/main.cpp @@ -15,12 +15,12 @@ int main() { std::cout << "b = " << b << "\n"; std::cout << "\n"; - std::cout << "create a copy `c` of array 'b':\n"; + std::cout << "create a copy 'c' of array 'b':\n"; int_array c = b; std::cout << "c = " << c << "\n"; std::cout << "\n"; - std::cout << "modify `c`, check that the original 'b' is left unchanged:\n"; + std::cout << "modify 'c', check that the original 'b' is left unchanged:\n"; for (size_t i = 0; i < c.size(); ++i) { c[i] = -c[i]; } @@ -28,13 +28,13 @@ int main() { std::cout << "c = " << c << "\n"; std::cout << "\n"; - std::cout << "move data from `c` to 'b':\n"; + std::cout << "move data from 'c' to 'b':\n"; b = std::move(c); std::cout << "b = " << b << "\n"; std::cout << "c = " << c << "\n"; std::cout << "\n"; - std::cout << "move data from `b` to 'a':\n"; + std::cout << "move data from 'b' to 'a':\n"; a = std::move(b); std::cout << "a = " << a << "\n"; std::cout << "b = " << b << "\n"; diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml index 733ada0..4754376 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-info.yaml @@ -3,8 +3,8 @@ custom_name: RAII & Copy-and-Swap Idiom files: - name: CMakeLists.txt visible: false +- name: src/main.cpp + visible: true - name: include/int_array.hpp visible: true editable: false -- name: src/main.cpp - visible: true From 653f57b627066259c7021ac83ea03965f6d17435 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 20:19:11 +0100 Subject: [PATCH 103/137] fix operators implementation Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/CollisionsRevisited/src/operators.cpp | 2 +- .../ClassesAndObjects/Encapsulation/src/operators.cpp | 2 +- .../ClassesAndObjects/Inheritance/src/operators.cpp | 2 +- .../ClassesAndObjects/IntroducingObjects/src/operators.cpp | 2 +- .../ClassesAndObjects/NewChallenge/src/operators.cpp | 2 +- .../ClassesAndObjects/NewDynamics/src/operators.cpp | 2 +- .../ClassesAndObjects/OperatorsOverloading/src/operators.cpp | 2 +- .../ClassesAndObjects/Polymorphism/src/operators.cpp | 2 +- .../ClassesAndObjects/StaticMembers/src/operators.cpp | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp index 19fa160..4dc9097 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/src/operators.cpp @@ -38,5 +38,5 @@ Circle operator*(float s, Circle c) { Rectangle operator*(float s, Rectangle r) { Point2D v = { width(r), height(r) }; - return r + s * v; + return { r.topLeft, r.topLeft + s * v }; } \ No newline at end of file From 6fbf88ea2fac381bc9c9e556b275402acb06a463 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 20:21:40 +0100 Subject: [PATCH 104/137] minor in smart pointers summary lesson Signed-off-by: Evgeniy Moiseenko --- .../SmartPointersSummary/task-info.yaml | 2 +- .../SmartPointersSummary/task.md | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml index fc62dcd..87f9701 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-info.yaml @@ -1,5 +1,5 @@ type: theory -custom_name: Smart pointers summary +custom_name: Smart Pointers Summary files: - name: CMakeLists.txt visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md index a59c16c..8a9c490 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task.md @@ -1,12 +1,12 @@ -To summarize the differences between the smart pointers, let's take a look on a table: +To summarize the differences between different types of pointers, let's take a look at the table: -| Feature | `T*` (Raw Pointer) | `std::unique_ptr` | `std::shared_ptr` | `std::weak_ptr` | -|---------------------------------|-------------------------------|---------------------------------------------------------------|---------------------------------------------------------------|------------------------------------------------------------| -| Ownership Semantics | Manual ownership management | Unique ownership | Shared ownership | Non-intrusive observer, no ownership | -| Memory Management | Manual deallocation required | Automatic deallocation | Automatic deallocation | No deallocation responsibility | -| Copyability | Copyable (shallow copy) | Movable (transfer ownership) | Copyable (shared ownership) | Copyable (shared ownership) | -| Reference Counting | No | No | Yes | Yes | -| Circular Dependency Resolution | N/A | N/A | Resolved using weak_ptr | Resolved using weak_ptr | -| Use Case | Low-level memory manipulation | Exclusive ownership, non-transferable | Shared ownership, multiple references | Observing shared ownership without affecting lifetime | -| Common Usage | Basic pointer usage | Scoped ownership, avoiding memory leaks | Managing shared resources, avoiding memory leaks | Breaking circular dependencies, observing shared ownership | -| Automatic Cleanup on Scope Exit | No | Yes | Yes | N/A (no ownership) | +| Feature | `T*` | `std::unique_ptr` | `std::shared_ptr` | `std::weak_ptr` | +|-------------------------------------|-------------------------------|-----------------------------------------|--------------------------------------------------|------------------------------------------------------------| +| **Ownership Semantics** | Manual ownership management | Unique ownership | Shared ownership | Non-intrusive observer, no ownership | +| **Memory Management** | Manual deallocation required | Automatic deallocation | Automatic deallocation | No deallocation responsibility | +| **Copyable** | Copyable (shallow copy) | Movable (transfer ownership) | Copyable (shared ownership) | Copyable (shared ownership) | +| **Reference Counting** | No | No | Yes | Yes | +| **Circular Dependency Resolution** | N/A | N/A | Resolved using weak_ptr | Resolved using weak_ptr | +| **Use Case** | Low-level memory manipulation | Exclusive ownership, non-transferable | Shared ownership, multiple references | Observing shared ownership without affecting lifetime | +| **Common Usage** | Basic pointer usage | Scoped ownership, avoiding memory leaks | Managing shared resources, avoiding memory leaks | Breaking circular dependencies, observing shared ownership | +| **Automatic Cleanup on Scope Exit** | No | Yes | Yes | N/A (no ownership) | From 6936190a43cf6aec96f28742d3dbe23530057d6d Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 28 Dec 2023 20:53:04 +0100 Subject: [PATCH 105/137] fill TODOs in RAII task Signed-off-by: Evgeniy Moiseenko --- .../MemoryOwnership/RAIICopySwapIdiom/task.md | 72 +++++++++++++++++-- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md index eb43a13..6803434 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md @@ -1,5 +1,5 @@ Concluding the presentation of the ownership model in the C++ language, -we need to discuss several related essential concepts and idioms. +we need to discuss several essential concepts and idioms. [_Resource Acquisition Is Initialization_](https://en.cppreference.com/w/cpp/language/raii), __RAII__ for short, is a programming technique which involves linking a resource @@ -9,25 +9,83 @@ According to this idiom: 1. the constructor of the class should allocate and initialize all the resources that the object will own during its lifetime; -2. the copy constructor (and copy assignment operator) of the class +2. the copy constructor and copy assignment operator of the class should copy the resource, if the resource is not copyable, then this constructor should be deleted; -3. the move constructor (and move assignment operator) of the class +3. the move constructor and move assignment operator of the class should transfer the ownership of the resource; 4. the destructor of the class should release all the resources that the object owns. -[//]: # (TODO: mention `= default` and `= delete` syntax) - [**Rule of five**](https://en.cppreference.com/w/cpp/language/rule_of_three) is a related concept, which states that if a class requires a custom + 1. destructor 2. copy constructor 3. copy assignment operator 4. move constructor 5. move assignment operator + then it actually requires all five. -[//]: # (TODO: mention rule of 3) +Note that if your class does not define none of these methods, +the compiler will generate default implementations for them: + +* default copy constructor/assignment performs a shallow copy of the object, + invoking copy constructor/assignment of its member fields; +* default move constructor/assignment performs elementwise move of the class' members, + invoking move constructor/assignment of its member fields; +* additionally, if there is no custom constructor, the default constructor + is automatically generated — it initializes all member fields to their default values. + +One can enforce the generation of default implementations using +the `= default` syntax: + +```c++ +class X { +public: + X() = default; + X(const X&) = default; + X(X&&) = default; + + X& operator=(const X&) = default; + X& operator=(X&&) = default; + /* ... */ +}; +``` + +This might be useful when you have defined a custom implementation +for one of the "five" methods, but still want to use the default +implementation for others. + +On the opposite, you might explicitly forbid auto-generating +any of these methods using the `= delete` syntax: + +```c++ +class X { +public: + X() = delete; + X(const X&) = delete; + X(X&&) = delete; + + X& operator=(const X&) = delete; + X& operator=(X&&) = delete; + /* ... */ +}; +``` + +For example, this can be useful in cases when the class should not be copyable. +In such a scenario, you can define move constructor and assignment operator, +but delete the copy constructor and copy assignment operator. + + +
+ +Before the [C++11 edition](https://en.cppreference.com/w/cpp/11) +of the language, the rule of five was known as the _rule of three_. +This is because before the C++11 the language did not have the feature of move semantics, +so it was not possible to define the move constructor and move assignment operator. + +
Let us revisit the `int_array` class to see how it fits into RAII and the rule of five (see complete definition of the class in the file `include/int_array.hpp`). @@ -67,7 +125,7 @@ As such, the destructor has to deallocate this memory: Now, the rule of five dictates that the class should also define custom copy constructor, move constructor, as well as copy and move assignment operators. -We have already seen both of these in the previous lessons: +We have already seen these constructors in the previous lessons: ```c++ int_array(const int_array& other) From d5ac580d4ef066fee931edc31d9cd03f3ef69df5 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Wed, 3 Jan 2024 13:02:24 +0000 Subject: [PATCH 106/137] language checked --- .../CollisionsRevisited/task.md | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md index a9d53da..ed5fddb 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md @@ -1,4 +1,4 @@ -Now let us restore the collision detection functionality in our refactored game. +Now, let us restore the collision detection functionality in our refactored game. We have already changed the signature of the collision detection function. @@ -6,8 +6,8 @@ We have already changed the signature of the collision detection function. CollisionInfo collisionInfo(const Circle& circle1, const Circle& circle2); ``` -Now it takes two circle shapes by constant references. -Instead of boolean flag indicating whether the collision occurred, +Now, it takes two circle shapes via constant references. +Instead of a boolean flag indicating whether a collision has occurred, it returns the `CollisionInfo` structure: ```c++ @@ -17,28 +17,28 @@ struct CollisionInfo { }; ``` -This design will give us flexibility to compute various information -about possible collision between two objects inside `collisionInfo` function, -and leave the opportunity to decide what to do with this information to other modules of our game. -For now, we will store the boolean flag `collide`, indicating whether the collision happened, -and the computed `distance` between two objects — but later you -will have an opportunity to extend this structure -with additional information required to implement new features in our game. +This design will give us the flexibility to compute various information +about a possible collision between two objects inside the `collisionInfo` function, +and it preserves the opportunity for other modules of our game to decide what to do with this information. +For now, we will store the boolean flag `collide`, indicating whether a collision has occurred, +and the computed `distance` between two objects. However, you +will have the opportunity to later extend this structure +with any additional information required to implement new features in our game. You might wonder why we decided to model `CollisionInfo` as a structure, -instead of using the fancy objects we learned in this lesson. -In fact, we did this on purpose to illustrate the following point. +rather than using the fancy objects we learned about in this lesson. +In fact, we did so purposefully to illustrate the following point. Structures, declared via `struct`, and objects/classes, declared via `class`, are not incompatible concepts in C++, -and often instances of both can be found in the same codebase. +and often instances of both can be found within the same codebase. * Objects are used to tie together data (fields) and behavior (methods). Objects provide _encapsulation_ and _polymorphism_. The state of objects can satisfy various invariants, - maintained by carefully controlling the visibility of class' members. + maintained by carefully controlling the visibility of a class' members. -* Structures are used as simple containers of data. - They have predictable memory layout and predictable behavior — +* Structures are used as simple data containers. + They have a predictable memory layout and predictable behavior — there are no associated virtual methods dispatched at runtime. In C++, structures are also sometimes referred to as [_POD types_]((https://en.wikipedia.org/wiki/Passive_data_structure)), @@ -46,46 +46,46 @@ where POD stands for _plain old data_.
-Technically, in C++ there is no big difference between `class` and `struct` keywords. +Technically, in C++, there is no big difference between the `class` and `struct` keywords. For example, one can declare classes using the `struct` keyword, and vice versa. The only real difference is that : -* in `struct` members by default have `public` visibility; -* in `class` members by default have `private` visibility. +* in a `struct`, members have `public` visibility by default; +* in a `class`, members have `private` visibility by default. -However, a prevalent convention among the C++ developers is -to use `class` keyword to declare actual classes in the object-oriented programming sense, +However, a prevalent convention among C++ developers is +to use the `class` keyword for declaring actual classes in the object-oriented programming sense, while the `struct` keyword is used to declare POD types.
Now, with the help of the new collision detection function, -your task is to re-implement the behavior of consumable objects. -- Upon collision with another object, the consumable should change its status into `DESTROYED`. -- When another object is approaching the consumable, it should change its status into `WARNED`. - This should happen whenever the distance between another object and consumable is less than `C * r`, where +your task is to reimplement the behavior of consumable objects. +- Upon colliding with another object, the consumable should change its status to `DESTROYED`. +- When another object is approaching the consumable, it should change its status to `WARNED`. + This should happen whenever the distance between the other object and the consumable is less than `C * r`, where - `r` is the radius of the consumable object, - - `C` is a special multiplier constant + - `C` is a special multiplier constant. ```c++ const float CONSUMABLE_WARNED_MULTIPLIER = 6.0f; ``` -To do so, please implement the `onCollision` method of `ConsumableObject` class. -This method is called periodically from the `Scene` class, notifying the object -about its potential collisions with other objects. -The function takes as a first argument another object, -and as a second argument the `CollisionInfo` structure, -containing the information about the distance between objects +To do so, please implement the `onCollision` method of the `ConsumableObject` class. +This method is periodically called from the `Scene` class, notifying the object +about potential collisions with other objects. +The function takes another object as its first argument, +and the `CollisionInfo` structure as the second, +which contains the information about the distance between the objects and whether they actually collided. It is up to the method's implementation to decide what to do with this information. Note that when the consumable object becomes `WARNED`, it should eventually -change its status back to `NORMAL` when other objects left its nearby area. -One way to achieve this is by also modifying the implementation of `update` method. -This method is also periodically called from the `Scene` class to give -the object an opportunity to update its internal state. -This function takes as a single argument the amount of time elapsed since the last update, -although you will not need this information in the current task. -Reset the status of the alive (that is --- not `DESTROYED`!) consumable back to `NORMAL`. -If there are some objects nearby, they will be detected again during `onCollision` call, -otherwise the consumable object will remain in the `NORMAL` status. +change its status back to `NORMAL` once other objects have left its nearby area. +This can be achieved by also modifying the implementation of the `update` method. +This method is also periodically called from the `Scene` class to enable +the object to update its internal state. +This function takes as its single argument the amount of time elapsed since the last update, +although you will not need this information for the current task. +Reset the status of the live (that is, not `DESTROYED`!) consumable back to `NORMAL`. +If there are any objects nearby, they will be detected again during an `onCollision` call; +otherwise, the consumable object will remain in the `NORMAL` status. From 21cdd626915d9a47235af9189f3efd055a6e84f2 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Wed, 3 Jan 2024 14:56:40 +0000 Subject: [PATCH 107/137] language checked --- .../ClassesAndObjects/Encapsulation/task.md | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md index 74c005c..928ec53 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task.md @@ -1,26 +1,26 @@ Let us now consider another essential class of the game — the `Scene` class. -Game scene object is responsible for handling user input, -keeping the game objects and managing their updates, -drawing the graphics on the window, and other activities. -In essence, it is a working horse of our simple game engine. +The game scene object is responsible for handling user input, +keeping game objects and managing their updates, +drawing graphics on the window, and other activities. +In essence, it is the workhorse of our simple game engine. Let us have a look at the declaration of the `Scene` class. This class is pretty packed — it includes both regular and virtual methods as well as some data fields. -For the details on the meaning of these methods, we refer to their documentation. +For details regarding the purpose of these methods, we refer to their documentation. The `Scene` class groups its members into three sections: `public`, `protected`, and `private`. Let us finally decipher their meaning! With the help of these _visibility modifiers_, -the class can govern how its clients interact with the objects of this class: - -* all the fields and methods declared inside `public` section - are visible and can be used freely outside the class; -* all the fields and methods declared inside `protected` section - are visible and can be used in the class itself and inside its ancestors; -* all the fields and methods declared inside `private` section - are visible and can be used only in the class itself, and in no other place; +the class can regulate how its clients interact with the objects of this class: + +* all the fields and methods declared within the `public` section + are visible and can be freely used outside the class; +* all the fields and methods declared within the `protected` section + are visible and can be used within the class itself and within its descendants; +* all the fields and methods declared within the `private` section + are visible and can be used only within the class itself and nowhere else; * there is a single exception to the previous rule — a class marked as a `friend` of a given class can access its protected and private members. @@ -35,46 +35,46 @@ the visibility modifiers have the following meaning: * if a class is inherited in `public` mode, the public members of the base class become public in the derived class, - and the protected members of the base class become protected in the derived class, - private members from the base class are not accessible directly from the derived class; + and the protected members of the base class become protected in the derived class; + however, private members from the base class are not directly accessible from the derived class; * if a class is inherited in `protected` mode, both the public and protected members of the base class become protected in the derived class, - private members from the base class are not accessible directly from the derived class; + while private members from the base class are not directly accessible from the derived class; * if a class is inherited in `private` mode, all public and protected members of the base class become private in the derived class, - private members from the base class are not accessible directly from the derived class. + and private members from the base class are not directly accessible from the derived class.
-The publicly available fields and methods of the class are also called its _public interface_ -(not to be confused with the term _interface_ denoting classes containing pure virtual functions and having no state). -The public interface of a class defines how the objects of this class are visible from the outside, -what fields and methods can the clients of the class access. +The publicly available fields and methods of a class are also called its _public interface_ +(not to be confused with the term _interface_ denoting classes containing pure virtual functions and devoid of state). +The public interface of a class defines the visibility of the class's objects from the outside, +denoting what fields and methods the clients of the class can access. -You might be wondering what is the point of hiding some fields or methods of the class — -after all, they can be useful outside. -However, the ability to hide some of the object's _implementation details_ -gains the objects an ultimate control over their internal state. +You might be wondering why it's necessary to hide some fields or methods of a class — +after all, they could be useful externally. +However, the ability to hide certain _implementation details_ +allows objects to have ultimate control over their internal state. This principle is known under the name _encapsulation_. -Encapsulation allows the developer of a class to maintain the _invariants_ on object's data, -ensuring that objects of this class always remain in some valid state. +Encapsulation allows the developer of a class to maintain the _invariants_ on an object's data, +ensuring that objects of this class always remain in a valid state. -Let us explain this on the example of the `Scene` class. +Let us explain this using the example of the `Scene` class. Among other things, this class is responsible for storing the game objects appearing on the scene. -One useful invariant that `Scene` class may enforce is that all of its game objects are lay within the scene's borders. -But if the `GameObject` class provides a `public` method to change object's position (i.e. `setPosition`), +One useful invariant that the `Scene` class might enforce is ensuring that all of its game objects lie within the scene's borders. +However, if the `GameObject` class provides a `public` method to change an object's position (i.e., `setPosition`), then the `Scene` object has no means to guarantee this property. -Any other class or function may change the position of an object and put it outside the visible area of the scene. -However, if the only way for a user class to change position of an object is through a call -to a scene's method (for example, its `move` method) — then the implementation of this method may -take some additional actions in order to guarantee that the object remains within the scene. -This way, by controlling the visibility of objects' fields and methods, -the developer of a class may enforce various useful invariants on the state of a program. +Any other class or function can change the position of an object and put it outside the scene's visible area. +Conversely, if the only way for a user class to change the position of an object is through a call +to a scene's method (for example, its `move` method), then the implementation of this method can +take some additional actions to ensure that the object remains within the scene. +This way, by controlling the visibility of an object's fields and methods, +a developer can enforce various useful program state invariants. -Mastering the invariants of classes and controlling the visibility of their members is -a skill that comes with the experience. The more complex applications you will architect and develop, -the better you will become at designing classes and their invariants. +Mastering class invariants and controlling the visibility of their members is +a skill that comes with experience. The more complex applications you architect and develop, +the more proficient you will become at designing classes and their invariants. To consolidate the material of this step, please implement the following two methods of the `Scene` class. @@ -84,19 +84,19 @@ void setObjectPosition(GameObject& object, Point2D position); void move(GameObject& object, Point2D vector); ``` -You need to guarantee the invariant of the `Scene` class we discussed above — -the objects of the scene should remain within its borders. +You need to ensure the invariant of the `Scene` class we discussed above — +objects in the scene must remain within its borders. -To implement these methods, you have to use -corresponding methods of the `GameObject` class, +To implement these methods, you must use +the corresponding methods of the `GameObject` class, as well as another method the `Scene` class — the `fitInto` method. -This method adjusts the position of an object to fit into the `Scene` borders. +This method adjusts the position of an object so that it fits within the `Scene` borders.
-Note that `Scene` class is declared as a `friend` of `GameObject` class +Note that the `Scene` class is declared as a `friend` of the `GameObject` class (see the class declaration in the `gobject.hpp` file). -Thus, the `Scene` class can access `setPosition` method of the `GameObject` class, +As such, the `Scene` class can access the `setPosition` method of the `GameObject` class, even though it is declared as a private method.
From 8e9655c387f64acfa4b8418769d1bb6aa42b29ec Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Thu, 4 Jan 2024 11:18:54 +0000 Subject: [PATCH 108/137] language checked --- .../ClassesAndObjects/Inheritance/task.md | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md index eb438a9..b293b40 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md @@ -1,19 +1,19 @@ -As we have mentioned, `GameObject` class on itself +As we have mentioned, the `GameObject` class itself does not specify the state of game objects — it just describes their behavior. -We need another class that extends `GameObject` with an actual state. +We need another class to extend `GameObject` with an actual state. Fortunately, object-oriented programming has a suitable concept for this job — it is called class _inheritance_. -Inheritance mechanism allows extending an existing class +The inheritance mechanism allows extending an existing class and providing concrete implementations for its virtual functions. A _derived_ class, also called a _subclass_ (_subtype_), inherits all the method and fields of the _base class_ (_base type_), -and can add its own new methods and fields. +and can add its own methods and fields. -Giving back to our problem, let us define -a `CircleGameObject` subclass of `GameObject` class. -Instances of `CircleGameObject` class represent -game objects of circular shape — like the planet object controlled by the player. +Returning to our problem, let us define +a `CircleGameObject` subclass of the `GameObject` class. +Instances of the `CircleGameObject` class represent +game objects of a circular shape — like the planet object controlled by the player. Have a look at the `CircleGameObject` class definition. The semicolon syntax: @@ -24,28 +24,28 @@ class CircleGameObject : public GameObject indicates that `CircleGameObject` is a subclass of `GameObject`. -For a time being let us again ignore the `public` and `private` keywords -used in the `CircleGameObject` class. +For the time being, let us again ignore the `public` and `private` keywords +used in the `CircleGameObject` class definition. -Instead, let us note that `CircleGameObject` declares not only methods, but also two fields: -* `circle` field stores its shape data; -* `status` field stores its current status. +Instead, note that `CircleGameObject` declares not only methods, but also two fields: +* the `circle` field stores its shape data; +* the `status` field stores its current status. -The very first method of the `CircleGameObject` is a special method called the _constructor_. -Constructor methods have the same name as the class itself, -and it takes single argument `circle`: `CircleGameObject(Circle circle)`. -The constructor is called to create an instance of an object and initialize its state. +The very first method of the `CircleGameObject` class is a special method called a _constructor_. +Constructors have the same name as the class itself, +and, in this case, it takes a single `circle` argument: `CircleGameObject(Circle circle)`. +A constructor is called to create an instance of an object and initialize its state.
The `explicit` [specifier](https://en.cppreference.com/w/cpp/language/explicit) before a constructor prevents implicit type casts. -In C++, if a class has a constructor with a single argument, -which is not marked with the `explicit` keyword, +In C++, if a class has a constructor with a single argument +that is not marked with the `explicit` keyword, then the compiler can automatically convert the argument type to the class type when necessary. -For example, if the constructor `CircleGameObject(Circle circle)` has not been +For example, if the constructor `CircleGameObject(Circle circle)` had not been marked as `explicit`, the following code would be able to compile: ```c++ @@ -54,13 +54,13 @@ void foo(CircleGameObject object) { /* ... */ } int main() { Circle circle = { { 0.0f, 0.0f, }, 10.0f }; // `Circle` will be implicitly converted into `CircleGameObject` - // by calling `CircleGameObject` constructor. + // by calling the `CircleGameObject` constructor. foo(circle); } ``` -However, with the constructor marked as `explicit` the code fragment above would not compile, -and should be rewritten as follows: +However, with the constructor marked as `explicit`, the code fragment above would not compile, +and it should be rewritten as follows: ```c++ void foo(CircleGameObject object) { /* ... */ } @@ -72,7 +72,7 @@ int main() { } ``` -In the C++ language, the usage of the `explicit` constructors is generally encouraged, +In the C++ language, the use of `explicit` constructors is generally encouraged, as it results in more predictable behavior.
@@ -88,29 +88,29 @@ CircleGameObject::CircleGameObject(Circle circle) {} ``` -After the arguments' list comes the semicolon `:`, followed by the list of the object's fields. -The value, provided in the brackets next to the field's name, is used to initialize the corresponding field. +After the argument list comes the colon `:`, followed by a list of the object's fields. +The value provided in the brackets next to the field's name is used to initialize the corresponding field. Please note that the order of the fields in the _constructor initializer list_ is important. It should match the order in which the fields are declared in the class. After the constructor initializer list comes the constructor body `{}` (empty in this case). -Similarly, as regular methods, it can contain arbitrary C++ statements. +Just like regular methods, it can contain any C++ statements. A constructor has its counterpart — the _destructor_ method, -which should have the same name as a class prefixed with `~`. -It is a method called automatically before destruction of the object to perform some clean-up routines. -A class can have several constructors taking different arguments, -but there could only one destructor taking no arguments. +which should have the same name as the class prefixed with `~`. +This method is automatically called before the destruction of an object to perform some clean-up routines. +A class can have several constructors with different arguments, +but there can be only one destructor, which does not take any arguments. -In fact, you may have already seen the destructor on the previous step: -a class `GameObject` has a virtual destructor `~GameObject()`. +In fact, you may have already seen a destructor in the previous step: +the `GameObject` class has a virtual destructor `~GameObject()`. The `= default` syntax at the end of its definition indicates that -this destructor has default auto-generated implementation. +this destructor has a default auto-generated implementation. As we will see later in the course, constructors and destructors have a pivotal role in C++. Going back to the `CircleGameObject` class, consider its methods. -Some of them, like `getPosition` and `setPosition`, are just re-declared methods of the `GameObject` class. -The keyword `override` at the end of the methods' declarations indicates this fact. +Some of them, like `getPosition` and `setPosition`, are just redeclarations of methods from the `GameObject` class. +The keyword `override` at the end of these methods' declarations indicates this fact. However, unlike the `GameObject` class, the `CircleGameObject` class can actually define the behavior of these methods. To be precise, it is your task to implement some of them, @@ -118,7 +118,7 @@ namely `getPosition`, `setPosition`, `getStatus`, `setStatus`, and `getCircle`.
-Note that the position of the `CircleGameObject` is a position of its circle's center. +Note that the position of a `CircleGameObject` corresponds to the center of its circle.
From cbf560f41fa193a65b68b29d31af0e232aa12161 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Thu, 4 Jan 2024 12:08:16 +0000 Subject: [PATCH 109/137] language checked --- .../IntroducingObjects/task.md | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md index 899e226..391836a 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task.md @@ -1,14 +1,14 @@ -Let us finally meet with the object-oriented programming paradigm. +Let us finally dive into the object-oriented programming paradigm. -At the core of this paradigm lies the concept of the _object_. -An object groups together some data, called object's _state_, +At its core lies the concept of an _object_. +An object groups together some data, called the object's _state_, with a set of functions operating on this data. These functions are also called the _methods_ of the object. In this way, objects are similar to structures, but unlike plain structures, -they also allow to add functions into their definition. +they also allow the addition of functions into their definition. Objects are grouped into _classes_. -Class defines a blueprint after which the objects are created. +A class defines a blueprint from which objects are created. In other words, a _class_ is just a type of objects. For example, let us consider the `GameObject` class. @@ -19,66 +19,66 @@ Instances of this class will store data related to a game object, such as its position on the scene, and some methods to manipulate the object. Let us have a look at the `GameObject` class definition. -First note that in C++ new class is defined with the help of the `class` keyword. -Next comes the keyword `public` --- we will describe its meaning in the later steps. +First, note that in C++, a new class is defined with the help of the `class` keyword. +Next comes the keyword `public` — we will describe its meaning in the later steps. After that come the methods of the class. -There are plenty of them, you can get the meaning of each method -by consulting its _documentation_ given as a docstring comment in front of the method declaration. +There are plenty of these, and you can get the meaning of each method +by consulting its _documentation_, given as a docstring comment in front of the method declaration.
-A docstring is a comment in a special format that is used to document a specific segment of code. +A docstring is a specially formated comment used to document a specific segment of code. This format is widely used in C/C++ libraries and frameworks to document their API. -Dedicated tools, like the [Doxygen](https://www.doxygen.nl/index.html), +Dedicated tools like [Doxygen](https://www.doxygen.nl/index.html) can scan the source code of your program and extract these docstring comments to produce documentation in various formats, such as HTML or PDF. The docstring format supports various annotations used to document specific aspects of a given code block. -For example, `@param` annotation can be used to document arguments of a function, -`@return` annotation can be used to document the return value of a function, etc. +For example, the `@param` annotation can be used to document the arguments of a function, +the `@return` annotation can be used to document the return value of a function, etc. You can learn more about the docstring format by consulting the dedicated [page](https://www.doxygen.nl/manual/docblocks.html).
-The `GameObject` class itself does not define any data fields, only the methods. +The `GameObject` class itself does not define any data fields, only methods. It, however, implicitly defines a bunch of _properties_ of an object, for example, its position. -A value of a property can be requested using its _getter_ method (e.g. `getPosition`), -and it can be changed using its _setter_ method (e.g. `setPosition`). -Note that some properties of an object have both _getter_ and _setter_ methods, -like aforementioned `getPosition` and `setPosition` methods, -while others have only _getter_, for example `getVelocity`. -This is for a reason — some properties are derivatives of the current objects' status, +A property's value can be requested using its _getter_ method (e.g., `getPosition`), +and it can be changed using its _setter_ method (e.g., `setPosition`). +Note that some object properties have both _getter_ and _setter_ methods, +like the aforementioned `getPosition` and `setPosition` methods, +while others have only a _getter_, for example, `getVelocity`. +There's a reason for this — some properties are derivatives of the objects' current status and they cannot be directly changed from the outside. -Another piece of unfamiliar syntax here is the `const` keyword coming after the arguments of a methods. -It denotes the _constant methods_ --- these methods cannot change the state of the object. +Another piece of unfamiliar syntax here is the `const` keyword coming after the arguments of a method. +It denotes _constant methods_ — those methods that cannot change the state of the object. -Finally, keyword `virtual` denotes the _virtual_ methods — these are the methods -that can be _overridden_ by the inheritors of the class -(we will delve back to inheritance in the next task). +Finally, the `virtual` keyword denotes _virtual_ methods — these are methods +that can be _overridden_ by inheritors of the class +(we will delve back into inheritance in the next task). The `= 0` syntax at the end of the virtual method indicates that it is a _pure virtual_ method. Such a method is not implemented for the given class — it is just a stub for an actual method implementation. -The classes that do declare any data fields and contain pure virtual methods are also called _interfaces_. +Classes that do not declare any data fields and contain pure virtual methods are also called _interfaces_. In some sense, interfaces just provide a description of the objects' behavior, without actually specifying their internal status. This leaves a programmer an opportunity to define several _subclasses_ of an interface that provide different implementations of its behavior. -For example, in the following steps of this lesson, you will have to implement -a subclass of playable objects, consumable objects, and others. +For example, in the following steps of this lesson, you will need to implement +subclasses for playable objects, consumable objects, and others. -But before moving to the following step, please complete a small programming exercise. -One of the methods of the `GameObject` class --- the `move` method --- is actually not `virtual`. -It is because it can be implemented in terms of other methods of this class, namely `getPosition` and `setObjectPosition`. -To finish the programming assignment, please provide an implementation of this method. +Before moving to the following step, please complete a small programming exercise. +One of the methods of the `GameObject` class — the `move` method — is actually not `virtual`. +This is because it can be implemented using other methods of this class, namely `getPosition` and `setObjectPosition`. +For the programming assignment, please provide an implementation of this method. -The implementation of the method should be put into `gobject.cpp` file. -Note that methods' definition given in this file contains both -name of the class, and the name of the method, separated by the `::`. +The implementation of the method should be put into the `gobject.cpp` file. +Note that the methods' definitions given in this file contains both +the name of the class and the name of the method, separated by `::`. ```c++ void GameObject::move(Point2D vector) { From 7ea1828dceece6a5cb08202937f6ca499aa652a9 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Thu, 4 Jan 2024 13:37:36 +0000 Subject: [PATCH 110/137] language checked --- .../ClassesAndObjects/Introduction/task.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md index ebe7b73..fb651ea 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task.md @@ -1,23 +1,23 @@ In the next few lessons, you will learn about the object-oriented programming paradigm in the context of the C++ language. As you will see, this paradigm helps -to organize the code into independent subcomponents, manage the complexity -of the applications, and streamline the addition of new functionality. +to organize code into independent subcomponents, manage the complexity +of applications, and streamline the addition of new functionality. -We will re-implement our game using the features of object-oriented programming. +We will reimplement our game using features of object-oriented programming. This effort will help us to easily extend the game by adding new kinds of objects. -In particular, we will add enemy objects that will make our little game a bit more challenging. +In particular, we will add enemy objects to make our little game a bit more challenging. There is a lot of fun coming, so get ready for your journey into the world of objects! -Here is a list of specific topics that are going to be covered: +Here is a list of specific topics that we'll be covering: -* Operators overloading. -* Classes and objects. -* Abstract classes and interfaces. -* Constructors and destructors. -* Inheritance and subtyping. -* Subtype polymorphism. -* Virtual methods. -* Visibility modifiers: `public`, `protected`, and `private`. -* Encapsulation and class invariants. -* Static methods, fields, and variables. -* Objects vs structures. \ No newline at end of file +* Operator overloading +* Classes and objects +* Abstract classes and interfaces +* Constructors and destructors +* Inheritance and subtyping +* Subtype polymorphism +* Virtual methods +* Visibility modifiers: `public`, `protected`, and `private` +* Encapsulation and class invariants +* Static methods, fields, and variables +* Objects vs structures \ No newline at end of file From 078ff768d0b031d70cd437ec45179a0f3effe08a Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Thu, 4 Jan 2024 14:18:10 +0000 Subject: [PATCH 111/137] language checked --- .../ClassesAndObjects/NewChallenge/task.md | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md index 87034e0..100a954 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task.md @@ -6,38 +6,38 @@ this task can be accomplished quite easily. The enemy object should behave as follows: - it should move on a scene in a random direction, changing it periodically; -- it should consume the star objects when colliding with them; -- star objects should change their status into `WARNED` when +- it should consume star objects upon collision; +- star objects should change their status to `WARNED` when the enemy object is approaching them - (similarly as they do when the player object is approaching); + (similar to how they behave when the player object approaches); - when the enemy object collides with the player object, the latter - should change its status into `DESTROYED`, becoming effectively immovable. + should change its status to `DESTROYED`, becoming effectively immovable. Let us proceed to the implementation of this behavior. First, take a look at the declaration of the `EnemyObject` class. -It again inherits from the `CircleGameObject` class, so it already -has a corresponding circle shape data and behavior attached to it. +As it inherits from the `CircleGameObject` class, +a corresponding circular shape and behavior are already attached to it. Moreover, the `getKind()` method of the `EnemyObject` class -distinguish the enemy objects from other kind of objects (player and consumable) +distinguishes enemy objects from other kinds of objects (player and consumable) by returning the `GameObjectKind::ENEMY` value. -We already partly implemented for you the movement behavior of the `EnemyObject`. +We have already partly implemented the movement behavior of the `EnemyObject` for you. To do so, we have added two new fields to the objects of this class: -- `velocity` field is vector storing the direction and speed of the current velocity of the object; -- `updateTimer` field stores the time elapsed since the last update of the object's velocity. +- the `velocity` field is a vector storing the direction and speed of the object's current velocity; +- the `updateTimer` field stores the time elapsed since the last update of the object's velocity. -The method `getVelocity()` simply returns the value of `velocity` field. -The method `update(sf::Time delta)` is responsible for periodically updating the velocity of the object. +The `getVelocity()` method simply returns the value of the `velocity` field. +The `update(sf::Time delta)` method is responsible for periodically updating the object's velocity. It takes as an argument the amount of time elapsed since the last update. The implementation simply checks if the overall amount of elapsed time is greater than the predefined time period (1 second), and if so, it resets the velocity to a new randomly generated one -by calling the method `updateVelocity()`. +by calling the `updateVelocity()` method. Your task is to implement this method. -The method should generate a random direction vector, multiply it to the speed scalar constant, -and assign it to the `velocity` field. -That is, the following formula should be used +The method should generate a random direction vector, multiply it by the speed scalar constant, +and assign the result to the `velocity` field. +That is, the following formula should be used: ``` v` = S * d ``` @@ -48,11 +48,11 @@ where: You have the freedom to define a direction vector as you like, but it should satisfy the following constraints: -* the `x` and `y` coordinates should either be equal `1`, `0`, or `-1`, and +* the `x` and `y` coordinates should either be equal to `1`, `0`, or `-1`, and * the vector should be randomly generated: several consecutive invocations of a method - with a high probability should not result in the same direction generated. + should not likely result in the same direction being generated. -To generate the random direction vector, you might find useful the following functions: +To generate a random direction vector, you might find the following functions useful: ```c++ bool generateBool(float prob = 0.5f); @@ -62,12 +62,12 @@ Point2D generatePoint(const Rectangle& boundingBox); ``` A detailed description of these functions can be found in the documentation -put alongside their definition in the `utils.hpp` file. +alongside their definitions in the `utils.hpp` file. Moving forward with the `Enemy` class, the easy part is to implement the `getTexture` method. It should return a new special texture for the enemy objects. -This texture has a corresponding id --- `GameTextureID::BLACKHOLE`. +This texture has a corresponding id — `GameTextureID::BLACKHOLE`. The harder part is to implement the collision behavior. When an enemy object collides with the player object, the player object should become inactive. @@ -79,11 +79,11 @@ modify the `onCollision` method of the `PlayerObject` class, not the `EnemyObjec
In the implementation of the `PlayerObject::onCollision` method, -remember to check that the collision occurred with the enemy object, +remember to check that the collision occurred with an enemy object, not an object of some other kind!
-Notice that from the point of view of the consumable objects, -the player and the enemy objects behave similarly, +Notice that from the perspective of the consumable objects, +both the player and the enemy objects behave similarly, so you do not need to modify their behavior. \ No newline at end of file From a537299182e1356245567439d1c4bfce527e0fe1 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Thu, 4 Jan 2024 15:36:16 +0000 Subject: [PATCH 112/137] language checked --- .../ClassesAndObjects/NewDynamics/task.md | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md index d2c75d9..92c6db1 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task.md @@ -1,8 +1,8 @@ Next, we need to restore the dynamic behavior of our game — the ability to add and remove objects dynamically during play. -To do so, we will re-implement the doubly linked list data structure +To do so, we will reimplement the doubly linked list data structure in terms of object-oriented programming. -Before proceeding with this task, please first finish the `Ownership` module, +Before proceeding with this task, please first complete the `Ownership` module, as we will need some concepts taught there. First of all, instead of completely rewriting the `GameplayStaticScene` class, @@ -10,37 +10,37 @@ we will just add a new class — `GameplayDynamicScene`, where we will implement Please have a look at the declaration of the `GameplayDynamicScene` class (file `dynscene.hpp`), and its definition (file `dynscene.cpp`). -You can find the brief description of its methods in the documentation comments. +You can find brief description of its methods in the documentation comments. -The `GameplayDynamicScene` class has a field `objects` of `GameObjectList` class. -This is the main class we are going to work with in this task — it will implement the doubly linked list. -Its declaration and definition can be found in the files `gobjectlist.hpp` and `gobjectlist.cpp` respectively. +The `GameplayDynamicScene` class has a field `objects` of the `GameObjectList` class. +This is the main class we will be working with in this task — it will implement the doubly linked list. +Its declaration and definition can be found in the `gobjectlist.hpp` and `gobjectlist.cpp` files, respectively. -This time, using the object-oriented programming and ownership model, +This time, using object-oriented programming and the ownership model, we will implement a list that will own its nodes. -The nodes will be destructed and deallocated automatically upon destruction of the list itself, -and copying of the list will result in all of its nodes being copied too. +The nodes will be automatically destructed and deallocated upon destruction of the list itself, +and copying of the list will result in the copying of all of its nodes as well. The nodes of the list are represented by the `Node` structure, -which implement the ownership semantics — each node is owning its successor. -Let us have a closer look on the fields of the `Node` structure. +which implements ownership semantics — each node owns its successor. +Let us have a closer look at the fields of the `Node` structure. -- `next` field is an owning pointer `std::unique_ptr`. +- the `next` field is an owning pointer `std::unique_ptr`. Whenever a node is destructed, all its succeeding nodes will also be destructed, due to its ownership semantics. -- `prev` field is a plain pointer `Node*`, storing the pointer to a previous node. - It is a non-owning pointer, because if it was made an owning pointer, then it would - result into ownership cycle. - Indeed, giving a node `x`, `x.prev` can point to a node `y`, such that `y.next` points to `x` --- +- the `prev` field is a plain pointer `Node*`, storing the pointer to the previous node. + It is a non-owning pointer because if it were an owning pointer, it would + result in an ownership cycle. + Indeed, given a node `x`, `x.prev` could point to a node `y`, such that `y.next` points to `x` — this is clearly a cycle. -- `object` field is a shared pointer `std::shared_ptr` to a game object stored in the node. - It has the shared ownership semantics, so that the shared pointers to game objects +- the `object` field is a shared pointer `std::shared_ptr` to a game object stored in the node. + It has shared ownership semantics, so that shared pointers to game objects can be safely returned from the methods of the `GameObjectList` class. Also, the shared ownership of game objects will allow us to implement the copying of a list — a copy would simply store copies of shared pointers. -The static methods `link` and `unlink` implement the linking and unlinking of nodes respectively. +The static methods `link` and `unlink` implement the linking and unlinking of nodes, respectively. You have already seen these functions in the `LinkedList` task. Here is a reminder of their semantics: @@ -57,21 +57,21 @@ static void unlink(Node* node); - Unlinks the node from its neighboring nodes. Please implement these methods. -Pay attention to the different ownership semantics of `next`, `prev`, and `object` pointers, -and use `std::move` to manage the ownership. -Remember that `std::unique_ptr` transitions into `nullptr` state after ownership transfer, -and so the order of `std::move` and pointer dereferences becomes important! +Pay attention to the different ownership semantics of the `next`, `prev`, and `object` pointers, +and use `std::move` to manage ownership. +Remember that `std::unique_ptr` transitions to a `nullptr` state after an ownership transfer, +so the order of `std::move` and pointer dereferences becomes important! Next, consider the fields of the `GameObjectList` class. -- `head` is an owning pointer `std::unique_ptr` to the first node of the list. -- `tail` is a non-owning pointer `Node*` to the last node of the list. - It is a non-owning pointer because the pointed-to node is actually owned +- `head` is an owning pointer, `std::unique_ptr`, to the first node of the list. +- `tail` is a non-owning pointer, `Node*`, to the last node of the list. + It is a non-owning pointer because the node it points to is actually owned by its predecessor via its `next` pointer. -Because the `head` pointer of the list is owning one, the whole sequence of nodes -belonging to the list will be destroyed automatically upon destruction of the list itself. -This is the reason why we left the default implementation of the class' destructor: +Since the `head` pointer of the list is an owning one, the whole sequence of nodes +belonging to the list will be destroyed automatically upon the list's destruction. +This is the reason why we've kept the default implementation of the class' destructor: ```c++ ~GameObjectList() = default; @@ -79,42 +79,42 @@ This is the reason why we left the default implementation of the class' destruct
-Indeed, the destructor of the `GameObjectList` will call +Indeed, the destructor of `GameObjectList` will call the `~std::unique_ptr()` destructor of the `head` field. -In turn, it will call the destructor of the `Node`, -which will call the destructor `~std::unique_ptr()` of its `next` field, -and so on, until all nodes will be destructed. +In turn, it will call the destructor of `Node`, +which will then call the `~std::unique_ptr()` destructor of its `next` field, +and so on, until all nodes are destructed.
Note that in the previous implementation of the list (in the `LinkedList` task), we used a single sentinel node to simplify the implementation of some list operating functions. -Moreover, under the hood the list was organized into a cyclic list: -the `next` field of the last node was pointing the first (sentinel) node. -This time we cannot reuse this trick, since a cyclic list would result into ownership cycle. -Therefore, we would need two sentinel nodes — one as the first node, and the second as a last node. +Moreover, under the hood, the list was organized into a cyclic list: +the `next` field of the last node pointed to the first (sentinel) node. +This time, we cannot reuse this trick, since a cyclic list would result in an ownership cycle. +Therefore, we would need two sentinel nodes — one as the first node, and the second as the last node. -Take a look at the pre-defined methods `foreach` and `remove` of the list -that utilize this list representation: +Take a look at the pre-defined `foreach` and `remove` methods of the list, +which utilize this list representation: - `foreach` applies the function given as an argument to every game object stored in the list; -- `remove` unlinks nodes (effectively removing them), whose game object satisfies predicate given as an argument. +- `remove` unlinks nodes (effectively removing them) whose game object satisfies the predicate given as an argument.
-You might find the type `std::function<...>` unfamiliar. +You might find the `std::function<...>` type unfamiliar. In essence, it is just an object-oriented counterpart of a function pointer. -We will have a closer look at this type in the later modules of the course. +We will have a closer look at this type in later modules of the course.
-Now please implement the default constructor of the `GameObjectList` class -that should initialize two sentinel nodes of the list: +Now, please implement the default constructor of the `GameObjectList` class, +which should initialize two sentinel nodes of the list: ```c++ GameObjectList(); ``` -Next implement the method for inserting a game object into the end of the list +Next, implement the method for inserting a game object at the end of the list (just before the last sentinel node): ```c++ @@ -123,7 +123,7 @@ void insert(const std::shared_ptr& object); Keep in mind that: - the first and the last nodes should remain sentinel nodes; -- only the sentinel nodes can store null pointer `nullptr` inside `object` field. +- only sentinel nodes can store a null pointer `nullptr` inside the `object` field. In order to complete this task, also finish the implementation of the ownership semantics of the `GameObjectList` class, following the rule-of-file and copy-and-swap idiom. From 08c5a3026ccfd7a46733937e523f1e8daf3b2ace Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 11:01:15 +0000 Subject: [PATCH 113/137] language checked --- .../OperatorsOverloading/task.md | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md index 1511a8b..1be691d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task.md @@ -1,13 +1,13 @@ Before we dive into object-oriented programming, we will learn about another useful feature of the C++ language -that can help to make your code easier to read and understand. -This feature is __operator overloading,__ and it allows you to define various operators, +that can help make your code easier to read and understand. +This feature is __operator overloading__; it allows you to define various operators, like arithmetic operators `+`, `-`, `*`, -for your custom data type. +for your custom data types. -Recall the function `move` you implemented before. -At first, your task was to implement it to move an object along `x` axis. -Your code, probably, looked like this: +Recall the `move` function you implemented before. +At first, your task was to implement it to move an object along the `x` axis. +Your code likely looked like this: ```c++ float move(float position, float velocity, float delta) { @@ -15,8 +15,8 @@ float move(float position, float velocity, float delta) { } ``` -At the next stage, your task was to re-implement `move`, -this time to move an object along both `x` and `y` axis, +At the next stage, your task was to reimplement `move`, +this time to move an object along both `x` and `y` axes, using our custom data type `Point2D` and two functions `add` and `mul` defined for it: @@ -38,7 +38,7 @@ Point2D move(Point2D position, Point2D velocity, float delta) { ``` To enable this syntax, it is sufficient to define -a special function with `operator` prefix in its name: +a special function with the `operator` prefix in its name: ```c++ Point2D operator+(Point2D a, Point2D b) { @@ -46,26 +46,26 @@ Point2D operator+(Point2D a, Point2D b) { } ``` -The C++ language allows overloading a [large number of operators](https://en.cppreference.com/w/cpp/language/operators). -Please remember, just like any other feature, it is possible to abuse the operator overloading feature. -It is recommended to overload the operators only if the corresponding notation +The C++ language allows for the overloading of a [large number of operators](https://en.cppreference.com/w/cpp/language/operators). +Please remember, just like any other feature, the operator overloading feature can be potentially abused. +It is recommended to overload operators only if the corresponding notation has a natural interpretation for your custom data type! Your next task is to implement several overloaded operators for the data types used in our game. -Let us start with the familiar operations on `Point2D` data type. +Let us start with the familiar operations on the `Point2D` data type. Please overload the arithmetic operators `+`, `-`, `*` for this data type (their signatures are already given in the task template). -It is allowed to use `add` and `mul` functions implemented on previous steps. -Note the difference between subtraction operator and unary minus operator — +It is allowed to use the `add` and `mul` functions implemented in previous steps. +Note the difference between the subtraction operator and the unary minus operator — the former subtracts the coordinates of one point from another, while the latter just changes the sign of both coordinates of a single point. -Arithmetic operators also do have natural interpretation for shape data types, such as `Circle` and `Rectangle`. +Arithmetic operators also have natural interpretations for shape data types, such as `Circle` and `Rectangle`.
-Please note that the rectangle is defined by two points — its top-left and bottom-right corners: +Please note that a rectangle is defined by two points — its top-left and bottom-right corners: ```c++ struct Rectangle { @@ -76,16 +76,16 @@ struct Rectangle {
-If we are adding a point to a shape, then the point is interpreted as a vector and -the shape should be moved in the direction of this vector. -* For `Circle` shape it is sufficient to add the point to the center of the circle. +When adding a point to a shape, the point is interpreted as a vector and +the shape should move in the direction of this vector. +* For the `Circle` shape, it is sufficient to add the point to the center of the circle. * For `Rectangle`, it is required to add the point to both corners of the rectangle. -Multiplying a shape by a scalar should perform the scaling operation. -* For `Circle`, it is sufficient to multiply the radius to the scalar. -* For `Rectangle` the implementation is a bit trickier. +Multiplying a shape by a scalar should result in a scaling operation. +* For `Circle`, it is sufficient to multiply the radius by the scalar. +* For `Rectangle`, the implementation is a bit trickier. It is required to scale the width and height of the rectangle and then recompute its bottom-right corner. - You might use the pre-defined functions `width` and `height` to get + You might use the predefined functions `width` and `height` to get the corresponding properties of the rectangle (defined in the file `rectangle.hpp`). ```c++ @@ -98,7 +98,7 @@ float height(const Rectangle& rect) { } ``` -Finally, it would be convenient to overload equality comparison operators for all the data types mentioned above. +Finally, it would be convenient to overload the equality comparison operators for all the data types mentioned above.
@@ -119,8 +119,8 @@ std::istream& operator>>(std::istream& is, Point2D& p) { ``` Notice the arguments of type `std::ostream` and `std::istream` — those are -output and input streams respectively. -We have not yet covered them in this course, that would be a topic of the next lessons. +output and input streams, respectively. +While we have not covered them in this course yet, they will be topics in upcoming lessons. For now, however, it is sufficient to know that `std::cout` and `std::cin` (which we have seen in previous lessons!) are particular objects of these classes. @@ -132,7 +132,7 @@ std::cin >> point; std::cout << "Your point is " << point << std::endl; ``` -You might find the overloads of the input/output operators -for the other data types used in our game in the file `operators.hpp`. +You can find the overloads of the input/output operators +for the other data types used in our game in the `operators.hpp` file.
From cf380e296768747d05b4372c5be5e3c67f130f00 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 12:26:16 +0000 Subject: [PATCH 114/137] language checked --- .../ClassesAndObjects/Polymorphism/task.md | 78 +++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md index 47d3d48..9496d44 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task.md @@ -1,78 +1,78 @@ -Despite class `CircleGameObject` adds some data fields to the `GameObject` class, -it still leaves some of `GameObject` virtual methods unimplemented. +Despite the class `CircleGameObject` adding some data fields to the `GameObject` class, +it still leaves some of `GameObject`'s virtual methods unimplemented. Therefore, this class is still an _abstract class_ — it cannot be instantiated. -We need to introduce concrete subclasses implementing the behavior of base class. -The instances of these subclasses can then be used in all places -where an instance of a base class is expected. +We need to introduce concrete subclasses to implement the behavior of the base class. +Instances of these subclasses can then be used +wherever an instance of the base class is expected. By substituting the concrete subclass of an object, we can change the behavior of the program without changing the code -of the functions which use this object! -This ability to treat objects of different classes implementing different behaviors +of the functions that use this object! +This ability to treat objects of different classes, which implement different behaviors, as objects of a common base class is known as _subtype polymorphism_. Let us introduce two concrete subclasses of the `CircleGameObject` class — -the `PlayerObject` class and the `ConsumableObject` classes, -representing the object controlled by the player, and consumable objects respectively. +the `PlayerObject` class and the `ConsumableObject` class, +representing the object controlled by the player and the consumable objects, respectively. At last, both of these classes implement all the functionality required by the `GameObject` class. -Please find the declaration of these classes in the files `player.hpp` and `consumable.hpp`. +Please find the declaration of these classes in the `player.hpp` and `consumable.hpp` files. There are no new syntactic constructs here, so you should be able to understand the code in these files. -The implementations of these classes can be found in the files `player.cpp` and `consumable.cpp`. +The implementations of these classes can be found in the `player.cpp` and `consumable.cpp` files. Note that the full implementation of some methods is already provided. -For example, the `getVelocity` method of the `PlayerObject` computes +For example, the `getVelocity` method of the `PlayerObject` class computes the current velocity vector by calling the `SFML` functions -to determine which keys are pressed by the player at the moment. +that determine which keys are pressed by the player at the moment. Your task is to implement the `getTexture` methods of both classes. These methods should return the current texture of an object to be displayed, depending on the current status of the object. -Although, we have not yet implemented the methods that actually update +Although we have not yet implemented the methods that actually update the status of the objects, implementing the `getTexture` methods first will give you a good opportunity to practice and learn the method call syntax. -The `getTexture` methods takes by reference one argument — object of the `TextureManager` class. -It is another predefined by us class — it is responsible for loading the textures required by the game. -A pointer to a texture can be requested by calling the `getTexture` method of the `TextureManager` class. -It takes as argument the ID of the textures — these IDs are represented by the `GameTextureID` enum. +The `getTexture` method takes one argument by reference — an object of the `TextureManager` class. +It is another class predefined by us — it is responsible for loading game-required textures. +A texture pointer can be requested by calling the `getTexture` method of the `TextureManager` class. +It takes the ID of the textures as an argument — these IDs are represented by the `GameTextureID` enum.
-Note that previously we used keyword `enum`, +Note that previously we used the keyword `enum`, but the `GameTextureID` type is defined with the two keywords: `enum class`. -So what is the difference between the `enum` and `enum class` declarations? +So, what is the difference between the `enum` and `enum class` declarations? In fact, the `enum class` (also known as a _scoped enumeration_) is a restricted form of the regular `enum`, -that was introduced in the C++ language to overcome +introduced in the C++ language to overcome some of its issues. -Firstly, in case of `enum class`, the names of enumeration values are kept -withing the scope of enumeration name. -This way the enumeration values do not pollute the global scope, +Firstly, in the case of `enum class`, the names of enumeration values are kept +within the scope of the enumeration name. +This way, the enumeration values do not pollute the global scope, so there is no risk of accidental name clashes. -Let us see the examples. -In case of regular `enum` the following syntax is used: +Let us see some examples. +For a regular `enum`, the following syntax is used: ```c++ enum Color { RED, GREEN, BLUE }; // RED, GREEN, and BLUE are globally accessible names Color green = GREEN; ``` -while in case of `enum class` the enumeration value can be accessed -only though the enumeration name: +Whereas in the case of `enum class`, the enumeration value can only be accessed +through the enumeration name: ```c++ enum class Color { RED, GREEN, BLUE }; -// RED, GREEN, and BLUE do not pollute global scope +// RED, GREEN, and BLUE do not pollute the global scope Color green = Color::GREEN; ``` -Secondly, the `enum class` does not permit implicit conversion to `int`. +Secondly, `enum class` does not permit implicit conversion to `int`. For example, the following code compiles just fine: ```c++ enum Color { RED, GREEN, BLUE }; -// no compilation error, variable green equals to 1. +// no compilation error, the variable green equals to 1. int green = GREEN; ``` However, if `enum class` is used, the code will not compile: @@ -84,12 +84,12 @@ int green = Color::GREEN;
-Please implement the `getTexture` methods of the `PlayerObject` and `ConsumableObject` -with the following logic: -* under `NORMAL` or `WARNED` status, the player object should have `PLANET` texture; -* under `DESTROYED` status, the player object should have `PLANET_DEAD` texture; -* under `NORMAL` status, the consumable object should have `STAR` texture; -* under `WARNED` status, the consumable object should have `STAR_CONCERNED` texture; +Please implement the `getTexture` methods of `PlayerObject` and `ConsumableObject` +using the following logic: +* under `NORMAL` or `WARNED` status, the player object should have the `PLANET` texture; +* under `DESTROYED` status, the player object should have the `PLANET_DEAD` texture; +* under `NORMAL` status, the consumable object should have the `STAR` texture; +* under `WARNED` status, the consumable object should have the `STAR_CONCERNED` texture; * under `DESTROYED` status, the consumable object should not be displayed.
@@ -99,8 +99,8 @@ consult the documentation of the `GameObject`'s `getTexture` method.
-To implement this method, you will have to call the `getTexture` method of the `TextureManager` class. -To do so use the dot syntax `.` — the same syntax as the one used to access fields of a structure: +To implement this method, you will need to call the `getTexture` method of the `TextureManager` class. +To do so, use the dot syntax `.` — the same syntax as the one used to access fields of a structure: ```c++ const sf::Texture* texture = textureManaged.getTexture(id); From 960a6a82dc46a3d4cef2a8c61b780be15b205506 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 13:01:36 +0000 Subject: [PATCH 115/137] language checked --- .../ClassesAndObjects/StaticMembers/task.md | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md index a3f8459..6da886b 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task.md @@ -1,67 +1,67 @@ -The `Scene` class is an _abstract class_ too — +The `Scene` class is also an _abstract class_ — it has pure virtual methods and thus cannot be instantiated. This gives us the flexibility of having different implementations of the `Scene` class. -One such implementation is given by the `GameplayStaticScene` subclass +One such implementation is provided by the `GameplayStaticScene` subclass (see files `statscene.hpp` and `statscene.cpp`). This scene implementation is called static because it contains only -static predefined number of game objects: +a static predefined number of game objects: one player object, one consumable object, and one enemy object, -which we are going to cover later in this module. -It is certainly a downgrade from our previous version implementation of the game, -when we learned how to create objects dynamically with the help of the linked lists. +which we will cover later in this module. +This is certainly a downgrade from our previous version of the game implementation, +where we learned how to create objects dynamically with the help of linked lists. Do not worry, we will restore this feature soon. But for now, let us work with the static scene. -Despite the `Scene` class is very important and does a lot of work, -like game objects maintenance and scene drawing, -by itself it is not responsible for managing the game itself. -This is the purpose of another class — `GameEngine` +While the `Scene` class is very important and does a lot of work, +like maintaining game objects and drawing scenes, +it is not responsible for managing the game itself. +This responsibility lies with another class — `GameEngine` (see files `engine.hpp` and `engine.cpp`). This class, in particular, controls the application window and the currently active scene.
Another important responsibility of the `GameEngine` class is to perform scene transitions. -As for now, this functionality would not be important for us, -since for some time we would only have one single scene (the gameplay scene). -But later in this course it will become useful, as we will start to implement other types of scenes — +For now, this functionality might not be important for us, +since we will initially have only one single scene (the gameplay scene). +However, it will become useful later in this course as we start to implement other types of scenes — for example, a scene managing the main menu of the game.
-The most important method of the `GameEngine` class is the `run()` method +The most important method of the `GameEngine` class is the `run()` method, which implements the main loop of the game. -In essence, the entry points of our game application is just -a creation of the `GameEngine` object and call to its `run()` method (see file `main.cpp`). +In essence, the entry point of our game application is just +creating a `GameEngine` object and calling its `run()` method (see file `main.cpp`). -Speaking of the creation of `GameEngine` object. -The `GameEngine` class has a certain peculiarity compared to the other classes we have seen so far — -there could exist only a single unique `GameEngine` object instance per one game instance. -We can express this in the code with the help of another C++ feature: `static` modifier. +Speaking of creating the `GameEngine` object, +the `GameEngine` class has a certain peculiarity compared to the other classes we have seen so far — +there can only exist a single unique `GameEngine` object instance per game instance. +We can express this in the code with the help of another C++ feature: the `static` modifier. First, note that the `GameEngine` class has one method that stands out from the others: -it is the `create()` method that has `static` modifier in front of it. -The `static` modifier, applied to a class member (either field or method), +the `create()` method, which has a `static` modifier in front of it. +The `static` modifier, when applied to a class member (either a field or a method), turns this member into a _static_ member. -Static members are not associated with the objects, instead they are associated with the class itself. +Static members are not associated with objects, rather they are associated with the class itself. This means that in order to access a static member, you do not need an instance of the class at hand. Instead, static members are accessed through the class name: ```c++ -// obtains an engine instance by calling static method `create` +// obtains an engine instance by calling the static method `create` GameEngine* engine = GameEngine::create(); ``` Static members provide a convenient way to associate some methods or data fields with the class itself. -For example, the `create` method shown above provides an ability to instantiate the game engine object. +For example, the `create` method shown above provides the ability to instantiate the game engine object. When applied to the declaration of local variables inside functions, -`static` modifier has a different meaning. -It allows creating a _static variable_ that survives and preserves its value between the function calls. +the `static` modifier has a different meaning. +It allows creating a _static variable_ that retains its value between function calls. Such variables are actually stored inside the static memory region of the program, -instead of the stack memory region where reside the other local variables of the function +instead of the stack memory region, where other local variables of the function reside (hence the name _static_). ```c++ @@ -78,17 +78,17 @@ std::cout << foo() << std::endl; With the help of the `static` modifier, it becomes possible to ensure that only one engine is created per each game run. -For this, it is sufficient to declare static `GameEngine` variable -inside `GameEngine::create` method and return a pointer to this variable. +To achieve this, it is sufficient to declare a static `GameEngine` variable +inside the `GameEngine::create` method and return a pointer to this variable.
Note that in this case, the address escape error does not occur. -Because the `static` variable resides in the static memory region, it lives thought the whole program execution time. +Because the `static` variable resides in the static memory region, it lives throughout the whole program execution time. Thus, it is safe to return the address of this variable from the function.
Please implement the `create` method as described above. -If you do this correctly, you will be finally able to run the refactored game application +If you do this correctly, you will finally be able to run the refactored game application and see the planet and star objects on the screen. From bb07631c7c361bcd078f5f28513497ae3aee8275 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 14:05:28 +0000 Subject: [PATCH 116/137] language checked --- .../MemoryOwnership/CopyConstructor/task.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md index ba159c0..ac9c4a3 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task.md @@ -1,9 +1,9 @@ - In C++, the class can define a special kind of a constructor, + In C++, a class can define a special kind of constructor, called the _copy constructor_. -For example, consider the class `int_array` which represents +For example, consider the `int_array` class, which represents a dynamically allocated array of integers -(see complete definition of the class in the file `include/int_array.hpp`): +(see the complete definition of the class in the file `include/int_array.hpp`): This class has two fields: a pointer to the allocated array and its size. It also defines the default constructor creating an empty array, @@ -12,18 +12,18 @@ The destructor of the class deallocates the array.
Note that we do not check for the null pointer in the destructor, - as deleting `nullptr` is a safe operation that has no effect. + as deleting a `nullptr` is a safe operation that has no effect.
To effectively work with an array, the class also -defines `size()` method to query for the size of the array, -overloads the array subscript operators to provide the access to the underlying array, +defines a `size()` method to query for the size of the array, +overloads the array subscript operators to provide access to the underlying array, and overloads the printing operator to display the contents of the array.
Note that there are two overloads of array subscript operator: -one for a mutable array, and one for an immutable array. +one for a mutable array and one for an immutable array.
@@ -36,12 +36,12 @@ for (size_t i = 0; i < a.size(); ++i) { } ``` -It now might want to create a copy of this array. -Of course, one can do that manually by creating an array of suitable size -and assigning elements in the loop. +They might want to create a copy of this array. +Of course, one can do that manually by creating an array of a suitable size +and assigning elements in a loop. -However, C++ provides a more convenient way to do so. -It is possible to define a special _copy constructor_: +However, C++ provides a more convenient way to do this, +by defining a special _copy constructor_: ```c++ int_array(const int_array& other) @@ -61,9 +61,9 @@ int_array b(a); ``` Another possible use-case scenario: given two existing arrays, -a user might want to re-assign one of them, copying the elements of the other. +a user might want to reassign one of them, copying the elements of the other. The C++ language has a tool for that too! -It is called the _copy assignment operator_, and can be declared as follows: +It is called the _copy assignment operator_, which can be declared as follows: ```c++ int_array& operator=(const int_array& other) { @@ -83,13 +83,13 @@ int_array& operator=(const int_array& other) {
The first `if` statement in the implementation of the operator -handles the case of self-assignment — in this case the method simply returns. +handles the case of self-assignment — in this case, the method simply returns.
-Note that the assignment operator returns reference `int_array&` +Note that the assignment operator returns a reference `int_array&` to the object itself as a result. It is required to support multiple assignments syntax, _e.g._: From 53f82127b729d8dd1585ada2514e16761cde32cf Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 14:11:24 +0000 Subject: [PATCH 117/137] language checked --- .../MemoryOwnership/Introduction/task.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md index e40bcab..181bb24 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md @@ -1,10 +1,10 @@ The next few lessons are dedicated to the important concept of _ownership_, which defines the rules for managing the lifecycle of resources, such as allocated memory blocks. -The concept of ownership is a crucial part of modern C++ language, +The concept of ownership is a crucial part of the modern C++ language, and mastering it is an important step towards writing safe C++ programs. -Here we are going to cover the following topics related to ownership: +Here, we are going to cover the following topics related to ownership: * Lifetime of objects. * `new` and `delete` operators. From ace8ae21862f0a90f4b3b96c803f53197349c89f Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 15:43:34 +0000 Subject: [PATCH 118/137] language checked --- .../MemoryOwnership/MoveSemantics/task.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md index 9620421..49931c3 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task.md @@ -6,16 +6,16 @@ and avoiding unnecessary memory allocations. Before diving into the move semantics details, let us briefly understand the concept of [value categories](https://en.cppreference.com/w/cpp/language/value_category). -* __lvalue__ expression represents an object that has a name or an identifier. +* An __lvalue__ expression represents an object that has a name or an identifier. It refers to something that exists in memory and typically persists beyond a single expression. It usually stands on the left-hand side of an assignment operator (`=`), hence the name __lvalue__. -* __rvalue__ expression represents a temporary or disposable value. +* An __rvalue__ expression represents a temporary or disposable value. It is usually the intermediate result of some computation that might not have a named memory location. It usually stands on the right-hand side of an assignment operator (`=`), hence the name __rvalue__. -For example, below variables `a`, `b`, and `c` are lvalues, -while expression `a + b` is an rvalue. +For example, below, variables `a`, `b`, and `c` are lvalues, +while the expression `a + b` is an rvalue. ```c++ int a = 2; @@ -25,7 +25,7 @@ int c = a + b; Move semantics utilize rvalues, representing temporary objects or objects about to be destroyed. -When another object wants to copy a soon-to-be disposed rvalue object, +When another object wants to copy a soon-to-be-disposed rvalue object, instead of actual copying, the contents of the rvalue object can be moved. For example, recall the `int_array` class, @@ -51,7 +51,7 @@ private: }; ``` -Suppose there is a function that creates an array filled with the given value: +Suppose there is a function that creates an array filled with a given value: ```c++ int_array create_array(int value, size_t size) { @@ -77,14 +77,14 @@ In the code above, unnecessary copying is performed, which copies an array from an object returned from the function into the newly created object. -However, because the returned object is actually a temporary rvalue -which is going to be disposed anyway, +However, since the returned object is actually a temporary rvalue +that is going to be disposed of anyway, we can take advantage of that and instead of copying the array, just _move_ the pointer. To do that, in addition to the copy constructor, -one can define the _move constructor_, -which takes as an argument rvalue reference denoted with `&&`: +one can define a _move constructor_, +which takes an rvalue reference, denoted with `&&`, as an argument: ```c++ class int_array { @@ -105,16 +105,16 @@ private: ``` Note that in addition to copying the pointer, -the move constructor of `int_array` class also nullifies +the move constructor of the `int_array` class also nullifies the pointer in the original object passed by rvalue reference. -It is necessary because otherwise, once the destructor +It is necessary because, otherwise, once the destructor of the original object is called, it would deallocate the memory -pointed-to by `data` field. +pointed-to by the `data` field. This way, the given move constructor implementation reflects the occurring ownership transfer. -Similarly to copy assignment operator, -one can also define move assignment operator for a class: +In a similar way to the copy assignment operator, +one can also define a move assignment operator for a class: ```c++ class int_array { @@ -132,7 +132,7 @@ private: With the help of the move assignment operator and the special standard function `std::move`, -one can manually transfer the ownership from one object to another: +one can manually transfer ownership from one object to another: ```c++ int_array b = create_array(1, 4); From 7aa26995091bbef9a8995408f6fc33e62efc38be Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 15:58:49 +0000 Subject: [PATCH 119/137] language checked --- .../NewAndDeleteOperators/task.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md index cfb6806..c56e0de 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md @@ -1,7 +1,7 @@ -Recall that in `Memory Management` module of this course, -we studied the `malloc` and `free` functions which are used +Recall that in the `Memory Management` module of this course, +we studied the `malloc` and `free` functions, which are used to allocate and deallocate memory. -As we mentioned, these functions implement the C-style memory management, +As we mentioned, these functions implement C-style memory management, and C++ has its own tools to manage memory. It is finally time to meet these tools. @@ -18,7 +18,7 @@ The `delete` operator releases the memory back to the heap: delete ptr; ``` -To allocate an array of a certain type, operator `new[]` is used: +To allocate an array of a certain type, the `new[]` operator is used: ```cpp int* array = new int[10]; @@ -34,12 +34,12 @@ delete[] array; What is the difference between `malloc/free` and `new/delete`? -The most important one is that the `new` and `delete` operators -call the constructor and destructor correspondingly. -The `malloc` and `free` function do not call constructors or destructors, +The most important difference is that the `new` and `delete` operators +call the constructor and destructor, respectively. +The `malloc` and `free` functions do not call constructors or destructors; they are used merely to allocate raw memory blocks. -To witness the difference between the two, we ask you to complete the following task. -Given the `Book` class defined in `book.hpp` file, -create an object of this class using `new`/`delete` syntax in `newAndDeleteBook` function, -and then try to create an object using `malloc`/`free` syntax in `mallocAndFreeBook` function. \ No newline at end of file +To highlight the difference between the two, we ask you to complete the following task. +Given the `Book` class defined in the `book.hpp` file, +create an object of this class using `new`/`delete` syntax in the `newAndDeleteBook` function, +and then try to create an object using `malloc`/`free` syntax in the `mallocAndFreeBook` function. \ No newline at end of file From b4c07e6f80fb56e4b2c6df3c57fa4d63cea2858b Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 16:09:05 +0000 Subject: [PATCH 120/137] language checked --- .../MemoryOwnership/ObjectLifetime/task.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md index 68eda6a..97c4a15 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task.md @@ -1,15 +1,15 @@ As we remember, the constructor and destructor are mandatory elements of any class, and they are responsible for creating and destroying an object. -For any object, the period of time between the creation of the object -through one of its constructors and its destruction through destructor +For any object, the period of time between its creation +through one of its constructors and its destruction through the destructor is called the [_lifetime_](https://en.cppreference.com/w/cpp/language/lifetime) of this object. -The concepts of object's lifetime and +The concepts of an object's lifetime and [_storage duration_](https://en.cppreference.com/w/cpp/language/storage_duration) are related, but they mean different things. Object lifetime refers to the object itself, while storage duration refers to the memory allocated for it. The storage duration is the time between the allocation of a memory region and its deallocation, -while the lifetime is the time between the construction of an object and its destruction. +while lifetime is the time between the construction of an object and its destruction. Object lifetime is equal to or less than the lifetime of its storage. -Two objects residing in the memory region with the same storage duration can have +Two objects residing in a memory region with the same storage duration can have different non-overlapping lifetimes. \ No newline at end of file From 255b70bbcedee1d6f5cc63f2c8ed936b74736d96 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Fri, 5 Jan 2024 16:29:30 +0000 Subject: [PATCH 121/137] language checked --- .../MemoryOwnership/Ownership/task.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md index 691c23c..2754f8d 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task.md @@ -12,7 +12,7 @@ and the memory they occupy is automatically managed by the parent object's lifec
-Note that constructors and destructors are called in specific order, +Note that constructors and destructors are called in a specific order, and, moreover, the order of destruction is the reverse order of initialization. For example, consider the following class hierarchy: @@ -44,8 +44,8 @@ The order of construction of a `Laptop` object would be as follows: 3. finally, the `Laptop` constructor would be called. The order of destruction is the opposite: -1. first the derived class destructor `Laptop` would be called; -2. then the sub-objects destructors would be called: +1. first, the derived class destructor `Laptop` would be called; +2. then the sub-objects' destructors would be called: * the `RandomAccessMemory` destructor; * the `Processor` destructor; * the `Motherboard` destructor; @@ -55,7 +55,7 @@ The order of destruction is the opposite: Objects stored in pointer-typed fields are, by default, considered to be non-owned. When a class contains a pointer to an object, -the responsibility for lifetime and storage duration management lies outside the class. +the responsibility for its lifetime and storage duration management lies outside the class. It is crucial to understand this fact in order to prevent memory leaks and undefined behavior, as ownership is not automatically transferred with the assignment of pointers. @@ -69,7 +69,7 @@ public: : laptop(laptop) {} // the destructor does not destroy the object - // pointed-by laptop field by default + // pointed to by the laptop field by default ~Student() = default; }; ``` @@ -79,7 +79,7 @@ a pointer to a dynamically allocated `Laptop` object. The ownership responsibility for the `Laptop` object may or may not be assigned to the `Student` class depending on the desired semantics. If the desired semantics is that the `Student` takes -the ownership of the `laptop` object passed to it in the constructor, +ownership of the `laptop` object passed to it in the constructor, then the developer of this class must ensure that the object is destroyed manually in the destructor (for example, by using the `delete` operator). @@ -92,8 +92,8 @@ In the scenarios where ownership needs to be transferred, [_smart pointers_](https://en.wikipedia.org/wiki/Smart_pointer) such as `std::unique_ptr` and `std::shared_ptr` can be used, providing automated memory management with reduced risks of memory-related issues. -These smart pointers are specialized classes defined in the standard library of the C++. +These smart pointers are specialized classes defined in the standard library of C++. They behave like plain pointers, in a sense that they support the same set of operations, like the dereferencing, -but in addition they provide specific ownership semantics. +but in addition, they provide specific ownership semantics. We will discuss smart pointers in more detail in the next few lessons. \ No newline at end of file From 185b650b3d4d0f96c4a533d9b8d9e0c8f4375783 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Mon, 8 Jan 2024 10:40:32 +0000 Subject: [PATCH 122/137] language checked --- .../MemoryOwnership/PlacementNew/task.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md index b6c0775..08b1ffe 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task.md @@ -1,19 +1,19 @@ -Placement `new` operator is a special version of the `new` operator -that allows to construct an object in a pre-allocated memory region. -This can be useful for various reasons, +The placement `new` operator is a special version of the `new` operator. +It allows for the construction of an object in a pre-allocated memory region. +This can be useful in various scenarios, such as reducing memory fragmentation and improving performance. To use the placement `new` operator, -one first has to allocate a memory region of the appropriate size. -Then, this memory region needs to be passed into placement `new` operator. -The operator will then construct an object of the specified type in the given memory region. +one first has to allocate a memory region of appropriate size. +Then, this memory region needs to be passed into the placement `new` operator. +The operator will then construct an object of the specified type within the given memory region. When an object created with the help of the placement `new` operator is no longer needed, it must be destroyed by explicitly calling the destructor. -When working with arrays, the destructor for each object in the array should be called. +When working with arrays, the destructor should be called for each object in the array. Here is an example of how to use the placement `new` operator to construct -an integer object in a pre-allocated memory region: +an integer object within a pre-allocated memory region: ```cpp #include @@ -43,15 +43,15 @@ int main() { } ``` -With the help of the placement `new` operator it is possible to -fit into the same storage duration lifetimes of several objects. +With the help of the placement `new` operator, it is possible to +fit the lifetimes of several objects within the same storage duration. For example, when you reuse a memory region to construct a new object, the lifetime of the previous object ends, and the lifetime of the new object begins. However, the storage duration of the memory region remains the same. -In order to finish this task, please implement the following functions. +In order to complete this task, please implement the following functions. -The `createCat` function should create the `Cat` object with the name `"Tom"` in the given memory block: +The `createCat` function should create a `Cat` object with the name `"Tom"` in the given memory block: ```c++ Cat* createCat(char* memory); @@ -63,7 +63,7 @@ The `destroyCat` function should destroy the `Cat` object residing in the given void destroyCat(char* memory); ``` -The `createMouse` function should create the `Mouse` object with the name `"Jerry"` in the given memory block: +The `createMouse` function should create a `Mouse` object with the name `"Jerry"` in the given memory block: ```c++ Mouse* createMouse(char* memory); From 24c582f0f60494a75ee5ac501732357987a64ef6 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Mon, 8 Jan 2024 11:26:15 +0000 Subject: [PATCH 123/137] language checked --- .../MemoryOwnership/RAIICopySwapIdiom/task.md | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md index 6803434..91ef021 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task.md @@ -2,22 +2,22 @@ Concluding the presentation of the ownership model in the C++ language, we need to discuss several essential concepts and idioms. [_Resource Acquisition Is Initialization_](https://en.cppreference.com/w/cpp/language/raii), -__RAII__ for short, is a programming technique which involves linking a resource -(such as dynamically allocated memory block, open file, network connection, _etc._) +or __RAII__ for short, is a programming technique that involves linking a resource +(such as a dynamically allocated memory block, an open file, a network connection, _etc._) to the lifetime of an object. According to this idiom: 1. the constructor of the class should allocate and initialize - all the resources that the object will own during its lifetime; + all the resources the object will own during its lifetime; 2. the copy constructor and copy assignment operator of the class - should copy the resource, + should copy the resource; if the resource is not copyable, then this constructor should be deleted; 3. the move constructor and move assignment operator of the class should transfer the ownership of the resource; 4. the destructor of the class should release all the resources that the object owns. -[**Rule of five**](https://en.cppreference.com/w/cpp/language/rule_of_three) -is a related concept, which states that if a class requires a custom +The [**Rule of five**](https://en.cppreference.com/w/cpp/language/rule_of_three) +is a related concept; it states that if a class requires a custom 1. destructor 2. copy constructor @@ -27,13 +27,13 @@ is a related concept, which states that if a class requires a custom then it actually requires all five. -Note that if your class does not define none of these methods, +Note that if your class does not define any of these methods, the compiler will generate default implementations for them: -* default copy constructor/assignment performs a shallow copy of the object, - invoking copy constructor/assignment of its member fields; -* default move constructor/assignment performs elementwise move of the class' members, - invoking move constructor/assignment of its member fields; +* the default copy constructor/assignment performs a shallow copy of the object, + invoking the copy constructor/assignment of its member fields; +* the default move constructor/assignment performs an elementwise move of the class' members, + invoking the move constructor/assignment of its member fields; * additionally, if there is no custom constructor, the default constructor is automatically generated — it initializes all member fields to their default values. @@ -57,8 +57,8 @@ This might be useful when you have defined a custom implementation for one of the "five" methods, but still want to use the default implementation for others. -On the opposite, you might explicitly forbid auto-generating -any of these methods using the `= delete` syntax: +On the other hand, you might want to explicitly forbid auto-generating +any of these methods, using the `= delete` syntax: ```c++ class X { @@ -74,15 +74,15 @@ public: ``` For example, this can be useful in cases when the class should not be copyable. -In such a scenario, you can define move constructor and assignment operator, +In such a scenario, you can define the move constructor and assignment operator, but delete the copy constructor and copy assignment operator.
-Before the [C++11 edition](https://en.cppreference.com/w/cpp/11) +Before the advent of the [C++11 edition](https://en.cppreference.com/w/cpp/11) of the language, the rule of five was known as the _rule of three_. -This is because before the C++11 the language did not have the feature of move semantics, +This is because before C++11, the language did not have the feature of move semantics, so it was not possible to define the move constructor and move assignment operator.
@@ -102,8 +102,8 @@ int_array() ``` Another way to look at the default constructor -is as if it creates an array in special "empty" state — -this point of view will become handy later. +is that it creates an array in a special "empty" state — +this point of view will become handy later on. Another constructor of the class creates an array of the given size by actually allocating the memory for it: @@ -124,8 +124,8 @@ As such, the destructor has to deallocate this memory: ``` Now, the rule of five dictates that the class should also define -custom copy constructor, move constructor, as well as copy and move assignment operators. -We have already seen these constructors in the previous lessons: +custom copy and move constructors, as well as copy and move assignment operators. +We have already seen these constructors in previous lessons: ```c++ int_array(const int_array& other) @@ -146,18 +146,18 @@ int_array(int_array&& other) }; ``` -Notice how the move constructor resets the argument object into an "empty" state. -Calling the destructor on this "empty" object later will effectively have no effect. +Notice how the move constructor resets the argument object to an "empty" state. +Calling the destructor on this "empty" object later will essentially have no effect. Validly so, as the ownership of the resource has been transferred to the new object. Next, we need to define the assignment operators. -To avoid code duplication between copy constructor and copy assignment operator -(and similarly between move constructor and move assignment operator), +To avoid code duplication between the copy constructor and copy assignment operator +(and similarly, between the move constructor and move assignment operator), it is possible to use another clever trick called the [__Copy-and-Swap idiom__](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap). With the help of this idiom, it is sufficient to define -just a single version of assignment operator, +just a single version of the assignment operator, which should take the argument by value: ```c++ @@ -168,15 +168,15 @@ int_array& operator=(int_array other) { ``` Depending on whether the operator is called with -an lvalue (taken as `const int_array&`) or rvalue (taken as `int_array&&`) +an lvalue (taken as `const int_array&`) or an rvalue (taken as `int_array&&`) as an argument, the copy constructor or move constructor will create a local copy (`int_array`) of the data in the `other` variable. Then, with the help of the swap function (see definition below), -the assignment operator swaps the data of the current object with the data of the local copy. +the assignment operator swaps the data of the current object with that of the local copy. The temporary local copy then destructs, releasing the old data along the way. -The `swap` function performs an exchange of the data between two objects, -with the help of the standard function `std::swap` capable of swapping the variables of primitive types: +The `swap` function performs an exchange of data between two objects, +with the help of the standard function `std::swap`, which is capable of swapping variables of primitive types: ```c++ void swap(int_array& other) { From 955e08e86958001fce3e7b528eca2d39f6d8c7d4 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Mon, 8 Jan 2024 11:45:54 +0000 Subject: [PATCH 124/137] language checked --- .../MemoryOwnership/SharedPtr/task.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md index 84cb758..09cd4b2 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task.md @@ -14,7 +14,7 @@ It increments the reference counter. The underlying object is only deallocated when the last shared pointer releases its ownership. -Let us have a look at the example of `std::shared_ptr` usage: +Let us have a look at an example of `std::shared_ptr` usage: ```c++ void test() { @@ -27,39 +27,39 @@ void test() { // both dog and copy share ownership of the same object std::cout << *dog << " " << *copy << "\n"; // one can query the count of shared pointers - // pointing-to the given object + // pointing to the given object std::cout << "dog.use_count() = " << dog.use_count() << "\n"; - // similarly to std::unique_ptr, it is possible to obtain a plain pointer + // similar to std::unique_ptr, it is possible to obtain a plain pointer std::cout << dog.get() << "\n"; // when the function exits, - // destructors of both shared pointers is called, + // destructors of both shared pointers are called, // dropping the reference count to 0 and thus // triggering the deallocation of the pointed-to Dog object } ``` -As an exercise, let iss develop a simple chat system. +As an exercise, let us develop a simple chat system. It consists of two classes: `Chat` and `User` (see file `include/chat.hpp`). -Each user has a shared pointer to the chat object it is currently logged in. -Your task is to write implementation of the following methods of the `User` class. +Each user has a shared pointer to the chat object in which they are currently logged in. +Your task is to write the implementation of the following methods for the `User` class. ```c++ void createNewChat(std::string name); ``` * The `createNewChat` method should create a new chat with the given name - and log in the user inside. + and log the user into it.
-To create a new object pointed-by a shared pointer +To create a new object pointed to by a shared pointer, use the function `std::make_shared`
-To assign a unique identifier to the newly create `Chat` object, +To assign a unique identifier to the newly created `Chat` object, use the static field `nextChatId`.
@@ -68,11 +68,11 @@ use the static field `nextChatId`. void joinChatByInvite(const User& user); ``` -* The `joinChatByInvite` method should log in the user into the chat of another user - (by re-assigning its chat pointer). +* The `joinChatByInvite` method should log the user into another user's chat + (by reassigning its chat pointer). ```c++ void leaveChat(); ``` -* The `leaveChat` method should log out the user from the chat. \ No newline at end of file +* The `leaveChat` method should log the user out of the chat. \ No newline at end of file From a63c1938952b96feb7580a8a8162c874542df0f8 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Mon, 8 Jan 2024 12:21:41 +0000 Subject: [PATCH 125/137] language checked --- .../MemoryOwnership/UniquePtr/task.md | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md index f97e1be..8e3e098 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task.md @@ -1,8 +1,8 @@ The first kind of smart pointers in C++ we are going to look at is -the [std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr). +[std::unique_ptr](https://en.cppreference.com/w/cpp/memory/unique_ptr). This smart pointer is designed to manage the lifetime of -dynamically allocated objects, and it provides exclusive ownership semantics. +dynamically allocated objects and provides exclusive ownership semantics. The unique ownership model ensures that at any given time, only one `std::unique_ptr` instance owns a particular dynamically allocated object. When the owning `std::unique_ptr` is destroyed or explicitly reset, @@ -10,15 +10,15 @@ the pointed-to object is automatically destroyed and the associated memory is deallocated. Another advantage of `std::unique_ptr` is that it helps to prevent -memory errors, such as memory leaks and double deletion. +memory errors, such as memory leaks and double deletions. The `std::unique_ptr` ensures that an object will be deleted -when it is no longer needed, and that it will be deleted only once. +when it is no longer needed and that it will be deleted only once. -In addition, usage of `std::unique_ptr` improves code readability: -by making clear what pointer owns an object and is responsible for deleting it. +In addition, the usage of `std::unique_ptr` improves code readability +by making clear which pointer owns an object and is responsible for deleting it. -Let us look at the examples. -Suppose we have a class `Dog` defined as follows. +Let us look at some examples. +Suppose we have a class `Dog` defined as follows: ```c++ class Dog { @@ -41,30 +41,30 @@ void makeBark() { // creating a unique pointer std::unique_ptr dog = std::make_unique("Snoopy"); // you can test if the unique pointer is a null pointer, - // similarly as you would do with a plain pointer + // similar to how you would do with a plain pointer if (!dog) { return; } // unique pointer can be used - // similarly to how plain pointers are used + // similar to how plain pointers are used dog->bark(); // when the function exits, // the unique_ptr automatically destroys - // the Dog object and deallocated memory + // the Dog object and deallocated the memory } ```
-How the automated destruction of the pointed-to object -by the `std::unique_ptr` is achieved? -In fact, the implementation of the `std::unique_ptr` class -in the standard library simply overrides the destructor of this class. +How is automated destruction of the pointed-to object +by the `std::unique_ptr` achieved? +In fact, the standard library's implementation of the `std::unique_ptr` class +simply overrides the destructor of this class.
Alternatively, one can explicitly reset the pointer, -and thus trigger the deletion of the pointed-to object: +thus triggering the deletion of the pointed-to object: ```c++ std::unique_ptr dog = std::make_unique("Snoopy"); @@ -87,35 +87,35 @@ Dog* dog = new Dog("Snoopy"); std::unique_ptr smartDog(dog); ``` -vice versa, it is possible to take the pointer together with the ownership -out of the `std::unique_ptr` command: +Conversely, it is possible to remove the pointer together with its ownership +from the `std::unique_ptr` command: ```c++ std::unique_ptr smartDog = std::make_unique("Snoopy"); -// Dog object is transferred to the plain pointer, -// it should be manually deleted eventually, +// the Dog object is transferred to the plain pointer, +// it should be manually deleted eventually // because the unique pointer would not do that Dog* dog = smartDog.release(); // after release, the unique pointer is in the null state assert(smartDog == nullptr); ``` -It is possible to obtain a plain pointer without releasing the ownership. +It is possible to obtain a plain pointer without releasing ownership. However, the plain pointer should not outlive the unique pointer; otherwise, it can result in a use-after-free error. ```c++ std::unique_ptr smartDog = std::make_unique("Snoopy"); -// now the dog and smartDog point-to the same object; -// the ownership still belongs to the smartDog pointer +// now the dog and smartDog point to the same object; +// ownership still belongs to the smartDog pointer Dog* dog = smartDog.get(); ```
The `get()` method is typically used to pass a unique pointer -into a function expecting the plain pointer. -It is often the case when C++ code needs to interact with the C libraries. +to a function expecting a plain pointer. +This is often the case when C++ code needs to interact with C libraries.
@@ -123,11 +123,11 @@ Note that by converting plain pointers to unique pointers and vice versa, using the methods given above, you might accidentally create two unique pointers pointing to the same object, thus violating the ownership rules of `std::unique_ptr`. -This situation would result in undefined behavior: +This would result in undefined behavior: ```c++ std::unique_ptr dog = std::make_unique("Snoopy"); -// the dog and anotherDog point-to the same object, +// the dog and anotherDog point to the same object, // and both incorrectly assume unique ownership of the object; // thus both can attempt to destroy the object, // leading to undefined behavior. @@ -136,11 +136,11 @@ std::unique_ptr anotherDog(dog.get()); This is why you should be extremely careful when converting between plain and unique pointers. -As a rule of thumb, try to complete avoid these conversions in your code. +As a rule of thumb, try to completely avoid these conversions in your code. -By default, the `std::unique_ptr` attempts to prevent such misuses +By default, `std::unique_ptr` attempts to prevent such misuses and enforce the single ownership rule. -This is why, for example, the copy constructor of the `std::unique_ptr` is disabled: +This is why, for example, the copy constructor of `std::unique_ptr` is disabled: ```c++ std::unique_ptr dog = std::make_unique("Snoopy"); @@ -149,12 +149,12 @@ std::unique_ptr anotherDog = dog; ``` In order to consolidate the material of this lesson, -finish the implementation of `copy` function. -This function takes as an argument an array of integers, -given as a plain pointer and a size, -and it should return a copied array as a unique pointer. +complete the implementation of the `copy` function. +This function takes an array of integers as an argument, +given as a plain pointer and its size, +and should return a copied array as a unique pointer. If the given array size is `0`, -then the function should return the null pointer. +then the function should return a null pointer.
From 3af869f16b96624a63182f7a7e64776aed2e60d8 Mon Sep 17 00:00:00 2001 From: Mikhail Oshukov Date: Mon, 8 Jan 2024 12:37:17 +0000 Subject: [PATCH 126/137] language checked --- .../MemoryOwnership/WeakPtr/task.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md index 80688ed..99aa48a 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task.md @@ -2,19 +2,19 @@ In C++ memory ownership, [std::weak_ptr](https://en.cppreference.com/w/cpp/memory/weak_ptr) stands out as a tool for managing transient ownership without affecting the object's lifetime. While `std::shared_ptr` enables shared ownership, -`std::weak_ptr` complements it by providing a non-intrusive observer-like role. +`std::weak_ptr` complements this by providing a non-intrusive, observer-like role. This pointer does not contribute to the object's reference count, -allowing for the detection of object state without extending its lifetime. +allowing for the detection of the object state without extending its lifetime. -The main use case of `std::weak_ptr` is to break the circular reference cycles, -which can lead to memory leaks otherwise. +The main use case of `std::weak_ptr` is to break circular reference cycles, +which could otherwise lead to memory leaks. Going back the chat example from the previous lesson, suppose we want to extend the `Chat` class -and add a possibility to assign a host to the chat. +and add the possibility of assigning a host to the chat. For this purpose, we might rework the `Chat` class as follows -(also see `include/chat.hpp` file): +(also see the `include/chat.hpp` file): ```c++ class Chat { @@ -33,7 +33,7 @@ private: }; ``` -Also, now, instead of `User` method `createNewChat`, +Also, now, instead of the `User` method `createNewChat`, we would declare a function with the same name: ```c++ @@ -41,34 +41,34 @@ std::shared_ptr createNewChat(std::string name, const std::shared_ptrhost` to `host`, and `host->chat` to `chat`. This would result in a reference cycle. -As long as `chat` and `host` store the shared pointers to each other, +As long as `chat` and `host` store shared pointers to each other, both their reference counters cannot drop below `1`. -It means that the object will never be deallocated — -in other words, we got the memory leak! +This means that the object will never be deallocated — +in other words, we got a memory leak! In order to avoid this, we need to use `std::weak_ptr` to break the reference cycle. In particular, the `host` field of the `Chat` object should be declared as a weak pointer. Note that the method `getHost` of the `Chat` class should still return -the shared pointer: +a shared pointer: ```c++ inline std::shared_ptr getHost() const; ``` -To achieve this, you need to use the `lock()` method of the `std::weak_ptr`. -This method creates new `std::shared_ptr` pointing to the same object +To achieve this, you need to use the `lock()` method of `std::weak_ptr`. +This method creates a new `std::shared_ptr` pointing to the same object as the given `std::weak_ptr`, if it still exists; otherwise, it returns an empty `std::shared_ptr`. -To finish this lesson, please fix the implementation of +To complete this lesson, please fix the implementation of the `Chat` class (see file `include/chat.hpp`) -by using `std::weak_ptr` instead of the `std::shared_ptr` for the `host` field. -Also, provide an implementation of the `createNewChat` function +by using `std::weak_ptr` instead of `std::shared_ptr` for the `host` field. +Also, provide an implementation for the `createNewChat` function (see file `task.cpp` ). \ No newline at end of file From 8b0f2897cf5721b62ff18211f1d26d2f26855ae4 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 19 Jan 2024 13:57:32 +0100 Subject: [PATCH 127/137] fix OperatorsOverloading task CMakeLists file (add `prepare_sfml_framework_lesson_task` configuration step) - might be the cause of the #EDC-944 Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/Introduction/CMakeLists.txt | 3 --- .../OperatorsOverloading/CMakeLists.txt | 10 ++++++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt index 86cf263..3af07ae 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/CMakeLists.txt @@ -13,9 +13,6 @@ set(SRC src/operators.cpp src/utils.cpp ) -set(TEST - test/test.cpp) - add_executable(${PROJECT_NAME}-run ${SRC}) prepare_sfml_framework_lesson_task( diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt index 5a241de..ae24761 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/CMakeLists.txt @@ -19,6 +19,12 @@ set(TASK set(TEST test/test.cpp) -include_directories(${CMAKE_SOURCE_DIR}/include/) +add_executable(${PROJECT_NAME}-run ${SRC}) -configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) \ No newline at end of file +configure_test_target(${PROJECT_NAME}-test "${SRC}" ${TEST}) + +prepare_sfml_framework_lesson_task( + "${CMAKE_CURRENT_SOURCE_DIR}/.." + ${PROJECT_NAME}-run + ${PROJECT_NAME}-test +) From d8e782ca4c25a3f34c838442d7857864a0dda353 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 19 Jan 2024 15:47:42 +0100 Subject: [PATCH 128/137] fix SharedPtr task test * might be the cause of #EDC-946 Signed-off-by: Evgeniy Moiseenko --- .../MemoryOwnership/SharedPtr/test/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp index c51ec89..393df01 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/test/test.cpp @@ -7,7 +7,7 @@ TEST(UserTest, CreateNewChatTest) { User bob("Bob"); bob.createNewChat("Chat"); ASSERT_NE(nullptr, bob.getChat()); - ASSERT_EQ("Test", bob.getChat()->getName()); + ASSERT_EQ("Chat", bob.getChat()->getName()); ASSERT_EQ(1, bob.getChat().use_count()); std::shared_ptr chat = bob.getChat(); From 4d8d34939e50fbc481784241d04ab60992942413 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 19 Jan 2024 15:53:10 +0100 Subject: [PATCH 129/137] fix CMakeLists.txt project names Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/CollisionsRevisited/task-info.yaml | 4 ++-- .../ClassesAndObjects/Encapsulation/task-info.yaml | 2 +- .../ClassesAndObjects/Inheritance/task-info.yaml | 4 ++-- .../ClassesAndObjects/IntroducingObjects/task-info.yaml | 4 ++-- .../ClassesAndObjects/NewChallenge/task-info.yaml | 4 ++-- .../ClassesAndObjects/NewDynamics/task-info.yaml | 4 ++-- .../ClassesAndObjects/OperatorsOverloading/task-info.yaml | 4 ++-- .../ClassesAndObjects/Polymorphism/task-info.yaml | 4 ++-- .../MemoryOwnership/ObjectLifetime/CMakeLists.txt | 2 +- .../MemoryOwnership/SmartPointersSummary/CMakeLists.txt | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml index 423d8de..c50488d 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-info.yaml @@ -39,8 +39,8 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/main.cpp visible: true editable: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml index b08adbf..4089f01 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-info.yaml @@ -43,4 +43,4 @@ files: - name: CMakeLists.txt visible: false - name: test/test.cpp - visible: false \ No newline at end of file + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml index e6c0982..2d169a0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-info.yaml @@ -39,8 +39,8 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/main.cpp visible: true editable: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml index 329091b..5295a52 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-info.yaml @@ -39,8 +39,8 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/main.cpp visible: true editable: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml index 68b4c68..49bd412 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-info.yaml @@ -39,8 +39,8 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/main.cpp visible: true editable: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml index 1ca3e09..674f828 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-info.yaml @@ -39,8 +39,8 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/main.cpp visible: true editable: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml index d648128..11b32b3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-info.yaml @@ -39,8 +39,8 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/main.cpp visible: true editable: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml index 3f838d5..be282b3 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-info.yaml @@ -39,8 +39,8 @@ files: visible: true - name: CMakeLists.txt visible: false -- name: test/test.cpp - visible: false - name: src/main.cpp visible: true editable: false +- name: test/test.cpp + visible: false diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt index c5c7dc3..0576868 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.26) -project(ObjectOrientedProgramming-MemoryOwnership-Object_Lifetime) +project(ObjectOrientedProgramming-MemoryOwnership-ObjectLifetime) set(CMAKE_CXX_STANDARD 14) diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt index 580c7d3..5d20ce4 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.26) -project(ObjectOrientedProgramming-MemoryOwnership-Smart_pointers_summary) +project(ObjectOrientedProgramming-MemoryOwnership-SmartPointersSummary) set(CMAKE_CXX_STANDARD 14) From cdbc70d92408f7a33ac479e1b5acbe99b7d583b5 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 19 Jan 2024 16:23:53 +0100 Subject: [PATCH 130/137] minor re-wording in some tasks Signed-off-by: Evgeniy Moiseenko --- ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md | 2 +- .../MemoryOwnership/NewAndDeleteOperators/task.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md index 181bb24..ab167d3 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task.md @@ -8,7 +8,7 @@ Here, we are going to cover the following topics related to ownership: * Lifetime of objects. * `new` and `delete` operators. -* Placement `new`. +* Placement `new` operator. * Ownership and move semantics. * Smart pointers: `unique_ptr`, `shared_ptr`, and `weak_ptr`. * Resource Acquisition Is Initialization (RAII) idiom. diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md index c56e0de..1db6309 100644 --- a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task.md @@ -42,4 +42,4 @@ they are used merely to allocate raw memory blocks. To highlight the difference between the two, we ask you to complete the following task. Given the `Book` class defined in the `book.hpp` file, create an object of this class using `new`/`delete` syntax in the `newAndDeleteBook` function, -and then try to create an object using `malloc`/`free` syntax in the `mallocAndFreeBook` function. \ No newline at end of file +and then try to allocate memory for an object using `malloc`/`free` syntax in the `mallocAndFreeBook` function. \ No newline at end of file From 9d564ca6ec5606c6a0b81cb334fe9b88292950dd Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Fri, 19 Jan 2024 16:47:07 +0100 Subject: [PATCH 131/137] fix UpdateVelocityChangedTest - should resolve EDC-945 Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/NewChallenge/test/test.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp index 3b795bf..149906c 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/test/test.cpp @@ -29,14 +29,18 @@ std::string updateVelocity_error_msg(GameObject* object, Point2D velocity) { TEST(EnemyObjectTest, UpdateVelocityChangedTest) { TestEnemyObject object = TestEnemyObject(); + // set some initial velocity, which does not satisfy + // the constraints specified in the task description; + // thus the call to `updateVelocity` should change it to some other value + Point2D initialVelocity = { -5.0f, -5.0f }; object.performSetStatus(GameObjectStatus::NORMAL); - object.performSetVelocity(Point2D { 0.0f, 0.0f }); + object.performSetVelocity(initialVelocity); TestEnemyObject actual = object; actual.performUpdateVelocity(); Point2D velocity = actual.getVelocity(); - ASSERT_FALSE(velocity.x == 0.0f && velocity.y == 0.0f) + ASSERT_FALSE(velocity.x == initialVelocity.x && velocity.y == initialVelocity.y) << updateVelocity_error_msg(&object, actual.getVelocity()) << "The velocity has not changed!"; } From 9e2a497f9d44cccbbb80f64e340279915c8e4f39 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Mon, 29 Jan 2024 16:06:04 +0100 Subject: [PATCH 132/137] fix SFML linkage with tests * should resolve EDC-948 Signed-off-by: Evgeniy Moiseenko --- cmake/utils.cmake | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 4e72b16..b664b65 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -28,8 +28,12 @@ macro(add_subprojects _base_dir _ignore_dirs) endforeach () endmacro() +function(create_test_target_lib_name TARGET_NAME OUTPUT_NAME) + set(${OUTPUT_NAME} "${TARGET_NAME}-src-part" PARENT_SCOPE) +endfunction() + macro(configure_test_target _target_name _src_files _test_files) - set(_src_part_lib ${_target_name}-src-part) + create_test_target_lib_name(${_target_name} _src_part_lib) # Create utility library to separate src files from test files add_library(${_src_part_lib} STATIC ${_src_files}) @@ -78,6 +82,8 @@ macro(prepare_sfml_framework_lesson_task _lesson_path _target_name _test_name) target_link_sfml(${_target_name}) if (NOT "${_test_name}" STREQUAL "") target_copy_resources(${_test_name}) + create_test_target_lib_name(${_test_name} _src_part_lib) + target_link_sfml(${_src_part_lib}) target_link_sfml(${_test_name}) endif() endmacro() \ No newline at end of file From 409fe6fbd38e587f6163bff243981a84c15881c0 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 15 Feb 2024 14:50:07 +0100 Subject: [PATCH 133/137] minor fixes Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/CollisionsRevisited/task.md | 2 +- ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md index ed5fddb..ba2b3d0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task.md @@ -41,7 +41,7 @@ and often instances of both can be found within the same codebase. They have a predictable memory layout and predictable behavior — there are no associated virtual methods dispatched at runtime. -In C++, structures are also sometimes referred to as [_POD types_]((https://en.wikipedia.org/wiki/Passive_data_structure)), +In C++, structures are also sometimes referred to as [_POD types_](https://en.wikipedia.org/wiki/Passive_data_structure), where POD stands for _plain old data_.
diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md index b293b40..c1314e0 100644 --- a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task.md @@ -77,8 +77,6 @@ as it results in more predictable behavior.
-[//]: # (TODO: explain explicit constructors) - The definition of the `CircleGameObject` constructor body contains some new interesting syntax: ```c++ From f2c56be1d37528a8de067704955705a903c1e991 Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 15 Feb 2024 14:51:19 +0100 Subject: [PATCH 134/137] update course README: add module 3 topics Signed-off-by: Evgeniy Moiseenko --- README.md | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c6b5016..a57116c 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,27 @@ with each module covering specific topics and aspects of the C++ language. * type cast operators: C style casts, `static_cast`, `reinterpret_cast` * C style strings -* __TBA__ ... - -* __Memory ownership__ - * smart pointers: `std::unique_ptr`, `std::shared_ptr`, `std::weak_ptr` +* __Object-Oriented Programming and Ownership Semantics__ + * operators overloading + * classes and objects + * class fields and methods, `virtual` methods + * abstract classes and interfaces + * inheritance, polymorphism, and encapsulation + * visibility modifiers: `public`, `protected`, and `private` + * class invariants + * `static` members + * `class` vs `struct`, plain old data types (POD) + * constructors and destructors + * `explicit` constructor + * object's lifetime, storage duration vs lifetime + * `new` and `delete` operators + * placement `new` operator + * copy and move constructors + * copy and move assignment operators + * copy-and-swap idiom + * ownership and move semantics + * resource acquisition is initialization idiom (RAII) + * smart pointers: `std::unique_ptr`, `std::shared_ptr`, and `std::weak_ptr` + +* __TBA__ ... From 87d72c45c561b22355fb7a3fed3612c2046fba6c Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 15 Feb 2024 15:15:20 +0100 Subject: [PATCH 135/137] fix after rebase Signed-off-by: Evgeniy Moiseenko --- WarmUp/MovingOn/HuntingBugs/task-info.yaml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/WarmUp/MovingOn/HuntingBugs/task-info.yaml b/WarmUp/MovingOn/HuntingBugs/task-info.yaml index ee18256..fb97fee 100644 --- a/WarmUp/MovingOn/HuntingBugs/task-info.yaml +++ b/WarmUp/MovingOn/HuntingBugs/task-info.yaml @@ -3,7 +3,6 @@ custom_name: Hunting Bugs files: - name: src/approaching.cpp visible: true -<<<<<<< HEAD placeholders: - offset: 113 length: 215 @@ -15,8 +14,6 @@ files: concerned[i] = dist < warnDist; } } -======= ->>>>>>> ffb8328 (rename `scene.hpp` into `game.hpp`, split it into several files) - name: src/loop.cpp visible: true placeholders: @@ -150,10 +147,8 @@ files: is_visible: false - name: src/main.cpp visible: true -<<<<<<< HEAD - name: test/test.cpp -======= + visible: false - name: CMakeLists.txt ->>>>>>> ffb8328 (rename `scene.hpp` into `game.hpp`, split it into several files) visible: false feedback_link: https://docs.google.com/forms/d/e/1FAIpQLScfBp0gzdxWOmXvQVdyNmeO1od7CG7zxLgNUP4LzKxLBCzkhQ/viewform?usp=pp_url&entry.2103429047=Warm+Up/Moving+On/Hunting+Bugs From 53646e092e3ee337d0857c1c544ec3ca635c233f Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 15 Feb 2024 15:33:36 +0100 Subject: [PATCH 136/137] add *-remote-info.yaml files for module 3 Signed-off-by: Evgeniy Moiseenko --- .../ClassesAndObjects/CollisionsRevisited/task-remote-info.yaml | 1 + .../ClassesAndObjects/Encapsulation/task-remote-info.yaml | 1 + .../ClassesAndObjects/Inheritance/task-remote-info.yaml | 1 + .../ClassesAndObjects/IntroducingObjects/task-remote-info.yaml | 1 + .../ClassesAndObjects/Introduction/task-remote-info.yaml | 1 + .../ClassesAndObjects/NewChallenge/task-remote-info.yaml | 1 + .../ClassesAndObjects/NewDynamics/task-remote-info.yaml | 1 + .../ClassesAndObjects/OperatorsOverloading/task-remote-info.yaml | 1 + .../ClassesAndObjects/Polymorphism/task-remote-info.yaml | 1 + .../ClassesAndObjects/StaticMembers/task-remote-info.yaml | 1 + .../ClassesAndObjects/lesson-remote-info.yaml | 1 + .../MemoryOwnership/CopyConstructor/task-remote-info.yaml | 1 + .../MemoryOwnership/Introduction/task-remote-info.yaml | 1 + .../MemoryOwnership/MoveSemantics/task-remote-info.yaml | 1 + .../MemoryOwnership/NewAndDeleteOperators/task-remote-info.yaml | 1 + .../MemoryOwnership/ObjectLifetime/task-remote-info.yaml | 1 + .../MemoryOwnership/Ownership/task-remote-info.yaml | 1 + .../MemoryOwnership/PlacementNew/task-remote-info.yaml | 1 + .../MemoryOwnership/RAIICopySwapIdiom/task-remote-info.yaml | 1 + .../MemoryOwnership/SharedPtr/task-remote-info.yaml | 1 + .../MemoryOwnership/SmartPointersSummary/task-remote-info.yaml | 1 + .../MemoryOwnership/UniquePtr/task-remote-info.yaml | 1 + .../MemoryOwnership/WeakPtr/task-remote-info.yaml | 1 + .../MemoryOwnership/lesson-remote-info.yaml | 1 + ObjectOrientedProgramming/section-remote-info.yaml | 1 + 25 files changed, 25 insertions(+) create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/ClassesAndObjects/lesson-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Introduction/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/Ownership/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-remote-info.yaml create mode 100644 ObjectOrientedProgramming/MemoryOwnership/lesson-remote-info.yaml create mode 100644 ObjectOrientedProgramming/section-remote-info.yaml diff --git a/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-remote-info.yaml new file mode 100644 index 0000000..a103451 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/CollisionsRevisited/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1504863152 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-remote-info.yaml new file mode 100644 index 0000000..7334942 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Encapsulation/task-remote-info.yaml @@ -0,0 +1 @@ +id: 2097510149 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-remote-info.yaml new file mode 100644 index 0000000..50eaea2 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Inheritance/task-remote-info.yaml @@ -0,0 +1 @@ +id: 980879362 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-remote-info.yaml new file mode 100644 index 0000000..02f9ab0 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/IntroducingObjects/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1543486324 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-remote-info.yaml new file mode 100644 index 0000000..c5f42d3 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Introduction/task-remote-info.yaml @@ -0,0 +1 @@ +id: 643261225 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-remote-info.yaml new file mode 100644 index 0000000..5060dbb --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewChallenge/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1572869330 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-remote-info.yaml new file mode 100644 index 0000000..4c6a4c9 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/NewDynamics/task-remote-info.yaml @@ -0,0 +1 @@ +id: 187428144 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-remote-info.yaml new file mode 100644 index 0000000..4154b3f --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/OperatorsOverloading/task-remote-info.yaml @@ -0,0 +1 @@ +id: 841812685 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-remote-info.yaml new file mode 100644 index 0000000..bb191d2 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/Polymorphism/task-remote-info.yaml @@ -0,0 +1 @@ +id: 656697934 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-remote-info.yaml new file mode 100644 index 0000000..adac39b --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/StaticMembers/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1527203786 diff --git a/ObjectOrientedProgramming/ClassesAndObjects/lesson-remote-info.yaml b/ObjectOrientedProgramming/ClassesAndObjects/lesson-remote-info.yaml new file mode 100644 index 0000000..f2f1c13 --- /dev/null +++ b/ObjectOrientedProgramming/ClassesAndObjects/lesson-remote-info.yaml @@ -0,0 +1 @@ +id: 1785975432 diff --git a/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-remote-info.yaml new file mode 100644 index 0000000..01cf9d0 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/CopyConstructor/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1236700959 diff --git a/ObjectOrientedProgramming/MemoryOwnership/Introduction/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task-remote-info.yaml new file mode 100644 index 0000000..f0bdb7a --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Introduction/task-remote-info.yaml @@ -0,0 +1 @@ +id: 546734997 diff --git a/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-remote-info.yaml new file mode 100644 index 0000000..9b70f51 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/MoveSemantics/task-remote-info.yaml @@ -0,0 +1 @@ +id: 811498572 diff --git a/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-remote-info.yaml new file mode 100644 index 0000000..dc0a00d --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/NewAndDeleteOperators/task-remote-info.yaml @@ -0,0 +1 @@ +id: 700142889 diff --git a/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-remote-info.yaml new file mode 100644 index 0000000..c741cbf --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/ObjectLifetime/task-remote-info.yaml @@ -0,0 +1 @@ +id: 2100131281 diff --git a/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-remote-info.yaml new file mode 100644 index 0000000..8b0dc9e --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/Ownership/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1596188677 diff --git a/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-remote-info.yaml new file mode 100644 index 0000000..77207aa --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/PlacementNew/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1562916100 diff --git a/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-remote-info.yaml new file mode 100644 index 0000000..f7a9788 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/RAIICopySwapIdiom/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1965960933 diff --git a/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-remote-info.yaml new file mode 100644 index 0000000..c110f8c --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SharedPtr/task-remote-info.yaml @@ -0,0 +1 @@ +id: 1307427631 diff --git a/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-remote-info.yaml new file mode 100644 index 0000000..77e46d2 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/SmartPointersSummary/task-remote-info.yaml @@ -0,0 +1 @@ +id: 911313023 diff --git a/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-remote-info.yaml new file mode 100644 index 0000000..929e0c5 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/UniquePtr/task-remote-info.yaml @@ -0,0 +1 @@ +id: 742587605 diff --git a/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-remote-info.yaml new file mode 100644 index 0000000..46ab583 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/WeakPtr/task-remote-info.yaml @@ -0,0 +1 @@ +id: 223242583 diff --git a/ObjectOrientedProgramming/MemoryOwnership/lesson-remote-info.yaml b/ObjectOrientedProgramming/MemoryOwnership/lesson-remote-info.yaml new file mode 100644 index 0000000..9e2ad20 --- /dev/null +++ b/ObjectOrientedProgramming/MemoryOwnership/lesson-remote-info.yaml @@ -0,0 +1 @@ +id: 1459191764 diff --git a/ObjectOrientedProgramming/section-remote-info.yaml b/ObjectOrientedProgramming/section-remote-info.yaml new file mode 100644 index 0000000..4025156 --- /dev/null +++ b/ObjectOrientedProgramming/section-remote-info.yaml @@ -0,0 +1 @@ +id: 255032253 From 74aec44832b26b842d18f06d06517333ec2a971d Mon Sep 17 00:00:00 2001 From: Evgeniy Moiseenko Date: Thu, 15 Feb 2024 15:52:43 +0100 Subject: [PATCH 137/137] fix HuntingBugs task after rebase Signed-off-by: Evgeniy Moiseenko --- WarmUp/MovingOn/HuntingBugs/test/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WarmUp/MovingOn/HuntingBugs/test/test.cpp b/WarmUp/MovingOn/HuntingBugs/test/test.cpp index 479a2bf..d19aedb 100644 --- a/WarmUp/MovingOn/HuntingBugs/test/test.cpp +++ b/WarmUp/MovingOn/HuntingBugs/test/test.cpp @@ -2,7 +2,7 @@ #include -#include "scene.hpp" +#include "game.hpp" #include "operators.hpp" #include "testing.hpp"