diff --git a/CreatingReliableSoftwareCpp/LICENSE b/CreatingReliableSoftwareCpp/LICENSE new file mode 100644 index 0000000..d15cf3b --- /dev/null +++ b/CreatingReliableSoftwareCpp/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2020 Hakim El Hattab, http://hakim.se, and reveal.js contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/agenda.md b/CreatingReliableSoftwareCpp/Presentation/agenda.md new file mode 100644 index 0000000..735274e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/agenda.md @@ -0,0 +1,28 @@ + + +### Agenda + +* Exceptions + * How to throw and catch exceptions + * Working with excpetions + * Alternative ways to signal an error +* Good practise: + * SOLID + * DRY + * KISS +* Code refactoring +* Testing + * GTEST + * GMOCK +* How to create simple thread safe logger +* Design patterns + * Observer + * Strategy + * Visitor + * Template method + * Command + * Builder + * Factory + * Decorator + + diff --git a/CreatingReliableSoftwareCpp/Presentation/builder.md b/CreatingReliableSoftwareCpp/Presentation/builder.md new file mode 100644 index 0000000..aa7860a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/builder.md @@ -0,0 +1,806 @@ +## Builder + +Whenever we need to create some complex object, which may have a lot of parameters, we end up with a constructor which takes plenty of arguments. In most cases we even don't want to set up all of this, so we create dozens of constructors which allow us to set up only needed parameters. And when we add another parameter, we need to add few more constructors. That's Horrible, isn't it? So how we can solve such a problem? How to create only one Constructor and allow user to build an object by injecting only needed parameters. In previous sentence I use a word `buid`, and this is exactly a design pattern that I want to present you. + +Builder UML +___ + +## Ship + +This is the constructor of class `Ship`. We can easily imagine that in the future we will add at least a few more parameters. + +```C++ +Ship::Ship(Time* time, + std::unique_ptr> strategy, + const std::string& name, + int capacity, + int maxCrew, + int crew, + std::unique_ptr&& visitor, + int armor, + int maxArmor, + int canons, + int maxCanons, + int durability, + int maxDurability) + : _time(time), + _strategy(std::move(strategy)), + _name(name), + _capacity(capacity), + _maxCrew(maxCrew), + _crew(crew), + _visitor(std::move(visitor)), + _armor(armor), + _maxArmor(maxArmor), + _canons(canons), + _maxCanons(maxCanons) + _durability(durability), + _maxDurability(maxDurability) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} +``` + + +___ + +## Create a ship + +Can you explain to me which number argument is an `armor` value, and which is a `cannon` value? Reading such code is quite hard. We can easily provide values in wrong order and instead of setting `armor` we will set a `maxCrew` or something else. + +```C++ +auto ship = Ship( + time, + std::move(strategy), + "Black Pearl", + 200, + 100, + 50, + std:move(visitor), + 100, + 200, + 20, + 25, + 1000, + 1000); +``` + + +___ + +## A new way + +This is a new way of creating a ship. What do you think about this approach? + +```C++ +// We need to provide all mandatory parameters, the rest will have default values. +auto ship = Ship(time, name, capacity, maxCrew, maxArmor, maxCannons, maxDurability) + // Provide optional arguemnts + .setDifficultLvlStrategy(std::move(strategy)) + .setCrew(100) + .setPrintVisitor(std::move(visitor)) + .setArmor(100) + .setCannons(10) + .setDurability(1000) + .build(); +``` + + +___ + +## A better way + +In previous example, we still need to provide few arguments in constructor before we will create an object. We can refactor this code and verify in `build` method if all mandatory parameters were set. + +```C++ +auto ship = ShipBuilder().setTime(time) + .setName("Black Pearl") + .setCapacity(500) + .setMaxCrew(200) + .setMaxArmor(1000) + .setMaxCannons(20) + .setMaxDurability(1000) + .setDifficultLvlStrategy(std::move(strategy)) + .setCrew(100) + .setPrintVisitor(std::move(visitor)) + .setArmor(100) + .setCannons(10) + .setDurability(1000) + .build(); +``` + + +___ + +## Builder class + +```C++ +class ShipBuilder { +public: + ShipBuilder() + : _ship(std::make_unique()) {} + + [[nodiscard]] std::unique_ptr build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->time() || + _ship->name().empty() || + _ship->capacity() == -1 || + _ship->maxCrew() == -1 || + _ship->maxArmor() == -1 || + _ship->maxCannons() == -1 || + _ship->maxDurability() == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + return std::move(_ship); + } + +private: + std::unique_ptr _ship; +}; +``` + + +___ + +## Setters + +Now we need to add all setters to `ShipBuilder` class + +```C++ + ShipBuilder& setTime(Time* time) { + _ship->setTime(time); + return *this; + } + ShipBuilder& setStrategy(std::unique_ptr> strategy) { + _ship->setStrategy(std::move(strategy)); + return *this; + } + ShipBuilder& setName(const std::string& name) { + _ship->setName(name); + return *this; + } + ShipBuilder& setCapacity(int capacity) { + _ship->setCapacity(capacity); + return *this; + } + ShipBuilder& setMaxCrew(int maxCrew) { + _ship->setMaxCrew(maxCrew); + return *this; + } + ShipBuilder& setCrew(int crew) { + _ship->setCrew(crew); + return *this; + } + ShipBuilder& setVisitor(std::unique_ptr visitor) { + _ship->setVisitor(std::move(visitor)); + return *this; + } + ShipBuilder& setArmor(int armor) { + _ship->setArmor(armor); + return *this; + } + ShipBuilder& setMaxArmor(int maxArmor) { + _ship->setMaxArmor(maxArmor); + return *this; + } + ShipBuilder& setCannons(int cannons) { + _ship->setCannons(cannons); + return *this; + } + ShipBuilder& setMaxCannons(int maxCannons) { + _ship->setMaxCannons(maxCannons); + return *this; + } + ShipBuilder& setDurability(int durability) { + _ship->setDurability(durability); + return *this; + } + ShipBuilder& setMaxDurability(int maxDurability) { + _ship->setMaxDurability(maxDurability); + return *this; + } +``` + + +___ + +## The ugly part + +Unfortunately, if we want to follow the encapsulation rule we need to duplicate `setters` in class `Ship` we need also provide getters for all members (even if we don't use them by other classes). + +```C++ +class Ship { +public: + Ship& setTime(Time* time) { + _time = time; + return *this; + } + Ship& setStrategy(std::unique_ptr> strategy) { + _strategy = std::move(strategy); + return *this; + } + Ship& setName(const std::string& name) { + _name = name; + return *this; + } + Ship& setCapacity(int capacity) { + _capacity = capacity; + return *this; + } + Ship& setMaxCrew(int maxCrew) { + _maxCrew = maxCrew; + return *this; + } + Ship& setCrew(int crew) { + _crew = crew; + return *this; + } + Ship& setVisitor(std::unique_ptr visitor) { + visitor = std::move(_visitor); + return *this; + } + Ship& setArmor(int armor) { + _armor = armor; + return *this; + } + Ship& setMaxArmor(int maxArmor) { + _maxArmor = maxArmor; + return *this; + } + Ship& setCannons(int cannons) { + _canons = cannons; + return *this; + } + Ship& setMaxCannons(int maxCannons) { + _maxCannons = maxCannons; + return *this; + } + Ship& setDurability(int durability) { + _durability = durability; + return *this; + } + Ship& setMaxDurability(int maxDurability) { + _maxDurability = maxDurability; + return *this; + } + + const Time* time() const { return _time; } + const std::string& name() const { return _name; } + int capacity() const { return _capacity; } + int maxCrew() const { return _maxCrew; } + int crew() const { return _crew; } + int armor() const { return _armor; } + int maxArmor() const { return _maxArmor; } + int canons() const { return _canons; } + int maxCannons() const { return _maxCannons; } + int durability() const { return _durability; } + int maxDurability() const { return _maxDurability; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + Ship() = default; + + Time* _time{}; + std::unique_ptr> _strategy{}; + std::string _name{}; + int _capacity{-1}; + int _maxCrew{-1}; + int _crew{10}; + std::unique_ptr _visitor{}; + int _armor{0}; + int _maxArmor{-1}; + int _canons{0}; + int _maxCannons{-1}; + int _durability{100}; + int _maxDurability{-1}; +}; +``` + + + +___ + +## Clean up + +In my opinion, a better option is to declare friendship with class `ShipBuilder`. Generally, friend with other classes is not a good idea. But in builder pattern we have a lot of profits: + +* Don't need to duplicate setters +* Some of the fields should not be changed after Ship will be built, but still we need to have a setter for this field +* We don't need to return a reference to `Ship` for all setters. +* Don't need to have getters for all members + +___ + +```C++ +class Ship { +public: + void setCrew(int crew) { _crew = crew; } + void setArmor(int armor) { _armor = armor; } + void setCannons(int cannons) { _canons = cannons; } + void setDurability(int durability) { _durability = durability; } + + const std::string& name() const { return _name; } + int capacity() const { return _capacity; } + int crew() const { return _crew; } + int armor() const { return _armor; } + int canons() const { return _canons; } + int durability() const { return _durability; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + Ship() = default; +} +``` + +___ + +```C++ +class ShipBuilder { +public: + ShipBuilder() + : _ship(std::make_unique()) {} + + [[nodiscard]] std::unique_ptr build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->_time || + _ship->_name.empty() || + _ship->_capacity == -1 || + _ship->_maxCrew == -1 || + _ship->_maxArmor == -1 || + _ship->_maxCannons == -1 || + _ship->_maxDurability == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + return std::move(_ship); + } + + ShipBuilder& setTime(Time* time) { + _ship->_time = time; + return *this; + } + ShipBuilder& setStrategy(std::unique_ptr> strategy) { + _ship->_strategy = std::move(strategy); + return *this; + } + ShipBuilder& setName(const std::string& name) { + _ship->_name = name; + return *this; + } + ShipBuilder& setCapacity(int capacity) { + _ship->_capacity = capacity; + return *this; + } + ShipBuilder& setMaxCrew(int maxCrew) { + _ship->_maxCrew = maxCrew; + return *this; + } + ShipBuilder& setCrew(int crew) { + _ship->_crew = crew; + return *this; + } + ShipBuilder& setVisitor(std::unique_ptr visitor) { + _ship->_visitor = std::move(visitor); + return *this; + } + ShipBuilder& setArmor(int armor) { + _ship->_armor = armor; + return *this; + } + ShipBuilder& setMaxArmor(int maxArmor) { + _ship->_maxArmor = maxArmor; + return *this; + } + ShipBuilder& setCannons(int cannons) { + _ship->_canons = cannons; + return *this; + } + ShipBuilder& setMaxCannons(int maxCannons) { + _ship->_maxCannons = maxCannons; + return *this; + } + ShipBuilder& setDurability(int durability) { + _ship->_durability = durability; + return *this; + } + ShipBuilder& setMaxDurability(int maxDurability) { + _ship->_maxDurability = maxDurability; + return *this; + } + +private: + std::unique_ptr _ship; +}; +``` + +___ + +## Usage + +For both implementations, we use builder in the same way: + +```C++ +int main() { + auto ship = ShipBuilder().build(); + if (ship) { + std::cout << "Ship name: " << ship->name() << '\n'; + } + + Time time; + auto ship2 = ShipBuilder() + .setTime(&time) + .setName("Black Pearl") + .setCapacity(500) + .setMaxCrew(200) + .setMaxArmor(1000) + .setMaxCannons(20) + .setMaxDurability(1000) + .build(); + + if (ship2) { + std::cout << "Ship name: " << ship2->name() << '\n'; + } +} +``` + + +```bash +Mandatory fields are not set! +Ship name: Black Pearl +``` + + +___ + +## gRPC + +A perfect example of builder is an gRPC library. We use the builder pattern to create a server object, which we can customize. Based on provided arguments we can create synchronous or asynchronous server, with or without compression, with one or more services, with additional options like max response size etc... + +```C++ +grpc::Status status; +std::shared_ptr provider = + grpc::FileWatcherAuthorizationPolicyProvider::Create( + authz_policy_path, /*refresh_interval_sec=*/3600, &status); +auto option = std::make_unqiue(); +grpc::ChannelArguments args; +args.SetMaxReceiveMessageSize(4096); +args.SetMaxSendMessageSize(4096); +option->UpdateArguments(args); + +std::unique_ptr server = + grpc::ServerBuilder().AddListeningPort("127.0.0.1:10000", InsecureServerCredentials()) + .RegisterService(&service1) + .RegisterService("127.0.0.1:9999", &service2) + .SetAuthorizationPolicyProvider(provider) + .SetOption(std::move(option)) + .SetDefaultCompressionLevel(grpc::GRPC_COMPRESS_LEVEL_HIGH) + .BuildAndStart(); +``` + + +___ + +## DRY + +At the beginning, we decided to have 3 types of ships. We always need to remember which value to pass to each parameter to create a specific type of ship. If we decide that something is not okay with balance, we need to change this parameter everywhere. If we forget to change it in some code, we end up with different parameters for the same ship type. We can of course decided to have some global variable, but this is an ugly solution. **We should wrap the whole logic in one place**. To make it more flexible, we will describe all parameters in a JSON file, so we will not be forced to recompile the whole binary every time we change the ship parameters. Because we don't want to modify existing code, we just create a new class that inherit `private` from the `ShipBuilder` so we can't use the interface `ShipBuilder` outside, but still `ShipJsonBuilder` may use this implementation. + +```C++ +class ShipJsonBuilder : private ShipBuilder { +public: + ShipJsonBuilder(const std::filesystem::path& path) { parseFile(path); } + + std::unique_ptr buildBrig(Time* time, const std::string& name) const { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["brig"]["capacity"]) + .setMaxCrew(_shipsData["brig"]["maxCrew"]) + .setMaxArmor(_shipsData["brig"]["maxArmor"]) + .setMaxCannons(_shipsData["brig"]["maxCannons"]) + .setMaxDurability(_shipsData["brig"]["maxDurability"]) + .build(); + } + std::unique_ptr buildFrigate(Time* time, const std::string& name) const; + std::unique_ptr buildGaleon(Time* time, const std::string& name) const; + +private: + json _shipsData; + void parseFile(const std::filesystem::path& path); // Parse file and store data in _shipsData +}; +``` + + + +___ + +## Usage + +Now, we can easily create one of existing types of ship, without worry about which values to pass. What's more, we read the file from JSON, so we decouple values from the binary, so every time we make some changes in balance, we don't need to recompile any single file. + +```C++ +int main() { + Time time; + + auto ship1 = ShipJsonBuilder().buildGaleon(&time, "Queens Anne Revenge"); + if (ship1) { + std::cout << "Ship name: " << ship1->name() << '\n'; + } + + auto ship2 = ShipJsonBuilder().buildFrigate(&time, "Black Pearl"); + if (ship2) { + std::cout << "Ship name: " << ship2->name() << '\n'; + } + + auto ship3 = ShipJsonBuilder().buildBrig(&time, "Black Widow"); + if (ship3) { + std::cout << "Ship name: " << ship3->name() << '\n'; + } +} +``` + + +```bash +Ship name: Queens Anne Revenge +Ship name: Black Pearl +Ship name: Black Widow +``` + + +___ + +## Different builder + +But there is even more! Currently, we focus only on one builder, which always create a `Ship` class. We also create a `ShipJsonBuilder` which reuse implementation from `ShipBuilder` and allow to user read default parameters from the file, so we always create a `Ship` of the same type with equal parameters. We also saw that gRPC use the same logic to also user create various type of server. Furthermore, gRPC add plenty of methods which allow us to set parameters of this server. We can even go further and create a few types of builder, and then pass a pointer to the base class to `Driector` and create a different object. + +```C++ +class CrewBuilder { +public: + CrewBuilder& setName(const std::string& name) = 0; + CrewBuilder& setWeapon(std::unique_ptr&& weapon) = 0; + CrewBuilder& setHp(Hp hp) = 0; + std::unique_ptr build() = 0; +}; +``` + + + +```C++ +class PirateBuilder : public CrewBuilder { +public: + CrewBuilder& setName(const std::string& name) override { /* some implementation */ } + CrewBuilder& setWeapon(std::unique_ptr&& weapon) override { /* some implementation */ } + CrewBuilder& setHp(Hp hp) override { /* some implementation */ } + std::unique_ptr build() { return std::make_unique(/* args */); } +}; +``` + +___ + +## Tavern + +Based on which tavern we are, we can recruit, for instance: +* the marine, +* the volunteer, +* the pirate + +Each kind of Sailor will act differently in combat and during sail. They also have a different pay. We don't want to hard-code a type of Tavern on every island, because during a game, we can for instance conquer an island and free it forms pirates, so we will no longer recruit their pirates, but maybe a marine or volunteer. That's why we should pass a builder class as an argument to tavern, so we can easily swap it during a game. + + +```C++ +class Tawern { +public: + Tawern(std::unique_ptr&& builder): _builder(std::move(builder)) {} + + std::unique_ptr recruit(const std::string& name, std::unique_ptr&& weapon) { + return _builder->setName(name).setWeapon(weapon).setHp(40).build(); + } + +private: + std::unique_ptr _builder; +}; +``` + + +___ + +## Mixing two design patterns + +If you have a feeling that this looks similar to factory method, you are right! Both design patterns are similar. There are few main differences: + +* Builder focuses on constructing a complex object step by step. Abstract Factory emphasizes a family of product objects (either simple or complex). Builder returns the product as a final step, but as far as the Abstract Factory is concerned, the product gets returned immediately. +* Builder often builds a Composite. +* Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, more complex) as the designer discovers where more flexibility is needed. +* Sometimes creational patterns are complementary: Builder can use one of the other patterns to implement which components get built. Abstract Factory, Builder, and Prototype can use Singleton in their implementations. + + +___ + +## Exercise 1 + +* Go to the directory Ship and create class ShipBuilder. The mandatory fields are: + * name + * capacity + * time +* The non-mandatory fields are: + * difficultStrategy -> default value should be set as Easy + * crew -> default value should be set as 10 + * durability -> default value should be set as 1000 + * armor -> default value should be set as 0 + * PrintVisitor -> default value should be set as Pretty Print +* Try to build your own Ship! +___ + +## Exercise 2 + +* Go to the directory Ship and finish implementation of class ShipJsonBuilder. You should allow building 3 types of ship: + * Brig + * Frigate + * Galoen +* If you want to read a JSON filed, just use operator[] to get any of nested field, example: _shipsData["brige"]["armor"] +* Try to build a ship using a new builder. +* Try to change some values in ships.json and run binary without recompilation. You should see new values! + +___ + +## Factory method + +Because we saw in the previous example a factory method patter, I want to tell a little more about it. Whenever we want to hide implementation details about creation of new object (and also a final type) we should use factory method. + +Factory method UML + +___ + +Interface of factory: +```C++ +class Factory { +public: + virtual std::unique_ptr create(const std::string& name, std::unique_ptr&& weapon, int hp) = 0; +}; +``` + + +Implementations: + + +```C++ +class PirateBuilder : public CrewBuilder { +public: + std::unique_ptr create(const std::string& name, std::unique_ptr&& weapon, int hp) override { + return std::make_unique(name, std::move(weapon), hp); + } +}; + +class MarineBuilder : public CrewBuilder { +public: + std::unique_ptr create(const std::string& name, std::unique_ptr&& weapon, int hp) override { + return std::make_unique(name, std::move(weapon), hp); + } +}; +``` + + +Class that use factory to create object: + + +```C++ +class Tawern { +public: + Tawern(std::unique_ptr&& factory): _factory(std::move(factory)) {} + std::unique_ptr recruit(const std::string& name, std::unique_ptr&& weapon) { + return _factory->create(name, std::move(weapon), 40); + } +private: + std::unique_ptr _factory_; +}; +``` + + + +___ + +## Exercise 3 + +* Go to the directory Ship and create abstract class FruitFactory. +* Create FruitFactory +* Create ItemFactory +* Create AlcoholFactory +* Test in main.cpp if you can create any type of cargo +* How to handle scenario, when different cargo takes other arguments? + +___ + +## Different arguments + +At the end, I want to talk about different arguments in constructors. We have three derived class from `Cargo`, each class takes different arguments. + +```C++ +struct Fruit : public Cargo { + int rottenCounter; + + Fruit(size_t amount, int rottenCounter); + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + enum class Type { Common, Rare, Epic, Legendary }; + Type type; + + Item(size_t amount, Type type); + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + enum class Type { White, Spiced, Dark}; + + int power; + Type type; + Alcohol(size_t amount, int power, Type type); + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; +``` + + + +___ + +## How to solve it? + +**Of course by using template!** + + +```C++ +template +class CargoFactory { +public: + virtual std::unique_ptr create(size_t amount, Args... args) = 0; +}; +``` + + +```C++ +class FruitFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, int rottenCounter) override { + return std::make_unique(amount, rottenCounter); + } +}; + +class AlcoholFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, int power, Alcohol::Type type) override { + return std::make_unique(amount, power, type); + } +}; + +class ItemFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, Item::Type type) override { + return std::make_unique(amount, type); + } +}; +``` + + \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/compilation.md b/CreatingReliableSoftwareCpp/Presentation/compilation.md new file mode 100644 index 0000000..898e363 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/compilation.md @@ -0,0 +1,43 @@ +# Cloning and building example project +___ + +## Setup + +* Clone repository: https://github.com/nauka-programowania-MA/CreatingReliableSoftwareCpp +* Go to exercises/exampleProject +* Compile it and run: + * mkdir build + * cd build + * cmake .. + * make -j4 (where 4 is available threads) + * ./ExampleProject +* Should print Hello World + +If you don't have linux, you can use replit + +* Click Create C++ +* Name it and confirm with button Create Repl. +* Click three dot on the rigt top screen and click upload folder +* Now find your directory with repo and upload it +* Congratulation, you can use linux shell wit sanitizers, cmake and valgridn support :) + + +___ + +## replit + +If you decided to use replit please configure this file: `replit.nix`: + +```bash +{ pkgs }: { + deps = [ + pkgs.clang_12 + pkgs.ccls + pkgs.gdb + pkgs.gnumake + pkgs.vim + pkgs.valgrind + pkgs.cmake + ]; +} +``` \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/decorator.md b/CreatingReliableSoftwareCpp/Presentation/decorator.md new file mode 100644 index 0000000..e797b17 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/decorator.md @@ -0,0 +1,577 @@ +## Decorator + +The last design pattern that I mention today is a `decorator`. Before we talk about it, let's extend `Cargo` class and handle a few new functionalities: + +* The item may be damaged during a time (we have salty air) so it can lose his value during travelling +* The fruit may rotten, so we also want to lower its price every day +* The alcohol price should be based on it's power +* The value of item should be higher based on its type (common is cheapest but legendary has a big value) + +___ + +## New implementation + +Let's start with a new implementation of class `Fruit`. Now we inherit from `TimeObserver` to count days. We also take in account that the price will be lowered every day. + +```C++ +struct Fruit : public Cargo, public TimeObserver { + static constexpr int MAX_TIME = 20; + static constexpr int BASE_PRICE = 10; + int rottenCounter; + Time* time; + + Fruit(Time* time, size_t amount, int rottenCounter) + : Cargo(amount), time(time), rottenCounter(rottenCounter) { + time->attach(this); + } + + // Rule of 5 + ~Fruit() { + time->detach(this); + } + + size_t getPrice() const override { + return (rottenCounter * BASE_PRICE) / MAX_TIME; + } + + const std::string& name() const override { + static const std::string name = "Banana"; + return name; + } + + void nextDay() override { + rottenCounter = std::max(0, rottenCounter - 1); + } +}; +``` + + + +___ + +```C++ +struct Item : public Cargo, public TimeObserver { + enum class Type { Common = 100, + Rare = 300, + Epic = 1000, + Legendary = 2500 }; + static constexpr int MAX_DURABILITY = 100; + Time* time; + Type type; + int durability{MAX_DURABILITY}; + + Item(Time* time, size_t amount, Type type) + : Cargo(amount), time(time), type(type) { + time->attach(this); + } + + // Rule of 5 + ~Item() { + time->detach(this); + } + + size_t getPrice() const override { + return (durability * static_cast(type)) / MAX_DURABILITY; + } + + const std::string& name() const override { + static const std::string name = "Item"; + return name; + } + + void nextDay() override { + durability = std::max(0, durability - 1); + } +}; +``` + +___ + +## Duplication of code + +At the end, we also implement a `Alcohol` class. As you can see, we duplicate behavior few times. `Item` and `Fruit` may lose value each day. Price of `Item` and `Alcohol` depend on it's type etc… + +```C++ +struct Alcohol : public Cargo { + enum class Type { White = 100, + Spiced = 200, + Dark = 300, + Seasoned = 500 }; + static constexpr int MAX_POWER = 96; + int power; + Type type; + + Alcohol(size_t amount, int power, Type type) + : Cargo(amount), power(power), type(type) {} + + size_t getPrice() const override { + return (power * static_cast(type)) / MAX_POWER; + } + + const std::string& name() const override { + static const std::string name = "Rum"; + return name; + } +}; +``` + + +___ + +The following code works as we want to. However, whenever we want to change a logic, for instance instead of decrement the counter every day, we want to subtract `durability` and `rottenCounter` every second day. We need to do this in two places. And when we will add more cargo, we will need to do this everywhere. This is not a good example of easy to maintenance code. + +```C++ +int main() { + Time time; + std::unique_ptr cargo = std::make_unique(&time, 10, Item::Type::Epic); + + for (int i = 1; i <= 15; ++i) { + std::cout << "DAY: " << i << " | Name: " << cargo->name() << " | Price: " << cargo->getPrice() << '\n'; + ++time; + } +} +``` + + +``` +DAY: 1 | Name: Item | Price: 1000 +DAY: 2 | Name: Item | Price: 990 +DAY: 3 | Name: Item | Price: 980 +DAY: 4 | Name: Item | Price: 970 +DAY: 5 | Name: Item | Price: 960 +DAY: 6 | Name: Item | Price: 950 +DAY: 7 | Name: Item | Price: 940 +DAY: 8 | Name: Item | Price: 930 +DAY: 9 | Name: Item | Price: 920 +DAY: 10 | Name: Item | Price: 910 +DAY: 11 | Name: Item | Price: 900 +DAY: 12 | Name: Item | Price: 890 +DAY: 13 | Name: Item | Price: 880 +DAY: 14 | Name: Item | Price: 870 +DAY: 15 | Name: Item | Price: 860 +``` + + +___ + +## Drawback + +According to one of SOLID rule **O**pen-close, we should write an easy to extend code, which don't require from us modification of already implemented code. Unfortunately, we need to modify each class to append a new logic, like: `decrement durability each day` or `get price based on durability or type`. What's more, we don't want to simulate `rotten of fruit` or lose `durability` by items that are stored in `shops`. So we need to add some `bool` flag like `isInShop` to ignore time elapsing. When the code grows and grows, we need to add more such flags and duplicate more code. This is definitely not the way we should develop a code. A better solution is to make a `Fruit` or `Item` class unaware of time elapsing. We can also make class `item` and `Alcohol` unaware of different types, and it's prices. This will allow to decouple the code, and make it easier to extend. + +___ + +## Decorator + +Instead of implement the whole logic to every class, and then make some `bool` flags to omit a few of them. We can use a `decorator` pattern. Let's start from the interface. The `DecoratedCargo` inherit form `Cargo` class and take as an argument `unique_ptr` for class `Cargo`. This interface has also two protected function allowing to get underlying cargo. + +```C++ +class DecoratedCargo : public Cargo { +public: + explicit DecoratedCargo(std::unique_ptr&& cargo) + : _cargo(std::move(cargo)) { + assert(_cargo); + } + +protected: + Cargo& cargo() { return *_cargo; } + const Cargo& cargo() const { return *_cargo; } + +private: + std::unique_ptr _cargo; +}; +``` + + +___ + +## Vulnerable + +A `Vulnerable` class inherit from `TimeObserver` to simulate time elapsing and from `DecoratedCargo`. When we calculate the price, it will calculate percentage value based on `durability` and `maxDurability`. Price can be manipulated by other decorator, so we call `getPrice`. + +```C++ +class Vulnerable : public DecoratedCargo, public TimeObserver { +public: + Vulnerable(std::unique_ptr&& cargo, Time* time, int durability, int maxDurability) + : DecoratedCargo(std::move(cargo)), _time(time), _durability(durability), _maxDurability(maxDurability) { + assert(_time); + time->attach(this); + } + + // Rule of 5 + ~Vulnerable() { + _time->detach(this); + } + + void nextDay() override { + _durability = std::max(0, _durability - 1); + } + + size_t getPrice() const override { + return (_durability * cargo().getPrice()) / _maxDurability; + } + + const std::string& name() const override { + return cargo().name(); + } + +private: + Time* _time; + int _durability; + int _maxDurability; +}; +``` + + +___ + +## Valuable + +A `Valuable` class is a template class that take `enum` as a template argument. It will be used later when we calculate a price. We will treat it as a multiplier for already calculated price. + +```C++ +template +class Valuable : public DecoratedCargo { +public: + Valuable(std::unique_ptr&& cargo, ValueType value) + : DecoratedCargo(std::move(cargo)), _value(value) { + } + + size_t getPrice() const override { + return cargo().getPrice() * static_cast(_value); + } + + const std::string& name() const override { + return cargo().name(); + } + +private: + ValueType _value; +}; +``` + + +___ + +## Type + +Because we want to make `Item` and `Alcohol` and any other type unaware about its type. We move it to the separate files. This allows us to develop new functionality without modification of existing code. For instance, when we decided later that we want to add also a `type` for `Fruit` we will create a new file and that's all! We don't need to recompile anything, and also don't need to modify anything. So even if `Fruit` is in a separate repository, and we can't modify it. We still can add new behavior. + +```C++ +enum class ItemType { Common = 1, + Rare = 3, + Epic = 10, + Legendary = 25 }; + +enum class AlcoholType { White = 1, + Spiced = 2, + Dark = 3, + Seasoned = 5 }; +``` + + + +___ + +## Usage + +Now we can concatenate any decorator with a `Cargo` type, so we can add new functionality `in fly`. + +```C++ +int main() { + Time time; + std::unique_ptr cargo = std::make_unique( + std::make_unique>( + std::make_unique(10), ItemType::Epic), + &time, 100, 100); + + for (int i = 1; i <= 15; ++i) { + std::cout << "DAY: " << i << " | Name: " << cargo->name() << " | Price: " << cargo->getPrice() << '\n'; + ++time; + } +} +``` + + +```bash +DAY: 1 | Name: Item | Price: 1000 +DAY: 2 | Name: Item | Price: 990 +DAY: 3 | Name: Item | Price: 980 +DAY: 4 | Name: Item | Price: 970 +DAY: 5 | Name: Item | Price: 960 +DAY: 6 | Name: Item | Price: 950 +DAY: 7 | Name: Item | Price: 940 +DAY: 8 | Name: Item | Price: 930 +DAY: 9 | Name: Item | Price: 920 +DAY: 10 | Name: Item | Price: 910 +DAY: 11 | Name: Item | Price: 900 +DAY: 12 | Name: Item | Price: 890 +DAY: 13 | Name: Item | Price: 880 +DAY: 14 | Name: Item | Price: 870 +DAY: 15 | Name: Item | Price: 860 +``` + + +___ + +## Store + +If cargo is inside the store, we just don't add a `Vulnerable` decorator! + +```C++ +Time time; +std::unique_ptr cargo = std::make_unique( + std::make_unique>( + std::make_unique(10), ItemType::Epic), + &time, 100, 100); +std::unique_ptr cargo2 = + std::make_unique>( + std::make_unique(10), ItemType::Epic); + +for (int i = 1; i <= 15; ++i) { + std::cout << "DAY: " << i << " | Name: " << cargo->name() << " | Price: " << cargo->getPrice(); + std::cout << " |-| Name: " << cargo2->name() << " | Price: " << cargo2->getPrice() << '\n'; + ++time; +} +``` + + +```bash +DAY: 1 | Name: Item | Price: 1000 |-| Name: Item | Price: 1000 +DAY: 2 | Name: Item | Price: 990 |-| Name: Item | Price: 1000 +DAY: 3 | Name: Item | Price: 980 |-| Name: Item | Price: 1000 +DAY: 4 | Name: Item | Price: 970 |-| Name: Item | Price: 1000 +DAY: 5 | Name: Item | Price: 960 |-| Name: Item | Price: 1000 +DAY: 6 | Name: Item | Price: 950 |-| Name: Item | Price: 1000 +DAY: 7 | Name: Item | Price: 940 |-| Name: Item | Price: 1000 +DAY: 8 | Name: Item | Price: 930 |-| Name: Item | Price: 1000 +DAY: 9 | Name: Item | Price: 920 |-| Name: Item | Price: 1000 +DAY: 10 | Name: Item | Price: 910 |-| Name: Item | Price: 1000 +DAY: 11 | Name: Item | Price: 900 |-| Name: Item | Price: 1000 +DAY: 12 | Name: Item | Price: 890 |-| Name: Item | Price: 1000 +DAY: 13 | Name: Item | Price: 880 |-| Name: Item | Price: 1000 +DAY: 14 | Name: Item | Price: 870 |-| Name: Item | Price: 1000 +DAY: 15 | Name: Item | Price: 860 |-| Name: Item | Price: 1000 +``` + + +___ + +## Decorator + +The decorator pattern can be used to extend (decorate) the functionality of a certain object statically, or in some cases at run-time, independently of other instances of the same class, provided some groundwork is done at design time. This is achieved by designing a new Decorator class that wraps the original class. + +Decorator UML + + +___ + +Decorator also helps with **D**ependency inversion. Because `decorator` classes are in low-lvl, so we can easily add a new decorator without changing anything in higher layers. + +Decorator Cargo UML + + +___ + +## Drawbacks + +Decorator is powerfull desing pattenr, but as everything it also has some drawbacks. + +* The main problem is that we need to use plenty of `unique_ptr` this will use more memory and also will be slower, because we need to call a few virtual functions. +* If we add to many decorators, the code became hard to read +* Debugging code containing decorators also may be more difficult, because we need to jump between a few of the same methods (but located in different decorators) before we reach the final result. So find a bug will be harder +* If the result of one decorator has an impact on another, we may cause a hard to spot bug when we swap the order. So we shouldn't create a decorator, which should be stored in order, because a risk of concatenated them wrong is high. +___ + +## Different approach + +As one popular proverb says: "Time is money". And if our code must be as fast as possible, and we can't afford a price, that few decorators means calling of few virtual functions, which also make the code much harder to optimize by compiler. We need to rising with decorator. I have good news for you: No, you don't need to! We have two types of polymorphism, so instead of using a `dynamic` one, let's use a `static`. + +___ + +## Get rid of virtual + +First, the class `Cargo` is no longer virtual. We don't need to implement rule of 5 and create a virtual destructor. We move constructors to `protected` section, to make it create only by derived classes (sth similar to abstract class, which we can't create). + +```C++ +struct Cargo { + size_t amount; + + auto operator<=>(const Cargo&) const = default; + +protected: + // Only derived class can create it + Cargo(size_t amount) + : amount(amount) {} + Cargo() = default; +}; +``` + + +___ + +Next, we also remove all virtual functions from derived classes. + +```C++ +struct Fruit : public Cargo { + static constexpr int BASE_PRICE = 10; + + Fruit(size_t amount) + : Cargo(amount) {} + + size_t getPrice() const { + return BASE_PRICE; + } + + const std::string& name() const { + static const std::string name = "Banana"; + return name; + } +}; +``` + + +```C++ +struct Item : public Cargo { + static constexpr int BASE_PRICE = 100; + + Item(size_t amount) + : Cargo(amount) {} + + size_t getPrice() const { + return BASE_PRICE; + } + + const std::string& name() const { + static const std::string name = "Item"; + return name; + } +}; +``` + + +___ + +We don't need to have a base class for decorator. We can move directly to implementation. + +```C++ +template +class Vulnerable : public TimeObserver { +public: + Vulnerable(Neasted neasted, Time* time, int durability, int maxDurability) + : _neasted(neasted), _time(time), _durability(durability), _maxDurability(maxDurability) { + assert(_time); + time->attach(this); + } + + // Rule of 5 + ~Vulnerable() { _time->detach(this);} + + void nextDay() override { + _durability = std::max(0, _durability - 1); + } + + size_t getPrice() const { + return (_durability * _neasted.getPrice()) / _maxDurability; + } + + const std::string& name() const { + return _neasted.name(); + } + +private: + Neasted _neasted; + Time* _time; + int _durability; + int _maxDurability; +}; +``` + + + +___ + +As you can see, we have even less code, and we avoid a lot of virtual functions. If we decide that an object is too heavy to copy, we can pass a pointer. The main bottleneck of decorator which slows down the code was calling a lot of virtual functions. Without virtual functions, our code is much faster. + +```C++ +template +class Valuable { +public: + Valuable(Neasted neasted, ValueType value) + : _neasted(neasted), _value(value) { + } + + size_t getPrice() const { + return _neasted.getPrice() * static_cast(_value); + } + + const std::string& name() const { + return _neasted.name(); + } + +private: + Neasted _neasted; + ValueType _value; +}; +``` + + +___ + +Even usage seems more intuitive, because we don't need to create a lot of `unique_ptr` even for small objects. + +```C++ +int main() { + Time time; + auto cargo = Vulnerable(Valuable(Item(10), ItemType::Epic), &time, 100, 100); + auto cargo2 = Valuable(Item(10), ItemType::Epic); + + for (int i = 1; i <= 15; ++i) { + std::cout << "DAY: " << i << " | Name: " << cargo.name() << " | Price: " << cargo.getPrice(); + std::cout << " |-| Name: " << cargo2.name() << " | Price: " << cargo2.getPrice() << '\n'; + ++time; + } +} +``` + + +```bash +DAY: 1 | Name: Item | Price: 1000 |-| Name: Item | Price: 1000 +DAY: 2 | Name: Item | Price: 990 |-| Name: Item | Price: 1000 +DAY: 3 | Name: Item | Price: 980 |-| Name: Item | Price: 1000 +DAY: 4 | Name: Item | Price: 970 |-| Name: Item | Price: 1000 +DAY: 5 | Name: Item | Price: 960 |-| Name: Item | Price: 1000 +DAY: 6 | Name: Item | Price: 950 |-| Name: Item | Price: 1000 +DAY: 7 | Name: Item | Price: 940 |-| Name: Item | Price: 1000 +DAY: 8 | Name: Item | Price: 930 |-| Name: Item | Price: 1000 +DAY: 9 | Name: Item | Price: 920 |-| Name: Item | Price: 1000 +DAY: 10 | Name: Item | Price: 910 |-| Name: Item | Price: 1000 +DAY: 11 | Name: Item | Price: 900 |-| Name: Item | Price: 1000 +DAY: 12 | Name: Item | Price: 890 |-| Name: Item | Price: 1000 +DAY: 13 | Name: Item | Price: 880 |-| Name: Item | Price: 1000 +DAY: 14 | Name: Item | Price: 870 |-| Name: Item | Price: 1000 +DAY: 15 | Name: Item | Price: 860 |-| Name: Item | Price: 1000 +``` + + +___ + +## Ecerice 1 + +* Go into directory Ship and implement DecoratedCargo: + * It should take a std::unique_ptr<Cargo> and has protected getter to this field. +* Implement Vunreable class: + * It should inherit from TimeObserver and DecoratedCargo + * It should take additional parameters in C'tor: Time, durability and maxDurability + * It should simulate time elapsing. Every day should subtract durability + * It should return price back on current durability of cargo + +___ + +## Exercise 2 + +* Implement Valuable class: + * It should inherit from DecoratedCargo + * it should be a template that takes enum type of value + * It should return value based on type (cast it to enum and multiply by current value) +* Try to compile code form main.cpp +* You may have some trouble with running code if you don't write a code carefully :) think where is a problem. diff --git a/CreatingReliableSoftwareCpp/Presentation/design_patterns_intro.md b/CreatingReliableSoftwareCpp/Presentation/design_patterns_intro.md new file mode 100644 index 0000000..9dfa41d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/design_patterns_intro.md @@ -0,0 +1 @@ +# Design patterns \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder.cpp new file mode 100644 index 0000000..accfa68 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder.cpp @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include +#include + +struct Time {}; +template +struct DifficultStrategy {}; +struct PrintVisitor {}; + +class Ship { +public: + Ship& setTime(Time* time) { + _time = time; + return *this; + } + Ship& setStrategy(std::unique_ptr> strategy) { + _strategy = std::move(strategy); + return *this; + } + Ship& setName(const std::string& name) { + _name = name; + return *this; + } + Ship& setCapacity(int capacity) { + _capacity = capacity; + return *this; + } + Ship& setMaxCrew(int maxCrew) { + _maxCrew = maxCrew; + return *this; + } + Ship& setCrew(int crew) { + _crew = crew; + return *this; + } + Ship& setVisitor(std::unique_ptr visitor) { + visitor = std::move(_visitor); + return *this; + } + Ship& setArmor(int armor) { + _armor = armor; + return *this; + } + Ship& setMaxArmor(int maxArmor) { + _maxArmor = maxArmor; + return *this; + } + Ship& setCannons(int cannons) { + _canons = cannons; + return *this; + } + Ship& setMaxCannons(int maxCannons) { + _maxCannons = maxCannons; + return *this; + } + Ship& setDurability(int durability) { + _durability = durability; + return *this; + } + Ship& setMaxDurability(int maxDurability) { + _maxDurability = maxDurability; + return *this; + } + + const Time* time() const { return _time; } + const std::string& name() const { return _name; } + int capacity() const { return _capacity; } + int maxCrew() const { return _maxCrew; } + int crew() const { return _crew; } + int armor() const { return _armor; } + int maxArmor() const { return _maxArmor; } + int canons() const { return _canons; } + int maxCannons() const { return _maxCannons; } + int durability() const { return _durability; } + int maxDurability() const { return _maxDurability; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + Ship() = default; + + Time* _time{}; + std::unique_ptr> _strategy{}; + std::string _name{}; + int _capacity{-1}; + int _maxCrew{-1}; + int _crew{10}; + std::unique_ptr _visitor{}; + int _armor{0}; + int _maxArmor{-1}; + int _canons{0}; + int _maxCannons{-1}; + int _durability{100}; + int _maxDurability{-1}; +}; + +class ShipBuilder { +public: + ShipBuilder() + : _ship(std::make_unique()) {} + + [[nodiscard]] std::unique_ptr build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->time() || + _ship->name().empty() || + _ship->capacity() == -1 || + _ship->maxCrew() == -1 || + _ship->maxArmor() == -1 || + _ship->maxCannons() == -1 || + _ship->maxDurability() == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + return std::move(_ship); + } + + ShipBuilder& setTime(Time* time) { + _ship->setTime(time); + return *this; + } + ShipBuilder& setStrategy(std::unique_ptr> strategy) { + _ship->setStrategy(std::move(strategy)); + return *this; + } + ShipBuilder& setName(const std::string& name) { + _ship->setName(name); + return *this; + } + ShipBuilder& setCapacity(int capacity) { + _ship->setCapacity(capacity); + return *this; + } + ShipBuilder& setMaxCrew(int maxCrew) { + _ship->setMaxCrew(maxCrew); + return *this; + } + ShipBuilder& setCrew(int crew) { + _ship->setCrew(crew); + return *this; + } + ShipBuilder& setVisitor(std::unique_ptr visitor) { + _ship->setVisitor(std::move(visitor)); + return *this; + } + ShipBuilder& setArmor(int armor) { + _ship->setArmor(armor); + return *this; + } + ShipBuilder& setMaxArmor(int maxArmor) { + _ship->setMaxArmor(maxArmor); + return *this; + } + ShipBuilder& setCannons(int cannons) { + _ship->setCannons(cannons); + return *this; + } + ShipBuilder& setMaxCannons(int maxCannons) { + _ship->setMaxCannons(maxCannons); + return *this; + } + ShipBuilder& setDurability(int durability) { + _ship->setDurability(durability); + return *this; + } + ShipBuilder& setMaxDurability(int maxDurability) { + _ship->setMaxDurability(maxDurability); + return *this; + } + +private: + std::unique_ptr _ship; +}; + +int main() { + auto ship = ShipBuilder().build(); + if (ship) { + std::cout << "Ship name: " << ship->name() << '\n'; + } + + Time time; + auto ship2 = ShipBuilder() + .setTime(&time) + .setName("Black Pearl") + .setCapacity(500) + .setMaxCrew(200) + .setMaxArmor(1000) + .setMaxCannons(20) + .setMaxDurability(1000) + .build(); + + if (ship2) { + std::cout << "Ship name: " << ship2->name() << '\n'; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/CMakeLists.txt new file mode 100644 index 0000000..751c5ff --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +include(FetchContent) +FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.11.3 +) +FetchContent_MakeAvailable(json) + +target_link_libraries(${PROJECT_NAME} + Battle + Ship + Store + Core + Player + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/main.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/main.cpp new file mode 100644 index 0000000..9608adf --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/main.cpp @@ -0,0 +1,65 @@ +#include +#include +#include + +#include "Battle/BattleField.h" +#include "Core/BasicPrintVisitor.h" +#include "Core/PrettyPrintVisitor.h" +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Player/RealPlayer.h" +#include "Ship/AlcoholFactory.h" +#include "Ship/Cargo.h" +#include "Ship/CargoDamageVisitor.h" +#include "Ship/FruitFactory.h" +#include "Ship/ItemFactory.h" +#include "Ship/Ship.h" +#include "Ship/ShipBuilder.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Ship/ShipJsonBuilder.h" +#include "Store/Store.h" + +int main() { + Time time; + + auto ship = ShipBuilder() + .setTime(&time) + .setName("Black Widow") + .setCapacity(1000) + .setCrew(40) + .setStrategy(std::make_unique()) + .setVisitor(std::make_unique()) + .setArmor(100) + .setDurability(1000) + .build(); + ship->load(AlcoholFactory().create(300, 40, Alcohol::Type::Spiced)); + ship->load(FruitFactory().create(200, 15)); + ship->load(FruitFactory().create(150, 15)); + ship->load(ItemFactory().create(250, Item::Type::Epic)); + ship->printCargo(); + RealPlayer user("Mateusz", std::move(ship)); + + Enemy enemy("Enemy1", + ShipJsonBuilder("../ships.json").buildGaleon(&time, "Queens Anne revenge"), + std::make_unique(), + CargoDamageVisitor{}); + + BattleField battefield{&user, &enemy}; + while (true) { + for (auto* player : battefield.players()) { + std::cout << "-------------------------------------------------------------------\n"; + std::cout << "Player HP: " << user.getShip().durability() << " ARMOR: " << user.getShip().armor() << " | "; + std::cout << "Enemy HP: " << enemy.getShip().durability() << " ARMOR: " << enemy.getShip().armor() << '\n'; + std::cout << "-------------------------------------------------------------------\n"; + for (size_t i = 0; i < 3; ++i) { + const auto status = player->makeAction(battefield); + if (status == Action::Status::Escaped || status == Action::Status::Defeated) { + return 0; + } + } + } + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/ships.json b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/ships.json new file mode 100644 index 0000000..9b19770 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/ships.json @@ -0,0 +1,20 @@ +{ + "brige":{ + "capacity":1000, + "crew":40, + "durability":1000, + "armor":100 + }, + "frigate":{ + "capacity":1500, + "crew":70, + "durability":2000, + "armor":500 + }, + "galeon":{ + "capacity":2500, + "crew":120, + "durability":3000, + "armor":1000 + } + } \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/CMakeLists.txt new file mode 100644 index 0000000..acd8033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Battle src/Defense.cpp src/BattleField.cpp src/Attack.cpp src/Escape.cpp) + +target_include_directories(Battle PUBLIC include) + +target_link_libraries(Battle + Ship + Player +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Action.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Action.h new file mode 100644 index 0000000..f271d40 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Action.h @@ -0,0 +1,24 @@ +#pragma once + +class Player; +class Ship; + +class Action { +public: + // As you can see keeping type here, require modify a base class whenever we add a new class, this volatile an open-close principle. + // We should be able to extend code without modification already implemented one + enum class Type { + Attack, + Defense, + Escape + }; + + enum class Status { + Escaped, + Defeated, + Nothing, + }; + + virtual Status operator()(Player* player, Ship* enemyShip) = 0; + virtual Type type() const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Attack.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Attack.h new file mode 100644 index 0000000..a06cba0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Attack.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Attack : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Attack; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/BattleField.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/BattleField.h new file mode 100644 index 0000000..6196660 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/BattleField.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Ship; +class Player; + +class BattleField { +public: + BattleField(Player* player, Player* enemy); + + Ship* getPlayerShip() const; + Ship* getEnemyShip() const; + std::vector players() const; + +private: + Player* _player; + Player* _enemy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Defense.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Defense.h new file mode 100644 index 0000000..fefd271 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Defense.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Defense : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Type::Defense; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Escape.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Escape.h new file mode 100644 index 0000000..97dc54d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/include/Battle/Escape.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Battle/Action.h" + +#include + +class Player; +class Ship; + +class Escape : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Escape; } + +private: + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Attack.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Attack.cpp new file mode 100644 index 0000000..b3f5e8a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Attack.cpp @@ -0,0 +1,22 @@ +#include "Battle/Attack.h" + +#include +#include + +#include + +Action::Status Attack::operator()(Player* player, Ship* enemyShip) { + const int damage = player->attack(*enemyShip); + std::cout << "Player ship: " << player->getShip().name() << " attack ship: " << enemyShip->name() << '\n'; + if (damage) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } + + if (enemyShip->durability() <= 0) { + return Status::Defeated; + } + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/BattleField.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/BattleField.cpp new file mode 100644 index 0000000..4e10569 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/BattleField.cpp @@ -0,0 +1,16 @@ +#include "Battle/BattleField.h" + +#include + +BattleField::BattleField(Player* player, Player* enemy) + : _player{player}, _enemy{enemy} {} + +Ship* BattleField::getPlayerShip() const { + return &_player->getShip(); +} +Ship* BattleField::getEnemyShip() const { + return &_enemy->getShip(); +} +std::vector BattleField::players() const { + return {_player, _enemy}; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Defense.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Defense.cpp new file mode 100644 index 0000000..a170c81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Defense.cpp @@ -0,0 +1,13 @@ +#include "Battle/Defense.h" + +#include +#include + +#include + +Action::Status Defense::operator()(Player* player, Ship*) { + player->getShip().increaseArmor(50); + std::cout << "Player ship: " << player->getShip().name() << " defense\n"; + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Escape.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Escape.cpp new file mode 100644 index 0000000..5b222fa --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Battle/src/Escape.cpp @@ -0,0 +1,22 @@ +#include "Battle/Escape.h" + +#include +#include + +#include + +std::random_device Escape::_rd{}; + +Action::Status Escape::operator()(Player* player, Ship*) { + std::cout << "Player ship: " << player->getShip().name() << " try to escape!\n"; + + std::uniform_int_distribution dice(0, 20); + const auto res = dice(_seed); + if (res > 12) { + std::cout << "Player escaped!\n"; + return Status::Escaped; + } + + std::cout << "Escape maneuver failed!\n"; + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/CMakeLists.txt new file mode 100644 index 0000000..9434ff8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(Battle) +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/CMakeLists.txt new file mode 100644 index 0000000..db39d82 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Core src/Time.cpp src/BasicPrintVisitor.cpp src/PrettyPrintVisitor.cpp) + +target_include_directories(Core PUBLIC include) + +target_link_libraries(Core + Ship + Store +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/BasicPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/BasicPrintVisitor.h new file mode 100644 index 0000000..cd3406f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/BasicPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class BasicPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/PrettyPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/PrettyPrintVisitor.h new file mode 100644 index 0000000..58dc6ad --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/PrettyPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class PrettyPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/PrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/PrintVisitor.h new file mode 100644 index 0000000..bec94c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/PrintVisitor.h @@ -0,0 +1,11 @@ +#pragma once + +class Store; +class Ship; + +class PrintVisitor { +public: + virtual ~PrintVisitor() = default; + virtual void visit(const Store&) const = 0; + virtual void visit(const Ship&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/BasicPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/BasicPrintVisitor.cpp new file mode 100644 index 0000000..05960c1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/BasicPrintVisitor.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include +#include +#include + +void BasicPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << std::setw(15) << cargo->getPrice() << "\n"; + } +} + +void BasicPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << "\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/PrettyPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/PrettyPrintVisitor.cpp new file mode 100644 index 0000000..99b6738 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/PrettyPrintVisitor.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +void PrettyPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} + +void PrettyPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/CMakeLists.txt new file mode 100644 index 0000000..b266b3f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(Player src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp src/Player.cpp src/RealPlayer.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Battle + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..e44bd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/Enemy.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include +#include +#include + +#include +#include + +template +class Enemy : public Player { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; + DamageVisitor _damageVisitor; +}; + +template +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)), _damageVisitor(visitor) {} + +template +int Enemy::attack(Ship& playerShip) { + if (const auto damage = _strategy->handle(playerShip)) { + for (auto& cargo : playerShip.cargoes()) { + cargo->accept(_damageVisitor); + } + return damage; + } + + return 0; +} + +template +Ship* Enemy::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getPlayerShip(); +} + +template +std::unique_ptr Enemy::chooseAction(const BattleField& battleField) const { + static bool flag = true; + if (flag) { + flag = !flag; + return std::make_unique(); + } + + flag = !flag; + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/Player.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/Player.h new file mode 100644 index 0000000..7717389 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/Player.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +class BattleField; +class Ship; + +class Player { +public: + virtual ~Player() = default; + virtual int attack(Ship& playerShip) = 0; + virtual Ship& getShip() = 0; + virtual const Ship& getShip() const = 0; + + Action::Status makeAction(const BattleField& battleField); + +protected: + virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0; + virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/RealPlayer.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/RealPlayer.h new file mode 100644 index 0000000..04d8b1a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/include/Player/RealPlayer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include + +class RealPlayer : public Player { +public: + RealPlayer(const std::string& name, std::unique_ptr&& ship); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/Player.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/Player.cpp new file mode 100644 index 0000000..5e1f83b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/Player.cpp @@ -0,0 +1,11 @@ +#include "Player/Player.h" + +Action::Status Player::makeAction(const BattleField& battleField) { + std::unique_ptr action = chooseAction(battleField); + if (action->type() == Action::Type::Attack) { + Ship* enemyShip = chooseEnemyShip(battleField); + return (*action)(this, enemyShip); + } + + return (*action)(this, nullptr); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/RealPlayer.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/RealPlayer.cpp new file mode 100644 index 0000000..b5cd4de --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Player/src/RealPlayer.cpp @@ -0,0 +1,56 @@ +#include "Player/RealPlayer.h" + +#include +#include +#include +#include +#include + +#include + +std::random_device RealPlayer::_rd{}; + +RealPlayer::RealPlayer(const std::string& name, std::unique_ptr&& ship) + : _name(name), _ship(std::move(ship)) {} + +int RealPlayer::attack(Ship& playerShip) { + std::uniform_int_distribution dice(0, 20); + const int res = dice(_seed); + int input = 0; + int damage = 0; + std::cout << "Choose Ammo: 1) Round Shot 2) Chain Shot 3) Grape Shot: "; + std::cin >> input; + + if (input == 1 && res > 4) { + damage = 50; + } else if (input == 2 && res > 7) { + damage = 80; + } else if (input == 3 && res > 10) { + damage = 100; + } else { + return 0; + } + + playerShip.takeDamage(damage); + return damage; +} + +Ship* RealPlayer::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getEnemyShip(); +} + +std::unique_ptr RealPlayer::chooseAction(const BattleField& battleField) const { + int input = 0; + std::cout << "Choose Action: 1) Attack 2) Defense 3) Escape: "; + std::cin >> input; + + if (input == 1) { + return std::make_unique(); + } + if (input == 3) { + return std::make_unique(); + } + + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..a1adb4e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp + src/CargoDamageVisitor.cpp + src/ShipBuilder.cpp + src/ShipJsonBuilder.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/AlcoholFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/AlcoholFactory.h new file mode 100644 index 0000000..2f4928f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/AlcoholFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class AlcoholFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, int power, Alcohol::Type type) override { + return std::make_unique(amount, power, type); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..afd4120 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include "Ship/CargoDamageVisitor.h" + +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + virtual void accept(const CargoDamageVisitor& visitor) = 0; +}; + +struct Fruit : public Cargo { + int rottenCounter; + + Fruit(size_t amount, int rottenCounter); + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + enum class Type { Common, Rare, Epic, Legendary }; + Type type; + + Item(size_t amount, Type type); + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + enum class Type { White, Spiced, Dark}; + + int power; + Type type; + + Alcohol(size_t amount, int power, Type type); + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/CargoDamageVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/CargoDamageVisitor.h new file mode 100644 index 0000000..6e84c58 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/CargoDamageVisitor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Fruit; +class Alcohol; +class Item; + +class CargoDamageVisitor { +public: + CargoDamageVisitor() = default; + virtual ~CargoDamageVisitor() = default; + CargoDamageVisitor(const CargoDamageVisitor&) = default; + CargoDamageVisitor(CargoDamageVisitor&&) = default; + CargoDamageVisitor& operator=(const CargoDamageVisitor&) = default; + CargoDamageVisitor& operator=(CargoDamageVisitor&&) = default; + + virtual void operator()(Fruit& fruit) const; + virtual void operator()(Alcohol& alcohol) const; + virtual void operator()(Item& item) const; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/CargoFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/CargoFactory.h new file mode 100644 index 0000000..53c1e9f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/CargoFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +class Cargo; + +template +class CargoFactory { +public: + virtual std::unique_ptr create(size_t amount, Args... args) = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/FruitFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/FruitFactory.h new file mode 100644 index 0000000..c882d2a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/FruitFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class FruitFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, int rottenCounter) override { + return std::make_unique(amount, rottenCounter); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ItemFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ItemFactory.h new file mode 100644 index 0000000..4224e56 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ItemFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class ItemFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, Item::Type type) override { + return std::make_unique(amount, type); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..88e6495 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/Ship.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class PrintVisitor; +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + const std::vector>& cargoes() const; + + void increaseArmor(int armor) { _armor += armor; } + int durability() const { return _durability; } + int armor() const { return _armor; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + friend class ShipBuilder; + Ship() = default; + void initialize(); + + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + int _armor{0}; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipBuilder.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipBuilder.h new file mode 100644 index 0000000..8c1922a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipBuilder.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +class PrintVisitor; +class Time; + +class ShipBuilder { +public: + ShipBuilder(); + + [[nodiscard]] std::unique_ptr build(); + + ShipBuilder& setTime(Time* time); + ShipBuilder& setStrategy(std::unique_ptr> strategy); + ShipBuilder& setName(const std::string& name); + ShipBuilder& setCapacity(int capacity); + ShipBuilder& setCrew(int crew); + ShipBuilder& setVisitor(std::unique_ptr visitor); + ShipBuilder& setArmor(int armor); + ShipBuilder& setDurability(int durability); + +private: + std::unique_ptr _ship; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipJsonBuilder.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipJsonBuilder.h new file mode 100644 index 0000000..ff495c8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/include/Ship/ShipJsonBuilder.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +#include "Ship/ShipBuilder.h" + +using json = nlohmann::json; + +class Time; +class Ship; + +class ShipJsonBuilder : private ShipBuilder { +public: + ShipJsonBuilder(const std::filesystem::path& path); + + std::unique_ptr buildBrig(Time* time, const std::string& name); + std::unique_ptr buildFrigate(Time* time, const std::string& name); + std::unique_ptr buildGaleon(Time* time, const std::string& name); + +private: + void parseFile(const std::filesystem::path& path); + + json _shipsData; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..19e980f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/Cargo.cpp @@ -0,0 +1,54 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +Fruit::Fruit(size_t amount, int rottenCounter) + : Cargo(amount), rottenCounter(rottenCounter) { +} + +void Fruit::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +Item::Item(size_t amount, Type type) + : Cargo(amount), type(type) { +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +void Item::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +Alcohol::Alcohol(size_t amount, int power, Type type) + : Cargo(amount), power(power), type(type) {} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} + +void Alcohol::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/CargoDamageVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/CargoDamageVisitor.cpp new file mode 100644 index 0000000..6ff89e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/CargoDamageVisitor.cpp @@ -0,0 +1,20 @@ +#include + +#include + +std::random_device CargoDamageVisitor::_rd{}; + +void CargoDamageVisitor::operator()(Fruit& fruit) const { + std::uniform_int_distribution dice(0, 5); + fruit.amount -= dice(_seed); +} + +void CargoDamageVisitor::operator()(Alcohol& alcohol) const { + std::uniform_int_distribution dice(0, 10); + alcohol.amount -= (dice(_seed) / 2); +} + +void CargoDamageVisitor::operator()(Item& item) const { + std::uniform_int_distribution dice(0, 20); + item.amount -= (dice(_seed) / 4); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..6871c94 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/Ship.cpp @@ -0,0 +1,107 @@ +#include "Ship/Ship.h" + +#include +#include + +#include +#include +#include +#include + +void Ship::initialize() { + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + _visitor->visit(*this); +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _armor -= damage; + if (_armor >= 0) { + return; + } + + _durability += _armor; + _armor = 0; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +const std::vector>& Ship::cargoes() const { + return _cargoes; +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > amount) { + (*it)->amount -= amount; + return true; + } + amount -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipBuilder.cpp new file mode 100644 index 0000000..fb77e5f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipBuilder.cpp @@ -0,0 +1,68 @@ +#include "Ship/ShipBuilder.h" + +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include +#include + +ShipBuilder::ShipBuilder() + : _ship(std::make_unique()) { + _ship->_strategy = std::make_unique(); + _ship->_visitor = std::make_unique(); +} + +std::unique_ptr ShipBuilder::build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->_time || _ship->_name.empty() || _ship->_capacity == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + _ship->initialize(); + return std::move(_ship); +} + +ShipBuilder& ShipBuilder::setTime(Time* time) { + _ship->_time = time; + return *this; +} + +ShipBuilder& ShipBuilder::setStrategy(std::unique_ptr> strategy) { + _ship->_strategy = std::move(strategy); + return *this; +} + +ShipBuilder& ShipBuilder::setName(const std::string& name) { + _ship->_name = name; + return *this; +} + +ShipBuilder& ShipBuilder::setCapacity(int capacity) { + _ship->_capacity = capacity; + return *this; +} + +ShipBuilder& ShipBuilder::setCrew(int crew) { + _ship->_crew = crew; + return *this; +} + +ShipBuilder& ShipBuilder::setVisitor(std::unique_ptr visitor) { + _ship->_visitor = std::move(visitor); + return *this; +} + +ShipBuilder& ShipBuilder::setArmor(int armor) { + _ship->_armor = armor; + return *this; +} + +ShipBuilder& ShipBuilder::setDurability(int durability) { + _ship->_durability = durability; + return *this; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipJsonBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipJsonBuilder.cpp new file mode 100644 index 0000000..a9b653a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Ship/src/ShipJsonBuilder.cpp @@ -0,0 +1,48 @@ +#include "Ship/ShipJsonBuilder.h" + +#include "Ship/Ship.h" + +#include +#include +#include + +ShipJsonBuilder::ShipJsonBuilder(const std::filesystem::path& path) { + parseFile(path); +} + +std::unique_ptr ShipJsonBuilder::buildBrig(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["brige"]["capacity"]) + .setCrew(_shipsData["brige"]["crew"]) + .setArmor(_shipsData["brige"]["armor"]) + .setDurability(_shipsData["brige"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildFrigate(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["frigate"]["capacity"]) + .setCrew(_shipsData["frigate"]["crew"]) + .setArmor(_shipsData["frigate"]["armor"]) + .setDurability(_shipsData["frigate"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildGaleon(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["galeon"]["capacity"]) + .setCrew(_shipsData["galeon"]["crew"]) + .setArmor(_shipsData["galeon"]["armor"]) + .setDurability(_shipsData["galeon"]["durability"]) + .build(); +} + +void ShipJsonBuilder::parseFile(const std::filesystem::path& path) { + std::ifstream file(path); + if (!file.is_open()) { + std::cout << "Can't open a file! erno: " << strerror(errno) << '\n'; + abort(); + } + _shipsData = json::parse(file); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/include/Store/Store.h new file mode 100644 index 0000000..fbbbf9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/include/Store/Store.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class PrintVisitor; + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos, std::unique_ptr&& visitor); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + const std::vector>& cargoes() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/src/Store.cpp new file mode 100644 index 0000000..07a9e35 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/src/Store/src/Store.cpp @@ -0,0 +1,41 @@ +#include "Store/Store.h" + +#include + +Store::Store(std::vector>&& cargos, std::unique_ptr&& visitor) + : _seed(_rd()), _cargoes(std::move(cargos)), _visitor(std::move(visitor)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +const std::vector>& Store::cargoes() const { + return _cargoes; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +void Store::printCargo() const { + _visitor->visit(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder2.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder2.cpp new file mode 100644 index 0000000..1832c35 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder2.cpp @@ -0,0 +1,152 @@ +#include +#include +#include +#include +#include +#include +#include + +struct Time {}; +template +struct DifficultStrategy {}; +struct PrintVisitor {}; + +class Ship { +public: + void setCrew(int crew) { _crew = crew; } + void setArmor(int armor) { _armor = armor; } + void setCannons(int cannons) { _canons = cannons; } + void setDurability(int durability) { _durability = durability; } + + const std::string& name() const { return _name; } + int capacity() const { return _capacity; } + int crew() const { return _crew; } + int armor() const { return _armor; } + int canons() const { return _canons; } + int durability() const { return _durability; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + friend class ShipBuilder; + Ship() = default; + + Time* _time{}; + std::unique_ptr> _strategy{}; + std::string _name{}; + int _capacity{-1}; + int _maxCrew{-1}; + int _crew{10}; + std::unique_ptr _visitor{}; + int _armor{0}; + int _maxArmor{-1}; + int _canons{0}; + int _maxCannons{-1}; + int _durability{100}; + int _maxDurability{-1}; +}; + +class ShipBuilder { +public: + ShipBuilder() + : _ship(std::make_unique()) {} + + [[nodiscard]] std::unique_ptr build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->_time || + _ship->_name.empty() || + _ship->_capacity == -1 || + _ship->_maxCrew == -1 || + _ship->_maxArmor == -1 || + _ship->_maxCannons == -1 || + _ship->_maxDurability == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + return std::move(_ship); + } + + ShipBuilder& setTime(Time* time) { + _ship->_time = time; + return *this; + } + ShipBuilder& setStrategy(std::unique_ptr> strategy) { + _ship->_strategy = std::move(strategy); + return *this; + } + ShipBuilder& setName(const std::string& name) { + _ship->_name = name; + return *this; + } + ShipBuilder& setCapacity(int capacity) { + _ship->_capacity = capacity; + return *this; + } + ShipBuilder& setMaxCrew(int maxCrew) { + _ship->_maxCrew = maxCrew; + return *this; + } + ShipBuilder& setCrew(int crew) { + _ship->_crew = crew; + return *this; + } + ShipBuilder& setVisitor(std::unique_ptr visitor) { + _ship->_visitor = std::move(visitor); + return *this; + } + ShipBuilder& setArmor(int armor) { + _ship->_armor = armor; + return *this; + } + ShipBuilder& setMaxArmor(int maxArmor) { + _ship->_maxArmor = maxArmor; + return *this; + } + ShipBuilder& setCannons(int cannons) { + _ship->_canons = cannons; + return *this; + } + ShipBuilder& setMaxCannons(int maxCannons) { + _ship->_maxCannons = maxCannons; + return *this; + } + ShipBuilder& setDurability(int durability) { + _ship->_durability = durability; + return *this; + } + ShipBuilder& setMaxDurability(int maxDurability) { + _ship->_maxDurability = maxDurability; + return *this; + } + +private: + std::unique_ptr _ship; +}; + +int main() { + auto ship = ShipBuilder().build(); + if (ship) { + std::cout << "Ship name: " << ship->name() << '\n'; + } + + Time time; + auto ship2 = ShipBuilder() + .setTime(&time) + .setName("Black Pearl") + .setCapacity(500) + .setMaxCrew(200) + .setMaxArmor(1000) + .setMaxCannons(20) + .setMaxDurability(1000) + .build(); + + if (ship2) { + std::cout << "Ship name: " << ship2->name() << '\n'; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder3.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder3.cpp new file mode 100644 index 0000000..40358a7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/builder/builder3.cpp @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include +#include + +struct Time {}; +template +struct DifficultStrategy {}; +struct PrintVisitor {}; + +class Ship { +public: + void setCrew(int crew) { _crew = crew; } + void setArmor(int armor) { _armor = armor; } + void setCannons(int cannons) { _canons = cannons; } + void setDurability(int durability) { _durability = durability; } + + const std::string& name() const { return _name; } + int capacity() const { return _capacity; } + int crew() const { return _crew; } + int armor() const { return _armor; } + int canons() const { return _canons; } + int durability() const { return _durability; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + friend class ShipBuilder; + Ship() = default; + + Time* _time{}; + std::unique_ptr> _strategy{}; + std::string _name{}; + int _capacity{-1}; + int _maxCrew{-1}; + int _crew{10}; + std::unique_ptr _visitor{}; + int _armor{0}; + int _maxArmor{-1}; + int _canons{0}; + int _maxCannons{-1}; + int _durability{100}; + int _maxDurability{-1}; +}; + +class ShipBuilder { +public: + ShipBuilder() + : _ship(std::make_unique()) {} + + [[nodiscard]] std::unique_ptr build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->_time || + _ship->_name.empty() || + _ship->_capacity == -1 || + _ship->_maxCrew == -1 || + _ship->_maxArmor == -1 || + _ship->_maxCannons == -1 || + _ship->_maxDurability == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + return std::move(_ship); + } + + ShipBuilder& setTime(Time* time) { + _ship->_time = time; + return *this; + } + ShipBuilder& setStrategy(std::unique_ptr> strategy) { + _ship->_strategy = std::move(strategy); + return *this; + } + ShipBuilder& setName(const std::string& name) { + _ship->_name = name; + return *this; + } + ShipBuilder& setCapacity(int capacity) { + _ship->_capacity = capacity; + return *this; + } + ShipBuilder& setMaxCrew(int maxCrew) { + _ship->_maxCrew = maxCrew; + return *this; + } + ShipBuilder& setCrew(int crew) { + _ship->_crew = crew; + return *this; + } + ShipBuilder& setVisitor(std::unique_ptr visitor) { + _ship->_visitor = std::move(visitor); + return *this; + } + ShipBuilder& setArmor(int armor) { + _ship->_armor = armor; + return *this; + } + ShipBuilder& setMaxArmor(int maxArmor) { + _ship->_maxArmor = maxArmor; + return *this; + } + ShipBuilder& setCannons(int cannons) { + _ship->_canons = cannons; + return *this; + } + ShipBuilder& setMaxCannons(int maxCannons) { + _ship->_maxCannons = maxCannons; + return *this; + } + ShipBuilder& setDurability(int durability) { + _ship->_durability = durability; + return *this; + } + ShipBuilder& setMaxDurability(int maxDurability) { + _ship->_maxDurability = maxDurability; + return *this; + } + +private: + std::unique_ptr _ship; +}; + +class ShipJsonBuilder : private ShipBuilder { +public: + std::unique_ptr buildBrig(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(500) + .setMaxCrew(200) + .setMaxArmor(1000) + .setMaxCannons(20) + .setMaxDurability(1000) + .build(); + } + std::unique_ptr buildFrigate(); + std::unique_ptr buildGaleon(); +}; + +int main() { + auto ship = ShipBuilder().build(); + if (ship) { + std::cout << "Ship name: " << ship->name() << '\n'; + } + + Time time; + auto ship2 = ShipBuilder() + .setTime(&time) + .setName("Black Pearl") + .setCapacity(500) + .setMaxCrew(200) + .setMaxArmor(1000) + .setMaxCannons(20) + .setMaxDurability(1000) + .build(); + + if (ship2) { + std::cout << "Ship name: " << ship2->name() << '\n'; + } + + auto ship3 = ShipJsonBuilder().buildBrig(&time, "Black Widow"); + if (ship3) { + std::cout << "Ship name: " << ship3->name() << '\n'; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator.cpp new file mode 100644 index 0000000..fa1cf6e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} + +struct Cargo { + size_t amount; + + Cargo(size_t amount) + : amount(amount) {} + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; +}; + +struct Fruit : public Cargo, public TimeObserver { + static constexpr int MAX_TIME = 20; + static constexpr int BASE_PRICE = 10; + int rottenCounter; + Time* time; + + Fruit(Time* time, size_t amount, int rottenCounter) + : Cargo(amount), time(time), rottenCounter(rottenCounter) { + time->attach(this); + } + + // Rule of 5 + ~Fruit() { + time->detach(this); + } + + size_t getPrice() const override { + return (rottenCounter * BASE_PRICE) / MAX_TIME; + } + + const std::string& name() const override { + static const std::string name = "Banana"; + return name; + } + + void nextDay() override { + rottenCounter = std::max(0, rottenCounter - 1); + } +}; + +struct Item : public Cargo, public TimeObserver { + enum class Type { Common = 100, + Rare = 300, + Epic = 1000, + Legendary = 2500 }; + static constexpr int MAX_DURABILITY = 100; + Time* time; + Type type; + int durability{MAX_DURABILITY}; + + Item(Time* time, size_t amount, Type type) + : Cargo(amount), time(time), type(type) { + time->attach(this); + } + + // Rule of 5 + ~Item() { + time->detach(this); + } + + size_t getPrice() const override { + return (durability * static_cast(type)) / MAX_DURABILITY; + } + + const std::string& name() const override { + static const std::string name = "Item"; + return name; + } + + void nextDay() override { + durability = std::max(0, durability - 1); + } +}; + +struct Alcohol : public Cargo { + enum class Type { White = 100, + Spiced = 200, + Dark = 300, + Seasoned = 500 }; + static constexpr int MAX_POWER = 96; + int power; + Type type; + + Alcohol(size_t amount, int power, Type type) + : Cargo(amount), power(power), type(type) {} + + size_t getPrice() const override { + return (power * static_cast(type)) / MAX_POWER; + } + + const std::string& name() const override { + static const std::string name = "Rum"; + return name; + } +}; + +int main() { + Time time; + std::unique_ptr cargo = std::make_unique(&time, 10, Item::Type::Epic); + + for (int i = 1; i <= 15; ++i) { + std::cout << "DAY: " << i << " | Name: " << cargo->name() << " | Price: " << cargo->getPrice() << '\n'; + ++time; + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/CMakeLists.txt new file mode 100644 index 0000000..751c5ff --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +include(FetchContent) +FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.11.3 +) +FetchContent_MakeAvailable(json) + +target_link_libraries(${PROJECT_NAME} + Battle + Ship + Store + Core + Player + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/main.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/main.cpp new file mode 100644 index 0000000..dd12d5f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/main.cpp @@ -0,0 +1,53 @@ +#include +#include +#include + +#include "Battle/BattleField.h" +#include "Core/BasicPrintVisitor.h" +#include "Core/FullInfoPrintVisitor.h" +#include "Core/PrettyPrintVisitor.h" +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Player/RealPlayer.h" +#include "Ship/AlcoholFactory.h" +#include "Ship/AlcoholType.h" +#include "Ship/Cargo.h" +#include "Ship/CargoDamageVisitor.h" +#include "Ship/FruitFactory.h" +#include "Ship/ItemFactory.h" +#include "Ship/ItemType.h" +#include "Ship/Ship.h" +#include "Ship/ShipBuilder.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Ship/ShipJsonBuilder.h" +#include "Ship/Valuable.h" +#include "Ship/Vulnerable.h" +#include "Store/Store.h" + +int main() { + Time time; + + auto ship = ShipBuilder() + .setTime(&time) + .setName("Black Widow") + .setCapacity(2500) + .setCrew(40) + .setStrategy(std::make_unique()) + .setVisitor(std::make_unique()) + .setArmor(100) + .setDurability(1000) + .build(); + ship->load(std::make_unique>(AlcoholFactory().create(800, 40), AlcoholType::Seasoned)); + ship->load(std::make_unique(FruitFactory().create(500), &time, 15, 15)); + ship->load(std::make_unique(FruitFactory().create(700), &time, 20, 20)); + ship->load(std::make_unique(std::make_unique>(ItemFactory().create(250), ItemType::Epic), &time, 100, 100)); + + for (size_t i = 1; i <= 15; ++i) { + std::cout << "day: " << i << '\n'; + ship->printCargo(); + ++time; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/ships.json b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/ships.json new file mode 100644 index 0000000..9b19770 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/ships.json @@ -0,0 +1,20 @@ +{ + "brige":{ + "capacity":1000, + "crew":40, + "durability":1000, + "armor":100 + }, + "frigate":{ + "capacity":1500, + "crew":70, + "durability":2000, + "armor":500 + }, + "galeon":{ + "capacity":2500, + "crew":120, + "durability":3000, + "armor":1000 + } + } \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/CMakeLists.txt new file mode 100644 index 0000000..acd8033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Battle src/Defense.cpp src/BattleField.cpp src/Attack.cpp src/Escape.cpp) + +target_include_directories(Battle PUBLIC include) + +target_link_libraries(Battle + Ship + Player +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Action.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Action.h new file mode 100644 index 0000000..f271d40 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Action.h @@ -0,0 +1,24 @@ +#pragma once + +class Player; +class Ship; + +class Action { +public: + // As you can see keeping type here, require modify a base class whenever we add a new class, this volatile an open-close principle. + // We should be able to extend code without modification already implemented one + enum class Type { + Attack, + Defense, + Escape + }; + + enum class Status { + Escaped, + Defeated, + Nothing, + }; + + virtual Status operator()(Player* player, Ship* enemyShip) = 0; + virtual Type type() const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Attack.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Attack.h new file mode 100644 index 0000000..a06cba0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Attack.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Attack : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Attack; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/BattleField.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/BattleField.h new file mode 100644 index 0000000..6196660 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/BattleField.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Ship; +class Player; + +class BattleField { +public: + BattleField(Player* player, Player* enemy); + + Ship* getPlayerShip() const; + Ship* getEnemyShip() const; + std::vector players() const; + +private: + Player* _player; + Player* _enemy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Defense.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Defense.h new file mode 100644 index 0000000..fefd271 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Defense.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Defense : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Type::Defense; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Escape.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Escape.h new file mode 100644 index 0000000..97dc54d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/include/Battle/Escape.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Battle/Action.h" + +#include + +class Player; +class Ship; + +class Escape : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Escape; } + +private: + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Attack.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Attack.cpp new file mode 100644 index 0000000..b3f5e8a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Attack.cpp @@ -0,0 +1,22 @@ +#include "Battle/Attack.h" + +#include +#include + +#include + +Action::Status Attack::operator()(Player* player, Ship* enemyShip) { + const int damage = player->attack(*enemyShip); + std::cout << "Player ship: " << player->getShip().name() << " attack ship: " << enemyShip->name() << '\n'; + if (damage) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } + + if (enemyShip->durability() <= 0) { + return Status::Defeated; + } + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/BattleField.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/BattleField.cpp new file mode 100644 index 0000000..4e10569 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/BattleField.cpp @@ -0,0 +1,16 @@ +#include "Battle/BattleField.h" + +#include + +BattleField::BattleField(Player* player, Player* enemy) + : _player{player}, _enemy{enemy} {} + +Ship* BattleField::getPlayerShip() const { + return &_player->getShip(); +} +Ship* BattleField::getEnemyShip() const { + return &_enemy->getShip(); +} +std::vector BattleField::players() const { + return {_player, _enemy}; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Defense.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Defense.cpp new file mode 100644 index 0000000..a170c81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Defense.cpp @@ -0,0 +1,13 @@ +#include "Battle/Defense.h" + +#include +#include + +#include + +Action::Status Defense::operator()(Player* player, Ship*) { + player->getShip().increaseArmor(50); + std::cout << "Player ship: " << player->getShip().name() << " defense\n"; + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Escape.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Escape.cpp new file mode 100644 index 0000000..5b222fa --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Battle/src/Escape.cpp @@ -0,0 +1,22 @@ +#include "Battle/Escape.h" + +#include +#include + +#include + +std::random_device Escape::_rd{}; + +Action::Status Escape::operator()(Player* player, Ship*) { + std::cout << "Player ship: " << player->getShip().name() << " try to escape!\n"; + + std::uniform_int_distribution dice(0, 20); + const auto res = dice(_seed); + if (res > 12) { + std::cout << "Player escaped!\n"; + return Status::Escaped; + } + + std::cout << "Escape maneuver failed!\n"; + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/CMakeLists.txt new file mode 100644 index 0000000..9434ff8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(Battle) +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/CMakeLists.txt new file mode 100644 index 0000000..25b294b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Core src/Time.cpp src/BasicPrintVisitor.cpp src/PrettyPrintVisitor.cpp src/FullInfoPrintVisitor.cpp) + +target_include_directories(Core PUBLIC include) + +target_link_libraries(Core + Ship + Store +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/BasicPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/BasicPrintVisitor.h new file mode 100644 index 0000000..cd3406f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/BasicPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class BasicPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/FullInfoPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/FullInfoPrintVisitor.h new file mode 100644 index 0000000..c932bc6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/FullInfoPrintVisitor.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class FullInfoPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; + +private: + void print() const; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/PrettyPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/PrettyPrintVisitor.h new file mode 100644 index 0000000..58dc6ad --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/PrettyPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class PrettyPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/PrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/PrintVisitor.h new file mode 100644 index 0000000..bec94c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/PrintVisitor.h @@ -0,0 +1,11 @@ +#pragma once + +class Store; +class Ship; + +class PrintVisitor { +public: + virtual ~PrintVisitor() = default; + virtual void visit(const Store&) const = 0; + virtual void visit(const Ship&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/BasicPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/BasicPrintVisitor.cpp new file mode 100644 index 0000000..a0afe9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/BasicPrintVisitor.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include +#include +#include + +void BasicPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount() << std::setw(15) << cargo->getPrice() << "\n"; + } +} + +void BasicPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount() << "\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/FullInfoPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/FullInfoPrintVisitor.cpp new file mode 100644 index 0000000..a43d164 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/FullInfoPrintVisitor.cpp @@ -0,0 +1,24 @@ +#include + +#include +#include +#include +#include + +void FullInfoPrintVisitor::visit(const Store& store) const { + std::cout << "Don't have time to implement this, sorry xD\n"; +} + +void FullInfoPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount() << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/PrettyPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/PrettyPrintVisitor.cpp new file mode 100644 index 0000000..e639a1b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/PrettyPrintVisitor.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +void PrettyPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount() << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} + +void PrettyPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount() << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/CMakeLists.txt new file mode 100644 index 0000000..b266b3f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(Player src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp src/Player.cpp src/RealPlayer.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Battle + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..e44bd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/Enemy.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include +#include +#include + +#include +#include + +template +class Enemy : public Player { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; + DamageVisitor _damageVisitor; +}; + +template +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)), _damageVisitor(visitor) {} + +template +int Enemy::attack(Ship& playerShip) { + if (const auto damage = _strategy->handle(playerShip)) { + for (auto& cargo : playerShip.cargoes()) { + cargo->accept(_damageVisitor); + } + return damage; + } + + return 0; +} + +template +Ship* Enemy::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getPlayerShip(); +} + +template +std::unique_ptr Enemy::chooseAction(const BattleField& battleField) const { + static bool flag = true; + if (flag) { + flag = !flag; + return std::make_unique(); + } + + flag = !flag; + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/Player.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/Player.h new file mode 100644 index 0000000..7717389 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/Player.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +class BattleField; +class Ship; + +class Player { +public: + virtual ~Player() = default; + virtual int attack(Ship& playerShip) = 0; + virtual Ship& getShip() = 0; + virtual const Ship& getShip() const = 0; + + Action::Status makeAction(const BattleField& battleField); + +protected: + virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0; + virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/RealPlayer.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/RealPlayer.h new file mode 100644 index 0000000..04d8b1a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/include/Player/RealPlayer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include + +class RealPlayer : public Player { +public: + RealPlayer(const std::string& name, std::unique_ptr&& ship); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/Player.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/Player.cpp new file mode 100644 index 0000000..5e1f83b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/Player.cpp @@ -0,0 +1,11 @@ +#include "Player/Player.h" + +Action::Status Player::makeAction(const BattleField& battleField) { + std::unique_ptr action = chooseAction(battleField); + if (action->type() == Action::Type::Attack) { + Ship* enemyShip = chooseEnemyShip(battleField); + return (*action)(this, enemyShip); + } + + return (*action)(this, nullptr); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/RealPlayer.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/RealPlayer.cpp new file mode 100644 index 0000000..b5cd4de --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Player/src/RealPlayer.cpp @@ -0,0 +1,56 @@ +#include "Player/RealPlayer.h" + +#include +#include +#include +#include +#include + +#include + +std::random_device RealPlayer::_rd{}; + +RealPlayer::RealPlayer(const std::string& name, std::unique_ptr&& ship) + : _name(name), _ship(std::move(ship)) {} + +int RealPlayer::attack(Ship& playerShip) { + std::uniform_int_distribution dice(0, 20); + const int res = dice(_seed); + int input = 0; + int damage = 0; + std::cout << "Choose Ammo: 1) Round Shot 2) Chain Shot 3) Grape Shot: "; + std::cin >> input; + + if (input == 1 && res > 4) { + damage = 50; + } else if (input == 2 && res > 7) { + damage = 80; + } else if (input == 3 && res > 10) { + damage = 100; + } else { + return 0; + } + + playerShip.takeDamage(damage); + return damage; +} + +Ship* RealPlayer::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getEnemyShip(); +} + +std::unique_ptr RealPlayer::chooseAction(const BattleField& battleField) const { + int input = 0; + std::cout << "Choose Action: 1) Attack 2) Defense 3) Escape: "; + std::cin >> input; + + if (input == 1) { + return std::make_unique(); + } + if (input == 3) { + return std::make_unique(); + } + + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..2c9233d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/CMakeLists.txt @@ -0,0 +1,17 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp + src/CargoDamageVisitor.cpp + src/ShipBuilder.cpp + src/ShipJsonBuilder.cpp + src/DecoratedCargo.cpp + src/Vulnerable.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/AlcoholFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/AlcoholFactory.h new file mode 100644 index 0000000..60a3ed6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/AlcoholFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class AlcoholFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, int power) override { + return std::make_unique(amount, power); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/AlcoholType.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/AlcoholType.h new file mode 100644 index 0000000..a59d85a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/AlcoholType.h @@ -0,0 +1,6 @@ +#pragma once + +enum class AlcoholType { White = 1, + Spiced = 2, + Dark = 3, + Seasoned = 5 }; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..5183c15 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +#include "Ship/CargoDamageVisitor.h" + +#include + +struct Cargo { + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + virtual size_t amount() const { return _amount; } + virtual size_t& amount() { return _amount; } + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + virtual void accept(const CargoDamageVisitor& visitor) = 0; + +protected: + Cargo() = default; + +private: + size_t _amount; +}; + +struct Fruit : public Cargo { + static constexpr int BASE_PRICE = 10; + + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + static constexpr int BASE_PRICE = 100; + + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + static constexpr int MAX_POWER = 96; + static constexpr int BASE_PRICE = 100; + int power; + + Alcohol(size_t amount, int power); + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/CargoDamageVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/CargoDamageVisitor.h new file mode 100644 index 0000000..6e84c58 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/CargoDamageVisitor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Fruit; +class Alcohol; +class Item; + +class CargoDamageVisitor { +public: + CargoDamageVisitor() = default; + virtual ~CargoDamageVisitor() = default; + CargoDamageVisitor(const CargoDamageVisitor&) = default; + CargoDamageVisitor(CargoDamageVisitor&&) = default; + CargoDamageVisitor& operator=(const CargoDamageVisitor&) = default; + CargoDamageVisitor& operator=(CargoDamageVisitor&&) = default; + + virtual void operator()(Fruit& fruit) const; + virtual void operator()(Alcohol& alcohol) const; + virtual void operator()(Item& item) const; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/CargoFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/CargoFactory.h new file mode 100644 index 0000000..53c1e9f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/CargoFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +class Cargo; + +template +class CargoFactory { +public: + virtual std::unique_ptr create(size_t amount, Args... args) = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/DecoratedCargo.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/DecoratedCargo.h new file mode 100644 index 0000000..8e2616a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/DecoratedCargo.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include + +class DecoratedCargo : public Cargo { +public: + explicit DecoratedCargo(std::unique_ptr&& cargo); + +protected: + Cargo& cargo() { return *_cargo; } + const Cargo& cargo() const { return *_cargo; } + +private: + std::unique_ptr _cargo; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/FruitFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/FruitFactory.h new file mode 100644 index 0000000..3906a69 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/FruitFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class FruitFactory : public CargoFactory<> { +public: + std::unique_ptr create(size_t amount) override { + return std::make_unique(amount); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ItemFactory.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ItemFactory.h new file mode 100644 index 0000000..fc949e3 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ItemFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class ItemFactory : public CargoFactory<> { +public: + std::unique_ptr create(size_t amount) override { + return std::make_unique(amount); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ItemType.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ItemType.h new file mode 100644 index 0000000..266429b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ItemType.h @@ -0,0 +1,6 @@ +#pragma once + +enum class ItemType { Common = 1, + Rare = 3, + Epic = 10, + Legendary = 25 }; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..88e6495 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Ship.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class PrintVisitor; +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + const std::vector>& cargoes() const; + + void increaseArmor(int armor) { _armor += armor; } + int durability() const { return _durability; } + int armor() const { return _armor; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + friend class ShipBuilder; + Ship() = default; + void initialize(); + + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + int _armor{0}; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipBuilder.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipBuilder.h new file mode 100644 index 0000000..8c1922a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipBuilder.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +class PrintVisitor; +class Time; + +class ShipBuilder { +public: + ShipBuilder(); + + [[nodiscard]] std::unique_ptr build(); + + ShipBuilder& setTime(Time* time); + ShipBuilder& setStrategy(std::unique_ptr> strategy); + ShipBuilder& setName(const std::string& name); + ShipBuilder& setCapacity(int capacity); + ShipBuilder& setCrew(int crew); + ShipBuilder& setVisitor(std::unique_ptr visitor); + ShipBuilder& setArmor(int armor); + ShipBuilder& setDurability(int durability); + +private: + std::unique_ptr _ship; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipJsonBuilder.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipJsonBuilder.h new file mode 100644 index 0000000..ff495c8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/ShipJsonBuilder.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +#include "Ship/ShipBuilder.h" + +using json = nlohmann::json; + +class Time; +class Ship; + +class ShipJsonBuilder : private ShipBuilder { +public: + ShipJsonBuilder(const std::filesystem::path& path); + + std::unique_ptr buildBrig(Time* time, const std::string& name); + std::unique_ptr buildFrigate(Time* time, const std::string& name); + std::unique_ptr buildGaleon(Time* time, const std::string& name); + +private: + void parseFile(const std::filesystem::path& path); + + json _shipsData; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Valuable.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Valuable.h new file mode 100644 index 0000000..9807d57 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Valuable.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include + +template +class Valuable : public DecoratedCargo { +public: + Valuable(std::unique_ptr&& cargo, ValueType value) + : DecoratedCargo(std::move(cargo)), _value(value) { + } + + size_t amount() const override { return cargo().amount(); } + size_t& amount() override { return cargo().amount(); } + + size_t getPrice() const override { + return cargo().getPrice() * static_cast(_value); + } + + const std::string& name() const override { + return cargo().name(); + } + + void accept(const CargoDamageVisitor& visitor) override { + cargo().accept(visitor); + } + +private: + ValueType _value; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Vulnerable.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Vulnerable.h new file mode 100644 index 0000000..c7cbced --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/include/Ship/Vulnerable.h @@ -0,0 +1,29 @@ +#include "Ship/DecoratedCargo.h" + +#include + +class Time; + +class Vulnerable : public DecoratedCargo, public TimeObserver { +public: + Vulnerable(std::unique_ptr&& cargo, Time* time, int durability, int maxDurability); + + Vulnerable(const Vulnerable&) = default; + Vulnerable(Vulnerable&&) = default; + Vulnerable& operator=(const Vulnerable&) = default; + Vulnerable& operator=(Vulnerable&&) = default; + ~Vulnerable(); + + size_t amount() const override { return cargo().amount(); } + size_t& amount() override { return cargo().amount(); } + + void nextDay() override; + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; + +private: + Time* _time; + int _durability; + int _maxDurability; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..0fa653f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Cargo.cpp @@ -0,0 +1,46 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : _amount(amount) {} + +size_t Fruit::getPrice() const { + return BASE_PRICE; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +void Fruit::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Item::getPrice() const { + return BASE_PRICE; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +void Item::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +Alcohol::Alcohol(size_t amount, int power) + : Cargo(amount), power(power) {} + +size_t Alcohol::getPrice() const { + return (power * BASE_PRICE) / MAX_POWER; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} + +void Alcohol::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/CargoDamageVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/CargoDamageVisitor.cpp new file mode 100644 index 0000000..0792d32 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/CargoDamageVisitor.cpp @@ -0,0 +1,20 @@ +#include + +#include + +std::random_device CargoDamageVisitor::_rd{}; + +void CargoDamageVisitor::operator()(Fruit& fruit) const { + std::uniform_int_distribution dice(0, 5); + fruit.amount() -= dice(_seed); +} + +void CargoDamageVisitor::operator()(Alcohol& alcohol) const { + std::uniform_int_distribution dice(0, 10); + alcohol.amount() -= (dice(_seed) / 2); +} + +void CargoDamageVisitor::operator()(Item& item) const { + std::uniform_int_distribution dice(0, 20); + item.amount() -= (dice(_seed) / 4); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/DecoratedCargo.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/DecoratedCargo.cpp new file mode 100644 index 0000000..ea9f724 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/DecoratedCargo.cpp @@ -0,0 +1,8 @@ +#include "Ship/DecoratedCargo.h" + +#include + +DecoratedCargo::DecoratedCargo(std::unique_ptr&& cargo) + : _cargo(std::move(cargo)) { + assert(_cargo); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..6754047 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Ship.cpp @@ -0,0 +1,108 @@ +#include "Ship/Ship.h" + +#include +#include + +#include +#include +#include +#include + +void Ship::initialize() { + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + _visitor->visit(*this); +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount()) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + std::cout << "cap: " << _capacity << " | amount: " << cargo->amount() << std::endl; + if (_capacity > cargo->amount()) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _armor -= damage; + if (_armor >= 0) { + return; + } + + _durability += _armor; + _armor = 0; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +const std::vector>& Ship::cargoes() const { + return _cargoes; +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount() > amount) { + (*it)->amount() -= amount; + return true; + } + amount -= (*it)->amount(); + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipBuilder.cpp new file mode 100644 index 0000000..fb77e5f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipBuilder.cpp @@ -0,0 +1,68 @@ +#include "Ship/ShipBuilder.h" + +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include +#include + +ShipBuilder::ShipBuilder() + : _ship(std::make_unique()) { + _ship->_strategy = std::make_unique(); + _ship->_visitor = std::make_unique(); +} + +std::unique_ptr ShipBuilder::build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->_time || _ship->_name.empty() || _ship->_capacity == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + _ship->initialize(); + return std::move(_ship); +} + +ShipBuilder& ShipBuilder::setTime(Time* time) { + _ship->_time = time; + return *this; +} + +ShipBuilder& ShipBuilder::setStrategy(std::unique_ptr> strategy) { + _ship->_strategy = std::move(strategy); + return *this; +} + +ShipBuilder& ShipBuilder::setName(const std::string& name) { + _ship->_name = name; + return *this; +} + +ShipBuilder& ShipBuilder::setCapacity(int capacity) { + _ship->_capacity = capacity; + return *this; +} + +ShipBuilder& ShipBuilder::setCrew(int crew) { + _ship->_crew = crew; + return *this; +} + +ShipBuilder& ShipBuilder::setVisitor(std::unique_ptr visitor) { + _ship->_visitor = std::move(visitor); + return *this; +} + +ShipBuilder& ShipBuilder::setArmor(int armor) { + _ship->_armor = armor; + return *this; +} + +ShipBuilder& ShipBuilder::setDurability(int durability) { + _ship->_durability = durability; + return *this; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipJsonBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipJsonBuilder.cpp new file mode 100644 index 0000000..a9b653a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/ShipJsonBuilder.cpp @@ -0,0 +1,48 @@ +#include "Ship/ShipJsonBuilder.h" + +#include "Ship/Ship.h" + +#include +#include +#include + +ShipJsonBuilder::ShipJsonBuilder(const std::filesystem::path& path) { + parseFile(path); +} + +std::unique_ptr ShipJsonBuilder::buildBrig(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["brige"]["capacity"]) + .setCrew(_shipsData["brige"]["crew"]) + .setArmor(_shipsData["brige"]["armor"]) + .setDurability(_shipsData["brige"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildFrigate(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["frigate"]["capacity"]) + .setCrew(_shipsData["frigate"]["crew"]) + .setArmor(_shipsData["frigate"]["armor"]) + .setDurability(_shipsData["frigate"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildGaleon(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["galeon"]["capacity"]) + .setCrew(_shipsData["galeon"]["crew"]) + .setArmor(_shipsData["galeon"]["armor"]) + .setDurability(_shipsData["galeon"]["durability"]) + .build(); +} + +void ShipJsonBuilder::parseFile(const std::filesystem::path& path) { + std::ifstream file(path); + if (!file.is_open()) { + std::cout << "Can't open a file! erno: " << strerror(errno) << '\n'; + abort(); + } + _shipsData = json::parse(file); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Vulnerable.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Vulnerable.cpp new file mode 100644 index 0000000..41d6a80 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Ship/src/Vulnerable.cpp @@ -0,0 +1,32 @@ +#include "Ship/Vulnerable.h" + +#include + +#include + +Vulnerable::Vulnerable(std::unique_ptr&& cargo, Time* time, int durability, int maxDurability) + : DecoratedCargo(std::move(cargo)), _time(time), _durability(durability), _maxDurability(maxDurability) { + assert(_time); + time->attach(this); +} + +// Rule of 5 +Vulnerable::~Vulnerable() { + _time->detach(this); +} + +void Vulnerable::nextDay() { + _durability = std::max(0, _durability - 1); +} + +size_t Vulnerable::getPrice() const { + return (_durability * cargo().getPrice()) / _maxDurability; +} + +const std::string& Vulnerable::name() const { + return cargo().name(); +} + +void Vulnerable::accept(const CargoDamageVisitor& visitor) { + cargo().accept(visitor); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/include/Store/Store.h new file mode 100644 index 0000000..fbbbf9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/include/Store/Store.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class PrintVisitor; + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos, std::unique_ptr&& visitor); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + const std::vector>& cargoes() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/src/Store.cpp new file mode 100644 index 0000000..a950548 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/src/Store/src/Store.cpp @@ -0,0 +1,41 @@ +#include "Store/Store.h" + +#include + +Store::Store(std::vector>&& cargos, std::unique_ptr&& visitor) + : _seed(_rd()), _cargoes(std::move(cargos)), _visitor(std::move(visitor)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount() += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +const std::vector>& Store::cargoes() const { + return _cargoes; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +void Store::printCargo() const { + _visitor->visit(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator2.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator2.cpp new file mode 100644 index 0000000..e3e8814 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator2.cpp @@ -0,0 +1,209 @@ +#include +#include +#include +#include +#include +#include +#include + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} + +struct Cargo { + size_t amount; + + Cargo(size_t amount) + : amount(amount) {} + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + +protected: + Cargo() = default; +}; + +class DecoratedCargo : public Cargo { +public: + explicit DecoratedCargo(std::unique_ptr&& cargo) + : _cargo(std::move(cargo)) { + assert(_cargo); + } + +protected: + Cargo& cargo() { return *_cargo; } + const Cargo& cargo() const { return *_cargo; } + +private: + std::unique_ptr _cargo; +}; + +class Vulnerable : public DecoratedCargo, public TimeObserver { +public: + Vulnerable(std::unique_ptr&& cargo, Time* time, int durability, int maxDurability) + : DecoratedCargo(std::move(cargo)), _time(time), _durability(durability), _maxDurability(maxDurability) { + assert(_time); + time->attach(this); + } + + // Rule of 5 + ~Vulnerable() { + _time->detach(this); + } + + void nextDay() override { + _durability = std::max(0, _durability - 1); + } + + size_t getPrice() const override { + return (_durability * cargo().getPrice()) / _maxDurability; + } + + const std::string& name() const override { + return cargo().name(); + } + +private: + Time* _time; + int _durability; + int _maxDurability; +}; + +template +class Valuable : public DecoratedCargo { +public: + Valuable(std::unique_ptr&& cargo, ValueType value) + : DecoratedCargo(std::move(cargo)), _value(value) { + } + + size_t getPrice() const override { + return cargo().getPrice() * static_cast(_value); + } + + const std::string& name() const override { + return cargo().name(); + } + +private: + ValueType _value; +}; + +struct Fruit : public Cargo { + static constexpr int BASE_PRICE = 10; + + using Cargo::Cargo; + + size_t getPrice() const override { + return BASE_PRICE; + } + + const std::string& name() const override { + static const std::string name = "Banana"; + return name; + } +}; + +enum class ItemType { Common = 1, + Rare = 3, + Epic = 10, + Legendary = 25 }; + +struct Item : public Cargo { + static constexpr int BASE_PRICE = 100; + + using Cargo::Cargo; + + size_t getPrice() const override { + return BASE_PRICE; + } + + const std::string& name() const override { + static const std::string name = "Item"; + return name; + } +}; + +enum class AlcoholType { White = 1, + Spiced = 2, + Dark = 3, + Seasoned = 5 }; + +struct Alcohol : public Cargo { + static constexpr int MAX_POWER = 96; + static constexpr int BASE_PRICE = 100; + int power; + + Alcohol(size_t amount, int power) + : Cargo(amount), power(power) {} + + size_t getPrice() const override { + return (power * BASE_PRICE) / MAX_POWER; + } + + const std::string& name() const override { + static const std::string name = "Rum"; + return name; + } +}; + +int main() { + Time time; + std::unique_ptr cargo = std::make_unique( + std::make_unique>( + std::make_unique(10), ItemType::Epic), + &time, 100, 100); + std::unique_ptr cargo2 = + std::make_unique>( + std::make_unique(10), ItemType::Epic); + + for (int i = 1; i <= 15; ++i) { + std::cout << "DAY: " << i << " | Name: " << cargo->name() << " | Price: " << cargo->getPrice(); + std::cout << " |-| Name: " << cargo2->name() << " | Price: " << cargo2->getPrice() << '\n'; + ++time; + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator3.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator3.cpp new file mode 100644 index 0000000..951f520 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/decorator/decorator3.cpp @@ -0,0 +1,186 @@ +#include +#include +#include +#include +#include +#include +#include + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} + +struct Cargo { + size_t amount; + + auto operator<=>(const Cargo&) const = default; + +protected: + // Only derived class can create it + Cargo(size_t amount) + : amount(amount) {} + Cargo() = default; +}; + +template +class Vulnerable : public TimeObserver { +public: + Vulnerable(Neasted neasted, Time* time, int durability, int maxDurability) + : _neasted(neasted), _time(time), _durability(durability), _maxDurability(maxDurability) { + assert(_time); + time->attach(this); + } + + // Rule of 5 + ~Vulnerable() { + _time->detach(this); + } + + void nextDay() override { + _durability = std::max(0, _durability - 1); + } + + size_t getPrice() const { + return (_durability * _neasted.getPrice()) / _maxDurability; + } + + const std::string& name() const { + return _neasted.name(); + } + +private: + Neasted _neasted; + Time* _time; + int _durability; + int _maxDurability; +}; + +template +class Valuable { +public: + Valuable(Neasted neasted, ValueType value) + : _neasted(neasted), _value(value) { + } + + size_t getPrice() const { + return _neasted.getPrice() * static_cast(_value); + } + + const std::string& name() const { + return _neasted.name(); + } + +private: + Neasted _neasted; + ValueType _value; +}; + +struct Fruit : public Cargo { + static constexpr int BASE_PRICE = 10; + + Fruit(size_t amount) + : Cargo(amount) {} + + size_t getPrice() const { + return BASE_PRICE; + } + + const std::string& name() const { + static const std::string name = "Banana"; + return name; + } +}; + +enum class ItemType { Common = 1, + Rare = 3, + Epic = 10, + Legendary = 25 }; + +struct Item : public Cargo { + static constexpr int BASE_PRICE = 100; + + Item(size_t amount) + : Cargo(amount) {} + + size_t getPrice() const { + return BASE_PRICE; + } + + const std::string& name() const { + static const std::string name = "Item"; + return name; + } +}; + +enum class AlcoholType { White = 1, + Spiced = 2, + Dark = 3, + Seasoned = 5 }; + +struct Alcohol : public Cargo { + static constexpr int MAX_POWER = 96; + static constexpr int BASE_PRICE = 100; + int power; + + Alcohol(size_t amount, int power) + : Cargo(amount), power(power) {} + + size_t getPrice() const { + return (power * BASE_PRICE) / MAX_POWER; + } + + const std::string& name() const { + static const std::string name = "Rum"; + return name; + } +}; + +int main() { + Time time; + auto cargo = Vulnerable(Valuable(Item(10), ItemType::Epic), &time, 100, 100); + auto cargo2 = Valuable(Item(10), ItemType::Epic); + + for (int i = 1; i <= 15; ++i) { + std::cout << "DAY: " << i << " | Name: " << cargo.name() << " | Price: " << cargo.getPrice(); + std::cout << " |-| Name: " << cargo2.name() << " | Price: " << cargo2.getPrice() << '\n'; + ++time; + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer.cpp new file mode 100644 index 0000000..91a5c4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include + +class Browser { +public: + struct Observer { + virtual ~Observer() = default; + virtual void browserAboutToQuit() = 0; + virtual void browserFocusChanged() = 0; + }; + + ~Browser() { + std::cout << "Browser is closing\n"; + notifyOnQuit(); + std::cout << "Browser closed\n"; + } + // Rule of 5 :) + + void attach(Observer* observer) { _observers.push_back(observer); } + void detach(Observer* observer) { std::erase(_observers, observer); } + +private: + void notifyOnQuit() const { + std::ranges::for_each(_observers, std::mem_fn(&Observer::browserAboutToQuit)); + } + void notifyOnFocusChanfed() const { + std::ranges::for_each(_observers, std::mem_fn(&Observer::browserFocusChanged)); + } + + std::vector _observers; +}; + +class TabStripManager : public Browser::Observer { +public: + TabStripManager(Browser* browser) + : _browser(browser) { + assert(browser); + browser->attach(this); + } + + ~TabStripManager() { + if (_browser) { + _browser->detach(this); + } + } + // Rule of 5 :) + + void browserAboutToQuit() override { + std::cout << "TabStripManager will close tabs\n"; + _browser = nullptr; + } + + void browserFocusChanged() override { + std::cout << "TabStripManager lost focus\n"; + } + +private: + Browser* _browser; +}; + +int main() { + std::unique_ptr browser = std::make_unique(); + TabStripManager manager(browser.get()); + browser = nullptr; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/CMakeLists.txt new file mode 100644 index 0000000..c6cd3a2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Ship + Store + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/main.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/main.cpp new file mode 100644 index 0000000..9bd33cf --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/main.cpp @@ -0,0 +1,44 @@ +#include +#include +#include + +#include "Core/Time.h" +#include "Ship/Cargo.h" +#include "Ship/Ship.h" +#include "Store/Store.h" + +int main() { + Time time; + + std::vector> cargoes; + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + auto store = std::unique_ptr>( + [&cargoes, &time]() { + auto* store = new Store(std::move(cargoes)); + try { + time.attach(store); + } catch (...) { + abort(); + } + return store; }(), + [&time](Store* store) { + time.detach(store); + delete store; + }); + + Ship ship(&time, "Black Widow", 1000, 40); + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.load(std::make_unique(150)); + ship.printCargo(); + + for (int i = 0; i < 10; ++i) { + ++time; + std::cout << "\nDAY: " << time.day() << "\n"; + // std::cout << "crew: " << ship.crew() << '\n'; + // ship.printCargo(); + store->printCargo(); + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/CMakeLists.txt new file mode 100644 index 0000000..482b48c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(Core) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/CMakeLists.txt new file mode 100644 index 0000000..5db9b9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(Core src/Time.cpp) + +target_include_directories(Core PUBLIC include) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..8523b82 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Ship src/Ship.cpp src/Cargo.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..3faa7e4 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +struct Cargo { + size_t amount; + + explicit Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..3badb50 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/include/Ship/Ship.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include + +#include +#include "Ship/Cargo.h" + +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(Time* time, const std::string& name, int capacity, int crew); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const Cargo& cargo, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const Cargo& cargo); + +private: + bool consume(const std::string& cargoName); + void rebel(); + + Time* _time; + std::string _name; + int _capacity; + int _crew; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..9c55175 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/src/Cargo.cpp @@ -0,0 +1,31 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..5fa6796 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Ship/src/Ship.cpp @@ -0,0 +1,107 @@ +#include "Ship/Ship.h" + +#include + +#include +#include +#include +#include +#include + +Ship::Ship(Time* time, const std::string& name, int capacity, int crew) + : _time(time), _name(name), _capacity(capacity), _crew(crew) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + bool shouldRebel = !consume("Rum"); + shouldRebel |= !consume("Banana"); + if (shouldRebel) { + rebel(); + } +} + +void Ship::printCargo() const { + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const Cargo& cargo, StatusCode& code) noexcept { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item) { return *item == cargo; }); it != _cargoes.end()) { + _cargoes.erase(it); + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const Cargo& cargo) { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item) { return *item == cargo; }); it != _cargoes.end()) { + _cargoes.erase(it); + throw std::runtime_error("Missing cargo"); + } +} + +bool Ship::consume(const std::string& cargoName) { + int left = _crew; + + while (left != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > left) { + (*it)->amount -= left; + return true; + } + left -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/include/Store/Store.h new file mode 100644 index 0000000..1eb93cb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/include/Store/Store.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/src/Store.cpp new file mode 100644 index 0000000..c5e0f60 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/src/Store/src/Store.cpp @@ -0,0 +1,47 @@ +#include "Store/Store.h" + +#include +#include + +Store::Store(std::vector>&& cargos) + : _seed(_rd()), _cargoes(std::move(cargos)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +// Shame on me, I copy-past it. But remember DRY!! +// This is only to speed up process of creating exercises, sorry :) +void Store::printCargo() const { + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer2.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer2.cpp new file mode 100644 index 0000000..8bb9990 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer2.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include + +template +struct Observer { + virtual ~Observer() = default; + virtual void update(State state) = 0; +}; + +class Browser { +public: + enum class State { + Closing, + FocusChanged + }; + using BrowserObserver = Observer; + + ~Browser() { + std::cout << "Browser is closing\n"; + notify(State::Closing); + std::cout << "Browser closed\n"; + } + // Rule of 5 :) + + void attach(BrowserObserver* observer) { _observers.push_back(observer); } + void detach(BrowserObserver* observer) { std::erase(_observers, observer); } + +private: + void notify(State state) const { + std::ranges::for_each(_observers, [state](auto* observer) { + observer->update(state); + }); + } + + std::vector _observers; +}; + +class TabStripManager : public Browser::BrowserObserver { +public: + TabStripManager(Browser* browser) + : _browser(browser) { + assert(browser); + browser->attach(this); + } + + ~TabStripManager() { + if (_browser) { + _browser->detach(this); + } + } + // Rule of 5 :) + + // dispatch message + void update(Browser::State state) override { + switch (state) { + case Browser::State::Closing: { + browserAboutToQuit(); + return; + } + case Browser::State::FocusChanged: { + browserFocusChanged(); + return; + } + } + } + +private: + void browserAboutToQuit() { + std::cout << "TabStripManager will close tabs\n"; + _browser = nullptr; + } + + void browserFocusChanged() { + std::cout << "TabStripManager lost focus\n"; + } + + Browser* _browser; +}; + +int main() { + std::unique_ptr browser = std::make_unique(); + TabStripManager manager(browser.get()); + browser = nullptr; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer3.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer3.cpp new file mode 100644 index 0000000..2abbd8b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/observer/observer3.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include +#include + +template +struct Observer { + // First, we don't need to use polymorphism, so also virtual D'tor is redundant + using OnUpdate = std::function; + + explicit Observer(OnUpdate fun) + : _onUpdate(std::move(fun)) {} + void update(const Subject& subject, State state) { + std::invoke(_onUpdate, subject, state); + } + +private: + OnUpdate _onUpdate; +}; + +class Browser; + +enum class BrowserState { + Closing, + FocusChanged +}; + +using BrowserObserver = Observer; + +class Browser { +public: + void attach(BrowserObserver* observer) { _observers.emplace(observer); } + void detach(BrowserObserver* observer) { _observers.erase(observer); } + void setFocus(bool focus) { + if (std::exchange(_hasFocus, focus) != focus) { + notify(BrowserState::FocusChanged); + } + } + bool hasFocus() const { return _hasFocus; } + +private: + void notify(BrowserState state) const { + std::ranges::for_each(_observers, [state, this](auto* observer) { + observer->update(*this, state); + }); + } + + bool _hasFocus{false}; + std::set _observers; +}; + +class Extension { +public: + explicit Extension(const std::string& name) + : _name(name) {} + // D'tor is not needed neither the rule of 5 + + // dispatch message + void update(const Browser& browser, BrowserState state) { + switch (state) { + case BrowserState::Closing: + return; + case BrowserState::FocusChanged: { + if (browser.hasFocus()) { + displayExtension(); + } + return; + } + } + } + +private: + void displayExtension() { + std::cout << "This is an extension: " << _name << '\n'; + } + + std::string _name; +}; + +int main() { + std::unique_ptr browser = std::make_unique(); + Extension extension1("A"); + Extension extension2("B"); + Extension extension3("C"); + BrowserObserver observer1([&extension1](const Browser& browser, BrowserState state) { + extension1.update(browser, state); + }); + BrowserObserver observer2([&extension2](const Browser& browser, BrowserState state) { + extension2.update(browser, state); + }); + BrowserObserver observer3([&extension3](const Browser& browser, BrowserState state) { + extension3.update(browser, state); + }); + browser->attach(&observer1); + browser->attach(&observer2); + browser->attach(&observer3); + browser->setFocus(true); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/CMakeLists.txt new file mode 100644 index 0000000..9d78935 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(Refactoring) + +set(SRC_LIST + refactoring.cpp +) + +add_executable(${PROJECT_NAME} ${SRC_LIST}) +target_compile_options(${PROJECT_NAME} PUBLIC -Wall -Werror -Wpedantic -Wextra) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/README.md b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/README.md new file mode 100644 index 0000000..7b9454c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/README.md @@ -0,0 +1,10 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise + +* Try to refactore code \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/refactoring.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/refactoring.cpp new file mode 100644 index 0000000..354c36c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/refactoring.cpp @@ -0,0 +1,216 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Credentail { + int cert_; +}; + +struct Download { + enum ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + bool cache_; + ConnectionType type_; +}; + +struct Upload { + enum ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + int size_; + ConnectionType type_; +}; + +struct RemoveFromCache { + std::string url_; +}; + +struct ClearCache { +}; + +struct Image { + std::vector bitmap_; +}; + +class Server { +public: + void handle(const Download& request, void (*callback)(bool, Image)) { + Image image; + + for (const auto& pair : cache_) { + if (pair.second == request.url_) { + image = pair.first; + callback(true, image); + return; + } + } + + if (download(request, &image)) { + if (request.cache_) { + cache_.push_back(std::pair(image, request.url_)); + } + + callback(true, image); + return; + } + + callback(false, Image{}); + } + + void handle(const Upload& request, void (*callback)(bool, Image)) { + if (request.size_ > 100) { + callback(false, Image{}); + return; + } + + Image image; + bool succes = upload(request, &image); + + callback(succes, Image{}); + } + + void handle(const RemoveFromCache& request, void (*callback)(bool, Image)) { + for (auto it = cache_.begin(); it != cache_.end(); ++it) { + if (it->second == request.url_) { + cache_.erase(it); + callback(true, Image{}); + return; + } + } + + callback(false, Image{}); + } + + void handle(const ClearCache& request, void (*callback)(bool, Image)) { + cache_.clear(); + + callback(true, Image{}); + } + +private: + bool download(const Download& request, Image* image) { + if (request.type_ == Download::Ssh && request.credentail_.cert_ != 123) { + return false; + } + if (request.type_ == Download::Telnet && request.credentail_.cert_ != 231) { + return false; + } + + // Simulate some other error + if (request.maxMbps_ % 2) { + return false; + } + image->bitmap_ = {97, 98, 99, 100, 101, 102}; + return true; + } + + bool upload(const Upload& request, const Image* image) { + if (request.type_ == Upload::Ssh && request.credentail_.cert_ != 1234) { + return false; + } + if (request.type_ == Upload::Telnet && request.credentail_.cert_ != 5432) { + return false; + } + + // Simulate some other error + if (!request.maxMbps_ % 2) { + return false; + } + return true; + } + + std::vector> cache_; +}; + +class RequestHandler { +public: + enum RequestType { Download, + Upload, + Remove, + Clear }; + + void start(Server* server) { + std::thread(&RequestHandler::run, this, server).detach(); + } + + void stop() { + stop_ = true; + } + + void pushRequest(void* request, RequestType type, void (*callback)(bool, Image)) { + requests_.push(std::tuple(request, type, callback)); + } + +private: + void run(Server* server) { + while (!stop_) { + auto tuple = waitForRequest(); + switch (std::get<1>(tuple)) { + case Download: + server->handle(*((::Download*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + case Upload: + server->handle(*((::Upload*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + case Remove: + server->handle(*((RemoveFromCache*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + case Clear: + server->handle(*((ClearCache*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + } + } + } + + std::tuple waitForRequest() { + while (requests_.empty() || !stop_) { + } + + auto pair = requests_.front(); + requests_.pop(); + + return pair; + } + + bool stop_; + std::queue> requests_; +}; + +int main() { + Server server; + RequestHandler handler; + + handler.start(&server); + + Download d{"sth.png", 123, 100, true, Download::Ssh}; + handler.pushRequest((void*)(&d), + RequestHandler::Download, + [](bool succes, Image image) { + if (succes) { + for (auto el : image.bitmap_) { + std::cout << el << ' '; + } + std::cout << '\n'; + } else { + std::cout << "FAILED!\n"; + } + }); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + handler.stop(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/soultions/refactoring1.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/soultions/refactoring1.cpp new file mode 100644 index 0000000..6ce7d45 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/soultions/refactoring1.cpp @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Credentail { + int cert_; +}; + +struct Download { + enum class ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + bool cache_; + ConnectionType type_; +}; + +struct Upload { + enum class ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + int size_; + ConnectionType type_; +}; + +struct RemoveFromCache { + std::string url_; +}; + +struct ClearCache { +}; + +struct Image { + std::vector bitmap_; +}; + +class Server { +public: + enum class ErrorCode { + Ok, + WrongUrl, + CanNotConnect, + WrongCredential, + MaximumSizeExceeded, + MissingImage, + }; + using CallbackType = void (*)(ErrorCode, Image); + + void handle(const Download& request, CallbackType callback) { + if (const auto it = findImage(request.url_); it != std::cend(cache_)) { + callback(ErrorCode::Ok, it->first); + return; + } + + Image img; + const auto ec = download(request, img); + if (ec == ErrorCode::Ok) { + if (request.cache_) { + cache_.emplace_back(img, request.url_); + } + } + + callback(ec, img); + } + + void handle(const Upload& request, CallbackType callback) const { + if (request.size_ > 100) { + callback(ErrorCode::MaximumSizeExceeded, Image{}); + return; + } + + Image img; + callback(upload(request, img), img); + } + + void handle(const RemoveFromCache& request, CallbackType callback) { + if (const auto it = findImage(request.url_); it != std::cend(cache_)) { + cache_.erase(it); + callback(ErrorCode::Ok, Image{}); + return; + } + + callback(ErrorCode::MissingImage, Image{}); + } + + void handle(const ClearCache& request, CallbackType callback) { + cache_.clear(); + callback(ErrorCode::Ok, Image{}); + } + +private: + std::vector>::const_iterator findImage(const std::string& url) const { + return std::find_if(cbegin(cache_), cend(cache_), + [url](const auto& pair) { + const auto& [image, this_url] = pair; + return this_url == url; + }); + } + + ErrorCode download(const Download& request, Image& image) const { + if (request.type_ == Download::ConnectionType::Ssh && request.credentail_.cert_ != 123) { + return ErrorCode::WrongCredential; + } + if (request.type_ == Download::ConnectionType::Telnet && request.credentail_.cert_ != 231) { + return ErrorCode::CanNotConnect; + } + + // Simulate some other error + if (request.maxMbps_ % 2) { + return ErrorCode::WrongUrl; + } + image.bitmap_ = {97, 98, 99, 100, 101, 102}; + return ErrorCode::Ok; + } + + ErrorCode upload(const Upload& request, const Image& image) const { + if (request.type_ == Upload::ConnectionType::Ssh && request.credentail_.cert_ != 1234) { + return ErrorCode::WrongCredential; + } + if (request.type_ == Upload::ConnectionType::Telnet && request.credentail_.cert_ != 5432) { + return ErrorCode::CanNotConnect; + } + + // Simulate some other error + if (!request.maxMbps_ % 2) { + return ErrorCode::WrongUrl; + } + return ErrorCode::Ok; + } + + std::vector> cache_; +}; + +class RequestHandler { +public: + using RequestType = std::variant; + + void start(Server* server) { + std::thread(&RequestHandler::run, this, server).detach(); + } + + void stop() { + stop_ = true; + cv_.notify_one(); + } + + void pushRequest(const RequestType& request, Server::CallbackType callback) { + { + std::lock_guard lock(m_); + requests_.emplace(request, callback); + } + cv_.notify_one(); + } + +private: + using QueueType = std::pair; + + void run(Server* server) { + while (!stop_) { + const auto res = waitForRequest(); + if (!res) { + return; + } + + const auto [request, callback] = *res; + switch (request.index()) { + case 0: + server->handle(std::get(request), callback); + break; + case 1: + server->handle(std::get(request), callback); + break; + case 2: + server->handle(std::get(request), callback); + break; + case 3: + server->handle(std::get(request), callback); + break; + } + } + } + + std::optional waitForRequest() { + std::unique_lock lk(m_); + cv_.wait(lk, [&]() { return !requests_.empty() || stop_; }); + if (stop_) { + return std::nullopt; + } + auto pair = requests_.front(); + requests_.pop(); + + return pair; + } + + std::mutex m_; + std::condition_variable cv_; + std::atomic stop_{false}; + std::queue requests_; +}; + +int main() { + Server server; + RequestHandler handler; + + handler.start(&server); + + handler.pushRequest(Download{"sth.png", 123, 100, true, Download::ConnectionType::Ssh}, + [](Server::ErrorCode ec, Image image) { + if (ec == Server::ErrorCode::Ok) { + for (auto el : image.bitmap_) { + std::cout << el << ' '; + } + std::cout << '\n'; + } else { + std::cout << "FAILED!\n"; + } + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + handler.stop(); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/soultions/refactoring2.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/soultions/refactoring2.cpp new file mode 100644 index 0000000..62017d6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/soultions/refactoring2.cpp @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Image { + std::vector bitmap_; +}; + +struct Credentail { + int cert_; +}; + +struct DownloadRequest { + enum class ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + bool cache_; + ConnectionType type_; +}; + +struct UploadRequest { + enum class ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + int size_; + ConnectionType type_; +}; + +class Command { +public: + enum class ErrorCode { + Ok, + WrongUrl, + CanNotConnect, + WrongCredential, + MaximumSizeExceeded, + MissingImage, + }; + + class Delegate { + public: + virtual ~Delegate() = default; + virtual ErrorCode removeFromCache(const std::string& url) = 0; + virtual void clearCache() = 0; + virtual void addToCache(const std::vector& data, const std::string& url) = 0; + virtual ErrorCode download(std::vector& data, const DownloadRequest& request) const = 0; + virtual ErrorCode upload(const std::vector& data, const UploadRequest& request) const = 0; + }; + + explicit Command(Delegate* delegate) + : delegate_(delegate) {} + + // Rule of 5! + virtual ~Command() = default; + Command(const Command&) = default; + Command(Command&&) = default; + Command& operator=(const Command&) = default; + Command& operator=(Command&&) = default; + + virtual void operator()() const = 0; + +protected: + Delegate* delegate_; +}; + +class DownloadImageCommand : public Command { +public: + using CallbackType = void (*)(ErrorCode, Image); + + ~DownloadImageCommand() override = default; + + DownloadImageCommand(Delegate* delegate, CallbackType callback, const DownloadRequest& request) + : Command(delegate), callback_(callback), request_(request) {} + + void operator()() const override { + std::vector data; + if (auto ec = delegate_->download(data, request_); ec == Command::ErrorCode::Ok) { + // Do some conversion on vector + if (request_.cache_) { + delegate_->addToCache(data, request_.url_); + } + Image image{data}; + callback_(ec, std::move(image)); + } else { + callback_(ec, Image{}); + } + }; + +private: + CallbackType callback_; + DownloadRequest request_; +}; + +class UploadImageCommand : public Command { +public: + using CallbackType = void (*)(ErrorCode); + + ~UploadImageCommand() override = default; + + UploadImageCommand(Delegate* delegate, const Image& image, CallbackType callback, const UploadRequest& request) + : Command(delegate), image_(image), callback_(callback), request_(request) {} + + void operator()() const override { + callback_(delegate_->upload(image_.bitmap_, request_)); + }; + +private: + Image image_; + CallbackType callback_; + UploadRequest request_; +}; + +class Server : public Command::Delegate { +public: + ~Server() override = default; + + Command::ErrorCode removeFromCache(const std::string& url) override { + if (const auto it = findFile(url); it != std::cend(cache_)) { + cache_.erase(it); + return Command::ErrorCode::Ok; + } + + return Command::ErrorCode::MissingImage; + } + + void clearCache() override { + cache_.clear(); + } + + void addToCache(const std::vector& data, const std::string& url) override { + cache_.emplace_back(data, url); + } + + Command::ErrorCode download(std::vector& data, const DownloadRequest& request) const override { + if (request.type_ == DownloadRequest::ConnectionType::Ssh && request.credentail_.cert_ != 123) { + return Command::ErrorCode::WrongCredential; + } + if (request.type_ == DownloadRequest::ConnectionType::Telnet && request.credentail_.cert_ != 231) { + return Command::ErrorCode::CanNotConnect; + } + + // Simulate some other error + if (request.maxMbps_ % 2) { + return Command::ErrorCode::WrongUrl; + } + + data = {97, 98, 99, 100, 101, 102}; + return Command::ErrorCode::Ok; + } + + Command::ErrorCode upload(const std::vector& data, const UploadRequest& request) const override { + if (request.type_ == UploadRequest::ConnectionType::Ssh && request.credentail_.cert_ != 1234) { + return Command::ErrorCode::WrongCredential; + } + if (request.type_ == UploadRequest::ConnectionType::Telnet && request.credentail_.cert_ != 5432) { + return Command::ErrorCode::CanNotConnect; + } + + // Simulate some other error + if (!request.maxMbps_ % 2) { + return Command::ErrorCode::WrongUrl; + } + return Command::ErrorCode::Ok; + } + +private: + std::vector, std::string>>::const_iterator findFile(const std::string& url) const { + return std::find_if(cbegin(cache_), cend(cache_), + [url](const auto& pair) { + const auto& [data, this_url] = pair; + return this_url == url; + }); + } + + std::vector, std::string>> cache_; +}; + +class RequestHandler { +public: + void start() { + std::thread(&RequestHandler::run, this).detach(); + } + + void stop() { + stop_ = true; + cv_.notify_one(); + } + + void pushRequest(std::unique_ptr command) { + { + std::lock_guard lock(m_); + requests_.push(std::move(command)); + } + cv_.notify_one(); + } + +private: + void run() { + while (!stop_) { + const auto request = waitForRequest(); + if (!request || !*request) { + return; + } + + (**request)(); + } + } + + std::optional> waitForRequest() { + std::unique_lock lk(m_); + cv_.wait(lk, [&]() { return !requests_.empty() || stop_; }); + if (stop_) { + return std::nullopt; + } + + std::unique_ptr request = std::move(requests_.front()); + requests_.pop(); + + return std::move(request); + } + + std::mutex m_; + std::condition_variable cv_; + std::atomic stop_{false}; + std::queue> requests_; +}; + +int main() { + Server server; + RequestHandler handler; + + handler.start(); + handler.pushRequest(std::make_unique( + &server, + [](Command::ErrorCode ec, Image img) { + if (ec == Command::ErrorCode::Ok) { + std::copy(cbegin(img.bitmap_), cend(img.bitmap_), std::ostream_iterator(std::cout, " ")); + std::cout << '\n'; + } else { + std::cout << "Sth went wrong!\n"; + } + }, + DownloadRequest{"Sth123", Credentail{123}, 200, true, DownloadRequest::ConnectionType::Ssh})); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + handler.stop(); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy.cpp new file mode 100644 index 0000000..16be8a6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include + +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void fire() = 0; + virtual void draw() = 0; + virtual int getDamage() const = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class FireStrategy { +public: + virtual void fire(const Ammunition& ammunition) = 0; +}; + +class SDLFireStrategy : public FireStrategy { +public: + void fire(const Ammunition& ammunition) override { + std::cout << "SDL Deal: " << ammunition.getDamage() << " damage!" << '\n'; + } +}; + +class WAMFireStrategy : public FireStrategy { +public: + void fire(const Ammunition& ammunition) override { + std::cout << "WAM Deal: " << ammunition.getDamage() << " damage!" << '\n'; + } +}; + +class DrawStrategy { +public: + virtual void draw(const Ammunition& ammunition) = 0; +}; + +class OpenGLDrawStrategy : public DrawStrategy { +public: + void draw(const Ammunition& ammunition) override { + std::cout << "OpenGL Ammunition: " << ammunition.amount() << '\n'; + } +}; + +class MetalDrawStrategy : public DrawStrategy { +public: + void draw(const Ammunition& ammunition) override { + std::cout << "Metal Ammunition: " << ammunition.amount() << '\n'; + } +}; + +class RoundShot : public Ammunition { +public: + RoundShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 10; } + +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; + +class ChainShot : public Ammunition { +public: + ChainShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 15; } + +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; + +class GrapeShot : public Ammunition { +public: + GrapeShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 20; } + +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; + +int main() { + std::unique_ptr grapeShot = std::make_unique( + 100, std::make_unique(), std::make_unique()); + + grapeShot->fire(); + grapeShot->draw(); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/CMakeLists.txt new file mode 100644 index 0000000..4b33b70 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Ship + Store + Core + Player +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/main.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/main.cpp new file mode 100644 index 0000000..247c2fb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/main.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Ship/Cargo.h" +#include "Ship/Ship.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Store/Store.h" + +int main() { + Time time; + + Ship ship(&time, std::make_unique(), "Black Widow", 1000, 40); + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.load(std::make_unique(150)); + ship.printCargo(); + + for (int i = 0; i < 10; ++i) { + ++time; + std::cout << "\nDAY: " << time.day() << "\n"; + std::cout << "crew: " << ship.crew() << '\n'; + ship.printCargo(); + } + + std::cout << "\n\n****************************************************\n"; + auto ship2 = std::make_unique(&time, std::make_unique(), "Queens Anne revenge", 1000, 40); + Enemy enemy("Enemy1", std::move(ship2), std::make_unique()); + + for (int i = 0; i < 15; ++i) { + enemy.attack(ship); + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/CMakeLists.txt new file mode 100644 index 0000000..c29ff6c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/CMakeLists.txt new file mode 100644 index 0000000..5db9b9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(Core src/Time.cpp) + +target_include_directories(Core PUBLIC include) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/CMakeLists.txt new file mode 100644 index 0000000..f96d5d4 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Player src/Enemy.cpp src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..840bc42 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/Enemy.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include +#include + +class Ship; + +class Enemy { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy); + + void attack(Ship& playerShip); + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/Enemy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/Enemy.cpp new file mode 100644 index 0000000..7cbbe29 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/Enemy.cpp @@ -0,0 +1,19 @@ +#include "Player/Enemy.h" + +#include + +#include +#include +#include + +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)) {} + +void Enemy::attack(Ship& playerShip) { + std::cout << "Player: " << _name << " attack ship: " << playerShip.name() << '\n'; + if (const auto damage = _strategy->handle(playerShip)) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..6d03561 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..7ed1893 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..e92d9c9 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/Ship.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + +private: + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..9c55175 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/Cargo.cpp @@ -0,0 +1,31 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..2e60865 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/Ship.cpp @@ -0,0 +1,108 @@ +#include "Ship/Ship.h" + +#include + +#include +#include +#include +#include +#include + +Ship::Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew) + : _time(time), _strategy(std::move(strategy)), _name(name), _capacity(capacity), _crew(crew) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _durability -= damage; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > amount) { + (*it)->amount -= amount; + return true; + } + amount -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/include/Store/Store.h new file mode 100644 index 0000000..1eb93cb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/include/Store/Store.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/src/Store.cpp new file mode 100644 index 0000000..41d32fe --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/src/Store/src/Store.cpp @@ -0,0 +1,48 @@ +#include "Store/Store.h" + +#include +#include + +Store::Store(std::vector>&& cargos) + : _seed(_rd()), _cargoes(std::move(cargos)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +// Shame on me, I copy-past it. But remember DRY!! +// This is only to speed up process of creating exercises, sorry :) +void Store::printCargo() const { + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy2.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy2.cpp new file mode 100644 index 0000000..6c46ef6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy2.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include +#include +#include +#include + +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void fire() = 0; + virtual void draw() = 0; + virtual int getDamage() const = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class ChainShot; +class RoundShot; +class GrapeShot; + +class FireStrategy { +public: + virtual void fire(const ChainShot& shot) = 0; + virtual void fire(const RoundShot& shot) = 0; + virtual void fire(const GrapeShot& shot) = 0; +}; + +class SDLFireStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override; + void fire(const RoundShot& shot) override; + void fire(const GrapeShot& shot) override; +}; + +class WAMFireStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override; + void fire(const RoundShot& shot) override; + void fire(const GrapeShot& shot) override; +}; + +class DrawStrategy { +public: + virtual void draw(const ChainShot& shot) = 0; + virtual void draw(const RoundShot& shot) = 0; + virtual void draw(const GrapeShot& shot) = 0; +}; + +class OpenGLDrawStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override; + void draw(const RoundShot& shot) override; + void draw(const GrapeShot& shot) override; +}; + +class MetalDrawStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override; + void draw(const RoundShot& shot) override; + void draw(const GrapeShot& shot) override; +}; + +class RoundShot : public Ammunition { +public: + RoundShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 10; } + +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; + +class ChainShot : public Ammunition { +public: + ChainShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 15; } + +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; + +class GrapeShot : public Ammunition { +public: + GrapeShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 20; } + +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; + +void SDLFireStrategy::fire(const ChainShot& shot) { + std::cout << "SDL Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void SDLFireStrategy::fire(const RoundShot& shot) { + std::cout << "SDL Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void SDLFireStrategy::fire(const GrapeShot& shot) { + std::cout << "SDL Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} + +void WAMFireStrategy::fire(const ChainShot& shot) { + std::cout << "WAM Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void WAMFireStrategy::fire(const RoundShot& shot) { + std::cout << "WAM Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void WAMFireStrategy::fire(const GrapeShot& shot) { + std::cout << "WAM Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} + +void OpenGLDrawStrategy::draw(const ChainShot& shot) { + std::cout << "OpenGL Chain Shot: " << shot.amount() << '\n'; +} +void OpenGLDrawStrategy::draw(const RoundShot& shot) { + std::cout << "OpenGL Round Shot: " << shot.amount() << '\n'; +} +void OpenGLDrawStrategy::draw(const GrapeShot& shot) { + std::cout << "OpenGL Grape Shot: " << shot.amount() << '\n'; +} + +void MetalDrawStrategy::draw(const ChainShot& shot) { + std::cout << "Metal Chain Shot: " << shot.amount() << '\n'; +} +void MetalDrawStrategy::draw(const RoundShot& shot) { + std::cout << "Metal Round Shot: " << shot.amount() << '\n'; +} +void MetalDrawStrategy::draw(const GrapeShot& shot) { + std::cout << "Metal Grape Shot: " << shot.amount() << '\n'; +} + +int main() { + std::unique_ptr grapeShot = std::make_unique( + 100, std::make_unique(), std::make_unique()); + + grapeShot->fire(); + grapeShot->draw(); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy3.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy3.cpp new file mode 100644 index 0000000..8160319 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/strategy/strategy3.cpp @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include +#include + +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void fire() = 0; + virtual void draw() = 0; + virtual int getDamage() const = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class ChainShot; +class RoundShot; +class GrapeShot; + +template +class FireStrategy { +public: + virtual void fire(const T& shot) = 0; +}; + +class SDLFireRoundShotStrategy : public FireStrategy { +public: + void fire(const RoundShot& shot) override; +}; + +class SDLFireChainShotStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override; +}; + +class SDLFireGrapeShotStrategy : public FireStrategy { +public: + void fire(const GrapeShot& shot) override; +}; + +class WAMFireRoundShotStrategy : public FireStrategy { +public: + void fire(const RoundShot& shot) override; +}; + +class WAMFirChainShotStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override; +}; + +class WAMFireGrapeShotStrategy : public FireStrategy { +public: + void fire(const GrapeShot& shot) override; +}; + +template +class DrawStrategy { +public: + virtual void draw(const T& shot) = 0; +}; + +class OpenGLDrawRoundShotStrategy : public DrawStrategy { +public: + void draw(const RoundShot& shot) override; +}; + +class OpenGLDrawChainShotStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override; +}; + +class OpenGLDrawGrapeShotStrategy : public DrawStrategy { +public: + void draw(const GrapeShot& shot) override; +}; + +class MetalDrawRoundShotStrategy : public DrawStrategy { +public: + void draw(const RoundShot& shot) override; +}; + +class MetalDrawChainShotStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override; +}; + +class MetalDrawGrapeShotStrategy : public DrawStrategy { +public: + void draw(const GrapeShot& shot) override; +}; + +class RoundShot : public Ammunition { +public: + RoundShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 10; } + +private: + std::unique_ptr> _fireStrategy; + std::unique_ptr> _drawStrategy; +}; + +class ChainShot : public Ammunition { +public: + ChainShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 15; } + +private: + std::unique_ptr> _fireStrategy; + std::unique_ptr> _drawStrategy; +}; + +class GrapeShot : public Ammunition { +public: + GrapeShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 20; } + +private: + std::unique_ptr> _fireStrategy; + std::unique_ptr> _drawStrategy; +}; + +void SDLFireChainShotStrategy::fire(const ChainShot& shot) { + std::cout << "SDL Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void SDLFireRoundShotStrategy::fire(const RoundShot& shot) { + std::cout << "SDL Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void SDLFireGrapeShotStrategy::fire(const GrapeShot& shot) { + std::cout << "SDL Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} + +void WAMFirChainShotStrategy::fire(const ChainShot& shot) { + std::cout << "WAM Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void WAMFireRoundShotStrategy::fire(const RoundShot& shot) { + std::cout << "WAM Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} +void WAMFireGrapeShotStrategy::fire(const GrapeShot& shot) { + std::cout << "WAM Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; +} + +void OpenGLDrawChainShotStrategy::draw(const ChainShot& shot) { + std::cout << "OpenGL Chain Shot: " << shot.amount() << '\n'; +} +void OpenGLDrawRoundShotStrategy::draw(const RoundShot& shot) { + std::cout << "OpenGL Round Shot: " << shot.amount() << '\n'; +} +void OpenGLDrawGrapeShotStrategy::draw(const GrapeShot& shot) { + std::cout << "OpenGL Grape Shot: " << shot.amount() << '\n'; +} + +void MetalDrawChainShotStrategy::draw(const ChainShot& shot) { + std::cout << "Metal Chain Shot: " << shot.amount() << '\n'; +} +void MetalDrawRoundShotStrategy::draw(const RoundShot& shot) { + std::cout << "Metal Round Shot: " << shot.amount() << '\n'; +} +void MetalDrawGrapeShotStrategy::draw(const GrapeShot& shot) { + std::cout << "Metal Grape Shot: " << shot.amount() << '\n'; +} + +int main() { + std::unique_ptr grapeShot = std::make_unique( + 100, std::make_unique(), std::make_unique()); + + grapeShot->fire(); + grapeShot->draw(); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/CMakeLists.txt new file mode 100644 index 0000000..9d2ce9e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Battle + Ship + Store + Core + Player +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/main.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/main.cpp new file mode 100644 index 0000000..01bfde8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/main.cpp @@ -0,0 +1,72 @@ +#include +#include +#include + +#include "Battle/BattleField.h" +#include "Core/BasicPrintVisitor.h" +#include "Core/PrettyPrintVisitor.h" +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Player/RealPlayer.h" +#include "Ship/Cargo.h" +#include "Ship/CargoDamageVisitor.h" +#include "Ship/Ship.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Store/Store.h" + +int main() { + Time time; + std::vector> cargoes; + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + auto store = std::unique_ptr>( + [&cargoes, &time]() { + auto* store = new Store(std::move(cargoes), std::make_unique()); + try { + time.attach(store); + } catch (...) { + abort(); + } + return store; }(), + [&time](Store* store) { + time.detach(store); + delete store; + }); + + Ship ship(&time, std::make_unique(), "Black Widow", 1000, 40, std::make_unique()); + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.load(std::make_unique(150)); + + std::cout << "Ship\n"; + ship.printCargo(); + std::cout << "\nStore\n"; + store->printCargo(); + + std::cout << "\n\n****************************************************\n"; + auto ship2 = std::make_unique(&time, std::make_unique(), "Queens Anne revenge", 1000, 40, std::make_unique()); + Enemy enemy("Enemy1", std::move(ship2), std::make_unique(), CargoDamageVisitor{}); + + auto ship3 = std::make_unique(&time, std::make_unique(), "Black Pearl", 1000, 40, std::make_unique()); + RealPlayer user("Mateusz", std::move(ship3)); + + BattleField battefield{&user, &enemy}; + while (true) { + for (auto* player : battefield.players()) { + std::cout << "-------------------------------------------------------------------\n"; + std::cout << "Player HP: " << user.getShip().durability() << " ARMOR: " << user.getShip().armor() << " | "; + std::cout << "Enemy HP: " << enemy.getShip().durability() << " ARMOR: " << enemy.getShip().armor() << '\n'; + std::cout << "-------------------------------------------------------------------\n"; + for (size_t i = 0; i < 3; ++i) { + const auto status = player->makeAction(battefield); + if (status == Action::Status::Escaped || status == Action::Status::Defeated) { + return 0; + } + } + } + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/CMakeLists.txt new file mode 100644 index 0000000..acd8033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Battle src/Defense.cpp src/BattleField.cpp src/Attack.cpp src/Escape.cpp) + +target_include_directories(Battle PUBLIC include) + +target_link_libraries(Battle + Ship + Player +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Action.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Action.h new file mode 100644 index 0000000..f271d40 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Action.h @@ -0,0 +1,24 @@ +#pragma once + +class Player; +class Ship; + +class Action { +public: + // As you can see keeping type here, require modify a base class whenever we add a new class, this volatile an open-close principle. + // We should be able to extend code without modification already implemented one + enum class Type { + Attack, + Defense, + Escape + }; + + enum class Status { + Escaped, + Defeated, + Nothing, + }; + + virtual Status operator()(Player* player, Ship* enemyShip) = 0; + virtual Type type() const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Attack.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Attack.h new file mode 100644 index 0000000..a06cba0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Attack.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Attack : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Attack; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/BattleField.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/BattleField.h new file mode 100644 index 0000000..6196660 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/BattleField.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Ship; +class Player; + +class BattleField { +public: + BattleField(Player* player, Player* enemy); + + Ship* getPlayerShip() const; + Ship* getEnemyShip() const; + std::vector players() const; + +private: + Player* _player; + Player* _enemy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Defense.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Defense.h new file mode 100644 index 0000000..fefd271 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Defense.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Defense : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Type::Defense; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Escape.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Escape.h new file mode 100644 index 0000000..97dc54d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/include/Battle/Escape.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Battle/Action.h" + +#include + +class Player; +class Ship; + +class Escape : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Escape; } + +private: + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Attack.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Attack.cpp new file mode 100644 index 0000000..b3f5e8a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Attack.cpp @@ -0,0 +1,22 @@ +#include "Battle/Attack.h" + +#include +#include + +#include + +Action::Status Attack::operator()(Player* player, Ship* enemyShip) { + const int damage = player->attack(*enemyShip); + std::cout << "Player ship: " << player->getShip().name() << " attack ship: " << enemyShip->name() << '\n'; + if (damage) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } + + if (enemyShip->durability() <= 0) { + return Status::Defeated; + } + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/BattleField.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/BattleField.cpp new file mode 100644 index 0000000..4e10569 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/BattleField.cpp @@ -0,0 +1,16 @@ +#include "Battle/BattleField.h" + +#include + +BattleField::BattleField(Player* player, Player* enemy) + : _player{player}, _enemy{enemy} {} + +Ship* BattleField::getPlayerShip() const { + return &_player->getShip(); +} +Ship* BattleField::getEnemyShip() const { + return &_enemy->getShip(); +} +std::vector BattleField::players() const { + return {_player, _enemy}; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Defense.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Defense.cpp new file mode 100644 index 0000000..a170c81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Defense.cpp @@ -0,0 +1,13 @@ +#include "Battle/Defense.h" + +#include +#include + +#include + +Action::Status Defense::operator()(Player* player, Ship*) { + player->getShip().increaseArmor(50); + std::cout << "Player ship: " << player->getShip().name() << " defense\n"; + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Escape.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Escape.cpp new file mode 100644 index 0000000..5b222fa --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Battle/src/Escape.cpp @@ -0,0 +1,22 @@ +#include "Battle/Escape.h" + +#include +#include + +#include + +std::random_device Escape::_rd{}; + +Action::Status Escape::operator()(Player* player, Ship*) { + std::cout << "Player ship: " << player->getShip().name() << " try to escape!\n"; + + std::uniform_int_distribution dice(0, 20); + const auto res = dice(_seed); + if (res > 12) { + std::cout << "Player escaped!\n"; + return Status::Escaped; + } + + std::cout << "Escape maneuver failed!\n"; + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/CMakeLists.txt new file mode 100644 index 0000000..9434ff8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(Battle) +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/CMakeLists.txt new file mode 100644 index 0000000..db39d82 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Core src/Time.cpp src/BasicPrintVisitor.cpp src/PrettyPrintVisitor.cpp) + +target_include_directories(Core PUBLIC include) + +target_link_libraries(Core + Ship + Store +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/BasicPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/BasicPrintVisitor.h new file mode 100644 index 0000000..cd3406f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/BasicPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class BasicPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/PrettyPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/PrettyPrintVisitor.h new file mode 100644 index 0000000..58dc6ad --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/PrettyPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class PrettyPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/PrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/PrintVisitor.h new file mode 100644 index 0000000..bec94c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/PrintVisitor.h @@ -0,0 +1,11 @@ +#pragma once + +class Store; +class Ship; + +class PrintVisitor { +public: + virtual ~PrintVisitor() = default; + virtual void visit(const Store&) const = 0; + virtual void visit(const Ship&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/BasicPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/BasicPrintVisitor.cpp new file mode 100644 index 0000000..05960c1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/BasicPrintVisitor.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include +#include +#include + +void BasicPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << std::setw(15) << cargo->getPrice() << "\n"; + } +} + +void BasicPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << "\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/PrettyPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/PrettyPrintVisitor.cpp new file mode 100644 index 0000000..99b6738 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/PrettyPrintVisitor.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +void PrettyPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} + +void PrettyPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/CMakeLists.txt new file mode 100644 index 0000000..b266b3f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(Player src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp src/Player.cpp src/RealPlayer.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Battle + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..e44bd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/Enemy.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include +#include +#include + +#include +#include + +template +class Enemy : public Player { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; + DamageVisitor _damageVisitor; +}; + +template +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)), _damageVisitor(visitor) {} + +template +int Enemy::attack(Ship& playerShip) { + if (const auto damage = _strategy->handle(playerShip)) { + for (auto& cargo : playerShip.cargoes()) { + cargo->accept(_damageVisitor); + } + return damage; + } + + return 0; +} + +template +Ship* Enemy::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getPlayerShip(); +} + +template +std::unique_ptr Enemy::chooseAction(const BattleField& battleField) const { + static bool flag = true; + if (flag) { + flag = !flag; + return std::make_unique(); + } + + flag = !flag; + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/Player.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/Player.h new file mode 100644 index 0000000..7717389 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/Player.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +class BattleField; +class Ship; + +class Player { +public: + virtual ~Player() = default; + virtual int attack(Ship& playerShip) = 0; + virtual Ship& getShip() = 0; + virtual const Ship& getShip() const = 0; + + Action::Status makeAction(const BattleField& battleField); + +protected: + virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0; + virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/RealPlayer.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/RealPlayer.h new file mode 100644 index 0000000..04d8b1a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/include/Player/RealPlayer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include + +class RealPlayer : public Player { +public: + RealPlayer(const std::string& name, std::unique_ptr&& ship); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/Player.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/Player.cpp new file mode 100644 index 0000000..5e1f83b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/Player.cpp @@ -0,0 +1,11 @@ +#include "Player/Player.h" + +Action::Status Player::makeAction(const BattleField& battleField) { + std::unique_ptr action = chooseAction(battleField); + if (action->type() == Action::Type::Attack) { + Ship* enemyShip = chooseEnemyShip(battleField); + return (*action)(this, enemyShip); + } + + return (*action)(this, nullptr); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/RealPlayer.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/RealPlayer.cpp new file mode 100644 index 0000000..b5cd4de --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Player/src/RealPlayer.cpp @@ -0,0 +1,56 @@ +#include "Player/RealPlayer.h" + +#include +#include +#include +#include +#include + +#include + +std::random_device RealPlayer::_rd{}; + +RealPlayer::RealPlayer(const std::string& name, std::unique_ptr&& ship) + : _name(name), _ship(std::move(ship)) {} + +int RealPlayer::attack(Ship& playerShip) { + std::uniform_int_distribution dice(0, 20); + const int res = dice(_seed); + int input = 0; + int damage = 0; + std::cout << "Choose Ammo: 1) Round Shot 2) Chain Shot 3) Grape Shot: "; + std::cin >> input; + + if (input == 1 && res > 4) { + damage = 50; + } else if (input == 2 && res > 7) { + damage = 80; + } else if (input == 3 && res > 10) { + damage = 100; + } else { + return 0; + } + + playerShip.takeDamage(damage); + return damage; +} + +Ship* RealPlayer::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getEnemyShip(); +} + +std::unique_ptr RealPlayer::chooseAction(const BattleField& battleField) const { + int input = 0; + std::cout << "Choose Action: 1) Attack 2) Defense 3) Escape: "; + std::cin >> input; + + if (input == 1) { + return std::make_unique(); + } + if (input == 3) { + return std::make_unique(); + } + + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..d2f40e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp + src/CargoDamageVisitor.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..3771b7a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "Ship/CargoDamageVisitor.h" + +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + virtual void accept(const CargoDamageVisitor& visitor) = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/CargoDamageVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/CargoDamageVisitor.h new file mode 100644 index 0000000..6e84c58 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/CargoDamageVisitor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Fruit; +class Alcohol; +class Item; + +class CargoDamageVisitor { +public: + CargoDamageVisitor() = default; + virtual ~CargoDamageVisitor() = default; + CargoDamageVisitor(const CargoDamageVisitor&) = default; + CargoDamageVisitor(CargoDamageVisitor&&) = default; + CargoDamageVisitor& operator=(const CargoDamageVisitor&) = default; + CargoDamageVisitor& operator=(CargoDamageVisitor&&) = default; + + virtual void operator()(Fruit& fruit) const; + virtual void operator()(Alcohol& alcohol) const; + virtual void operator()(Item& item) const; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..186d0bb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/Ship.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class PrintVisitor; +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew, std::unique_ptr&& visitor); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + const std::vector>& cargoes() const; + + void increaseArmor(int armor) { _armor += armor; } + int durability() const { return _durability; } + int armor() const { return _armor; } + +private: + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + int _armor{0}; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..31d75b7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/Cargo.cpp @@ -0,0 +1,43 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +void Fruit::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +void Item::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} + +void Alcohol::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/CargoDamageVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/CargoDamageVisitor.cpp new file mode 100644 index 0000000..6ff89e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/CargoDamageVisitor.cpp @@ -0,0 +1,20 @@ +#include + +#include + +std::random_device CargoDamageVisitor::_rd{}; + +void CargoDamageVisitor::operator()(Fruit& fruit) const { + std::uniform_int_distribution dice(0, 5); + fruit.amount -= dice(_seed); +} + +void CargoDamageVisitor::operator()(Alcohol& alcohol) const { + std::uniform_int_distribution dice(0, 10); + alcohol.amount -= (dice(_seed) / 2); +} + +void CargoDamageVisitor::operator()(Item& item) const { + std::uniform_int_distribution dice(0, 20); + item.amount -= (dice(_seed) / 4); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..2529f92 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/Ship.cpp @@ -0,0 +1,111 @@ +#include "Ship/Ship.h" + +#include +#include + +#include +#include +#include +#include + +Ship::Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew, std::unique_ptr&& visitor) + : _time(time), _strategy(std::move(strategy)), _name(name), _capacity(capacity), _crew(crew), _visitor(std::move(visitor)) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + _visitor->visit(*this); +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _armor -= damage; + if (_armor >= 0) { + return; + } + + _durability += _armor; + _armor = 0; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +const std::vector>& Ship::cargoes() const { + return _cargoes; +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > amount) { + (*it)->amount -= amount; + return true; + } + amount -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/include/Store/Store.h new file mode 100644 index 0000000..fbbbf9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/include/Store/Store.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class PrintVisitor; + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos, std::unique_ptr&& visitor); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + const std::vector>& cargoes() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/src/Store.cpp new file mode 100644 index 0000000..07a9e35 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/src/Store/src/Store.cpp @@ -0,0 +1,41 @@ +#include "Store/Store.h" + +#include + +Store::Store(std::vector>&& cargos, std::unique_ptr&& visitor) + : _seed(_rd()), _cargoes(std::move(cargos)), _visitor(std::move(visitor)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +const std::vector>& Store::cargoes() const { + return _cargoes; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +void Store::printCargo() const { + _visitor->visit(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/templateMethod/templateMethod/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor.cpp new file mode 100644 index 0000000..3554151 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor.cpp @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include +#include +#include + +class AmmunitionVisitor; + +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void accept(const AmmunitionVisitor&) = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class RoundShot; +class ChainShot; +class GrapeShot; + +class AmmunitionVisitor { +public: + virtual ~AmmunitionVisitor() = default; + virtual void visit(const RoundShot&) const = 0; + virtual void visit(const ChainShot&) const = 0; + virtual void visit(const GrapeShot&) const = 0; +}; + +class RoundShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AmmunitionVisitor& visitor) { + visitor.visit(*this); + } +}; + +class ChainShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AmmunitionVisitor& visitor) { + visitor.visit(*this); + } +}; + +class GrapeShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AmmunitionVisitor& visitor) { + visitor.visit(*this); + } +}; + +class DamageVisitor : public AmmunitionVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Round shot make 20 damages\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Chain shot make 30 damages\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Grape shot make 40 damages\n"; + } +}; + +class SoundVisitor : public AmmunitionVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Fiuuuuuuuu Bam!\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam!\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n"; + } +}; + +int main() { + std::unique_ptr grapeShot = std::make_unique(100); + SoundVisitor soundVisitor; + DamageVisitor damageVisitor; + grapeShot->accept(soundVisitor); + grapeShot->accept(damageVisitor); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/CMakeLists.txt new file mode 100644 index 0000000..4b33b70 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Ship + Store + Core + Player +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/main.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/main.cpp new file mode 100644 index 0000000..0cf86be --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/main.cpp @@ -0,0 +1,56 @@ +#include +#include +#include + +#include "Core/BasicPrintVisitor.h" +#include "Core/PrettyPrintVisitor.h" +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Ship/Cargo.h" +#include "Ship/CargoDamageVisitor.h" +#include "Ship/Ship.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Store/Store.h" + +int main() { + Time time; + std::vector> cargoes; + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + auto store = std::unique_ptr>( + [&cargoes, &time]() { + auto* store = new Store(std::move(cargoes), std::make_unique()); + try { + time.attach(store); + } catch (...) { + abort(); + } + return store; }(), + [&time](Store* store) { + time.detach(store); + delete store; + }); + + Ship ship(&time, std::make_unique(), "Black Widow", 1000, 40, std::make_unique()); + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.load(std::make_unique(150)); + + std::cout << "Ship\n"; + ship.printCargo(); + std::cout << "\nStore\n"; + store->printCargo(); + + std::cout << "\n\n****************************************************\n"; + auto ship2 = std::make_unique(&time, std::make_unique(), "Queens Anne revenge", 1000, 40, std::make_unique()); + Enemy enemy("Enemy1", std::move(ship2), std::make_unique(), CargoDamageVisitor{}); + + for (int i = 0; i < 5; ++i) { + enemy.attack(ship); + ship.printCargo(); + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/CMakeLists.txt new file mode 100644 index 0000000..c29ff6c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/CMakeLists.txt new file mode 100644 index 0000000..db39d82 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Core src/Time.cpp src/BasicPrintVisitor.cpp src/PrettyPrintVisitor.cpp) + +target_include_directories(Core PUBLIC include) + +target_link_libraries(Core + Ship + Store +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/BasicPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/BasicPrintVisitor.h new file mode 100644 index 0000000..cd3406f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/BasicPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class BasicPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/PrettyPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/PrettyPrintVisitor.h new file mode 100644 index 0000000..58dc6ad --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/PrettyPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class PrettyPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/PrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/PrintVisitor.h new file mode 100644 index 0000000..bec94c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/PrintVisitor.h @@ -0,0 +1,11 @@ +#pragma once + +class Store; +class Ship; + +class PrintVisitor { +public: + virtual ~PrintVisitor() = default; + virtual void visit(const Store&) const = 0; + virtual void visit(const Ship&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/BasicPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/BasicPrintVisitor.cpp new file mode 100644 index 0000000..05960c1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/BasicPrintVisitor.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include +#include +#include + +void BasicPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << std::setw(15) << cargo->getPrice() << "\n"; + } +} + +void BasicPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << "\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/PrettyPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/PrettyPrintVisitor.cpp new file mode 100644 index 0000000..99b6738 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/PrettyPrintVisitor.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +void PrettyPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} + +void PrettyPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/CMakeLists.txt new file mode 100644 index 0000000..4dbb77f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Player src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..59fc1f1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/Enemy.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include + +template +class Enemy { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor); + + void attack(Ship& playerShip); + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; + DamageVisitor _damageVisitor; +}; + +template +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)), _damageVisitor(visitor) {} + +template +void Enemy::attack(Ship& playerShip) { + std::cout << "Player: " << _name << " attack ship: " << playerShip.name() << '\n'; + if (const auto damage = _strategy->handle(playerShip)) { + std::cout << "Dealed damage: " << damage << '\n'; + for (auto& cargo : playerShip.cargoes()) { + cargo->accept(_damageVisitor); + } + } else { + std::cout << "Missed\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..d2f40e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp + src/CargoDamageVisitor.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..3771b7a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "Ship/CargoDamageVisitor.h" + +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + virtual void accept(const CargoDamageVisitor& visitor) = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/CargoDamageVisitor.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/CargoDamageVisitor.h new file mode 100644 index 0000000..6e84c58 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/CargoDamageVisitor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Fruit; +class Alcohol; +class Item; + +class CargoDamageVisitor { +public: + CargoDamageVisitor() = default; + virtual ~CargoDamageVisitor() = default; + CargoDamageVisitor(const CargoDamageVisitor&) = default; + CargoDamageVisitor(CargoDamageVisitor&&) = default; + CargoDamageVisitor& operator=(const CargoDamageVisitor&) = default; + CargoDamageVisitor& operator=(CargoDamageVisitor&&) = default; + + virtual void operator()(Fruit& fruit) const; + virtual void operator()(Alcohol& alcohol) const; + virtual void operator()(Item& item) const; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..7300a0d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/Ship.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class PrintVisitor; +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew, std::unique_ptr&& visitor); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + const std::vector>& cargoes() const; + +private: + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..31d75b7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/Cargo.cpp @@ -0,0 +1,43 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +void Fruit::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +void Item::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} + +void Alcohol::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/CargoDamageVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/CargoDamageVisitor.cpp new file mode 100644 index 0000000..6ff89e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/CargoDamageVisitor.cpp @@ -0,0 +1,20 @@ +#include + +#include + +std::random_device CargoDamageVisitor::_rd{}; + +void CargoDamageVisitor::operator()(Fruit& fruit) const { + std::uniform_int_distribution dice(0, 5); + fruit.amount -= dice(_seed); +} + +void CargoDamageVisitor::operator()(Alcohol& alcohol) const { + std::uniform_int_distribution dice(0, 10); + alcohol.amount -= (dice(_seed) / 2); +} + +void CargoDamageVisitor::operator()(Item& item) const { + std::uniform_int_distribution dice(0, 20); + item.amount -= (dice(_seed) / 4); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..b3b15c9 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/Ship.cpp @@ -0,0 +1,105 @@ +#include "Ship/Ship.h" + +#include +#include + +#include +#include +#include +#include + +Ship::Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew, std::unique_ptr&& visitor) + : _time(time), _strategy(std::move(strategy)), _name(name), _capacity(capacity), _crew(crew), _visitor(std::move(visitor)) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + _visitor->visit(*this); +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _durability -= damage; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +const std::vector>& Ship::cargoes() const { + return _cargoes; +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > amount) { + (*it)->amount -= amount; + return true; + } + amount -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/include/Store/Store.h new file mode 100644 index 0000000..fbbbf9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/include/Store/Store.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class PrintVisitor; + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos, std::unique_ptr&& visitor); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + const std::vector>& cargoes() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/src/Store.cpp new file mode 100644 index 0000000..07a9e35 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/src/Store/src/Store.cpp @@ -0,0 +1,41 @@ +#include "Store/Store.h" + +#include + +Store::Store(std::vector>&& cargos, std::unique_ptr&& visitor) + : _seed(_rd()), _cargoes(std::move(cargos)), _visitor(std::move(visitor)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +const std::vector>& Store::cargoes() const { + return _cargoes; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +void Store::printCargo() const { + _visitor->visit(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor2.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor2.cpp new file mode 100644 index 0000000..42170f1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor2.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class RoundShot final : public Ammunition { +public: + using Ammunition::Ammunition; +}; + +class ChainShot final : public Ammunition { +public: + using Ammunition::Ammunition; +}; + +class GrapeShot final : public Ammunition { +public: + using Ammunition::Ammunition; +}; + +class DamageVisitor { +public: + void operator()(const RoundShot& shot) const { + std::cout << "Round shot make 20 damages\n"; + } + void operator()(const ChainShot& shot) const { + std::cout << "Chain shot make 30 damages\n"; + } + void operator()(const GrapeShot& shot) const { + std::cout << "Grape shot make 40 damages\n"; + } +}; + +class SoundVisitor { +public: + void operator()(const RoundShot& shot) const { + std::cout << "Fiuuuuuuuu Bam!\n"; + } + void operator()(const ChainShot& shot) const { + std::cout << "Fiuuuuuuuuuuu Bam bam!\n"; + } + void operator()(const GrapeShot& shot) const { + std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n"; + } +}; + +int main() { + using Shot = std::variant; + + Shot shot = GrapeShot(100); + SoundVisitor soundVisitor; + DamageVisitor damageVisitor; + std::visit(damageVisitor, shot); + std::visit(soundVisitor, shot); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor3.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor3.cpp new file mode 100644 index 0000000..926fef7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor3.cpp @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class AbstractVisitor; + +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void accept(const AbstractVisitor&) = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class RoundShot; +class ChainShot; +class GrapeShot; + +class AbstractVisitor { +protected: + // Can be only use by derived class + virtual ~AbstractVisitor() = default; +}; + +class RoundShotVisitor { +public: + virtual void visit(const RoundShot& shot) const = 0; + +protected: + virtual ~RoundShotVisitor() = default; +}; + +class GrapeShotVisitor { +public: + virtual void visit(const GrapeShot& shot) const = 0; + +protected: + virtual ~GrapeShotVisitor() = default; +}; + +class ChainShotVisitor { +public: + virtual void visit(const ChainShot& shot) const = 0; + +protected: + virtual ~ChainShotVisitor() = default; +}; + +class DamageVisitor : public AbstractVisitor, + public RoundShotVisitor, + public GrapeShotVisitor, + public ChainShotVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Round shot make 20 damages\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Chain shot make 30 damages\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Grape shot make 40 damages\n"; + } +}; + +class SoundVisitor : public AbstractVisitor, + public RoundShotVisitor, + public GrapeShotVisitor, + public ChainShotVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Fiuuuuuuuu Bam!\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam!\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n"; + } +}; + +class RoundShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast(&visitor)) { + v->visit(*this); + } + } +}; + +class ChainShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast(&visitor)) { + v->visit(*this); + } + } +}; + +class GrapeShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast(&visitor)) { + v->visit(*this); + } + } +}; + +int main() { + std::unique_ptr grapeShot = std::make_unique(100); + SoundVisitor soundVisitor; + DamageVisitor damageVisitor; + grapeShot->accept(soundVisitor); + grapeShot->accept(damageVisitor); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor4.cpp b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor4.cpp new file mode 100644 index 0000000..8cec69b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/examples/visitor/visitor4.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class AbstractVisitor; + +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void accept(const AbstractVisitor&) = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class RoundShot; +class ChainShot; +class GrapeShot; + +class AbstractVisitor { +protected: + // Can be only use by derived class + virtual ~AbstractVisitor() = default; +}; + +template +class Visitor { +public: + virtual void visit(const T& shot) const = 0; + +protected: + virtual ~Visitor() = default; +}; + +class DamageVisitor : public AbstractVisitor, + public Visitor, + public Visitor, + public Visitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Round shot make 20 damages\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Chain shot make 30 damages\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Grape shot make 40 damages\n"; + } +}; + +class SoundVisitor : public AbstractVisitor, + public Visitor, + public Visitor, + public Visitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Fiuuuuuuuu Bam!\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam!\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n"; + } +}; + +class RoundShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast*>(&visitor)) { + v->visit(*this); + } + } +}; + +class ChainShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast*>(&visitor)) { + v->visit(*this); + } + } +}; + +class GrapeShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast*>(&visitor)) { + v->visit(*this); + } + } +}; + +int main() { + std::unique_ptr grapeShot = std::make_unique(100); + SoundVisitor soundVisitor; + DamageVisitor damageVisitor; + grapeShot->accept(soundVisitor); + grapeShot->accept(damageVisitor); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exceptions.md b/CreatingReliableSoftwareCpp/Presentation/exceptions.md new file mode 100644 index 0000000..12f9dde --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exceptions.md @@ -0,0 +1,431 @@ +# Exceptions +___ + +## Throw + +If you want to throw an exception you can simply call `throw` + +```C++ +void foo() { + throw "Error"; +} +``` + +If you want to catch an error you need to use `try-catch` + + +```C++ +void bar() { + try { + foo(); + } catch (const std::exception& err) { + std::cout << err.what() << '\n'; + } +} +``` + +___ + +## What happens during throw? + +Programm will stop executing the next line, when reach `throw` command. It will startup **unwind** a stack untill it reach the first `try-catch` block which will catch this exception. Because the stack is unwinded all variables which go out of scope will be properly destructed. + +
+
+ +```C++ +struct Foo { + explicit Foo(int id): id(id) { std::cout << "C'tor id: " << id << "\n"; } + ~Foo() { std::cout << "D'tor id: " << id << "\n"; } + + int id; +}; + +void fun2() { + Foo foo(2); + std::cout << "This will print!\n"; + throw std::runtime_error("Bad!"); + std::cout << "This will not print!\n"; +} + +void fun1() { + std::cout << "We start here!\n"; + Foo foo(1); + std::cout << "After construction of Foo we call function fun2 which throw\n"; + fun2(); + std::cout << "This will not print!\n"; +} + +int main() { + try { + fun1(); + std::cout << "This will not print!\n"; + } catch (const std::exception& err) { + std::cout << "exception: " << err.what() << '\n'; + } + std::cout << "Exit\n"; +} +``` + +
+
+ +```bash +We start here! +C'tor id: 1 +After construction of Foo we call function fun2 which throw +C'tor id: 2 +This will print! +D'tor id: 2 +D'tor id: 1 +exception: Bad! +Exit +``` + +
+
+ +___ + +## Hierarchy + +There are more than 30 exceptions in C++! Each exception could be a base class of other (see cppreference). The most common are: + +* logic_error +* out_of_range +* bad_optional_access +* runtime_error +* bad_weak_ptr +* bad_alloc +___ + +## How to properly catch an error + +```C++ +void doSth() { + std::cout << "Hello! "; + throw std::invalid_argument("SthBad!"); +} + +int main() { + try { + doSth(); + } catch (const std::invalid_argument& err) { + std::cout << "invalid_argument: " << err.what() << '\n'; + } catch (const std::logic_error& err) { + std::cout << "logic_error: " << err.what() << '\n'; + } catch (const std::exception& err) { + std::cout << "exception: " << err.what() << '\n'; + } catch (...) { + std::cout << "Undefined error!\n"; + } +} +``` +Output: `Hello! invalid_argument: SthBad!`. + + + +___ + +What happens when I will try to catch an error with an unordered hierarchy? + +```C++ +void doSth() { + std::cout << "Hello! "; + throw std::invalid_argument("SthBad!"); +} + +int main() { + try { + doSth(); + } catch (const std::logic_error& err) { + std::cout << "logic_error: " << err.what() << '\n'; + } catch (const std::invalid_argument& err) { + std::cout << "invalid_argument: " << err.what() << '\n'; + } catch (...) { + std::cout << "Undefined error!\n"; + } +} +``` + +Output: `Hello! logic_error: SthBad!` + + +```bash +main.cpp: In function ‘int main()’: +main.cpp:21:7: warning: exception of type ‘std::invalid_argument’ will be caught + 21 | } catch (const std::invalid_argument& err) { + | ^~~~~ +main.cpp:19:7: warning: by earlier handler for ‘std::logic_error’ + 19 | } catch (const std::logic_error& err) { +``` + + +___ + +You can quickly create your error class by inheriting from the base class `std::exception`. We need to `override` one method which will return a proper error message: `virtual const char* what() const noexcept` + +```C++ +class MyError : public std::exception { +public: + explicit MyError(const std::string& what): _what(what) {} + explicit MyError(const char* what): MyError(std::string(what)) {} + explicit MyError(std::string_view what): MyError(std::string(what)) {} + const char* what() const noexcept override { return _what.c_str(); } +private: + std::string _what; +}; + +class OtherError : public MyError { +public: + using MyError::MyError; +}; + +void doSth() { + throw OtherError("SthBad!"); +} + +int main() { + try { + doSth(); + } catch (const OtherError& err) { + std::cout << "OtherError: " << err.what() << '\n'; + } catch (const MyError& err) { + std::cout << "MyError: " << err.what() << '\n'; + } catch (const std::exception& err) { + std::cout << "exception: " << err.what() << '\n'; + } +} +``` + + +___ + +It's important to use your error classes instead of those defined by the standard library. It will allow you to determine if the error comes from the standard library or your functions. + +```C++ +class MyError : public std::exception { +public: + explicit MyError(const std::string& what): _what(what) {} + const char* what() const noexcept override { return _what.c_str(); } +private: + std::string _what; +}; + +class Foo { +public: + const std::string& getProcessName(uint64_t pid) { return _processes.at(pid); } + void addProcess(uint64_t pid, const std::string& processName) { + if (_processes.contains(pid)) { + throw MyError(std::format("Process with PID {} already exist", pid)); + } + _processes[pid] = processName; + } +private: + std::map _processes; +}; + +int main() { + try { + Foo foo; + const auto& processName = foo.getProcessName(20); + foo.addProcess(1234, "MySuperprocess!"); + } catch (const MyError& err) { + std::cout << "MyError: " << err.what() << '\n'; + } catch (const std::exception& err) { + std::cout << "exception: " << err.what() << '\n'; + } +} +``` + +___ + +## Ways to signal an error + +* throw an exception +* return enum class ErrorCode +* return nullptr +* return false +* return std::optional (C++17) +* return std::expected (C++23) +___ + +## Descriptive errors + +We have three ways to strictly describe what happens wrong: `exception`, `enum class`, and `std::expected`. Using these types we can say what exactly goes wrong, like: providing the wrong credentials, or we can't find some element, etc... + +
+
+ +```C++ +int foo(const std::string& num) { + if (num != "42") { + throw std::runtime_error("Number is different than 42!"); + } + + return 42; +} + +enum class StatusCode { + Ok, + Not42Number +}; + +StatusCode foo2(const std::string& num, int& res) { + if (num != "42") { + return StatusCode::Not42Number; + } + + res = 42; + return StatusCode::Ok; +} +``` + +
+
+ +```C++ +enum class ParserError { + invalid_input, + overflow +}; + +std::expected parse(std::string_view str) { + if (str.size() > 1'000) { + return std::unexpected(ParserError::overflow); + } else if (str.empty()) { + return std::unexpected(ParserError::invalid_input); + } + + return 42; +} + +int main() { + if (const auto num = parse("something")) { + std::cout << "value: " << *num << '\n'; + } else if (num.error() == ParserError::overflow) { + std::cout << "error: overflow\n"; + } else if (num.error() == ParserError::invalid_input) { + std::cout << "invalid_input!\n"; + } +} +``` +
+ +
+ +___ + +## Non descriptive errors + +There are also three ways how to inform about the error but without describing why. This is useful for simple functions that may have only one error like whether there is a value inside the map or not. + +```C++ +std::unique_ptr foo3(const std::string& num) { + if (num != "42") { + return nullptr; + } + + return std::make_unique(42); +} + +bool foo4(const std::string& num, int& res) { + if (num != "42") { + return false; + } + + res = 42; + return true; +} + +std::optional foo5(const std::string& num) { + if (num != "42") { + return std::nullopt; + } + + return 42; +} +``` + + +___ + +### std::optional - more info + +Introduced in c++17 `std::optional` is a useful class that may have a value or not. + +* Doesn't allocate data on heap! +* Require additional storage info -> bigger size +* Not as efficient as normal integer -> need to perform additional actions + +```C++ +class Foo { + int a; + int b; + int c; + int d; +}; + +int main() { + std::cout << sizeof(int) << '\n'; + std::cout << sizeof(std::optional) << '\n'; + std::cout << sizeof(Foo) << '\n'; + std::cout << sizeof(std::optional) << '\n'; +} +``` + + +```C++ +4 +8 +16 +20 +``` + + +___ + +### std::expected - more info + +Introduced in c++23 `std::expected` is a useful class that may store one of two values: +* expected -> the return value which we expect +* unexpected -> the value that will be returned when something goes wrong + +In most cases as an unexpected value, we return an enum code with a description of what went wrong, but we can return anything (except reference) + + +```C++ +struct Foo { + void print() const { std::cout << "Sorry your function doesn't work!"; } +}; + +std::expected parse(std::string_view str) { + if (str.empty()) { + return std::unexpected(Foo{}); + } + return "42"; +} + +int main() { + if (const auto num = parse("")) { + std::cout << "value: " << *num << '\n'; + } else { + num.error().print(); + } +} +``` + + +```bash +Sorry your function doesn't work! +``` + + +___ + +## Exercise + +* Create an exception class `Exception` which inherits from `std::exception` and two classes `ParserException` and `ReaderException` which inherit form class `Exception`. +* Rewrite a code to `ParserException` in the method `parse` and `ReaderException` in the method `read`. +* Try to catch errors in `main` and print them +* Rewrite a code once again and return `nullopt` when an error occurs instead of throwing an exception \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/CMakeLists.txt new file mode 100644 index 0000000..6e8f2b6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/CMakeLists.txt new file mode 100644 index 0000000..e13b23f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(Ship) +add_subdirectory(Store) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..2f46bbd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(Ship src/Ship.cpp src/Cargo.cpp) + +target_include_directories(Ship PUBLIC include) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..2ba4877 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +struct Cargo { + size_t amount; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; +}; + +struct Fruit : public Cargo { + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Item : public Cargo { + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Alcohol : public Cargo { + size_t getPrice() const override; + const std::string& name() const override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..2a355fa --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/include/Ship/Ship.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include "Ship/Cargo.h" + +class Ship { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(const std::string& name, int capacity); + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const Cargo& cargo, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const Cargo& cargo); + +private: + std::string _name; + int _capacity; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..3fd59ca --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/src/Cargo.cpp @@ -0,0 +1,28 @@ +#include "Ship/Cargo.h" + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Fruit"}; + return name; +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Alcohol"}; + return name; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..4c66981 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Ship/src/Ship.cpp @@ -0,0 +1,48 @@ +#include "Ship/Ship.h" + +#include +#include + +Ship::Ship(const std::string& name, int capacity): + _name(name), _capacity(capacity) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + } + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const Cargo& cargo, StatusCode& code) noexcept { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item){ return *item == cargo; }) ; it != _cargoes.end()) { + _cargoes.erase(it); + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const Cargo& cargo) { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item){ return *item == cargo; }) ; it != _cargoes.end()) { + _cargoes.erase(it); + throw std::runtime_error("Missing cargo"); + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/include/Store/Store.h new file mode 100644 index 0000000..cb4d5f3 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/include/Store/Store.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include + +class Store { +public: + Store() = default; + explicit Store(std::vector>&& cargos); + + size_t getTotalPrice() const; + std::vector getCargosName() const; + +private: + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/src/Store.cpp new file mode 100644 index 0000000..2fe4f85 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/src/Store/src/Store.cpp @@ -0,0 +1,21 @@ +#include "Store/Store.h" + +Store::Store(std::vector>&& cargos): _cargoes(std::move(cargos)) {} + +size_t Store::getTotalPrice() const { + size_t value {0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..a80abf7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + Ship ship("Black Widow", 100); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..697a39f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/SHM/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + Store store; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/CMakeLists.txt new file mode 100644 index 0000000..751c5ff --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +include(FetchContent) +FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.11.3 +) +FetchContent_MakeAvailable(json) + +target_link_libraries(${PROJECT_NAME} + Battle + Ship + Store + Core + Player + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/README.md new file mode 100644 index 0000000..0c8a0c6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/README.md @@ -0,0 +1,39 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise 1 + +* Go to the directory `Ship` and create class `ShipBuilder`. The mandatory fields are: + * name + * capacity + * time +* The non-mandatory fields are: + * difficultStrategy -> default value should be set as **Easy** + * crew -> default value should be set as **10** + * durability -> default value should be set as **1000** + * armor -> default value should be set as **0** + * PrintVisitor -> default value should be set as **Pretty Print** +* Try to build your own `Ship`! + +## Exercise 2 + +* Go to the directory `Ship` and finish implementation of class `ShipJsonBuilder`. You should allow building 3 types of ship: + * Brig + * Frigate + * Galoen +* If you want to read a JSON filed, just use operator[], example: `_shipsData["brige"]["armor"]` +* Try to build a ship using a new builder. +* Try to change some values in `ships.json` and run binary without recompilation. You should see new values! + +## Exercise 3 + +* Go to the directory `Ship` and create abstract class `FruitFactory`. +* Create `FruitFactory` +* Create `ItemFactory` +* Create `AlcoholFactory` +* Test in main.cpp if you can create any type of cargo +* How to handle scenario, when different cargo takes other arguments? diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/main.cpp new file mode 100644 index 0000000..2609baf --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/main.cpp @@ -0,0 +1,60 @@ +#include +#include +#include + +#include "Battle/BattleField.h" +#include "Core/BasicPrintVisitor.h" +#include "Core/PrettyPrintVisitor.h" +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Player/RealPlayer.h" +#include "Ship/Cargo.h" +#include "Ship/CargoDamageVisitor.h" +#include "Ship/Ship.h" +#include "Ship/ShipBuilder.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipJsonBuilder.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Store/Store.h" + +int main() { + Time time; + + auto ship = ShipBuilder() + .setTime(&time) + .setName("Black Widow") + .setCapacity(1000) + .setCrew(40) + .setStrategy(std::make_unique()) + .setVisitor(std::make_unique()) + .setArmor(100) + .setDurability(1000) + .build(); + ship->load(std::make_unique(300)); + ship->load(std::make_unique(200)); + ship->load(std::make_unique(150)); + RealPlayer user("Mateusz", std::move(ship)); + + Enemy enemy("Enemy1", + ShipJsonBuilder("../ships.json").buildGaleon(&time, "Queens Anne revenge"), + std::make_unique(), + CargoDamageVisitor{}); + + BattleField battefield{&user, &enemy}; + while (true) { + for (auto* player : battefield.players()) { + std::cout << "-------------------------------------------------------------------\n"; + std::cout << "Player HP: " << user.getShip().durability() << " ARMOR: " << user.getShip().armor() << " | "; + std::cout << "Enemy HP: " << enemy.getShip().durability() << " ARMOR: " << enemy.getShip().armor() << '\n'; + std::cout << "-------------------------------------------------------------------\n"; + for (size_t i = 0; i < 3; ++i) { + const auto status = player->makeAction(battefield); + if (status == Action::Status::Escaped || status == Action::Status::Defeated) { + return 0; + } + } + } + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/ships.json b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/ships.json new file mode 100644 index 0000000..9b19770 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/ships.json @@ -0,0 +1,20 @@ +{ + "brige":{ + "capacity":1000, + "crew":40, + "durability":1000, + "armor":100 + }, + "frigate":{ + "capacity":1500, + "crew":70, + "durability":2000, + "armor":500 + }, + "galeon":{ + "capacity":2500, + "crew":120, + "durability":3000, + "armor":1000 + } + } \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/CMakeLists.txt new file mode 100644 index 0000000..acd8033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Battle src/Defense.cpp src/BattleField.cpp src/Attack.cpp src/Escape.cpp) + +target_include_directories(Battle PUBLIC include) + +target_link_libraries(Battle + Ship + Player +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Action.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Action.h new file mode 100644 index 0000000..f271d40 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Action.h @@ -0,0 +1,24 @@ +#pragma once + +class Player; +class Ship; + +class Action { +public: + // As you can see keeping type here, require modify a base class whenever we add a new class, this volatile an open-close principle. + // We should be able to extend code without modification already implemented one + enum class Type { + Attack, + Defense, + Escape + }; + + enum class Status { + Escaped, + Defeated, + Nothing, + }; + + virtual Status operator()(Player* player, Ship* enemyShip) = 0; + virtual Type type() const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Attack.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Attack.h new file mode 100644 index 0000000..a06cba0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Attack.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Attack : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Attack; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/BattleField.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/BattleField.h new file mode 100644 index 0000000..6196660 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/BattleField.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Ship; +class Player; + +class BattleField { +public: + BattleField(Player* player, Player* enemy); + + Ship* getPlayerShip() const; + Ship* getEnemyShip() const; + std::vector players() const; + +private: + Player* _player; + Player* _enemy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Defense.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Defense.h new file mode 100644 index 0000000..fefd271 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Defense.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Defense : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Type::Defense; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Escape.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Escape.h new file mode 100644 index 0000000..97dc54d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/include/Battle/Escape.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Battle/Action.h" + +#include + +class Player; +class Ship; + +class Escape : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Escape; } + +private: + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Attack.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Attack.cpp new file mode 100644 index 0000000..b3f5e8a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Attack.cpp @@ -0,0 +1,22 @@ +#include "Battle/Attack.h" + +#include +#include + +#include + +Action::Status Attack::operator()(Player* player, Ship* enemyShip) { + const int damage = player->attack(*enemyShip); + std::cout << "Player ship: " << player->getShip().name() << " attack ship: " << enemyShip->name() << '\n'; + if (damage) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } + + if (enemyShip->durability() <= 0) { + return Status::Defeated; + } + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/BattleField.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/BattleField.cpp new file mode 100644 index 0000000..4e10569 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/BattleField.cpp @@ -0,0 +1,16 @@ +#include "Battle/BattleField.h" + +#include + +BattleField::BattleField(Player* player, Player* enemy) + : _player{player}, _enemy{enemy} {} + +Ship* BattleField::getPlayerShip() const { + return &_player->getShip(); +} +Ship* BattleField::getEnemyShip() const { + return &_enemy->getShip(); +} +std::vector BattleField::players() const { + return {_player, _enemy}; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Defense.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Defense.cpp new file mode 100644 index 0000000..a170c81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Defense.cpp @@ -0,0 +1,13 @@ +#include "Battle/Defense.h" + +#include +#include + +#include + +Action::Status Defense::operator()(Player* player, Ship*) { + player->getShip().increaseArmor(50); + std::cout << "Player ship: " << player->getShip().name() << " defense\n"; + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Escape.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Escape.cpp new file mode 100644 index 0000000..5b222fa --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Battle/src/Escape.cpp @@ -0,0 +1,22 @@ +#include "Battle/Escape.h" + +#include +#include + +#include + +std::random_device Escape::_rd{}; + +Action::Status Escape::operator()(Player* player, Ship*) { + std::cout << "Player ship: " << player->getShip().name() << " try to escape!\n"; + + std::uniform_int_distribution dice(0, 20); + const auto res = dice(_seed); + if (res > 12) { + std::cout << "Player escaped!\n"; + return Status::Escaped; + } + + std::cout << "Escape maneuver failed!\n"; + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/CMakeLists.txt new file mode 100644 index 0000000..9434ff8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(Battle) +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/CMakeLists.txt new file mode 100644 index 0000000..db39d82 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Core src/Time.cpp src/BasicPrintVisitor.cpp src/PrettyPrintVisitor.cpp) + +target_include_directories(Core PUBLIC include) + +target_link_libraries(Core + Ship + Store +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/BasicPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/BasicPrintVisitor.h new file mode 100644 index 0000000..cd3406f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/BasicPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class BasicPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/PrettyPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/PrettyPrintVisitor.h new file mode 100644 index 0000000..58dc6ad --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/PrettyPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class PrettyPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/PrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/PrintVisitor.h new file mode 100644 index 0000000..bec94c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/PrintVisitor.h @@ -0,0 +1,11 @@ +#pragma once + +class Store; +class Ship; + +class PrintVisitor { +public: + virtual ~PrintVisitor() = default; + virtual void visit(const Store&) const = 0; + virtual void visit(const Ship&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/BasicPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/BasicPrintVisitor.cpp new file mode 100644 index 0000000..05960c1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/BasicPrintVisitor.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include +#include +#include + +void BasicPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << std::setw(15) << cargo->getPrice() << "\n"; + } +} + +void BasicPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << "\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/PrettyPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/PrettyPrintVisitor.cpp new file mode 100644 index 0000000..99b6738 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/PrettyPrintVisitor.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +void PrettyPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} + +void PrettyPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/CMakeLists.txt new file mode 100644 index 0000000..b266b3f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(Player src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp src/Player.cpp src/RealPlayer.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Battle + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..e44bd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/Enemy.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include +#include +#include + +#include +#include + +template +class Enemy : public Player { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; + DamageVisitor _damageVisitor; +}; + +template +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)), _damageVisitor(visitor) {} + +template +int Enemy::attack(Ship& playerShip) { + if (const auto damage = _strategy->handle(playerShip)) { + for (auto& cargo : playerShip.cargoes()) { + cargo->accept(_damageVisitor); + } + return damage; + } + + return 0; +} + +template +Ship* Enemy::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getPlayerShip(); +} + +template +std::unique_ptr Enemy::chooseAction(const BattleField& battleField) const { + static bool flag = true; + if (flag) { + flag = !flag; + return std::make_unique(); + } + + flag = !flag; + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/Player.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/Player.h new file mode 100644 index 0000000..7717389 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/Player.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +class BattleField; +class Ship; + +class Player { +public: + virtual ~Player() = default; + virtual int attack(Ship& playerShip) = 0; + virtual Ship& getShip() = 0; + virtual const Ship& getShip() const = 0; + + Action::Status makeAction(const BattleField& battleField); + +protected: + virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0; + virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/RealPlayer.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/RealPlayer.h new file mode 100644 index 0000000..04d8b1a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/include/Player/RealPlayer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include + +class RealPlayer : public Player { +public: + RealPlayer(const std::string& name, std::unique_ptr&& ship); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/Player.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/Player.cpp new file mode 100644 index 0000000..5e1f83b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/Player.cpp @@ -0,0 +1,11 @@ +#include "Player/Player.h" + +Action::Status Player::makeAction(const BattleField& battleField) { + std::unique_ptr action = chooseAction(battleField); + if (action->type() == Action::Type::Attack) { + Ship* enemyShip = chooseEnemyShip(battleField); + return (*action)(this, enemyShip); + } + + return (*action)(this, nullptr); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/RealPlayer.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/RealPlayer.cpp new file mode 100644 index 0000000..b5cd4de --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Player/src/RealPlayer.cpp @@ -0,0 +1,56 @@ +#include "Player/RealPlayer.h" + +#include +#include +#include +#include +#include + +#include + +std::random_device RealPlayer::_rd{}; + +RealPlayer::RealPlayer(const std::string& name, std::unique_ptr&& ship) + : _name(name), _ship(std::move(ship)) {} + +int RealPlayer::attack(Ship& playerShip) { + std::uniform_int_distribution dice(0, 20); + const int res = dice(_seed); + int input = 0; + int damage = 0; + std::cout << "Choose Ammo: 1) Round Shot 2) Chain Shot 3) Grape Shot: "; + std::cin >> input; + + if (input == 1 && res > 4) { + damage = 50; + } else if (input == 2 && res > 7) { + damage = 80; + } else if (input == 3 && res > 10) { + damage = 100; + } else { + return 0; + } + + playerShip.takeDamage(damage); + return damage; +} + +Ship* RealPlayer::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getEnemyShip(); +} + +std::unique_ptr RealPlayer::chooseAction(const BattleField& battleField) const { + int input = 0; + std::cout << "Choose Action: 1) Attack 2) Defense 3) Escape: "; + std::cin >> input; + + if (input == 1) { + return std::make_unique(); + } + if (input == 3) { + return std::make_unique(); + } + + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..a1adb4e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp + src/CargoDamageVisitor.cpp + src/ShipBuilder.cpp + src/ShipJsonBuilder.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..3771b7a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "Ship/CargoDamageVisitor.h" + +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + virtual void accept(const CargoDamageVisitor& visitor) = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/CargoDamageVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/CargoDamageVisitor.h new file mode 100644 index 0000000..6e84c58 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/CargoDamageVisitor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Fruit; +class Alcohol; +class Item; + +class CargoDamageVisitor { +public: + CargoDamageVisitor() = default; + virtual ~CargoDamageVisitor() = default; + CargoDamageVisitor(const CargoDamageVisitor&) = default; + CargoDamageVisitor(CargoDamageVisitor&&) = default; + CargoDamageVisitor& operator=(const CargoDamageVisitor&) = default; + CargoDamageVisitor& operator=(CargoDamageVisitor&&) = default; + + virtual void operator()(Fruit& fruit) const; + virtual void operator()(Alcohol& alcohol) const; + virtual void operator()(Item& item) const; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..88e6495 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/Ship.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class PrintVisitor; +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + const std::vector>& cargoes() const; + + void increaseArmor(int armor) { _armor += armor; } + int durability() const { return _durability; } + int armor() const { return _armor; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + friend class ShipBuilder; + Ship() = default; + void initialize(); + + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + int _armor{0}; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipBuilder.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipBuilder.h new file mode 100644 index 0000000..8c1922a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipBuilder.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +class PrintVisitor; +class Time; + +class ShipBuilder { +public: + ShipBuilder(); + + [[nodiscard]] std::unique_ptr build(); + + ShipBuilder& setTime(Time* time); + ShipBuilder& setStrategy(std::unique_ptr> strategy); + ShipBuilder& setName(const std::string& name); + ShipBuilder& setCapacity(int capacity); + ShipBuilder& setCrew(int crew); + ShipBuilder& setVisitor(std::unique_ptr visitor); + ShipBuilder& setArmor(int armor); + ShipBuilder& setDurability(int durability); + +private: + std::unique_ptr _ship; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipJsonBuilder.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipJsonBuilder.h new file mode 100644 index 0000000..ff495c8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/include/Ship/ShipJsonBuilder.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +#include "Ship/ShipBuilder.h" + +using json = nlohmann::json; + +class Time; +class Ship; + +class ShipJsonBuilder : private ShipBuilder { +public: + ShipJsonBuilder(const std::filesystem::path& path); + + std::unique_ptr buildBrig(Time* time, const std::string& name); + std::unique_ptr buildFrigate(Time* time, const std::string& name); + std::unique_ptr buildGaleon(Time* time, const std::string& name); + +private: + void parseFile(const std::filesystem::path& path); + + json _shipsData; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..31d75b7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/Cargo.cpp @@ -0,0 +1,43 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +void Fruit::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +void Item::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} + +void Alcohol::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/CargoDamageVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/CargoDamageVisitor.cpp new file mode 100644 index 0000000..6ff89e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/CargoDamageVisitor.cpp @@ -0,0 +1,20 @@ +#include + +#include + +std::random_device CargoDamageVisitor::_rd{}; + +void CargoDamageVisitor::operator()(Fruit& fruit) const { + std::uniform_int_distribution dice(0, 5); + fruit.amount -= dice(_seed); +} + +void CargoDamageVisitor::operator()(Alcohol& alcohol) const { + std::uniform_int_distribution dice(0, 10); + alcohol.amount -= (dice(_seed) / 2); +} + +void CargoDamageVisitor::operator()(Item& item) const { + std::uniform_int_distribution dice(0, 20); + item.amount -= (dice(_seed) / 4); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..6871c94 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/Ship.cpp @@ -0,0 +1,107 @@ +#include "Ship/Ship.h" + +#include +#include + +#include +#include +#include +#include + +void Ship::initialize() { + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + _visitor->visit(*this); +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _armor -= damage; + if (_armor >= 0) { + return; + } + + _durability += _armor; + _armor = 0; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +const std::vector>& Ship::cargoes() const { + return _cargoes; +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > amount) { + (*it)->amount -= amount; + return true; + } + amount -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipBuilder.cpp new file mode 100644 index 0000000..fb77e5f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipBuilder.cpp @@ -0,0 +1,68 @@ +#include "Ship/ShipBuilder.h" + +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include +#include + +ShipBuilder::ShipBuilder() + : _ship(std::make_unique()) { + _ship->_strategy = std::make_unique(); + _ship->_visitor = std::make_unique(); +} + +std::unique_ptr ShipBuilder::build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->_time || _ship->_name.empty() || _ship->_capacity == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + _ship->initialize(); + return std::move(_ship); +} + +ShipBuilder& ShipBuilder::setTime(Time* time) { + _ship->_time = time; + return *this; +} + +ShipBuilder& ShipBuilder::setStrategy(std::unique_ptr> strategy) { + _ship->_strategy = std::move(strategy); + return *this; +} + +ShipBuilder& ShipBuilder::setName(const std::string& name) { + _ship->_name = name; + return *this; +} + +ShipBuilder& ShipBuilder::setCapacity(int capacity) { + _ship->_capacity = capacity; + return *this; +} + +ShipBuilder& ShipBuilder::setCrew(int crew) { + _ship->_crew = crew; + return *this; +} + +ShipBuilder& ShipBuilder::setVisitor(std::unique_ptr visitor) { + _ship->_visitor = std::move(visitor); + return *this; +} + +ShipBuilder& ShipBuilder::setArmor(int armor) { + _ship->_armor = armor; + return *this; +} + +ShipBuilder& ShipBuilder::setDurability(int durability) { + _ship->_durability = durability; + return *this; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipJsonBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipJsonBuilder.cpp new file mode 100644 index 0000000..a9b653a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Ship/src/ShipJsonBuilder.cpp @@ -0,0 +1,48 @@ +#include "Ship/ShipJsonBuilder.h" + +#include "Ship/Ship.h" + +#include +#include +#include + +ShipJsonBuilder::ShipJsonBuilder(const std::filesystem::path& path) { + parseFile(path); +} + +std::unique_ptr ShipJsonBuilder::buildBrig(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["brige"]["capacity"]) + .setCrew(_shipsData["brige"]["crew"]) + .setArmor(_shipsData["brige"]["armor"]) + .setDurability(_shipsData["brige"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildFrigate(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["frigate"]["capacity"]) + .setCrew(_shipsData["frigate"]["crew"]) + .setArmor(_shipsData["frigate"]["armor"]) + .setDurability(_shipsData["frigate"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildGaleon(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["galeon"]["capacity"]) + .setCrew(_shipsData["galeon"]["crew"]) + .setArmor(_shipsData["galeon"]["armor"]) + .setDurability(_shipsData["galeon"]["durability"]) + .build(); +} + +void ShipJsonBuilder::parseFile(const std::filesystem::path& path) { + std::ifstream file(path); + if (!file.is_open()) { + std::cout << "Can't open a file! erno: " << strerror(errno) << '\n'; + abort(); + } + _shipsData = json::parse(file); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/include/Store/Store.h new file mode 100644 index 0000000..fbbbf9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/include/Store/Store.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class PrintVisitor; + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos, std::unique_ptr&& visitor); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + const std::vector>& cargoes() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/src/Store.cpp new file mode 100644 index 0000000..07a9e35 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/src/Store/src/Store.cpp @@ -0,0 +1,41 @@ +#include "Store/Store.h" + +#include + +Store::Store(std::vector>&& cargos, std::unique_ptr&& visitor) + : _seed(_rd()), _cargoes(std::move(cargos)), _visitor(std::move(visitor)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +const std::vector>& Store::cargoes() const { + return _cargoes; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +void Store::printCargo() const { + _visitor->visit(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/builder/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/CMakeLists.txt new file mode 100644 index 0000000..751c5ff --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +include(FetchContent) +FetchContent_Declare(json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.11.3 +) +FetchContent_MakeAvailable(json) + +target_link_libraries(${PROJECT_NAME} + Battle + Ship + Store + Core + Player + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/README.md new file mode 100644 index 0000000..f3a591a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/README.md @@ -0,0 +1,25 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise 1 + +* Go into directory `Ship` and implement `DecoratedCargo`: + * It should take a `std::unique_ptr` and has protected getter to this field. +* Implement `Vunreable` class: + * It should inherit from `TimeObserver` and `DecoratedCargo` + * It should take additional parameters in C'tor: `Time`, `durability` and `maxDurability` + * It should simulate time elapsing. Every day should subtract durability + * It should return price back on current durability of cargo + +## Exercise 2 + +* Implement `Valuable` class: + * It should inherit from `DecoratedCargo` + * it should be a template that takes `enum` type of value + * It should return value based on type (cast it to enum and multiply by current value) +* Try to compile code form `main.cpp` +* You may have some trouble with running code if you don't write a code carefully :) think where is a problem. diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/main.cpp new file mode 100644 index 0000000..dd12d5f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/main.cpp @@ -0,0 +1,53 @@ +#include +#include +#include + +#include "Battle/BattleField.h" +#include "Core/BasicPrintVisitor.h" +#include "Core/FullInfoPrintVisitor.h" +#include "Core/PrettyPrintVisitor.h" +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Player/RealPlayer.h" +#include "Ship/AlcoholFactory.h" +#include "Ship/AlcoholType.h" +#include "Ship/Cargo.h" +#include "Ship/CargoDamageVisitor.h" +#include "Ship/FruitFactory.h" +#include "Ship/ItemFactory.h" +#include "Ship/ItemType.h" +#include "Ship/Ship.h" +#include "Ship/ShipBuilder.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Ship/ShipJsonBuilder.h" +#include "Ship/Valuable.h" +#include "Ship/Vulnerable.h" +#include "Store/Store.h" + +int main() { + Time time; + + auto ship = ShipBuilder() + .setTime(&time) + .setName("Black Widow") + .setCapacity(2500) + .setCrew(40) + .setStrategy(std::make_unique()) + .setVisitor(std::make_unique()) + .setArmor(100) + .setDurability(1000) + .build(); + ship->load(std::make_unique>(AlcoholFactory().create(800, 40), AlcoholType::Seasoned)); + ship->load(std::make_unique(FruitFactory().create(500), &time, 15, 15)); + ship->load(std::make_unique(FruitFactory().create(700), &time, 20, 20)); + ship->load(std::make_unique(std::make_unique>(ItemFactory().create(250), ItemType::Epic), &time, 100, 100)); + + for (size_t i = 1; i <= 15; ++i) { + std::cout << "day: " << i << '\n'; + ship->printCargo(); + ++time; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/ships.json b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/ships.json new file mode 100644 index 0000000..9b19770 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/ships.json @@ -0,0 +1,20 @@ +{ + "brige":{ + "capacity":1000, + "crew":40, + "durability":1000, + "armor":100 + }, + "frigate":{ + "capacity":1500, + "crew":70, + "durability":2000, + "armor":500 + }, + "galeon":{ + "capacity":2500, + "crew":120, + "durability":3000, + "armor":1000 + } + } \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/CMakeLists.txt new file mode 100644 index 0000000..acd8033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Battle src/Defense.cpp src/BattleField.cpp src/Attack.cpp src/Escape.cpp) + +target_include_directories(Battle PUBLIC include) + +target_link_libraries(Battle + Ship + Player +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Action.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Action.h new file mode 100644 index 0000000..f271d40 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Action.h @@ -0,0 +1,24 @@ +#pragma once + +class Player; +class Ship; + +class Action { +public: + // As you can see keeping type here, require modify a base class whenever we add a new class, this volatile an open-close principle. + // We should be able to extend code without modification already implemented one + enum class Type { + Attack, + Defense, + Escape + }; + + enum class Status { + Escaped, + Defeated, + Nothing, + }; + + virtual Status operator()(Player* player, Ship* enemyShip) = 0; + virtual Type type() const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Attack.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Attack.h new file mode 100644 index 0000000..a06cba0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Attack.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Attack : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Attack; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/BattleField.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/BattleField.h new file mode 100644 index 0000000..6196660 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/BattleField.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Ship; +class Player; + +class BattleField { +public: + BattleField(Player* player, Player* enemy); + + Ship* getPlayerShip() const; + Ship* getEnemyShip() const; + std::vector players() const; + +private: + Player* _player; + Player* _enemy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Defense.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Defense.h new file mode 100644 index 0000000..fefd271 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Defense.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Defense : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Type::Defense; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Escape.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Escape.h new file mode 100644 index 0000000..97dc54d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/include/Battle/Escape.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Battle/Action.h" + +#include + +class Player; +class Ship; + +class Escape : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Escape; } + +private: + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Attack.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Attack.cpp new file mode 100644 index 0000000..b3f5e8a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Attack.cpp @@ -0,0 +1,22 @@ +#include "Battle/Attack.h" + +#include +#include + +#include + +Action::Status Attack::operator()(Player* player, Ship* enemyShip) { + const int damage = player->attack(*enemyShip); + std::cout << "Player ship: " << player->getShip().name() << " attack ship: " << enemyShip->name() << '\n'; + if (damage) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } + + if (enemyShip->durability() <= 0) { + return Status::Defeated; + } + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/BattleField.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/BattleField.cpp new file mode 100644 index 0000000..4e10569 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/BattleField.cpp @@ -0,0 +1,16 @@ +#include "Battle/BattleField.h" + +#include + +BattleField::BattleField(Player* player, Player* enemy) + : _player{player}, _enemy{enemy} {} + +Ship* BattleField::getPlayerShip() const { + return &_player->getShip(); +} +Ship* BattleField::getEnemyShip() const { + return &_enemy->getShip(); +} +std::vector BattleField::players() const { + return {_player, _enemy}; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Defense.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Defense.cpp new file mode 100644 index 0000000..a170c81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Defense.cpp @@ -0,0 +1,13 @@ +#include "Battle/Defense.h" + +#include +#include + +#include + +Action::Status Defense::operator()(Player* player, Ship*) { + player->getShip().increaseArmor(50); + std::cout << "Player ship: " << player->getShip().name() << " defense\n"; + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Escape.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Escape.cpp new file mode 100644 index 0000000..5b222fa --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Battle/src/Escape.cpp @@ -0,0 +1,22 @@ +#include "Battle/Escape.h" + +#include +#include + +#include + +std::random_device Escape::_rd{}; + +Action::Status Escape::operator()(Player* player, Ship*) { + std::cout << "Player ship: " << player->getShip().name() << " try to escape!\n"; + + std::uniform_int_distribution dice(0, 20); + const auto res = dice(_seed); + if (res > 12) { + std::cout << "Player escaped!\n"; + return Status::Escaped; + } + + std::cout << "Escape maneuver failed!\n"; + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/CMakeLists.txt new file mode 100644 index 0000000..9434ff8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(Battle) +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/CMakeLists.txt new file mode 100644 index 0000000..25b294b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Core src/Time.cpp src/BasicPrintVisitor.cpp src/PrettyPrintVisitor.cpp src/FullInfoPrintVisitor.cpp) + +target_include_directories(Core PUBLIC include) + +target_link_libraries(Core + Ship + Store +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/BasicPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/BasicPrintVisitor.h new file mode 100644 index 0000000..cd3406f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/BasicPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class BasicPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/FullInfoPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/FullInfoPrintVisitor.h new file mode 100644 index 0000000..c932bc6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/FullInfoPrintVisitor.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class FullInfoPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; + +private: + void print() const; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/PrettyPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/PrettyPrintVisitor.h new file mode 100644 index 0000000..58dc6ad --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/PrettyPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class PrettyPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/PrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/PrintVisitor.h new file mode 100644 index 0000000..bec94c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/PrintVisitor.h @@ -0,0 +1,11 @@ +#pragma once + +class Store; +class Ship; + +class PrintVisitor { +public: + virtual ~PrintVisitor() = default; + virtual void visit(const Store&) const = 0; + virtual void visit(const Ship&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/BasicPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/BasicPrintVisitor.cpp new file mode 100644 index 0000000..a0afe9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/BasicPrintVisitor.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include +#include +#include + +void BasicPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount() << std::setw(15) << cargo->getPrice() << "\n"; + } +} + +void BasicPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount() << "\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/FullInfoPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/FullInfoPrintVisitor.cpp new file mode 100644 index 0000000..a43d164 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/FullInfoPrintVisitor.cpp @@ -0,0 +1,24 @@ +#include + +#include +#include +#include +#include + +void FullInfoPrintVisitor::visit(const Store& store) const { + std::cout << "Don't have time to implement this, sorry xD\n"; +} + +void FullInfoPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount() << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/PrettyPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/PrettyPrintVisitor.cpp new file mode 100644 index 0000000..e639a1b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/PrettyPrintVisitor.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +void PrettyPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount() << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} + +void PrettyPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount() << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/CMakeLists.txt new file mode 100644 index 0000000..b266b3f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(Player src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp src/Player.cpp src/RealPlayer.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Battle + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..e44bd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/Enemy.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include +#include +#include + +#include +#include + +template +class Enemy : public Player { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; + DamageVisitor _damageVisitor; +}; + +template +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)), _damageVisitor(visitor) {} + +template +int Enemy::attack(Ship& playerShip) { + if (const auto damage = _strategy->handle(playerShip)) { + for (auto& cargo : playerShip.cargoes()) { + cargo->accept(_damageVisitor); + } + return damage; + } + + return 0; +} + +template +Ship* Enemy::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getPlayerShip(); +} + +template +std::unique_ptr Enemy::chooseAction(const BattleField& battleField) const { + static bool flag = true; + if (flag) { + flag = !flag; + return std::make_unique(); + } + + flag = !flag; + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/Player.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/Player.h new file mode 100644 index 0000000..7717389 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/Player.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +class BattleField; +class Ship; + +class Player { +public: + virtual ~Player() = default; + virtual int attack(Ship& playerShip) = 0; + virtual Ship& getShip() = 0; + virtual const Ship& getShip() const = 0; + + Action::Status makeAction(const BattleField& battleField); + +protected: + virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0; + virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/RealPlayer.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/RealPlayer.h new file mode 100644 index 0000000..04d8b1a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/include/Player/RealPlayer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Player/Player.h" + +#include +#include +#include + +class RealPlayer : public Player { +public: + RealPlayer(const std::string& name, std::unique_ptr&& ship); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +protected: + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + +private: + std::string _name; + std::unique_ptr _ship; + static std::random_device _rd; + std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/Player.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/Player.cpp new file mode 100644 index 0000000..5e1f83b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/Player.cpp @@ -0,0 +1,11 @@ +#include "Player/Player.h" + +Action::Status Player::makeAction(const BattleField& battleField) { + std::unique_ptr action = chooseAction(battleField); + if (action->type() == Action::Type::Attack) { + Ship* enemyShip = chooseEnemyShip(battleField); + return (*action)(this, enemyShip); + } + + return (*action)(this, nullptr); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/RealPlayer.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/RealPlayer.cpp new file mode 100644 index 0000000..b5cd4de --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Player/src/RealPlayer.cpp @@ -0,0 +1,56 @@ +#include "Player/RealPlayer.h" + +#include +#include +#include +#include +#include + +#include + +std::random_device RealPlayer::_rd{}; + +RealPlayer::RealPlayer(const std::string& name, std::unique_ptr&& ship) + : _name(name), _ship(std::move(ship)) {} + +int RealPlayer::attack(Ship& playerShip) { + std::uniform_int_distribution dice(0, 20); + const int res = dice(_seed); + int input = 0; + int damage = 0; + std::cout << "Choose Ammo: 1) Round Shot 2) Chain Shot 3) Grape Shot: "; + std::cin >> input; + + if (input == 1 && res > 4) { + damage = 50; + } else if (input == 2 && res > 7) { + damage = 80; + } else if (input == 3 && res > 10) { + damage = 100; + } else { + return 0; + } + + playerShip.takeDamage(damage); + return damage; +} + +Ship* RealPlayer::chooseEnemyShip(const BattleField& battleField) const { + // To simplify palyer and enemy has one ship + return battleField.getEnemyShip(); +} + +std::unique_ptr RealPlayer::chooseAction(const BattleField& battleField) const { + int input = 0; + std::cout << "Choose Action: 1) Attack 2) Defense 3) Escape: "; + std::cin >> input; + + if (input == 1) { + return std::make_unique(); + } + if (input == 3) { + return std::make_unique(); + } + + return std::make_unique(); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..a1adb4e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp + src/CargoDamageVisitor.cpp + src/ShipBuilder.cpp + src/ShipJsonBuilder.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core + nlohmann_json::nlohmann_json +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/AlcoholFactory.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/AlcoholFactory.h new file mode 100644 index 0000000..60a3ed6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/AlcoholFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class AlcoholFactory : public CargoFactory { +public: + std::unique_ptr create(size_t amount, int power) override { + return std::make_unique(amount, power); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/AlcoholType.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/AlcoholType.h new file mode 100644 index 0000000..a59d85a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/AlcoholType.h @@ -0,0 +1,6 @@ +#pragma once + +enum class AlcoholType { White = 1, + Spiced = 2, + Dark = 3, + Seasoned = 5 }; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..a52042f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include "Ship/CargoDamageVisitor.h" + +#include + +struct Cargo { + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + size_t amount() const { return _amount; } + size_t& amount() { return _amount; } + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + virtual void accept(const CargoDamageVisitor& visitor) = 0; + +private: + size_t _amount; +}; + +struct Fruit : public Cargo { + static constexpr int BASE_PRICE = 10; + + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + static constexpr int BASE_PRICE = 100; + + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + static constexpr int MAX_POWER = 96; + static constexpr int BASE_PRICE = 100; + int power; + + Alcohol(size_t amount, int power); + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/CargoDamageVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/CargoDamageVisitor.h new file mode 100644 index 0000000..6e84c58 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/CargoDamageVisitor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Fruit; +class Alcohol; +class Item; + +class CargoDamageVisitor { +public: + CargoDamageVisitor() = default; + virtual ~CargoDamageVisitor() = default; + CargoDamageVisitor(const CargoDamageVisitor&) = default; + CargoDamageVisitor(CargoDamageVisitor&&) = default; + CargoDamageVisitor& operator=(const CargoDamageVisitor&) = default; + CargoDamageVisitor& operator=(CargoDamageVisitor&&) = default; + + virtual void operator()(Fruit& fruit) const; + virtual void operator()(Alcohol& alcohol) const; + virtual void operator()(Item& item) const; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/CargoFactory.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/CargoFactory.h new file mode 100644 index 0000000..53c1e9f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/CargoFactory.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +class Cargo; + +template +class CargoFactory { +public: + virtual std::unique_ptr create(size_t amount, Args... args) = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/FruitFactory.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/FruitFactory.h new file mode 100644 index 0000000..3906a69 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/FruitFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class FruitFactory : public CargoFactory<> { +public: + std::unique_ptr create(size_t amount) override { + return std::make_unique(amount); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ItemFactory.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ItemFactory.h new file mode 100644 index 0000000..fc949e3 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ItemFactory.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +#include +#include + +class ItemFactory : public CargoFactory<> { +public: + std::unique_ptr create(size_t amount) override { + return std::make_unique(amount); + } +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ItemType.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ItemType.h new file mode 100644 index 0000000..266429b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ItemType.h @@ -0,0 +1,6 @@ +#pragma once + +enum class ItemType { Common = 1, + Rare = 3, + Epic = 10, + Legendary = 25 }; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..88e6495 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/Ship.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class PrintVisitor; +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + const std::vector>& cargoes() const; + + void increaseArmor(int armor) { _armor += armor; } + int durability() const { return _durability; } + int armor() const { return _armor; } + +private: + // Allow to be created only by std::unique_ptr + friend std::unique_ptr std::make_unique(); + friend class ShipBuilder; + Ship() = default; + void initialize(); + + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + int _armor{0}; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipBuilder.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipBuilder.h new file mode 100644 index 0000000..8c1922a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipBuilder.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +class PrintVisitor; +class Time; + +class ShipBuilder { +public: + ShipBuilder(); + + [[nodiscard]] std::unique_ptr build(); + + ShipBuilder& setTime(Time* time); + ShipBuilder& setStrategy(std::unique_ptr> strategy); + ShipBuilder& setName(const std::string& name); + ShipBuilder& setCapacity(int capacity); + ShipBuilder& setCrew(int crew); + ShipBuilder& setVisitor(std::unique_ptr visitor); + ShipBuilder& setArmor(int armor); + ShipBuilder& setDurability(int durability); + +private: + std::unique_ptr _ship; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipJsonBuilder.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipJsonBuilder.h new file mode 100644 index 0000000..ff495c8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/include/Ship/ShipJsonBuilder.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include + +#include "Ship/ShipBuilder.h" + +using json = nlohmann::json; + +class Time; +class Ship; + +class ShipJsonBuilder : private ShipBuilder { +public: + ShipJsonBuilder(const std::filesystem::path& path); + + std::unique_ptr buildBrig(Time* time, const std::string& name); + std::unique_ptr buildFrigate(Time* time, const std::string& name); + std::unique_ptr buildGaleon(Time* time, const std::string& name); + +private: + void parseFile(const std::filesystem::path& path); + + json _shipsData; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..0fa653f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/Cargo.cpp @@ -0,0 +1,46 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : _amount(amount) {} + +size_t Fruit::getPrice() const { + return BASE_PRICE; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +void Fruit::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Item::getPrice() const { + return BASE_PRICE; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +void Item::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +Alcohol::Alcohol(size_t amount, int power) + : Cargo(amount), power(power) {} + +size_t Alcohol::getPrice() const { + return (power * BASE_PRICE) / MAX_POWER; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} + +void Alcohol::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/CargoDamageVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/CargoDamageVisitor.cpp new file mode 100644 index 0000000..0792d32 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/CargoDamageVisitor.cpp @@ -0,0 +1,20 @@ +#include + +#include + +std::random_device CargoDamageVisitor::_rd{}; + +void CargoDamageVisitor::operator()(Fruit& fruit) const { + std::uniform_int_distribution dice(0, 5); + fruit.amount() -= dice(_seed); +} + +void CargoDamageVisitor::operator()(Alcohol& alcohol) const { + std::uniform_int_distribution dice(0, 10); + alcohol.amount() -= (dice(_seed) / 2); +} + +void CargoDamageVisitor::operator()(Item& item) const { + std::uniform_int_distribution dice(0, 20); + item.amount() -= (dice(_seed) / 4); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..6754047 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/Ship.cpp @@ -0,0 +1,108 @@ +#include "Ship/Ship.h" + +#include +#include + +#include +#include +#include +#include + +void Ship::initialize() { + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + _visitor->visit(*this); +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount()) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + std::cout << "cap: " << _capacity << " | amount: " << cargo->amount() << std::endl; + if (_capacity > cargo->amount()) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _armor -= damage; + if (_armor >= 0) { + return; + } + + _durability += _armor; + _armor = 0; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +const std::vector>& Ship::cargoes() const { + return _cargoes; +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount() > amount) { + (*it)->amount() -= amount; + return true; + } + amount -= (*it)->amount(); + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipBuilder.cpp new file mode 100644 index 0000000..fb77e5f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipBuilder.cpp @@ -0,0 +1,68 @@ +#include "Ship/ShipBuilder.h" + +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include +#include + +ShipBuilder::ShipBuilder() + : _ship(std::make_unique()) { + _ship->_strategy = std::make_unique(); + _ship->_visitor = std::make_unique(); +} + +std::unique_ptr ShipBuilder::build() { + if (!_ship) { + std::cout << "Builder may be use once\n"; + return nullptr; + } + + // Check mandatory fields + if (!_ship->_time || _ship->_name.empty() || _ship->_capacity == -1) { + std::cout << "Mandatory fields are not set!\n"; + return nullptr; + } + + _ship->initialize(); + return std::move(_ship); +} + +ShipBuilder& ShipBuilder::setTime(Time* time) { + _ship->_time = time; + return *this; +} + +ShipBuilder& ShipBuilder::setStrategy(std::unique_ptr> strategy) { + _ship->_strategy = std::move(strategy); + return *this; +} + +ShipBuilder& ShipBuilder::setName(const std::string& name) { + _ship->_name = name; + return *this; +} + +ShipBuilder& ShipBuilder::setCapacity(int capacity) { + _ship->_capacity = capacity; + return *this; +} + +ShipBuilder& ShipBuilder::setCrew(int crew) { + _ship->_crew = crew; + return *this; +} + +ShipBuilder& ShipBuilder::setVisitor(std::unique_ptr visitor) { + _ship->_visitor = std::move(visitor); + return *this; +} + +ShipBuilder& ShipBuilder::setArmor(int armor) { + _ship->_armor = armor; + return *this; +} + +ShipBuilder& ShipBuilder::setDurability(int durability) { + _ship->_durability = durability; + return *this; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipJsonBuilder.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipJsonBuilder.cpp new file mode 100644 index 0000000..a9b653a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Ship/src/ShipJsonBuilder.cpp @@ -0,0 +1,48 @@ +#include "Ship/ShipJsonBuilder.h" + +#include "Ship/Ship.h" + +#include +#include +#include + +ShipJsonBuilder::ShipJsonBuilder(const std::filesystem::path& path) { + parseFile(path); +} + +std::unique_ptr ShipJsonBuilder::buildBrig(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["brige"]["capacity"]) + .setCrew(_shipsData["brige"]["crew"]) + .setArmor(_shipsData["brige"]["armor"]) + .setDurability(_shipsData["brige"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildFrigate(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["frigate"]["capacity"]) + .setCrew(_shipsData["frigate"]["crew"]) + .setArmor(_shipsData["frigate"]["armor"]) + .setDurability(_shipsData["frigate"]["durability"]) + .build(); +} +std::unique_ptr ShipJsonBuilder::buildGaleon(Time* time, const std::string& name) { + return setTime(time) + .setName(name) + .setCapacity(_shipsData["galeon"]["capacity"]) + .setCrew(_shipsData["galeon"]["crew"]) + .setArmor(_shipsData["galeon"]["armor"]) + .setDurability(_shipsData["galeon"]["durability"]) + .build(); +} + +void ShipJsonBuilder::parseFile(const std::filesystem::path& path) { + std::ifstream file(path); + if (!file.is_open()) { + std::cout << "Can't open a file! erno: " << strerror(errno) << '\n'; + abort(); + } + _shipsData = json::parse(file); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/include/Store/Store.h new file mode 100644 index 0000000..fbbbf9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/include/Store/Store.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class PrintVisitor; + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos, std::unique_ptr&& visitor); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + const std::vector>& cargoes() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/src/Store.cpp new file mode 100644 index 0000000..a950548 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/src/Store/src/Store.cpp @@ -0,0 +1,41 @@ +#include "Store/Store.h" + +#include + +Store::Store(std::vector>&& cargos, std::unique_ptr&& visitor) + : _seed(_rd()), _cargoes(std::move(cargos)), _visitor(std::move(visitor)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount() += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +const std::vector>& Store::cargoes() const { + return _cargoes; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +void Store::printCargo() const { + _visitor->visit(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/decorator/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/CMakeLists.txt new file mode 100644 index 0000000..91a27d2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(ExampleProject) + +set(SRC_LIST + exampleProject.cpp +) + +add_executable(${PROJECT_NAME} ${SRC_LIST}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/README.md new file mode 100644 index 0000000..14082ac --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/README.md @@ -0,0 +1,10 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Run program + +Run program ./ExampleProject and check output, which should be "Hello World"; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/exampleProject.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/exampleProject.cpp new file mode 100644 index 0000000..ab426f2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/exampleProject/exampleProject.cpp @@ -0,0 +1,5 @@ +#include + +int main() { + std::cout << "Hello world!\n"; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/exception/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/CMakeLists.txt new file mode 100644 index 0000000..f50ba0c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(Exception) + +set(SRC_LIST + main.cpp +) + +add_executable(${PROJECT_NAME} ${SRC_LIST}) +target_compile_options(${PROJECT_NAME} PUBLIC -Wall -Werror -Wpedantic -Wextra) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/exception/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/README.md new file mode 100644 index 0000000..62a4147 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/README.md @@ -0,0 +1,13 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise 1 + +* Create an exception class `Exception` which inherits from `std`::exception` and two classes `ParserException` and `ReaderException` which inherit form class `Exception`. +* Rewrite a code to `ParserException` in the method `parse` and `ReaderException` in the method `read`. +* Try to catch errors in `main` and print them +* Rewrite a code once again and return `nullopt` when an error occurs instead of throwing an exception \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/exception/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/main.cpp new file mode 100644 index 0000000..3b31264 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/main.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include + +int parse(const std::string& str) { + if (str.empty()) { + std::cout << "String is empty!"; + return 0; + } + if (str.size() > 20) { + std::cout << "Value is to big to fit in integer!"; + return 0; + } + + return std::stoi(str); +} + +std::string read(const std::vector& vec) { + if (vec.empty()) { + std::cout << "Vector is empty, can't read"; + return ""; + } + if (vec.size() > 20) { + std::cout << "Vector is to big"; + return ""; + } + + return std::accumulate(vec.begin(), vec.end(), std::string{}, [](const auto& str, int num){ + if (str.empty()) { + return std::to_string(num); + } + return str + ", " + std::to_string(num); + }); +} + +int main() { + std::cout << "Parsed number: " << parse("-123") << '\n'; + std::cout << "Read numbers: " << read({1,2,3-1,-2,-3}) << '\n'; + std::cout << "Parsed number: " << parse("123456789012345678901234") << '\n'; + std::cout << "Read numbers: " << read({}) << '\n'; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/exception/soulutions/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/soulutions/main.cpp new file mode 100644 index 0000000..abbcf1d --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/exception/soulutions/main.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include +#include +#include + +class Error : public std::exception { +public: + explicit Error(const std::string& what): _what(what) {} + explicit Error(const char* what): Error(std::string(what)) {} + explicit Error(std::string_view what): Error(std::string(what)) {} + const char* what() const noexcept override { return _what.c_str(); } +private: + std::string _what; +}; + +class ParserException : public Error { +public: + using Error::Error; +}; + +class ReaderException : public Error { +public: + using Error::Error; +}; + +int parse(const std::string& str) { + if (str.empty()) { + throw ParserException("String is empty!"); + } + if (str.size() > 20) { + throw ParserException("Value is to big to fit in integer!"); + } + + return std::stoi(str); +} + +std::string read(const std::vector& vec) { + if (vec.empty()) { + throw ReaderException("Vector is empty, can't read"); + } + if (vec.size() > 20) { + throw ReaderException("Vector is to big"); + return ""; + } + + return std::accumulate(vec.begin(), vec.end(), std::string{}, [](const auto& str, int num){ + if (str.empty()) { + return std::to_string(num); + } + return str + ", " + std::to_string(num); + }); +} + +int main() { + try { + std::cout << "Parsed number: " << parse("-123") << '\n'; + std::cout << "Read numbers: " << read({1,2,3-1,-2,-3}) << '\n'; + std::cout << "Parsed number: " << parse("123456789012345678901234") << '\n'; + std::cout << "Read numbers: " << read({}) << '\n'; + } catch (const ReaderException& ec) { + std::cout << "ReaderException: " << ec.what() << '\n'; + } catch (const ParserException& ec) { + std::cout << "ParserException: " << ec.what() << '\n'; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/CMakeLists.txt new file mode 100644 index 0000000..c6cd3a2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Ship + Store + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/README.md new file mode 100644 index 0000000..706fe9f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/README.md @@ -0,0 +1,143 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise 1 + +* Go into directory *Core* and implement *Time* and *TimeObserver* class +* Class *Time* should have 4 methods: + * void attach(TimeObserver* observer); -> attach observer + * void detach(TimeObserver* observer); -> detach observer + * Time& operator++(); -> increment day and notify observers + * size_t day() const; -> return current day +* Go into directory *Ship* and make *Ship* class inherit from *TimeObserver* +* Each time the observer will be triggered, you should give the crew food and drink +* Each crew member should consume **1** rum and **1** banana (I know they usually eat biscuits) +* If you don't have enough cargo crew should rebel +* Each day of a rebel you should subtract **5** members of the crew +* If the number of crew drops below **0**, throw an exception "Game Over" + +Example (We have 40 members of crew): +Day: 0 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 300 | +|Banana | 200 | +|Banana | 150 | +|----------------|----------------| + +DAY: 1 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 260 | +|Banana | 160 | +|Banana | 150 | +|----------------|----------------| + +DAY: 2 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 220 | +|Banana | 120 | +|Banana | 150 | +|----------------|----------------| + +DAY: 3 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 180 | +|Banana | 80 | +|Banana | 150 | +|----------------|----------------| + +DAY: 4 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 140 | +|Banana | 40 | +|Banana | 150 | +|----------------|----------------| + +DAY: 5 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 100 | +|Banana | 150 | +|----------------|----------------| + +DAY: 6 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 60 | +|Banana | 110 | +|----------------|----------------| + +DAY: 7 +crew: 40 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 20 | +|Banana | 70 | +|----------------|----------------| + +********** Crew rebel ********** + +DAY: 8 +crew: 35 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Banana | 30 | +|----------------|----------------| + +********** Crew rebel ********** + +DAY: 9 +crew: 30 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|----------------|----------------| + +********** Crew rebel ********** + +DAY: 10 +crew: 25 +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|----------------|----------------| + +## Exercise 2 + +* Go into directory *Store* and make *Store* class inherit from *TimeObserver* +* Class *Store* should don't know anything about class *Time* +* We should attach and detach observer outside of class. Think about how you can make it exception-safe (RAII) +* Each time the observer will be triggered, you should change the number of available cargo in the store +* Each time draw a number between -50 and 50 and add it to the amount of cargo +* Print everyday cargo from the Shop and verify if the amount changes + +## Exercise 3 + +* Rewrite Observer to use modern approach -> by value semantic. +* Rewrite class Ship +* Rewrite class Store +* Question: Which class was easier to rewrite? diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/main.cpp new file mode 100644 index 0000000..363f2ea --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/main.cpp @@ -0,0 +1,22 @@ +#include + +#include "Core/Time.h" +#include "Ship/Cargo.h" +#include "Ship/Ship.h" +#include "Store/Store.h" + +int main() { + Time time; + Ship ship(&time, "Black Widow", 1000, 40); + + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.printCargo(); + + for (int i = 0; i < 8; ++i) { + ++time; + std::cout << "\nDAY: " << time.day() << "\n"; + std::cout << "crew: " << ship.crew() << '\n'; + ship.printCargo(); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/CMakeLists.txt new file mode 100644 index 0000000..482b48c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(Core) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/CMakeLists.txt new file mode 100644 index 0000000..5db9b9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(Core src/Time.cpp) + +target_include_directories(Core PUBLIC include) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/include/Core/Time.h new file mode 100644 index 0000000..8a5e089 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/include/Core/Time.h @@ -0,0 +1,11 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..106a258 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,5 @@ +#pragma once + +struct TimeObserver { + +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/src/Time.cpp new file mode 100644 index 0000000..0ab265b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Core/src/Time.cpp @@ -0,0 +1,6 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..8523b82 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Ship src/Ship.cpp src/Cargo.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..3faa7e4 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +struct Cargo { + size_t amount; + + explicit Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..4fde92b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/include/Ship/Ship.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include +#include + +#include "Ship/Cargo.h" + +class Ship { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(const std::string& name, int capacity, int crew); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const Cargo& cargo, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const Cargo& cargo); + +private: + bool consume(const std::string& cargoName); + void rebel(); + + std::string _name; + int _capacity; + int _crew; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..9c55175 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/src/Cargo.cpp @@ -0,0 +1,31 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..261bcfa --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Ship/src/Ship.cpp @@ -0,0 +1,74 @@ +#include "Ship/Ship.h" + +#include +#include +#include +#include +#include + +Ship::Ship(const std::string& name, int capacity, int crew) + : _name(name), _capacity(capacity), _crew(crew) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::printCargo() const { + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const Cargo& cargo, StatusCode& code) noexcept { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item) { return *item == cargo; }); it != _cargoes.end()) { + _cargoes.erase(it); + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const Cargo& cargo) { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item) { return *item == cargo; }); it != _cargoes.end()) { + _cargoes.erase(it); + throw std::runtime_error("Missing cargo"); + } +} + +bool Ship::consume(const std::string& cargoName) { + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/include/Store/Store.h new file mode 100644 index 0000000..cb4d5f3 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/include/Store/Store.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include + +class Store { +public: + Store() = default; + explicit Store(std::vector>&& cargos); + + size_t getTotalPrice() const; + std::vector getCargosName() const; + +private: + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/src/Store.cpp new file mode 100644 index 0000000..2fe4f85 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/src/Store/src/Store.cpp @@ -0,0 +1,21 @@ +#include "Store/Store.h" + +Store::Store(std::vector>&& cargos): _cargoes(std::move(cargos)) {} + +size_t Store::getTotalPrice() const { + size_t value {0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/observer/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/CMakeLists.txt new file mode 100644 index 0000000..4b33b70 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Ship + Store + Core + Player +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/README.md new file mode 100644 index 0000000..180991e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/README.md @@ -0,0 +1,38 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise 1 + +* Go into directory Core and implement ShipStrategy as a template class +* Strategy should have one pure virtual function: Res handle(T&) +* Implement in directory Ship class ShipEasyDifficultLvlStrategy + * Each cargo supplies 2 sailors, so if you have 40 crew, you need 20 banas and 20 rum +* Implement NextDay by using strategy class +* Run code, and verify result + +## Exercise 2 + +* Implement in directory *Player* class *PalyerEasyDifficultLvlStrategy* +    * Strategy should roll a die (1-20): +    * If result is lower than 5 -> 0 DMG +    * If result is higher than 19 -> multiple damage by 2 +    * Damage should be rolled from (25 to 50) +    * deal damage to the ship and return how much damage was deal. Print it in console +* Add all necessary implementation to the *Enemy* file +* Test it + +## Exercise 3 + +* Implement tow new classes *EnemyHardDifficultLvlStrategy* and *ShipHardDifficultLvlStrategy* + * Strategy for Ship, should subtract cargo equal crew size +    * Strategy for Enemy should roll a die (1-20): +     * If result is lower than 3 -> 0 DMG + * If result is higher than 15 but not equal 20 -> multiple damage by 3 +     * If result is higher than 19 -> multiple damage by 3 +     * Damage should be rolled from (30 to 60) +* You shouldn't modify any existing file, except the main.cpp, where you should use a new strategy +* Run code and verify the result \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/main.cpp new file mode 100644 index 0000000..38b7c17 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/main.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Ship/Cargo.h" +#include "Ship/Ship.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Store/Store.h" + +int main() { + Time time; + + Ship ship(&time, "Black Widow", 1000, 40); + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.load(std::make_unique(150)); + ship.printCargo(); + + for (int i = 0; i < 10; ++i) { + ++time; + std::cout << "\nDAY: " << time.day() << "\n"; + std::cout << "crew: " << ship.crew() << '\n'; + ship.printCargo(); + } + + std::cout << "\n\n****************************************************\n"; + auto ship2 = std::make_unique(&time, "Queens Anne revenge", 1000, 40); + Enemy enemy("Enemy1", std::move(ship2)); + + for (int i = 0; i < 15; ++i) { + enemy.attack(ship); + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/CMakeLists.txt new file mode 100644 index 0000000..c29ff6c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/CMakeLists.txt new file mode 100644 index 0000000..5db9b9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(Core src/Time.cpp) + +target_include_directories(Core PUBLIC include) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/CMakeLists.txt new file mode 100644 index 0000000..1dc7329 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Player src/Enemy.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..4a5039b --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/include/Player/Enemy.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include +#include + +class Ship; + +class Enemy { +public: + Enemy(const std::string& name, std::unique_ptr&& ship); + + void attack(Ship& playerShip); + +private: + std::string _name; + std::unique_ptr _ship; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/src/Enemy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/src/Enemy.cpp new file mode 100644 index 0000000..d3f3a9c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Player/src/Enemy.cpp @@ -0,0 +1,15 @@ +#include "Player/Enemy.h" + +#include + +#include +#include +#include + +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship) + : _name(name), _ship(std::move(ship)) {} + +void Enemy::attack(Ship& playerShip) { + std::cout << "Player: " << _name << " attack ship: " << playerShip.name() << '\n'; + playerShip.dealDamage(100); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..a6a3997 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..b4afe1f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..df72ed4 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/include/Ship/Ship.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +#include +#include "Ship/Cargo.h" + +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(Time* time, const std::string& name, int capacity, int crew); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + +private: + bool consume(const std::string& cargoName); + void rebel(); + + Time* _time; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..9c55175 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/src/Cargo.cpp @@ -0,0 +1,31 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..766f7dd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Ship/src/Ship.cpp @@ -0,0 +1,114 @@ +#include "Ship/Ship.h" + +#include + +#include +#include +#include +#include +#include + +Ship::Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew) + : _time(time), _strategy(std::move(strategy)), _name(name), _capacity(capacity), _crew(crew) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + bool shouldRebel = !consume("Rum"); + shouldRebel |= !consume("Banana"); + if (shouldRebel) { + rebel(); + } +} + +void Ship::printCargo() const { + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const Cargo& cargo, StatusCode& code) noexcept { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item) { return *item == cargo; }); it != _cargoes.end()) { + _cargoes.erase(it); + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const Cargo& cargo) { + if (auto it = std::find_if(_cargoes.begin(), _cargoes.end(), [&cargo](const auto& item) { return *item == cargo; }); it != _cargoes.end()) { + _cargoes.erase(it); + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _durability -= damage; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +bool Ship::consume(const std::string& cargoName) { + int left = _crew; + + while (left != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > left) { + (*it)->amount -= left; + return true; + } + left -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/include/Store/Store.h new file mode 100644 index 0000000..1eb93cb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/include/Store/Store.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/src/Store.cpp new file mode 100644 index 0000000..41d32fe --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/src/Store/src/Store.cpp @@ -0,0 +1,48 @@ +#include "Store/Store.h" + +#include +#include + +Store::Store(std::vector>&& cargos) + : _seed(_rd()), _cargoes(std::move(cargos)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +// Shame on me, I copy-past it. But remember DRY!! +// This is only to speed up process of creating exercises, sorry :) +void Store::printCargo() const { + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/strategy/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/CMakeLists.txt new file mode 100644 index 0000000..7103268 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +project(Streamer) + +set(SRC_LIST + streamer.cpp +) + +add_executable(${PROJECT_NAME} ${SRC_LIST}) +target_compile_options(${PROJECT_NAME} PUBLIC -Wall -Werror -Wpedantic -Wextra) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/README.md new file mode 100644 index 0000000..c37b48c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/README.md @@ -0,0 +1,17 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise + +* Open project streamer +* Create Mpeg2Streamer class +* Create MjpegStreamer class +* Allow to add data to stream +* Allow to add/remove receivers +* start/stop stream +* validate data +* Make some dummy implementations that will allow to compile code \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/solution/streamer.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/solution/streamer.cpp new file mode 100644 index 0000000..8ce0e89 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/solution/streamer.cpp @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include + +class Streamer { +public: + using IpAddress = std::string; + using Port = uint16_t; + using Vlan = uint16_t; + + struct StreamInfo { + IpAddress sourceAddress_; + IpAddress destinationAddress_; + Port sourcePort_; + Port destinationPort_; + Vlan vlan_; + + bool operator==(const StreamInfo& other) const { + return std::tie(sourceAddress_, destinationAddress_, sourcePort_, destinationPort_, vlan_) == + std::tie(other.sourceAddress_, other.destinationAddress_, other.sourcePort_, other.destinationPort_, other.vlan_); + } + }; + + virtual ~Streamer() = default; + Streamer() = default; + Streamer(const Streamer&) = default; + Streamer& operator=(const Streamer&) = default; + Streamer(Streamer&&) = default; + Streamer& operator=(Streamer&&) = default; + + virtual bool addReceiver(const StreamInfo& info) = 0; + virtual bool removeReceiver(const StreamInfo& info) = 0; + virtual bool addData(const std::vector& data) = 0; + virtual bool validateData(const std::vector& data) const = 0; + virtual bool startStream() = 0; + virtual bool stopStream() = 0; + virtual bool streamInProgress() const = 0; +}; + +class StreamerImpl : public Streamer { +public: + StreamerImpl(std::initializer_list info) + : info_(info) { + std::cout << "C'tor: td::initializer_list info\n"; + } + + StreamerImpl(const StreamInfo& info) + : info_(1, info) { + std::cout << "C'tor: const StreamInfo& info\n"; + } + + bool addReceiver(const StreamInfo& info) override { + if (findReceiver(info) == std::cend(info_)) { + info_.push_back(info); + return true; + } + + return false; + } + + bool removeReceiver(const StreamInfo& info) override { + if (const auto it = findReceiver(info); it != std::cend(info_)) { + info_.erase(it); + return true; + } + + return false; + } + + bool addData(const std::vector& data) override { + // Used pure virtual functions -> this will be implemented by derived class! + if (!validateData(data) || streamInProgress()) { + return false; + } + + data_ = data; + return true; + } + +protected: + std::vector::const_iterator findReceiver(const StreamInfo& info) const { + return std::find(cbegin(info_), cend(info_), info); + } + + std::vector info_; + std::vector data_; +}; + +class Mpeg2Streamer : public StreamerImpl { +public: + using StreamerImpl::StreamerImpl; + + bool validateData(const std::vector& data) const override { + return data.size() % 2; + } + + bool startStream() override { + if (streamInProgress()) { + return false; + } + + inProgress_ = true; + std::thread([data = std::ref(data_), inProgress = std::ref(inProgress_)]() { + for (const auto el : data.get()) { + if (!inProgress.get()) { + return; + } + std::cout << el << ' ' << std::flush; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + }).detach(); + return true; + } + + bool stopStream() override { + if (!streamInProgress()) { + return false; + } + + inProgress_ = false; + return true; + } + + bool streamInProgress() const override { + return inProgress_ && !data_.empty(); + } + +private: + std::atomic inProgress_{false}; +}; + +class MjpegStreamer : public StreamerImpl { +public: + using StreamerImpl::StreamerImpl; + + bool validateData(const std::vector& data) const override { + return !(data.size() % 2); + } + + bool startStream() override { + if (streamInProgress() || data_.front() == 5) { + return false; + } + + inProgress_ = true; + std::thread([data = std::ref(data_), inProgress = std::ref(inProgress_)]() { + for (const auto el : data.get() | std::views::reverse) { + if (!inProgress.get()) { + return; + } + std::cout << el << ' ' << std::flush; + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + }).detach(); + + return true; + } + + bool stopStream() override { + if (!streamInProgress() || data_.back() == 10) { + return false; + } + + inProgress_ = false; + return true; + } + + bool streamInProgress() const override { + return inProgress_; + } + +private: + std::atomic inProgress_{false}; +}; + +int main() { + std::unique_ptr streamer = std::make_unique( + Streamer::StreamInfo{"192.168.0.0", "192.168.10.24", 5432, 5432, 12}); + + // true + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6, 7}) << '\n'; + std::cout << streamer->startStream() << '\n'; + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << '\n' + << streamer->stopStream() << '\n'; + + // false + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6}) << '\n'; + + // NEW TYPE OF STREAMER + streamer = std::make_unique( + Streamer::StreamInfo{"192.168.0.0", "192.168.10.24", 5432, 5432, 12}); + + // true + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6}) << '\n'; + std::cout << streamer->startStream() << '\n'; + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << '\n' + << streamer->stopStream() << '\n'; + + // false + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6, 7}) << '\n'; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/streamer.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/streamer.cpp new file mode 100644 index 0000000..daa53d6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/streamer/streamer.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include +#include +#include + +class Streamer { +public: + struct StreamInfo { + std::string sourceAddress_; + std::string destinationAddress_; + uint16_t sourcePort_; + uint16_t destinationPort_; + uint16_t vlan_; + + bool operator==(const StreamInfo& other) const { + return std::tie(sourceAddress_, destinationAddress_, sourcePort_, destinationPort_, vlan_) == + std::tie(other.sourceAddress_, other.destinationAddress_, other.sourcePort_, other.destinationPort_, other.vlan_); + } + }; + + virtual ~Streamer() = default; + Streamer(const Streamer&) = default; + Streamer(Streamer&&) = default; + Streamer& operator=(const Streamer&) = default; + Streamer& operator=(Streamer&&) = default; + + Streamer(const std::vector& info) + : info_(info) {} + + virtual bool addReceiver(const StreamInfo& info) = 0; + virtual bool removeReceiver(const StreamInfo& info) = 0; + virtual bool addData(const std::vector& data) = 0; + virtual bool validateData(const std::vector& data) const = 0; + virtual bool startStream() = 0; + virtual bool stopStream() = 0; + virtual bool streamInProgress() const = 0; + +private: + std::vector info_; +}; + +// Create two classes and add some dummy implementations to allow check if code works :) + +int main() { + // Testing Mpeg2Streamer + std::unique_ptr streamer = std::make_unique( + Streamer::StreamInfo{"192.168.0.0", "192.168.10.24", 5432, 5432, 12}); + + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6, 7}) << '\n'; + std::cout << streamer->startStream() << '\n'; + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << '\n' + << streamer->stopStream() << '\n'; + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6}) << '\n'; + + // Testing MjpegStreamer + streamer = std::make_unique( + Streamer::StreamInfo{"192.168.0.0", "192.168.10.24", 5432, 5432, 12}); + + // true + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6}) << '\n'; + std::cout << streamer->startStream() << '\n'; + std::this_thread::sleep_for(std::chrono::seconds(1)); + std::cout << '\n' + << streamer->stopStream() << '\n'; + std::cout << std::boolalpha << streamer->addData({1, 2, 3, 4, 5, 6, 7}) << '\n'; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/CMakeLists.txt new file mode 100644 index 0000000..9d2ce9e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Battle + Ship + Store + Core + Player +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/README.md new file mode 100644 index 0000000..e27d56c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/README.md @@ -0,0 +1,35 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise 1 + +* Go into directory Player and implement a base class `Player`, this class should have 3 public virtual members: + * `virtual int attack(Ship& playerShip) = 0;` + * `virtual Ship& getShip() = 0;` + * `virtual const Ship& getShip() const = 0;` +* Add to `Player` class a non-virtual function: `Action::Status makeAction(const BattleField& battleField);`. +* Add to `Player` class 2 virtual protected function (a template method): + * `virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0;` + * `virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0;` +* Implement some logic in method `makeAction` based on 2 template method: + * To simplify, Player can only attack or defense + * Use class Command from directory `Battle` + +## Exercise 2 + +* Implement class `RealUser` which will inherit from class `Player`. Implement both template methods: + * Don't make it too complex, just ask user about action and type of ammunition, then you can randomly choose a damage or if missed. + * To increase defense, you can use method `setArmor` from `Ship` class. +* Rewrite class `Enemy` to inherit from `Player` add also implementation for both template methods +* Uncomment code in main, check if you can perform a real battle! + +## Exercise 3 + +* Go into directory `Battle` and implement class `Escape` which will inherit from class `Action`. + * Roll a die (1 to 20), if value is bigger than `12` a player successfully escaped. +* Noticed that we shouldn't modify existing code, but we need to do this in two place, where? +* How would you refactor the code? \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/main.cpp new file mode 100644 index 0000000..f9eb477 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/main.cpp @@ -0,0 +1,73 @@ +#include +#include +#include + +#include "Battle/BattleField.h" +#include "Core/BasicPrintVisitor.h" +#include "Core/PrettyPrintVisitor.h" +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Player/RealPlayer.h" +#include "Ship/Cargo.h" +#include "Ship/CargoDamageVisitor.h" +#include "Ship/Ship.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Store/Store.h" + +int main() { + Time time; + std::vector> cargoes; + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + cargoes.push_back(std::make_unique(1'000)); + auto store = std::unique_ptr>( + [&cargoes, &time]() { + auto* store = new Store(std::move(cargoes), std::make_unique()); + try { + time.attach(store); + } catch (...) { + abort(); + } + return store; }(), + [&time](Store* store) { + time.detach(store); + delete store; + }); + + Ship ship(&time, std::make_unique(), "Black Widow", 1000, 40, std::make_unique()); + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.load(std::make_unique(150)); + + std::cout << "Ship\n"; + ship.printCargo(); + std::cout << "\nStore\n"; + store->printCargo(); + + std::cout << "\n\n****************************************************\n"; + auto ship2 = std::make_unique(&time, std::make_unique(), "Queens Anne revenge", 1000, 40, std::make_unique()); + Enemy enemy("Enemy1", std::move(ship2), std::make_unique(), CargoDamageVisitor{}); + + // UNCOMMENT THIS IN EXERCISE 2 + // auto ship3 = std::make_unique(&time, std::make_unique(), "Black Pearl", 1000, 40, std::make_unique()); + // RealPlayer user("Mateusz", std::move(ship3)); + + // BattleField battefield{&user, &enemy}; + // while (true) { + // for (auto* player : battefield.players()) { + // std::cout << "-------------------------------------------------------------------\n"; + // std::cout << "Player HP: " << user.getShip().durability() << " ARMOR: " << user.getShip().armor() << " | "; + // std::cout << "Enemy HP: " << enemy.getShip().durability() << " ARMOR: " << enemy.getShip().armor() << '\n'; + // std::cout << "-------------------------------------------------------------------\n"; + // for (size_t i = 0; i < 3; ++i) { + // const auto status = player->makeAction(battefield); + // if (status == Action::Status::Escaped || status == Action::Status::Defeated) { + // return 0; + // } + // } + // } + // } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/CMakeLists.txt new file mode 100644 index 0000000..38fdc09 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Battle src/Defense.cpp src/BattleField.cpp src/Attack.cpp) + +target_include_directories(Battle PUBLIC include) + +target_link_libraries(Battle + Ship + Player +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Action.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Action.h new file mode 100644 index 0000000..d4ddfa6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Action.h @@ -0,0 +1,21 @@ +#pragma once + +class Player; +class Ship; + +class Action { +public: + enum class Type { + Attack, + Defense + }; + + enum class Status { + Escaped, + Defeated, + Nothing, + }; + + virtual Status operator()(Player* player, Ship* enemyShip) = 0; + virtual Type type() const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Attack.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Attack.h new file mode 100644 index 0000000..a06cba0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Attack.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Attack : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Action::Type::Attack; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/BattleField.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/BattleField.h new file mode 100644 index 0000000..6196660 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/BattleField.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +class Ship; +class Player; + +class BattleField { +public: + BattleField(Player* player, Player* enemy); + + Ship* getPlayerShip() const; + Ship* getEnemyShip() const; + std::vector players() const; + +private: + Player* _player; + Player* _enemy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Defense.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Defense.h new file mode 100644 index 0000000..fefd271 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/include/Battle/Defense.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Battle/Action.h" + +class Player; +class Ship; + +class Defense : public Action { +public: + Status operator()(Player* player, Ship* enemyShip) override; + Type type() const override { return Type::Defense; } +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/Attack.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/Attack.cpp new file mode 100644 index 0000000..b3f5e8a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/Attack.cpp @@ -0,0 +1,22 @@ +#include "Battle/Attack.h" + +#include +#include + +#include + +Action::Status Attack::operator()(Player* player, Ship* enemyShip) { + const int damage = player->attack(*enemyShip); + std::cout << "Player ship: " << player->getShip().name() << " attack ship: " << enemyShip->name() << '\n'; + if (damage) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } + + if (enemyShip->durability() <= 0) { + return Status::Defeated; + } + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/BattleField.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/BattleField.cpp new file mode 100644 index 0000000..4e10569 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/BattleField.cpp @@ -0,0 +1,16 @@ +#include "Battle/BattleField.h" + +#include + +BattleField::BattleField(Player* player, Player* enemy) + : _player{player}, _enemy{enemy} {} + +Ship* BattleField::getPlayerShip() const { + return &_player->getShip(); +} +Ship* BattleField::getEnemyShip() const { + return &_enemy->getShip(); +} +std::vector BattleField::players() const { + return {_player, _enemy}; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/Defense.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/Defense.cpp new file mode 100644 index 0000000..a170c81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Battle/src/Defense.cpp @@ -0,0 +1,13 @@ +#include "Battle/Defense.h" + +#include +#include + +#include + +Action::Status Defense::operator()(Player* player, Ship*) { + player->getShip().increaseArmor(50); + std::cout << "Player ship: " << player->getShip().name() << " defense\n"; + + return Status::Nothing; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/CMakeLists.txt new file mode 100644 index 0000000..9434ff8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/CMakeLists.txt @@ -0,0 +1,5 @@ +add_subdirectory(Battle) +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/CMakeLists.txt new file mode 100644 index 0000000..db39d82 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Core src/Time.cpp src/BasicPrintVisitor.cpp src/PrettyPrintVisitor.cpp) + +target_include_directories(Core PUBLIC include) + +target_link_libraries(Core + Ship + Store +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/BasicPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/BasicPrintVisitor.h new file mode 100644 index 0000000..cd3406f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/BasicPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class BasicPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/PrettyPrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/PrettyPrintVisitor.h new file mode 100644 index 0000000..58dc6ad --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/PrettyPrintVisitor.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/PrintVisitor.h" + +class Store; +class Ship; + +class PrettyPrintVisitor : public PrintVisitor { +public: + void visit(const Store&) const override; + void visit(const Ship&) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/PrintVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/PrintVisitor.h new file mode 100644 index 0000000..bec94c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/PrintVisitor.h @@ -0,0 +1,11 @@ +#pragma once + +class Store; +class Ship; + +class PrintVisitor { +public: + virtual ~PrintVisitor() = default; + virtual void visit(const Store&) const = 0; + virtual void visit(const Ship&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/BasicPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/BasicPrintVisitor.cpp new file mode 100644 index 0000000..05960c1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/BasicPrintVisitor.cpp @@ -0,0 +1,22 @@ +#include + +#include +#include +#include +#include + +void BasicPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << std::setw(15) << cargo->getPrice() << "\n"; + } +} + +void BasicPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + for (const auto& cargo : cargoes) { + std::cout << std::setw(15) << std::left << cargo->name() << std::setw(15) << cargo->amount << "\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/PrettyPrintVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/PrettyPrintVisitor.cpp new file mode 100644 index 0000000..99b6738 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/PrettyPrintVisitor.cpp @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +void PrettyPrintVisitor::visit(const Store& store) const { + const auto& cargoes = store.cargoes(); + + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} + +void PrettyPrintVisitor::visit(const Ship& ship) const { + const auto& cargoes = ship.cargoes(); + + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/CMakeLists.txt new file mode 100644 index 0000000..1ec8794 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(Player src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Battle + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..d70c028 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/Enemy.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#include +#include + +template +class Enemy { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor); + + int attack(Ship& playerShip) override; + Ship& getShip() { return *_ship; } + const Ship& getShip() const { return *_ship; } + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; + DamageVisitor _damageVisitor; +}; + +template +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy, DamageVisitor visitor) + : _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)), _damageVisitor(visitor) {} + +template +int Enemy::attack(Ship& playerShip) { + if (const auto damage = _strategy->handle(playerShip)) { + for (auto& cargo : playerShip.cargoes()) { + cargo->accept(_damageVisitor); + } + return damage; + } + + return 0; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..d2f40e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp + src/CargoDamageVisitor.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..3771b7a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +#include "Ship/CargoDamageVisitor.h" + +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; + virtual void accept(const CargoDamageVisitor& visitor) = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; + void accept(const CargoDamageVisitor& visitor) override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/CargoDamageVisitor.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/CargoDamageVisitor.h new file mode 100644 index 0000000..6e84c58 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/CargoDamageVisitor.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +class Fruit; +class Alcohol; +class Item; + +class CargoDamageVisitor { +public: + CargoDamageVisitor() = default; + virtual ~CargoDamageVisitor() = default; + CargoDamageVisitor(const CargoDamageVisitor&) = default; + CargoDamageVisitor(CargoDamageVisitor&&) = default; + CargoDamageVisitor& operator=(const CargoDamageVisitor&) = default; + CargoDamageVisitor& operator=(CargoDamageVisitor&&) = default; + + virtual void operator()(Fruit& fruit) const; + virtual void operator()(Alcohol& alcohol) const; + virtual void operator()(Item& item) const; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed{_rd()}; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..186d0bb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/Ship.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class PrintVisitor; +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew, std::unique_ptr&& visitor); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + const std::vector>& cargoes() const; + + void increaseArmor(int armor) { _armor += armor; } + int durability() const { return _durability; } + int armor() const { return _armor; } + +private: + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + int _armor{0}; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..31d75b7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/Cargo.cpp @@ -0,0 +1,43 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +void Fruit::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +void Item::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} + +void Alcohol::accept(const CargoDamageVisitor& visitor) { + visitor(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/CargoDamageVisitor.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/CargoDamageVisitor.cpp new file mode 100644 index 0000000..6ff89e2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/CargoDamageVisitor.cpp @@ -0,0 +1,20 @@ +#include + +#include + +std::random_device CargoDamageVisitor::_rd{}; + +void CargoDamageVisitor::operator()(Fruit& fruit) const { + std::uniform_int_distribution dice(0, 5); + fruit.amount -= dice(_seed); +} + +void CargoDamageVisitor::operator()(Alcohol& alcohol) const { + std::uniform_int_distribution dice(0, 10); + alcohol.amount -= (dice(_seed) / 2); +} + +void CargoDamageVisitor::operator()(Item& item) const { + std::uniform_int_distribution dice(0, 20); + item.amount -= (dice(_seed) / 4); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..2529f92 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/Ship.cpp @@ -0,0 +1,111 @@ +#include "Ship/Ship.h" + +#include +#include + +#include +#include +#include +#include + +Ship::Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew, std::unique_ptr&& visitor) + : _time(time), _strategy(std::move(strategy)), _name(name), _capacity(capacity), _crew(crew), _visitor(std::move(visitor)) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + _visitor->visit(*this); +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _armor -= damage; + if (_armor >= 0) { + return; + } + + _durability += _armor; + _armor = 0; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +const std::vector>& Ship::cargoes() const { + return _cargoes; +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > amount) { + (*it)->amount -= amount; + return true; + } + amount -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/include/Store/Store.h new file mode 100644 index 0000000..fbbbf9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/include/Store/Store.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class PrintVisitor; + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos, std::unique_ptr&& visitor); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + const std::vector>& cargoes() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; + std::unique_ptr _visitor; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/src/Store.cpp new file mode 100644 index 0000000..07a9e35 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/src/Store/src/Store.cpp @@ -0,0 +1,41 @@ +#include "Store/Store.h" + +#include + +Store::Store(std::vector>&& cargos, std::unique_ptr&& visitor) + : _seed(_rd()), _cargoes(std::move(cargos)), _visitor(std::move(visitor)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +const std::vector>& Store::cargoes() const { + return _cargoes; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +void Store::printCargo() const { + _visitor->visit(*this); +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/templateMethod/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/CMakeLists.txt new file mode 100644 index 0000000..4b33b70 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.16) +project(SHM LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +enable_testing() + +add_subdirectory(src) +add_subdirectory(tests) + +add_executable(${PROJECT_NAME} main.cpp) + +target_link_libraries(${PROJECT_NAME} + Ship + Store + Core + Player +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/README.md b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/README.md new file mode 100644 index 0000000..142e27c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/README.md @@ -0,0 +1,67 @@ +## Linux compilation + + > mkdir build + > cd build + > cmake -DCMAKE_BUILD_TYPE=Debug .. + > make + +## Exercise 1 + +* Go into directory `Core` and implement a base class `PrintVisitor` which should implement `visit` method for two objects: `Ship` and `Store` +* Create `PrettyPrintVisitor` which will print cargo from `Ship` and `Sotore` (just move the implementation `printCargo`). +* Add the rest of necessary implementation for `Ship` and `Store`. Visitor should be taken as `std::unique_ptr` +* Verify in the `main.cpp` if cargo print in the same way as previously. + +Example: + +``` +Ship +|----------------|----------------| +| NAME | AMOUNT | +|----------------|----------------| +|Rum | 300 | +|Banana | 200 | +|Banana | 150 | +|----------------|----------------| + +Store +|----------------|----------------|----------------| +| NAME | AMOUNT | PRICE | +|----------------|----------------|----------------| +|Rum | 1000 | 40 | +|Banana | 1000 | 20 | +|Item | 1000 | 30 | +|----------------|----------------|----------------| +``` + +## Exercise 2 + +* Create another visitor: `BasicPrintVisitor` which should print the cargo but without special frames, just raw info. +* For `Ship` print Name and Amount +* For `Store` print Name Amount and Price +* You shouldn't modify anything in your code, just change the visitor in `main.cpp` and you should get a new output + +Example: + +``` +Ship +Rum 300 +Banana 200 +Banana 150 + +Store +Rum 1000 40 +Banana 1000 20 +Item 1000 30 +``` + +## Exercise 3 + +* Create CargoDamageVisitor. You don't need to create a base class, because we know that we will not extend this code, +* You should create three `operator()` one for each cargo type: Alcohol, Fruit, Item, +* For fruit, you should draw a number between 0 and 5 and subtract such an amount of cargo +* For alcohol, you should draw a number between 0 and 10 and subtract half an amount of cargo +* For item, you should draw a number between 0 and 20 and subtract one quarter of cargo +* Visitor should be taken as a template argument by Player class +* Apply visitor whenever you successfully deal damage to ship +* Check in the main class if ship lost cargo after get damaged. \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/main.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/main.cpp new file mode 100644 index 0000000..247c2fb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/main.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +#include "Core/Time.h" +#include "Player/Enemy.h" +#include "Player/EnemyEasyDifficultLvlStrategy.h" +#include "Player/EnemyHardDifficultLvlStrategy.h" +#include "Ship/Cargo.h" +#include "Ship/Ship.h" +#include "Ship/ShipEasyDifficultLvlStrategy.h" +#include "Ship/ShipHardDifficultLvlStrategy.h" +#include "Store/Store.h" + +int main() { + Time time; + + Ship ship(&time, std::make_unique(), "Black Widow", 1000, 40); + ship.load(std::make_unique(300)); + ship.load(std::make_unique(200)); + ship.load(std::make_unique(150)); + ship.printCargo(); + + for (int i = 0; i < 10; ++i) { + ++time; + std::cout << "\nDAY: " << time.day() << "\n"; + std::cout << "crew: " << ship.crew() << '\n'; + ship.printCargo(); + } + + std::cout << "\n\n****************************************************\n"; + auto ship2 = std::make_unique(&time, std::make_unique(), "Queens Anne revenge", 1000, 40); + Enemy enemy("Enemy1", std::move(ship2), std::make_unique()); + + for (int i = 0; i < 15; ++i) { + enemy.attack(ship); + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/CMakeLists.txt new file mode 100644 index 0000000..c29ff6c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(Core) +add_subdirectory(Player) +add_subdirectory(Ship) +add_subdirectory(Store) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/CMakeLists.txt new file mode 100644 index 0000000..5db9b9a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(Core src/Time.cpp) + +target_include_directories(Core PUBLIC include) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/DifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/DifficultLvlStrategy.h new file mode 100644 index 0000000..7c23854 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/DifficultLvlStrategy.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include +#include + +template +class DifficultLvlStrategy { +public: + virtual Res handle(T&) const = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/Time.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/Time.h new file mode 100644 index 0000000..83824bd --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/Time.h @@ -0,0 +1,27 @@ +#pragma once + +class TimeObserver; + +#include +#include +#include +#include + +class Time { +public: + enum class State { + Closing, + FocusChanged + }; + + void attach(TimeObserver* observer); + void detach(TimeObserver* observer); + Time& operator++(); + size_t day() const { return _day; } + +private: + void notify() const; + + size_t _day{0}; + std::set _observers; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/TimeObserver.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/TimeObserver.h new file mode 100644 index 0000000..2bbf49e --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/include/Core/TimeObserver.h @@ -0,0 +1,6 @@ +#pragma once + +struct TimeObserver { + virtual ~TimeObserver() = default; + virtual void nextDay() = 0; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/src/Time.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/src/Time.cpp new file mode 100644 index 0000000..5faf899 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Core/src/Time.cpp @@ -0,0 +1,24 @@ +#include "Core/Time.h" + +#include "Core/TimeObserver.h" + +#include +#include + +void Time::attach(TimeObserver* observer) { + _observers.emplace(observer); +} + +void Time::detach(TimeObserver* observer) { + _observers.erase(observer); +} + +Time& Time::operator++() { + ++_day; + notify(); + return *this; +} + +void Time::notify() const { + std::ranges::for_each(_observers, std::mem_fn(&TimeObserver::nextDay)); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/CMakeLists.txt new file mode 100644 index 0000000..f96d5d4 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/CMakeLists.txt @@ -0,0 +1,8 @@ +add_library(Player src/Enemy.cpp src/EnemyEasyDifficultLvlStrategy.cpp src/EnemyHardDifficultLvlStrategy.cpp) + +target_include_directories(Player PUBLIC include) + +target_link_libraries(Player + Core + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/Enemy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/Enemy.h new file mode 100644 index 0000000..840bc42 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/Enemy.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include +#include + +class Ship; + +class Enemy { +public: + Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy); + + void attack(Ship& playerShip); + +private: + std::string _name; + std::unique_ptr _ship; + std::unique_ptr> _strategy; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..d2b7b05 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/EnemyEasyDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyEasyDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h new file mode 100644 index 0000000..ac01b4c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/include/Player/EnemyHardDifficultLvlStrategy.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include + +class Ship; + +class EnemyHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + EnemyHardDifficultLvlStrategy(); + int handle(Ship& ship) const override; + +private: + static std::random_device _rd; + mutable std::mt19937 _seed; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/Enemy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/Enemy.cpp new file mode 100644 index 0000000..fe28321 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/Enemy.cpp @@ -0,0 +1,19 @@ +#include "Player/Enemy.h" + +#include + +#include +#include +#include + +Enemy::Enemy(const std::string& name, std::unique_ptr&& ship, std::unique_ptr>&& strategy): + _name(name), _ship(std::move(ship)), _strategy(std::move(strategy)) {} + +void Enemy::attack(Ship& playerShip) { + std::cout << "Player: " << _name << " attack ship: " << playerShip.name() << '\n'; + if (const auto damage = _strategy->handle(playerShip)) { + std::cout << "Dealed damage: " << damage << '\n'; + } else { + std::cout << "Missed\n"; + } +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..0ff5663 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/EnemyEasyDifficultLvlStrategy.cpp @@ -0,0 +1,28 @@ +#include "Player/EnemyEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyEasyDifficultLvlStrategy::_rd{}; + +EnemyEasyDifficultLvlStrategy::EnemyEasyDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyEasyDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 5) { + // miss + return 0; + } else if (res > 19) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(25, 50); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/EnemyHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/EnemyHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..790cd81 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Player/src/EnemyHardDifficultLvlStrategy.cpp @@ -0,0 +1,31 @@ +#include "Player/EnemyHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +std::random_device EnemyHardDifficultLvlStrategy::_rd{}; + +EnemyHardDifficultLvlStrategy::EnemyHardDifficultLvlStrategy() + : _seed(_rd()) {} + +int EnemyHardDifficultLvlStrategy::handle(Ship& ship) const { + std::uniform_int_distribution dice(1, 20); + int multiplier = 1; + const int res = dice(_seed); + + if (res < 3) { + // miss + return 0; + } else if (res == 20) { + // turbo critical + multiplier = 3; + } else if (res > 15) { + // criticall + multiplier = 2; + } + + std::uniform_int_distribution damage(30, 60); + const int total = damage(_seed) * multiplier; + ship.takeDamage(total); + + return total; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/CMakeLists.txt new file mode 100644 index 0000000..6d03561 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library(Ship + src/Ship.cpp + src/Cargo.cpp + src/ShipEasyDifficultLvlStrategy.cpp + src/ShipHardDifficultLvlStrategy.cpp) + +target_include_directories(Ship PUBLIC include) + +target_link_libraries(Ship + Core +) diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/Cargo.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/Cargo.h new file mode 100644 index 0000000..7ed1893 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/Cargo.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include + +struct Cargo { + size_t amount; + + Cargo(size_t amount); + virtual ~Cargo() = default; + Cargo(const Cargo&) = default; + Cargo(Cargo&&) = default; + Cargo& operator=(const Cargo&) = default; + Cargo& operator=(Cargo&&) = default; + + auto operator<=>(const Cargo&) const = default; + + virtual size_t getPrice() const = 0; + virtual const std::string& name() const = 0; +}; + +struct Fruit : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Item : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; + +struct Alcohol : public Cargo { + using Cargo::Cargo; + + size_t getPrice() const override; + const std::string& name() const override; +}; \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/Ship.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/Ship.h new file mode 100644 index 0000000..e92d9c9 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/Ship.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include "Ship/Cargo.h" + +class Time; + +class Ship : public TimeObserver { +public: + enum class StatusCode { + OK, + MissingCargo, + LackOfSpace + }; + + Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew); + ~Ship(); + Ship(const Ship&) = default; + Ship(Ship&&) = default; + Ship& operator=(const Ship&) = default; + Ship& operator=(Ship&&) = default; + + // Override from TimeObserver + void nextDay() override; + + std::string& name() { return _name; } + const std::string& name() const { return _name; } + int crew() const { return _crew; } + + void printCargo() const; + + Cargo* load(std::unique_ptr&& cargo, StatusCode& code) noexcept; + void unload(const std::string& name, int amount, StatusCode& code) noexcept; + + // May throw + Cargo* load(std::unique_ptr&& cargo); + void unload(const std::string& name, int amount); + + void takeDamage(int damage); + +private: + bool unloadCargo(const std::string& cargoName, int amount); + void rebel(); + + Time* _time; + std::unique_ptr> _strategy; + std::string _name; + int _capacity; + int _crew; + int _durability{1000}; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h new file mode 100644 index 0000000..ce7627a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/ShipEasyDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipEasyDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h new file mode 100644 index 0000000..a5f6a64 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/include/Ship/ShipHardDifficultLvlStrategy.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class Ship; + +class ShipHardDifficultLvlStrategy : public DifficultLvlStrategy { +public: + bool handle(Ship& ship) const override; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/Cargo.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/Cargo.cpp new file mode 100644 index 0000000..9c55175 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/Cargo.cpp @@ -0,0 +1,31 @@ +#include "Ship/Cargo.h" + +Cargo::Cargo(size_t amount) + : amount(amount) {} + +size_t Fruit::getPrice() const { + return 20; +} + +const std::string& Fruit::name() const { + static std::string name{"Banana"}; + return name; +} + +size_t Item::getPrice() const { + return 30; +} + +const std::string& Item::name() const { + static std::string name{"Item"}; + return name; +} + +size_t Alcohol::getPrice() const { + return 40; +} + +const std::string& Alcohol::name() const { + static std::string name{"Rum"}; + return name; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/Ship.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/Ship.cpp new file mode 100644 index 0000000..2e60865 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/Ship.cpp @@ -0,0 +1,108 @@ +#include "Ship/Ship.h" + +#include + +#include +#include +#include +#include +#include + +Ship::Ship(Time* time, std::unique_ptr> strategy, const std::string& name, int capacity, int crew) + : _time(time), _strategy(std::move(strategy)), _name(name), _capacity(capacity), _crew(crew) { + if (_capacity < 0) { + throw std::runtime_error("Capacity can't be negative value!"); + } + assert(time); + _time->attach(this); +} + +Ship::~Ship() { + _time->detach(this); +} + +void Ship::nextDay() { + if (!_strategy->handle(*this)) { + rebel(); + } +} + +void Ship::printCargo() const { + std::cout << "|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT |" << '\n'; + std::cout << "|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "|\n"; + } + std::cout << "|----------------|----------------|\n"; +} + +Cargo* Ship::load(std::unique_ptr&& cargo, StatusCode& code) noexcept { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + code = StatusCode::LackOfSpace; + return _cargoes.back().get(); + } + + code = StatusCode::OK; + return nullptr; +} + +void Ship::unload(const std::string& cargoName, int amount, StatusCode& code) noexcept { + if (!unloadCargo(cargoName, amount)) { + code = StatusCode::MissingCargo; + return; + } + + code = StatusCode::OK; +} + +Cargo* Ship::load(std::unique_ptr&& cargo) { + if (_capacity > cargo->amount) { + _cargoes.push_back(std::move(cargo)); + return _cargoes.back().get(); + } + + throw std::runtime_error("Lack of space"); +} + +void Ship::unload(const std::string& cargoName, int amount) { + if (!unloadCargo(cargoName, amount)) { + throw std::runtime_error("Missing cargo"); + } +} + +void Ship::takeDamage(int damage) { + _durability -= damage; + if (_durability <= 0) { + std::cout << "Ship: " << _name << " was destroyed!\n"; + } +} + +bool Ship::unloadCargo(const std::string& cargoName, int amount) { + while (amount != 0) { + auto it = std::ranges::find_if(_cargoes, [&cargoName](const auto& cargo) { return cargo->name() == cargoName; }); + if (it != _cargoes.end()) { + if ((*it)->amount > amount) { + (*it)->amount -= amount; + return true; + } + amount -= (*it)->amount; + _cargoes.erase(it); + } else { + return false; + } + } + + return true; +} + +void Ship::rebel() { + std::cout << "\n********** Crew rebel **********\n"; + _crew -= 5; + + if (_crew < 5) { + throw std::runtime_error("There is not enought crew! Game over!"); + } +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp new file mode 100644 index 0000000..6d1a148 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/ShipEasyDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipEasyDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipEasyDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/ShipHardDifficultLvlStrategy.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/ShipHardDifficultLvlStrategy.cpp new file mode 100644 index 0000000..2a6815a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Ship/src/ShipHardDifficultLvlStrategy.cpp @@ -0,0 +1,12 @@ +#include "Ship/ShipHardDifficultLvlStrategy.h" + +#include "Ship/Ship.h" + +bool ShipHardDifficultLvlStrategy::handle(Ship& ship) const { + Ship::StatusCode status; + ship.unload("Rum", ship.crew(), status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew(), status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/CMakeLists.txt new file mode 100644 index 0000000..30a4ee2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(Store src/Store.cpp) + +target_include_directories(Store PUBLIC include) + +target_link_libraries(Store + Ship +) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/include/Store/Store.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/include/Store/Store.h new file mode 100644 index 0000000..1eb93cb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/include/Store/Store.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Ship/Cargo.h" + +#include +#include +#include +#include +#include + +class Store : public TimeObserver { +public: + explicit Store(std::vector>&& cargos); + + // Override from TimeObserver + void nextDay() override; + + size_t getTotalPrice() const; + std::vector getCargosName() const; + void printCargo() const; + +private: + static std::random_device _rd; + std::mt19937 _seed; + std::vector> _cargoes; +}; diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/src/Store.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/src/Store.cpp new file mode 100644 index 0000000..41d32fe --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/src/Store/src/Store.cpp @@ -0,0 +1,48 @@ +#include "Store/Store.h" + +#include +#include + +Store::Store(std::vector>&& cargos) + : _seed(_rd()), _cargoes(std::move(cargos)) {} + +std::random_device Store::_rd{}; + +void Store::nextDay() { + std::uniform_int_distribution dist(-50, 50); + for (auto& cargo : _cargoes) { + cargo->amount += dist(_seed); + } +} + +size_t Store::getTotalPrice() const { + size_t value{0}; + for (const auto& cargo : _cargoes) { + value += cargo->getPrice(); + } + + return value; +} + +std::vector Store::getCargosName() const { + std::vector names; + for (const auto& cargo : _cargoes) { + names.push_back(cargo->name()); + } + + return names; +} + +// Shame on me, I copy-past it. But remember DRY!! +// This is only to speed up process of creating exercises, sorry :) +void Store::printCargo() const { + std::cout << "|----------------|----------------|----------------|\n"; + std::cout << "| NAME " + << "| AMOUNT " + << "| PRICE |" << '\n'; + std::cout << "|----------------|----------------|----------------|\n"; + for (const auto& cargo : _cargoes) { + std::cout << "|" << std::setw(15) << std::left << cargo->name() << " | " << std::setw(15) << cargo->amount << "| " << std::setw(15) << cargo->getPrice() << "|\n"; + } + std::cout << "|----------------|----------------|----------------|\n"; +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/CMakeLists.txt b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/CMakeLists.txt new file mode 100644 index 0000000..7dea033 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.11.0 +) +FetchContent_MakeAvailable(googletest) +add_library(GTest::GTest INTERFACE IMPORTED) +target_link_libraries(GTest::GTest INTERFACE gtest_main) +target_link_libraries(GTest::GTest INTERFACE gmock_main) + +add_executable(SHM_Tests ShipUnitTests.cpp StoreUnitTests.cpp) + +target_link_libraries(SHM_Tests + PRIVATE + GTest::GTest + Ship + Store) + +add_test(shm_gtests SHM_Tests) \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/CargoMock.h b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/CargoMock.h new file mode 100644 index 0000000..10044a1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/CargoMock.h @@ -0,0 +1,3 @@ +#pragma once + +// Write mock here \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/ShipUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/ShipUnitTests.cpp new file mode 100644 index 0000000..da29d07 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/ShipUnitTests.cpp @@ -0,0 +1,9 @@ +#include "Ship/Cargo.h" +#include "Ship/Ship.h" + +#include + +TEST(ShipTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/StoreUnitTests.cpp b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/StoreUnitTests.cpp new file mode 100644 index 0000000..0562132 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/exercises/visitor/tests/StoreUnitTests.cpp @@ -0,0 +1,11 @@ +#include "CargoMock.h" +#include "Ship/Cargo.h" +#include "Store/Store.h" + +#include +#include + +TEST(StoreTests, ShouldCreate) +{ + +} diff --git a/CreatingReliableSoftwareCpp/Presentation/good_practise_dry.md b/CreatingReliableSoftwareCpp/Presentation/good_practise_dry.md new file mode 100644 index 0000000..49ec54f --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/good_practise_dry.md @@ -0,0 +1,39 @@ +# DRY +___ + +## Don't Repeat Yourself + +* If you have two identical functionality, like searching for some resource, just make a separate function and always call it +* Create some utility files, which will contain functions reused in your project +* It could contain functions for: + * Filtering + * Searching + * Removing + * Operate with string etc... +* If you have some value/ string which is used in few places, create one constexpr variable for it and always refer to this variable instead magic value/ string +___ + +```C++ +class Foo { + constexpr static const char* DATABASE_NAME = "superDb"; + constexpr static size_t CONNECTION_TIMEOUT_SECONDS = 30; + +public: + // Don't hardcode value here, instead of create a variable which store this info + void connect(const std::string& dbName = DATABASE_NAME) { + db.createConnection(dbName, CONNECTION_TIMEOUT_SECONDS); + } + + bool connected() const { return db.connected(); } + + json sendRequest(const json& request) { + // use already implemented functions + if (!connected()) { + connect(); + } + + return db.request(request); + } +}; +``` + \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/good_practise_intro.md b/CreatingReliableSoftwareCpp/Presentation/good_practise_intro.md new file mode 100644 index 0000000..9ed5cfb --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/good_practise_intro.md @@ -0,0 +1,200 @@ +# Good practise +___ + +## What can we do better? + +```C++ +class Bar { +public: + void doSth() { + std::cout << "Do sth\n"; + } +}; + +class Foo { +public: + Foo(std::string name) { + name_ = name; + bar_ = new Bar(); + } + + ~Foo() { + delete bar_; + bar_ = nullptr; + } + + void printName() { + std::cout << name_ << std::endl; + } + + void doSth() { + bar_->doSth(); + } + +private: + Bar* bar_; + std::string name_; +}; +``` + + +___ + +## Refactor + +```C++ +class Bar { +public: + // doSth should be cont method because only print sth + void doSth() { + std::cout << "Do sth\n"; + } +}; + +class Foo { +public: + // Get string by const& or use std::move when initialize + Foo(std::string name) { + // Use initialization list, instead assign inside C'tor + name_ = name; + // Avoid usage of new, use unique_ptr and make_unique method + // Class Foo will be hard to test because we can't substitute bar objects! + // Bar should be provided in C'tor -> Dependency injection + bar_ = new Bar(); + } + + ~Foo() { + // Unneccessary, because we should use unique_ptr + delete bar_; + bar_ = nullptr; + } + + // Should be const method + void printName() { + // better use '\n' then endline + // If you need to flush stream use flush() method + std::cout << name_ << std::endl; + } + + // If there is a risk, that ptr may by empty, we shoudl validate it + void doSth() { + bar_->doSth(); + } + +private: + Bar* bar_; + std::string name_; +}; +``` + + +___ + +## What can we do better? + +```C++ +class Screen { +public: + Screen(int height, int width) : height_(height), width_(width) {} + Screen(int size) : height_(size), width_(size) {} + ~Screen() {} + + void print(const std::vector& numbers) { + int current = 0; + for (int j = 0; j < width_; ++j) { + std::cout << "_"; + } + std::cout << '\n'; + + for (int i = 0; i < height_; ++i) { + std::cout << "|"; + for (int j = 0; j < width_; ++j) { + int num_width = std::to_string(numbers[current]).size(); + std::cout << numbers[current]; + j += num_width; + if (j < width_) { + std::cout << ' '; + } + ++current; + } + std::cout << "|\n"; + } + + for (int j = 0; j < width_; ++j) { + std::cout << "_"; + } + std::cout << '\n'; + } +private: + Screen() {} + int height_; + int width_; +}; +``` + + +___ + +## Refactor + +```C++ +class Screen { +public: + // Use alias -> using Height = int + Screen(int height, int width) + : height_(height), width_(width) {} + // User probably not expect square screen when initialize with one value + // C'tor with on argument should be mark as explicit. + Screen(int size) + : height_(size), width_(size) {} + // Not needed here, we also break rule of 5 + ~Screen() {} + + // Should be const method + void print(const std::vector& numbers) { + int current = 0; + + // This function repeat twice. + // Should be separate function like print underscore + for (int j = 0; j < width_; ++j) { + // This is not efficient better use std::cout << string(width_, '_') << '\n'; + std::cout << "_"; + } + std::cout << '\n'; + + // This is hard to understand. There is already implemented stream mainpulators + // like which allow to use `setw` to describe the width ov vlaue + // or added in c++20 std::format() + for (int i = 0; i < height_; ++i) { + std::cout << "|"; + for (int j = 0; j < width_; ++j) { + // should be const + int num_width = std::to_string(numbers[current]).size(); + std::cout << numbers[current]; + // do sth with `j` which should be handled by for loop + j += num_width; + if (j < width_) { + std::cout << ' '; + } + ++current; + } + std::cout << "|\n"; + } + + // DRY - do not repeat yourself + for (int j = 0; j < width_; ++j) { + std::cout << "_"; + } + std::cout << '\n'; + } + +private: + // By default when we create at least one C'tor, the compiler will not add a default one + Screen() {} + + int height_; + int width_; +}; +``` + + diff --git a/CreatingReliableSoftwareCpp/Presentation/good_practise_kiss.md b/CreatingReliableSoftwareCpp/Presentation/good_practise_kiss.md new file mode 100644 index 0000000..c26547c --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/good_practise_kiss.md @@ -0,0 +1,38 @@ +# KISS +___ + +## keep it simple, stupid + +* Writing only enough code to pass a unit test, which relates to TDD and the first SOLID principle – Single Responsibility. +* Intuitive approaches to writing software, approachable and simply understood by everyone. +___ + +## Small understandable classes and methods + +```C++ +class ImageStorage { +public: + Image* getImage(const std::string& url) { + const auto it = std::ranges::find_if(images_, [&url](const auto& image) { return image->url() == url; }); + if (it != std::cend(images_)) { + return it->get(); + } + + return nullptr; + } + + Image* store(std::unique_ptr&& image) { + if (const auto* image = getImage()) { + return image; + } + + images_.push_back(std::move(image)); + return images_.back().get(); + } + +private: + std::vector> images_; +}; +``` + + \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/good_practise_solid.md b/CreatingReliableSoftwareCpp/Presentation/good_practise_solid.md new file mode 100644 index 0000000..b21f762 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/good_practise_solid.md @@ -0,0 +1,896 @@ +# SOLID +___ + +## S - Single resposibility (1) + +* A class (module) should has one and only one reason to change, meaning that a class should has only one job. +* When we create a class UrlDownloader it shouldn't: + * Make a connection -> We should have separate object for that like TcpConnection + * Display it -> We should have separate class to do that like Canvas + * Modifying the response, like resizing, it should be done by other class like Bitmap + * Cache it -> We should have other class to store cached images like FileCache + +* Advantages: + * Testing – A class with one responsibility will have far fewer test cases. + * Lower coupling – Less functionality in a single class will have fewer dependencies. + * Organization – Smaller, well-organized classes are easier to search than monolithic ones. +___ + +## S - Single resposibility (2) + +Let's say we want to create a program that downloads images and allows us to display them. In such cases we need to implement a few classes that are responsible for: + +* Download object: Create request, make connection, download raw bits + * ImageDownloader, TcpConnection class +* Image representation: Store info about image, bitmap, allow to modify it. + * Image, Bitmap class +* User interface: Print object + * Canvas, Window class +* Cache: Cache object for future usage and quick access + * FileStorage, RamStorage class + +___ + +## S - Single resposibility (3) + +* By following of rule one responsibility let's create Image class. This class will be responsible for: + * Store bitmap + * Store url of image to allow identification of it + * This class don't need to know how to display an image or resize it + +```C++ +class Image { +public: + Image(const std::string& url, const Bitmap& bitmap) + : url_(url), bitmap_(bitmap) {} + Image(const std::string& url, Bitmap&& bitmap) + : url_(url), bitmap_(std::move(bitmap)) {} + + const Bitmap& bitmap() const { return bitmap_; } + const std::string& url() const { return url_; } + +private: + std::string url_; + Bitmap bitmap_; +}; +``` + + +___ + +Let's create a second class form this module, a Bitmap class. This class will be responsible for: +* Store raw bit +* Store type of image (png, jpg) +* Store additional info about image like: size, name, url etc... + +```C++ +class Bitmap { +public: + struct Pixel { uint8_t r; uint8_t g; uint8_t b; } + enum class Type { PNG, JPG }; + + Image(size_t width, size_t height, Type type, std::vector bits) + : _width(width), _height(height), _type(type), _bits(std::move(bits)) {} + + void resize(size_t newWidth, size_t newHeight) { ... } + void mirrorReflection() { ... } + // getter like, witdh(), height() etc... + +private: + size_t _width; + size_t _height; + Type _type; + std::vector _bits; +}; +``` + + +* Does Bitmap follow the rule of single responsibility? + +___ + +Bitmap keeps info about types of images. So when we want to support a new type of image we need to modify the Image class and also modify the methods, like resize, mirror reflection, etc... because each type of image may be formatted differently. That's why we should separate this class. I will show it later when we will talk about other rules from SOLID. +___ + +Let's create a second module, which will be resposible for printing image. We starts from a Canvas class. This class will be responsible for: +* Knowing how to display bitmap +* Store additional info about canvas like: size +* This class don't need to know how to transform image or resize it. + +```C++ +class Canvas { +public: + Canvas(size_t width, size_t height) : _width(width), _height(height) {} + + void print(const Image& img) const { + const auto& bitmap = img.bitmap(); + const size_t imgWidth = bitmap.width(); + const size_t imgHeight = bitmap.height(); + if (imgWidth > _width || imgHeight > _height) { + bitmap.resize(_width, _height); + } + .... + } + +private: + size_t _width; + size_t _height; +}; + +``` + + +* Does Canvas follow the rule of single responsibility? + + +___ + +Once again no. Canvas is tightly coupled with types of images, so whenever we add a new type of image we need to rewrite methods. As you can see, one change (adding a new type of image) demands a user to reimplement the `Canvas` class and `Bitmap` class and probably even more classes. This is not a single responsibility. The class should have one reason to change and now class has a lot of reasons to change. Once again I will show you later how we can resolve it. + +___ + +## Single resposibility in STL + +SOLID is also valid for static polymorphism. As an example, we can check the STL algorithm like `std::stransform`. + +```C++ +template +OutputIt transform(InputIt first1, InputIt last1, + OutputIt d_first, UnaryOp unary_op) +{ + while (first1 != last1) + *d_first++ = unary_op(*first1++); + + return d_first; +} +``` + +Transform performs one operation e2e: iterate through the input range, call `unary_op`, and store the result in an output range. + +___ + +## Single resposibility in STL (2) + +Another example will be a `std::vector` class. The whole interface is strongly connected with a dynamically allocated array, we can: + +* push_back +* insert +* erase + +But we don't have methods like: + + +* sort +* unique +* transform + +Now you can understand, why `std::remove` is a separate method, because `std::vector` should not have logic, on how to rearrange an array, because this is not a part of its responsibility. `std::vector` gives us an interface only for adding, modifying, and removing elements form a dynamic array and STL algorithm give us a possibility to rearrange the container. + + +___ + +## Breaking single resposibility in STL + +Two years after the STL library was introduced we get also a `std::string` which is not a good example of a single responsibility. You may ask why. Let's check `std::string` interface: + +* find +* rfind +* find_first_of +* find_first_not_of +* find_last_of +* find_last_not_of +* replace +* substr +* contains +* starts_with +* ends_with + +I know that this is handy, to have such a method I use them a lot. But this is not exactly a doo design. Because every standard when `std::string` grows, the authors of libraries need to modify the class and can introduce new bugs, so development of this class is hard. + + +___ + +## std::list + +You can argue that `std::list` also has a specific functions like: + +* merge +* splice +* remove +* remove_if +* reverse +* unique +* sort + +yes, I agree, but these functions are specified to the type. `std::list` can perform these operations differently, because we may manipulate the pointers. This is an implementation of detail for `std::list`. STL algorithm should not know that this specific iterator should be treated differently. + + +___ + +## Single resposibility - summary + +In short words, we can say that a single responsibility means that a function, class, or module should not implement details of two orthogonal issues. +___ + +## O - Open-Closed (1) + +* Objects or entities should be open for extension but closed for modification. + * When we add new functionality or a new type our changes should not require modification of existing code + * When we have a bug in code of course we need to modify it :) + +* Advantages: + * We stop ourselves from modifying existing code and causing potential new bugs + * We mainly focus on adding new functionalities rather than modifying code to fit the new version + +___ + +## O - Open-Closed (2) + +We implemented an Image class which stores information about `URL` and `bitmap`, now we have a task that we should also have the possibility to store info about: +* date when the image was downloaded +* date when the image was created originally +* a place where the image was taken + +We need to ask ourselves whether these pieces of information are required for the existing code or not. + +* If required, we probably need to modify the class `Image` extend an interface, and add new fields. But still, this is done by extension of already implemented code. Other classes still compile, and we decide which class should use a new functionality. +* If not required, we probably need to add a derived class that will have this information and use it in a new part of the code that we implement. So we don't modify anything from existing code, we just add a new functionality. + + +```C++ +class DescribedImage : public Image { +public: + DescribedImage(const std::string& url, const Bitmap& bitmap, DateTime downloadTime, DateTime imageTime, Coordinate place): + Image(url, bitmap), _downloadTime(downloadTime), _imageTime(imageTime), _place(place) {} + + const DateTime& downloadTime() const { return _downloadTime; } + const DateTime& imageTime() const { return _imageTime; } + const Coordinate& place() const { return _place; } + +private: + DateTime _downloadTime; + DateTime _imageTime; + Coordinate _place; +}; +``` + + +___ + +## O - Open-Closed (3) + +When we depend on interfaces, we can also add easily new functionality by extending them. Unfortunately, if other classes inherit from these interfaces we need to write the implementation for each class, which requires modification of existing code and we break the rule `open-close` in such a way we need to think about what will change often: types or methods? + +* If types we can use for instance a strategy design pattern, because when we create a new type, we need to create a new strategy only for this type and we don't modify anything in existing code. +* If methods we can use for instance a visitor design pattern, because when we add a new method, we need to implement a visit method only for this new one, but the existing code doesn't change. +* I will describe more these patterns in the section design patterns + +___ + +## Visitor + +```C++ +class BitmapVisitor { +public: + virtual ~BitmapVisitor() = default; + + virtual void visit(JPGImage& image, /* other args */) = 0; + virtual void visit(PNGImage& image, /* other args */) = 0; + virtual void visit(GIFImage& image, /* other args */) = 0; + virtual void visit(SVGImage& image, /* other args */) = 0; +}; +``` + +```C++ +class ResizeVisitor : public BitmapVisitor { +public: + void visit(JPGImage& image, /* other args */) override; + void visit(PNGImage& image, /* other args */) override; + void visit(GIFImage& image, /* other args */) override; + void visit(SVGImage& image, /* other args */) override; +}; + +class RotateVisitor : public BitmapVisitor { +public: + void visit(JPGImage& image, /* other args */) override; + void visit(PNGImage& image, /* other args */) override; + void visit(GIFImage& image, /* other args */) override; + void visit(SVGImage& image, /* other args */) override; +}; +``` + + +___ + +## Strategy + +```C++ +class DownloadStrategy { +public: + virtual ~DownloadStrategy() = default; + using DownloadToVectorCallback = std::function&&)>; + using DownloadToStringCallback = std::function; + using DownloadToFileCallback = std::function; + + virtual void downloadToVector(const std::string& url, DownloadToVectorCallback callback) = 0; + virtual void downloadToString(const std::string& url, DownloadToStringCallback callback) = 0; + virtual void downloadToFile(const std::string& url, const std::string& path, DownloadToFileCallback callback) = 0; +}; +``` + +```C++ +class ImageDownloadStrategy : public DownloadStrategy { +public: + void downloadToVector(const std::string& url, DownloadToVectorCallback callback) override; + void downloadToString(const std::string& url, DownloadToStringCallback callback) override; + void downloadToFile(const std::string& url, const std::string& path, DownloadToFileCallback callback) override; +}; + +class VideoDownloadStrategy : public DownloadStrategy { +public: + void downloadToVector(const std::string& url, DownloadToVectorCallback callback) override; + void downloadToString(const std::string& url, DownloadToStringCallback callback) override; + void downloadToFile(const std::string& url, const std::string& path, DownloadToFileCallback callback) override; +}; +``` + + +___ + +## STL + +As previously for static polymorphism, we also should apply SOLID principles. Once more look at the STL library. It was designed for the open-close principle. because if we want to add new behavior like a new algorithm we don't need to modify anything from the STL library. So every user can do that in its code. For instance, I want to implement the `tranform_if` method: + +```C++ +template +OUT transform_if(IN first, IN last, OUT out, PRED pred, FUN fun) { + while (first != last) { + if (pred(*first)) { + *out = fun(*first); + } else { + *out = *first; + } + ++out; + ++first; + } + + return out; +} +``` + + +___ + +## Open-close summary + +Adding new functionality or types to your code should not require modification of existing code. + +___ + +## L - Liskov Substitution + +* Subtypes must be substitutable for their base types. + * Preconditions cannot be strengthened in a substitute + * Postconditions cannot be weakened in a substitute + * Invariants of the super type must be preserved in a substitute + +* Advantages: + * We will not be surprised when we use a method from the interface that exists with an exception or will be empty + * We will not be surprised when a method acts weird in terms of validation of input data or output data + * Our code will be readable and reliable +___ + +## Shapes + +Let's put aside the topic of downloading for a moment. I want to show you an another example. The problem with Shapes. This is common example, so you can saw it before on the internet. I have one question for you, **which example A or B is the correct one**? + +
+
+ +**A** + +```C++ +class Square { +public: + virtual void setWidth(int); + virtual int getArea() const; +protected: + int width{}; +}; + +class Rectangle : public Square { +public: + virtual void setHeight(int); + int getArea() const override; +private: + int height{}; +}; +``` +
+ +
+ +**B** + +```C++ +class Rectangle { +public: + virtual void setWidth(int); + virtual void setHeight(int); + virtual int getArea() const; +private: + int width{}; + int height{}; +}; + +class Square : public Rectangle { +public: + int getArea() const override; +}; +``` +
+ +
+ + +___ + +The answer is A, because in B we need to modify invariants. Let's check the following code form example B: + +```C++ +class Square : public Rectangle { +public: + void setWidth(int width) override { + // This shape needs to be a square, so both width and height need to be the same + _width = width; + _height = width; + } + void setHeight(int height) override { + // This shape needs to be a square, so both width and height need to be the same + _width = height; + _height = height; + } + int getArea() const override; +}; +``` + +I hope you see, why this is not a good example of inheritance, and option A is much better. + + +___ + +## L - Liskov Substitution + +Backing to previous example. Now we want to implement a downloader class, that allow to fetch any type of data to `std::vector`. + +```C++ +class Downloader { +public: + using Callback = std::function&&)>; + + virtual void downloadToVector(const std::string& url, Callback callback) { + if (!validateUrl(url)) { + throw InvalidUrlException(url); + } + startDownloading(url, callback); + } + +private: + bool validateUrl(const std::string& url); + void startDownloading(const std::string& url, Callback callback); +}; +``` + + +Now we want to add also a downloader that loads data not from the network, but from the database. We use the same virtual function, because `URL` we can treat as the destination of the table (eg: "databaseName.tableName"). So we implement a new class: + + +```C++ +class DatabaseDownloader : public Downloader { +public: + virtual void downloadToVector(const std::string& tableName, Callback callback) { + // Check if user provide string: databaseName.tableName + if (!validatePath(url)) { + throw InvalidTableNameException(tableName); + } + startDownloading(tableName, callback); + } +} +``` + + +___ + +## L - Liskov Substitution (2) + +In the previous example, we once again modify invariants. A programmer who has used the `Downloader` class for a long time, will be sure, that he needs to provide a valid `URL` address. He will be very surprised when he will get an exception `InvalidTableNameException`. This is a dangerous situation because sometimes we may not see such an exception and the program will continue with invalid data. The wrong output will show later, so finding a root cause may be hard and for a few days, we will greet with debugger. + +Another bad example will be when we change the preconditions: + + +```C++ +class Ship { +public: + virtual StatusCode sail() { + if (_numberOfCrew < 20) { + return StatusCode::NotEnoughSailor; + } + if (_cargoWeight > _maxLoad) { + return StatusCode::ShipOverloaded; + } + return StatusCode::OK; + } +}; + +class WarShip : public Ship { + StatusCode sail() override { + // Weakened precondition (previously was 20) + if (_numberOfCrew < 15) { + return StatusCode::NotEnoughSailor; + } + // Strengthened precondition (now we need also add the weight of cannons) + if (_cargoWeight + _cannonsWeight > _maxLoad) {s + return StatusCode::ShipOverloaded; + } + return StatusCode::OK; + } +}; +``` + + +___ + +You may ask me: Why our ship can't behave differently? How can I develop a few types of ships? The answer is easy, just create an interface for the ship, and then in implementation, you will provide a different behavior, but if you inherit from an already implemented class you should not break the Liskov substitution, because we don't know which class will be under the pointer or reference. We have assumption that a base class has some prediction, so we need to satisfy them ant everything should work. + +```C++ +class Ship { +public: + virtual ~Ship(); + virtual StatusCode sail() = 0 +}; + +class CargoShip : public Ship { +public: + StatusCode sail() override { + if (_numberOfCrew < 20) { + return StatusCode::NotEnoughSailor; + } + if (_cargoWeight > _maxLoad) { + return StatusCode::ShipOverloaded; + } + return StatusCode::OK; + } +}; + +class WarShip : public Ship { + StatusCode sail() override { + if (_numberOfCrew < 15) { + return StatusCode::NotEnoughSailor; + } + if (_cargoWeight + _cannonsWeight > _maxLoad) {s + return StatusCode::ShipOverloaded; + } + return StatusCode::OK; + } +}; +``` + + + +___ + +## STL once more + +Let's check `std::transform` method. This algorithm satisfies all rules o far: **S**ingle responsibility, **O**pen-close and also a **L**iskov substitution. The **L** letter is supported, because we have a class `InputIt` and `OutputIt` which need to provide interface like: `operator++`, `operator*`, `operator=` and `operator!=`. We demand from a provided class that these 4 methods need to be implemented. On the other case, the code will not compile. Still, we may provide the wrong implementation, but this is our bug, not a bug from `transform` method. + +```C++ +template +OutputIt transform(InputIt first1, InputIt last1, + OutputIt d_first, UnaryOp unary_op) +{ + while (first1 != last1) + *d_first++ = unary_op(*first1++); + + return d_first; +} +``` + + +___ + +## Liskov Substitution - summary + +Make sure that inheritance is about behavior not about data + +___ + +## I - Interface Segregation + +* A client should never be forced to implement an interface that it doesn’t use, or clients shouldn’t be forced to depend on methods they do not use +    * When we inherit from some interface, we should always implement all methods +    * There is an exception. This may be hard to achieve this for the observer class, because sometimes we need to know only about one event, but other classes could need more events, That is why we do not always implement all of them. +* Advantages: +    * We will not be surprised when we use a method from interface which exits with an exception or will be empty +    * We don't need to worry about how to implement a function which don't make a sense for new class +    * We will follow a rule: Single responsibility. Actually, this is a special case of single responsibility. +___ + +## I - Interface Segregation (2) + +Let's rewrite the interface for the `Download` class to have only one function download, and create classes that will be responsible for downloading a specific type of info, like Image. + + +```C++ +class Downloader { +public: + using DownloadImageFinishedCallback = std::function; + using DownloadCertificateFinishedCallback = std::function; + using DownloadAudioFinishedCallback = std::function; + using DownloadVideoFinishedCallback = std::function; + + virtual ~Downloader() = default; + + virtual void downloadImage(const std::string& url, DownloadImageFinishedCallback callback) = 0; + virtual void downloadCertificate(const std::string& url, DownloadCertificateFinishedCallback callback) = 0; + virtual void downloadAudio(const std::string& url, DownloadAudioFinishedCallback callback) = 0; + virtual void downloadVideo(const std::string& url, DownloadVideoFinishedCallback callback) = 0; +}; +``` + + +```C++ +class Downloader { +public: + using DownloadCallback = std::function&)>; + + virtual ~Downloader() = default; + + virtual void download(const std::string& url, DownloadCallback cb) = 0; +}; +``` + + +___ + +## I - Interface Segregation (3) + +Now depending on what we want to download we can use one of the implementations. Of course, we lose a multitasking object which knows how to download everything, but do we need it? Based on the first rule `single responsibility` is better to have a simpler class. + +```C++ +class ImageDownloader : public Downloader { +public: + void download(const std::string& url, DownloadCallback cb) { + // download an image + } +}; +``` + +```C++ +class VideoDownloader : public Downloader { +public: + void download(const std::string& url, DownloadCallback cb) { + // download a video + } +}; +``` + +___ + +## I - Interface Segregation (4) + +Ok, but what if I need all of these 4 methods. In such case I need to provide 4 object co C'tor of class, to be able to download everything. + +```C++ +class UrlDownloader { + UrlDownloader(ImageDownloader* imageDownloader, + VideoDownloader* videoDownloader, + CertificateDownloader* certificateDownloader, + AudioDownloader* audioDownloader) {} +}; +``` + + +Yes, but this is ok. Usual, classes will not need to all downloader objects. For instance, a `Player` will need to have only 2 objects: `AudioDownloader` and `VideoDownloader`. Another class like `Canvas` will need to have only `ImageDownloader` and `SecurityManager` will need to download only a certificate. Having smaller classes allows us to provide only the functionality that we need. We can also achieve the same by splitting the interface of `Downloader` into 4 smaller classes, instead of having one function `download`. This depends on what will be more useful in our case. **Noticed that this is what I show you earlier → a strategy design pattern**. + + +___ + +```C++ +class ImageDownloader { +public: + using DownloadImageFinishedCallback = std::function; + virtual void downloadImage(const std::string& url, DownloadImageFinishedCallback callback) = 0; +}; +class VideoDownloader { +public: + using DownloadVideoFinishedCallback = std::function; + virtual void downloadVideo(const std::string& url, DownloadVideoFinishedCallback callback) = 0; +}; +class CertificateDownloader { +public: + using DownloadCertificateFinishedCallback = std::function; + virtual void downloadCertificate(const std::string& url, DownloadCertificateFinishedCallback callback) = 0; +}; +class AudioDownloader { +public: + using DownloadAudioFinishedCallback = std::function; + virtual void downloadAudio(const std::string& url, DownloadAudioFinishedCallback callback) = 0; +}; +``` +```C++ +class Player { +public: + Player(AudioDownloader* audioDownloader, VideoDownloader* videoDownloader): + _audioDownloader(audioDownloader), _videoDownloader(videoDownloader) {} + + void playAudio(const std::string& url) { + _audioDownloader->downloadAudio(url, [](Result res, const Audio* audio){ OnAutioDownloaded(res, autio); }); + } + + void playAudio(const std::string& url) { + _videoDownloader->downloadVideo(url, [](Result res, const Audio* audio){ OnAutioDownloaded(res, autio); }); + } +} +``` + + +___ + +## STL example + +Of course, I can't leave this principle without show you that STL library also follow this rule. Once more, check `std::transform` class. We have two types of interface: `InputIt` and `OutputIt`. Each iterator require something else. `Input` is use for reading and `output` for store values. That's why we can for instance `read` form a file or `write` to the file. If we use only one interface, `read-write` we will lose this possibility, because `input_iterator` can't store a value the same as `output-iterator` can't read a value. Keeping 2 interfaces, we allow to read and write from anything. + +```C++ +template +OutputIt transform(InputIt first1, InputIt last1, + OutputIt d_first, UnaryOp unary_op) +{ + while (first1 != last1) + *d_first++ = unary_op(*first1++); + + return d_first; +} +``` + + + +___ + +## Interface segregation - summary + +Make sure interfaces don't include unnecessary dependencies + +___ + +## D - Dependency Inversion + +* High-level modules should not depend on low-level modules. Both should depend on abstraction +* Abstraction should not depend on details. Details should depend on abstraction +* In other words this principle allows for decoupling classes +* Advantages: + * We can easy substitute object of any other class which inherit from the same interface + * This also makes testing easy, because we can mock each object! + +___ + +## D - Dependency Inversion (2) + +Take a look for a `Player` class which has two functions and two members and allow to `playAudio` or `palyVideo`. + +```C++ +class Player { +public: + void playAudio(const std::string& url) { + _audioDownloader.downloadAudio(url, [](Result res, const Audio* audio) { + OnAudioDownloaded(res, autio); }); + } + + void playVideo(const std::string& url) { + _videoDownloader.downloadVideo(url, [](Result res, const Video* video) { + OnVideoDownloaded(res, video); }); + } + +private: + Mp3AudioDownloader _audioDownloader; + Mp4VideoDownloader _videoDownloader; +} +``` + + + +At first sight, everything still works correctly. We can download everything as previously. But what happens when we want to test this class by using mock? Or instead of `Mp3AudioDownloader`, we want to provide `WAVAudioDownloader`, **Creating objects inside class tightly coupled these classes together!**. + +___ + +## D - Dependency Inversion (3) + +High level module should not depend on low level module and vice versa. Both should depends on abstraction: + +```C++ +class Player { +public: + Player(AudioDownloader* audioDownloader, VideoDownloader* videoDownloader) + : _audioDownloader(audioDownloader), _videoDownloader(videoDownloader) {} + + void playAudio(const std::string& url) { + _audioDownloader.downloadAudio(url, [](Result res, const Audio* audio) { OnAudioDownloaded(res, autio); }); + } + + void playVideo(const std::string& url) { + _videoDownloader.downloadVideo(url, [](Result res, const Video* video) { OnVideoDownloaded(res, video); }); + } + +private: + AudioDownloader* _audioDownloader; + VideoDownloader* _videoDownloader; +}; + +int main() { + WAVAudioDownloader wavDownloader; + MP4VideoDownloader mp4Downloader; + Player(&wavDownloader, &mp4Downloader); +} +``` + +___ + +## UML + +Dependency inversion +___ + +## MVC + +The good example of dependency inversion is model-view-controller. Model should be a high level because it will rarely be modified, but view and controller should be on low level because we will continuously add new functionality there. Additionally, we should create two interfaces in the module, one for communication with Controller and the second for communication with view. + +MVC + +___ + +## STL for the fifth time + +And let's back to the `std::transform` method. We represent a set of requirements for each iterator. We demand comparison, dereference etc… if we provide such implementation it will work. Furthermore, we don't need to know what exactly is passed here until it satisfies interface. So `transform` don't depend on low level modules we need to satisfy interface to compile the code. + +```C++ +template +OutputIt transform(InputIt first1, InputIt last1, + OutputIt d_first, UnaryOp unary_op) +{ + while (first1 != last1) + *d_first++ = unary_op(*first1++); + + return d_first; +} +``` + + +___ + +## Dependency inversion - summary + +Prefer depend on abstraction instead of concrete types. + +___ + +## Exercise + +* Open project streamer +* Create Mpeg2Streamer class +* Create MjpegStreamer class +* Allow to add data to stream +* Allow to add/remove receivers +* start/stop stream +* validate data +* Make some dummy implementations that will allow to compile code + +```C++ +virtual bool addReceiver(const StreamInfo& info) = 0; +virtual bool removeReceiver(const StreamInfo& info) = 0; +virtual bool addData(const std::vector& data) = 0; +virtual bool validateData(const std::vector& data) const = 0; +virtual bool startStream() = 0; +virtual bool stopStream() = 0; +virtual bool streamInProgress() const = 0; +``` + + \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/images/MVC.png b/CreatingReliableSoftwareCpp/Presentation/images/MVC.png new file mode 100644 index 0000000..a6aa821 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/MVC.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/acyclic_visitor_bench.png b/CreatingReliableSoftwareCpp/Presentation/images/acyclic_visitor_bench.png new file mode 100644 index 0000000..35ea8b7 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/acyclic_visitor_bench.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/acyclic_visitor_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/acyclic_visitor_uml.png new file mode 100644 index 0000000..6c17e9f Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/acyclic_visitor_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml.png new file mode 100644 index 0000000..2fef9b5 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml2.png b/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml2.png new file mode 100644 index 0000000..4e0de07 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml2.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml3.png b/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml3.png new file mode 100644 index 0000000..71a2926 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/ammunition_uml3.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/benchmark_observer.png b/CreatingReliableSoftwareCpp/Presentation/images/benchmark_observer.png new file mode 100644 index 0000000..096227c Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/benchmark_observer.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/builder_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/builder_uml.png new file mode 100644 index 0000000..10ece2f Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/builder_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/command_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/command_uml.png new file mode 100644 index 0000000..0ffe15e Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/command_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/decorator_cargo__uml.png b/CreatingReliableSoftwareCpp/Presentation/images/decorator_cargo__uml.png new file mode 100644 index 0000000..dc85e95 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/decorator_cargo__uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/decorator_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/decorator_uml.png new file mode 100644 index 0000000..3bf6b03 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/decorator_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/dependency_inversion.png b/CreatingReliableSoftwareCpp/Presentation/images/dependency_inversion.png new file mode 100644 index 0000000..1e4da16 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/dependency_inversion.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/factory_method_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/factory_method_uml.png new file mode 100644 index 0000000..9e93a7e Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/factory_method_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/observer.png b/CreatingReliableSoftwareCpp/Presentation/images/observer.png new file mode 100644 index 0000000..967edb1 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/observer.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/palyer_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/palyer_uml.png new file mode 100644 index 0000000..b4d64b7 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/palyer_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/strategy.png b/CreatingReliableSoftwareCpp/Presentation/images/strategy.png new file mode 100644 index 0000000..76b4a74 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/strategy.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/strategy_broken_architecture.png b/CreatingReliableSoftwareCpp/Presentation/images/strategy_broken_architecture.png new file mode 100644 index 0000000..84cbfe9 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/strategy_broken_architecture.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/strategy_good_architecture.png b/CreatingReliableSoftwareCpp/Presentation/images/strategy_good_architecture.png new file mode 100644 index 0000000..9fbb90e Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/strategy_good_architecture.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/strategy_perfect_architecture.png b/CreatingReliableSoftwareCpp/Presentation/images/strategy_perfect_architecture.png new file mode 100644 index 0000000..d2b7cba Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/strategy_perfect_architecture.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/template_method_uml.png b/CreatingReliableSoftwareCpp/Presentation/images/template_method_uml.png new file mode 100644 index 0000000..8f5a857 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/template_method_uml.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/visitor.png b/CreatingReliableSoftwareCpp/Presentation/images/visitor.png new file mode 100644 index 0000000..5eb9afa Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/visitor.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/visitor_acyclic.png b/CreatingReliableSoftwareCpp/Presentation/images/visitor_acyclic.png new file mode 100644 index 0000000..880e245 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/visitor_acyclic.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/visitor_bench.png b/CreatingReliableSoftwareCpp/Presentation/images/visitor_bench.png new file mode 100644 index 0000000..ba9305e Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/visitor_bench.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/visitor_better_design.png b/CreatingReliableSoftwareCpp/Presentation/images/visitor_better_design.png new file mode 100644 index 0000000..c018e34 Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/visitor_better_design.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/images/visitor_first_apprach.png b/CreatingReliableSoftwareCpp/Presentation/images/visitor_first_apprach.png new file mode 100644 index 0000000..da6fbbe Binary files /dev/null and b/CreatingReliableSoftwareCpp/Presentation/images/visitor_first_apprach.png differ diff --git a/CreatingReliableSoftwareCpp/Presentation/index.html b/CreatingReliableSoftwareCpp/Presentation/index.html new file mode 100644 index 0000000..eabf840 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/index.html @@ -0,0 +1,188 @@ + + + + + + + Creating reliable software in C++ + + + + + + + + + + + + + +
+
+ +
+

Creating reliable software in C++

+
+

Altkom Akademia

+ https://www.altkomakademia.pl + +
+
+ Altkom Akademia + +48 801 258 566 +
+ +
+ +
+ Altkom Akademia + Mateusz Adamski + nauka.programowania.ma@gmail.com +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Altkom Akademia

+ https://www.altkomakademia.pl + +

+ +
+
+
+ altkomakademia +
+ Zapraszamy do współpracy + ALTKOM AKADEMIA + ul. Chłodna 51, budynek WTT, + 00-867 Warszawa + Telefon: (+48 22) 460 99 99, + Fax: (+48 22) 460 99 90 + warszawa@altkom.pl +
+
+
+ +
+ +
+ Mateusz Adamski + nauka.programowania.ma@gmail.com +
+
+
+ +
+ +
+ + + + + + diff --git a/CreatingReliableSoftwareCpp/Presentation/introduction.md b/CreatingReliableSoftwareCpp/Presentation/introduction.md new file mode 100644 index 0000000..1524aae --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/introduction.md @@ -0,0 +1,58 @@ + + +

Presentation authors

+ +
+
+ Mateusz +
+
+ Mateusz Adamski +
+
+___ + + +

Mateusz Adamski

+ +
+
+ +Experience: + +* Trainer at Coders School and Altkom Academy +* C++ developer: worked in Nokia, Opera, Consult red and Synerise + +Training experience: + +* [C++ trainings @ Coders School](https://coders.school/) +* [C++ trainings @ Altkom Academy](https://www.altkomakademia.pl/) +* [C++ trainings @ Sages](https://www.sages.pl/) +* [Nokia Academy](http://nokiawroclaw.pl/nasze-akcje/akademia/) +* Internal corporate trainings + +
+ +
+ Mateusz +
+ +___ + +## Let's introduce yourself! + +* Your name and experience form C++ programing. +* Have you ever used std::optional +* Which design patterns do you know? +* Are you familiar with GTEST? +* Do you know SOLID? +* What do you expect from today's session? + +___ + +## Contract + +* 🎰 Vegas rule +* 🗣 Discussion, not a lecture +* ☕️ Additional breaks on demand +* ⌚️ Be on time after breaks \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/logger.md b/CreatingReliableSoftwareCpp/Presentation/logger.md new file mode 100644 index 0000000..180ce00 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/logger.md @@ -0,0 +1,220 @@ +# logger +___ + +## source_loaction + +Since C++20 we got a nice functionality to perform easy and efficient logging. + +```C++ +void log(const std::string_view message, + const std::source_location location = + std::source_location::current()) +{ + std::cout << std::format("{}({}:{}) '{}':{}\n", + location.file_name(), + location.line(), + location.column(), + location.function_name(), + message); +} + +template void fun(T x) +{ + log(x); +} + +int main(int, char*[]) +{ + log("Hello world!"); + fun("Hello C++20!"); +} +``` + +```bash +prog.cc(24:8) 'int main(int, char**)': Hello world! +prog.cc(19:8) 'void fun(T) [with T = const char*]': Hello C++20! +``` + + +___ + +It's easy to rebuild a little this solution and create own logger: + +```C++ +enum LogType {INF, WRN, ERR}; + +struct Logger { + Logger(LogType logType, std::source_location location = std::source_location::current()): + logType_{logType}, + location_{location} {} + + Logger& operator<<(std::string_view message) { + file_ << std::format("[{}]({}): {}({}:{}) `{}`: {}\n", + toString(logType_), + std::chrono::system_clock::now(), + location_.file_name(), + location_.line(), + location_.column(), + location_.function_name(), + message); + file_ << std::flush; + + return *this; + } + +private: + std::string_view toString(LogType logType) { + switch (logType) { + case INF: return "I"; + case WRN: return "W"; + case ERR: return "E"; + default: return "UNKNOWN!"; + } + } + + LogType logType_; + std::source_location location_; + static std::ofstream file_; +}; + +std::ofstream Logger::file_("log.txt"); +``` + +___ + +Ok now is time to log sth + +```C++ +int main() { + Logger(INF) << "Hello!" << "And Hi" << "And Dzien dobry!"; + Logger(WRN) << "Ojej!"; + Logger(ERR) << "Critical!!!" << "UPS!" << "Bad bad!"; +} +``` + +And in file `log.txt` we got: + + +```C++ +[I](2024-03-17 15:29:34.248747710): prog.cc(55:15) `int main()`: Hello! +[I](2024-03-17 15:29:34.248817593): prog.cc(55:15) `int main()`: And Hi +[I](2024-03-17 15:29:34.248832174): prog.cc(55:15) `int main()`: And Dzien dobry! +[W](2024-03-17 15:29:34.248843859): prog.cc(56:15) `int main()`: Ojej! +[E](2024-03-17 15:29:34.248855275): prog.cc(57:15) `int main()`: Critical!!! +[E](2024-03-17 15:29:34.248866476): prog.cc(57:15) `int main()`: UPS! +[E](2024-03-17 15:29:34.248877615): prog.cc(57:15) `int main()`: Bad bad! +``` + + +___ + +## Second version + +We can also write a whole output when the logger will be destroyed, this allows to log only one line. + +```C++ +Logger::Logger(LogType logType, std::source_location location) + : logType_{logType}, + location_{location} { + file_ << std::format("[{}]({}): {}({}:{}) `{}`:", + toString(logType_), + std::chrono::system_clock::now(), + location_.file_name(), + location_.line(), + location_.column(), + location_.function_name()); +} + +Logger::~Logger() { + file_ << std::endl; // flush and new line +} + +Logger& Logger::operator<<(const std::string& str) { + file_ << " " << str; + return *this; +} +``` + + +___ + +```C++ +int main() { + Logger(INF) << "Hello!" << "And Hi" << "And Dzien dobry!"; + Logger(WRN) << "Ojej!"; + Logger(ERR) << "Critical!!!" << "UPS!" << "Bad bad!"; +} +``` + +And in file `log.txt` we got: + + +```C++ +[I](2024-03-17 15:29:34.248747710): prog.cc(55:15) `int main()`: Hello! And Hi And Dzien dobry! +[W](2024-03-17 15:29:34.248843859): prog.cc(56:15) `int main()`: Ojej! +[E](2024-03-17 15:29:34.248855275): prog.cc(57:15) `int main()`: Critical!!! UPS! Bad bad! +``` + + +___ + +## Ok but this is not thread safe! + +Now we need to make the code a thread-safe and put the `id` of the thread. We need to lock a mutex before writing output to the file and keep it until we destroy a logger. Ofc we risk that someone will create a logger instance and don't destroy them in the following line, but we assume that we use it correctly. We can add a special function that wraps this for use (the most common is macro). + +```C++ +Logger::Logger(LogType logType, std::source_location location): logType_{logType}, location_{location} { + formatted_ = std::format("[{}][th:{}]({}): {}({}:{}) `{}`:", + toString(logType_), + std::this_thread::get_id(), + std::chrono::system_clock::now(), + location_.file_name(), + location_.line(), + location_.column(), + location_.function_name()); +} + +Logger& Logger::operator<<(const std::string& str) { + formatted_ = std::format("{} {}", std::move(formatted_), str); + return *this; +} + +Logger::~Logger() { + std::lock_guard lg(loggMutex_); + loggFile_ << formatted_ << std::endl; +} +``` + + +___ + +```C++ +#define LOG_INF Logger(INF) +#define LOG_ERR Logger(ERR) +#define LOG_WRN Logger(WRN) + +int main() { + LOG_INF << "This" << "is" << "an" << "info"; + LOG_WRN << "This" << "is" << "a" << "warning"; + LOG_ERR << "This" << "is" << "an" << "error"; +} +``` + +```C++ +[I][th:1](2024-03-17 15:53:04.743470912): prog.cc(86:5) `int main()`: This is an info +[W][th:1](2024-03-17 15:53:04.743541096): prog.cc(87:5) `int main()`: This is a warning +[E][th:1](2024-03-17 15:53:04.743558245): prog.cc(88:5) `int main()`: This is an error +``` + + + +___ + +## Cleanup + +At the beginning of the program, we should also clear all logged data, this is easy to achieve: + +```C++ +// Always clear file when program start +std::ofstream Logger::loggFile_("log.txt", std::ofstream::trunc); +``` diff --git a/CreatingReliableSoftwareCpp/Presentation/observer.md b/CreatingReliableSoftwareCpp/Presentation/observer.md new file mode 100644 index 0000000..5d35cc0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/observer.md @@ -0,0 +1,615 @@ +# Observer + +One of the most popular design patterns is an observer. Almost every project uses at least one observer. This popular pattern is used to notify other classes about some changes. There are two types of observers: + +* Pull observer: We inform a class about change, but this class needs to find out what changed +* Push observer: We send all necessary information to class about what changes + +A first approach is more flexible, but forcing a class to get info on what changed, may take a long time to get this info, which may cause an observer slower + + +A second approach is less flexible because we need to create a method per change, to be sure, that the other class will know what happens and can use changed values without explicitly taking arguments or checking the state from the observed class + +___ + +The observer design pattern is a behavioural pattern listed among the 23 well-known "Gang of Four" design patterns that address recurring design challenges in order to design flexible and reusable object-oriented software, yielding objects that are easier to implement, change, test and reuse. Observer has the following traits: + +* A one-to-many dependency between objects should be defined without making the objects tightly coupled. +* When one object changes state, an open-ended number of dependent objects should be updated automatically. +* An object can notify multiple other objects + +Observer UML + +___ + +## Simple example + +We want to inform other classes when the browser will change the focus or will be closed. + +```C++ +struct Observer { + virtual ~Observer() = default; + virtual void browserAboutToQuit() = 0; + virtual void browserFocusChanged() = 0; +} +``` +___ + +```C++ +class Browser { +public: + struct Observer { /* implementation */ }; + + ~Browser() { + std::cout << "Browser is closing\n"; + notifyOnQuit(); + std::cout << "Browser closed\n"; + } + // Rule of 5 :) + + void attach(Observer* observer) { _observers.push_back(observer); } + void detach(Observer* observer) { std::erase(_observers, observer); } + +private: + void notifyOnQuit() const { + std::ranges::for_each(_observers, std::mem_fn(&Observer::browserAboutToQuit)); + } + void notifyOnFocusChanfed() const { + std::ranges::for_each(_observers, std::mem_fn(&Observer::browserFocusChanged)); + } + + std::vector _observers; +}; +``` + +___ + +```C++ +class TabStripManager : public Browser::Observer { +public: + TabStripManager(Browser* browser): _browser(browser) { + browser->attach(this); + } + + ~TabStripManager() { + _browser->detach(this); + } + // Rule of 5 :) + + void browserAboutToQuit() override { + std::cout << "TabStripManager will close tabs\n"; + } + + void browserFocusChanged() override { + std::cout << "TabStripManager lost focus\n"; + } + +private: + Browser* _browser; +}; + +``` + + +___ + +```C++ +int main() { + std::unique_ptr browser = std::make_unique(); + TabStripManager manager(browser.get()); + browser = nullptr; +} +``` + +```bash +Browser is closing +TabStripManager will close tabs +Browser closed +``` + +___ + +## Problem with observer + +Let's check once more the class `TabStripManager`, did you spot a problem here? + +```C++ +class TabStripManager : public Browser::Observer { +public: + TabStripManager(Browser* browser): _browser(browser) { + browser->attach(this); + } + + ~TabStripManager() { + _browser->detach(this); + } + // Rule of 5 :) + + void browserAboutToQuit() override { + std::cout << "TabStripManager will close tabs\n"; + } + + void browserFocusChanged() override { + std::cout << "TabStripManager lost focus\n"; + } + +private: + Browser* _browser; +}; + +``` + + +___ + +## About to quit + +Observer is a useful mechanism to inform other classes that hold a pointer to our class to invalidate this pointer because we are destroying it. If we forget to invalidate the observer we will call` detach` in destructor which will cause a UB because we hold a dangling pointer. + +```C++ +class TabStripManager : public Browser::Observer { +public: + TabStripManager(Browser* browser): _browser(browser) { + assert(browser); + browser->attach(this); + } + + ~TabStripManager() { + if (_browser) { + _browser->detach(this); + } + } + // Rule of 5 :) + + void browserAboutToQuit() override { + std::cout << "TabStripManager will close tabs\n"; + _browser = nullptr; + } + + void browserFocusChanged() override { + std::cout << "TabStripManager lost focus\n"; + } + +private: + Browser* _browser; +}; +``` + + + +___ + +## Template observer + +Do we always need to copy and paste the `Observer` class to every other class that wants to use the pattern? And do we always need to add a method for every state? Can we do this without duplicating the code? + + +Spoiler was in the topic, yes we can! + + +```C++ +template +struct Observer { + virtual ~Observer() = default; + virtual void update(State state) = 0; +}; +``` + +___ + +```C++ +class Browser { +public: + enum class State { + Closing, + FocusChanged + }; + using BrowserObserver = Observer; + + ~Browser() { + std::cout << "Browser is closing\n"; + notify(State::Closing); + std::cout << "Browser closed\n"; + } + // Rule of 5 :) + + void attach(BrowserObserver* observer) { _observers.push_back(observer); } + void detach(BrowserObserver* observer) { std::erase(_observers, observer); } + +private: + void notify(State state) const { + std::ranges::for_each(_observers, [state](auto* observer){ + observer->update(state); + }); + } + + std::vector _observers; +}; +``` + + +___ + +```C++ +class TabStripManager : public Browser::BrowserObserver { +public: + TabStripManager(Browser* browser): _browser(browser) { + assert(browser); + browser->attach(this); + } + + ~TabStripManager() { + if (_browser) { + _browser->detach(this); + } + } + // Rule of 5 :) + + // dispatch message + void update(Browser::State state) override { + switch (state) { + case Browser::State::Closing: { + browserAboutToQuit(); + return; + } + case Browser::State::FocusChanged: { + browserFocusChanged(); + return; + } + } + } + +private: + void browserAboutToQuit() { + std::cout << "TabStripManager will close tabs\n"; + _browser = nullptr; + } + + void browserFocusChanged() { + std::cout << "TabStripManager lost focus\n"; + } + + Browser* _browser; +}; +``` + + +___ + +## Still we have the same behavior + +```C++ +int main() { + std::unique_ptr browser = std::make_unique(); + TabStripManager manager(browser.get()); + browser = nullptr; +} +``` + +```bash +Browser is closing +TabStripManager will close tabs +Browser closed +``` + +___ + +## Problem with observer part 2 + +Let's check once more the class `Browser` do you spot any problem here? + +```C++ +class Browser { +public: + enum class State { + Closing, + FocusChanged + }; + using BrowserObserver = Observer; + + ~Browser() { + std::cout << "Browser is closing\n"; + notify(State::Closing); + std::cout << "Browser closed\n"; + } + // Rule of 5 :) + + void attach(BrowserObserver* observer) { _observers.push_back(observer); } + void detach(BrowserObserver* observer) { std::erase(_observers, observer); } + +private: + void notify(State state) const { + std::ranges::for_each(_observers, [state](auto* observer){ + observer->update(state); + }); + } + + std::vector _observers; +}; +``` + +___ + +## Attach / dettach + +Question1: How do we bahve, when someone will attach twice the same observer? + +- 1) Register it twice, and than notify twice? Like `std::recursive_mutex`? +- 2) Throw an exception? +- 3) Return an error? +- 4) Ignore it, and only print some warning logs about that? + +The answer is not that easy, as usual, it depends, on what we want to achieve + + +Question2: How do we bahve, when someone will detach twice the same observer? + + +- 1) Check if someone attach it twice? +- 2) Throw an exception? +- 3) Return an error? +- 4) Ignore it, and only print some warning logs about that? + +I guess you know the answer :) it depens. + + +___ + +## Problem with observer part 3 + +This is not all the problems that we may have with the observer, let's check this snippet of code. Do you spot any problem here? + +```C++ +class Browser { +public: + enum class State { + Closing, + FocusChanged + }; + using BrowserObserver = Observer; + + void attach(BrowserObserver* observer) { _observers.emplace(observer); } + void detach(BrowserObserver* observer) { _observers.erase(observer); } + void setFocus(bool focus) { + if (std::exchange(_hasFocus, focus) != focus) { + notify(State::FocusChanged); + } + } + bool hasFocus() const { return _hasFocus; } + +private: + void notify(State state) const { + std::ranges::for_each(_observers, [state](auto* observer){ + observer->update(state); + }); + } + + bool _hasFocus{false}; + std::set _observers; +}; +``` + +___ + +## Maybe now? + +```C++ +class Extension : public Browser::BrowserObserver { +public: + Extension(Browser* browser, const std::string& name): _browser(browser), _name(name) { browser->attach(this); } + // Rule of 5 :) + ~Extension() { if (_browser) { _browser->detach(this); } } + + // dispatch message + void update(Browser::State state) override { + switch (state) { + case Browser::State::Closing: return; + case Browser::State::FocusChanged: { + if (_browser->hasFocus()) { displayExtension(); } + return; + } + } + } + +private: + void displayExtension() { + std::cout << "This is an extension: " << _name << '\n'; + } + + Browser* _browser; + std::string _name; +}; +``` + +___ + +## Order of observer + +What will be printed? + +```C++ +int main() { + std::unique_ptr browser = std::make_unique(); + Extension extA(browser.get(), "A"); + Extension extB(browser.get(), "B"); + Extension extC(browser.get(), "C"); + browser->setFocus(true); +} +``` + +The order is unknown because we use `std::set`. Generally, we should **never depend on the order of calling an observer**. Because in the future someone can change the implementation and the program will stop working! + + +___ + +## Exercise + +* Go into directory Core and implement Time and TimeObserver class +* Class Time should have 4 methods: + * void attach(TimeObserver* observer); -> attach observer + * void detach(TimeObserver* observer); -> detach observer + * Time& operator++(); -> increment day and notify observers + * size_t day() const; -> return current day +* Go into directory Ship and make Ship class inherit from TimeObserver +* Each time the observer will be triggered, you should give the crew food and drink +* Each crew member should consume 1 rum and 1 banana (I know they usually eat biscuits) +* If you don't have enough cargo crew should rebel +* Each day of a rebel you should subtract 5 members of the crew +* If the number of crew drops below 0, throw an exception "Game Over" + +___ + +## Exercise 2 + +* Go into directory Store and make Store class inherit from *TimeObserver* +* Class Store should don't know anything about class Time +* We should attach and detach observer outside of class. Think about how you can make it exception-safe (RAII) +* Each time the observer will be triggered, you should change the number of available cargo in the store +* Each time draw a number between -50 and 50 and add it to the amount of cargo +* Print everyday cargo from the Shop and verify if the amount changes +___ + +## Moder apporach + +In modern C++ we try to avoid too many references or pointers because they are problematic. We will try to implement an observer pattern by using value semantics. First let's get rid of the virtual function, and forcing classes to inherit from the observer class. + +```C++ +template +struct Observer { + // First, we don't need to use polymorphism, so also virtual D'tor is redundant + using OnUpdate = std::function; + + explciit Observer(OnUpdate fun): _onUpdate(std::move(fun)) {} + void update(const Subject& subject, State state) { + std::invoke(_onUpdate, subject, state); + } +private: + OnUpdate _onUpdate; +}; +``` + + +___ + +Now move out of the `Browser` class `BorwserObserver` so the files that want to use `BrowserObserver` don't need to include the whole `Browser` file in the header. Only in the source file we will need a `Browser` library, so every time the `Browser` class changes we don't need to recompile a lot of files. + +```C++ +class Browser; + +enum class BrowserState { + Closing, + FocusChanged +}; + +using BrowserObserver = Observer; +``` +___ + +We will still use pointer as `Observer` because they are easy to use (we know that each address is unique) but we can change it to some `uuid` or sth similar to get rid of the pointer also in a `Borwser` class. + +```C++ +class Browser { +public: + void attach(BrowserObserver* observer) { _observers.emplace(observer); } + void detach(BrowserObserver* observer) { _observers.erase(observer); } + void setFocus(bool focus) { + if (std::exchange(_hasFocus, focus) != focus) { + notify(BrowserState::FocusChanged); + } + } + bool hasFocus() const { return _hasFocus; } + +private: + void notify(BrowserState state) const { + std::ranges::for_each(_observers, [state, this](auto* observer){ + observer->update(*this, state); + }); + } + + bool _hasFocus{false}; + std::set _observers; +}; +``` + +___ + +Extension class doesn't need to inherit from `Observer` so we get rid of destructor and vtable. Another advantage is that we separate observers from extension classes. Now it will be a separate object. + +```C++ +class Extension { +public: + explicit Extension(const std::string& name): _name(name) { } + // D'tor is not needed neither the rule of 5 + + // dispatch message + void update(const Browser& browser, BrowserState state) { + switch (state) { + case BrowserState::Closing: + return; + case BrowserState::FocusChanged: { + if (browser.hasFocus()) { + displayExtension(); + } + return; + } + } + } + +private: + void displayExtension() { + std::cout << "This is an extension: " << _name << '\n'; + } + + std::string _name; +}; +``` + +___ + +We have three separate objects, one is a `Browser` the second is an `Extension` and the third is a `BrowserObserver`. We still can use traditional observer patterns, where we use inheritance, but in a lot of cases, we can avoid virtual and make our code faster. + +```C++ +int main() { + std::unique_ptr browser = std::make_unique(); + Extension extension1("A"); + Extension extension2("B"); + Extension extension3("C"); + BrowserObserver observer1([&extension1](const Browser& browser, BrowserState state){ + extension1.update(browser, state); + }); + BrowserObserver observer2([&extension2](const Browser& browser, BrowserState state){ + extension2.update(browser, state); + }); + BrowserObserver observer3([&extension3](const Browser& browser, BrowserState state){ + extension3.update(browser, state); + }); + browser->attach(&observer1); + browser->attach(&observer2); + browser->attach(&observer3); + browser->setFocus(true); +} +``` + +___ + +## Benchmark + +By using value semantics, we may also get around 10% faster code. This depends on what we do as an observer, it the action is long, we will not see any difference, but for quick actions we may improve a performance. + + + +Benchamrk Observer + +___ + +## The last drawback of Oobserver + +Please don't use it everywhere, because your code becomes more complicated when you will have hundreds of observers. Remember you can't relay on the order of notification, so if class `A` calls `notify` which triggers class `B` which also triggers a notification for class `C` which also triggers a notification for class `D` you will have really complicated structure and finding bugs and fixing it will be hard. +___ + +## Exercise 3 + +* Rewrite Observer to use modern approach -> by value semantic. +* Rewrite class Ship +* Rewrite class Store +* Question: Which class was easier to rewrite? diff --git a/CreatingReliableSoftwareCpp/Presentation/recap.md b/CreatingReliableSoftwareCpp/Presentation/recap.md new file mode 100644 index 0000000..458df21 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/recap.md @@ -0,0 +1,8 @@ + + +# Recap + +___ + +## What do you remember from today's session? + diff --git a/CreatingReliableSoftwareCpp/Presentation/refactor.md b/CreatingReliableSoftwareCpp/Presentation/refactor.md new file mode 100644 index 0000000..1d0d0c0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/refactor.md @@ -0,0 +1,880 @@ +# Refactoring + +___ + +## Old C++98/11 code + +Please everyone to sit comfortably. I will show You a piece of code which I had to reactor a few years ago. This code will be simpler than those which I handled, but general ideas stayed, like: `void*` etc... + +Let's start from Valgrind output about this piece of code: + + +```C++ +==1667238== More than 10000000 total errors detected. I'm not reporting any more. +==1667238== Final error counts will be inaccurate. Go fix your program! +``` + +___ + +## General idea + +We want to have a `RequestHandler` which will store requests in the queue and handle them on a separate thread. We have 4 requests: +* Download sth, for instnace an image, +* Upload sth, +* Remove data from cache, +* Clear all cached values. + +We have an API to Server class that has one method handle, which has 4 overloads, each for one request. + +___ + +## Requests + +
+
+ +```C++ +struct Credentail { int cert_; }; + +struct Download { + enum ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + bool cache_; + ConnectionType type_; +}; +``` +
+
+ +```C++ +struct Upload { + enum ConnectionType { Telnet, + Ssh }; + + std::string url_; + Image image_; + Credentail credentail_; + int maxMbps_; + int size_; + ConnectionType type_; +}; +``` +
+
+ +
+
+ +```C++ +struct RemoveFromCache { + std::string url_; +}; +``` +
+
+ +```C++ +struct ClearCache { +}; +``` +
+
+___ + +## Server class + +```C++ +class Server { +public: + void handle(const Download& request, void (*callback)(bool, Image)); + + void handle(const Upload& request, void (*callback)(bool, Image)); + + void handle(const RemoveFromCache& request, void (*callback)(bool, Image)); + + void handle(const ClearCache& request, void (*callback)(bool, Image)); + +private: + bool download(const Download& request, Image* image); + + bool upload(const Upload& request, const Image* image); + + std::vector> cache_; +}; +``` + +___ + +## Server class - one of handle method + +```C++ +void handle(const Download& request, void (*callback)(bool, Image)) { + Image image; + + for (const auto& pair : cache_) { + if (pair.second == request.url_) { + image = pair.first; + callback(true, image); + return; + } + } + + if (download(request, &image)) { + if (request.cache_) { + cache_.push_back(std::pair(image, request.url_)); + } + + callback(true, image); + return; + } + + callback(false, Image{}); +} +``` + +___ + +## Server class - download method + +```C++ +bool download(const Download& request, Image* image) { + if (request.type_ == Download::Ssh && request.credentail_.cert_ != 123) { + return false; + } + if (request.type_ == Download::Telnet && request.credentail_.cert_ != 231) { + return false; + } + + // Simulate some other error + if (request.maxMbps_ % 2) { + return false; + } + image->bitmap_ = {97, 98, 99, 100, 101, 102}; + return true; +} +``` + +___ + +## Let's stop here for a while + +* What's wrong with this code? +* How we can improve it? +* Which modern C++ feature we should use here? + +```C++ +void handle(const Download& request, void (*callback)(bool, Image)) { + Image image; + for (const auto& pair : cache_) { + if (pair.second == request.url_) { + image = pair.first; + callback(true, image); + return; + } + } + if (download(request, &image)) { + if (request.cache_) { + cache_.push_back(std::pair(image, request.url_)); + } + callback(true, image); + return; + } + callback(false, Image{}); +} + +bool download(const Download& request, Image* image) { + if (request.type_ == Download::Ssh && request.credentail_.cert_ != 123) { + return false; + } + if (request.type_ == Download::Telnet && request.credentail_.cert_ != 231) { + return false; + } + // Simulate some other error + if (request.maxMbps_ % 2) { + return false; + } + image->bitmap_ = {97, 98, 99, 100, 101, 102}; + return true; +} +``` + + +___ + +## Now will be only worse! + +```C++ +class RequestHandler { +public: + enum RequestType { Download, Upload, Remove, Clear }; + + void start(Server* server); + + void stop(); + + void pushRequest(void* request, RequestType type, void (*callback)(bool, Image)); + +private: + void run(Server* server); + std::tuple waitForRequest(); + + bool stop_; + std::queue> requests_; +}; +``` + +___ + +## Implementations (1) + +```C++ +void start(Server* server) { + std::thread(&RequestHandler::run, this, server).detach(); +} + +void stop() { + stop_ = true; +} + +void pushRequest(void* request, RequestType type, void (*callback)(bool, Image)) { + requests_.push(std::tuple( + request, type, callback)); +} +``` + +___ + +## Implementations (2) + +```C++ + void run(Server* server) { + while (!stop_) { + auto tuple = waitForRequest(); + switch (std::get<1>(tuple)) { + case Download: + server->handle(*((::Download*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + case Upload: + server->handle(*((::Upload*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + case Remove: + server->handle(*((RemoveFromCache*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + case Clear: + server->handle(*((ClearCache*)(std::get<0>(tuple))), std::get<2>(tuple)); + break; + } + } + } + + std::tuple waitForRequest() { + while (requests_.empty() || !stop_) { + } + + auto pair = requests_.front(); + requests_.pop(); + + return pair; + } +``` + +___ + +## The funny part - it actually works! + +```C++ +int main() { + Server server; + RequestHandler handler; + + handler.start(&server); + + Download d{"sth.png", 123, 100, true, Download::Ssh}; + handler.pushRequest((void*)(&d), + RequestHandler::Download, + [](bool succes, Image image) { + if (succes) { + for (auto el : image.bitmap_) { + std::cout << el << ' '; + } + std::cout << '\n'; + } else { + std::cout << "FAILED!\n"; + } + }); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + handler.stop(); +} +``` + + +___ + +## Let's fix this ugly code! + +For Requests we only need add `enum class` instead of `enum` the rest seems ok. + +
+
+ +```C++ +struct Credentail { int cert_; }; + +struct Download { + enum class ConnectionType { Telnet, + Ssh }; + + std::string url_; + Credentail credentail_; + int maxMbps_; + bool cache_; + ConnectionType type_; +}; +``` +
+
+ +```C++ +struct Upload { + enum class ConnectionType { Telnet, + Ssh }; + + std::string url_; + Image image_; + Credentail credentail_; + int maxMbps_; + int size_; + ConnectionType type_; +}; +``` +
+
+ +
+
+ +```C++ +struct RemoveFromCache { + std::string url_; +}; +``` +
+
+ +```C++ +struct ClearCache { +}; +``` +
+
+ +___ + +## Alias and status code + +The `bool` flag is the best option for describing an error + +```C++ +enum class StatusCode { + Ok, + WrongUrl, + CanNotConnect, + WrongCredential, + MaximumSizeExceeded, + MissingImage, +}; +``` + +Let's add also an aliast for `callback` + + +```C++ +using CallbackType = void (*)(StatusCode, Image); + +void handle(const Download& request, CallbackType callback); +``` + +___ + +## Use references and STL algorithms (1) + +```C++ +void handle(const Download& request, CallbackType callback) { + if (const auto it = findImage(request.url_); it != std::cend(cache_)) { + callback(StatusCode::Ok, it->first); + return; + } + + Image img; + const auto status = download(request, img); + if (status == StatusCode::Ok) { + if (request.cache_) { + cache_.emplace_back(img, request.url_); + } + } + + callback(status, img); +} +``` + +___ + +## Use references and STL algorithms (2) + +```C++ +std::vector>::const_iterator findImage(const std::string& url) const { + return std::find_if(cbegin(cache_), cend(cache_), + [url](const auto& pair) { + const auto& [_, thisUrl] = pair; + return thisUrl == url; + }); +} + +StatusCode download(const Download& request, Image& image) const { + if (request.type_ == Download::ConnectionType::Ssh && request.credentail_.cert_ != 123) { + return StatusCode::WrongCredential; + } + if (request.type_ == Download::ConnectionType::Telnet && request.credentail_.cert_ != 231) { + return StatusCode::CanNotConnect; + } + + // Simulate some other error + if (request.maxMbps_ % 2) { + return StatusCode::WrongUrl; + } + image.bitmap_ = {97, 98, 99, 100, 101, 102}; + return StatusCode::Ok; +} +``` + + +___ + +## Use references and STL algorithms (3) + +```C++ +void handle(const Upload& request, CallbackType callback) const { + if (request.size_ > 100) { + callback(StatusCode::MaximumSizeExceeded, Image{}); + return; + } + + callback(upload(request), Image{}); +} + +void handle(const RemoveFromCache& request, CallbackType callback) { + if (const auto it = findImage(request.url_); it != std::cend(cache_)) { + cache_.erase(it); + callback(StatusCode::Ok, Image{}); + return; + } + + callback(StatusCode::MissingImage, Image{}); +} +``` + +___ + +## Let's fix void* + +Could be better, but this work and is safe. + +```C++ +using RequestType = std::variant; + +void pushRequest(const RequestType& request, Server::CallbackType callback) { + requests_.emplace(request, callback); +} + +void run(Server* server) { + const auto [request, callback] = waitForRequest(); + switch (request.index()) { + case 0: + server->handle(std::get(request), callback); + break; + case 1: + server->handle(std::get(request), callback); + break; + case 2: + server->handle(std::get(request), callback); + break; + case 3: + server->handle(std::get(request), callback); + break; + } +} +``` + +___ + +## Make it thread safe (1) + +```C++ +void start(Server* server) { + std::thread(&RequestHandler::run, this, server).detach(); +} + +void stop() { + stop_ = true; + cv_.notify_one(); +} + +void pushRequest(const RequestType& request, Server::CallbackType callback) { + { + std::lock_guard lock(m_); + requests_.emplace(request, callback); + } + cv_.notify_one(); +} + +using QueueType = std::pair; + +std::mutex m_; +std::condition_variable cv_; +std::atomic stop_{false}; +std::queue requests_; +``` + +___ + +## Make it thread safe (2) + +```C++ +std::optional waitForRequest() { + std::unique_lock lk(m_); + cv_.wait(lk, [&]() { return !requests_.empty() || stop_; }); + if (stop_) { + return std::nullopt; + } + auto pair = requests_.front(); + requests_.pop(); + + return pair; +} +``` +___ + +## Usage actually not change + +```C++ +Server server; +RequestHandler handler; + +handler.start(&server); + +handler.pushRequest(Download{"sth.png", 123, 100, true, Download::ConnectionType::Ssh}, + [](Server::StatusCode status, Image image) { + if (status == Server::StatusCode::Ok) { + for (auto el : image.bitmap_) { + std::cout << el << ' '; + } + std::cout << '\n'; + } else { + std::cout << "FAILED!\n"; + } + }); + +std::this_thread::sleep_for(std::chrono::milliseconds(10)); +handler.stop(); +``` + +___ + +## Valgird is almost satisfied + +It is suspicious only for a pointer to a server that was created in another thread. It marks it as possibly lost, but we know that we didn't do any allocation, because we created it on the stack, and pass only the address. + +```C++ +==1678528== LEAK SUMMARY: +==1678528== definitely lost: 0 bytes in 0 blocks +==1678528== indirectly lost: 0 bytes in 0 blocks +==1678528== possibly lost: 288 bytes in 1 blocks +==1678528== still reachable: 0 bytes in 0 blocks +==1678528== suppressed: 0 bytes in 0 blocks +==1678528== +==1678528== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) +``` +___ + +## Further improvements + +We can have 2 callbacks. One returns a downloaded image and error code, and the second returns only an error code. We can change the name of function `handle` to `download`, `upload` etc.. because inside `switch` we know which function we call because we know the type of request (there is no polymorphism). So we can avoid unnecessary arguments for image, especially this is important when classes don't have default C'tor. We should only make it as a template to allow the download other things than `Images.` We can also use `std::visit` method to avoid switch case. + +___ + +## Better solution - use OOP + +This particular example works, but what happens when we extend the program to handle also other types like Videos and audio? We need to extend server interface to handle different types. + +```C++ + void handle(const DownloadVideo& request, void (*callback)(bool, Video)); + void handle(const UploadVideo& request, void (*callback)(bool, Video)); + + void handle(const DownloadAudio& request, void (*callback)(bool, Audio)); + void handle(const UploadAudio& request, void (*callback)(bool, Audio)); + + void handle(const DownloadCertificates& request, void (*callback)(bool, Certificates)); + void handle(const UploadCertificates& request, void (*callback)(bool, Certificates)); +``` + + +Server class grow and grow, and now have a lot of resposibilities and depends form implementations of `Image`, `Video`, `Audio` and `Certificates` this is bad, really bad. We should folow SOLID principles. + + +___ + +## Command pattern + +Let's create an abstraction and break the dependency between `Server` and `Request`. `Command` object will have only one method `operator()` that we can call. This operator will perform one action like: `Download`, `Upload`, `RemoveFormCache`, `ClearCache`. + +```C++ +class Command { +public: + enum class StatusCode { + Ok, WrongUrl, CanNotConnect, WrongCredential, MaximumSizeExceeded, MissingImage + }; + + // Rule of 5! + virtual ~Command() = default; + Command(const Command&) = default; + Command(Command&&) = default; + Command& operator=(const Command&) = default; + Command& operator=(Command&&) = default; + + virtual void operator()() const = 0; +}; +``` + + + +___ + +## Delegate pattern + +Because `Command` needs to know only how to deal with Files like `Image`, `Video` or `Audio`. We need to create a `Delegate` that delegates the rest of the actions to the `Server` class. + +```C++ +class Command { + class Delegate { + public: + virtual ~Delegate() = default; + virtual StatusCode removeFromCache(const std::string& url) = 0; + virtual void clearCache() = 0; + virtual void addToCache(const std::vector& data, const std::string& url) = 0; + virtual StatusCode download(std::vector& data, const DownloadRequest& request) const = 0; + virtual StatusCode upload(const std::vector& data, const UploadRequest& request) const = 0; + }; + + explicit Command(Delegate* delegate) + : delegate_(delegate) {} + // ... +protected: + Delegate* delegate_; +}; +``` + + +___ + +## Server class + +Now server-class depends on abstraction which is `Delegate` and only performs actions with connecting with network and caching values. we can also create another class that will cache values. Class `Command` depends on abstraction, because we don't need to know which class will implement delegate methods. + +```C++ +class Server : public Command::Delegate { +public: + ~Server() override = default; + + Command::StatusCode removeFromCache(const std::string& url) override; + + void clearCache() override; + + void addToCache(const std::vector& data, const std::string& url) override; + + Command::StatusCode download(std::vector& data, const DownloadRequest& request) const override; + + Command::StatusCode upload(const std::vector& data, const UploadRequest& request) const override; + +private: + std::vector, std::string>>::const_iterator findFile(const std::string& url) const; + + std::vector, std::string>> cache_; +}; +``` + + + +___ + +## Easy to extend + +Now we can create whatever command we want to. + +```C++ +class UploadImageCommand : public Command { +public: + using CallbackType = void (*)(StatusCode); + + ~UploadImageCommand() override = default; + + UploadImageCommand(Delegate* delegate, const Image& image, CallbackType callback, const UploadRequest& request) + : Command(delegate), image_(image), callback_(callback), request_(request) {} + + void operator()() const override { + callback_(delegate_->upload(image_.bitmap_, request_)); + }; + +private: + Image image_; + CallbackType callback_; + UploadRequest request_; +}; +``` + + + +___ + +## Download Image command + +We can deal with different parameters, and callbacks. + +```C++ +class DownloadImageCommand : public Command { +public: + using CallbackType = void (*)(StatusCode, Image); + + ~DownloadImageCommand() override = default; + + DownloadImageCommand(Delegate* delegate, CallbackType callback, const DownloadRequest& request) + : Command(delegate), callback_(callback), request_(request) {} + + void operator()() const override { + std::vector data; + if (auto status = delegate_->download(data, request_); status == Command::StatusCode::Ok) { + // Do some conversion on vector + if (request_.cache_) { + delegate_->addToCache(data, request_.url_); + } + Image image{data}; + callback_(status, std::move(image)); + } else { + callback_(status, Image{}); + } + }; + +private: + CallbackType callback_; + DownloadRequest request_; +}; +``` + + +___ + +## Request handler + +Request handlers also break their dependency on the server. We don't need to know about `Server` class anymore. We just push the command on queue and call `operator()`. + +```C++ +class RequestHandler { +public: + void start() { + std::thread(&RequestHandler::run, this).detach(); + } + + void stop() { + stop_ = true; + cv_.notify_one(); + } + + void pushRequest(std::unique_ptr command) { + { + std::lock_guard lock(m_); + requests_.push(std::move(command)); + } + cv_.notify_one(); + } + +private: + void run() { + while (!stop_) { + const auto request = waitForRequest(); + if (!request) { + return; + } + + (*request)(); + } + } + + std::unique_ptr waitForRequest() { + std::unique_lock lk(m_); + cv_.wait(lk, [&]() { return !requests_.empty() || stop_; }); + if (stop_) { + return nullptr; + } + + std::unique_ptr request = std::move(requests_.front()); + requests_.pop(); + + return request; + } + + std::mutex m_; + std::condition_variable cv_; + std::atomic stop_{false}; + std::queue> requests_; +}; +``` + + +___ + +## Usage + +```C++ +int main() { + Server server; + RequestHandler handler; + + handler.start(); + handler.pushRequest(std::make_unique( + &server, + [](Command::StatusCode status, Image img) { + if (status == Command::StatusCode::Ok) { + std::copy(cbegin(img.bitmap_), cend(img.bitmap_), + std::ostream_iterator(std::cout, " ")); + std::cout << '\n'; + } else { + std::cout << "Sth went wrong!\n"; + } + }, + DownloadRequest{"Sth123", Credentail{123}, 200, true, DownloadRequest::ConnectionType::Ssh})); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + handler.stop(); +} +``` + +___ + +## Valgrind + +Now we have easy to extend code, which also satisfies Valgrind. + +```C++ +==1790927== +==1790927== HEAP SUMMARY: +==1790927== in use at exit: 0 bytes in 0 blocks +==1790927== total heap usage: 11 allocs, 11 frees, 74,770 bytes allocated +==1790927== +==1790927== All heap blocks were freed -- no leaks are possible +==1790927== +==1790927== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +``` \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/strategy.md b/CreatingReliableSoftwareCpp/Presentation/strategy.md new file mode 100644 index 0000000..3421691 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/strategy.md @@ -0,0 +1,735 @@ +# Strategy + +The second most popular design pattern is a strategy. If I need to bet how many of you used this pattern at least once I will say all of you. Strategy is the most common pattern used by the STL library and makes it so flexible. That's why we should use it often (maybe not everywhere) but in most cases, strategy will be an answer. + +Why we should use strategy? As David Thomas and Andrew Hunt said in the book: *Inheritance is Rarely the Answer*. + + +___ + +## Example of bad inheritance + +Before we talk about strategy patterns, let's look at the bad design of the application. We want to create three types of ammunition: *Round*, *Chain* and *Grape*. Each type of ammunition needs to be displayed as an icon (for instance when we check, the inventory, or buy it from the store) and also needs to be animated when a ship `fires` this ammunition during combat. + + +We decided to solve this problem by create inheritance. We inherit from class `Ammunition` and create three classes of `Shots`, then depending on which graphic library we used we `draw` it using `metal` or `OpenGL`. + + +Amunnition UML + + +___ + +## Next iteration + +After we successfully implemented the `draw` method, we decided to add `SDL` and `WindowsAnimationManager` libraries for displaying animation during combat. Because we already use `OpenGL` and `metal` libraries, we still need to support them. So we need to create another layer of inheritance. + +Amunnition UML + + + +___ + +I hope all of you saw that this is not a good example of a design application. Each time we add support for a new library, we need to create another layer of inheritance. How we can make it more flexible? The answer is the `Strategy` design pattern. + +Amunnition UML + + + +___ + +## Simplify + +First of all, we don't want to break 2 of the SOLID letters: **L**iskov substitution and **S**ingle responsibility. That's why we should inherit from class which all methods make sense for us. We create two strategies, one for `fire` and one for `draw`. We also want to avoid an oversized hierarchy of class. + +Amunnition UML + + + +___ + +## Implementation + +The ammunition class will be a base class with the interface. It also contains info about the amount of ammunition. + +```C++ +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void fire() = 0; + virtual void draw() = 0; + virtual int getDamage() const = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; +``` +___ + +```C++ +class FireStrategy { +public: + virtual void fire(const Ammunition& ammunition) = 0; +}; + +class SDLFireStrategy : public FireStrategy { +public: + void fire(const Ammunition& ammunition) override { + std::cout << "SDL Deal: " << ammunition.getDamage() << " damage!" << '\n'; + } +}; + +class WAMFireStrategy : public FireStrategy { +public: + void fire(const Ammunition& ammunition) override { + std::cout << "WAM Deal: " << ammunition.getDamage() << " damage!" << '\n'; + } +}; +``` + +```C++ +class DrawStrategy { +public: + virtual void draw(const Ammunition& ammunition) = 0; +}; + +class OpenGLDrawStrategy : public DrawStrategy { +public: + void draw(const Ammunition& ammunition) override { + std::cout << "OpenGL Ammunition: " << ammunition.amount() << '\n'; + } +}; + +class MetalDrawStrategy : public DrawStrategy { +public: + void draw(const Ammunition& ammunition) override { + std::cout << "Metal Ammunition: " << ammunition.amount() << '\n'; + } +}; +``` + + +___ + +```C++ +class RoundShot : public Ammunition { +public: + RoundShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } + void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } + int getDamage() const override { return 10; } +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; +``` + +```C++ +class ChainShot : public Ammunition { +public: + ChainShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } + void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } + int getDamage() const override { return 15; } +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; +``` + + +```C++ +class GrapeShot : public Ammunition { +public: + GrapeShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } + void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } + int getDamage() const override { return 20; } +private: + std::unique_ptr _fireStrategy; + std::unique_ptr _drawStrategy; +}; +``` + + + +___ + +## Usage + +We can easily inject any strategy into an ammunition class. This will help us keep another solid rule **D**ependency inversion and also dependency injection. + +```C++ +int main() { + std::unique_ptr grapeShot = std::make_unique( + 100, std::make_unique(), std::make_unique()); + + grapeShot->fire(); + grapeShot->draw(); +} +``` + +```bash +SDL Deal: 20 damage! +OpenGL Ammunition: 100 +``` + + +___ + +## Drawback + +The main drawback of the strategy pattern is visible when we try to add a new type. In such case, we need to implement a new behavior for each of these classes: + +* DrawOpenGLStrategy +* DrawMetalStrategy +* WamFireStrategy +* SDLFireStrategy + +This will be not such big deal, when we have access to all of the source code, but in case, when we can't modify this it will be problematic + + +___ + +## Improvement + +For now, `DrawStrategy` and `FireStrategy`, have one `virtual` function which probably needs to distinguish which type of ammunition should be drawn or animate. That's why we can create a more flexible interface, which takes a specific type and we don't need to use any casting. + +```C++ +class FireStrategy { +public: + virtual void fire(const ChainShot& shot) = 0; + virtual void fire(const RoundShot& shot) = 0; + virtual void fire(const GrapeShot& shot) = 0; +}; +``` + +___ + +```C++ +class SDLFireStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override { + std::cout << "SDL Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; + } + void fire(const RoundShot& shot) override { + std::cout << "SDL Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; + } + void fire(const GrapeShot& shot) override { + std::cout << "SDL Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; + } +}; +``` + +```C++ +class WAMFireStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override { + std::cout << "WAM Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; + } + void fire(const RoundShot& shot) override { + std::cout << "WAM Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; + } + void fire(const GrapeShot& shot) override { + std::cout << "WAM Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; + } +}; +``` + + +___ + +```C++ +class DrawStrategy { +public: + virtual void draw(const ChainShot& shot) = 0; + virtual void draw(const RoundShot& shot) = 0; + virtual void draw(const GrapeShot& shot) = 0; +}; +``` + +```C++ +class OpenGLDrawStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override { + std::cout << "OpenGL Chain Shot: " << shot.amount() << '\n'; + } + void draw(const RoundShot& shot) override { + std::cout << "OpenGL Round Shot: " << shot.amount() << '\n'; + } + void draw(const GrapeShot& shot) override { + std::cout << "OpenGL Grape Shot: " << shot.amount() << '\n'; + } +}; +``` + + +```C++ +class MetalDrawStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override { + std::cout << "Metal Chain Shot: " << shot.amount() << '\n'; + } + void draw(const RoundShot& shot) override { + std::cout << "Metal Round Shot: " << shot.amount() << '\n'; + } + void draw(const GrapeShot& shot) override { + std::cout << "Metal Grape Shot: " << shot.amount() << '\n'; + } +}; +``` + + +___ + +## Usage + +Now the function exactly knows, which type is handling, and can animate it or draw it correctly, without any type checking. + +```C++ +int main() { + std::unique_ptr grapeShot = std::make_unique( + 100, std::make_unique(), std::make_unique()); + + grapeShot->fire(); + grapeShot->draw(); +} +``` + +```bash +SDL Grape Shot Deal: 20 damage! +OpenGL Grape Shot: 100 +``` + + +___ + +## Separate implementation + +Strategy can distinguish type, but still depends on all types, let's rewrite the code once more and use another letter form SOLID: **I**nterface segregation. Each implementation will be a separate strategy class, so when we add a new type, other strategies will not know about it. + +```C++ +class FireChainShotStrategy { +public: + virtual void fire(const ChainShot& shot) = 0; +}; + +class FireRoundShotStrategy { + virtual void fire(const RoundShot& shot) = 0; +}; + +class FireGrapeShotStrategy { + virtual void fire(const GrapeShot& shot) = 0; +}; + +class SDLFireChainShotStrategy : public FireChainShotStrategy { +public: + void fire(const ChainShot& shot) override; +}; + +class SDLFireRoundShotStrategy : public FireRoundShotStrategy { +public: + void fire(const RoundShot& shot) override; +}; + +class SDLFireGrapeShotStrategy : public FireGrapeShotStrategy { +public: + void fire(const GrapeShot& shot) override; +}; +``` + + +___ + +```C++ +class DrawChainShotStrategy { +public: + virtual void draw(const ChainShot& shot) = 0; +}; + +class DrawRoundShotStrategy { + virtual void draw(const RoundShot& shot) = 0; +}; + +class DrawGrapeShotStrategy { + virtual void draw(const GrapeShot& shot) = 0; +}; +``` + +```C++ +class OpnGLDrawChainShotStrategy : public DrawChainShotStrategy { +public: + void draw(const ChainShot& shot) override; +}; + +class OpenGLDrawRoundShotStrategy : public DrawRoundShotStrategy { +public: + void draw(const RoundShot& shot) override; +}; + +class OpenGLDrawGrapeShotStrategy : public DrawGrapeShotStrategy { +public: + void draw(const GrapeShot& shot) override; +}; +``` + + +___ + +## Single resposibility + +In the beginning, we may think that we will need to create too many classes because each `Ammunition` type needs 4 classes (2 for draw, and 2 for fire), but when we take a look at the first letter of SOLID - **S**ingle responsibility, we understand that now we follow this rule. Previously `OpenGLDrawStrategy` and `MEtalDrawStrategy` needed to know about 3 different types of ammunition, and we broke all rules: +* Single resposinility -> each class need to know about all types +* Open close -> code was not open for extension, when we add a new type, we need to implement behavior in all 4 classes, even if we don't want to, which may cause to break another letter: +* Liskov substitution -> we may inherit from an interface which is not fully suitable for us, that's why we should: +* Interface segregation -> separate too big interface into a smaller one. +* Dependency inversion -> High level class DrawStrategy nad FireStrategy depends on low level classes RoundShot, ChainShot and GrapeShot. + +As you can see, the following implementation of strategy, with many small classes is the correct one. + + +___ + +## Broken Dependency inversion + +Dependency inversion -> High level class DrawStrategy nad FireStrategy depends on low level classes RoundShot, ChainShot and GrapeShot. + +Broken architecture + +___ + +## Modern approach + +Following the rule of SOLID, we almost reached a perfect architecture, but the one thing left. Class `DrawChainStrategy`, `DrawRoundStrategy`, and `DrawGrapeStrategy` is connected with a specific type like: `RoundShot` or `ChainShot`. We follow all SOLID rules, but still there is one thing to improve. **Do you know how we can improve it**? + +Good architecture + +___ + +## Template strategy + +By creating a template for `DrawStrategy` we remove all dependencies between specific types of ammunition and strategy: + +```C++ +template +class DrawStrategy { + virtual ~DrawStrategy() = default; + virtual void draw(const T&) const = 0; +}; +``` + + +Perfect architecture + + +___ + +## Full implementation + +```C++ +template +class FireStrategy { +public: + virtual void fire(const T& shot) = 0; +}; + +class SDLFireRoundShotStrategy : public FireStrategy { +public: + void fire(const RoundShot& shot) override; +}; + +class SDLFireChainShotStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override; +}; + +class SDLFireGrapeShotStrategy : public FireStrategy { +public: + void fire(const GrapeShot& shot) override; +}; +``` + +```C++ +class WAMFireRoundShotStrategy : public FireStrategy { +public: + void fire(const RoundShot& shot) override; +}; + +class WAMFirChainShotStrategy : public FireStrategy { +public: + void fire(const ChainShot& shot) override; +}; + +class WAMFireGrapeShotStrategy : public FireStrategy { +public: + void fire(const GrapeShot& shot) override; +}; +``` + + +___ + +```C++ +template +class DrawStrategy { +public: + virtual void draw(const T& shot) = 0; +}; + +class OpenGLDrawRoundShotStrategy : public DrawStrategy { +public: + void draw(const RoundShot& shot) override; +}; + +class OpenGLDrawChainShotStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override; +}; + +class OpenGLDrawGrapeShotStrategy : public DrawStrategy { +public: + void draw(const GrapeShot& shot) override; +}; +``` + +```C++ +class MetalDrawRoundShotStrategy : public DrawStrategy { +public: + void draw(const RoundShot& shot) override; +}; + +class MetalDrawChainShotStrategy : public DrawStrategy { +public: + void draw(const ChainShot& shot) override; +}; + +class MetalDrawGrapeShotStrategy : public DrawStrategy { +public: + void draw(const GrapeShot& shot) override; +}; +``` + + +___ + +```C++ +class RoundShot : public Ammunition { +public: + RoundShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 10; } + +private: + std::unique_ptr> _fireStrategy; + std::unique_ptr> _drawStrategy; +}; +``` + +```C++ +class ChainShot : public Ammunition { +public: + ChainShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 15; } + +private: + std::unique_ptr> _fireStrategy; + std::unique_ptr> _drawStrategy; +}; +``` + + +___ + +```C++ +class GrapeShot : public Ammunition { +public: + GrapeShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) + : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} + void fire() override { + _fireStrategy->fire(*this /* other args if needed*/); + } + void draw() override { + _drawStrategy->draw(*this /* other args if needed*/); + } + int getDamage() const override { return 20; } + +private: + std::unique_ptr> _fireStrategy; + std::unique_ptr> _drawStrategy; +}; +``` + +```C++ +int main() { + std::unique_ptr grapeShot = std::make_unique( + 100, std::make_unique(), std::make_unique()); + + grapeShot->fire(); + grapeShot->draw(); +} +``` + + +```bash +SDL Grape Shot Deal: 20 damage! +OpenGL Grape Shot: 100 +``` + + +___ + +## Exercise 1 + +* Go into directory Core and implement ShipStrategy as a template class +* Strategy should have one pure virtual function: Res handle(T&) +* Implement in directory Ship class ShipEasyDifficultLvlStrategy + * Each cargo supplies 2 sailors, so if you have 40 crew, you need 20 banas and 20 rum +* Implement NextDay by using strategy class +* Run code, and verify result + +___ + +## Exercise 2 + +* Implement in directory *Player* class *PalyerEasyDifficultLvlStrategy* +    * Strategy should roll a die (1-20): +    * If result is lower than 5 -> 0 DMG +    * If result is higher than 19 -> multiple damage by 2 +    * Damage should be rolled from (25 to 50) +    * deal damage to the ship and return how much damage was deal. Print it in console +* Add all necessary implementation to the *Enemy* file +* Test it + +___ + +## Exercise 3 + +* Implement new classes *EnemyHardDifficultLvlStrategy* and *ShipHardDifficultLvlStrategy* + * Strategy for Ship, should subtract cargo equal crew size +    * Strategy for Enemy should roll a die (1-20): +     * If result is lower than 3 -> 0 DMG + * If result is higher than 15 but not equal 20 -> multiple damage by 3 +     * If result is higher than 19 -> multiple damage by 3 +     * Damage should be rolled from (30 to 60) + * You shouldn't modify any existing file, except the main.cpp, where you should use a new strategy +* Run code and verify the result + +___ + +## Strategy is everywhere + +Before we go on to the next design pattern. I want to show you that `Strategy` is almost everywhere. Let's closely look at some STL algorithms: + +```C++ +template< class RandomIt > +void sort( RandomIt first, RandomIt last ); +``` + + +```C++ +template< class RandomIt, class Compare > +void nth_element( RandomIt first, RandomIt nth, RandomIt last, Compare comp ); +``` + + +```C++ +template< class InputIt1, class InputIt2, class OutputIt, class Compare > +OutputIt set_difference( InputIt1 first1, InputIt1 last1, + InputIt2 first2, InputIt2 last2, + OutputIt d_first, Compare comp ); +``` + + +**What is common for all of these functions?** + + + +___ + +STL library was designed by using design patterns, which gives this library a lot of flexibility. Users who want to use one of the STL algorithms, need to provide a strategy, mostly it will be a lambda object, which tells the algorithm how to treat a specific object. What's more, it uses modern by-value semantics, because we don't have any polymorphisms and virtual functions. The first version of STL was designed in 1994-1998, so this modern approach has 30 years :) I think we should learn a lot from this design, because not always need to solve everything by creating virtual classes. + +___ + +## Rewrite Difficult Strategy + +```C++ +template +class Ship : public TimeObserver { +public: + Ship(Time* time, DifficultLvlStrategy strategy, const std::string& name, int capacity, int crew); + // ohter public methods + +private: + // other members + DifficultLvlStrategy _strategy; +}; +``` + +```C++ +int main() { + auto strategy = [](Ship& ship[]){ + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; + }; + + Ship ship(&time, strategy, "Black Widow", 1000, 40); +} +``` + + +___ + +## If we don't like templates + +```C++ +class Ship : public TimeObserver { +public: + Ship(Time* time, std::function strategy, const std::string& name, int capacity, int crew); + // ohter public methods + +private: + // other members + std::function _strategy; +}; +``` + +```C++ +int main() { + auto strategy = [](Ship& ship[]){ + Ship::StatusCode status; + ship.unload("Rum", ship.crew() / 2, status); + Ship::StatusCode status2; + ship.unload("Banana", ship.crew() / 2, status2); + + return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; + }; + + Ship ship(&time,strategy, "Black Widow", 1000, 40); +} +``` + diff --git a/CreatingReliableSoftwareCpp/Presentation/template_method.md b/CreatingReliableSoftwareCpp/Presentation/template_method.md new file mode 100644 index 0000000..3abf0ac --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/template_method.md @@ -0,0 +1,577 @@ +# Template method + +This design pattern similar to strategy was used by you, even if you didn't notice that. This is a very popular approach whenever you have some functionality to be handled, but a base class shouldn't contain such logic. Let's check the following example: + +```C++ +class Player { +public: + virtual ~Player() = default; + void attack() { + std::cout << "Player " << _name << " turn\n"; + const auto [damage, attackedPlayer] = doAttack(); + std::cout << "Deal: " << damage << " to player: " << attackedPlayer->name() << "\n"; + } + +protected: + std::pair doAttack(); +}; +``` + + +```C++ +class Enemy : public Player { +protected: + std::pair doAttack() { + const Player* player = choosePlayer(); + const int damage = calculateDamage(player); + + return {damage, player}; + } +}; +``` + + +___ + +## UML + +Template method is a pattern, that allow you to create a sketch of algorithm, but details will be implemented by derived classes. Each class will decide how to acct in a various scenario, for instance enemy will use AI to choice an opponent to fight, but real user need to take an input from keyboard or mouse. + +Template method UML + +___ + +## GTEST + +A perfect example of template method is a GTEST. Whenever you inherit from `testing::Test` class you actually get possibility to overload two virtual functions: + +* void SetUp() +* void TearDown() + +A base class has two virtual function that are empty. So if you don't provide any implementation in the derived class, it will do nothing. This class may look like: + + +```C++ +class Test { +public: + Test() { + // do stuff related with test + SetUp(); + } + + ~Test() { + // cleanup stuff releated with test + TearDown(); + } + +protected: + virtual void SetUp() {} + virtual void TearDown() {} +}; +``` + + + +___ + +## Pirates once more + +To show you how useful this pattern is, let's implement a new functionality in our project. We need to implement a real battle! Because we played recently a baldurs gate 3 we decided to implement is as a turn based. We create the following rules to battle: + +* Each player (enemy or real user) roll a die who will start first +* After a roll we add initiative and based on the final result we decide who will begin +* Player can do 3 actions. It could choose any of these: + * Sail + * Attack + * Protect + * Boarding + * try to escape +* For instance, player can sail twice and then make boarding or attack 3 times with canons +* After one player finish, next player perform his turn +* Battle ends when all ships from enemy side will be sunk or enemy run away + + + +___ + +## Battle + +We create a simple class for battle. We can have few enemies and only one real player. The battle will ends when user or enemy espacded or one will be defeated. + +```C++ +class Battle { +public: + void battle() { + const std::vector& sorted = calculateOrderOfPlayers(_battleField->players()); + while (true) { + for (const auto* player : sorted) { + for (size_t i = 0 ; i < 3 ; ++i) { + const auto status = player->makeAction(*_battleField); + if (status == Action::Status::Escaped || + status == Action::Status::EnemyDefeated || + status == Action::Status::PlayerDefeated) { + return; + } + } + } + } + } + +private: + std::unique_ptr _battleField; +} +``` + + +___ + +## Player + +A player should have one function `makeAction` that should return `Status`. In this method, we will create a draft of algorithm. User need to choose which ship will make an action (Player may have few ships), then choose action and if action is equal `board` or `attack` he needs to choose enemy ship. + +```C++ +class Player { +public: + virtual ~Player() = default; + Action::Status makeAction(const BattleField& battleField) { + Ship* ship = chooseShip(battleField); + std::unique_ptr action = chooseAction(battleField); + if (action->type() == Action::Type::Board || action->type() == Action::Type::Attack) { + Ship* enemyShip = choosEnemyShip(battleField); + return (*action)(ship, enemyShip); + } + + return (*action)(ship, nullptr); + } + +protected: + virtual Ship* chooseShip(const BattleField& battleField) const = 0; + virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0; + virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0; +}; +``` + + +___ + +## Enemy + +Enemy class will be directed by an AI, so we implement some logic and based on it, it will decide what to do. + +```C++ +class Enemy : public Player { +public: +protected: + Ship* chooseShip(const BattleField& battleField) const override { + // Check the battlefield and choos the best ship + } + + Ship* chooseEnemyShip(const BattleField& battleField) const override { + // Check the battlefield and choose the weakened player ship which is closest + } + + std::unique_ptr chooseAction(const BattleField& battleField) const override { + // If don't have chance to whin escape + // If don't have ship in range sail + // If want to bard but it is too far sail + } +}; +``` + + +___ + +## User + +For user class we need to take an input from real person. In the future, we should also add possibility to revert user action and allow choosing once again. + +```C++ +class User : public Player { +public: +protected: + Ship* chooseShip(const BattleField& battleField) const override { + return GetUserInput(battleField, Type::ChoosePlayerShip); + } + + Ship* chooseEnemyShip(const BattleField& battleField) const override { + return GetUserInput(battleField, Type::ChooseEnemyShip); + } + + std::unique_ptr chooseAction(const BattleField& battleField) const override { + return GetUserInput(battleField, Type::ChooseAction); + } +}; +``` + + + +___ + +## A Few desing patterns + +Can you named all desing patterns I used in this example? + +* The Player class is a Factory method (that was obvious) +* The BattleField class is a context pattern. We wrap all necessary information about battle here +* The Action class is a command pattern. We wrap the logic in command, and also allow reverting changes. + +___ + +## Enemy AI + +We need to develop the `Enemy` AI. First, we focus on implementation of the algorithm that will choose the enemy ship. In such case we want to measure few parameters and based on the score choose the best (this is something like min-max algorithm). + +```C++ +Ship* chooseEnemyShip(const BattleField& battleField) const override final { + // Sort form highest value to the lowest + std::map> scores; + + for (const Ship* ship : battleField->getPlayerShip()) { + int score = 0; + score += calculateDistance(ship); + score += calculateThreat(ship); + score += calculatePossibleDamage(ship); + scores[score] = ship; + } + + return scores.begin()->second; +} +``` + + +We need to implement 3 methods: `calculateDistance`, `calculateThreat` and `calculatePossibleDamage`. We may choose how enemy will measure such things, because we can have a few types of enemy. + + +___ + +## Elite and Boss + +If we create a new hierarchy of derived class, we need to add implementation on how these classes should measure scores. It may depend on various things, like type of ammunition, protection, resistance, speed etc... + +```C++ +class Enemy : public Player { +protected: + // Don't allow to override this methods + Ship* chooseShip(const BattleField& battleField) const override final; + Ship* chooseEnemyShip(const BattleField& battleField) const override final; + std::unique_ptr chooseAction(const BattleField& battleField) const override final; + + // Define a new template methods + virtual int calculateDistance(Ship* ship) const = 0; + virtual int calculateThreat(Ship* ship) const = 0; + virtual int calculatePossibleDamage(Ship* ship) const = 0; +}; +``` + +```C++ +class FlameThrowerShip : public Enemy { +protected: + int calculateDistance(Ship* ship) override; + int calculateThreat(Ship* ship) override; + int calculatePossibleDamage(Ship* ship) override; +}; +``` + + +___ + +```C++ +int FlameThrowerShip::calculateDistance(Ship* ship) override { + // Because flame thrower has short range, we need to be very close to give a high score + const int distance = getDistance(_ship, ship); + if (distance < FLAME_THROWER_RANGE) { + return 10; + // We can reach in one turn + } else if (distance - _ship.speed() < FLAME_THROWER_RANGE) { + return 5; + } + + return 1; +} +``` + +```C++ +int FlameThrowerShip::calculateThreat(Ship* ship) override { + // The biggest threat for flameThroweShip is for instance a ship that deal explosive damage + // because may set on fire the oil + if (ship->hasExplosiveAmmo()) { + return 10; + } else if (ship->hasFlameThrower()) { + return 5; + } + + return 2; +} +``` + + +___ + +As we can see we can have few layers of template methods, and finally we achieve a final class (we should think about mark it as final also) which has implemented all logic. By using template method pattern we follow SOLID rules, because we have a single responsibility, our code is open to extension and don't demand modification of existing code (of course only if we add new types of enemies not modifying algorithm). And also we have dependency inversion, because low level modules depends on high level module's abstraction (base class defied interface → template methods that should be implemented) + +```C++ +int FlameThrowerShip::calculatePossibleDamage(Ship* ship) override { + // Check the type of shipe. for instance it may has some protection for fire (some black magic) + if (ship->hasProtectionToFire()) { + return 1; + } else if (ship->armor() > 50) { + return 5; + } + + return 10; +} +``` + + + +___ + +Lets summary everything on UML diagram + +Player UML +___ + +## Pros and cons + +Template method is a powerful design pattern, because we can hide from the end user that we use polymorphism. In case of usage of class `Player` we have one public method `makeAction` which is not virtual. The whole magic is hidden in protected methods. What's more template method is very flexible, for class `Player` we decided to mark class as `final` because we implement everything. But class `enemy` which is directed by `AI` create another set of `proceted virtual` function and demands form derived class to implement them. We can easily extend this code by adding new types of enemies. Unfortunately, we can't do this with new virtual functions. Whenever we add a new template method we need to implement it in every derived class (or leave it empty as in GTEST, but this is rarely scenario). + +**To summarize: Use this pattern for adding new implementations rather than new functions.** + + +___ + +## Exericse 1 + +* Go into directory Player and implement a base class Player, this class should have 3 public virtual members: + * virtual int attack(Ship& playerShip) = 0; + * virtual Ship& getShip() = 0; + * virtual const Ship& getShip() const = 0; +* Add to Player class a non-virtual function: Action::Status makeAction(const BattleField& battleField);. +* Add to Player class 2 virtual protected function (a template method): + * virtual Ship* chooseEnemyShip(const BattleField& battleField) const = 0; + * virtual std::unique_ptr chooseAction(const BattleField& battleField) const = 0; +* Implement some logic in method makeAction based on 2 template method: + * To simplify, Player can only attack or defense + * Use class Command from directory Battle + +___ + +## Exercise 2 + +* Implement class RealUser which will inherit from class Player. Implement both template methods: + * Don't make it too complex, just ask user about action and type of ammunition, then you can randomly choose a damage or if missed. + * To increase defense, you can use method setArmor from Ship class. +* Rewrite class Enemy to inherit from Player add also implementation for both template methods +* Uncomment code in main, check if you can perform a real battle! + +___ + +## A little abour command pattern + +Similar to `Strategy` or `Template method` we also have a `Command` pattern. This pattern is quite small, so I decided to talk about it in this section. The general idea is to provide functionality that we can do and optionally an option to undo this action. + +Template method UML + +___ + +## Battle Actions + +A perfect example of `command pattern` is a battle between two ships. We mentioned earlier that we want to give a few different actions to choose during the combat: + +* Sail +* Attack +* Protect +* Boarding +* try to escape + +Each player can do three actions per turn. Because a player may decide that he don't like his choose, we may add the possibility to undo an action in a single turn. + +___ + +## Action + +Let's create an interface for a `Action`. We should have two method `run` for running a command and `undo` for reverting. We may also have an additional info like `Status` in return statement. Keep in mind if this status may change it may be not a good idea to have it inside `Action` class because whenever we will add new field it needs to recompile all classes that include a `Action` class. A second disadvantage is that sometimes, we may not have access to this interface, so we can't add a new `Status`. I decided to put `Status` inside class `Action` because I know it will not be changed. If `Status` may change, I will put it in separate file which will be visible for all programmers or move the logic about if player `escaped` or was `defeated` outside `Action` class. + +```C++ +class Action { +public: + // This should not change so we can leave it here + enum class Status { + Escaped, + Defeated, + Nothing, + }; + + virtual Status run(Player* player, std::unique_ptr& enemyShip) = 0; + virtual void undo(Player& player, std::unique_ptr& enemyShip) = 0; +}; +``` + + +___ + +To implement class `Attack` we need to store one variable: the amount of damage. This is needed to revert a `takeDamage` operation. If we also simulate that part of cargo will be destroyed, we also need to keep info how many cargoes we subtract or create a `snapshot` of cargo and then apply this snapshot during revert. It depends on how big such snapshot will be and how hard it will be to undo the operation of destroying part of cargo. + +```C++ +class Attack : public Action { +public: + Status run(Player* player, std::unique_ptr& enemyShip) override { + _damage = calculateDamage(player); + std::cout << "Player Ship: " << player->getShip().name() << " attack: " << enemyShip->name() << '\n'; + if (_damage) { + enemyShip->takeDamage(_damage); + std::cout << "Dealed damage: " << _damage << '\n'; + return enemyShip->durability() <= 0 ? Status::Defeated : Status::Nothing; + } + + std::cout << "Missed\n"; + return Status::Nothing; + } + + void undo(Player* player, std::unique_ptr& enemyShip) override { + enemyShip->repair(_damage); + } + +private: + int _damage = 0; +}; +``` + + +___ + +## How to revert + +If we want to allow a user to revert operations, we need to store them. Because we decided to revert action only in a single turn, we need to keep max 3 actions. If the user ends the turn, we will clear it, so it will not be possible to undo action from the previous turn. + +```C++ +while (true) { + for (auto* player : players) { + std::vector> actions; + while (!endOfTurn()) { + const auto userInput = getuserInput(); + if (userInput.revertAction() && !actions.empty()) { + actions.back()->undo(); + continue; + } else if (userInput.revertAction() && actions.empty()) { + std::cout << "Can't undo more actions\n"; + continue; + } + + actions.push_back(player->makeAction(battefield, userInput)); + if (actions.back().status() == Action::Status::Escaped || + actions.back().status() == Action::Status::Defeated) { + std::cout << "The battle ends!\n"; + return; + } + } + } +} +``` + + +___ + +As you can see, we can easily implement a battle and give a user possibility to revert actions. This pattern also gives us an easy mechanism to add a new action in the future if we will want to extend a combat possibility. + +```C++ +class Boarding : public Action { +public: + Status run(Player* player, std::unique_ptr& enemyShip) override { + // Based on number and type of crew calculate a boarding result + const bool success = doBoarding(player->getShip(), enemyShip); + std::cout << "Player Ship: " << player->getShip().name() << " boarding: " << enemyShip->name() << '\n'; + if (success) { + // Hold a pointer to newaly added ship in case of revert + _shipPtr = player->addShip(std::move(enemyShip)); + std::cout << "Ship: " << _shipPtr->name() << "conquered\n"; + return Status::Defeated; + } + + // We lost some crew, but we escaped, so player still not loose + std::cout << "Boarding failed!\n"; + return Status::Nothing; + } + + void undo(Player* player, std::unique_ptr& enemyShip) override { + if (_shipPtr) { + enemyShip = player->moveShip(_shipPtr); + } + } + +private: + Ship* _shipPtr{nullptr}; +}; +``` + + +___ + +## Modern approach + +We should prefer `by-value` semantic. For `template method` pattern we use `virtual protected methods`, so it will be a tricky to rewrite it by using `by-value` semantic, but for `command` pattern this will be easy. We can check the `std::transform` method, specifically `UnaryOperator`. This is nothing but `command` pattern in modern approach. + +```C++ +template +OutputIt transform(InputIt first1, InputIt last1, + OutputIt d_first, UnaryOp unary_op) +{ + while (first1 != last1) + *d_first++ = unary_op(*first1++); + + return d_first; +} +``` + + +___ + +Whenever we want to do some action, but we don't need to undo it, we can choose faster and better approach with `std::function` and in c++23 `std::move_only_function`. This approach don't require virtual function and creating a base class for command, and we don't need to keep pointer or reference to base class. We will keep command by value, so it will be safer and probably even faster. + +```C++ +Status run(Player* player, std::unique_ptr& enemyShip) { + const auto damage = calculateDamage(player); + std::cout << "Player Ship: " << player->getShip().name() << " attack: " << enemyShip->name() << '\n'; + if (damage) { + enemyShip->takeDamage(damage); + std::cout << "Dealed damage: " << damage << '\n'; + return enemyShip->durability() <= 0 ? Status::Defeated : Status::Nothing; + } + + std::cout << "Missed\n"; + return Status::Nothing; +} +``` + + +```C++ +while (true) { + for (auto* player : players) { + std::vector& enemyShip)>> actions; + while (!endOfTurn()) { + const auto userInput = getuserInput(); + if (userInput.revertAction() && !actions.empty()) { + actions.back().undo(); + continue; + } else if (userInput.revertAction() && actions.empty()) { + continue; + } + + actions.push_back(player->makeAction(battefield, userInput)); + if (actions.back().status() == Action::Status::Escaped || actions.back().status() == Action::Status::Defeated) { + return; + } + } + } +} +``` + + +___ + +## Exericse 3 + +* Go into directory Battle and implement class Escape which will inherit from class Action. + * Roll a die (1 to 20), if value is bigger than 12 a player successfully escaped. +* Noticed that we shouldn't modify existing code, but we need to do this in two place, where? +* How would you refactor the code? diff --git a/CreatingReliableSoftwareCpp/Presentation/testing_gmock.md b/CreatingReliableSoftwareCpp/Presentation/testing_gmock.md new file mode 100644 index 0000000..cdd500a --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/testing_gmock.md @@ -0,0 +1,681 @@ +## GMOCK + +* Avoid depricated MOCK_METHDn and MOCK_CONST_METHODn +* Use MOCK_METHOD() +* Use Times when want to expect how many times function was call (by default it expect once) +* Use AtLeast To expect n or more calls +* Use WillOnce and WillRepeatedly to perform some actions +* Use SaveArg to save argument provided to mock function +* Use InSequence to perform sequenced actions +* You can mock non-virtual function +* Use StrictMock when you need to be sure about every call of this mock +* Use NiceMock when you don't care about function called for this mock +* Use ON_CALL and WillByDefault to perform some default action, when function will be called +* Use DoAll to perform more then one action +* Use Invoke to invoke funtion/lambda + + +___ + +## MOCK_METHOD + +MOCK_METHOD(return_type, name_of_function, arguments, parameters) + + +```C++ +class MockTurtle : public Turtle { + public: + ... + MOCK_METHOD(void, PenUp, (), (override)); + MOCK_METHOD(void, PenDown, (), (override)); + MOCK_METHOD(void, Forward, (int distance), (override)); + MOCK_METHOD(void, Turn, (int degrees), (override)); + MOCK_METHOD(void, GoTo, (int x, int y), (override)); + MOCK_METHOD(int, GetX, (), (const, override)); + MOCK_METHOD(int, GetY, (), (const, override)); +}; +``` + + +* const - Makes the mocked method a const method. Required if overriding a const method. +* override - Marks the method with override. Recommended if overriding a virtual method. +* noexcept - Marks the method with noexcept. Required if overriding a noexcept method. +* Calltype(...) - Sets the call type for the method (e.g. to STDMETHODCALLTYPE), useful in Windows. +* ref(...) - Marks the method with the reference qualification specified. Required if overriding a method that has reference qualifications. Eg ref(&) or ref(&&). + +___ + +## Test class + +Let's use once agin `Foo` and `Bar` class + +```C++ +class Bar { +public: + virtual ~Bar() = default; + + virtual bool doOtherStuff(const std::string& str, int*, const std::vector& vec) const { + // do some stuff + return false; + } +}; + +class MockBar : public Bar { +public: + ~MockBar() override = default; + MOCK_METHOD(bool, doOtherStuff, (const std::string&, int*, const std::vector&), (const override)); +}; + +class Foo { +public: + Foo(std::unique_ptr bar) + : val_(std::make_unique(42)), + bar_(std::move(bar)) {} + + bool doSth(const std::string& str) { + return bar_->doOtherStuff(str, val_.get(), {1, 2, 3, 4}); + } + +private: + std::unique_ptr val_; + std::unique_ptr bar_; +}; + +``` + +___ + +## Times + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + EXPECT_CALL(*mockBarPtr, doOtherStuff).Times(2); + + foo.doSth("Sth1"); + foo.doSth("Sth2"); +} +``` + + + +___ + +## AtLeast + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + EXPECT_CALL(*mockBarPtr, doOtherStuff).Times(testing::AtLeast(2)); + + foo.doSth("Sth1"); + foo.doSth("Sth2"); +} +``` + + +___ + +## WillOnce and WillRepeatedly + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + // Dont do that. Next exceptaion will override first one! + // EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(Return(false)); + // EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(Return(true)); + + // Correct one! + EXPECT_CALL(*mockBarPtr, doOtherStuff) + .WillOnce(Return(false)) + .WillOnce(Return(true)); + + EXPECT_FALSE(foo.doSth("Sth1")); + EXPECT_TRUE(foo.doSth("Sth2")); +} +``` + + +```C++ +TEST(ExampleTest, ShouldTest) { + //... + EXPECT_CALL(*mockBarPtr, doOtherStuff) + .WillOnce(Return(false)) + .WillOnce(Return(true)) + .WillRepeatedly(Return(false)); + + EXPECT_FALSE(foo.doSth("Sth1")); + EXPECT_TRUE(foo.doSth("Sth2")); + EXPECT_FALSE(foo.doSth("Sth3")); + EXPECT_FALSE(foo.doSth("Sth4")); +} +``` + + + +___ + + +## Save arguments provided to functions (1) + +We want to capture parameters provided to bar function. + +```C++ +class Bar { +public: + virtual ~Bar() = default; + + virtual bool doOtherStuff(const std::string& str, int val, const std::vector& vec) { + // do some stuff + return false; + } +}; + +class MockBar : public Bar { +public: + ~MockBar() override = default; + MOCK_METHOD(bool, doOtherStuff, (const std::string&, int, const std::vector&), (override)); +}; + +class Foo { +public: + Foo(std::unique_ptr bar) + : bar_(std::move(bar)) {} + + bool doSth(const std::string& str) { + return bar_->doOtherStuff(str, 42, {1, 2, 3, 4}); + } + +private: + std::unique_ptr bar_; +}; +``` + + +___ + +## Save arguments provided to functions (2) + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + int val; + std::vector vec; + // Also return true, instead of false like in original function + EXPECT_CALL(*mockBarPtr, doOtherStuff). + WillOnce(DoAll(SaveArg<1>(&val), SaveArg<2>(&vec), Return(true))); + + EXPECT_TRUE(foo.doSth("Sth")); + EXPECT_EQ(val, 42); + const auto expected = std::vector{1, 2, 3, 4}; + EXPECT_EQ(vec, expected); +} +``` + + +``` +[----------] 1 test from ExampleTest +[ RUN ] ExampleTest.ShouldTest +[ OK ] ExampleTest.ShouldTest (0 ms) +[----------] 1 test from ExampleTest (0 ms total) +``` + + +___ + +## Save pointer provided to function + +```C++ +class Bar { +public: + virtual ~Bar() = default; + + virtual bool doOtherStuff(const std::string& str, int* val, const std::vector& vec) { + // do some stuff + return false; + } +}; + +``` + + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + int* val; + std::vector vec; + // Capture by SaveArgPointee + EXPECT_CALL(*mockBarPtr, doOtherStuff). + WillOnce(DoAll(SaveArgPointee<1>(val), SaveArg<2>(&vec), Return(true))); + + EXPECT_TRUE(foo.doSth("Sth")); + EXPECT_EQ(*val, 42); + const auto expected = std::vector{1, 2, 3, 4}; + EXPECT_EQ(vec, expected); +} +``` + + +___ + +## Capture non-copyable object + +* There is a problem to capture non-copyable objects because we can't copy them! +* We need to make an ugly hack to capture this object -> move this object to our variable. +* This will be problematic when this object needs to be used in a function, but it will work when we used in on mock function because the argument will not be used later. + +```C++ +// Bar +bool doSth(const std::string& str) { + return bar_->doOtherStuff(str, std::make_unique(42), {1, 2, 3, 4}); +} + +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + std::unique_ptr val; + EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(DoAll(MoveObject<1>(&val), Return(true))); + + EXPECT_TRUE(foo.doSth("Sth")); + EXPECT_EQ(*val, 42); +} +``` + + +* There is no such function in gtest, so we need to implement somehow MoveObject + +___ + +## Move object + +```C++ +ACTION_TEMPLATE(MoveObject, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(pointer)) { + using PtrType = std::remove_cv_t(args))>>; + // This unique_ptr will be deleted after function end + // so we need to capture it + *pointer = std::move(const_cast(std::get(args))); +} +``` + + +When we only need a raw pointer from `unique_ptr` + + +```C++ +ACTION_TEMPLATE(ExtractPtr, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(pointer)) { + *pointer = std::get(args).get(); +} +``` + +But we will lost it, because `unique_ptr` will be deleted after exit form mock function! + + + + +This is a very old way, the better one is to just lambda instead, I will show this later + +___ + + +## InSequence (1) + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + testing::Sequence seq; + // Four expectations in sequence. There is no override like before + for (int i = 0; i < 4; ++i) { + EXPECT_CALL(*mockBarPtr, doOtherStuff) + .InSequence(seq) + .WillOnce(Return(i % 2)); + } + + EXPECT_FALSE(foo.doSth("Sth1")); + EXPECT_TRUE(foo.doSth("Sth2")); + EXPECT_FALSE(foo.doSth("Sth3")); + EXPECT_TRUE(foo.doSth("Sth4")); +} +``` + + + +___ + +## InSequence (2) + +This has the same behaviour like previous example + + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + { + testing::InSequence seq; + // Four expectations in sequence. There is no override like before + for (int i = 0; i < 4; ++i) { + EXPECT_CALL(*mockBarPtr, doOtherStuff).WillOnce(Return(i % 2)); + } + } + + EXPECT_FALSE(foo.doSth("Sth1")); + EXPECT_TRUE(foo.doSth("Sth2")); + EXPECT_FALSE(foo.doSth("Sth3")); + EXPECT_TRUE(foo.doSth("Sth4")); +} +``` + + +___ + +## InSequence - branch out + +```C++ +testing::Sequence s1, s2; + +EXPECT_CALL(foo, Method1()) + .InSequence(s1, s2); +EXPECT_CALL(bar, Method2()) + .InSequence(s1); +EXPECT_CALL(bar, Method3()) + .InSequence(s2); +EXPECT_CALL(foo, Method4()) + .InSequence(s2); +``` + + +```C++ + +---> Method2 (seq1) +(seq1 seq2) | + Method1----| + | + +---> Method3 ---> Method4 (seq2) +``` + + +___ + +## mock non-virtual function (1) + +We have the following class + + +```C++ +struct Packet {}; + +class ConcretePacketStream { +public: + void AppendPacket(Packet* new_packet) {} + const Packet* GetPacket(size_t packet_number) const {} + size_t NumberOfPackets() const {} +}; +``` + + + +___ + +## mock non-virtual function (2) + +We can create mock and instead of dynamic polymorphism use static polymorphism (templates) + + +```C++ +// A mock packet stream class. It inherits from no other, but defines +// GetPacket() and NumberOfPackets(). No need to add all functions like: AppendPacket +class MockPacketStream { +public: + // Do not add override! + MOCK_METHOD(const Packet*, GetPacket, (size_t packet_number), (const)); + MOCK_METHOD(size_t, NumberOfPackets, (), (const)); +}; + +// Now we can use static polymorphism to insert Mock class in a test, +// and real class in production code +template +class PacketReader { +public: + const Packet* ReadPackets(PacketStream* stream, size_t packet_num) { + return stream->GetPacket(packet_num); + } +}; +``` + + + +___ + +## mock non-virtual function (3) + +Now in test we can use mock class + + +```C++ +TEST(TestNonVirtualMethod, ShouldTest) { + MockPacketStream mock_stream; + EXPECT_CALL(mock_stream, GetPacket).WillOnce(Return(nullptr)); + + PacketReader reader; + reader.ReadPackets(&mock_stream, 12); +} +``` + + +And in production code the real one + + +```C++ +ConcretePacketStream stream; +PacketReader reader; + +auto* packet = reader.ReadPackets(&stream, 12); +``` + + + + +___ + +## StrictMock + +The usage of `StrictMock` is similar to normal mock, except that it makes all uninteresting calls failures: + +```C++ +class MockFoo : public Foo { + //... + MOCK_METHOD(void, Fun1, (), (override)); + MOCK_METHOD(void, Fun2, (), (override)); +}; + +TEST(SimpleTest, TestSth) { + StrictMock mock_foo; + // When Fun2 will be also called it cause test failed + EXPECT_CALL(mock_foo, Fun1()); +} +``` + + + +___ + + +## NiceMock + +* The usage of `StrictMock` is similar to normal mock, except that it reject uninteresting calls (we will not see them while run test) +* NiceMock and StrictMock only affects uninteresting calls (calls of methods with no expectations); they do not affect unexpected calls (calls of methods with expectations, but they don’t match) + +```C++ +class MockFoo : public Foo { + //... + MOCK_METHOD(void, Fun1, (), (override)); + MOCK_METHOD(void, Fun2, (), (override)); +}; + +TEST(SimpleTest, TestSth) { + NiceMock mock_foo; + // When Fun2 will be also called it will be ignored + EXPECT_CALL(mock_foo, Fun1()); +} +``` + + + +___ + +## ON_CALL and WillByDefault + +We can do whatever we want when some mock method will be called. We can even save all types of arguments, or move objects to test them (call later). + + +When a function takes for instance non-copyable callback, and we need to capture it to continue testing, we can move it inside lambda! This is much better than the tricky template shown before. + + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + // Nice way to capture all types of argument + int* ptr; + ON_CALL(*mockBarPtr, doOtherStuff) + .WillByDefault([&ptr](const std::string&, int* new_ptr, const std::vector&) { + ptr = new_ptr; + return true; + }); + + EXPECT_CALL(*mockBarPtr, doOtherStuff); + EXPECT_TRUE(foo.doSth("Sth1")); + EXPECT_EQ(*ptr, 42); +} +``` + + + +___ + +## DoAll + +If we need to combine a few actions, we can do this inside `doAll(...)` + + +```C++ +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + + std::string str; + int* ptr; + + EXPECT_CALL(*mockBarPtr, doOtherStuff) + .WillOnce(DoAll( + SaveArg<0>(&str), + SetArgPointee<1>(80), // Replace pointer value + SaveArgPointee<1>(ptr), // Capture value of arg1 + Return(true))); + + EXPECT_TRUE(foo.doSth("Sth1")); + EXPECT_EQ(str, "Sth1"); + EXPECT_EQ(*ptr, 80); +} +``` + + + +___ + +## Invoke (1) + +Let's change a little `MockBar` + + +```C++ +class MockBar : public Bar { +public: + ~MockBar() override = default; + MOCK_METHOD(bool, doOtherStuff, (const std::string&, int*, std::function), (const override)); +}; +``` + +Now we want to run callback. We can do this without capture it! + + +```C++ +EXPECT_CALL(*mockBarPtr, doOtherStuff) + .WillOnce(DoAll( + SaveArg<0>(&str), + SetArgPointee<1>(80), // Replace pointer value + SaveArgPointee<1>(ptr), // Capture value of arg1 + InvokeArgument<2>(500), // Invoke callback + Return(true))); +``` + + + +___ + +## Invoke (2) + +```C++ +class Helper { +public: + void validateArguments(const std::string& str, int* ptr, std::function callback) { + ASSERT_EQ(str.size(), 4); + EXPECT_EQ(str, "Sth1"); + ASSERT_TRUE(ptr); + EXPECT_EQ(*ptr, 42); + ASSERT_TRUE(callback); + callback(50); // We can invoke callback here + } +}; + +TEST(ExampleTest, ShouldTest) { + auto mockBar = std::make_unique(); + auto* mockBarPtr = mockBar.get(); + Foo foo(std::move(mockBar)); + Helper helper; + + EXPECT_CALL(*mockBarPtr, doOtherStuff) + .WillOnce(DoAll( + Invoke(&helper, &Helper::validateArguments), + Return(true))); + + EXPECT_TRUE(foo.doSth("Sth1")); +} +``` + + +___ + +## Exercise 2 + +Open project **SHM** and write test for class `Store.cpp` which will test it's interface. You need to mock `Cargo` class and check if you get correct result based on what you mock. Write mock in `Presentation/exercises/SHM/tests/CargoMock.h` for `Cargo` class. + +Go to `Presentation/exercises/SHM/build/test` and run test using command `./SHM_Tests`. \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/Presentation/testing_gtest.md b/CreatingReliableSoftwareCpp/Presentation/testing_gtest.md new file mode 100644 index 0000000..814b464 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/testing_gtest.md @@ -0,0 +1,473 @@ +## GTEST + +* TEST(...) Create single test +* TEST_F(...) Use fixtures for testing +* TEST_P(...) Use package of parameters for all tests +* EXPECT_* Check condition +* ASSERT_* Check condition and stop test if fails. +* EXPECT_PRED* Allow to create own predicate, and other format of display errors +* testing::FloatLE and testing::DoubleLE can be use for EXPECT_PRED_FORMAT* for floating-point comparison +* GTEST_SKIP() can be use for skipping the test +* EXPECT_EXIT can test signall which end program, like: SIGKILL +* EXPECT_THROW will catch and check exceptions + +___ + +## TEST + +To create a simple test we can use `TEST` macro + +```C++ +class Calculator { +public: + int add(int lsh, int rhs) { return lhs + rhs; } +}; + +TEST(CalculatorTest, ShouldAddTwoValues) { + Calculator calc; + EXPECT_EQ(calc.add(10, 20), 10 + 20); +} +``` + + +___ + +## TEST_F + +If we need to set up some structures before creating a test, we shouldn't do this every test, but instead create a test fixture that sets everything up during construction. It also provides useful methods which we can use in every test. This fixture is created first, then a test starts. + +```C++ +class DownloaderTest : public testing::Test { +public: + DownloaderTest() { + _connection = std::make_unique(); + _connection->set_certificates(_certificateMock); + _downloader = std::make_unqiue(_connection.get()); + } + + CertificateMock _certificateMock; + std::unqiue_ptr _connection; + std::unique_ptr _downloader; +}; + +TEST_F(DownloaderTest, shouldDownloadImage) { + EXPECT_CALL(_certificateMock, validate).WillOnce(Return(true)); + EXPECT_TRUE(_downloader.downloadImage("exampleImg")); +} +``` + +___ + +## TEST_P (1) + +Whenever we need to run the same test but with a different set of parameters we should use `TEST_P` + +```C++ +class Firewall { +public: + virtual ~Firewall() = default; + + virtual bool block(const std::string& ipAddress) = 0; +}; + +class WhiteListFirewall : public Firewall { +public: + void addToWhitelist(const std::string& ipAddress) { + whiteList_.push_back(ipAddress); + } + + bool block(const std::string& ipAddress) override { + return std::find(cbegin(whiteList_), cend(whiteList_), ipAddress) == std::cend(whiteList_); + } + +private: + std::vector whiteList_; +}; + +``` + + +___ + +## TEST_P (2) + +```C++ +class TestFirewall : public testing::TestWithParam> { +}; + +TEST_P(TestFirewall, ShouldFilterAdresses) { + auto firewall = std::make_unique(); + firewall->addToWhitelist("192.201.1.12"); + firewall->addToWhitelist("192.122.1.12"); + + const auto& [address, isBlock] = GetParam(); + EXPECT_EQ(firewall->block(address), isBlock); +} + +INSTANTIATE_TEST_SUITE_P(TestFirewallParameters, + TestFirewall, + testing::Values( + std::pair{"192.178.1.12", true}, + std::pair{"192.144.1.12", true}, + std::pair{"192.188.1.12", true}, + std::pair{"192.201.1.12", false}, + std::pair{"192.122.1.12", false})); +``` + + +___ + +## TEST_P (3) + +``` +[----------] 5 tests from TestFirewallParameters/TestFirewall +[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/0 +[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/0 (0 ms) +[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/1 +[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/1 (0 ms) +[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/2 +[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/2 (0 ms) +[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/3 +[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/3 (0 ms) +[ RUN ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/4 +[ OK ] TestFirewallParameters/TestFirewall.ShouldFilterAdresses/4 (0 ms) +[----------] 5 tests from TestFirewallParameters/TestFirewall (0 ms total) +``` + + +___ + +## TEST_P - inherit from other test + +```C++ +class BlackListFirewall : public Firewall { +public: + void addToBlacklist(const std::string& ipAddress) { + blackList_.push_back(ipAddress); + } + + bool block(const std::string& ipAddress) override { + return std::find(cbegin(blackList_), cend(blackList_), ipAddress) != std::cend(blackList_); + } + +private: + std::vector blackList_; +}; + +class BaseTestBlackListFirewall : public testing::Test { +public: + void SetUp() override { + firewall_ = std::make_unique(); + + firewall_->addToBlacklist("192.201.1.12"); + firewall_->addToBlacklist("192.122.1.12"); + firewall_->addToBlacklist("192.178.1.12"); + firewall_->addToBlacklist("192.144.1.12"); + firewall_->addToBlacklist("192.188.1.12"); + } + + BlackListFirewall* firewall() const { return firewall_.get(); } + +private: + std::unique_ptr firewall_; +}; +``` + + +___ + +## testing::WithParamInterface + +```C++ +class TestBlackListFirewall : public BaseTestBlackListFirewall, + public testing::WithParamInterface { +}; + +TEST_P(TestBlackListFirewall, ShouldBlock) { + EXPECT_TRUE(firewall()->block(GetParam())); +} + +INSTANTIATE_TEST_SUITE_P(TestBlackListFirewallPackage, + TestBlackListFirewall, + testing::Values( + "192.178.1.12", + "192.144.1.12", + "192.188.1.12", + "192.201.1.12", + "192.122.1.12")); +``` + + +``` +[----------] 5 tests from TestBlackListFirewallPackage/TestBlackListFirewall +[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/0 +[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/0 (0 ms) +[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/1 +[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/1 (0 ms) +[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/2 +[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/2 (0 ms) +[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/3 +[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/3 (0 ms) +[ RUN ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/4 +[ OK ] TestBlackListFirewallPackage/TestBlackListFirewall.ShouldBlock/4 (0 ms) +[----------] 5 tests from TestBlackListFirewallPackage/TestBlackListFirewall (0 ms total) +``` + + +___ + + +## EXPECT_PRED* - own error message + +```C++ +testing::AssertionResult isBlocked(Firewall* firewall, const std::string& addr, bool block) { + if (firewall->block(addr) == block) { + return testing::AssertionSuccess(); + } + + return testing::AssertionFailure() << addr << (block ? " should be blocked" : " shouldn't be blocked"); +} + +TEST_F(BaseTestBlackListFirewall, ShouldFilter) { + EXPECT_TRUE(isBlocked(firewall(), "192.188.1.12", true)); + EXPECT_TRUE(isBlocked(firewall(), "192.188.1.12", false)); +} +``` + + +``` +[----------] 1 test from BaseTestBlackListFirewall +[ RUN ] BaseTestBlackListFirewall.ShouldFilter +/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:101: Failure +Value of: isBlocked(firewall(), "192.188.1.12", false) + Actual: false (192.188.1.12 shouldn't be blocked) +Expected: true +[ FAILED ] BaseTestBlackListFirewall.ShouldFilter (0 ms) +[----------] 1 test from BaseTestBlackListFirewall (0 ms total) +``` + + +___ + +## EXPECT_PRED* - check string + +```C++ +for (const auto& str : {"There is a nauka!", "nauka.programowania.ma@gmail.com"}) { + EXPECT_THAT(str, testing::MatchesRegex( + "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$")); + + ASSERT_THAT(str, testing::HasSubstr("nauka")); +} +``` + + +``` +[ RUN ] ExampleTest.ShouldFail +/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:104: Failure +Value of: str +Expected: matches regular expression "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$" + Actual: 0x5625cd2c080c pointing to "There is a nauka!" (of type char const*) +[ FAILED ] ExampleTest.ShouldFail (0 ms) +[----------] 1 test from ExampleTest (0 ms total) +``` + + +___ + +## EXPECT_PRED* - comparison of floating point numbers + +```C++ +std::vector> vec{ + {43.45, 43.45}, {44.34321, 45.1234}, {1.12, 1.1199999}}; + +for (const auto& [lhs, rhs] : vec) { + EXPECT_PRED_FORMAT2(testing::FloatLE, lhs, rhs); + EXPECT_PRED_FORMAT2(testing::DoubleLE, lhs, rhs); +} +``` + + +``` +[ RUN ] ExampleTest.ShouldFail +/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:108: Failure +Expected: (lhs) <= (rhs) + Actual: 1.1200000000000001 vs 1.1199999 +[ FAILED ] ExampleTest.ShouldFail (0 ms) +[----------] 1 test from ExampleTest (0 ms total) + +[----------] Global test environment tear-down +``` + + +___ + +## GTEST_SKIP + +```C++ +TEST(ExampleTest, ShouldFail) { + GTEST_SKIP() << "Skipping test PR12345"; + + std::vector> vec{ + {43.45, 43.45}, {44.34321, 45.1234}, {1.12, 1.1199999}}; + + for (const auto& [lhs, rhs] : vec) { + EXPECT_PRED_FORMAT2(testing::FloatLE, lhs, rhs); + EXPECT_PRED_FORMAT2(testing::DoubleLE, lhs, rhs); + } +} +``` + + +``` +[ RUN ] ExampleTest.ShouldFail +/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:102: Skipped +Skipping test PR12345 +``` + + +___ + +## Skipp all fixture + +```C++ +class BaseTestBlackListFirewall : public testing::Test { +public: + void SetUp() override { + GTEST_SKIP() << "Skipping test PR12345"; + firewall_ = std::make_unique(); + + firewall_->addToBlacklist("192.201.1.12"); + firewall_->addToBlacklist("192.122.1.12"); + firewall_->addToBlacklist("192.178.1.12"); + firewall_->addToBlacklist("192.144.1.12"); + firewall_->addToBlacklist("192.188.1.12"); + } + + BlackListFirewall* firewall() const { return firewall_.get(); } + +private: + std::unique_ptr firewall_; +}; +``` + + +``` +[----------] 3 tests from BaseTestBlackListFirewall +[ RUN ] BaseTestBlackListFirewall.ShouldFilter +/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:76: Skipped +Skipping test PR12345 +[ SKIPPED ] BaseTestBlackListFirewall.ShouldFilter (0 ms) +[ RUN ] BaseTestBlackListFirewall.ShouldNotFilter +/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:86: Skipped +Skipping test PR12345 +[ SKIPPED ] BaseTestBlackListFirewall.ShouldNotFilter (0 ms) +[ RUN ] BaseTestBlackListFirewall.ShouldReportError +/home/mateusz.adamski/Documents/repoGit/advanced_cpp/examples/gtest.cpp:96: Skipped +Skipping test PR12345 +[ SKIPPED ] BaseTestBlackListFirewall.ShouldReportError (0 ms) +[----------] 3 tests from BaseTestBlackListFirewall (0 ms total) +``` + + +___ + +## EXPECT_EXIT and EXPECT_THROW + +```C++ +TEST(MyDeathTest, NormalExit) { + EXPECT_EXIT(normalExit(), testing::ExitedWithCode(0), "Succes"); +} + +TEST(MyDeathTest, KillProcess) { + EXPECT_EXIT(killProcess(), testing::KilledBySignal(SIGKILL), + "Sending myself unblockable signal"); +} + +void foo(int val) { + if (val != 42) { + throw std::runtime_error("Wrong number"); + } +} + +TEST(MyDeathTest, NormalExit) { + EXPECT_THROW(foo(5), std::runtime_error); +} +``` + + +___ + +## Sharing Resources Between Tests + +```C++ +struct Foo { + std::string name_; + int value_; +}; + +class FooTest : public testing::Test { +public: + // Shared + static void SetUpTestSuite() { + if (!shared_resource_) { + shared_resource_ = std::make_unique(); + shared_resource_->name_ = "Shared Foo!\n"; + shared_resource_->value_ = 42; + } + } + + // Shared + static void TearDownTestSuite() { + shared_resource_ = nullptr; + } + + // Per-test + void SetUp() override {} + + // Per-test + void TearDown() override {} + +protected: + inline static std::unique_ptr shared_resource_; +}; + +TEST_F(FooTest, Test1) { + EXPECT_EQ(FooTest::shared_resource_->name_, "Shared Foo!\n"); + EXPECT_EQ(FooTest::shared_resource_->value_, 42); + FooTest::shared_resource_->name_ = "Foo!!!"; + FooTest::shared_resource_->value_ = 13; +} + +TEST_F(FooTest, Test2) { + EXPECT_EQ(FooTest::shared_resource_->name_, "Foo!!!"); + EXPECT_EQ(FooTest::shared_resource_->value_, 13); +} +``` + + +___ + +## Testing private code - should be avoided + +```C++ +class Foo { +private: + FRIEND_TEST(FooTest, PrivateTest); + + int doSth(int val) { return val; } +}; + +TEST(FooTest, PrivateTest) { + Foo foo; + EXPECT_EQ(foo.doSth(42), 42); +} +``` + + +___ + +## Exercise 1 + +Open project **SHM** and write test for class `Ship.cpp` which will test it's interface. Remember to test edge cases :). Test it well and fix issues. + +Go to `Presentation/exercises/SHM/build/test` and run test using command `./SHM_Tests`. diff --git a/CreatingReliableSoftwareCpp/Presentation/visitor.md b/CreatingReliableSoftwareCpp/Presentation/visitor.md new file mode 100644 index 0000000..6e393f4 --- /dev/null +++ b/CreatingReliableSoftwareCpp/Presentation/visitor.md @@ -0,0 +1,600 @@ +# Visitor + +Another popular design pattern is **Visitor**. As previously, I'm certain that You also use it at least once, but maybe you didn't know about it. Let's check the following STL algorithm: + +```C++ +class VariantVisitor +{ +public: + void operator()(int num) const { + std::cout << "int: " << num << "\n"; + } + void operator()(std::string str) const { + std::cout << "string: " << str << "\n"; + } +}; + +int main() { + std::variant variant; + variant = "Ala has a cat"; + // print: string: Ala has a cat + std::visit(VariantVisitor{}, variant); +} +``` + + + +**Seems familiar? If not, let's check another example** + +___ + +```C++ +class Shape +{ +public: + virtual ~Shape() = default; + virtual void accept( ShapeVisitor const& v ) = 0; 3 +}; + +class Circle : public Shape +{ + public: + explicit Circle( double radius ): radius_( radius ) {} + void accept( ShapeVisitor const& v ) override { v.visit( *this ); } + double radius() const { return radius_; } + private: + double radius_; +}; + +class Square : public Shape +{ +public: + explicit Square( double side ): side_( side ) {} + void accept( ShapeVisitor const& v ) override { v.visit( *this ); } + double side() const { return side_; } +private: + double side_; +}; +``` + +```C++ +class Draw : public ShapeVisitor +{ +public: + void visit( Circle const& c, /*...*/ ) const override; + void visit( Square const& s, /*...*/ ) const override; +}; + +class Translate : public ShapeVisitor +{ +public: + void visit( Circle const& c, /*...*/ ) const override; + void visit( Square const& s, /*...*/ ) const override; +}; +``` + + +___ + +## Visitor + +These two examples were two different implementations of the same design pattern which is `Visitor`. In short, the visitor is used to dispatch functions between a few types. + +Visitor UML + + +___ + +## Example + +Let's back to the pirates. In the previous example, we implemented the ammunition class and added an implementation of `fire` and `draw` methods, so what next? We need to add also `methods` that will calculate how much damage each ammunition does and what sound should be played. + +Visitor first apprach + + +___ + +## Drawback + +In the first approach, we can see that we tightly coupled the base class `Visitor` with a base class `Ammunition` and the implementation of `Visitor` with types of `Ammunition`. So we have a lot of dependencies. Unfortunately, the visitor is a nice pattern, but it is good only for extending functions, but it is closed for adding new types because when we add a new type of ammunition we also need to implement every visitor class. There is a solution for this, but it is inefficient, and can't be always used. I will talk about this solution later. For now, it should be enough to know that there is sth like `Acyclic visitor` that can solve this problem, but it is inefficient. In this solution, we have double dispatch and the compiler can't optimize such code, also we have to call 2 or 3 virtual functions which are not as fast as calling normal functions. +___ + +## Implementation + +let's start by defining an interface for both, the `Ammunition` and `Visitor` class. + +```C++ +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + virtual void accept(const AmmunitionVisitor&) = 0; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; +``` + +```C++ +class AmmunitionVisitor { +public: + virtual ~AmmunitionVisitor() = default; + virtual void visit(const RoundShot&) const = 0; + virtual void visit(const ChainShot&) const = 0; + virtual void visit(const GrapeShot&) const = 0; +}; +``` + + +___ + +Each class that is derived from `Ammunition` needs to create a definition of the `accept` method. The is needed because each class passes a pointer to `*this` which allows us to distinguish which inherited class was provided. + +```C++ +class RoundShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AmmunitionVisitor& visitor) { + visitor.visit(*this); + } +}; + +class ChainShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AmmunitionVisitor& visitor) { + visitor.visit(*this); + } +}; + +class GrapeShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AmmunitionVisitor& visitor) { + visitor.visit(*this); + } +}; +``` + + +___ + +At the end we provide implementation for each visitor + +```C++ +class DamageVisitor : public AmmunitionVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Round shot make 20 damages\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Chain shot make 30 damages\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Grape shot make 40 damages\n"; + } +}; + +class SoundVisitor : public AmmunitionVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Fiuuuuuuuu Bam!\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam!\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n"; + } +}; +``` + + +___ + +## Usage + +```C++ +int main() { + std::unique_ptr grapeShot = std::make_unique(100); + SoundVisitor soundVisitor; + DamageVisitor damageVisitor; + grapeShot->accept(soundVisitor); + grapeShot->accept(damageVisitor); +} +``` + +```bash +Fiuuuuuuuuuuu Bam bam bam bam bam! +Grape shot make 40 damages +``` + + +___ + +## Drawbacks + +Visitor in this approach has 4 drawbacks: + +* First I mentioned earlier it will be hard to add a new type, we can solve it by using acyclic visitor +* Visitor is tightly coupled with other classes and not elastic. For instance, DamageVisitor implementations may be similar, but we need to implement a method for every class. We can separate common parts to another function, but still, it will be great if we can have one function for all types if needed. By using visitors, this is not possible +* If we want to introduce visitors in an already implemented hierarchy of classes we need to add the accept method to every base class, sometimes we don't have access to all files in the repo, or the repository is divided and it is not possible. There is a solution for this, which I will show you later +* If we add a new class that inherits from an already existing type, and we forget to override method, we will have the same behavior as the class that we inherit, which is a huge bug! + + +___ + +## Improvements + +As I said a few times, the modern approach uses by-value semantics, instead of virtual functions. We can use static or compile-time polymorphism to achieve the same and have even better performance. let's rewrite this example to use `std::variant` which allows us to decouple classes, and make a code more flexible and even faster (I will show you a benchmark later). + +```C++ +class Ammunition { +public: + Ammunition(size_t amount) + : _amount(amount) {} + virtual ~Ammunition() = default; + + size_t amount() const { return _amount; } + +private: + size_t _amount = 0; +}; + +class RoundShot final : public Ammunition { +public: + using Ammunition::Ammunition; +}; + +class ChainShot final : public Ammunition { +public: + using Ammunition::Ammunition; +}; + +class GrapeShot final : public Ammunition { +public: + using Ammunition::Ammunition; +}; +``` + + +___ + +```C++ +class DamageVisitor { +public: + void operator()(const RoundShot& shot) const { + std::cout << "Round shot make 20 damages\n"; + } + void operator()(const ChainShot& shot) const { + std::cout << "Chain shot make 30 damages\n"; + } + void operator()(const GrapeShot& shot) const { + std::cout << "Grape shot make 40 damages\n"; + } +}; + +class SoundVisitor { +public: + void operator()(const RoundShot& shot) const { + std::cout << "Fiuuuuuuuu Bam!\n"; + } + void operator()(const ChainShot& shot) const { + std::cout << "Fiuuuuuuuuuuu Bam bam!\n"; + } + void operator()(const GrapeShot& shot) const { + std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n"; + } +}; +``` + +___ + +We get rid of a lot of virtual functions and simplify the code. + +```C++ +int main() { + using Shot = std::variant; + + Shot shot = GrapeShot(100); + SoundVisitor soundVisitor; + DamageVisitor damageVisitor; + std::visit(damageVisitor, shot); + std::visit(soundVisitor, shot); +} +``` + + +```bash +Grape shot make 40 damages +Fiuuuuuuuuuuu Bam bam bam bam bam! +``` + + +___ + +## Benchmark + +I created a cimple bechmark to show you, that visitors based on variant may be faster. It depends on how heavy the is function visit, if we perform a fast operation, we will gain a lot, but if the operation is heavy we will barely see the difference between both solutions. + +Visitor benchamrk + + +___ + +## Simplified class hierarchy + +Visitor better design + +___ + +## Drawback std::variant + +We improved our code, but still, one problem exists, we can't easily add new types. The variant also provides another drawback: When types differ in size a lot, we will waste a lot of memory. In such a case we may think about using `std::unique_ptr` but this will force us to use dynamic allocation, so we lost some improvements (not fully use by-value semantic). + + +Last things left: How to eliminate this problem by adding a new type? Let's check the `acyclic variant` class: + + + +___ + +## Acyclic visitor + +Visitor acyclic + +___ + +## Implementation + +First, we create an abstract visitor class and three types of visitor for each type. This allows us to cut dependency between each visitor, because each visitor has knowledge about only one ammunition type. + +```C++ +class AbstractVisitor { +protected: + // Can be only use by derived class + virtual ~AbstractVisitor() = default; +}; + +class RoundShotVisitor { +public: + virtual void visit(const RoundShot& shot) const = 0; + +protected: + virtual ~RoundShotVisitor() = default; +}; + +class GrapeShotVisitor { +public: + virtual void visit(const GrapeShot& shot) const = 0; + +protected: + virtual ~GrapeShotVisitor() = default; +}; + +class ChainShotVisitor { +public: + virtual void visit(const ChainShot& shot) const = 0; + +protected: + virtual ~ChainShotVisitor() = default; +}; +``` + + +___ + +Next, we create dedicated visitors for `damage` and for `sound`. To hide implementations detail, each class inherit also from `AbstractVisitor`. This allows to use any visitor as pointer to this abstract class. + +```C++ +class DamageVisitor : public AbstractVisitor, + public RoundShotVisitor, + public GrapeShotVisitor, + public ChainShotVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Round shot make 20 damages\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Chain shot make 30 damages\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Grape shot make 40 damages\n"; + } +}; + +class SoundVisitor : public AbstractVisitor, + public RoundShotVisitor, + public GrapeShotVisitor, + public ChainShotVisitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Fiuuuuuuuu Bam!\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam!\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Fiuuuuuuuuuuu Bam bam bam bam bam!\n"; + } +}; +``` + + +___ + +For now, everything looks ok. But unfortunately, because `AbstracVisitor` has empty interface, we need to cast visitor to a specific type. This `dynamic_cast` cost a lot and make code harder to optimize. That's why this visitor is much slower than previous implementations. + +```C++ +class RoundShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast(&visitor)) { + v->visit(*this); + } + } +}; + +class ChainShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast(&visitor)) { + v->visit(*this); + } + } +}; + +class GrapeShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast(&visitor)) { + v->visit(*this); + } + } +}; +``` + + +___ + +## Usage + +The usage of this code doesn't change in comparison to the first implementation. But we lost on efficiency. + +```C++ +int main() { + std::unique_ptr grapeShot = std::make_unique(100); + SoundVisitor soundVisitor; + DamageVisitor damageVisitor; + grapeShot->accept(soundVisitor); + grapeShot->accept(damageVisitor); +} + +``` + +```bash +Fiuuuuuuuuuuu Bam bam bam bam bam! +Grape shot make 40 damages +``` + + +___ + +## Lost efficiency + +Acyclic visitor benchmark +___ + +## Improvement + +When visitor operations are quick, better to avoid acyclic visitor to not make the code slower. But when the operations are heavy, the difference will be slightly visible, and we will decouple visitor and ammunition classes. There is also one improvement, that make a code easier to extend. Instead of writing every time a visitor for a type, we can use template. + +```C++ +template +class Visitor { +public: + virtual void visit(const T& shot) const = 0; + +protected: + virtual ~Visitor() = default; +}; + +class DamageVisitor : public AbstractVisitor, + public Visitor, + public Visitor, + public Visitor { +public: + void visit(const RoundShot& shot) const override { + std::cout << "Round shot make 20 damages\n"; + } + void visit(const ChainShot& shot) const override { + std::cout << "Chain shot make 30 damages\n"; + } + void visit(const GrapeShot& shot) const override { + std::cout << "Grape shot make 40 damages\n"; + } +}; +``` + + +___ + +```C++ +class RoundShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast*>(&visitor)) { + v->visit(*this); + } + } +}; + +class ChainShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast*>(&visitor)) { + v->visit(*this); + } + } +}; + +class GrapeShot : public Ammunition { +public: + using Ammunition::Ammunition; + void accept(const AbstractVisitor& visitor) { + if (const auto* v = dynamic_cast*>(&visitor)) { + v->visit(*this); + } + } +}; +``` + +___ + +## UML + +Acyclic visitor UML +___ + +## Exericse 1 + +* Go into directory Core and implement a base class PrintVisitor which should implement visit method for two objects: Ship and Store +* Create PrettyPrintVisitor which will print cargo from Ship and Sotore (just move the implementation from printCargo). +* Add the rest of necessary implementation for Ship and Store. Visitor should be taken as std::unique_ptr +* Verify in the main.cpp if cargo print in the same way as previously. + +___ + +## Exercise 2 + +* Create another visitor: BasicPrintVisitor which should print the cargo but without special frames, just raw info. +* For Ship print Name and Amount +* For Store print Name Amount and Price +* You shouldn't modify anything in your code, just change the visitor in main.cpp and you should get a new output +___ + +## Exercise 3 + +* Create CargoDamageVisitor. You don't need to create a base class, because we know that we will not extend this code, +* You should create three operator() one for each cargo type: Alcohol, Fruit, Item, +* For fruit, you should draw a number between 0 and 5 and subtract such an amount of cargo +* For alcohol, you should draw a number between 0 and 10 and subtract half an amount of cargo +* For item, you should draw a number between 0 and 20 and subtract one quarter of cargo +* Visitor should be taken as a template argument by Player class +* Apply visitor whenever you successfully deal damage to ship +* Check in the main class if ship lost cargo after get damaged. + +___ + +## Summary + +As you can see visitor is a very useful pattern to add additional functionality to your class, in such was you don't need to modify already existing code, instead you can inherit from visitor and create totally new functionality. You can also use `std::variant` if you want to avoid modify original class (by adding accept method) or when you can't modify class (for instance it is in separate repo). Version, with variant, is also more flexible and faster than traditional implementation. If you really want to break all dependencies between visitor class and subject, you can use acyclic visitor, but this solution is less efficient, so you need to be sure you can accept this in your code. \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/README.md b/CreatingReliableSoftwareCpp/README.md new file mode 100644 index 0000000..e217134 --- /dev/null +++ b/CreatingReliableSoftwareCpp/README.md @@ -0,0 +1 @@ +# CreatingReliableSoftwareCpp \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/bower.json b/CreatingReliableSoftwareCpp/bower.json new file mode 100644 index 0000000..bc825ab --- /dev/null +++ b/CreatingReliableSoftwareCpp/bower.json @@ -0,0 +1,24 @@ +{ + "name": "reveal.js", + "version": "3.9.2", + "main": [ + "js/reveal.js", + "css/reveal.css" + ], + "homepage": "http://revealjs.com", + "license": "MIT", + "description": "The HTML Presentation Framework", + "authors": [ + "Hakim El Hattab " + ], + "repository": { + "type": "git", + "url": "git://github.com/hakimel/reveal.js.git" + }, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test" + ] +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/print/paper.css b/CreatingReliableSoftwareCpp/css/print/paper.css new file mode 100644 index 0000000..27d19dd --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/print/paper.css @@ -0,0 +1,203 @@ +/* Default Print Stylesheet Template + by Rob Glazebrook of CSSnewbie.com + Last Updated: June 4, 2008 + + Feel free (nay, compelled) to edit, append, and + manipulate this file as you see fit. */ + + +@media print { + + /* SECTION 1: Set default width, margin, float, and + background. This prevents elements from extending + beyond the edge of the printed page, and prevents + unnecessary background images from printing */ + html { + background: #fff; + width: auto; + height: auto; + overflow: visible; + } + body { + background: #fff; + font-size: 20pt; + width: auto; + height: auto; + border: 0; + margin: 0 5%; + padding: 0; + overflow: visible; + float: none !important; + } + + /* SECTION 2: Remove any elements not needed in print. + This would include navigation, ads, sidebars, etc. */ + .nestedarrow, + .controls, + .fork-reveal, + .share-reveal, + .state-background, + .reveal .progress, + .reveal .backgrounds, + .reveal .slide-number { + display: none !important; + } + + /* SECTION 3: Set body font face, size, and color. + Consider using a serif font for readability. */ + body, p, td, li, div { + font-size: 20pt!important; + font-family: Georgia, "Times New Roman", Times, serif !important; + color: #000; + } + + /* SECTION 4: Set heading font face, sizes, and color. + Differentiate your headings from your body text. + Perhaps use a large sans-serif for distinction. */ + h1,h2,h3,h4,h5,h6 { + color: #000!important; + height: auto; + line-height: normal; + font-family: Georgia, "Times New Roman", Times, serif !important; + text-shadow: 0 0 0 #000 !important; + text-align: left; + letter-spacing: normal; + } + /* Need to reduce the size of the fonts for printing */ + h1 { font-size: 28pt !important; } + h2 { font-size: 24pt !important; } + h3 { font-size: 22pt !important; } + h4 { font-size: 22pt !important; font-variant: small-caps; } + h5 { font-size: 21pt !important; } + h6 { font-size: 20pt !important; font-style: italic; } + + /* SECTION 5: Make hyperlinks more usable. + Ensure links are underlined, and consider appending + the URL to the end of the link for usability. */ + a:link, + a:visited { + color: #000 !important; + font-weight: bold; + text-decoration: underline; + } + /* + .reveal a:link:after, + .reveal a:visited:after { + content: " (" attr(href) ") "; + color: #222 !important; + font-size: 90%; + } + */ + + + /* SECTION 6: more reveal.js specific additions by @skypanther */ + ul, ol, div, p { + visibility: visible; + position: static; + width: auto; + height: auto; + display: block; + overflow: visible; + margin: 0; + text-align: left !important; + } + .reveal pre, + .reveal table { + margin-left: 0; + margin-right: 0; + } + .reveal pre code { + padding: 20px; + border: 1px solid #ddd; + } + .reveal blockquote { + margin: 20px 0; + } + .reveal .slides { + position: static !important; + width: auto !important; + height: auto !important; + + left: 0 !important; + top: 0 !important; + margin-left: 0 !important; + margin-top: 0 !important; + padding: 0 !important; + zoom: 1 !important; + + overflow: visible !important; + display: block !important; + + text-align: left !important; + -webkit-perspective: none; + -moz-perspective: none; + -ms-perspective: none; + perspective: none; + + -webkit-perspective-origin: 50% 50%; + -moz-perspective-origin: 50% 50%; + -ms-perspective-origin: 50% 50%; + perspective-origin: 50% 50%; + } + .reveal .slides section { + visibility: visible !important; + position: static !important; + width: auto !important; + height: auto !important; + display: block !important; + overflow: visible !important; + + left: 0 !important; + top: 0 !important; + margin-left: 0 !important; + margin-top: 0 !important; + padding: 60px 20px !important; + z-index: auto !important; + + opacity: 1 !important; + + page-break-after: always !important; + + -webkit-transform-style: flat !important; + -moz-transform-style: flat !important; + -ms-transform-style: flat !important; + transform-style: flat !important; + + -webkit-transform: none !important; + -moz-transform: none !important; + -ms-transform: none !important; + transform: none !important; + + -webkit-transition: none !important; + -moz-transition: none !important; + -ms-transition: none !important; + transition: none !important; + } + .reveal .slides section.stack { + padding: 0 !important; + } + .reveal section:last-of-type { + page-break-after: avoid !important; + } + .reveal section .fragment { + opacity: 1 !important; + visibility: visible !important; + + -webkit-transform: none !important; + -moz-transform: none !important; + -ms-transform: none !important; + transform: none !important; + } + .reveal section img { + display: block; + margin: 15px 0px; + background: rgba(255,255,255,1); + border: 1px solid #666; + box-shadow: none; + } + + .reveal section small { + font-size: 0.8em; + } + +} diff --git a/CreatingReliableSoftwareCpp/css/print/pdf.css b/CreatingReliableSoftwareCpp/css/print/pdf.css new file mode 100644 index 0000000..bf96ed1 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/print/pdf.css @@ -0,0 +1,165 @@ +/** + * This stylesheet is used to print reveal.js + * presentations to PDF. + * + * https://github.com/hakimel/reveal.js#pdf-export + */ + +* { + -webkit-print-color-adjust: exact; +} + +body { + margin: 0 auto !important; + border: 0; + padding: 0; + float: none !important; + overflow: visible; +} + +html { + width: 100%; + height: 100%; + overflow: visible; +} + +/* Remove any elements not needed in print. */ +.nestedarrow, +.reveal .controls, +.reveal .progress, +.reveal .playback, +.reveal.overview, +.fork-reveal, +.share-reveal, +.state-background { + display: none !important; +} + +h1, h2, h3, h4, h5, h6 { + text-shadow: 0 0 0 #000 !important; +} + +.reveal pre code { + overflow: hidden !important; + font-family: Courier, 'Courier New', monospace !important; +} + +ul, ol, div, p { + visibility: visible; + position: static; + width: auto; + height: auto; + display: block; + overflow: visible; + margin: auto; +} +.reveal { + width: auto !important; + height: auto !important; + overflow: hidden !important; +} +.reveal .slides { + position: static; + width: 100% !important; + height: auto !important; + zoom: 1 !important; + pointer-events: initial; + + left: auto; + top: auto; + margin: 0 !important; + padding: 0 !important; + + overflow: visible; + display: block; + + perspective: none; + perspective-origin: 50% 50%; +} + +.reveal .slides .pdf-page { + position: relative; + overflow: hidden; + z-index: 1; + + page-break-after: always; +} + +.reveal .slides section { + visibility: visible !important; + display: block !important; + position: absolute !important; + + margin: 0 !important; + padding: 0 !important; + box-sizing: border-box !important; + min-height: 1px; + + opacity: 1 !important; + + transform-style: flat !important; + transform: none !important; +} + +.reveal section.stack { + position: relative !important; + margin: 0 !important; + padding: 0 !important; + page-break-after: avoid !important; + height: auto !important; + min-height: auto !important; +} + +.reveal img { + box-shadow: none; +} + +.reveal .roll { + overflow: visible; + line-height: 1em; +} + +/* Slide backgrounds are placed inside of their slide when exporting to PDF */ +.reveal .slide-background { + display: block !important; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: auto !important; +} + +/* Display slide speaker notes when 'showNotes' is enabled */ +.reveal.show-notes { + max-width: none; + max-height: none; +} +.reveal .speaker-notes-pdf { + display: block; + width: 100%; + height: auto; + max-height: none; + top: auto; + right: auto; + bottom: auto; + left: auto; + z-index: 100; +} + +/* Layout option which makes notes appear on a separate page */ +.reveal .speaker-notes-pdf[data-layout="separate-page"] { + position: relative; + color: inherit; + background-color: transparent; + padding: 20px; + page-break-after: always; + border: 0; +} + +/* Display slide numbers when 'slideNumber' is enabled */ +.reveal .slide-number-pdf { + display: block; + position: absolute; + font-size: 14px; +} diff --git a/CreatingReliableSoftwareCpp/css/reset.css b/CreatingReliableSoftwareCpp/css/reset.css new file mode 100644 index 0000000..e238539 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/reset.css @@ -0,0 +1,30 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v4.0 | 20180602 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +main, menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, main, menu, nav, section { + display: block; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/reveal.css b/CreatingReliableSoftwareCpp/css/reveal.css new file mode 100644 index 0000000..b4bc4fd --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/reveal.css @@ -0,0 +1,1606 @@ +/*! + * reveal.js + * http://revealjs.com + * MIT licensed + * + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se + */ +/********************************************* + * GLOBAL STYLES + *********************************************/ +html { + width: 100%; + height: 100%; + height: 100vh; + height: calc( var(--vh, 1vh) * 100); + overflow: hidden; } + +body { + height: 100%; + overflow: hidden; + position: relative; + line-height: 1; + margin: 0; + background-color: #fff; + color: #000; } + +/********************************************* + * VIEW FRAGMENTS + *********************************************/ +.reveal .slides section .fragment { + opacity: 0; + visibility: hidden; + transition: all .2s ease; } + .reveal .slides section .fragment.visible { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.grow { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.grow.visible { + -webkit-transform: scale(1.3); + transform: scale(1.3); } + +.reveal .slides section .fragment.shrink { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.shrink.visible { + -webkit-transform: scale(0.7); + transform: scale(0.7); } + +.reveal .slides section .fragment.zoom-in { + -webkit-transform: scale(0.1); + transform: scale(0.1); } + .reveal .slides section .fragment.zoom-in.visible { + -webkit-transform: none; + transform: none; } + +.reveal .slides section .fragment.fade-out { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.fade-out.visible { + opacity: 0; + visibility: hidden; } + +.reveal .slides section .fragment.semi-fade-out { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.semi-fade-out.visible { + opacity: 0.5; + visibility: inherit; } + +.reveal .slides section .fragment.strike { + opacity: 1; + visibility: inherit; } + .reveal .slides section .fragment.strike.visible { + text-decoration: line-through; } + +.reveal .slides section .fragment.fade-up { + -webkit-transform: translate(0, 40px); + transform: translate(0, 40px); } + .reveal .slides section .fragment.fade-up.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-down { + -webkit-transform: translate(0, -40px); + transform: translate(0, -40px); } + .reveal .slides section .fragment.fade-down.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-right { + -webkit-transform: translate(-40px, 0); + transform: translate(-40px, 0); } + .reveal .slides section .fragment.fade-right.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-left { + -webkit-transform: translate(40px, 0); + transform: translate(40px, 0); } + .reveal .slides section .fragment.fade-left.visible { + -webkit-transform: translate(0, 0); + transform: translate(0, 0); } + +.reveal .slides section .fragment.fade-in-then-out, +.reveal .slides section .fragment.current-visible { + opacity: 0; + visibility: hidden; } + .reveal .slides section .fragment.fade-in-then-out.current-fragment, + .reveal .slides section .fragment.current-visible.current-fragment { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.fade-in-then-semi-out { + opacity: 0; + visibility: hidden; } + .reveal .slides section .fragment.fade-in-then-semi-out.visible { + opacity: 0.5; + visibility: inherit; } + .reveal .slides section .fragment.fade-in-then-semi-out.current-fragment { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.highlight-red, +.reveal .slides section .fragment.highlight-current-red, +.reveal .slides section .fragment.highlight-green, +.reveal .slides section .fragment.highlight-current-green, +.reveal .slides section .fragment.highlight-blue, +.reveal .slides section .fragment.highlight-current-blue { + opacity: 1; + visibility: inherit; } + +.reveal .slides section .fragment.highlight-red.visible { + color: #ff2c2d; } + +.reveal .slides section .fragment.highlight-green.visible { + color: #216428; } + +.reveal .slides section .fragment.highlight-blue.visible { + color: #1b91ff; } + +.reveal .slides section .fragment.highlight-current-red.current-fragment { + color: #ff2c2d; } + +.reveal .slides section .fragment.highlight-current-green.current-fragment { + color: #17ff2e; } + +.reveal .slides section .fragment.highlight-current-blue.current-fragment { + color: #1b91ff; } + +/********************************************* + * DEFAULT ELEMENT STYLES + *********************************************/ +/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */ +.reveal:after { + content: ''; + font-style: italic; } + +.reveal iframe { + z-index: 1; } + +/** Prevents layering issues in certain browser/transition combinations */ +.reveal a { + position: relative; } + +.reveal .stretch { + max-width: none; + max-height: none; } + +.reveal pre.stretch code { + height: 100%; + max-height: 100%; + box-sizing: border-box; } + +/********************************************* + * CONTROLS + *********************************************/ +@-webkit-keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateX(0); + transform: translateX(0); } + 20% { + -webkit-transform: translateX(10px); + transform: translateX(10px); } + 30% { + -webkit-transform: translateX(-5px); + transform: translateX(-5px); } } + +@-webkit-keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% { + -webkit-transform: translateY(0); + transform: translateY(0); } + 20% { + -webkit-transform: translateY(10px); + transform: translateY(10px); } + 30% { + -webkit-transform: translateY(-5px); + transform: translateY(-5px); } } + +.reveal .controls { + display: none; + position: absolute; + top: auto; + bottom: 12px; + right: 12px; + left: auto; + z-index: 11; + color: #000; + pointer-events: none; + font-size: 10px; } + .reveal .controls button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + -webkit-transform: scale(0.9999); + transform: scale(0.9999); + transition: color 0.2s ease, opacity 0.2s ease, -webkit-transform 0.2s ease; + transition: color 0.2s ease, opacity 0.2s ease, transform 0.2s ease; + z-index: 2; + pointer-events: auto; + font-size: inherit; + visibility: hidden; + opacity: 0; + -webkit-appearance: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } + .reveal .controls .controls-arrow:before, + .reveal .controls .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 2.6em; + height: 0.5em; + border-radius: 0.25em; + background-color: currentColor; + transition: all 0.15s ease, background-color 0.8s ease; + -webkit-transform-origin: 0.2em 50%; + transform-origin: 0.2em 50%; + will-change: transform; } + .reveal .controls .controls-arrow { + position: relative; + width: 3.6em; + height: 3.6em; } + .reveal .controls .controls-arrow:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } + .reveal .controls .controls-arrow:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } + .reveal .controls .controls-arrow:hover:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(40deg); } + .reveal .controls .controls-arrow:hover:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-40deg); } + .reveal .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(36deg); } + .reveal .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-36deg); } + .reveal .controls .navigate-left { + right: 6.4em; + bottom: 3.2em; + -webkit-transform: translateX(-10px); + transform: translateX(-10px); } + .reveal .controls .navigate-right { + right: 0; + bottom: 3.2em; + -webkit-transform: translateX(10px); + transform: translateX(10px); } + .reveal .controls .navigate-right .controls-arrow { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); } + .reveal .controls .navigate-right.highlight { + -webkit-animation: bounce-right 2s 50 both ease-out; + animation: bounce-right 2s 50 both ease-out; } + .reveal .controls .navigate-up { + right: 3.2em; + bottom: 6.4em; + -webkit-transform: translateY(-10px); + transform: translateY(-10px); } + .reveal .controls .navigate-up .controls-arrow { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); } + .reveal .controls .navigate-down { + right: 3.2em; + bottom: -1.4em; + padding-bottom: 1.4em; + -webkit-transform: translateY(10px); + transform: translateY(10px); } + .reveal .controls .navigate-down .controls-arrow { + -webkit-transform: rotate(-90deg); + transform: rotate(-90deg); } + .reveal .controls .navigate-down.highlight { + -webkit-animation: bounce-down 2s 50 both ease-out; + animation: bounce-down 2s 50 both ease-out; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; } + .reveal .controls[data-controls-back-arrows="faded"] .navigate-left.enabled:hover, + .reveal .controls[data-controls-back-arrows="faded"] .navigate-up.enabled:hover { + opacity: 1; } + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-left.enabled, + .reveal .controls[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; } + .reveal .controls .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + -webkit-transform: none; + transform: none; } + .reveal .controls .enabled.fragmented { + opacity: 0.5; } + .reveal .controls .enabled:hover, + .reveal .controls .enabled.fragmented:hover { + opacity: 1; } + +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-up, +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-down { + display: none; } + +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-left, +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: 1.4em; + right: 5.5em; } + +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-right, +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: 1.4em; + right: 0.5em; } + +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: 1.4em; + bottom: 5em; } + +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: 1.4em; + bottom: 0.5em; } + +.reveal.has-dark-background .controls { + color: #fff; } + +.reveal.has-light-background .controls { + color: #000; } + +.reveal.no-hover .controls .controls-arrow:hover:before, +.reveal.no-hover .controls .controls-arrow:active:before { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(45deg); } + +.reveal.no-hover .controls .controls-arrow:hover:after, +.reveal.no-hover .controls .controls-arrow:active:after { + -webkit-transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); + transform: translateX(0.5em) translateY(1.55em) rotate(-45deg); } + +@media screen and (min-width: 500px) { + .reveal .controls[data-controls-layout="edges"] { + top: 0; + right: 0; + bottom: 0; + left: 0; } + .reveal .controls[data-controls-layout="edges"] .navigate-left, + .reveal .controls[data-controls-layout="edges"] .navigate-right, + .reveal .controls[data-controls-layout="edges"] .navigate-up, + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: auto; + right: auto; } + .reveal .controls[data-controls-layout="edges"] .navigate-left { + top: 50%; + left: 0.8em; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-right { + top: 50%; + right: 0.8em; + margin-top: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-up { + top: 0.8em; + left: 50%; + margin-left: -1.8em; } + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: -0.3em; + left: 50%; + margin-left: -1.8em; } } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + position: absolute; + display: none; + height: 3px; + width: 100%; + bottom: 0; + left: 0; + z-index: 10; + background-color: rgba(0, 0, 0, 0.2); + color: #fff; } + +.reveal .progress:after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + top: -10px; } + +.reveal .progress span { + display: block; + height: 100%; + width: 0px; + background-color: currentColor; + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * SLIDE NUMBER + *********************************************/ +.reveal .slide-number { + position: absolute; + display: block; + right: 8px; + bottom: 8px; + z-index: 31; + font-family: Helvetica, sans-serif; + font-size: 12px; + line-height: 1; + color: #fff; + background-color: rgba(0, 0, 0, 0.4); + padding: 5px; } + +.reveal .slide-number a { + color: currentColor; } + +.reveal .slide-number-delimiter { + margin: 0 3px; } + +/********************************************* + * SLIDES + *********************************************/ +.reveal { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + -ms-touch-action: pinch-zoom; + touch-action: pinch-zoom; } + +.reveal .slides { + position: absolute; + width: 100%; + height: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + pointer-events: none; + overflow: visible; + z-index: 1; + text-align: center; + -webkit-perspective: 600px; + perspective: 600px; + -webkit-perspective-origin: 50% 40%; + perspective-origin: 50% 40%; } + +.reveal .slides > section { + -webkit-perspective: 600px; + perspective: 600px; } + +.reveal .slides > section, +.reveal .slides > section > section { + display: none; + position: absolute; + width: 100%; + padding: 20px 0px; + pointer-events: auto; + z-index: 10; + -webkit-transform-style: flat; + transform-style: flat; + transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"] .slides section { + transition-duration: 400ms; } + +.reveal[data-transition-speed="slow"] .slides section { + transition-duration: 1200ms; } + +/* Slide-specific transition speed overrides */ +.reveal .slides section[data-transition-speed="fast"] { + transition-duration: 400ms; } + +.reveal .slides section[data-transition-speed="slow"] { + transition-duration: 1200ms; } + +.reveal .slides > section.stack { + padding-top: 0; + padding-bottom: 0; + pointer-events: none; + height: 100%; } + +.reveal .slides > section.present, +.reveal .slides > section > section.present { + display: block; + z-index: 11; + opacity: 1; } + +.reveal .slides > section:empty, +.reveal .slides > section > section:empty, +.reveal .slides > section[data-background-interactive], +.reveal .slides > section > section[data-background-interactive] { + pointer-events: none; } + +.reveal.center, +.reveal.center .slides, +.reveal.center .slides section { + min-height: 0 !important; } + +/* Don't allow interaction with invisible slides */ +.reveal .slides > section.future, +.reveal .slides > section > section.future, +.reveal .slides > section.past, +.reveal .slides > section > section.past { + pointer-events: none; } + +.reveal.overview .slides > section, +.reveal.overview .slides > section > section { + pointer-events: auto; } + +.reveal .slides > section.past, +.reveal .slides > section.future, +.reveal .slides > section > section.past, +.reveal .slides > section > section.future { + opacity: 0; } + +/********************************************* + * Mixins for readability of transitions + *********************************************/ +/********************************************* + * SLIDE TRANSITION + * Aliased 'linear' for backwards compatibility + *********************************************/ +.reveal.slide section { + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal .slides > section[data-transition=slide].past, +.reveal .slides > section[data-transition~=slide-out].past, +.reveal.slide .slides > section:not([data-transition]).past { + -webkit-transform: translate(-150%, 0); + transform: translate(-150%, 0); } + +.reveal .slides > section[data-transition=slide].future, +.reveal .slides > section[data-transition~=slide-in].future, +.reveal.slide .slides > section:not([data-transition]).future { + -webkit-transform: translate(150%, 0); + transform: translate(150%, 0); } + +.reveal .slides > section > section[data-transition=slide].past, +.reveal .slides > section > section[data-transition~=slide-out].past, +.reveal.slide .slides > section > section:not([data-transition]).past { + -webkit-transform: translate(0, -150%); + transform: translate(0, -150%); } + +.reveal .slides > section > section[data-transition=slide].future, +.reveal .slides > section > section[data-transition~=slide-in].future, +.reveal.slide .slides > section > section:not([data-transition]).future { + -webkit-transform: translate(0, 150%); + transform: translate(0, 150%); } + +.reveal.linear section { + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal .slides > section[data-transition=linear].past, +.reveal .slides > section[data-transition~=linear-out].past, +.reveal.linear .slides > section:not([data-transition]).past { + -webkit-transform: translate(-150%, 0); + transform: translate(-150%, 0); } + +.reveal .slides > section[data-transition=linear].future, +.reveal .slides > section[data-transition~=linear-in].future, +.reveal.linear .slides > section:not([data-transition]).future { + -webkit-transform: translate(150%, 0); + transform: translate(150%, 0); } + +.reveal .slides > section > section[data-transition=linear].past, +.reveal .slides > section > section[data-transition~=linear-out].past, +.reveal.linear .slides > section > section:not([data-transition]).past { + -webkit-transform: translate(0, -150%); + transform: translate(0, -150%); } + +.reveal .slides > section > section[data-transition=linear].future, +.reveal .slides > section > section[data-transition~=linear-in].future, +.reveal.linear .slides > section > section:not([data-transition]).future { + -webkit-transform: translate(0, 150%); + transform: translate(0, 150%); } + +/********************************************* + * CONVEX TRANSITION + * Aliased 'default' for backwards compatibility + *********************************************/ +.reveal .slides section[data-transition=default].stack, +.reveal.default .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=default].past, +.reveal .slides > section[data-transition~=default-out].past, +.reveal.default .slides > section:not([data-transition]).past { + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } + +.reveal .slides > section[data-transition=default].future, +.reveal .slides > section[data-transition~=default-in].future, +.reveal.default .slides > section:not([data-transition]).future { + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } + +.reveal .slides > section > section[data-transition=default].past, +.reveal .slides > section > section[data-transition~=default-out].past, +.reveal.default .slides > section > section:not([data-transition]).past { + -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); + transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); } + +.reveal .slides > section > section[data-transition=default].future, +.reveal .slides > section > section[data-transition~=default-in].future, +.reveal.default .slides > section > section:not([data-transition]).future { + -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); + transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); } + +.reveal .slides section[data-transition=convex].stack, +.reveal.convex .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=convex].past, +.reveal .slides > section[data-transition~=convex-out].past, +.reveal.convex .slides > section:not([data-transition]).past { + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } + +.reveal .slides > section[data-transition=convex].future, +.reveal .slides > section[data-transition~=convex-in].future, +.reveal.convex .slides > section:not([data-transition]).future { + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } + +.reveal .slides > section > section[data-transition=convex].past, +.reveal .slides > section > section[data-transition~=convex-out].past, +.reveal.convex .slides > section > section:not([data-transition]).past { + -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); + transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); } + +.reveal .slides > section > section[data-transition=convex].future, +.reveal .slides > section > section[data-transition~=convex-in].future, +.reveal.convex .slides > section > section:not([data-transition]).future { + -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); + transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); } + +/********************************************* + * CONCAVE TRANSITION + *********************************************/ +.reveal .slides section[data-transition=concave].stack, +.reveal.concave .slides section.stack { + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal .slides > section[data-transition=concave].past, +.reveal .slides > section[data-transition~=concave-out].past, +.reveal.concave .slides > section:not([data-transition]).past { + -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } + +.reveal .slides > section[data-transition=concave].future, +.reveal .slides > section[data-transition~=concave-in].future, +.reveal.concave .slides > section:not([data-transition]).future { + -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); } + +.reveal .slides > section > section[data-transition=concave].past, +.reveal .slides > section > section[data-transition~=concave-out].past, +.reveal.concave .slides > section > section:not([data-transition]).past { + -webkit-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); + transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); } + +.reveal .slides > section > section[data-transition=concave].future, +.reveal .slides > section > section[data-transition~=concave-in].future, +.reveal.concave .slides > section > section:not([data-transition]).future { + -webkit-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); + transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); } + +/********************************************* + * ZOOM TRANSITION + *********************************************/ +.reveal .slides section[data-transition=zoom], +.reveal.zoom .slides section:not([data-transition]) { + transition-timing-function: ease; } + +.reveal .slides > section[data-transition=zoom].past, +.reveal .slides > section[data-transition~=zoom-out].past, +.reveal.zoom .slides > section:not([data-transition]).past { + visibility: hidden; + -webkit-transform: scale(16); + transform: scale(16); } + +.reveal .slides > section[data-transition=zoom].future, +.reveal .slides > section[data-transition~=zoom-in].future, +.reveal.zoom .slides > section:not([data-transition]).future { + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +.reveal .slides > section > section[data-transition=zoom].past, +.reveal .slides > section > section[data-transition~=zoom-out].past, +.reveal.zoom .slides > section > section:not([data-transition]).past { + -webkit-transform: scale(16); + transform: scale(16); } + +.reveal .slides > section > section[data-transition=zoom].future, +.reveal .slides > section > section[data-transition~=zoom-in].future, +.reveal.zoom .slides > section > section:not([data-transition]).future { + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +/********************************************* + * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ +.reveal.cube .slides { + -webkit-perspective: 1300px; + perspective: 1300px; } + +.reveal.cube .slides section { + padding: 30px; + min-height: 700px; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal.center.cube .slides section { + min-height: 0; } + +.reveal.cube .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0, 0, 0, 0.1); + border-radius: 4px; + -webkit-transform: translateZ(-20px); + transform: translateZ(-20px); } + +.reveal.cube .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2); + -webkit-transform: translateZ(-90px) rotateX(65deg); + transform: translateZ(-90px) rotateX(65deg); } + +.reveal.cube .slides > section.stack { + padding: 0; + background: none; } + +.reveal.cube .slides > section.past { + -webkit-transform-origin: 100% 0%; + transform-origin: 100% 0%; + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg); + transform: translate3d(-100%, 0, 0) rotateY(-90deg); } + +.reveal.cube .slides > section.future { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg); + transform: translate3d(100%, 0, 0) rotateY(90deg); } + +.reveal.cube .slides > section > section.past { + -webkit-transform-origin: 0% 100%; + transform-origin: 0% 100%; + -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg); + transform: translate3d(0, -100%, 0) rotateX(90deg); } + +.reveal.cube .slides > section > section.future { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg); + transform: translate3d(0, 100%, 0) rotateX(-90deg); } + +/********************************************* + * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ +.reveal.page .slides { + -webkit-perspective-origin: 0% 50%; + perspective-origin: 0% 50%; + -webkit-perspective: 3000px; + perspective: 3000px; } + +.reveal.page .slides section { + padding: 30px; + min-height: 700px; + box-sizing: border-box; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; } + +.reveal.page .slides section.past { + z-index: 12; } + +.reveal.page .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0, 0, 0, 0.1); + -webkit-transform: translateZ(-20px); + transform: translateZ(-20px); } + +.reveal.page .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2); + -webkit-transform: translateZ(-90px) rotateX(65deg); } + +.reveal.page .slides > section.stack { + padding: 0; + background: none; } + +.reveal.page .slides > section.past { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(-40%, 0, 0) rotateY(-80deg); + transform: translate3d(-40%, 0, 0) rotateY(-80deg); } + +.reveal.page .slides > section.future { + -webkit-transform-origin: 100% 0%; + transform-origin: 100% 0%; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +.reveal.page .slides > section > section.past { + -webkit-transform-origin: 0% 0%; + transform-origin: 0% 0%; + -webkit-transform: translate3d(0, -40%, 0) rotateX(80deg); + transform: translate3d(0, -40%, 0) rotateX(80deg); } + +.reveal.page .slides > section > section.future { + -webkit-transform-origin: 0% 100%; + transform-origin: 0% 100%; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); } + +/********************************************* + * FADE TRANSITION + *********************************************/ +.reveal .slides section[data-transition=fade], +.reveal.fade .slides section:not([data-transition]), +.reveal.fade .slides > section > section:not([data-transition]) { + -webkit-transform: none; + transform: none; + transition: opacity 0.5s; } + +.reveal.fade.overview .slides section, +.reveal.fade.overview .slides > section > section { + transition: none; } + +/********************************************* + * NO TRANSITION + *********************************************/ +.reveal .slides section[data-transition=none], +.reveal.none .slides section:not([data-transition]) { + -webkit-transform: none; + transform: none; + transition: none; } + +/********************************************* + * PAUSED MODE + *********************************************/ +.reveal .pause-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: black; + visibility: hidden; + opacity: 0; + z-index: 100; + transition: all 1s ease; } + +.reveal .pause-overlay .resume-button { + position: absolute; + bottom: 20px; + right: 20px; + color: #ccc; + border-radius: 2px; + padding: 6px 14px; + border: 2px solid #ccc; + font-size: 16px; + background: transparent; + cursor: pointer; } + .reveal .pause-overlay .resume-button:hover { + color: #fff; + border-color: #fff; } + +.reveal.paused .pause-overlay { + visibility: visible; + opacity: 1; } + +/********************************************* + * FALLBACK + *********************************************/ +.no-transforms { + overflow-y: auto; } + +.no-transforms .reveal { + overflow: visible; } + +.no-transforms .reveal .slides { + position: relative; + width: 80%; + max-width: 1280px; + height: auto; + top: 0; + margin: 0 auto; + text-align: center; } + +.no-transforms .reveal .controls, +.no-transforms .reveal .progress { + display: none; } + +.no-transforms .reveal .slides section { + display: block; + opacity: 1; + position: relative; + height: auto; + min-height: 0; + top: 0; + left: 0; + margin: 10vh 0; + margin: 70px 0; + -webkit-transform: none; + transform: none; } + +.reveal .no-transition, +.reveal .no-transition * { + transition: none !important; } + +/********************************************* + * PER-SLIDE BACKGROUNDS + *********************************************/ +.reveal .backgrounds { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + -webkit-perspective: 600px; + perspective: 600px; } + +.reveal .slide-background { + display: none; + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + visibility: hidden; + overflow: hidden; + background-color: rgba(0, 0, 0, 0); + transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +.reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; } + +.reveal .slide-background.stack { + display: block; } + +.reveal .slide-background.present { + opacity: 1; + visibility: visible; + z-index: 2; } + +.print-pdf .reveal .slide-background { + opacity: 1 !important; + visibility: visible !important; } + +/* Video backgrounds */ +.reveal .slide-background video { + position: absolute; + width: 100%; + height: 100%; + max-width: none; + max-height: none; + top: 0; + left: 0; + -o-object-fit: cover; + object-fit: cover; } + +.reveal .slide-background[data-background-size="contain"] video { + -o-object-fit: contain; + object-fit: contain; } + +/* Immediate transition style */ +.reveal[data-background-transition=none] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=none] { + transition: none; } + +/* Slide */ +.reveal[data-background-transition=slide] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=slide] { + opacity: 1; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=slide] { + -webkit-transform: translate(-100%, 0); + transform: translate(-100%, 0); } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=slide] { + -webkit-transform: translate(100%, 0); + transform: translate(100%, 0); } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=slide] { + -webkit-transform: translate(0, -100%); + transform: translate(0, -100%); } + +.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=slide] { + -webkit-transform: translate(0, 100%); + transform: translate(0, 100%); } + +/* Convex */ +.reveal[data-background-transition=convex] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); } + +.reveal[data-background-transition=convex] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); } + +.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); } + +.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=convex] { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); } + +/* Concave */ +.reveal[data-background-transition=concave] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); } + +.reveal[data-background-transition=concave] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); } + +.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); } + +.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=concave] { + opacity: 0; + -webkit-transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); + transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); } + +/* Zoom */ +.reveal[data-background-transition=zoom] > .backgrounds .slide-background, +.reveal > .backgrounds .slide-background[data-background-transition=zoom] { + transition-timing-function: ease; } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background.past, +.reveal > .backgrounds .slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(16); + transform: scale(16); } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background.future, +.reveal > .backgrounds .slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.past, +.reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(16); + transform: scale(16); } + +.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.future, +.reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"] > .backgrounds .slide-background { + transition-duration: 400ms; } + +.reveal[data-transition-speed="slow"] > .backgrounds .slide-background { + transition-duration: 1200ms; } + +/********************************************* + * OVERVIEW + *********************************************/ +.reveal.overview { + -webkit-perspective-origin: 50% 50%; + perspective-origin: 50% 50%; + -webkit-perspective: 700px; + perspective: 700px; } + .reveal.overview .slides { + -moz-transform-style: preserve-3d; } + .reveal.overview .slides section { + height: 100%; + top: 0 !important; + opacity: 1 !important; + overflow: hidden; + visibility: visible !important; + cursor: pointer; + box-sizing: border-box; } + .reveal.overview .slides section:hover, + .reveal.overview .slides section.present { + outline: 10px solid rgba(150, 150, 150, 0.4); + outline-offset: 10px; } + .reveal.overview .slides section .fragment { + opacity: 1; + transition: none; } + .reveal.overview .slides section:after, + .reveal.overview .slides section:before { + display: none !important; } + .reveal.overview .slides > section.stack { + padding: 0; + top: 0 !important; + background: none; + outline: none; + overflow: visible; } + .reveal.overview .backgrounds { + -webkit-perspective: inherit; + perspective: inherit; + -moz-transform-style: preserve-3d; } + .reveal.overview .backgrounds .slide-background { + opacity: 1; + visibility: visible; + outline: 10px solid rgba(150, 150, 150, 0.1); + outline-offset: 10px; } + .reveal.overview .backgrounds .slide-background.stack { + overflow: visible; } + +.reveal.overview .slides section, +.reveal.overview-deactivating .slides section { + transition: none; } + +.reveal.overview .backgrounds .slide-background, +.reveal.overview-deactivating .backgrounds .slide-background { + transition: none; } + +/********************************************* + * RTL SUPPORT + *********************************************/ +.reveal.rtl .slides, +.reveal.rtl .slides h1, +.reveal.rtl .slides h2, +.reveal.rtl .slides h3, +.reveal.rtl .slides h4, +.reveal.rtl .slides h5, +.reveal.rtl .slides h6 { + direction: rtl; + font-family: sans-serif; } + +.reveal.rtl pre, +.reveal.rtl code { + direction: ltr; } + +.reveal.rtl ol, +.reveal.rtl ul { + text-align: right; } + +.reveal.rtl .progress span { + float: right; } + +/********************************************* + * PARALLAX BACKGROUND + *********************************************/ +.reveal.has-parallax-background .backgrounds { + transition: all 0.8s ease; } + +/* Global transition speed settings */ +.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds { + transition-duration: 400ms; } + +.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds { + transition-duration: 1200ms; } + +/********************************************* + * OVERLAY FOR LINK PREVIEWS AND HELP + *********************************************/ +.reveal > .overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + background: rgba(0, 0, 0, 0.9); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; } + +.reveal > .overlay.visible { + opacity: 1; + visibility: visible; } + +.reveal > .overlay .spinner { + position: absolute; + display: block; + top: 50%; + left: 50%; + width: 32px; + height: 32px; + margin: -16px 0 0 -16px; + z-index: 10; + background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); + visibility: visible; + opacity: 0.6; + transition: all 0.3s ease; } + +.reveal > .overlay header { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 40px; + z-index: 2; + border-bottom: 1px solid #222; } + +.reveal > .overlay header a { + display: inline-block; + width: 40px; + height: 40px; + line-height: 36px; + padding: 0 10px; + float: right; + opacity: 0.6; + box-sizing: border-box; } + +.reveal > .overlay header a:hover { + opacity: 1; } + +.reveal > .overlay header a .icon { + display: inline-block; + width: 20px; + height: 20px; + background-position: 50% 50%; + background-size: 100%; + background-repeat: no-repeat; } + +.reveal > .overlay header a.close .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); } + +.reveal > .overlay header a.external .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); } + +.reveal > .overlay .viewport { + position: absolute; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + top: 40px; + right: 0; + bottom: 0; + left: 0; } + +.reveal > .overlay.overlay-preview .viewport iframe { + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + border: 0; + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; } + +.reveal > .overlay.overlay-preview.loaded .viewport iframe { + opacity: 1; + visibility: visible; } + +.reveal > .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; } + +.reveal > .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; } + +.reveal > .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; } + +.reveal > .overlay.overlay-preview.loaded .spinner { + opacity: 0; + visibility: hidden; + -webkit-transform: scale(0.2); + transform: scale(0.2); } + +.reveal > .overlay.overlay-help .viewport { + overflow: auto; + color: #fff; } + +.reveal > .overlay.overlay-help .viewport .viewport-inner { + width: 600px; + margin: auto; + padding: 20px 20px 80px 20px; + text-align: center; + letter-spacing: normal; } + +.reveal > .overlay.overlay-help .viewport .viewport-inner .title { + font-size: 20px; } + +.reveal > .overlay.overlay-help .viewport .viewport-inner table { + border: 1px solid #fff; + border-collapse: collapse; + font-size: 16px; } + +.reveal > .overlay.overlay-help .viewport .viewport-inner table th, +.reveal > .overlay.overlay-help .viewport .viewport-inner table td { + width: 200px; + padding: 14px; + border: 1px solid #fff; + vertical-align: middle; } + +.reveal > .overlay.overlay-help .viewport .viewport-inner table th { + padding-top: 20px; + padding-bottom: 20px; } + +/********************************************* + * PLAYBACK COMPONENT + *********************************************/ +.reveal .playback { + position: absolute; + left: 15px; + bottom: 20px; + z-index: 30; + cursor: pointer; + transition: all 400ms ease; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } + +.reveal.overview .playback { + opacity: 0; + visibility: hidden; } + +/********************************************* + * CODE HIGHLGIHTING + *********************************************/ +.reveal .hljs table { + margin: initial; } + +.reveal .hljs-ln-code, +.reveal .hljs-ln-numbers { + padding: 0; + border: 0; } + +.reveal .hljs-ln-numbers { + opacity: 0.6; + padding-right: 0.75em; + text-align: right; + vertical-align: top; } + +.reveal .hljs.has-highlights tr:not(.highlight-line) { + opacity: 0.4; } + +.reveal .hljs:not(:first-child).fragment { + position: absolute; + top: 0; + left: 0; + width: 100%; + box-sizing: border-box; } + +/********************************************* + * ROLLING LINKS + *********************************************/ +.reveal .roll { + display: inline-block; + line-height: 1.2; + overflow: hidden; + vertical-align: top; + -webkit-perspective: 400px; + perspective: 400px; + -webkit-perspective-origin: 50% 50%; + perspective-origin: 50% 50%; } + +.reveal .roll:hover { + background: none; + text-shadow: none; } + +.reveal .roll span { + display: block; + position: relative; + padding: 0 2px; + pointer-events: none; + transition: all 400ms ease; + -webkit-transform-origin: 50% 0%; + transform-origin: 50% 0%; + -webkit-transform-style: preserve-3d; + transform-style: preserve-3d; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; } + +.reveal .roll:hover span { + background: rgba(0, 0, 0, 0.5); + -webkit-transform: translate3d(0px, 0px, -45px) rotateX(90deg); + transform: translate3d(0px, 0px, -45px) rotateX(90deg); } + +.reveal .roll span:after { + content: attr(data-title); + display: block; + position: absolute; + left: 0; + top: 0; + padding: 0 2px; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-transform-origin: 50% 0%; + transform-origin: 50% 0%; + -webkit-transform: translate3d(0px, 110%, 0px) rotateX(-90deg); + transform: translate3d(0px, 110%, 0px) rotateX(-90deg); } + +/********************************************* + * SPEAKER NOTES + *********************************************/ +.reveal aside.notes { + display: none; } + +.reveal .speaker-notes { + display: none; + position: absolute; + width: 33.3333333333%; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; + z-index: 1; + font-size: 18px; + line-height: 1.4; + border: 1px solid rgba(0, 0, 0, 0.05); + color: #222; + background-color: #f5f5f5; + overflow: auto; + box-sizing: border-box; + text-align: left; + font-family: Helvetica, sans-serif; + -webkit-overflow-scrolling: touch; } + .reveal .speaker-notes .notes-placeholder { + color: #ccc; + font-style: italic; } + .reveal .speaker-notes:focus { + outline: none; } + .reveal .speaker-notes:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; } + +.reveal.show-notes { + max-width: 75%; + overflow: visible; } + +.reveal.show-notes .speaker-notes { + display: block; } + +@media screen and (min-width: 1600px) { + .reveal .speaker-notes { + font-size: 20px; } } + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + max-height: 70vh; + overflow: visible; } + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: 42.8571428571%; + height: 30vh; + border: 0; } } + +@media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; + max-height: 60vh; } + .reveal.show-notes .speaker-notes { + top: 100%; + height: 66.6666666667%; + height: 40vh; } + .reveal .speaker-notes { + font-size: 14px; } } + +/********************************************* + * ZOOM PLUGIN + *********************************************/ +.zoomed .reveal *, +.zoomed .reveal *:before, +.zoomed .reveal *:after { + -webkit-backface-visibility: visible !important; + backface-visibility: visible !important; } + +.zoomed .reveal .progress, +.zoomed .reveal .controls { + opacity: 0; } + +.zoomed .reveal .roll span { + background: none; } + +.zoomed .reveal .roll span:after { + visibility: hidden; } diff --git a/CreatingReliableSoftwareCpp/css/reveal.scss b/CreatingReliableSoftwareCpp/css/reveal.scss new file mode 100644 index 0000000..ab11f32 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/reveal.scss @@ -0,0 +1,1777 @@ +/*! + * reveal.js + * http://revealjs.com + * MIT licensed + * + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se + */ + + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +html { + width: 100%; + height: 100%; + height: 100vh; + height: calc( var(--vh, 1vh) * 100 ); + overflow: hidden; +} + +body { + height: 100%; + overflow: hidden; + position: relative; + line-height: 1; + margin: 0; + + background-color: #fff; + color: #000; +} + + +/********************************************* + * VIEW FRAGMENTS + *********************************************/ + +.reveal .slides section .fragment { + opacity: 0; + visibility: hidden; + transition: all .2s ease; + + &.visible { + opacity: 1; + visibility: inherit; + } +} + +.reveal .slides section .fragment.grow { + opacity: 1; + visibility: inherit; + + &.visible { + transform: scale( 1.3 ); + } +} + +.reveal .slides section .fragment.shrink { + opacity: 1; + visibility: inherit; + + &.visible { + transform: scale( 0.7 ); + } +} + +.reveal .slides section .fragment.zoom-in { + transform: scale( 0.1 ); + + &.visible { + transform: none; + } +} + +.reveal .slides section .fragment.fade-out { + opacity: 1; + visibility: inherit; + + &.visible { + opacity: 0; + visibility: hidden; + } +} + +.reveal .slides section .fragment.semi-fade-out { + opacity: 1; + visibility: inherit; + + &.visible { + opacity: 0.5; + visibility: inherit; + } +} + +.reveal .slides section .fragment.strike { + opacity: 1; + visibility: inherit; + + &.visible { + text-decoration: line-through; + } +} + +.reveal .slides section .fragment.fade-up { + transform: translate(0, 40px); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-down { + transform: translate(0, -40px); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-right { + transform: translate(-40px, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-left { + transform: translate(40px, 0); + + &.visible { + transform: translate(0, 0); + } +} + +.reveal .slides section .fragment.fade-in-then-out, +.reveal .slides section .fragment.current-visible { + opacity: 0; + visibility: hidden; + + &.current-fragment { + opacity: 1; + visibility: inherit; + } +} + +.reveal .slides section .fragment.fade-in-then-semi-out { + opacity: 0; + visibility: hidden; + + &.visible { + opacity: 0.5; + visibility: inherit; + } + + &.current-fragment { + opacity: 1; + visibility: inherit; + } +} + +.reveal .slides section .fragment.highlight-red, +.reveal .slides section .fragment.highlight-current-red, +.reveal .slides section .fragment.highlight-green, +.reveal .slides section .fragment.highlight-current-green, +.reveal .slides section .fragment.highlight-blue, +.reveal .slides section .fragment.highlight-current-blue { + opacity: 1; + visibility: inherit; +} + .reveal .slides section .fragment.highlight-red.visible { + color: #ff2c2d + } + .reveal .slides section .fragment.highlight-green.visible { + color: #17ff2e; + } + .reveal .slides section .fragment.highlight-blue.visible { + color: #1b91ff; + } + +.reveal .slides section .fragment.highlight-current-red.current-fragment { + color: #ff2c2d +} +.reveal .slides section .fragment.highlight-current-green.current-fragment { + color: #17ff2e; +} +.reveal .slides section .fragment.highlight-current-blue.current-fragment { + color: #1b91ff; +} + + +/********************************************* + * DEFAULT ELEMENT STYLES + *********************************************/ + +/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */ +.reveal:after { + content: ''; + font-style: italic; +} + +.reveal iframe { + z-index: 1; +} + +/** Prevents layering issues in certain browser/transition combinations */ +.reveal a { + position: relative; +} + +.reveal .stretch { + max-width: none; + max-height: none; +} + +.reveal pre.stretch code { + height: 100%; + max-height: 100%; + box-sizing: border-box; +} + + +/********************************************* + * CONTROLS + *********************************************/ + +@keyframes bounce-right { + 0%, 10%, 25%, 40%, 50% {transform: translateX(0);} + 20% {transform: translateX(10px);} + 30% {transform: translateX(-5px);} +} + +@keyframes bounce-down { + 0%, 10%, 25%, 40%, 50% {transform: translateY(0);} + 20% {transform: translateY(10px);} + 30% {transform: translateY(-5px);} +} + +$controlArrowSize: 3.6em; +$controlArrowSpacing: 1.4em; +$controlArrowLength: 2.6em; +$controlArrowThickness: 0.5em; +$controlsArrowAngle: 45deg; +$controlsArrowAngleHover: 40deg; +$controlsArrowAngleActive: 36deg; + +@mixin controlsArrowTransform( $angle ) { + &:before { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( $angle ); + } + + &:after { + transform: translateX(($controlArrowSize - $controlArrowLength)/2) translateY(($controlArrowSize - $controlArrowThickness)/2) rotate( -$angle ); + } +} + +.reveal .controls { + $spacing: 12px; + + display: none; + position: absolute; + top: auto; + bottom: $spacing; + right: $spacing; + left: auto; + z-index: 11; + color: #000; + pointer-events: none; + font-size: 10px; + + button { + position: absolute; + padding: 0; + background-color: transparent; + border: 0; + outline: 0; + cursor: pointer; + color: currentColor; + transform: scale(.9999); + transition: color 0.2s ease, + opacity 0.2s ease, + transform 0.2s ease; + z-index: 2; // above slides + pointer-events: auto; + font-size: inherit; + + visibility: hidden; + opacity: 0; + + -webkit-appearance: none; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); + } + + .controls-arrow:before, + .controls-arrow:after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: $controlArrowLength; + height: $controlArrowThickness; + border-radius: $controlArrowThickness/2; + background-color: currentColor; + + transition: all 0.15s ease, background-color 0.8s ease; + transform-origin: floor(($controlArrowThickness/2)*10)/10 50%; + will-change: transform; + } + + .controls-arrow { + position: relative; + width: $controlArrowSize; + height: $controlArrowSize; + + @include controlsArrowTransform( $controlsArrowAngle ); + + &:hover { + @include controlsArrowTransform( $controlsArrowAngleHover ); + } + + &:active { + @include controlsArrowTransform( $controlsArrowAngleActive ); + } + } + + .navigate-left { + right: $controlArrowSize + $controlArrowSpacing*2; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( -10px ); + } + + .navigate-right { + right: 0; + bottom: $controlArrowSpacing + $controlArrowSize/2; + transform: translateX( 10px ); + + .controls-arrow { + transform: rotate( 180deg ); + } + + &.highlight { + animation: bounce-right 2s 50 both ease-out; + } + } + + .navigate-up { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: $controlArrowSpacing*2 + $controlArrowSize; + transform: translateY( -10px ); + + .controls-arrow { + transform: rotate( 90deg ); + } + } + + .navigate-down { + right: $controlArrowSpacing + $controlArrowSize/2; + bottom: -$controlArrowSpacing; + padding-bottom: $controlArrowSpacing; + transform: translateY( 10px ); + + .controls-arrow { + transform: rotate( -90deg ); + } + + &.highlight { + animation: bounce-down 2s 50 both ease-out; + } + } + + // Back arrow style: "faded": + // Deemphasize backwards navigation arrows in favor of drawing + // attention to forwards navigation + &[data-controls-back-arrows="faded"] .navigate-left.enabled, + &[data-controls-back-arrows="faded"] .navigate-up.enabled { + opacity: 0.3; + + &:hover { + opacity: 1; + } + } + + // Back arrow style: "hidden": + // Never show arrows for backwards navigation + &[data-controls-back-arrows="hidden"] .navigate-left.enabled, + &[data-controls-back-arrows="hidden"] .navigate-up.enabled { + opacity: 0; + visibility: hidden; + } + + // Any control button that can be clicked is "enabled" + .enabled { + visibility: visible; + opacity: 0.9; + cursor: pointer; + transform: none; + } + + // Any control button that leads to showing or hiding + // a fragment + .enabled.fragmented { + opacity: 0.5; + } + + .enabled:hover, + .enabled.fragmented:hover { + opacity: 1; + } +} + +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-up, +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-down { + display: none; +} + +// Adjust the layout when there are no vertical slides +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-left, +.reveal:not(.has-vertical-slides) .controls .navigate-left { + bottom: $controlArrowSpacing; + right: 0.5em + $controlArrowSpacing + $controlArrowSize; +} + +.reveal[data-navigation-mode="linear"].has-horizontal-slides .navigate-right, +.reveal:not(.has-vertical-slides) .controls .navigate-right { + bottom: $controlArrowSpacing; + right: 0.5em; +} + +// Adjust the layout when there are no horizontal slides +.reveal:not(.has-horizontal-slides) .controls .navigate-up { + right: $controlArrowSpacing; + bottom: $controlArrowSpacing + $controlArrowSize; +} +.reveal:not(.has-horizontal-slides) .controls .navigate-down { + right: $controlArrowSpacing; + bottom: 0.5em; +} + +// Invert arrows based on background color +.reveal.has-dark-background .controls { + color: #fff; +} +.reveal.has-light-background .controls { + color: #000; +} + +// Disable active states on touch devices +.reveal.no-hover .controls .controls-arrow:hover, +.reveal.no-hover .controls .controls-arrow:active { + @include controlsArrowTransform( $controlsArrowAngle ); +} + +// Edge aligned controls layout +@media screen and (min-width: 500px) { + + $spacing: 0.8em; + + .reveal .controls[data-controls-layout="edges"] { + & { + top: 0; + right: 0; + bottom: 0; + left: 0; + } + + .navigate-left, + .navigate-right, + .navigate-up, + .navigate-down { + bottom: auto; + right: auto; + } + + .navigate-left { + top: 50%; + left: $spacing; + margin-top: -$controlArrowSize/2; + } + + .navigate-right { + top: 50%; + right: $spacing; + margin-top: -$controlArrowSize/2; + } + + .navigate-up { + top: $spacing; + left: 50%; + margin-left: -$controlArrowSize/2; + } + + .navigate-down { + bottom: $spacing - $controlArrowSpacing + 0.3em; + left: 50%; + margin-left: -$controlArrowSize/2; + } + } + +} + + +/********************************************* + * PROGRESS BAR + *********************************************/ + +.reveal .progress { + position: absolute; + display: none; + height: 3px; + width: 100%; + bottom: 0; + left: 0; + z-index: 10; + + background-color: rgba( 0, 0, 0, 0.2 ); + color: #fff; +} + .reveal .progress:after { + content: ''; + display: block; + position: absolute; + height: 10px; + width: 100%; + top: -10px; + } + .reveal .progress span { + display: block; + height: 100%; + width: 0px; + + background-color: currentColor; + transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + +/********************************************* + * SLIDE NUMBER + *********************************************/ + +.reveal .slide-number { + position: absolute; + display: block; + right: 8px; + bottom: 8px; + z-index: 31; + font-family: Helvetica, sans-serif; + font-size: 12px; + line-height: 1; + color: #fff; + background-color: rgba( 0, 0, 0, 0.4 ); + padding: 5px; +} + +.reveal .slide-number a { + color: currentColor; +} + +.reveal .slide-number-delimiter { + margin: 0 3px; +} + +/********************************************* + * SLIDES + *********************************************/ + +.reveal { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; + touch-action: pinch-zoom; +} + +.reveal .slides { + position: absolute; + width: 100%; + height: 100%; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + pointer-events: none; + + overflow: visible; + z-index: 1; + text-align: center; + perspective: 600px; + perspective-origin: 50% 40%; +} + +.reveal .slides>section { + perspective: 600px; +} + +.reveal .slides>section, +.reveal .slides>section>section { + display: none; + position: absolute; + width: 100%; + padding: 20px 0px; + pointer-events: auto; + + z-index: 10; + transform-style: flat; + transition: transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985), + opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); +} + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"] .slides section { + transition-duration: 400ms; +} +.reveal[data-transition-speed="slow"] .slides section { + transition-duration: 1200ms; +} + +/* Slide-specific transition speed overrides */ +.reveal .slides section[data-transition-speed="fast"] { + transition-duration: 400ms; +} +.reveal .slides section[data-transition-speed="slow"] { + transition-duration: 1200ms; +} + +.reveal .slides>section.stack { + padding-top: 0; + padding-bottom: 0; + pointer-events: none; + height: 100%; +} + +.reveal .slides>section.present, +.reveal .slides>section>section.present { + display: block; + z-index: 11; + opacity: 1; +} + +.reveal .slides>section:empty, +.reveal .slides>section>section:empty, +.reveal .slides>section[data-background-interactive], +.reveal .slides>section>section[data-background-interactive] { + pointer-events: none; +} + +.reveal.center, +.reveal.center .slides, +.reveal.center .slides section { + min-height: 0 !important; +} + +/* Don't allow interaction with invisible slides */ +.reveal .slides>section.future, +.reveal .slides>section>section.future, +.reveal .slides>section.past, +.reveal .slides>section>section.past { + pointer-events: none; +} + +.reveal.overview .slides>section, +.reveal.overview .slides>section>section { + pointer-events: auto; +} + +.reveal .slides>section.past, +.reveal .slides>section.future, +.reveal .slides>section>section.past, +.reveal .slides>section>section.future { + opacity: 0; +} + + +/********************************************* + * Mixins for readability of transitions + *********************************************/ + +@mixin transition-global($style) { + .reveal .slides section[data-transition=#{$style}], + .reveal.#{$style} .slides section:not([data-transition]) { + @content; + } +} +@mixin transition-stack($style) { + .reveal .slides section[data-transition=#{$style}].stack, + .reveal.#{$style} .slides section.stack { + @content; + } +} +@mixin transition-horizontal-past($style) { + .reveal .slides>section[data-transition=#{$style}].past, + .reveal .slides>section[data-transition~=#{$style}-out].past, + .reveal.#{$style} .slides>section:not([data-transition]).past { + @content; + } +} +@mixin transition-horizontal-future($style) { + .reveal .slides>section[data-transition=#{$style}].future, + .reveal .slides>section[data-transition~=#{$style}-in].future, + .reveal.#{$style} .slides>section:not([data-transition]).future { + @content; + } +} + +@mixin transition-vertical-past($style) { + .reveal .slides>section>section[data-transition=#{$style}].past, + .reveal .slides>section>section[data-transition~=#{$style}-out].past, + .reveal.#{$style} .slides>section>section:not([data-transition]).past { + @content; + } +} +@mixin transition-vertical-future($style) { + .reveal .slides>section>section[data-transition=#{$style}].future, + .reveal .slides>section>section[data-transition~=#{$style}-in].future, + .reveal.#{$style} .slides>section>section:not([data-transition]).future { + @content; + } +} + +/********************************************* + * SLIDE TRANSITION + * Aliased 'linear' for backwards compatibility + *********************************************/ + +@each $stylename in slide, linear { + .reveal.#{$stylename} section { + backface-visibility: hidden; + } + @include transition-horizontal-past(#{$stylename}) { + transform: translate(-150%, 0); + } + @include transition-horizontal-future(#{$stylename}) { + transform: translate(150%, 0); + } + @include transition-vertical-past(#{$stylename}) { + transform: translate(0, -150%); + } + @include transition-vertical-future(#{$stylename}) { + transform: translate(0, 150%); + } +} + +/********************************************* + * CONVEX TRANSITION + * Aliased 'default' for backwards compatibility + *********************************************/ + +@each $stylename in default, convex { + @include transition-stack(#{$stylename}) { + transform-style: preserve-3d; + } + + @include transition-horizontal-past(#{$stylename}) { + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); + } + @include transition-horizontal-future(#{$stylename}) { + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); + } + @include transition-vertical-past(#{$stylename}) { + transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); + } + @include transition-vertical-future(#{$stylename}) { + transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); + } +} + +/********************************************* + * CONCAVE TRANSITION + *********************************************/ + +@include transition-stack(concave) { + transform-style: preserve-3d; +} + +@include transition-horizontal-past(concave) { + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); +} +@include transition-horizontal-future(concave) { + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); +} +@include transition-vertical-past(concave) { + transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); +} +@include transition-vertical-future(concave) { + transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); +} + + +/********************************************* + * ZOOM TRANSITION + *********************************************/ + +@include transition-global(zoom) { + transition-timing-function: ease; +} +@include transition-horizontal-past(zoom) { + visibility: hidden; + transform: scale(16); +} +@include transition-horizontal-future(zoom) { + visibility: hidden; + transform: scale(0.2); +} +@include transition-vertical-past(zoom) { + transform: scale(16); +} +@include transition-vertical-future(zoom) { + transform: scale(0.2); +} + + +/********************************************* + * CUBE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ + +.reveal.cube .slides { + perspective: 1300px; +} + +.reveal.cube .slides section { + padding: 30px; + min-height: 700px; + backface-visibility: hidden; + box-sizing: border-box; + transform-style: preserve-3d; +} + .reveal.center.cube .slides section { + min-height: 0; + } + .reveal.cube .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0,0,0,0.1); + border-radius: 4px; + transform: translateZ( -20px ); + } + .reveal.cube .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0,0,0,0.2); + transform: translateZ(-90px) rotateX( 65deg ); + } + +.reveal.cube .slides>section.stack { + padding: 0; + background: none; +} + +.reveal.cube .slides>section.past { + transform-origin: 100% 0%; + transform: translate3d(-100%, 0, 0) rotateY(-90deg); +} + +.reveal.cube .slides>section.future { + transform-origin: 0% 0%; + transform: translate3d(100%, 0, 0) rotateY(90deg); +} + +.reveal.cube .slides>section>section.past { + transform-origin: 0% 100%; + transform: translate3d(0, -100%, 0) rotateX(90deg); +} + +.reveal.cube .slides>section>section.future { + transform-origin: 0% 0%; + transform: translate3d(0, 100%, 0) rotateX(-90deg); +} + + +/********************************************* + * PAGE TRANSITION + * + * WARNING: + * this is deprecated and will be removed in a + * future version. + *********************************************/ + +.reveal.page .slides { + perspective-origin: 0% 50%; + perspective: 3000px; +} + +.reveal.page .slides section { + padding: 30px; + min-height: 700px; + box-sizing: border-box; + transform-style: preserve-3d; +} + .reveal.page .slides section.past { + z-index: 12; + } + .reveal.page .slides section:not(.stack):before { + content: ''; + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 0; + top: 0; + background: rgba(0,0,0,0.1); + transform: translateZ( -20px ); + } + .reveal.page .slides section:not(.stack):after { + content: ''; + position: absolute; + display: block; + width: 90%; + height: 30px; + left: 5%; + bottom: 0; + background: none; + z-index: 1; + + border-radius: 4px; + box-shadow: 0px 95px 25px rgba(0,0,0,0.2); + + -webkit-transform: translateZ(-90px) rotateX( 65deg ); + } + +.reveal.page .slides>section.stack { + padding: 0; + background: none; +} + +.reveal.page .slides>section.past { + transform-origin: 0% 0%; + transform: translate3d(-40%, 0, 0) rotateY(-80deg); +} + +.reveal.page .slides>section.future { + transform-origin: 100% 0%; + transform: translate3d(0, 0, 0); +} + +.reveal.page .slides>section>section.past { + transform-origin: 0% 0%; + transform: translate3d(0, -40%, 0) rotateX(80deg); +} + +.reveal.page .slides>section>section.future { + transform-origin: 0% 100%; + transform: translate3d(0, 0, 0); +} + + +/********************************************* + * FADE TRANSITION + *********************************************/ + +.reveal .slides section[data-transition=fade], +.reveal.fade .slides section:not([data-transition]), +.reveal.fade .slides>section>section:not([data-transition]) { + transform: none; + transition: opacity 0.5s; +} + + +.reveal.fade.overview .slides section, +.reveal.fade.overview .slides>section>section { + transition: none; +} + + +/********************************************* + * NO TRANSITION + *********************************************/ + +@include transition-global(none) { + transform: none; + transition: none; +} + + +/********************************************* + * PAUSED MODE + *********************************************/ + +.reveal .pause-overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: black; + visibility: hidden; + opacity: 0; + z-index: 100; + transition: all 1s ease; +} + +.reveal .pause-overlay .resume-button { + position: absolute; + bottom: 20px; + right: 20px; + color: #ccc; + border-radius: 2px; + padding: 6px 14px; + border: 2px solid #ccc; + font-size: 16px; + background: transparent; + cursor: pointer; + + &:hover { + color: #fff; + border-color: #fff; + } +} + +.reveal.paused .pause-overlay { + visibility: visible; + opacity: 1; +} + + +/********************************************* + * FALLBACK + *********************************************/ + +.no-transforms { + overflow-y: auto; +} + +.no-transforms .reveal { + overflow: visible; +} + +.no-transforms .reveal .slides { + position: relative; + width: 80%; + max-width: 1280px; + height: auto; + top: 0; + margin: 0 auto; + text-align: center; +} + +.no-transforms .reveal .controls, +.no-transforms .reveal .progress { + display: none; +} + +.no-transforms .reveal .slides section { + display: block; + opacity: 1; + position: relative; + height: auto; + min-height: 0; + top: 0; + left: 0; + margin: 10vh 0; + margin: 70px 0; + transform: none; +} + +.reveal .no-transition, +.reveal .no-transition * { + transition: none !important; +} + + +/********************************************* + * PER-SLIDE BACKGROUNDS + *********************************************/ + +.reveal .backgrounds { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + perspective: 600px; +} + .reveal .slide-background { + display: none; + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + visibility: hidden; + overflow: hidden; + + background-color: rgba( 0, 0, 0, 0 ); + + transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + + .reveal .slide-background-content { + position: absolute; + width: 100%; + height: 100%; + + background-position: 50% 50%; + background-repeat: no-repeat; + background-size: cover; + } + + .reveal .slide-background.stack { + display: block; + } + + .reveal .slide-background.present { + opacity: 1; + visibility: visible; + z-index: 2; + } + + .print-pdf .reveal .slide-background { + opacity: 1 !important; + visibility: visible !important; + } + +/* Video backgrounds */ +.reveal .slide-background video { + position: absolute; + width: 100%; + height: 100%; + max-width: none; + max-height: none; + top: 0; + left: 0; + object-fit: cover; +} + .reveal .slide-background[data-background-size="contain"] video { + object-fit: contain; + } + +/* Immediate transition style */ +.reveal[data-background-transition=none]>.backgrounds .slide-background, +.reveal>.backgrounds .slide-background[data-background-transition=none] { + transition: none; +} + +/* Slide */ +.reveal[data-background-transition=slide]>.backgrounds .slide-background, +.reveal>.backgrounds .slide-background[data-background-transition=slide] { + opacity: 1; + backface-visibility: hidden; +} + .reveal[data-background-transition=slide]>.backgrounds .slide-background.past, + .reveal>.backgrounds .slide-background.past[data-background-transition=slide] { + transform: translate(-100%, 0); + } + .reveal[data-background-transition=slide]>.backgrounds .slide-background.future, + .reveal>.backgrounds .slide-background.future[data-background-transition=slide] { + transform: translate(100%, 0); + } + + .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.past, + .reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=slide] { + transform: translate(0, -100%); + } + .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.future, + .reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=slide] { + transform: translate(0, 100%); + } + + +/* Convex */ +.reveal[data-background-transition=convex]>.backgrounds .slide-background.past, +.reveal>.backgrounds .slide-background.past[data-background-transition=convex] { + opacity: 0; + transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); +} +.reveal[data-background-transition=convex]>.backgrounds .slide-background.future, +.reveal>.backgrounds .slide-background.future[data-background-transition=convex] { + opacity: 0; + transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); +} + +.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.past, +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=convex] { + opacity: 0; + transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); +} +.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.future, +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=convex] { + opacity: 0; + transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); +} + + +/* Concave */ +.reveal[data-background-transition=concave]>.backgrounds .slide-background.past, +.reveal>.backgrounds .slide-background.past[data-background-transition=concave] { + opacity: 0; + transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); +} +.reveal[data-background-transition=concave]>.backgrounds .slide-background.future, +.reveal>.backgrounds .slide-background.future[data-background-transition=concave] { + opacity: 0; + transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); +} + +.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.past, +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=concave] { + opacity: 0; + transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); +} +.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.future, +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=concave] { + opacity: 0; + transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); +} + +/* Zoom */ +.reveal[data-background-transition=zoom]>.backgrounds .slide-background, +.reveal>.backgrounds .slide-background[data-background-transition=zoom] { + transition-timing-function: ease; +} + +.reveal[data-background-transition=zoom]>.backgrounds .slide-background.past, +.reveal>.backgrounds .slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(16); +} +.reveal[data-background-transition=zoom]>.backgrounds .slide-background.future, +.reveal>.backgrounds .slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(0.2); +} + +.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.past, +.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(16); +} +.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.future, +.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=zoom] { + opacity: 0; + visibility: hidden; + transform: scale(0.2); +} + + +/* Global transition speed settings */ +.reveal[data-transition-speed="fast"]>.backgrounds .slide-background { + transition-duration: 400ms; +} +.reveal[data-transition-speed="slow"]>.backgrounds .slide-background { + transition-duration: 1200ms; +} + + +/********************************************* + * OVERVIEW + *********************************************/ + +.reveal.overview { + perspective-origin: 50% 50%; + perspective: 700px; + + .slides { + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + + .slides section { + height: 100%; + top: 0 !important; + opacity: 1 !important; + overflow: hidden; + visibility: visible !important; + cursor: pointer; + box-sizing: border-box; + } + .slides section:hover, + .slides section.present { + outline: 10px solid rgba(150,150,150,0.4); + outline-offset: 10px; + } + .slides section .fragment { + opacity: 1; + transition: none; + } + .slides section:after, + .slides section:before { + display: none !important; + } + .slides>section.stack { + padding: 0; + top: 0 !important; + background: none; + outline: none; + overflow: visible; + } + + .backgrounds { + perspective: inherit; + + // Fixes overview rendering errors in FF48+, not applied to + // other browsers since it degrades performance + -moz-transform-style: preserve-3d; + } + + .backgrounds .slide-background { + opacity: 1; + visibility: visible; + + // This can't be applied to the slide itself in Safari + outline: 10px solid rgba(150,150,150,0.1); + outline-offset: 10px; + } + + .backgrounds .slide-background.stack { + overflow: visible; + } +} + +// Disable transitions transitions while we're activating +// or deactivating the overview mode. +.reveal.overview .slides section, +.reveal.overview-deactivating .slides section { + transition: none; +} + +.reveal.overview .backgrounds .slide-background, +.reveal.overview-deactivating .backgrounds .slide-background { + transition: none; +} + + +/********************************************* + * RTL SUPPORT + *********************************************/ + +.reveal.rtl .slides, +.reveal.rtl .slides h1, +.reveal.rtl .slides h2, +.reveal.rtl .slides h3, +.reveal.rtl .slides h4, +.reveal.rtl .slides h5, +.reveal.rtl .slides h6 { + direction: rtl; + font-family: sans-serif; +} + +.reveal.rtl pre, +.reveal.rtl code { + direction: ltr; +} + +.reveal.rtl ol, +.reveal.rtl ul { + text-align: right; +} + +.reveal.rtl .progress span { + float: right +} + +/********************************************* + * PARALLAX BACKGROUND + *********************************************/ + +.reveal.has-parallax-background .backgrounds { + transition: all 0.8s ease; +} + +/* Global transition speed settings */ +.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds { + transition-duration: 400ms; +} +.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds { + transition-duration: 1200ms; +} + + +/********************************************* + * OVERLAY FOR LINK PREVIEWS AND HELP + *********************************************/ + +.reveal > .overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1000; + background: rgba( 0, 0, 0, 0.9 ); + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; +} + .reveal > .overlay.visible { + opacity: 1; + visibility: visible; + } + + .reveal > .overlay .spinner { + position: absolute; + display: block; + top: 50%; + left: 50%; + width: 32px; + height: 32px; + margin: -16px 0 0 -16px; + z-index: 10; + background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D); + + visibility: visible; + opacity: 0.6; + transition: all 0.3s ease; + } + + .reveal > .overlay header { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 40px; + z-index: 2; + border-bottom: 1px solid #222; + } + .reveal > .overlay header a { + display: inline-block; + width: 40px; + height: 40px; + line-height: 36px; + padding: 0 10px; + float: right; + opacity: 0.6; + + box-sizing: border-box; + } + .reveal > .overlay header a:hover { + opacity: 1; + } + .reveal > .overlay header a .icon { + display: inline-block; + width: 20px; + height: 20px; + + background-position: 50% 50%; + background-size: 100%; + background-repeat: no-repeat; + } + .reveal > .overlay header a.close .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); + } + .reveal > .overlay header a.external .icon { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); + } + + .reveal > .overlay .viewport { + position: absolute; + display: flex; + top: 40px; + right: 0; + bottom: 0; + left: 0; + } + + .reveal > .overlay.overlay-preview .viewport iframe { + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + border: 0; + + opacity: 0; + visibility: hidden; + transition: all 0.3s ease; + } + + .reveal > .overlay.overlay-preview.loaded .viewport iframe { + opacity: 1; + visibility: visible; + } + + .reveal > .overlay.overlay-preview.loaded .viewport-inner { + position: absolute; + z-index: -1; + left: 0; + top: 45%; + width: 100%; + text-align: center; + letter-spacing: normal; + } + .reveal > .overlay.overlay-preview .x-frame-error { + opacity: 0; + transition: opacity 0.3s ease 0.3s; + } + .reveal > .overlay.overlay-preview.loaded .x-frame-error { + opacity: 1; + } + + .reveal > .overlay.overlay-preview.loaded .spinner { + opacity: 0; + visibility: hidden; + transform: scale(0.2); + } + + .reveal > .overlay.overlay-help .viewport { + overflow: auto; + color: #fff; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner { + width: 600px; + margin: auto; + padding: 20px 20px 80px 20px; + text-align: center; + letter-spacing: normal; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner .title { + font-size: 20px; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner table { + border: 1px solid #fff; + border-collapse: collapse; + font-size: 16px; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner table th, + .reveal > .overlay.overlay-help .viewport .viewport-inner table td { + width: 200px; + padding: 14px; + border: 1px solid #fff; + vertical-align: middle; + } + + .reveal > .overlay.overlay-help .viewport .viewport-inner table th { + padding-top: 20px; + padding-bottom: 20px; + } + + +/********************************************* + * PLAYBACK COMPONENT + *********************************************/ + +.reveal .playback { + position: absolute; + left: 15px; + bottom: 20px; + z-index: 30; + cursor: pointer; + transition: all 400ms ease; + -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 ); +} + +.reveal.overview .playback { + opacity: 0; + visibility: hidden; +} + + +/********************************************* + * CODE HIGHLGIHTING + *********************************************/ + +.reveal .hljs table { + margin: initial; +} + +.reveal .hljs-ln-code, +.reveal .hljs-ln-numbers { + padding: 0; + border: 0; +} + +.reveal .hljs-ln-numbers { + opacity: 0.6; + padding-right: 0.75em; + text-align: right; + vertical-align: top; +} + +.reveal .hljs.has-highlights tr:not(.highlight-line) { + opacity: 0.4; +} + +.reveal .hljs:not(:first-child).fragment { + position: absolute; + top: 0; + left: 0; + width: 100%; + box-sizing: border-box; +} + + +/********************************************* + * ROLLING LINKS + *********************************************/ + +.reveal .roll { + display: inline-block; + line-height: 1.2; + overflow: hidden; + + vertical-align: top; + perspective: 400px; + perspective-origin: 50% 50%; +} + .reveal .roll:hover { + background: none; + text-shadow: none; + } +.reveal .roll span { + display: block; + position: relative; + padding: 0 2px; + + pointer-events: none; + transition: all 400ms ease; + transform-origin: 50% 0%; + transform-style: preserve-3d; + backface-visibility: hidden; +} + .reveal .roll:hover span { + background: rgba(0,0,0,0.5); + transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg ); + } +.reveal .roll span:after { + content: attr(data-title); + + display: block; + position: absolute; + left: 0; + top: 0; + padding: 0 2px; + backface-visibility: hidden; + transform-origin: 50% 0%; + transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg ); +} + + +/********************************************* + * SPEAKER NOTES + *********************************************/ + +$notesWidthPercent: 25%; + +// Hide on-page notes +.reveal aside.notes { + display: none; +} + +// An interface element that can optionally be used to show the +// speaker notes to all viewers, on top of the presentation +.reveal .speaker-notes { + display: none; + position: absolute; + width: $notesWidthPercent / (1-$notesWidthPercent/100) * 1%; + height: 100%; + top: 0; + left: 100%; + padding: 14px 18px 14px 18px; + z-index: 1; + font-size: 18px; + line-height: 1.4; + border: 1px solid rgba( 0, 0, 0, 0.05 ); + color: #222; + background-color: #f5f5f5; + overflow: auto; + box-sizing: border-box; + text-align: left; + font-family: Helvetica, sans-serif; + -webkit-overflow-scrolling: touch; + + .notes-placeholder { + color: #ccc; + font-style: italic; + } + + &:focus { + outline: none; + } + + &:before { + content: 'Speaker notes'; + display: block; + margin-bottom: 10px; + opacity: 0.5; + } +} + + +.reveal.show-notes { + max-width: 100% - $notesWidthPercent; + overflow: visible; +} + +.reveal.show-notes .speaker-notes { + display: block; +} + +@media screen and (min-width: 1600px) { + .reveal .speaker-notes { + font-size: 20px; + } +} + +@media screen and (max-width: 1024px) { + .reveal.show-notes { + border-left: 0; + max-width: none; + max-height: 70%; + max-height: 70vh; + overflow: visible; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + left: 0; + width: 100%; + height: (30/0.7)*1%; + height: 30vh; + border: 0; + } +} + +@media screen and (max-width: 600px) { + .reveal.show-notes { + max-height: 60%; + max-height: 60vh; + } + + .reveal.show-notes .speaker-notes { + top: 100%; + height: (40/0.6)*1%; + height: 40vh; + } + + .reveal .speaker-notes { + font-size: 14px; + } +} + + +/********************************************* + * ZOOM PLUGIN + *********************************************/ + +.zoomed .reveal *, +.zoomed .reveal *:before, +.zoomed .reveal *:after { + backface-visibility: visible !important; +} + +.zoomed .reveal .progress, +.zoomed .reveal .controls { + opacity: 0; +} + +.zoomed .reveal .roll span { + background: none; +} + +.zoomed .reveal .roll span:after { + visibility: hidden; +} diff --git a/CreatingReliableSoftwareCpp/css/theme/README.md b/CreatingReliableSoftwareCpp/css/theme/README.md new file mode 100644 index 0000000..5ebe72a --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/README.md @@ -0,0 +1,21 @@ +## Dependencies + +Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment including the Grunt dependencies installed before proceeding: https://github.com/hakimel/reveal.js#full-setup + +## Creating a Theme + +To create your own theme, start by duplicating a ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source). It will be automatically compiled by Grunt from Sass to CSS (see the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/gruntfile.js)) when you run `npm run build -- css-themes`. + +Each theme file does four things in the following order: + +1. **Include [/css/theme/template/mixins.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/mixins.scss)** +Shared utility functions. + +2. **Include [/css/theme/template/settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss)** +Declares a set of custom variables that the template file (step 4) expects. Can be overridden in step 3. + +3. **Override** +This is where you override the default theme. Either by specifying variables (see [settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss) for reference) or by adding any selectors and styles you please. + +4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)** +The template theme file which will generate final CSS output based on the currently defined variables. diff --git a/CreatingReliableSoftwareCpp/css/theme/beige.css b/CreatingReliableSoftwareCpp/css/theme/beige.css new file mode 100644 index 0000000..615dd6d --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/beige.css @@ -0,0 +1,277 @@ +/** + * Beige theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #f7f2d3; + background: -moz-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, white), color-stop(100%, #f7f2d3)); + background: -webkit-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -o-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: -ms-radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background: radial-gradient(center, circle cover, white 0%, #f7f2d3 100%); + background-color: #f7f3de; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #333; } + +::selection { + color: #fff; + background: rgba(79, 64, 28, 0.99); + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: rgba(79, 64, 28, 0.99); + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #333; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #8b743d; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #c0a86e; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #564826; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #333; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #8b743d; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #8b743d; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #8b743d; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #f7f3de; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/black.css b/CreatingReliableSoftwareCpp/css/theme/black.css new file mode 100644 index 0000000..7dd88c2 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/black.css @@ -0,0 +1,273 @@ +/** + * Black theme for reveal.js. This is the opposite of the 'white' theme. + * + * By Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); +section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { + color: #222; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #191919; + background-color: #191919; } + +.reveal { + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 42px; + font-weight: normal; + color: #fff; } + +::selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #fff; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-weight: 600; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 2.5em; } + +.reveal h2 { + font-size: 1.6em; } + +.reveal h3 { + font-size: 1.3em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #42affa; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #8dcffc; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #068de9; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #fff; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #42affa; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #42affa; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #42affa; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #191919; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/blood.css b/CreatingReliableSoftwareCpp/css/theme/blood.css new file mode 100644 index 0000000..5cbd488 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/blood.css @@ -0,0 +1,296 @@ +/** + * Blood theme for reveal.js + * Author: Walther http://github.com/Walther + * + * Designed to be used with highlight.js theme + * "monokai_sublime.css" available from + * https://github.com/isagalaev/highlight.js/ + * + * For other themes, change $codeBackground accordingly. + * + */ +@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #222; + background-color: #222; } + +.reveal { + font-family: Ubuntu, "sans-serif"; + font-size: 40px; + font-weight: normal; + color: #eee; } + +::selection { + color: #fff; + background: #a23; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #a23; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee; + font-family: Ubuntu, "sans-serif"; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: 2px 2px 2px #222; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #a23; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #dd5566; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #6a1520; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #eee; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #a23; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #a23; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #a23; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #222; } } + +.reveal p { + font-weight: 300; + text-shadow: 1px 1px #222; } + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + font-weight: 700; } + +.reveal p code { + background-color: #23241f; + display: inline-block; + border-radius: 7px; } + +.reveal small code { + vertical-align: baseline; } diff --git a/CreatingReliableSoftwareCpp/css/theme/coders.css b/CreatingReliableSoftwareCpp/css/theme/coders.css new file mode 100644 index 0000000..133b81e --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/coders.css @@ -0,0 +1,308 @@ +/** + * Coders School theme for reveal.js. + * + * By Łukasz "Lukin" Ziobroń + */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); +@import url(../../lib/font/rajdhani/rajdhani.css); +section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { + color: #000; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #cac7c7; + background-color: #cac7c7; + background-image: url(../../img/altkom_logo.png); + background-size: 12%; + background-repeat: no-repeat; + background-position: 2% 98%; } + +.reveal { + font-family: Rajdhani, "Source Sans Pro", Helvetica, sans-serif; + font-size: 40px; + font-weight: normal; + color: #000; + /* frame for head - streaming + background-image: url(../../img/talking_head_placeholder.png); + background-size: 320px 180px; + background-repeat: no-repeat; + background-position: 98% 2%; */ } + +::selection { + color: #000; + background: #cac7c7; + text-shadow: none; } + +::-moz-selection { + color: #000; + background: #cac7c7; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 10px 0 20px 0; + color: #000; + font-family: "Rajdhani", "Source Sans Pro", Helvetica, sans-serif; + font-weight: 600; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 2.5em; } + +.reveal h2 { + font-size: 1.6em; } + +.reveal h3 { + font-size: 1.3em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + font-size: 0.8em; + line-height: 1.3; + text-align: justify; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: justify; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; + font-size: 0.8em; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 95%; + margin: 20px auto; + text-align: left; + font-size: 0.6em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + /*box-shadow: 0px 15px 30px rgba(0, 0, 0, 0.15);*/ } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 15px; + overflow: auto; + max-height: 560px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #cf802a; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #ce904e; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #068de9; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #fff; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + background: none; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.05); + border-color: #ce904e; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #cf802a; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #cf802a; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #191919; } } + +/********************************************* + * OWN BOXES + *********************************************/ +.reveal .box { + position: absolute; + /*box-shadow: 0 1px 4px rgba(0,0,0,0.5), 0 5px 25px rgba(0,0,0,0.2);*/ + background-color: rgba(0, 0, 0, 0.7); + color: #fff; + padding: 20px; + margin: 20px 0; + font-size: 0.6em; + text-align: left; } + +/********************************************* + * MULTICOLUMN SUPPORT + *********************************************/ +.multicolumn { + display: flex; } +.col { + flex: 1; } diff --git a/CreatingReliableSoftwareCpp/css/theme/coders_white.css b/CreatingReliableSoftwareCpp/css/theme/coders_white.css new file mode 100644 index 0000000..79b423c --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/coders_white.css @@ -0,0 +1,294 @@ +/** + * Coders School theme for reveal.js. + * + * By Łukasz "Lukin" Ziobroń + */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); +section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 { + color: #222; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #fff; + background-color: #fff; + background-image: url(../../img/altkom_logo.png); + background-size: 10%; + background-repeat: no-repeat; + background-position: 1% 98%; } + +.reveal { + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 40px; + font-weight: normal; + color: #222; } + +::selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #bee4fd; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #222; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-weight: 600; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 2.5em; } + +.reveal h2 { + font-size: 1.6em; } + +.reveal h3 { + font-size: 1.3em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + font-size: 0.8em; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; + font-size: 0.9em; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.6em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 15px 30px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 560px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #cf802a; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #ce904e; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #068de9; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #222; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + background: none; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.05); + border-color: #ce904e; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #cf802a; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #cf802a; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #191919; } } + +/********************************************* + * OWN BOXES + *********************************************/ +.reveal .box { + position: absolute; + box-shadow: 0 1px 4px rgba(0,0,0,0.5), 0 5px 25px rgba(0,0,0,0.2); + background-color: rgba(0, 0, 0, 0.7); + color: #fff; + padding: 20px; + margin: 20px 0; + font-size: 0.6em; + text-align: left; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/league.css b/CreatingReliableSoftwareCpp/css/theme/league.css new file mode 100644 index 0000000..f8fba4d --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/league.css @@ -0,0 +1,279 @@ +/** + * League theme for reveal.js. + * + * This was the default theme pre-3.0.0. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #1c1e20; + background: -moz-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #555a5f), color-stop(100%, #1c1e20)); + background: -webkit-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: -o-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: -ms-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background: radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%); + background-color: #2b2b2b; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #eee; } + +::selection { + color: #fff; + background: #FF5E99; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #FF5E99; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2); + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #13DAEC; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #71e9f4; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #0d99a5; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #eee; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #13DAEC; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #13DAEC; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #13DAEC; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #2b2b2b; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/moon.css b/CreatingReliableSoftwareCpp/css/theme/moon.css new file mode 100644 index 0000000..d18f526 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/moon.css @@ -0,0 +1,277 @@ +/** + * Solarized Dark theme for reveal.js. + * Author: Achim Staebler + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #002b36; + background-color: #002b36; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #93a1a1; } + +::selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee8d5; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #268bd2; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #78b9e6; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #1a6091; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #93a1a1; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #268bd2; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #268bd2; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #002b36; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/night.css b/CreatingReliableSoftwareCpp/css/theme/night.css new file mode 100644 index 0000000..f5ccb52 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/night.css @@ -0,0 +1,271 @@ +/** + * Black theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(https://fonts.googleapis.com/css?family=Montserrat:700); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #111; + background-color: #111; } + +.reveal { + font-family: "Open Sans", sans-serif; + font-size: 40px; + font-weight: normal; + color: #eee; } + +::selection { + color: #fff; + background: #e7ad52; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #e7ad52; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #eee; + font-family: "Montserrat", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: -0.03em; + text-transform: none; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #e7ad52; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #f3d7ac; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #d08a1d; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #eee; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #e7ad52; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #e7ad52; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #e7ad52; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #111; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/serif.css b/CreatingReliableSoftwareCpp/css/theme/serif.css new file mode 100644 index 0000000..6514a6f --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/serif.css @@ -0,0 +1,273 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is brown. + * + * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. + */ +.reveal a { + line-height: 1.3em; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #F0F1EB; + background-color: #F0F1EB; } + +.reveal { + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + font-size: 40px; + font-weight: normal; + color: #000; } + +::selection { + color: #fff; + background: #26351C; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #26351C; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #383D3D; + font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: none; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #51483D; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #8b7c69; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #25211c; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #000; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #51483D; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #51483D; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #51483D; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #F0F1EB; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/simple.css b/CreatingReliableSoftwareCpp/css/theme/simple.css new file mode 100644 index 0000000..a7a29a6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/simple.css @@ -0,0 +1,276 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is darkblue. + * + * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. + * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { + color: #fff; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #fff; + background-color: #fff; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #000; } + +::selection { + color: #fff; + background: rgba(0, 0, 0, 0.99); + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: rgba(0, 0, 0, 0.99); + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #000; + font-family: "News Cycle", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: none; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #00008B; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #0000f1; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #00003f; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #000; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #00008B; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #00008B; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #00008B; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #fff; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/sky.css b/CreatingReliableSoftwareCpp/css/theme/sky.css new file mode 100644 index 0000000..d8734c9 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/sky.css @@ -0,0 +1,280 @@ +/** + * Sky theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ +@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); +.reveal a { + line-height: 1.3em; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #add9e4; + background: -moz-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #f7fbfc), color-stop(100%, #add9e4)); + background: -webkit-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: -o-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: -ms-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background: radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%); + background-color: #f7fbfc; } + +.reveal { + font-family: "Open Sans", sans-serif; + font-size: 40px; + font-weight: normal; + color: #333; } + +::selection { + color: #fff; + background: #134674; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #134674; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #333; + font-family: "Quicksand", sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: -0.08em; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #3b759e; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #74a7cb; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #264c66; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #333; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #3b759e; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #3b759e; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #3b759e; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #f7fbfc; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/solarized.css b/CreatingReliableSoftwareCpp/css/theme/solarized.css new file mode 100644 index 0000000..f1a2b9e --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/solarized.css @@ -0,0 +1,277 @@ +/** + * Solarized Light theme for reveal.js. + * Author: Achim Staebler + */ +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #fdf6e3; + background-color: #fdf6e3; } + +.reveal { + font-family: "Lato", sans-serif; + font-size: 40px; + font-weight: normal; + color: #657b83; } + +::selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #d33682; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #586e75; + font-family: "League Gothic", Impact, sans-serif; + font-weight: normal; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 3.77em; } + +.reveal h2 { + font-size: 2.11em; } + +.reveal h3 { + font-size: 1.55em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #268bd2; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #78b9e6; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #1a6091; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #657b83; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #268bd2; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #268bd2; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #268bd2; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #fdf6e3; } } diff --git a/CreatingReliableSoftwareCpp/css/theme/source/beige.scss b/CreatingReliableSoftwareCpp/css/theme/source/beige.scss new file mode 100644 index 0000000..5564f53 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/beige.scss @@ -0,0 +1,39 @@ +/** + * Beige theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$mainColor: #333; +$headingColor: #333; +$headingTextShadow: none; +$backgroundColor: #f7f3de; +$linkColor: #8b743d; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: rgba(79, 64, 28, 0.99); +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( rgba(247,242,211,1), rgba(255,255,255,1) ); +} + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/source/black.scss b/CreatingReliableSoftwareCpp/css/theme/source/black.scss new file mode 100644 index 0000000..4720c8a --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/black.scss @@ -0,0 +1,49 @@ +/** + * Black theme for reveal.js. This is the opposite of the 'white' theme. + * + * By Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #191919; + +$mainColor: #fff; +$headingColor: #fff; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #42affa; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: lighten( $linkColor, 25% ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +section.has-light-background { + &, h1, h2, h3, h4, h5, h6 { + color: #222; + } +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/source/blood.scss b/CreatingReliableSoftwareCpp/css/theme/source/blood.scss new file mode 100644 index 0000000..4533fc0 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/blood.scss @@ -0,0 +1,78 @@ +/** + * Blood theme for reveal.js + * Author: Walther http://github.com/Walther + * + * Designed to be used with highlight.js theme + * "monokai_sublime.css" available from + * https://github.com/isagalaev/highlight.js/ + * + * For other themes, change $codeBackground accordingly. + * + */ + + // Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + +// Include theme-specific fonts + +@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic); + +// Colors used in the theme +$blood: #a23; +$coal: #222; +$codeBackground: #23241f; + +$backgroundColor: $coal; + +// Main text +$mainFont: Ubuntu, 'sans-serif'; +$mainColor: #eee; + +// Headings +$headingFont: Ubuntu, 'sans-serif'; +$headingTextShadow: 2px 2px 2px $coal; + +// h1 shadow, borrowed humbly from +// (c) Default theme by Hakim El Hattab +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Links +$linkColor: $blood; +$linkColorHover: lighten( $linkColor, 20% ); + +// Text selection +$selectionBackgroundColor: $blood; +$selectionColor: #fff; + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- + +// some overrides after theme template import + +.reveal p { + font-weight: 300; + text-shadow: 1px 1px $coal; +} + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + font-weight: 700; +} + +.reveal p code { + background-color: $codeBackground; + display: inline-block; + border-radius: 7px; +} + +.reveal small code { + vertical-align: baseline; +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/source/league.scss b/CreatingReliableSoftwareCpp/css/theme/source/league.scss new file mode 100644 index 0000000..46ea04a --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/league.scss @@ -0,0 +1,34 @@ +/** + * League theme for reveal.js. + * + * This was the default theme pre-3.0.0. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + +// Override theme settings (see ../template/settings.scss) +$headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2); +$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15); + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( rgba(28,30,32,1), rgba(85,90,95,1) ); +} + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/source/moon.scss b/CreatingReliableSoftwareCpp/css/theme/source/moon.scss new file mode 100644 index 0000000..e47e5b5 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/moon.scss @@ -0,0 +1,57 @@ +/** + * Solarized Dark theme for reveal.js. + * Author: Achim Staebler + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; +} + +// Solarized colors +$base03: #002b36; +$base02: #073642; +$base01: #586e75; +$base00: #657b83; +$base0: #839496; +$base1: #93a1a1; +$base2: #eee8d5; +$base3: #fdf6e3; +$yellow: #b58900; +$orange: #cb4b16; +$red: #dc322f; +$magenta: #d33682; +$violet: #6c71c4; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; + +// Override theme settings (see ../template/settings.scss) +$mainColor: $base1; +$headingColor: $base2; +$headingTextShadow: none; +$backgroundColor: $base03; +$linkColor: $blue; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: $magenta; + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/CreatingReliableSoftwareCpp/css/theme/source/night.scss b/CreatingReliableSoftwareCpp/css/theme/source/night.scss new file mode 100644 index 0000000..d49a282 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/night.scss @@ -0,0 +1,34 @@ +/** + * Black theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=Montserrat:700); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #111; + +$mainFont: 'Open Sans', sans-serif; +$linkColor: #e7ad52; +$linkColorHover: lighten( $linkColor, 20% ); +$headingFont: 'Montserrat', Impact, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: -0.03em; +$headingTextTransform: none; +$selectionBackgroundColor: #e7ad52; + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/source/serif.scss b/CreatingReliableSoftwareCpp/css/theme/source/serif.scss new file mode 100644 index 0000000..ec3fcb3 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/serif.scss @@ -0,0 +1,35 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is brown. + * + * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; +$mainColor: #000; +$headingFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; +$headingColor: #383D3D; +$headingTextShadow: none; +$headingTextTransform: none; +$backgroundColor: #F0F1EB; +$linkColor: #51483D; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: #26351C; + +.reveal a { + line-height: 1.3em; +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/CreatingReliableSoftwareCpp/css/theme/source/simple.scss b/CreatingReliableSoftwareCpp/css/theme/source/simple.scss new file mode 100644 index 0000000..394c9cd --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/simple.scss @@ -0,0 +1,43 @@ +/** + * A simple theme for reveal.js presentations, similar + * to the default theme. The accent color is darkblue. + * + * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed. + * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Lato', sans-serif; +$mainColor: #000; +$headingFont: 'News Cycle', Impact, sans-serif; +$headingColor: #000; +$headingTextShadow: none; +$headingTextTransform: none; +$backgroundColor: #fff; +$linkColor: #00008B; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: rgba(0, 0, 0, 0.99); + +section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: #fff; + } +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/source/sky.scss b/CreatingReliableSoftwareCpp/css/theme/source/sky.scss new file mode 100644 index 0000000..3fee67c --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/sky.scss @@ -0,0 +1,46 @@ +/** + * Sky theme for reveal.js. + * + * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic); +@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700); + + +// Override theme settings (see ../template/settings.scss) +$mainFont: 'Open Sans', sans-serif; +$mainColor: #333; +$headingFont: 'Quicksand', sans-serif; +$headingColor: #333; +$headingLetterSpacing: -0.08em; +$headingTextShadow: none; +$backgroundColor: #f7fbfc; +$linkColor: #3b759e; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: #134674; + +// Fix links so they are not cut off +.reveal a { + line-height: 1.3em; +} + +// Background generator +@mixin bodyBackground() { + @include radial-gradient( #add9e4, #f7fbfc ); +} + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/CreatingReliableSoftwareCpp/css/theme/source/solarized.scss b/CreatingReliableSoftwareCpp/css/theme/source/solarized.scss new file mode 100644 index 0000000..912be56 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/solarized.scss @@ -0,0 +1,63 @@ +/** + * Solarized Light theme for reveal.js. + * Author: Achim Staebler + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + + +// Include theme-specific fonts +@import url(../../lib/font/league-gothic/league-gothic.css); +@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic); + + +/** + * Solarized colors by Ethan Schoonover + */ +html * { + color-profile: sRGB; + rendering-intent: auto; +} + +// Solarized colors +$base03: #002b36; +$base02: #073642; +$base01: #586e75; +$base00: #657b83; +$base0: #839496; +$base1: #93a1a1; +$base2: #eee8d5; +$base3: #fdf6e3; +$yellow: #b58900; +$orange: #cb4b16; +$red: #dc322f; +$magenta: #d33682; +$violet: #6c71c4; +$blue: #268bd2; +$cyan: #2aa198; +$green: #859900; + +// Override theme settings (see ../template/settings.scss) +$mainColor: $base00; +$headingColor: $base01; +$headingTextShadow: none; +$backgroundColor: $base3; +$linkColor: $blue; +$linkColorHover: lighten( $linkColor, 20% ); +$selectionBackgroundColor: $magenta; + +// Background generator +// @mixin bodyBackground() { +// @include radial-gradient( rgba($base3,1), rgba(lighten($base3, 20%),1) ); +// } + + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- diff --git a/CreatingReliableSoftwareCpp/css/theme/source/white.scss b/CreatingReliableSoftwareCpp/css/theme/source/white.scss new file mode 100644 index 0000000..7f06ffd --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/source/white.scss @@ -0,0 +1,49 @@ +/** + * White theme for reveal.js. This is the opposite of the 'black' theme. + * + * By Hakim El Hattab, http://hakim.se + */ + + +// Default mixins and settings ----------------- +@import "../template/mixins"; +@import "../template/settings"; +// --------------------------------------------- + + +// Include theme-specific fonts +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); + + +// Override theme settings (see ../template/settings.scss) +$backgroundColor: #fff; + +$mainColor: #222; +$headingColor: #222; + +$mainFontSize: 42px; +$mainFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingFont: 'Source Sans Pro', Helvetica, sans-serif; +$headingTextShadow: none; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingFontWeight: 600; +$linkColor: #2a76dd; +$linkColorHover: lighten( $linkColor, 15% ); +$selectionBackgroundColor: lighten( $linkColor, 25% ); + +$heading1Size: 2.5em; +$heading2Size: 1.6em; +$heading3Size: 1.3em; +$heading4Size: 1.0em; + +section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: #fff; + } +} + + +// Theme template ------------------------------ +@import "../template/theme"; +// --------------------------------------------- \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/template/mixins.scss b/CreatingReliableSoftwareCpp/css/theme/template/mixins.scss new file mode 100644 index 0000000..e0c5606 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/template/mixins.scss @@ -0,0 +1,29 @@ +@mixin vertical-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( top, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( top, $top 0%, $bottom 100% ); + background: -o-linear-gradient( top, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( top, $top 0%, $bottom 100% ); + background: linear-gradient( top, $top 0%, $bottom 100% ); +} + +@mixin horizontal-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( left, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( left, $top 0%, $bottom 100% ); + background: -o-linear-gradient( left, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( left, $top 0%, $bottom 100% ); + background: linear-gradient( left, $top 0%, $bottom 100% ); +} + +@mixin radial-gradient( $outer, $inner, $type: circle ) { + background: $outer; + background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) ); + background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: radial-gradient( center, $type cover, $inner 0%, $outer 100% ); +} \ No newline at end of file diff --git a/CreatingReliableSoftwareCpp/css/theme/template/settings.scss b/CreatingReliableSoftwareCpp/css/theme/template/settings.scss new file mode 100644 index 0000000..5a917f8 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/template/settings.scss @@ -0,0 +1,45 @@ +// Base settings for all themes that can optionally be +// overridden by the super-theme + +// Background of the presentation +$backgroundColor: #2b2b2b; + +// Primary/body text +$mainFont: 'Lato', sans-serif; +$mainFontSize: 40px; +$mainColor: #eee; + +// Vertical spacing between blocks of text +$blockMargin: 20px; + +// Headings +$headingMargin: 0 0 $blockMargin 0; +$headingFont: 'League Gothic', Impact, sans-serif; +$headingColor: #eee; +$headingLineHeight: 1.2; +$headingLetterSpacing: normal; +$headingTextTransform: uppercase; +$headingTextShadow: none; +$headingFontWeight: normal; +$heading1TextShadow: $headingTextShadow; + +$heading1Size: 3.77em; +$heading2Size: 2.11em; +$heading3Size: 1.55em; +$heading4Size: 1.00em; + +$codeFont: monospace; + +// Links and actions +$linkColor: #13DAEC; +$linkColorHover: lighten( $linkColor, 20% ); + +// Text selection +$selectionBackgroundColor: #FF5E99; +$selectionColor: #fff; + +// Generates the presentation background, can be overridden +// to return a background image or gradient +@mixin bodyBackground() { + background: $backgroundColor; +} diff --git a/CreatingReliableSoftwareCpp/css/theme/template/theme.scss b/CreatingReliableSoftwareCpp/css/theme/template/theme.scss new file mode 100644 index 0000000..9ccfaf5 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/template/theme.scss @@ -0,0 +1,325 @@ +// Base theme template for reveal.js + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +body { + @include bodyBackground(); + background-color: $backgroundColor; +} + +.reveal { + font-family: $mainFont; + font-size: $mainFontSize; + font-weight: normal; + color: $mainColor; +} + +::selection { + color: $selectionColor; + background: $selectionBackgroundColor; + text-shadow: none; +} + +::-moz-selection { + color: $selectionColor; + background: $selectionBackgroundColor; + text-shadow: none; +} + +.reveal .slides section, +.reveal .slides section>section { + line-height: 1.3; + font-weight: inherit; +} + +/********************************************* + * HEADERS + *********************************************/ + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: $headingMargin; + color: $headingColor; + + font-family: $headingFont; + font-weight: $headingFontWeight; + line-height: $headingLineHeight; + letter-spacing: $headingLetterSpacing; + + text-transform: $headingTextTransform; + text-shadow: $headingTextShadow; + + word-wrap: break-word; +} + +.reveal h1 {font-size: $heading1Size; } +.reveal h2 {font-size: $heading2Size; } +.reveal h3 {font-size: $heading3Size; } +.reveal h4 {font-size: $heading4Size; } + +.reveal h1 { + text-shadow: $heading1TextShadow; +} + + +/********************************************* + * OTHER + *********************************************/ + +.reveal p { + margin: $blockMargin 0; + line-height: 1.3; +} + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; +} +.reveal strong, +.reveal b { + font-weight: bold; +} + +.reveal em { + font-style: italic; +} + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + + text-align: left; + margin: 0 0 0 1em; +} + +.reveal ol { + list-style-type: decimal; +} + +.reveal ul { + list-style-type: disc; +} + +.reveal ul ul { + list-style-type: square; +} + +.reveal ul ul ul { + list-style-type: circle; +} + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; +} + +.reveal dt { + font-weight: bold; +} + +.reveal dd { + margin-left: 40px; +} + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: $blockMargin auto; + padding: 5px; + + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0,0,0,0.2); +} + .reveal blockquote p:first-child, + .reveal blockquote p:last-child { + display: inline-block; + } + +.reveal q { + font-style: italic; +} + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: $blockMargin auto; + + text-align: left; + font-size: 0.55em; + font-family: $codeFont; + line-height: 1.2em; + + word-wrap: break-word; + + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); +} + +.reveal code { + font-family: $codeFont; + text-transform: none; +} + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; +} + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; +} + +.reveal table th { + font-weight: bold; +} + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; +} + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; +} + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; +} + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; +} + +.reveal sup { + vertical-align: super; + font-size: smaller; +} +.reveal sub { + vertical-align: sub; + font-size: smaller; +} + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; +} + +.reveal small * { + vertical-align: top; +} + + +/********************************************* + * LINKS + *********************************************/ + +.reveal a { + color: $linkColor; + text-decoration: none; + + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; +} + .reveal a:hover { + color: $linkColorHover; + + text-shadow: none; + border: none; + } + +.reveal .roll span:after { + color: #fff; + background: darken( $linkColor, 15% ); +} + + +/********************************************* + * IMAGES + *********************************************/ + +.reveal section img { + margin: 15px 0px; + background: rgba(255,255,255,0.12); + border: 4px solid $mainColor; + + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); +} + + .reveal section img.plain { + border: 0; + box-shadow: none; + } + + .reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; + } + + .reveal a:hover img { + background: rgba(255,255,255,0.2); + border-color: $linkColor; + + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); + } + + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ + +.reveal .controls { + color: $linkColor; +} + + +/********************************************* + * PROGRESS BAR + *********************************************/ + +.reveal .progress { + background: rgba(0,0,0,0.2); + color: $linkColor; +} + .reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985); + } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ + @media print { + .backgrounds { + background-color: $backgroundColor; + } +} diff --git a/CreatingReliableSoftwareCpp/css/theme/white.css b/CreatingReliableSoftwareCpp/css/theme/white.css new file mode 100644 index 0000000..43ef2c7 --- /dev/null +++ b/CreatingReliableSoftwareCpp/css/theme/white.css @@ -0,0 +1,273 @@ +/** + * White theme for reveal.js. This is the opposite of the 'black' theme. + * + * By Hakim El Hattab, http://hakim.se + */ +@import url(../../lib/font/source-sans-pro/source-sans-pro.css); +section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { + color: #fff; } + +/********************************************* + * GLOBAL STYLES + *********************************************/ +body { + background: #fff; + background-color: #fff; } + +.reveal { + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-size: 42px; + font-weight: normal; + color: #222; } + +::selection { + color: #fff; + background: #98bdef; + text-shadow: none; } + +::-moz-selection { + color: #fff; + background: #98bdef; + text-shadow: none; } + +.reveal .slides section, +.reveal .slides section > section { + line-height: 1.3; + font-weight: inherit; } + +/********************************************* + * HEADERS + *********************************************/ +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: 0 0 20px 0; + color: #222; + font-family: "Source Sans Pro", Helvetica, sans-serif; + font-weight: 600; + line-height: 1.2; + letter-spacing: normal; + text-transform: uppercase; + text-shadow: none; + word-wrap: break-word; } + +.reveal h1 { + font-size: 2.5em; } + +.reveal h2 { + font-size: 1.6em; } + +.reveal h3 { + font-size: 1.3em; } + +.reveal h4 { + font-size: 1em; } + +.reveal h1 { + text-shadow: none; } + +/********************************************* + * OTHER + *********************************************/ +.reveal p { + margin: 20px 0; + line-height: 1.3; } + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; } + +.reveal strong, +.reveal b { + font-weight: bold; } + +.reveal em { + font-style: italic; } + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + text-align: left; + margin: 0 0 0 1em; } + +.reveal ol { + list-style-type: decimal; } + +.reveal ul { + list-style-type: disc; } + +.reveal ul ul { + list-style-type: square; } + +.reveal ul ul ul { + list-style-type: circle; } + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; } + +.reveal dt { + font-weight: bold; } + +.reveal dd { + margin-left: 40px; } + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: 20px auto; + padding: 5px; + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } + +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: inline-block; } + +.reveal q { + font-style: italic; } + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: 20px auto; + text-align: left; + font-size: 0.55em; + font-family: monospace; + line-height: 1.2em; + word-wrap: break-word; + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } + +.reveal code { + font-family: monospace; + text-transform: none; } + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; } + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; } + +.reveal table th { + font-weight: bold; } + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; } + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; } + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; } + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; } + +.reveal sup { + vertical-align: super; + font-size: smaller; } + +.reveal sub { + vertical-align: sub; + font-size: smaller; } + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; } + +.reveal small * { + vertical-align: top; } + +/********************************************* + * LINKS + *********************************************/ +.reveal a { + color: #2a76dd; + text-decoration: none; + -webkit-transition: color .15s ease; + -moz-transition: color .15s ease; + transition: color .15s ease; } + +.reveal a:hover { + color: #6ca0e8; + text-shadow: none; + border: none; } + +.reveal .roll span:after { + color: #fff; + background: #1a53a1; } + +/********************************************* + * IMAGES + *********************************************/ +.reveal section img { + margin: 15px 0px; + background: rgba(255, 255, 255, 0.12); + border: 4px solid #222; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } + +.reveal section img.plain { + border: 0; + box-shadow: none; } + +.reveal a img { + -webkit-transition: all .15s linear; + -moz-transition: all .15s linear; + transition: all .15s linear; } + +.reveal a:hover img { + background: rgba(255, 255, 255, 0.2); + border-color: #2a76dd; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ +.reveal .controls { + color: #2a76dd; } + +/********************************************* + * PROGRESS BAR + *********************************************/ +.reveal .progress { + background: rgba(0, 0, 0, 0.2); + color: #2a76dd; } + +.reveal .progress span { + -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); + transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); } + +/********************************************* + * PRINT BACKGROUND + *********************************************/ +@media print { + .backgrounds { + background-color: #fff; } } diff --git a/CreatingReliableSoftwareCpp/demo.html b/CreatingReliableSoftwareCpp/demo.html new file mode 100644 index 0000000..cf05e88 --- /dev/null +++ b/CreatingReliableSoftwareCpp/demo.html @@ -0,0 +1,425 @@ + + + + + + + reveal.js – The HTML Presentation Framework + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+

Reveal.js

+

The HTML Presentation Framework

+

+ Created by Hakim El Hattab and contributors +

+
+ +
+

Hello There

+

+ reveal.js enables you to create beautiful interactive slide decks using HTML. This presentation will show you examples of what it can do. +

+
+ + +
+
+

Vertical Slides

+

Slides can be nested inside of each other.

+

Use the Space key to navigate through all slides.

+
+ + Down arrow + +
+
+

Basement Level 1

+

Nested slides are useful for adding additional detail underneath a high level horizontal slide.

+
+
+

Basement Level 2

+

That's it, time to go back up.

+
+ + Up arrow + +
+
+ +
+

Slides

+

+ Not a coder? Not a problem. There's a fully-featured visual editor for authoring these, try it out at https://slides.com. +

+
+ +
+

Point of View

+

+ Press ESC to enter the slide overview. +

+

+ Hold down the alt key (ctrl in Linux) and click on any element to zoom towards it using zoom.js. Click again to zoom back out. +

+

+ (NOTE: Use ctrl + click in Linux.) +

+
+ +
+

Touch Optimized

+

+ Presentations look great on touch devices, like mobile phones and tablets. Simply swipe through your slides. +

+
+ +
+ +
+ +
+
+

Fragments

+

Hit the next arrow...

+

... to step through ...

+

... a fragmented slide.

+ + +
+
+

Fragment Styles

+

There's different types of fragments, like:

+

grow

+

shrink

+

fade-out

+

+ fade-right, + up, + down, + left +

+

fade-in-then-out

+

fade-in-then-semi-out

+

Highlight red blue green

+
+
+ +
+

Transition Styles

+

+ You can select from different transitions, like:
+ None - + Fade - + Slide - + Convex - + Concave - + Zoom +

+
+ +
+

Themes

+

+ reveal.js comes with a few themes built in:
+ + Black (default) - + White - + League - + Sky - + Beige - + Simple
+ Serif - + Blood - + Night - + Moon - + Solarized +

+
+ +
+
+

Slide Backgrounds

+

+ Set data-background="#dddddd" on a slide to change the background color. All CSS color formats are supported. +

+ + Down arrow + +
+
+

Image Backgrounds

+
<section data-background="image.png">
+
+
+

Tiled Backgrounds

+
<section data-background="image.png" data-background-repeat="repeat" data-background-size="100px">
+
+
+
+

Video Backgrounds

+
<section data-background-video="video.mp4,video.webm">
+
+
+
+

... and GIFs!

+
+
+ +
+

Background Transitions

+

+ Different background transitions are available via the backgroundTransition option. This one's called "zoom". +

+
Reveal.configure({ backgroundTransition: 'zoom' })
+
+ +
+

Background Transitions

+

+ You can override background transitions per-slide. +

+
<section data-background-transition="zoom">
+
+ +
+
+

Iframe Backgrounds

+

Since reveal.js runs on the web, you can easily embed other web content. Try interacting with the page in the background.

+
+
+ +
+

Pretty Code

+

+import React, { useState } from 'react';
+
+function Example() {
+  const [count, setCount] = useState(0);
+
+  return (
+    <div>
+      <p>You clicked {count} times</p>
+      <button onClick={() => setCount(count + 1)}>
+        Click me
+      </button>
+    </div>
+  );
+}
+					
+

Code syntax highlighting courtesy of highlight.js.

+
+ +
+

Marvelous List

+
    +
  • No order here
  • +
  • Or here
  • +
  • Or here
  • +
  • Or here
  • +
+
+ +
+

Fantastic Ordered List

+
    +
  1. One is smaller than...
  2. +
  3. Two is smaller than...
  4. +
  5. Three!
  6. +
+
+ +
+

Tabular Tables

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ItemValueQuantity
Apples$17
Lemonade$218
Bread$32
+
+ +
+

Clever Quotes

+

+ These guys come in two forms, inline: The nice thing about standards is that there are so many to choose from and block: +

+
+ “For years there has been a theory that millions of monkeys typing at random on millions of typewriters would + reproduce the entire works of Shakespeare. The Internet has proven this theory to be untrue.” +
+
+ +
+

Intergalactic Interconnections

+

+ You can link between slides internally, + like this. +

+
+ +
+

Speaker View

+

There's a speaker view. It includes a timer, preview of the upcoming slide as well as your speaker notes.

+

Press the S key to try it out.

+ + +
+ +
+

Export to PDF

+

Presentations can be exported to PDF, here's an example:

+ +
+ +
+

Global State

+

+ Set data-state="something" on a slide and "something" + will be added as a class to the document element when the slide is open. This lets you + apply broader style changes, like switching the page background. +

+
+ +
+

State Events

+

+ Additionally custom events can be triggered on a per slide basis by binding to the data-state name. +

+

+Reveal.addEventListener( 'customevent', function() {
+	console.log( '"customevent" has fired' );
+} );
+					
+
+ +
+

Take a Moment

+

+ Press B or . on your keyboard to pause the presentation. This is helpful when you're on stage and want to take distracting slides off the screen. +

+
+ +
+

Much more

+ +
+ +
+

THE END

+

+ - Try the online editor
+ - Source code & documentation +

+
+ +
+ +
+ + + + + + + diff --git a/CreatingReliableSoftwareCpp/gruntfile.js b/CreatingReliableSoftwareCpp/gruntfile.js new file mode 100644 index 0000000..acf34b6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/gruntfile.js @@ -0,0 +1,189 @@ +const sass = require('node-sass'); + +module.exports = grunt => { + + require('load-grunt-tasks')(grunt); + + let port = grunt.option('port') || 8000; + let root = grunt.option('root') || '.'; + + if (!Array.isArray(root)) root = [root]; + + // Project configuration + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + meta: { + banner: + '/*!\n' + + ' * reveal.js <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' + + ' * http://revealjs.com\n' + + ' * MIT licensed\n' + + ' *\n' + + ' * Copyright (C) 2020 Hakim El Hattab, http://hakim.se\n' + + ' */' + }, + + qunit: { + files: [ 'test/*.html' ] + }, + + uglify: { + options: { + banner: '<%= meta.banner %>\n', + ie8: true + }, + build: { + src: 'js/reveal.js', + dest: 'js/reveal.min.js' + } + }, + + sass: { + options: { + implementation: sass, + sourceMap: false + }, + core: { + src: 'css/reveal.scss', + dest: 'css/reveal.css' + }, + themes: { + expand: true, + cwd: 'css/theme/source', + src: ['*.sass', '*.scss'], + dest: 'css/theme', + ext: '.css' + } + }, + + autoprefixer: { + core: { + src: 'css/reveal.css' + } + }, + + cssmin: { + options: { + compatibility: 'ie9' + }, + compress: { + src: 'css/reveal.css', + dest: 'css/reveal.min.css' + } + }, + + jshint: { + options: { + curly: false, + eqeqeq: true, + immed: true, + esnext: true, + latedef: 'nofunc', + newcap: true, + noarg: true, + sub: true, + undef: true, + eqnull: true, + browser: true, + expr: true, + loopfunc: true, + globals: { + head: false, + module: false, + console: false, + unescape: false, + define: false, + exports: false, + require: false + } + }, + files: [ 'gruntfile.js', 'js/reveal.js' ] + }, + + connect: { + server: { + options: { + port: port, + base: root, + livereload: true, + open: true, + useAvailablePort: true + } + } + }, + + zip: { + bundle: { + src: [ + 'index.html', + 'css/**', + 'js/**', + 'lib/**', + 'images/**', + 'plugin/**', + '**.md' + ], + dest: 'reveal-js-presentation.zip' + } + }, + + watch: { + js: { + files: [ 'gruntfile.js', 'js/reveal.js' ], + tasks: 'js' + }, + theme: { + files: [ + 'css/theme/source/*.sass', + 'css/theme/source/*.scss', + 'css/theme/template/*.sass', + 'css/theme/template/*.scss' + ], + tasks: 'css-themes' + }, + css: { + files: [ 'css/reveal.scss' ], + tasks: 'css-core' + }, + test: { + files: [ 'test/*.html' ], + tasks: 'test' + }, + html: { + files: root.map(path => path + '/*.html') + }, + markdown: { + files: root.map(path => path + '/*.md') + }, + options: { + livereload: true + } + } + + }); + + // Default task + grunt.registerTask( 'default', [ 'css', 'js' ] ); + + // JS task + grunt.registerTask( 'js', [ 'jshint', 'uglify', 'qunit' ] ); + + // Theme CSS + grunt.registerTask( 'css-themes', [ 'sass:themes' ] ); + + // Core framework CSS + grunt.registerTask( 'css-core', [ 'sass:core', 'autoprefixer', 'cssmin' ] ); + + // All CSS + grunt.registerTask( 'css', [ 'sass', 'autoprefixer', 'cssmin' ] ); + + // Package presentation to archive + grunt.registerTask( 'package', [ 'default', 'zip' ] ); + + // Serve presentation locally + grunt.registerTask( 'serve', [ 'connect', 'watch' ] ); + + // Run tests + grunt.registerTask( 'test', [ 'jshint', 'qunit' ] ); + +}; diff --git a/CreatingReliableSoftwareCpp/img/altkom_logo.png b/CreatingReliableSoftwareCpp/img/altkom_logo.png new file mode 100644 index 0000000..abf0643 Binary files /dev/null and b/CreatingReliableSoftwareCpp/img/altkom_logo.png differ diff --git a/CreatingReliableSoftwareCpp/img/altkom_logo2.png b/CreatingReliableSoftwareCpp/img/altkom_logo2.png new file mode 100644 index 0000000..b995fc7 Binary files /dev/null and b/CreatingReliableSoftwareCpp/img/altkom_logo2.png differ diff --git a/CreatingReliableSoftwareCpp/img/cpp_logo.png b/CreatingReliableSoftwareCpp/img/cpp_logo.png new file mode 100644 index 0000000..432d4eb Binary files /dev/null and b/CreatingReliableSoftwareCpp/img/cpp_logo.png differ diff --git a/CreatingReliableSoftwareCpp/img/mateusz.png b/CreatingReliableSoftwareCpp/img/mateusz.png new file mode 100644 index 0000000..139f905 Binary files /dev/null and b/CreatingReliableSoftwareCpp/img/mateusz.png differ diff --git a/CreatingReliableSoftwareCpp/js/reveal.js b/CreatingReliableSoftwareCpp/js/reveal.js new file mode 100644 index 0000000..a1357a6 --- /dev/null +++ b/CreatingReliableSoftwareCpp/js/reveal.js @@ -0,0 +1,6191 @@ +/*! + * reveal.js + * http://revealjs.com + * MIT licensed + * + * Copyright (C) 2020 Hakim El Hattab, http://hakim.se + */ +(function( root, factory ) { + if( typeof define === 'function' && define.amd ) { + // AMD. Register as an anonymous module. + define( function() { + root.Reveal = factory(); + return root.Reveal; + } ); + } else if( typeof exports === 'object' ) { + // Node. Does not work with strict CommonJS. + module.exports = factory(); + } else { + // Browser globals. + root.Reveal = factory(); + } +}( this, function() { + + 'use strict'; + + var Reveal; + + // The reveal.js version + var VERSION = '3.9.2'; + + var SLIDES_SELECTOR = '.slides section', + HORIZONTAL_SLIDES_SELECTOR = '.slides>section', + VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section', + HOME_SLIDE_SELECTOR = '.slides>section:first-of-type', + + UA = navigator.userAgent, + + // Methods that may not be invoked via the postMessage API + POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/, + + // Configuration defaults, can be overridden at initialization time + config = { + + // The "normal" size of the presentation, aspect ratio will be preserved + // when the presentation is scaled to fit different resolutions + width: 960, + height: 700, + + // Factor of the display size that should remain empty around the content + margin: 0.04, + + // Bounds for smallest/largest possible scale to apply to content + minScale: 0.2, + maxScale: 2.0, + + // Display presentation control arrows + controls: true, + + // Help the user learn the controls by providing hints, for example by + // bouncing the down arrow when they first encounter a vertical slide + controlsTutorial: true, + + // Determines where controls appear, "edges" or "bottom-right" + controlsLayout: 'bottom-right', + + // Visibility rule for backwards navigation arrows; "faded", "hidden" + // or "visible" + controlsBackArrows: 'faded', + + // Display a presentation progress bar + progress: true, + + // Display the page number of the current slide + // - true: Show slide number + // - false: Hide slide number + // + // Can optionally be set as a string that specifies the number formatting: + // - "h.v": Horizontal . vertical slide number (default) + // - "h/v": Horizontal / vertical slide number + // - "c": Flattened slide number + // - "c/t": Flattened slide number / total slides + // + // Alternatively, you can provide a function that returns the slide + // number for the current slide. The function should take in a slide + // object and return an array with one string [slideNumber] or + // three strings [n1,delimiter,n2]. See #formatSlideNumber(). + slideNumber: false, + + // Can be used to limit the contexts in which the slide number appears + // - "all": Always show the slide number + // - "print": Only when printing to PDF + // - "speaker": Only in the speaker view + showSlideNumber: 'all', + + // Use 1 based indexing for # links to match slide number (default is zero + // based) + hashOneBasedIndex: false, + + // Add the current slide number to the URL hash so that reloading the + // page/copying the URL will return you to the same slide + hash: false, + + // Push each slide change to the browser history. Implies `hash: true` + history: false, + + // Enable keyboard shortcuts for navigation + keyboard: true, + + // Optional function that blocks keyboard events when retuning false + keyboardCondition: null, + + // Enable the slide overview mode + overview: true, + + // Disables the default reveal.js slide layout so that you can use + // custom CSS layout + disableLayout: false, + + // Vertical centering of slides + center: true, + + // Enables touch navigation on devices with touch input + touch: true, + + // Loop the presentation + loop: false, + + // Change the presentation direction to be RTL + rtl: false, + + // Changes the behavior of our navigation directions. + // + // "default" + // Left/right arrow keys step between horizontal slides, up/down + // arrow keys step between vertical slides. Space key steps through + // all slides (both horizontal and vertical). + // + // "linear" + // Removes the up/down arrows. Left/right arrows step through all + // slides (both horizontal and vertical). + // + // "grid" + // When this is enabled, stepping left/right from a vertical stack + // to an adjacent vertical stack will land you at the same vertical + // index. + // + // Consider a deck with six slides ordered in two vertical stacks: + // 1.1 2.1 + // 1.2 2.2 + // 1.3 2.3 + // + // If you're on slide 1.3 and navigate right, you will normally move + // from 1.3 -> 2.1. If "grid" is used, the same navigation takes you + // from 1.3 -> 2.3. + navigationMode: 'default', + + // Randomizes the order of slides each time the presentation loads + shuffle: false, + + // Turns fragments on and off globally + fragments: true, + + // Flags whether to include the current fragment in the URL, + // so that reloading brings you to the same fragment position + fragmentInURL: false, + + // Flags if the presentation is running in an embedded mode, + // i.e. contained within a limited portion of the screen + embedded: false, + + // Flags if we should show a help overlay when the question-mark + // key is pressed + help: true, + + // Flags if it should be possible to pause the presentation (blackout) + pause: true, + + // Flags if speaker notes should be visible to all viewers + showNotes: false, + + // Global override for autolaying embedded media (video/audio/iframe) + // - null: Media will only autoplay if data-autoplay is present + // - true: All media will autoplay, regardless of individual setting + // - false: No media will autoplay, regardless of individual setting + autoPlayMedia: null, + + // Global override for preloading lazy-loaded iframes + // - null: Iframes with data-src AND data-preload will be loaded when within + // the viewDistance, iframes with only data-src will be loaded when visible + // - true: All iframes with data-src will be loaded when within the viewDistance + // - false: All iframes with data-src will be loaded only when visible + preloadIframes: null, + + // Controls automatic progression to the next slide + // - 0: Auto-sliding only happens if the data-autoslide HTML attribute + // is present on the current slide or fragment + // - 1+: All slides will progress automatically at the given interval + // - false: No auto-sliding, even if data-autoslide is present + autoSlide: 0, + + // Stop auto-sliding after user input + autoSlideStoppable: true, + + // Use this method for navigation when auto-sliding (defaults to navigateNext) + autoSlideMethod: null, + + // Specify the average time in seconds that you think you will spend + // presenting each slide. This is used to show a pacing timer in the + // speaker view + defaultTiming: null, + + // Enable slide navigation via mouse wheel + mouseWheel: false, + + // Apply a 3D roll to links on hover + rollingLinks: false, + + // Hides the address bar on mobile devices + hideAddressBar: true, + + // Opens links in an iframe preview overlay + // Add `data-preview-link` and `data-preview-link="false"` to customise each link + // individually + previewLinks: false, + + // Exposes the reveal.js API through window.postMessage + postMessage: true, + + // Dispatches all reveal.js events to the parent window through postMessage + postMessageEvents: false, + + // Focuses body when page changes visibility to ensure keyboard shortcuts work + focusBodyOnPageVisibilityChange: true, + + // Transition style + transition: 'slide', // none/fade/slide/convex/concave/zoom + + // Transition speed + transitionSpeed: 'default', // default/fast/slow + + // Transition style for full page slide backgrounds + backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom + + // Parallax background image + parallaxBackgroundImage: '', // CSS syntax, e.g. "a.jpg" + + // Parallax background size + parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px" + + // Parallax background repeat + parallaxBackgroundRepeat: '', // repeat/repeat-x/repeat-y/no-repeat/initial/inherit + + // Parallax background position + parallaxBackgroundPosition: '', // CSS syntax, e.g. "top left" + + // Amount of pixels to move the parallax background per slide step + parallaxBackgroundHorizontal: null, + parallaxBackgroundVertical: null, + + // The maximum number of pages a single slide can expand onto when printing + // to PDF, unlimited by default + pdfMaxPagesPerSlide: Number.POSITIVE_INFINITY, + + // Prints each fragment on a separate slide + pdfSeparateFragments: true, + + // Offset used to reduce the height of content within exported PDF pages. + // This exists to account for environment differences based on how you + // print to PDF. CLI printing options, like phantomjs and wkpdf, can end + // on precisely the total height of the document whereas in-browser + // printing has to end one pixel before. + pdfPageHeightOffset: -1, + + // Number of slides away from the current that are visible + viewDistance: 3, + + // Number of slides away from the current that are visible on mobile + // devices. It is advisable to set this to a lower number than + // viewDistance in order to save resources. + mobileViewDistance: 2, + + // The display mode that will be used to show slides + display: 'block', + + // Hide cursor if inactive + hideInactiveCursor: true, + + // Time before the cursor is hidden (in ms) + hideCursorTime: 5000, + + // Script dependencies to load + dependencies: [] + + }, + + // Flags if Reveal.initialize() has been called + initialized = false, + + // Flags if reveal.js is loaded (has dispatched the 'ready' event) + loaded = false, + + // Flags if the overview mode is currently active + overview = false, + + // Holds the dimensions of our overview slides, including margins + overviewSlideWidth = null, + overviewSlideHeight = null, + + // The horizontal and vertical index of the currently active slide + indexh, + indexv, + + // The previous and current slide HTML elements + previousSlide, + currentSlide, + + previousBackground, + + // Remember which directions that the user has navigated towards + hasNavigatedRight = false, + hasNavigatedDown = false, + + // Slides may hold a data-state attribute which we pick up and apply + // as a class to the body. This list contains the combined state of + // all current slides. + state = [], + + // The current scale of the presentation (see width/height config) + scale = 1, + + // CSS transform that is currently applied to the slides container, + // split into two groups + slidesTransform = { layout: '', overview: '' }, + + // Cached references to DOM elements + dom = {}, + + // A list of registered reveal.js plugins + plugins = {}, + + // List of asynchronously loaded reveal.js dependencies + asyncDependencies = [], + + // Features supported by the browser, see #checkCapabilities() + features = {}, + + // Client is a mobile device, see #checkCapabilities() + isMobileDevice, + + // Client is a desktop Chrome, see #checkCapabilities() + isChrome, + + // Throttles mouse wheel navigation + lastMouseWheelStep = 0, + + // Delays updates to the URL due to a Chrome thumbnailer bug + writeURLTimeout = 0, + + // Is the mouse pointer currently hidden from view + cursorHidden = false, + + // Timeout used to determine when the cursor is inactive + cursorInactiveTimeout = 0, + + // Flags if the interaction event listeners are bound + eventsAreBound = false, + + // The current auto-slide duration + autoSlide = 0, + + // Auto slide properties + autoSlidePlayer, + autoSlideTimeout = 0, + autoSlideStartTime = -1, + autoSlidePaused = false, + + // Holds information about the currently ongoing touch input + touch = { + startX: 0, + startY: 0, + startCount: 0, + captured: false, + threshold: 40 + }, + + // A key:value map of shortcut keyboard keys and descriptions of + // the actions they trigger, generated in #configure() + keyboardShortcuts = {}, + + // Holds custom key code mappings + registeredKeyBindings = {}; + + /** + * Starts up the presentation if the client is capable. + */ + function initialize( options ) { + + // Make sure we only initialize once + if( initialized === true ) return; + + initialized = true; + + checkCapabilities(); + + if( !features.transforms2d && !features.transforms3d ) { + document.body.setAttribute( 'class', 'no-transforms' ); + + // Since JS won't be running any further, we load all lazy + // loading elements upfront + var images = toArray( document.getElementsByTagName( 'img' ) ), + iframes = toArray( document.getElementsByTagName( 'iframe' ) ); + + var lazyLoadable = images.concat( iframes ); + + for( var i = 0, len = lazyLoadable.length; i < len; i++ ) { + var element = lazyLoadable[i]; + if( element.getAttribute( 'data-src' ) ) { + element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); + element.removeAttribute( 'data-src' ); + } + } + + // If the browser doesn't support core features we won't be + // using JavaScript to control the presentation + return; + } + + // Cache references to key DOM elements + dom.wrapper = document.querySelector( '.reveal' ); + dom.slides = document.querySelector( '.reveal .slides' ); + + // Force a layout when the whole page, incl fonts, has loaded + window.addEventListener( 'load', layout, false ); + + var query = Reveal.getQueryHash(); + + // Do not accept new dependencies via query config to avoid + // the potential of malicious script injection + if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies']; + + // Copy options over to our config object + extend( config, options ); + extend( config, query ); + + // Hide the address bar in mobile browsers + hideAddressBar(); + + // Loads dependencies and continues to #start() once done + load(); + + } + + /** + * Inspect the client to see what it's capable of, this + * should only happens once per runtime. + */ + function checkCapabilities() { + + isMobileDevice = /(iphone|ipod|ipad|android)/gi.test( UA ) || + ( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS + isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA ); + + var testElement = document.createElement( 'div' ); + + features.transforms3d = 'WebkitPerspective' in testElement.style || + 'MozPerspective' in testElement.style || + 'msPerspective' in testElement.style || + 'OPerspective' in testElement.style || + 'perspective' in testElement.style; + + features.transforms2d = 'WebkitTransform' in testElement.style || + 'MozTransform' in testElement.style || + 'msTransform' in testElement.style || + 'OTransform' in testElement.style || + 'transform' in testElement.style; + + features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame; + features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function'; + + features.canvas = !!document.createElement( 'canvas' ).getContext; + + // Transitions in the overview are disabled in desktop and + // Safari due to lag + features.overviewTransitions = !/Version\/[\d\.]+.*Safari/.test( UA ); + + // Flags if we should use zoom instead of transform to scale + // up slides. Zoom produces crisper results but has a lot of + // xbrowser quirks so we only use it in whitelsited browsers. + features.zoom = 'zoom' in testElement.style && !isMobileDevice && + ( isChrome || /Version\/[\d\.]+.*Safari/.test( UA ) ); + + } + + /** + * Loads the dependencies of reveal.js. Dependencies are + * defined via the configuration option 'dependencies' + * and will be loaded prior to starting/binding reveal.js. + * Some dependencies may have an 'async' flag, if so they + * will load after reveal.js has been started up. + */ + function load() { + + var scripts = [], + scriptsToLoad = 0; + + config.dependencies.forEach( function( s ) { + // Load if there's no condition or the condition is truthy + if( !s.condition || s.condition() ) { + if( s.async ) { + asyncDependencies.push( s ); + } + else { + scripts.push( s ); + } + } + } ); + + if( scripts.length ) { + scriptsToLoad = scripts.length; + + // Load synchronous scripts + scripts.forEach( function( s ) { + loadScript( s.src, function() { + + if( typeof s.callback === 'function' ) s.callback(); + + if( --scriptsToLoad === 0 ) { + initPlugins(); + } + + } ); + } ); + } + else { + initPlugins(); + } + + } + + /** + * Initializes our plugins and waits for them to be ready + * before proceeding. + */ + function initPlugins() { + + var pluginsToInitialize = Object.keys( plugins ).length; + + // If there are no plugins, skip this step + if( pluginsToInitialize === 0 ) { + loadAsyncDependencies(); + } + // ... otherwise initialize plugins + else { + + var afterPlugInitialized = function() { + if( --pluginsToInitialize === 0 ) { + loadAsyncDependencies(); + } + }; + + for( var i in plugins ) { + + var plugin = plugins[i]; + + // If the plugin has an 'init' method, invoke it + if( typeof plugin.init === 'function' ) { + var callback = plugin.init(); + + // If the plugin returned a Promise, wait for it + if( callback && typeof callback.then === 'function' ) { + callback.then( afterPlugInitialized ); + } + else { + afterPlugInitialized(); + } + } + else { + afterPlugInitialized(); + } + + } + + } + + } + + /** + * Loads all async reveal.js dependencies. + */ + function loadAsyncDependencies() { + + if( asyncDependencies.length ) { + asyncDependencies.forEach( function( s ) { + loadScript( s.src, s.callback ); + } ); + } + + start(); + + } + + /** + * Loads a JavaScript file from the given URL and executes it. + * + * @param {string} url Address of the .js file to load + * @param {function} callback Method to invoke when the script + * has loaded and executed + */ + function loadScript( url, callback ) { + + var script = document.createElement( 'script' ); + script.type = 'text/javascript'; + script.async = false; + script.defer = false; + script.src = url; + + if( callback ) { + + // Success callback + script.onload = script.onreadystatechange = function( event ) { + if( event.type === "load" || (/loaded|complete/.test( script.readyState ) ) ) { + + // Kill event listeners + script.onload = script.onreadystatechange = script.onerror = null; + + callback(); + + } + }; + + // Error callback + script.onerror = function( err ) { + + // Kill event listeners + script.onload = script.onreadystatechange = script.onerror = null; + + callback( new Error( 'Failed loading script: ' + script.src + '\n' + err) ); + + }; + + } + + // Append the script at the end of + var head = document.querySelector( 'head' ); + head.insertBefore( script, head.lastChild ); + + } + + /** + * Starts up reveal.js by binding input events and navigating + * to the current URL deeplink if there is one. + */ + function start() { + + loaded = true; + + // Make sure we've got all the DOM elements we need + setupDOM(); + + // Listen to messages posted to this window + setupPostMessage(); + + // Prevent the slides from being scrolled out of view + setupScrollPrevention(); + + // Resets all vertical slides so that only the first is visible + resetVerticalSlides(); + + // Updates the presentation to match the current configuration values + configure(); + + // Read the initial hash + readURL(); + + // Update all backgrounds + updateBackground( true ); + + // Notify listeners that the presentation is ready but use a 1ms + // timeout to ensure it's not fired synchronously after #initialize() + setTimeout( function() { + // Enable transitions now that we're loaded + dom.slides.classList.remove( 'no-transition' ); + + dom.wrapper.classList.add( 'ready' ); + + dispatchEvent( 'ready', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + }, 1 ); + + // Special setup and config is required when printing to PDF + if( isPrintingPDF() ) { + removeEventListeners(); + + // The document needs to have loaded for the PDF layout + // measurements to be accurate + if( document.readyState === 'complete' ) { + setupPDF(); + } + else { + window.addEventListener( 'load', setupPDF ); + } + } + + } + + /** + * Finds and stores references to DOM elements which are + * required by the presentation. If a required element is + * not found, it is created. + */ + function setupDOM() { + + // Prevent transitions while we're loading + dom.slides.classList.add( 'no-transition' ); + + if( isMobileDevice ) { + dom.wrapper.classList.add( 'no-hover' ); + } + else { + dom.wrapper.classList.remove( 'no-hover' ); + } + + if( /iphone/gi.test( UA ) ) { + dom.wrapper.classList.add( 'ua-iphone' ); + } + else { + dom.wrapper.classList.remove( 'ua-iphone' ); + } + + // Background element + dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null ); + + // Progress bar + dom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', '' ); + dom.progressbar = dom.progress.querySelector( 'span' ); + + // Arrow controls + dom.controls = createSingletonNode( dom.wrapper, 'aside', 'controls', + '' + + '' + + '' + + '' ); + + // Slide number + dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' ); + + // Element containing notes that are visible to the audience + dom.speakerNotes = createSingletonNode( dom.wrapper, 'div', 'speaker-notes', null ); + dom.speakerNotes.setAttribute( 'data-prevent-swipe', '' ); + dom.speakerNotes.setAttribute( 'tabindex', '0' ); + + // Overlay graphic which is displayed during the paused mode + dom.pauseOverlay = createSingletonNode( dom.wrapper, 'div', 'pause-overlay', config.controls ? '' : null ); + + dom.wrapper.setAttribute( 'role', 'application' ); + + // There can be multiple instances of controls throughout the page + dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) ); + dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) ); + dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) ); + dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) ); + dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) ); + dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) ); + + // The right and down arrows in the standard reveal.js controls + dom.controlsRightArrow = dom.controls.querySelector( '.navigate-right' ); + dom.controlsDownArrow = dom.controls.querySelector( '.navigate-down' ); + + dom.statusDiv = createStatusDiv(); + } + + /** + * Creates a hidden div with role aria-live to announce the + * current slide content. Hide the div off-screen to make it + * available only to Assistive Technologies. + * + * @return {HTMLElement} + */ + function createStatusDiv() { + + var statusDiv = document.getElementById( 'aria-status-div' ); + if( !statusDiv ) { + statusDiv = document.createElement( 'div' ); + statusDiv.style.position = 'absolute'; + statusDiv.style.height = '1px'; + statusDiv.style.width = '1px'; + statusDiv.style.overflow = 'hidden'; + statusDiv.style.clip = 'rect( 1px, 1px, 1px, 1px )'; + statusDiv.setAttribute( 'id', 'aria-status-div' ); + statusDiv.setAttribute( 'aria-live', 'polite' ); + statusDiv.setAttribute( 'aria-atomic','true' ); + dom.wrapper.appendChild( statusDiv ); + } + return statusDiv; + + } + + /** + * Converts the given HTML element into a string of text + * that can be announced to a screen reader. Hidden + * elements are excluded. + */ + function getStatusText( node ) { + + var text = ''; + + // Text node + if( node.nodeType === 3 ) { + text += node.textContent; + } + // Element node + else if( node.nodeType === 1 ) { + + var isAriaHidden = node.getAttribute( 'aria-hidden' ); + var isDisplayHidden = window.getComputedStyle( node )['display'] === 'none'; + if( isAriaHidden !== 'true' && !isDisplayHidden ) { + + toArray( node.childNodes ).forEach( function( child ) { + text += getStatusText( child ); + } ); + + } + + } + + return text; + + } + + /** + * Configures the presentation for printing to a static + * PDF. + */ + function setupPDF() { + + var slideSize = getComputedSlideSize( window.innerWidth, window.innerHeight ); + + // Dimensions of the PDF pages + var pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ), + pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) ); + + // Dimensions of slides within the pages + var slideWidth = slideSize.width, + slideHeight = slideSize.height; + + // Let the browser know what page size we want to print + injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' ); + + // Limit the size of certain elements to the dimensions of the slide + injectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' ); + + document.body.classList.add( 'print-pdf' ); + document.body.style.width = pageWidth + 'px'; + document.body.style.height = pageHeight + 'px'; + + // Make sure stretch elements fit on slide + layoutSlideContents( slideWidth, slideHeight ); + + // Compute slide numbers now, before we start duplicating slides + var doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber ); + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + slide.setAttribute( 'data-slide-number', getSlideNumber( slide ) ); + } ); + + // Slide and slide background layout + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + + // Vertical stacks are not centred since their section + // children will be + if( slide.classList.contains( 'stack' ) === false ) { + // Center the slide inside of the page, giving the slide some margin + var left = ( pageWidth - slideWidth ) / 2, + top = ( pageHeight - slideHeight ) / 2; + + var contentHeight = slide.scrollHeight; + var numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 ); + + // Adhere to configured pages per slide limit + numberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide ); + + // Center slides vertically + if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) { + top = Math.max( ( pageHeight - contentHeight ) / 2, 0 ); + } + + // Wrap the slide in a page element and hide its overflow + // so that no page ever flows onto another + var page = document.createElement( 'div' ); + page.className = 'pdf-page'; + page.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px'; + slide.parentNode.insertBefore( page, slide ); + page.appendChild( slide ); + + // Position the slide inside of the page + slide.style.left = left + 'px'; + slide.style.top = top + 'px'; + slide.style.width = slideWidth + 'px'; + + if( slide.slideBackgroundElement ) { + page.insertBefore( slide.slideBackgroundElement, slide ); + } + + // Inject notes if `showNotes` is enabled + if( config.showNotes ) { + + // Are there notes for this slide? + var notes = getSlideNotes( slide ); + if( notes ) { + + var notesSpacing = 8; + var notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline'; + var notesElement = document.createElement( 'div' ); + notesElement.classList.add( 'speaker-notes' ); + notesElement.classList.add( 'speaker-notes-pdf' ); + notesElement.setAttribute( 'data-layout', notesLayout ); + notesElement.innerHTML = notes; + + if( notesLayout === 'separate-page' ) { + page.parentNode.insertBefore( notesElement, page.nextSibling ); + } + else { + notesElement.style.left = notesSpacing + 'px'; + notesElement.style.bottom = notesSpacing + 'px'; + notesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px'; + page.appendChild( notesElement ); + } + + } + + } + + // Inject slide numbers if `slideNumbers` are enabled + if( doingSlideNumbers ) { + var numberElement = document.createElement( 'div' ); + numberElement.classList.add( 'slide-number' ); + numberElement.classList.add( 'slide-number-pdf' ); + numberElement.innerHTML = slide.getAttribute( 'data-slide-number' ); + page.appendChild( numberElement ); + } + + // Copy page and show fragments one after another + if( config.pdfSeparateFragments ) { + + // Each fragment 'group' is an array containing one or more + // fragments. Multiple fragments that appear at the same time + // are part of the same group. + var fragmentGroups = sortFragments( page.querySelectorAll( '.fragment' ), true ); + + var previousFragmentStep; + var previousPage; + + fragmentGroups.forEach( function( fragments ) { + + // Remove 'current-fragment' from the previous group + if( previousFragmentStep ) { + previousFragmentStep.forEach( function( fragment ) { + fragment.classList.remove( 'current-fragment' ); + } ); + } + + // Show the fragments for the current index + fragments.forEach( function( fragment ) { + fragment.classList.add( 'visible', 'current-fragment' ); + } ); + + // Create a separate page for the current fragment state + var clonedPage = page.cloneNode( true ); + page.parentNode.insertBefore( clonedPage, ( previousPage || page ).nextSibling ); + + previousFragmentStep = fragments; + previousPage = clonedPage; + + } ); + + // Reset the first/original page so that all fragments are hidden + fragmentGroups.forEach( function( fragments ) { + fragments.forEach( function( fragment ) { + fragment.classList.remove( 'visible', 'current-fragment' ); + } ); + } ); + + } + // Show all fragments + else { + toArray( page.querySelectorAll( '.fragment:not(.fade-out)' ) ).forEach( function( fragment ) { + fragment.classList.add( 'visible' ); + } ); + } + + } + + } ); + + // Notify subscribers that the PDF layout is good to go + dispatchEvent( 'pdf-ready' ); + + } + + /** + * This is an unfortunate necessity. Some actions – such as + * an input field being focused in an iframe or using the + * keyboard to expand text selection beyond the bounds of + * a slide – can trigger our content to be pushed out of view. + * This scrolling can not be prevented by hiding overflow in + * CSS (we already do) so we have to resort to repeatedly + * checking if the slides have been offset :( + */ + function setupScrollPrevention() { + + setInterval( function() { + if( dom.wrapper.scrollTop !== 0 || dom.wrapper.scrollLeft !== 0 ) { + dom.wrapper.scrollTop = 0; + dom.wrapper.scrollLeft = 0; + } + }, 1000 ); + + } + + /** + * Creates an HTML element and returns a reference to it. + * If the element already exists the existing instance will + * be returned. + * + * @param {HTMLElement} container + * @param {string} tagname + * @param {string} classname + * @param {string} innerHTML + * + * @return {HTMLElement} + */ + function createSingletonNode( container, tagname, classname, innerHTML ) { + + // Find all nodes matching the description + var nodes = container.querySelectorAll( '.' + classname ); + + // Check all matches to find one which is a direct child of + // the specified container + for( var i = 0; i < nodes.length; i++ ) { + var testNode = nodes[i]; + if( testNode.parentNode === container ) { + return testNode; + } + } + + // If no node was found, create it now + var node = document.createElement( tagname ); + node.className = classname; + if( typeof innerHTML === 'string' ) { + node.innerHTML = innerHTML; + } + container.appendChild( node ); + + return node; + + } + + /** + * Creates the slide background elements and appends them + * to the background container. One element is created per + * slide no matter if the given slide has visible background. + */ + function createBackgrounds() { + + var printMode = isPrintingPDF(); + + // Clear prior backgrounds + dom.background.innerHTML = ''; + dom.background.classList.add( 'no-transition' ); + + // Iterate over all horizontal slides + toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) { + + var backgroundStack = createBackground( slideh, dom.background ); + + // Iterate over all vertical slides + toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) { + + createBackground( slidev, backgroundStack ); + + backgroundStack.classList.add( 'stack' ); + + } ); + + } ); + + // Add parallax background if specified + if( config.parallaxBackgroundImage ) { + + dom.background.style.backgroundImage = 'url("' + config.parallaxBackgroundImage + '")'; + dom.background.style.backgroundSize = config.parallaxBackgroundSize; + dom.background.style.backgroundRepeat = config.parallaxBackgroundRepeat; + dom.background.style.backgroundPosition = config.parallaxBackgroundPosition; + + // Make sure the below properties are set on the element - these properties are + // needed for proper transitions to be set on the element via CSS. To remove + // annoying background slide-in effect when the presentation starts, apply + // these properties after short time delay + setTimeout( function() { + dom.wrapper.classList.add( 'has-parallax-background' ); + }, 1 ); + + } + else { + + dom.background.style.backgroundImage = ''; + dom.wrapper.classList.remove( 'has-parallax-background' ); + + } + + } + + /** + * Creates a background for the given slide. + * + * @param {HTMLElement} slide + * @param {HTMLElement} container The element that the background + * should be appended to + * @return {HTMLElement} New background div + */ + function createBackground( slide, container ) { + + + // Main slide background element + var element = document.createElement( 'div' ); + element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' ); + + // Inner background element that wraps images/videos/iframes + var contentElement = document.createElement( 'div' ); + contentElement.className = 'slide-background-content'; + + element.appendChild( contentElement ); + container.appendChild( element ); + + slide.slideBackgroundElement = element; + slide.slideBackgroundContentElement = contentElement; + + // Syncs the background to reflect all current background settings + syncBackground( slide ); + + return element; + + } + + /** + * Renders all of the visual properties of a slide background + * based on the various background attributes. + * + * @param {HTMLElement} slide + */ + function syncBackground( slide ) { + + var element = slide.slideBackgroundElement, + contentElement = slide.slideBackgroundContentElement; + + // Reset the prior background state in case this is not the + // initial sync + slide.classList.remove( 'has-dark-background' ); + slide.classList.remove( 'has-light-background' ); + + element.removeAttribute( 'data-loaded' ); + element.removeAttribute( 'data-background-hash' ); + element.removeAttribute( 'data-background-size' ); + element.removeAttribute( 'data-background-transition' ); + element.style.backgroundColor = ''; + + contentElement.style.backgroundSize = ''; + contentElement.style.backgroundRepeat = ''; + contentElement.style.backgroundPosition = ''; + contentElement.style.backgroundImage = ''; + contentElement.style.opacity = ''; + contentElement.innerHTML = ''; + + var data = { + background: slide.getAttribute( 'data-background' ), + backgroundSize: slide.getAttribute( 'data-background-size' ), + backgroundImage: slide.getAttribute( 'data-background-image' ), + backgroundVideo: slide.getAttribute( 'data-background-video' ), + backgroundIframe: slide.getAttribute( 'data-background-iframe' ), + backgroundColor: slide.getAttribute( 'data-background-color' ), + backgroundRepeat: slide.getAttribute( 'data-background-repeat' ), + backgroundPosition: slide.getAttribute( 'data-background-position' ), + backgroundTransition: slide.getAttribute( 'data-background-transition' ), + backgroundOpacity: slide.getAttribute( 'data-background-opacity' ) + }; + + if( data.background ) { + // Auto-wrap image urls in url(...) + if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test( data.background ) ) { + slide.setAttribute( 'data-background-image', data.background ); + } + else { + element.style.background = data.background; + } + } + + // Create a hash for this combination of background settings. + // This is used to determine when two slide backgrounds are + // the same. + if( data.background || data.backgroundColor || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) { + element.setAttribute( 'data-background-hash', data.background + + data.backgroundSize + + data.backgroundImage + + data.backgroundVideo + + data.backgroundIframe + + data.backgroundColor + + data.backgroundRepeat + + data.backgroundPosition + + data.backgroundTransition + + data.backgroundOpacity ); + } + + // Additional and optional background properties + if( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize ); + if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor; + if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition ); + + if( slide.hasAttribute( 'data-preload' ) ) element.setAttribute( 'data-preload', '' ); + + // Background image options are set on the content wrapper + if( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize; + if( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat; + if( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition; + if( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity; + + // If this slide has a background color, we add a class that + // signals if it is light or dark. If the slide has no background + // color, no class will be added + var contrastColor = data.backgroundColor; + + // If no bg color was found, check the computed background + if( !contrastColor ) { + var computedBackgroundStyle = window.getComputedStyle( element ); + if( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) { + contrastColor = computedBackgroundStyle.backgroundColor; + } + } + + if( contrastColor ) { + var rgb = colorToRgb( contrastColor ); + + // Ignore fully transparent backgrounds. Some browsers return + // rgba(0,0,0,0) when reading the computed background color of + // an element with no background + if( rgb && rgb.a !== 0 ) { + if( colorBrightness( contrastColor ) < 128 ) { + slide.classList.add( 'has-dark-background' ); + } + else { + slide.classList.add( 'has-light-background' ); + } + } + } + + } + + /** + * Registers a listener to postMessage events, this makes it + * possible to call all reveal.js API methods from another + * window. For example: + * + * revealWindow.postMessage( JSON.stringify({ + * method: 'slide', + * args: [ 2 ] + * }), '*' ); + */ + function setupPostMessage() { + + if( config.postMessage ) { + window.addEventListener( 'message', function ( event ) { + var data = event.data; + + // Make sure we're dealing with JSON + if( typeof data === 'string' && data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) { + data = JSON.parse( data ); + + // Check if the requested method can be found + if( data.method && typeof Reveal[data.method] === 'function' ) { + + if( POST_MESSAGE_METHOD_BLACKLIST.test( data.method ) === false ) { + + var result = Reveal[data.method].apply( Reveal, data.args ); + + // Dispatch a postMessage event with the returned value from + // our method invocation for getter functions + dispatchPostMessage( 'callback', { method: data.method, result: result } ); + + } + else { + console.warn( 'reveal.js: "'+ data.method +'" is is blacklisted from the postMessage API' ); + } + + } + } + }, false ); + } + + } + + /** + * Applies the configuration settings from the config + * object. May be called multiple times. + * + * @param {object} options + */ + function configure( options ) { + + var oldTransition = config.transition; + + // New config options may be passed when this method + // is invoked through the API after initialization + if( typeof options === 'object' ) extend( config, options ); + + // Abort if reveal.js hasn't finished loading, config + // changes will be applied automatically once loading + // finishes + if( loaded === false ) return; + + var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length; + + // Remove the previously configured transition class + dom.wrapper.classList.remove( oldTransition ); + + // Force linear transition based on browser capabilities + if( features.transforms3d === false ) config.transition = 'linear'; + + dom.wrapper.classList.add( config.transition ); + + dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed ); + dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition ); + + dom.controls.style.display = config.controls ? 'block' : 'none'; + dom.progress.style.display = config.progress ? 'block' : 'none'; + + dom.controls.setAttribute( 'data-controls-layout', config.controlsLayout ); + dom.controls.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows ); + + if( config.shuffle ) { + shuffle(); + } + + if( config.rtl ) { + dom.wrapper.classList.add( 'rtl' ); + } + else { + dom.wrapper.classList.remove( 'rtl' ); + } + + if( config.center ) { + dom.wrapper.classList.add( 'center' ); + } + else { + dom.wrapper.classList.remove( 'center' ); + } + + // Exit the paused mode if it was configured off + if( config.pause === false ) { + resume(); + } + + if( config.showNotes ) { + dom.speakerNotes.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' ); + } + + if( config.mouseWheel ) { + document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF + document.addEventListener( 'mousewheel', onDocumentMouseScroll, false ); + } + else { + document.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF + document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false ); + } + + // Rolling 3D links + if( config.rollingLinks ) { + enableRollingLinks(); + } + else { + disableRollingLinks(); + } + + // Auto-hide the mouse pointer when its inactive + if( config.hideInactiveCursor ) { + document.addEventListener( 'mousemove', onDocumentCursorActive, false ); + document.addEventListener( 'mousedown', onDocumentCursorActive, false ); + } + else { + showCursor(); + + document.removeEventListener( 'mousemove', onDocumentCursorActive, false ); + document.removeEventListener( 'mousedown', onDocumentCursorActive, false ); + } + + // Iframe link previews + if( config.previewLinks ) { + enablePreviewLinks(); + disablePreviewLinks( '[data-preview-link=false]' ); + } + else { + disablePreviewLinks(); + enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' ); + } + + // Remove existing auto-slide controls + if( autoSlidePlayer ) { + autoSlidePlayer.destroy(); + autoSlidePlayer = null; + } + + // Generate auto-slide controls if needed + if( numberOfSlides > 1 && config.autoSlide && config.autoSlideStoppable && features.canvas && features.requestAnimationFrame ) { + autoSlidePlayer = new Playback( dom.wrapper, function() { + return Math.min( Math.max( ( Date.now() - autoSlideStartTime ) / autoSlide, 0 ), 1 ); + } ); + + autoSlidePlayer.on( 'click', onAutoSlidePlayerClick ); + autoSlidePaused = false; + } + + // When fragments are turned off they should be visible + if( config.fragments === false ) { + toArray( dom.slides.querySelectorAll( '.fragment' ) ).forEach( function( element ) { + element.classList.add( 'visible' ); + element.classList.remove( 'current-fragment' ); + } ); + } + + // Slide numbers + var slideNumberDisplay = 'none'; + if( config.slideNumber && !isPrintingPDF() ) { + if( config.showSlideNumber === 'all' ) { + slideNumberDisplay = 'block'; + } + else if( config.showSlideNumber === 'speaker' && isSpeakerNotes() ) { + slideNumberDisplay = 'block'; + } + } + + dom.slideNumber.style.display = slideNumberDisplay; + + // Add the navigation mode to the DOM so we can adjust styling + if( config.navigationMode !== 'default' ) { + dom.wrapper.setAttribute( 'data-navigation-mode', config.navigationMode ); + } + else { + dom.wrapper.removeAttribute( 'data-navigation-mode' ); + } + + // Define our contextual list of keyboard shortcuts + if( config.navigationMode === 'linear' ) { + keyboardShortcuts['→ , ↓ , SPACE , N , L , J'] = 'Next slide'; + keyboardShortcuts['← , ↑ , P , H , K'] = 'Previous slide'; + } + else { + keyboardShortcuts['N , SPACE'] = 'Next slide'; + keyboardShortcuts['P'] = 'Previous slide'; + keyboardShortcuts['← , H'] = 'Navigate left'; + keyboardShortcuts['→ , L'] = 'Navigate right'; + keyboardShortcuts['↑ , K'] = 'Navigate up'; + keyboardShortcuts['↓ , J'] = 'Navigate down'; + } + + keyboardShortcuts['Home , Shift ←'] = 'First slide'; + keyboardShortcuts['End , Shift →'] = 'Last slide'; + keyboardShortcuts['B , .'] = 'Pause'; + keyboardShortcuts['F'] = 'Fullscreen'; + keyboardShortcuts['ESC, O'] = 'Slide overview'; + + sync(); + + } + + /** + * Binds all event listeners. + */ + function addEventListeners() { + + eventsAreBound = true; + + window.addEventListener( 'hashchange', onWindowHashChange, false ); + window.addEventListener( 'resize', onWindowResize, false ); + + if( config.touch ) { + if( 'onpointerdown' in window ) { + // Use W3C pointer events + dom.wrapper.addEventListener( 'pointerdown', onPointerDown, false ); + dom.wrapper.addEventListener( 'pointermove', onPointerMove, false ); + dom.wrapper.addEventListener( 'pointerup', onPointerUp, false ); + } + else if( window.navigator.msPointerEnabled ) { + // IE 10 uses prefixed version of pointer events + dom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false ); + dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false ); + dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false ); + } + else { + // Fall back to touch events + dom.wrapper.addEventListener( 'touchstart', onTouchStart, false ); + dom.wrapper.addEventListener( 'touchmove', onTouchMove, false ); + dom.wrapper.addEventListener( 'touchend', onTouchEnd, false ); + } + } + + if( config.keyboard ) { + document.addEventListener( 'keydown', onDocumentKeyDown, false ); + document.addEventListener( 'keypress', onDocumentKeyPress, false ); + } + + if( config.progress && dom.progress ) { + dom.progress.addEventListener( 'click', onProgressClicked, false ); + } + + dom.pauseOverlay.addEventListener( 'click', resume, false ); + + if( config.focusBodyOnPageVisibilityChange ) { + var visibilityChange; + + if( 'hidden' in document ) { + visibilityChange = 'visibilitychange'; + } + else if( 'msHidden' in document ) { + visibilityChange = 'msvisibilitychange'; + } + else if( 'webkitHidden' in document ) { + visibilityChange = 'webkitvisibilitychange'; + } + + if( visibilityChange ) { + document.addEventListener( visibilityChange, onPageVisibilityChange, false ); + } + } + + // Listen to both touch and click events, in case the device + // supports both + var pointerEvents = [ 'touchstart', 'click' ]; + + // Only support touch for Android, fixes double navigations in + // stock browser + if( UA.match( /android/gi ) ) { + pointerEvents = [ 'touchstart' ]; + } + + pointerEvents.forEach( function( eventName ) { + dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } ); + dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } ); + dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } ); + dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } ); + dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } ); + dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } ); + } ); + + } + + /** + * Unbinds all event listeners. + */ + function removeEventListeners() { + + eventsAreBound = false; + + document.removeEventListener( 'keydown', onDocumentKeyDown, false ); + document.removeEventListener( 'keypress', onDocumentKeyPress, false ); + window.removeEventListener( 'hashchange', onWindowHashChange, false ); + window.removeEventListener( 'resize', onWindowResize, false ); + + dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false ); + + dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false ); + dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false ); + dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false ); + + dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false ); + dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false ); + dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false ); + + dom.pauseOverlay.removeEventListener( 'click', resume, false ); + + if ( config.progress && dom.progress ) { + dom.progress.removeEventListener( 'click', onProgressClicked, false ); + } + + [ 'touchstart', 'click' ].forEach( function( eventName ) { + dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } ); + dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } ); + dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } ); + dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } ); + dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } ); + dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } ); + } ); + + } + + /** + * Registers a new plugin with this reveal.js instance. + * + * reveal.js waits for all regisered plugins to initialize + * before considering itself ready, as long as the plugin + * is registered before calling `Reveal.initialize()`. + */ + function registerPlugin( id, plugin ) { + + if( plugins[id] === undefined ) { + plugins[id] = plugin; + + // If a plugin is registered after reveal.js is loaded, + // initialize it right away + if( loaded && typeof plugin.init === 'function' ) { + plugin.init(); + } + } + else { + console.warn( 'reveal.js: "'+ id +'" plugin has already been registered' ); + } + + } + + /** + * Checks if a specific plugin has been registered. + * + * @param {String} id Unique plugin identifier + */ + function hasPlugin( id ) { + + return !!plugins[id]; + + } + + /** + * Returns the specific plugin instance, if a plugin + * with the given ID has been registered. + * + * @param {String} id Unique plugin identifier + */ + function getPlugin( id ) { + + return plugins[id]; + + } + + /** + * Add a custom key binding with optional description to + * be added to the help screen. + */ + function addKeyBinding( binding, callback ) { + + if( typeof binding === 'object' && binding.keyCode ) { + registeredKeyBindings[binding.keyCode] = { + callback: callback, + key: binding.key, + description: binding.description + }; + } + else { + registeredKeyBindings[binding] = { + callback: callback, + key: null, + description: null + }; + } + + } + + /** + * Removes the specified custom key binding. + */ + function removeKeyBinding( keyCode ) { + + delete registeredKeyBindings[keyCode]; + + } + + /** + * Extend object a with the properties of object b. + * If there's a conflict, object b takes precedence. + * + * @param {object} a + * @param {object} b + */ + function extend( a, b ) { + + for( var i in b ) { + a[ i ] = b[ i ]; + } + + return a; + + } + + /** + * Converts the target object to an array. + * + * @param {object} o + * @return {object[]} + */ + function toArray( o ) { + + return Array.prototype.slice.call( o ); + + } + + /** + * Utility for deserializing a value. + * + * @param {*} value + * @return {*} + */ + function deserialize( value ) { + + if( typeof value === 'string' ) { + if( value === 'null' ) return null; + else if( value === 'true' ) return true; + else if( value === 'false' ) return false; + else if( value.match( /^-?[\d\.]+$/ ) ) return parseFloat( value ); + } + + return value; + + } + + /** + * Measures the distance in pixels between point a + * and point b. + * + * @param {object} a point with x/y properties + * @param {object} b point with x/y properties + * + * @return {number} + */ + function distanceBetween( a, b ) { + + var dx = a.x - b.x, + dy = a.y - b.y; + + return Math.sqrt( dx*dx + dy*dy ); + + } + + /** + * Applies a CSS transform to the target element. + * + * @param {HTMLElement} element + * @param {string} transform + */ + function transformElement( element, transform ) { + + element.style.WebkitTransform = transform; + element.style.MozTransform = transform; + element.style.msTransform = transform; + element.style.transform = transform; + + } + + /** + * Applies CSS transforms to the slides container. The container + * is transformed from two separate sources: layout and the overview + * mode. + * + * @param {object} transforms + */ + function transformSlides( transforms ) { + + // Pick up new transforms from arguments + if( typeof transforms.layout === 'string' ) slidesTransform.layout = transforms.layout; + if( typeof transforms.overview === 'string' ) slidesTransform.overview = transforms.overview; + + // Apply the transforms to the slides container + if( slidesTransform.layout ) { + transformElement( dom.slides, slidesTransform.layout + ' ' + slidesTransform.overview ); + } + else { + transformElement( dom.slides, slidesTransform.overview ); + } + + } + + /** + * Injects the given CSS styles into the DOM. + * + * @param {string} value + */ + function injectStyleSheet( value ) { + + var tag = document.createElement( 'style' ); + tag.type = 'text/css'; + if( tag.styleSheet ) { + tag.styleSheet.cssText = value; + } + else { + tag.appendChild( document.createTextNode( value ) ); + } + document.getElementsByTagName( 'head' )[0].appendChild( tag ); + + } + + /** + * Find the closest parent that matches the given + * selector. + * + * @param {HTMLElement} target The child element + * @param {String} selector The CSS selector to match + * the parents against + * + * @return {HTMLElement} The matched parent or null + * if no matching parent was found + */ + function closestParent( target, selector ) { + + var parent = target.parentNode; + + while( parent ) { + + // There's some overhead doing this each time, we don't + // want to rewrite the element prototype but should still + // be enough to feature detect once at startup... + var matchesMethod = parent.matches || parent.matchesSelector || parent.msMatchesSelector; + + // If we find a match, we're all set + if( matchesMethod && matchesMethod.call( parent, selector ) ) { + return parent; + } + + // Keep searching + parent = parent.parentNode; + + } + + return null; + + } + + /** + * Converts various color input formats to an {r:0,g:0,b:0} object. + * + * @param {string} color The string representation of a color + * @example + * colorToRgb('#000'); + * @example + * colorToRgb('#000000'); + * @example + * colorToRgb('rgb(0,0,0)'); + * @example + * colorToRgb('rgba(0,0,0)'); + * + * @return {{r: number, g: number, b: number, [a]: number}|null} + */ + function colorToRgb( color ) { + + var hex3 = color.match( /^#([0-9a-f]{3})$/i ); + if( hex3 && hex3[1] ) { + hex3 = hex3[1]; + return { + r: parseInt( hex3.charAt( 0 ), 16 ) * 0x11, + g: parseInt( hex3.charAt( 1 ), 16 ) * 0x11, + b: parseInt( hex3.charAt( 2 ), 16 ) * 0x11 + }; + } + + var hex6 = color.match( /^#([0-9a-f]{6})$/i ); + if( hex6 && hex6[1] ) { + hex6 = hex6[1]; + return { + r: parseInt( hex6.substr( 0, 2 ), 16 ), + g: parseInt( hex6.substr( 2, 2 ), 16 ), + b: parseInt( hex6.substr( 4, 2 ), 16 ) + }; + } + + var rgb = color.match( /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i ); + if( rgb ) { + return { + r: parseInt( rgb[1], 10 ), + g: parseInt( rgb[2], 10 ), + b: parseInt( rgb[3], 10 ) + }; + } + + var rgba = color.match( /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i ); + if( rgba ) { + return { + r: parseInt( rgba[1], 10 ), + g: parseInt( rgba[2], 10 ), + b: parseInt( rgba[3], 10 ), + a: parseFloat( rgba[4] ) + }; + } + + return null; + + } + + /** + * Calculates brightness on a scale of 0-255. + * + * @param {string} color See colorToRgb for supported formats. + * @see {@link colorToRgb} + */ + function colorBrightness( color ) { + + if( typeof color === 'string' ) color = colorToRgb( color ); + + if( color ) { + return ( color.r * 299 + color.g * 587 + color.b * 114 ) / 1000; + } + + return null; + + } + + /** + * Returns the remaining height within the parent of the + * target element. + * + * remaining height = [ configured parent height ] - [ current parent height ] + * + * @param {HTMLElement} element + * @param {number} [height] + */ + function getRemainingHeight( element, height ) { + + height = height || 0; + + if( element ) { + var newHeight, oldHeight = element.style.height; + + // Change the .stretch element height to 0 in order find the height of all + // the other elements + element.style.height = '0px'; + + // In Overview mode, the parent (.slide) height is set of 700px. + // Restore it temporarily to its natural height. + element.parentNode.style.height = 'auto'; + + newHeight = height - element.parentNode.offsetHeight; + + // Restore the old height, just in case + element.style.height = oldHeight + 'px'; + + // Clear the parent (.slide) height. .removeProperty works in IE9+ + element.parentNode.style.removeProperty('height'); + + return newHeight; + } + + return height; + + } + + /** + * Checks if this instance is being used to print a PDF. + */ + function isPrintingPDF() { + + return ( /print-pdf/gi ).test( window.location.search ); + + } + + /** + * Hides the address bar if we're on a mobile device. + */ + function hideAddressBar() { + + if( config.hideAddressBar && isMobileDevice ) { + // Events that should trigger the address bar to hide + window.addEventListener( 'load', removeAddressBar, false ); + window.addEventListener( 'orientationchange', removeAddressBar, false ); + } + + } + + /** + * Causes the address bar to hide on mobile devices, + * more vertical space ftw. + */ + function removeAddressBar() { + + setTimeout( function() { + window.scrollTo( 0, 1 ); + }, 10 ); + + } + + /** + * Dispatches an event of the specified type from the + * reveal DOM element. + */ + function dispatchEvent( type, args ) { + + var event = document.createEvent( 'HTMLEvents', 1, 2 ); + event.initEvent( type, true, true ); + extend( event, args ); + dom.wrapper.dispatchEvent( event ); + + // If we're in an iframe, post each reveal.js event to the + // parent window. Used by the notes plugin + dispatchPostMessage( type ); + + } + + /** + * Dispatched a postMessage of the given type from our window. + */ + function dispatchPostMessage( type, data ) { + + if( config.postMessageEvents && window.parent !== window.self ) { + var message = { + namespace: 'reveal', + eventName: type, + state: getState() + }; + + extend( message, data ); + + window.parent.postMessage( JSON.stringify( message ), '*' ); + } + + } + + /** + * Wrap all links in 3D goodness. + */ + function enableRollingLinks() { + + if( features.transforms3d && !( 'msPerspective' in document.body.style ) ) { + var anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a' ); + + for( var i = 0, len = anchors.length; i < len; i++ ) { + var anchor = anchors[i]; + + if( anchor.textContent && !anchor.querySelector( '*' ) && ( !anchor.className || !anchor.classList.contains( anchor, 'roll' ) ) ) { + var span = document.createElement('span'); + span.setAttribute('data-title', anchor.text); + span.innerHTML = anchor.innerHTML; + + anchor.classList.add( 'roll' ); + anchor.innerHTML = ''; + anchor.appendChild(span); + } + } + } + + } + + /** + * Unwrap all 3D links. + */ + function disableRollingLinks() { + + var anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a.roll' ); + + for( var i = 0, len = anchors.length; i < len; i++ ) { + var anchor = anchors[i]; + var span = anchor.querySelector( 'span' ); + + if( span ) { + anchor.classList.remove( 'roll' ); + anchor.innerHTML = span.innerHTML; + } + } + + } + + /** + * Bind preview frame links. + * + * @param {string} [selector=a] - selector for anchors + */ + function enablePreviewLinks( selector ) { + + var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) ); + + anchors.forEach( function( element ) { + if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { + element.addEventListener( 'click', onPreviewLinkClicked, false ); + } + } ); + + } + + /** + * Unbind preview frame links. + */ + function disablePreviewLinks( selector ) { + + var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) ); + + anchors.forEach( function( element ) { + if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) { + element.removeEventListener( 'click', onPreviewLinkClicked, false ); + } + } ); + + } + + /** + * Opens a preview window for the target URL. + * + * @param {string} url - url for preview iframe src + */ + function showPreview( url ) { + + closeOverlay(); + + dom.overlay = document.createElement( 'div' ); + dom.overlay.classList.add( 'overlay' ); + dom.overlay.classList.add( 'overlay-preview' ); + dom.wrapper.appendChild( dom.overlay ); + + dom.overlay.innerHTML = [ + '
', + '', + '', + '
', + '
', + '
', + '', + '', + 'Unable to load iframe. This is likely due to the site\'s policy (x-frame-options).', + '', + '
' + ].join(''); + + dom.overlay.querySelector( 'iframe' ).addEventListener( 'load', function( event ) { + dom.overlay.classList.add( 'loaded' ); + }, false ); + + dom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) { + closeOverlay(); + event.preventDefault(); + }, false ); + + dom.overlay.querySelector( '.external' ).addEventListener( 'click', function( event ) { + closeOverlay(); + }, false ); + + setTimeout( function() { + dom.overlay.classList.add( 'visible' ); + }, 1 ); + + } + + /** + * Open or close help overlay window. + * + * @param {Boolean} [override] Flag which overrides the + * toggle logic and forcibly sets the desired state. True means + * help is open, false means it's closed. + */ + function toggleHelp( override ){ + + if( typeof override === 'boolean' ) { + override ? showHelp() : closeOverlay(); + } + else { + if( dom.overlay ) { + closeOverlay(); + } + else { + showHelp(); + } + } + } + + /** + * Opens an overlay window with help material. + */ + function showHelp() { + + if( config.help ) { + + closeOverlay(); + + dom.overlay = document.createElement( 'div' ); + dom.overlay.classList.add( 'overlay' ); + dom.overlay.classList.add( 'overlay-help' ); + dom.wrapper.appendChild( dom.overlay ); + + var html = '

Keyboard Shortcuts


'; + + html += ''; + for( var key in keyboardShortcuts ) { + html += ''; + } + + // Add custom key bindings that have associated descriptions + for( var binding in registeredKeyBindings ) { + if( registeredKeyBindings[binding].key && registeredKeyBindings[binding].description ) { + html += ''; + } + } + + html += '
KEYACTION
' + key + '' + keyboardShortcuts[ key ] + '
' + registeredKeyBindings[binding].key + '' + registeredKeyBindings[binding].description + '
'; + + dom.overlay.innerHTML = [ + '
', + '', + '
', + '
', + '
'+ html +'
', + '
' + ].join(''); + + dom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) { + closeOverlay(); + event.preventDefault(); + }, false ); + + setTimeout( function() { + dom.overlay.classList.add( 'visible' ); + }, 1 ); + + } + + } + + /** + * Closes any currently open overlay. + */ + function closeOverlay() { + + if( dom.overlay ) { + dom.overlay.parentNode.removeChild( dom.overlay ); + dom.overlay = null; + } + + } + + /** + * Applies JavaScript-controlled layout rules to the + * presentation. + */ + function layout() { + + if( dom.wrapper && !isPrintingPDF() ) { + + if( !config.disableLayout ) { + + // On some mobile devices '100vh' is taller than the visible + // viewport which leads to part of the presentation being + // cut off. To work around this we define our own '--vh' custom + // property where 100x adds up to the correct height. + // + // https://css-tricks.com/the-trick-to-viewport-units-on-mobile/ + if( isMobileDevice ) { + document.documentElement.style.setProperty( '--vh', ( window.innerHeight * 0.01 ) + 'px' ); + } + + var size = getComputedSlideSize(); + + var oldScale = scale; + + // Layout the contents of the slides + layoutSlideContents( config.width, config.height ); + + dom.slides.style.width = size.width + 'px'; + dom.slides.style.height = size.height + 'px'; + + // Determine scale of content to fit within available space + scale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height ); + + // Respect max/min scale settings + scale = Math.max( scale, config.minScale ); + scale = Math.min( scale, config.maxScale ); + + // Don't apply any scaling styles if scale is 1 + if( scale === 1 ) { + dom.slides.style.zoom = ''; + dom.slides.style.left = ''; + dom.slides.style.top = ''; + dom.slides.style.bottom = ''; + dom.slides.style.right = ''; + transformSlides( { layout: '' } ); + } + else { + // Zoom Scaling + // Content remains crisp no matter how much we scale. Side + // effects are minor differences in text layout and iframe + // viewports changing size. A 200x200 iframe viewport in a + // 2x zoomed presentation ends up having a 400x400 viewport. + if( scale > 1 && features.zoom && window.devicePixelRatio < 2 ) { + dom.slides.style.zoom = scale; + dom.slides.style.left = ''; + dom.slides.style.top = ''; + dom.slides.style.bottom = ''; + dom.slides.style.right = ''; + transformSlides( { layout: '' } ); + } + // Transform Scaling + // Content layout remains the exact same when scaled up. + // Side effect is content becoming blurred, especially with + // high scale values on ldpi screens. + else { + dom.slides.style.zoom = ''; + dom.slides.style.left = '50%'; + dom.slides.style.top = '50%'; + dom.slides.style.bottom = 'auto'; + dom.slides.style.right = 'auto'; + transformSlides( { layout: 'translate(-50%, -50%) scale('+ scale +')' } ); + } + } + + // Select all slides, vertical and horizontal + var slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ); + + for( var i = 0, len = slides.length; i < len; i++ ) { + var slide = slides[ i ]; + + // Don't bother updating invisible slides + if( slide.style.display === 'none' ) { + continue; + } + + if( config.center || slide.classList.contains( 'center' ) ) { + // Vertical stacks are not centred since their section + // children will be + if( slide.classList.contains( 'stack' ) ) { + slide.style.top = 0; + } + else { + slide.style.top = Math.max( ( size.height - slide.scrollHeight ) / 2, 0 ) + 'px'; + } + } + else { + slide.style.top = ''; + } + + } + + if( oldScale !== scale ) { + dispatchEvent( 'resize', { + 'oldScale': oldScale, + 'scale': scale, + 'size': size + } ); + } + } + + updateProgress(); + updateParallax(); + + if( isOverview() ) { + updateOverview(); + } + + } + + } + + /** + * Applies layout logic to the contents of all slides in + * the presentation. + * + * @param {string|number} width + * @param {string|number} height + */ + function layoutSlideContents( width, height ) { + + // Handle sizing of elements with the 'stretch' class + toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) { + + // Determine how much vertical space we can use + var remainingHeight = getRemainingHeight( element, height ); + + // Consider the aspect ratio of media elements + if( /(img|video)/gi.test( element.nodeName ) ) { + var nw = element.naturalWidth || element.videoWidth, + nh = element.naturalHeight || element.videoHeight; + + var es = Math.min( width / nw, remainingHeight / nh ); + + element.style.width = ( nw * es ) + 'px'; + element.style.height = ( nh * es ) + 'px'; + + } + else { + element.style.width = width + 'px'; + element.style.height = remainingHeight + 'px'; + } + + } ); + + } + + /** + * Calculates the computed pixel size of our slides. These + * values are based on the width and height configuration + * options. + * + * @param {number} [presentationWidth=dom.wrapper.offsetWidth] + * @param {number} [presentationHeight=dom.wrapper.offsetHeight] + */ + function getComputedSlideSize( presentationWidth, presentationHeight ) { + + var size = { + // Slide size + width: config.width, + height: config.height, + + // Presentation size + presentationWidth: presentationWidth || dom.wrapper.offsetWidth, + presentationHeight: presentationHeight || dom.wrapper.offsetHeight + }; + + // Reduce available space by margin + size.presentationWidth -= ( size.presentationWidth * config.margin ); + size.presentationHeight -= ( size.presentationHeight * config.margin ); + + // Slide width may be a percentage of available width + if( typeof size.width === 'string' && /%$/.test( size.width ) ) { + size.width = parseInt( size.width, 10 ) / 100 * size.presentationWidth; + } + + // Slide height may be a percentage of available height + if( typeof size.height === 'string' && /%$/.test( size.height ) ) { + size.height = parseInt( size.height, 10 ) / 100 * size.presentationHeight; + } + + return size; + + } + + /** + * Stores the vertical index of a stack so that the same + * vertical slide can be selected when navigating to and + * from the stack. + * + * @param {HTMLElement} stack The vertical stack element + * @param {string|number} [v=0] Index to memorize + */ + function setPreviousVerticalIndex( stack, v ) { + + if( typeof stack === 'object' && typeof stack.setAttribute === 'function' ) { + stack.setAttribute( 'data-previous-indexv', v || 0 ); + } + + } + + /** + * Retrieves the vertical index which was stored using + * #setPreviousVerticalIndex() or 0 if no previous index + * exists. + * + * @param {HTMLElement} stack The vertical stack element + */ + function getPreviousVerticalIndex( stack ) { + + if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) { + // Prefer manually defined start-indexv + var attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv'; + + return parseInt( stack.getAttribute( attributeName ) || 0, 10 ); + } + + return 0; + + } + + /** + * Displays the overview of slides (quick nav) by scaling + * down and arranging all slide elements. + */ + function activateOverview() { + + // Only proceed if enabled in config + if( config.overview && !isOverview() ) { + + overview = true; + + dom.wrapper.classList.add( 'overview' ); + dom.wrapper.classList.remove( 'overview-deactivating' ); + + if( features.overviewTransitions ) { + setTimeout( function() { + dom.wrapper.classList.add( 'overview-animated' ); + }, 1 ); + } + + // Don't auto-slide while in overview mode + cancelAutoSlide(); + + // Move the backgrounds element into the slide container to + // that the same scaling is applied + dom.slides.appendChild( dom.background ); + + // Clicking on an overview slide navigates to it + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + if( !slide.classList.contains( 'stack' ) ) { + slide.addEventListener( 'click', onOverviewSlideClicked, true ); + } + } ); + + // Calculate slide sizes + var margin = 70; + var slideSize = getComputedSlideSize(); + overviewSlideWidth = slideSize.width + margin; + overviewSlideHeight = slideSize.height + margin; + + // Reverse in RTL mode + if( config.rtl ) { + overviewSlideWidth = -overviewSlideWidth; + } + + updateSlidesVisibility(); + layoutOverview(); + updateOverview(); + + layout(); + + // Notify observers of the overview showing + dispatchEvent( 'overviewshown', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + + } + + } + + /** + * Uses CSS transforms to position all slides in a grid for + * display inside of the overview mode. + */ + function layoutOverview() { + + // Layout slides + toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( hslide, h ) { + hslide.setAttribute( 'data-index-h', h ); + transformElement( hslide, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); + + if( hslide.classList.contains( 'stack' ) ) { + + toArray( hslide.querySelectorAll( 'section' ) ).forEach( function( vslide, v ) { + vslide.setAttribute( 'data-index-h', h ); + vslide.setAttribute( 'data-index-v', v ); + + transformElement( vslide, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); + } ); + + } + } ); + + // Layout slide backgrounds + toArray( dom.background.childNodes ).forEach( function( hbackground, h ) { + transformElement( hbackground, 'translate3d(' + ( h * overviewSlideWidth ) + 'px, 0, 0)' ); + + toArray( hbackground.querySelectorAll( '.slide-background' ) ).forEach( function( vbackground, v ) { + transformElement( vbackground, 'translate3d(0, ' + ( v * overviewSlideHeight ) + 'px, 0)' ); + } ); + } ); + + } + + /** + * Moves the overview viewport to the current slides. + * Called each time the current slide changes. + */ + function updateOverview() { + + var vmin = Math.min( window.innerWidth, window.innerHeight ); + var scale = Math.max( vmin / 5, 150 ) / vmin; + + transformSlides( { + overview: [ + 'scale('+ scale +')', + 'translateX('+ ( -indexh * overviewSlideWidth ) +'px)', + 'translateY('+ ( -indexv * overviewSlideHeight ) +'px)' + ].join( ' ' ) + } ); + + } + + /** + * Exits the slide overview and enters the currently + * active slide. + */ + function deactivateOverview() { + + // Only proceed if enabled in config + if( config.overview ) { + + overview = false; + + dom.wrapper.classList.remove( 'overview' ); + dom.wrapper.classList.remove( 'overview-animated' ); + + // Temporarily add a class so that transitions can do different things + // depending on whether they are exiting/entering overview, or just + // moving from slide to slide + dom.wrapper.classList.add( 'overview-deactivating' ); + + setTimeout( function () { + dom.wrapper.classList.remove( 'overview-deactivating' ); + }, 1 ); + + // Move the background element back out + dom.wrapper.appendChild( dom.background ); + + // Clean up changes made to slides + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) { + transformElement( slide, '' ); + + slide.removeEventListener( 'click', onOverviewSlideClicked, true ); + } ); + + // Clean up changes made to backgrounds + toArray( dom.background.querySelectorAll( '.slide-background' ) ).forEach( function( background ) { + transformElement( background, '' ); + } ); + + transformSlides( { overview: '' } ); + + slide( indexh, indexv ); + + layout(); + + cueAutoSlide(); + + // Notify observers of the overview hiding + dispatchEvent( 'overviewhidden', { + 'indexh': indexh, + 'indexv': indexv, + 'currentSlide': currentSlide + } ); + + } + } + + /** + * Toggles the slide overview mode on and off. + * + * @param {Boolean} [override] Flag which overrides the + * toggle logic and forcibly sets the desired state. True means + * overview is open, false means it's closed. + */ + function toggleOverview( override ) { + + if( typeof override === 'boolean' ) { + override ? activateOverview() : deactivateOverview(); + } + else { + isOverview() ? deactivateOverview() : activateOverview(); + } + + } + + /** + * Checks if the overview is currently active. + * + * @return {Boolean} true if the overview is active, + * false otherwise + */ + function isOverview() { + + return overview; + + } + + /** + * Return a hash URL that will resolve to the given slide location. + * + * @param {HTMLElement} [slide=currentSlide] The slide to link to + */ + function locationHash( slide ) { + + var url = '/'; + + // Attempt to create a named link based on the slide's ID + var s = slide || currentSlide; + var id = s ? s.getAttribute( 'id' ) : null; + if( id ) { + id = encodeURIComponent( id ); + } + + var index = getIndices( slide ); + if( !config.fragmentInURL ) { + index.f = undefined; + } + + // If the current slide has an ID, use that as a named link, + // but we don't support named links with a fragment index + if( typeof id === 'string' && id.length && index.f === undefined ) { + url = '/' + id; + } + // Otherwise use the /h/v index + else { + var hashIndexBase = config.hashOneBasedIndex ? 1 : 0; + if( index.h > 0 || index.v > 0 || index.f !== undefined ) url += index.h + hashIndexBase; + if( index.v > 0 || index.f !== undefined ) url += '/' + (index.v + hashIndexBase ); + if( index.f !== undefined ) url += '/' + index.f; + } + + return url; + + } + + /** + * Checks if the current or specified slide is vertical + * (nested within another slide). + * + * @param {HTMLElement} [slide=currentSlide] The slide to check + * orientation of + * @return {Boolean} + */ + function isVerticalSlide( slide ) { + + // Prefer slide argument, otherwise use current slide + slide = slide ? slide : currentSlide; + + return slide && slide.parentNode && !!slide.parentNode.nodeName.match( /section/i ); + + } + + /** + * Handling the fullscreen functionality via the fullscreen API + * + * @see http://fullscreen.spec.whatwg.org/ + * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode + */ + function enterFullscreen() { + + var element = document.documentElement; + + // Check which implementation is available + var requestMethod = element.requestFullscreen || + element.webkitRequestFullscreen || + element.webkitRequestFullScreen || + element.mozRequestFullScreen || + element.msRequestFullscreen; + + if( requestMethod ) { + requestMethod.apply( element ); + } + + } + + /** + * Shows the mouse pointer after it has been hidden with + * #hideCursor. + */ + function showCursor() { + + if( cursorHidden ) { + cursorHidden = false; + dom.wrapper.style.cursor = ''; + } + + } + + /** + * Hides the mouse pointer when it's on top of the .reveal + * container. + */ + function hideCursor() { + + if( cursorHidden === false ) { + cursorHidden = true; + dom.wrapper.style.cursor = 'none'; + } + + } + + /** + * Enters the paused mode which fades everything on screen to + * black. + */ + function pause() { + + if( config.pause ) { + var wasPaused = dom.wrapper.classList.contains( 'paused' ); + + cancelAutoSlide(); + dom.wrapper.classList.add( 'paused' ); + + if( wasPaused === false ) { + dispatchEvent( 'paused' ); + } + } + + } + + /** + * Exits from the paused mode. + */ + function resume() { + + var wasPaused = dom.wrapper.classList.contains( 'paused' ); + dom.wrapper.classList.remove( 'paused' ); + + cueAutoSlide(); + + if( wasPaused ) { + dispatchEvent( 'resumed' ); + } + + } + + /** + * Toggles the paused mode on and off. + */ + function togglePause( override ) { + + if( typeof override === 'boolean' ) { + override ? pause() : resume(); + } + else { + isPaused() ? resume() : pause(); + } + + } + + /** + * Checks if we are currently in the paused mode. + * + * @return {Boolean} + */ + function isPaused() { + + return dom.wrapper.classList.contains( 'paused' ); + + } + + /** + * Toggles the auto slide mode on and off. + * + * @param {Boolean} [override] Flag which sets the desired state. + * True means autoplay starts, false means it stops. + */ + + function toggleAutoSlide( override ) { + + if( typeof override === 'boolean' ) { + override ? resumeAutoSlide() : pauseAutoSlide(); + } + + else { + autoSlidePaused ? resumeAutoSlide() : pauseAutoSlide(); + } + + } + + /** + * Checks if the auto slide mode is currently on. + * + * @return {Boolean} + */ + function isAutoSliding() { + + return !!( autoSlide && !autoSlidePaused ); + + } + + /** + * Steps from the current point in the presentation to the + * slide which matches the specified horizontal and vertical + * indices. + * + * @param {number} [h=indexh] Horizontal index of the target slide + * @param {number} [v=indexv] Vertical index of the target slide + * @param {number} [f] Index of a fragment within the + * target slide to activate + * @param {number} [o] Origin for use in multimaster environments + */ + function slide( h, v, f, o ) { + + // Remember where we were at before + previousSlide = currentSlide; + + // Query all horizontal slides in the deck + var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ); + + // Abort if there are no slides + if( horizontalSlides.length === 0 ) return; + + // If no vertical index is specified and the upcoming slide is a + // stack, resume at its previous vertical index + if( v === undefined && !isOverview() ) { + v = getPreviousVerticalIndex( horizontalSlides[ h ] ); + } + + // If we were on a vertical stack, remember what vertical index + // it was on so we can resume at the same position when returning + if( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) { + setPreviousVerticalIndex( previousSlide.parentNode, indexv ); + } + + // Remember the state before this slide + var stateBefore = state.concat(); + + // Reset the state array + state.length = 0; + + var indexhBefore = indexh || 0, + indexvBefore = indexv || 0; + + // Activate and transition to the new slide + indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h ); + indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v ); + + // Update the visibility of slides now that the indices have changed + updateSlidesVisibility(); + + layout(); + + // Update the overview if it's currently active + if( isOverview() ) { + updateOverview(); + } + + // Find the current horizontal slide and any possible vertical slides + // within it + var currentHorizontalSlide = horizontalSlides[ indexh ], + currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' ); + + // Store references to the previous and current slides + currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide; + + // Show fragment, if specified + if( typeof f !== 'undefined' ) { + navigateFragment( f ); + } + + // Dispatch an event if the slide changed + var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore ); + if (!slideChanged) { + // Ensure that the previous slide is never the same as the current + previousSlide = null; + } + + // Solves an edge case where the previous slide maintains the + // 'present' class when navigating between adjacent vertical + // stacks + if( previousSlide && previousSlide !== currentSlide ) { + previousSlide.classList.remove( 'present' ); + previousSlide.setAttribute( 'aria-hidden', 'true' ); + + // Reset all slides upon navigate to home + // Issue: #285 + if ( dom.wrapper.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) { + // Launch async task + setTimeout( function () { + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i; + for( i in slides ) { + if( slides[i] ) { + // Reset stack + setPreviousVerticalIndex( slides[i], 0 ); + } + } + }, 0 ); + } + } + + // Apply the new state + stateLoop: for( var i = 0, len = state.length; i < len; i++ ) { + // Check if this state existed on the previous slide. If it + // did, we will avoid adding it repeatedly + for( var j = 0; j < stateBefore.length; j++ ) { + if( stateBefore[j] === state[i] ) { + stateBefore.splice( j, 1 ); + continue stateLoop; + } + } + + document.documentElement.classList.add( state[i] ); + + // Dispatch custom event matching the state's name + dispatchEvent( state[i] ); + } + + // Clean up the remains of the previous state + while( stateBefore.length ) { + document.documentElement.classList.remove( stateBefore.pop() ); + } + + if( slideChanged ) { + dispatchEvent( 'slidechanged', { + 'indexh': indexh, + 'indexv': indexv, + 'previousSlide': previousSlide, + 'currentSlide': currentSlide, + 'origin': o + } ); + } + + // Handle embedded content + if( slideChanged || !previousSlide ) { + stopEmbeddedContent( previousSlide ); + startEmbeddedContent( currentSlide ); + } + + // Announce the current slide contents, for screen readers + dom.statusDiv.textContent = getStatusText( currentSlide ); + + updateControls(); + updateProgress(); + updateBackground(); + updateParallax(); + updateSlideNumber(); + updateNotes(); + updateFragments(); + + // Update the URL hash + writeURL(); + + cueAutoSlide(); + + } + + /** + * Syncs the presentation with the current DOM. Useful + * when new slides or control elements are added or when + * the configuration has changed. + */ + function sync() { + + // Subscribe to input + removeEventListeners(); + addEventListeners(); + + // Force a layout to make sure the current config is accounted for + layout(); + + // Reflect the current autoSlide value + autoSlide = config.autoSlide; + + // Start auto-sliding if it's enabled + cueAutoSlide(); + + // Re-create the slide backgrounds + createBackgrounds(); + + // Write the current hash to the URL + writeURL(); + + sortAllFragments(); + + updateControls(); + updateProgress(); + updateSlideNumber(); + updateSlidesVisibility(); + updateBackground( true ); + updateNotesVisibility(); + updateNotes(); + + formatEmbeddedContent(); + + // Start or stop embedded content depending on global config + if( config.autoPlayMedia === false ) { + stopEmbeddedContent( currentSlide, { unloadIframes: false } ); + } + else { + startEmbeddedContent( currentSlide ); + } + + if( isOverview() ) { + layoutOverview(); + } + + } + + /** + * Updates reveal.js to keep in sync with new slide attributes. For + * example, if you add a new `data-background-image` you can call + * this to have reveal.js render the new background image. + * + * Similar to #sync() but more efficient when you only need to + * refresh a specific slide. + * + * @param {HTMLElement} slide + */ + function syncSlide( slide ) { + + // Default to the current slide + slide = slide || currentSlide; + + syncBackground( slide ); + syncFragments( slide ); + + loadSlide( slide ); + + updateBackground(); + updateNotes(); + + } + + /** + * Formats the fragments on the given slide so that they have + * valid indices. Call this if fragments are changed in the DOM + * after reveal.js has already initialized. + * + * @param {HTMLElement} slide + * @return {Array} a list of the HTML fragments that were synced + */ + function syncFragments( slide ) { + + // Default to the current slide + slide = slide || currentSlide; + + return sortFragments( slide.querySelectorAll( '.fragment' ) ); + + } + + /** + * Resets all vertical slides so that only the first + * is visible. + */ + function resetVerticalSlides() { + + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + horizontalSlides.forEach( function( horizontalSlide ) { + + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + verticalSlides.forEach( function( verticalSlide, y ) { + + if( y > 0 ) { + verticalSlide.classList.remove( 'present' ); + verticalSlide.classList.remove( 'past' ); + verticalSlide.classList.add( 'future' ); + verticalSlide.setAttribute( 'aria-hidden', 'true' ); + } + + } ); + + } ); + + } + + /** + * Sorts and formats all of fragments in the + * presentation. + */ + function sortAllFragments() { + + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + horizontalSlides.forEach( function( horizontalSlide ) { + + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + verticalSlides.forEach( function( verticalSlide, y ) { + + sortFragments( verticalSlide.querySelectorAll( '.fragment' ) ); + + } ); + + if( verticalSlides.length === 0 ) sortFragments( horizontalSlide.querySelectorAll( '.fragment' ) ); + + } ); + + } + + /** + * Randomly shuffles all slides in the deck. + */ + function shuffle() { + + var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + slides.forEach( function( slide ) { + + // Insert this slide next to another random slide. This may + // cause the slide to insert before itself but that's fine. + dom.slides.insertBefore( slide, slides[ Math.floor( Math.random() * slides.length ) ] ); + + } ); + + } + + /** + * Updates one dimension of slides by showing the slide + * with the specified index. + * + * @param {string} selector A CSS selector that will fetch + * the group of slides we are working with + * @param {number} index The index of the slide that should be + * shown + * + * @return {number} The index of the slide that is now shown, + * might differ from the passed in index if it was out of + * bounds. + */ + function updateSlides( selector, index ) { + + // Select all slides and convert the NodeList result to + // an array + var slides = toArray( dom.wrapper.querySelectorAll( selector ) ), + slidesLength = slides.length; + + var printMode = isPrintingPDF(); + + if( slidesLength ) { + + // Should the index loop? + if( config.loop ) { + index %= slidesLength; + + if( index < 0 ) { + index = slidesLength + index; + } + } + + // Enforce max and minimum index bounds + index = Math.max( Math.min( index, slidesLength - 1 ), 0 ); + + for( var i = 0; i < slidesLength; i++ ) { + var element = slides[i]; + + var reverse = config.rtl && !isVerticalSlide( element ); + + element.classList.remove( 'past' ); + element.classList.remove( 'present' ); + element.classList.remove( 'future' ); + + // http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute + element.setAttribute( 'hidden', '' ); + element.setAttribute( 'aria-hidden', 'true' ); + + // If this element contains vertical slides + if( element.querySelector( 'section' ) ) { + element.classList.add( 'stack' ); + } + + // If we're printing static slides, all slides are "present" + if( printMode ) { + element.classList.add( 'present' ); + continue; + } + + if( i < index ) { + // Any element previous to index is given the 'past' class + element.classList.add( reverse ? 'future' : 'past' ); + + if( config.fragments ) { + // Show all fragments in prior slides + toArray( element.querySelectorAll( '.fragment' ) ).forEach( function( fragment ) { + fragment.classList.add( 'visible' ); + fragment.classList.remove( 'current-fragment' ); + } ); + } + } + else if( i > index ) { + // Any element subsequent to index is given the 'future' class + element.classList.add( reverse ? 'past' : 'future' ); + + if( config.fragments ) { + // Hide all fragments in future slides + toArray( element.querySelectorAll( '.fragment.visible' ) ).forEach( function( fragment ) { + fragment.classList.remove( 'visible' ); + fragment.classList.remove( 'current-fragment' ); + } ); + } + } + } + + // Mark the current slide as present + slides[index].classList.add( 'present' ); + slides[index].removeAttribute( 'hidden' ); + slides[index].removeAttribute( 'aria-hidden' ); + + // If this slide has a state associated with it, add it + // onto the current state of the deck + var slideState = slides[index].getAttribute( 'data-state' ); + if( slideState ) { + state = state.concat( slideState.split( ' ' ) ); + } + + } + else { + // Since there are no slides we can't be anywhere beyond the + // zeroth index + index = 0; + } + + return index; + + } + + /** + * Optimization method; hide all slides that are far away + * from the present slide. + */ + function updateSlidesVisibility() { + + // Select all slides and convert the NodeList result to + // an array + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ), + horizontalSlidesLength = horizontalSlides.length, + distanceX, + distanceY; + + if( horizontalSlidesLength && typeof indexh !== 'undefined' ) { + + // The number of steps away from the present slide that will + // be visible + var viewDistance = isOverview() ? 10 : config.viewDistance; + + // Shorten the view distance on devices that typically have + // less resources + if( isMobileDevice ) { + viewDistance = isOverview() ? 6 : config.mobileViewDistance; + } + + // All slides need to be visible when exporting to PDF + if( isPrintingPDF() ) { + viewDistance = Number.MAX_VALUE; + } + + for( var x = 0; x < horizontalSlidesLength; x++ ) { + var horizontalSlide = horizontalSlides[x]; + + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ), + verticalSlidesLength = verticalSlides.length; + + // Determine how far away this slide is from the present + distanceX = Math.abs( ( indexh || 0 ) - x ) || 0; + + // If the presentation is looped, distance should measure + // 1 between the first and last slides + if( config.loop ) { + distanceX = Math.abs( ( ( indexh || 0 ) - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0; + } + + // Show the horizontal slide if it's within the view distance + if( distanceX < viewDistance ) { + loadSlide( horizontalSlide ); + } + else { + unloadSlide( horizontalSlide ); + } + + if( verticalSlidesLength ) { + + var oy = getPreviousVerticalIndex( horizontalSlide ); + + for( var y = 0; y < verticalSlidesLength; y++ ) { + var verticalSlide = verticalSlides[y]; + + distanceY = x === ( indexh || 0 ) ? Math.abs( ( indexv || 0 ) - y ) : Math.abs( y - oy ); + + if( distanceX + distanceY < viewDistance ) { + loadSlide( verticalSlide ); + } + else { + unloadSlide( verticalSlide ); + } + } + + } + } + + // Flag if there are ANY vertical slides, anywhere in the deck + if( hasVerticalSlides() ) { + dom.wrapper.classList.add( 'has-vertical-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-vertical-slides' ); + } + + // Flag if there are ANY horizontal slides, anywhere in the deck + if( hasHorizontalSlides() ) { + dom.wrapper.classList.add( 'has-horizontal-slides' ); + } + else { + dom.wrapper.classList.remove( 'has-horizontal-slides' ); + } + + } + + } + + /** + * Pick up notes from the current slide and display them + * to the viewer. + * + * @see {@link config.showNotes} + */ + function updateNotes() { + + if( config.showNotes && dom.speakerNotes && currentSlide && !isPrintingPDF() ) { + + dom.speakerNotes.innerHTML = getSlideNotes() || 'No notes on this slide.'; + + } + + } + + /** + * Updates the visibility of the speaker notes sidebar that + * is used to share annotated slides. The notes sidebar is + * only visible if showNotes is true and there are notes on + * one or more slides in the deck. + */ + function updateNotesVisibility() { + + if( config.showNotes && hasNotes() ) { + dom.wrapper.classList.add( 'show-notes' ); + } + else { + dom.wrapper.classList.remove( 'show-notes' ); + } + + } + + /** + * Checks if there are speaker notes for ANY slide in the + * presentation. + */ + function hasNotes() { + + return dom.slides.querySelectorAll( '[data-notes], aside.notes' ).length > 0; + + } + + /** + * Updates the progress bar to reflect the current slide. + */ + function updateProgress() { + + // Update progress if enabled + if( config.progress && dom.progressbar ) { + + dom.progressbar.style.width = getProgress() * dom.wrapper.offsetWidth + 'px'; + + } + + } + + + /** + * Updates the slide number to match the current slide. + */ + function updateSlideNumber() { + + // Update slide number if enabled + if( config.slideNumber && dom.slideNumber ) { + dom.slideNumber.innerHTML = getSlideNumber(); + } + + } + + /** + * Returns the HTML string corresponding to the current slide number, + * including formatting. + */ + function getSlideNumber( slide ) { + + var value; + var format = 'h.v'; + if( slide === undefined ) { + slide = currentSlide; + } + + if ( typeof config.slideNumber === 'function' ) { + value = config.slideNumber( slide ); + } else { + // Check if a custom number format is available + if( typeof config.slideNumber === 'string' ) { + format = config.slideNumber; + } + + // If there are ONLY vertical slides in this deck, always use + // a flattened slide number + if( !/c/.test( format ) && dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ).length === 1 ) { + format = 'c'; + } + + value = []; + switch( format ) { + case 'c': + value.push( getSlidePastCount( slide ) + 1 ); + break; + case 'c/t': + value.push( getSlidePastCount( slide ) + 1, '/', getTotalSlides() ); + break; + default: + var indices = getIndices( slide ); + value.push( indices.h + 1 ); + var sep = format === 'h/v' ? '/' : '.'; + if( isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 ); + } + } + + var url = '#' + locationHash( slide ); + return formatSlideNumber( value[0], value[1], value[2], url ); + + } + + /** + * Applies HTML formatting to a slide number before it's + * written to the DOM. + * + * @param {number} a Current slide + * @param {string} delimiter Character to separate slide numbers + * @param {(number|*)} b Total slides + * @param {HTMLElement} [url='#'+locationHash()] The url to link to + * @return {string} HTML string fragment + */ + function formatSlideNumber( a, delimiter, b, url ) { + + if( url === undefined ) { + url = '#' + locationHash(); + } + if( typeof b === 'number' && !isNaN( b ) ) { + return '' + + ''+ a +'' + + ''+ delimiter +'' + + ''+ b +'' + + ''; + } + else { + return '' + + ''+ a +'' + + ''; + } + + } + + /** + * Updates the state of all control/navigation arrows. + */ + function updateControls() { + + var routes = availableRoutes(); + var fragments = availableFragments(); + + // Remove the 'enabled' class from all directions + dom.controlsLeft.concat( dom.controlsRight ) + .concat( dom.controlsUp ) + .concat( dom.controlsDown ) + .concat( dom.controlsPrev ) + .concat( dom.controlsNext ).forEach( function( node ) { + node.classList.remove( 'enabled' ); + node.classList.remove( 'fragmented' ); + + // Set 'disabled' attribute on all directions + node.setAttribute( 'disabled', 'disabled' ); + } ); + + // Add the 'enabled' class to the available routes; remove 'disabled' attribute to enable buttons + if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + + // Prev/next buttons + if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } ); + + // Highlight fragment directions + if( currentSlide ) { + + // Always apply fragment decorator to prev/next buttons + if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + + // Apply fragment decorators to directional buttons based on + // what slide axis they are in + if( isVerticalSlide( currentSlide ) ) { + if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + } + else { + if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } ); + } + + } + + if( config.controlsTutorial ) { + + // Highlight control arrows with an animation to ensure + // that the viewer knows how to navigate + if( !hasNavigatedDown && routes.down ) { + dom.controlsDownArrow.classList.add( 'highlight' ); + } + else { + dom.controlsDownArrow.classList.remove( 'highlight' ); + + if( !hasNavigatedRight && routes.right && indexv === 0 ) { + dom.controlsRightArrow.classList.add( 'highlight' ); + } + else { + dom.controlsRightArrow.classList.remove( 'highlight' ); + } + } + + } + + } + + /** + * Updates the background elements to reflect the current + * slide. + * + * @param {boolean} includeAll If true, the backgrounds of + * all vertical slides (not just the present) will be updated. + */ + function updateBackground( includeAll ) { + + var currentBackground = null; + + // Reverse past/future classes when in RTL mode + var horizontalPast = config.rtl ? 'future' : 'past', + horizontalFuture = config.rtl ? 'past' : 'future'; + + // Update the classes of all backgrounds to match the + // states of their slides (past/present/future) + toArray( dom.background.childNodes ).forEach( function( backgroundh, h ) { + + backgroundh.classList.remove( 'past' ); + backgroundh.classList.remove( 'present' ); + backgroundh.classList.remove( 'future' ); + + if( h < indexh ) { + backgroundh.classList.add( horizontalPast ); + } + else if ( h > indexh ) { + backgroundh.classList.add( horizontalFuture ); + } + else { + backgroundh.classList.add( 'present' ); + + // Store a reference to the current background element + currentBackground = backgroundh; + } + + if( includeAll || h === indexh ) { + toArray( backgroundh.querySelectorAll( '.slide-background' ) ).forEach( function( backgroundv, v ) { + + backgroundv.classList.remove( 'past' ); + backgroundv.classList.remove( 'present' ); + backgroundv.classList.remove( 'future' ); + + if( v < indexv ) { + backgroundv.classList.add( 'past' ); + } + else if ( v > indexv ) { + backgroundv.classList.add( 'future' ); + } + else { + backgroundv.classList.add( 'present' ); + + // Only if this is the present horizontal and vertical slide + if( h === indexh ) currentBackground = backgroundv; + } + + } ); + } + + } ); + + // Stop content inside of previous backgrounds + if( previousBackground ) { + + stopEmbeddedContent( previousBackground, { unloadIframes: !shouldPreload( previousBackground ) } ); + + } + + // Start content in the current background + if( currentBackground ) { + + startEmbeddedContent( currentBackground ); + + var currentBackgroundContent = currentBackground.querySelector( '.slide-background-content' ); + if( currentBackgroundContent ) { + + var backgroundImageURL = currentBackgroundContent.style.backgroundImage || ''; + + // Restart GIFs (doesn't work in Firefox) + if( /\.gif/i.test( backgroundImageURL ) ) { + currentBackgroundContent.style.backgroundImage = ''; + window.getComputedStyle( currentBackgroundContent ).opacity; + currentBackgroundContent.style.backgroundImage = backgroundImageURL; + } + + } + + // Don't transition between identical backgrounds. This + // prevents unwanted flicker. + var previousBackgroundHash = previousBackground ? previousBackground.getAttribute( 'data-background-hash' ) : null; + var currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' ); + if( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== previousBackground ) { + dom.background.classList.add( 'no-transition' ); + } + + previousBackground = currentBackground; + + } + + // If there's a background brightness flag for this slide, + // bubble it to the .reveal container + if( currentSlide ) { + [ 'has-light-background', 'has-dark-background' ].forEach( function( classToBubble ) { + if( currentSlide.classList.contains( classToBubble ) ) { + dom.wrapper.classList.add( classToBubble ); + } + else { + dom.wrapper.classList.remove( classToBubble ); + } + } ); + } + + // Allow the first background to apply without transition + setTimeout( function() { + dom.background.classList.remove( 'no-transition' ); + }, 1 ); + + } + + /** + * Updates the position of the parallax background based + * on the current slide index. + */ + function updateParallax() { + + if( config.parallaxBackgroundImage ) { + + var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ), + verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); + + var backgroundSize = dom.background.style.backgroundSize.split( ' ' ), + backgroundWidth, backgroundHeight; + + if( backgroundSize.length === 1 ) { + backgroundWidth = backgroundHeight = parseInt( backgroundSize[0], 10 ); + } + else { + backgroundWidth = parseInt( backgroundSize[0], 10 ); + backgroundHeight = parseInt( backgroundSize[1], 10 ); + } + + var slideWidth = dom.background.offsetWidth, + horizontalSlideCount = horizontalSlides.length, + horizontalOffsetMultiplier, + horizontalOffset; + + if( typeof config.parallaxBackgroundHorizontal === 'number' ) { + horizontalOffsetMultiplier = config.parallaxBackgroundHorizontal; + } + else { + horizontalOffsetMultiplier = horizontalSlideCount > 1 ? ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) : 0; + } + + horizontalOffset = horizontalOffsetMultiplier * indexh * -1; + + var slideHeight = dom.background.offsetHeight, + verticalSlideCount = verticalSlides.length, + verticalOffsetMultiplier, + verticalOffset; + + if( typeof config.parallaxBackgroundVertical === 'number' ) { + verticalOffsetMultiplier = config.parallaxBackgroundVertical; + } + else { + verticalOffsetMultiplier = ( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ); + } + + verticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indexv : 0; + + dom.background.style.backgroundPosition = horizontalOffset + 'px ' + -verticalOffset + 'px'; + + } + + } + + /** + * Should the given element be preloaded? + * Decides based on local element attributes and global config. + * + * @param {HTMLElement} element + */ + function shouldPreload( element ) { + + // Prefer an explicit global preload setting + var preload = config.preloadIframes; + + // If no global setting is available, fall back on the element's + // own preload setting + if( typeof preload !== 'boolean' ) { + preload = element.hasAttribute( 'data-preload' ); + } + + return preload; + } + + /** + * Called when the given slide is within the configured view + * distance. Shows the slide element and loads any content + * that is set to load lazily (data-src). + * + * @param {HTMLElement} slide Slide to show + */ + function loadSlide( slide, options ) { + + options = options || {}; + + // Show the slide element + slide.style.display = config.display; + + // Media elements with data-src attributes + toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( function( element ) { + if( element.tagName !== 'IFRAME' || shouldPreload( element ) ) { + element.setAttribute( 'src', element.getAttribute( 'data-src' ) ); + element.setAttribute( 'data-lazy-loaded', '' ); + element.removeAttribute( 'data-src' ); + } + } ); + + // Media elements with children + toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( media ) { + var sources = 0; + + toArray( media.querySelectorAll( 'source[data-src]' ) ).forEach( function( source ) { + source.setAttribute( 'src', source.getAttribute( 'data-src' ) ); + source.removeAttribute( 'data-src' ); + source.setAttribute( 'data-lazy-loaded', '' ); + sources += 1; + } ); + + // If we rewrote sources for this video/audio element, we need + // to manually tell it to load from its new origin + if( sources > 0 ) { + media.load(); + } + } ); + + + // Show the corresponding background element + var background = slide.slideBackgroundElement; + if( background ) { + background.style.display = 'block'; + + var backgroundContent = slide.slideBackgroundContentElement; + var backgroundIframe = slide.getAttribute( 'data-background-iframe' ); + + // If the background contains media, load it + if( background.hasAttribute( 'data-loaded' ) === false ) { + background.setAttribute( 'data-loaded', 'true' ); + + var backgroundImage = slide.getAttribute( 'data-background-image' ), + backgroundVideo = slide.getAttribute( 'data-background-video' ), + backgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ), + backgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' ); + + // Images + if( backgroundImage ) { + backgroundContent.style.backgroundImage = 'url('+ encodeURI( backgroundImage ) +')'; + } + // Videos + else if ( backgroundVideo && !isSpeakerNotes() ) { + var video = document.createElement( 'video' ); + + if( backgroundVideoLoop ) { + video.setAttribute( 'loop', '' ); + } + + if( backgroundVideoMuted ) { + video.muted = true; + } + + // Inline video playback works (at least in Mobile Safari) as + // long as the video is muted and the `playsinline` attribute is + // present + if( isMobileDevice ) { + video.muted = true; + video.autoplay = true; + video.setAttribute( 'playsinline', '' ); + } + + // Support comma separated lists of video sources + backgroundVideo.split( ',' ).forEach( function( source ) { + video.innerHTML += ''; + } ); + + backgroundContent.appendChild( video ); + } + // Iframes + else if( backgroundIframe && options.excludeIframes !== true ) { + var iframe = document.createElement( 'iframe' ); + iframe.setAttribute( 'allowfullscreen', '' ); + iframe.setAttribute( 'mozallowfullscreen', '' ); + iframe.setAttribute( 'webkitallowfullscreen', '' ); + iframe.setAttribute( 'allow', 'autoplay' ); + + iframe.setAttribute( 'data-src', backgroundIframe ); + + iframe.style.width = '100%'; + iframe.style.height = '100%'; + iframe.style.maxHeight = '100%'; + iframe.style.maxWidth = '100%'; + + backgroundContent.appendChild( iframe ); + } + } + + // Start loading preloadable iframes + var backgroundIframeElement = backgroundContent.querySelector( 'iframe[data-src]' ); + if( backgroundIframeElement ) { + + // Check if this iframe is eligible to be preloaded + if( shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) { + if( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) { + backgroundIframeElement.setAttribute( 'src', backgroundIframe ); + } + } + + } + + } + + } + + /** + * Unloads and hides the given slide. This is called when the + * slide is moved outside of the configured view distance. + * + * @param {HTMLElement} slide + */ + function unloadSlide( slide ) { + + // Hide the slide element + slide.style.display = 'none'; + + // Hide the corresponding background element + var background = getSlideBackground( slide ); + if( background ) { + background.style.display = 'none'; + + // Unload any background iframes + toArray( background.querySelectorAll( 'iframe[src]' ) ).forEach( function( element ) { + element.removeAttribute( 'src' ); + } ); + } + + // Reset lazy-loaded media elements with src attributes + toArray( slide.querySelectorAll( 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]' ) ).forEach( function( element ) { + element.setAttribute( 'data-src', element.getAttribute( 'src' ) ); + element.removeAttribute( 'src' ); + } ); + + // Reset lazy-loaded media elements with children + toArray( slide.querySelectorAll( 'video[data-lazy-loaded] source[src], audio source[src]' ) ).forEach( function( source ) { + source.setAttribute( 'data-src', source.getAttribute( 'src' ) ); + source.removeAttribute( 'src' ); + } ); + + } + + /** + * Determine what available routes there are for navigation. + * + * @return {{left: boolean, right: boolean, up: boolean, down: boolean}} + */ + function availableRoutes() { + + var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ), + verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR ); + + var routes = { + left: indexh > 0, + right: indexh < horizontalSlides.length - 1, + up: indexv > 0, + down: indexv < verticalSlides.length - 1 + }; + + // Looped presentations can always be navigated as long as + // there are slides available + if( config.loop ) { + if( horizontalSlides.length > 1 ) { + routes.left = true; + routes.right = true; + } + + if( verticalSlides.length > 1 ) { + routes.up = true; + routes.down = true; + } + } + + // Reverse horizontal controls for rtl + if( config.rtl ) { + var left = routes.left; + routes.left = routes.right; + routes.right = left; + } + + return routes; + + } + + /** + * Returns an object describing the available fragment + * directions. + * + * @return {{prev: boolean, next: boolean}} + */ + function availableFragments() { + + if( currentSlide && config.fragments ) { + var fragments = currentSlide.querySelectorAll( '.fragment' ); + var hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' ); + + return { + prev: fragments.length - hiddenFragments.length > 0, + next: !!hiddenFragments.length + }; + } + else { + return { prev: false, next: false }; + } + + } + + /** + * Enforces origin-specific format rules for embedded media. + */ + function formatEmbeddedContent() { + + var _appendParamToIframeSource = function( sourceAttribute, sourceURL, param ) { + toArray( dom.slides.querySelectorAll( 'iframe['+ sourceAttribute +'*="'+ sourceURL +'"]' ) ).forEach( function( el ) { + var src = el.getAttribute( sourceAttribute ); + if( src && src.indexOf( param ) === -1 ) { + el.setAttribute( sourceAttribute, src + ( !/\?/.test( src ) ? '?' : '&' ) + param ); + } + }); + }; + + // YouTube frames must include "?enablejsapi=1" + _appendParamToIframeSource( 'src', 'youtube.com/embed/', 'enablejsapi=1' ); + _appendParamToIframeSource( 'data-src', 'youtube.com/embed/', 'enablejsapi=1' ); + + // Vimeo frames must include "?api=1" + _appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' ); + _appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' ); + + } + + /** + * Start playback of any embedded content inside of + * the given element. + * + * @param {HTMLElement} element + */ + function startEmbeddedContent( element ) { + + if( element && !isSpeakerNotes() ) { + + // Restart GIFs + toArray( element.querySelectorAll( 'img[src$=".gif"]' ) ).forEach( function( el ) { + // Setting the same unchanged source like this was confirmed + // to work in Chrome, FF & Safari + el.setAttribute( 'src', el.getAttribute( 'src' ) ); + } ); + + // HTML5 media elements + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = el.hasAttribute( 'data-autoplay' ) || !!closestParent( el, '.slide-background' ); + } + + if( autoplay && typeof el.play === 'function' ) { + + // If the media is ready, start playback + if( el.readyState > 1 ) { + startEmbeddedMedia( { target: el } ); + } + // Mobile devices never fire a loaded event so instead + // of waiting, we initiate playback + else if( isMobileDevice ) { + var promise = el.play(); + + // If autoplay does not work, ensure that the controls are visible so + // that the viewer can start the media on their own + if( promise && typeof promise.catch === 'function' && el.controls === false ) { + promise.catch( function() { + el.controls = true; + + // Once the video does start playing, hide the controls again + el.addEventListener( 'play', function() { + el.controls = false; + } ); + } ); + } + } + // If the media isn't loaded, wait before playing + else { + el.removeEventListener( 'loadeddata', startEmbeddedMedia ); // remove first to avoid dupes + el.addEventListener( 'loadeddata', startEmbeddedMedia ); + } + + } + } ); + + // Normal iframes + toArray( element.querySelectorAll( 'iframe[src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + startEmbeddedIframe( { target: el } ); + } ); + + // Lazy loading iframes + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + if( closestParent( el, '.fragment' ) && !closestParent( el, '.fragment.visible' ) ) { + return; + } + + if( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) { + el.removeEventListener( 'load', startEmbeddedIframe ); // remove first to avoid dupes + el.addEventListener( 'load', startEmbeddedIframe ); + el.setAttribute( 'src', el.getAttribute( 'data-src' ) ); + } + } ); + + } + + } + + /** + * Starts playing an embedded video/audio element after + * it has finished loading. + * + * @param {object} event + */ + function startEmbeddedMedia( event ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + event.target.currentTime = 0; + event.target.play(); + } + + event.target.removeEventListener( 'loadeddata', startEmbeddedMedia ); + + } + + /** + * "Starts" the content of an embedded iframe using the + * postMessage API. + * + * @param {object} event + */ + function startEmbeddedIframe( event ) { + + var iframe = event.target; + + if( iframe && iframe.contentWindow ) { + + var isAttachedToDOM = !!closestParent( event.target, 'html' ), + isVisible = !!closestParent( event.target, '.present' ); + + if( isAttachedToDOM && isVisible ) { + + // Prefer an explicit global autoplay setting + var autoplay = config.autoPlayMedia; + + // If no global setting is available, fall back on the element's + // own autoplay setting + if( typeof autoplay !== 'boolean' ) { + autoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closestParent( iframe, '.slide-background' ); + } + + // YouTube postMessage API + if( /youtube\.com\/embed\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' ); + } + // Vimeo postMessage API + else if( /player\.vimeo\.com\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) { + iframe.contentWindow.postMessage( '{"method":"play"}', '*' ); + } + // Generic postMessage API + else { + iframe.contentWindow.postMessage( 'slide:start', '*' ); + } + + } + + } + + } + + /** + * Stop playback of any embedded content inside of + * the targeted slide. + * + * @param {HTMLElement} element + */ + function stopEmbeddedContent( element, options ) { + + options = extend( { + // Defaults + unloadIframes: true + }, options || {} ); + + if( element && element.parentNode ) { + // HTML5 media elements + toArray( element.querySelectorAll( 'video, audio' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) { + el.setAttribute('data-paused-by-reveal', ''); + el.pause(); + } + } ); + + // Generic postMessage API for non-lazy loaded iframes + toArray( element.querySelectorAll( 'iframe' ) ).forEach( function( el ) { + if( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' ); + el.removeEventListener( 'load', startEmbeddedIframe ); + }); + + // YouTube postMessage API + toArray( element.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { + el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' ); + } + }); + + // Vimeo postMessage API + toArray( element.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) { + if( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) { + el.contentWindow.postMessage( '{"method":"pause"}', '*' ); + } + }); + + if( options.unloadIframes === true ) { + // Unload lazy-loaded iframes + toArray( element.querySelectorAll( 'iframe[data-src]' ) ).forEach( function( el ) { + // Only removing the src doesn't actually unload the frame + // in all browsers (Firefox) so we set it to blank first + el.setAttribute( 'src', 'about:blank' ); + el.removeAttribute( 'src' ); + } ); + } + } + + } + + /** + * Returns the number of past slides. This can be used as a global + * flattened index for slides. + * + * @param {HTMLElement} [slide=currentSlide] The slide we're counting before + * + * @return {number} Past slide count + */ + function getSlidePastCount( slide ) { + + if( slide === undefined ) { + slide = currentSlide; + } + + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + // The number of past slides + var pastCount = 0; + + // Step through all slides and count the past ones + mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) { + + var horizontalSlide = horizontalSlides[i]; + var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ); + + for( var j = 0; j < verticalSlides.length; j++ ) { + + // Stop as soon as we arrive at the present + if( verticalSlides[j] === slide ) { + break mainLoop; + } + + pastCount++; + + } + + // Stop as soon as we arrive at the present + if( horizontalSlide === slide ) { + break; + } + + // Don't count the wrapping section for vertical slides + if( horizontalSlide.classList.contains( 'stack' ) === false ) { + pastCount++; + } + + } + + return pastCount; + + } + + /** + * Returns a value ranging from 0-1 that represents + * how far into the presentation we have navigated. + * + * @return {number} + */ + function getProgress() { + + // The number of past and total slides + var totalCount = getTotalSlides(); + var pastCount = getSlidePastCount(); + + if( currentSlide ) { + + var allFragments = currentSlide.querySelectorAll( '.fragment' ); + + // If there are fragments in the current slide those should be + // accounted for in the progress. + if( allFragments.length > 0 ) { + var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' ); + + // This value represents how big a portion of the slide progress + // that is made up by its fragments (0-1) + var fragmentWeight = 0.9; + + // Add fragment progress to the past slide count + pastCount += ( visibleFragments.length / allFragments.length ) * fragmentWeight; + } + + } + + return Math.min( pastCount / ( totalCount - 1 ), 1 ); + + } + + /** + * Checks if this presentation is running inside of the + * speaker notes window. + * + * @return {boolean} + */ + function isSpeakerNotes() { + + return !!window.location.search.match( /receiver/gi ); + + } + + /** + * Reads the current URL (hash) and navigates accordingly. + */ + function readURL() { + + var hash = window.location.hash; + + // Attempt to parse the hash as either an index or name + var bits = hash.slice( 2 ).split( '/' ), + name = hash.replace( /#|\//gi, '' ); + + // If the first bit is not fully numeric and there is a name we + // can assume that this is a named link + if( !/^[0-9]*$/.test( bits[0] ) && name.length ) { + var element; + + // Ensure the named link is a valid HTML ID attribute + try { + element = document.getElementById( decodeURIComponent( name ) ); + } + catch ( error ) { } + + // Ensure that we're not already on a slide with the same name + var isSameNameAsCurrentSlide = currentSlide ? currentSlide.getAttribute( 'id' ) === name : false; + + if( element ) { + // If the slide exists and is not the current slide... + if ( !isSameNameAsCurrentSlide ) { + // ...find the position of the named slide and navigate to it + var indices = Reveal.getIndices(element); + slide(indices.h, indices.v); + } + } + // If the slide doesn't exist, navigate to the current slide + else { + slide( indexh || 0, indexv || 0 ); + } + } + else { + var hashIndexBase = config.hashOneBasedIndex ? 1 : 0; + + // Read the index components of the hash + var h = ( parseInt( bits[0], 10 ) - hashIndexBase ) || 0, + v = ( parseInt( bits[1], 10 ) - hashIndexBase ) || 0, + f; + + if( config.fragmentInURL ) { + f = parseInt( bits[2], 10 ); + if( isNaN( f ) ) { + f = undefined; + } + } + + if( h !== indexh || v !== indexv || f !== undefined ) { + slide( h, v, f ); + } + } + + } + + /** + * Updates the page URL (hash) to reflect the current + * state. + * + * @param {number} delay The time in ms to wait before + * writing the hash + */ + function writeURL( delay ) { + + // Make sure there's never more than one timeout running + clearTimeout( writeURLTimeout ); + + // If a delay is specified, timeout this call + if( typeof delay === 'number' ) { + writeURLTimeout = setTimeout( writeURL, delay ); + } + else if( currentSlide ) { + // If we're configured to push to history OR the history + // API is not avaialble. + if( config.history || !window.history ) { + window.location.hash = locationHash(); + } + // If we're configured to reflect the current slide in the + // URL without pushing to history. + else if( config.hash ) { + window.history.replaceState( null, null, '#' + locationHash() ); + } + // If history and hash are both disabled, a hash may still + // be added to the URL by clicking on a href with a hash + // target. Counter this by always removing the hash. + else { + window.history.replaceState( null, null, window.location.pathname + window.location.search ); + } + } + + } + /** + * Retrieves the h/v location and fragment of the current, + * or specified, slide. + * + * @param {HTMLElement} [slide] If specified, the returned + * index will be for this slide rather than the currently + * active one + * + * @return {{h: number, v: number, f: number}} + */ + function getIndices( slide ) { + + // By default, return the current indices + var h = indexh, + v = indexv, + f; + + // If a slide is specified, return the indices of that slide + if( slide ) { + var isVertical = isVerticalSlide( slide ); + var slideh = isVertical ? slide.parentNode : slide; + + // Select all horizontal slides + var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + // Now that we know which the horizontal slide is, get its index + h = Math.max( horizontalSlides.indexOf( slideh ), 0 ); + + // Assume we're not vertical + v = undefined; + + // If this is a vertical slide, grab the vertical index + if( isVertical ) { + v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 ); + } + } + + if( !slide && currentSlide ) { + var hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0; + if( hasFragments ) { + var currentFragment = currentSlide.querySelector( '.current-fragment' ); + if( currentFragment && currentFragment.hasAttribute( 'data-fragment-index' ) ) { + f = parseInt( currentFragment.getAttribute( 'data-fragment-index' ), 10 ); + } + else { + f = currentSlide.querySelectorAll( '.fragment.visible' ).length - 1; + } + } + } + + return { h: h, v: v, f: f }; + + } + + /** + * Retrieves all slides in this presentation. + */ + function getSlides() { + + return toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ) ); + + } + + /** + * Returns a list of all horizontal slides in the deck. Each + * vertical stack is included as one horizontal slide in the + * resulting array. + */ + function getHorizontalSlides() { + + return toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ); + + } + + /** + * Returns all vertical slides that exist within this deck. + */ + function getVerticalSlides() { + + return toArray( dom.wrapper.querySelectorAll( '.slides>section>section' ) ); + + } + + /** + * Returns true if there are at least two horizontal slides. + */ + function hasHorizontalSlides() { + + return getHorizontalSlides().length > 1; + } + + /** + * Returns true if there are at least two vertical slides. + */ + function hasVerticalSlides() { + + return getVerticalSlides().length > 1; + + } + + /** + * Returns an array of objects where each object represents the + * attributes on its respective slide. + */ + function getSlidesAttributes() { + + return getSlides().map( function( slide ) { + + var attributes = {}; + for( var i = 0; i < slide.attributes.length; i++ ) { + var attribute = slide.attributes[ i ]; + attributes[ attribute.name ] = attribute.value; + } + return attributes; + + } ); + + } + + /** + * Retrieves the total number of slides in this presentation. + * + * @return {number} + */ + function getTotalSlides() { + + return getSlides().length; + + } + + /** + * Returns the slide element matching the specified index. + * + * @return {HTMLElement} + */ + function getSlide( x, y ) { + + var horizontalSlide = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR )[ x ]; + var verticalSlides = horizontalSlide && horizontalSlide.querySelectorAll( 'section' ); + + if( verticalSlides && verticalSlides.length && typeof y === 'number' ) { + return verticalSlides ? verticalSlides[ y ] : undefined; + } + + return horizontalSlide; + + } + + /** + * Returns the background element for the given slide. + * All slides, even the ones with no background properties + * defined, have a background element so as long as the + * index is valid an element will be returned. + * + * @param {mixed} x Horizontal background index OR a slide + * HTML element + * @param {number} y Vertical background index + * @return {(HTMLElement[]|*)} + */ + function getSlideBackground( x, y ) { + + var slide = typeof x === 'number' ? getSlide( x, y ) : x; + if( slide ) { + return slide.slideBackgroundElement; + } + + return undefined; + + } + + /** + * Retrieves the speaker notes from a slide. Notes can be + * defined in two ways: + * 1. As a data-notes attribute on the slide
+ * 2. As an