From fe0bea34dec211fef42554d300ff83abbc416e4e Mon Sep 17 00:00:00 2001 From: Leonid Parfentiev Date: Sat, 20 Jun 2020 20:58:52 +0300 Subject: [PATCH] Parfentev Leonid: lab1--7 done --- 8303/Parfentev_Leonid/lab1/Makefile | 16 + 8303/Parfentev_Leonid/lab1/catapult_units.hpp | 145 +++++ .../Parfentev_Leonid/lab1/common_policies.hpp | 82 +++ 8303/Parfentev_Leonid/lab1/demo.cpp | 202 ++++++ 8303/Parfentev_Leonid/lab1/demo.hpp | 8 + 8303/Parfentev_Leonid/lab1/main.cpp | 16 + 8303/Parfentev_Leonid/lab1/map.cpp | 50 ++ 8303/Parfentev_Leonid/lab1/map.hpp | 97 +++ 8303/Parfentev_Leonid/lab1/melee_units.hpp | 88 +++ 8303/Parfentev_Leonid/lab1/pathfinder.cpp | 60 ++ 8303/Parfentev_Leonid/lab1/pathfinder.hpp | 56 ++ 8303/Parfentev_Leonid/lab1/placeable.hpp | 34 ++ 8303/Parfentev_Leonid/lab1/point.cpp | 29 + 8303/Parfentev_Leonid/lab1/point.hpp | 40 ++ 8303/Parfentev_Leonid/lab1/ranged_units.hpp | 91 +++ 8303/Parfentev_Leonid/lab1/rectmap.cpp | 53 ++ 8303/Parfentev_Leonid/lab1/rectmap.hpp | 85 +++ 8303/Parfentev_Leonid/lab1/report.pdf | Bin 0 -> 132954 bytes 8303/Parfentev_Leonid/lab1/unit.cpp | 5 + 8303/Parfentev_Leonid/lab1/unit.hpp | 152 +++++ 8303/Parfentev_Leonid/lab2/Makefile | 19 + 8303/Parfentev_Leonid/lab2/base.cpp | 105 ++++ 8303/Parfentev_Leonid/lab2/base.hpp | 89 +++ 8303/Parfentev_Leonid/lab2/catapult_units.hpp | 145 +++++ .../Parfentev_Leonid/lab2/common_policies.hpp | 181 ++++++ 8303/Parfentev_Leonid/lab2/demo.cpp | 538 ++++++++++++++++ 8303/Parfentev_Leonid/lab2/demo.hpp | 11 + 8303/Parfentev_Leonid/lab2/event.cpp | 31 + 8303/Parfentev_Leonid/lab2/event.hpp | 117 ++++ 8303/Parfentev_Leonid/lab2/landscape.hpp | 25 + .../Parfentev_Leonid/lab2/landscape_types.hpp | 70 +++ 8303/Parfentev_Leonid/lab2/main.cpp | 25 + 8303/Parfentev_Leonid/lab2/map.cpp | 144 +++++ 8303/Parfentev_Leonid/lab2/map.hpp | 106 ++++ 8303/Parfentev_Leonid/lab2/melee_units.hpp | 88 +++ 8303/Parfentev_Leonid/lab2/neutral_object.hpp | 21 + .../lab2/neutral_object_types.hpp | 184 ++++++ .../Parfentev_Leonid/lab2/object_w_health.hpp | 30 + 8303/Parfentev_Leonid/lab2/pathfinder.cpp | 60 ++ 8303/Parfentev_Leonid/lab2/pathfinder.hpp | 56 ++ 8303/Parfentev_Leonid/lab2/placeable.hpp | 34 ++ 8303/Parfentev_Leonid/lab2/point.cpp | 29 + 8303/Parfentev_Leonid/lab2/point.hpp | 40 ++ 8303/Parfentev_Leonid/lab2/ranged_units.hpp | 94 +++ 8303/Parfentev_Leonid/lab2/rectmap.cpp | 55 ++ 8303/Parfentev_Leonid/lab2/rectmap.hpp | 95 +++ 8303/Parfentev_Leonid/lab2/report.pdf | Bin 0 -> 115828 bytes 8303/Parfentev_Leonid/lab2/unit.cpp | 5 + 8303/Parfentev_Leonid/lab2/unit.hpp | 253 ++++++++ 8303/Parfentev_Leonid/lab2/unit_factory.hpp | 24 + 8303/Parfentev_Leonid/lab3/Makefile | 21 + 8303/Parfentev_Leonid/lab3/base.cpp | 121 ++++ 8303/Parfentev_Leonid/lab3/base.hpp | 96 +++ 8303/Parfentev_Leonid/lab3/catapult_units.hpp | 145 +++++ .../Parfentev_Leonid/lab3/common_policies.hpp | 181 ++++++ 8303/Parfentev_Leonid/lab3/demo.cpp | 578 ++++++++++++++++++ 8303/Parfentev_Leonid/lab3/demo.hpp | 14 + 8303/Parfentev_Leonid/lab3/event.cpp | 31 + 8303/Parfentev_Leonid/lab3/event.hpp | 62 ++ 8303/Parfentev_Leonid/lab3/event_printer.hpp | 127 ++++ 8303/Parfentev_Leonid/lab3/event_types.hpp | 161 +++++ 8303/Parfentev_Leonid/lab3/game.cpp | 89 +++ 8303/Parfentev_Leonid/lab3/game.hpp | 49 ++ .../Parfentev_Leonid/lab3/iostream_player.cpp | 89 +++ .../Parfentev_Leonid/lab3/iostream_player.hpp | 252 ++++++++ 8303/Parfentev_Leonid/lab3/landscape.hpp | 25 + .../Parfentev_Leonid/lab3/landscape_types.hpp | 70 +++ 8303/Parfentev_Leonid/lab3/main.cpp | 34 ++ 8303/Parfentev_Leonid/lab3/map.cpp | 155 +++++ 8303/Parfentev_Leonid/lab3/map.hpp | 119 ++++ 8303/Parfentev_Leonid/lab3/mediator.cpp | 111 ++++ 8303/Parfentev_Leonid/lab3/mediator.hpp | 31 + 8303/Parfentev_Leonid/lab3/melee_units.hpp | 88 +++ 8303/Parfentev_Leonid/lab3/neutral_object.hpp | 22 + .../lab3/neutral_object_types.hpp | 187 ++++++ 8303/Parfentev_Leonid/lab3/object_print.cpp | 146 +++++ 8303/Parfentev_Leonid/lab3/object_print.hpp | 40 ++ .../Parfentev_Leonid/lab3/object_w_health.hpp | 30 + 8303/Parfentev_Leonid/lab3/pathfinder.cpp | 60 ++ 8303/Parfentev_Leonid/lab3/pathfinder.hpp | 56 ++ 8303/Parfentev_Leonid/lab3/placeable.hpp | 36 ++ 8303/Parfentev_Leonid/lab3/player.hpp | 25 + 8303/Parfentev_Leonid/lab3/point.cpp | 29 + 8303/Parfentev_Leonid/lab3/point.hpp | 40 ++ 8303/Parfentev_Leonid/lab3/ranged_units.hpp | 94 +++ 8303/Parfentev_Leonid/lab3/rectmap.cpp | 55 ++ 8303/Parfentev_Leonid/lab3/rectmap.hpp | 99 +++ 8303/Parfentev_Leonid/lab3/report.pdf | Bin 0 -> 109391 bytes 8303/Parfentev_Leonid/lab3/unit.cpp | 5 + 8303/Parfentev_Leonid/lab3/unit.hpp | 258 ++++++++ 8303/Parfentev_Leonid/lab3/unit_factory.hpp | 24 + .../lab3/zombie_collector.hpp | 36 ++ 8303/Parfentev_Leonid/lab4/Makefile | 21 + 8303/Parfentev_Leonid/lab4/base.cpp | 121 ++++ 8303/Parfentev_Leonid/lab4/base.hpp | 84 +++ 8303/Parfentev_Leonid/lab4/catapult_units.hpp | 145 +++++ .../Parfentev_Leonid/lab4/common_policies.hpp | 181 ++++++ 8303/Parfentev_Leonid/lab4/demo.cpp | 520 ++++++++++++++++ 8303/Parfentev_Leonid/lab4/demo.hpp | 14 + 8303/Parfentev_Leonid/lab4/event.cpp | 31 + 8303/Parfentev_Leonid/lab4/event.hpp | 62 ++ 8303/Parfentev_Leonid/lab4/event_printer.hpp | 131 ++++ 8303/Parfentev_Leonid/lab4/event_types.hpp | 161 +++++ 8303/Parfentev_Leonid/lab4/factory_table.hpp | 81 +++ 8303/Parfentev_Leonid/lab4/game.cpp | 100 +++ 8303/Parfentev_Leonid/lab4/game.hpp | 50 ++ .../Parfentev_Leonid/lab4/iostream_player.cpp | 101 +++ .../Parfentev_Leonid/lab4/iostream_player.hpp | 286 +++++++++ 8303/Parfentev_Leonid/lab4/landscape.hpp | 25 + .../Parfentev_Leonid/lab4/landscape_types.hpp | 70 +++ 8303/Parfentev_Leonid/lab4/logging.hpp | 46 ++ 8303/Parfentev_Leonid/lab4/main.cpp | 130 ++++ 8303/Parfentev_Leonid/lab4/map.cpp | 155 +++++ 8303/Parfentev_Leonid/lab4/map.hpp | 119 ++++ 8303/Parfentev_Leonid/lab4/mediator.cpp | 111 ++++ 8303/Parfentev_Leonid/lab4/mediator.hpp | 31 + 8303/Parfentev_Leonid/lab4/melee_units.hpp | 88 +++ 8303/Parfentev_Leonid/lab4/neutral_object.hpp | 22 + .../lab4/neutral_object_types.hpp | 187 ++++++ 8303/Parfentev_Leonid/lab4/object_print.cpp | 146 +++++ 8303/Parfentev_Leonid/lab4/object_print.hpp | 40 ++ .../Parfentev_Leonid/lab4/object_w_health.hpp | 30 + 8303/Parfentev_Leonid/lab4/pathfinder.cpp | 60 ++ 8303/Parfentev_Leonid/lab4/pathfinder.hpp | 56 ++ 8303/Parfentev_Leonid/lab4/placeable.hpp | 36 ++ 8303/Parfentev_Leonid/lab4/player.hpp | 26 + 8303/Parfentev_Leonid/lab4/point.cpp | 29 + 8303/Parfentev_Leonid/lab4/point.hpp | 40 ++ 8303/Parfentev_Leonid/lab4/ranged_units.hpp | 94 +++ 8303/Parfentev_Leonid/lab4/rectmap.cpp | 55 ++ 8303/Parfentev_Leonid/lab4/rectmap.hpp | 99 +++ 8303/Parfentev_Leonid/lab4/report.pdf | Bin 0 -> 103969 bytes 8303/Parfentev_Leonid/lab4/unit.cpp | 5 + 8303/Parfentev_Leonid/lab4/unit.hpp | 258 ++++++++ 8303/Parfentev_Leonid/lab4/unit_factory.hpp | 24 + .../lab4/zombie_collector.hpp | 36 ++ 8303/Parfentev_Leonid/lab5/Makefile | 23 + 8303/Parfentev_Leonid/lab5/base.cpp | 138 +++++ 8303/Parfentev_Leonid/lab5/base.hpp | 90 +++ 8303/Parfentev_Leonid/lab5/catapult_units.hpp | 170 ++++++ .../Parfentev_Leonid/lab5/common_policies.hpp | 308 ++++++++++ .../lab5/common_storables.hpp | 117 ++++ 8303/Parfentev_Leonid/lab5/demo.cpp | 520 ++++++++++++++++ 8303/Parfentev_Leonid/lab5/demo.hpp | 14 + 8303/Parfentev_Leonid/lab5/event.cpp | 31 + 8303/Parfentev_Leonid/lab5/event.hpp | 62 ++ 8303/Parfentev_Leonid/lab5/event_printer.hpp | 124 ++++ 8303/Parfentev_Leonid/lab5/event_types.hpp | 156 +++++ 8303/Parfentev_Leonid/lab5/factory_table.hpp | 81 +++ 8303/Parfentev_Leonid/lab5/game.cpp | 198 ++++++ 8303/Parfentev_Leonid/lab5/game.hpp | 56 ++ .../Parfentev_Leonid/lab5/iostream_player.cpp | 120 ++++ .../Parfentev_Leonid/lab5/iostream_player.hpp | 324 ++++++++++ 8303/Parfentev_Leonid/lab5/landscape.hpp | 29 + .../Parfentev_Leonid/lab5/landscape_types.hpp | 75 +++ 8303/Parfentev_Leonid/lab5/logging.hpp | 46 ++ 8303/Parfentev_Leonid/lab5/main.cpp | 176 ++++++ 8303/Parfentev_Leonid/lab5/map.cpp | 222 +++++++ 8303/Parfentev_Leonid/lab5/map.hpp | 126 ++++ 8303/Parfentev_Leonid/lab5/mediator.cpp | 111 ++++ 8303/Parfentev_Leonid/lab5/mediator.hpp | 31 + 8303/Parfentev_Leonid/lab5/melee_units.hpp | 111 ++++ 8303/Parfentev_Leonid/lab5/neutral_object.hpp | 24 + .../lab5/neutral_object_types.hpp | 255 ++++++++ 8303/Parfentev_Leonid/lab5/object_print.cpp | 146 +++++ 8303/Parfentev_Leonid/lab5/object_print.hpp | 40 ++ .../Parfentev_Leonid/lab5/object_w_health.hpp | 43 ++ 8303/Parfentev_Leonid/lab5/pathfinder.cpp | 60 ++ 8303/Parfentev_Leonid/lab5/pathfinder.hpp | 56 ++ 8303/Parfentev_Leonid/lab5/placeable.hpp | 36 ++ 8303/Parfentev_Leonid/lab5/player.hpp | 44 ++ 8303/Parfentev_Leonid/lab5/point.cpp | 29 + 8303/Parfentev_Leonid/lab5/point.hpp | 40 ++ 8303/Parfentev_Leonid/lab5/ranged_units.hpp | 118 ++++ 8303/Parfentev_Leonid/lab5/rectmap.cpp | 55 ++ 8303/Parfentev_Leonid/lab5/rectmap.hpp | 99 +++ 8303/Parfentev_Leonid/lab5/report.pdf | Bin 0 -> 103631 bytes 8303/Parfentev_Leonid/lab5/restorers.hpp | 61 ++ 8303/Parfentev_Leonid/lab5/storable.cpp | 78 +++ 8303/Parfentev_Leonid/lab5/storable.hpp | 70 +++ 8303/Parfentev_Leonid/lab5/unit.cpp | 48 ++ 8303/Parfentev_Leonid/lab5/unit.hpp | 271 ++++++++ 8303/Parfentev_Leonid/lab5/unit_factory.hpp | 24 + .../lab5/zombie_collector.hpp | 36 ++ 8303/Parfentev_Leonid/lab6/Makefile | 23 + 8303/Parfentev_Leonid/lab6/base.cpp | 162 +++++ 8303/Parfentev_Leonid/lab6/base.hpp | 98 +++ 8303/Parfentev_Leonid/lab6/catapult_units.hpp | 170 ++++++ .../Parfentev_Leonid/lab6/common_policies.hpp | 308 ++++++++++ .../lab6/common_storables.hpp | 117 ++++ 8303/Parfentev_Leonid/lab6/demo.cpp | 520 ++++++++++++++++ 8303/Parfentev_Leonid/lab6/demo.hpp | 14 + 8303/Parfentev_Leonid/lab6/event.cpp | 31 + 8303/Parfentev_Leonid/lab6/event.hpp | 62 ++ 8303/Parfentev_Leonid/lab6/event_printer.hpp | 127 ++++ 8303/Parfentev_Leonid/lab6/event_types.hpp | 156 +++++ 8303/Parfentev_Leonid/lab6/factory_table.hpp | 81 +++ 8303/Parfentev_Leonid/lab6/game.cpp | 215 +++++++ 8303/Parfentev_Leonid/lab6/game.hpp | 68 +++ 8303/Parfentev_Leonid/lab6/game_driver.hpp | 123 ++++ 8303/Parfentev_Leonid/lab6/game_rules.hpp | 145 +++++ .../Parfentev_Leonid/lab6/iostream_player.cpp | 122 ++++ .../Parfentev_Leonid/lab6/iostream_player.hpp | 366 +++++++++++ 8303/Parfentev_Leonid/lab6/landscape.hpp | 29 + .../Parfentev_Leonid/lab6/landscape_types.hpp | 75 +++ 8303/Parfentev_Leonid/lab6/logging.hpp | 46 ++ 8303/Parfentev_Leonid/lab6/main.cpp | 122 ++++ 8303/Parfentev_Leonid/lab6/map.cpp | 222 +++++++ 8303/Parfentev_Leonid/lab6/map.hpp | 126 ++++ 8303/Parfentev_Leonid/lab6/mediator.cpp | 127 ++++ 8303/Parfentev_Leonid/lab6/mediator.hpp | 32 + 8303/Parfentev_Leonid/lab6/melee_units.hpp | 111 ++++ 8303/Parfentev_Leonid/lab6/neutral_object.hpp | 24 + .../lab6/neutral_object_types.hpp | 255 ++++++++ 8303/Parfentev_Leonid/lab6/object_print.cpp | 146 +++++ 8303/Parfentev_Leonid/lab6/object_print.hpp | 40 ++ .../Parfentev_Leonid/lab6/object_w_health.hpp | 43 ++ 8303/Parfentev_Leonid/lab6/pathfinder.cpp | 60 ++ 8303/Parfentev_Leonid/lab6/pathfinder.hpp | 56 ++ 8303/Parfentev_Leonid/lab6/placeable.hpp | 36 ++ 8303/Parfentev_Leonid/lab6/player.hpp | 44 ++ 8303/Parfentev_Leonid/lab6/point.cpp | 29 + 8303/Parfentev_Leonid/lab6/point.hpp | 40 ++ 8303/Parfentev_Leonid/lab6/ranged_units.hpp | 118 ++++ 8303/Parfentev_Leonid/lab6/rectmap.cpp | 55 ++ 8303/Parfentev_Leonid/lab6/rectmap.hpp | 99 +++ 8303/Parfentev_Leonid/lab6/report.pdf | Bin 0 -> 107708 bytes 8303/Parfentev_Leonid/lab6/restorers.hpp | 61 ++ 8303/Parfentev_Leonid/lab6/storable.cpp | 81 +++ 8303/Parfentev_Leonid/lab6/storable.hpp | 70 +++ 8303/Parfentev_Leonid/lab6/unit.cpp | 48 ++ 8303/Parfentev_Leonid/lab6/unit.hpp | 271 ++++++++ 8303/Parfentev_Leonid/lab6/unit_factory.hpp | 24 + .../lab6/zombie_collector.hpp | 36 ++ 8303/Parfentev_Leonid/lab7/Makefile | 24 + 8303/Parfentev_Leonid/lab7/base.cpp | 162 +++++ 8303/Parfentev_Leonid/lab7/base.hpp | 98 +++ 8303/Parfentev_Leonid/lab7/catapult_units.hpp | 170 ++++++ .../Parfentev_Leonid/lab7/common_policies.hpp | 308 ++++++++++ .../lab7/common_storables.hpp | 117 ++++ 8303/Parfentev_Leonid/lab7/demo.cpp | 520 ++++++++++++++++ 8303/Parfentev_Leonid/lab7/demo.hpp | 14 + 8303/Parfentev_Leonid/lab7/event.cpp | 31 + 8303/Parfentev_Leonid/lab7/event.hpp | 62 ++ 8303/Parfentev_Leonid/lab7/event_printer.hpp | 127 ++++ 8303/Parfentev_Leonid/lab7/event_types.hpp | 156 +++++ 8303/Parfentev_Leonid/lab7/exceptions.hpp | 57 ++ 8303/Parfentev_Leonid/lab7/factory_table.hpp | 81 +++ 8303/Parfentev_Leonid/lab7/game.cpp | 215 +++++++ 8303/Parfentev_Leonid/lab7/game.hpp | 68 +++ 8303/Parfentev_Leonid/lab7/game_driver.hpp | 123 ++++ 8303/Parfentev_Leonid/lab7/game_rules.hpp | 145 +++++ .../Parfentev_Leonid/lab7/iostream_player.cpp | 122 ++++ .../Parfentev_Leonid/lab7/iostream_player.hpp | 366 +++++++++++ 8303/Parfentev_Leonid/lab7/landscape.hpp | 29 + .../Parfentev_Leonid/lab7/landscape_types.hpp | 75 +++ 8303/Parfentev_Leonid/lab7/logging.hpp | 46 ++ 8303/Parfentev_Leonid/lab7/main.cpp | 132 ++++ 8303/Parfentev_Leonid/lab7/map.cpp | 222 +++++++ 8303/Parfentev_Leonid/lab7/map.hpp | 126 ++++ 8303/Parfentev_Leonid/lab7/mediator.cpp | 127 ++++ 8303/Parfentev_Leonid/lab7/mediator.hpp | 32 + 8303/Parfentev_Leonid/lab7/melee_units.hpp | 111 ++++ 8303/Parfentev_Leonid/lab7/neutral_object.hpp | 24 + .../lab7/neutral_object_types.hpp | 255 ++++++++ 8303/Parfentev_Leonid/lab7/object_print.cpp | 146 +++++ 8303/Parfentev_Leonid/lab7/object_print.hpp | 40 ++ .../Parfentev_Leonid/lab7/object_w_health.hpp | 43 ++ 8303/Parfentev_Leonid/lab7/pathfinder.cpp | 60 ++ 8303/Parfentev_Leonid/lab7/pathfinder.hpp | 56 ++ 8303/Parfentev_Leonid/lab7/placeable.hpp | 36 ++ 8303/Parfentev_Leonid/lab7/player.hpp | 44 ++ 8303/Parfentev_Leonid/lab7/point.cpp | 29 + 8303/Parfentev_Leonid/lab7/point.hpp | 40 ++ 8303/Parfentev_Leonid/lab7/ranged_units.hpp | 118 ++++ 8303/Parfentev_Leonid/lab7/rectmap.cpp | 55 ++ 8303/Parfentev_Leonid/lab7/rectmap.hpp | 99 +++ 8303/Parfentev_Leonid/lab7/report.pdf | Bin 0 -> 100100 bytes 8303/Parfentev_Leonid/lab7/restorers.hpp | 61 ++ 8303/Parfentev_Leonid/lab7/storable.cpp | 81 +++ 8303/Parfentev_Leonid/lab7/storable.hpp | 70 +++ 8303/Parfentev_Leonid/lab7/unit.cpp | 48 ++ 8303/Parfentev_Leonid/lab7/unit.hpp | 271 ++++++++ 8303/Parfentev_Leonid/lab7/unit_factory.hpp | 24 + .../lab7/zombie_collector.hpp | 36 ++ 285 files changed, 28009 insertions(+) create mode 100644 8303/Parfentev_Leonid/lab1/Makefile create mode 100644 8303/Parfentev_Leonid/lab1/catapult_units.hpp create mode 100644 8303/Parfentev_Leonid/lab1/common_policies.hpp create mode 100644 8303/Parfentev_Leonid/lab1/demo.cpp create mode 100644 8303/Parfentev_Leonid/lab1/demo.hpp create mode 100644 8303/Parfentev_Leonid/lab1/main.cpp create mode 100644 8303/Parfentev_Leonid/lab1/map.cpp create mode 100644 8303/Parfentev_Leonid/lab1/map.hpp create mode 100644 8303/Parfentev_Leonid/lab1/melee_units.hpp create mode 100644 8303/Parfentev_Leonid/lab1/pathfinder.cpp create mode 100644 8303/Parfentev_Leonid/lab1/pathfinder.hpp create mode 100644 8303/Parfentev_Leonid/lab1/placeable.hpp create mode 100644 8303/Parfentev_Leonid/lab1/point.cpp create mode 100644 8303/Parfentev_Leonid/lab1/point.hpp create mode 100644 8303/Parfentev_Leonid/lab1/ranged_units.hpp create mode 100644 8303/Parfentev_Leonid/lab1/rectmap.cpp create mode 100644 8303/Parfentev_Leonid/lab1/rectmap.hpp create mode 100644 8303/Parfentev_Leonid/lab1/report.pdf create mode 100644 8303/Parfentev_Leonid/lab1/unit.cpp create mode 100644 8303/Parfentev_Leonid/lab1/unit.hpp create mode 100644 8303/Parfentev_Leonid/lab2/Makefile create mode 100644 8303/Parfentev_Leonid/lab2/base.cpp create mode 100644 8303/Parfentev_Leonid/lab2/base.hpp create mode 100644 8303/Parfentev_Leonid/lab2/catapult_units.hpp create mode 100644 8303/Parfentev_Leonid/lab2/common_policies.hpp create mode 100644 8303/Parfentev_Leonid/lab2/demo.cpp create mode 100644 8303/Parfentev_Leonid/lab2/demo.hpp create mode 100644 8303/Parfentev_Leonid/lab2/event.cpp create mode 100644 8303/Parfentev_Leonid/lab2/event.hpp create mode 100644 8303/Parfentev_Leonid/lab2/landscape.hpp create mode 100644 8303/Parfentev_Leonid/lab2/landscape_types.hpp create mode 100644 8303/Parfentev_Leonid/lab2/main.cpp create mode 100644 8303/Parfentev_Leonid/lab2/map.cpp create mode 100644 8303/Parfentev_Leonid/lab2/map.hpp create mode 100644 8303/Parfentev_Leonid/lab2/melee_units.hpp create mode 100644 8303/Parfentev_Leonid/lab2/neutral_object.hpp create mode 100644 8303/Parfentev_Leonid/lab2/neutral_object_types.hpp create mode 100644 8303/Parfentev_Leonid/lab2/object_w_health.hpp create mode 100644 8303/Parfentev_Leonid/lab2/pathfinder.cpp create mode 100644 8303/Parfentev_Leonid/lab2/pathfinder.hpp create mode 100644 8303/Parfentev_Leonid/lab2/placeable.hpp create mode 100644 8303/Parfentev_Leonid/lab2/point.cpp create mode 100644 8303/Parfentev_Leonid/lab2/point.hpp create mode 100644 8303/Parfentev_Leonid/lab2/ranged_units.hpp create mode 100644 8303/Parfentev_Leonid/lab2/rectmap.cpp create mode 100644 8303/Parfentev_Leonid/lab2/rectmap.hpp create mode 100644 8303/Parfentev_Leonid/lab2/report.pdf create mode 100644 8303/Parfentev_Leonid/lab2/unit.cpp create mode 100644 8303/Parfentev_Leonid/lab2/unit.hpp create mode 100644 8303/Parfentev_Leonid/lab2/unit_factory.hpp create mode 100644 8303/Parfentev_Leonid/lab3/Makefile create mode 100644 8303/Parfentev_Leonid/lab3/base.cpp create mode 100644 8303/Parfentev_Leonid/lab3/base.hpp create mode 100644 8303/Parfentev_Leonid/lab3/catapult_units.hpp create mode 100644 8303/Parfentev_Leonid/lab3/common_policies.hpp create mode 100644 8303/Parfentev_Leonid/lab3/demo.cpp create mode 100644 8303/Parfentev_Leonid/lab3/demo.hpp create mode 100644 8303/Parfentev_Leonid/lab3/event.cpp create mode 100644 8303/Parfentev_Leonid/lab3/event.hpp create mode 100644 8303/Parfentev_Leonid/lab3/event_printer.hpp create mode 100644 8303/Parfentev_Leonid/lab3/event_types.hpp create mode 100644 8303/Parfentev_Leonid/lab3/game.cpp create mode 100644 8303/Parfentev_Leonid/lab3/game.hpp create mode 100644 8303/Parfentev_Leonid/lab3/iostream_player.cpp create mode 100644 8303/Parfentev_Leonid/lab3/iostream_player.hpp create mode 100644 8303/Parfentev_Leonid/lab3/landscape.hpp create mode 100644 8303/Parfentev_Leonid/lab3/landscape_types.hpp create mode 100644 8303/Parfentev_Leonid/lab3/main.cpp create mode 100644 8303/Parfentev_Leonid/lab3/map.cpp create mode 100644 8303/Parfentev_Leonid/lab3/map.hpp create mode 100644 8303/Parfentev_Leonid/lab3/mediator.cpp create mode 100644 8303/Parfentev_Leonid/lab3/mediator.hpp create mode 100644 8303/Parfentev_Leonid/lab3/melee_units.hpp create mode 100644 8303/Parfentev_Leonid/lab3/neutral_object.hpp create mode 100644 8303/Parfentev_Leonid/lab3/neutral_object_types.hpp create mode 100644 8303/Parfentev_Leonid/lab3/object_print.cpp create mode 100644 8303/Parfentev_Leonid/lab3/object_print.hpp create mode 100644 8303/Parfentev_Leonid/lab3/object_w_health.hpp create mode 100644 8303/Parfentev_Leonid/lab3/pathfinder.cpp create mode 100644 8303/Parfentev_Leonid/lab3/pathfinder.hpp create mode 100644 8303/Parfentev_Leonid/lab3/placeable.hpp create mode 100644 8303/Parfentev_Leonid/lab3/player.hpp create mode 100644 8303/Parfentev_Leonid/lab3/point.cpp create mode 100644 8303/Parfentev_Leonid/lab3/point.hpp create mode 100644 8303/Parfentev_Leonid/lab3/ranged_units.hpp create mode 100644 8303/Parfentev_Leonid/lab3/rectmap.cpp create mode 100644 8303/Parfentev_Leonid/lab3/rectmap.hpp create mode 100644 8303/Parfentev_Leonid/lab3/report.pdf create mode 100644 8303/Parfentev_Leonid/lab3/unit.cpp create mode 100644 8303/Parfentev_Leonid/lab3/unit.hpp create mode 100644 8303/Parfentev_Leonid/lab3/unit_factory.hpp create mode 100644 8303/Parfentev_Leonid/lab3/zombie_collector.hpp create mode 100644 8303/Parfentev_Leonid/lab4/Makefile create mode 100644 8303/Parfentev_Leonid/lab4/base.cpp create mode 100644 8303/Parfentev_Leonid/lab4/base.hpp create mode 100644 8303/Parfentev_Leonid/lab4/catapult_units.hpp create mode 100644 8303/Parfentev_Leonid/lab4/common_policies.hpp create mode 100644 8303/Parfentev_Leonid/lab4/demo.cpp create mode 100644 8303/Parfentev_Leonid/lab4/demo.hpp create mode 100644 8303/Parfentev_Leonid/lab4/event.cpp create mode 100644 8303/Parfentev_Leonid/lab4/event.hpp create mode 100644 8303/Parfentev_Leonid/lab4/event_printer.hpp create mode 100644 8303/Parfentev_Leonid/lab4/event_types.hpp create mode 100644 8303/Parfentev_Leonid/lab4/factory_table.hpp create mode 100644 8303/Parfentev_Leonid/lab4/game.cpp create mode 100644 8303/Parfentev_Leonid/lab4/game.hpp create mode 100644 8303/Parfentev_Leonid/lab4/iostream_player.cpp create mode 100644 8303/Parfentev_Leonid/lab4/iostream_player.hpp create mode 100644 8303/Parfentev_Leonid/lab4/landscape.hpp create mode 100644 8303/Parfentev_Leonid/lab4/landscape_types.hpp create mode 100644 8303/Parfentev_Leonid/lab4/logging.hpp create mode 100644 8303/Parfentev_Leonid/lab4/main.cpp create mode 100644 8303/Parfentev_Leonid/lab4/map.cpp create mode 100644 8303/Parfentev_Leonid/lab4/map.hpp create mode 100644 8303/Parfentev_Leonid/lab4/mediator.cpp create mode 100644 8303/Parfentev_Leonid/lab4/mediator.hpp create mode 100644 8303/Parfentev_Leonid/lab4/melee_units.hpp create mode 100644 8303/Parfentev_Leonid/lab4/neutral_object.hpp create mode 100644 8303/Parfentev_Leonid/lab4/neutral_object_types.hpp create mode 100644 8303/Parfentev_Leonid/lab4/object_print.cpp create mode 100644 8303/Parfentev_Leonid/lab4/object_print.hpp create mode 100644 8303/Parfentev_Leonid/lab4/object_w_health.hpp create mode 100644 8303/Parfentev_Leonid/lab4/pathfinder.cpp create mode 100644 8303/Parfentev_Leonid/lab4/pathfinder.hpp create mode 100644 8303/Parfentev_Leonid/lab4/placeable.hpp create mode 100644 8303/Parfentev_Leonid/lab4/player.hpp create mode 100644 8303/Parfentev_Leonid/lab4/point.cpp create mode 100644 8303/Parfentev_Leonid/lab4/point.hpp create mode 100644 8303/Parfentev_Leonid/lab4/ranged_units.hpp create mode 100644 8303/Parfentev_Leonid/lab4/rectmap.cpp create mode 100644 8303/Parfentev_Leonid/lab4/rectmap.hpp create mode 100644 8303/Parfentev_Leonid/lab4/report.pdf create mode 100644 8303/Parfentev_Leonid/lab4/unit.cpp create mode 100644 8303/Parfentev_Leonid/lab4/unit.hpp create mode 100644 8303/Parfentev_Leonid/lab4/unit_factory.hpp create mode 100644 8303/Parfentev_Leonid/lab4/zombie_collector.hpp create mode 100644 8303/Parfentev_Leonid/lab5/Makefile create mode 100644 8303/Parfentev_Leonid/lab5/base.cpp create mode 100644 8303/Parfentev_Leonid/lab5/base.hpp create mode 100644 8303/Parfentev_Leonid/lab5/catapult_units.hpp create mode 100644 8303/Parfentev_Leonid/lab5/common_policies.hpp create mode 100644 8303/Parfentev_Leonid/lab5/common_storables.hpp create mode 100644 8303/Parfentev_Leonid/lab5/demo.cpp create mode 100644 8303/Parfentev_Leonid/lab5/demo.hpp create mode 100644 8303/Parfentev_Leonid/lab5/event.cpp create mode 100644 8303/Parfentev_Leonid/lab5/event.hpp create mode 100644 8303/Parfentev_Leonid/lab5/event_printer.hpp create mode 100644 8303/Parfentev_Leonid/lab5/event_types.hpp create mode 100644 8303/Parfentev_Leonid/lab5/factory_table.hpp create mode 100644 8303/Parfentev_Leonid/lab5/game.cpp create mode 100644 8303/Parfentev_Leonid/lab5/game.hpp create mode 100644 8303/Parfentev_Leonid/lab5/iostream_player.cpp create mode 100644 8303/Parfentev_Leonid/lab5/iostream_player.hpp create mode 100644 8303/Parfentev_Leonid/lab5/landscape.hpp create mode 100644 8303/Parfentev_Leonid/lab5/landscape_types.hpp create mode 100644 8303/Parfentev_Leonid/lab5/logging.hpp create mode 100644 8303/Parfentev_Leonid/lab5/main.cpp create mode 100644 8303/Parfentev_Leonid/lab5/map.cpp create mode 100644 8303/Parfentev_Leonid/lab5/map.hpp create mode 100644 8303/Parfentev_Leonid/lab5/mediator.cpp create mode 100644 8303/Parfentev_Leonid/lab5/mediator.hpp create mode 100644 8303/Parfentev_Leonid/lab5/melee_units.hpp create mode 100644 8303/Parfentev_Leonid/lab5/neutral_object.hpp create mode 100644 8303/Parfentev_Leonid/lab5/neutral_object_types.hpp create mode 100644 8303/Parfentev_Leonid/lab5/object_print.cpp create mode 100644 8303/Parfentev_Leonid/lab5/object_print.hpp create mode 100644 8303/Parfentev_Leonid/lab5/object_w_health.hpp create mode 100644 8303/Parfentev_Leonid/lab5/pathfinder.cpp create mode 100644 8303/Parfentev_Leonid/lab5/pathfinder.hpp create mode 100644 8303/Parfentev_Leonid/lab5/placeable.hpp create mode 100644 8303/Parfentev_Leonid/lab5/player.hpp create mode 100644 8303/Parfentev_Leonid/lab5/point.cpp create mode 100644 8303/Parfentev_Leonid/lab5/point.hpp create mode 100644 8303/Parfentev_Leonid/lab5/ranged_units.hpp create mode 100644 8303/Parfentev_Leonid/lab5/rectmap.cpp create mode 100644 8303/Parfentev_Leonid/lab5/rectmap.hpp create mode 100644 8303/Parfentev_Leonid/lab5/report.pdf create mode 100644 8303/Parfentev_Leonid/lab5/restorers.hpp create mode 100644 8303/Parfentev_Leonid/lab5/storable.cpp create mode 100644 8303/Parfentev_Leonid/lab5/storable.hpp create mode 100644 8303/Parfentev_Leonid/lab5/unit.cpp create mode 100644 8303/Parfentev_Leonid/lab5/unit.hpp create mode 100644 8303/Parfentev_Leonid/lab5/unit_factory.hpp create mode 100644 8303/Parfentev_Leonid/lab5/zombie_collector.hpp create mode 100644 8303/Parfentev_Leonid/lab6/Makefile create mode 100644 8303/Parfentev_Leonid/lab6/base.cpp create mode 100644 8303/Parfentev_Leonid/lab6/base.hpp create mode 100644 8303/Parfentev_Leonid/lab6/catapult_units.hpp create mode 100644 8303/Parfentev_Leonid/lab6/common_policies.hpp create mode 100644 8303/Parfentev_Leonid/lab6/common_storables.hpp create mode 100644 8303/Parfentev_Leonid/lab6/demo.cpp create mode 100644 8303/Parfentev_Leonid/lab6/demo.hpp create mode 100644 8303/Parfentev_Leonid/lab6/event.cpp create mode 100644 8303/Parfentev_Leonid/lab6/event.hpp create mode 100644 8303/Parfentev_Leonid/lab6/event_printer.hpp create mode 100644 8303/Parfentev_Leonid/lab6/event_types.hpp create mode 100644 8303/Parfentev_Leonid/lab6/factory_table.hpp create mode 100644 8303/Parfentev_Leonid/lab6/game.cpp create mode 100644 8303/Parfentev_Leonid/lab6/game.hpp create mode 100644 8303/Parfentev_Leonid/lab6/game_driver.hpp create mode 100644 8303/Parfentev_Leonid/lab6/game_rules.hpp create mode 100644 8303/Parfentev_Leonid/lab6/iostream_player.cpp create mode 100644 8303/Parfentev_Leonid/lab6/iostream_player.hpp create mode 100644 8303/Parfentev_Leonid/lab6/landscape.hpp create mode 100644 8303/Parfentev_Leonid/lab6/landscape_types.hpp create mode 100644 8303/Parfentev_Leonid/lab6/logging.hpp create mode 100644 8303/Parfentev_Leonid/lab6/main.cpp create mode 100644 8303/Parfentev_Leonid/lab6/map.cpp create mode 100644 8303/Parfentev_Leonid/lab6/map.hpp create mode 100644 8303/Parfentev_Leonid/lab6/mediator.cpp create mode 100644 8303/Parfentev_Leonid/lab6/mediator.hpp create mode 100644 8303/Parfentev_Leonid/lab6/melee_units.hpp create mode 100644 8303/Parfentev_Leonid/lab6/neutral_object.hpp create mode 100644 8303/Parfentev_Leonid/lab6/neutral_object_types.hpp create mode 100644 8303/Parfentev_Leonid/lab6/object_print.cpp create mode 100644 8303/Parfentev_Leonid/lab6/object_print.hpp create mode 100644 8303/Parfentev_Leonid/lab6/object_w_health.hpp create mode 100644 8303/Parfentev_Leonid/lab6/pathfinder.cpp create mode 100644 8303/Parfentev_Leonid/lab6/pathfinder.hpp create mode 100644 8303/Parfentev_Leonid/lab6/placeable.hpp create mode 100644 8303/Parfentev_Leonid/lab6/player.hpp create mode 100644 8303/Parfentev_Leonid/lab6/point.cpp create mode 100644 8303/Parfentev_Leonid/lab6/point.hpp create mode 100644 8303/Parfentev_Leonid/lab6/ranged_units.hpp create mode 100644 8303/Parfentev_Leonid/lab6/rectmap.cpp create mode 100644 8303/Parfentev_Leonid/lab6/rectmap.hpp create mode 100644 8303/Parfentev_Leonid/lab6/report.pdf create mode 100644 8303/Parfentev_Leonid/lab6/restorers.hpp create mode 100644 8303/Parfentev_Leonid/lab6/storable.cpp create mode 100644 8303/Parfentev_Leonid/lab6/storable.hpp create mode 100644 8303/Parfentev_Leonid/lab6/unit.cpp create mode 100644 8303/Parfentev_Leonid/lab6/unit.hpp create mode 100644 8303/Parfentev_Leonid/lab6/unit_factory.hpp create mode 100644 8303/Parfentev_Leonid/lab6/zombie_collector.hpp create mode 100644 8303/Parfentev_Leonid/lab7/Makefile create mode 100644 8303/Parfentev_Leonid/lab7/base.cpp create mode 100644 8303/Parfentev_Leonid/lab7/base.hpp create mode 100644 8303/Parfentev_Leonid/lab7/catapult_units.hpp create mode 100644 8303/Parfentev_Leonid/lab7/common_policies.hpp create mode 100644 8303/Parfentev_Leonid/lab7/common_storables.hpp create mode 100644 8303/Parfentev_Leonid/lab7/demo.cpp create mode 100644 8303/Parfentev_Leonid/lab7/demo.hpp create mode 100644 8303/Parfentev_Leonid/lab7/event.cpp create mode 100644 8303/Parfentev_Leonid/lab7/event.hpp create mode 100644 8303/Parfentev_Leonid/lab7/event_printer.hpp create mode 100644 8303/Parfentev_Leonid/lab7/event_types.hpp create mode 100644 8303/Parfentev_Leonid/lab7/exceptions.hpp create mode 100644 8303/Parfentev_Leonid/lab7/factory_table.hpp create mode 100644 8303/Parfentev_Leonid/lab7/game.cpp create mode 100644 8303/Parfentev_Leonid/lab7/game.hpp create mode 100644 8303/Parfentev_Leonid/lab7/game_driver.hpp create mode 100644 8303/Parfentev_Leonid/lab7/game_rules.hpp create mode 100644 8303/Parfentev_Leonid/lab7/iostream_player.cpp create mode 100644 8303/Parfentev_Leonid/lab7/iostream_player.hpp create mode 100644 8303/Parfentev_Leonid/lab7/landscape.hpp create mode 100644 8303/Parfentev_Leonid/lab7/landscape_types.hpp create mode 100644 8303/Parfentev_Leonid/lab7/logging.hpp create mode 100644 8303/Parfentev_Leonid/lab7/main.cpp create mode 100644 8303/Parfentev_Leonid/lab7/map.cpp create mode 100644 8303/Parfentev_Leonid/lab7/map.hpp create mode 100644 8303/Parfentev_Leonid/lab7/mediator.cpp create mode 100644 8303/Parfentev_Leonid/lab7/mediator.hpp create mode 100644 8303/Parfentev_Leonid/lab7/melee_units.hpp create mode 100644 8303/Parfentev_Leonid/lab7/neutral_object.hpp create mode 100644 8303/Parfentev_Leonid/lab7/neutral_object_types.hpp create mode 100644 8303/Parfentev_Leonid/lab7/object_print.cpp create mode 100644 8303/Parfentev_Leonid/lab7/object_print.hpp create mode 100644 8303/Parfentev_Leonid/lab7/object_w_health.hpp create mode 100644 8303/Parfentev_Leonid/lab7/pathfinder.cpp create mode 100644 8303/Parfentev_Leonid/lab7/pathfinder.hpp create mode 100644 8303/Parfentev_Leonid/lab7/placeable.hpp create mode 100644 8303/Parfentev_Leonid/lab7/player.hpp create mode 100644 8303/Parfentev_Leonid/lab7/point.cpp create mode 100644 8303/Parfentev_Leonid/lab7/point.hpp create mode 100644 8303/Parfentev_Leonid/lab7/ranged_units.hpp create mode 100644 8303/Parfentev_Leonid/lab7/rectmap.cpp create mode 100644 8303/Parfentev_Leonid/lab7/rectmap.hpp create mode 100644 8303/Parfentev_Leonid/lab7/report.pdf create mode 100644 8303/Parfentev_Leonid/lab7/restorers.hpp create mode 100644 8303/Parfentev_Leonid/lab7/storable.cpp create mode 100644 8303/Parfentev_Leonid/lab7/storable.hpp create mode 100644 8303/Parfentev_Leonid/lab7/unit.cpp create mode 100644 8303/Parfentev_Leonid/lab7/unit.hpp create mode 100644 8303/Parfentev_Leonid/lab7/unit_factory.hpp create mode 100644 8303/Parfentev_Leonid/lab7/zombie_collector.hpp diff --git a/8303/Parfentev_Leonid/lab1/Makefile b/8303/Parfentev_Leonid/lab1/Makefile new file mode 100644 index 000000000..e58f66d8f --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/Makefile @@ -0,0 +1,16 @@ +EXENAME = main +HEADERS = point.hpp placeable.hpp rectmap.hpp map.hpp pathfinder.hpp \ + unit.hpp common_policies.hpp melee_units.hpp ranged_units.hpp \ + catapult_units.hpp demo.hpp +OBJFILES = point.o rectmap.o map.o pathfinder.o unit.o demo.o main.o +LDLIBS = -lm + +all: $(EXENAME) + +$(OBJFILES): $(HEADERS) + +$(EXENAME): $(OBJFILES) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + $(RM) $(EXENAME) $(OBJFILES) diff --git a/8303/Parfentev_Leonid/lab1/catapult_units.hpp b/8303/Parfentev_Leonid/lab1/catapult_units.hpp new file mode 100644 index 000000000..26a217f7d --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/catapult_units.hpp @@ -0,0 +1,145 @@ +#ifndef _H_CATAPULT_UNITS_HPP +#define _H_CATAPULT_UNITS_HPP + +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" +#include "ranged_units.hpp" + + +class CatapultAttack: public RangedAttack { + double _spread_tang, _spread_normal; + + struct FVec2 { + double x, y; + + explicit FVec2(const Vec2 &v) + :x{(double)v.x()}, y{(double)v.y()} {} + + FVec2(double x, double y) + :x{x}, y{y} {} + + operator Vec2() const + { + return Vec2{int(round(x)), int(round(y))}; + } + + FVec2 + orthogonal() const { return {y, -x}; } + + FVec2 & + operator*=(double a) + { + x *= a; + y *= a; + return *this; + } + FVec2 + operator*(double a) const + { + FVec2 tmp {*this}; + return tmp *= a; + } + + FVec2 + normalized() const { return (*this) * (1/sqrt(x*x + y*y)); } + + FVec2 & + operator+=(const FVec2 &dxy) + { + x += dxy.x; + y += dxy.y; + return *this; + } + FVec2 + operator+(const FVec2 &dxy) const + { + FVec2 tmp{*this}; + return tmp += dxy; + } + + FVec2 + apply(double t, double n) const + { + return normalized() * t + + orthogonal().normalized() * n; + } + }; + +public: + CatapultAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n) + :RangedAttack{kind, base_dmg, min_dist, max_dist, dist_pow}, + _spread_tang{spread_t}, + _spread_normal{spread_n} {} + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + Vec2 dest = to.point(); + Vec2 delta = dest.delta(u->position()); + FVec2 fdelta {delta}; + + std::uniform_real_distribution<> + t_dist {-_spread_tang, _spread_tang}, + n_dist {-_spread_normal, _spread_normal}; + + double + t = t_dist(global_random), + n = n_dist(global_random); + + FVec2 result = fdelta.apply(t, n); + return to.shifted(Vec2{result}); + } +}; + +class BasicCatapultUnit: public Unit { +public: + BasicCatapultUnit(AttackKind attack_kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n, + int base_health) + :Unit{new BasicMovement {1}, + new CatapultAttack {attack_kind, + base_dmg, min_dist, max_dist, + dist_pow, spread_t, spread_n}, + DefenseLevelDeco::good_defense_deco( + AttackKind::arrow, + new BasicDefense {0.75}), + base_health} {} +}; + +namespace units { + class Onager: public BasicCatapultUnit { + public: + Onager() :BasicCatapultUnit{ + AttackKind::rock, 90, + 3, 10, 0.05, + 0.2, 0.1, + 30} {} + }; + + class BoltThrower: public BasicCatapultUnit { + public: + BoltThrower() :BasicCatapultUnit{ + AttackKind::bolt, 110, + 2, 6, 0.15, + 0.05, 0.05, + 20} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab1/common_policies.hpp b/8303/Parfentev_Leonid/lab1/common_policies.hpp new file mode 100644 index 000000000..1681e07bc --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/common_policies.hpp @@ -0,0 +1,82 @@ +#ifndef _H_COMMON_POLICIES_HPP +#define _H_COMMON_POLICIES_HPP + +#include "map.hpp" +#include "pathfinder.hpp" + + +class BasicMovement: public MovePolicy { + int _steps_per_turn; + +public: + BasicMovement(int n) + :_steps_per_turn{n} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _steps_per_turn}; + return pf.run(); + } +}; + +class BasicDefense: public DefensePolicy { + double _lvl; + +public: + explicit BasicDefense(double level=1.0) + :_lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *, AttackKind, int base) override + { + return normal_defense(base); + } +}; + +class DefenseLevelDeco: public DefensePolicy { + // Controls nested policy lifetime + DefensePolicy *_p; + AttackKind _kind; + double _lvl; + +public: + DefenseLevelDeco(DefensePolicy *p, + AttackKind kind, + double level) + :_p{p}, _kind{kind}, _lvl{level} {} + + ~DefenseLevelDeco() + { + delete _p; + } + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + if (kind == _kind) + return defense_level(_lvl, base); + return _p->actualDamage(u, kind, base); + } + + static DefenseLevelDeco * + defense_level_deco(AttackKind kind, double lvl, DefensePolicy *p) + { + return new DefenseLevelDeco {p, kind, lvl}; + } + + static DefenseLevelDeco * + good_defense_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 2.0, p); + } + + static DefenseLevelDeco * + vulnerability_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 0.5, p); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab1/demo.cpp b/8303/Parfentev_Leonid/lab1/demo.cpp new file mode 100644 index 000000000..82a71b3fa --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/demo.cpp @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" + + +#define UNIT_ENTRY(T, x) {std::type_index{typeid(units::T)}, x} +static const std::map unit_chars { + UNIT_ENTRY(Swordsman, 'S'), + UNIT_ENTRY(Spearsman, 'P'), + UNIT_ENTRY(Cavalry, 'C'), + UNIT_ENTRY(Archer, 'A'), + UNIT_ENTRY(Slinger, 's'), + UNIT_ENTRY(Onager, 'O'), + UNIT_ENTRY(BoltThrower, 'B'), +}; +#undef UNIT_ENTRY + +#define UNIT_ENTRY(T) {std::type_index{typeid(units::T)}, #T} +static const std::map unit_names { + UNIT_ENTRY(Swordsman), + UNIT_ENTRY(Spearsman), + UNIT_ENTRY(Cavalry), + UNIT_ENTRY(Archer), + UNIT_ENTRY(Slinger), + UNIT_ENTRY(Onager), + UNIT_ENTRY(BoltThrower), +}; +#undef UNIT_ENTRY + +std::ostream & +operator<<(std::ostream &os, Map *map) +{ + for (MapIter iter = map->begin(); + iter != map->end(); + iter.advance(1)) { + + if (Unit *u = iter.unit()) { + os << unit_chars.at(std::type_index{typeid(*u)}); + } else { + os << "."; + } + + os << ((iter.x() == map->width() - 1) ? "\n" : " "); + } + return os; +} + +std::ostream & +operator<<(std::ostream &os, Vec2 pt) +{ + return os << "{" << pt.x() << "," << pt.y() << "}"; +} + +std::ostream & +operator<<(std::ostream &os, Unit *u) +{ + return os << "a " + << unit_names.at(std::type_index{typeid(*u)}) + << " with " << u->health() << " HP"; +} + +void +demo1() +{ + // create a map + auto *map = new Map {10, 10}; + + // create a few units + auto sw1_iter = map->addUnit(new units::Swordsman {}, {3, 3}); + map->addUnit(new units::Swordsman {}, {4, 4}); + + // write the map + std::cout << map << "\n"; + + // test the pathfinder + static const std::vector pts { + {3, 4}, + {3, 3}, + {5, 5}, + {5, 4}, + {5, 3}, + }; + for (auto pt: pts) { + std::cout << sw1_iter.unit() << " at " << sw1_iter.point() << " " + << ((sw1_iter.unit()->canMove(map->iterAt(pt))) + ? "can" : "can't") + << " move to " << pt << "\n"; + } + + // clean up + delete map; +} + +void +demo2() +{ + auto *map = new Map {10, 10}; + + auto c_iter = map->addUnit(new units::Cavalry {}, {3, 3}); + auto *u = c_iter.unit(); + + std::cout << map << "\n"; + + static const std::vector path { + {4, 5}, {6, 5}, {9, 5}, {8, 7}, {7, 9}, + }; + for (auto pt: path) { + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + std::cout << u << " at " << u->position() + << " can't move to " << pt << "!\n"; + break; + } + + std::cout << "moving " << u << " at " << u->position() + << " to " << pt << "...\n"; + + c_iter = map->addUnit(map->removeUnitAt(c_iter), pt); + if (c_iter.null()) { + std::cout << "failed!\n"; + } + } + + std::cout << "\n" << map; + + delete map; +} + +void +demo3() +{ + auto *map = new Map {10, 10}; + map->setMaxUnitsCount(2); + + Unit *sw = new units::Swordsman {}; + Unit *ar = new units::Archer {}; + + map->addUnit(sw, {5, 0}); + map->addUnit(ar, {5, 9}); + + Unit *x = new units::Swordsman {}; + if (!map->addUnit(x, {1, 1}).null()) { + std::cout << "Added one more unit!\n"; + delete map->removeUnitAt({1, 1}); + } else { + std::cout << "Max units: " << map->maxUnitsCount() + << ", current units: " << map->unitsCount() + << "\n"; + delete x; + } + + std::cout << map; + + while (sw->alive() && ar->alive()) { + Vec2 from = sw->position(); + Vec2 to = from.shifted({0, 1}); + + std::cout << "\nmoving " << sw << " from " << from + << " to " << to << "...\n"; + + if (!sw->canMove(map->iterAt(to))) { + std::cout << "can't move\n"; + break; + } + + if (map->addUnit(map->removeUnitAt(from), to).null()) { + std::cout << "failed\n"; + break; + } + + std::cout << ar << " shoots at " << sw->position() << "...\n"; + + auto toi = map->iterAt(sw->position()); + if (!ar->canAttackTo(toi)) { + std::cout << "can't shoot\n"; + // it’s ok + } else { + auto pos = ar->actualPosition(toi); + if (Unit *targ = pos.unit()) { + auto dam = ar->baseAttack(toi); + + std::cout << "hits " << targ << "\n"; + + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + + std::cout << "now: " << targ << "\n"; + } + } + } +} diff --git a/8303/Parfentev_Leonid/lab1/demo.hpp b/8303/Parfentev_Leonid/lab1/demo.hpp new file mode 100644 index 000000000..8141a6bdc --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/demo.hpp @@ -0,0 +1,8 @@ +#ifndef _H_DEMO_HPP +#define _H_DEMO_HPP + +void demo1(); +void demo2(); +void demo3(); + +#endif diff --git a/8303/Parfentev_Leonid/lab1/main.cpp b/8303/Parfentev_Leonid/lab1/main.cpp new file mode 100644 index 000000000..1568f6fd3 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/main.cpp @@ -0,0 +1,16 @@ +#include + +#include "demo.hpp" + +int +main(void) +{ + std::cout << "Demo 1\n"; + demo1(); + + std::cout << "\nDemo 2\n"; + demo2(); + + std::cout << "\nDemo 3\n"; + demo3(); +} diff --git a/8303/Parfentev_Leonid/lab1/map.cpp b/8303/Parfentev_Leonid/lab1/map.cpp new file mode 100644 index 000000000..2feecf165 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/map.cpp @@ -0,0 +1,50 @@ +#include "point.hpp" +#include "unit.hpp" +#include "map.hpp" + + +MapIter +Map::addUnit(Unit *u, Vec2 pt) +{ + if (u->hasPosition()) + return MapIter::makeNull(); + + if (_units_max >= 0 + && _units_count == _units_max) + return MapIter::makeNull(); + + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.placeable()) + return MapIter::makeNull(); + + cell.setPlaceable(u); + u->setPosition(pt); + + ++_units_count; + + return MapIter{rmiter}; +} + +Unit * +Map::removeUnitAt(Vec2 at) +{ + RectMapIter rmiter = _rm.iterAt(at); + Cell &cell = rmiter.cell(); + Unit *u = dynamic_cast(cell.placeable()); + + if (u) { + --_units_count; + cell.setPlaceable(nullptr); + u->unsetPosition(); + } + + return u; +} + +Unit * +MapIter::unit() const +{ + return dynamic_cast(_it.cell().placeable()); +} diff --git a/8303/Parfentev_Leonid/lab1/map.hpp b/8303/Parfentev_Leonid/lab1/map.hpp new file mode 100644 index 000000000..2a84ebb31 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/map.hpp @@ -0,0 +1,97 @@ +#ifndef _H_MAP_HPP +#define _H_MAP_HPP + +#include "rectmap.hpp" + +// Map interface doesn’t know about cells -- instead, it only cares +// about certain kinds of Placeables + + +class Map; + +class Unit; +class Base; + +class MapIter { + RectMapIter _it; + + friend class Map; + + MapIter(RectMapIter r) + :_it{r} {} + +public: + static MapIter makeNull() + { + return MapIter{RectMapIter::makeNull()}; + } + + bool operator==(const MapIter &o) const { return _it == o._it; } + bool operator!=(const MapIter &o) const { return _it != o._it; } + + int x() const { return _it.x(); } + int y() const { return _it.y(); } + Vec2 point() const { return _it.point(); } + + bool null() const { return _it.null(); } + bool valid() const { return _it.valid(); } + + void shift(Vec2 dxy) { _it.moveTo(point().shifted(dxy)); } + MapIter shifted(Vec2 dxy) const + { + return MapIter{_it.otherAt(point().shifted(dxy))}; + } + + void moveTo(Vec2 xy) { _it.moveTo(xy); } + MapIter otherAt(Vec2 xy) const + { + return MapIter{_it.otherAt(xy)}; + } + + void advance(int d) { _it.advance(d); } + MapIter advanced(int d) const + { + return MapIter{_it.advanced(d)}; + } + + Unit *unit() const; + bool occupied() { return unit() != nullptr; } + // other placeable types in the future +}; + +class Map { + RectMap _rm; + int _units_count = 0; + int _units_max = -1; + +public: + Map(int w, int h) + :_rm{w, h} {} + + int width() const { return _rm.width(); } + int height() const { return _rm.height(); } + MapIter iterAt(Vec2 pt) { return MapIter{_rm.iterAt(pt)}; } + MapIter iterAt(int x, int y) { return iterAt({x, y}); } + + MapIter begin() { return iterAt(0, 0); } + MapIter end() { return iterAt(0, height()); } + + MapIter addUnit(Unit *u, Vec2 pt); + Unit *removeUnitAt(Vec2 at); + Unit *removeUnitAt(MapIter iter) + { + return removeUnitAt(iter.point()); + } + + int maxUnitsCount() const { return _units_max; } + bool setMaxUnitsCount(int x) + { + if (_units_count > x) + return false; + _units_max = x; + return true; + } + int unitsCount() const { return _units_count; } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab1/melee_units.hpp b/8303/Parfentev_Leonid/lab1/melee_units.hpp new file mode 100644 index 000000000..f36626401 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/melee_units.hpp @@ -0,0 +1,88 @@ +#ifndef _H_MELEE_UNITS_HPP +#define _H_MELEE_UNITS_HPP + +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class MeleeAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + +public: + MeleeAttack(AttackKind kind, int base_dmg) + :_kind{kind}, _base_damage{base_dmg} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return to.unit() != nullptr + && to.point().adjacent(u->position()); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter) + { + return std::make_pair( + _kind, + int(_base_damage * u->relativeHealth())); + } +}; + +class BasicMeleeUnit: public Unit { +public: + BasicMeleeUnit(int speed, + AttackKind attack_kind, + int base_dmg, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new MeleeAttack {attack_kind, base_dmg}, + def, base_health} {} +}; + +namespace units { + class Swordsman: public BasicMeleeUnit { + public: + Swordsman() :BasicMeleeUnit{ + 2, + AttackKind::sword, 40, + DefenseLevelDeco::good_defense_deco( + AttackKind::spear, + DefenseLevelDeco::vulnerability_deco( + AttackKind::cavalry, + new BasicDefense {})), + 100} {} + }; + + class Spearsman: public BasicMeleeUnit { + public: + Spearsman() :BasicMeleeUnit{ + 2, + AttackKind::spear, 75, + DefenseLevelDeco::good_defense_deco( + AttackKind::cavalry, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; + + class Cavalry: public BasicMeleeUnit { + public: + Cavalry() :BasicMeleeUnit{ + 3, + AttackKind::cavalry, 50, + DefenseLevelDeco::good_defense_deco( + AttackKind::sword, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab1/pathfinder.cpp b/8303/Parfentev_Leonid/lab1/pathfinder.cpp new file mode 100644 index 000000000..4bf6c470d --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/pathfinder.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "map.hpp" +#include "point.hpp" +#include "pathfinder.hpp" + + +PathFinder::Pt2 +PathFinder::Pt2::toDirection(int dir) const +{ + // assert(dir >= 0 && dir < 8); + + int d1 = (dir + 1) % 8; + int dx = (d1 % 4 == 3) ? 0 : (d1 < 4) ? 1 : -1, + dy = (dir % 4 == 0) ? 0 : (dir < 4) ? 1 : -1; + + return Pt2{pt.shifted({dx, dy}), depth + 1}; +} + +bool +PathFinder::run() +{ + Vec2 start_pt = _start.point(); + + while (!_frontier.empty()) { + Pt2 current = _frontier.front(); + _frontier.pop(); + + if (start_pt.shifted(current.pt) == _end) + return true; + + if (_max >= 0 + && current.depth >= _max) + continue; + + Vec2WithCmp cur_v2wc {current.pt}; + auto iter = _dirs.find(cur_v2wc); + if (iter == _dirs.end()) + _dirs[cur_v2wc] = -1; + + for (int i = 0; i < 8; ++i) { + Pt2 shifted_delta = current.toDirection(i); + + Vec2WithCmp sh_v2wc {shifted_delta.pt}; + auto iter = _dirs.find(sh_v2wc); + if (iter != _dirs.end()) + continue; + + MapIter shifted = _start.shifted(shifted_delta.pt); + if (!shifted.valid() + || shifted.occupied()) + continue; + + _frontier.push(shifted_delta); + } + } + + return false; +} diff --git a/8303/Parfentev_Leonid/lab1/pathfinder.hpp b/8303/Parfentev_Leonid/lab1/pathfinder.hpp new file mode 100644 index 000000000..d4c614aae --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/pathfinder.hpp @@ -0,0 +1,56 @@ +#ifndef _H_PATHFINDER_HPP +#define _H_PATHFINDER_HPP + +#include +#include + +#include "point.hpp" +#include "map.hpp" + + +class PathFinder { + MapIter _start; + Vec2 _end; + int _max; + + struct Vec2WithCmp { + Vec2 v; + + bool operator<(Vec2WithCmp o) const + { + if (v.y() == o.v.y()) + return v.x() < o.v.x(); + return v.y() < o.v.y(); + } + }; + + struct Pt2 { + Vec2 pt; + int depth; + + static Pt2 zero() { return {Vec2{0, 0}, 0}; } + + Pt2(Vec2 pt, int depth=0) + :pt{pt}, depth{depth} {} + + Pt2 toDirection(int dir) const; + + bool pt_equal(Vec2 pt2) + { + return pt == pt2; + } + }; + + std::map _dirs {}; + std::queue _frontier {{Pt2::zero()}}; + +public: + PathFinder(MapIter from, MapIter to, int max_steps) + :_start{from}, + _end{to.point()}, + _max{max_steps} {} + + bool run(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab1/placeable.hpp b/8303/Parfentev_Leonid/lab1/placeable.hpp new file mode 100644 index 000000000..b7da086ea --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/placeable.hpp @@ -0,0 +1,34 @@ +#ifndef _H_PLACEABLE_HPP +#define _H_PLACEABLE_HPP + +class Placeable { + bool _placed = false; + Vec2 _pos; + +public: + bool + hasPosition() const { return _placed; } + + const Vec2 & + position() const + { + return _pos; + } + + void + setPosition(const Vec2 &pos) + { + _pos = pos; + _placed = true; + } + + void + unsetPosition() + { + _placed = false; + } + + virtual ~Placeable() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab1/point.cpp b/8303/Parfentev_Leonid/lab1/point.cpp new file mode 100644 index 000000000..bce7b3325 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/point.cpp @@ -0,0 +1,29 @@ +#include + +#include "point.hpp" + +double +Vec2::length() const +{ + return sqrt(_x*_x + _y*_y); +} + +double +Vec2::distance(const Vec2 &pt) const +{ + return delta(pt).length(); +} + +bool +Vec2::unit() const +{ + return (_x || _y) + && abs(_x) <= 1 + && abs(_y) <= 1; +} + +bool +Vec2::adjacent(const Vec2 &pt) const +{ + return delta(pt).unit(); +} diff --git a/8303/Parfentev_Leonid/lab1/point.hpp b/8303/Parfentev_Leonid/lab1/point.hpp new file mode 100644 index 000000000..8eec01d76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/point.hpp @@ -0,0 +1,40 @@ +#ifndef _H_POINT_HPP +#define _H_POINT_HPP + +class Vec2 { + int _x, _y; + +public: + Vec2() :Vec2{0, 0} {} + Vec2(int x, int y) :_x{x}, _y{y} {} + + int x() const { return _x; } + int y() const { return _y; } + + bool operator==(const Vec2 &pt) const + { + return _x == pt._x && _y == pt._y; + } + bool operator!=(const Vec2 &pt) const + { + return !(*this == pt); + } + + Vec2 delta(const Vec2 &o) const + { + return Vec2{_x - o._x, _y - o._y}; + } + + double length() const; + double distance(const Vec2 &pt) const; + + bool unit() const; + bool adjacent(const Vec2 &pt) const; + + Vec2 shifted(const Vec2 &dxy) const + { + return Vec2{_x + dxy._x, _y + dxy._y}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab1/ranged_units.hpp b/8303/Parfentev_Leonid/lab1/ranged_units.hpp new file mode 100644 index 000000000..24f296223 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/ranged_units.hpp @@ -0,0 +1,91 @@ +#ifndef _H_RANGED_UNITS_HPP +#define _H_RANGED_UNITS_HPP + +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class RangedAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + double _min_distance, _max_distance; + double _dist_pow; + + static double + distance(const Unit *u, MapIter to) + { + return to.point().distance(u->position()); + } + +public: + RangedAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow) + :_kind{kind}, + _base_damage{base_dmg}, + _min_distance{min_dist}, + _max_distance{max_dist}, + _dist_pow{dist_pow} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return dist >= _min_distance + && dist <= _max_distance; + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return std::make_pair( + _kind, + int(_base_damage + * u->relativeHealth() + / pow(dist, _dist_pow))); + } +}; + +class BasicRangedUnit: public Unit { +public: + BasicRangedUnit(int speed, + AttackKind attack_kind, + int base_dmg, + double max_dist, + double dist_pow, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new RangedAttack {attack_kind, base_dmg, + 1., max_dist, dist_pow}, + def, base_health} {} +}; + +namespace units { + class Archer: public BasicRangedUnit { + public: + Archer() :BasicRangedUnit{ + 2, + AttackKind::arrow, 50, 5., .20, + new BasicDefense {0.9}, + 40} {} + }; + + class Slinger: public BasicRangedUnit { + public: + Slinger() :BasicRangedUnit{ + 2, + AttackKind::stone, 60, 3., .30, + new BasicDefense {.09}, + 50} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab1/rectmap.cpp b/8303/Parfentev_Leonid/lab1/rectmap.cpp new file mode 100644 index 000000000..a9fcff46a --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/rectmap.cpp @@ -0,0 +1,53 @@ +#include "rectmap.hpp" + + +bool +RectMapIter::valid() const +{ + return x() >= 0 + && x() < _map->width() + && y() >= 0 + && y() < _map->height(); +} + +Cell & +RectMapIter::cell() const +{ + return _map->at(point()); +} + +// Vec2 +// RectMapIter::delta(const RectMapIter &o) const +// { +// return o.point().delta(point()); +// } + +void +RectMapIter::moveTo(Vec2 xy) +{ + _pt = xy; +} + +RectMapIter +RectMapIter::otherAt(Vec2 xy) const +{ + RectMapIter other = *this; + other.moveTo(xy); + return other; +} + +void +RectMapIter::advance(int d) +{ + int nx = x() + d, + w = _map->width(); + _pt = Vec2{nx % w, y() + nx / w}; +} + +RectMapIter +RectMapIter::advanced(int d) const +{ + RectMapIter other = *this; + other.advance(d); + return other; +} diff --git a/8303/Parfentev_Leonid/lab1/rectmap.hpp b/8303/Parfentev_Leonid/lab1/rectmap.hpp new file mode 100644 index 000000000..b780bd000 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/rectmap.hpp @@ -0,0 +1,85 @@ +#ifndef _H_RECTMAP_HPP +#define _H_RECTMAP_HPP + +#include "point.hpp" +#include "placeable.hpp" + + +class Cell { + Placeable *_p = nullptr; + +public: + Cell() {} + + ~Cell() + { + delete _p; + } + + Placeable *placeable() const { return _p; } + void setPlaceable(Placeable *p) { _p = p; } +}; + +class RectMap; + +class RectMapIter { + RectMap *_map; + Vec2 _pt; + +public: + RectMapIter(RectMap *map, Vec2 pt) + :_map{map}, _pt{pt} {} + RectMapIter(RectMap *map, int x, int y) + :_map{map}, _pt{x, y} {} + + static RectMapIter makeNull() { return {nullptr, {0, 0}}; } + + bool operator==(const RectMapIter &o) const + { + return _map == o._map + && _pt == o._pt; + } + bool operator!=(const RectMapIter &o) const + { + return !(*this == o); + } + + int x() const { return _pt.x(); } + int y() const { return _pt.y(); } + Vec2 point() const { return _pt; } + + Cell &cell() const; + + bool null() const { return _map == nullptr; } + bool valid() const; + + // Vec2 delta(const RectMapIter &o) const; + + void moveTo(Vec2 xy); + RectMapIter otherAt(Vec2 xy) const; + + void advance(int d); + RectMapIter advanced(int d) const; +}; + +class RectMap { + const int _w, _h; + Cell * const _storage; + +public: + RectMap(int w, int h) + :_w{w}, _h{h}, _storage{new Cell [w * h]} {} + + int width() const { return _w; } + int height() const { return _h; } + + Cell &at(Vec2 pt) { return _storage[pt.x() + pt.y()*_w]; } + RectMapIter iterAt(Vec2 pt) { return RectMapIter{this, pt}; } + + ~RectMap() + { + delete[] _storage; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab1/report.pdf b/8303/Parfentev_Leonid/lab1/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0ccc9c36a65b5c85364ff9d73e9660cfb380a695 GIT binary patch literal 132954 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

HR1-938F}6<*IiColI3&;Ge=TE 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 literal 0 HcmV?d00001 diff --git a/8303/Parfentev_Leonid/lab1/unit.cpp b/8303/Parfentev_Leonid/lab1/unit.cpp new file mode 100644 index 000000000..ef46978a2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/unit.cpp @@ -0,0 +1,5 @@ +#include + +#include "unit.hpp" + +std::default_random_engine global_random {}; diff --git a/8303/Parfentev_Leonid/lab1/unit.hpp b/8303/Parfentev_Leonid/lab1/unit.hpp new file mode 100644 index 000000000..75fc66d17 --- /dev/null +++ b/8303/Parfentev_Leonid/lab1/unit.hpp @@ -0,0 +1,152 @@ +#ifndef _H_UNIT_HPP +#define _H_UNIT_HPP + +#include +#include +#include + +#include "map.hpp" + + +extern std::default_random_engine global_random; + + +class MovePolicy { +public: + virtual bool canMove(const Unit *u, MapIter to) =0; + virtual ~MovePolicy() {} +}; + +enum class AttackKind { + sword, spear, cavalry, arrow, stone, rock, bolt, +}; + +// NOTE: can’t do area damage +class AttackPolicy { +public: + virtual bool canAttackTo(const Unit *u, MapIter to) =0; + + virtual MapIter actualPosition(const Unit *, MapIter to) + { + return to; + } + + // returns kind and base damage + virtual std::pair + baseAttack(const Unit *u, MapIter to) =0; + + virtual ~AttackPolicy() {} +}; + +struct DamageSpec { + int base_damage, damage_spread; + + int evaluate() const + { + std::uniform_int_distribution<> + dist {-damage_spread, damage_spread}; + + return base_damage + dist(global_random); + } +}; + +class DefensePolicy { +protected: + static DamageSpec + make_spec(double base, double spread) + { + return DamageSpec{(int)base, (int)spread}; + } + + static DamageSpec + defense_level(double k, int dmg) + { + return make_spec(round(1.0*dmg/k), + round(0.25*dmg/k)); + } + + static DamageSpec + normal_defense(double dmg) + { + return defense_level(1.0, dmg); + } + +public: + // returns base damage and spread + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) =0; + + virtual ~DefensePolicy() {} +}; + +class Unit: public Placeable { + // Controls the lifetime of policies once they are given to the unit + // through the constructor. + MovePolicy *_move_policy; + AttackPolicy *_attack_policy; + DefensePolicy *_defence_policy; + + int _health, _base_health; + +public: + Unit(MovePolicy *move, + AttackPolicy *attack, + DefensePolicy *defense, + int base_health) + :_move_policy{move}, + _attack_policy{attack}, + _defence_policy{defense}, + _health{base_health}, + _base_health{base_health} {} + + ~Unit() + { + delete _move_policy; + delete _attack_policy; + delete _defence_policy; + } + + int + health() const { return _health; } + int + baseHealth() const { return _base_health; } + double + relativeHealth() const { return _health / (double)_base_health; } + bool + alive() const { return health() > 0; } + + void + takeDamage(int dmg) { _health -= dmg; } + + bool + canMove(MapIter to) const + { + return _move_policy->canMove(this, to); + } + + bool + canAttackTo(MapIter to) const + { + return _attack_policy->canAttackTo(this, to); + } + + MapIter + actualPosition(MapIter to) const + { + return _attack_policy->actualPosition(this, to); + } + + std::pair + baseAttack(MapIter to) const + { + return _attack_policy->baseAttack(this, to); + } + + DamageSpec + actualDamage(AttackKind kind, int base) const + { + return _defence_policy->actualDamage(this, kind, base); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/Makefile b/8303/Parfentev_Leonid/lab2/Makefile new file mode 100644 index 000000000..bff746c2f --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/Makefile @@ -0,0 +1,19 @@ +EXENAME = main +HEADERS = point.hpp placeable.hpp rectmap.hpp map.hpp pathfinder.hpp \ + unit.hpp common_policies.hpp melee_units.hpp ranged_units.hpp \ + catapult_units.hpp demo.hpp event.hpp base.hpp object_w_health.hpp \ + landscape.hpp landscape_types.hpp neutral_object.hpp \ + neutral_object_types.hpp unit_factory.hpp +OBJFILES = point.o rectmap.o map.o pathfinder.o unit.o demo.o main.o \ + event.o base.o +LDLIBS = -lm + +all: $(EXENAME) + +$(OBJFILES): $(HEADERS) + +$(EXENAME): $(OBJFILES) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + $(RM) $(EXENAME) $(OBJFILES) diff --git a/8303/Parfentev_Leonid/lab2/base.cpp b/8303/Parfentev_Leonid/lab2/base.cpp new file mode 100644 index 000000000..1238ec33f --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/base.cpp @@ -0,0 +1,105 @@ +#include +#include +#include + +#include "unit.hpp" +#include "unit_factory.hpp" +#include "event.hpp" +#include "base.hpp" + + +bool +Base::canCreateUnit(const std::string &key) const +{ + return _cs->canCreate(key); +} + +Unit * +Base::createUnit(const std::string &key) +{ + if (unitsCount() == maxUnitsCount()) { + return nullptr; + } + + if (!_cs->canCreate(key)) { + return nullptr; + } + + Unit *u = _cs->create(key); + + if (addUnit(u) < 0) { + delete u; + return nullptr; + } + + return u; +} + +bool +Base::setMaxUnitsCount(int m) +{ + if (m < unitsCount()) { + return false; + } + _max_count = m; + return true; +} + +int +Base::addUnit(Unit *u) +{ + if (maxUnitsCount() >= 0 + && unitsCount() == maxUnitsCount()) { + return -1; + } + + _units[_next_idx] = u; + u->subscribe(this); + // u->emit(new UnitAddedEvent {u}); + + return _next_idx++; +} + +void +Base::removeUnit(Unit *u) +{ + u->unsubscribe(this); + + for (auto iter = _units.begin(); + iter != _units.end(); + ++iter) { + if (iter->second == u) { + _units.erase(iter); + break; + } + } +} + +Unit * +Base::getUnitById(int id) const +{ + auto iter = _units.find(id); + return (iter != _units.end()) + ? iter->second + : nullptr; +} + +void +Base::handle(Event *e) +{ + EventForwarder::handle(e); + + if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } else if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } +} + +Base::~Base() +{ + for (auto p: _units) { + p.second->unsubscribe(this); + } + delete _cs; +} diff --git a/8303/Parfentev_Leonid/lab2/base.hpp b/8303/Parfentev_Leonid/lab2/base.hpp new file mode 100644 index 000000000..7ad4c826a --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/base.hpp @@ -0,0 +1,89 @@ +#ifndef _H_BASE_HPP +#define _H_BASE_HPP + +#include +#include +#include + +#include "placeable.hpp" +#include "event.hpp" +#include "unit.hpp" + + +class UnitCreationStrategy { +public: + virtual bool canCreate(const std::string &key) const =0; + virtual std::vector keys() const =0; + virtual Unit *create(const std::string &key) =0; + + virtual ~UnitCreationStrategy() {} +}; + +class Base: public Placeable, + public EventForwarder { + + std::map _units {}; + int _next_idx = 0; + int _max_count = -1; + + UnitCreationStrategy *_cs; + +public: + Base(UnitCreationStrategy *cs) + :_cs{cs} {} + + bool + canCreateUnit(const std::string &key) const; + Unit * + createUnit(const std::string &key); + + int + unitsCount() const { return (int)_units.size(); } + bool + setMaxUnitsCount(int m); + int + maxUnitsCount() const { return _max_count; } + + int + addUnit(Unit *u); + void + removeUnit(Unit *u); + Unit * + getUnitById(int id) const; + + class unitsIter { + using real_iter_t = std::map::const_iterator; + real_iter_t _iter; + + public: + unitsIter(real_iter_t it) + :_iter{it} {} + + int id() const { return _iter->first; } + Unit *unit() const { return _iter->second; } + unitsIter &operator++() { ++_iter; return *this; } + unitsIter operator++(int) { unitsIter x{_iter}; ++x; return x; } + bool + operator==(const unitsIter &o) const + { + return _iter == o._iter; + } + bool + operator!=(const unitsIter &o) const + { + return !(*this == o); + } + }; + + unitsIter + unitsBegin() const { return unitsIter{_units.begin()}; } + unitsIter + unitsEnd() const { return unitsIter{_units.end()}; } + + virtual void + handle(Event *e) override; + + virtual ~Base() override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/catapult_units.hpp b/8303/Parfentev_Leonid/lab2/catapult_units.hpp new file mode 100644 index 000000000..26a217f7d --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/catapult_units.hpp @@ -0,0 +1,145 @@ +#ifndef _H_CATAPULT_UNITS_HPP +#define _H_CATAPULT_UNITS_HPP + +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" +#include "ranged_units.hpp" + + +class CatapultAttack: public RangedAttack { + double _spread_tang, _spread_normal; + + struct FVec2 { + double x, y; + + explicit FVec2(const Vec2 &v) + :x{(double)v.x()}, y{(double)v.y()} {} + + FVec2(double x, double y) + :x{x}, y{y} {} + + operator Vec2() const + { + return Vec2{int(round(x)), int(round(y))}; + } + + FVec2 + orthogonal() const { return {y, -x}; } + + FVec2 & + operator*=(double a) + { + x *= a; + y *= a; + return *this; + } + FVec2 + operator*(double a) const + { + FVec2 tmp {*this}; + return tmp *= a; + } + + FVec2 + normalized() const { return (*this) * (1/sqrt(x*x + y*y)); } + + FVec2 & + operator+=(const FVec2 &dxy) + { + x += dxy.x; + y += dxy.y; + return *this; + } + FVec2 + operator+(const FVec2 &dxy) const + { + FVec2 tmp{*this}; + return tmp += dxy; + } + + FVec2 + apply(double t, double n) const + { + return normalized() * t + + orthogonal().normalized() * n; + } + }; + +public: + CatapultAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n) + :RangedAttack{kind, base_dmg, min_dist, max_dist, dist_pow}, + _spread_tang{spread_t}, + _spread_normal{spread_n} {} + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + Vec2 dest = to.point(); + Vec2 delta = dest.delta(u->position()); + FVec2 fdelta {delta}; + + std::uniform_real_distribution<> + t_dist {-_spread_tang, _spread_tang}, + n_dist {-_spread_normal, _spread_normal}; + + double + t = t_dist(global_random), + n = n_dist(global_random); + + FVec2 result = fdelta.apply(t, n); + return to.shifted(Vec2{result}); + } +}; + +class BasicCatapultUnit: public Unit { +public: + BasicCatapultUnit(AttackKind attack_kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n, + int base_health) + :Unit{new BasicMovement {1}, + new CatapultAttack {attack_kind, + base_dmg, min_dist, max_dist, + dist_pow, spread_t, spread_n}, + DefenseLevelDeco::good_defense_deco( + AttackKind::arrow, + new BasicDefense {0.75}), + base_health} {} +}; + +namespace units { + class Onager: public BasicCatapultUnit { + public: + Onager() :BasicCatapultUnit{ + AttackKind::rock, 90, + 3, 10, 0.05, + 0.2, 0.1, + 30} {} + }; + + class BoltThrower: public BasicCatapultUnit { + public: + BoltThrower() :BasicCatapultUnit{ + AttackKind::bolt, 110, + 2, 6, 0.15, + 0.05, 0.05, + 20} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab2/common_policies.hpp b/8303/Parfentev_Leonid/lab2/common_policies.hpp new file mode 100644 index 000000000..4a99320aa --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/common_policies.hpp @@ -0,0 +1,181 @@ +#ifndef _H_COMMON_POLICIES_HPP +#define _H_COMMON_POLICIES_HPP + +#include + +#include "unit.hpp" +#include "map.hpp" +#include "pathfinder.hpp" + + +class BasicMovement: public MovePolicy { + int _steps_per_turn; + +public: + BasicMovement(int n) + :_steps_per_turn{n} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _steps_per_turn}; + return pf.run(); + } +}; + +class NestedMovement: public MovePolicy, + public MovePolicyContainer { +public: + using MovePolicyContainer::MovePolicyContainer; +}; + +class ModifyingMovePolicy: public NestedMovement { + int _max; + +public: + ModifyingMovePolicy(MovePolicy *p, int max_dist) + :NestedMovement{p}, _max{max_dist} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _max}; + if (!pf.run()) + return false; + + return movePolicy()->canMove(u, to); + } +}; + + + +class BasicDefense: public DefensePolicy { + double _lvl; + +public: + explicit BasicDefense(double level=1.0) + :_lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *, AttackKind, int base) override + { + return normal_defense(base); + } +}; + +class NestedDefense: public DefensePolicy, + public DefensePolicyContainer { +public: + using DefensePolicyContainer::DefensePolicyContainer; +}; + +class DefenseLevelDeco: public NestedDefense { + // Controls nested policy lifetime + AttackKind _kind; + double _lvl; + +public: + DefenseLevelDeco(DefensePolicy *p, + AttackKind kind, + double level) + :NestedDefense{p}, _kind{kind}, _lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + if (kind == _kind) + return defense_level(_lvl, base); + return defensePolicy()->actualDamage(u, kind, base); + } + + static DefenseLevelDeco * + defense_level_deco(AttackKind kind, double lvl, DefensePolicy *p) + { + return new DefenseLevelDeco {p, kind, lvl}; + } + + static DefenseLevelDeco * + good_defense_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 2.0, p); + } + + static DefenseLevelDeco * + vulnerability_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 0.5, p); + } +}; + +class MultiplierDefensePolicy: public NestedDefense { + double _mul; + +public: + MultiplierDefensePolicy(DefensePolicy *p, double mul) + :NestedDefense{p}, _mul{mul} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + DamageSpec ds = defensePolicy()->actualDamage(u, kind, base); + ds.scale(1/_mul); + return ds; + } +}; + + + +class AttackForbidden: public AttackPolicy { +public: + using AttackPolicy::AttackPolicy; + + virtual bool + canAttackTo(const Unit *, MapIter) override + { + return false; + } + + virtual std::pair + baseAttack(const Unit *, MapIter) override + { + return std::make_pair(AttackKind::invalid, 0); + } +}; + +class NestedAttack: public AttackPolicy, + public AttackPolicyContainer { +public: + using AttackPolicyContainer::AttackPolicyContainer; +}; + +class MultiplierAttackPolicy: public NestedAttack { + double _mul; + +public: + MultiplierAttackPolicy(AttackPolicy *p, double mul) + :NestedAttack{p}, _mul{mul} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return attackPolicy()->canAttackTo(u, to); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + auto ba = attackPolicy()->baseAttack(u, to); + return std::make_pair(ba.first, (int)(ba.second * _mul)); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab2/demo.cpp b/8303/Parfentev_Leonid/lab2/demo.cpp new file mode 100644 index 000000000..6448f7a1f --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/demo.cpp @@ -0,0 +1,538 @@ +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" + +#include "base.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "unit_factory.hpp" + + +static const std::map class_chars { +#define UNIT_ENTRY(T, x) {std::type_index{typeid(units::T)}, x} + UNIT_ENTRY(Swordsman, 'S'), + UNIT_ENTRY(Spearsman, 'P'), + UNIT_ENTRY(Cavalry, 'C'), + UNIT_ENTRY(Archer, 'A'), + UNIT_ENTRY(Slinger, 's'), + UNIT_ENTRY(Onager, 'O'), + UNIT_ENTRY(BoltThrower, 'B'), +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T, x) \ + {std::type_index{typeid(landscapes::T)}, x} + LANDSCAPE_ENTRY(Normal, '.'), + LANDSCAPE_ENTRY(Swamp, '='), + LANDSCAPE_ENTRY(Forest, '*'), +#undef LANDSCAPE_ENTRY +}; + +#define UNIT_ENTRY(T) {std::type_index{typeid(units::T)}, #T} +static const std::map unit_names { + UNIT_ENTRY(Swordsman), + UNIT_ENTRY(Spearsman), + UNIT_ENTRY(Cavalry), + UNIT_ENTRY(Archer), + UNIT_ENTRY(Slinger), + UNIT_ENTRY(Onager), + UNIT_ENTRY(BoltThrower), +}; +#undef UNIT_ENTRY + +std::ostream & +operator<<(std::ostream &os, Map *map) +{ + for (MapIter iter = map->begin(); + iter != map->end(); + iter.advance(1)) { + + if (Unit *u = iter.unit()) { + os << class_chars.at(std::type_index{typeid(*u)}); + } else { + auto *l = iter.landscape(); + os << class_chars.at(std::type_index{typeid(*l)}); + } + + os << ((iter.x() == map->width() - 1) ? "\n" : " "); + } + return os; +} + +std::ostream & +operator<<(std::ostream &os, Vec2 pt) +{ + return os << "{" << pt.x() << "," << pt.y() << "}"; +} + +std::ostream & +operator<<(std::ostream &os, Unit *u) +{ + return os << "a " + << unit_names.at(std::type_index{typeid(*u)}) + << " with " << u->health() << " HP at " + << u->position(); +} + +void +demo1() +{ + // create a map + auto *map = new Map {10, 10}; + + // create a few units + auto sw1_iter = map->addUnit(new units::Swordsman {}, {3, 3}); + map->addUnit(new units::Swordsman {}, {4, 4}); + + // write the map + std::cout << map << "\n"; + + // test the pathfinder + static const std::vector pts { + {3, 4}, + {3, 3}, + {5, 5}, + {5, 4}, + {5, 3}, + }; + for (auto pt: pts) { + std::cout << sw1_iter.unit() << " " + << ((sw1_iter.unit()->canMove(map->iterAt(pt))) + ? "can" : "can't") + << " move to " << pt << "\n"; + } + + // clean up + delete map; +} + +void +demo2() +{ + auto *map = new Map {10, 10}; + + auto c_iter = map->addUnit(new units::Cavalry {}, {3, 3}); + auto *u = c_iter.unit(); + + std::cout << map << "\n"; + + static const std::vector path { + {4, 5}, {6, 5}, {9, 5}, {8, 7}, {7, 9}, + }; + for (auto pt: path) { + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + std::cout << u << " can't move to " << pt << "!\n"; + break; + } + + std::cout << "moving " << u + << " to " << pt << "...\n"; + + c_iter = map->addUnit(map->removeUnitAt(c_iter), pt); + if (c_iter.null()) { + std::cout << "failed!\n"; + } + } + + std::cout << "\n" << map; + + delete map; +} + +void +demo3() +{ + auto *map = new Map {10, 10}; + map->setMaxUnitsCount(2); + + Unit *sw = new units::Swordsman {}; + Unit *ar = new units::Archer {}; + + map->addUnit(sw, {5, 0}); + map->addUnit(ar, {5, 9}); + + Unit *x = new units::Swordsman {}; + if (!map->addUnit(x, {1, 1}).null()) { + std::cout << "Added one more unit!\n"; + delete map->removeUnitAt({1, 1}); + } else { + std::cout << "Max units: " << map->maxUnitsCount() + << ", current units: " << map->unitsCount() + << "\n"; + delete x; + } + + std::cout << map; + + while (sw->alive() && ar->alive()) { + Vec2 from = sw->position(); + Vec2 to = from.shifted({0, 1}); + + std::cout << "\nmoving " << sw << " from " << from + << " to " << to << "...\n"; + + if (!sw->canMove(map->iterAt(to))) { + std::cout << "can't move\n"; + break; + } + + if (map->addUnit(map->removeUnitAt(from), to).null()) { + std::cout << "failed\n"; + break; + } + + std::cout << ar << " shoots at " << sw->position() << "...\n"; + + auto toi = map->iterAt(sw->position()); + if (!ar->canAttackTo(toi)) { + std::cout << "can't shoot\n"; + // it’s ok + } else { + auto pos = ar->actualPosition(toi); + if (Unit *targ = pos.unit()) { + auto dam = ar->baseAttack(toi); + + std::cout << "hits " << targ << "\n"; + + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + + std::cout << "now: " << targ << "\n"; + } + } + } +} + +class EventPrinter: public EventListener { + std::ostream *_os; + std::string _prefix; + +public: + EventPrinter(std::ostream &os, const std::string &prefix) + :_os{&os}, _prefix{prefix} {} + + virtual void + handle(Event *e) override + { + if (dynamic_cast(e)) + return; + + (*_os) << _prefix << ": "; + + if (auto *ee = dynamic_cast(e)) { + (*_os) << "Unit added: " << ee->unit() << "\n"; + + } else if (auto *ee = dynamic_cast(e)) { + (*_os) << "Unit died: " << ee->unit() << "\n"; + + } else if (auto *ee = dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " takes " + << ee->damage() << " health points of damage\n"; + + } else if (auto *ee = dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " gets healed by " + << ee->health() << " health points\n"; + + } else if (auto *ee = dynamic_cast(e)) { + (*_os) << "Unit " << ee->attacker() + << " attacked another unit " << ee->target() << "\n"; + + } else if (auto *ee = dynamic_cast(e)) { + (*_os) << "Unit " << ee->target() + << " was attacked by another unit " + << ee->attacker() << "\n"; + + } else { + (*_os) << "Unknown event\n"; + } + } +}; + +MapIter +doAttack(Unit *u, MapIter to) +{ + if (!u->canAttackTo(to)) { + return MapIter::makeNull(); + + } else { + auto pos = u->actualPosition(to); + + if (Unit *targ = pos.unit()) { + auto dam = u->baseAttack(to); + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + return pos; + } + + return MapIter::makeNull(); + } +} + +class FactoryTable: public UnitCreationStrategy { + std::map _tab {}; + + void + registerFactory(const std::string &key, UnitFactory *f) + { + _tab[key] = f; + } + +public: + FactoryTable() + { +#define REG(k, T) \ + registerFactory( \ + k, new SimpleUnitFactory {}) + REG("swordsman", Swordsman); + REG("spearsman", Spearsman); + REG("cavalry", Cavalry); + REG("archer", Archer); + REG("slinger", Slinger); + REG("onager", Onager); + REG("boltthrower", BoltThrower); +#undef REG + } + + virtual bool + canCreate(const std::string &key) const override + { + return _tab.find(key) != _tab.end(); + } + + virtual std::vector + keys() const override + { + std::vector keys {}; + for (auto p: _tab) { + keys.push_back(p.first); + } + return keys; + } + + virtual Unit * + create(const std::string &key) override + { + auto iter = _tab.find(key); + if (iter == _tab.end()) { + return nullptr; + } + + return iter->second->create(); + } + + virtual ~FactoryTable() override + { + for (auto p: _tab) { + delete p.second; + } + } +}; + +struct SimpleGame { + Map *map; + Base *b1, *b2; + EventPrinter *pr1, *pr2; + + explicit SimpleGame(int w=10, int h=10, + int x1=1, int y1=1, + int x2=9, int y2=9) + { + map = new Map {w, h}; + b1 = new Base {new FactoryTable {}}; + pr1 = new EventPrinter {std::cout, "Base 1"}; + + map->addBase(b1, {x1, y1}); + b1->subscribe(pr1); + + b2 = new Base {new FactoryTable {}}; + pr2 = new EventPrinter {std::cout, "Base 2"}; + + map->addBase(b2, {x2, y2}); + b2->subscribe(pr2); + } + + ~SimpleGame() + { + delete map; + delete pr1; + delete pr2; + } +}; + +void +demo4() +{ + SimpleGame g {}; + + Unit *u1 = new units::Swordsman {}; + Unit *u2 = new units::Swordsman {}; + + g.b1->addUnit(u1); + g.b2->addUnit(u2); + + g.map->addUnit(u1, {3, 3}); + g.map->addUnit(u2, {4, 3}); + + while (u1->alive() + && u2->alive()) { + doAttack(u1, g.map->iterAt(u2->position())); + if (u2->alive()) { + doAttack(u2, g.map->iterAt(u1->position())); + } + } +} + +MapIter +doMove(Map *map, const Unit *u, Vec2 pt) +{ + auto from = map->iterAt(u->position()); + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + return MapIter::makeNull(); + } + + return map->addUnit(map->removeUnitAt(from), pt); +} + +Unit * +setupUnit(Base *base, const std::string &k, Map *map, Vec2 pt) +{ + Unit *u = base->createUnit(k); + + if (map->addUnit(u, pt).null()) { + base->removeUnit(u); + delete u; + return nullptr; + } + + return u; +} + +void +demo5() +{ + SimpleGame g {}; + + // 2,2 .. 5,5: swamp + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + g.map->setLandscape(new landscapes::Swamp {}, + {2+i, 2+j}); + } + } + + // 1,7 .. 6,9: forest + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 5; ++i) { + g.map->setLandscape(new landscapes::Forest {}, + {1+i, 7+j}); + } + } + + auto u1 = setupUnit(g.b1, "swordsman", g.map, {2, 2}); + auto u2 = setupUnit(g.b2, "swordsman", g.map, {3, 2}); + + if (doMove(g.map, u1, {0, 2}).null()) { + std::cout << "Can't move " << u1 << " across 2 cells\n"; + } else { + std::cout << "Moved " << u1 << " across 2 cells \n"; + } + + std::cout << "u1: " << u1 << "\n"; + + if (doAttack(u1, g.map->iterAt(u2->position())).null()) { + std::cout << "Can't attack in swamp\n"; + } else { + std::cout << "Attacked in a swamp\n"; + } + + std::cout << "u2: " << u2 << "\n"; + + doMove(g.map, u1, {1, 2}); + doMove(g.map, u2, {2, 3}); + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + doAttack(u1, g.map->iterAt(u2->position())); + + auto u3 = setupUnit(g.b1, "spearsman", g.map, {3, 8}); + auto u4 = setupUnit(g.b1, "spearsman", g.map, {7, 8}); + auto u5 = setupUnit(g.b2, "onager", g.map, {5, 5}); + + while (u3->alive()) + doAttack(u5, g.map->iterAt(u3->position())); + + while (u4->alive()) + doAttack(u5, g.map->iterAt(u4->position())); +} + +bool +doUseObject(MapIter u_iter) +{ + auto *u = u_iter.unit(); + auto *n = u_iter.neutralObject(); + + if (!n->canUse(u, u_iter)) { + return false; + } + + n->onUse(u, u_iter); + return true; +} + +void +demo6() +{ + SimpleGame g {}; + + auto *u1 = setupUnit(g.b1, "slinger", g.map, {1, 5}); + auto *u2 = setupUnit(g.b2, "swordsman", g.map, {6, 5}); + + auto u1_iter = g.map->iterAt(u1->position()); + auto u2_iter = g.map->iterAt(u2->position()); + + g.map->addNeutralObject(new objects::Tower {}, {1, 5}); + g.map->addNeutralObject(new objects::HealingWell {}, {6, 5}); + + if (doAttack(u1, u2_iter).null()) { + std::cout << u1 << " can't reach " << u2 << "\n"; + } else { + std::cout << u1 << " somehow reached " << u2 << "\n"; + } + + if (doUseObject(u1_iter)) { + std::cout << u1 << " used the tower\n"; + } else { + std::cout << u1 << " can't use the tower\n"; + } + + if (doAttack(u1, u2_iter).null()) { + std::cout << u1 << " still can't reach " << u2 << "\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + if (doUseObject(u2_iter)) { + std::cout << u2 << " used the healing well\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; +} diff --git a/8303/Parfentev_Leonid/lab2/demo.hpp b/8303/Parfentev_Leonid/lab2/demo.hpp new file mode 100644 index 000000000..555a0b3e7 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/demo.hpp @@ -0,0 +1,11 @@ +#ifndef _H_DEMO_HPP +#define _H_DEMO_HPP + +void demo1(); +void demo2(); +void demo3(); +void demo4(); +void demo5(); +void demo6(); + +#endif diff --git a/8303/Parfentev_Leonid/lab2/event.cpp b/8303/Parfentev_Leonid/lab2/event.cpp new file mode 100644 index 000000000..1de27f8a9 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/event.cpp @@ -0,0 +1,31 @@ +#include "event.hpp" + +void +EventEmitter::emit_shared(Event *e) +{ + for (auto iter = _listeners.begin(); iter != _listeners.end();) { + auto *listener = *iter++; + // note: the listener may safely unsubscribe when handling the + // event. + listener->handle(e); + } +} + +void +EventEmitter::emit(Event *e) +{ + emit_shared(e); + delete e; +} + +void +EventEmitter::subscribe(EventListener *l) +{ + _listeners.insert(l); +} + +void +EventEmitter::unsubscribe(EventListener *l) +{ + _listeners.erase(l); +} diff --git a/8303/Parfentev_Leonid/lab2/event.hpp b/8303/Parfentev_Leonid/lab2/event.hpp new file mode 100644 index 000000000..1eab3d139 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/event.hpp @@ -0,0 +1,117 @@ +#ifndef _H_EVENT_HPP +#define _H_EVENT_HPP + +#include + +class Unit; +class EventListener; +class Event; + +class EventEmitter { + std::set _listeners {}; + +public: + void emit_shared(Event *e); + void emit(Event *e); + + void subscribe(EventListener *l); + void unsubscribe(EventListener *l); + + virtual ~EventEmitter() {} +}; + +class Event { +public: + virtual ~Event() {} +}; + + + +class UnitEvent: public Event { + Unit *_u; + +public: + UnitEvent(Unit *u) :_u{u} {} + + Unit *unit() const { return _u; } +}; + +class UnitDeathEvent: public UnitEvent { +public: + using UnitEvent::UnitEvent; +}; + +class UnitAddedEvent: public UnitEvent { +public: + using UnitEvent::UnitEvent; +}; + +class UnitLiveDeletedEvent: public UnitEvent { +public: + using UnitEvent::UnitEvent; +}; + +class UnitTakesDamageEvent: public Event { + Unit *_u; + int _dmg; + +public: + UnitTakesDamageEvent(Unit *u, int dmg) + :_u{u}, _dmg{dmg} {} + + Unit *unit() const { return _u; } + int damage() const { return _dmg; } +}; + +class UnitGetsHealedEvent: public Event { + Unit *_u; + int _hp; + +public: + UnitGetsHealedEvent(Unit *u, int hp) + :_u{u}, _hp{hp} {} + + Unit *unit() const { return _u; } + int health() const { return _hp; } +}; + +class AttackEvent: public Event { + Unit *_a, *_b; + +public: + AttackEvent(Unit *a, Unit *b) :_a{a}, _b{b} {} + + Unit *attacker() const { return _a; } + Unit *target() const { return _b; } +}; + +class UnitWasAttackedEvent: public AttackEvent { +public: + using AttackEvent::AttackEvent; +}; + +class UnitAttackedEvent: public AttackEvent { +public: + using AttackEvent::AttackEvent; +}; + + + +class EventListener { +public: + virtual void handle(Event *e) =0; + + virtual ~EventListener() {} +}; + +class EventForwarder: public EventEmitter, + public EventListener { +public: + virtual void + handle(Event *e) override + { + emit_shared(e); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/landscape.hpp b/8303/Parfentev_Leonid/lab2/landscape.hpp new file mode 100644 index 000000000..7245da6b3 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/landscape.hpp @@ -0,0 +1,25 @@ +#ifndef _H_LANDSCAPE_HPP +#define _H_LANDSCAPE_HPP + + +class Unit; + +class Landscape { +public: + virtual void onEnter(Unit *u) =0; + virtual void onLeave(Unit *u) =0; + + virtual ~Landscape() {} +}; + +namespace landscapes { + + class Normal: public Landscape { + public: + virtual void onEnter(Unit *) override {} + virtual void onLeave(Unit *) override {} + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab2/landscape_types.hpp b/8303/Parfentev_Leonid/lab2/landscape_types.hpp new file mode 100644 index 000000000..268521e35 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/landscape_types.hpp @@ -0,0 +1,70 @@ +#ifndef _H_LANDSCAPE_TYPES_HPP +#define _H_LANDSCAPE_TYPES_HPP + +#include "landscape.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "common_policies.hpp" + + +namespace landscapes { + + // Swamp: max speed is 1; attacking is forbidden + class Swamp: public Landscape { + ModifyingMovePolicy *_p; + AttackPolicy *_prev, *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _p = new ModifyingMovePolicy {u->movePolicy(), 1}; + u->setMovePolicy(_p); + + _prev = u->attackPolicy(); + _cur = new AttackForbidden {}; + u->setAttackPolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *mpc = u->findMoveContainerOf(_p)) { + mpc->setMovePolicy(_p->movePolicy()); + _p->setMovePolicy(nullptr); + delete _p; + _p = nullptr; + } + + // our policy might’ve been wrapped into something + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + delete _cur; + _cur = nullptr; + } + } + }; + + class Forest: public Landscape { + DefensePolicy *_prev; + MultiplierDefensePolicy *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _prev = u->defensePolicy(); + _cur = new MultiplierDefensePolicy {_prev, 2.0}; + u->setDefensePolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *dpc = u->findDefenseContainerOf(_cur)) { + dpc->setDefensePolicy(_prev); + _cur->setDefensePolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab2/main.cpp b/8303/Parfentev_Leonid/lab2/main.cpp new file mode 100644 index 000000000..d601161bb --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/main.cpp @@ -0,0 +1,25 @@ +#include + +#include "demo.hpp" + +int +main(void) +{ + std::cout << "Demo 1\n"; + demo1(); + + std::cout << "\nDemo 2\n"; + demo2(); + + std::cout << "\nDemo 3\n"; + demo3(); + + std::cout << "\nDemo 4\n"; + demo4(); + + std::cout << "\nDemo 5\n"; + demo5(); + + std::cout << "\nDemo 6\n"; + demo6(); +} diff --git a/8303/Parfentev_Leonid/lab2/map.cpp b/8303/Parfentev_Leonid/lab2/map.cpp new file mode 100644 index 000000000..ac83b9aa1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/map.cpp @@ -0,0 +1,144 @@ +#include "point.hpp" +#include "unit.hpp" +#include "placeable.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "landscape.hpp" +#include "map.hpp" + + +Map::Map(int w, int h) + :_rm{w, h} +{ + for (auto rmiter = _rm.iterAt({0, 0}); + rmiter.y() < _rm.height(); + rmiter.advance(1)) { + rmiter.cell().setLandscape(new landscapes::Normal {}); + } +} + +MapIter +Map::addUnit(Unit *u, Vec2 pt) +{ + if (u->hasPosition()) + return MapIter::makeNull(); + + if (_units_max >= 0 + && _units_count == _units_max) + return MapIter::makeNull(); + + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.unit()) + return MapIter::makeNull(); + + cell.setUnit(u); + u->setPosition(pt); + + ++_units_count; + + cell.landscape()->onEnter(u); + + return MapIter{rmiter}; +} + +Unit * +Map::removeUnitAt(Vec2 at) +{ + RectMapIter rmiter = _rm.iterAt(at); + Cell &cell = rmiter.cell(); + Unit *u = dynamic_cast(cell.unit()); + + if (u) { + --_units_count; + cell.landscape()->onLeave(u); + if (auto *n = dynamic_cast(cell.object())) { + n->onLeave(u); + } + + cell.setUnit(nullptr); + u->unsetPosition(); + } + + return u; +} + +MapIter +Map::addPlaceable(Placeable *p, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.object()) { + return MapIter::makeNull(); + } + + cell.setObject(p); + + return MapIter{rmiter}; +} + +MapIter +Map::addBase(Base *b, Vec2 pt) +{ + return addPlaceable(b, pt); +} + +MapIter +Map::addNeutralObject(NeutralObject *n, Vec2 pt) +{ + return addPlaceable(n, pt); +} + +void +Map::setLandscape(Landscape *l, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + cell.setLandscape(l); +} + +Unit * +MapIter::unit() const +{ + return _it.cell().unit(); +} + +bool +MapIter::moveUnitTo(MapIter to) const +{ + auto &sc = _it.cell(); + auto &dc = to._it.cell(); + + if (!sc.unit() + || dc.unit()) { + return false; + } + + Unit *u = sc.unit(); + + dc.setUnit(u); + u->setPosition(to.point()); + sc.setUnit(nullptr); + + return true; +} + +Base * +MapIter::base() const +{ + return dynamic_cast(_it.cell().object()); +} + +NeutralObject * +MapIter::neutralObject() const +{ + return dynamic_cast(_it.cell().object()); +} + +Landscape * +MapIter::landscape() const +{ + return _it.cell().landscape(); +} diff --git a/8303/Parfentev_Leonid/lab2/map.hpp b/8303/Parfentev_Leonid/lab2/map.hpp new file mode 100644 index 000000000..579ab9b75 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/map.hpp @@ -0,0 +1,106 @@ +#ifndef _H_MAP_HPP +#define _H_MAP_HPP + +#include "rectmap.hpp" + +// Map interface doesn’t know about cells -- instead, it only cares +// about certain kinds of Placeables + + +class Map; + +class Unit; +class Base; +class NeutralObject; + +class MapIter { + RectMapIter _it; + + friend class Map; + + MapIter(RectMapIter r) + :_it{r} {} + +public: + static MapIter makeNull() + { + return MapIter{RectMapIter::makeNull()}; + } + + bool operator==(const MapIter &o) const { return _it == o._it; } + bool operator!=(const MapIter &o) const { return _it != o._it; } + + int x() const { return _it.x(); } + int y() const { return _it.y(); } + Vec2 point() const { return _it.point(); } + + bool null() const { return _it.null(); } + bool valid() const { return _it.valid(); } + + void shift(Vec2 dxy) { _it.moveTo(point().shifted(dxy)); } + MapIter shifted(Vec2 dxy) const + { + return MapIter{_it.otherAt(point().shifted(dxy))}; + } + + void moveTo(Vec2 xy) { _it.moveTo(xy); } + MapIter otherAt(Vec2 xy) const + { + return MapIter{_it.otherAt(xy)}; + } + + void advance(int d) { _it.advance(d); } + MapIter advanced(int d) const + { + return MapIter{_it.advanced(d)}; + } + + Unit *unit() const; + bool moveUnitTo(MapIter to) const; + + Base *base() const; + NeutralObject *neutralObject() const; + Landscape *landscape() const; +}; + +class Map { + RectMap _rm; + int _units_count = 0; + int _units_max = -1; + + MapIter addPlaceable(Placeable *p, Vec2 pt); + +public: + Map(int w, int h); + + int width() const { return _rm.width(); } + int height() const { return _rm.height(); } + MapIter iterAt(Vec2 pt) { return MapIter{_rm.iterAt(pt)}; } + MapIter iterAt(int x, int y) { return iterAt({x, y}); } + + MapIter begin() { return iterAt(0, 0); } + MapIter end() { return iterAt(0, height()); } + + MapIter addUnit(Unit *u, Vec2 pt); + Unit *removeUnitAt(Vec2 at); + Unit *removeUnitAt(MapIter iter) + { + return removeUnitAt(iter.point()); + } + + MapIter addBase(Base *b, Vec2 pt); + MapIter addNeutralObject(NeutralObject *n, Vec2 pt); + void setLandscape(Landscape *l, Vec2 pt); + + int maxUnitsCount() const { return _units_max; } + bool setMaxUnitsCount(int x) + { + if (_units_count > x) + return false; + _units_max = x; + return true; + } + int unitsCount() const { return _units_count; } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/melee_units.hpp b/8303/Parfentev_Leonid/lab2/melee_units.hpp new file mode 100644 index 000000000..f36626401 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/melee_units.hpp @@ -0,0 +1,88 @@ +#ifndef _H_MELEE_UNITS_HPP +#define _H_MELEE_UNITS_HPP + +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class MeleeAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + +public: + MeleeAttack(AttackKind kind, int base_dmg) + :_kind{kind}, _base_damage{base_dmg} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return to.unit() != nullptr + && to.point().adjacent(u->position()); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter) + { + return std::make_pair( + _kind, + int(_base_damage * u->relativeHealth())); + } +}; + +class BasicMeleeUnit: public Unit { +public: + BasicMeleeUnit(int speed, + AttackKind attack_kind, + int base_dmg, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new MeleeAttack {attack_kind, base_dmg}, + def, base_health} {} +}; + +namespace units { + class Swordsman: public BasicMeleeUnit { + public: + Swordsman() :BasicMeleeUnit{ + 2, + AttackKind::sword, 40, + DefenseLevelDeco::good_defense_deco( + AttackKind::spear, + DefenseLevelDeco::vulnerability_deco( + AttackKind::cavalry, + new BasicDefense {})), + 100} {} + }; + + class Spearsman: public BasicMeleeUnit { + public: + Spearsman() :BasicMeleeUnit{ + 2, + AttackKind::spear, 75, + DefenseLevelDeco::good_defense_deco( + AttackKind::cavalry, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; + + class Cavalry: public BasicMeleeUnit { + public: + Cavalry() :BasicMeleeUnit{ + 3, + AttackKind::cavalry, 50, + DefenseLevelDeco::good_defense_deco( + AttackKind::sword, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab2/neutral_object.hpp b/8303/Parfentev_Leonid/lab2/neutral_object.hpp new file mode 100644 index 000000000..1807cabda --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/neutral_object.hpp @@ -0,0 +1,21 @@ +#ifndef _H_NEUTRAL_OBJECT_HPP +#define _H_NEUTRAL_OBJECT_HPP + +#include "placeable.hpp" +#include "unit.hpp" +#include "map.hpp" + + +class NeutralObject: public Placeable { +public: + virtual bool canUse(const Unit *, MapIter) { return true; } + virtual void onUse(Unit *u, MapIter at) =0; + + // It’s the object’s job to determine whether it was used by the + // leaving unit. + virtual void onLeave(Unit *) {}; + + virtual ~NeutralObject() {}; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/neutral_object_types.hpp b/8303/Parfentev_Leonid/lab2/neutral_object_types.hpp new file mode 100644 index 000000000..ac4528609 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/neutral_object_types.hpp @@ -0,0 +1,184 @@ +#ifndef _H_NEUTRAL_OBJECT_TYPES_HPP +#define _H_NEUTRAL_OBJECT_TYPES_HPP + +#include + +#include "neutral_object.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "ranged_units.hpp" +#include "common_policies.hpp" + + +class ExtendedShootingRange: public NestedAttack { + double _delta; + +public: + ExtendedShootingRange(AttackPolicy *p, double delta) + :NestedAttack{p}, _delta{delta} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = to.point().distance(u->position()); + auto *a = dynamic_cast(attackPolicy()); + return dist >= a->minRange() + && dist <= (a->maxRange() + _delta); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + return attackPolicy()->baseAttack(u, to); + } +}; + + + +namespace objects { + + class HealingWell: public NeutralObject { + public: + virtual void + onUse(Unit *u, MapIter) override + { + u->heal(25); + } + }; + + class Tower: public NeutralObject { + AttackPolicy *_prev; + ExtendedShootingRange *_cur = nullptr; + + public: + virtual bool + canUse(const Unit *u, MapIter) override + { + return dynamic_cast(u); + } + + virtual void + onUse(Unit *u, MapIter) override + { + _prev = u->attackPolicy(); + _cur = new ExtendedShootingRange {_prev, 5}; + u->setAttackPolicy(_cur); + } + + virtual void + onLeave(Unit *u) override + { + if (_cur == nullptr) { + return; + } + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + _cur->setAttackPolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + }; + + class TunnelsEntrance: public NeutralObject { + public: + virtual void + onUse(Unit *, MapIter at) override + { + static const int w = 5; + + int max_n = 0; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (iter.unit() == nullptr) { + ++max_n; + } + } + } + + std::uniform_int_distribution<> distr {0, max_n-1}; + int n = distr(global_random); + + MapIter dest = MapIter::makeNull(); + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (iter.unit() != nullptr) { + continue; + } + if (!--n) { + dest = iter; + break; + } + } + } + + at.moveUnitTo(dest); + } + }; + + class WeaponSmiths: public NeutralObject { + public: + class UnitFilter { + public: + virtual bool + applicable(const Unit *u) =0; + }; + + template + class SimpleUnitFilter: public UnitFilter { + public: + virtual bool + applicable(const Unit *u) override + { + return dynamic_cast(u); + } + }; + + private: + double _mul; + UnitFilter *_filter; + + public: + explicit WeaponSmiths(double mul, UnitFilter *filter=nullptr) + :_mul{mul}, _filter{filter} {} + + virtual bool + canUse(const Unit *u, MapIter) override + { + if (_filter + && !_filter->applicable(u)) { + return false; + } + + for (const AttackPolicyContainer *apc = u; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (dynamic_cast(apc)) { + return false; + } + } + + return true; + } + + virtual void + onUse(Unit *u, MapIter) override + { + auto *prev = u->attackPolicy(); + auto *new_p = new MultiplierAttackPolicy {prev, _mul}; + u->setAttackPolicy(new_p); + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab2/object_w_health.hpp b/8303/Parfentev_Leonid/lab2/object_w_health.hpp new file mode 100644 index 000000000..e3db6d050 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/object_w_health.hpp @@ -0,0 +1,30 @@ +#ifndef _H_OBJECT_W_HEALTH_HPP +#define _H_OBJECT_W_HEALTH_HPP + + +class ObjectWithHealth { + int _health, _base_health; + +public: + ObjectWithHealth(int base_health) + :_health{base_health}, + _base_health{base_health} {} + + int + health() const { return _health; } + int + baseHealth() const { return _base_health; } + double + relativeHealth() const { return _health / (double)_base_health; } + bool + alive() const { return health() > 0; } + + virtual void + heal(int hp) { _health += hp; } + + virtual void + takeDamage(int dmg) { _health -= dmg; } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab2/pathfinder.cpp b/8303/Parfentev_Leonid/lab2/pathfinder.cpp new file mode 100644 index 000000000..865f34de2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/pathfinder.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "map.hpp" +#include "point.hpp" +#include "pathfinder.hpp" + + +PathFinder::Pt2 +PathFinder::Pt2::toDirection(int dir) const +{ + // assert(dir >= 0 && dir < 8); + + int d1 = (dir + 1) % 8; + int dx = (d1 % 4 == 3) ? 0 : (d1 < 4) ? 1 : -1, + dy = (dir % 4 == 0) ? 0 : (dir < 4) ? 1 : -1; + + return Pt2{pt.shifted({dx, dy}), depth + 1}; +} + +bool +PathFinder::run() +{ + Vec2 start_pt = _start.point(); + + while (!_frontier.empty()) { + Pt2 current = _frontier.front(); + _frontier.pop(); + + if (start_pt.shifted(current.pt) == _end) + return true; + + if (_max >= 0 + && current.depth >= _max) + continue; + + Vec2WithCmp cur_v2wc {current.pt}; + auto iter = _dirs.find(cur_v2wc); + if (iter == _dirs.end()) + _dirs[cur_v2wc] = -1; + + for (int i = 0; i < 8; ++i) { + Pt2 shifted_delta = current.toDirection(i); + + Vec2WithCmp sh_v2wc {shifted_delta.pt}; + auto iter = _dirs.find(sh_v2wc); + if (iter != _dirs.end()) + continue; + + MapIter shifted = _start.shifted(shifted_delta.pt); + if (!shifted.valid() + || shifted.unit()) + continue; + + _frontier.push(shifted_delta); + } + } + + return false; +} diff --git a/8303/Parfentev_Leonid/lab2/pathfinder.hpp b/8303/Parfentev_Leonid/lab2/pathfinder.hpp new file mode 100644 index 000000000..d4c614aae --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/pathfinder.hpp @@ -0,0 +1,56 @@ +#ifndef _H_PATHFINDER_HPP +#define _H_PATHFINDER_HPP + +#include +#include + +#include "point.hpp" +#include "map.hpp" + + +class PathFinder { + MapIter _start; + Vec2 _end; + int _max; + + struct Vec2WithCmp { + Vec2 v; + + bool operator<(Vec2WithCmp o) const + { + if (v.y() == o.v.y()) + return v.x() < o.v.x(); + return v.y() < o.v.y(); + } + }; + + struct Pt2 { + Vec2 pt; + int depth; + + static Pt2 zero() { return {Vec2{0, 0}, 0}; } + + Pt2(Vec2 pt, int depth=0) + :pt{pt}, depth{depth} {} + + Pt2 toDirection(int dir) const; + + bool pt_equal(Vec2 pt2) + { + return pt == pt2; + } + }; + + std::map _dirs {}; + std::queue _frontier {{Pt2::zero()}}; + +public: + PathFinder(MapIter from, MapIter to, int max_steps) + :_start{from}, + _end{to.point()}, + _max{max_steps} {} + + bool run(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/placeable.hpp b/8303/Parfentev_Leonid/lab2/placeable.hpp new file mode 100644 index 000000000..b7da086ea --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/placeable.hpp @@ -0,0 +1,34 @@ +#ifndef _H_PLACEABLE_HPP +#define _H_PLACEABLE_HPP + +class Placeable { + bool _placed = false; + Vec2 _pos; + +public: + bool + hasPosition() const { return _placed; } + + const Vec2 & + position() const + { + return _pos; + } + + void + setPosition(const Vec2 &pos) + { + _pos = pos; + _placed = true; + } + + void + unsetPosition() + { + _placed = false; + } + + virtual ~Placeable() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/point.cpp b/8303/Parfentev_Leonid/lab2/point.cpp new file mode 100644 index 000000000..bce7b3325 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/point.cpp @@ -0,0 +1,29 @@ +#include + +#include "point.hpp" + +double +Vec2::length() const +{ + return sqrt(_x*_x + _y*_y); +} + +double +Vec2::distance(const Vec2 &pt) const +{ + return delta(pt).length(); +} + +bool +Vec2::unit() const +{ + return (_x || _y) + && abs(_x) <= 1 + && abs(_y) <= 1; +} + +bool +Vec2::adjacent(const Vec2 &pt) const +{ + return delta(pt).unit(); +} diff --git a/8303/Parfentev_Leonid/lab2/point.hpp b/8303/Parfentev_Leonid/lab2/point.hpp new file mode 100644 index 000000000..8eec01d76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/point.hpp @@ -0,0 +1,40 @@ +#ifndef _H_POINT_HPP +#define _H_POINT_HPP + +class Vec2 { + int _x, _y; + +public: + Vec2() :Vec2{0, 0} {} + Vec2(int x, int y) :_x{x}, _y{y} {} + + int x() const { return _x; } + int y() const { return _y; } + + bool operator==(const Vec2 &pt) const + { + return _x == pt._x && _y == pt._y; + } + bool operator!=(const Vec2 &pt) const + { + return !(*this == pt); + } + + Vec2 delta(const Vec2 &o) const + { + return Vec2{_x - o._x, _y - o._y}; + } + + double length() const; + double distance(const Vec2 &pt) const; + + bool unit() const; + bool adjacent(const Vec2 &pt) const; + + Vec2 shifted(const Vec2 &dxy) const + { + return Vec2{_x + dxy._x, _y + dxy._y}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/ranged_units.hpp b/8303/Parfentev_Leonid/lab2/ranged_units.hpp new file mode 100644 index 000000000..1798f04ce --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/ranged_units.hpp @@ -0,0 +1,94 @@ +#ifndef _H_RANGED_UNITS_HPP +#define _H_RANGED_UNITS_HPP + +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class RangedAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + double _min_distance, _max_distance; + double _dist_pow; + + static double + distance(const Unit *u, MapIter to) + { + return to.point().distance(u->position()); + } + +public: + double minRange() const { return _min_distance; } + double maxRange() const { return _max_distance; } + + RangedAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow) + :_kind{kind}, + _base_damage{base_dmg}, + _min_distance{min_dist}, + _max_distance{max_dist}, + _dist_pow{dist_pow} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return dist >= _min_distance + && dist <= _max_distance; + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return std::make_pair( + _kind, + int(_base_damage + * u->relativeHealth() + / pow(dist, _dist_pow))); + } +}; + +class BasicRangedUnit: public Unit { +public: + BasicRangedUnit(int speed, + AttackKind attack_kind, + int base_dmg, + double max_dist, + double dist_pow, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new RangedAttack {attack_kind, base_dmg, + 1., max_dist, dist_pow}, + def, base_health} {} +}; + +namespace units { + class Archer: public BasicRangedUnit { + public: + Archer() :BasicRangedUnit{ + 2, + AttackKind::arrow, 50, 5., .20, + new BasicDefense {0.9}, + 40} {} + }; + + class Slinger: public BasicRangedUnit { + public: + Slinger() :BasicRangedUnit{ + 2, + AttackKind::stone, 60, 3., .30, + new BasicDefense {.09}, + 50} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab2/rectmap.cpp b/8303/Parfentev_Leonid/lab2/rectmap.cpp new file mode 100644 index 000000000..b82667c40 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/rectmap.cpp @@ -0,0 +1,55 @@ +#include "rectmap.hpp" + +#include "unit.hpp" + +Cell::~Cell() +{ + delete _u; + delete _obj; + delete _l; +} + +bool +RectMapIter::valid() const +{ + return x() >= 0 + && x() < _map->width() + && y() >= 0 + && y() < _map->height(); +} + +Cell & +RectMapIter::cell() const +{ + return _map->at(point()); +} + +void +RectMapIter::moveTo(Vec2 xy) +{ + _pt = xy; +} + +RectMapIter +RectMapIter::otherAt(Vec2 xy) const +{ + RectMapIter other = *this; + other.moveTo(xy); + return other; +} + +void +RectMapIter::advance(int d) +{ + int nx = x() + d, + w = _map->width(); + _pt = Vec2{nx % w, y() + nx / w}; +} + +RectMapIter +RectMapIter::advanced(int d) const +{ + RectMapIter other = *this; + other.advance(d); + return other; +} diff --git a/8303/Parfentev_Leonid/lab2/rectmap.hpp b/8303/Parfentev_Leonid/lab2/rectmap.hpp new file mode 100644 index 000000000..afe030552 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/rectmap.hpp @@ -0,0 +1,95 @@ +#ifndef _H_RECTMAP_HPP +#define _H_RECTMAP_HPP + +#include "point.hpp" +#include "placeable.hpp" +#include "landscape.hpp" + + +class Unit; + +class Cell { + Landscape *_l = nullptr; + Unit *_u = nullptr; + Placeable *_obj = nullptr; + +public: + Cell() {} + + ~Cell(); + + Landscape *landscape() const { return _l; } + void setLandscape(Landscape *l) + { + delete _l; + _l = l; + } + + Unit *unit() const { return _u; } + void setUnit(Unit *u) { _u = u; } + + Placeable *object() const { return _obj; } + void setObject(Placeable *p) { _obj = p; } +}; + +class RectMap; + +class RectMapIter { + RectMap *_map; + Vec2 _pt; + +public: + RectMapIter(RectMap *map, Vec2 pt) + :_map{map}, _pt{pt} {} + RectMapIter(RectMap *map, int x, int y) + :_map{map}, _pt{x, y} {} + + static RectMapIter makeNull() { return {nullptr, {0, 0}}; } + + bool operator==(const RectMapIter &o) const + { + return _map == o._map + && _pt == o._pt; + } + bool operator!=(const RectMapIter &o) const + { + return !(*this == o); + } + + int x() const { return _pt.x(); } + int y() const { return _pt.y(); } + Vec2 point() const { return _pt; } + + Cell &cell() const; + + bool null() const { return _map == nullptr; } + bool valid() const; + + void moveTo(Vec2 xy); + RectMapIter otherAt(Vec2 xy) const; + + void advance(int d); + RectMapIter advanced(int d) const; +}; + +class RectMap { + const int _w, _h; + Cell * const _storage; + +public: + RectMap(int w, int h) + :_w{w}, _h{h}, _storage{new Cell [w * h]} {} + + int width() const { return _w; } + int height() const { return _h; } + + Cell &at(Vec2 pt) { return _storage[pt.x() + pt.y()*_w]; } + RectMapIter iterAt(Vec2 pt) { return RectMapIter{this, pt}; } + + ~RectMap() + { + delete[] _storage; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/report.pdf b/8303/Parfentev_Leonid/lab2/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..561e35d123f31911f57845ef1e8c4c9d76a70c5e GIT binary patch literal 115828 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 ao%9`?+#HNepcwv_&eKDYl8VUwh59cZAXGB| literal 0 HcmV?d00001 diff --git a/8303/Parfentev_Leonid/lab2/unit.cpp b/8303/Parfentev_Leonid/lab2/unit.cpp new file mode 100644 index 000000000..ef46978a2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/unit.cpp @@ -0,0 +1,5 @@ +#include + +#include "unit.hpp" + +std::default_random_engine global_random {}; diff --git a/8303/Parfentev_Leonid/lab2/unit.hpp b/8303/Parfentev_Leonid/lab2/unit.hpp new file mode 100644 index 000000000..4206a06c3 --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/unit.hpp @@ -0,0 +1,253 @@ +#ifndef _H_UNIT_HPP +#define _H_UNIT_HPP + +#include +#include +#include + +#include "event.hpp" +#include "object_w_health.hpp" +#include "map.hpp" + + +extern std::default_random_engine global_random; + + +class MovePolicy { +public: + virtual bool canMove(const Unit *u, MapIter to) =0; + virtual ~MovePolicy() {} +}; + +enum class AttackKind { + invalid, sword, spear, cavalry, arrow, stone, rock, bolt, +}; + +// NOTE: can’t do area damage +class AttackPolicy { +public: + virtual bool canAttackTo(const Unit *u, MapIter to) =0; + + virtual MapIter actualPosition(const Unit *, MapIter to) + { + return to; + } + + // returns kind and base damage + virtual std::pair + baseAttack(const Unit *u, MapIter to) =0; + + virtual ~AttackPolicy() {} +}; + +struct DamageSpec { + int base_damage, damage_spread; + + void + scale(double k) + { + base_damage *= k; + damage_spread *= k; + } + + DamageSpec + scaled(double k) const + { + auto ds = *this; + ds.scale(k); + return ds; + } + + int evaluate() const + { + std::uniform_int_distribution<> + dist {-damage_spread, damage_spread}; + + return base_damage + dist(global_random); + } +}; + +class DefensePolicy { +protected: + static DamageSpec + make_spec(double base, double spread) + { + return DamageSpec{(int)base, (int)spread}; + } + + static DamageSpec + defense_level(double k, int dmg) + { + return make_spec(round(1.0*dmg/k), + round(0.25*dmg/k)); + } + + static DamageSpec + normal_defense(double dmg) + { + return defense_level(1.0, dmg); + } + +public: + // returns base damage and spread + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) =0; + + virtual ~DefensePolicy() {} +}; + +class MovePolicyContainer { + MovePolicy *_mp; + +public: + MovePolicyContainer(MovePolicy *mp) :_mp{mp} {} + + MovePolicy *movePolicy() const { return _mp; } + void setMovePolicy(MovePolicy *mp) + { + _mp = mp; + } + virtual ~MovePolicyContainer() { delete _mp; } + + MovePolicyContainer * + findMoveContainerOf(const MovePolicy *mp) + { + for (MovePolicyContainer *mpc = this; mpc; + mpc = dynamic_cast( + mpc->movePolicy())) { + if (mpc->movePolicy() == mp) { + return mpc; + } + } + return nullptr; + } +}; + +class DefensePolicyContainer { + DefensePolicy *_dp; + +public: + DefensePolicyContainer(DefensePolicy *dp) :_dp{dp} {} + + DefensePolicy *defensePolicy() const { return _dp; } + void setDefensePolicy(DefensePolicy *dp) + { + _dp = dp; + } + virtual ~DefensePolicyContainer() { delete _dp; } + + DefensePolicyContainer * + findDefenseContainerOf(const DefensePolicy *dp) + { + for (DefensePolicyContainer *dpc = this; dpc; + dpc = dynamic_cast( + dpc->defensePolicy())) { + if (dpc->defensePolicy() == dp) { + return dpc; + } + } + return nullptr; + } +}; + +class AttackPolicyContainer { + AttackPolicy *_ap; + +public: + AttackPolicyContainer(AttackPolicy *ap) :_ap{ap} {} + + AttackPolicy *attackPolicy() const { return _ap; } + void setAttackPolicy(AttackPolicy *ap) + { + _ap = ap; + } + virtual ~AttackPolicyContainer() { delete _ap; } + + AttackPolicyContainer * + findAttackContainerOf(const AttackPolicy *ap) + { + for (AttackPolicyContainer *apc = this; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (apc->attackPolicy() == ap) { + return apc; + } + } + return nullptr; + } +}; + +class Unit: public Placeable, + public ObjectWithHealth, + public EventEmitter, + public MovePolicyContainer, + public DefensePolicyContainer, + public AttackPolicyContainer { +public: + Unit(MovePolicy *move, + AttackPolicy *attack, + DefensePolicy *defense, + int base_health) + :ObjectWithHealth{base_health}, + MovePolicyContainer{move}, + DefensePolicyContainer{defense}, + AttackPolicyContainer{attack} {} + + virtual void + heal(int hp) + { + emit(new UnitGetsHealedEvent {this, hp}); + ObjectWithHealth::heal(hp); + } + + virtual void + takeDamage(int dmg) + { + emit(new UnitTakesDamageEvent {this, dmg}); + + ObjectWithHealth::takeDamage(dmg); + + if (!alive()) { + emit(new UnitDeathEvent {this}); + } + } + + bool + canMove(MapIter to) const + { + return movePolicy()->canMove(this, to); + } + + bool + canAttackTo(MapIter to) const + { + return attackPolicy()->canAttackTo(this, to); + } + + MapIter + actualPosition(MapIter to) const + { + return attackPolicy()->actualPosition(this, to); + } + + std::pair + baseAttack(MapIter to) const + { + return attackPolicy()->baseAttack(this, to); + } + + DamageSpec + actualDamage(AttackKind kind, int base) const + { + return defensePolicy()->actualDamage(this, kind, base); + } + + virtual ~Unit() override + { + if (alive()) { + emit(new UnitLiveDeletedEvent {this}); + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab2/unit_factory.hpp b/8303/Parfentev_Leonid/lab2/unit_factory.hpp new file mode 100644 index 000000000..5f1f475bd --- /dev/null +++ b/8303/Parfentev_Leonid/lab2/unit_factory.hpp @@ -0,0 +1,24 @@ +#ifndef _H_UNIT_FACTORY_HPP +#define _H_UNIT_FACTORY_HPP + +#include "unit.hpp" + + +class UnitFactory { +public: + virtual Unit *create() const =0; + + virtual ~UnitFactory() {} +}; + +template +class SimpleUnitFactory: public UnitFactory { +public: + virtual Unit * + create() const override + { + return new U {}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/Makefile b/8303/Parfentev_Leonid/lab3/Makefile new file mode 100644 index 000000000..d94084a84 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/Makefile @@ -0,0 +1,21 @@ +EXENAME = main +HEADERS = point.hpp placeable.hpp rectmap.hpp map.hpp pathfinder.hpp \ + unit.hpp common_policies.hpp melee_units.hpp ranged_units.hpp \ + catapult_units.hpp demo.hpp event.hpp base.hpp object_w_health.hpp \ + landscape.hpp landscape_types.hpp neutral_object.hpp \ + neutral_object_types.hpp unit_factory.hpp game.hpp \ + object_print.hpp event_printer.hpp iostream_player.hpp \ + mediator.hpp zombie_collector.hpp +OBJFILES = point.o rectmap.o map.o pathfinder.o unit.o demo.o main.o \ + event.o base.o game.o object_print.o iostream_player.o mediator.o +LDLIBS = -lm + +all: $(EXENAME) + +$(OBJFILES): $(HEADERS) + +$(EXENAME): $(OBJFILES) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + $(RM) $(EXENAME) $(OBJFILES) diff --git a/8303/Parfentev_Leonid/lab3/base.cpp b/8303/Parfentev_Leonid/lab3/base.cpp new file mode 100644 index 000000000..4322abdc0 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/base.cpp @@ -0,0 +1,121 @@ +#include +#include +#include + +#include "unit.hpp" +#include "unit_factory.hpp" +#include "event.hpp" +#include "base.hpp" +#include "mediator.hpp" + + +bool +Base::canCreateUnit(const std::string &key) const +{ + if (unitsCount() == maxUnitsCount()) { + return false; + } + + if (!_cs->canCreate(key)) { + return false; + } + + return true; +} + +int +Base::createUnit(const std::string &key, Mediator *m) +{ + if (!canCreateUnit(key)) { + return -1; + } + + if (m->infoAt(position()).unit()) { + return false; + } + + Unit *u = _cs->create(key); + int id = addUnit(u); + + if (id < 0) { + delete u; + return -1; + } + + if (!m->spawnUnit(u, position())) { + removeUnit(u); + delete u; + return -1; + } + + return id; +} + +bool +Base::setMaxUnitsCount(int m) +{ + if (m < unitsCount()) { + return false; + } + _max_count = m; + return true; +} + +int +Base::addUnit(Unit *u) +{ + if (maxUnitsCount() >= 0 + && unitsCount() == maxUnitsCount()) { + return -1; + } + + _units[_next_idx] = u; + u->subscribe(this); + u->emit(new events::UnitAdded {u}); + + return _next_idx++; +} + +void +Base::removeUnit(Unit *u) +{ + u->unsubscribe(this); + + for (auto iter = _units.begin(); + iter != _units.end(); + ++iter) { + if (iter->second == u) { + _units.erase(iter); + break; + } + } +} + +Unit * +Base::getUnitById(int id) const +{ + auto iter = _units.find(id); + return (iter != _units.end()) + ? iter->second + : nullptr; +} + +void +Base::handle(Event *e) +{ + EventForwarder::handle(e); + + if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } else if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } +} + +Base::~Base() +{ + for (auto p: _units) { + p.second->unsubscribe(this); + } + delete _cs; +} diff --git a/8303/Parfentev_Leonid/lab3/base.hpp b/8303/Parfentev_Leonid/lab3/base.hpp new file mode 100644 index 000000000..61c75dae8 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/base.hpp @@ -0,0 +1,96 @@ +#ifndef _H_BASE_HPP +#define _H_BASE_HPP + +#include +#include +#include + +#include "placeable.hpp" +#include "event.hpp" +#include "unit.hpp" +#include "mediator.hpp" + + +class UnitCreationStrategy { +public: + virtual bool canCreate(const std::string &key) const =0; + virtual std::vector keys() const =0; + virtual Unit *create(const std::string &key) =0; + + virtual ~UnitCreationStrategy() {} +}; + +class Base: public Placeable, + public EventForwarder { + + std::map _units {}; + int _next_idx = 0; + int _max_count = -1; + + UnitCreationStrategy *_cs; + +public: + Base(UnitCreationStrategy *cs) + :_cs{cs} {} + + bool + canCreateUnit(const std::string &key) const; + int + createUnit(const std::string &key, Mediator *m); + + int + unitsCount() const { return (int)_units.size(); } + bool + setMaxUnitsCount(int m); + int + maxUnitsCount() const { return _max_count; } + + int + addUnit(Unit *u); + void + removeUnit(Unit *u); + Unit * + getUnitById(int id) const; + + class unitsIter { + using real_iter_t = std::map::const_iterator; + real_iter_t _iter; + + public: + unitsIter(real_iter_t it) + :_iter{it} {} + + int id() const { return _iter->first; } + Unit *unit() const { return _iter->second; } + unitsIter &operator++() { ++_iter; return *this; } + unitsIter + operator++(int) + { + unitsIter x{_iter}; + ++*this; + return x; + } + bool + operator==(const unitsIter &o) const + { + return _iter == o._iter; + } + bool + operator!=(const unitsIter &o) const + { + return !(*this == o); + } + }; + + unitsIter + unitsBegin() const { return unitsIter{_units.begin()}; } + unitsIter + unitsEnd() const { return unitsIter{_units.end()}; } + + virtual void + handle(Event *e) override; + + virtual ~Base() override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/catapult_units.hpp b/8303/Parfentev_Leonid/lab3/catapult_units.hpp new file mode 100644 index 000000000..26a217f7d --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/catapult_units.hpp @@ -0,0 +1,145 @@ +#ifndef _H_CATAPULT_UNITS_HPP +#define _H_CATAPULT_UNITS_HPP + +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" +#include "ranged_units.hpp" + + +class CatapultAttack: public RangedAttack { + double _spread_tang, _spread_normal; + + struct FVec2 { + double x, y; + + explicit FVec2(const Vec2 &v) + :x{(double)v.x()}, y{(double)v.y()} {} + + FVec2(double x, double y) + :x{x}, y{y} {} + + operator Vec2() const + { + return Vec2{int(round(x)), int(round(y))}; + } + + FVec2 + orthogonal() const { return {y, -x}; } + + FVec2 & + operator*=(double a) + { + x *= a; + y *= a; + return *this; + } + FVec2 + operator*(double a) const + { + FVec2 tmp {*this}; + return tmp *= a; + } + + FVec2 + normalized() const { return (*this) * (1/sqrt(x*x + y*y)); } + + FVec2 & + operator+=(const FVec2 &dxy) + { + x += dxy.x; + y += dxy.y; + return *this; + } + FVec2 + operator+(const FVec2 &dxy) const + { + FVec2 tmp{*this}; + return tmp += dxy; + } + + FVec2 + apply(double t, double n) const + { + return normalized() * t + + orthogonal().normalized() * n; + } + }; + +public: + CatapultAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n) + :RangedAttack{kind, base_dmg, min_dist, max_dist, dist_pow}, + _spread_tang{spread_t}, + _spread_normal{spread_n} {} + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + Vec2 dest = to.point(); + Vec2 delta = dest.delta(u->position()); + FVec2 fdelta {delta}; + + std::uniform_real_distribution<> + t_dist {-_spread_tang, _spread_tang}, + n_dist {-_spread_normal, _spread_normal}; + + double + t = t_dist(global_random), + n = n_dist(global_random); + + FVec2 result = fdelta.apply(t, n); + return to.shifted(Vec2{result}); + } +}; + +class BasicCatapultUnit: public Unit { +public: + BasicCatapultUnit(AttackKind attack_kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n, + int base_health) + :Unit{new BasicMovement {1}, + new CatapultAttack {attack_kind, + base_dmg, min_dist, max_dist, + dist_pow, spread_t, spread_n}, + DefenseLevelDeco::good_defense_deco( + AttackKind::arrow, + new BasicDefense {0.75}), + base_health} {} +}; + +namespace units { + class Onager: public BasicCatapultUnit { + public: + Onager() :BasicCatapultUnit{ + AttackKind::rock, 90, + 3, 10, 0.05, + 0.2, 0.1, + 30} {} + }; + + class BoltThrower: public BasicCatapultUnit { + public: + BoltThrower() :BasicCatapultUnit{ + AttackKind::bolt, 110, + 2, 6, 0.15, + 0.05, 0.05, + 20} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/common_policies.hpp b/8303/Parfentev_Leonid/lab3/common_policies.hpp new file mode 100644 index 000000000..4a99320aa --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/common_policies.hpp @@ -0,0 +1,181 @@ +#ifndef _H_COMMON_POLICIES_HPP +#define _H_COMMON_POLICIES_HPP + +#include + +#include "unit.hpp" +#include "map.hpp" +#include "pathfinder.hpp" + + +class BasicMovement: public MovePolicy { + int _steps_per_turn; + +public: + BasicMovement(int n) + :_steps_per_turn{n} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _steps_per_turn}; + return pf.run(); + } +}; + +class NestedMovement: public MovePolicy, + public MovePolicyContainer { +public: + using MovePolicyContainer::MovePolicyContainer; +}; + +class ModifyingMovePolicy: public NestedMovement { + int _max; + +public: + ModifyingMovePolicy(MovePolicy *p, int max_dist) + :NestedMovement{p}, _max{max_dist} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _max}; + if (!pf.run()) + return false; + + return movePolicy()->canMove(u, to); + } +}; + + + +class BasicDefense: public DefensePolicy { + double _lvl; + +public: + explicit BasicDefense(double level=1.0) + :_lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *, AttackKind, int base) override + { + return normal_defense(base); + } +}; + +class NestedDefense: public DefensePolicy, + public DefensePolicyContainer { +public: + using DefensePolicyContainer::DefensePolicyContainer; +}; + +class DefenseLevelDeco: public NestedDefense { + // Controls nested policy lifetime + AttackKind _kind; + double _lvl; + +public: + DefenseLevelDeco(DefensePolicy *p, + AttackKind kind, + double level) + :NestedDefense{p}, _kind{kind}, _lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + if (kind == _kind) + return defense_level(_lvl, base); + return defensePolicy()->actualDamage(u, kind, base); + } + + static DefenseLevelDeco * + defense_level_deco(AttackKind kind, double lvl, DefensePolicy *p) + { + return new DefenseLevelDeco {p, kind, lvl}; + } + + static DefenseLevelDeco * + good_defense_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 2.0, p); + } + + static DefenseLevelDeco * + vulnerability_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 0.5, p); + } +}; + +class MultiplierDefensePolicy: public NestedDefense { + double _mul; + +public: + MultiplierDefensePolicy(DefensePolicy *p, double mul) + :NestedDefense{p}, _mul{mul} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + DamageSpec ds = defensePolicy()->actualDamage(u, kind, base); + ds.scale(1/_mul); + return ds; + } +}; + + + +class AttackForbidden: public AttackPolicy { +public: + using AttackPolicy::AttackPolicy; + + virtual bool + canAttackTo(const Unit *, MapIter) override + { + return false; + } + + virtual std::pair + baseAttack(const Unit *, MapIter) override + { + return std::make_pair(AttackKind::invalid, 0); + } +}; + +class NestedAttack: public AttackPolicy, + public AttackPolicyContainer { +public: + using AttackPolicyContainer::AttackPolicyContainer; +}; + +class MultiplierAttackPolicy: public NestedAttack { + double _mul; + +public: + MultiplierAttackPolicy(AttackPolicy *p, double mul) + :NestedAttack{p}, _mul{mul} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return attackPolicy()->canAttackTo(u, to); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + auto ba = attackPolicy()->baseAttack(u, to); + return std::make_pair(ba.first, (int)(ba.second * _mul)); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab3/demo.cpp b/8303/Parfentev_Leonid/lab3/demo.cpp new file mode 100644 index 000000000..983d8aba4 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/demo.cpp @@ -0,0 +1,578 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" + +#include "event.hpp" +#include "event_types.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "unit_factory.hpp" + +#include "game.hpp" +#include "event_printer.hpp" +#include "iostream_player.hpp" + + +void +demo1() +{ + // create a map + auto *map = new Map {10, 10}; + + // create a few units + auto sw1_iter = map->addUnit(new units::Swordsman {}, {3, 3}); + map->addUnit(new units::Swordsman {}, {4, 4}); + + // write the map + std::cout << map << "\n"; + + // test the pathfinder + static const std::vector pts { + {3, 4}, + {3, 3}, + {5, 5}, + {5, 4}, + {5, 3}, + }; + for (auto pt: pts) { + std::cout << sw1_iter.unit() << " " + << ((sw1_iter.unit()->canMove(map->iterAt(pt))) + ? "can" : "can't") + << " move to " << pt << "\n"; + } + + // clean up + delete map; +} + +void +demo2() +{ + auto *map = new Map {10, 10}; + + auto c_iter = map->addUnit(new units::Cavalry {}, {3, 3}); + auto *u = c_iter.unit(); + + std::cout << map << "\n"; + + static const std::vector path { + {4, 5}, {6, 5}, {9, 5}, {8, 7}, {7, 9}, + }; + for (auto pt: path) { + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + std::cout << u << " can't move to " << pt << "!\n"; + break; + } + + std::cout << "moving " << u + << " to " << pt << "...\n"; + + c_iter = map->addUnit(map->removeUnitAt(c_iter), pt); + if (c_iter.null()) { + std::cout << "failed!\n"; + } + } + + std::cout << "\n" << map; + + delete map; +} + +void +demo3() +{ + auto *map = new Map {10, 10}; + map->setMaxUnitsCount(2); + + Unit *sw = new units::Swordsman {}; + Unit *ar = new units::Archer {}; + + map->addUnit(sw, {5, 0}); + map->addUnit(ar, {5, 9}); + + Unit *x = new units::Swordsman {}; + if (!map->addUnit(x, {1, 1}).null()) { + std::cout << "Added one more unit!\n"; + delete map->removeUnitAt({1, 1}); + } else { + std::cout << "Max units: " << map->maxUnitsCount() + << ", current units: " << map->unitsCount() + << "\n"; + delete x; + } + + std::cout << map; + + while (sw->alive() && ar->alive()) { + Vec2 from = sw->position(); + Vec2 to = from.shifted({0, 1}); + + std::cout << "\nmoving " << sw << " from " << from + << " to " << to << "...\n"; + + if (!sw->canMove(map->iterAt(to))) { + std::cout << "can't move\n"; + break; + } + + if (map->addUnit(map->removeUnitAt(from), to).null()) { + std::cout << "failed\n"; + break; + } + + std::cout << ar << " shoots at " << sw->position() << "...\n"; + + auto toi = map->iterAt(sw->position()); + if (!ar->canAttackTo(toi)) { + std::cout << "can't shoot\n"; + // it’s ok + } else { + auto pos = ar->actualPosition(toi); + if (Unit *targ = pos.unit()) { + auto dam = ar->baseAttack(toi); + + std::cout << "hits " << targ << "\n"; + + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + + std::cout << "now: " << targ << "\n"; + } + } + } +} + +MapIter +doAttack(Unit *u, MapIter to) +{ + if (!u->canAttackTo(to)) { + return MapIter::makeNull(); + + } else { + auto pos = u->actualPosition(to); + + if (Unit *targ = pos.unit()) { + auto dam = u->baseAttack(pos); + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + return pos; + } + + return MapIter::makeNull(); + } +} + +class FactoryTable: public UnitCreationStrategy { + std::map _tab {}; + + void + registerFactory(const std::string &key, UnitFactory *f) + { + _tab[key] = f; + } + +public: + FactoryTable() + { +#define REG(k, T) \ + registerFactory( \ + k, new SimpleUnitFactory {}) + REG("swordsman", Swordsman); + REG("spearsman", Spearsman); + REG("cavalry", Cavalry); + REG("archer", Archer); + REG("slinger", Slinger); + REG("onager", Onager); + REG("boltthrower", BoltThrower); +#undef REG + } + + virtual bool + canCreate(const std::string &key) const override + { + return _tab.find(key) != _tab.end(); + } + + virtual std::vector + keys() const override + { + std::vector keys {}; + for (auto p: _tab) { + keys.push_back(p.first); + } + return keys; + } + + virtual Unit * + create(const std::string &key) override + { + auto iter = _tab.find(key); + if (iter == _tab.end()) { + return nullptr; + } + + return iter->second->create(); + } + + virtual ~FactoryTable() override + { + for (auto p: _tab) { + delete p.second; + } + } +}; + +struct SimpleGame { + Map *map; + Base *b1, *b2; + EventPrinter *pr; + + explicit SimpleGame(int w=10, int h=10, + int x1=1, int y1=1, + int x2=9, int y2=9) + { + pr = new EventPrinter {std::cout}; + + map = new Map {w, h}; + + b1 = new Base {new FactoryTable {}}; + pr->setPrefix(b1, "Base 1"); + + map->addBase(b1, {x1, y1}); + b1->subscribe(pr); + + b2 = new Base {new FactoryTable {}}; + pr->setPrefix(b2, "Base 2"); + + map->addBase(b2, {x2, y2}); + b2->subscribe(pr); + } + + ~SimpleGame() + { + delete map; + delete pr; + } +}; + +void +demo4() +{ + SimpleGame g {}; + + Unit *u1 = new units::Swordsman {}; + Unit *u2 = new units::Swordsman {}; + + g.b1->addUnit(u1); + g.b2->addUnit(u2); + + g.map->addUnit(u1, {3, 3}); + g.map->addUnit(u2, {4, 3}); + + while (u1->alive() + && u2->alive()) { + doAttack(u1, g.map->iterAt(u2->position())); + if (u2->alive()) { + doAttack(u2, g.map->iterAt(u1->position())); + } + } +} + +MapIter +doMove(Map *map, const Unit *u, Vec2 pt) +{ + auto from = map->iterAt(u->position()); + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + return MapIter::makeNull(); + } + + return map->addUnit(map->removeUnitAt(from), pt); +} + +Unit * +setupUnit(Base *base, const std::string &k, Mediator *m, Vec2 pt) +{ + Unit *u = base->getUnitById(base->createUnit(k, m)); + m->teleportUnit(u, pt); + return u; +} + +void +demo5() +{ + SimpleGame g {}; + + // 2,2 .. 5,5: swamp + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + g.map->setLandscape(new landscapes::Swamp {}, + {2+i, 2+j}); + } + } + + // 1,7 .. 6,9: forest + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 5; ++i) { + g.map->setLandscape(new landscapes::Forest {}, + {1+i, 7+j}); + } + } + + auto *m = new Mediator {g.map}; + auto u1 = setupUnit(g.b1, "swordsman", m, {2, 2}); + auto u2 = setupUnit(g.b2, "swordsman", m, {3, 2}); + + if (doMove(g.map, u1, {0, 2}).null()) { + std::cout << "Can't move " << u1 << " across 2 cells\n"; + } else { + std::cout << "Moved " << u1 << " across 2 cells \n"; + } + + std::cout << "u1: " << u1 << "\n"; + + if (doAttack(u1, g.map->iterAt(u2->position())).null()) { + std::cout << "Can't attack in swamp\n"; + } else { + std::cout << "Attacked in a swamp\n"; + } + + std::cout << "u2: " << u2 << "\n"; + + doMove(g.map, u1, {1, 2}); + doMove(g.map, u2, {2, 3}); + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + doAttack(u1, g.map->iterAt(u2->position())); + + auto u3 = setupUnit(g.b1, "spearsman", m, {3, 8}); + auto u4 = setupUnit(g.b1, "spearsman", m, {7, 8}); + auto u5 = setupUnit(g.b2, "onager", m, {5, 5}); + + while (u3->alive()) + doAttack(u5, g.map->iterAt(u3->position())); + + while (u4->alive()) + doAttack(u5, g.map->iterAt(u4->position())); + + std::cout << g.map; + + delete m; +} + +void +demo6() +{ + SimpleGame g {}; + + auto *m = new Mediator {g.map}; + auto *u1 = setupUnit(g.b1, "slinger", m, {1, 5}); + auto *u2 = setupUnit(g.b2, "swordsman", m, {6, 5}); + + g.map->addNeutralObject(new objects::Tower {}, {1, 5}); + g.map->addNeutralObject(new objects::HealingWell {}, {6, 5}); + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " can't reach " << u2 << "\n"; + } else { + std::cout << u1 << " somehow reached " << u2 << "\n"; + } + + if (m->useObject(u1)) { + std::cout << u1 << " used the tower\n"; + } else { + std::cout << u1 << " can't use the tower\n"; + } + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " still can't reach " << u2 << "\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + if (m->useObject(u2)) { + std::cout << u2 << " used the healing well\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; +} + +void +demo7() +{ + class MoverPlayer: public Player { + std::string _unit_type; + int _id = -1; + + public: + using Player::Player; + + MoverPlayer(std::string name, + std::string type) + :Player{name}, _unit_type{std::move(type)} {} + + virtual bool + takeTurn(Mediator *m, Base *b) override + { + if (_id >= 0) { + Unit *u = b->getUnitById(_id); + m->moveUnitTo(u, u->position().shifted({1, 0})); + } else { + _id = b->createUnit(_unit_type, m); + } + return true; + } + }; + + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + auto *b = new Base {new FactoryTable {}}; + map->addBase(b, {3, 3}); + int b_id = g.addBase(b); + + auto *p = new MoverPlayer {"Player 1", "swordsman"}; + g.setPlayer(b_id, p); + + for (int i = 0; i < 5; ++i) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo8() +{ + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + std::stringstream s1 {}; + s1 << "base\n" + << "create spearsman\n" + << "map 0 0 9 9\n" + << "move 0 3 5\n" + << "map 0 0 9 9\n" + << "describe 3 5\n"; + + auto *b1 = new Base {new FactoryTable {}}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + std::stringstream s2 {}; + s2 << "create archer\n" + << "attack 0 3 5\n"; + + auto *b2 = new Base {new FactoryTable {}}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo9() +{ + Map *map = new Map {10, 10}; + + auto *uf = (new objects + ::WeaponSmiths + ::SimpleUnitFilter {}); + auto *ws = new objects::WeaponSmiths {2.0, uf}; + map->addNeutralObject(ws, {3, 4}); + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + Base *b1 = new Base {new FactoryTable {}}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + Base *b2 = new Base {new FactoryTable {}}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + std::stringstream s1 {}; + std::stringstream s2 {}; + + s1 << "create onager\n" + << "move 0 3 4\n" + << "use 0\n" + << "create onager\n" + << "move 1 3 2\n" + << "attack 0 7 4\n" + << "attack 1 7 2\n" + << "create swordsman\n" + << "move 0 3 5\n" + << "move 2 3 4\n" + << "use 2\n"; + + s2 << "create swordsman\n" + << "move 0 7 4\n" + << "create swordsman\n" + << "move 1 7 2\n" + << "skip skip skip skip skip\n"; + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} diff --git a/8303/Parfentev_Leonid/lab3/demo.hpp b/8303/Parfentev_Leonid/lab3/demo.hpp new file mode 100644 index 000000000..ae8bc7a95 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/demo.hpp @@ -0,0 +1,14 @@ +#ifndef _H_DEMO_HPP +#define _H_DEMO_HPP + +void demo1(); +void demo2(); +void demo3(); +void demo4(); +void demo5(); +void demo6(); +void demo7(); +void demo8(); +void demo9(); + +#endif diff --git a/8303/Parfentev_Leonid/lab3/event.cpp b/8303/Parfentev_Leonid/lab3/event.cpp new file mode 100644 index 000000000..b7ff78ebd --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/event.cpp @@ -0,0 +1,31 @@ +#include "event.hpp" + +void +EventEmitter::emit_shared(Event *e) const +{ + for (auto iter = _listeners.begin(); iter != _listeners.end();) { + auto *listener = *iter++; + // note: the listener may safely unsubscribe when handling the + // event. + listener->handle(e); + } +} + +void +EventEmitter::emit(Event *e) const +{ + emit_shared(e); + delete e; +} + +void +EventEmitter::subscribe(EventListener *l) +{ + _listeners.insert(l); +} + +void +EventEmitter::unsubscribe(EventListener *l) +{ + _listeners.erase(l); +} diff --git a/8303/Parfentev_Leonid/lab3/event.hpp b/8303/Parfentev_Leonid/lab3/event.hpp new file mode 100644 index 000000000..fa2d1f541 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/event.hpp @@ -0,0 +1,62 @@ +#ifndef _H_EVENT_HPP +#define _H_EVENT_HPP + +#include + +class EventListener; +class Event; + +class EventEmitter { + std::set _listeners {}; + +public: + void emit_shared(Event *e) const; + void emit(Event *e) const; + + void subscribe(EventListener *l); + void unsubscribe(EventListener *l); + + virtual ~EventEmitter() {} +}; + +class Event { +public: + virtual ~Event() {} +}; + +class EventListener { +public: + virtual void handle(Event *e) =0; + + virtual ~EventListener() {} +}; + +class EventForwarder; + +namespace events { + + class Forwarded: public Event { + Event *_e; + EventForwarder *_f; + + public: + Forwarded(Event *e, EventForwarder *f) + :_e{e}, _f{f} {} + + Event *event() const { return _e; } + EventForwarder *forwarder() const { return _f; } + }; + +} + +class EventForwarder: public EventEmitter, + public EventListener { +public: + virtual void + handle(Event *e) override + { + emit(new events::Forwarded {e, this}); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/event_printer.hpp b/8303/Parfentev_Leonid/lab3/event_printer.hpp new file mode 100644 index 000000000..4648085a0 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/event_printer.hpp @@ -0,0 +1,127 @@ +#ifndef _H_EVENT_PRINTER_HPP +#define _H_EVENT_PRINTER_HPP + +#include +#include +#include +#include + +#include "event.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "player.hpp" +#include "object_print.hpp" + + +class EventPrinter: public EventListener { + std::ostream *_os; + bool _free_os; + + std::map _prefix_map {}; + int _base_idx = 0; + + std::string + makeName(const char *base, int idx) + { + std::ostringstream oss {}; + oss << base << " " << idx; + return oss.str(); + } + +public: + EventPrinter(std::ostream &os) + :_os{&os}, _free_os{false} {} + + EventPrinter(std::ostream *os) + :_os{os}, _free_os{true} {} + + void + setPrefix(EventForwarder *f, const std::string &s) + { + _prefix_map[f] = s; + } + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + auto iter = _prefix_map.find(ee->forwarder()); + if (iter != _prefix_map.end()) { + (*_os) << iter->second << ": "; + } + + return handle(ee->event()); + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit added: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit died: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " takes " + << ee->damage() << " health points of damage\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " gets healed by " + << ee->health() << " health points\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->attacker() + << " attacked another unit " << ee->target() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->target() + << " was attacked by another unit " + << ee->attacker() << "\n"; + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " moved from " << ee->sourcePos() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " used object " << ee->neutralObject() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "(Live unit " << ((void *)ee->unit()) + << " deleted)\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + auto name = makeName("Base", ++_base_idx); + setPrefix(ee->base(), name); + (*_os) << "New base: " << name << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << " over\n"; + + } else { + (*_os) << "Unknown event\n"; + } + } + + virtual ~EventPrinter() override + { + if (_free_os) { + delete _os; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/event_types.hpp b/8303/Parfentev_Leonid/lab3/event_types.hpp new file mode 100644 index 000000000..8de61313b --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/event_types.hpp @@ -0,0 +1,161 @@ +#ifndef _H_EVENT_TYPES_HPP +#define _H_EVENT_TYPES_HPP + +#include "point.hpp" +#include "event.hpp" + + +class Unit; +class Base; +class NeutralObject; +class Player; + + +class UnitEvent: public Event { + Unit *_u; + +public: + UnitEvent(Unit *u) :_u{u} {} + + Unit *unit() const { return _u; } +}; + +class AttackEvent: public Event { + Unit *_a, *_b; + +public: + AttackEvent(Unit *a, Unit *b) :_a{a}, _b{b} {} + + Unit *attacker() const { return _a; } + Unit *target() const { return _b; } +}; + +class BaseEvent: public Event { + Base *_b; + +public: + BaseEvent(Base *b) :_b{b} {} + + Base *base() const { return _b; } +}; + +class PlayerEvent: public Event { + Player *_p; + +public: + PlayerEvent(Player *p) :_p{p} {} + + Player *player() const { return _p; } +}; + +class UnitMoveEvent: public UnitEvent { + Vec2 _src; + +public: + UnitMoveEvent(Unit *u, Vec2 src) + :UnitEvent{u}, _src{src} {} + + Vec2 sourcePos() const { return _src; } +}; + + + +namespace events { + + class UnitDeath: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitAdded: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitLiveDeleted: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitTakesDamage: public UnitEvent { + int _dmg; + + public: + UnitTakesDamage(Unit *u, int dmg) + :UnitEvent{u}, _dmg{dmg} {} + + int damage() const { return _dmg; } + }; + + class UnitGetsHealed: public UnitEvent { + int _hp; + + public: + UnitGetsHealed(Unit *u, int hp) + :UnitEvent{u}, _hp{hp} {} + + int health() const { return _hp; } + }; + + class UnitWasAttacked: public AttackEvent { + public: + using AttackEvent::AttackEvent; + }; + + class UnitAttacked: public AttackEvent { + Vec2 _tc; + + public: + UnitAttacked(Unit *u, Vec2 tc, Unit *tu) + :AttackEvent{u, tu}, _tc{tc} {} + + Vec2 targetCoordinates() const { return _tc; } + }; + + class UnitMoved: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitTeleported: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitUsedObject: public UnitEvent { + NeutralObject *_n; + + public: + UnitUsedObject(Unit *u, NeutralObject *n) + :UnitEvent{u}, _n{n} {} + + NeutralObject *neutralObject() const { return _n; } + }; + + + + class BaseAdded: public BaseEvent { + public: + using BaseEvent::BaseEvent; + }; + + class BaseDestroyed: public BaseEvent { + public: + using BaseEvent::BaseEvent; + }; + + + + class TurnStarted: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + + class TurnOver: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/game.cpp b/8303/Parfentev_Leonid/lab3/game.cpp new file mode 100644 index 000000000..1daf422a4 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/game.cpp @@ -0,0 +1,89 @@ +#include "base.hpp" +#include "game.hpp" +#include "event.hpp" +#include "event_types.hpp" + + +int +Game::addBase(Base *base) +{ + base->subscribe(_coll); + base->subscribe(this); + + _recs.push_back(BaseRecord{base, nullptr}); + + emit(new events::BaseAdded {base}); + + return (int)(_recs.size() - 1); +} + +void +Game::setPlayer(int base_idx, Player *p) +{ + Player *&p_place = _recs[base_idx].player; + if (p_place) { + delete p_place; + --_players; + } + p_place = p; + if (p) { + ++_players; + } +} + +int +Game::basesCount() const +{ + return _recs.size(); +} + +int +Game::playersCount() const +{ + return _players; +} + +Base * +Game::baseByIdx(int idx) const +{ + return _recs[idx].base; +} + +void +Game::spin() +{ + auto &rec = _recs[_next]; + Player *p = rec.player; + Base *b = rec.base; + + if (p) { + emit(new events::TurnStarted {p}); + + bool res = p->takeTurn(_med, b); + + _coll->collect(_map); + + emit(new events::TurnOver {p}); + + if (!res) { + setPlayer(_next, nullptr); + } + } + + if (++_next == (int)_recs.size()) { + _next = 0; + } +} + +Game::~Game() +{ + for (int i = 0; i < (int)_recs.size(); ++i) { + setPlayer(i, nullptr); + auto &rec = _recs[i]; + rec.base->unsubscribe(this); + rec.base->unsubscribe(_coll); + } + + delete _coll; + delete _map; +} diff --git a/8303/Parfentev_Leonid/lab3/game.hpp b/8303/Parfentev_Leonid/lab3/game.hpp new file mode 100644 index 000000000..b63690d53 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/game.hpp @@ -0,0 +1,49 @@ +#ifndef _H_GAME_HPP +#define _H_GAME_HPP + +#include + +#include "event.hpp" +#include "map.hpp" +#include "base.hpp" +#include "player.hpp" +#include "zombie_collector.hpp" +#include "mediator.hpp" + + +class Game: public EventForwarder { + Map *_map; + Mediator *_med; + + struct BaseRecord { + Base *base; + Player *player; + }; + + std::vector _recs {}; + int _next = 0; + int _players = 0; + + ZombieCollector *_coll; + +public: + Game(Map *map) + :_map{map}, + _med{new Mediator {map}}, + _coll{new ZombieCollector {}} {} + + int addBase(Base *b); + void setPlayer(int base_idx, Player *p); + + int basesCount() const; + int playersCount() const; + + Base *baseByIdx(int idx) const; + + void spin(); + + ~Game(); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab3/iostream_player.cpp b/8303/Parfentev_Leonid/lab3/iostream_player.cpp new file mode 100644 index 000000000..de2bb852d --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/iostream_player.cpp @@ -0,0 +1,89 @@ +#include +#include + +#include "game.hpp" +#include "base.hpp" +#include "iostream_player.hpp" + + +void +IostreamPlayer::addCommand(const std::string &str, + IostreamCommand *cmd) +{ + _cmd_tab[str] = cmd; +} + +IostreamPlayer::IostreamPlayer(std::string name) + :Player{name} +{ + addCommand("move", new iostream_commands::Move {}); + addCommand("attack", new iostream_commands::Attack {}); + addCommand("use", new iostream_commands::Use {}); + addCommand("create", new iostream_commands::Create {}); + addCommand("base", new iostream_commands::FindBase {}); + addCommand("units", new iostream_commands::ListUnits {}); + addCommand("describe", new iostream_commands::DescribeAt {}); + addCommand("map", new iostream_commands::PrintMap {}); + addCommand("skip", new iostream_commands::Skip {}); +} + +bool +IostreamPlayer::takeTurn(Mediator *m, Base *b) +{ + for (;;) { + std::string cmd_name; + (*_is) >> cmd_name; + + if (_is->fail()) { + return false; + } + + auto iter = _cmd_tab.find(cmd_name); + if (iter == _cmd_tab.end()) { + (*_os) << "Unknown command: \"" << cmd_name << "\"\n"; + continue; + } + + if (iter->second->execute(this, m, b)) { + break; + } + } + + return true; +} + +int +IostreamPlayer::readInt() +{ + int x; + (*_is) >> x; + return x; +} + +Unit * +IostreamPlayer::readUnitId(Base *b) +{ + int id = readInt(); + Unit *u = b->getUnitById(id); + if (!u) { + (*_os) << "No such unit: " << id << "\n"; + } + + return u; +} + +Vec2 +IostreamPlayer::readVec2() +{ + int x, y; + (*_is) >> x >> y; + return {x, y}; +} + +std::string +IostreamPlayer::readString() +{ + std::string s; + (*_is) >> s; + return s; +} diff --git a/8303/Parfentev_Leonid/lab3/iostream_player.hpp b/8303/Parfentev_Leonid/lab3/iostream_player.hpp new file mode 100644 index 000000000..19f6e928d --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/iostream_player.hpp @@ -0,0 +1,252 @@ +#ifndef _H_STDIO_PLAYER_HPP +#define _H_STDIO_PLAYER_HPP + +#include +#include +#include +#include + +#include "point.hpp" +#include "game.hpp" + +#include "object_print.hpp" + + +class IostreamPlayer; + +class IostreamCommand { +public: + // -> whether to end turn + virtual bool execute(IostreamPlayer *p, Mediator *m, Base *b) =0; + + virtual ~IostreamCommand() {} +}; + +class IostreamPlayer: public Player { + std::ostream *_os = nullptr; + std::istream *_is = nullptr; + bool _free_os, _free_is; + + std::map _cmd_tab {}; + + void + addCommand(const std::string &str, + IostreamCommand *cmd); + +public: + IostreamPlayer(std::string name); + + void + setOstream(std::ostream *os) { _os = os; _free_is = true; } + void + setOstream(std::ostream &os) { _os = &os; _free_os = false; } + + std::ostream & + ostream() const { return *_os; } + + void + setIstream(std::istream *is) { _is = is; _free_is = true; } + void + setIstream(std::istream &is) { _is = &is; _free_is = false; } + + std::istream & + istream() const { return *_is; } + + virtual bool + takeTurn(Mediator *m, Base *b) override; + + int + readInt(); + + Unit * + readUnitId(Base *b); + + Vec2 + readVec2(); + + std::string + readString(); +}; + +namespace iostream_commands { + + class Move: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + if (!m->moveUnitTo(u, to)) { + p->ostream() << "Can't move unit " << u + << " to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Attack: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + if (!m->attackTo(u, to)) { + p->ostream() << "Unit " << u + << " can't attack to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Use: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + if (!u) + return false; + + if (!m->useObject(u)) { + p->ostream() << "Unit " << u + << " can't use any object there\n"; + return false; + } + + return true; + } + }; + + class Create: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + std::string s = p->readString(); + + if (!b->canCreateUnit(s)) { + p->ostream() << "Can't create unit of type " + << s << "\n"; + return false; + } + + int id = b->createUnit(s, m); + if (id < 0) { + p->ostream() << "Failed to create a unit of type " + << s << "\n"; + return false; + } + + p->ostream() << "New unit of type " << s + << ": " << id << "\n"; + return true; + } + }; + + class FindBase: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *, Base *b) override + { + p->ostream() << "Base: " << b << "\n"; + return false; + } + }; + + class ListUnits: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *, Base *b) override + { + p->ostream() << "Units:"; + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + p->ostream() << "- " << iter.id() + << ": " << iter.unit() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class DescribeAt: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *) override + { + auto pos = p->readVec2(); + auto info = m->infoAt(pos); + + p->ostream() << "At " << pos << "\n"; + p->ostream() + << "- Landscape: " << info.landscape() << "\n"; + + if (auto *b = info.base()) { + p->ostream() << "- Base: " << b << "\n"; + } + + if (auto *u = info.unit()) { + p->ostream() << "- Unit: " << u << "\n"; + } + + if (auto *n = info.neutralObject()) { + p->ostream() << "- Object: " << n << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class PrintMap: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *) override + { + auto from = p->readVec2(); + auto to = p->readVec2(); + + p->ostream() << "From " << from + << " to " << to << ":\n"; + + for (int y = from.y(); y < to.y(); ++y) { + for (int x = from.x(); x < to.x(); ++x) { + if (x != from.x()) { + p->ostream() << " "; + } + displayMapInfo(p->ostream(), m->infoAt({x, y})); + } + p->ostream() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class Skip: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *, Mediator *, Base *) override + { + return true; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/landscape.hpp b/8303/Parfentev_Leonid/lab3/landscape.hpp new file mode 100644 index 000000000..7245da6b3 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/landscape.hpp @@ -0,0 +1,25 @@ +#ifndef _H_LANDSCAPE_HPP +#define _H_LANDSCAPE_HPP + + +class Unit; + +class Landscape { +public: + virtual void onEnter(Unit *u) =0; + virtual void onLeave(Unit *u) =0; + + virtual ~Landscape() {} +}; + +namespace landscapes { + + class Normal: public Landscape { + public: + virtual void onEnter(Unit *) override {} + virtual void onLeave(Unit *) override {} + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/landscape_types.hpp b/8303/Parfentev_Leonid/lab3/landscape_types.hpp new file mode 100644 index 000000000..268521e35 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/landscape_types.hpp @@ -0,0 +1,70 @@ +#ifndef _H_LANDSCAPE_TYPES_HPP +#define _H_LANDSCAPE_TYPES_HPP + +#include "landscape.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "common_policies.hpp" + + +namespace landscapes { + + // Swamp: max speed is 1; attacking is forbidden + class Swamp: public Landscape { + ModifyingMovePolicy *_p; + AttackPolicy *_prev, *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _p = new ModifyingMovePolicy {u->movePolicy(), 1}; + u->setMovePolicy(_p); + + _prev = u->attackPolicy(); + _cur = new AttackForbidden {}; + u->setAttackPolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *mpc = u->findMoveContainerOf(_p)) { + mpc->setMovePolicy(_p->movePolicy()); + _p->setMovePolicy(nullptr); + delete _p; + _p = nullptr; + } + + // our policy might’ve been wrapped into something + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + delete _cur; + _cur = nullptr; + } + } + }; + + class Forest: public Landscape { + DefensePolicy *_prev; + MultiplierDefensePolicy *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _prev = u->defensePolicy(); + _cur = new MultiplierDefensePolicy {_prev, 2.0}; + u->setDefensePolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *dpc = u->findDefenseContainerOf(_cur)) { + dpc->setDefensePolicy(_prev); + _cur->setDefensePolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/main.cpp b/8303/Parfentev_Leonid/lab3/main.cpp new file mode 100644 index 000000000..742fe3e11 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/main.cpp @@ -0,0 +1,34 @@ +#include + +#include "demo.hpp" + +int +main(void) +{ + std::cout << "Demo 1\n"; + demo1(); + + std::cout << "\nDemo 2\n"; + demo2(); + + std::cout << "\nDemo 3\n"; + demo3(); + + std::cout << "\nDemo 4\n"; + demo4(); + + std::cout << "\nDemo 5\n"; + demo5(); + + std::cout << "\nDemo 6\n"; + demo6(); + + std::cout << "\nDemo 7\n"; + demo7(); + + std::cout << "\nDemo 8\n"; + demo8(); + + std::cout << "\nDemo 9\n"; + demo9(); +} diff --git a/8303/Parfentev_Leonid/lab3/map.cpp b/8303/Parfentev_Leonid/lab3/map.cpp new file mode 100644 index 000000000..c21541c17 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/map.cpp @@ -0,0 +1,155 @@ +#include "point.hpp" +#include "unit.hpp" +#include "placeable.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "landscape.hpp" +#include "map.hpp" + + +Map::Map(int w, int h) + :_rm{w, h} +{ + for (auto rmiter = _rm.iterAt({0, 0}); + rmiter.y() < _rm.height(); + rmiter.advance(1)) { + rmiter.cell().setLandscape(new landscapes::Normal {}); + } +} + +MapIter +Map::addUnit(Unit *u, Vec2 pt) +{ + if (u->hasPosition()) + return MapIter::makeNull(); + + if (_units_max >= 0 + && _units_count == _units_max) + return MapIter::makeNull(); + + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.unit()) + return MapIter::makeNull(); + + cell.setUnit(u); + u->setPosition(pt); + + ++_units_count; + + cell.landscape()->onEnter(u); + + return MapIter{rmiter}; +} + +Unit * +Map::removeUnitAt(Vec2 at) +{ + RectMapIter rmiter = _rm.iterAt(at); + Cell &cell = rmiter.cell(); + Unit *u = dynamic_cast(cell.unit()); + + if (u) { + --_units_count; + cell.landscape()->onLeave(u); + if (auto *n = dynamic_cast(cell.object())) { + n->onLeave(u); + } + + cell.setUnit(nullptr); + u->unsetPosition(); + } + + return u; +} + +MapIter +Map::addPlaceable(Placeable *p, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.object()) { + return MapIter::makeNull(); + } + + cell.setObject(p); + p->setPosition(pt); + + return MapIter{rmiter}; +} + +MapIter +Map::addBase(Base *b, Vec2 pt) +{ + return addPlaceable(b, pt); +} + +MapIter +Map::addNeutralObject(NeutralObject *n, Vec2 pt) +{ + return addPlaceable(n, pt); +} + +void +Map::setLandscape(Landscape *l, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + cell.setLandscape(l); +} + +MapInfo +Map::infoAt(Vec2 pt) const +{ + return MapInfo{&_rm.at(pt)}; +} + +Unit * +MapIter::unit() const +{ + return _it.cell().unit(); +} + +Base * +MapIter::base() const +{ + return dynamic_cast(_it.cell().object()); +} + +NeutralObject * +MapIter::neutralObject() const +{ + return dynamic_cast(_it.cell().object()); +} + +Landscape * +MapIter::landscape() const +{ + return _it.cell().landscape(); +} + +const Landscape * +MapInfo::landscape() const +{ + return _cell->landscape(); +} + +const Unit * +MapInfo::unit() const +{ + return _cell->unit(); +} + +const Base * +MapInfo::base() const +{ + return dynamic_cast(_cell->object()); +} + +const NeutralObject * +MapInfo::neutralObject() const +{ + return dynamic_cast(_cell->object()); +} diff --git a/8303/Parfentev_Leonid/lab3/map.hpp b/8303/Parfentev_Leonid/lab3/map.hpp new file mode 100644 index 000000000..eb150cf76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/map.hpp @@ -0,0 +1,119 @@ +#ifndef _H_MAP_HPP +#define _H_MAP_HPP + +#include "rectmap.hpp" + +// Map interface doesn’t know about cells -- instead, it only cares +// about certain kinds of Placeables + + +class Map; + +class Unit; +class Base; +class NeutralObject; + +class MapIter { + RectMapIter _it; + + friend class Map; + + MapIter(RectMapIter r) + :_it{r} {} + +public: + static MapIter makeNull() + { + return MapIter{RectMapIter::makeNull()}; + } + + bool operator==(const MapIter &o) const { return _it == o._it; } + bool operator!=(const MapIter &o) const { return _it != o._it; } + + int x() const { return _it.x(); } + int y() const { return _it.y(); } + Vec2 point() const { return _it.point(); } + + bool null() const { return _it.null(); } + bool valid() const { return _it.valid(); } + + void shift(Vec2 dxy) { _it.moveTo(point().shifted(dxy)); } + MapIter shifted(Vec2 dxy) const + { + return MapIter{_it.otherAt(point().shifted(dxy))}; + } + + void moveTo(Vec2 xy) { _it.moveTo(xy); } + MapIter otherAt(Vec2 xy) const + { + return MapIter{_it.otherAt(xy)}; + } + + void advance(int d) { _it.advance(d); } + MapIter advanced(int d) const + { + return MapIter{_it.advanced(d)}; + } + + Unit *unit() const; + Base *base() const; + NeutralObject *neutralObject() const; + Landscape *landscape() const; +}; + +class MapInfo { + const Cell *_cell; + +public: + MapInfo(const Cell *c) + :_cell{c} {} + + const Landscape *landscape() const; + const Unit *unit() const; + const Base *base() const; + const NeutralObject *neutralObject() const; +}; + +class Map { + RectMap _rm; + int _units_count = 0; + int _units_max = -1; + + MapIter addPlaceable(Placeable *p, Vec2 pt); + +public: + Map(int w, int h); + + int width() const { return _rm.width(); } + int height() const { return _rm.height(); } + MapIter iterAt(Vec2 pt) { return MapIter{_rm.iterAt(pt)}; } + MapIter iterAt(int x, int y) { return iterAt({x, y}); } + + MapIter begin() { return iterAt(0, 0); } + MapIter end() { return iterAt(0, height()); } + + MapInfo infoAt(Vec2 pt) const; + + MapIter addUnit(Unit *u, Vec2 pt); + Unit *removeUnitAt(Vec2 at); + Unit *removeUnitAt(MapIter iter) + { + return removeUnitAt(iter.point()); + } + + MapIter addBase(Base *b, Vec2 pt); + MapIter addNeutralObject(NeutralObject *n, Vec2 pt); + void setLandscape(Landscape *l, Vec2 pt); + + int maxUnitsCount() const { return _units_max; } + bool setMaxUnitsCount(int x) + { + if (_units_count > x) + return false; + _units_max = x; + return true; + } + int unitsCount() const { return _units_count; } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/mediator.cpp b/8303/Parfentev_Leonid/lab3/mediator.cpp new file mode 100644 index 000000000..348997877 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/mediator.cpp @@ -0,0 +1,111 @@ +#include "point.hpp" +#include "map.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "neutral_object.hpp" +#include "mediator.hpp" + + +MapInfo +Mediator::infoAt(Vec2 pt) +{ + return _map->infoAt(pt); +} + +Vec2 +Mediator::mapSize() +{ + return {_map->width(), + _map->height()}; +} + +bool +Mediator::moveUnitTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit() + || !u->canMove(ito)) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitMoved {u, from}); + return true; +} + +bool +Mediator::attackTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (!ito.unit() + || !u->canAttackTo(ito)) { + return false; + } + + auto pos = u->actualPosition(ito); + Unit *t = pos.unit(); + + u->emit(new events::UnitAttacked {u, pos.point(), t}); + + if (t) { + t->emit(new events::UnitWasAttacked {u, t}); + + auto damage_pair = u->baseAttack(pos); + auto damage_spec = t->actualDamage(damage_pair.first, + damage_pair.second); + int dmg = damage_spec.evaluate(); + + t->takeDamage(dmg); + } + + return true; +} + +bool +Mediator::useObject(Unit *u) +{ + auto iter = _map->iterAt(u->position()); + + NeutralObject *n = iter.neutralObject(); + + if (!n + || !n->canUse(u)) { + return false; + } + + n->onUse(u, this); + + u->emit(new events::UnitUsedObject {u, n}); + + return true; +} + +bool +Mediator::spawnUnit(Unit *u, Vec2 at) +{ + return !_map->addUnit(u, at).null(); +} + +bool +Mediator::teleportUnit(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit()) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitTeleported {u, from}); + return true; +} diff --git a/8303/Parfentev_Leonid/lab3/mediator.hpp b/8303/Parfentev_Leonid/lab3/mediator.hpp new file mode 100644 index 000000000..cc1549f65 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/mediator.hpp @@ -0,0 +1,31 @@ +#ifndef _H_MEDIATOR_HPP +#define _H_MEDIATOR_HPP + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" + + +class Mediator { + Map *_map; + +public: + Mediator(Map *map) + :_map{map} {} + + MapInfo infoAt(Vec2 pt); + + Vec2 mapSize(); + + bool moveUnitTo(Unit *u, Vec2 to); + + bool attackTo(Unit *u, Vec2 to); + + bool useObject(Unit *u); + + bool spawnUnit(Unit *u, Vec2 at); + bool teleportUnit(Unit *u, Vec2 to); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab3/melee_units.hpp b/8303/Parfentev_Leonid/lab3/melee_units.hpp new file mode 100644 index 000000000..f36626401 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/melee_units.hpp @@ -0,0 +1,88 @@ +#ifndef _H_MELEE_UNITS_HPP +#define _H_MELEE_UNITS_HPP + +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class MeleeAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + +public: + MeleeAttack(AttackKind kind, int base_dmg) + :_kind{kind}, _base_damage{base_dmg} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return to.unit() != nullptr + && to.point().adjacent(u->position()); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter) + { + return std::make_pair( + _kind, + int(_base_damage * u->relativeHealth())); + } +}; + +class BasicMeleeUnit: public Unit { +public: + BasicMeleeUnit(int speed, + AttackKind attack_kind, + int base_dmg, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new MeleeAttack {attack_kind, base_dmg}, + def, base_health} {} +}; + +namespace units { + class Swordsman: public BasicMeleeUnit { + public: + Swordsman() :BasicMeleeUnit{ + 2, + AttackKind::sword, 40, + DefenseLevelDeco::good_defense_deco( + AttackKind::spear, + DefenseLevelDeco::vulnerability_deco( + AttackKind::cavalry, + new BasicDefense {})), + 100} {} + }; + + class Spearsman: public BasicMeleeUnit { + public: + Spearsman() :BasicMeleeUnit{ + 2, + AttackKind::spear, 75, + DefenseLevelDeco::good_defense_deco( + AttackKind::cavalry, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; + + class Cavalry: public BasicMeleeUnit { + public: + Cavalry() :BasicMeleeUnit{ + 3, + AttackKind::cavalry, 50, + DefenseLevelDeco::good_defense_deco( + AttackKind::sword, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/neutral_object.hpp b/8303/Parfentev_Leonid/lab3/neutral_object.hpp new file mode 100644 index 000000000..bcb94331e --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/neutral_object.hpp @@ -0,0 +1,22 @@ +#ifndef _H_NEUTRAL_OBJECT_HPP +#define _H_NEUTRAL_OBJECT_HPP + +#include "placeable.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "mediator.hpp" + + +class NeutralObject: public Placeable { +public: + virtual bool canUse(const Unit *) { return true; } + virtual void onUse(Unit *u, Mediator *m) =0; + + // It’s the object’s job to determine whether it was used by the + // leaving unit. + virtual void onLeave(Unit *) {}; + + virtual ~NeutralObject() {}; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/neutral_object_types.hpp b/8303/Parfentev_Leonid/lab3/neutral_object_types.hpp new file mode 100644 index 000000000..203d967f6 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/neutral_object_types.hpp @@ -0,0 +1,187 @@ +#ifndef _H_NEUTRAL_OBJECT_TYPES_HPP +#define _H_NEUTRAL_OBJECT_TYPES_HPP + +#include + +#include "neutral_object.hpp" +#include "mediator.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "ranged_units.hpp" +#include "common_policies.hpp" + + +class ExtendedShootingRange: public NestedAttack { + double _delta; + +public: + ExtendedShootingRange(AttackPolicy *p, double delta) + :NestedAttack{p}, _delta{delta} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = to.point().distance(u->position()); + auto *a = dynamic_cast(attackPolicy()); + return dist >= a->minRange() + && dist <= (a->maxRange() + _delta); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + return attackPolicy()->baseAttack(u, to); + } +}; + + + +namespace objects { + + class HealingWell: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *) override + { + u->heal(25); + } + }; + + class Tower: public NeutralObject { + AttackPolicy *_prev; + ExtendedShootingRange *_cur = nullptr; + + public: + virtual bool + canUse(const Unit *u) override + { + return dynamic_cast(u); + } + + virtual void + onUse(Unit *u, Mediator *) override + { + _prev = u->attackPolicy(); + _cur = new ExtendedShootingRange {_prev, 5}; + u->setAttackPolicy(_cur); + } + + virtual void + onLeave(Unit *u) override + { + if (_cur == nullptr) { + return; + } + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + _cur->setAttackPolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + }; + + class TunnelsEntrance: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *m) override + { + static const int w = 5; + + Vec2 at = u->position(); + + int max_n = 0; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() == nullptr) { + ++max_n; + } + } + } + + std::uniform_int_distribution<> distr {0, max_n-1}; + int n = distr(global_random); + + Vec2 dest; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() != nullptr) { + continue; + } + if (!--n) { + dest = iter; + break; + } + } + } + + m->teleportUnit(u, dest); + } + }; + + class WeaponSmiths: public NeutralObject { + public: + class UnitFilter { + public: + virtual bool + applicable(const Unit *u) =0; + }; + + template + class SimpleUnitFilter: public UnitFilter { + public: + virtual bool + applicable(const Unit *u) override + { + return dynamic_cast(u); + } + }; + + private: + double _mul; + UnitFilter *_filter; + + public: + explicit WeaponSmiths(double mul, UnitFilter *filter=nullptr) + :_mul{mul}, _filter{filter} {} + + virtual bool + canUse(const Unit *u) override + { + if (_filter + && !_filter->applicable(u)) { + return false; + } + + for (const AttackPolicyContainer *apc = u; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (dynamic_cast(apc)) { + return false; + } + } + + return true; + } + + virtual void + onUse(Unit *u, Mediator *) override + { + auto *prev = u->attackPolicy(); + auto *new_p = new MultiplierAttackPolicy {prev, _mul}; + u->setAttackPolicy(new_p); + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/object_print.cpp b/8303/Parfentev_Leonid/lab3/object_print.cpp new file mode 100644 index 000000000..da59e1054 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/object_print.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "object_print.hpp" + + +#define UNIT_ENTRY(T) {std::type_index{typeid(units::T)}, #T} +static const std::map unit_names { + UNIT_ENTRY(Swordsman), + UNIT_ENTRY(Spearsman), + UNIT_ENTRY(Cavalry), + UNIT_ENTRY(Archer), + UNIT_ENTRY(Slinger), + UNIT_ENTRY(Onager), + UNIT_ENTRY(BoltThrower), +}; +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T) {std::type_index{typeid(landscapes::T)}, #T} +static const std::map landscape_names { + LANDSCAPE_ENTRY(Normal), + LANDSCAPE_ENTRY(Swamp), + LANDSCAPE_ENTRY(Forest), +}; +#undef LANDSCAPE_ENTRY + +#define N_OBJECT_ENTRY(T, n) {std::type_index{typeid(objects::T)}, n} +static const std::map objects_names { + N_OBJECT_ENTRY(HealingWell, "Healing Well"), + N_OBJECT_ENTRY(Tower, "Tower"), + N_OBJECT_ENTRY(TunnelsEntrance, "Tunnels Entrance"), + N_OBJECT_ENTRY(WeaponSmiths, "Weapon Smiths"), +}; +#undef N_OBJECT_ENTRY + + +std::ostream & +operator<<(std::ostream &os, Vec2 pt) +{ + return os << "{" << pt.x() << "," << pt.y() << "}"; +} + +std::ostream & +operator<<(std::ostream &os, const Unit *u) +{ + return os << "a " + << unit_names.at(std::type_index{typeid(*u)}) + << " with " << u->health() << " HP at " + << u->position(); +} + +std::ostream & +operator<<(std::ostream &os, const Landscape *l) +{ + return os << landscape_names.at(std::type_index{typeid(*l)}); +} + +std::ostream & +operator<<(std::ostream &os, const Base *b) +{ + os << "a Base with " << b->unitsCount(); + int m = b->maxUnitsCount(); + if (m >= 0) { + os << "/" << m; + } + + return os << " units at " << b->position(); +} + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n) +{ + return os << "a " + << objects_names.at(std::type_index{typeid(*n)}) + << " at " << n->position(); +} + + + +static const std::map class_chars { +#define UNIT_ENTRY(T, x) {std::type_index{typeid(units::T)}, x} + UNIT_ENTRY(Swordsman, 'S'), + UNIT_ENTRY(Spearsman, 'P'), + UNIT_ENTRY(Cavalry, 'C'), + UNIT_ENTRY(Archer, 'A'), + UNIT_ENTRY(Slinger, 's'), + UNIT_ENTRY(Onager, 'O'), + UNIT_ENTRY(BoltThrower, 'B'), +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T, x) \ + {std::type_index{typeid(landscapes::T)}, x} + LANDSCAPE_ENTRY(Normal, '.'), + LANDSCAPE_ENTRY(Swamp, '='), + LANDSCAPE_ENTRY(Forest, '*'), +#undef LANDSCAPE_ENTRY +}; + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info) +{ + if (const Unit *u = info.unit()) { + os << class_chars.at(std::type_index{typeid(*u)}); + } else if (info.base()) { + os << "+"; + } else if (info.neutralObject()) { + os << '#'; + } else { + auto *l = info.landscape(); + os << class_chars.at(std::type_index{typeid(*l)}); + } + + return os; +} + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1) +{ + for (int y = y0; y < y1; ++y) { + for (int x = x0; x < x1; ++x) { + displayMapInfo(os, map->infoAt({x, y})); + } + os << "\n"; + } + return os; +} + +std::ostream & +operator<<(std::ostream &os, const Map *map) +{ + return displayMap(os, map, 0, 0, map->width(), map->height()); +} diff --git a/8303/Parfentev_Leonid/lab3/object_print.hpp b/8303/Parfentev_Leonid/lab3/object_print.hpp new file mode 100644 index 000000000..8df10d966 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/object_print.hpp @@ -0,0 +1,40 @@ +#ifndef _H_OBJECT_PRINT_HPP +#define _H_OBJECT_PRINT_HPP + +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "neutral_object.hpp" + +std::ostream & +operator<<(std::ostream &os, Vec2 pt); + +std::ostream & +operator<<(std::ostream &os, const Unit *u); + +std::ostream & +operator<<(std::ostream &os, const Landscape *l); + +std::ostream & +operator<<(std::ostream &os, const Base *b); + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n); + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info); + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1); + +std::ostream & +operator<<(std::ostream &os, const Map *map); + +#endif diff --git a/8303/Parfentev_Leonid/lab3/object_w_health.hpp b/8303/Parfentev_Leonid/lab3/object_w_health.hpp new file mode 100644 index 000000000..e3db6d050 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/object_w_health.hpp @@ -0,0 +1,30 @@ +#ifndef _H_OBJECT_W_HEALTH_HPP +#define _H_OBJECT_W_HEALTH_HPP + + +class ObjectWithHealth { + int _health, _base_health; + +public: + ObjectWithHealth(int base_health) + :_health{base_health}, + _base_health{base_health} {} + + int + health() const { return _health; } + int + baseHealth() const { return _base_health; } + double + relativeHealth() const { return _health / (double)_base_health; } + bool + alive() const { return health() > 0; } + + virtual void + heal(int hp) { _health += hp; } + + virtual void + takeDamage(int dmg) { _health -= dmg; } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab3/pathfinder.cpp b/8303/Parfentev_Leonid/lab3/pathfinder.cpp new file mode 100644 index 000000000..865f34de2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/pathfinder.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "map.hpp" +#include "point.hpp" +#include "pathfinder.hpp" + + +PathFinder::Pt2 +PathFinder::Pt2::toDirection(int dir) const +{ + // assert(dir >= 0 && dir < 8); + + int d1 = (dir + 1) % 8; + int dx = (d1 % 4 == 3) ? 0 : (d1 < 4) ? 1 : -1, + dy = (dir % 4 == 0) ? 0 : (dir < 4) ? 1 : -1; + + return Pt2{pt.shifted({dx, dy}), depth + 1}; +} + +bool +PathFinder::run() +{ + Vec2 start_pt = _start.point(); + + while (!_frontier.empty()) { + Pt2 current = _frontier.front(); + _frontier.pop(); + + if (start_pt.shifted(current.pt) == _end) + return true; + + if (_max >= 0 + && current.depth >= _max) + continue; + + Vec2WithCmp cur_v2wc {current.pt}; + auto iter = _dirs.find(cur_v2wc); + if (iter == _dirs.end()) + _dirs[cur_v2wc] = -1; + + for (int i = 0; i < 8; ++i) { + Pt2 shifted_delta = current.toDirection(i); + + Vec2WithCmp sh_v2wc {shifted_delta.pt}; + auto iter = _dirs.find(sh_v2wc); + if (iter != _dirs.end()) + continue; + + MapIter shifted = _start.shifted(shifted_delta.pt); + if (!shifted.valid() + || shifted.unit()) + continue; + + _frontier.push(shifted_delta); + } + } + + return false; +} diff --git a/8303/Parfentev_Leonid/lab3/pathfinder.hpp b/8303/Parfentev_Leonid/lab3/pathfinder.hpp new file mode 100644 index 000000000..d4c614aae --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/pathfinder.hpp @@ -0,0 +1,56 @@ +#ifndef _H_PATHFINDER_HPP +#define _H_PATHFINDER_HPP + +#include +#include + +#include "point.hpp" +#include "map.hpp" + + +class PathFinder { + MapIter _start; + Vec2 _end; + int _max; + + struct Vec2WithCmp { + Vec2 v; + + bool operator<(Vec2WithCmp o) const + { + if (v.y() == o.v.y()) + return v.x() < o.v.x(); + return v.y() < o.v.y(); + } + }; + + struct Pt2 { + Vec2 pt; + int depth; + + static Pt2 zero() { return {Vec2{0, 0}, 0}; } + + Pt2(Vec2 pt, int depth=0) + :pt{pt}, depth{depth} {} + + Pt2 toDirection(int dir) const; + + bool pt_equal(Vec2 pt2) + { + return pt == pt2; + } + }; + + std::map _dirs {}; + std::queue _frontier {{Pt2::zero()}}; + +public: + PathFinder(MapIter from, MapIter to, int max_steps) + :_start{from}, + _end{to.point()}, + _max{max_steps} {} + + bool run(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/placeable.hpp b/8303/Parfentev_Leonid/lab3/placeable.hpp new file mode 100644 index 000000000..bd7f0ef74 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/placeable.hpp @@ -0,0 +1,36 @@ +#ifndef _H_PLACEABLE_HPP +#define _H_PLACEABLE_HPP + +#include "point.hpp" + +class Placeable { + bool _placed = false; + Vec2 _pos; + +public: + bool + hasPosition() const { return _placed; } + + const Vec2 & + position() const + { + return _pos; + } + + void + setPosition(const Vec2 &pos) + { + _pos = pos; + _placed = true; + } + + void + unsetPosition() + { + _placed = false; + } + + virtual ~Placeable() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/player.hpp b/8303/Parfentev_Leonid/lab3/player.hpp new file mode 100644 index 000000000..1f8a8ebe0 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/player.hpp @@ -0,0 +1,25 @@ +#ifndef _H_PLAYER_HPP +#define _H_PLAYER_HPP + +#include +#include + +#include "mediator.hpp" +#include "base.hpp" + + +class Player { + std::string _name; + +public: + Player(std::string name) + :_name{std::move(name)} {} + + const std::string &name() const { return _name; } + + virtual bool takeTurn(Mediator *m, Base *b) =0; + + virtual ~Player() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/point.cpp b/8303/Parfentev_Leonid/lab3/point.cpp new file mode 100644 index 000000000..bce7b3325 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/point.cpp @@ -0,0 +1,29 @@ +#include + +#include "point.hpp" + +double +Vec2::length() const +{ + return sqrt(_x*_x + _y*_y); +} + +double +Vec2::distance(const Vec2 &pt) const +{ + return delta(pt).length(); +} + +bool +Vec2::unit() const +{ + return (_x || _y) + && abs(_x) <= 1 + && abs(_y) <= 1; +} + +bool +Vec2::adjacent(const Vec2 &pt) const +{ + return delta(pt).unit(); +} diff --git a/8303/Parfentev_Leonid/lab3/point.hpp b/8303/Parfentev_Leonid/lab3/point.hpp new file mode 100644 index 000000000..8eec01d76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/point.hpp @@ -0,0 +1,40 @@ +#ifndef _H_POINT_HPP +#define _H_POINT_HPP + +class Vec2 { + int _x, _y; + +public: + Vec2() :Vec2{0, 0} {} + Vec2(int x, int y) :_x{x}, _y{y} {} + + int x() const { return _x; } + int y() const { return _y; } + + bool operator==(const Vec2 &pt) const + { + return _x == pt._x && _y == pt._y; + } + bool operator!=(const Vec2 &pt) const + { + return !(*this == pt); + } + + Vec2 delta(const Vec2 &o) const + { + return Vec2{_x - o._x, _y - o._y}; + } + + double length() const; + double distance(const Vec2 &pt) const; + + bool unit() const; + bool adjacent(const Vec2 &pt) const; + + Vec2 shifted(const Vec2 &dxy) const + { + return Vec2{_x + dxy._x, _y + dxy._y}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/ranged_units.hpp b/8303/Parfentev_Leonid/lab3/ranged_units.hpp new file mode 100644 index 000000000..1798f04ce --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/ranged_units.hpp @@ -0,0 +1,94 @@ +#ifndef _H_RANGED_UNITS_HPP +#define _H_RANGED_UNITS_HPP + +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class RangedAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + double _min_distance, _max_distance; + double _dist_pow; + + static double + distance(const Unit *u, MapIter to) + { + return to.point().distance(u->position()); + } + +public: + double minRange() const { return _min_distance; } + double maxRange() const { return _max_distance; } + + RangedAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow) + :_kind{kind}, + _base_damage{base_dmg}, + _min_distance{min_dist}, + _max_distance{max_dist}, + _dist_pow{dist_pow} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return dist >= _min_distance + && dist <= _max_distance; + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return std::make_pair( + _kind, + int(_base_damage + * u->relativeHealth() + / pow(dist, _dist_pow))); + } +}; + +class BasicRangedUnit: public Unit { +public: + BasicRangedUnit(int speed, + AttackKind attack_kind, + int base_dmg, + double max_dist, + double dist_pow, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new RangedAttack {attack_kind, base_dmg, + 1., max_dist, dist_pow}, + def, base_health} {} +}; + +namespace units { + class Archer: public BasicRangedUnit { + public: + Archer() :BasicRangedUnit{ + 2, + AttackKind::arrow, 50, 5., .20, + new BasicDefense {0.9}, + 40} {} + }; + + class Slinger: public BasicRangedUnit { + public: + Slinger() :BasicRangedUnit{ + 2, + AttackKind::stone, 60, 3., .30, + new BasicDefense {.09}, + 50} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab3/rectmap.cpp b/8303/Parfentev_Leonid/lab3/rectmap.cpp new file mode 100644 index 000000000..b82667c40 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/rectmap.cpp @@ -0,0 +1,55 @@ +#include "rectmap.hpp" + +#include "unit.hpp" + +Cell::~Cell() +{ + delete _u; + delete _obj; + delete _l; +} + +bool +RectMapIter::valid() const +{ + return x() >= 0 + && x() < _map->width() + && y() >= 0 + && y() < _map->height(); +} + +Cell & +RectMapIter::cell() const +{ + return _map->at(point()); +} + +void +RectMapIter::moveTo(Vec2 xy) +{ + _pt = xy; +} + +RectMapIter +RectMapIter::otherAt(Vec2 xy) const +{ + RectMapIter other = *this; + other.moveTo(xy); + return other; +} + +void +RectMapIter::advance(int d) +{ + int nx = x() + d, + w = _map->width(); + _pt = Vec2{nx % w, y() + nx / w}; +} + +RectMapIter +RectMapIter::advanced(int d) const +{ + RectMapIter other = *this; + other.advance(d); + return other; +} diff --git a/8303/Parfentev_Leonid/lab3/rectmap.hpp b/8303/Parfentev_Leonid/lab3/rectmap.hpp new file mode 100644 index 000000000..a3119ffb1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/rectmap.hpp @@ -0,0 +1,99 @@ +#ifndef _H_RECTMAP_HPP +#define _H_RECTMAP_HPP + +#include "point.hpp" +#include "placeable.hpp" +#include "landscape.hpp" + + +class Unit; + +class Cell { + Landscape *_l = nullptr; + Unit *_u = nullptr; + Placeable *_obj = nullptr; + +public: + Cell() {} + + ~Cell(); + + Landscape *landscape() const { return _l; } + void setLandscape(Landscape *l) + { + delete _l; + _l = l; + } + + Unit *unit() const { return _u; } + void setUnit(Unit *u) { _u = u; } + + Placeable *object() const { return _obj; } + void setObject(Placeable *p) { _obj = p; } +}; + +class RectMap; + +class RectMapIter { + RectMap *_map; + Vec2 _pt; + +public: + RectMapIter(RectMap *map, Vec2 pt) + :_map{map}, _pt{pt} {} + RectMapIter(RectMap *map, int x, int y) + :_map{map}, _pt{x, y} {} + + static RectMapIter makeNull() { return {nullptr, {0, 0}}; } + + bool operator==(const RectMapIter &o) const + { + return _map == o._map + && _pt == o._pt; + } + bool operator!=(const RectMapIter &o) const + { + return !(*this == o); + } + + int x() const { return _pt.x(); } + int y() const { return _pt.y(); } + Vec2 point() const { return _pt; } + + Cell &cell() const; + + bool null() const { return _map == nullptr; } + bool valid() const; + + void moveTo(Vec2 xy); + RectMapIter otherAt(Vec2 xy) const; + + void advance(int d); + RectMapIter advanced(int d) const; +}; + +class RectMap { + const int _w, _h; + Cell * const _storage; + +public: + RectMap(int w, int h) + :_w{w}, _h{h}, _storage{new Cell [w * h]} {} + + int width() const { return _w; } + int height() const { return _h; } + + Cell &at(Vec2 pt) { return _storage[pt.x() + pt.y()*_w]; } + const Cell &at(Vec2 pt) const + { + return _storage[pt.x() + pt.y()*_w]; + } + RectMapIter iterAt(Vec2 pt) { return RectMapIter{this, pt}; } + + ~RectMap() + { + delete[] _storage; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/report.pdf b/8303/Parfentev_Leonid/lab3/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..63be5c5ba8c8c99b12465837bd3048645b57134a GIT binary patch literal 109391 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 z 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 AYybcN literal 0 HcmV?d00001 diff --git a/8303/Parfentev_Leonid/lab3/unit.cpp b/8303/Parfentev_Leonid/lab3/unit.cpp new file mode 100644 index 000000000..ef46978a2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/unit.cpp @@ -0,0 +1,5 @@ +#include + +#include "unit.hpp" + +std::default_random_engine global_random {}; diff --git a/8303/Parfentev_Leonid/lab3/unit.hpp b/8303/Parfentev_Leonid/lab3/unit.hpp new file mode 100644 index 000000000..45cfef0f0 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/unit.hpp @@ -0,0 +1,258 @@ +#ifndef _H_UNIT_HPP +#define _H_UNIT_HPP + +#include +#include +#include + +#include "event.hpp" +#include "event_types.hpp" +#include "object_w_health.hpp" +#include "map.hpp" + + +extern std::default_random_engine global_random; + + +class MovePolicy { +public: + virtual bool canMove(const Unit *u, MapIter to) =0; + virtual ~MovePolicy() {} +}; + +enum class AttackKind { + invalid, sword, spear, cavalry, arrow, stone, rock, bolt, +}; + +// NOTE: can’t do area damage +class AttackPolicy { +public: + virtual bool canAttackTo(const Unit *u, MapIter to) =0; + + virtual MapIter actualPosition(const Unit *, MapIter to) + { + return to; + } + + // returns kind and base damage + virtual std::pair + baseAttack(const Unit *u, MapIter to) =0; + + virtual ~AttackPolicy() {} +}; + +struct DamageSpec { + int base_damage, damage_spread; + + void + scale(double k) + { + base_damage *= k; + damage_spread *= k; + } + + DamageSpec + scaled(double k) const + { + auto ds = *this; + ds.scale(k); + return ds; + } + + int evaluate() const + { + std::uniform_int_distribution<> + dist {-damage_spread, damage_spread}; + + return base_damage + dist(global_random); + } +}; + +class DefensePolicy { +protected: + static DamageSpec + make_spec(double base, double spread) + { + return DamageSpec{(int)base, (int)spread}; + } + + static DamageSpec + defense_level(double k, int dmg) + { + return make_spec(round(1.0*dmg/k), + round(0.25*dmg/k)); + } + + static DamageSpec + normal_defense(double dmg) + { + return defense_level(1.0, dmg); + } + +public: + // returns base damage and spread + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) =0; + + virtual ~DefensePolicy() {} +}; + +class MovePolicyContainer { + MovePolicy *_mp; + +public: + MovePolicyContainer(MovePolicy *mp) :_mp{mp} {} + + MovePolicy *movePolicy() const { return _mp; } + void setMovePolicy(MovePolicy *mp) + { + _mp = mp; + } + virtual ~MovePolicyContainer() { delete _mp; } + + MovePolicyContainer * + findMoveContainerOf(const MovePolicy *mp) + { + for (MovePolicyContainer *mpc = this; mpc; + mpc = dynamic_cast( + mpc->movePolicy())) { + if (mpc->movePolicy() == mp) { + return mpc; + } + } + return nullptr; + } +}; + +class DefensePolicyContainer { + DefensePolicy *_dp; + +public: + DefensePolicyContainer(DefensePolicy *dp) :_dp{dp} {} + + DefensePolicy *defensePolicy() const { return _dp; } + void setDefensePolicy(DefensePolicy *dp) + { + _dp = dp; + } + virtual ~DefensePolicyContainer() { delete _dp; } + + DefensePolicyContainer * + findDefenseContainerOf(const DefensePolicy *dp) + { + for (DefensePolicyContainer *dpc = this; dpc; + dpc = dynamic_cast( + dpc->defensePolicy())) { + if (dpc->defensePolicy() == dp) { + return dpc; + } + } + return nullptr; + } +}; + +class AttackPolicyContainer { + AttackPolicy *_ap; + +public: + AttackPolicyContainer(AttackPolicy *ap) :_ap{ap} {} + + AttackPolicy *attackPolicy() const { return _ap; } + void setAttackPolicy(AttackPolicy *ap) + { + _ap = ap; + } + virtual ~AttackPolicyContainer() { delete _ap; } + + AttackPolicyContainer * + findAttackContainerOf(const AttackPolicy *ap) + { + for (AttackPolicyContainer *apc = this; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (apc->attackPolicy() == ap) { + return apc; + } + } + return nullptr; + } +}; + +class Unit: public Placeable, + public ObjectWithHealth, + public EventEmitter, + public MovePolicyContainer, + public DefensePolicyContainer, + public AttackPolicyContainer { +public: + Unit(MovePolicy *move, + AttackPolicy *attack, + DefensePolicy *defense, + int base_health) + :ObjectWithHealth{base_health}, + MovePolicyContainer{move}, + DefensePolicyContainer{defense}, + AttackPolicyContainer{attack} {} + + virtual void + heal(int hp) + { + emit(new events::UnitGetsHealed {this, hp}); + ObjectWithHealth::heal(hp); + } + + virtual void + takeDamage(int dmg) + { + if (!alive()) { + return; + } + + emit(new events::UnitTakesDamage {this, dmg}); + + ObjectWithHealth::takeDamage(dmg); + + if (!alive()) { + emit(new events::UnitDeath {this}); + } + } + + bool + canMove(MapIter to) const + { + return movePolicy()->canMove(this, to); + } + + bool + canAttackTo(MapIter to) const + { + return attackPolicy()->canAttackTo(this, to); + } + + MapIter + actualPosition(MapIter to) const + { + return attackPolicy()->actualPosition(this, to); + } + + std::pair + baseAttack(MapIter to) const + { + return attackPolicy()->baseAttack(this, to); + } + + DamageSpec + actualDamage(AttackKind kind, int base) const + { + return defensePolicy()->actualDamage(this, kind, base); + } + + virtual ~Unit() override + { + if (alive()) { + emit(new events::UnitLiveDeleted {this}); + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/unit_factory.hpp b/8303/Parfentev_Leonid/lab3/unit_factory.hpp new file mode 100644 index 000000000..5f1f475bd --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/unit_factory.hpp @@ -0,0 +1,24 @@ +#ifndef _H_UNIT_FACTORY_HPP +#define _H_UNIT_FACTORY_HPP + +#include "unit.hpp" + + +class UnitFactory { +public: + virtual Unit *create() const =0; + + virtual ~UnitFactory() {} +}; + +template +class SimpleUnitFactory: public UnitFactory { +public: + virtual Unit * + create() const override + { + return new U {}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab3/zombie_collector.hpp b/8303/Parfentev_Leonid/lab3/zombie_collector.hpp new file mode 100644 index 000000000..9de5f6981 --- /dev/null +++ b/8303/Parfentev_Leonid/lab3/zombie_collector.hpp @@ -0,0 +1,36 @@ +#ifndef _H_ZOMBIE_COLLECTOR_HPP +#define _H_ZOMBIE_COLLECTOR_HPP + +#include + +#include "unit.hpp" +#include "event.hpp" +#include "map.hpp" + + +class ZombieCollector: public EventListener { + std::vector _units {}; + +public: + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + return handle(ee->event()); + } + if (auto *ee = dynamic_cast(e)) { + _units.push_back(ee->unit()); + } + } + + void + collect(Map *m) + { + for (auto *unit: _units) { + delete m->removeUnitAt(unit->position()); + } + _units.clear(); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/Makefile b/8303/Parfentev_Leonid/lab4/Makefile new file mode 100644 index 000000000..d54e095ab --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/Makefile @@ -0,0 +1,21 @@ +EXENAME = main +HEADERS = point.hpp placeable.hpp rectmap.hpp map.hpp pathfinder.hpp \ + unit.hpp common_policies.hpp melee_units.hpp ranged_units.hpp \ + catapult_units.hpp demo.hpp event.hpp base.hpp object_w_health.hpp \ + landscape.hpp landscape_types.hpp neutral_object.hpp \ + neutral_object_types.hpp unit_factory.hpp game.hpp \ + object_print.hpp event_printer.hpp iostream_player.hpp \ + mediator.hpp zombie_collector.hpp factory_table.hpp logging.hpp +OBJFILES = point.o rectmap.o map.o pathfinder.o unit.o demo.o main.o \ + event.o base.o game.o object_print.o iostream_player.o mediator.o +LDLIBS = -lm + +all: $(EXENAME) + +$(OBJFILES): $(HEADERS) + +$(EXENAME): $(OBJFILES) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + $(RM) $(EXENAME) $(OBJFILES) diff --git a/8303/Parfentev_Leonid/lab4/base.cpp b/8303/Parfentev_Leonid/lab4/base.cpp new file mode 100644 index 000000000..d6bc63c82 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/base.cpp @@ -0,0 +1,121 @@ +#include +#include +#include + +#include "unit.hpp" +#include "unit_factory.hpp" +#include "event.hpp" +#include "base.hpp" +#include "mediator.hpp" +#include "factory_table.hpp" + + +bool +Base::canCreateUnit(const std::string &key) const +{ + if (unitsCount() == maxUnitsCount()) { + return false; + } + + if (!FactoryTable::instance()->canCreate(key)) { + return false; + } + + return true; +} + +int +Base::createUnit(const std::string &key, Mediator *m) +{ + if (!canCreateUnit(key)) { + return -1; + } + + if (m->infoAt(position()).unit()) { + return false; + } + + Unit *u = FactoryTable::instance()->create(key); + int id = addUnit(u); + + if (id < 0) { + delete u; + return -1; + } + + if (!m->spawnUnit(u, position())) { + removeUnit(u); + delete u; + return -1; + } + + return id; +} + +bool +Base::setMaxUnitsCount(int m) +{ + if (m < unitsCount()) { + return false; + } + _max_count = m; + return true; +} + +int +Base::addUnit(Unit *u) +{ + if (maxUnitsCount() >= 0 + && unitsCount() == maxUnitsCount()) { + return -1; + } + + _units[_next_idx] = u; + u->subscribe(this); + u->emit(new events::UnitAdded {u}); + + return _next_idx++; +} + +void +Base::removeUnit(Unit *u) +{ + u->unsubscribe(this); + + for (auto iter = _units.begin(); + iter != _units.end(); + ++iter) { + if (iter->second == u) { + _units.erase(iter); + break; + } + } +} + +Unit * +Base::getUnitById(int id) const +{ + auto iter = _units.find(id); + return (iter != _units.end()) + ? iter->second + : nullptr; +} + +void +Base::handle(Event *e) +{ + EventForwarder::handle(e); + + if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } else if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } +} + +Base::~Base() +{ + for (auto p: _units) { + p.second->unsubscribe(this); + } +} diff --git a/8303/Parfentev_Leonid/lab4/base.hpp b/8303/Parfentev_Leonid/lab4/base.hpp new file mode 100644 index 000000000..c47e6fe0f --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/base.hpp @@ -0,0 +1,84 @@ +#ifndef _H_BASE_HPP +#define _H_BASE_HPP + +#include +#include +#include + +#include "placeable.hpp" +#include "event.hpp" +#include "unit.hpp" +#include "mediator.hpp" + + +class Base: public Placeable, + public EventForwarder { + + std::map _units {}; + int _next_idx = 0; + int _max_count = -1; + +public: + Base() {} + + bool + canCreateUnit(const std::string &key) const; + int + createUnit(const std::string &key, Mediator *m); + + int + unitsCount() const { return (int)_units.size(); } + bool + setMaxUnitsCount(int m); + int + maxUnitsCount() const { return _max_count; } + + int + addUnit(Unit *u); + void + removeUnit(Unit *u); + Unit * + getUnitById(int id) const; + + class unitsIter { + using real_iter_t = std::map::const_iterator; + real_iter_t _iter; + + public: + unitsIter(real_iter_t it) + :_iter{it} {} + + int id() const { return _iter->first; } + Unit *unit() const { return _iter->second; } + unitsIter &operator++() { ++_iter; return *this; } + unitsIter + operator++(int) + { + unitsIter x{_iter}; + ++*this; + return x; + } + bool + operator==(const unitsIter &o) const + { + return _iter == o._iter; + } + bool + operator!=(const unitsIter &o) const + { + return !(*this == o); + } + }; + + unitsIter + unitsBegin() const { return unitsIter{_units.begin()}; } + unitsIter + unitsEnd() const { return unitsIter{_units.end()}; } + + virtual void + handle(Event *e) override; + + virtual ~Base() override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/catapult_units.hpp b/8303/Parfentev_Leonid/lab4/catapult_units.hpp new file mode 100644 index 000000000..26a217f7d --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/catapult_units.hpp @@ -0,0 +1,145 @@ +#ifndef _H_CATAPULT_UNITS_HPP +#define _H_CATAPULT_UNITS_HPP + +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" +#include "ranged_units.hpp" + + +class CatapultAttack: public RangedAttack { + double _spread_tang, _spread_normal; + + struct FVec2 { + double x, y; + + explicit FVec2(const Vec2 &v) + :x{(double)v.x()}, y{(double)v.y()} {} + + FVec2(double x, double y) + :x{x}, y{y} {} + + operator Vec2() const + { + return Vec2{int(round(x)), int(round(y))}; + } + + FVec2 + orthogonal() const { return {y, -x}; } + + FVec2 & + operator*=(double a) + { + x *= a; + y *= a; + return *this; + } + FVec2 + operator*(double a) const + { + FVec2 tmp {*this}; + return tmp *= a; + } + + FVec2 + normalized() const { return (*this) * (1/sqrt(x*x + y*y)); } + + FVec2 & + operator+=(const FVec2 &dxy) + { + x += dxy.x; + y += dxy.y; + return *this; + } + FVec2 + operator+(const FVec2 &dxy) const + { + FVec2 tmp{*this}; + return tmp += dxy; + } + + FVec2 + apply(double t, double n) const + { + return normalized() * t + + orthogonal().normalized() * n; + } + }; + +public: + CatapultAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n) + :RangedAttack{kind, base_dmg, min_dist, max_dist, dist_pow}, + _spread_tang{spread_t}, + _spread_normal{spread_n} {} + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + Vec2 dest = to.point(); + Vec2 delta = dest.delta(u->position()); + FVec2 fdelta {delta}; + + std::uniform_real_distribution<> + t_dist {-_spread_tang, _spread_tang}, + n_dist {-_spread_normal, _spread_normal}; + + double + t = t_dist(global_random), + n = n_dist(global_random); + + FVec2 result = fdelta.apply(t, n); + return to.shifted(Vec2{result}); + } +}; + +class BasicCatapultUnit: public Unit { +public: + BasicCatapultUnit(AttackKind attack_kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n, + int base_health) + :Unit{new BasicMovement {1}, + new CatapultAttack {attack_kind, + base_dmg, min_dist, max_dist, + dist_pow, spread_t, spread_n}, + DefenseLevelDeco::good_defense_deco( + AttackKind::arrow, + new BasicDefense {0.75}), + base_health} {} +}; + +namespace units { + class Onager: public BasicCatapultUnit { + public: + Onager() :BasicCatapultUnit{ + AttackKind::rock, 90, + 3, 10, 0.05, + 0.2, 0.1, + 30} {} + }; + + class BoltThrower: public BasicCatapultUnit { + public: + BoltThrower() :BasicCatapultUnit{ + AttackKind::bolt, 110, + 2, 6, 0.15, + 0.05, 0.05, + 20} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/common_policies.hpp b/8303/Parfentev_Leonid/lab4/common_policies.hpp new file mode 100644 index 000000000..4a99320aa --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/common_policies.hpp @@ -0,0 +1,181 @@ +#ifndef _H_COMMON_POLICIES_HPP +#define _H_COMMON_POLICIES_HPP + +#include + +#include "unit.hpp" +#include "map.hpp" +#include "pathfinder.hpp" + + +class BasicMovement: public MovePolicy { + int _steps_per_turn; + +public: + BasicMovement(int n) + :_steps_per_turn{n} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _steps_per_turn}; + return pf.run(); + } +}; + +class NestedMovement: public MovePolicy, + public MovePolicyContainer { +public: + using MovePolicyContainer::MovePolicyContainer; +}; + +class ModifyingMovePolicy: public NestedMovement { + int _max; + +public: + ModifyingMovePolicy(MovePolicy *p, int max_dist) + :NestedMovement{p}, _max{max_dist} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _max}; + if (!pf.run()) + return false; + + return movePolicy()->canMove(u, to); + } +}; + + + +class BasicDefense: public DefensePolicy { + double _lvl; + +public: + explicit BasicDefense(double level=1.0) + :_lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *, AttackKind, int base) override + { + return normal_defense(base); + } +}; + +class NestedDefense: public DefensePolicy, + public DefensePolicyContainer { +public: + using DefensePolicyContainer::DefensePolicyContainer; +}; + +class DefenseLevelDeco: public NestedDefense { + // Controls nested policy lifetime + AttackKind _kind; + double _lvl; + +public: + DefenseLevelDeco(DefensePolicy *p, + AttackKind kind, + double level) + :NestedDefense{p}, _kind{kind}, _lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + if (kind == _kind) + return defense_level(_lvl, base); + return defensePolicy()->actualDamage(u, kind, base); + } + + static DefenseLevelDeco * + defense_level_deco(AttackKind kind, double lvl, DefensePolicy *p) + { + return new DefenseLevelDeco {p, kind, lvl}; + } + + static DefenseLevelDeco * + good_defense_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 2.0, p); + } + + static DefenseLevelDeco * + vulnerability_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 0.5, p); + } +}; + +class MultiplierDefensePolicy: public NestedDefense { + double _mul; + +public: + MultiplierDefensePolicy(DefensePolicy *p, double mul) + :NestedDefense{p}, _mul{mul} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + DamageSpec ds = defensePolicy()->actualDamage(u, kind, base); + ds.scale(1/_mul); + return ds; + } +}; + + + +class AttackForbidden: public AttackPolicy { +public: + using AttackPolicy::AttackPolicy; + + virtual bool + canAttackTo(const Unit *, MapIter) override + { + return false; + } + + virtual std::pair + baseAttack(const Unit *, MapIter) override + { + return std::make_pair(AttackKind::invalid, 0); + } +}; + +class NestedAttack: public AttackPolicy, + public AttackPolicyContainer { +public: + using AttackPolicyContainer::AttackPolicyContainer; +}; + +class MultiplierAttackPolicy: public NestedAttack { + double _mul; + +public: + MultiplierAttackPolicy(AttackPolicy *p, double mul) + :NestedAttack{p}, _mul{mul} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return attackPolicy()->canAttackTo(u, to); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + auto ba = attackPolicy()->baseAttack(u, to); + return std::make_pair(ba.first, (int)(ba.second * _mul)); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab4/demo.cpp b/8303/Parfentev_Leonid/lab4/demo.cpp new file mode 100644 index 000000000..e12cc82a6 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/demo.cpp @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" + +#include "event.hpp" +#include "event_types.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "unit_factory.hpp" + +#include "game.hpp" +#include "event_printer.hpp" +#include "iostream_player.hpp" + +#include "factory_table.hpp" + + +void +demo1() +{ + // create a map + auto *map = new Map {10, 10}; + + // create a few units + auto sw1_iter = map->addUnit(new units::Swordsman {}, {3, 3}); + map->addUnit(new units::Swordsman {}, {4, 4}); + + // write the map + std::cout << map << "\n"; + + // test the pathfinder + static const std::vector pts { + {3, 4}, + {3, 3}, + {5, 5}, + {5, 4}, + {5, 3}, + }; + for (auto pt: pts) { + std::cout << sw1_iter.unit() << " " + << ((sw1_iter.unit()->canMove(map->iterAt(pt))) + ? "can" : "can't") + << " move to " << pt << "\n"; + } + + // clean up + delete map; +} + +void +demo2() +{ + auto *map = new Map {10, 10}; + + auto c_iter = map->addUnit(new units::Cavalry {}, {3, 3}); + auto *u = c_iter.unit(); + + std::cout << map << "\n"; + + static const std::vector path { + {4, 5}, {6, 5}, {9, 5}, {8, 7}, {7, 9}, + }; + for (auto pt: path) { + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + std::cout << u << " can't move to " << pt << "!\n"; + break; + } + + std::cout << "moving " << u + << " to " << pt << "...\n"; + + c_iter = map->addUnit(map->removeUnitAt(c_iter), pt); + if (c_iter.null()) { + std::cout << "failed!\n"; + } + } + + std::cout << "\n" << map; + + delete map; +} + +void +demo3() +{ + auto *map = new Map {10, 10}; + map->setMaxUnitsCount(2); + + Unit *sw = new units::Swordsman {}; + Unit *ar = new units::Archer {}; + + map->addUnit(sw, {5, 0}); + map->addUnit(ar, {5, 9}); + + Unit *x = new units::Swordsman {}; + if (!map->addUnit(x, {1, 1}).null()) { + std::cout << "Added one more unit!\n"; + delete map->removeUnitAt({1, 1}); + } else { + std::cout << "Max units: " << map->maxUnitsCount() + << ", current units: " << map->unitsCount() + << "\n"; + delete x; + } + + std::cout << map; + + while (sw->alive() && ar->alive()) { + Vec2 from = sw->position(); + Vec2 to = from.shifted({0, 1}); + + std::cout << "\nmoving " << sw << " from " << from + << " to " << to << "...\n"; + + if (!sw->canMove(map->iterAt(to))) { + std::cout << "can't move\n"; + break; + } + + if (map->addUnit(map->removeUnitAt(from), to).null()) { + std::cout << "failed\n"; + break; + } + + std::cout << ar << " shoots at " << sw->position() << "...\n"; + + auto toi = map->iterAt(sw->position()); + if (!ar->canAttackTo(toi)) { + std::cout << "can't shoot\n"; + // it’s ok + } else { + auto pos = ar->actualPosition(toi); + if (Unit *targ = pos.unit()) { + auto dam = ar->baseAttack(toi); + + std::cout << "hits " << targ << "\n"; + + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + + std::cout << "now: " << targ << "\n"; + } + } + } +} + +MapIter +doAttack(Unit *u, MapIter to) +{ + if (!u->canAttackTo(to)) { + return MapIter::makeNull(); + + } else { + auto pos = u->actualPosition(to); + + if (Unit *targ = pos.unit()) { + auto dam = u->baseAttack(pos); + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + return pos; + } + + return MapIter::makeNull(); + } +} + +struct SimpleGame { + Map *map; + Base *b1, *b2; + EventPrinter *pr; + + explicit SimpleGame(int w=10, int h=10, + int x1=1, int y1=1, + int x2=9, int y2=9) + { + pr = new EventPrinter {std::cout}; + + map = new Map {w, h}; + + b1 = new Base {}; + pr->setPrefix(b1, "Base 1"); + + map->addBase(b1, {x1, y1}); + b1->subscribe(pr); + + b2 = new Base {}; + pr->setPrefix(b2, "Base 2"); + + map->addBase(b2, {x2, y2}); + b2->subscribe(pr); + } + + ~SimpleGame() + { + delete map; + delete pr; + } +}; + +void +demo4() +{ + SimpleGame g {}; + + Unit *u1 = new units::Swordsman {}; + Unit *u2 = new units::Swordsman {}; + + g.b1->addUnit(u1); + g.b2->addUnit(u2); + + g.map->addUnit(u1, {3, 3}); + g.map->addUnit(u2, {4, 3}); + + while (u1->alive() + && u2->alive()) { + doAttack(u1, g.map->iterAt(u2->position())); + if (u2->alive()) { + doAttack(u2, g.map->iterAt(u1->position())); + } + } +} + +MapIter +doMove(Map *map, const Unit *u, Vec2 pt) +{ + auto from = map->iterAt(u->position()); + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + return MapIter::makeNull(); + } + + return map->addUnit(map->removeUnitAt(from), pt); +} + +Unit * +setupUnit(Base *base, const std::string &k, Mediator *m, Vec2 pt) +{ + Unit *u = base->getUnitById(base->createUnit(k, m)); + m->teleportUnit(u, pt); + return u; +} + +void +demo5() +{ + SimpleGame g {}; + + // 2,2 .. 5,5: swamp + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + g.map->setLandscape(new landscapes::Swamp {}, + {2+i, 2+j}); + } + } + + // 1,7 .. 6,9: forest + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 5; ++i) { + g.map->setLandscape(new landscapes::Forest {}, + {1+i, 7+j}); + } + } + + auto *m = new Mediator {g.map}; + auto u1 = setupUnit(g.b1, "swordsman", m, {2, 2}); + auto u2 = setupUnit(g.b2, "swordsman", m, {3, 2}); + + if (doMove(g.map, u1, {0, 2}).null()) { + std::cout << "Can't move " << u1 << " across 2 cells\n"; + } else { + std::cout << "Moved " << u1 << " across 2 cells \n"; + } + + std::cout << "u1: " << u1 << "\n"; + + if (doAttack(u1, g.map->iterAt(u2->position())).null()) { + std::cout << "Can't attack in swamp\n"; + } else { + std::cout << "Attacked in a swamp\n"; + } + + std::cout << "u2: " << u2 << "\n"; + + doMove(g.map, u1, {1, 2}); + doMove(g.map, u2, {2, 3}); + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + doAttack(u1, g.map->iterAt(u2->position())); + + auto u3 = setupUnit(g.b1, "spearsman", m, {3, 8}); + auto u4 = setupUnit(g.b1, "spearsman", m, {7, 8}); + auto u5 = setupUnit(g.b2, "onager", m, {5, 5}); + + while (u3->alive()) + doAttack(u5, g.map->iterAt(u3->position())); + + while (u4->alive()) + doAttack(u5, g.map->iterAt(u4->position())); + + std::cout << g.map; + + delete m; +} + +void +demo6() +{ + SimpleGame g {}; + + auto *m = new Mediator {g.map}; + auto *u1 = setupUnit(g.b1, "slinger", m, {1, 5}); + auto *u2 = setupUnit(g.b2, "swordsman", m, {6, 5}); + + g.map->addNeutralObject(new objects::Tower {}, {1, 5}); + g.map->addNeutralObject(new objects::HealingWell {}, {6, 5}); + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " can't reach " << u2 << "\n"; + } else { + std::cout << u1 << " somehow reached " << u2 << "\n"; + } + + if (m->useObject(u1)) { + std::cout << u1 << " used the tower\n"; + } else { + std::cout << u1 << " can't use the tower\n"; + } + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " still can't reach " << u2 << "\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + if (m->useObject(u2)) { + std::cout << u2 << " used the healing well\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; +} + +void +demo7() +{ + class MoverPlayer: public Player { + std::string _unit_type; + int _id = -1; + + public: + using Player::Player; + + MoverPlayer(std::string name, + std::string type) + :Player{name}, _unit_type{std::move(type)} {} + + virtual bool + takeTurn(Mediator *m, Base *b) override + { + if (_id >= 0) { + Unit *u = b->getUnitById(_id); + m->moveUnitTo(u, u->position().shifted({1, 0})); + } else { + _id = b->createUnit(_unit_type, m); + } + return true; + } + }; + + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + auto *b = new Base {}; + map->addBase(b, {3, 3}); + int b_id = g.addBase(b); + + auto *p = new MoverPlayer {"Player 1", "swordsman"}; + g.setPlayer(b_id, p); + + for (int i = 0; i < 5; ++i) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo8() +{ + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + std::stringstream s1 {}; + s1 << "base\n" + << "create spearsman\n" + << "map 0 0 9 9\n" + << "move 0 3 5\n" + << "map 0 0 9 9\n" + << "describe 3 5\n"; + + auto *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + std::stringstream s2 {}; + s2 << "create archer\n" + << "attack 0 3 5\n"; + + auto *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo9() +{ + Map *map = new Map {10, 10}; + + auto *uf = (new objects + ::WeaponSmiths + ::SimpleUnitFilter {}); + auto *ws = new objects::WeaponSmiths {2.0, uf}; + map->addNeutralObject(ws, {3, 4}); + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + Base *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + Base *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + std::stringstream s1 {}; + std::stringstream s2 {}; + + s1 << "create onager\n" + << "move 0 3 4\n" + << "use 0\n" + << "create onager\n" + << "move 1 3 2\n" + << "attack 0 7 4\n" + << "attack 1 7 2\n" + << "create swordsman\n" + << "move 0 3 5\n" + << "move 2 3 4\n" + << "use 2\n"; + + s2 << "create swordsman\n" + << "move 0 7 4\n" + << "create swordsman\n" + << "move 1 7 2\n" + << "skip skip skip skip skip\n"; + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} diff --git a/8303/Parfentev_Leonid/lab4/demo.hpp b/8303/Parfentev_Leonid/lab4/demo.hpp new file mode 100644 index 000000000..ae8bc7a95 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/demo.hpp @@ -0,0 +1,14 @@ +#ifndef _H_DEMO_HPP +#define _H_DEMO_HPP + +void demo1(); +void demo2(); +void demo3(); +void demo4(); +void demo5(); +void demo6(); +void demo7(); +void demo8(); +void demo9(); + +#endif diff --git a/8303/Parfentev_Leonid/lab4/event.cpp b/8303/Parfentev_Leonid/lab4/event.cpp new file mode 100644 index 000000000..b7ff78ebd --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/event.cpp @@ -0,0 +1,31 @@ +#include "event.hpp" + +void +EventEmitter::emit_shared(Event *e) const +{ + for (auto iter = _listeners.begin(); iter != _listeners.end();) { + auto *listener = *iter++; + // note: the listener may safely unsubscribe when handling the + // event. + listener->handle(e); + } +} + +void +EventEmitter::emit(Event *e) const +{ + emit_shared(e); + delete e; +} + +void +EventEmitter::subscribe(EventListener *l) +{ + _listeners.insert(l); +} + +void +EventEmitter::unsubscribe(EventListener *l) +{ + _listeners.erase(l); +} diff --git a/8303/Parfentev_Leonid/lab4/event.hpp b/8303/Parfentev_Leonid/lab4/event.hpp new file mode 100644 index 000000000..fa2d1f541 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/event.hpp @@ -0,0 +1,62 @@ +#ifndef _H_EVENT_HPP +#define _H_EVENT_HPP + +#include + +class EventListener; +class Event; + +class EventEmitter { + std::set _listeners {}; + +public: + void emit_shared(Event *e) const; + void emit(Event *e) const; + + void subscribe(EventListener *l); + void unsubscribe(EventListener *l); + + virtual ~EventEmitter() {} +}; + +class Event { +public: + virtual ~Event() {} +}; + +class EventListener { +public: + virtual void handle(Event *e) =0; + + virtual ~EventListener() {} +}; + +class EventForwarder; + +namespace events { + + class Forwarded: public Event { + Event *_e; + EventForwarder *_f; + + public: + Forwarded(Event *e, EventForwarder *f) + :_e{e}, _f{f} {} + + Event *event() const { return _e; } + EventForwarder *forwarder() const { return _f; } + }; + +} + +class EventForwarder: public EventEmitter, + public EventListener { +public: + virtual void + handle(Event *e) override + { + emit(new events::Forwarded {e, this}); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/event_printer.hpp b/8303/Parfentev_Leonid/lab4/event_printer.hpp new file mode 100644 index 000000000..05218c2a6 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/event_printer.hpp @@ -0,0 +1,131 @@ +#ifndef _H_EVENT_PRINTER_HPP +#define _H_EVENT_PRINTER_HPP + +#include +#include +#include +#include + +#include "event.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "player.hpp" +#include "object_print.hpp" + + +class EventPrinter: public EventListener { + std::ostream *_os; + bool _free_os; + + std::map _prefix_map {}; + int _base_idx = 0; + + std::string + makeName(const char *base, int idx) + { + std::ostringstream oss {}; + oss << base << " " << idx; + return oss.str(); + } + +public: + EventPrinter(std::ostream &os) + :_os{&os}, _free_os{false} {} + + EventPrinter(std::ostream *os) + :_os{os}, _free_os{true} {} + + std::ostream & + ostream() const { return *_os; } + + void + setPrefix(EventForwarder *f, const std::string &s) + { + _prefix_map[f] = s; + } + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + auto iter = _prefix_map.find(ee->forwarder()); + if (iter != _prefix_map.end()) { + (*_os) << iter->second << ": "; + } + + return handle(ee->event()); + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit added: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit died: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " takes " + << ee->damage() << " health points of damage\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " gets healed by " + << ee->health() << " health points\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->attacker() + << " attacked another unit " << ee->target() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->target() + << " was attacked by another unit " + << ee->attacker() << "\n"; + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " moved from " << ee->sourcePos() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " used object " << ee->neutralObject() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "(Live unit " << ((void *)ee->unit()) + << " deleted)\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + auto name = makeName("Base", ++_base_idx); + setPrefix(ee->base(), name); + (*_os) << "New base: " << name << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << " over\n"; + + } else { + (*_os) << "Unknown event\n"; + } + } + + virtual ~EventPrinter() override + { + if (_free_os) { + _os->flush(); + delete _os; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/event_types.hpp b/8303/Parfentev_Leonid/lab4/event_types.hpp new file mode 100644 index 000000000..8de61313b --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/event_types.hpp @@ -0,0 +1,161 @@ +#ifndef _H_EVENT_TYPES_HPP +#define _H_EVENT_TYPES_HPP + +#include "point.hpp" +#include "event.hpp" + + +class Unit; +class Base; +class NeutralObject; +class Player; + + +class UnitEvent: public Event { + Unit *_u; + +public: + UnitEvent(Unit *u) :_u{u} {} + + Unit *unit() const { return _u; } +}; + +class AttackEvent: public Event { + Unit *_a, *_b; + +public: + AttackEvent(Unit *a, Unit *b) :_a{a}, _b{b} {} + + Unit *attacker() const { return _a; } + Unit *target() const { return _b; } +}; + +class BaseEvent: public Event { + Base *_b; + +public: + BaseEvent(Base *b) :_b{b} {} + + Base *base() const { return _b; } +}; + +class PlayerEvent: public Event { + Player *_p; + +public: + PlayerEvent(Player *p) :_p{p} {} + + Player *player() const { return _p; } +}; + +class UnitMoveEvent: public UnitEvent { + Vec2 _src; + +public: + UnitMoveEvent(Unit *u, Vec2 src) + :UnitEvent{u}, _src{src} {} + + Vec2 sourcePos() const { return _src; } +}; + + + +namespace events { + + class UnitDeath: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitAdded: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitLiveDeleted: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitTakesDamage: public UnitEvent { + int _dmg; + + public: + UnitTakesDamage(Unit *u, int dmg) + :UnitEvent{u}, _dmg{dmg} {} + + int damage() const { return _dmg; } + }; + + class UnitGetsHealed: public UnitEvent { + int _hp; + + public: + UnitGetsHealed(Unit *u, int hp) + :UnitEvent{u}, _hp{hp} {} + + int health() const { return _hp; } + }; + + class UnitWasAttacked: public AttackEvent { + public: + using AttackEvent::AttackEvent; + }; + + class UnitAttacked: public AttackEvent { + Vec2 _tc; + + public: + UnitAttacked(Unit *u, Vec2 tc, Unit *tu) + :AttackEvent{u, tu}, _tc{tc} {} + + Vec2 targetCoordinates() const { return _tc; } + }; + + class UnitMoved: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitTeleported: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitUsedObject: public UnitEvent { + NeutralObject *_n; + + public: + UnitUsedObject(Unit *u, NeutralObject *n) + :UnitEvent{u}, _n{n} {} + + NeutralObject *neutralObject() const { return _n; } + }; + + + + class BaseAdded: public BaseEvent { + public: + using BaseEvent::BaseEvent; + }; + + class BaseDestroyed: public BaseEvent { + public: + using BaseEvent::BaseEvent; + }; + + + + class TurnStarted: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + + class TurnOver: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/factory_table.hpp b/8303/Parfentev_Leonid/lab4/factory_table.hpp new file mode 100644 index 000000000..693031648 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/factory_table.hpp @@ -0,0 +1,81 @@ +#ifndef _H_FACTORY_TABLE_HPP +#define _H_FACTORY_TABLE_HPP + +#include +#include + +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "unit_factory.hpp" + + +class FactoryTable { + std::map _tab {}; + + void + registerFactory(const std::string &key, UnitFactory *f) + { + _tab[key] = f; + } + + FactoryTable() + { +#define REG(k, T) \ + registerFactory( \ + k, new SimpleUnitFactory {}) + REG("swordsman", Swordsman); + REG("spearsman", Spearsman); + REG("cavalry", Cavalry); + REG("archer", Archer); + REG("slinger", Slinger); + REG("onager", Onager); + REG("boltthrower", BoltThrower); +#undef REG + } + +public: + static const FactoryTable * + instance() + { + static FactoryTable *inst = new FactoryTable {}; + return inst; + } + + virtual bool + canCreate(const std::string &key) const + { + return _tab.find(key) != _tab.end(); + } + + virtual std::vector + keys() const + { + std::vector keys {}; + for (auto p: _tab) { + keys.push_back(p.first); + } + return keys; + } + + virtual Unit * + create(const std::string &key) const + { + auto iter = _tab.find(key); + if (iter == _tab.end()) { + return nullptr; + } + + return iter->second->create(); + } + + ~FactoryTable() + { + for (auto p: _tab) { + delete p.second; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/game.cpp b/8303/Parfentev_Leonid/lab4/game.cpp new file mode 100644 index 000000000..8cb8c6fd1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/game.cpp @@ -0,0 +1,100 @@ +#include "base.hpp" +#include "game.hpp" +#include "event.hpp" +#include "event_types.hpp" + + +Game::Game(Map *map) + :_map{map}, + _med{new Mediator {map}} +{ + this->subscribe(_log_sink); +} + +int +Game::addBase(Base *base) +{ + base->subscribe(_coll); + base->subscribe(this); + + _recs.push_back(BaseRecord{base, nullptr}); + + emit(new events::BaseAdded {base}); + + return (int)(_recs.size() - 1); +} + +void +Game::setPlayer(int base_idx, Player *p) +{ + Player *&p_place = _recs[base_idx].player; + if (p_place) { + p_place->unsubscribe(_log_sink); + delete p_place; + --_players; + } + p_place = p; + if (p) { + p->subscribe(_log_sink); + ++_players; + } +} + +int +Game::basesCount() const +{ + return _recs.size(); +} + +int +Game::playersCount() const +{ + return _players; +} + +Base * +Game::baseByIdx(int idx) const +{ + return _recs[idx].base; +} + +void +Game::spin() +{ + auto &rec = _recs[_next]; + Player *p = rec.player; + Base *b = rec.base; + + if (p) { + emit(new events::TurnStarted {p}); + + bool res = p->takeTurn(_med, b); + + _coll->collect(_map); + + emit(new events::TurnOver {p}); + + if (!res) { + setPlayer(_next, nullptr); + } + } + + if (++_next == (int)_recs.size()) { + _next = 0; + } +} + +Game::~Game() +{ + for (int i = 0; i < (int)_recs.size(); ++i) { + setPlayer(i, nullptr); + auto &rec = _recs[i]; + rec.base->unsubscribe(this); + rec.base->unsubscribe(_coll); + } + + delete _coll; + delete _map; + + delete _log_sink; +} diff --git a/8303/Parfentev_Leonid/lab4/game.hpp b/8303/Parfentev_Leonid/lab4/game.hpp new file mode 100644 index 000000000..ff1750d3b --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/game.hpp @@ -0,0 +1,50 @@ +#ifndef _H_GAME_HPP +#define _H_GAME_HPP + +#include + +#include "event.hpp" +#include "map.hpp" +#include "base.hpp" +#include "player.hpp" +#include "zombie_collector.hpp" +#include "mediator.hpp" + + +class Game: public EventForwarder { + Map *_map; + Mediator *_med; + + struct BaseRecord { + Base *base; + Player *player; + }; + + std::vector _recs {}; + int _next = 0; + int _players = 0; + + ZombieCollector *_coll {new ZombieCollector {}}; + + EventForwarder *_log_sink {new EventForwarder {}}; + +public: + Game(Map *map); + + int addBase(Base *b); + void setPlayer(int base_idx, Player *p); + + int basesCount() const; + int playersCount() const; + + Base *baseByIdx(int idx) const; + + EventForwarder *logSink() const { return _log_sink; } + + void spin(); + + ~Game(); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab4/iostream_player.cpp b/8303/Parfentev_Leonid/lab4/iostream_player.cpp new file mode 100644 index 000000000..dc0928f4a --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/iostream_player.cpp @@ -0,0 +1,101 @@ +#include +#include +#include + +#include "game.hpp" +#include "base.hpp" +#include "iostream_player.hpp" +#include "event.hpp" +#include "logging.hpp" + + +void +IostreamPlayer::addCommand(const std::string &str, + IostreamCommand *cmd) +{ + _cmd_tab[str] = cmd; +} + +IostreamPlayer::IostreamPlayer(std::string name) + :Player{name} +{ + addCommand("move", new iostream_commands::Move {}); + addCommand("attack", new iostream_commands::Attack {}); + addCommand("use", new iostream_commands::Use {}); + addCommand("create", new iostream_commands::Create {}); + addCommand("base", new iostream_commands::FindBase {}); + addCommand("units", new iostream_commands::ListUnits {}); + addCommand("describe", new iostream_commands::DescribeAt {}); + addCommand("map", new iostream_commands::PrintMap {}); + addCommand("skip", new iostream_commands::Skip {}); +} + +bool +IostreamPlayer::takeTurn(Mediator *m, Base *b) +{ + for (;;) { + std::string cmd_name; + (*_is) >> cmd_name; + + if (_is->fail()) { + return false; + } + + { + std::ostringstream oss {}; + oss << "User picked action: " << cmd_name; + emit(new events::UserActionEvent {this, oss.str()}); + } + + auto iter = _cmd_tab.find(cmd_name); + if (iter == _cmd_tab.end()) { + std::ostringstream oss {}; + oss << "Unknown command: \"" << cmd_name << "\""; + emit(new events::UserActionEvent {this, oss.str()}); + (*_os) << oss.str() << "\n"; + continue; + } + + if (iter->second->execute(this, m, b)) { + break; + } + } + + return true; +} + +int +IostreamPlayer::readInt() +{ + int x; + (*_is) >> x; + return x; +} + +Unit * +IostreamPlayer::readUnitId(Base *b) +{ + int id = readInt(); + Unit *u = b->getUnitById(id); + if (!u) { + (*_os) << "No such unit: " << id << "\n"; + } + + return u; +} + +Vec2 +IostreamPlayer::readVec2() +{ + int x, y; + (*_is) >> x >> y; + return {x, y}; +} + +std::string +IostreamPlayer::readString() +{ + std::string s; + (*_is) >> s; + return s; +} diff --git a/8303/Parfentev_Leonid/lab4/iostream_player.hpp b/8303/Parfentev_Leonid/lab4/iostream_player.hpp new file mode 100644 index 000000000..684425469 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/iostream_player.hpp @@ -0,0 +1,286 @@ +#ifndef _H_STDIO_PLAYER_HPP +#define _H_STDIO_PLAYER_HPP + +#include +#include +#include +#include + +#include "point.hpp" +#include "game.hpp" +#include "object_print.hpp" + +#include "event.hpp" +#include "logging.hpp" + + +class IostreamPlayer; + +class IostreamCommand { +public: + // -> whether to end turn + virtual bool execute(IostreamPlayer *p, Mediator *m, Base *b) =0; + + virtual ~IostreamCommand() {} +}; + +class IostreamPlayer: public Player { + std::ostream *_os = nullptr; + std::istream *_is = nullptr; + bool _free_os, _free_is; + + std::map _cmd_tab {}; + + void + addCommand(const std::string &str, + IostreamCommand *cmd); + + +public: + IostreamPlayer(std::string name); + + void + setOstream(std::ostream *os) { _os = os; _free_is = true; } + void + setOstream(std::ostream &os) { _os = &os; _free_os = false; } + + std::ostream & + ostream() const { return *_os; } + + void + setIstream(std::istream *is) { _is = is; _free_is = true; } + void + setIstream(std::istream &is) { _is = &is; _free_is = false; } + + std::istream & + istream() const { return *_is; } + + virtual bool + takeTurn(Mediator *m, Base *b) override; + + int + readInt(); + + Unit * + readUnitId(Base *b); + + Vec2 + readVec2(); + + std::string + readString(); +}; + +namespace iostream_commands { + + class Move: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested moving " << u << " to " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->moveUnitTo(u, to)) { + p->ostream() << "Can't move unit " << u + << " to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Attack: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u << " attacks " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->attackTo(u, to)) { + p->ostream() << "Unit " << u + << " can't attack to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Use: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u + << " uses an object"; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->useObject(u)) { + p->ostream() << "Unit " << u + << " can't use any object there\n"; + return false; + } + + return true; + } + }; + + class Create: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *b) override + { + std::string s = p->readString(); + + { + std::ostringstream oss {}; + oss << "User requested creation of unit " << s; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!b->canCreateUnit(s)) { + p->ostream() << "Can't create unit of type " + << s << "\n"; + return false; + } + + int id = b->createUnit(s, m); + if (id < 0) { + p->ostream() << "Failed to create a unit of type " + << s << "\n"; + return false; + } + + p->ostream() << "New unit of type " << s + << ": " << id << "\n"; + return true; + } + }; + + class FindBase: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *, Base *b) override + { + p->ostream() << "Base: " << b << "\n"; + return false; + } + }; + + class ListUnits: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *, Base *b) override + { + p->ostream() << "Units:"; + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + p->ostream() << "- " << iter.id() + << ": " << iter.unit() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class DescribeAt: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *) override + { + auto pos = p->readVec2(); + auto info = m->infoAt(pos); + + p->ostream() << "At " << pos << "\n"; + p->ostream() + << "- Landscape: " << info.landscape() << "\n"; + + if (auto *b = info.base()) { + p->ostream() << "- Base: " << b << "\n"; + } + + if (auto *u = info.unit()) { + p->ostream() << "- Unit: " << u << "\n"; + } + + if (auto *n = info.neutralObject()) { + p->ostream() << "- Object: " << n << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class PrintMap: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *m, Base *) override + { + auto from = p->readVec2(); + auto to = p->readVec2(); + + p->ostream() << "From " << from + << " to " << to << ":\n"; + + for (int y = from.y(); y < to.y(); ++y) { + for (int x = from.x(); x < to.x(); ++x) { + if (x != from.x()) { + p->ostream() << " "; + } + displayMapInfo(p->ostream(), m->infoAt({x, y})); + } + p->ostream() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class Skip: public IostreamCommand { + public: + virtual bool + execute(IostreamPlayer *p, Mediator *, Base *) override + { + { + std::ostringstream oss {}; + oss << "User decided to skip the turn"; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + return true; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/landscape.hpp b/8303/Parfentev_Leonid/lab4/landscape.hpp new file mode 100644 index 000000000..7245da6b3 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/landscape.hpp @@ -0,0 +1,25 @@ +#ifndef _H_LANDSCAPE_HPP +#define _H_LANDSCAPE_HPP + + +class Unit; + +class Landscape { +public: + virtual void onEnter(Unit *u) =0; + virtual void onLeave(Unit *u) =0; + + virtual ~Landscape() {} +}; + +namespace landscapes { + + class Normal: public Landscape { + public: + virtual void onEnter(Unit *) override {} + virtual void onLeave(Unit *) override {} + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/landscape_types.hpp b/8303/Parfentev_Leonid/lab4/landscape_types.hpp new file mode 100644 index 000000000..268521e35 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/landscape_types.hpp @@ -0,0 +1,70 @@ +#ifndef _H_LANDSCAPE_TYPES_HPP +#define _H_LANDSCAPE_TYPES_HPP + +#include "landscape.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "common_policies.hpp" + + +namespace landscapes { + + // Swamp: max speed is 1; attacking is forbidden + class Swamp: public Landscape { + ModifyingMovePolicy *_p; + AttackPolicy *_prev, *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _p = new ModifyingMovePolicy {u->movePolicy(), 1}; + u->setMovePolicy(_p); + + _prev = u->attackPolicy(); + _cur = new AttackForbidden {}; + u->setAttackPolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *mpc = u->findMoveContainerOf(_p)) { + mpc->setMovePolicy(_p->movePolicy()); + _p->setMovePolicy(nullptr); + delete _p; + _p = nullptr; + } + + // our policy might’ve been wrapped into something + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + delete _cur; + _cur = nullptr; + } + } + }; + + class Forest: public Landscape { + DefensePolicy *_prev; + MultiplierDefensePolicy *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _prev = u->defensePolicy(); + _cur = new MultiplierDefensePolicy {_prev, 2.0}; + u->setDefensePolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *dpc = u->findDefenseContainerOf(_cur)) { + dpc->setDefensePolicy(_prev); + _cur->setDefensePolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/logging.hpp b/8303/Parfentev_Leonid/lab4/logging.hpp new file mode 100644 index 000000000..72b272969 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/logging.hpp @@ -0,0 +1,46 @@ +#ifndef _H_LOGGING_HPP +#define _H_LOGGING_HPP + +#include +#include + +#include "event.hpp" +#include "event_printer.hpp" + + +namespace events { + + class UserActionEvent: public Event { + Player *_p; + std::string _s; + + public: + UserActionEvent(Player *p, std::string s) + :_p{p}, _s{std::move(s)} {} + + const std::string & + message() const { return _s; } + + Player *player() const { return _p;} + }; + +} + +class LoggingEventPrinter: public EventPrinter { +public: + using EventPrinter::EventPrinter; + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + ostream() << ee->player()->name() + << ": " << ee->message() << "\n"; + return; + } + + EventPrinter::handle(e); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/main.cpp b/8303/Parfentev_Leonid/lab4/main.cpp new file mode 100644 index 000000000..096391d1b --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/main.cpp @@ -0,0 +1,130 @@ +#include + +#include +#include + +#include "demo.hpp" + +#include "player.hpp" +#include "iostream_player.hpp" +#include "event_printer.hpp" +#include "base.hpp" +#include "map.hpp" +#include "factory_table.hpp" + +void +run_demos(void) +{ + std::cout << "Demo 1\n"; + demo1(); + + std::cout << "\nDemo 2\n"; + demo2(); + + std::cout << "\nDemo 3\n"; + demo3(); + + std::cout << "\nDemo 4\n"; + demo4(); + + std::cout << "\nDemo 5\n"; + demo5(); + + std::cout << "\nDemo 6\n"; + demo6(); + + std::cout << "\nDemo 7\n"; + demo7(); + + std::cout << "\nDemo 8\n"; + demo8(); + + std::cout << "\nDemo 9\n"; + demo9(); +} + +int +run_game(int argc, char **argv) +{ + std::vector loggers {}; + bool have_stdout = false; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-log")) { + char *fn = argv[++i]; + if (!strcmp(fn, "-")) { + loggers.push_back(new LoggingEventPrinter {std::cout}); + have_stdout = true; + } else { + auto *of = new std::ofstream {fn}; + if (!*of) { + std::cerr << "Failed to open file: " << fn << "\n"; + return 1; + } + loggers.push_back(new LoggingEventPrinter {of}); + } + } else { + std::cerr << "Unknown option: " << argv[i] << "\n"; + return 1; + } + } + + Map *map = new Map {10, 10}; + + Game g {map}; + + for (auto *logger: loggers) { + g.logSink()->subscribe(logger); + } + + EventPrinter *pr = nullptr; + if (!have_stdout) { + pr = new EventPrinter {std::cout}; + g.subscribe(pr); + } + + Base *b1 = new Base {}; + map->addBase(b1, {1, 1}); + g.addBase(b1); + + Base *b2 = new Base {}; + map->addBase(b2, {8, 8}); + g.addBase(b2); + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(std::cin); + g.setPlayer(0, p1); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(std::cin); + g.setPlayer(1, p2); + + while (g.playersCount()) + g.spin(); + + for (auto *logger: loggers) { + g.logSink()->unsubscribe(logger); + delete logger; + } + + if (pr) { + g.unsubscribe(pr); + delete pr; + } + + return 0; +} + +int +main(int argc, char **argv) +{ + if (argc == 2 + && !strcmp(argv[1], "-demo")) { + run_demos(); + return 0; + } + + return run_game(argc, argv); +} diff --git a/8303/Parfentev_Leonid/lab4/map.cpp b/8303/Parfentev_Leonid/lab4/map.cpp new file mode 100644 index 000000000..c21541c17 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/map.cpp @@ -0,0 +1,155 @@ +#include "point.hpp" +#include "unit.hpp" +#include "placeable.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "landscape.hpp" +#include "map.hpp" + + +Map::Map(int w, int h) + :_rm{w, h} +{ + for (auto rmiter = _rm.iterAt({0, 0}); + rmiter.y() < _rm.height(); + rmiter.advance(1)) { + rmiter.cell().setLandscape(new landscapes::Normal {}); + } +} + +MapIter +Map::addUnit(Unit *u, Vec2 pt) +{ + if (u->hasPosition()) + return MapIter::makeNull(); + + if (_units_max >= 0 + && _units_count == _units_max) + return MapIter::makeNull(); + + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.unit()) + return MapIter::makeNull(); + + cell.setUnit(u); + u->setPosition(pt); + + ++_units_count; + + cell.landscape()->onEnter(u); + + return MapIter{rmiter}; +} + +Unit * +Map::removeUnitAt(Vec2 at) +{ + RectMapIter rmiter = _rm.iterAt(at); + Cell &cell = rmiter.cell(); + Unit *u = dynamic_cast(cell.unit()); + + if (u) { + --_units_count; + cell.landscape()->onLeave(u); + if (auto *n = dynamic_cast(cell.object())) { + n->onLeave(u); + } + + cell.setUnit(nullptr); + u->unsetPosition(); + } + + return u; +} + +MapIter +Map::addPlaceable(Placeable *p, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.object()) { + return MapIter::makeNull(); + } + + cell.setObject(p); + p->setPosition(pt); + + return MapIter{rmiter}; +} + +MapIter +Map::addBase(Base *b, Vec2 pt) +{ + return addPlaceable(b, pt); +} + +MapIter +Map::addNeutralObject(NeutralObject *n, Vec2 pt) +{ + return addPlaceable(n, pt); +} + +void +Map::setLandscape(Landscape *l, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + cell.setLandscape(l); +} + +MapInfo +Map::infoAt(Vec2 pt) const +{ + return MapInfo{&_rm.at(pt)}; +} + +Unit * +MapIter::unit() const +{ + return _it.cell().unit(); +} + +Base * +MapIter::base() const +{ + return dynamic_cast(_it.cell().object()); +} + +NeutralObject * +MapIter::neutralObject() const +{ + return dynamic_cast(_it.cell().object()); +} + +Landscape * +MapIter::landscape() const +{ + return _it.cell().landscape(); +} + +const Landscape * +MapInfo::landscape() const +{ + return _cell->landscape(); +} + +const Unit * +MapInfo::unit() const +{ + return _cell->unit(); +} + +const Base * +MapInfo::base() const +{ + return dynamic_cast(_cell->object()); +} + +const NeutralObject * +MapInfo::neutralObject() const +{ + return dynamic_cast(_cell->object()); +} diff --git a/8303/Parfentev_Leonid/lab4/map.hpp b/8303/Parfentev_Leonid/lab4/map.hpp new file mode 100644 index 000000000..eb150cf76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/map.hpp @@ -0,0 +1,119 @@ +#ifndef _H_MAP_HPP +#define _H_MAP_HPP + +#include "rectmap.hpp" + +// Map interface doesn’t know about cells -- instead, it only cares +// about certain kinds of Placeables + + +class Map; + +class Unit; +class Base; +class NeutralObject; + +class MapIter { + RectMapIter _it; + + friend class Map; + + MapIter(RectMapIter r) + :_it{r} {} + +public: + static MapIter makeNull() + { + return MapIter{RectMapIter::makeNull()}; + } + + bool operator==(const MapIter &o) const { return _it == o._it; } + bool operator!=(const MapIter &o) const { return _it != o._it; } + + int x() const { return _it.x(); } + int y() const { return _it.y(); } + Vec2 point() const { return _it.point(); } + + bool null() const { return _it.null(); } + bool valid() const { return _it.valid(); } + + void shift(Vec2 dxy) { _it.moveTo(point().shifted(dxy)); } + MapIter shifted(Vec2 dxy) const + { + return MapIter{_it.otherAt(point().shifted(dxy))}; + } + + void moveTo(Vec2 xy) { _it.moveTo(xy); } + MapIter otherAt(Vec2 xy) const + { + return MapIter{_it.otherAt(xy)}; + } + + void advance(int d) { _it.advance(d); } + MapIter advanced(int d) const + { + return MapIter{_it.advanced(d)}; + } + + Unit *unit() const; + Base *base() const; + NeutralObject *neutralObject() const; + Landscape *landscape() const; +}; + +class MapInfo { + const Cell *_cell; + +public: + MapInfo(const Cell *c) + :_cell{c} {} + + const Landscape *landscape() const; + const Unit *unit() const; + const Base *base() const; + const NeutralObject *neutralObject() const; +}; + +class Map { + RectMap _rm; + int _units_count = 0; + int _units_max = -1; + + MapIter addPlaceable(Placeable *p, Vec2 pt); + +public: + Map(int w, int h); + + int width() const { return _rm.width(); } + int height() const { return _rm.height(); } + MapIter iterAt(Vec2 pt) { return MapIter{_rm.iterAt(pt)}; } + MapIter iterAt(int x, int y) { return iterAt({x, y}); } + + MapIter begin() { return iterAt(0, 0); } + MapIter end() { return iterAt(0, height()); } + + MapInfo infoAt(Vec2 pt) const; + + MapIter addUnit(Unit *u, Vec2 pt); + Unit *removeUnitAt(Vec2 at); + Unit *removeUnitAt(MapIter iter) + { + return removeUnitAt(iter.point()); + } + + MapIter addBase(Base *b, Vec2 pt); + MapIter addNeutralObject(NeutralObject *n, Vec2 pt); + void setLandscape(Landscape *l, Vec2 pt); + + int maxUnitsCount() const { return _units_max; } + bool setMaxUnitsCount(int x) + { + if (_units_count > x) + return false; + _units_max = x; + return true; + } + int unitsCount() const { return _units_count; } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/mediator.cpp b/8303/Parfentev_Leonid/lab4/mediator.cpp new file mode 100644 index 000000000..348997877 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/mediator.cpp @@ -0,0 +1,111 @@ +#include "point.hpp" +#include "map.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "neutral_object.hpp" +#include "mediator.hpp" + + +MapInfo +Mediator::infoAt(Vec2 pt) +{ + return _map->infoAt(pt); +} + +Vec2 +Mediator::mapSize() +{ + return {_map->width(), + _map->height()}; +} + +bool +Mediator::moveUnitTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit() + || !u->canMove(ito)) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitMoved {u, from}); + return true; +} + +bool +Mediator::attackTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (!ito.unit() + || !u->canAttackTo(ito)) { + return false; + } + + auto pos = u->actualPosition(ito); + Unit *t = pos.unit(); + + u->emit(new events::UnitAttacked {u, pos.point(), t}); + + if (t) { + t->emit(new events::UnitWasAttacked {u, t}); + + auto damage_pair = u->baseAttack(pos); + auto damage_spec = t->actualDamage(damage_pair.first, + damage_pair.second); + int dmg = damage_spec.evaluate(); + + t->takeDamage(dmg); + } + + return true; +} + +bool +Mediator::useObject(Unit *u) +{ + auto iter = _map->iterAt(u->position()); + + NeutralObject *n = iter.neutralObject(); + + if (!n + || !n->canUse(u)) { + return false; + } + + n->onUse(u, this); + + u->emit(new events::UnitUsedObject {u, n}); + + return true; +} + +bool +Mediator::spawnUnit(Unit *u, Vec2 at) +{ + return !_map->addUnit(u, at).null(); +} + +bool +Mediator::teleportUnit(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit()) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitTeleported {u, from}); + return true; +} diff --git a/8303/Parfentev_Leonid/lab4/mediator.hpp b/8303/Parfentev_Leonid/lab4/mediator.hpp new file mode 100644 index 000000000..cc1549f65 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/mediator.hpp @@ -0,0 +1,31 @@ +#ifndef _H_MEDIATOR_HPP +#define _H_MEDIATOR_HPP + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" + + +class Mediator { + Map *_map; + +public: + Mediator(Map *map) + :_map{map} {} + + MapInfo infoAt(Vec2 pt); + + Vec2 mapSize(); + + bool moveUnitTo(Unit *u, Vec2 to); + + bool attackTo(Unit *u, Vec2 to); + + bool useObject(Unit *u); + + bool spawnUnit(Unit *u, Vec2 at); + bool teleportUnit(Unit *u, Vec2 to); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab4/melee_units.hpp b/8303/Parfentev_Leonid/lab4/melee_units.hpp new file mode 100644 index 000000000..f36626401 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/melee_units.hpp @@ -0,0 +1,88 @@ +#ifndef _H_MELEE_UNITS_HPP +#define _H_MELEE_UNITS_HPP + +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class MeleeAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + +public: + MeleeAttack(AttackKind kind, int base_dmg) + :_kind{kind}, _base_damage{base_dmg} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return to.unit() != nullptr + && to.point().adjacent(u->position()); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter) + { + return std::make_pair( + _kind, + int(_base_damage * u->relativeHealth())); + } +}; + +class BasicMeleeUnit: public Unit { +public: + BasicMeleeUnit(int speed, + AttackKind attack_kind, + int base_dmg, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new MeleeAttack {attack_kind, base_dmg}, + def, base_health} {} +}; + +namespace units { + class Swordsman: public BasicMeleeUnit { + public: + Swordsman() :BasicMeleeUnit{ + 2, + AttackKind::sword, 40, + DefenseLevelDeco::good_defense_deco( + AttackKind::spear, + DefenseLevelDeco::vulnerability_deco( + AttackKind::cavalry, + new BasicDefense {})), + 100} {} + }; + + class Spearsman: public BasicMeleeUnit { + public: + Spearsman() :BasicMeleeUnit{ + 2, + AttackKind::spear, 75, + DefenseLevelDeco::good_defense_deco( + AttackKind::cavalry, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; + + class Cavalry: public BasicMeleeUnit { + public: + Cavalry() :BasicMeleeUnit{ + 3, + AttackKind::cavalry, 50, + DefenseLevelDeco::good_defense_deco( + AttackKind::sword, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/neutral_object.hpp b/8303/Parfentev_Leonid/lab4/neutral_object.hpp new file mode 100644 index 000000000..bcb94331e --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/neutral_object.hpp @@ -0,0 +1,22 @@ +#ifndef _H_NEUTRAL_OBJECT_HPP +#define _H_NEUTRAL_OBJECT_HPP + +#include "placeable.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "mediator.hpp" + + +class NeutralObject: public Placeable { +public: + virtual bool canUse(const Unit *) { return true; } + virtual void onUse(Unit *u, Mediator *m) =0; + + // It’s the object’s job to determine whether it was used by the + // leaving unit. + virtual void onLeave(Unit *) {}; + + virtual ~NeutralObject() {}; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/neutral_object_types.hpp b/8303/Parfentev_Leonid/lab4/neutral_object_types.hpp new file mode 100644 index 000000000..203d967f6 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/neutral_object_types.hpp @@ -0,0 +1,187 @@ +#ifndef _H_NEUTRAL_OBJECT_TYPES_HPP +#define _H_NEUTRAL_OBJECT_TYPES_HPP + +#include + +#include "neutral_object.hpp" +#include "mediator.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "ranged_units.hpp" +#include "common_policies.hpp" + + +class ExtendedShootingRange: public NestedAttack { + double _delta; + +public: + ExtendedShootingRange(AttackPolicy *p, double delta) + :NestedAttack{p}, _delta{delta} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = to.point().distance(u->position()); + auto *a = dynamic_cast(attackPolicy()); + return dist >= a->minRange() + && dist <= (a->maxRange() + _delta); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + return attackPolicy()->baseAttack(u, to); + } +}; + + + +namespace objects { + + class HealingWell: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *) override + { + u->heal(25); + } + }; + + class Tower: public NeutralObject { + AttackPolicy *_prev; + ExtendedShootingRange *_cur = nullptr; + + public: + virtual bool + canUse(const Unit *u) override + { + return dynamic_cast(u); + } + + virtual void + onUse(Unit *u, Mediator *) override + { + _prev = u->attackPolicy(); + _cur = new ExtendedShootingRange {_prev, 5}; + u->setAttackPolicy(_cur); + } + + virtual void + onLeave(Unit *u) override + { + if (_cur == nullptr) { + return; + } + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + _cur->setAttackPolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + }; + + class TunnelsEntrance: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *m) override + { + static const int w = 5; + + Vec2 at = u->position(); + + int max_n = 0; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() == nullptr) { + ++max_n; + } + } + } + + std::uniform_int_distribution<> distr {0, max_n-1}; + int n = distr(global_random); + + Vec2 dest; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() != nullptr) { + continue; + } + if (!--n) { + dest = iter; + break; + } + } + } + + m->teleportUnit(u, dest); + } + }; + + class WeaponSmiths: public NeutralObject { + public: + class UnitFilter { + public: + virtual bool + applicable(const Unit *u) =0; + }; + + template + class SimpleUnitFilter: public UnitFilter { + public: + virtual bool + applicable(const Unit *u) override + { + return dynamic_cast(u); + } + }; + + private: + double _mul; + UnitFilter *_filter; + + public: + explicit WeaponSmiths(double mul, UnitFilter *filter=nullptr) + :_mul{mul}, _filter{filter} {} + + virtual bool + canUse(const Unit *u) override + { + if (_filter + && !_filter->applicable(u)) { + return false; + } + + for (const AttackPolicyContainer *apc = u; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (dynamic_cast(apc)) { + return false; + } + } + + return true; + } + + virtual void + onUse(Unit *u, Mediator *) override + { + auto *prev = u->attackPolicy(); + auto *new_p = new MultiplierAttackPolicy {prev, _mul}; + u->setAttackPolicy(new_p); + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/object_print.cpp b/8303/Parfentev_Leonid/lab4/object_print.cpp new file mode 100644 index 000000000..da59e1054 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/object_print.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "object_print.hpp" + + +#define UNIT_ENTRY(T) {std::type_index{typeid(units::T)}, #T} +static const std::map unit_names { + UNIT_ENTRY(Swordsman), + UNIT_ENTRY(Spearsman), + UNIT_ENTRY(Cavalry), + UNIT_ENTRY(Archer), + UNIT_ENTRY(Slinger), + UNIT_ENTRY(Onager), + UNIT_ENTRY(BoltThrower), +}; +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T) {std::type_index{typeid(landscapes::T)}, #T} +static const std::map landscape_names { + LANDSCAPE_ENTRY(Normal), + LANDSCAPE_ENTRY(Swamp), + LANDSCAPE_ENTRY(Forest), +}; +#undef LANDSCAPE_ENTRY + +#define N_OBJECT_ENTRY(T, n) {std::type_index{typeid(objects::T)}, n} +static const std::map objects_names { + N_OBJECT_ENTRY(HealingWell, "Healing Well"), + N_OBJECT_ENTRY(Tower, "Tower"), + N_OBJECT_ENTRY(TunnelsEntrance, "Tunnels Entrance"), + N_OBJECT_ENTRY(WeaponSmiths, "Weapon Smiths"), +}; +#undef N_OBJECT_ENTRY + + +std::ostream & +operator<<(std::ostream &os, Vec2 pt) +{ + return os << "{" << pt.x() << "," << pt.y() << "}"; +} + +std::ostream & +operator<<(std::ostream &os, const Unit *u) +{ + return os << "a " + << unit_names.at(std::type_index{typeid(*u)}) + << " with " << u->health() << " HP at " + << u->position(); +} + +std::ostream & +operator<<(std::ostream &os, const Landscape *l) +{ + return os << landscape_names.at(std::type_index{typeid(*l)}); +} + +std::ostream & +operator<<(std::ostream &os, const Base *b) +{ + os << "a Base with " << b->unitsCount(); + int m = b->maxUnitsCount(); + if (m >= 0) { + os << "/" << m; + } + + return os << " units at " << b->position(); +} + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n) +{ + return os << "a " + << objects_names.at(std::type_index{typeid(*n)}) + << " at " << n->position(); +} + + + +static const std::map class_chars { +#define UNIT_ENTRY(T, x) {std::type_index{typeid(units::T)}, x} + UNIT_ENTRY(Swordsman, 'S'), + UNIT_ENTRY(Spearsman, 'P'), + UNIT_ENTRY(Cavalry, 'C'), + UNIT_ENTRY(Archer, 'A'), + UNIT_ENTRY(Slinger, 's'), + UNIT_ENTRY(Onager, 'O'), + UNIT_ENTRY(BoltThrower, 'B'), +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T, x) \ + {std::type_index{typeid(landscapes::T)}, x} + LANDSCAPE_ENTRY(Normal, '.'), + LANDSCAPE_ENTRY(Swamp, '='), + LANDSCAPE_ENTRY(Forest, '*'), +#undef LANDSCAPE_ENTRY +}; + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info) +{ + if (const Unit *u = info.unit()) { + os << class_chars.at(std::type_index{typeid(*u)}); + } else if (info.base()) { + os << "+"; + } else if (info.neutralObject()) { + os << '#'; + } else { + auto *l = info.landscape(); + os << class_chars.at(std::type_index{typeid(*l)}); + } + + return os; +} + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1) +{ + for (int y = y0; y < y1; ++y) { + for (int x = x0; x < x1; ++x) { + displayMapInfo(os, map->infoAt({x, y})); + } + os << "\n"; + } + return os; +} + +std::ostream & +operator<<(std::ostream &os, const Map *map) +{ + return displayMap(os, map, 0, 0, map->width(), map->height()); +} diff --git a/8303/Parfentev_Leonid/lab4/object_print.hpp b/8303/Parfentev_Leonid/lab4/object_print.hpp new file mode 100644 index 000000000..8df10d966 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/object_print.hpp @@ -0,0 +1,40 @@ +#ifndef _H_OBJECT_PRINT_HPP +#define _H_OBJECT_PRINT_HPP + +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "neutral_object.hpp" + +std::ostream & +operator<<(std::ostream &os, Vec2 pt); + +std::ostream & +operator<<(std::ostream &os, const Unit *u); + +std::ostream & +operator<<(std::ostream &os, const Landscape *l); + +std::ostream & +operator<<(std::ostream &os, const Base *b); + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n); + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info); + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1); + +std::ostream & +operator<<(std::ostream &os, const Map *map); + +#endif diff --git a/8303/Parfentev_Leonid/lab4/object_w_health.hpp b/8303/Parfentev_Leonid/lab4/object_w_health.hpp new file mode 100644 index 000000000..e3db6d050 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/object_w_health.hpp @@ -0,0 +1,30 @@ +#ifndef _H_OBJECT_W_HEALTH_HPP +#define _H_OBJECT_W_HEALTH_HPP + + +class ObjectWithHealth { + int _health, _base_health; + +public: + ObjectWithHealth(int base_health) + :_health{base_health}, + _base_health{base_health} {} + + int + health() const { return _health; } + int + baseHealth() const { return _base_health; } + double + relativeHealth() const { return _health / (double)_base_health; } + bool + alive() const { return health() > 0; } + + virtual void + heal(int hp) { _health += hp; } + + virtual void + takeDamage(int dmg) { _health -= dmg; } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab4/pathfinder.cpp b/8303/Parfentev_Leonid/lab4/pathfinder.cpp new file mode 100644 index 000000000..865f34de2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/pathfinder.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "map.hpp" +#include "point.hpp" +#include "pathfinder.hpp" + + +PathFinder::Pt2 +PathFinder::Pt2::toDirection(int dir) const +{ + // assert(dir >= 0 && dir < 8); + + int d1 = (dir + 1) % 8; + int dx = (d1 % 4 == 3) ? 0 : (d1 < 4) ? 1 : -1, + dy = (dir % 4 == 0) ? 0 : (dir < 4) ? 1 : -1; + + return Pt2{pt.shifted({dx, dy}), depth + 1}; +} + +bool +PathFinder::run() +{ + Vec2 start_pt = _start.point(); + + while (!_frontier.empty()) { + Pt2 current = _frontier.front(); + _frontier.pop(); + + if (start_pt.shifted(current.pt) == _end) + return true; + + if (_max >= 0 + && current.depth >= _max) + continue; + + Vec2WithCmp cur_v2wc {current.pt}; + auto iter = _dirs.find(cur_v2wc); + if (iter == _dirs.end()) + _dirs[cur_v2wc] = -1; + + for (int i = 0; i < 8; ++i) { + Pt2 shifted_delta = current.toDirection(i); + + Vec2WithCmp sh_v2wc {shifted_delta.pt}; + auto iter = _dirs.find(sh_v2wc); + if (iter != _dirs.end()) + continue; + + MapIter shifted = _start.shifted(shifted_delta.pt); + if (!shifted.valid() + || shifted.unit()) + continue; + + _frontier.push(shifted_delta); + } + } + + return false; +} diff --git a/8303/Parfentev_Leonid/lab4/pathfinder.hpp b/8303/Parfentev_Leonid/lab4/pathfinder.hpp new file mode 100644 index 000000000..d4c614aae --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/pathfinder.hpp @@ -0,0 +1,56 @@ +#ifndef _H_PATHFINDER_HPP +#define _H_PATHFINDER_HPP + +#include +#include + +#include "point.hpp" +#include "map.hpp" + + +class PathFinder { + MapIter _start; + Vec2 _end; + int _max; + + struct Vec2WithCmp { + Vec2 v; + + bool operator<(Vec2WithCmp o) const + { + if (v.y() == o.v.y()) + return v.x() < o.v.x(); + return v.y() < o.v.y(); + } + }; + + struct Pt2 { + Vec2 pt; + int depth; + + static Pt2 zero() { return {Vec2{0, 0}, 0}; } + + Pt2(Vec2 pt, int depth=0) + :pt{pt}, depth{depth} {} + + Pt2 toDirection(int dir) const; + + bool pt_equal(Vec2 pt2) + { + return pt == pt2; + } + }; + + std::map _dirs {}; + std::queue _frontier {{Pt2::zero()}}; + +public: + PathFinder(MapIter from, MapIter to, int max_steps) + :_start{from}, + _end{to.point()}, + _max{max_steps} {} + + bool run(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/placeable.hpp b/8303/Parfentev_Leonid/lab4/placeable.hpp new file mode 100644 index 000000000..bd7f0ef74 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/placeable.hpp @@ -0,0 +1,36 @@ +#ifndef _H_PLACEABLE_HPP +#define _H_PLACEABLE_HPP + +#include "point.hpp" + +class Placeable { + bool _placed = false; + Vec2 _pos; + +public: + bool + hasPosition() const { return _placed; } + + const Vec2 & + position() const + { + return _pos; + } + + void + setPosition(const Vec2 &pos) + { + _pos = pos; + _placed = true; + } + + void + unsetPosition() + { + _placed = false; + } + + virtual ~Placeable() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/player.hpp b/8303/Parfentev_Leonid/lab4/player.hpp new file mode 100644 index 000000000..e30e6f004 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/player.hpp @@ -0,0 +1,26 @@ +#ifndef _H_PLAYER_HPP +#define _H_PLAYER_HPP + +#include +#include + +#include "mediator.hpp" +#include "base.hpp" +#include "event.hpp" + + +class Player: public EventEmitter { + std::string _name; + +public: + Player(std::string name) + :_name{std::move(name)} {} + + const std::string &name() const { return _name; } + + virtual bool takeTurn(Mediator *m, Base *b) =0; + + virtual ~Player() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/point.cpp b/8303/Parfentev_Leonid/lab4/point.cpp new file mode 100644 index 000000000..bce7b3325 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/point.cpp @@ -0,0 +1,29 @@ +#include + +#include "point.hpp" + +double +Vec2::length() const +{ + return sqrt(_x*_x + _y*_y); +} + +double +Vec2::distance(const Vec2 &pt) const +{ + return delta(pt).length(); +} + +bool +Vec2::unit() const +{ + return (_x || _y) + && abs(_x) <= 1 + && abs(_y) <= 1; +} + +bool +Vec2::adjacent(const Vec2 &pt) const +{ + return delta(pt).unit(); +} diff --git a/8303/Parfentev_Leonid/lab4/point.hpp b/8303/Parfentev_Leonid/lab4/point.hpp new file mode 100644 index 000000000..8eec01d76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/point.hpp @@ -0,0 +1,40 @@ +#ifndef _H_POINT_HPP +#define _H_POINT_HPP + +class Vec2 { + int _x, _y; + +public: + Vec2() :Vec2{0, 0} {} + Vec2(int x, int y) :_x{x}, _y{y} {} + + int x() const { return _x; } + int y() const { return _y; } + + bool operator==(const Vec2 &pt) const + { + return _x == pt._x && _y == pt._y; + } + bool operator!=(const Vec2 &pt) const + { + return !(*this == pt); + } + + Vec2 delta(const Vec2 &o) const + { + return Vec2{_x - o._x, _y - o._y}; + } + + double length() const; + double distance(const Vec2 &pt) const; + + bool unit() const; + bool adjacent(const Vec2 &pt) const; + + Vec2 shifted(const Vec2 &dxy) const + { + return Vec2{_x + dxy._x, _y + dxy._y}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/ranged_units.hpp b/8303/Parfentev_Leonid/lab4/ranged_units.hpp new file mode 100644 index 000000000..1798f04ce --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/ranged_units.hpp @@ -0,0 +1,94 @@ +#ifndef _H_RANGED_UNITS_HPP +#define _H_RANGED_UNITS_HPP + +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class RangedAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + double _min_distance, _max_distance; + double _dist_pow; + + static double + distance(const Unit *u, MapIter to) + { + return to.point().distance(u->position()); + } + +public: + double minRange() const { return _min_distance; } + double maxRange() const { return _max_distance; } + + RangedAttack(AttackKind kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow) + :_kind{kind}, + _base_damage{base_dmg}, + _min_distance{min_dist}, + _max_distance{max_dist}, + _dist_pow{dist_pow} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return dist >= _min_distance + && dist <= _max_distance; + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return std::make_pair( + _kind, + int(_base_damage + * u->relativeHealth() + / pow(dist, _dist_pow))); + } +}; + +class BasicRangedUnit: public Unit { +public: + BasicRangedUnit(int speed, + AttackKind attack_kind, + int base_dmg, + double max_dist, + double dist_pow, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new RangedAttack {attack_kind, base_dmg, + 1., max_dist, dist_pow}, + def, base_health} {} +}; + +namespace units { + class Archer: public BasicRangedUnit { + public: + Archer() :BasicRangedUnit{ + 2, + AttackKind::arrow, 50, 5., .20, + new BasicDefense {0.9}, + 40} {} + }; + + class Slinger: public BasicRangedUnit { + public: + Slinger() :BasicRangedUnit{ + 2, + AttackKind::stone, 60, 3., .30, + new BasicDefense {.09}, + 50} {} + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab4/rectmap.cpp b/8303/Parfentev_Leonid/lab4/rectmap.cpp new file mode 100644 index 000000000..b82667c40 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/rectmap.cpp @@ -0,0 +1,55 @@ +#include "rectmap.hpp" + +#include "unit.hpp" + +Cell::~Cell() +{ + delete _u; + delete _obj; + delete _l; +} + +bool +RectMapIter::valid() const +{ + return x() >= 0 + && x() < _map->width() + && y() >= 0 + && y() < _map->height(); +} + +Cell & +RectMapIter::cell() const +{ + return _map->at(point()); +} + +void +RectMapIter::moveTo(Vec2 xy) +{ + _pt = xy; +} + +RectMapIter +RectMapIter::otherAt(Vec2 xy) const +{ + RectMapIter other = *this; + other.moveTo(xy); + return other; +} + +void +RectMapIter::advance(int d) +{ + int nx = x() + d, + w = _map->width(); + _pt = Vec2{nx % w, y() + nx / w}; +} + +RectMapIter +RectMapIter::advanced(int d) const +{ + RectMapIter other = *this; + other.advance(d); + return other; +} diff --git a/8303/Parfentev_Leonid/lab4/rectmap.hpp b/8303/Parfentev_Leonid/lab4/rectmap.hpp new file mode 100644 index 000000000..a3119ffb1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/rectmap.hpp @@ -0,0 +1,99 @@ +#ifndef _H_RECTMAP_HPP +#define _H_RECTMAP_HPP + +#include "point.hpp" +#include "placeable.hpp" +#include "landscape.hpp" + + +class Unit; + +class Cell { + Landscape *_l = nullptr; + Unit *_u = nullptr; + Placeable *_obj = nullptr; + +public: + Cell() {} + + ~Cell(); + + Landscape *landscape() const { return _l; } + void setLandscape(Landscape *l) + { + delete _l; + _l = l; + } + + Unit *unit() const { return _u; } + void setUnit(Unit *u) { _u = u; } + + Placeable *object() const { return _obj; } + void setObject(Placeable *p) { _obj = p; } +}; + +class RectMap; + +class RectMapIter { + RectMap *_map; + Vec2 _pt; + +public: + RectMapIter(RectMap *map, Vec2 pt) + :_map{map}, _pt{pt} {} + RectMapIter(RectMap *map, int x, int y) + :_map{map}, _pt{x, y} {} + + static RectMapIter makeNull() { return {nullptr, {0, 0}}; } + + bool operator==(const RectMapIter &o) const + { + return _map == o._map + && _pt == o._pt; + } + bool operator!=(const RectMapIter &o) const + { + return !(*this == o); + } + + int x() const { return _pt.x(); } + int y() const { return _pt.y(); } + Vec2 point() const { return _pt; } + + Cell &cell() const; + + bool null() const { return _map == nullptr; } + bool valid() const; + + void moveTo(Vec2 xy); + RectMapIter otherAt(Vec2 xy) const; + + void advance(int d); + RectMapIter advanced(int d) const; +}; + +class RectMap { + const int _w, _h; + Cell * const _storage; + +public: + RectMap(int w, int h) + :_w{w}, _h{h}, _storage{new Cell [w * h]} {} + + int width() const { return _w; } + int height() const { return _h; } + + Cell &at(Vec2 pt) { return _storage[pt.x() + pt.y()*_w]; } + const Cell &at(Vec2 pt) const + { + return _storage[pt.x() + pt.y()*_w]; + } + RectMapIter iterAt(Vec2 pt) { return RectMapIter{this, pt}; } + + ~RectMap() + { + delete[] _storage; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/report.pdf b/8303/Parfentev_Leonid/lab4/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1d15982cc3d45625076736fce10018ca54cd74fd GIT binary patch literal 103969 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

  • BI=NkjOhrI*l3h?gt~sOO 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%0DF~$K(bq=@3}b7A``6)+<{$ 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 literal 0 HcmV?d00001 diff --git a/8303/Parfentev_Leonid/lab4/unit.cpp b/8303/Parfentev_Leonid/lab4/unit.cpp new file mode 100644 index 000000000..ef46978a2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/unit.cpp @@ -0,0 +1,5 @@ +#include + +#include "unit.hpp" + +std::default_random_engine global_random {}; diff --git a/8303/Parfentev_Leonid/lab4/unit.hpp b/8303/Parfentev_Leonid/lab4/unit.hpp new file mode 100644 index 000000000..45cfef0f0 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/unit.hpp @@ -0,0 +1,258 @@ +#ifndef _H_UNIT_HPP +#define _H_UNIT_HPP + +#include +#include +#include + +#include "event.hpp" +#include "event_types.hpp" +#include "object_w_health.hpp" +#include "map.hpp" + + +extern std::default_random_engine global_random; + + +class MovePolicy { +public: + virtual bool canMove(const Unit *u, MapIter to) =0; + virtual ~MovePolicy() {} +}; + +enum class AttackKind { + invalid, sword, spear, cavalry, arrow, stone, rock, bolt, +}; + +// NOTE: can’t do area damage +class AttackPolicy { +public: + virtual bool canAttackTo(const Unit *u, MapIter to) =0; + + virtual MapIter actualPosition(const Unit *, MapIter to) + { + return to; + } + + // returns kind and base damage + virtual std::pair + baseAttack(const Unit *u, MapIter to) =0; + + virtual ~AttackPolicy() {} +}; + +struct DamageSpec { + int base_damage, damage_spread; + + void + scale(double k) + { + base_damage *= k; + damage_spread *= k; + } + + DamageSpec + scaled(double k) const + { + auto ds = *this; + ds.scale(k); + return ds; + } + + int evaluate() const + { + std::uniform_int_distribution<> + dist {-damage_spread, damage_spread}; + + return base_damage + dist(global_random); + } +}; + +class DefensePolicy { +protected: + static DamageSpec + make_spec(double base, double spread) + { + return DamageSpec{(int)base, (int)spread}; + } + + static DamageSpec + defense_level(double k, int dmg) + { + return make_spec(round(1.0*dmg/k), + round(0.25*dmg/k)); + } + + static DamageSpec + normal_defense(double dmg) + { + return defense_level(1.0, dmg); + } + +public: + // returns base damage and spread + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) =0; + + virtual ~DefensePolicy() {} +}; + +class MovePolicyContainer { + MovePolicy *_mp; + +public: + MovePolicyContainer(MovePolicy *mp) :_mp{mp} {} + + MovePolicy *movePolicy() const { return _mp; } + void setMovePolicy(MovePolicy *mp) + { + _mp = mp; + } + virtual ~MovePolicyContainer() { delete _mp; } + + MovePolicyContainer * + findMoveContainerOf(const MovePolicy *mp) + { + for (MovePolicyContainer *mpc = this; mpc; + mpc = dynamic_cast( + mpc->movePolicy())) { + if (mpc->movePolicy() == mp) { + return mpc; + } + } + return nullptr; + } +}; + +class DefensePolicyContainer { + DefensePolicy *_dp; + +public: + DefensePolicyContainer(DefensePolicy *dp) :_dp{dp} {} + + DefensePolicy *defensePolicy() const { return _dp; } + void setDefensePolicy(DefensePolicy *dp) + { + _dp = dp; + } + virtual ~DefensePolicyContainer() { delete _dp; } + + DefensePolicyContainer * + findDefenseContainerOf(const DefensePolicy *dp) + { + for (DefensePolicyContainer *dpc = this; dpc; + dpc = dynamic_cast( + dpc->defensePolicy())) { + if (dpc->defensePolicy() == dp) { + return dpc; + } + } + return nullptr; + } +}; + +class AttackPolicyContainer { + AttackPolicy *_ap; + +public: + AttackPolicyContainer(AttackPolicy *ap) :_ap{ap} {} + + AttackPolicy *attackPolicy() const { return _ap; } + void setAttackPolicy(AttackPolicy *ap) + { + _ap = ap; + } + virtual ~AttackPolicyContainer() { delete _ap; } + + AttackPolicyContainer * + findAttackContainerOf(const AttackPolicy *ap) + { + for (AttackPolicyContainer *apc = this; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (apc->attackPolicy() == ap) { + return apc; + } + } + return nullptr; + } +}; + +class Unit: public Placeable, + public ObjectWithHealth, + public EventEmitter, + public MovePolicyContainer, + public DefensePolicyContainer, + public AttackPolicyContainer { +public: + Unit(MovePolicy *move, + AttackPolicy *attack, + DefensePolicy *defense, + int base_health) + :ObjectWithHealth{base_health}, + MovePolicyContainer{move}, + DefensePolicyContainer{defense}, + AttackPolicyContainer{attack} {} + + virtual void + heal(int hp) + { + emit(new events::UnitGetsHealed {this, hp}); + ObjectWithHealth::heal(hp); + } + + virtual void + takeDamage(int dmg) + { + if (!alive()) { + return; + } + + emit(new events::UnitTakesDamage {this, dmg}); + + ObjectWithHealth::takeDamage(dmg); + + if (!alive()) { + emit(new events::UnitDeath {this}); + } + } + + bool + canMove(MapIter to) const + { + return movePolicy()->canMove(this, to); + } + + bool + canAttackTo(MapIter to) const + { + return attackPolicy()->canAttackTo(this, to); + } + + MapIter + actualPosition(MapIter to) const + { + return attackPolicy()->actualPosition(this, to); + } + + std::pair + baseAttack(MapIter to) const + { + return attackPolicy()->baseAttack(this, to); + } + + DamageSpec + actualDamage(AttackKind kind, int base) const + { + return defensePolicy()->actualDamage(this, kind, base); + } + + virtual ~Unit() override + { + if (alive()) { + emit(new events::UnitLiveDeleted {this}); + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/unit_factory.hpp b/8303/Parfentev_Leonid/lab4/unit_factory.hpp new file mode 100644 index 000000000..5f1f475bd --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/unit_factory.hpp @@ -0,0 +1,24 @@ +#ifndef _H_UNIT_FACTORY_HPP +#define _H_UNIT_FACTORY_HPP + +#include "unit.hpp" + + +class UnitFactory { +public: + virtual Unit *create() const =0; + + virtual ~UnitFactory() {} +}; + +template +class SimpleUnitFactory: public UnitFactory { +public: + virtual Unit * + create() const override + { + return new U {}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab4/zombie_collector.hpp b/8303/Parfentev_Leonid/lab4/zombie_collector.hpp new file mode 100644 index 000000000..9de5f6981 --- /dev/null +++ b/8303/Parfentev_Leonid/lab4/zombie_collector.hpp @@ -0,0 +1,36 @@ +#ifndef _H_ZOMBIE_COLLECTOR_HPP +#define _H_ZOMBIE_COLLECTOR_HPP + +#include + +#include "unit.hpp" +#include "event.hpp" +#include "map.hpp" + + +class ZombieCollector: public EventListener { + std::vector _units {}; + +public: + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + return handle(ee->event()); + } + if (auto *ee = dynamic_cast(e)) { + _units.push_back(ee->unit()); + } + } + + void + collect(Map *m) + { + for (auto *unit: _units) { + delete m->removeUnitAt(unit->position()); + } + _units.clear(); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/Makefile b/8303/Parfentev_Leonid/lab5/Makefile new file mode 100644 index 000000000..390e0a2e5 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/Makefile @@ -0,0 +1,23 @@ +EXENAME = main +HEADERS = point.hpp placeable.hpp rectmap.hpp map.hpp pathfinder.hpp \ + unit.hpp common_policies.hpp melee_units.hpp ranged_units.hpp \ + catapult_units.hpp demo.hpp event.hpp base.hpp object_w_health.hpp \ + landscape.hpp landscape_types.hpp neutral_object.hpp \ + neutral_object_types.hpp unit_factory.hpp game.hpp \ + object_print.hpp event_printer.hpp iostream_player.hpp \ + mediator.hpp logging.hpp factory_table.hpp storable.hpp \ + common_storables.hpp restorers.hpp +OBJFILES = point.o rectmap.o map.o pathfinder.o unit.o demo.o main.o \ + event.o base.o game.o object_print.o iostream_player.o mediator.o \ + storable.o +LDLIBS = -lm + +all: $(EXENAME) + +$(OBJFILES): $(HEADERS) + +$(EXENAME): $(OBJFILES) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + $(RM) $(EXENAME) $(OBJFILES) diff --git a/8303/Parfentev_Leonid/lab5/base.cpp b/8303/Parfentev_Leonid/lab5/base.cpp new file mode 100644 index 000000000..c2242496d --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/base.cpp @@ -0,0 +1,138 @@ +#include +#include +#include + +#include "unit.hpp" +#include "unit_factory.hpp" +#include "event.hpp" +#include "base.hpp" +#include "mediator.hpp" +#include "factory_table.hpp" + +#include "storable.hpp" +#include "common_storables.hpp" + + +bool +Base::canCreateUnit(const std::string &key) const +{ + if (unitsCount() == maxUnitsCount()) { + return false; + } + + if (!FactoryTable::instance()->canCreate(key)) { + return false; + } + + return true; +} + +int +Base::createUnit(const std::string &key, Mediator *m) +{ + if (!canCreateUnit(key)) { + return -1; + } + + if (m->infoAt(position()).unit()) { + return false; + } + + Unit *u = FactoryTable::instance()->create(key); + int id = addUnit(u); + + if (id < 0) { + delete u; + return -1; + } + + if (!m->spawnUnit(u, position())) { + removeUnit(u); + delete u; + return -1; + } + + return id; +} + +bool +Base::setMaxUnitsCount(int m) +{ + if (m < unitsCount()) { + return false; + } + _max_count = m; + return true; +} + +int +Base::addUnit(Unit *u) +{ + if (maxUnitsCount() >= 0 + && unitsCount() == maxUnitsCount()) { + return -1; + } + + _units[_next_idx] = u; + u->subscribe(this); + u->emit(new events::UnitAdded {u}); + + return _next_idx++; +} + +void +Base::removeUnit(Unit *u) +{ + u->unsubscribe(this); + + for (auto iter = _units.begin(); + iter != _units.end(); + ++iter) { + if (iter->second == u) { + _units.erase(iter); + break; + } + } +} + +Unit * +Base::getUnitById(int id) const +{ + auto iter = _units.find(id); + return (iter != _units.end()) + ? iter->second + : nullptr; +} + +void +Base::store(std::ostream &os) const +{ + os << "base " << _max_count << "\n"; +} + +bool +Base::restore(std::istream &is, + RestorerTable *) +{ + is >> _max_count; + return !is.fail(); +} + +void +Base::handle(Event *e) +{ + EventForwarder::handle(e); + + if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } else if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } +} + +Base::~Base() +{ + for (auto p: _units) { + p.second->unsubscribe(this); + } +} diff --git a/8303/Parfentev_Leonid/lab5/base.hpp b/8303/Parfentev_Leonid/lab5/base.hpp new file mode 100644 index 000000000..d07e8f29e --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/base.hpp @@ -0,0 +1,90 @@ +#ifndef _H_BASE_HPP +#define _H_BASE_HPP + +#include +#include +#include + +#include "storable.hpp" +#include "placeable.hpp" +#include "event.hpp" +#include "unit.hpp" +#include "mediator.hpp" + + +class Base: public Placeable, + public EventForwarder, + public Storable { + + std::map _units {}; + int _next_idx = 0; + int _max_count = -1; + +public: + Base() {} + + bool + canCreateUnit(const std::string &key) const; + int + createUnit(const std::string &key, Mediator *m); + + int + unitsCount() const { return (int)_units.size(); } + bool + setMaxUnitsCount(int m); + int + maxUnitsCount() const { return _max_count; } + + int + addUnit(Unit *u); + void + removeUnit(Unit *u); + Unit * + getUnitById(int id) const; + + class unitsIter { + using real_iter_t = std::map::const_iterator; + real_iter_t _iter; + + public: + unitsIter(real_iter_t it) + :_iter{it} {} + + int id() const { return _iter->first; } + Unit *unit() const { return _iter->second; } + unitsIter &operator++() { ++_iter; return *this; } + unitsIter + operator++(int) + { + unitsIter x{_iter}; + ++*this; + return x; + } + bool + operator==(const unitsIter &o) const + { + return _iter == o._iter; + } + bool + operator!=(const unitsIter &o) const + { + return !(*this == o); + } + }; + + unitsIter + unitsBegin() const { return unitsIter{_units.begin()}; } + unitsIter + unitsEnd() const { return unitsIter{_units.end()}; } + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &, + RestorerTable *) override; + + virtual void + handle(Event *e) override; + + virtual ~Base() override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/catapult_units.hpp b/8303/Parfentev_Leonid/lab5/catapult_units.hpp new file mode 100644 index 000000000..914c28041 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/catapult_units.hpp @@ -0,0 +1,170 @@ +#ifndef _H_CATAPULT_UNITS_HPP +#define _H_CATAPULT_UNITS_HPP + +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" +#include "ranged_units.hpp" + + +class CatapultAttack: public RangedAttack { + double _spread_tang, _spread_normal; + + struct FVec2 { + double x, y; + + explicit FVec2(const Vec2 &v) + :x{(double)v.x()}, y{(double)v.y()} {} + + FVec2(double x, double y) + :x{x}, y{y} {} + + operator Vec2() const + { + return Vec2{int(round(x)), int(round(y))}; + } + + FVec2 + orthogonal() const { return {y, -x}; } + + FVec2 & + operator*=(double a) + { + x *= a; + y *= a; + return *this; + } + FVec2 + operator*(double a) const + { + FVec2 tmp {*this}; + return tmp *= a; + } + + FVec2 + normalized() const { return (*this) * (1/sqrt(x*x + y*y)); } + + FVec2 & + operator+=(const FVec2 &dxy) + { + x += dxy.x; + y += dxy.y; + return *this; + } + FVec2 + operator+(const FVec2 &dxy) const + { + FVec2 tmp{*this}; + return tmp += dxy; + } + + FVec2 + apply(double t, double n) const + { + return normalized() * t + + orthogonal().normalized() * n; + } + }; + +public: + explicit CatapultAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0, + double min_dist=0, + double max_dist=0, + double dist_pow=0, + double spread_t=0, + double spread_n=0) + :RangedAttack{kind, base_dmg, min_dist, max_dist, dist_pow}, + _spread_tang{spread_t}, + _spread_normal{spread_n} {} + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + Vec2 dest = to.point(); + Vec2 delta = dest.delta(u->position()); + FVec2 fdelta {delta}; + + std::uniform_real_distribution<> + t_dist {-_spread_tang, _spread_tang}, + n_dist {-_spread_normal, _spread_normal}; + + double + t = t_dist(global_random), + n = n_dist(global_random); + + FVec2 result = fdelta.apply(t, n); + return to.shifted(Vec2{result}); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_catapult " << static_cast(_kind) + << " " << _base_damage << " " << _min_distance + << " " << _max_distance << " " + << _dist_pow << " " << _spread_tang << " " + << _spread_normal << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage >> _min_distance + >> _max_distance >> _dist_pow >> _spread_tang + >> _spread_normal; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicCatapultUnit: public Unit { +public: + BasicCatapultUnit(AttackKind attack_kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n, + int base_health) + :Unit{new BasicMovement {1}, + new CatapultAttack {attack_kind, + base_dmg, min_dist, max_dist, + dist_pow, spread_t, spread_n}, + DefenseLevelDeco::good_defense_deco( + AttackKind::arrow, + new BasicDefense {0.75}), + base_health} {} +}; + +namespace units { + class Onager: public BasicCatapultUnit { + public: + Onager() :BasicCatapultUnit{ + AttackKind::rock, 90, + 3, 10, 0.05, + 0.2, 0.1, + 30} {} + + UNIT_STORABLE_NAME("u_onager"); + }; + + class BoltThrower: public BasicCatapultUnit { + public: + BoltThrower() :BasicCatapultUnit{ + AttackKind::bolt, 110, + 2, 6, 0.15, + 0.05, 0.05, + 20} {} + + UNIT_STORABLE_NAME("u_boltthrower"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/common_policies.hpp b/8303/Parfentev_Leonid/lab5/common_policies.hpp new file mode 100644 index 000000000..99ad30554 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/common_policies.hpp @@ -0,0 +1,308 @@ +#ifndef _H_COMMON_POLICIES_HPP +#define _H_COMMON_POLICIES_HPP + +#include + +#include "unit.hpp" +#include "map.hpp" +#include "pathfinder.hpp" + + +class BasicMovement: public MovePolicy { + int _steps_per_turn; + +public: + explicit BasicMovement(int n=0) + :_steps_per_turn{n} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _steps_per_turn}; + return pf.run(); + } + + virtual void + store(std::ostream &os) const override + { + os << "mp_basic " << _steps_per_turn << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + is >> _steps_per_turn; + return !is.fail(); + } +}; + +class NestedMovement: public MovePolicy, + public MovePolicyContainer { +public: + using MovePolicyContainer::MovePolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setMovePolicy(p); + return true; + } + delete s; + return false; + } +}; + +class ModifyingMovePolicy: public NestedMovement { + int _max; + +public: + explicit ModifyingMovePolicy(MovePolicy *p=nullptr, int max_dist=0) + :NestedMovement{p}, _max{max_dist} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _max}; + if (!pf.run()) + return false; + + return movePolicy()->canMove(u, to); + } + + virtual void + store(std::ostream &os) const override + { + os << "mp_modifyiing " << _max << "\n"; + movePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _max; + return !is.fail() && NestedMovement::restore(is, tab); + } +}; + + + +class BasicDefense: public DefensePolicy { + double _lvl; + +public: + explicit BasicDefense(double level=1.0) + :_lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *, AttackKind, int base) override + { + return normal_defense(base); + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_basic " << _lvl << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + is >> _lvl; + return !is.fail(); + } +}; + +class NestedDefense: public DefensePolicy, + public DefensePolicyContainer { +public: + using DefensePolicyContainer::DefensePolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setDefensePolicy(p); + return true; + } + delete s; + return false; + } +}; + +class DefenseLevelDeco: public NestedDefense { + // Controls nested policy lifetime + AttackKind _kind; + double _lvl; + +public: + explicit DefenseLevelDeco(DefensePolicy *p=0, + AttackKind kind=AttackKind::invalid, + double level=0) + :NestedDefense{p}, _kind{kind}, _lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + if (kind == _kind) + return defense_level(_lvl, base); + return defensePolicy()->actualDamage(u, kind, base); + } + + static DefenseLevelDeco * + defense_level_deco(AttackKind kind, double lvl, DefensePolicy *p) + { + return new DefenseLevelDeco {p, kind, lvl}; + } + + static DefenseLevelDeco * + good_defense_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 2.0, p); + } + + static DefenseLevelDeco * + vulnerability_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 0.5, p); + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_level_deco " << static_cast(_kind) + << " " << _lvl << "\n"; + defensePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + int x; + is >> x; + _kind = static_cast(x); + is >> _lvl; + + return !is.fail() && NestedDefense::restore(is, tab); + } +}; + +class MultiplierDefensePolicy: public NestedDefense { + double _mul; + +public: + explicit MultiplierDefensePolicy(DefensePolicy *p=nullptr, + double mul=0) + :NestedDefense{p}, _mul{mul} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + DamageSpec ds = defensePolicy()->actualDamage(u, kind, base); + ds.scale(1/_mul); + return ds; + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_multiplier " << _mul << "\n"; + defensePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _mul; + return !is.fail() && NestedDefense::restore(is, tab); + } +}; + + + +class AttackForbidden: public AttackPolicy { +public: + using AttackPolicy::AttackPolicy; + + virtual bool + canAttackTo(const Unit *, MapIter) override + { + return false; + } + + virtual std::pair + baseAttack(const Unit *, MapIter) override + { + return std::make_pair(AttackKind::invalid, 0); + } + + TRIVIALLY_STORABLE("ap_forbidden"); +}; + +class NestedAttack: public AttackPolicy, + public AttackPolicyContainer { +public: + using AttackPolicyContainer::AttackPolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setAttackPolicy(p); + return true; + } + delete s; + return false; + } +}; + +class MultiplierAttackPolicy: public NestedAttack { + double _mul; + +public: + explicit MultiplierAttackPolicy(AttackPolicy *p=nullptr, + double mul=0) + :NestedAttack{p}, _mul{mul} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return attackPolicy()->canAttackTo(u, to); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + auto ba = attackPolicy()->baseAttack(u, to); + return std::make_pair(ba.first, (int)(ba.second * _mul)); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_multiplier " << _mul << "\n"; + attackPolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _mul; + return !is.fail() && NestedAttack::restore(is, tab); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab5/common_storables.hpp b/8303/Parfentev_Leonid/lab5/common_storables.hpp new file mode 100644 index 000000000..27cfd5a0a --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/common_storables.hpp @@ -0,0 +1,117 @@ +#ifndef _H_COMMON_STORABLES_HPP +#define _H_COMMON_STORABLES_HPP + +#include "storable.hpp" +#include "object_print.hpp" + + +class StorableEnd: public Storable { + TRIVIALLY_STORABLE("end"); +}; + +class StorableCoordinates: public Storable { + Vec2 _c; + +public: + StorableCoordinates() {} + + StorableCoordinates(Vec2 c) :_c{c} {} + + virtual void + store(std::ostream &os) const override + { + os << "coords " << _c.x() << " " << _c.y() << "\n"; + } + + virtual bool + restore(std::istream &is, + RestorerTable *) override + { + int x, y; + is >> x >> y; + _c = Vec2{x, y}; + return !is.fail(); + } + + Vec2 coords() const { return _c; } +}; + +class StorableWithIndex: public Storable { + int _i; + Storable *_s; + +public: + StorableWithIndex() {} + + StorableWithIndex(int idx, Storable *s) + :_i{idx}, _s{s} {} + + virtual void + store(std::ostream &os) const override + { + os << "index " << _i << " "; + _s->store(os); + } + + virtual bool + restore(std::istream &is, + RestorerTable *tab) override + { + is >> _i; + _s = tab->restore(is); + return !is.fail() && _s; + } + + int index() const { return _i; } + Storable *child() const { return _s; } + + static void + storeWithIndex(int idx, const Storable *s, + std::ostream &os) + { + os << "index " << idx << " "; + s->store(os); + } +}; + +class StorableWithCoords: public Storable { + Vec2 _c; + Storable *_s; + +public: + StorableWithCoords() {} + + StorableWithCoords(Vec2 c, Storable *s) + :_c{c}, _s{s} {} + + virtual void + store(std::ostream &os) const override + { + os << "at " << _c.x() << " " << _c.y() << " "; + _s->store(os); + } + + virtual bool + restore(std::istream &is, + RestorerTable *tab) override + { + int x, y; + is >> x >> y; + _c = Vec2{x, y}; + _s = tab->restore(is); + return !is.fail() && _s; + } + + Vec2 coords() const { return _c; } + Storable *child() const { return _s; } + + static void + storeWithCoords(Vec2 pt, const Storable *s, + std::ostream &os) + { + os << "at " << pt.x() << " " << pt.y() << " "; + s->store(os); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/demo.cpp b/8303/Parfentev_Leonid/lab5/demo.cpp new file mode 100644 index 000000000..2c438ae19 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/demo.cpp @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" + +#include "event.hpp" +#include "event_types.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "unit_factory.hpp" + +#include "game.hpp" +#include "event_printer.hpp" +#include "iostream_player.hpp" + +#include "factory_table.hpp" + + +void +demo1() +{ + // create a map + auto *map = new Map {10, 10}; + + // create a few units + auto sw1_iter = map->addUnit(new units::Swordsman {}, {3, 3}); + map->addUnit(new units::Swordsman {}, {4, 4}); + + // write the map + std::cout << map << "\n"; + + // test the pathfinder + static const std::vector pts { + {3, 4}, + {3, 3}, + {5, 5}, + {5, 4}, + {5, 3}, + }; + for (auto pt: pts) { + std::cout << sw1_iter.unit() << " " + << ((sw1_iter.unit()->canMove(map->iterAt(pt))) + ? "can" : "can't") + << " move to " << pt << "\n"; + } + + // clean up + delete map; +} + +void +demo2() +{ + auto *map = new Map {10, 10}; + + auto c_iter = map->addUnit(new units::Cavalry {}, {3, 3}); + auto *u = c_iter.unit(); + + std::cout << map << "\n"; + + static const std::vector path { + {4, 5}, {6, 5}, {9, 5}, {8, 7}, {7, 9}, + }; + for (auto pt: path) { + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + std::cout << u << " can't move to " << pt << "!\n"; + break; + } + + std::cout << "moving " << u + << " to " << pt << "...\n"; + + c_iter = map->addUnit(map->removeUnitAt(c_iter), pt); + if (c_iter.null()) { + std::cout << "failed!\n"; + } + } + + std::cout << "\n" << map; + + delete map; +} + +void +demo3() +{ + auto *map = new Map {10, 10}; + map->setMaxUnitsCount(2); + + Unit *sw = new units::Swordsman {}; + Unit *ar = new units::Archer {}; + + map->addUnit(sw, {5, 0}); + map->addUnit(ar, {5, 9}); + + Unit *x = new units::Swordsman {}; + if (!map->addUnit(x, {1, 1}).null()) { + std::cout << "Added one more unit!\n"; + delete map->removeUnitAt({1, 1}); + } else { + std::cout << "Max units: " << map->maxUnitsCount() + << ", current units: " << map->unitsCount() + << "\n"; + delete x; + } + + std::cout << map; + + while (sw->alive() && ar->alive()) { + Vec2 from = sw->position(); + Vec2 to = from.shifted({0, 1}); + + std::cout << "\nmoving " << sw << " from " << from + << " to " << to << "...\n"; + + if (!sw->canMove(map->iterAt(to))) { + std::cout << "can't move\n"; + break; + } + + if (map->addUnit(map->removeUnitAt(from), to).null()) { + std::cout << "failed\n"; + break; + } + + std::cout << ar << " shoots at " << sw->position() << "...\n"; + + auto toi = map->iterAt(sw->position()); + if (!ar->canAttackTo(toi)) { + std::cout << "can't shoot\n"; + // it’s ok + } else { + auto pos = ar->actualPosition(toi); + if (Unit *targ = pos.unit()) { + auto dam = ar->baseAttack(toi); + + std::cout << "hits " << targ << "\n"; + + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + + std::cout << "now: " << targ << "\n"; + } + } + } +} + +MapIter +doAttack(Unit *u, MapIter to) +{ + if (!u->canAttackTo(to)) { + return MapIter::makeNull(); + + } else { + auto pos = u->actualPosition(to); + + if (Unit *targ = pos.unit()) { + auto dam = u->baseAttack(pos); + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + return pos; + } + + return MapIter::makeNull(); + } +} + +struct SimpleGame { + Map *map; + Base *b1, *b2; + EventPrinter *pr; + + explicit SimpleGame(int w=10, int h=10, + int x1=1, int y1=1, + int x2=9, int y2=9) + { + pr = new EventPrinter {std::cout}; + + map = new Map {w, h}; + + b1 = new Base {}; + pr->setPrefix(b1, "Base 1"); + + map->addBase(b1, {x1, y1}); + b1->subscribe(pr); + + b2 = new Base {}; + pr->setPrefix(b2, "Base 2"); + + map->addBase(b2, {x2, y2}); + b2->subscribe(pr); + } + + ~SimpleGame() + { + delete map; + delete pr; + } +}; + +void +demo4() +{ + SimpleGame g {}; + + Unit *u1 = new units::Swordsman {}; + Unit *u2 = new units::Swordsman {}; + + g.b1->addUnit(u1); + g.b2->addUnit(u2); + + g.map->addUnit(u1, {3, 3}); + g.map->addUnit(u2, {4, 3}); + + while (u1->alive() + && u2->alive()) { + doAttack(u1, g.map->iterAt(u2->position())); + if (u2->alive()) { + doAttack(u2, g.map->iterAt(u1->position())); + } + } +} + +MapIter +doMove(Map *map, const Unit *u, Vec2 pt) +{ + auto from = map->iterAt(u->position()); + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + return MapIter::makeNull(); + } + + return map->addUnit(map->removeUnitAt(from), pt); +} + +Unit * +setupUnit(Base *base, const std::string &k, Mediator *m, Vec2 pt) +{ + Unit *u = base->getUnitById(base->createUnit(k, m)); + m->teleportUnit(u, pt); + return u; +} + +void +demo5() +{ + SimpleGame g {}; + + // 2,2 .. 5,5: swamp + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + g.map->setLandscape(new landscapes::Swamp {}, + {2+i, 2+j}); + } + } + + // 1,7 .. 6,9: forest + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 5; ++i) { + g.map->setLandscape(new landscapes::Forest {}, + {1+i, 7+j}); + } + } + + auto *m = new Mediator {g.map}; + auto u1 = setupUnit(g.b1, "swordsman", m, {2, 2}); + auto u2 = setupUnit(g.b2, "swordsman", m, {3, 2}); + + if (doMove(g.map, u1, {0, 2}).null()) { + std::cout << "Can't move " << u1 << " across 2 cells\n"; + } else { + std::cout << "Moved " << u1 << " across 2 cells \n"; + } + + std::cout << "u1: " << u1 << "\n"; + + if (doAttack(u1, g.map->iterAt(u2->position())).null()) { + std::cout << "Can't attack in swamp\n"; + } else { + std::cout << "Attacked in a swamp\n"; + } + + std::cout << "u2: " << u2 << "\n"; + + doMove(g.map, u1, {1, 2}); + doMove(g.map, u2, {2, 3}); + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + doAttack(u1, g.map->iterAt(u2->position())); + + auto u3 = setupUnit(g.b1, "spearsman", m, {3, 8}); + auto u4 = setupUnit(g.b1, "spearsman", m, {7, 8}); + auto u5 = setupUnit(g.b2, "onager", m, {5, 5}); + + while (u3->alive()) + doAttack(u5, g.map->iterAt(u3->position())); + + while (u4->alive()) + doAttack(u5, g.map->iterAt(u4->position())); + + std::cout << g.map; + + delete m; +} + +void +demo6() +{ + SimpleGame g {}; + + auto *m = new Mediator {g.map}; + auto *u1 = setupUnit(g.b1, "slinger", m, {1, 5}); + auto *u2 = setupUnit(g.b2, "swordsman", m, {6, 5}); + + g.map->addNeutralObject(new objects::Tower {}, {1, 5}); + g.map->addNeutralObject(new objects::HealingWell {}, {6, 5}); + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " can't reach " << u2 << "\n"; + } else { + std::cout << u1 << " somehow reached " << u2 << "\n"; + } + + if (m->useObject(u1)) { + std::cout << u1 << " used the tower\n"; + } else { + std::cout << u1 << " can't use the tower\n"; + } + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " still can't reach " << u2 << "\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + if (m->useObject(u2)) { + std::cout << u2 << " used the healing well\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; +} + +void +demo7() +{ + class MoverPlayer: public Player { + std::string _unit_type; + int _id = -1; + + public: + using Player::Player; + + MoverPlayer(std::string name, + std::string type) + :Player{name}, _unit_type{std::move(type)} {} + + virtual bool + takeTurn(const Game *, Mediator *m, Base *b) override + { + if (_id >= 0) { + Unit *u = b->getUnitById(_id); + m->moveUnitTo(u, u->position().shifted({1, 0})); + } else { + _id = b->createUnit(_unit_type, m); + } + return true; + } + }; + + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + auto *b = new Base {}; + map->addBase(b, {3, 3}); + int b_id = g.addBase(b); + + auto *p = new MoverPlayer {"Player 1", "swordsman"}; + g.setPlayer(b_id, p); + + for (int i = 0; i < 5; ++i) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo8() +{ + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + std::stringstream s1 {}; + s1 << "base\n" + << "create spearsman\n" + << "map 0 0 9 9\n" + << "move 0 3 5\n" + << "map 0 0 9 9\n" + << "describe 3 5\n"; + + auto *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + std::stringstream s2 {}; + s2 << "create archer\n" + << "attack 0 3 5\n"; + + auto *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo9() +{ + Map *map = new Map {10, 10}; + + auto *uf = (new objects + ::WeaponSmiths + ::CatapultUnitFilter {}); + auto *ws = new objects::WeaponSmiths {2.0, uf}; + map->addNeutralObject(ws, {3, 4}); + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + Base *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + Base *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + std::stringstream s1 {}; + std::stringstream s2 {}; + + s1 << "create onager\n" + << "move 0 3 4\n" + << "use 0\n" + << "create onager\n" + << "move 1 3 2\n" + << "attack 0 7 4\n" + << "attack 1 7 2\n" + << "create swordsman\n" + << "move 0 3 5\n" + << "move 2 3 4\n" + << "use 2\n"; + + s2 << "create swordsman\n" + << "move 0 7 4\n" + << "create swordsman\n" + << "move 1 7 2\n" + << "skip skip skip skip skip\n"; + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} diff --git a/8303/Parfentev_Leonid/lab5/demo.hpp b/8303/Parfentev_Leonid/lab5/demo.hpp new file mode 100644 index 000000000..ae8bc7a95 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/demo.hpp @@ -0,0 +1,14 @@ +#ifndef _H_DEMO_HPP +#define _H_DEMO_HPP + +void demo1(); +void demo2(); +void demo3(); +void demo4(); +void demo5(); +void demo6(); +void demo7(); +void demo8(); +void demo9(); + +#endif diff --git a/8303/Parfentev_Leonid/lab5/event.cpp b/8303/Parfentev_Leonid/lab5/event.cpp new file mode 100644 index 000000000..b7ff78ebd --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/event.cpp @@ -0,0 +1,31 @@ +#include "event.hpp" + +void +EventEmitter::emit_shared(Event *e) const +{ + for (auto iter = _listeners.begin(); iter != _listeners.end();) { + auto *listener = *iter++; + // note: the listener may safely unsubscribe when handling the + // event. + listener->handle(e); + } +} + +void +EventEmitter::emit(Event *e) const +{ + emit_shared(e); + delete e; +} + +void +EventEmitter::subscribe(EventListener *l) +{ + _listeners.insert(l); +} + +void +EventEmitter::unsubscribe(EventListener *l) +{ + _listeners.erase(l); +} diff --git a/8303/Parfentev_Leonid/lab5/event.hpp b/8303/Parfentev_Leonid/lab5/event.hpp new file mode 100644 index 000000000..fa2d1f541 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/event.hpp @@ -0,0 +1,62 @@ +#ifndef _H_EVENT_HPP +#define _H_EVENT_HPP + +#include + +class EventListener; +class Event; + +class EventEmitter { + std::set _listeners {}; + +public: + void emit_shared(Event *e) const; + void emit(Event *e) const; + + void subscribe(EventListener *l); + void unsubscribe(EventListener *l); + + virtual ~EventEmitter() {} +}; + +class Event { +public: + virtual ~Event() {} +}; + +class EventListener { +public: + virtual void handle(Event *e) =0; + + virtual ~EventListener() {} +}; + +class EventForwarder; + +namespace events { + + class Forwarded: public Event { + Event *_e; + EventForwarder *_f; + + public: + Forwarded(Event *e, EventForwarder *f) + :_e{e}, _f{f} {} + + Event *event() const { return _e; } + EventForwarder *forwarder() const { return _f; } + }; + +} + +class EventForwarder: public EventEmitter, + public EventListener { +public: + virtual void + handle(Event *e) override + { + emit(new events::Forwarded {e, this}); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/event_printer.hpp b/8303/Parfentev_Leonid/lab5/event_printer.hpp new file mode 100644 index 000000000..77e363cca --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/event_printer.hpp @@ -0,0 +1,124 @@ +#ifndef _H_EVENT_PRINTER_HPP +#define _H_EVENT_PRINTER_HPP + +#include +#include +#include +#include + +#include "event.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "player.hpp" +#include "object_print.hpp" + + +class EventPrinter: public EventListener { + std::ostream *_os; + bool _free_os; + + std::map _prefix_map {}; + + std::string + makeName(const char *base, int idx) + { + std::ostringstream oss {}; + oss << base << " " << idx; + return oss.str(); + } + +public: + EventPrinter(std::ostream &os) + :_os{&os}, _free_os{false} {} + + EventPrinter(std::ostream *os) + :_os{os}, _free_os{true} {} + + std::ostream & + ostream() const { return *_os; } + + void + setPrefix(EventForwarder *f, const std::string &s) + { + _prefix_map[f] = s; + } + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + auto iter = _prefix_map.find(ee->forwarder()); + if (iter != _prefix_map.end()) { + (*_os) << iter->second << ": "; + } + + return handle(ee->event()); + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit added: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit died: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " takes " + << ee->damage() << " health points of damage\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " gets healed by " + << ee->health() << " health points\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->attacker() + << " attacked another unit " << ee->target() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->target() + << " was attacked by another unit " + << ee->attacker() << "\n"; + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " moved from " << ee->sourcePos() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " used object " << ee->neutralObject() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "(Live unit " << ((void *)ee->unit()) + << " deleted)\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << " over\n"; + + } else { + (*_os) << "Unknown event\n"; + } + } + + virtual ~EventPrinter() override + { + if (_free_os) { + _os->flush(); + delete _os; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/event_types.hpp b/8303/Parfentev_Leonid/lab5/event_types.hpp new file mode 100644 index 000000000..810d3bd94 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/event_types.hpp @@ -0,0 +1,156 @@ +#ifndef _H_EVENT_TYPES_HPP +#define _H_EVENT_TYPES_HPP + +#include "point.hpp" +#include "event.hpp" + + +class Unit; +class Base; +class NeutralObject; +class Player; + + +class UnitEvent: public Event { + Unit *_u; + +public: + UnitEvent(Unit *u) :_u{u} {} + + Unit *unit() const { return _u; } +}; + +class AttackEvent: public Event { + Unit *_a, *_b; + +public: + AttackEvent(Unit *a, Unit *b) :_a{a}, _b{b} {} + + Unit *attacker() const { return _a; } + Unit *target() const { return _b; } +}; + +class BaseEvent: public Event { + Base *_b; + +public: + BaseEvent(Base *b) :_b{b} {} + + Base *base() const { return _b; } +}; + +class PlayerEvent: public Event { + Player *_p; + +public: + PlayerEvent(Player *p) :_p{p} {} + + Player *player() const { return _p; } +}; + +class UnitMoveEvent: public UnitEvent { + Vec2 _src; + +public: + UnitMoveEvent(Unit *u, Vec2 src) + :UnitEvent{u}, _src{src} {} + + Vec2 sourcePos() const { return _src; } +}; + + + +namespace events { + + class UnitDeath: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitAdded: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitLiveDeleted: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitTakesDamage: public UnitEvent { + int _dmg; + + public: + UnitTakesDamage(Unit *u, int dmg) + :UnitEvent{u}, _dmg{dmg} {} + + int damage() const { return _dmg; } + }; + + class UnitGetsHealed: public UnitEvent { + int _hp; + + public: + UnitGetsHealed(Unit *u, int hp) + :UnitEvent{u}, _hp{hp} {} + + int health() const { return _hp; } + }; + + class UnitWasAttacked: public AttackEvent { + public: + using AttackEvent::AttackEvent; + }; + + class UnitAttacked: public AttackEvent { + Vec2 _tc; + + public: + UnitAttacked(Unit *u, Vec2 tc, Unit *tu) + :AttackEvent{u, tu}, _tc{tc} {} + + Vec2 targetCoordinates() const { return _tc; } + }; + + class UnitMoved: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitTeleported: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitUsedObject: public UnitEvent { + NeutralObject *_n; + + public: + UnitUsedObject(Unit *u, NeutralObject *n) + :UnitEvent{u}, _n{n} {} + + NeutralObject *neutralObject() const { return _n; } + }; + + + + class BaseDestroyed: public BaseEvent { + public: + using BaseEvent::BaseEvent; + }; + + + + class TurnStarted: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + + class TurnOver: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/factory_table.hpp b/8303/Parfentev_Leonid/lab5/factory_table.hpp new file mode 100644 index 000000000..693031648 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/factory_table.hpp @@ -0,0 +1,81 @@ +#ifndef _H_FACTORY_TABLE_HPP +#define _H_FACTORY_TABLE_HPP + +#include +#include + +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "unit_factory.hpp" + + +class FactoryTable { + std::map _tab {}; + + void + registerFactory(const std::string &key, UnitFactory *f) + { + _tab[key] = f; + } + + FactoryTable() + { +#define REG(k, T) \ + registerFactory( \ + k, new SimpleUnitFactory {}) + REG("swordsman", Swordsman); + REG("spearsman", Spearsman); + REG("cavalry", Cavalry); + REG("archer", Archer); + REG("slinger", Slinger); + REG("onager", Onager); + REG("boltthrower", BoltThrower); +#undef REG + } + +public: + static const FactoryTable * + instance() + { + static FactoryTable *inst = new FactoryTable {}; + return inst; + } + + virtual bool + canCreate(const std::string &key) const + { + return _tab.find(key) != _tab.end(); + } + + virtual std::vector + keys() const + { + std::vector keys {}; + for (auto p: _tab) { + keys.push_back(p.first); + } + return keys; + } + + virtual Unit * + create(const std::string &key) const + { + auto iter = _tab.find(key); + if (iter == _tab.end()) { + return nullptr; + } + + return iter->second->create(); + } + + ~FactoryTable() + { + for (auto p: _tab) { + delete p.second; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/game.cpp b/8303/Parfentev_Leonid/lab5/game.cpp new file mode 100644 index 000000000..26fd7ef44 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/game.cpp @@ -0,0 +1,198 @@ +#include +#include + +#include "base.hpp" +#include "game.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "storable.hpp" +#include "common_storables.hpp" + + +Game::Game(Map *map) + :_map{map}, + _med{new Mediator {map}} +{ + this->subscribe(_log_sink); +} + +int +Game::addBase(Base *base) +{ + base->subscribe(_coll); + base->subscribe(this); + + _recs.push_back(BaseRecord{base, nullptr}); + + return (int)(_recs.size() - 1); +} + +void +Game::setPlayer(int base_idx, Player *p) +{ + Player *&p_place = _recs[base_idx].player; + if (p_place) { + p_place->unsubscribe(_log_sink); + delete p_place; + --_players; + } + p_place = p; + if (p) { + p->subscribe(_log_sink); + ++_players; + } +} + +int +Game::basesCount() const +{ + return _recs.size(); +} + +int +Game::playersCount() const +{ + return _players; +} + +Base * +Game::baseByIdx(int idx) const +{ + return _recs[idx].base; +} + +void +Game::spin() +{ + auto &rec = _recs[_next]; + Player *p = rec.player; + Base *b = rec.base; + + if (p) { + emit(new events::TurnStarted {p}); + + bool res = p->takeTurn(this, _med, b); + + _coll->collect(_map); + + emit(new events::TurnOver {p}); + + if (!res) { + setPlayer(_next, nullptr); + } + } + + if (++_next == (int)_recs.size()) { + _next = 0; + } +} + +void +Game::store(std::ostream &os) const +{ + os << "game\n"; + _map->store(os); + + os << _next << "\n"; + + for (int i = 0; i < basesCount(); ++i) { + auto *b = baseByIdx(i); + + StorableWithCoords::storeWithCoords(b->position(), b, os); + + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + auto *u = iter.unit(); + auto *sc = new StorableWithCoords {u->position(), u}; + StorableWithIndex::storeWithIndex(i, sc, os); + } + } + + for (int i = 0; i < basesCount(); ++i) { + auto *p = _recs[i].player; + if (p) { + StorableWithIndex::storeWithIndex(i, p, os); + } + } + + os << "end\n"; +} + +bool +Game::restore(std::istream &is, + RestorerTable *tab) +{ + is >> _next; + + for (;;) { + Storable *s = tab->restore(is); + if (auto *sc = + dynamic_cast(s)) { + if (auto *b + = dynamic_cast(sc->child())) { + _map->addBase(b, sc->coords()); + addBase(b); + } else { + delete sc->child(); + delete sc; + return false; + } + delete sc; + } else if (auto *si = + dynamic_cast(s)) { + if (auto *p + = dynamic_cast(si->child())) { + if (si->index() < 0 + || si->index() >= basesCount()) { + delete p; + delete si; + return false; + } + setPlayer(si->index(), p); + } else if (auto *sc = + dynamic_cast( + si->child())) { + if (auto *u + = dynamic_cast(sc->child())) { + _map->addUnit(u, sc->coords()); + baseByIdx(si->index())->addUnit(u); + delete sc; + } else { + delete si; + delete sc; + delete sc->child(); + return false; + } + } else { + delete si->child(); + delete si; + return false; + } + delete si; + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} + +Game::~Game() +{ + for (int i = 0; i < (int)_recs.size(); ++i) { + setPlayer(i, nullptr); + auto &rec = _recs[i]; + rec.base->unsubscribe(this); + rec.base->unsubscribe(_coll); + } + + delete _coll; + delete _map; + + delete _log_sink; +} diff --git a/8303/Parfentev_Leonid/lab5/game.hpp b/8303/Parfentev_Leonid/lab5/game.hpp new file mode 100644 index 000000000..871262d70 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/game.hpp @@ -0,0 +1,56 @@ +#ifndef _H_GAME_HPP +#define _H_GAME_HPP + +#include + +#include "event.hpp" +#include "map.hpp" +#include "base.hpp" +#include "player.hpp" +#include "zombie_collector.hpp" +#include "mediator.hpp" +#include "storable.hpp" + + +class Game: public EventForwarder, + public Storable { + Map *_map; + Mediator *_med; + + struct BaseRecord { + Base *base; + Player *player; + }; + + std::vector _recs {}; + int _next = 0; + int _players = 0; + + ZombieCollector *_coll {new ZombieCollector {}}; + + EventForwarder *_log_sink {new EventForwarder {}}; + +public: + Game(Map *map); + + int addBase(Base *b); + void setPlayer(int base_idx, Player *p); + + int basesCount() const; + int playersCount() const; + + Base *baseByIdx(int idx) const; + + EventForwarder *logSink() const { return _log_sink; } + + void spin(); + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; + + ~Game(); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab5/iostream_player.cpp b/8303/Parfentev_Leonid/lab5/iostream_player.cpp new file mode 100644 index 000000000..80b639819 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/iostream_player.cpp @@ -0,0 +1,120 @@ +#include +#include +#include + +#include "game.hpp" +#include "base.hpp" +#include "iostream_player.hpp" +#include "event.hpp" +#include "logging.hpp" + + +void +IostreamPlayer::addCommand(const std::string &str, + IostreamCommand *cmd) +{ + _cmd_tab[str] = cmd; +} + +IostreamPlayer::IostreamPlayer(std::string name) + :Player{name} +{ + addCommand("move", new iostream_commands::Move {}); + addCommand("attack", new iostream_commands::Attack {}); + addCommand("use", new iostream_commands::Use {}); + addCommand("create", new iostream_commands::Create {}); + addCommand("base", new iostream_commands::FindBase {}); + addCommand("units", new iostream_commands::ListUnits {}); + addCommand("describe", new iostream_commands::DescribeAt {}); + addCommand("map", new iostream_commands::PrintMap {}); + addCommand("skip", new iostream_commands::Skip {}); + addCommand("save", new iostream_commands::Save {}); +} + +bool +IostreamPlayer::takeTurn(const Game *g, Mediator *m, Base *b) +{ + for (;;) { + std::string cmd_name; + (*_is) >> cmd_name; + + if (_is->fail()) { + return false; + } + + { + std::ostringstream oss {}; + oss << "User picked action: " << cmd_name; + emit(new events::UserActionEvent {this, oss.str()}); + } + + auto iter = _cmd_tab.find(cmd_name); + if (iter == _cmd_tab.end()) { + std::ostringstream oss {}; + oss << "Unknown command: \"" << cmd_name << "\""; + emit(new events::UserActionEvent {this, oss.str()}); + (*_os) << oss.str() << "\n"; + continue; + } + + if (iter->second->execute(g, this, m, b)) { + break; + } + } + + return true; +} + +int +IostreamPlayer::readInt() +{ + int x; + (*_is) >> x; + return x; +} + +Unit * +IostreamPlayer::readUnitId(Base *b) +{ + int id = readInt(); + Unit *u = b->getUnitById(id); + if (!u) { + (*_os) << "No such unit: " << id << "\n"; + } + + return u; +} + +Vec2 +IostreamPlayer::readVec2() +{ + int x, y; + (*_is) >> x >> y; + return {x, y}; +} + +std::string +IostreamPlayer::readString() +{ + std::string s; + (*_is) >> s; + return s; +} + +void +IostreamPlayer::store(std::ostream &os) const +{ + os << "iostream_player\n" << name() << "\n"; +} + +bool +IostreamPlayer::restore(std::istream &is, + RestorerTable *tab) +{ + Player::restore(is, tab); + + // We can’t serialize/deserialize IO streams + setOstream(std::cout); + setIstream(std::cin); + return true; +} diff --git a/8303/Parfentev_Leonid/lab5/iostream_player.hpp b/8303/Parfentev_Leonid/lab5/iostream_player.hpp new file mode 100644 index 000000000..14310f8c9 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/iostream_player.hpp @@ -0,0 +1,324 @@ +#ifndef _H_STDIO_PLAYER_HPP +#define _H_STDIO_PLAYER_HPP + +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "game.hpp" +#include "object_print.hpp" + +#include "event.hpp" +#include "logging.hpp" + + +class IostreamPlayer; + +class IostreamCommand { +public: + // -> whether to end turn + virtual bool execute(const Game *g, IostreamPlayer *p, + Mediator *m, Base *b) =0; + + virtual ~IostreamCommand() {} +}; + +class IostreamPlayer: public Player { + std::ostream *_os = nullptr; + std::istream *_is = nullptr; + bool _free_os, _free_is; + + std::map _cmd_tab {}; + + void + addCommand(const std::string &str, + IostreamCommand *cmd); + + +public: + IostreamPlayer(std::string name=""); + + void + setOstream(std::ostream *os) { _os = os; _free_is = true; } + void + setOstream(std::ostream &os) { _os = &os; _free_os = false; } + + std::ostream & + ostream() const { return *_os; } + + void + setIstream(std::istream *is) { _is = is; _free_is = true; } + void + setIstream(std::istream &is) { _is = &is; _free_is = false; } + + std::istream & + istream() const { return *_is; } + + virtual bool + takeTurn(const Game *g, Mediator *m, Base *b) override; + + int + readInt(); + + Unit * + readUnitId(Base *b); + + Vec2 + readVec2(); + + std::string + readString(); + + virtual void store(std::ostream &os) const override; + + virtual bool restore(std::istream &is, + RestorerTable *tab) override; +}; + +namespace iostream_commands { + + class Move: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested moving " << u << " to " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->moveUnitTo(u, to)) { + p->ostream() << "Can't move unit " << u + << " to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Attack: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u << " attacks " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->attackTo(u, to)) { + p->ostream() << "Unit " << u + << " can't attack to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Use: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u + << " uses an object"; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->useObject(u)) { + p->ostream() << "Unit " << u + << " can't use any object there\n"; + return false; + } + + return true; + } + }; + + class Create: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + std::string s = p->readString(); + + { + std::ostringstream oss {}; + oss << "User requested creation of unit " << s; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!b->canCreateUnit(s)) { + p->ostream() << "Can't create unit of type " + << s << "\n"; + return false; + } + + int id = b->createUnit(s, m); + if (id < 0) { + p->ostream() << "Failed to create a unit of type " + << s << "\n"; + return false; + } + + p->ostream() << "New unit of type " << s + << ": " << id << "\n"; + return true; + } + }; + + class FindBase: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *b) override + { + p->ostream() << "Base: " << b << "\n"; + return false; + } + }; + + class ListUnits: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *b) override + { + p->ostream() << "Units:"; + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + p->ostream() << "- " << iter.id() + << ": " << iter.unit() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class DescribeAt: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *) override + { + auto pos = p->readVec2(); + auto info = m->infoAt(pos); + + p->ostream() << "At " << pos << "\n"; + p->ostream() + << "- Landscape: " << info.landscape() << "\n"; + + if (auto *b = info.base()) { + p->ostream() << "- Base: " << b << "\n"; + } + + if (auto *u = info.unit()) { + p->ostream() << "- Unit: " << u << "\n"; + } + + if (auto *n = info.neutralObject()) { + p->ostream() << "- Object: " << n << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class PrintMap: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *) override + { + auto from = p->readVec2(); + auto to = p->readVec2(); + + p->ostream() << "From " << from + << " to " << to << ":\n"; + + for (int y = from.y(); y < to.y(); ++y) { + for (int x = from.x(); x < to.x(); ++x) { + if (x != from.x()) { + p->ostream() << " "; + } + displayMapInfo(p->ostream(), m->infoAt({x, y})); + } + p->ostream() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class Skip: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *) override + { + std::ostringstream oss {}; + oss << "User decided to skip the turn"; + p->emit(new events::UserActionEvent {p, oss.str()}); + + return true; + } + }; + + class Save: public IostreamCommand { + public: + virtual bool + execute(const Game *g, IostreamPlayer *p, + Mediator *, Base *) override + { + std::string fn = p->readString(); + std::ofstream of {fn}; + if (!of) { + p->ostream() << "Failed to open file\n"; + return false; + } + + g->store(of); + p->ostream() << "Save complete\n"; + + std::ostringstream oss {}; + oss << "Saved current game to " << fn; + p->emit(new events::UserActionEvent {p, oss.str()}); + + return false; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/landscape.hpp b/8303/Parfentev_Leonid/lab5/landscape.hpp new file mode 100644 index 000000000..3df7e506d --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/landscape.hpp @@ -0,0 +1,29 @@ +#ifndef _H_LANDSCAPE_HPP +#define _H_LANDSCAPE_HPP + + +#include "storable.hpp" + +class Unit; + +class Landscape: public Storable { +public: + virtual void onEnter(Unit *u) =0; + virtual void onLeave(Unit *u) =0; + + virtual ~Landscape() {} +}; + +namespace landscapes { + + class Normal: public Landscape { + public: + virtual void onEnter(Unit *) override {} + virtual void onLeave(Unit *) override {} + + TRIVIALLY_STORABLE("l_normal"); + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/landscape_types.hpp b/8303/Parfentev_Leonid/lab5/landscape_types.hpp new file mode 100644 index 000000000..c53d2b99a --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/landscape_types.hpp @@ -0,0 +1,75 @@ +#ifndef _H_LANDSCAPE_TYPES_HPP +#define _H_LANDSCAPE_TYPES_HPP + +#include "landscape.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "common_policies.hpp" +#include "storable.hpp" + + +namespace landscapes { + + // Swamp: max speed is 1; attacking is forbidden + class Swamp: public Landscape { + ModifyingMovePolicy *_p; + AttackPolicy *_prev, *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _p = new ModifyingMovePolicy {u->movePolicy(), 1}; + u->setMovePolicy(_p); + + _prev = u->attackPolicy(); + _cur = new AttackForbidden {}; + u->setAttackPolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *mpc = u->findMoveContainerOf(_p)) { + mpc->setMovePolicy(_p->movePolicy()); + _p->setMovePolicy(nullptr); + delete _p; + _p = nullptr; + } + + // our policy might’ve been wrapped into something + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("l_swamp"); + }; + + class Forest: public Landscape { + DefensePolicy *_prev; + MultiplierDefensePolicy *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _prev = u->defensePolicy(); + _cur = new MultiplierDefensePolicy {_prev, 2.0}; + u->setDefensePolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *dpc = u->findDefenseContainerOf(_cur)) { + dpc->setDefensePolicy(_prev); + _cur->setDefensePolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("l_forest"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/logging.hpp b/8303/Parfentev_Leonid/lab5/logging.hpp new file mode 100644 index 000000000..72b272969 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/logging.hpp @@ -0,0 +1,46 @@ +#ifndef _H_LOGGING_HPP +#define _H_LOGGING_HPP + +#include +#include + +#include "event.hpp" +#include "event_printer.hpp" + + +namespace events { + + class UserActionEvent: public Event { + Player *_p; + std::string _s; + + public: + UserActionEvent(Player *p, std::string s) + :_p{p}, _s{std::move(s)} {} + + const std::string & + message() const { return _s; } + + Player *player() const { return _p;} + }; + +} + +class LoggingEventPrinter: public EventPrinter { +public: + using EventPrinter::EventPrinter; + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + ostream() << ee->player()->name() + << ": " << ee->message() << "\n"; + return; + } + + EventPrinter::handle(e); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/main.cpp b/8303/Parfentev_Leonid/lab5/main.cpp new file mode 100644 index 000000000..ea7dbbea6 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/main.cpp @@ -0,0 +1,176 @@ +#include + +#include +#include + +#include "demo.hpp" + +#include "player.hpp" +#include "iostream_player.hpp" +#include "event_printer.hpp" +#include "base.hpp" +#include "map.hpp" +#include "factory_table.hpp" + +void +run_demos(void) +{ + std::cout << "Demo 1\n"; + demo1(); + + std::cout << "\nDemo 2\n"; + demo2(); + + std::cout << "\nDemo 3\n"; + demo3(); + + std::cout << "\nDemo 4\n"; + demo4(); + + std::cout << "\nDemo 5\n"; + demo5(); + + std::cout << "\nDemo 6\n"; + demo6(); + + std::cout << "\nDemo 7\n"; + demo7(); + + std::cout << "\nDemo 8\n"; + demo8(); + + std::cout << "\nDemo 9\n"; + demo9(); +} + +void +registerBases(Game *g, EventPrinter *p) +{ + int n = g->basesCount(); + for (int i = 0; i < n; ++i) { + std::ostringstream oss {}; + oss << "Base " << (i+1); + p->setPrefix(g->baseByIdx(i), oss.str()); + } +} + +int +run_game(int argc, char **argv) +{ + std::vector loggers {}; + bool have_stdout = false; + + const char *load_fn = nullptr; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-log")) { + char *fn = argv[++i]; + if (!strcmp(fn, "-")) { + loggers.push_back(new LoggingEventPrinter {std::cout}); + have_stdout = true; + } else { + auto *of = new std::ofstream {fn}; + if (!*of) { + std::cerr << "Failed to open file: " << fn << "\n"; + return 1; + } + loggers.push_back(new LoggingEventPrinter {of}); + } + } else if (!strcmp(argv[i], "-load")) { + load_fn = argv[++i]; + } else { + std::cerr << "Unknown option: " << argv[i] << "\n"; + return 1; + } + } + + Map *map = new Map {10, 10}; + + Game *g; + + if (load_fn) { + std::ifstream f {load_fn}; + if (!f) { + std::cerr << "Failed to open save file: " + << load_fn << "\n"; + return 1; + } + auto *tab = RestorerTable::defaultTable(); + Storable *s = tab->restore(f); + delete tab; + + if (auto *lg = dynamic_cast(s)) { + g = lg; + } else { + std::cerr << "Invalid save file contents\n"; + delete s; + return 1; + } + } else { + g = new Game {map}; + } + + for (auto *logger: loggers) { + g->logSink()->subscribe(logger); + } + + EventPrinter *pr = nullptr; + if (!have_stdout) { + pr = new EventPrinter {std::cout}; + g->subscribe(pr); + } + + Base *b1 = new Base {}; + map->addBase(b1, {1, 1}); + g->addBase(b1); + + Base *b2 = new Base {}; + map->addBase(b2, {8, 8}); + g->addBase(b2); + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(std::cin); + g->setPlayer(0, p1); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(std::cin); + g->setPlayer(1, p2); + + for (auto *logger: loggers) { + registerBases(g, logger); + } + if (pr) { + registerBases(g, pr); + } + + while (g->playersCount()) + g->spin(); + + for (auto *logger: loggers) { + g->logSink()->unsubscribe(logger); + delete logger; + } + + if (pr) { + g->unsubscribe(pr); + delete pr; + } + + delete g; + + return 0; +} + +int +main(int argc, char **argv) +{ + if (argc == 2 + && !strcmp(argv[1], "-demo")) { + run_demos(); + return 0; + } + + return run_game(argc, argv); +} diff --git a/8303/Parfentev_Leonid/lab5/map.cpp b/8303/Parfentev_Leonid/lab5/map.cpp new file mode 100644 index 000000000..66eddff6e --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/map.cpp @@ -0,0 +1,222 @@ +#include + +#include "point.hpp" +#include "unit.hpp" +#include "placeable.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "landscape.hpp" +#include "map.hpp" +#include "storable.hpp" +#include "common_storables.hpp" + + +Map::Map(int w, int h) + :_rm{w, h} +{ + for (auto rmiter = _rm.iterAt({0, 0}); + rmiter.y() < _rm.height(); + rmiter.advance(1)) { + rmiter.cell().setLandscape(new landscapes::Normal {}); + } +} + +MapIter +Map::addUnit(Unit *u, Vec2 pt) +{ + if (u->hasPosition()) + return MapIter::makeNull(); + + if (_units_max >= 0 + && _units_count == _units_max) + return MapIter::makeNull(); + + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.unit()) + return MapIter::makeNull(); + + cell.setUnit(u); + u->setPosition(pt); + + ++_units_count; + + cell.landscape()->onEnter(u); + + return MapIter{rmiter}; +} + +Unit * +Map::removeUnitAt(Vec2 at) +{ + RectMapIter rmiter = _rm.iterAt(at); + Cell &cell = rmiter.cell(); + Unit *u = dynamic_cast(cell.unit()); + + if (u) { + --_units_count; + cell.landscape()->onLeave(u); + if (auto *n = dynamic_cast(cell.object())) { + n->onLeave(u); + } + + cell.setUnit(nullptr); + u->unsetPosition(); + } + + return u; +} + +MapIter +Map::addPlaceable(Placeable *p, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.object()) { + return MapIter::makeNull(); + } + + cell.setObject(p); + p->setPosition(pt); + + return MapIter{rmiter}; +} + +MapIter +Map::addBase(Base *b, Vec2 pt) +{ + return addPlaceable(b, pt); +} + +MapIter +Map::addNeutralObject(NeutralObject *n, Vec2 pt) +{ + return addPlaceable(n, pt); +} + +void +Map::setLandscape(Landscape *l, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + cell.setLandscape(l); +} + +void +Map::store(std::ostream &os) const +{ + os << "map " << width() << " " << height() << "\n"; + os << _units_max << "\n"; + + for (int y = 0; y < height(); ++y) { + for (int x = 0; x < width(); ++x) { + auto info = infoAt({x, y}); + + const auto *l = info.landscape(); + if (!dynamic_cast(l)) { + StorableWithCoords::storeWithCoords({x, y}, l, os); + } else if (const auto *n = info.neutralObject()) { + StorableWithCoords::storeWithCoords({x, y}, n, os); + } + + // bases and units are restored in Game::restore + } + } + + os << "end\n"; +} + +bool +Map::restore(std::istream &is, + RestorerTable *tab) +{ + is >> _units_max; + if (is.fail()) { + return false; + } + + for (;;) { + Storable *s = tab->restore(is); + + if (auto *sc = + dynamic_cast(s)) { + if (auto *l = + dynamic_cast(sc->child())) { + setLandscape(l, sc->coords()); + delete sc; + } else if (auto *n = + dynamic_cast(sc->child())) { + addNeutralObject(n, sc->coords()); + delete sc; + } else { + delete sc->child(); + delete sc; + return false; + } + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} + +MapInfo +Map::infoAt(Vec2 pt) const +{ + return MapInfo{&_rm.at(pt)}; +} + +Unit * +MapIter::unit() const +{ + return _it.cell().unit(); +} + +Base * +MapIter::base() const +{ + return dynamic_cast(_it.cell().object()); +} + +NeutralObject * +MapIter::neutralObject() const +{ + return dynamic_cast(_it.cell().object()); +} + +Landscape * +MapIter::landscape() const +{ + return _it.cell().landscape(); +} + +const Landscape * +MapInfo::landscape() const +{ + return _cell->landscape(); +} + +const Unit * +MapInfo::unit() const +{ + return _cell->unit(); +} + +const Base * +MapInfo::base() const +{ + return dynamic_cast(_cell->object()); +} + +const NeutralObject * +MapInfo::neutralObject() const +{ + return dynamic_cast(_cell->object()); +} diff --git a/8303/Parfentev_Leonid/lab5/map.hpp b/8303/Parfentev_Leonid/lab5/map.hpp new file mode 100644 index 000000000..d8831d7cf --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/map.hpp @@ -0,0 +1,126 @@ +#ifndef _H_MAP_HPP +#define _H_MAP_HPP + +#include + +#include "rectmap.hpp" +#include "storable.hpp" + +// Map interface doesn’t know about cells -- instead, it only cares +// about certain kinds of Placeables + + +class Map; + +class Unit; +class Base; +class NeutralObject; + +class MapIter { + RectMapIter _it; + + friend class Map; + + MapIter(RectMapIter r) + :_it{r} {} + +public: + static MapIter makeNull() + { + return MapIter{RectMapIter::makeNull()}; + } + + bool operator==(const MapIter &o) const { return _it == o._it; } + bool operator!=(const MapIter &o) const { return _it != o._it; } + + int x() const { return _it.x(); } + int y() const { return _it.y(); } + Vec2 point() const { return _it.point(); } + + bool null() const { return _it.null(); } + bool valid() const { return _it.valid(); } + + void shift(Vec2 dxy) { _it.moveTo(point().shifted(dxy)); } + MapIter shifted(Vec2 dxy) const + { + return MapIter{_it.otherAt(point().shifted(dxy))}; + } + + void moveTo(Vec2 xy) { _it.moveTo(xy); } + MapIter otherAt(Vec2 xy) const + { + return MapIter{_it.otherAt(xy)}; + } + + void advance(int d) { _it.advance(d); } + MapIter advanced(int d) const + { + return MapIter{_it.advanced(d)}; + } + + Unit *unit() const; + Base *base() const; + NeutralObject *neutralObject() const; + Landscape *landscape() const; +}; + +class MapInfo { + const Cell *_cell; + +public: + MapInfo(const Cell *c) + :_cell{c} {} + + const Landscape *landscape() const; + const Unit *unit() const; + const Base *base() const; + const NeutralObject *neutralObject() const; +}; + +class Map: public Storable { + RectMap _rm; + int _units_count = 0; + int _units_max = -1; + + MapIter addPlaceable(Placeable *p, Vec2 pt); + +public: + Map(int w, int h); + + int width() const { return _rm.width(); } + int height() const { return _rm.height(); } + MapIter iterAt(Vec2 pt) { return MapIter{_rm.iterAt(pt)}; } + MapIter iterAt(int x, int y) { return iterAt({x, y}); } + + MapIter begin() { return iterAt(0, 0); } + MapIter end() { return iterAt(0, height()); } + + MapInfo infoAt(Vec2 pt) const; + + MapIter addUnit(Unit *u, Vec2 pt); + Unit *removeUnitAt(Vec2 at); + Unit *removeUnitAt(MapIter iter) + { + return removeUnitAt(iter.point()); + } + + MapIter addBase(Base *b, Vec2 pt); + MapIter addNeutralObject(NeutralObject *n, Vec2 pt); + void setLandscape(Landscape *l, Vec2 pt); + + int maxUnitsCount() const { return _units_max; } + bool setMaxUnitsCount(int x) + { + if (_units_count > x) + return false; + _units_max = x; + return true; + } + int unitsCount() const { return _units_count; } + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/mediator.cpp b/8303/Parfentev_Leonid/lab5/mediator.cpp new file mode 100644 index 000000000..348997877 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/mediator.cpp @@ -0,0 +1,111 @@ +#include "point.hpp" +#include "map.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "neutral_object.hpp" +#include "mediator.hpp" + + +MapInfo +Mediator::infoAt(Vec2 pt) +{ + return _map->infoAt(pt); +} + +Vec2 +Mediator::mapSize() +{ + return {_map->width(), + _map->height()}; +} + +bool +Mediator::moveUnitTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit() + || !u->canMove(ito)) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitMoved {u, from}); + return true; +} + +bool +Mediator::attackTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (!ito.unit() + || !u->canAttackTo(ito)) { + return false; + } + + auto pos = u->actualPosition(ito); + Unit *t = pos.unit(); + + u->emit(new events::UnitAttacked {u, pos.point(), t}); + + if (t) { + t->emit(new events::UnitWasAttacked {u, t}); + + auto damage_pair = u->baseAttack(pos); + auto damage_spec = t->actualDamage(damage_pair.first, + damage_pair.second); + int dmg = damage_spec.evaluate(); + + t->takeDamage(dmg); + } + + return true; +} + +bool +Mediator::useObject(Unit *u) +{ + auto iter = _map->iterAt(u->position()); + + NeutralObject *n = iter.neutralObject(); + + if (!n + || !n->canUse(u)) { + return false; + } + + n->onUse(u, this); + + u->emit(new events::UnitUsedObject {u, n}); + + return true; +} + +bool +Mediator::spawnUnit(Unit *u, Vec2 at) +{ + return !_map->addUnit(u, at).null(); +} + +bool +Mediator::teleportUnit(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit()) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitTeleported {u, from}); + return true; +} diff --git a/8303/Parfentev_Leonid/lab5/mediator.hpp b/8303/Parfentev_Leonid/lab5/mediator.hpp new file mode 100644 index 000000000..cc1549f65 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/mediator.hpp @@ -0,0 +1,31 @@ +#ifndef _H_MEDIATOR_HPP +#define _H_MEDIATOR_HPP + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" + + +class Mediator { + Map *_map; + +public: + Mediator(Map *map) + :_map{map} {} + + MapInfo infoAt(Vec2 pt); + + Vec2 mapSize(); + + bool moveUnitTo(Unit *u, Vec2 to); + + bool attackTo(Unit *u, Vec2 to); + + bool useObject(Unit *u); + + bool spawnUnit(Unit *u, Vec2 at); + bool teleportUnit(Unit *u, Vec2 to); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab5/melee_units.hpp b/8303/Parfentev_Leonid/lab5/melee_units.hpp new file mode 100644 index 000000000..6409e96cb --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/melee_units.hpp @@ -0,0 +1,111 @@ +#ifndef _H_MELEE_UNITS_HPP +#define _H_MELEE_UNITS_HPP + +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class MeleeAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + +public: + explicit MeleeAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0) + :_kind{kind}, _base_damage{base_dmg} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return to.unit() != nullptr + && to.point().adjacent(u->position()); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter) + { + return std::make_pair( + _kind, + int(_base_damage * u->relativeHealth())); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_melee " << static_cast(_kind) + << " " << _base_damage << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicMeleeUnit: public Unit { +public: + BasicMeleeUnit(int speed, + AttackKind attack_kind, + int base_dmg, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new MeleeAttack {attack_kind, base_dmg}, + def, base_health} {} +}; + +namespace units { + class Swordsman: public BasicMeleeUnit { + public: + Swordsman() :BasicMeleeUnit{ + 2, + AttackKind::sword, 40, + DefenseLevelDeco::good_defense_deco( + AttackKind::spear, + DefenseLevelDeco::vulnerability_deco( + AttackKind::cavalry, + new BasicDefense {})), + 100} {} + + UNIT_STORABLE_NAME("u_swordsman"); + }; + + class Spearsman: public BasicMeleeUnit { + public: + Spearsman() :BasicMeleeUnit{ + 2, + AttackKind::spear, 75, + DefenseLevelDeco::good_defense_deco( + AttackKind::cavalry, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + + UNIT_STORABLE_NAME("u_spearsman"); + }; + + class Cavalry: public BasicMeleeUnit { + public: + Cavalry() :BasicMeleeUnit{ + 3, + AttackKind::cavalry, 50, + DefenseLevelDeco::good_defense_deco( + AttackKind::sword, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + + UNIT_STORABLE_NAME("u_cavalry"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/neutral_object.hpp b/8303/Parfentev_Leonid/lab5/neutral_object.hpp new file mode 100644 index 000000000..95e11d9c7 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/neutral_object.hpp @@ -0,0 +1,24 @@ +#ifndef _H_NEUTRAL_OBJECT_HPP +#define _H_NEUTRAL_OBJECT_HPP + +#include "placeable.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "mediator.hpp" +#include "storable.hpp" + + +class NeutralObject: public Placeable, + public Storable { +public: + virtual bool canUse(const Unit *) { return true; } + virtual void onUse(Unit *u, Mediator *m) =0; + + // It’s the object’s job to determine whether it was used by the + // leaving unit. + virtual void onLeave(Unit *) {}; + + virtual ~NeutralObject() {}; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/neutral_object_types.hpp b/8303/Parfentev_Leonid/lab5/neutral_object_types.hpp new file mode 100644 index 000000000..e6fc373c1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/neutral_object_types.hpp @@ -0,0 +1,255 @@ +#ifndef _H_NEUTRAL_OBJECT_TYPES_HPP +#define _H_NEUTRAL_OBJECT_TYPES_HPP + +#include + +#include "neutral_object.hpp" +#include "mediator.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "ranged_units.hpp" +#include "common_policies.hpp" + +#include "storable.hpp" +#include "common_storables.hpp" + + +class ExtendedShootingRange: public NestedAttack { + double _delta; + +public: + explicit ExtendedShootingRange(AttackPolicy *p=nullptr, + double delta=0) + :NestedAttack{p}, _delta{delta} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = to.point().distance(u->position()); + auto *a = dynamic_cast(attackPolicy()); + return dist >= a->minRange() + && dist <= (a->maxRange() + _delta); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + return attackPolicy()->baseAttack(u, to); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_extended " << _delta << "\n"; + attackPolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _delta; + return !is.fail() && NestedAttack::restore(is, tab); + } +}; + + + +namespace objects { + + class HealingWell: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *) override + { + u->heal(25); + } + + TRIVIALLY_STORABLE("n_healingwell"); + }; + + class Tower: public NeutralObject { + AttackPolicy *_prev; + ExtendedShootingRange *_cur = nullptr; + + public: + virtual bool + canUse(const Unit *u) override + { + return dynamic_cast(u); + } + + virtual void + onUse(Unit *u, Mediator *) override + { + _prev = u->attackPolicy(); + _cur = new ExtendedShootingRange {_prev, 5}; + u->setAttackPolicy(_cur); + } + + virtual void + onLeave(Unit *u) override + { + if (_cur == nullptr) { + return; + } + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + _cur->setAttackPolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("n_tower"); + }; + + class TunnelsEntrance: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *m) override + { + static const int w = 5; + + Vec2 at = u->position(); + + int max_n = 0; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() == nullptr) { + ++max_n; + } + } + } + + std::uniform_int_distribution<> distr {0, max_n-1}; + int n = distr(global_random); + + Vec2 dest; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() != nullptr) { + continue; + } + if (!--n) { + dest = iter; + break; + } + } + } + + m->teleportUnit(u, dest); + } + + TRIVIALLY_STORABLE("n_tunnelentrance"); + }; + + class WeaponSmiths: public NeutralObject { + public: + class UnitFilter: public Storable { + public: + virtual bool + applicable(const Unit *u) =0; + }; + + template + class SimpleUnitFilter: public UnitFilter{ + public: + virtual bool + applicable(const Unit *u) override + { + return dynamic_cast(u); + } + }; + + class MeleeUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_melee"); + }; + + class RangedUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_ranged"); + }; + + class CatapultUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_catapult"); + }; + + private: + double _mul; + UnitFilter *_filter; + + public: + explicit WeaponSmiths(double mul=0, UnitFilter *filter=nullptr) + :_mul{mul}, _filter{filter} {} + + virtual bool + canUse(const Unit *u) override + { + if (_filter + && !_filter->applicable(u)) { + return false; + } + + for (const AttackPolicyContainer *apc = u; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (dynamic_cast(apc)) { + return false; + } + } + + return true; + } + + virtual void + onUse(Unit *u, Mediator *) override + { + auto *prev = u->attackPolicy(); + auto *new_p = new MultiplierAttackPolicy {prev, _mul}; + u->setAttackPolicy(new_p); + } + + virtual void + store(std::ostream &os) const override + { + os << "n_weaponsmiths " << _mul << "\n"; + if (_filter) { + _filter->store(os); + } else { + os << "end\n"; + } + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + + if (auto *uf = dynamic_cast(s)) { + _filter = uf; + } else if (dynamic_cast(s)) { + _filter = nullptr; + delete s; + } else { + delete s; + return false; + } + + return true; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/object_print.cpp b/8303/Parfentev_Leonid/lab5/object_print.cpp new file mode 100644 index 000000000..da59e1054 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/object_print.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "object_print.hpp" + + +#define UNIT_ENTRY(T) {std::type_index{typeid(units::T)}, #T} +static const std::map unit_names { + UNIT_ENTRY(Swordsman), + UNIT_ENTRY(Spearsman), + UNIT_ENTRY(Cavalry), + UNIT_ENTRY(Archer), + UNIT_ENTRY(Slinger), + UNIT_ENTRY(Onager), + UNIT_ENTRY(BoltThrower), +}; +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T) {std::type_index{typeid(landscapes::T)}, #T} +static const std::map landscape_names { + LANDSCAPE_ENTRY(Normal), + LANDSCAPE_ENTRY(Swamp), + LANDSCAPE_ENTRY(Forest), +}; +#undef LANDSCAPE_ENTRY + +#define N_OBJECT_ENTRY(T, n) {std::type_index{typeid(objects::T)}, n} +static const std::map objects_names { + N_OBJECT_ENTRY(HealingWell, "Healing Well"), + N_OBJECT_ENTRY(Tower, "Tower"), + N_OBJECT_ENTRY(TunnelsEntrance, "Tunnels Entrance"), + N_OBJECT_ENTRY(WeaponSmiths, "Weapon Smiths"), +}; +#undef N_OBJECT_ENTRY + + +std::ostream & +operator<<(std::ostream &os, Vec2 pt) +{ + return os << "{" << pt.x() << "," << pt.y() << "}"; +} + +std::ostream & +operator<<(std::ostream &os, const Unit *u) +{ + return os << "a " + << unit_names.at(std::type_index{typeid(*u)}) + << " with " << u->health() << " HP at " + << u->position(); +} + +std::ostream & +operator<<(std::ostream &os, const Landscape *l) +{ + return os << landscape_names.at(std::type_index{typeid(*l)}); +} + +std::ostream & +operator<<(std::ostream &os, const Base *b) +{ + os << "a Base with " << b->unitsCount(); + int m = b->maxUnitsCount(); + if (m >= 0) { + os << "/" << m; + } + + return os << " units at " << b->position(); +} + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n) +{ + return os << "a " + << objects_names.at(std::type_index{typeid(*n)}) + << " at " << n->position(); +} + + + +static const std::map class_chars { +#define UNIT_ENTRY(T, x) {std::type_index{typeid(units::T)}, x} + UNIT_ENTRY(Swordsman, 'S'), + UNIT_ENTRY(Spearsman, 'P'), + UNIT_ENTRY(Cavalry, 'C'), + UNIT_ENTRY(Archer, 'A'), + UNIT_ENTRY(Slinger, 's'), + UNIT_ENTRY(Onager, 'O'), + UNIT_ENTRY(BoltThrower, 'B'), +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T, x) \ + {std::type_index{typeid(landscapes::T)}, x} + LANDSCAPE_ENTRY(Normal, '.'), + LANDSCAPE_ENTRY(Swamp, '='), + LANDSCAPE_ENTRY(Forest, '*'), +#undef LANDSCAPE_ENTRY +}; + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info) +{ + if (const Unit *u = info.unit()) { + os << class_chars.at(std::type_index{typeid(*u)}); + } else if (info.base()) { + os << "+"; + } else if (info.neutralObject()) { + os << '#'; + } else { + auto *l = info.landscape(); + os << class_chars.at(std::type_index{typeid(*l)}); + } + + return os; +} + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1) +{ + for (int y = y0; y < y1; ++y) { + for (int x = x0; x < x1; ++x) { + displayMapInfo(os, map->infoAt({x, y})); + } + os << "\n"; + } + return os; +} + +std::ostream & +operator<<(std::ostream &os, const Map *map) +{ + return displayMap(os, map, 0, 0, map->width(), map->height()); +} diff --git a/8303/Parfentev_Leonid/lab5/object_print.hpp b/8303/Parfentev_Leonid/lab5/object_print.hpp new file mode 100644 index 000000000..8df10d966 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/object_print.hpp @@ -0,0 +1,40 @@ +#ifndef _H_OBJECT_PRINT_HPP +#define _H_OBJECT_PRINT_HPP + +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "neutral_object.hpp" + +std::ostream & +operator<<(std::ostream &os, Vec2 pt); + +std::ostream & +operator<<(std::ostream &os, const Unit *u); + +std::ostream & +operator<<(std::ostream &os, const Landscape *l); + +std::ostream & +operator<<(std::ostream &os, const Base *b); + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n); + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info); + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1); + +std::ostream & +operator<<(std::ostream &os, const Map *map); + +#endif diff --git a/8303/Parfentev_Leonid/lab5/object_w_health.hpp b/8303/Parfentev_Leonid/lab5/object_w_health.hpp new file mode 100644 index 000000000..e9c925221 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/object_w_health.hpp @@ -0,0 +1,43 @@ +#ifndef _H_OBJECT_W_HEALTH_HPP +#define _H_OBJECT_W_HEALTH_HPP + +#include "storable.hpp" + + +class ObjectWithHealth: public Storable { + int _health, _base_health; + +public: + ObjectWithHealth(int base_health) + :_health{base_health}, + _base_health{base_health} {} + + int + health() const { return _health; } + int + baseHealth() const { return _base_health; } + double + relativeHealth() const { return _health / (double)_base_health; } + bool + alive() const { return health() > 0; } + + virtual void + heal(int hp) { _health += hp; } + + virtual void + takeDamage(int dmg) { _health -= dmg; } + + virtual void store(std::ostream &os) const override + { + os << health(); + } + + virtual bool restore(std::istream &is, RestorerTable *) override + { + is >> _health; + return !is.fail(); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab5/pathfinder.cpp b/8303/Parfentev_Leonid/lab5/pathfinder.cpp new file mode 100644 index 000000000..865f34de2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/pathfinder.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "map.hpp" +#include "point.hpp" +#include "pathfinder.hpp" + + +PathFinder::Pt2 +PathFinder::Pt2::toDirection(int dir) const +{ + // assert(dir >= 0 && dir < 8); + + int d1 = (dir + 1) % 8; + int dx = (d1 % 4 == 3) ? 0 : (d1 < 4) ? 1 : -1, + dy = (dir % 4 == 0) ? 0 : (dir < 4) ? 1 : -1; + + return Pt2{pt.shifted({dx, dy}), depth + 1}; +} + +bool +PathFinder::run() +{ + Vec2 start_pt = _start.point(); + + while (!_frontier.empty()) { + Pt2 current = _frontier.front(); + _frontier.pop(); + + if (start_pt.shifted(current.pt) == _end) + return true; + + if (_max >= 0 + && current.depth >= _max) + continue; + + Vec2WithCmp cur_v2wc {current.pt}; + auto iter = _dirs.find(cur_v2wc); + if (iter == _dirs.end()) + _dirs[cur_v2wc] = -1; + + for (int i = 0; i < 8; ++i) { + Pt2 shifted_delta = current.toDirection(i); + + Vec2WithCmp sh_v2wc {shifted_delta.pt}; + auto iter = _dirs.find(sh_v2wc); + if (iter != _dirs.end()) + continue; + + MapIter shifted = _start.shifted(shifted_delta.pt); + if (!shifted.valid() + || shifted.unit()) + continue; + + _frontier.push(shifted_delta); + } + } + + return false; +} diff --git a/8303/Parfentev_Leonid/lab5/pathfinder.hpp b/8303/Parfentev_Leonid/lab5/pathfinder.hpp new file mode 100644 index 000000000..d4c614aae --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/pathfinder.hpp @@ -0,0 +1,56 @@ +#ifndef _H_PATHFINDER_HPP +#define _H_PATHFINDER_HPP + +#include +#include + +#include "point.hpp" +#include "map.hpp" + + +class PathFinder { + MapIter _start; + Vec2 _end; + int _max; + + struct Vec2WithCmp { + Vec2 v; + + bool operator<(Vec2WithCmp o) const + { + if (v.y() == o.v.y()) + return v.x() < o.v.x(); + return v.y() < o.v.y(); + } + }; + + struct Pt2 { + Vec2 pt; + int depth; + + static Pt2 zero() { return {Vec2{0, 0}, 0}; } + + Pt2(Vec2 pt, int depth=0) + :pt{pt}, depth{depth} {} + + Pt2 toDirection(int dir) const; + + bool pt_equal(Vec2 pt2) + { + return pt == pt2; + } + }; + + std::map _dirs {}; + std::queue _frontier {{Pt2::zero()}}; + +public: + PathFinder(MapIter from, MapIter to, int max_steps) + :_start{from}, + _end{to.point()}, + _max{max_steps} {} + + bool run(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/placeable.hpp b/8303/Parfentev_Leonid/lab5/placeable.hpp new file mode 100644 index 000000000..bd7f0ef74 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/placeable.hpp @@ -0,0 +1,36 @@ +#ifndef _H_PLACEABLE_HPP +#define _H_PLACEABLE_HPP + +#include "point.hpp" + +class Placeable { + bool _placed = false; + Vec2 _pos; + +public: + bool + hasPosition() const { return _placed; } + + const Vec2 & + position() const + { + return _pos; + } + + void + setPosition(const Vec2 &pos) + { + _pos = pos; + _placed = true; + } + + void + unsetPosition() + { + _placed = false; + } + + virtual ~Placeable() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/player.hpp b/8303/Parfentev_Leonid/lab5/player.hpp new file mode 100644 index 000000000..58c15829d --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/player.hpp @@ -0,0 +1,44 @@ +#ifndef _H_PLAYER_HPP +#define _H_PLAYER_HPP + +#include +#include + +#include "mediator.hpp" +#include "base.hpp" +#include "event.hpp" +#include "storable.hpp" + + +class Game; + +class Player: public EventEmitter, + public Storable { + std::string _name; + +public: + explicit Player(std::string name="") + :_name{std::move(name)} {} + + const std::string &name() const { return _name; } + + virtual bool takeTurn(const Game *g, Mediator *m, Base *b) =0; + + virtual void + store(std::ostream &os) const + { + os << name() << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + std::ws(is); + std::getline(is, _name); + return !is.fail(); + } + + virtual ~Player() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/point.cpp b/8303/Parfentev_Leonid/lab5/point.cpp new file mode 100644 index 000000000..bce7b3325 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/point.cpp @@ -0,0 +1,29 @@ +#include + +#include "point.hpp" + +double +Vec2::length() const +{ + return sqrt(_x*_x + _y*_y); +} + +double +Vec2::distance(const Vec2 &pt) const +{ + return delta(pt).length(); +} + +bool +Vec2::unit() const +{ + return (_x || _y) + && abs(_x) <= 1 + && abs(_y) <= 1; +} + +bool +Vec2::adjacent(const Vec2 &pt) const +{ + return delta(pt).unit(); +} diff --git a/8303/Parfentev_Leonid/lab5/point.hpp b/8303/Parfentev_Leonid/lab5/point.hpp new file mode 100644 index 000000000..8eec01d76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/point.hpp @@ -0,0 +1,40 @@ +#ifndef _H_POINT_HPP +#define _H_POINT_HPP + +class Vec2 { + int _x, _y; + +public: + Vec2() :Vec2{0, 0} {} + Vec2(int x, int y) :_x{x}, _y{y} {} + + int x() const { return _x; } + int y() const { return _y; } + + bool operator==(const Vec2 &pt) const + { + return _x == pt._x && _y == pt._y; + } + bool operator!=(const Vec2 &pt) const + { + return !(*this == pt); + } + + Vec2 delta(const Vec2 &o) const + { + return Vec2{_x - o._x, _y - o._y}; + } + + double length() const; + double distance(const Vec2 &pt) const; + + bool unit() const; + bool adjacent(const Vec2 &pt) const; + + Vec2 shifted(const Vec2 &dxy) const + { + return Vec2{_x + dxy._x, _y + dxy._y}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/ranged_units.hpp b/8303/Parfentev_Leonid/lab5/ranged_units.hpp new file mode 100644 index 000000000..2a41e3ecb --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/ranged_units.hpp @@ -0,0 +1,118 @@ +#ifndef _H_RANGED_UNITS_HPP +#define _H_RANGED_UNITS_HPP + +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class RangedAttack: public AttackPolicy { +protected: + AttackKind _kind; + int _base_damage; + double _min_distance, _max_distance; + double _dist_pow; + + static double + distance(const Unit *u, MapIter to) + { + return to.point().distance(u->position()); + } + +public: + double minRange() const { return _min_distance; } + double maxRange() const { return _max_distance; } + + explicit RangedAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0, + double min_dist=0, + double max_dist=0, + double dist_pow=0) + :_kind{kind}, + _base_damage{base_dmg}, + _min_distance{min_dist}, + _max_distance{max_dist}, + _dist_pow{dist_pow} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return dist >= _min_distance + && dist <= _max_distance; + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return std::make_pair( + _kind, + int(_base_damage + * u->relativeHealth() + / pow(dist, _dist_pow))); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_ranged " << static_cast(_kind) + << " " << _base_damage << " " << _min_distance + << " " << _max_distance << " " + << _dist_pow << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage >> _min_distance + >> _max_distance >> _dist_pow; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicRangedUnit: public Unit { +public: + BasicRangedUnit(int speed, + AttackKind attack_kind, + int base_dmg, + double max_dist, + double dist_pow, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new RangedAttack {attack_kind, base_dmg, + 1., max_dist, dist_pow}, + def, base_health} {} +}; + +namespace units { + class Archer: public BasicRangedUnit { + public: + Archer() :BasicRangedUnit{ + 2, + AttackKind::arrow, 50, 5., .20, + new BasicDefense {0.9}, + 40} {} + + UNIT_STORABLE_NAME("u_archer"); + }; + + class Slinger: public BasicRangedUnit { + public: + Slinger() :BasicRangedUnit{ + 2, + AttackKind::stone, 60, 3., .30, + new BasicDefense {.09}, + 50} {} + + UNIT_STORABLE_NAME("u_slinger"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/rectmap.cpp b/8303/Parfentev_Leonid/lab5/rectmap.cpp new file mode 100644 index 000000000..b82667c40 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/rectmap.cpp @@ -0,0 +1,55 @@ +#include "rectmap.hpp" + +#include "unit.hpp" + +Cell::~Cell() +{ + delete _u; + delete _obj; + delete _l; +} + +bool +RectMapIter::valid() const +{ + return x() >= 0 + && x() < _map->width() + && y() >= 0 + && y() < _map->height(); +} + +Cell & +RectMapIter::cell() const +{ + return _map->at(point()); +} + +void +RectMapIter::moveTo(Vec2 xy) +{ + _pt = xy; +} + +RectMapIter +RectMapIter::otherAt(Vec2 xy) const +{ + RectMapIter other = *this; + other.moveTo(xy); + return other; +} + +void +RectMapIter::advance(int d) +{ + int nx = x() + d, + w = _map->width(); + _pt = Vec2{nx % w, y() + nx / w}; +} + +RectMapIter +RectMapIter::advanced(int d) const +{ + RectMapIter other = *this; + other.advance(d); + return other; +} diff --git a/8303/Parfentev_Leonid/lab5/rectmap.hpp b/8303/Parfentev_Leonid/lab5/rectmap.hpp new file mode 100644 index 000000000..a3119ffb1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/rectmap.hpp @@ -0,0 +1,99 @@ +#ifndef _H_RECTMAP_HPP +#define _H_RECTMAP_HPP + +#include "point.hpp" +#include "placeable.hpp" +#include "landscape.hpp" + + +class Unit; + +class Cell { + Landscape *_l = nullptr; + Unit *_u = nullptr; + Placeable *_obj = nullptr; + +public: + Cell() {} + + ~Cell(); + + Landscape *landscape() const { return _l; } + void setLandscape(Landscape *l) + { + delete _l; + _l = l; + } + + Unit *unit() const { return _u; } + void setUnit(Unit *u) { _u = u; } + + Placeable *object() const { return _obj; } + void setObject(Placeable *p) { _obj = p; } +}; + +class RectMap; + +class RectMapIter { + RectMap *_map; + Vec2 _pt; + +public: + RectMapIter(RectMap *map, Vec2 pt) + :_map{map}, _pt{pt} {} + RectMapIter(RectMap *map, int x, int y) + :_map{map}, _pt{x, y} {} + + static RectMapIter makeNull() { return {nullptr, {0, 0}}; } + + bool operator==(const RectMapIter &o) const + { + return _map == o._map + && _pt == o._pt; + } + bool operator!=(const RectMapIter &o) const + { + return !(*this == o); + } + + int x() const { return _pt.x(); } + int y() const { return _pt.y(); } + Vec2 point() const { return _pt; } + + Cell &cell() const; + + bool null() const { return _map == nullptr; } + bool valid() const; + + void moveTo(Vec2 xy); + RectMapIter otherAt(Vec2 xy) const; + + void advance(int d); + RectMapIter advanced(int d) const; +}; + +class RectMap { + const int _w, _h; + Cell * const _storage; + +public: + RectMap(int w, int h) + :_w{w}, _h{h}, _storage{new Cell [w * h]} {} + + int width() const { return _w; } + int height() const { return _h; } + + Cell &at(Vec2 pt) { return _storage[pt.x() + pt.y()*_w]; } + const Cell &at(Vec2 pt) const + { + return _storage[pt.x() + pt.y()*_w]; + } + RectMapIter iterAt(Vec2 pt) { return RectMapIter{this, pt}; } + + ~RectMap() + { + delete[] _storage; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/report.pdf b/8303/Parfentev_Leonid/lab5/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2d4c4df4e61ce145fe0acfba1eeadf3feb4e6a20 GIT binary patch literal 103631 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�d1aqhI_yl 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 b5I0oFpD0f+RwYFhWfcgkq~tA2Bi4Tb?C>rZ literal 0 HcmV?d00001 diff --git a/8303/Parfentev_Leonid/lab5/restorers.hpp b/8303/Parfentev_Leonid/lab5/restorers.hpp new file mode 100644 index 000000000..6fe67c4e2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/restorers.hpp @@ -0,0 +1,61 @@ +#ifndef _H_RESTORERS_HPP +#define _H_RESTORERS_HPP + +#include "storable.hpp" + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "base.hpp" +#include "game.hpp" +#include "player.hpp" + + +template +class SimpleRestorer: public Restorer { +public: + virtual Storable * + restore(std::istream &, + RestorerTable *) const override + { + return new T {}; + } +}; + + +namespace restorers { + + class GameRestorer: public Restorer { + public: + virtual Storable * + restore(std::istream &is, + RestorerTable *tab) const override + { + Storable *s = tab->restore(is); + Map *map = dynamic_cast(s); + if (!map) { + delete s; + return nullptr; + } + + return new Game {map}; + } + }; + + class MapRestorer: public Restorer { + public: + virtual Storable * + restore(std::istream &is, + RestorerTable *) const override + { + int w, h; + is >> w >> h; + + return new Map {w, h}; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab5/storable.cpp b/8303/Parfentev_Leonid/lab5/storable.cpp new file mode 100644 index 000000000..8a28f8f64 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/storable.cpp @@ -0,0 +1,78 @@ +#include +#include +#include + +#include "storable.hpp" +#include "restorers.hpp" +#include "common_storables.hpp" + +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "landscape_types.hpp" +#include "neutral_object_types.hpp" + +#include "factory_table.hpp" +#include "iostream_player.hpp" + + +RestorerTable * +RestorerTable::defaultTable() +{ + return new RestorerTable {{ + +{"end", new SimpleRestorer {}}, +{"coords", new SimpleRestorer {}}, +{"index", new SimpleRestorer {}}, +{"at", new SimpleRestorer {}}, + +{"game", new restorers::GameRestorer {}}, +{"map", new restorers::MapRestorer {}}, + +{"base", new SimpleRestorer {}}, + +{"iostream_player", new SimpleRestorer {}}, + +{"l_normal", new SimpleRestorer {}}, +{"l_swamp", new SimpleRestorer {}}, +{"l_forest", new SimpleRestorer {}}, + +{"u_swordsman", new SimpleRestorer {}}, +{"u_spearsman", new SimpleRestorer {}}, +{"u_cavalry", new SimpleRestorer {}}, + +{"u_archer", new SimpleRestorer {}}, +{"u_slinger", new SimpleRestorer {}}, + +{"u_onager", new SimpleRestorer {}}, +{"u_boltthrower", new SimpleRestorer {}}, + +{"n_healingwell", new SimpleRestorer {}}, +{"n_tower", new SimpleRestorer {}}, +{"n_tunnelentrance", new SimpleRestorer {}}, +{"n_weaponsmiths", new SimpleRestorer {}}, + +{"uf_melee", + new SimpleRestorer {}}, +{"uf_ranged", + new SimpleRestorer {}}, +{"uf_catapult", + new SimpleRestorer {}}, + +{"mp_basic", new SimpleRestorer {}}, +{"mp_modifyiing", new SimpleRestorer {}}, + +{"dp_basic", new SimpleRestorer {}}, +{"dp_level_deco", new SimpleRestorer {}}, +{"dp_multiplier", new SimpleRestorer {}}, + +{"ap_forbidden", new SimpleRestorer {}}, +{"ap_multiplier", new SimpleRestorer {}}, +{"ap_extended", new SimpleRestorer {}}, + +{"ap_melee", new SimpleRestorer {}}, +{"ap_ranged", new SimpleRestorer {}}, +{"ap_catapult", new SimpleRestorer {}}, + + }}; +} diff --git a/8303/Parfentev_Leonid/lab5/storable.hpp b/8303/Parfentev_Leonid/lab5/storable.hpp new file mode 100644 index 000000000..d152b700c --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/storable.hpp @@ -0,0 +1,70 @@ +#ifndef _STORABLE_HPP +#define _STORABLE_HPP + +#include +#include +#include +#include + + +class RestorerTable; + +class Storable { +public: + virtual void store(std::ostream &os) const =0; + virtual bool restore(std::istream &, + RestorerTable *) { return true; }; + + virtual ~Storable() {} +}; + +#define TRIVIALLY_STORABLE(keyword) \ + public: \ + virtual void \ + store(std::ostream &os) const override \ + { \ + os << keyword "\n"; \ + } + + + +class Restorer { +public: + virtual Storable *restore(std::istream &is, + RestorerTable *tab) const =0; +}; + +class RestorerTable { + std::map _tab; + +public: + RestorerTable(std::map m) + :_tab{std::move(m)} {} + + Storable * + restore(std::istream &is) + { + std::string n; + is >> n; + + auto iter = _tab.find(n); + if (iter == _tab.end()) { + return nullptr; + } + + Storable *s = iter->second->restore(is, this); + if (!s) { + return nullptr; + } + + if (!s->restore(is, this)) { + delete s; + return nullptr; + } + return s; + } + + static RestorerTable *defaultTable(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/unit.cpp b/8303/Parfentev_Leonid/lab5/unit.cpp new file mode 100644 index 000000000..39098695f --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/unit.cpp @@ -0,0 +1,48 @@ +#include + +#include "storable.hpp" +#include "common_storables.hpp" +#include "unit.hpp" + + +std::default_random_engine global_random {}; + +void +Unit::store(std::ostream &os) const +{ + os << health() << "\n"; + + movePolicy()->store(os); + defensePolicy()->store(os); + attackPolicy()->store(os); + + os << "end\n"; +} + +bool +Unit::restore(std::istream &is, RestorerTable *tab) +{ + if (!ObjectWithHealth::restore(is, tab)) { + return false; + } + + for (;;) { + Storable *s = tab->restore(is); + + if (auto *mp = dynamic_cast(s)) { + setMovePolicy(mp); + } else if (auto *dp = dynamic_cast(s)) { + setDefensePolicy(dp); + } else if (auto *ap = dynamic_cast(s)) { + setAttackPolicy(ap); + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} diff --git a/8303/Parfentev_Leonid/lab5/unit.hpp b/8303/Parfentev_Leonid/lab5/unit.hpp new file mode 100644 index 000000000..93f745061 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/unit.hpp @@ -0,0 +1,271 @@ +#ifndef _H_UNIT_HPP +#define _H_UNIT_HPP + +#include +#include +#include + +#include "event.hpp" +#include "event_types.hpp" +#include "object_w_health.hpp" +#include "map.hpp" + + +extern std::default_random_engine global_random; + + +class MovePolicy: public Storable { +public: + virtual bool canMove(const Unit *u, MapIter to) =0; + virtual ~MovePolicy() {} +}; + +enum class AttackKind { + invalid, sword, spear, cavalry, arrow, stone, rock, bolt, +}; + +// NOTE: can’t do area damage +class AttackPolicy: public Storable { +public: + virtual bool canAttackTo(const Unit *u, MapIter to) =0; + + virtual MapIter actualPosition(const Unit *, MapIter to) + { + return to; + } + + // returns kind and base damage + virtual std::pair + baseAttack(const Unit *u, MapIter to) =0; + + virtual ~AttackPolicy() {} +}; + +struct DamageSpec { + int base_damage, damage_spread; + + void + scale(double k) + { + base_damage *= k; + damage_spread *= k; + } + + DamageSpec + scaled(double k) const + { + auto ds = *this; + ds.scale(k); + return ds; + } + + int evaluate() const + { + std::uniform_int_distribution<> + dist {-damage_spread, damage_spread}; + + return base_damage + dist(global_random); + } +}; + +class DefensePolicy: public Storable { +protected: + static DamageSpec + make_spec(double base, double spread) + { + return DamageSpec{(int)base, (int)spread}; + } + + static DamageSpec + defense_level(double k, int dmg) + { + return make_spec(round(1.0*dmg/k), + round(0.25*dmg/k)); + } + + static DamageSpec + normal_defense(double dmg) + { + return defense_level(1.0, dmg); + } + +public: + // returns base damage and spread + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) =0; + + virtual ~DefensePolicy() {} +}; + +class MovePolicyContainer { + MovePolicy *_mp; + +public: + MovePolicyContainer(MovePolicy *mp) :_mp{mp} {} + + MovePolicy *movePolicy() const { return _mp; } + void setMovePolicy(MovePolicy *mp) + { + _mp = mp; + } + virtual ~MovePolicyContainer() { delete _mp; } + + MovePolicyContainer * + findMoveContainerOf(const MovePolicy *mp) + { + for (MovePolicyContainer *mpc = this; mpc; + mpc = dynamic_cast( + mpc->movePolicy())) { + if (mpc->movePolicy() == mp) { + return mpc; + } + } + return nullptr; + } +}; + +class DefensePolicyContainer { + DefensePolicy *_dp; + +public: + DefensePolicyContainer(DefensePolicy *dp) :_dp{dp} {} + + DefensePolicy *defensePolicy() const { return _dp; } + void setDefensePolicy(DefensePolicy *dp) + { + _dp = dp; + } + virtual ~DefensePolicyContainer() { delete _dp; } + + DefensePolicyContainer * + findDefenseContainerOf(const DefensePolicy *dp) + { + for (DefensePolicyContainer *dpc = this; dpc; + dpc = dynamic_cast( + dpc->defensePolicy())) { + if (dpc->defensePolicy() == dp) { + return dpc; + } + } + return nullptr; + } +}; + +class AttackPolicyContainer { + AttackPolicy *_ap; + +public: + AttackPolicyContainer(AttackPolicy *ap) :_ap{ap} {} + + AttackPolicy *attackPolicy() const { return _ap; } + void setAttackPolicy(AttackPolicy *ap) + { + _ap = ap; + } + virtual ~AttackPolicyContainer() { delete _ap; } + + AttackPolicyContainer * + findAttackContainerOf(const AttackPolicy *ap) + { + for (AttackPolicyContainer *apc = this; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (apc->attackPolicy() == ap) { + return apc; + } + } + return nullptr; + } +}; + +class Unit: public Placeable, + public ObjectWithHealth, + public EventEmitter, + public MovePolicyContainer, + public DefensePolicyContainer, + public AttackPolicyContainer { +public: + Unit(MovePolicy *move, + AttackPolicy *attack, + DefensePolicy *defense, + int base_health) + :ObjectWithHealth{base_health}, + MovePolicyContainer{move}, + DefensePolicyContainer{defense}, + AttackPolicyContainer{attack} {} + + virtual void + heal(int hp) + { + emit(new events::UnitGetsHealed {this, hp}); + ObjectWithHealth::heal(hp); + } + + virtual void + takeDamage(int dmg) + { + if (!alive()) { + return; + } + + emit(new events::UnitTakesDamage {this, dmg}); + + ObjectWithHealth::takeDamage(dmg); + + if (!alive()) { + emit(new events::UnitDeath {this}); + } + } + + bool + canMove(MapIter to) const + { + return movePolicy()->canMove(this, to); + } + + bool + canAttackTo(MapIter to) const + { + return attackPolicy()->canAttackTo(this, to); + } + + MapIter + actualPosition(MapIter to) const + { + return attackPolicy()->actualPosition(this, to); + } + + std::pair + baseAttack(MapIter to) const + { + return attackPolicy()->baseAttack(this, to); + } + + DamageSpec + actualDamage(AttackKind kind, int base) const + { + return defensePolicy()->actualDamage(this, kind, base); + } + + // override to add type symbol! + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; + + virtual ~Unit() override + { + if (alive()) { + emit(new events::UnitLiveDeleted {this}); + } + } +}; + +#define UNIT_STORABLE_NAME(n) \ + virtual void \ + store(std::ostream &os) const override \ + { \ + os << n "\n"; \ + Unit::store(os); \ + } + +#endif diff --git a/8303/Parfentev_Leonid/lab5/unit_factory.hpp b/8303/Parfentev_Leonid/lab5/unit_factory.hpp new file mode 100644 index 000000000..5f1f475bd --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/unit_factory.hpp @@ -0,0 +1,24 @@ +#ifndef _H_UNIT_FACTORY_HPP +#define _H_UNIT_FACTORY_HPP + +#include "unit.hpp" + + +class UnitFactory { +public: + virtual Unit *create() const =0; + + virtual ~UnitFactory() {} +}; + +template +class SimpleUnitFactory: public UnitFactory { +public: + virtual Unit * + create() const override + { + return new U {}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab5/zombie_collector.hpp b/8303/Parfentev_Leonid/lab5/zombie_collector.hpp new file mode 100644 index 000000000..9de5f6981 --- /dev/null +++ b/8303/Parfentev_Leonid/lab5/zombie_collector.hpp @@ -0,0 +1,36 @@ +#ifndef _H_ZOMBIE_COLLECTOR_HPP +#define _H_ZOMBIE_COLLECTOR_HPP + +#include + +#include "unit.hpp" +#include "event.hpp" +#include "map.hpp" + + +class ZombieCollector: public EventListener { + std::vector _units {}; + +public: + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + return handle(ee->event()); + } + if (auto *ee = dynamic_cast(e)) { + _units.push_back(ee->unit()); + } + } + + void + collect(Map *m) + { + for (auto *unit: _units) { + delete m->removeUnitAt(unit->position()); + } + _units.clear(); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/Makefile b/8303/Parfentev_Leonid/lab6/Makefile new file mode 100644 index 000000000..94cc99971 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/Makefile @@ -0,0 +1,23 @@ +EXENAME = main +HEADERS = point.hpp placeable.hpp rectmap.hpp map.hpp pathfinder.hpp \ + unit.hpp common_policies.hpp melee_units.hpp ranged_units.hpp \ + catapult_units.hpp demo.hpp event.hpp base.hpp object_w_health.hpp \ + landscape.hpp landscape_types.hpp neutral_object.hpp \ + neutral_object_types.hpp unit_factory.hpp game.hpp \ + object_print.hpp event_printer.hpp iostream_player.hpp \ + mediator.hpp logging.hpp factory_table.hpp storable.hpp \ + common_storables.hpp restorers.hpp game_driver.hpp game_rules.hpp +OBJFILES = point.o rectmap.o map.o pathfinder.o unit.o demo.o main.o \ + event.o base.o game.o object_print.o iostream_player.o mediator.o \ + storable.o +LDLIBS = -lm + +all: $(EXENAME) + +$(OBJFILES): $(HEADERS) + +$(EXENAME): $(OBJFILES) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + $(RM) $(EXENAME) $(OBJFILES) diff --git a/8303/Parfentev_Leonid/lab6/base.cpp b/8303/Parfentev_Leonid/lab6/base.cpp new file mode 100644 index 000000000..78079331b --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/base.cpp @@ -0,0 +1,162 @@ +#include +#include +#include + +#include "unit.hpp" +#include "unit_factory.hpp" +#include "event.hpp" +#include "base.hpp" +#include "mediator.hpp" +#include "factory_table.hpp" + +#include "storable.hpp" +#include "common_storables.hpp" + + +bool +Base::canCreateUnit(const std::string &key) const +{ + if (unitsCount() == maxUnitsCount()) { + return false; + } + + if (!FactoryTable::instance()->canCreate(key)) { + return false; + } + + return true; +} + +int +Base::createUnit(const std::string &key, Mediator *m) +{ + if (!canCreateUnit(key)) { + return -1; + } + + if (m->infoAt(position()).unit()) { + return false; + } + + Unit *u = FactoryTable::instance()->create(key); + int id = addUnit(u); + + if (id < 0) { + delete u; + return -1; + } + + if (!m->spawnUnit(u, position())) { + removeUnit(u); + delete u; + return -1; + } + + return id; +} + +bool +Base::setMaxUnitsCount(int m) +{ + if (m < unitsCount()) { + return false; + } + _max_count = m; + return true; +} + +int +Base::addUnit(Unit *u) +{ + if (maxUnitsCount() >= 0 + && unitsCount() == maxUnitsCount()) { + return -1; + } + + _units[_next_idx] = u; + u->subscribe(this); + u->emit(new events::UnitAdded {u}); + + return _next_idx++; +} + +void +Base::removeUnit(Unit *u) +{ + u->unsubscribe(this); + + for (auto iter = _units.begin(); + iter != _units.end(); + ++iter) { + if (iter->second == u) { + _units.erase(iter); + break; + } + } +} + +Unit * +Base::getUnitById(int id) const +{ + auto iter = _units.find(id); + return (iter != _units.end()) + ? iter->second + : nullptr; +} + +bool +Base::becomeDestroyedBy(Unit *u) +{ + for (auto iter = unitsBegin(); + iter != unitsEnd(); + ++iter) { + if (iter.unit() == u) { + return false; + } + } + + _destroyed = true; + + for (auto iter = unitsBegin(); + iter != unitsEnd(); + ++iter) { + Unit *v = iter.unit(); + v->emit(new events::UnitDeath {v}); + } + + emit(new events::BaseDestroyed {this}); + return true; +} + +void +Base::store(std::ostream &os) const +{ + os << "base " << _max_count << " " << _destroyed << "\n"; +} + +bool +Base::restore(std::istream &is, + RestorerTable *) +{ + is >> _max_count >> _destroyed; + return !is.fail(); +} + +void +Base::handle(Event *e) +{ + EventForwarder::handle(e); + + if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } else if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } +} + +Base::~Base() +{ + for (auto p: _units) { + p.second->unsubscribe(this); + } +} diff --git a/8303/Parfentev_Leonid/lab6/base.hpp b/8303/Parfentev_Leonid/lab6/base.hpp new file mode 100644 index 000000000..deadd93cc --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/base.hpp @@ -0,0 +1,98 @@ +#ifndef _H_BASE_HPP +#define _H_BASE_HPP + +#include +#include +#include + +#include "storable.hpp" +#include "placeable.hpp" +#include "event.hpp" +#include "unit.hpp" +#include "mediator.hpp" + + +class Base: public Placeable, + public EventForwarder, + public Storable { + + std::map _units {}; + int _next_idx = 0; + int _max_count = -1; + bool _destroyed = false; + +public: + Base() {} + + virtual bool + canCreateUnit(const std::string &key) const; + virtual int + createUnit(const std::string &key, Mediator *m); + + int + unitsCount() const { return (int)_units.size(); } + bool + setMaxUnitsCount(int m); + int + maxUnitsCount() const { return _max_count; } + + int + addUnit(Unit *u); + void + removeUnit(Unit *u); + Unit * + getUnitById(int id) const; + + virtual void spin() {} + + bool + becomeDestroyedBy(Unit *u); + bool + destroyed() const { return _destroyed; } + + class unitsIter { + using real_iter_t = std::map::const_iterator; + real_iter_t _iter; + + public: + unitsIter(real_iter_t it) + :_iter{it} {} + + int id() const { return _iter->first; } + Unit *unit() const { return _iter->second; } + unitsIter &operator++() { ++_iter; return *this; } + unitsIter + operator++(int) + { + unitsIter x{_iter}; + ++*this; + return x; + } + bool + operator==(const unitsIter &o) const + { + return _iter == o._iter; + } + bool + operator!=(const unitsIter &o) const + { + return !(*this == o); + } + }; + + unitsIter + unitsBegin() const { return unitsIter{_units.begin()}; } + unitsIter + unitsEnd() const { return unitsIter{_units.end()}; } + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &, + RestorerTable *) override; + + virtual void + handle(Event *e) override; + + virtual ~Base() override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/catapult_units.hpp b/8303/Parfentev_Leonid/lab6/catapult_units.hpp new file mode 100644 index 000000000..914c28041 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/catapult_units.hpp @@ -0,0 +1,170 @@ +#ifndef _H_CATAPULT_UNITS_HPP +#define _H_CATAPULT_UNITS_HPP + +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" +#include "ranged_units.hpp" + + +class CatapultAttack: public RangedAttack { + double _spread_tang, _spread_normal; + + struct FVec2 { + double x, y; + + explicit FVec2(const Vec2 &v) + :x{(double)v.x()}, y{(double)v.y()} {} + + FVec2(double x, double y) + :x{x}, y{y} {} + + operator Vec2() const + { + return Vec2{int(round(x)), int(round(y))}; + } + + FVec2 + orthogonal() const { return {y, -x}; } + + FVec2 & + operator*=(double a) + { + x *= a; + y *= a; + return *this; + } + FVec2 + operator*(double a) const + { + FVec2 tmp {*this}; + return tmp *= a; + } + + FVec2 + normalized() const { return (*this) * (1/sqrt(x*x + y*y)); } + + FVec2 & + operator+=(const FVec2 &dxy) + { + x += dxy.x; + y += dxy.y; + return *this; + } + FVec2 + operator+(const FVec2 &dxy) const + { + FVec2 tmp{*this}; + return tmp += dxy; + } + + FVec2 + apply(double t, double n) const + { + return normalized() * t + + orthogonal().normalized() * n; + } + }; + +public: + explicit CatapultAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0, + double min_dist=0, + double max_dist=0, + double dist_pow=0, + double spread_t=0, + double spread_n=0) + :RangedAttack{kind, base_dmg, min_dist, max_dist, dist_pow}, + _spread_tang{spread_t}, + _spread_normal{spread_n} {} + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + Vec2 dest = to.point(); + Vec2 delta = dest.delta(u->position()); + FVec2 fdelta {delta}; + + std::uniform_real_distribution<> + t_dist {-_spread_tang, _spread_tang}, + n_dist {-_spread_normal, _spread_normal}; + + double + t = t_dist(global_random), + n = n_dist(global_random); + + FVec2 result = fdelta.apply(t, n); + return to.shifted(Vec2{result}); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_catapult " << static_cast(_kind) + << " " << _base_damage << " " << _min_distance + << " " << _max_distance << " " + << _dist_pow << " " << _spread_tang << " " + << _spread_normal << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage >> _min_distance + >> _max_distance >> _dist_pow >> _spread_tang + >> _spread_normal; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicCatapultUnit: public Unit { +public: + BasicCatapultUnit(AttackKind attack_kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n, + int base_health) + :Unit{new BasicMovement {1}, + new CatapultAttack {attack_kind, + base_dmg, min_dist, max_dist, + dist_pow, spread_t, spread_n}, + DefenseLevelDeco::good_defense_deco( + AttackKind::arrow, + new BasicDefense {0.75}), + base_health} {} +}; + +namespace units { + class Onager: public BasicCatapultUnit { + public: + Onager() :BasicCatapultUnit{ + AttackKind::rock, 90, + 3, 10, 0.05, + 0.2, 0.1, + 30} {} + + UNIT_STORABLE_NAME("u_onager"); + }; + + class BoltThrower: public BasicCatapultUnit { + public: + BoltThrower() :BasicCatapultUnit{ + AttackKind::bolt, 110, + 2, 6, 0.15, + 0.05, 0.05, + 20} {} + + UNIT_STORABLE_NAME("u_boltthrower"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/common_policies.hpp b/8303/Parfentev_Leonid/lab6/common_policies.hpp new file mode 100644 index 000000000..99ad30554 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/common_policies.hpp @@ -0,0 +1,308 @@ +#ifndef _H_COMMON_POLICIES_HPP +#define _H_COMMON_POLICIES_HPP + +#include + +#include "unit.hpp" +#include "map.hpp" +#include "pathfinder.hpp" + + +class BasicMovement: public MovePolicy { + int _steps_per_turn; + +public: + explicit BasicMovement(int n=0) + :_steps_per_turn{n} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _steps_per_turn}; + return pf.run(); + } + + virtual void + store(std::ostream &os) const override + { + os << "mp_basic " << _steps_per_turn << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + is >> _steps_per_turn; + return !is.fail(); + } +}; + +class NestedMovement: public MovePolicy, + public MovePolicyContainer { +public: + using MovePolicyContainer::MovePolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setMovePolicy(p); + return true; + } + delete s; + return false; + } +}; + +class ModifyingMovePolicy: public NestedMovement { + int _max; + +public: + explicit ModifyingMovePolicy(MovePolicy *p=nullptr, int max_dist=0) + :NestedMovement{p}, _max{max_dist} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _max}; + if (!pf.run()) + return false; + + return movePolicy()->canMove(u, to); + } + + virtual void + store(std::ostream &os) const override + { + os << "mp_modifyiing " << _max << "\n"; + movePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _max; + return !is.fail() && NestedMovement::restore(is, tab); + } +}; + + + +class BasicDefense: public DefensePolicy { + double _lvl; + +public: + explicit BasicDefense(double level=1.0) + :_lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *, AttackKind, int base) override + { + return normal_defense(base); + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_basic " << _lvl << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + is >> _lvl; + return !is.fail(); + } +}; + +class NestedDefense: public DefensePolicy, + public DefensePolicyContainer { +public: + using DefensePolicyContainer::DefensePolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setDefensePolicy(p); + return true; + } + delete s; + return false; + } +}; + +class DefenseLevelDeco: public NestedDefense { + // Controls nested policy lifetime + AttackKind _kind; + double _lvl; + +public: + explicit DefenseLevelDeco(DefensePolicy *p=0, + AttackKind kind=AttackKind::invalid, + double level=0) + :NestedDefense{p}, _kind{kind}, _lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + if (kind == _kind) + return defense_level(_lvl, base); + return defensePolicy()->actualDamage(u, kind, base); + } + + static DefenseLevelDeco * + defense_level_deco(AttackKind kind, double lvl, DefensePolicy *p) + { + return new DefenseLevelDeco {p, kind, lvl}; + } + + static DefenseLevelDeco * + good_defense_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 2.0, p); + } + + static DefenseLevelDeco * + vulnerability_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 0.5, p); + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_level_deco " << static_cast(_kind) + << " " << _lvl << "\n"; + defensePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + int x; + is >> x; + _kind = static_cast(x); + is >> _lvl; + + return !is.fail() && NestedDefense::restore(is, tab); + } +}; + +class MultiplierDefensePolicy: public NestedDefense { + double _mul; + +public: + explicit MultiplierDefensePolicy(DefensePolicy *p=nullptr, + double mul=0) + :NestedDefense{p}, _mul{mul} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + DamageSpec ds = defensePolicy()->actualDamage(u, kind, base); + ds.scale(1/_mul); + return ds; + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_multiplier " << _mul << "\n"; + defensePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _mul; + return !is.fail() && NestedDefense::restore(is, tab); + } +}; + + + +class AttackForbidden: public AttackPolicy { +public: + using AttackPolicy::AttackPolicy; + + virtual bool + canAttackTo(const Unit *, MapIter) override + { + return false; + } + + virtual std::pair + baseAttack(const Unit *, MapIter) override + { + return std::make_pair(AttackKind::invalid, 0); + } + + TRIVIALLY_STORABLE("ap_forbidden"); +}; + +class NestedAttack: public AttackPolicy, + public AttackPolicyContainer { +public: + using AttackPolicyContainer::AttackPolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setAttackPolicy(p); + return true; + } + delete s; + return false; + } +}; + +class MultiplierAttackPolicy: public NestedAttack { + double _mul; + +public: + explicit MultiplierAttackPolicy(AttackPolicy *p=nullptr, + double mul=0) + :NestedAttack{p}, _mul{mul} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return attackPolicy()->canAttackTo(u, to); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + auto ba = attackPolicy()->baseAttack(u, to); + return std::make_pair(ba.first, (int)(ba.second * _mul)); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_multiplier " << _mul << "\n"; + attackPolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _mul; + return !is.fail() && NestedAttack::restore(is, tab); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab6/common_storables.hpp b/8303/Parfentev_Leonid/lab6/common_storables.hpp new file mode 100644 index 000000000..27cfd5a0a --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/common_storables.hpp @@ -0,0 +1,117 @@ +#ifndef _H_COMMON_STORABLES_HPP +#define _H_COMMON_STORABLES_HPP + +#include "storable.hpp" +#include "object_print.hpp" + + +class StorableEnd: public Storable { + TRIVIALLY_STORABLE("end"); +}; + +class StorableCoordinates: public Storable { + Vec2 _c; + +public: + StorableCoordinates() {} + + StorableCoordinates(Vec2 c) :_c{c} {} + + virtual void + store(std::ostream &os) const override + { + os << "coords " << _c.x() << " " << _c.y() << "\n"; + } + + virtual bool + restore(std::istream &is, + RestorerTable *) override + { + int x, y; + is >> x >> y; + _c = Vec2{x, y}; + return !is.fail(); + } + + Vec2 coords() const { return _c; } +}; + +class StorableWithIndex: public Storable { + int _i; + Storable *_s; + +public: + StorableWithIndex() {} + + StorableWithIndex(int idx, Storable *s) + :_i{idx}, _s{s} {} + + virtual void + store(std::ostream &os) const override + { + os << "index " << _i << " "; + _s->store(os); + } + + virtual bool + restore(std::istream &is, + RestorerTable *tab) override + { + is >> _i; + _s = tab->restore(is); + return !is.fail() && _s; + } + + int index() const { return _i; } + Storable *child() const { return _s; } + + static void + storeWithIndex(int idx, const Storable *s, + std::ostream &os) + { + os << "index " << idx << " "; + s->store(os); + } +}; + +class StorableWithCoords: public Storable { + Vec2 _c; + Storable *_s; + +public: + StorableWithCoords() {} + + StorableWithCoords(Vec2 c, Storable *s) + :_c{c}, _s{s} {} + + virtual void + store(std::ostream &os) const override + { + os << "at " << _c.x() << " " << _c.y() << " "; + _s->store(os); + } + + virtual bool + restore(std::istream &is, + RestorerTable *tab) override + { + int x, y; + is >> x >> y; + _c = Vec2{x, y}; + _s = tab->restore(is); + return !is.fail() && _s; + } + + Vec2 coords() const { return _c; } + Storable *child() const { return _s; } + + static void + storeWithCoords(Vec2 pt, const Storable *s, + std::ostream &os) + { + os << "at " << pt.x() << " " << pt.y() << " "; + s->store(os); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/demo.cpp b/8303/Parfentev_Leonid/lab6/demo.cpp new file mode 100644 index 000000000..2c438ae19 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/demo.cpp @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" + +#include "event.hpp" +#include "event_types.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "unit_factory.hpp" + +#include "game.hpp" +#include "event_printer.hpp" +#include "iostream_player.hpp" + +#include "factory_table.hpp" + + +void +demo1() +{ + // create a map + auto *map = new Map {10, 10}; + + // create a few units + auto sw1_iter = map->addUnit(new units::Swordsman {}, {3, 3}); + map->addUnit(new units::Swordsman {}, {4, 4}); + + // write the map + std::cout << map << "\n"; + + // test the pathfinder + static const std::vector pts { + {3, 4}, + {3, 3}, + {5, 5}, + {5, 4}, + {5, 3}, + }; + for (auto pt: pts) { + std::cout << sw1_iter.unit() << " " + << ((sw1_iter.unit()->canMove(map->iterAt(pt))) + ? "can" : "can't") + << " move to " << pt << "\n"; + } + + // clean up + delete map; +} + +void +demo2() +{ + auto *map = new Map {10, 10}; + + auto c_iter = map->addUnit(new units::Cavalry {}, {3, 3}); + auto *u = c_iter.unit(); + + std::cout << map << "\n"; + + static const std::vector path { + {4, 5}, {6, 5}, {9, 5}, {8, 7}, {7, 9}, + }; + for (auto pt: path) { + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + std::cout << u << " can't move to " << pt << "!\n"; + break; + } + + std::cout << "moving " << u + << " to " << pt << "...\n"; + + c_iter = map->addUnit(map->removeUnitAt(c_iter), pt); + if (c_iter.null()) { + std::cout << "failed!\n"; + } + } + + std::cout << "\n" << map; + + delete map; +} + +void +demo3() +{ + auto *map = new Map {10, 10}; + map->setMaxUnitsCount(2); + + Unit *sw = new units::Swordsman {}; + Unit *ar = new units::Archer {}; + + map->addUnit(sw, {5, 0}); + map->addUnit(ar, {5, 9}); + + Unit *x = new units::Swordsman {}; + if (!map->addUnit(x, {1, 1}).null()) { + std::cout << "Added one more unit!\n"; + delete map->removeUnitAt({1, 1}); + } else { + std::cout << "Max units: " << map->maxUnitsCount() + << ", current units: " << map->unitsCount() + << "\n"; + delete x; + } + + std::cout << map; + + while (sw->alive() && ar->alive()) { + Vec2 from = sw->position(); + Vec2 to = from.shifted({0, 1}); + + std::cout << "\nmoving " << sw << " from " << from + << " to " << to << "...\n"; + + if (!sw->canMove(map->iterAt(to))) { + std::cout << "can't move\n"; + break; + } + + if (map->addUnit(map->removeUnitAt(from), to).null()) { + std::cout << "failed\n"; + break; + } + + std::cout << ar << " shoots at " << sw->position() << "...\n"; + + auto toi = map->iterAt(sw->position()); + if (!ar->canAttackTo(toi)) { + std::cout << "can't shoot\n"; + // it’s ok + } else { + auto pos = ar->actualPosition(toi); + if (Unit *targ = pos.unit()) { + auto dam = ar->baseAttack(toi); + + std::cout << "hits " << targ << "\n"; + + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + + std::cout << "now: " << targ << "\n"; + } + } + } +} + +MapIter +doAttack(Unit *u, MapIter to) +{ + if (!u->canAttackTo(to)) { + return MapIter::makeNull(); + + } else { + auto pos = u->actualPosition(to); + + if (Unit *targ = pos.unit()) { + auto dam = u->baseAttack(pos); + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + return pos; + } + + return MapIter::makeNull(); + } +} + +struct SimpleGame { + Map *map; + Base *b1, *b2; + EventPrinter *pr; + + explicit SimpleGame(int w=10, int h=10, + int x1=1, int y1=1, + int x2=9, int y2=9) + { + pr = new EventPrinter {std::cout}; + + map = new Map {w, h}; + + b1 = new Base {}; + pr->setPrefix(b1, "Base 1"); + + map->addBase(b1, {x1, y1}); + b1->subscribe(pr); + + b2 = new Base {}; + pr->setPrefix(b2, "Base 2"); + + map->addBase(b2, {x2, y2}); + b2->subscribe(pr); + } + + ~SimpleGame() + { + delete map; + delete pr; + } +}; + +void +demo4() +{ + SimpleGame g {}; + + Unit *u1 = new units::Swordsman {}; + Unit *u2 = new units::Swordsman {}; + + g.b1->addUnit(u1); + g.b2->addUnit(u2); + + g.map->addUnit(u1, {3, 3}); + g.map->addUnit(u2, {4, 3}); + + while (u1->alive() + && u2->alive()) { + doAttack(u1, g.map->iterAt(u2->position())); + if (u2->alive()) { + doAttack(u2, g.map->iterAt(u1->position())); + } + } +} + +MapIter +doMove(Map *map, const Unit *u, Vec2 pt) +{ + auto from = map->iterAt(u->position()); + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + return MapIter::makeNull(); + } + + return map->addUnit(map->removeUnitAt(from), pt); +} + +Unit * +setupUnit(Base *base, const std::string &k, Mediator *m, Vec2 pt) +{ + Unit *u = base->getUnitById(base->createUnit(k, m)); + m->teleportUnit(u, pt); + return u; +} + +void +demo5() +{ + SimpleGame g {}; + + // 2,2 .. 5,5: swamp + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + g.map->setLandscape(new landscapes::Swamp {}, + {2+i, 2+j}); + } + } + + // 1,7 .. 6,9: forest + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 5; ++i) { + g.map->setLandscape(new landscapes::Forest {}, + {1+i, 7+j}); + } + } + + auto *m = new Mediator {g.map}; + auto u1 = setupUnit(g.b1, "swordsman", m, {2, 2}); + auto u2 = setupUnit(g.b2, "swordsman", m, {3, 2}); + + if (doMove(g.map, u1, {0, 2}).null()) { + std::cout << "Can't move " << u1 << " across 2 cells\n"; + } else { + std::cout << "Moved " << u1 << " across 2 cells \n"; + } + + std::cout << "u1: " << u1 << "\n"; + + if (doAttack(u1, g.map->iterAt(u2->position())).null()) { + std::cout << "Can't attack in swamp\n"; + } else { + std::cout << "Attacked in a swamp\n"; + } + + std::cout << "u2: " << u2 << "\n"; + + doMove(g.map, u1, {1, 2}); + doMove(g.map, u2, {2, 3}); + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + doAttack(u1, g.map->iterAt(u2->position())); + + auto u3 = setupUnit(g.b1, "spearsman", m, {3, 8}); + auto u4 = setupUnit(g.b1, "spearsman", m, {7, 8}); + auto u5 = setupUnit(g.b2, "onager", m, {5, 5}); + + while (u3->alive()) + doAttack(u5, g.map->iterAt(u3->position())); + + while (u4->alive()) + doAttack(u5, g.map->iterAt(u4->position())); + + std::cout << g.map; + + delete m; +} + +void +demo6() +{ + SimpleGame g {}; + + auto *m = new Mediator {g.map}; + auto *u1 = setupUnit(g.b1, "slinger", m, {1, 5}); + auto *u2 = setupUnit(g.b2, "swordsman", m, {6, 5}); + + g.map->addNeutralObject(new objects::Tower {}, {1, 5}); + g.map->addNeutralObject(new objects::HealingWell {}, {6, 5}); + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " can't reach " << u2 << "\n"; + } else { + std::cout << u1 << " somehow reached " << u2 << "\n"; + } + + if (m->useObject(u1)) { + std::cout << u1 << " used the tower\n"; + } else { + std::cout << u1 << " can't use the tower\n"; + } + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " still can't reach " << u2 << "\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + if (m->useObject(u2)) { + std::cout << u2 << " used the healing well\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; +} + +void +demo7() +{ + class MoverPlayer: public Player { + std::string _unit_type; + int _id = -1; + + public: + using Player::Player; + + MoverPlayer(std::string name, + std::string type) + :Player{name}, _unit_type{std::move(type)} {} + + virtual bool + takeTurn(const Game *, Mediator *m, Base *b) override + { + if (_id >= 0) { + Unit *u = b->getUnitById(_id); + m->moveUnitTo(u, u->position().shifted({1, 0})); + } else { + _id = b->createUnit(_unit_type, m); + } + return true; + } + }; + + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + auto *b = new Base {}; + map->addBase(b, {3, 3}); + int b_id = g.addBase(b); + + auto *p = new MoverPlayer {"Player 1", "swordsman"}; + g.setPlayer(b_id, p); + + for (int i = 0; i < 5; ++i) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo8() +{ + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + std::stringstream s1 {}; + s1 << "base\n" + << "create spearsman\n" + << "map 0 0 9 9\n" + << "move 0 3 5\n" + << "map 0 0 9 9\n" + << "describe 3 5\n"; + + auto *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + std::stringstream s2 {}; + s2 << "create archer\n" + << "attack 0 3 5\n"; + + auto *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo9() +{ + Map *map = new Map {10, 10}; + + auto *uf = (new objects + ::WeaponSmiths + ::CatapultUnitFilter {}); + auto *ws = new objects::WeaponSmiths {2.0, uf}; + map->addNeutralObject(ws, {3, 4}); + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + Base *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + Base *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + std::stringstream s1 {}; + std::stringstream s2 {}; + + s1 << "create onager\n" + << "move 0 3 4\n" + << "use 0\n" + << "create onager\n" + << "move 1 3 2\n" + << "attack 0 7 4\n" + << "attack 1 7 2\n" + << "create swordsman\n" + << "move 0 3 5\n" + << "move 2 3 4\n" + << "use 2\n"; + + s2 << "create swordsman\n" + << "move 0 7 4\n" + << "create swordsman\n" + << "move 1 7 2\n" + << "skip skip skip skip skip\n"; + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} diff --git a/8303/Parfentev_Leonid/lab6/demo.hpp b/8303/Parfentev_Leonid/lab6/demo.hpp new file mode 100644 index 000000000..ae8bc7a95 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/demo.hpp @@ -0,0 +1,14 @@ +#ifndef _H_DEMO_HPP +#define _H_DEMO_HPP + +void demo1(); +void demo2(); +void demo3(); +void demo4(); +void demo5(); +void demo6(); +void demo7(); +void demo8(); +void demo9(); + +#endif diff --git a/8303/Parfentev_Leonid/lab6/event.cpp b/8303/Parfentev_Leonid/lab6/event.cpp new file mode 100644 index 000000000..b7ff78ebd --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/event.cpp @@ -0,0 +1,31 @@ +#include "event.hpp" + +void +EventEmitter::emit_shared(Event *e) const +{ + for (auto iter = _listeners.begin(); iter != _listeners.end();) { + auto *listener = *iter++; + // note: the listener may safely unsubscribe when handling the + // event. + listener->handle(e); + } +} + +void +EventEmitter::emit(Event *e) const +{ + emit_shared(e); + delete e; +} + +void +EventEmitter::subscribe(EventListener *l) +{ + _listeners.insert(l); +} + +void +EventEmitter::unsubscribe(EventListener *l) +{ + _listeners.erase(l); +} diff --git a/8303/Parfentev_Leonid/lab6/event.hpp b/8303/Parfentev_Leonid/lab6/event.hpp new file mode 100644 index 000000000..fa2d1f541 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/event.hpp @@ -0,0 +1,62 @@ +#ifndef _H_EVENT_HPP +#define _H_EVENT_HPP + +#include + +class EventListener; +class Event; + +class EventEmitter { + std::set _listeners {}; + +public: + void emit_shared(Event *e) const; + void emit(Event *e) const; + + void subscribe(EventListener *l); + void unsubscribe(EventListener *l); + + virtual ~EventEmitter() {} +}; + +class Event { +public: + virtual ~Event() {} +}; + +class EventListener { +public: + virtual void handle(Event *e) =0; + + virtual ~EventListener() {} +}; + +class EventForwarder; + +namespace events { + + class Forwarded: public Event { + Event *_e; + EventForwarder *_f; + + public: + Forwarded(Event *e, EventForwarder *f) + :_e{e}, _f{f} {} + + Event *event() const { return _e; } + EventForwarder *forwarder() const { return _f; } + }; + +} + +class EventForwarder: public EventEmitter, + public EventListener { +public: + virtual void + handle(Event *e) override + { + emit(new events::Forwarded {e, this}); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/event_printer.hpp b/8303/Parfentev_Leonid/lab6/event_printer.hpp new file mode 100644 index 000000000..afeca85b3 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/event_printer.hpp @@ -0,0 +1,127 @@ +#ifndef _H_EVENT_PRINTER_HPP +#define _H_EVENT_PRINTER_HPP + +#include +#include +#include +#include + +#include "event.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "player.hpp" +#include "object_print.hpp" + + +class EventPrinter: public EventListener { + std::ostream *_os; + bool _free_os; + + std::map _prefix_map {}; + + std::string + makeName(const char *base, int idx) + { + std::ostringstream oss {}; + oss << base << " " << idx; + return oss.str(); + } + +public: + EventPrinter(std::ostream &os) + :_os{&os}, _free_os{false} {} + + EventPrinter(std::ostream *os) + :_os{os}, _free_os{true} {} + + std::ostream & + ostream() const { return *_os; } + + void + setPrefix(EventForwarder *f, const std::string &s) + { + _prefix_map[f] = s; + } + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + auto iter = _prefix_map.find(ee->forwarder()); + if (iter != _prefix_map.end()) { + (*_os) << iter->second << ": "; + } + + return handle(ee->event()); + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit added: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit died: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " takes " + << ee->damage() << " health points of damage\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " gets healed by " + << ee->health() << " health points\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->attacker() + << " attacked another unit " << ee->target() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->target() + << " was attacked by another unit " + << ee->attacker() << "\n"; + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " moved from " << ee->sourcePos() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " used object " << ee->neutralObject() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "(Live unit " << ((void *)ee->unit()) + << " deleted)\n"; + + } else if (dynamic_cast(e)) { + (*_os) << "Base destroyed\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << " over\n"; + + } else { + (*_os) << "Unknown event\n"; + } + } + + virtual ~EventPrinter() override + { + if (_free_os) { + _os->flush(); + delete _os; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/event_types.hpp b/8303/Parfentev_Leonid/lab6/event_types.hpp new file mode 100644 index 000000000..810d3bd94 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/event_types.hpp @@ -0,0 +1,156 @@ +#ifndef _H_EVENT_TYPES_HPP +#define _H_EVENT_TYPES_HPP + +#include "point.hpp" +#include "event.hpp" + + +class Unit; +class Base; +class NeutralObject; +class Player; + + +class UnitEvent: public Event { + Unit *_u; + +public: + UnitEvent(Unit *u) :_u{u} {} + + Unit *unit() const { return _u; } +}; + +class AttackEvent: public Event { + Unit *_a, *_b; + +public: + AttackEvent(Unit *a, Unit *b) :_a{a}, _b{b} {} + + Unit *attacker() const { return _a; } + Unit *target() const { return _b; } +}; + +class BaseEvent: public Event { + Base *_b; + +public: + BaseEvent(Base *b) :_b{b} {} + + Base *base() const { return _b; } +}; + +class PlayerEvent: public Event { + Player *_p; + +public: + PlayerEvent(Player *p) :_p{p} {} + + Player *player() const { return _p; } +}; + +class UnitMoveEvent: public UnitEvent { + Vec2 _src; + +public: + UnitMoveEvent(Unit *u, Vec2 src) + :UnitEvent{u}, _src{src} {} + + Vec2 sourcePos() const { return _src; } +}; + + + +namespace events { + + class UnitDeath: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitAdded: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitLiveDeleted: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitTakesDamage: public UnitEvent { + int _dmg; + + public: + UnitTakesDamage(Unit *u, int dmg) + :UnitEvent{u}, _dmg{dmg} {} + + int damage() const { return _dmg; } + }; + + class UnitGetsHealed: public UnitEvent { + int _hp; + + public: + UnitGetsHealed(Unit *u, int hp) + :UnitEvent{u}, _hp{hp} {} + + int health() const { return _hp; } + }; + + class UnitWasAttacked: public AttackEvent { + public: + using AttackEvent::AttackEvent; + }; + + class UnitAttacked: public AttackEvent { + Vec2 _tc; + + public: + UnitAttacked(Unit *u, Vec2 tc, Unit *tu) + :AttackEvent{u, tu}, _tc{tc} {} + + Vec2 targetCoordinates() const { return _tc; } + }; + + class UnitMoved: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitTeleported: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitUsedObject: public UnitEvent { + NeutralObject *_n; + + public: + UnitUsedObject(Unit *u, NeutralObject *n) + :UnitEvent{u}, _n{n} {} + + NeutralObject *neutralObject() const { return _n; } + }; + + + + class BaseDestroyed: public BaseEvent { + public: + using BaseEvent::BaseEvent; + }; + + + + class TurnStarted: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + + class TurnOver: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/factory_table.hpp b/8303/Parfentev_Leonid/lab6/factory_table.hpp new file mode 100644 index 000000000..693031648 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/factory_table.hpp @@ -0,0 +1,81 @@ +#ifndef _H_FACTORY_TABLE_HPP +#define _H_FACTORY_TABLE_HPP + +#include +#include + +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "unit_factory.hpp" + + +class FactoryTable { + std::map _tab {}; + + void + registerFactory(const std::string &key, UnitFactory *f) + { + _tab[key] = f; + } + + FactoryTable() + { +#define REG(k, T) \ + registerFactory( \ + k, new SimpleUnitFactory {}) + REG("swordsman", Swordsman); + REG("spearsman", Spearsman); + REG("cavalry", Cavalry); + REG("archer", Archer); + REG("slinger", Slinger); + REG("onager", Onager); + REG("boltthrower", BoltThrower); +#undef REG + } + +public: + static const FactoryTable * + instance() + { + static FactoryTable *inst = new FactoryTable {}; + return inst; + } + + virtual bool + canCreate(const std::string &key) const + { + return _tab.find(key) != _tab.end(); + } + + virtual std::vector + keys() const + { + std::vector keys {}; + for (auto p: _tab) { + keys.push_back(p.first); + } + return keys; + } + + virtual Unit * + create(const std::string &key) const + { + auto iter = _tab.find(key); + if (iter == _tab.end()) { + return nullptr; + } + + return iter->second->create(); + } + + ~FactoryTable() + { + for (auto p: _tab) { + delete p.second; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/game.cpp b/8303/Parfentev_Leonid/lab6/game.cpp new file mode 100644 index 000000000..dd9272b12 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/game.cpp @@ -0,0 +1,215 @@ +#include +#include + +#include "base.hpp" +#include "game.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "storable.hpp" +#include "common_storables.hpp" + + +Game::Game(Map *map) + :_map{map}, + _med{new Mediator {map}} +{ + this->subscribe(_log_sink); +} + +int +Game::addBase(Base *base) +{ + base->subscribe(_coll); + base->subscribe(this); + + _recs.push_back(BaseRecord{base, nullptr}); + + return (int)(_recs.size() - 1); +} + +void +Game::setPlayer(int base_idx, Player *p) +{ + Player *&p_place = _recs[base_idx].player; + if (p_place) { + p_place->unsubscribe(_log_sink); + delete p_place; + --_players; + } + p_place = p; + if (p) { + p->subscribe(_log_sink); + ++_players; + } +} + +int +Game::basesCount() const +{ + return _recs.size(); +} + +int +Game::playersCount() const +{ + return _players; +} + +Base * +Game::baseByIdx(int idx) const +{ + return _recs[idx].base; +} + +void +Game::spin() +{ + auto &rec = _recs[_next]; + Player *p = rec.player; + Base *b = rec.base; + + if (p) { + if (b->destroyed()) { + setPlayer(_next, nullptr); + + } else { + emit(new events::TurnStarted {p}); + + bool res = p->takeTurn(this, _med, b); + + _coll->collect(_map); + + emit(new events::TurnOver {p}); + + if (!res) { + setPlayer(_next, nullptr); + } + } + } + + if (++_next == (int)_recs.size()) { + _next = 0; + } +} + +void +Game::setResetHandler(ResetHandler *r) +{ + _reset = r; +} + +void +Game::requestReset() const +{ + _reset->reset(); +} + +void +Game::store(std::ostream &os) const +{ + os << "game\n"; + _map->store(os); + + os << _next << "\n"; + + for (int i = 0; i < basesCount(); ++i) { + auto *b = baseByIdx(i); + + StorableWithCoords::storeWithCoords(b->position(), b, os); + + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + auto *u = iter.unit(); + auto *sc = new StorableWithCoords {u->position(), u}; + StorableWithIndex::storeWithIndex(i, sc, os); + } + } + + for (int i = 0; i < basesCount(); ++i) { + auto *p = _recs[i].player; + if (p) { + StorableWithIndex::storeWithIndex(i, p, os); + } + } + + os << "end\n"; +} + +bool +Game::restore(std::istream &is, + RestorerTable *tab) +{ + is >> _next; + + for (;;) { + Storable *s = tab->restore(is); + if (auto *sc = + dynamic_cast(s)) { + if (auto *b + = dynamic_cast(sc->child())) { + _map->addBase(b, sc->coords()); + addBase(b); + } else { + delete sc->child(); + delete sc; + return false; + } + delete sc; + } else if (auto *si = + dynamic_cast(s)) { + if (auto *p + = dynamic_cast(si->child())) { + if (si->index() < 0 + || si->index() >= basesCount()) { + delete p; + delete si; + return false; + } + setPlayer(si->index(), p); + } else if (auto *sc = + dynamic_cast( + si->child())) { + if (auto *u + = dynamic_cast(sc->child())) { + _map->addUnit(u, sc->coords()); + baseByIdx(si->index())->addUnit(u); + delete sc; + } else { + delete si; + delete sc; + delete sc->child(); + return false; + } + } else { + delete si->child(); + delete si; + return false; + } + delete si; + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} + +Game::~Game() +{ + for (int i = 0; i < (int)_recs.size(); ++i) { + setPlayer(i, nullptr); + auto &rec = _recs[i]; + rec.base->unsubscribe(this); + rec.base->unsubscribe(_coll); + } + + delete _coll; + delete _map; + + delete _log_sink; +} diff --git a/8303/Parfentev_Leonid/lab6/game.hpp b/8303/Parfentev_Leonid/lab6/game.hpp new file mode 100644 index 000000000..732271be2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/game.hpp @@ -0,0 +1,68 @@ +#ifndef _H_GAME_HPP +#define _H_GAME_HPP + +#include + +#include "event.hpp" +#include "map.hpp" +#include "base.hpp" +#include "player.hpp" +#include "zombie_collector.hpp" +#include "mediator.hpp" +#include "storable.hpp" + + +class ResetHandler { +public: + virtual void reset() =0; + + virtual ~ResetHandler() {} +}; + +class Game: public EventForwarder, + public Storable { + Map *_map; + Mediator *_med; + + struct BaseRecord { + Base *base; + Player *player; + }; + + std::vector _recs {}; + int _next = 0; + int _players = 0; + + ZombieCollector *_coll {new ZombieCollector {}}; + + EventForwarder *_log_sink {new EventForwarder {}}; + + ResetHandler *_reset; + +public: + Game(Map *map); + + int addBase(Base *b); + void setPlayer(int base_idx, Player *p); + + int basesCount() const; + int playersCount() const; + + Base *baseByIdx(int idx) const; + + EventForwarder *logSink() const { return _log_sink; } + + void spin(); + + void setResetHandler(ResetHandler *r); + void requestReset() const; + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; + + ~Game(); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab6/game_driver.hpp b/8303/Parfentev_Leonid/lab6/game_driver.hpp new file mode 100644 index 000000000..79f78c3fd --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/game_driver.hpp @@ -0,0 +1,123 @@ +#ifndef _H_GAME_DRIVER_HPP +#define _H_GAME_DRIVER_HPP + +#include "map.hpp" +#include "game.hpp" + + +class GameRules { +public: + virtual Map *makeMap() =0; + virtual void setup(Game *g, Map *m) =0; + virtual bool gameEnded(Game *g) =0; + virtual int winner(Game *g) =0; +}; + +template +class GameDriver: public ResetHandler { + + Game *_g = nullptr; + Game *_g_reset = nullptr; // required to reset game while running + Rules *_r {new Rules {}}; + + std::vector _loggers {}; + EventPrinter *_printer = nullptr; + + Game *init() + { + Map *m = _r->makeMap(); + Game *g = new Game {m}; + _r->setup(g, m); + return g; + } + + void setBasePrefixes(EventPrinter *p) + { + int n = _g->basesCount(); + for (int i = 0; i < n; ++i) { + std::ostringstream oss {}; + oss << "Base " << (i+1); + p->setPrefix(_g->baseByIdx(i), oss.str()); + } + } + +public: + void + addLogger(EventPrinter *l) + { + _loggers.push_back(l); + if (_g) { + _g->logSink()->subscribe(l); + setBasePrefixes(l); + } + } + + void + setPrinter(EventPrinter *pr) + { + if (_printer) { + if (_g) { + _g->unsubscribe(_printer); + } + delete _printer; + } + + _printer = pr; + if (_g && _printer) { + _g->subscribe(_printer); + setBasePrefixes(_printer); + } + } + + virtual void reset() override + { + resetFrom(init()); + } + + void resetFrom(Game *g) + { + _g_reset = g; + } + + void run() + { + for (;;) { + if (_g_reset) { + delete _g; + _g = _g_reset; + _g_reset = nullptr; + _g->setResetHandler(this); + + if (_printer) { + _g->subscribe(_printer); + setBasePrefixes(_printer); + } + for (auto *l: _loggers) { + _g->logSink()->subscribe(l); + setBasePrefixes(l); + } + } + if (_r->gameEnded(_g)) { + break; + } + _g->spin(); + } + } + + int winner() + { + return _r->winner(_g); + } + + virtual ~GameDriver() override + { + delete _g; + delete _g_reset; + delete _printer; + for (auto *l: _loggers) { + delete l; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/game_rules.hpp b/8303/Parfentev_Leonid/lab6/game_rules.hpp new file mode 100644 index 000000000..6a43579aa --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/game_rules.hpp @@ -0,0 +1,145 @@ +#ifndef _H_GAME_RULES_HPP +#define _H_GAME_RULES_HPP + +#include +#include + +#include "map.hpp" +#include "game.hpp" +#include "iostream_player.hpp" +#include "game_driver.hpp" +#include "landscape_types.hpp" + + +class BaseWithSpawnCountdown: public Base { + int _cd_max; + int _cd = 0; + +public: + BaseWithSpawnCountdown(int cd_max=0) + :_cd_max{cd_max} {} + + virtual bool + canCreateUnit(const std::string &key) const override + { + return _cd == 0 + && Base::canCreateUnit(key); + } + + virtual int + createUnit(const std::string &key, Mediator *m) override + { + int id = Base::createUnit(key, m); + if (id < 0) { + return id; + } + + _cd = _cd_max; + return id; + } + + virtual void + store(std::ostream &os) const override + { + os << "base_w_countdown " << maxUnitsCount() + << " " << destroyed() << " " << _cd_max << " " + << _cd << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) override + { + if (!Base::restore(is, tab)) { + return false; + } + + is >> _cd_max >> _cd; + return !is.fail(); + } + + virtual void + spin() override + { + if (_cd > 0) { + --_cd; + } + } +}; + +class DefaultRules: public GameRules { +protected: + static void + addBaseAndPlayer(Game *g, Map *m, + std::string name, int x, int y) + { + Base *b = new BaseWithSpawnCountdown {5}; + m->addBase(b, {x, y}); + int id = g->addBase(b); + + auto *p = new IostreamPlayer {std::move(name)}; + p->setOstream(std::cout); + p->setIstream(std::cin); + g->setPlayer(id, p); + } + +public: + virtual Map *makeMap() override + { + return new Map {10, 10}; + } + + virtual void setup(Game *g, Map *m) override + { + addBaseAndPlayer(g, m, "Player 1", 1, 1); + addBaseAndPlayer(g, m, "Player 2", 8, 8); + } + + virtual bool gameEnded(Game *g) override + { + return g->playersCount() < 2; + } + + virtual int winner(Game *g) override + { + int n = g->basesCount(); + int only = -1; + + for (int i = 0; i < n; ++i) { + if (!g->baseByIdx(i)->destroyed()) { + if (only < 0) { + only = i; + } else { + return -1; + } + } + } + + return only; + } +}; + +class FancyRules: public DefaultRules { + virtual Map *makeMap() override + { + Map *m = new Map {15, 15}; + + for (int y = 0; y < 5; ++y) { + for (int x = 0; x < 5; ++x) { + m->setLandscape(new landscapes::Forest {}, + {5+x, 5+y}); + } + } + + return m; + } + + virtual void setup(Game *g, Map *m) override + { + addBaseAndPlayer(g, m, "Player 1", 3, 3); + addBaseAndPlayer(g, m, "Player 2", 3, 11); + addBaseAndPlayer(g, m, "Player 3", 11, 11); + addBaseAndPlayer(g, m, "Player 4", 11, 3); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/iostream_player.cpp b/8303/Parfentev_Leonid/lab6/iostream_player.cpp new file mode 100644 index 000000000..b66882c5e --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/iostream_player.cpp @@ -0,0 +1,122 @@ +#include +#include +#include + +#include "game.hpp" +#include "base.hpp" +#include "iostream_player.hpp" +#include "event.hpp" +#include "logging.hpp" + + +void +IostreamPlayer::addCommand(const std::string &str, + IostreamCommand *cmd) +{ + _cmd_tab[str] = cmd; +} + +IostreamPlayer::IostreamPlayer(std::string name) + :Player{name} +{ + addCommand("move", new iostream_commands::Move {}); + addCommand("attack", new iostream_commands::Attack {}); + addCommand("use", new iostream_commands::Use {}); + addCommand("destroy", new iostream_commands::DestroyBase {}); + addCommand("create", new iostream_commands::Create {}); + addCommand("base", new iostream_commands::FindBase {}); + addCommand("units", new iostream_commands::ListUnits {}); + addCommand("describe", new iostream_commands::DescribeAt {}); + addCommand("map", new iostream_commands::PrintMap {}); + addCommand("skip", new iostream_commands::Skip {}); + addCommand("save", new iostream_commands::Save {}); + addCommand("reset", new iostream_commands::Reset {}); +} + +bool +IostreamPlayer::takeTurn(const Game *g, Mediator *m, Base *b) +{ + for (;;) { + std::string cmd_name; + (*_is) >> cmd_name; + + if (_is->fail()) { + return false; + } + + { + std::ostringstream oss {}; + oss << "User picked action: " << cmd_name; + emit(new events::UserActionEvent {this, oss.str()}); + } + + auto iter = _cmd_tab.find(cmd_name); + if (iter == _cmd_tab.end()) { + std::ostringstream oss {}; + oss << "Unknown command: \"" << cmd_name << "\""; + emit(new events::UserActionEvent {this, oss.str()}); + (*_os) << oss.str() << "\n"; + continue; + } + + if (iter->second->execute(g, this, m, b)) { + break; + } + } + + return true; +} + +int +IostreamPlayer::readInt() +{ + int x; + (*_is) >> x; + return x; +} + +Unit * +IostreamPlayer::readUnitId(Base *b) +{ + int id = readInt(); + Unit *u = b->getUnitById(id); + if (!u) { + (*_os) << "No such unit: " << id << "\n"; + } + + return u; +} + +Vec2 +IostreamPlayer::readVec2() +{ + int x, y; + (*_is) >> x >> y; + return {x, y}; +} + +std::string +IostreamPlayer::readString() +{ + std::string s; + (*_is) >> s; + return s; +} + +void +IostreamPlayer::store(std::ostream &os) const +{ + os << "iostream_player\n" << name() << "\n"; +} + +bool +IostreamPlayer::restore(std::istream &is, + RestorerTable *tab) +{ + Player::restore(is, tab); + + // We can’t serialize/deserialize IO streams + setOstream(std::cout); + setIstream(std::cin); + return true; +} diff --git a/8303/Parfentev_Leonid/lab6/iostream_player.hpp b/8303/Parfentev_Leonid/lab6/iostream_player.hpp new file mode 100644 index 000000000..95e5a8345 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/iostream_player.hpp @@ -0,0 +1,366 @@ +#ifndef _H_STDIO_PLAYER_HPP +#define _H_STDIO_PLAYER_HPP + +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "game.hpp" +#include "object_print.hpp" + +#include "event.hpp" +#include "logging.hpp" + + +class IostreamPlayer; + +class IostreamCommand { +public: + // -> whether to end turn + virtual bool execute(const Game *g, IostreamPlayer *p, + Mediator *m, Base *b) =0; + + virtual ~IostreamCommand() {} +}; + +class IostreamPlayer: public Player { + std::ostream *_os = nullptr; + std::istream *_is = nullptr; + bool _free_os, _free_is; + + std::map _cmd_tab {}; + + void + addCommand(const std::string &str, + IostreamCommand *cmd); + + +public: + IostreamPlayer(std::string name=""); + + void + setOstream(std::ostream *os) { _os = os; _free_is = true; } + void + setOstream(std::ostream &os) { _os = &os; _free_os = false; } + + std::ostream & + ostream() const { return *_os; } + + void + setIstream(std::istream *is) { _is = is; _free_is = true; } + void + setIstream(std::istream &is) { _is = &is; _free_is = false; } + + std::istream & + istream() const { return *_is; } + + virtual bool + takeTurn(const Game *g, Mediator *m, Base *b) override; + + int + readInt(); + + Unit * + readUnitId(Base *b); + + Vec2 + readVec2(); + + std::string + readString(); + + virtual void store(std::ostream &os) const override; + + virtual bool restore(std::istream &is, + RestorerTable *tab) override; +}; + +namespace iostream_commands { + + class Move: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested moving " << u << " to " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->moveUnitTo(u, to)) { + p->ostream() << "Can't move unit " << u + << " to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Attack: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u << " attacks " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->attackTo(u, to)) { + p->ostream() << "Unit " << u + << " can't attack to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Use: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u + << " uses an object"; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->useObject(u)) { + p->ostream() << "Unit " << u + << " can't use any object there\n"; + return false; + } + + return true; + } + }; + + class DestroyBase: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u + << " destroys a base"; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->destroyBase(u)) { + p->ostream() << "Unit " << u + << " cannot destroy any bases there\n"; + return false; + } + + return true; + } + }; + + class Create: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + std::string s = p->readString(); + + { + std::ostringstream oss {}; + oss << "User requested creation of unit " << s; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!b->canCreateUnit(s)) { + p->ostream() << "Can't create unit of type " + << s << "\n"; + return false; + } + + int id = b->createUnit(s, m); + if (id < 0) { + p->ostream() << "Failed to create a unit of type " + << s << "\n"; + return false; + } + + p->ostream() << "New unit of type " << s + << ": " << id << "\n"; + return true; + } + }; + + class FindBase: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *b) override + { + p->ostream() << "Base: " << b << "\n"; + return false; + } + }; + + class ListUnits: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *b) override + { + p->ostream() << "Units:"; + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + p->ostream() << "- " << iter.id() + << ": " << iter.unit() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class DescribeAt: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *) override + { + auto pos = p->readVec2(); + auto info = m->infoAt(pos); + + p->ostream() << "At " << pos << "\n"; + p->ostream() + << "- Landscape: " << info.landscape() << "\n"; + + if (auto *b = info.base()) { + p->ostream() << "- Base: " << b << "\n"; + } + + if (auto *u = info.unit()) { + p->ostream() << "- Unit: " << u << "\n"; + } + + if (auto *n = info.neutralObject()) { + p->ostream() << "- Object: " << n << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class PrintMap: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *) override + { + auto from = p->readVec2(); + auto to = p->readVec2(); + + p->ostream() << "From " << from + << " to " << to << ":\n"; + + for (int y = from.y(); y < to.y(); ++y) { + for (int x = from.x(); x < to.x(); ++x) { + if (x != from.x()) { + p->ostream() << " "; + } + displayMapInfo(p->ostream(), m->infoAt({x, y})); + } + p->ostream() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class Skip: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *) override + { + std::ostringstream oss {}; + oss << "User decided to skip the turn"; + p->emit(new events::UserActionEvent {p, oss.str()}); + + return true; + } + }; + + class Save: public IostreamCommand { + public: + virtual bool + execute(const Game *g, IostreamPlayer *p, + Mediator *, Base *) override + { + std::string fn = p->readString(); + std::ofstream of {fn}; + if (!of) { + p->ostream() << "Failed to open file\n"; + return false; + } + + g->store(of); + p->ostream() << "Save complete\n"; + + std::ostringstream oss {}; + oss << "Saved current game to " << fn; + p->emit(new events::UserActionEvent {p, oss.str()}); + + return false; + } + }; + + class Reset: public IostreamCommand { + public: + virtual bool + execute(const Game *g, IostreamPlayer *p, + Mediator *, Base *) override + { + std::ostringstream oss {}; + oss << "Requesting reset"; + p->emit(new events::UserActionEvent {p, oss.str()}); + + g->requestReset(); + return true; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/landscape.hpp b/8303/Parfentev_Leonid/lab6/landscape.hpp new file mode 100644 index 000000000..3df7e506d --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/landscape.hpp @@ -0,0 +1,29 @@ +#ifndef _H_LANDSCAPE_HPP +#define _H_LANDSCAPE_HPP + + +#include "storable.hpp" + +class Unit; + +class Landscape: public Storable { +public: + virtual void onEnter(Unit *u) =0; + virtual void onLeave(Unit *u) =0; + + virtual ~Landscape() {} +}; + +namespace landscapes { + + class Normal: public Landscape { + public: + virtual void onEnter(Unit *) override {} + virtual void onLeave(Unit *) override {} + + TRIVIALLY_STORABLE("l_normal"); + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/landscape_types.hpp b/8303/Parfentev_Leonid/lab6/landscape_types.hpp new file mode 100644 index 000000000..c53d2b99a --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/landscape_types.hpp @@ -0,0 +1,75 @@ +#ifndef _H_LANDSCAPE_TYPES_HPP +#define _H_LANDSCAPE_TYPES_HPP + +#include "landscape.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "common_policies.hpp" +#include "storable.hpp" + + +namespace landscapes { + + // Swamp: max speed is 1; attacking is forbidden + class Swamp: public Landscape { + ModifyingMovePolicy *_p; + AttackPolicy *_prev, *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _p = new ModifyingMovePolicy {u->movePolicy(), 1}; + u->setMovePolicy(_p); + + _prev = u->attackPolicy(); + _cur = new AttackForbidden {}; + u->setAttackPolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *mpc = u->findMoveContainerOf(_p)) { + mpc->setMovePolicy(_p->movePolicy()); + _p->setMovePolicy(nullptr); + delete _p; + _p = nullptr; + } + + // our policy might’ve been wrapped into something + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("l_swamp"); + }; + + class Forest: public Landscape { + DefensePolicy *_prev; + MultiplierDefensePolicy *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _prev = u->defensePolicy(); + _cur = new MultiplierDefensePolicy {_prev, 2.0}; + u->setDefensePolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *dpc = u->findDefenseContainerOf(_cur)) { + dpc->setDefensePolicy(_prev); + _cur->setDefensePolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("l_forest"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/logging.hpp b/8303/Parfentev_Leonid/lab6/logging.hpp new file mode 100644 index 000000000..72b272969 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/logging.hpp @@ -0,0 +1,46 @@ +#ifndef _H_LOGGING_HPP +#define _H_LOGGING_HPP + +#include +#include + +#include "event.hpp" +#include "event_printer.hpp" + + +namespace events { + + class UserActionEvent: public Event { + Player *_p; + std::string _s; + + public: + UserActionEvent(Player *p, std::string s) + :_p{p}, _s{std::move(s)} {} + + const std::string & + message() const { return _s; } + + Player *player() const { return _p;} + }; + +} + +class LoggingEventPrinter: public EventPrinter { +public: + using EventPrinter::EventPrinter; + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + ostream() << ee->player()->name() + << ": " << ee->message() << "\n"; + return; + } + + EventPrinter::handle(e); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/main.cpp b/8303/Parfentev_Leonid/lab6/main.cpp new file mode 100644 index 000000000..25739d5ef --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/main.cpp @@ -0,0 +1,122 @@ +#include + +#include +#include + +#include "demo.hpp" + +#include "event_printer.hpp" +#include "game_driver.hpp" +#include "game_rules.hpp" + +void +run_demos(void) +{ + std::cout << "Demo 1\n"; + demo1(); + + std::cout << "\nDemo 2\n"; + demo2(); + + std::cout << "\nDemo 3\n"; + demo3(); + + std::cout << "\nDemo 4\n"; + demo4(); + + std::cout << "\nDemo 5\n"; + demo5(); + + std::cout << "\nDemo 6\n"; + demo6(); + + std::cout << "\nDemo 7\n"; + demo7(); + + std::cout << "\nDemo 8\n"; + demo8(); + + std::cout << "\nDemo 9\n"; + demo9(); +} + +int +run_game(int argc, char **argv) +{ + std::vector loggers {}; + bool have_stdout = false; + + const char *load_fn = nullptr; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-log")) { + char *fn = argv[++i]; + if (!strcmp(fn, "-")) { + loggers.push_back(new LoggingEventPrinter {std::cout}); + have_stdout = true; + } else { + auto *of = new std::ofstream {fn}; + if (!*of) { + std::cerr << "Failed to open file: " << fn << "\n"; + return 1; + } + loggers.push_back(new LoggingEventPrinter {of}); + } + } else if (!strcmp(argv[i], "-load")) { + load_fn = argv[++i]; + } else { + std::cerr << "Unknown option: " << argv[i] << "\n"; + return 1; + } + } + + GameDriver drv {}; + + for (auto *logger: loggers) { + drv.addLogger(logger); + } + + if (!have_stdout) { + drv.setPrinter(new EventPrinter {std::cout}); + } + + if (load_fn) { + std::ifstream f {load_fn}; + if (!f) { + std::cerr << "Failed to open save file: " + << load_fn << "\n"; + return 1; + } + auto *tab = RestorerTable::defaultTable(); + Storable *s = tab->restore(f); + delete tab; + + if (auto *lg = dynamic_cast(s)) { + + drv.resetFrom(lg); + + } else { + std::cerr << "Invalid save file contents\n"; + delete s; + return 1; + } + } else { + drv.reset(); + } + + drv.run(); + + return 0; +} + +int +main(int argc, char **argv) +{ + if (argc == 2 + && !strcmp(argv[1], "-demo")) { + run_demos(); + return 0; + } + + return run_game(argc, argv); +} diff --git a/8303/Parfentev_Leonid/lab6/map.cpp b/8303/Parfentev_Leonid/lab6/map.cpp new file mode 100644 index 000000000..66eddff6e --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/map.cpp @@ -0,0 +1,222 @@ +#include + +#include "point.hpp" +#include "unit.hpp" +#include "placeable.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "landscape.hpp" +#include "map.hpp" +#include "storable.hpp" +#include "common_storables.hpp" + + +Map::Map(int w, int h) + :_rm{w, h} +{ + for (auto rmiter = _rm.iterAt({0, 0}); + rmiter.y() < _rm.height(); + rmiter.advance(1)) { + rmiter.cell().setLandscape(new landscapes::Normal {}); + } +} + +MapIter +Map::addUnit(Unit *u, Vec2 pt) +{ + if (u->hasPosition()) + return MapIter::makeNull(); + + if (_units_max >= 0 + && _units_count == _units_max) + return MapIter::makeNull(); + + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.unit()) + return MapIter::makeNull(); + + cell.setUnit(u); + u->setPosition(pt); + + ++_units_count; + + cell.landscape()->onEnter(u); + + return MapIter{rmiter}; +} + +Unit * +Map::removeUnitAt(Vec2 at) +{ + RectMapIter rmiter = _rm.iterAt(at); + Cell &cell = rmiter.cell(); + Unit *u = dynamic_cast(cell.unit()); + + if (u) { + --_units_count; + cell.landscape()->onLeave(u); + if (auto *n = dynamic_cast(cell.object())) { + n->onLeave(u); + } + + cell.setUnit(nullptr); + u->unsetPosition(); + } + + return u; +} + +MapIter +Map::addPlaceable(Placeable *p, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.object()) { + return MapIter::makeNull(); + } + + cell.setObject(p); + p->setPosition(pt); + + return MapIter{rmiter}; +} + +MapIter +Map::addBase(Base *b, Vec2 pt) +{ + return addPlaceable(b, pt); +} + +MapIter +Map::addNeutralObject(NeutralObject *n, Vec2 pt) +{ + return addPlaceable(n, pt); +} + +void +Map::setLandscape(Landscape *l, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + cell.setLandscape(l); +} + +void +Map::store(std::ostream &os) const +{ + os << "map " << width() << " " << height() << "\n"; + os << _units_max << "\n"; + + for (int y = 0; y < height(); ++y) { + for (int x = 0; x < width(); ++x) { + auto info = infoAt({x, y}); + + const auto *l = info.landscape(); + if (!dynamic_cast(l)) { + StorableWithCoords::storeWithCoords({x, y}, l, os); + } else if (const auto *n = info.neutralObject()) { + StorableWithCoords::storeWithCoords({x, y}, n, os); + } + + // bases and units are restored in Game::restore + } + } + + os << "end\n"; +} + +bool +Map::restore(std::istream &is, + RestorerTable *tab) +{ + is >> _units_max; + if (is.fail()) { + return false; + } + + for (;;) { + Storable *s = tab->restore(is); + + if (auto *sc = + dynamic_cast(s)) { + if (auto *l = + dynamic_cast(sc->child())) { + setLandscape(l, sc->coords()); + delete sc; + } else if (auto *n = + dynamic_cast(sc->child())) { + addNeutralObject(n, sc->coords()); + delete sc; + } else { + delete sc->child(); + delete sc; + return false; + } + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} + +MapInfo +Map::infoAt(Vec2 pt) const +{ + return MapInfo{&_rm.at(pt)}; +} + +Unit * +MapIter::unit() const +{ + return _it.cell().unit(); +} + +Base * +MapIter::base() const +{ + return dynamic_cast(_it.cell().object()); +} + +NeutralObject * +MapIter::neutralObject() const +{ + return dynamic_cast(_it.cell().object()); +} + +Landscape * +MapIter::landscape() const +{ + return _it.cell().landscape(); +} + +const Landscape * +MapInfo::landscape() const +{ + return _cell->landscape(); +} + +const Unit * +MapInfo::unit() const +{ + return _cell->unit(); +} + +const Base * +MapInfo::base() const +{ + return dynamic_cast(_cell->object()); +} + +const NeutralObject * +MapInfo::neutralObject() const +{ + return dynamic_cast(_cell->object()); +} diff --git a/8303/Parfentev_Leonid/lab6/map.hpp b/8303/Parfentev_Leonid/lab6/map.hpp new file mode 100644 index 000000000..d8831d7cf --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/map.hpp @@ -0,0 +1,126 @@ +#ifndef _H_MAP_HPP +#define _H_MAP_HPP + +#include + +#include "rectmap.hpp" +#include "storable.hpp" + +// Map interface doesn’t know about cells -- instead, it only cares +// about certain kinds of Placeables + + +class Map; + +class Unit; +class Base; +class NeutralObject; + +class MapIter { + RectMapIter _it; + + friend class Map; + + MapIter(RectMapIter r) + :_it{r} {} + +public: + static MapIter makeNull() + { + return MapIter{RectMapIter::makeNull()}; + } + + bool operator==(const MapIter &o) const { return _it == o._it; } + bool operator!=(const MapIter &o) const { return _it != o._it; } + + int x() const { return _it.x(); } + int y() const { return _it.y(); } + Vec2 point() const { return _it.point(); } + + bool null() const { return _it.null(); } + bool valid() const { return _it.valid(); } + + void shift(Vec2 dxy) { _it.moveTo(point().shifted(dxy)); } + MapIter shifted(Vec2 dxy) const + { + return MapIter{_it.otherAt(point().shifted(dxy))}; + } + + void moveTo(Vec2 xy) { _it.moveTo(xy); } + MapIter otherAt(Vec2 xy) const + { + return MapIter{_it.otherAt(xy)}; + } + + void advance(int d) { _it.advance(d); } + MapIter advanced(int d) const + { + return MapIter{_it.advanced(d)}; + } + + Unit *unit() const; + Base *base() const; + NeutralObject *neutralObject() const; + Landscape *landscape() const; +}; + +class MapInfo { + const Cell *_cell; + +public: + MapInfo(const Cell *c) + :_cell{c} {} + + const Landscape *landscape() const; + const Unit *unit() const; + const Base *base() const; + const NeutralObject *neutralObject() const; +}; + +class Map: public Storable { + RectMap _rm; + int _units_count = 0; + int _units_max = -1; + + MapIter addPlaceable(Placeable *p, Vec2 pt); + +public: + Map(int w, int h); + + int width() const { return _rm.width(); } + int height() const { return _rm.height(); } + MapIter iterAt(Vec2 pt) { return MapIter{_rm.iterAt(pt)}; } + MapIter iterAt(int x, int y) { return iterAt({x, y}); } + + MapIter begin() { return iterAt(0, 0); } + MapIter end() { return iterAt(0, height()); } + + MapInfo infoAt(Vec2 pt) const; + + MapIter addUnit(Unit *u, Vec2 pt); + Unit *removeUnitAt(Vec2 at); + Unit *removeUnitAt(MapIter iter) + { + return removeUnitAt(iter.point()); + } + + MapIter addBase(Base *b, Vec2 pt); + MapIter addNeutralObject(NeutralObject *n, Vec2 pt); + void setLandscape(Landscape *l, Vec2 pt); + + int maxUnitsCount() const { return _units_max; } + bool setMaxUnitsCount(int x) + { + if (_units_count > x) + return false; + _units_max = x; + return true; + } + int unitsCount() const { return _units_count; } + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/mediator.cpp b/8303/Parfentev_Leonid/lab6/mediator.cpp new file mode 100644 index 000000000..d7e6e9ff1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/mediator.cpp @@ -0,0 +1,127 @@ +#include "point.hpp" +#include "map.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "neutral_object.hpp" +#include "base.hpp" +#include "mediator.hpp" + + +MapInfo +Mediator::infoAt(Vec2 pt) +{ + return _map->infoAt(pt); +} + +Vec2 +Mediator::mapSize() +{ + return {_map->width(), + _map->height()}; +} + +bool +Mediator::moveUnitTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit() + || !u->canMove(ito)) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitMoved {u, from}); + return true; +} + +bool +Mediator::attackTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (!ito.unit() + || !u->canAttackTo(ito)) { + return false; + } + + auto pos = u->actualPosition(ito); + Unit *t = pos.unit(); + + u->emit(new events::UnitAttacked {u, pos.point(), t}); + + if (t) { + t->emit(new events::UnitWasAttacked {u, t}); + + auto damage_pair = u->baseAttack(pos); + auto damage_spec = t->actualDamage(damage_pair.first, + damage_pair.second); + int dmg = damage_spec.evaluate(); + + t->takeDamage(dmg); + } + + return true; +} + +bool +Mediator::useObject(Unit *u) +{ + auto iter = _map->iterAt(u->position()); + + NeutralObject *n = iter.neutralObject(); + + if (!n + || !n->canUse(u)) { + return false; + } + + n->onUse(u, this); + + u->emit(new events::UnitUsedObject {u, n}); + + return true; +} + +bool +Mediator::destroyBase(Unit *u) +{ + auto iter = _map->iterAt(u->position()); + + Base *b = iter.base(); + + if (!b + || !b->becomeDestroyedBy(u)) { + return false; + } + + return true; +} + +bool +Mediator::spawnUnit(Unit *u, Vec2 at) +{ + return !_map->addUnit(u, at).null(); +} + +bool +Mediator::teleportUnit(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit()) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitTeleported {u, from}); + return true; +} diff --git a/8303/Parfentev_Leonid/lab6/mediator.hpp b/8303/Parfentev_Leonid/lab6/mediator.hpp new file mode 100644 index 000000000..31a0d545a --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/mediator.hpp @@ -0,0 +1,32 @@ +#ifndef _H_MEDIATOR_HPP +#define _H_MEDIATOR_HPP + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" + + +class Mediator { + Map *_map; + +public: + Mediator(Map *map) + :_map{map} {} + + MapInfo infoAt(Vec2 pt); + + Vec2 mapSize(); + + bool moveUnitTo(Unit *u, Vec2 to); + + bool attackTo(Unit *u, Vec2 to); + + bool useObject(Unit *u); + bool destroyBase(Unit *u); + + bool spawnUnit(Unit *u, Vec2 at); + bool teleportUnit(Unit *u, Vec2 to); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab6/melee_units.hpp b/8303/Parfentev_Leonid/lab6/melee_units.hpp new file mode 100644 index 000000000..6409e96cb --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/melee_units.hpp @@ -0,0 +1,111 @@ +#ifndef _H_MELEE_UNITS_HPP +#define _H_MELEE_UNITS_HPP + +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class MeleeAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + +public: + explicit MeleeAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0) + :_kind{kind}, _base_damage{base_dmg} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return to.unit() != nullptr + && to.point().adjacent(u->position()); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter) + { + return std::make_pair( + _kind, + int(_base_damage * u->relativeHealth())); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_melee " << static_cast(_kind) + << " " << _base_damage << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicMeleeUnit: public Unit { +public: + BasicMeleeUnit(int speed, + AttackKind attack_kind, + int base_dmg, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new MeleeAttack {attack_kind, base_dmg}, + def, base_health} {} +}; + +namespace units { + class Swordsman: public BasicMeleeUnit { + public: + Swordsman() :BasicMeleeUnit{ + 2, + AttackKind::sword, 40, + DefenseLevelDeco::good_defense_deco( + AttackKind::spear, + DefenseLevelDeco::vulnerability_deco( + AttackKind::cavalry, + new BasicDefense {})), + 100} {} + + UNIT_STORABLE_NAME("u_swordsman"); + }; + + class Spearsman: public BasicMeleeUnit { + public: + Spearsman() :BasicMeleeUnit{ + 2, + AttackKind::spear, 75, + DefenseLevelDeco::good_defense_deco( + AttackKind::cavalry, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + + UNIT_STORABLE_NAME("u_spearsman"); + }; + + class Cavalry: public BasicMeleeUnit { + public: + Cavalry() :BasicMeleeUnit{ + 3, + AttackKind::cavalry, 50, + DefenseLevelDeco::good_defense_deco( + AttackKind::sword, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + + UNIT_STORABLE_NAME("u_cavalry"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/neutral_object.hpp b/8303/Parfentev_Leonid/lab6/neutral_object.hpp new file mode 100644 index 000000000..95e11d9c7 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/neutral_object.hpp @@ -0,0 +1,24 @@ +#ifndef _H_NEUTRAL_OBJECT_HPP +#define _H_NEUTRAL_OBJECT_HPP + +#include "placeable.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "mediator.hpp" +#include "storable.hpp" + + +class NeutralObject: public Placeable, + public Storable { +public: + virtual bool canUse(const Unit *) { return true; } + virtual void onUse(Unit *u, Mediator *m) =0; + + // It’s the object’s job to determine whether it was used by the + // leaving unit. + virtual void onLeave(Unit *) {}; + + virtual ~NeutralObject() {}; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/neutral_object_types.hpp b/8303/Parfentev_Leonid/lab6/neutral_object_types.hpp new file mode 100644 index 000000000..e6fc373c1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/neutral_object_types.hpp @@ -0,0 +1,255 @@ +#ifndef _H_NEUTRAL_OBJECT_TYPES_HPP +#define _H_NEUTRAL_OBJECT_TYPES_HPP + +#include + +#include "neutral_object.hpp" +#include "mediator.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "ranged_units.hpp" +#include "common_policies.hpp" + +#include "storable.hpp" +#include "common_storables.hpp" + + +class ExtendedShootingRange: public NestedAttack { + double _delta; + +public: + explicit ExtendedShootingRange(AttackPolicy *p=nullptr, + double delta=0) + :NestedAttack{p}, _delta{delta} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = to.point().distance(u->position()); + auto *a = dynamic_cast(attackPolicy()); + return dist >= a->minRange() + && dist <= (a->maxRange() + _delta); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + return attackPolicy()->baseAttack(u, to); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_extended " << _delta << "\n"; + attackPolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _delta; + return !is.fail() && NestedAttack::restore(is, tab); + } +}; + + + +namespace objects { + + class HealingWell: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *) override + { + u->heal(25); + } + + TRIVIALLY_STORABLE("n_healingwell"); + }; + + class Tower: public NeutralObject { + AttackPolicy *_prev; + ExtendedShootingRange *_cur = nullptr; + + public: + virtual bool + canUse(const Unit *u) override + { + return dynamic_cast(u); + } + + virtual void + onUse(Unit *u, Mediator *) override + { + _prev = u->attackPolicy(); + _cur = new ExtendedShootingRange {_prev, 5}; + u->setAttackPolicy(_cur); + } + + virtual void + onLeave(Unit *u) override + { + if (_cur == nullptr) { + return; + } + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + _cur->setAttackPolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("n_tower"); + }; + + class TunnelsEntrance: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *m) override + { + static const int w = 5; + + Vec2 at = u->position(); + + int max_n = 0; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() == nullptr) { + ++max_n; + } + } + } + + std::uniform_int_distribution<> distr {0, max_n-1}; + int n = distr(global_random); + + Vec2 dest; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() != nullptr) { + continue; + } + if (!--n) { + dest = iter; + break; + } + } + } + + m->teleportUnit(u, dest); + } + + TRIVIALLY_STORABLE("n_tunnelentrance"); + }; + + class WeaponSmiths: public NeutralObject { + public: + class UnitFilter: public Storable { + public: + virtual bool + applicable(const Unit *u) =0; + }; + + template + class SimpleUnitFilter: public UnitFilter{ + public: + virtual bool + applicable(const Unit *u) override + { + return dynamic_cast(u); + } + }; + + class MeleeUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_melee"); + }; + + class RangedUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_ranged"); + }; + + class CatapultUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_catapult"); + }; + + private: + double _mul; + UnitFilter *_filter; + + public: + explicit WeaponSmiths(double mul=0, UnitFilter *filter=nullptr) + :_mul{mul}, _filter{filter} {} + + virtual bool + canUse(const Unit *u) override + { + if (_filter + && !_filter->applicable(u)) { + return false; + } + + for (const AttackPolicyContainer *apc = u; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (dynamic_cast(apc)) { + return false; + } + } + + return true; + } + + virtual void + onUse(Unit *u, Mediator *) override + { + auto *prev = u->attackPolicy(); + auto *new_p = new MultiplierAttackPolicy {prev, _mul}; + u->setAttackPolicy(new_p); + } + + virtual void + store(std::ostream &os) const override + { + os << "n_weaponsmiths " << _mul << "\n"; + if (_filter) { + _filter->store(os); + } else { + os << "end\n"; + } + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + + if (auto *uf = dynamic_cast(s)) { + _filter = uf; + } else if (dynamic_cast(s)) { + _filter = nullptr; + delete s; + } else { + delete s; + return false; + } + + return true; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/object_print.cpp b/8303/Parfentev_Leonid/lab6/object_print.cpp new file mode 100644 index 000000000..da59e1054 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/object_print.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "object_print.hpp" + + +#define UNIT_ENTRY(T) {std::type_index{typeid(units::T)}, #T} +static const std::map unit_names { + UNIT_ENTRY(Swordsman), + UNIT_ENTRY(Spearsman), + UNIT_ENTRY(Cavalry), + UNIT_ENTRY(Archer), + UNIT_ENTRY(Slinger), + UNIT_ENTRY(Onager), + UNIT_ENTRY(BoltThrower), +}; +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T) {std::type_index{typeid(landscapes::T)}, #T} +static const std::map landscape_names { + LANDSCAPE_ENTRY(Normal), + LANDSCAPE_ENTRY(Swamp), + LANDSCAPE_ENTRY(Forest), +}; +#undef LANDSCAPE_ENTRY + +#define N_OBJECT_ENTRY(T, n) {std::type_index{typeid(objects::T)}, n} +static const std::map objects_names { + N_OBJECT_ENTRY(HealingWell, "Healing Well"), + N_OBJECT_ENTRY(Tower, "Tower"), + N_OBJECT_ENTRY(TunnelsEntrance, "Tunnels Entrance"), + N_OBJECT_ENTRY(WeaponSmiths, "Weapon Smiths"), +}; +#undef N_OBJECT_ENTRY + + +std::ostream & +operator<<(std::ostream &os, Vec2 pt) +{ + return os << "{" << pt.x() << "," << pt.y() << "}"; +} + +std::ostream & +operator<<(std::ostream &os, const Unit *u) +{ + return os << "a " + << unit_names.at(std::type_index{typeid(*u)}) + << " with " << u->health() << " HP at " + << u->position(); +} + +std::ostream & +operator<<(std::ostream &os, const Landscape *l) +{ + return os << landscape_names.at(std::type_index{typeid(*l)}); +} + +std::ostream & +operator<<(std::ostream &os, const Base *b) +{ + os << "a Base with " << b->unitsCount(); + int m = b->maxUnitsCount(); + if (m >= 0) { + os << "/" << m; + } + + return os << " units at " << b->position(); +} + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n) +{ + return os << "a " + << objects_names.at(std::type_index{typeid(*n)}) + << " at " << n->position(); +} + + + +static const std::map class_chars { +#define UNIT_ENTRY(T, x) {std::type_index{typeid(units::T)}, x} + UNIT_ENTRY(Swordsman, 'S'), + UNIT_ENTRY(Spearsman, 'P'), + UNIT_ENTRY(Cavalry, 'C'), + UNIT_ENTRY(Archer, 'A'), + UNIT_ENTRY(Slinger, 's'), + UNIT_ENTRY(Onager, 'O'), + UNIT_ENTRY(BoltThrower, 'B'), +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T, x) \ + {std::type_index{typeid(landscapes::T)}, x} + LANDSCAPE_ENTRY(Normal, '.'), + LANDSCAPE_ENTRY(Swamp, '='), + LANDSCAPE_ENTRY(Forest, '*'), +#undef LANDSCAPE_ENTRY +}; + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info) +{ + if (const Unit *u = info.unit()) { + os << class_chars.at(std::type_index{typeid(*u)}); + } else if (info.base()) { + os << "+"; + } else if (info.neutralObject()) { + os << '#'; + } else { + auto *l = info.landscape(); + os << class_chars.at(std::type_index{typeid(*l)}); + } + + return os; +} + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1) +{ + for (int y = y0; y < y1; ++y) { + for (int x = x0; x < x1; ++x) { + displayMapInfo(os, map->infoAt({x, y})); + } + os << "\n"; + } + return os; +} + +std::ostream & +operator<<(std::ostream &os, const Map *map) +{ + return displayMap(os, map, 0, 0, map->width(), map->height()); +} diff --git a/8303/Parfentev_Leonid/lab6/object_print.hpp b/8303/Parfentev_Leonid/lab6/object_print.hpp new file mode 100644 index 000000000..8df10d966 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/object_print.hpp @@ -0,0 +1,40 @@ +#ifndef _H_OBJECT_PRINT_HPP +#define _H_OBJECT_PRINT_HPP + +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "neutral_object.hpp" + +std::ostream & +operator<<(std::ostream &os, Vec2 pt); + +std::ostream & +operator<<(std::ostream &os, const Unit *u); + +std::ostream & +operator<<(std::ostream &os, const Landscape *l); + +std::ostream & +operator<<(std::ostream &os, const Base *b); + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n); + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info); + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1); + +std::ostream & +operator<<(std::ostream &os, const Map *map); + +#endif diff --git a/8303/Parfentev_Leonid/lab6/object_w_health.hpp b/8303/Parfentev_Leonid/lab6/object_w_health.hpp new file mode 100644 index 000000000..e9c925221 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/object_w_health.hpp @@ -0,0 +1,43 @@ +#ifndef _H_OBJECT_W_HEALTH_HPP +#define _H_OBJECT_W_HEALTH_HPP + +#include "storable.hpp" + + +class ObjectWithHealth: public Storable { + int _health, _base_health; + +public: + ObjectWithHealth(int base_health) + :_health{base_health}, + _base_health{base_health} {} + + int + health() const { return _health; } + int + baseHealth() const { return _base_health; } + double + relativeHealth() const { return _health / (double)_base_health; } + bool + alive() const { return health() > 0; } + + virtual void + heal(int hp) { _health += hp; } + + virtual void + takeDamage(int dmg) { _health -= dmg; } + + virtual void store(std::ostream &os) const override + { + os << health(); + } + + virtual bool restore(std::istream &is, RestorerTable *) override + { + is >> _health; + return !is.fail(); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab6/pathfinder.cpp b/8303/Parfentev_Leonid/lab6/pathfinder.cpp new file mode 100644 index 000000000..865f34de2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/pathfinder.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "map.hpp" +#include "point.hpp" +#include "pathfinder.hpp" + + +PathFinder::Pt2 +PathFinder::Pt2::toDirection(int dir) const +{ + // assert(dir >= 0 && dir < 8); + + int d1 = (dir + 1) % 8; + int dx = (d1 % 4 == 3) ? 0 : (d1 < 4) ? 1 : -1, + dy = (dir % 4 == 0) ? 0 : (dir < 4) ? 1 : -1; + + return Pt2{pt.shifted({dx, dy}), depth + 1}; +} + +bool +PathFinder::run() +{ + Vec2 start_pt = _start.point(); + + while (!_frontier.empty()) { + Pt2 current = _frontier.front(); + _frontier.pop(); + + if (start_pt.shifted(current.pt) == _end) + return true; + + if (_max >= 0 + && current.depth >= _max) + continue; + + Vec2WithCmp cur_v2wc {current.pt}; + auto iter = _dirs.find(cur_v2wc); + if (iter == _dirs.end()) + _dirs[cur_v2wc] = -1; + + for (int i = 0; i < 8; ++i) { + Pt2 shifted_delta = current.toDirection(i); + + Vec2WithCmp sh_v2wc {shifted_delta.pt}; + auto iter = _dirs.find(sh_v2wc); + if (iter != _dirs.end()) + continue; + + MapIter shifted = _start.shifted(shifted_delta.pt); + if (!shifted.valid() + || shifted.unit()) + continue; + + _frontier.push(shifted_delta); + } + } + + return false; +} diff --git a/8303/Parfentev_Leonid/lab6/pathfinder.hpp b/8303/Parfentev_Leonid/lab6/pathfinder.hpp new file mode 100644 index 000000000..d4c614aae --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/pathfinder.hpp @@ -0,0 +1,56 @@ +#ifndef _H_PATHFINDER_HPP +#define _H_PATHFINDER_HPP + +#include +#include + +#include "point.hpp" +#include "map.hpp" + + +class PathFinder { + MapIter _start; + Vec2 _end; + int _max; + + struct Vec2WithCmp { + Vec2 v; + + bool operator<(Vec2WithCmp o) const + { + if (v.y() == o.v.y()) + return v.x() < o.v.x(); + return v.y() < o.v.y(); + } + }; + + struct Pt2 { + Vec2 pt; + int depth; + + static Pt2 zero() { return {Vec2{0, 0}, 0}; } + + Pt2(Vec2 pt, int depth=0) + :pt{pt}, depth{depth} {} + + Pt2 toDirection(int dir) const; + + bool pt_equal(Vec2 pt2) + { + return pt == pt2; + } + }; + + std::map _dirs {}; + std::queue _frontier {{Pt2::zero()}}; + +public: + PathFinder(MapIter from, MapIter to, int max_steps) + :_start{from}, + _end{to.point()}, + _max{max_steps} {} + + bool run(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/placeable.hpp b/8303/Parfentev_Leonid/lab6/placeable.hpp new file mode 100644 index 000000000..bd7f0ef74 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/placeable.hpp @@ -0,0 +1,36 @@ +#ifndef _H_PLACEABLE_HPP +#define _H_PLACEABLE_HPP + +#include "point.hpp" + +class Placeable { + bool _placed = false; + Vec2 _pos; + +public: + bool + hasPosition() const { return _placed; } + + const Vec2 & + position() const + { + return _pos; + } + + void + setPosition(const Vec2 &pos) + { + _pos = pos; + _placed = true; + } + + void + unsetPosition() + { + _placed = false; + } + + virtual ~Placeable() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/player.hpp b/8303/Parfentev_Leonid/lab6/player.hpp new file mode 100644 index 000000000..58c15829d --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/player.hpp @@ -0,0 +1,44 @@ +#ifndef _H_PLAYER_HPP +#define _H_PLAYER_HPP + +#include +#include + +#include "mediator.hpp" +#include "base.hpp" +#include "event.hpp" +#include "storable.hpp" + + +class Game; + +class Player: public EventEmitter, + public Storable { + std::string _name; + +public: + explicit Player(std::string name="") + :_name{std::move(name)} {} + + const std::string &name() const { return _name; } + + virtual bool takeTurn(const Game *g, Mediator *m, Base *b) =0; + + virtual void + store(std::ostream &os) const + { + os << name() << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + std::ws(is); + std::getline(is, _name); + return !is.fail(); + } + + virtual ~Player() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/point.cpp b/8303/Parfentev_Leonid/lab6/point.cpp new file mode 100644 index 000000000..bce7b3325 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/point.cpp @@ -0,0 +1,29 @@ +#include + +#include "point.hpp" + +double +Vec2::length() const +{ + return sqrt(_x*_x + _y*_y); +} + +double +Vec2::distance(const Vec2 &pt) const +{ + return delta(pt).length(); +} + +bool +Vec2::unit() const +{ + return (_x || _y) + && abs(_x) <= 1 + && abs(_y) <= 1; +} + +bool +Vec2::adjacent(const Vec2 &pt) const +{ + return delta(pt).unit(); +} diff --git a/8303/Parfentev_Leonid/lab6/point.hpp b/8303/Parfentev_Leonid/lab6/point.hpp new file mode 100644 index 000000000..8eec01d76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/point.hpp @@ -0,0 +1,40 @@ +#ifndef _H_POINT_HPP +#define _H_POINT_HPP + +class Vec2 { + int _x, _y; + +public: + Vec2() :Vec2{0, 0} {} + Vec2(int x, int y) :_x{x}, _y{y} {} + + int x() const { return _x; } + int y() const { return _y; } + + bool operator==(const Vec2 &pt) const + { + return _x == pt._x && _y == pt._y; + } + bool operator!=(const Vec2 &pt) const + { + return !(*this == pt); + } + + Vec2 delta(const Vec2 &o) const + { + return Vec2{_x - o._x, _y - o._y}; + } + + double length() const; + double distance(const Vec2 &pt) const; + + bool unit() const; + bool adjacent(const Vec2 &pt) const; + + Vec2 shifted(const Vec2 &dxy) const + { + return Vec2{_x + dxy._x, _y + dxy._y}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/ranged_units.hpp b/8303/Parfentev_Leonid/lab6/ranged_units.hpp new file mode 100644 index 000000000..2a41e3ecb --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/ranged_units.hpp @@ -0,0 +1,118 @@ +#ifndef _H_RANGED_UNITS_HPP +#define _H_RANGED_UNITS_HPP + +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class RangedAttack: public AttackPolicy { +protected: + AttackKind _kind; + int _base_damage; + double _min_distance, _max_distance; + double _dist_pow; + + static double + distance(const Unit *u, MapIter to) + { + return to.point().distance(u->position()); + } + +public: + double minRange() const { return _min_distance; } + double maxRange() const { return _max_distance; } + + explicit RangedAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0, + double min_dist=0, + double max_dist=0, + double dist_pow=0) + :_kind{kind}, + _base_damage{base_dmg}, + _min_distance{min_dist}, + _max_distance{max_dist}, + _dist_pow{dist_pow} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return dist >= _min_distance + && dist <= _max_distance; + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return std::make_pair( + _kind, + int(_base_damage + * u->relativeHealth() + / pow(dist, _dist_pow))); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_ranged " << static_cast(_kind) + << " " << _base_damage << " " << _min_distance + << " " << _max_distance << " " + << _dist_pow << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage >> _min_distance + >> _max_distance >> _dist_pow; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicRangedUnit: public Unit { +public: + BasicRangedUnit(int speed, + AttackKind attack_kind, + int base_dmg, + double max_dist, + double dist_pow, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new RangedAttack {attack_kind, base_dmg, + 1., max_dist, dist_pow}, + def, base_health} {} +}; + +namespace units { + class Archer: public BasicRangedUnit { + public: + Archer() :BasicRangedUnit{ + 2, + AttackKind::arrow, 50, 5., .20, + new BasicDefense {0.9}, + 40} {} + + UNIT_STORABLE_NAME("u_archer"); + }; + + class Slinger: public BasicRangedUnit { + public: + Slinger() :BasicRangedUnit{ + 2, + AttackKind::stone, 60, 3., .30, + new BasicDefense {.09}, + 50} {} + + UNIT_STORABLE_NAME("u_slinger"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/rectmap.cpp b/8303/Parfentev_Leonid/lab6/rectmap.cpp new file mode 100644 index 000000000..b82667c40 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/rectmap.cpp @@ -0,0 +1,55 @@ +#include "rectmap.hpp" + +#include "unit.hpp" + +Cell::~Cell() +{ + delete _u; + delete _obj; + delete _l; +} + +bool +RectMapIter::valid() const +{ + return x() >= 0 + && x() < _map->width() + && y() >= 0 + && y() < _map->height(); +} + +Cell & +RectMapIter::cell() const +{ + return _map->at(point()); +} + +void +RectMapIter::moveTo(Vec2 xy) +{ + _pt = xy; +} + +RectMapIter +RectMapIter::otherAt(Vec2 xy) const +{ + RectMapIter other = *this; + other.moveTo(xy); + return other; +} + +void +RectMapIter::advance(int d) +{ + int nx = x() + d, + w = _map->width(); + _pt = Vec2{nx % w, y() + nx / w}; +} + +RectMapIter +RectMapIter::advanced(int d) const +{ + RectMapIter other = *this; + other.advance(d); + return other; +} diff --git a/8303/Parfentev_Leonid/lab6/rectmap.hpp b/8303/Parfentev_Leonid/lab6/rectmap.hpp new file mode 100644 index 000000000..a3119ffb1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/rectmap.hpp @@ -0,0 +1,99 @@ +#ifndef _H_RECTMAP_HPP +#define _H_RECTMAP_HPP + +#include "point.hpp" +#include "placeable.hpp" +#include "landscape.hpp" + + +class Unit; + +class Cell { + Landscape *_l = nullptr; + Unit *_u = nullptr; + Placeable *_obj = nullptr; + +public: + Cell() {} + + ~Cell(); + + Landscape *landscape() const { return _l; } + void setLandscape(Landscape *l) + { + delete _l; + _l = l; + } + + Unit *unit() const { return _u; } + void setUnit(Unit *u) { _u = u; } + + Placeable *object() const { return _obj; } + void setObject(Placeable *p) { _obj = p; } +}; + +class RectMap; + +class RectMapIter { + RectMap *_map; + Vec2 _pt; + +public: + RectMapIter(RectMap *map, Vec2 pt) + :_map{map}, _pt{pt} {} + RectMapIter(RectMap *map, int x, int y) + :_map{map}, _pt{x, y} {} + + static RectMapIter makeNull() { return {nullptr, {0, 0}}; } + + bool operator==(const RectMapIter &o) const + { + return _map == o._map + && _pt == o._pt; + } + bool operator!=(const RectMapIter &o) const + { + return !(*this == o); + } + + int x() const { return _pt.x(); } + int y() const { return _pt.y(); } + Vec2 point() const { return _pt; } + + Cell &cell() const; + + bool null() const { return _map == nullptr; } + bool valid() const; + + void moveTo(Vec2 xy); + RectMapIter otherAt(Vec2 xy) const; + + void advance(int d); + RectMapIter advanced(int d) const; +}; + +class RectMap { + const int _w, _h; + Cell * const _storage; + +public: + RectMap(int w, int h) + :_w{w}, _h{h}, _storage{new Cell [w * h]} {} + + int width() const { return _w; } + int height() const { return _h; } + + Cell &at(Vec2 pt) { return _storage[pt.x() + pt.y()*_w]; } + const Cell &at(Vec2 pt) const + { + return _storage[pt.x() + pt.y()*_w]; + } + RectMapIter iterAt(Vec2 pt) { return RectMapIter{this, pt}; } + + ~RectMap() + { + delete[] _storage; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/report.pdf b/8303/Parfentev_Leonid/lab6/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..05a62b6c9a53be49e145b192df0a84bb25be21f9 GIT binary patch literal 107708 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

    +YCPP)CRWKfJ*ys>1`q6$S~N~KC?wWvs0 z%~MY5bjHu~*?a|uQtdo|(%_ZPOHmf-_lR|#lIL!J?cW|QG;hi{E8FR3Z~pLL!IjqD zar2?~)MeWaOnvC~P@G<@d+^pnvE^g*`O$bV9v|=fxSNiw+R*cQ!Ln=3+qQEO9T|jv zUJU*0Cerzmmc^utjC*-4a8?zBryL%Ls5R7aoKh7dZk55P0y3Gtly^|7Qrpz12CK$S zQAe$~)WrlWY!OA;!DqEtr>(pd8eeXB7!pM$2}qFUV*A=`Nx_|nSd)S=5mqM!Lsuk) z&CJekI>T{pqaIt%Xu@_i>f9=9P$W*kLw;IrPu7}li6fF1Jm=UU-W5y7S08%Jm1!?D zY+vNct>*9k)1f6--gRR#7>jX@2k7&IIS-99-e#8Le-3Q}ybW!WEUJj*cX+vc`Fl+7 zw)16qZ=gnM}MN%K&P$+hTUMIK(D<{x^K%|NP z3hRRu>jZ<#O3)(DO;>SBDr*S)!YY|iwYp2x-ml`6!ZJyStP-1>7_Wqe#obPe#3?<( zm#?Vw)m$G}biVWOOM4Ej)yCEw-FeS5GqnG0W&GW7fBBxm;$y9R;h8lzXA_4u(+Gcv z-gxqUr9W42UJmZyrX@O2-bV{_O$Wld8|c8j{Bg2@OJi?#DKrYLu1D9aXy-es+WYxC zJa^>pXyqU5xUh-4!+J;b$fk$PiajfCS$@k3e)WpZ&dy#wwPK}arHR+BFmLj%5H~4) zVE$eQm#$>;Dy_b*GEu#L0~;|BxY>|co2tk|pdz85gat@K0q|h#UcI`@Y&0@-6+`uL zS)$g_)rB3}747MD^z`&vtu^iK@fzhCHr$$8YIW303yez5cW9R_mI$^1S*d=SFMdab2ns;V@Mp9ru`0(e>TDPLFbK(xcP0imvk- z-|5kus9K;Th&Tjp8__HiB9D4@@+gwN@GouTTwo{BRa-yD@i?=xhG7X?sZujDql^>c z;axnnfc|t#c3G6k1@4c-_gRLQ+ z&J5mqpyNaD=+;7GbRYRp$AOQnXo$rdY=&g(dwq)j)3>!LW3+p_@bs}I`WTK8&+J9+ z7yL=Uco`fcCZ$%X;})qpRNP7(+gyD4tRhN{#sEWKo))23-~>Tqb9!7ZPdvluV2-My z>|wb^?Wwovg@DLlx+TyX-~s_+HilgP+Uq>*p!u+w%bTAva~I5)S!mqMvC~F5vqW;iJbr2K1hd_bdAU(z z3$q7zZ&^Ll#OK6DsP~8{7t5H#0*Uq9znY*7eWfAL&Y}vWF%qx z6{HS;W!gJGhglY$%2}MGbb6%1Xs@7T%8c$vWl|{b3Wt(Hpet-h3f8WOD=FCE*_p7X zS15akH<3tjOLvA@kf$&Q%DK(Ng#=7>NNF(2d@sz)ZW2f;)O;c?omiqwsQh*d9}w`w z5?f&5i6wX-6J=8R`nmZ{RuHbY%EyAw6Lpw^Zl&+%8kW)fmRI>0CDOKbQDKTpcNR8n z%DSpn(uX0Q`sg=TH$%2;zVjh@vSc4US*Tx^r?FV;Ui$n^i6A}&S_R=kc^z?*<@6b8 zcGFhT+m%Q4Kd5@K>eVV`XVpimzEQ<%tGrc7@SI*}g|o@o>+ALRwGFf#X?xwKGuph) z5$DZLg~gI4q{>)j=7T)P^O`SFKe2OPIJ;y?b62hsA<0b{-35?Tl_)RIW@~k2e*e;? zOH}U4P`EOTkQ9!l5t7mfNofg^(udO=J7jAh&5x(2)6b^)W9j4RQ|YJDigbFp%T7Ld7?4`^-PR^DhIWl8>YgwMQD-ccsMD?|S>|d;V(pM7+iSC(vF?>1Po- z^O`t)HA-)4xTRq{|0=I&%1(i=s`uKz*F6$+_6s ztXxV+b5j$eo~#TGnxZ^)@H}0#NF#>X#Ix>%>XeG>Qw^xbRlHQyZ@ znk^_zqGnyP7H#4uRV7mMY>^uc$>u85&Jt)U0U;*>AQPUK=)9}CGnU_MOB^or4(>hWTV#L%F)@QD zfRKt!tvImK)Rwurr7_$)eJFlAQM*P&5|;2<_(|M z=w?Q}`h`M!`_;%oI((XGcFZd))ibL06MQZ3V+j@q53IW}S`~pgZe1)3Gbj#16pUOFnB%yU#OqM$2ip(TPGK5z< z-OhwF=UnMr=TKERsoxoM0PE&-#0jr*=$%Qzdr2c{bn?{|out#*TcId-Ih|2b?jWR` z+8qvy)umeJ@EDk;M>RXzo=_LD^NE1bWMb=yK<{vrGJ1!w-4`adwS#fdW`jYs&TPtS z4Rxk2n;%cev%^hUm%H5QpbERoZj0ubZqu(21!&kDgz%sd7yuk+)*bF-c5}?QF@o2e zr)TmGBD&lbt53Dg9W}yR?&^FvByIRZf=CitLEuMuBa<@{Mj2gTw)E(lyg_6}l&aYR zM@7<$n89}iaZ0n#!=^W%mmTIjAxGdYsv@_Pn#VOVqgLd{%!0`;l*#;*MccC+gk$_?)QA17OD$r$0?nFyglS026x^B{J-{muz7>QS`*_;uC(eL86b&jXF&M zT>`ZnZbAa;^e9b83|2~Fuq4~eF?x;ZDnhMqz&G496=*$xpndU@Lfe`}g)c`6=NwH- z3ZIhD%CcW`pOIj2Tds^vLR{hs#}%&p0B?(!L_Q-;rk`u*Aj08TU+qBcky@UTnAhh^ z0ThX1NT*S&)4)R;O`HjL_?VC$F-1K{(0E+9!`sAq&B9E3W;`vP0T0i1N~(HViHtdc|sxMVbCJ!C~?M=6~^V`<_gbdCnQL*xKuB-yBJ zl4N25AsV54B$O&uE3c^ZmAEREs)EJQh{3hK_&K#B8Kt;l(?P29)sMMsIht5Hj^mB< zAz57flQJ_D^xzD)wq+bo=_v4l0D#+qwBHX0q=I%l56@_b?NN;*h`7h)hZSYS}_Nl#we~>~( z9M}4BWhTNo-QAVqD%U2`7QTOyN{~`{mIL)}!yb-iyqms zJV@eFp{m(&GJuT1GO+6~|4G#c?)nK7Km=A6@SzGj=A?OMmduIq^#VII&E; zcK*|)WVubvZDUNe);Sg1l&hz=$tqwjOkH~D21ri-bNI}JW4&Y>rk3BVN)}zZm+DBw zH7gk*tylx2hn8#VF7IBbuP_nK_n7}Pz?P`fL;xW{9`H)2QFE{7wK|&Bbls{l>UjU_58W*a!2PyKk3#E-xA-UN3Zk_=kkJ9 zo1~lKsEt=H>ZrGXY>!yhJN3dP|2A^g6^iTn4Cyu5uKG9$=v`f zZ%~9kOnTer^5AlAQ`oi!Alm# zia_PvHPS|r5k1YvRTI(qXhP!SuX??V8I%fz*lNY9ZUK>|pa-Rpi8%OrPfYflU?8;m zlRa}x_Ur;XyZ}&9@qS$K;CnhQ=Jnp10R{h@J5U?C^X$ZtxgT<(a5`E)cra^4a>Ks`|_najUT~5RQ{Eq5qM^ThyS{l>Uy5ce-ZpF z9_wvg*KRv&a9CtrW#&o&1VV5vN^^Onr5vkOxMUETu~fcN$D5)FMZY`3GhxJO*I=Nh z7Xs;`R~B^V63#o&&=7r~BYnf5e4r!1>PlE5LxB0~W?99F9$p=6nTbTW2@wxz4 z5Aa{9k%DQcWvaJS3Xi^)gD|^>VbtZ!5gkHwrqLmrJ!IKmpRW)veb6%rLPD-ThUWW8 zrnl6C1zuh0Wmedax%cpUo_ykaCAj)s!XW%3@2P@L%hz-p5B~NxX@fg_hv99eGt0Pt zdc>PWo+TY9@D02mr3Cy5#=l#7LYg$|T@@c}+g^01VHRqFm_8 zARe`9!lYa*qkVO^M^L7Z*y8EUpEH&s6JzC@8V?Z@BB*)Ps6}*Zn!AQsq6m53uvbYW z8JtFhX?ob=+F;|LH1|&~hS4FDG7(Eee59$PLnYBRJMzCFk_EU)^Tp!*P$Uf)8VQmq z{^Njt13&$59g4#HO;j0p!WiR-N@JQXwtviHixh1kAibC$t;yw8*^S3i#U!Fh=hqEP z&cz5bWzIzn)b7iC4!c(^jDM!6XY4*YQ}!Phhc5hN^IhFu=P#$~Y7F`>fNYGYH^bY; zssgcFDuWPzn?lru%p$m&d`tf2Dyed1`!-q$ZhW0}s0a~>VqtU?z~iR*mmH+6jX)k1 zBkfp_=4=xJb;8GY)yT~9O#4U?Hl&3M+mN>f^JT6ays@tC;Iu`nS-ff93{|`ZgK0s& zSht2L3kUDrG*{BW=Su~Q1}k}tT>1k7UItC{11?2-rusdN6&1$mnzD1oGbMz6TD&Bc zc5V8S!|8h7;<9vM9W^xdyOPsYIHcs` zu|z@HnIF7c+TCay;@TZDP;>`zxO1CVr%VsMbv(j+{%!`^ffG(`*UzNQrRap-Q@RWF zNBoI6G$*MqT{?iJ3f~gffp^}$O1>c#{;E_~`XAo>Wr0$_+M08AVQnMMjJ0&>R^?k( zYIeR*R?sgrN*j)3YV2fyCrlAfgqq;p6Fpj1MFEex#bHHeq<4%ru1YelDnq2wQS%^m z-1Im4@GmA!hhb^tYxJZ{jGC2rV#C4bN<|SAOSD&_(j02E3QT-1E_=-$y2jys9~_@c z^q>QT2(d6!Bf6wkbvso7x87egYivs|(m3hDT3TCan2*|e7V5K35*UhF>XEm9!k$>6 ze_Hh`7fJY*A>67=m8_dolQ;fM=s+oQ0Wbah22pa(5~u}`OSOzG-TIt41ku#qO&5Bw z^e{-&v2H^%MlD=J>9N~|{^A|7u`)E{npHRg#Dr)B*5Il`>E{(aV1o-H#lZ! z`kFB}trtCd&P%WY6|KU$c8XU z6$WDh4W{)--LrZJDEOVpxLo6$Y;)g5-vU9B9iGyd_*lG%?oFT3>n&#qx;VlG?}VC!=f5jJCk z(en)vK<>w)(dO{F=@Enf4|UXd>KBFQtjq0AnfQayBCpSFmvg1fTL|J`h;oulcL8lC zMcRYt)BOF~E12d4NZ0%C(+u>tCj55gyC6tV`!z4TZsk6Xz=1AZ7NWX|dBBoo;LNC~ zEjqeHrpC!Kk#bJ#+Fz^S=d&BrBFq^hVA_Ane_{4@W9%wtTOTmoNY|P7TyJ1b{3S<% zE2PHwEB))@zjF)l9q!%QK9=^KHaS0+X+j>m-^ZO1F>*(E+lHnKe9o@fo0gvcGO3lW$gjgi8Y+UD*t%M8A-=J|cc)6(r`};1<-M;TVrPrV z0{mT&j5J0)8SBO@a;%*LzZE{K0l979qgVxIsr!Xf<6l_2|ZUmg)#L0yTHb)f0jL62T zwE4B^Q3T|_&r($x4)66~?+5Z^jRdRsOriN4y0GhLI;7lB!aHR!$T|%b8c$wUYZWWi zTlP*Nkn)f~94X2&f0SHa{R%WLl3DP>u6DGm|LhWvA>{r9hUeS0XxZS89S59L z=xbTpR8v*&$PwCCyYt_EZlC`4X(0S;(kJK^5pMXuz3~QoIepxX;3^wC3HA9Q$Bl(8 z)z45b_xpb7a$^8PRqykW`U@%jQm9A1*31 zDP?s8q;M9iZtov~5(kQ7$uFhC7CQ@e8q?&|~U+Y^~MBlT(0fJ}?_UX?^1bj2vKk zZk%m4p-fREZ@nExs5eb<(f9gg(d#Zs!oufzTCoGhCfsiCGSEhhX(l(iN=0v*@UAgm z)eErgDAo~NYITaHoo`WPI5?17O|FWH^F=dWwD5{Q>^KwM!jwWG60t6BDq%5dIZ^q6 zghXL0?CT}{yU|tIOJD{cdOcy(XLoY4Yt!An8RvUp5j2+Z+vDLEu-8&ad)sEatP9zu zEyp-P70IJ&Ns3E%)KaPOyWQw^6ZLgyuf)E`53&z=!8NE0jsf1Do(p-)-4S=dErb{BhNm_u zha_Mh^7x$O7c&5c<}ng zqRKq>3#S`|m5OmraL)K-VPM7v9e`!U9Ks^X!ITA;Ul~~-lT34hhV029MDq?H$vx5d zfgUu-$^CntVgDiSpMDVg&{k#TroC9}*QamRz5W(DVf+!_G4y5im_UayrO?Vg!(sOu zX_B;f>^sr2`%Mf8J&mgV?WX)0?W^VUtSq37Z|6cY&EQ6}p`oVS&d;Zre_Ma` zC-;TW&60->zwi3XeKnyAew*TS}HL`v|lLXu#)#O3QT6S?^&;Fw8rsWttWNpYcd^{BpxEG`-A z3Yz;r5_O*%JoS{37j$P$QN9(6Q{-zoLCeyT|jYOrpqry}cx~@#sRW7{YHi>OzuLtXk$$C&9JFtgplRX)G zKlR~0nP()K@TjCw>7XT&hjF9b-tmy7F|+9Tv=BT{k<3?xBMAr4)b2|D#t!C7tq5(w-~FIv4tE*dMG!yx zw<8$Ikbb%KZ@Wk0Q$P}xVY=*s?$Z00<2nmMdEJKiij8}~J>U`Z5_N+P9^Tm+-|!yj zo>xoLhaR|ia3_zlz=|JFTZzZ!hhnYx|2sNy86dv+!pJa#I4FqF zK8ad#_L3$ObTHTuUEAAAuk>*WX-PJ0X$;9O8hGk-U-%`ZiV~1&b~bcyNGCt9A*8sN z%?NjB{?48<9P{V95Ng44KmU&0%J#5C+D#LAL+K>`J*K#-$qiVv^fa0D8rKi9LKNEI z|KkGj$-D0$79JwwXw&fhDwGX3q_f~bSzkh@0Wp8c{GGaGvaPv8d}RWW%U*&!D*S>& zKe1;@m}-JoC=#5In{kOY_(>5ejjXdrG*|$}s^W53kfA+X7nAYJwBm;)%Mp4$dVHTe z+PWRGi#^68b$gTOLY?M`^hhwKzXM-cc8Bd_!<$wN^{X-TkopzQ)=05~)Z|i&aNhCx z-taWKf<5+0>>1HwuT7u<=OMi-5AZv^z=6dP#WeC|IgEH zGl3WkZ?Z zIM)7U(n3575p@7Xa!{Yqu;>*oCvhI(s6Kuu*NOooJ4fDNuT_2Zh48PX3(!p(@c@qjL$B}@#O6|=@#7QuqKJsY>2m+ zA*ll>AE(y7FS~P?F7#}XZWh-ZB!?fwUjN+$Zh1_Zm^uQpGY3Dek`0#Wj&k1W@UpVy9|p!K#wb^udZ z*E;pw`;x=^M#0a+tnn$GS(Gz)&l^o{2#~dS>O%*Ke$;`q8Y=H8!N1|?x9xy1;UQK4 zqw9po`!Avk9jOb%7$woT=8G?gzXkZT5J9R9;gpLyf!*;s=uX9XH^i$he9iuD^T_oo?ii^F;YQ1o+*tVBhyN~ZSkGfC)f)@!V|=xk6s;H~?;3=b7$mr;do)9sER zvVrVfx`0`z@*&|MEnRZk&`VD*i(EA#$SP5y8x2^U;Qt%l*wi}#male^zNOM^} z9kz9oSGd|`r>iB{WaV&k^wrcA;P}@@VCG_HX)Twr#m7fqjoqrZu~yb)<>TgS2=J_s zVZ-4KZ&QRLMDiJncztT}=!8U!zs^RH9J9w-C%(bL733P&LDqU%ZZXdB>S%5^cXahl zUt?~!7+Z_aCTg{@T_tER*_|W%RQ|0^l6Gol%g;4IJ!nk2E+*xLkQ9H9Ni>HOpi%gj za*Y{oXSNbF1a;`3mtS-U^#H9}(xPvTztO}KaCB$vg*zu>pH!u`sDr1^=epQP=f6k|kRvzfJ<0e+ishc!+fE+yOavTA~u2bTul=kfR` zX7%KwkNqg=G@IJ0PMj1`Yq_m9cemf=TTwNLk;HVm1z>zTRQ!ll9`#0i#-P;LV{wf&|u+UFOS&C*BC7C zCXkHoa6sD0*VFiQdO$N@jU$E&u}a%3|YZei>Crjf#g*0E;8<;yHgojU`U&p zsQrmbXiHAZ+jqqJ)zb&cGCf$1kyYR9CdDN>`0t*a{>u#o*l zbGx4-$|O&#g*^}&Z=vt%(vqhN1#5TtMgO=w>EVzlX^5H$DyQk19)z-eN`wqbC+2{0 zGJ33;%XNca4fFf0bF_WhfuJVqE*FHGu{>*cfG650EywHNJRr+?;EfaKQ}a~r04Ug7 z6!qUPms1W-WD2fyBo~z_4cOcL3MZ!>_+okkYXim`O&@ctbqH%`2*(WRUJ-fr=R><5 z1YY6avctkQy<=u;HQBX4OA>~bkBt?XN^Qc!TvFGiB`x@sGL8xlCmuqsB&^L;;A3KG zi4ji^)X|3ERR1_QoJS5+Ip3tKgN6nl!gNGxz$qKqHW4M|%@mA=M5)Lz@&?#^|V$zSQxzSNxS?RFH| zg=PGM<6oni+Y6ieiaqrDpr}`xU!ELLd^Sf`^5hSyX7kXk8thDVvsznhMt0bZPcM3J zW;r4+ue5f0?8PkrGEUgttBbIUYLu~J9E zlZ~md;aP2jurgSLU2i%G<@9Do^{gwGpA2JfX)ll@g6=YCYqjg8qutQ%BCcz>I$ju7 z6M~K-)?knP+H!YJ#0e7YKz_@{OlqSMpq*TeAnV}%W&Q#FLH<#4M7%dRxc%+x#wZhg zUGZM~O?|F!Q&-#3R`aq^JN^w*Lx*W zGlJ62!7y%x*u4vS{z3}RxZkMvg!WCQh}709G6{W@vX5I6NdIQwVn76pauhIPKr*vu zkfj73eEOcQ@Q6N;rcN1<1b4@neE0>JXxc*?24Tj4Y6--RxTE7bz5@TDEZ^K{e1F_4 zubRuJRx~FxMl|;|TbF#xcYFnPhZY%bVr38%c)y=+G;=O6Na z_YAIPme?&X1cVCMwK-v7!Wgz!S#ka2-C z53xfrfYzpu+~uDNA|8LiE#^kpKx|>Po9v;@HNFae$zM(GPVGt!-ran@cSWIoz218i z{#xDn?FGObfZqv1zfKD>?CyqOXoP%?9la$SL^%rokT(7rMZSjo61Q9Y2#&vgzWcrH zq+aFkQCfr+l&S@ulC)bp@`c`n&u!1SdNkz%JC=U!-WR`>LiPiK`*a=(JM43z#p@I7 z`LAEh6@^lEVB`a6o40D_oZC>q`FK?2T%=@FCJ2MsB8X;)rCgx2OSAGrZ{-_%kQ`eA zQ3vklZc^rM=zh?283TQA4tK#%*6(baF;OX4oni+}S6EDitQTsx+K;lUe+9d{-gLHru z25vK=%!3UMe8%nJC$fVh{vixTBSxh~-rz0#lNQqX;XqOwZ!!!25uXNcW?9|5pw_fK zZm!otwrlstg=9mN2X5GhcqZ+_*yA=&aZoL(x5g3Hg5A*V<%Qq5?9uMe!ktei&cZdA zN3u69s_jqaJq6(caWV+1&G1wA4lNj%EB2G`RYDuD_lBPCd1r?8yA9+b-^4mvgsLjK z)QLjVJE9LfJK3&+$M_)NJrU~00IH5pLHAVOzF5kr1*V!jI#wUghr`JD|CUfDyYfgs z&d}Ga`(iP0WM99SY*Tg-Ui(++G}QR~_vy%)ZxS&BD5+{Xi!hlMgb2(WV*^;JO z=V&K@e5K>c1KZC6UInp-%L(R%UL`VYgKNid?AdD#QW?ZGFf_ra6@JH#5sU|DM)IFx z@ck=TUjb~(FRir@e68?XPL6y>eu8{`getOyDOdq8wE!dEx||=dQOJF5--s~}tebZI zUWkE~z)c8yyqsjd5eJ~|uApPYRibt$N6G=}x66G8(7vEgkgsjPHlX}*6vra5T{jdd zTg}9(#S!96m9z)5j&R@--_vfuhRxxErFX&XOD6r^GMyP1xE9YGinIr}uCTjz*|ThU zZPQw=6^EFU6B?QHbhtWN@0{^|BV^Te2brZ8hvkRC^h$>!)x z&V|J@kMwMQJ@Z8U4u*AxcA$%8@eBIyhi%bU<8JCnExHNZ&y2#GdF`TvaZv%h>o@o6 zH{3lL5E6j>h1>s2XH~|0C&<6}g^>1>zxSOm@LevYIkXUv{Iu^gW<@#rTl}>%X0foj zU;R2=f;gxlD6#Oij0h-U6uHu4J?HKJ+>`T0>o#F!R`18P$fEtnjQic*SPpBo6W{kC z*GWEXc3M=1v+~B*tN!POyAVqEjoe~9bsjyLykb}1D00=VrL*S9v*|}wER7u&H_y(5 z6+57e1zl{U+S;|(92Oqmb7geBws|J1_1c^cKgzKt>mHx;4EeyaHgf9UdHU%(+wss7 zuTL`1N=8%AD#Zp&%iWoDuNL`5vA4f6k?D;GXk-#jDZBJ#?3dL%)|;HjNRMCc;GD1@ z>_O^+dX2sz$%@diiqQDNDDtjYdN+$C*x)jWo+TL0R@E`uz308kwoHj%UDxie1ae~BK|9m<4+3)?yhcc4ZRQR8h5U6*9D&Yv z8CD|LZ<2D25-0ZZ%K3{SO?W35I|1~181Y}9hy`~(3(ayd(9|54w}03CbD*bAI)N>1 zea8}2_B;e@iuTHEygfpcx@~Q5B1N<cE)LA5ceEWgHD`KHC zG_~g6t(7vW=#n=yVdBa7lMfS>vESfL;xHsK!WV&CE;7Si?|o?G3X=XCr?!|@LkpFq z>*gpwl4FE(cNKlXo0~;D4QwBNjp8R!NQv4z{gh->Zq1^z&V^(5GMDPmg&kUrE~MQK z=Vhmg>ss_Eesrhb=|Q=Tc=>k!JeZVR%;bI3(?YvX$^Ya#za;7eZH&I(F>Vw(2shVt z{q9ENZc-0le8w&>9c{ZJnvzsH)b_joOlnpS!Ud=3lwyWGW*(lEW73~i##Ri%jYkFr zHbN*VNvLkznM(3w|EIxjvaH%d-l>>S0JSpjr|4RSP76a#w1NdP9=c6-F%k{ar33+6 zjQ-!@yWw@HyXOGUMiizkX0&w{L~?t0@O^5l5F;_$feDU)Jl zq2G9I4hqKd>9C6!17=?l5L1x((Q$lGOs9WL^r>4 zJ*98uWottJZe77m`!(OOJ+LSZa}JgS3qoTTD zS#4@{?Wed0B|A2b&NJDElC)TIOuwFPjmx-ah7T+YoR|i2gJl~99*giBY(%(1(`lU5 zSn$KLeTNR-+e$=LBW3!^rVDmA;hstvshr80jM3naY8rJT@&%aY*yF!C;Gu{Jv}!q0 z*i|tnpsYb1fmkqNAb!BCOc;0R!&>)QR4o4ve9^@RC1(+v6`j6@K!m;AB=}evi28_~ z11)<)1-pM&N+K3a&N(LtlRn5)tb2xR6tdE5WrMt|^f4X=GWT@<3}ZUJg3s(}&H+qi z4DHRBy8o$*r(-9R$AE9rQNUopQeiyi^^|KoyV`4#i?<7wIWp!GZ4Pj)0cm1PEB1!} zK-NM%S@*9iDnGT-Pbwk9*_mvnm^v!dw_0>UZdKQPRM`4)j+Y~Zy z5kmY&3Aqu@yQiLu;`_AKW+)i^4f4X-o1=S$`Ps9#9A^=dCaqP@L=O@I<8Ia?;>K0b z?oMN-M`_uvXT%?*TF!W5EHb60%z&z?2SoJHZN^s<{pJ7)QhfFcw8IEv1!HD!>f-8b zW@Pt2M-Il;FzlQ}Oho^4z{kfZVP)%T=FIqyljdqBW@h4GYQ`vMW^dtYNyN+|AOQ2f z13fpYe-nTpfm9yh*#95-xc*PR{}s$k#Kz6S#QFcp&cemU&i;Q-kkbw6i)Qx0_0xMh z{ldYjW8t%FJ5^=}QvxTJNP>cviUa~Ih9*271&-^PN7B$AviMn&G{-_#7K}=Kc;;i$UuA z8jCRpwC~Ls53xdRvmw^j^+NG91NH<(KA9~e7_<7$XdLoS*)98Et_1-)Og0h*06`w4 zPA_shK)%zmNSMSUlgJc>=w`rq?0Zwf*2BceTrW)F(|uT9R?pqK-b*EvUC^o3Zz$G_ zd^ZYpqPA0C5sLXaHC`t6nFt9Ln%IaXloC=Jf27P~*PPmnqXxY1{IJuEKN)er57?*H zjkjmIn`wNgqdBoIzU2Cfktn;MpdEH|IAv;1nT;oMsaql{hh)0BC60WKD zPv6uAdHIMM@*?#!RZGccmCiPo4K30gt_^d27wXu(AZE5I$%zk5qiQA4B2Z3 zMe9QyMgxd6>g9Ir3#>4-fP;%o&werZftd7S^2EF51)ear9(e<)L&S52Q|?rK@q3|n zgKh_H?g{T$-)M<)5V68Lg1%Wl)oyyaIRXOw@%dK6wuEq<`sL_(VDH{3K2UZTgH+m~ z`{a|!Mhw+=)2Gx<0Mm>UeK`SyVSnIe?Sw4)c@soWhyKGX#+^ravS@#if5LRd`i3nS zHl)V@!tlo%Jl%7-KW2(4zi(?#SOW@ZF(p`+dSR75k$>@h$?Toswy`(4+0+&26WGoD zRr!I(?pwagG4B`R)sAFHVQvM_dt~7W?G+O2XH8v$biofB8~ogXeH9E;1mdoocs@cT z1F$jCX0B=`^>BK)+xy?_Y?b+{`Qy!DfZ|aU(Y)b;EU`hi4E6-T&-tS9gJ-OREjG8jn_@kG-h5= z+$M>_8PA$gu;xy)Pt%yQHYL309Iv72jK-Vru8mwR!;~1Nay;WwUQwRB&Rli%-70o0 zY+-lEI=7wZPxhpLlGRP!P6tG%R=}OCMW6KJ)rGwFVQ&YigZlA-yL@5!h4+Hw1R&NM z)MOPZqWJ;8Q?wDh{z;**}GZHcswFb4#uSkd{h{+t zjI=W_7UaT7f<1u#0wgh(K_>%Qgy0fio_6`82f@56}( zqyA+?)0;$)xJ*X+V=9h}_QV{4D}S6JVT7}XdK~fK@!mB4xo^Gss!kD@+LM}$?-n=^ zXondDdu#=rB;mgy_si1X9o9L*`X^FLt{y4`O5kV7-iQb{CV~a!Q5FUq7?{vH-Z=Hk zXXr+1jtZF#@(^xEcm08R-Pb#Vf6!5lvtVV+L;93~5}1$SWGbG6 z@Xhgy^NXu@EKdi@G9%(AD@c?SmXHIPg=11G$kHw$qAvJ#PYkpm&ie|iBY&J5dDB0E z?M{OeJ)E~aVPQepP5BJMA5{ezZVh3xQeUY|D$?IKs@AL$#R{$)3+W^M8_@qeuwai3Y|4fLJPk zue&gxMY5;{HQu?$S#n90&NHn9&w_ew04qO;xKdw$O?)DKfNlWcSkv{J;OKe~XTg4_ z2ZeV+=plaf!EHmZ>=8V1dB^aHtFr<-!+K@v3NuI$79nP8U_s|cyfjU%lb`9LNubta z=jnoKJ`A|E9q$H}k0imC1fV-o&-wjSfBC#qe!dKT6GU|ER^r9?DsvR0un`cNS*X({QaQOlPnM|n!q;Xp?O1NQ+gow{<0hU}ZPr$GRju@F59 z)k@W=FloeCr4peM?r&Yi_}jK@Y}Tq-t)i?2CZp2$_}l(xI9?ar+dl~JY`>KjGL9!< z<+;@8v#AiX!#RBf%jGIR$ixA!*%qN+{GOAc6My=?WB*k5b!pTlq+HS-cLHAo0qO1$ z?zf-81#sTnC7cm>jfm@!^2`Qq+Ra&Ii_SE)oTF_NEDNhaw%Gev?nBr*3_FLdNy4oQ zC2jzvT)2nNV$IWs&la$rnUj#3>&|Vn7(GO?>IMnf@GQq=TZEar7th_X)~S~XSMWcK;YgicTmgM(B`&AEq&LG4U@Z-CDZ)eTj&57%|@B*>V*4n$y9`yfngxN?FjLaeZ(nF<; z0NIwW{c{=1x`j(uahWrcEjGcJ$CIpJxAz8O$cZIC620T-Z1&Qhp_`&u-qLUiCxo$x z9VP)P?D}c(ZbXqHdZ*CRoS_%fX}&yYb!Ro=sP+mTm=x2+K7+5fg|wkqcdIVep4fK@ z35JGr@%^!shdn}I7WxT1Pg9x#tQ>Oxr z5$LWEBk($Qg%}$sfJg}0*Crvn{sm7n8ut*4!Gj3dN_Ss~egTuloUY&7kLX zzh=XVoqgVPyHtaOZY&>+7`NM==|;wXx8|gGb*WJw!32MCDwJ;q{`Ho0$0|hDtKh+Y z#$vo5B+t|yjP4o_*t#Of6zvfCOZ_IFp7h}4GUAdOP|IE2tQ4_vp;0QTZA`}n8>@?L zBozJsL0|c>Zg!xkWE(AxVv>RqJ`bA?Zf*(PHR$>WBz#~Me+%|;Mc&e&amjqAI#ehe z4*Rz|%npv?47b=k;2gG*!W$+wzo4X@84G)wQ#ihwgGP;sp5hF779RCB$wJd6i~rjq zZnJncBzvv={A9{n7KQQC-4|AR7e>LVYw}(jj9d!ffc3i&*89D}RcC#|ccdI~Xs zxc&FPM+!_Cpa4$$;?gh1T}M%Nn2!CO8r5+R#$0I5v#bqvax@_9gP<<7PLO6R*S1Ly zAik|j2=f97>=wv1ZWyNfM3}g(0@Y{3Z*xyfa9LWo1se1}>yu2wl}huyHCRy>_p4mV z7A}&PW6#U^Gx*hn|K7!AAHCim^o-RBFY7avYQAaOnw z+MdSyoae+--i->rQgw6W(22N$XTMAGdSDLECaWWs;zfSpvB8X#qU@ABwweLU$X zzU7sGXBeL+G%L%yLw_39?an|SLPDTeH9UA83w1_aO+!8K#_UKLJ6QjW1Rx%ji+M#R z!czF>{7SR2kXab^Www{vzkE@Vz~d~qw3PT7+D(*2Z8uSBb&=a<0(DyUYBSKDz|EG4 z#(R7w8N-GhhluR@Gwb?@xrCHa##^v2Wp|2jukRTB%iUHqw7XpzKFt=<%T_kiJ1DCn zpYk=x;RD~Ex_fM1zZP-l#F9~{F0UFga?l1W+JRX!$RbYlLwvq&*yX5d=v<@T;WS4n zpD`K{Gd^$%|NT-+pBXP#x z+va`FIe095&}0c`;EIomuA^;uC_r)H;2sKr;lONjkDx z@@+HNZbxU0OWv}~*0Rj8q8e9)qeE`F=_!x9OC=TMQw2tyfljuGRFToR2u}Buh(XO? z+f1KrUmf2ZhtL)`!J)1ZS8I(OGNziTXP5Rjo*m)9-lpNd=KmU)(j-{rgck;CxE^I5 zaac|K^L!y&4?GOrK=2Y;Df{ei?M}T5-Ji}@9qGP=1$}$dswETh&Ta)%-)zJ);Z`L9 zXVv9|3Jk}+DZD_$*Pel~R1(JXwkul()y$Yu)s4>e%6fXx?)lN&mFn$$NFW~5j9)vK zuHN}jK-3S$d*Xslj=_=NfQ23U!4QG+#aF3&E_bS351?P8Ax%g6n4<=VR9Os$~?U@$0xBv=`mtn3{TA+WLUyH_R#y6GnAxW zv`g${m^;N**6}PC9V!&5NA`F5Z_ftyvnxulA^Ake;jM92H&p|cs9$(kW_OC8bs6hj za(fUk5vF8PMlaj95lT)za6&s{L0xvu;&1dNcs+)KHs$Zv*W))pnazL=*yoGt>oLi~ zl}n=~M1@P2jX5<|(K=I!ODFY*iPZ-aYZ{BfGpXb|4y$Tbr*RKEP7n652TIZ~<5rA% zjRufi%6xZ_c*ZW-w2aN-+$<(5Z1T7+%Um$y^PaOY(*b_d0mo~}S{^$8aVX8{xDvNr z4wf0wI@2FTP8NT?+-v~huBQ^M_tPZ=$AnZy9cF`)gK6sI$?UEM zD=fQvp&@V32J8AN8_-M@llJ*mVeU~vQcG9JcCVB{i~Sf+LHYS%k-Ot2&J9_;FaSLB zwQ{UJW)T1tz#cGj^BCmJ<`T(9KgQWaj#8t{Pb_fTfR%2KQl~N*)L@jsvvLHn5RIRE z+42&s3Lcucjz$I)+FnIfqDFw;{ zi-8G%*k|-x?zrSe_MZZIFdWUicC59mkQBKwMNM#Afh!Llv<^v0`kG{^(aoWH z>3Fv7#R}jWLiow|K1>n~?gda@QA44xI{6i(US!@Hgbx|IMDaJqPJmFvdJ0t+_cL^3 zscgPto3N0hr6=wb-wv;Q)Q;xwM==ZkN{qkT6ns3{vlf>iNld6!vw|0S=BDD# zV5w9H4fz=1{N@=E4~Y7yI>v8GcjyjBr7um9H^FX9v!%cbnjB;X+T_#Z6;i)A(4k%i zpyxI^niwMe#qSR?N{cs4_O;kZ)&xrZeNHmLHHd-=Qc`Y99*$E)`w$gt{LVPec;gAG zVj{VQ9=MA)>o0W3yvKOpmf@wX>g5`uYmw6r$CY?|u$Y9rquP-wsEu-atCKAr>&t~S=rh2o2xhl@o=SOn*xNrB2x$GQ0 z>g!dRs@4(LZ?u5P;Mxtg0&NT}+;AMr5=j{zM!1lP3%8%c*TlxFeSj+M+n-lG_)Zob zoxlT*XiJP9BXjxUtuy$`MxRcexKwwEctbqf5}m$ClKyhHP15mT3npzB7>+T=ts1xB1Q zYFvt1);7d%70p>xkUFLM+_w8$^?yi>)2E!%y#dp4rFqB{yi21$ol8Ct7TIe#NU|5R z-G1^e3rfnI7p?3^4xi4u{Jz(7wtKVTj|`g;+}emcS^+!Bw7a%YQH5IrdYiRhvZ83G0ymQJq{3O+e((UxlSz7mq7G)P)T$vd<|F)*u{?IXG8gsi7JXub;OXez`NgA$*I0Gq-|8W$WEfD~R++W{Vd#3RcGJH4*DQ3E!xrY&a4pCMHlYIq#4; zD%?8g;Dy5!f?9)O%EdE#Icc)JX<1#<%#LgA`t^S)OLh0Fl*+2(ILGW6I=Fv4Cl2rN zKW3oggy%LIKQ_Q{5y`uyWv6veKv<(u;@(Q)RbFt(_}- zY}$uiLuOpIDEHCt0dIfGf5HN+5D>(sd9`31IcxNVc)FQj_#?VF+vm0;`KzC~zIh8n7SutCyV8yL5{j&TmjYzHj3 z$qmox#JWm_((v_mytIG|5SUbrk2`eeZ0<0Ce;h}BYl@J*L(l|gp^getnH8uZQxK=_ zs%$u7wy=swRY<~Sg;m1gY*fM)R*(>q(Q*TP;85G%S&M2rv8`a*L>>Ou#;ORSGp0Tyv>lDRNovZG@_WV0j9IxXY=P$m0 z^&2zE`l>B6fA`tcGEU$erpumNICkZ^l*e#a+q9MSRThn%D`}Xt^EVUkk>fb;Q9c9v z)q?`)Ey)17-cP&%g~#W%0bmbr^>|*inXEz&?vMnd@kOgY5ST+4u%sCh@c9As&oK8h20=7l$|$>G)ak>G4bB_r_ln|K|A0p%y72+bAKM zq2ls~)E3Skw)vdc=Lw(}08|kwP5>T>`2TQJ(|WLlOf*x_(1G;RfG zcZUWFm(n9)QEeZjUSQ*7vb_`Y{nCPZcc)QrD5xshwna~O$(UOj9)9=8<|{8fTZ%e3 z-u%0bt9CuU^u`5hhM$u3r9n%$D(Fpm1K#NN{4tuuA{n#nP<1mqRy~QGqEV$cSd91n3JlDL~e2e*}@DB5q@Q1n& zg|7@|9agASeq~fNh5eDhZ2zpl;=tmP%Q6egHU)Pi-qU}gJ**d|sKF@Wc)@SB_^e)= z$1ZXXLlA`x`iL&7#bp^%ngZ1lS0xh)yV7U~mjj=y-I8u#7>%n3|DR;@HCPm}293e- zv9bXrxP(7ZmS7VvkT+2U3ga-+lgFP6Hk5%YCzkKPwfKesldqiyIk>)dFzlOpL6cBy zA1XvQ?L}XU3ad!UA7IU#i8l!*MyWUG3`Ch?6B72DgFW~Uk`+a@VCzM~C2HtQD$)`T zn0$0Zha-lVgyO20Oc<0jxO&;rqtdNY);h8^RnR|4JV-1ut$^;K)Ka9LP!L%yU|tOp z51x1S+Sxbnd+~|+FH|=-m92UA+KIIe8*dQe4S&k*6r+zWSh#x4>={$)iTSci4?K3y zPdDHC^xF^La`EcfL6a!hwU*q5&jN34eel`aZv4Z!HNcnez?W9Ebc2SW8e9qD zD?yY>hP;#@}A%t{_0ta^@8z=xG3l-ci)|ngn9;fnzVRFx?ZAQVNWamX? z##nvx@mHO^gVPEii9Z9M;xpN&h@f)3Ogck&BKSJ`27RMvovg>2OrM{eO%ysK>u?z@ zjxNVt_d2&$p_Qx@T0LUWT$q!etYsw?dwVNV!Sp$$}MRycQ(31-k$v5H>W+D1aML{f(u;&G*?!K}zMC=H<={8zBY z;x;bYzFc2hLZWu zH-WbRFY@^&a3bhYDgz%v;eeLH0X0l(*dg~>^i5U=oNlFkR@!H!eW$n%tpi$IjsnI8 z)(lk2nyNwskkYpKq&j21_>zNb)*QTK?$pGPcklV#{vmP0?=N5U`_)%mx!V4RrAzSMYRO#-|_ zs2QTrVPH++c!O3!<(Z_-fI!m*8f=E>c92;H^0=f8H<+AR=f|ps)C=TER5d8NxTAew zZXE;C_!JeW;ZRd=F{o5GD7a#=(qM?S8jN})QSxBam^ll_Dix80256*22Q(6k2CZQ# zsm!>PrOv224xL6QLRfGHP=N~O}+k>DRoW_Zn{^2j3>4!x{0 zK>mE$q6=P{dw<^@%bxyp;}tFUWSWAUgSYF|m%owW-X6O+DBtX<&w?e{wI` z*dAfeJCPjah|9N%L^`rKv^$T3(?tACddQ^ge936sU6=^1I2`?Se^US zD)J7$@}$YWkt+)dUaQ_rZL*nY7+c4V13&jh$-6;`)f!D0VGGh|5nXu;BOdB%#H-sd zoi@_8@xwIuDWt{EgO0ri{N?pXji2AB+=hF|dZ1{yrzoKDH#mB52uv(7gB`NcJ=EI^ z|42OQ=oL9AmZ^(TP<5$8uF#h<GR%XrNIJHez%a!1WRtt~X1;oyxcL z-|LxE;CV8f+jtqa0OMggfGve9g>MnIpjy{UdbBO0#7Y2d%QXPnz{#NPJM=$*b}&eX zi=4?`a$=DTTSj~0Uq#sFlrn5_H|`#UEsqeplf4rqP$}LjC98Q*;jYG1P5Mk>wDYXg z$aIU)Vw>)qo@z<|m@p+#B9SV^grv1x4|z@f#W?|UI@kMkTDvq zOhlvcgeR0pX&AXnsjQR@508eVMYG&xvz;f{Y-YhD3VhH_yGHqtZ>evkkJ;zLzPQWh zbGtl2m(!U_B)l%C#pQAeJntoG@VnCCP)G|9$KIr=)K^MMOEqFT6?K}UPLVjbXJ;FCd6Zt%jY`iI^tr%&`fPDBT+sb6}IC>#OHTy;T zM|YpLzV^R2<*QX-cF}Jkc+H&V%;BOF9l)mzFiePHuIc-rQ(iyK{h5}-@MS;Bv8X5T z{lnz$SE$psX4U7ukMXkHoBhrn^Ccz!<9{4*{+5wheGk#Bhx37kT7ib#D26KVh0+Vp zCe|natbJMczE-g!(UpA2|3GAQ@|lS8n(&gyWy#CaE3_*tw}w|l)RVZ`+!F0VZXv%= zSZG#_@sA6P4v$UVY*bX3hWdvDhD4eYLz5#+XL4#yM)bP_u81q)%7jdbq?s^g-kBz7mNsK+}GQW`tl^84hF;6^di$&F5kGH&{ z!louRbtq!e`}7%olYYE@x_*IPrSHKvO7V0A@f=S~{7QZozmGr2AK{g}vno~s4m({M zJVHfb<(S0kPa z@ief>9-KDu z)|nLLq&noDTQmNq6Sh%v>!hn10SHU-2iUXNr%^3BAYGNV;0$U);}N5RwO^FI$$ei&TUl=MyMh=oYNO3wQ{fMJO$NjW zy3ppN5UJ7K%6cQo8iplFjWMe32t=dKA+D&Y+*h6{Zz^ZY#kyrDAz>U{;U3r5N5S9J zpTtl!ql)Xb_Bv`&ftp2QyLIP|?G8_v0=BUmJOPm2)ZF*vzuscA+1(CX(S~lNCNyw* ze+aSY%*AAfjs8Iwl5L-&?MroADU;1s3DCoQP|zH_ohZ&*bk~gYr0DRN8*`iIt#9G2 zf-QN@n-{lD8#Qft#Z8|r+sFEbQj~n*b2?lThqWes>9ePgoV@D!+*i}4TW!2OGqpYB z8nynn=dZsOGq-~Lei(S~3gA5th&sI#xI?>KcZ;yxyxek|b%k$*zbkNitSi1Eq1Tn* zm_P0YqyM0!xi7XQK$_Kd50$q%XB;`59^|pB32kQ;aXG;2QS+rHpVwydcSEC_b zH3>&eVBDDeCSr1yro3M42W%50rgz{vtRD2k2bqDIV88@&K|RF(7Cr}FsPZSYR-?&a z(wlT9RvC@NA|;V{gjJdai$IitNJ1Mf#eQq36h}xaU_l&H8n&p7uO|%ja%u7sJEDw@t|HEi%RA z+Uax0UA*Xq@23o>T+#K?w0qBPXl+T227Z_f_{ac0vRIIu(|rq+OOy<+GbRPWqYJuy z*-*&iW;9CBGn-7_CVG@oCb3C*9s#XraoUq+v&UIgN(r2lC$m{kX)H}+AS98DMm=d? z51uF0I|+{JLgA=0i=q)PLOLhWse@6I8-MH0yNP=k6Ga-_qFJNar#YxOqETqF(P$}3 zb7_+90gY&jM8H7tYR)ldgl~l-)NHCAv%pcrTrbVtp*0X*?VVthb{3^sUm?s$|3P~7 zf?MCdzkZ-nlxT7@nd~1J=%lhyEGK4t9z>FZNPB-H$OEIe3P zxXQWuv0&19nTIIo`>s0`4X0QP2JJ5(0i&g$J8lBqQIDfi?QQ=1{UpORapO5=w0>mx zJYBo~yzt|?$HUJnx9c@*$R3L7W1(ocI;^Zlbt_R_9r9FXGgM?%nksNbb!kOKrqq+w zs(mpoZN^@^0~A!cI^pwhOu$tit?usl8CSx?}eqMX)nge z*%*tO)IK#)iw(;boTgahBu!2ZEy@y&+Ixov6--?*qeBjYog(Pk3zDwTpeFlCx@VXc zaiDb+`J*V6{}RRFTay2BOV}2)h59uRs34kf)ronkTS`e$wb+XbSN$5Puv`L7mTIGZ z7E=_Kw%AO^&gskEykq?GZ*JRjn~Fx-90G60%D1jrv|~awMjxN`>+=h!!r=RG7Q8Wc zZ??K+<;LarcVR`>f^v(=>3z{B+C7uz&Ay}kiu>L=<}blDfMN%>n+-OV%vYBHUo8N> zYQ}$*^uq6L&tx{)c4pXuyPz{9i(Z1$FI)ed2YZqMkH;VII8zmJ7s}vFyfTxisPv@j zhtn?3)Z}X-P08k_rs2(=`hqW^Qznb9M8T2J+2Tb%A{mkIV=BQV$#6+YB^ zD1`R{|FKNepP|{mj1>5fp3_~98~fgoMRx8ZnenTrbMccbLr8j|*D3mbqCHj9`_B=5 z3rTg(d#c+Z3-hz^=KlH#TmLxF+^67#d;v7)L+?xJ3|p!Q>HP-3#c$2HGu}o;rM}Ek zW^HmedCyif>m`e19qS(N8Sk}fOfu!@tK|d&pd_E%c2^^}+lM@2!5-3q>@V0u4uQ5~ z)@t5r6?l)s7Zru5=pZDjHbpfWHFZS!@f_wv|7`~x19lL_Iv|^3z4w1X<M825X$4%e-bm46a>#QWE>&cF;|Qu-l*p`VYNC6Tk*jzD~CSipys2l2UmQ zzJtENtFOv0^=bdh;Ik=_DPgOT6u@pv(@U|M=>5d!bk z0$r$wcqE-S67~7KUbiQTX?u*@?W(OIj5X>arI}cDG?UTk^sG6mS4U&DwH~k6Q&a7Y zNoX(j#iqv=#GZ}qj45JLEM66pgsdsHBDODfFm?p`ddSC;)#Jm{FXT9o%83yi`ql zsWl^;F|tir-_%#g8K)^X^#ut@qbSR+D@3~#*Kxa(j$f8?o&Rs55-y{;CHPmMVmomN zhkjA!Vf>4$=OOZNNA3mg0a=K>PRld1wEPQh#C3m>1zcf4eUI1W^gSTZmGjftvM)vU zpDNp!FXb5BMPMgg1a^`EiTH$+f6er=NQA?-!;YW0pM;~fqoVQ^+h4iA3h&w8cYMKp zA*h_3Q?S}>4)zt{-=+Z4B}m+HY$wBAJ?P^fE^ zHR?)Dy?zK+DOB2qIMfM})MvPe5V2((Ly78n(@t)qu+hBH+HKqE*e#&MQ7#WaOGM^MKdLrm4?8f%^7 zY||KSgrL?bwQ85rrH-3o=9m>M7ZI~+v&q0Bl^u*SKB8re5gKmsqcXGxDbSUch)Ue# zY`(gfK1rALX@I|&x=u3|;wt>tHp)@)oIanv_QhAZj>=JwtSJpMcwJoVl2(h00HxhTb^EXZs zQzz7`oOk<3zxGTPwdc=k%X09T3feLy>&g)FMg&v1DNMp)2dK<=1iV(aGm{3@$%&=M z27@{MAp{yhp#Dxfq8XE>umrFVgVh3s&P45$*5aFmN0sjl7vs3ieV8@zPSYBOZz-64c`HZ6{Zw zG+84V5WI+&tBkl&6i0h-`<5l{6>j40!6h&j#4W)!!F|CaK{iO$l!=!(oB@BAaANJ4 zd8bBE+dB`nA8MDg?(5|YURf8&(RE-2itOI{Q)8q0F zq&{o&frQvt5{=F?B5{*SF2S{ep1G3a#a}vdzcA+S z%0*w&?5L*cSJRVHykV%*VNzHSt}#}Wg_zPR8hhn+Vzj=#ijOqw{2I;hNxECv(pzIy zkw7!U0{aK#Kx|E9x_5z>c%36H5ve>Hib}&4#FxZZ#8=0kjVt2fh=;ag-${8nhk8Mq z7j~o+qO-;DY?ABa<*dR&eoi4L%U;-xNYfdxyffmo&}(76*-L0#ueg@jpIK5|sxGF~ z#G(T!TFk0~r6}03;?dvS`dH~%9T%=2c7E&OKmF}S3f;oUj)xz9Zsdrvd*7Nm^_{1? z*+w@-(tBPSjlFrrjEZwAeZ1Qf>zcN5&+;-l^f?XGPW|1(^M=pyTAiU$qi*`mi?ScQ z0{FXLmaBJ236n;jrNO13+gD8kfJCjx(s+_tv{i%6ErtZJ3J9>1*n<~r7T&uI2X`Phh#+77 z$BjPDO;h__pmT#)x(+E1yTA1PM1D~I;QG;b+^^9QR*79Y-%YLuluE%-Fs!Ydl~^l9 ztJNWTghW}vKu+Uy9HrA`$dll;g@{62V=!nn9zGtT?K7j5K}sH;co|5jP&6KQLhYTCngyCAniU#_Ml2t4$)G@zV?LB_+lyOt z3w~Vx_Llz#$6Vf5A&ZyV;#L@d_&@-z-@uf!DXO!lIYvz1*H0{b=IS%OPNUw7tK|sw zi@!d1`5f8sEOfAqeZw~#nfc09w(OU|U9In#JaTcpb zZ?y#zvrN|Uh4!Yr9ox6#8=uK;V#jq3v%zS#{~q70#5b&QSfJs+wS}X^HfpSTy;V~vPuQ3*Pi`0XSb%NKJ_h(N zVa$b1$&$nBAU3-~$$}wX;j{In>80*Pg7rJ; zDHhLk5B6k5kH;x`{GtN^IQ%`ie@j(XmhoE@EbFyeETD)=fD9cX`0paYguz~i9qM+% z5X^Y3HaNpZqV^tgh2(Wa5ys$$;Uwr}Nh#L1h96wQM5w|Jw%Z+!tCXF&`+n#F0!>a*Zo7Ho-f3XBxv z6)O~b6|CX~7>%P5cp^;hf^9o#o) zKt>khUcgAbeX=tzZ-rF!?}?<9!6XT|-zkX>KbQFM-#Z5RYHW8D47~&xde4!i#A$ch zoJEsvY`1e@*_Y(M-bmE;p8SywHm={~qk;DJ_SQg%W=otZe?w+-rP+L1AM^fizWL%e z*ZO1;TuU|K?gjt2;fwh%7i2Q+@G(s%USb;uqL)D?bK))L{{~}Mq#@VG+|7Q5cr;Rq z@;c-92vz97PWT?<3bjtlsRe|?8ctiLZP7AXQJB5%9Rlaufe@~Bqi-F#m=99)Xw z|DJ5qa$-cW&Z^@az%2E-K5~}~i$=+RIxIMypwWhLlbnlIfWnD>D4Y%h#o+2<(b^$R zdrpp!KC2i;7p?v++cdIFM16bNH*+CmK!(k#HmqYanZjPkQ!~cyKK`f-!*-u2_U#?O zBS9;{{yC3Xib_y5o+(XuO7)oUsZu7Yiui`Ii_BL#uW&83+~mB|a<_B6YK`SF=QEit zs^^UxESsI%ynBpC%dJ{0;sj>y=kIcoYf8IHA1HmwxW4q|@^{NWEmxO-_wtVl3D7mjg()T#AF9`vSRUj89u*xh zAiqB#8Ej3a0L}!O0^-f6Ucgl_J7K?1S6RE&BtjZrjS@??iqniYE zsk*rE1nrTm6`n9 zzTZ7{@ckjTj9Sr?r@o%-}?!2S6^w{BE|!yBlwdPdT58Cg*U z4Q?B4o5hS(6m8a^+pA)*&L7pc>w564Ezy9+9{`(wmXu)J2r(*+&J!>JW3Pppi8IRYE_MV3) zDcV)-x(o1yig~)r$u-(*yo-Iyz1w{+1h+(NAV-dD)Nv)?TW|2%3fuD9ac3*$0Y}Jc zLDYc@o#tBOld{Hf;@hd;dY&JXFA{jBeuKj(h<m`v3W5=8drBmdr&NvqrBF--E z0q!2h-OjbvwQz0-+zyjyaml$&qT62ZtcKL|U znX&|;6wPa**cA0eS4NqrnB095rh(t&wdnTFaYqZACE@Q-aRquXy{X_oQ$GVNOd7!G z_n?(0d(gI%vz9_Zl&wPGD8EcaB9=3jl+WI^HFM87R*tvNe)QEnx#L*3=OxDA zrhM|E&uMp^x%A85J-UBXi^a|-hhKu3SN34N{9~2qdVu$O`g^+o@sGETPF$2AGywGs zIC2WC$jC9MpxW!8U7Rapcexz)pjT@PmT20wzzUm60swJf34btP@glv>BCj#qeH#B# z`b)GJJ5!OsQZNR4@NJutiKYD+nw>=m(Ew7toC*f|@euqUrM&RVj#_!y*lt^q5jGjs zf|`n%LEhMg5@0X*EivlMM)RL-3aP{5fSejoxS_0az;v&$_mlIK&yLTh^5uzF7azRxssmTQia#t+^O}!7T0^Os$hi|4 zpk)#H(LU*7t?fQ5sUXA2Ib;_3Gx@Xmb@4;tL-8ZmKOCR>{%tde?u5IF)OyFb&hkxl zP4O*o>LFy3KW;`#$e;3e)Ab?e5*oZs9feYhI=LW!~xcgC6ER0lSI~@H+iO=g>6> zFGTc*GCq~;LH|%Zbd3csuJDz z*zB_Syx>)SJs|Ee{0D5d(-P_mw22BoETdx6C!Vx@x^CKEhMA3=gDd;th7WQFu<6ym zVD0(hyQ^01cjAX0eWkI|B=S60aXxmv_8dr$f8KEGGk>_f;QPD}zRwh(%__7Mp3?;-3~go34Di1c7da$=y}a7hqF z+T#HaLV{l`O*@ntwKfRUC~2!v3j3XVW#YVBGGwfhb)j{im9>i5%eUb-iU~gB&rN||7AGE1$f?#Lt z^F}Nw*pUQXm%E<59%wU$DzR58A7NQ&As+?aiF_S66geI^8dhEspP!zUnN@kU;o5j- z)@Zslj7dYoInQeLf`8}CmIjuFR)*Gux^r;i_h`rT!FZomEY*Kfal4|(-|S!QgTI6O72M9$JVYjk4RiES8f*@97S z>|JlXx$3Sr-vnNm1iUaGc%c^mN=o17Jnknfw&Dwvmn&D|yT}^+80p5Fh<2^=an)wU z7S*3sAE*vERZcZ;mnFB!;+1gh2kK5ADg;QjA)MbuchiDJb8sW?tU}3T ztIUv`)1^=;BQAyL!%mgEz>;*=@ZgHWHl)lS-Qw5#ECm~=wGi7b>?alMxJdkss4^S4$ri9@m3qx&g(9f8 zxy8G!V9zoylOBB6R=3ii*CMgeBA(0_fbHj79>n-a;%5XLmLIq)_8<1pfO8T3T z5Bq%dp?a>#CvQ0c8`L*7Mr$U6$!4oU7PDaXqmTum5He@O z7DWgLhq~R?p)O@~XsU8(I-N=p-B5u?>Y~_2cMWDy9uxe=) z@mG~qbyP7`lqWqkX3&5jgBNO+Y6z$CYs#QPrLj^YR`0+Mp{0c|(!fp$`KKCbcCj39 zr2(U2oJdZUDkh8D>z1*lNjX!^oZr96TTd6=_iu976pms=;b>3G$BmYfCRE&)x=e9h z;TN|QKl595r-o)vXZv417a00KPFESZ`ucw1V9p=h9Oa+Zkz-PW zmShMlNsBr{f99wvf9&KOPdEe4YAK%eY;vQuy?pKNvrM zlH>F;o}H8##z2{8My?01o*^$(9-+NMbHBq^GWx+4Xf#04g1-_?Jpx=l-S-4?jrTg8$HW^70QoX6hRBNm=4KdZ58cdR@*)&2U zMD^AB&903pHioM)ndF|Sn(1DoTI5z#t18?hR3qGzREjcnO@qv#2ZrDwBO8YdX=n`A zSWUFc>lbi~uunKB91&O~aDpT-!bqban2bSd#3w6E6y$;=IMN#o`naJ#OHILaM(K@~rb|3-B~~SBq@ie}G7(^$0gZ-H)l^qU zB37-z=(pP>U$)G))JAM4qMrY+y>EeT@;DQn`G37_Sx@U_Tlyv0mLI}4eqb9JTe2)a zu(2IK0xmcpTLKG=4sV?tah2E7bDK(iD*&=aPQK`sf!(xe9VYJ~AahVO9vMo0Ljug4fOs~O@jzYJa zw{*Z-vG}h@1WLO2bBOEb(22j99D`y0ZoHJFz|u_bWq?UiG6yFADAuShmw(-o4xw|G z-caEmsL8aa_1Md+>2G#S`o`=H@zonwb9bT|Yx#wEU8HC=kB>CWR0B*B6wAc+1kDoYu)nkn{B*=MDI?-ukyBZ^@Wf zC9leuugT^lb29c`>0T)_E)B^-dF+wAZ%CL2Y^U+F44ZG;ZetJ@F^pBF<>%$9Tw5-a z+fFxP9opriJHA`WAKVfbrG{{h^MS;=ZARJo(2g%hc6=zX7{=RyGRP7 zJ*`%wkSjRL+h%Mgor;HIl7qENjmA%nX7|E;>reHV7N2VOzFwk4S z;n9&TeGMC_MEr`r&hx3AgZJN?KF$-QWzR@ksdZo1_4k&>c_R<_KXd)xZV(X|=N(*p zThs(Hl7)=8)7zjmhzxq8fqfmnp7j><3-Lcn-^!BgkVZ62mA{o)7Ds`WW~e#Vg5rt2_wD3OGoF~stzW+b|_Lpye`RL`h$yzeZp-KHEubr&$wyadB8eO33 zR@{Y$nNej3Lzzf+C=>XBvRFmdE0UKKKa>7U{&UyQR$i67tB{#lH~WC}5%w^9oRt{z zX{9w+7MRWX1r7t>Q)Ov?n9(iy9F;6^`^Z+~1XeX*z{yNVpvT0swM zDLG6^edXdmo^m-euE>y^*?c`CD;;E6`IeO`V|BUv_{=AM8eh2n;;$az_sF7Yi_Bks z^U&do7Y~2wB0F|?{3&WS2< z4|#Xx_j9VoQX+f}s>T!OTX`pn*a5WJ5<*j!?e;kOgyp`9Pow>ohbj&iA9a7J;ycC1 z-QTRxeBFtUls?TpUCQ#ah@A8#5x#ZK%Kf3xF!K%Z0BMMmQv_sjtuT4CWH_I*!k({? zlhqQJ9X;-l7Gks9%RD96b2O8~huLqK~n=puNxa>OYZ#-SR%gcI|HM=d@pxd|4;W=a)2WKNRM0dAaA9 z6ENlRdga2@j9g1P;q5EwrFW7X4Y<|ju{LaocVigv5TNQU|P*8<(2H0u;OI%lo9{>XWppu_i_g`?FNnHN%HoD1X)cBp4<_}{z_>s#`DjJ5}jph(+s z?s)xI8-ApFz4e#6ONL9$zi9of?n>=%TK}Z`p!N@~+AN*KAa0ho+H|>w+~&O2eU2w; zpI2qA*L|!Z(y-lgf5U^G&oz9`^9}u+Uh$~sjE%WVR$l5XUg=%aRBJJ*a-_N0ji|P| z$|6^PZCJ!{t3HFm#Z9@jm~auv8@9)HoBU%bk0M;_^LqrJ~c4otFEOZ&

    8X6?j`-C0eVF5NVF7}ngf0*D=Ajkv+fZp9X%x}JgJP!$rCZD^OK~M=?Phl@FL^V z=}F-(qLzO#sG+`9%jFm8ij2*g> zLwcWv4yeiCir0ig>2#cvEdOUc`dUT)3yZ%-MyS7SP;r~}^-zCLI}A$Lkm68(mGk_-*0vq zpFF0Pb|zFWNjnp|^w65CxKMc0i2VdVt0g;K4FjLu-|K0wdgx?ZaPv=o@Pj?FTqQjf zYBo9#N56jTu7QOgJlu2f@e^!$0m$lOR*S*hu~?r0 z0nV+kRkhsT)>~Q0)lM``?jiE}c^GebM4v})^t!k3!#u3av*a;fSDaD2tawv#MIpW~ z=b@Y@a=w}KTGpj3iBU%0IXr~wJ?r&z8O5#XwcN0Et1U`6G}QkI=9mpN4Y#$r}puduP`vJ?9X@`PYJS6FE094>57 z={dUxxfDi|$7Z#vWO9#MCDGg2ERI9SXdv?td8t~%$u3B-lq@%wEMIPJYT}=nYT|D) z@plnEa)+2~aJ>rbCCx3Dv8b%ok=MO+B8?4cC3>Fod^OYw)$m1>3)Rm6!y)bBJHiec zJrv752v>4u_Q;7$T`ZvrlR$3VQ2e!Y!gZ!E* zDFYArHB~4BC;p_j>1q75_PCDa6kHZZu3qMFDh_6E9^9Z^t=r0M(?s+U=Se_%N~iVO zu=9okrRwB?QUJ(R;S=6mB~q%Dl}c9GORo$&6dEnx$pSBIIBl_%+y_2E-WJ+KF9bW} z4CWU{TTJ}wXm4t9RKsA*@xK|McOt~;^m?66uhU|r5SBsn)E))vQOG4ukA5C+_h#uB zPo<_sb5g@<&f)DyhviGBwTVPqe!Ey5N2Bl}Hc2Vt01CLg=H z-oeijlWS%Ti;JQ!Kv|ctz24apgUk33`;_4u#(Czf;aQ^$F=}Ry;eg?!;d_R+4GRX@ zF=m#zz_2orEY~D5<(4p|qLN&fu|d?3+bQbIT`yX%-;le(yrE<(-XWUE-Dcco-d6H} z=;OJE3|};UlX+V7?c8I=GtBd%`P^CKbLQttUN`)}_zS~D<3Aa$7|XK^d4_VP+)!@( ztogGg#|_ULUK78j|6hjR;NKYkzOPh`qGyWR3Zlllq) z{_G>DH*6M+j^2CkjczwOrkz7gu3MZbk(p&8_0pgS$Lvy;b+OAX(5D|UP5oxl!xLxc zCr*@->iTP#44q3blYjjRPOHOP8U6KtVV+E{$6;u*w?jSlGoSZ9 zVbj>OjJ83uUc=;(l^UC43*M=X+M~`b{#WoT>Yr$TV*iozN7XOazU)`YkO>`fu&5eq z{Ti*`sdhNk_S%|iY_F|$saNqy}axyh8Fkyp3r2v@uK zcVtsaD{M-EdHl4#gui^MbrSO(vO)&tFX!@_nuWov&?k_^DyMoCgjc&Eu-XeD)d~ov zwn7-S6$Z^}M=BHzrh-3y_;5yJQmSfeppBaYlOI6v`$;rmHH+_f<=U(kjWtW#QbV46 zUJHOmVK6pppkrfg)Of-LezNQ5e&0n9V1_(4i`Sl4*G`pgJYpzv+R;@SZHLJIi}fm)>;H~aihT=b7oMkG+`@ZSi%M6FKe_N!p$`1tB|TmU=i!17sY34( z-$MNTg#%K9QrOyRT=)UMW~wwuVS3vsqdsJ%=RK_9YgM*EM(3eVEIcSW1of&Kgwbb0 zCauX)uCyELaGj=4={0_+`@fDXxvpEc)-i!6G#}S}-0`sPVaHj`i@I};*Bt-g$Z?qD zs@7pA)e-Hma`*v}S4Eu=)>dmZ)jED?WK}9FQ)8k$hjp(NTMs*|R)-_c;VfTCHkd1_ z=mga0&8n`bSXu2Tuhz-=lXqhAVgB5m93zXK_DjYpBQ{p*j7GiAVbN8W6%yB-E?1?~ zYW!13;??%v(2yQ}Iyx_WxLs;9btQ$5}DePyQq+E$IP!JC`$?`!3C7bhA@dEAn#(Krp3`o^eZ z+Dmac*1cMIlQAB$YlzHicUcTH2G6enl+Hc2O%->9%ddy?8WN{Ul5C+w`c;#u^PtXn zfgudCh@H7JpzugQzIOX`Ez#SolM0{iEcKh{lPy&9n%1>+JP@?0Iq)y10+bimOB8=> zVrlRxZbJfz{Ej)r+%qHZVhQMUAJj=WH<7-VPHe}O=_h7SB@A^J^uW_uAR0EQec9Lf zt5un7bDKuHSXWU;<6_R}+7FuG<0UCJD$8U2q$BOI|7+-Akq_(Dl)Jy7={;6QvYcTw zD19APCq3YsCE+5YB5qu2a;tYYif-t1*NwG-7++g@%eY#HMIDc)6Kl z`}XW#QoQ~&%6H!J_4pB`OW=hkiV^^iV#QxXdfIC)ZO7q-O{VzdgX9jVW(bH7l75p} zxDL)ZwPtvgAEe$!}L>$vOB9#7nY=CV6+BBTi+jXoDEKOK z4Qoyf4^__vf1(%5*il8+W*;x>ab|nGu#|0uRAs#wfZpZ8Nf@$zW0jp$NX=Jg zmw)|}@#Z8D&^`p_ALG&WTw|LyP;<%?i-MlG4wL^56j3K{rxybQQM$FQ9uOVIvceqpHY_LvTM4fPzYy7p8H)S- zz`oJM|9ipca9DtL^MvpxM;c|82A2FB;A-lK^|akCCp%$kg*13Y6Y>Y;R?8|Q2TSsp zR=UFqpAxaBYsUx9kIGJpY5~w70%r|6!;tt&9&-8ngyn_)F(Z0XdTHvVg^9si=T^|E zwO#jb{QHUvW2_#|0lg9Gh%6{LPuW4=&U^ zon{+SJz7&Ak60$&fT}_YFK6)5eUeK`txE3USD`<*TMb(b-DD~7%SXK$t`dtDrA0aY zS6WKViVk37rHx(Y=^M6MpT9CyrF?v;Jqxw*yYqb;Ve?gCWUBi%f3~cAP46VDUI)`j z$xL6#+~7nRxFE1%d2kT!^UU6zETVgeChx%(MB&l0P{7g}iqO*F+QS!KiEl*Ym1zM- zRJt3H80n=Nz{r&USOie`0pxwx%cC*m4A1uA@pBJ=)rH}w87!?HMnGT`)7b2Tm>~|k z*hb}whF*yO_nXyP?^6H@s&V(yaA`%}7uy(IlPIn+fC;s@X}Vr{<|&>H{{qLgh0}s5y|Tyq*HzrezQ*RQ~E3+)KdZWnxo1}tyo$}sNj z#<|5E(&%4CYvp#tDmIAdq*ka<5$_!H^bUBkFAzp>g%*$_W#NIbub{p2JH!e@1Eny- zP#XY?^6FV)b{Q=yunaCe*fSUxr;r<)oi_eE(Px4kd0$JK*lUeERBAdfb#j~HjOI87 zc1w^Srf-C8f-@a&yijgMf_mIA4?_1oxM#4H(CRX%p8JdkL7R450DB|(@CTAL-A3fB zqk2jk6`6=4cfZv;yGKdz=ZG>s%Jkx2PL8yHvz1eKU?LG;;9+=y2~ImfhO(AYvI?A2 zILym-J0f!P-}RL>x9Nc4@&Jg0X?)p@e$=lr?Q0EPP4UiH)v!zrPqKfrDm4fk8!X6q zGvecXbj7se1WDtRDX)63bdN7ClCDA1>5R<5_T%%2j;x9uEjb7!e=P!r!R+ka8?CVS zXe{CM;5v$_5qlPLUmXzaCUX#EucoUI0ZhCNv8F^->$Db++f0#E2j^LjON#t#7h_~w z2~N2OE8Qvg%Bzav+r*U7GpA3Ov(bj^Fp9VbPb_IrZ7@G4#cYq}THD}=XE@b=NAh}S zhfZB5NH}vtAypxCQRGWj&fYua*{U#V9ny-G+)OCjsyX4M(ys`sK)h1mJ=>V7{B8Y5 z!7KYyFBFqtv$fQiebm3^vQ{V$B}y&f*U6H5kC@!N-j=M^eVmCeZxQL;7$$|tF=9oo zqI(k2dyKw@!+6d&Ho!2oSqn_(EnQPm0Lktq(4atCCnszr&@KvPMYIMy8MrU;kC!x(H%ob0@i7hirv|RLG#6RNno){UGBxJ3Gq@#OAJ#b~YF%**FKUzLa%8_9YLZJSO)j~f5% z463@#ypU)l!%1D(rJ6HdmN2ZB&fXiGNl(CZF$bm!wc_N>{4u?eF|s~eT;mWDreLz& z>E^;|yP4E!NR{On>!33KJ6BPQ&D3@L*tqhYd|F|4>w_BE#8QfI4v4m7b5DT@dm+tR zTV&A`%x+v*o+X!R?yc9KZd|Im=cKCuQn!{}I#<+rI?-3U+ao<#H5!SFuUVCTtW(AEur+cG%^?Y2$u5@~6Pk>1Dd^W2MU>LFJwLP_BS*a1{X5Y7h?9xyJqS$#7^V2k!vur z*d@|4s@?vYwdjU|-DbcxMzib+U`=cv70@CcB_L*9hpv#+km{j~o9Lt^A=w?I?c*+f`!?E&E^Je(R@ZygCP&T*9QL^yC zONxmg%U@Ovrv-1>nCltNAz}Ek*y~7rUE%jE_KWW)crX&f5jt(15Y_I?W)KcDebU=b zeVX%OvCv6LrOsYH^`yV7K;brAw`6npNpON4xI8$;2QJW(p-p(9HHEdiOx)W9EIu}4mZ(h}YX@(CpIdnD8RmUtS{i@$fiWwq5*F3c|ZAB+;D2emJuOU&H~*&V1VGR@+U^PAnbZV z-3_4{eaxx3yA9f&#LE!MJTf|C@1XC+ylD13-`IJkV?UI8jL4G2CDeW0jfzyeJVFji zo+r?^bhHtA-;JQZEc33ceL7P$XAlg=i*!job#0X~)webK%)>*Ka}YuT_YOVL`T9ih zvk>O+(qOF-+vJpDXg@cH_{|T#!8@tp0rl(p@aXUXea~ZUC=floU2ZECBXobiVH7+@ z6H5L2GWW3S!r4x;! zAo9p*N5SCl9i*e?L08F8mq;cE6$T1{F@!j3usraM5CKErqC`pI%IJeaDB~_- z81TjHKSK`9lT6XB0K+3my_)#J*%>MOl?RF=MTMEyA=&gv@Wz`AiabH1z&2Yt9+AYf zMgv@HGaFxLkklYc=F`c-fqg9lY&>0`?7eEcfz4x3oTA;n$*w0I5$U@ey{G_0l^-{% z*eOypX;Pl=j9u8^8^Vgdmw}4=R6!3xLRAG&T0yl4^xMog9I6eIVxycRs6Tl=lOlAS zw?$9v9TGfZpeR9N18PZ!L?wfH7vFmU(oN|kw)(ZHSfu@gw~otaB!{ZpGeR+3GSNsH+**^g!UE^gL~~!N+Nr^|$6y>XIdy(Ib+)X66hjqs4Kn_H&vX!3 z5Arqi#6P1l_pnd`RZmt-$eW8Y=Rj*H5*&=gNhIu|kpg*XA#HrfclafizH}t($X*~J z@ZuJuok@l}oJQ-vU=z)~^t7we)2UWgi_)v8TCX0hK zav)hUM8P^r!kC6+cVf59bE8()BnVm_nXQKlNqQ=IFhe{@iO-&mx}IVl6l293E6iRe z#gcBt=T#)-5k?@5Qkg}^C>gPkJ&YyzATCc6o|?Ca;3=Ov48uq4k=te*b*{i8=Ppv;xjw=`7I15oH8B=8v8sszNN^Bi7Y zyVbb3*mb*g#{sYwQ8bX5vJ$@C<-(mIUO(<^i!LlKD>Uw2zI6&vMeZ!Xm4g1Q+m>tR z$QQvNIE3~B!A~r&o6M6;#xEiBwKa@(=Dm01a{mH>ot1uMn^PE|_jSUfF-mZ_5E{Y_7UL zT{d30S|%S%gT``|l3vlX->~gomc<7>$1-S!-X8o4zdomJtg>}9=`?oo^)wkLbr>fd zOy?>k3uc2n_?2EJ?6Sl*^>zJF=ltocUkO*{v_So&=Qw}GJ6*q!)I?Mlr54$%g%=|OBn><+b^ z8t#F9$3Ns7H7(}^fZ3-~Odb$7U5Ocl0T4kF?B9z08aRmDr$4hLtYs&7x!#YBaGI;D!HFxd1_T25-igGm70$xpPB0` zfqqCp3^|TahI9$dWPWlcWT}zR>ELgGGdtCgT@Ir#jEvnN%x^TE(I#?CTRsIu;te!X z90mE2^ks#11uXLwxXbvC%y4>WAO+$u8NYlul}kiV!6bBDKs)DHK3GY6L6I^ zh+Ux)EVFjirg1MfXWaswx`(!K-4B8ADi)%SE}~axu*i=Zb-r#~=9}mtM>8_+T9QloknV5#(*SKUAR(qQ3FHZ8-8DzBK;z6=>?<&}}RK zCAO(`ro5C}mn<@axVzr}`I;r6F93JW#*8RG3$IW~Hu6>W&a#54_*?p!Or3f1%{b@~ z2f~cw0267D>o87k^hj>>OKx;dcBYE=iai`ROkt&F)!H(s)&E#q&8oGO!m2QlDuPAT zcV77A57nYxZ3ti*FPT|q$DwG+e8;itA+w^NHstu>~agxZ8OSp!)40%&ofnWu&A zLmIpKs2NYBB7>=!tjs5*`?Ta|sCQ%*2|nHrX`m*rl;xi)8O$_puDblihE2r>Bi3Je ze%2Ij@lH)W)UMN}Lk85@go1k4)ve@iI4xNg=M&$LOQR8*unsqyZeC)^m5u+NpsrYC zh97g2t#QQ@N6uT`ShV=x)DydZvG|f_6c>}gMXnrq_41E?)X^}yQhL4G`m@9L$X4mu zd3%TKxjN_M6yT(PgOB6DPjw{vrbR?;Hj-21X)Nb#{v30%jE{k>O^U+YGW)_EWDWg`Qm0qe7%TwbFxC{`OtvsTrI97UQP#^sWQOCmY8ls9qv7u66LMr#?Vr z?@P{K#$S=!l1fk1Dw6?4m*l-0P;vi`wR9ytbModHOuJ0E=(n%6{8ZF(S2Hw&mbiuK zG;8>eKrljzlhFQM$e$ZgI`YE^Ein#};8Su80BR7y$PStrt{lVV{VPEKw^8YTb?t;!48<`H$THRrANo$Nir@d>2~V>f{hy{~5!= zwqCOx&7M-lFw)*qr?nD>*@W9Y6cpC!sTGlUzmlu%2fbWcL~NTZp=Mq`Xq{`@cCF@f zO>^d!x4yDkgs#+L%y3PByM>+?+sWgjL|KY(-z9re>AFr-l~X5$Y3M`d%8| zLTHyf4RanOq08=hN_38?Ey}RP^g+8)6|YYlk+y*ft&;M{(X_!H@lX;MmDl74z6Gk2 zu#YCiLQ`012zn%BC@=^gSx|6jw@5@-UP*`I9y@B^bqo0K)G_B?uVFdr#mMsbhp>AT zLfq4~`8EFQX5Wj$NsrlylJ1ga^n5drdtIPAqkIn+{f|zG&cMg1%ZFXud2>=vZw$5z zZ!7ECzPjdgRcLVQw;;8!f3Vmz^SJn#4CS6(?4GkQ!)-JzXW{lD`*t0I+RJEN59wG0 z=J4F6{&K7GFJHpH_zm=&huG>8P*LZA&DY&LnxxMKDY6|X&wtgo=n>d|-*r;*^mJTu zJt8nP(fRX?DQm-R7g-H$T6}e+~1w^BPnB+`PW>!|1?0g0<;68vOZeD9kfG zZeCC=pYJ#CK?u+;d0q2X1Mg8ugh0l!+`7R@UmFt{;{=utv>4ASXP03>$~hcX=Yu9Tql%Ra=`8@h0vi z4=Q)GmcGpih}P%H<75fGyY}2||IvFmxlcm(K!rfAje>>o6lR5a)VpV7<@Cuj)fGs;zv3#-j0Ld%C)*`gV#^?%W0ORz>@(@51!N18VwNrvitLO ze}~I|$@?~0X?1h?4riy1?w-vpS}HP}@lAoBbn`gA3V8OitAd<*JX{+-&ozraYgpdL zSD81pudGlmQqNmlubZS0Eq?C&K^-{o(WN0$dh$3KY%Wh=pSTryB?5f6VOqoX=7o6h z;SB%1hwmD@y3J9%VvyHJd+gWe;MG}L!Wvg!(spvd2Ic*d_x1W%)B0iJPYD@cz8bQ~ z`kLvSgAVy%8uD?U==g&rnS}qCd zz+jxm(3p{E={G0L=kuCD>1ze49l%U61=A0Wf4H_Cn#dDta75~J1R$rNo$HHC&Q_BL z#v(2etdwWGtnwB`^%p!tlGeDlI!33uq%k%{KmkX1n<5XdYeit+)$t5Fk_)}3?Af_| zC&V+K@%YqE=${?uKI#5f;+SH63!JY(hB$Fs-GBBi^6ZHNK8n@?RL_|JRXxRkqF*cl zJ9WRQ-y1%v+`NCK{7wDp0VMq^Nh`Ve_wDc9?Z7Ldc8;^{j?(mM2t>z0tNM%Bmb;4b zR*(NbM31c^d5rc`#jIy1IfSWKzce28 zDs5&M<8KFZc=9sIE6=uuCWIcdz0e;HBe!S2El;R4&3lKi_pA-q99JokVeQr)$yhH| zQ7re&uvonVJ|q0y9_x40|E^cNiJZG%Gp~u(D3|^tdyRRsWHERIX^$Yi3!-88!W(YW@f>|1?GkrbAfrx z_)MT8|Nj~Woa)xz=IH!foLYL^G`uuC2LG?u0Q7&;z4FZpVvDN*Lw8&B-!yMsbq{({ zK~{r^gP%2Qy5uE`V9YUMmK0^dO^tkp?k@)nmJWbFzH|ZcVbuzTtp}>^hY?}`D`YRh zwVX6R0Z?bE+c;a|noi^>MWUbfwVBufM6;+-EG19V9BGY0;)1h4>W|h-n ze9B8rsUC}&oJhiq+ +class SimpleRestorer: public Restorer { +public: + virtual Storable * + restore(std::istream &, + RestorerTable *) const override + { + return new T {}; + } +}; + + +namespace restorers { + + class GameRestorer: public Restorer { + public: + virtual Storable * + restore(std::istream &is, + RestorerTable *tab) const override + { + Storable *s = tab->restore(is); + Map *map = dynamic_cast(s); + if (!map) { + delete s; + return nullptr; + } + + return new Game {map}; + } + }; + + class MapRestorer: public Restorer { + public: + virtual Storable * + restore(std::istream &is, + RestorerTable *) const override + { + int w, h; + is >> w >> h; + + return new Map {w, h}; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab6/storable.cpp b/8303/Parfentev_Leonid/lab6/storable.cpp new file mode 100644 index 000000000..2bd50e407 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/storable.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +#include "storable.hpp" +#include "restorers.hpp" +#include "common_storables.hpp" + +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "landscape_types.hpp" +#include "neutral_object_types.hpp" + +#include "factory_table.hpp" +#include "iostream_player.hpp" + +#include "game_rules.hpp" + + +RestorerTable * +RestorerTable::defaultTable() +{ + return new RestorerTable {{ + +{"end", new SimpleRestorer {}}, +{"coords", new SimpleRestorer {}}, +{"index", new SimpleRestorer {}}, +{"at", new SimpleRestorer {}}, + +{"game", new restorers::GameRestorer {}}, +{"map", new restorers::MapRestorer {}}, + +{"base", new SimpleRestorer {}}, +{"base_w_countdown", new SimpleRestorer {}}, + +{"iostream_player", new SimpleRestorer {}}, + +{"l_normal", new SimpleRestorer {}}, +{"l_swamp", new SimpleRestorer {}}, +{"l_forest", new SimpleRestorer {}}, + +{"u_swordsman", new SimpleRestorer {}}, +{"u_spearsman", new SimpleRestorer {}}, +{"u_cavalry", new SimpleRestorer {}}, + +{"u_archer", new SimpleRestorer {}}, +{"u_slinger", new SimpleRestorer {}}, + +{"u_onager", new SimpleRestorer {}}, +{"u_boltthrower", new SimpleRestorer {}}, + +{"n_healingwell", new SimpleRestorer {}}, +{"n_tower", new SimpleRestorer {}}, +{"n_tunnelentrance", new SimpleRestorer {}}, +{"n_weaponsmiths", new SimpleRestorer {}}, + +{"uf_melee", + new SimpleRestorer {}}, +{"uf_ranged", + new SimpleRestorer {}}, +{"uf_catapult", + new SimpleRestorer {}}, + +{"mp_basic", new SimpleRestorer {}}, +{"mp_modifyiing", new SimpleRestorer {}}, + +{"dp_basic", new SimpleRestorer {}}, +{"dp_level_deco", new SimpleRestorer {}}, +{"dp_multiplier", new SimpleRestorer {}}, + +{"ap_forbidden", new SimpleRestorer {}}, +{"ap_multiplier", new SimpleRestorer {}}, +{"ap_extended", new SimpleRestorer {}}, + +{"ap_melee", new SimpleRestorer {}}, +{"ap_ranged", new SimpleRestorer {}}, +{"ap_catapult", new SimpleRestorer {}}, + + }}; +} diff --git a/8303/Parfentev_Leonid/lab6/storable.hpp b/8303/Parfentev_Leonid/lab6/storable.hpp new file mode 100644 index 000000000..d152b700c --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/storable.hpp @@ -0,0 +1,70 @@ +#ifndef _STORABLE_HPP +#define _STORABLE_HPP + +#include +#include +#include +#include + + +class RestorerTable; + +class Storable { +public: + virtual void store(std::ostream &os) const =0; + virtual bool restore(std::istream &, + RestorerTable *) { return true; }; + + virtual ~Storable() {} +}; + +#define TRIVIALLY_STORABLE(keyword) \ + public: \ + virtual void \ + store(std::ostream &os) const override \ + { \ + os << keyword "\n"; \ + } + + + +class Restorer { +public: + virtual Storable *restore(std::istream &is, + RestorerTable *tab) const =0; +}; + +class RestorerTable { + std::map _tab; + +public: + RestorerTable(std::map m) + :_tab{std::move(m)} {} + + Storable * + restore(std::istream &is) + { + std::string n; + is >> n; + + auto iter = _tab.find(n); + if (iter == _tab.end()) { + return nullptr; + } + + Storable *s = iter->second->restore(is, this); + if (!s) { + return nullptr; + } + + if (!s->restore(is, this)) { + delete s; + return nullptr; + } + return s; + } + + static RestorerTable *defaultTable(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/unit.cpp b/8303/Parfentev_Leonid/lab6/unit.cpp new file mode 100644 index 000000000..39098695f --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/unit.cpp @@ -0,0 +1,48 @@ +#include + +#include "storable.hpp" +#include "common_storables.hpp" +#include "unit.hpp" + + +std::default_random_engine global_random {}; + +void +Unit::store(std::ostream &os) const +{ + os << health() << "\n"; + + movePolicy()->store(os); + defensePolicy()->store(os); + attackPolicy()->store(os); + + os << "end\n"; +} + +bool +Unit::restore(std::istream &is, RestorerTable *tab) +{ + if (!ObjectWithHealth::restore(is, tab)) { + return false; + } + + for (;;) { + Storable *s = tab->restore(is); + + if (auto *mp = dynamic_cast(s)) { + setMovePolicy(mp); + } else if (auto *dp = dynamic_cast(s)) { + setDefensePolicy(dp); + } else if (auto *ap = dynamic_cast(s)) { + setAttackPolicy(ap); + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} diff --git a/8303/Parfentev_Leonid/lab6/unit.hpp b/8303/Parfentev_Leonid/lab6/unit.hpp new file mode 100644 index 000000000..93f745061 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/unit.hpp @@ -0,0 +1,271 @@ +#ifndef _H_UNIT_HPP +#define _H_UNIT_HPP + +#include +#include +#include + +#include "event.hpp" +#include "event_types.hpp" +#include "object_w_health.hpp" +#include "map.hpp" + + +extern std::default_random_engine global_random; + + +class MovePolicy: public Storable { +public: + virtual bool canMove(const Unit *u, MapIter to) =0; + virtual ~MovePolicy() {} +}; + +enum class AttackKind { + invalid, sword, spear, cavalry, arrow, stone, rock, bolt, +}; + +// NOTE: can’t do area damage +class AttackPolicy: public Storable { +public: + virtual bool canAttackTo(const Unit *u, MapIter to) =0; + + virtual MapIter actualPosition(const Unit *, MapIter to) + { + return to; + } + + // returns kind and base damage + virtual std::pair + baseAttack(const Unit *u, MapIter to) =0; + + virtual ~AttackPolicy() {} +}; + +struct DamageSpec { + int base_damage, damage_spread; + + void + scale(double k) + { + base_damage *= k; + damage_spread *= k; + } + + DamageSpec + scaled(double k) const + { + auto ds = *this; + ds.scale(k); + return ds; + } + + int evaluate() const + { + std::uniform_int_distribution<> + dist {-damage_spread, damage_spread}; + + return base_damage + dist(global_random); + } +}; + +class DefensePolicy: public Storable { +protected: + static DamageSpec + make_spec(double base, double spread) + { + return DamageSpec{(int)base, (int)spread}; + } + + static DamageSpec + defense_level(double k, int dmg) + { + return make_spec(round(1.0*dmg/k), + round(0.25*dmg/k)); + } + + static DamageSpec + normal_defense(double dmg) + { + return defense_level(1.0, dmg); + } + +public: + // returns base damage and spread + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) =0; + + virtual ~DefensePolicy() {} +}; + +class MovePolicyContainer { + MovePolicy *_mp; + +public: + MovePolicyContainer(MovePolicy *mp) :_mp{mp} {} + + MovePolicy *movePolicy() const { return _mp; } + void setMovePolicy(MovePolicy *mp) + { + _mp = mp; + } + virtual ~MovePolicyContainer() { delete _mp; } + + MovePolicyContainer * + findMoveContainerOf(const MovePolicy *mp) + { + for (MovePolicyContainer *mpc = this; mpc; + mpc = dynamic_cast( + mpc->movePolicy())) { + if (mpc->movePolicy() == mp) { + return mpc; + } + } + return nullptr; + } +}; + +class DefensePolicyContainer { + DefensePolicy *_dp; + +public: + DefensePolicyContainer(DefensePolicy *dp) :_dp{dp} {} + + DefensePolicy *defensePolicy() const { return _dp; } + void setDefensePolicy(DefensePolicy *dp) + { + _dp = dp; + } + virtual ~DefensePolicyContainer() { delete _dp; } + + DefensePolicyContainer * + findDefenseContainerOf(const DefensePolicy *dp) + { + for (DefensePolicyContainer *dpc = this; dpc; + dpc = dynamic_cast( + dpc->defensePolicy())) { + if (dpc->defensePolicy() == dp) { + return dpc; + } + } + return nullptr; + } +}; + +class AttackPolicyContainer { + AttackPolicy *_ap; + +public: + AttackPolicyContainer(AttackPolicy *ap) :_ap{ap} {} + + AttackPolicy *attackPolicy() const { return _ap; } + void setAttackPolicy(AttackPolicy *ap) + { + _ap = ap; + } + virtual ~AttackPolicyContainer() { delete _ap; } + + AttackPolicyContainer * + findAttackContainerOf(const AttackPolicy *ap) + { + for (AttackPolicyContainer *apc = this; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (apc->attackPolicy() == ap) { + return apc; + } + } + return nullptr; + } +}; + +class Unit: public Placeable, + public ObjectWithHealth, + public EventEmitter, + public MovePolicyContainer, + public DefensePolicyContainer, + public AttackPolicyContainer { +public: + Unit(MovePolicy *move, + AttackPolicy *attack, + DefensePolicy *defense, + int base_health) + :ObjectWithHealth{base_health}, + MovePolicyContainer{move}, + DefensePolicyContainer{defense}, + AttackPolicyContainer{attack} {} + + virtual void + heal(int hp) + { + emit(new events::UnitGetsHealed {this, hp}); + ObjectWithHealth::heal(hp); + } + + virtual void + takeDamage(int dmg) + { + if (!alive()) { + return; + } + + emit(new events::UnitTakesDamage {this, dmg}); + + ObjectWithHealth::takeDamage(dmg); + + if (!alive()) { + emit(new events::UnitDeath {this}); + } + } + + bool + canMove(MapIter to) const + { + return movePolicy()->canMove(this, to); + } + + bool + canAttackTo(MapIter to) const + { + return attackPolicy()->canAttackTo(this, to); + } + + MapIter + actualPosition(MapIter to) const + { + return attackPolicy()->actualPosition(this, to); + } + + std::pair + baseAttack(MapIter to) const + { + return attackPolicy()->baseAttack(this, to); + } + + DamageSpec + actualDamage(AttackKind kind, int base) const + { + return defensePolicy()->actualDamage(this, kind, base); + } + + // override to add type symbol! + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; + + virtual ~Unit() override + { + if (alive()) { + emit(new events::UnitLiveDeleted {this}); + } + } +}; + +#define UNIT_STORABLE_NAME(n) \ + virtual void \ + store(std::ostream &os) const override \ + { \ + os << n "\n"; \ + Unit::store(os); \ + } + +#endif diff --git a/8303/Parfentev_Leonid/lab6/unit_factory.hpp b/8303/Parfentev_Leonid/lab6/unit_factory.hpp new file mode 100644 index 000000000..5f1f475bd --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/unit_factory.hpp @@ -0,0 +1,24 @@ +#ifndef _H_UNIT_FACTORY_HPP +#define _H_UNIT_FACTORY_HPP + +#include "unit.hpp" + + +class UnitFactory { +public: + virtual Unit *create() const =0; + + virtual ~UnitFactory() {} +}; + +template +class SimpleUnitFactory: public UnitFactory { +public: + virtual Unit * + create() const override + { + return new U {}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab6/zombie_collector.hpp b/8303/Parfentev_Leonid/lab6/zombie_collector.hpp new file mode 100644 index 000000000..9de5f6981 --- /dev/null +++ b/8303/Parfentev_Leonid/lab6/zombie_collector.hpp @@ -0,0 +1,36 @@ +#ifndef _H_ZOMBIE_COLLECTOR_HPP +#define _H_ZOMBIE_COLLECTOR_HPP + +#include + +#include "unit.hpp" +#include "event.hpp" +#include "map.hpp" + + +class ZombieCollector: public EventListener { + std::vector _units {}; + +public: + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + return handle(ee->event()); + } + if (auto *ee = dynamic_cast(e)) { + _units.push_back(ee->unit()); + } + } + + void + collect(Map *m) + { + for (auto *unit: _units) { + delete m->removeUnitAt(unit->position()); + } + _units.clear(); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/Makefile b/8303/Parfentev_Leonid/lab7/Makefile new file mode 100644 index 000000000..49be692ea --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/Makefile @@ -0,0 +1,24 @@ +EXENAME = main +HEADERS = point.hpp placeable.hpp rectmap.hpp map.hpp pathfinder.hpp \ + unit.hpp common_policies.hpp melee_units.hpp ranged_units.hpp \ + catapult_units.hpp demo.hpp event.hpp base.hpp object_w_health.hpp \ + landscape.hpp landscape_types.hpp neutral_object.hpp \ + neutral_object_types.hpp unit_factory.hpp game.hpp \ + object_print.hpp event_printer.hpp iostream_player.hpp \ + mediator.hpp logging.hpp factory_table.hpp storable.hpp \ + common_storables.hpp restorers.hpp game_driver.hpp game_rules.hpp \ + exceptions.hpp +OBJFILES = point.o rectmap.o map.o pathfinder.o unit.o demo.o main.o \ + event.o base.o game.o object_print.o iostream_player.o mediator.o \ + storable.o +LDLIBS = -lm + +all: $(EXENAME) + +$(OBJFILES): $(HEADERS) + +$(EXENAME): $(OBJFILES) + $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +clean: + $(RM) $(EXENAME) $(OBJFILES) diff --git a/8303/Parfentev_Leonid/lab7/base.cpp b/8303/Parfentev_Leonid/lab7/base.cpp new file mode 100644 index 000000000..78079331b --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/base.cpp @@ -0,0 +1,162 @@ +#include +#include +#include + +#include "unit.hpp" +#include "unit_factory.hpp" +#include "event.hpp" +#include "base.hpp" +#include "mediator.hpp" +#include "factory_table.hpp" + +#include "storable.hpp" +#include "common_storables.hpp" + + +bool +Base::canCreateUnit(const std::string &key) const +{ + if (unitsCount() == maxUnitsCount()) { + return false; + } + + if (!FactoryTable::instance()->canCreate(key)) { + return false; + } + + return true; +} + +int +Base::createUnit(const std::string &key, Mediator *m) +{ + if (!canCreateUnit(key)) { + return -1; + } + + if (m->infoAt(position()).unit()) { + return false; + } + + Unit *u = FactoryTable::instance()->create(key); + int id = addUnit(u); + + if (id < 0) { + delete u; + return -1; + } + + if (!m->spawnUnit(u, position())) { + removeUnit(u); + delete u; + return -1; + } + + return id; +} + +bool +Base::setMaxUnitsCount(int m) +{ + if (m < unitsCount()) { + return false; + } + _max_count = m; + return true; +} + +int +Base::addUnit(Unit *u) +{ + if (maxUnitsCount() >= 0 + && unitsCount() == maxUnitsCount()) { + return -1; + } + + _units[_next_idx] = u; + u->subscribe(this); + u->emit(new events::UnitAdded {u}); + + return _next_idx++; +} + +void +Base::removeUnit(Unit *u) +{ + u->unsubscribe(this); + + for (auto iter = _units.begin(); + iter != _units.end(); + ++iter) { + if (iter->second == u) { + _units.erase(iter); + break; + } + } +} + +Unit * +Base::getUnitById(int id) const +{ + auto iter = _units.find(id); + return (iter != _units.end()) + ? iter->second + : nullptr; +} + +bool +Base::becomeDestroyedBy(Unit *u) +{ + for (auto iter = unitsBegin(); + iter != unitsEnd(); + ++iter) { + if (iter.unit() == u) { + return false; + } + } + + _destroyed = true; + + for (auto iter = unitsBegin(); + iter != unitsEnd(); + ++iter) { + Unit *v = iter.unit(); + v->emit(new events::UnitDeath {v}); + } + + emit(new events::BaseDestroyed {this}); + return true; +} + +void +Base::store(std::ostream &os) const +{ + os << "base " << _max_count << " " << _destroyed << "\n"; +} + +bool +Base::restore(std::istream &is, + RestorerTable *) +{ + is >> _max_count >> _destroyed; + return !is.fail(); +} + +void +Base::handle(Event *e) +{ + EventForwarder::handle(e); + + if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } else if (auto *ee = dynamic_cast(e)) { + removeUnit(ee->unit()); + } +} + +Base::~Base() +{ + for (auto p: _units) { + p.second->unsubscribe(this); + } +} diff --git a/8303/Parfentev_Leonid/lab7/base.hpp b/8303/Parfentev_Leonid/lab7/base.hpp new file mode 100644 index 000000000..deadd93cc --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/base.hpp @@ -0,0 +1,98 @@ +#ifndef _H_BASE_HPP +#define _H_BASE_HPP + +#include +#include +#include + +#include "storable.hpp" +#include "placeable.hpp" +#include "event.hpp" +#include "unit.hpp" +#include "mediator.hpp" + + +class Base: public Placeable, + public EventForwarder, + public Storable { + + std::map _units {}; + int _next_idx = 0; + int _max_count = -1; + bool _destroyed = false; + +public: + Base() {} + + virtual bool + canCreateUnit(const std::string &key) const; + virtual int + createUnit(const std::string &key, Mediator *m); + + int + unitsCount() const { return (int)_units.size(); } + bool + setMaxUnitsCount(int m); + int + maxUnitsCount() const { return _max_count; } + + int + addUnit(Unit *u); + void + removeUnit(Unit *u); + Unit * + getUnitById(int id) const; + + virtual void spin() {} + + bool + becomeDestroyedBy(Unit *u); + bool + destroyed() const { return _destroyed; } + + class unitsIter { + using real_iter_t = std::map::const_iterator; + real_iter_t _iter; + + public: + unitsIter(real_iter_t it) + :_iter{it} {} + + int id() const { return _iter->first; } + Unit *unit() const { return _iter->second; } + unitsIter &operator++() { ++_iter; return *this; } + unitsIter + operator++(int) + { + unitsIter x{_iter}; + ++*this; + return x; + } + bool + operator==(const unitsIter &o) const + { + return _iter == o._iter; + } + bool + operator!=(const unitsIter &o) const + { + return !(*this == o); + } + }; + + unitsIter + unitsBegin() const { return unitsIter{_units.begin()}; } + unitsIter + unitsEnd() const { return unitsIter{_units.end()}; } + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &, + RestorerTable *) override; + + virtual void + handle(Event *e) override; + + virtual ~Base() override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/catapult_units.hpp b/8303/Parfentev_Leonid/lab7/catapult_units.hpp new file mode 100644 index 000000000..914c28041 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/catapult_units.hpp @@ -0,0 +1,170 @@ +#ifndef _H_CATAPULT_UNITS_HPP +#define _H_CATAPULT_UNITS_HPP + +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" +#include "ranged_units.hpp" + + +class CatapultAttack: public RangedAttack { + double _spread_tang, _spread_normal; + + struct FVec2 { + double x, y; + + explicit FVec2(const Vec2 &v) + :x{(double)v.x()}, y{(double)v.y()} {} + + FVec2(double x, double y) + :x{x}, y{y} {} + + operator Vec2() const + { + return Vec2{int(round(x)), int(round(y))}; + } + + FVec2 + orthogonal() const { return {y, -x}; } + + FVec2 & + operator*=(double a) + { + x *= a; + y *= a; + return *this; + } + FVec2 + operator*(double a) const + { + FVec2 tmp {*this}; + return tmp *= a; + } + + FVec2 + normalized() const { return (*this) * (1/sqrt(x*x + y*y)); } + + FVec2 & + operator+=(const FVec2 &dxy) + { + x += dxy.x; + y += dxy.y; + return *this; + } + FVec2 + operator+(const FVec2 &dxy) const + { + FVec2 tmp{*this}; + return tmp += dxy; + } + + FVec2 + apply(double t, double n) const + { + return normalized() * t + + orthogonal().normalized() * n; + } + }; + +public: + explicit CatapultAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0, + double min_dist=0, + double max_dist=0, + double dist_pow=0, + double spread_t=0, + double spread_n=0) + :RangedAttack{kind, base_dmg, min_dist, max_dist, dist_pow}, + _spread_tang{spread_t}, + _spread_normal{spread_n} {} + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + Vec2 dest = to.point(); + Vec2 delta = dest.delta(u->position()); + FVec2 fdelta {delta}; + + std::uniform_real_distribution<> + t_dist {-_spread_tang, _spread_tang}, + n_dist {-_spread_normal, _spread_normal}; + + double + t = t_dist(global_random), + n = n_dist(global_random); + + FVec2 result = fdelta.apply(t, n); + return to.shifted(Vec2{result}); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_catapult " << static_cast(_kind) + << " " << _base_damage << " " << _min_distance + << " " << _max_distance << " " + << _dist_pow << " " << _spread_tang << " " + << _spread_normal << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage >> _min_distance + >> _max_distance >> _dist_pow >> _spread_tang + >> _spread_normal; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicCatapultUnit: public Unit { +public: + BasicCatapultUnit(AttackKind attack_kind, + int base_dmg, + double min_dist, + double max_dist, + double dist_pow, + double spread_t, + double spread_n, + int base_health) + :Unit{new BasicMovement {1}, + new CatapultAttack {attack_kind, + base_dmg, min_dist, max_dist, + dist_pow, spread_t, spread_n}, + DefenseLevelDeco::good_defense_deco( + AttackKind::arrow, + new BasicDefense {0.75}), + base_health} {} +}; + +namespace units { + class Onager: public BasicCatapultUnit { + public: + Onager() :BasicCatapultUnit{ + AttackKind::rock, 90, + 3, 10, 0.05, + 0.2, 0.1, + 30} {} + + UNIT_STORABLE_NAME("u_onager"); + }; + + class BoltThrower: public BasicCatapultUnit { + public: + BoltThrower() :BasicCatapultUnit{ + AttackKind::bolt, 110, + 2, 6, 0.15, + 0.05, 0.05, + 20} {} + + UNIT_STORABLE_NAME("u_boltthrower"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/common_policies.hpp b/8303/Parfentev_Leonid/lab7/common_policies.hpp new file mode 100644 index 000000000..99ad30554 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/common_policies.hpp @@ -0,0 +1,308 @@ +#ifndef _H_COMMON_POLICIES_HPP +#define _H_COMMON_POLICIES_HPP + +#include + +#include "unit.hpp" +#include "map.hpp" +#include "pathfinder.hpp" + + +class BasicMovement: public MovePolicy { + int _steps_per_turn; + +public: + explicit BasicMovement(int n=0) + :_steps_per_turn{n} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _steps_per_turn}; + return pf.run(); + } + + virtual void + store(std::ostream &os) const override + { + os << "mp_basic " << _steps_per_turn << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + is >> _steps_per_turn; + return !is.fail(); + } +}; + +class NestedMovement: public MovePolicy, + public MovePolicyContainer { +public: + using MovePolicyContainer::MovePolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setMovePolicy(p); + return true; + } + delete s; + return false; + } +}; + +class ModifyingMovePolicy: public NestedMovement { + int _max; + +public: + explicit ModifyingMovePolicy(MovePolicy *p=nullptr, int max_dist=0) + :NestedMovement{p}, _max{max_dist} {} + + virtual bool + canMove(const Unit *u, MapIter to) override + { + MapIter from = to.otherAt(u->position()); + PathFinder pf {from, to, _max}; + if (!pf.run()) + return false; + + return movePolicy()->canMove(u, to); + } + + virtual void + store(std::ostream &os) const override + { + os << "mp_modifyiing " << _max << "\n"; + movePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _max; + return !is.fail() && NestedMovement::restore(is, tab); + } +}; + + + +class BasicDefense: public DefensePolicy { + double _lvl; + +public: + explicit BasicDefense(double level=1.0) + :_lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *, AttackKind, int base) override + { + return normal_defense(base); + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_basic " << _lvl << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + is >> _lvl; + return !is.fail(); + } +}; + +class NestedDefense: public DefensePolicy, + public DefensePolicyContainer { +public: + using DefensePolicyContainer::DefensePolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setDefensePolicy(p); + return true; + } + delete s; + return false; + } +}; + +class DefenseLevelDeco: public NestedDefense { + // Controls nested policy lifetime + AttackKind _kind; + double _lvl; + +public: + explicit DefenseLevelDeco(DefensePolicy *p=0, + AttackKind kind=AttackKind::invalid, + double level=0) + :NestedDefense{p}, _kind{kind}, _lvl{level} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + if (kind == _kind) + return defense_level(_lvl, base); + return defensePolicy()->actualDamage(u, kind, base); + } + + static DefenseLevelDeco * + defense_level_deco(AttackKind kind, double lvl, DefensePolicy *p) + { + return new DefenseLevelDeco {p, kind, lvl}; + } + + static DefenseLevelDeco * + good_defense_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 2.0, p); + } + + static DefenseLevelDeco * + vulnerability_deco(AttackKind kind, DefensePolicy *p) + { + return defense_level_deco(kind, 0.5, p); + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_level_deco " << static_cast(_kind) + << " " << _lvl << "\n"; + defensePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + int x; + is >> x; + _kind = static_cast(x); + is >> _lvl; + + return !is.fail() && NestedDefense::restore(is, tab); + } +}; + +class MultiplierDefensePolicy: public NestedDefense { + double _mul; + +public: + explicit MultiplierDefensePolicy(DefensePolicy *p=nullptr, + double mul=0) + :NestedDefense{p}, _mul{mul} {} + + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) override + { + DamageSpec ds = defensePolicy()->actualDamage(u, kind, base); + ds.scale(1/_mul); + return ds; + } + + virtual void + store(std::ostream &os) const override + { + os << "dp_multiplier " << _mul << "\n"; + defensePolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _mul; + return !is.fail() && NestedDefense::restore(is, tab); + } +}; + + + +class AttackForbidden: public AttackPolicy { +public: + using AttackPolicy::AttackPolicy; + + virtual bool + canAttackTo(const Unit *, MapIter) override + { + return false; + } + + virtual std::pair + baseAttack(const Unit *, MapIter) override + { + return std::make_pair(AttackKind::invalid, 0); + } + + TRIVIALLY_STORABLE("ap_forbidden"); +}; + +class NestedAttack: public AttackPolicy, + public AttackPolicyContainer { +public: + using AttackPolicyContainer::AttackPolicyContainer; + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + if (auto *p = dynamic_cast(s)) { + setAttackPolicy(p); + return true; + } + delete s; + return false; + } +}; + +class MultiplierAttackPolicy: public NestedAttack { + double _mul; + +public: + explicit MultiplierAttackPolicy(AttackPolicy *p=nullptr, + double mul=0) + :NestedAttack{p}, _mul{mul} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return attackPolicy()->canAttackTo(u, to); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + auto ba = attackPolicy()->baseAttack(u, to); + return std::make_pair(ba.first, (int)(ba.second * _mul)); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_multiplier " << _mul << "\n"; + attackPolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _mul; + return !is.fail() && NestedAttack::restore(is, tab); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab7/common_storables.hpp b/8303/Parfentev_Leonid/lab7/common_storables.hpp new file mode 100644 index 000000000..27cfd5a0a --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/common_storables.hpp @@ -0,0 +1,117 @@ +#ifndef _H_COMMON_STORABLES_HPP +#define _H_COMMON_STORABLES_HPP + +#include "storable.hpp" +#include "object_print.hpp" + + +class StorableEnd: public Storable { + TRIVIALLY_STORABLE("end"); +}; + +class StorableCoordinates: public Storable { + Vec2 _c; + +public: + StorableCoordinates() {} + + StorableCoordinates(Vec2 c) :_c{c} {} + + virtual void + store(std::ostream &os) const override + { + os << "coords " << _c.x() << " " << _c.y() << "\n"; + } + + virtual bool + restore(std::istream &is, + RestorerTable *) override + { + int x, y; + is >> x >> y; + _c = Vec2{x, y}; + return !is.fail(); + } + + Vec2 coords() const { return _c; } +}; + +class StorableWithIndex: public Storable { + int _i; + Storable *_s; + +public: + StorableWithIndex() {} + + StorableWithIndex(int idx, Storable *s) + :_i{idx}, _s{s} {} + + virtual void + store(std::ostream &os) const override + { + os << "index " << _i << " "; + _s->store(os); + } + + virtual bool + restore(std::istream &is, + RestorerTable *tab) override + { + is >> _i; + _s = tab->restore(is); + return !is.fail() && _s; + } + + int index() const { return _i; } + Storable *child() const { return _s; } + + static void + storeWithIndex(int idx, const Storable *s, + std::ostream &os) + { + os << "index " << idx << " "; + s->store(os); + } +}; + +class StorableWithCoords: public Storable { + Vec2 _c; + Storable *_s; + +public: + StorableWithCoords() {} + + StorableWithCoords(Vec2 c, Storable *s) + :_c{c}, _s{s} {} + + virtual void + store(std::ostream &os) const override + { + os << "at " << _c.x() << " " << _c.y() << " "; + _s->store(os); + } + + virtual bool + restore(std::istream &is, + RestorerTable *tab) override + { + int x, y; + is >> x >> y; + _c = Vec2{x, y}; + _s = tab->restore(is); + return !is.fail() && _s; + } + + Vec2 coords() const { return _c; } + Storable *child() const { return _s; } + + static void + storeWithCoords(Vec2 pt, const Storable *s, + std::ostream &os) + { + os << "at " << pt.x() << " " << pt.y() << " "; + s->store(os); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/demo.cpp b/8303/Parfentev_Leonid/lab7/demo.cpp new file mode 100644 index 000000000..2c438ae19 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/demo.cpp @@ -0,0 +1,520 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" + +#include "event.hpp" +#include "event_types.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "unit_factory.hpp" + +#include "game.hpp" +#include "event_printer.hpp" +#include "iostream_player.hpp" + +#include "factory_table.hpp" + + +void +demo1() +{ + // create a map + auto *map = new Map {10, 10}; + + // create a few units + auto sw1_iter = map->addUnit(new units::Swordsman {}, {3, 3}); + map->addUnit(new units::Swordsman {}, {4, 4}); + + // write the map + std::cout << map << "\n"; + + // test the pathfinder + static const std::vector pts { + {3, 4}, + {3, 3}, + {5, 5}, + {5, 4}, + {5, 3}, + }; + for (auto pt: pts) { + std::cout << sw1_iter.unit() << " " + << ((sw1_iter.unit()->canMove(map->iterAt(pt))) + ? "can" : "can't") + << " move to " << pt << "\n"; + } + + // clean up + delete map; +} + +void +demo2() +{ + auto *map = new Map {10, 10}; + + auto c_iter = map->addUnit(new units::Cavalry {}, {3, 3}); + auto *u = c_iter.unit(); + + std::cout << map << "\n"; + + static const std::vector path { + {4, 5}, {6, 5}, {9, 5}, {8, 7}, {7, 9}, + }; + for (auto pt: path) { + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + std::cout << u << " can't move to " << pt << "!\n"; + break; + } + + std::cout << "moving " << u + << " to " << pt << "...\n"; + + c_iter = map->addUnit(map->removeUnitAt(c_iter), pt); + if (c_iter.null()) { + std::cout << "failed!\n"; + } + } + + std::cout << "\n" << map; + + delete map; +} + +void +demo3() +{ + auto *map = new Map {10, 10}; + map->setMaxUnitsCount(2); + + Unit *sw = new units::Swordsman {}; + Unit *ar = new units::Archer {}; + + map->addUnit(sw, {5, 0}); + map->addUnit(ar, {5, 9}); + + Unit *x = new units::Swordsman {}; + if (!map->addUnit(x, {1, 1}).null()) { + std::cout << "Added one more unit!\n"; + delete map->removeUnitAt({1, 1}); + } else { + std::cout << "Max units: " << map->maxUnitsCount() + << ", current units: " << map->unitsCount() + << "\n"; + delete x; + } + + std::cout << map; + + while (sw->alive() && ar->alive()) { + Vec2 from = sw->position(); + Vec2 to = from.shifted({0, 1}); + + std::cout << "\nmoving " << sw << " from " << from + << " to " << to << "...\n"; + + if (!sw->canMove(map->iterAt(to))) { + std::cout << "can't move\n"; + break; + } + + if (map->addUnit(map->removeUnitAt(from), to).null()) { + std::cout << "failed\n"; + break; + } + + std::cout << ar << " shoots at " << sw->position() << "...\n"; + + auto toi = map->iterAt(sw->position()); + if (!ar->canAttackTo(toi)) { + std::cout << "can't shoot\n"; + // it’s ok + } else { + auto pos = ar->actualPosition(toi); + if (Unit *targ = pos.unit()) { + auto dam = ar->baseAttack(toi); + + std::cout << "hits " << targ << "\n"; + + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + + std::cout << "now: " << targ << "\n"; + } + } + } +} + +MapIter +doAttack(Unit *u, MapIter to) +{ + if (!u->canAttackTo(to)) { + return MapIter::makeNull(); + + } else { + auto pos = u->actualPosition(to); + + if (Unit *targ = pos.unit()) { + auto dam = u->baseAttack(pos); + targ->takeDamage( + targ->actualDamage( + dam.first, dam.second).evaluate()); + return pos; + } + + return MapIter::makeNull(); + } +} + +struct SimpleGame { + Map *map; + Base *b1, *b2; + EventPrinter *pr; + + explicit SimpleGame(int w=10, int h=10, + int x1=1, int y1=1, + int x2=9, int y2=9) + { + pr = new EventPrinter {std::cout}; + + map = new Map {w, h}; + + b1 = new Base {}; + pr->setPrefix(b1, "Base 1"); + + map->addBase(b1, {x1, y1}); + b1->subscribe(pr); + + b2 = new Base {}; + pr->setPrefix(b2, "Base 2"); + + map->addBase(b2, {x2, y2}); + b2->subscribe(pr); + } + + ~SimpleGame() + { + delete map; + delete pr; + } +}; + +void +demo4() +{ + SimpleGame g {}; + + Unit *u1 = new units::Swordsman {}; + Unit *u2 = new units::Swordsman {}; + + g.b1->addUnit(u1); + g.b2->addUnit(u2); + + g.map->addUnit(u1, {3, 3}); + g.map->addUnit(u2, {4, 3}); + + while (u1->alive() + && u2->alive()) { + doAttack(u1, g.map->iterAt(u2->position())); + if (u2->alive()) { + doAttack(u2, g.map->iterAt(u1->position())); + } + } +} + +MapIter +doMove(Map *map, const Unit *u, Vec2 pt) +{ + auto from = map->iterAt(u->position()); + auto to = map->iterAt(pt); + + if (!u->canMove(to)) { + return MapIter::makeNull(); + } + + return map->addUnit(map->removeUnitAt(from), pt); +} + +Unit * +setupUnit(Base *base, const std::string &k, Mediator *m, Vec2 pt) +{ + Unit *u = base->getUnitById(base->createUnit(k, m)); + m->teleportUnit(u, pt); + return u; +} + +void +demo5() +{ + SimpleGame g {}; + + // 2,2 .. 5,5: swamp + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + g.map->setLandscape(new landscapes::Swamp {}, + {2+i, 2+j}); + } + } + + // 1,7 .. 6,9: forest + for (int j = 0; j < 2; ++j) { + for (int i = 0; i < 5; ++i) { + g.map->setLandscape(new landscapes::Forest {}, + {1+i, 7+j}); + } + } + + auto *m = new Mediator {g.map}; + auto u1 = setupUnit(g.b1, "swordsman", m, {2, 2}); + auto u2 = setupUnit(g.b2, "swordsman", m, {3, 2}); + + if (doMove(g.map, u1, {0, 2}).null()) { + std::cout << "Can't move " << u1 << " across 2 cells\n"; + } else { + std::cout << "Moved " << u1 << " across 2 cells \n"; + } + + std::cout << "u1: " << u1 << "\n"; + + if (doAttack(u1, g.map->iterAt(u2->position())).null()) { + std::cout << "Can't attack in swamp\n"; + } else { + std::cout << "Attacked in a swamp\n"; + } + + std::cout << "u2: " << u2 << "\n"; + + doMove(g.map, u1, {1, 2}); + doMove(g.map, u2, {2, 3}); + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + doAttack(u1, g.map->iterAt(u2->position())); + + auto u3 = setupUnit(g.b1, "spearsman", m, {3, 8}); + auto u4 = setupUnit(g.b1, "spearsman", m, {7, 8}); + auto u5 = setupUnit(g.b2, "onager", m, {5, 5}); + + while (u3->alive()) + doAttack(u5, g.map->iterAt(u3->position())); + + while (u4->alive()) + doAttack(u5, g.map->iterAt(u4->position())); + + std::cout << g.map; + + delete m; +} + +void +demo6() +{ + SimpleGame g {}; + + auto *m = new Mediator {g.map}; + auto *u1 = setupUnit(g.b1, "slinger", m, {1, 5}); + auto *u2 = setupUnit(g.b2, "swordsman", m, {6, 5}); + + g.map->addNeutralObject(new objects::Tower {}, {1, 5}); + g.map->addNeutralObject(new objects::HealingWell {}, {6, 5}); + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " can't reach " << u2 << "\n"; + } else { + std::cout << u1 << " somehow reached " << u2 << "\n"; + } + + if (m->useObject(u1)) { + std::cout << u1 << " used the tower\n"; + } else { + std::cout << u1 << " can't use the tower\n"; + } + + if (m->attackTo(u1, u2->position())) { + std::cout << u1 << " still can't reach " << u2 << "\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; + + if (m->useObject(u2)) { + std::cout << u2 << " used the healing well\n"; + } + + std::cout << "u1: " << u1 << "\n"; + std::cout << "u2: " << u2 << "\n"; +} + +void +demo7() +{ + class MoverPlayer: public Player { + std::string _unit_type; + int _id = -1; + + public: + using Player::Player; + + MoverPlayer(std::string name, + std::string type) + :Player{name}, _unit_type{std::move(type)} {} + + virtual bool + takeTurn(const Game *, Mediator *m, Base *b) override + { + if (_id >= 0) { + Unit *u = b->getUnitById(_id); + m->moveUnitTo(u, u->position().shifted({1, 0})); + } else { + _id = b->createUnit(_unit_type, m); + } + return true; + } + }; + + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + auto *b = new Base {}; + map->addBase(b, {3, 3}); + int b_id = g.addBase(b); + + auto *p = new MoverPlayer {"Player 1", "swordsman"}; + g.setPlayer(b_id, p); + + for (int i = 0; i < 5; ++i) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo8() +{ + auto *map = new Map {10, 10}; + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + std::stringstream s1 {}; + s1 << "base\n" + << "create spearsman\n" + << "map 0 0 9 9\n" + << "move 0 3 5\n" + << "map 0 0 9 9\n" + << "describe 3 5\n"; + + auto *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + std::stringstream s2 {}; + s2 << "create archer\n" + << "attack 0 3 5\n"; + + auto *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} + +void +demo9() +{ + Map *map = new Map {10, 10}; + + auto *uf = (new objects + ::WeaponSmiths + ::CatapultUnitFilter {}); + auto *ws = new objects::WeaponSmiths {2.0, uf}; + map->addNeutralObject(ws, {3, 4}); + + Game g {map}; + + auto *pr = new EventPrinter {std::cout}; + g.subscribe(pr); + + Base *b1 = new Base {}; + map->addBase(b1, {3, 3}); + int b1_id = g.addBase(b1); + + Base *b2 = new Base {}; + map->addBase(b2, {7, 3}); + int b2_id = g.addBase(b2); + + std::stringstream s1 {}; + std::stringstream s2 {}; + + s1 << "create onager\n" + << "move 0 3 4\n" + << "use 0\n" + << "create onager\n" + << "move 1 3 2\n" + << "attack 0 7 4\n" + << "attack 1 7 2\n" + << "create swordsman\n" + << "move 0 3 5\n" + << "move 2 3 4\n" + << "use 2\n"; + + s2 << "create swordsman\n" + << "move 0 7 4\n" + << "create swordsman\n" + << "move 1 7 2\n" + << "skip skip skip skip skip\n"; + + auto *p1 = new IostreamPlayer {"Player 1"}; + p1->setOstream(std::cout); + p1->setIstream(s1); + g.setPlayer(b1_id, p1); + + auto *p2 = new IostreamPlayer {"Player 2"}; + p2->setOstream(std::cout); + p2->setIstream(s2); + g.setPlayer(b2_id, p2); + + while (g.playersCount()) + g.spin(); + + g.unsubscribe(pr); + delete pr; +} diff --git a/8303/Parfentev_Leonid/lab7/demo.hpp b/8303/Parfentev_Leonid/lab7/demo.hpp new file mode 100644 index 000000000..ae8bc7a95 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/demo.hpp @@ -0,0 +1,14 @@ +#ifndef _H_DEMO_HPP +#define _H_DEMO_HPP + +void demo1(); +void demo2(); +void demo3(); +void demo4(); +void demo5(); +void demo6(); +void demo7(); +void demo8(); +void demo9(); + +#endif diff --git a/8303/Parfentev_Leonid/lab7/event.cpp b/8303/Parfentev_Leonid/lab7/event.cpp new file mode 100644 index 000000000..b7ff78ebd --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/event.cpp @@ -0,0 +1,31 @@ +#include "event.hpp" + +void +EventEmitter::emit_shared(Event *e) const +{ + for (auto iter = _listeners.begin(); iter != _listeners.end();) { + auto *listener = *iter++; + // note: the listener may safely unsubscribe when handling the + // event. + listener->handle(e); + } +} + +void +EventEmitter::emit(Event *e) const +{ + emit_shared(e); + delete e; +} + +void +EventEmitter::subscribe(EventListener *l) +{ + _listeners.insert(l); +} + +void +EventEmitter::unsubscribe(EventListener *l) +{ + _listeners.erase(l); +} diff --git a/8303/Parfentev_Leonid/lab7/event.hpp b/8303/Parfentev_Leonid/lab7/event.hpp new file mode 100644 index 000000000..fa2d1f541 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/event.hpp @@ -0,0 +1,62 @@ +#ifndef _H_EVENT_HPP +#define _H_EVENT_HPP + +#include + +class EventListener; +class Event; + +class EventEmitter { + std::set _listeners {}; + +public: + void emit_shared(Event *e) const; + void emit(Event *e) const; + + void subscribe(EventListener *l); + void unsubscribe(EventListener *l); + + virtual ~EventEmitter() {} +}; + +class Event { +public: + virtual ~Event() {} +}; + +class EventListener { +public: + virtual void handle(Event *e) =0; + + virtual ~EventListener() {} +}; + +class EventForwarder; + +namespace events { + + class Forwarded: public Event { + Event *_e; + EventForwarder *_f; + + public: + Forwarded(Event *e, EventForwarder *f) + :_e{e}, _f{f} {} + + Event *event() const { return _e; } + EventForwarder *forwarder() const { return _f; } + }; + +} + +class EventForwarder: public EventEmitter, + public EventListener { +public: + virtual void + handle(Event *e) override + { + emit(new events::Forwarded {e, this}); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/event_printer.hpp b/8303/Parfentev_Leonid/lab7/event_printer.hpp new file mode 100644 index 000000000..afeca85b3 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/event_printer.hpp @@ -0,0 +1,127 @@ +#ifndef _H_EVENT_PRINTER_HPP +#define _H_EVENT_PRINTER_HPP + +#include +#include +#include +#include + +#include "event.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "player.hpp" +#include "object_print.hpp" + + +class EventPrinter: public EventListener { + std::ostream *_os; + bool _free_os; + + std::map _prefix_map {}; + + std::string + makeName(const char *base, int idx) + { + std::ostringstream oss {}; + oss << base << " " << idx; + return oss.str(); + } + +public: + EventPrinter(std::ostream &os) + :_os{&os}, _free_os{false} {} + + EventPrinter(std::ostream *os) + :_os{os}, _free_os{true} {} + + std::ostream & + ostream() const { return *_os; } + + void + setPrefix(EventForwarder *f, const std::string &s) + { + _prefix_map[f] = s; + } + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + auto iter = _prefix_map.find(ee->forwarder()); + if (iter != _prefix_map.end()) { + (*_os) << iter->second << ": "; + } + + return handle(ee->event()); + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit added: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit died: " << ee->unit() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " takes " + << ee->damage() << " health points of damage\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() << " gets healed by " + << ee->health() << " health points\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->attacker() + << " attacked another unit " << ee->target() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->target() + << " was attacked by another unit " + << ee->attacker() << "\n"; + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " moved from " << ee->sourcePos() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Unit " << ee->unit() + << " used object " << ee->neutralObject() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "(Live unit " << ((void *)ee->unit()) + << " deleted)\n"; + + } else if (dynamic_cast(e)) { + (*_os) << "Base destroyed\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << "\n"; + + } else if (auto *ee = + dynamic_cast(e)) { + (*_os) << "Turn of player " + << ee->player()->name() << " over\n"; + + } else { + (*_os) << "Unknown event\n"; + } + } + + virtual ~EventPrinter() override + { + if (_free_os) { + _os->flush(); + delete _os; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/event_types.hpp b/8303/Parfentev_Leonid/lab7/event_types.hpp new file mode 100644 index 000000000..810d3bd94 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/event_types.hpp @@ -0,0 +1,156 @@ +#ifndef _H_EVENT_TYPES_HPP +#define _H_EVENT_TYPES_HPP + +#include "point.hpp" +#include "event.hpp" + + +class Unit; +class Base; +class NeutralObject; +class Player; + + +class UnitEvent: public Event { + Unit *_u; + +public: + UnitEvent(Unit *u) :_u{u} {} + + Unit *unit() const { return _u; } +}; + +class AttackEvent: public Event { + Unit *_a, *_b; + +public: + AttackEvent(Unit *a, Unit *b) :_a{a}, _b{b} {} + + Unit *attacker() const { return _a; } + Unit *target() const { return _b; } +}; + +class BaseEvent: public Event { + Base *_b; + +public: + BaseEvent(Base *b) :_b{b} {} + + Base *base() const { return _b; } +}; + +class PlayerEvent: public Event { + Player *_p; + +public: + PlayerEvent(Player *p) :_p{p} {} + + Player *player() const { return _p; } +}; + +class UnitMoveEvent: public UnitEvent { + Vec2 _src; + +public: + UnitMoveEvent(Unit *u, Vec2 src) + :UnitEvent{u}, _src{src} {} + + Vec2 sourcePos() const { return _src; } +}; + + + +namespace events { + + class UnitDeath: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitAdded: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitLiveDeleted: public UnitEvent { + public: + using UnitEvent::UnitEvent; + }; + + class UnitTakesDamage: public UnitEvent { + int _dmg; + + public: + UnitTakesDamage(Unit *u, int dmg) + :UnitEvent{u}, _dmg{dmg} {} + + int damage() const { return _dmg; } + }; + + class UnitGetsHealed: public UnitEvent { + int _hp; + + public: + UnitGetsHealed(Unit *u, int hp) + :UnitEvent{u}, _hp{hp} {} + + int health() const { return _hp; } + }; + + class UnitWasAttacked: public AttackEvent { + public: + using AttackEvent::AttackEvent; + }; + + class UnitAttacked: public AttackEvent { + Vec2 _tc; + + public: + UnitAttacked(Unit *u, Vec2 tc, Unit *tu) + :AttackEvent{u, tu}, _tc{tc} {} + + Vec2 targetCoordinates() const { return _tc; } + }; + + class UnitMoved: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitTeleported: public UnitMoveEvent { + public: + using UnitMoveEvent::UnitMoveEvent; + }; + + class UnitUsedObject: public UnitEvent { + NeutralObject *_n; + + public: + UnitUsedObject(Unit *u, NeutralObject *n) + :UnitEvent{u}, _n{n} {} + + NeutralObject *neutralObject() const { return _n; } + }; + + + + class BaseDestroyed: public BaseEvent { + public: + using BaseEvent::BaseEvent; + }; + + + + class TurnStarted: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + + class TurnOver: public PlayerEvent { + public: + using PlayerEvent::PlayerEvent; + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/exceptions.hpp b/8303/Parfentev_Leonid/lab7/exceptions.hpp new file mode 100644 index 000000000..9822a5084 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/exceptions.hpp @@ -0,0 +1,57 @@ +#ifndef _H_EXCEPTIONS_HPP +#define _H_EXCEPTIONS_HPP + +#include +#include +#include + + +class NoSavegameFile: public std::exception { + std::string _fn; + + mutable std::string _msg {""}; + +public: + NoSavegameFile(const std::string &fn) + :_fn{fn} {} + + virtual const char * + what() const noexcept override + { + if (!_msg.length()) { + std::ostringstream oss {}; + oss << "Can't open savegame file: " << _fn; + _msg = oss.str(); + } + return _msg.c_str(); + } +}; + +class InvalidSaveFileContents: public std::exception { + using pos_t = std::istream::pos_type; + + std::string _fn; + pos_t _pos; + + mutable std::string _msg {""}; + +public: + InvalidSaveFileContents(const std::string &fn, pos_t pos) + :_fn{fn}, _pos{pos} {} + + virtual const char * + what() const noexcept override + { + if (!_msg.length()) { + std::ostringstream oss {}; + oss << "Error while reading savegame file " << _fn; + if (_pos >= 0) { + oss << " around position " << _pos; + } + _msg = oss.str(); + } + return _msg.c_str(); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/factory_table.hpp b/8303/Parfentev_Leonid/lab7/factory_table.hpp new file mode 100644 index 000000000..693031648 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/factory_table.hpp @@ -0,0 +1,81 @@ +#ifndef _H_FACTORY_TABLE_HPP +#define _H_FACTORY_TABLE_HPP + +#include +#include + +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "unit_factory.hpp" + + +class FactoryTable { + std::map _tab {}; + + void + registerFactory(const std::string &key, UnitFactory *f) + { + _tab[key] = f; + } + + FactoryTable() + { +#define REG(k, T) \ + registerFactory( \ + k, new SimpleUnitFactory {}) + REG("swordsman", Swordsman); + REG("spearsman", Spearsman); + REG("cavalry", Cavalry); + REG("archer", Archer); + REG("slinger", Slinger); + REG("onager", Onager); + REG("boltthrower", BoltThrower); +#undef REG + } + +public: + static const FactoryTable * + instance() + { + static FactoryTable *inst = new FactoryTable {}; + return inst; + } + + virtual bool + canCreate(const std::string &key) const + { + return _tab.find(key) != _tab.end(); + } + + virtual std::vector + keys() const + { + std::vector keys {}; + for (auto p: _tab) { + keys.push_back(p.first); + } + return keys; + } + + virtual Unit * + create(const std::string &key) const + { + auto iter = _tab.find(key); + if (iter == _tab.end()) { + return nullptr; + } + + return iter->second->create(); + } + + ~FactoryTable() + { + for (auto p: _tab) { + delete p.second; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/game.cpp b/8303/Parfentev_Leonid/lab7/game.cpp new file mode 100644 index 000000000..dd9272b12 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/game.cpp @@ -0,0 +1,215 @@ +#include +#include + +#include "base.hpp" +#include "game.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "storable.hpp" +#include "common_storables.hpp" + + +Game::Game(Map *map) + :_map{map}, + _med{new Mediator {map}} +{ + this->subscribe(_log_sink); +} + +int +Game::addBase(Base *base) +{ + base->subscribe(_coll); + base->subscribe(this); + + _recs.push_back(BaseRecord{base, nullptr}); + + return (int)(_recs.size() - 1); +} + +void +Game::setPlayer(int base_idx, Player *p) +{ + Player *&p_place = _recs[base_idx].player; + if (p_place) { + p_place->unsubscribe(_log_sink); + delete p_place; + --_players; + } + p_place = p; + if (p) { + p->subscribe(_log_sink); + ++_players; + } +} + +int +Game::basesCount() const +{ + return _recs.size(); +} + +int +Game::playersCount() const +{ + return _players; +} + +Base * +Game::baseByIdx(int idx) const +{ + return _recs[idx].base; +} + +void +Game::spin() +{ + auto &rec = _recs[_next]; + Player *p = rec.player; + Base *b = rec.base; + + if (p) { + if (b->destroyed()) { + setPlayer(_next, nullptr); + + } else { + emit(new events::TurnStarted {p}); + + bool res = p->takeTurn(this, _med, b); + + _coll->collect(_map); + + emit(new events::TurnOver {p}); + + if (!res) { + setPlayer(_next, nullptr); + } + } + } + + if (++_next == (int)_recs.size()) { + _next = 0; + } +} + +void +Game::setResetHandler(ResetHandler *r) +{ + _reset = r; +} + +void +Game::requestReset() const +{ + _reset->reset(); +} + +void +Game::store(std::ostream &os) const +{ + os << "game\n"; + _map->store(os); + + os << _next << "\n"; + + for (int i = 0; i < basesCount(); ++i) { + auto *b = baseByIdx(i); + + StorableWithCoords::storeWithCoords(b->position(), b, os); + + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + auto *u = iter.unit(); + auto *sc = new StorableWithCoords {u->position(), u}; + StorableWithIndex::storeWithIndex(i, sc, os); + } + } + + for (int i = 0; i < basesCount(); ++i) { + auto *p = _recs[i].player; + if (p) { + StorableWithIndex::storeWithIndex(i, p, os); + } + } + + os << "end\n"; +} + +bool +Game::restore(std::istream &is, + RestorerTable *tab) +{ + is >> _next; + + for (;;) { + Storable *s = tab->restore(is); + if (auto *sc = + dynamic_cast(s)) { + if (auto *b + = dynamic_cast(sc->child())) { + _map->addBase(b, sc->coords()); + addBase(b); + } else { + delete sc->child(); + delete sc; + return false; + } + delete sc; + } else if (auto *si = + dynamic_cast(s)) { + if (auto *p + = dynamic_cast(si->child())) { + if (si->index() < 0 + || si->index() >= basesCount()) { + delete p; + delete si; + return false; + } + setPlayer(si->index(), p); + } else if (auto *sc = + dynamic_cast( + si->child())) { + if (auto *u + = dynamic_cast(sc->child())) { + _map->addUnit(u, sc->coords()); + baseByIdx(si->index())->addUnit(u); + delete sc; + } else { + delete si; + delete sc; + delete sc->child(); + return false; + } + } else { + delete si->child(); + delete si; + return false; + } + delete si; + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} + +Game::~Game() +{ + for (int i = 0; i < (int)_recs.size(); ++i) { + setPlayer(i, nullptr); + auto &rec = _recs[i]; + rec.base->unsubscribe(this); + rec.base->unsubscribe(_coll); + } + + delete _coll; + delete _map; + + delete _log_sink; +} diff --git a/8303/Parfentev_Leonid/lab7/game.hpp b/8303/Parfentev_Leonid/lab7/game.hpp new file mode 100644 index 000000000..732271be2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/game.hpp @@ -0,0 +1,68 @@ +#ifndef _H_GAME_HPP +#define _H_GAME_HPP + +#include + +#include "event.hpp" +#include "map.hpp" +#include "base.hpp" +#include "player.hpp" +#include "zombie_collector.hpp" +#include "mediator.hpp" +#include "storable.hpp" + + +class ResetHandler { +public: + virtual void reset() =0; + + virtual ~ResetHandler() {} +}; + +class Game: public EventForwarder, + public Storable { + Map *_map; + Mediator *_med; + + struct BaseRecord { + Base *base; + Player *player; + }; + + std::vector _recs {}; + int _next = 0; + int _players = 0; + + ZombieCollector *_coll {new ZombieCollector {}}; + + EventForwarder *_log_sink {new EventForwarder {}}; + + ResetHandler *_reset; + +public: + Game(Map *map); + + int addBase(Base *b); + void setPlayer(int base_idx, Player *p); + + int basesCount() const; + int playersCount() const; + + Base *baseByIdx(int idx) const; + + EventForwarder *logSink() const { return _log_sink; } + + void spin(); + + void setResetHandler(ResetHandler *r); + void requestReset() const; + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; + + ~Game(); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab7/game_driver.hpp b/8303/Parfentev_Leonid/lab7/game_driver.hpp new file mode 100644 index 000000000..79f78c3fd --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/game_driver.hpp @@ -0,0 +1,123 @@ +#ifndef _H_GAME_DRIVER_HPP +#define _H_GAME_DRIVER_HPP + +#include "map.hpp" +#include "game.hpp" + + +class GameRules { +public: + virtual Map *makeMap() =0; + virtual void setup(Game *g, Map *m) =0; + virtual bool gameEnded(Game *g) =0; + virtual int winner(Game *g) =0; +}; + +template +class GameDriver: public ResetHandler { + + Game *_g = nullptr; + Game *_g_reset = nullptr; // required to reset game while running + Rules *_r {new Rules {}}; + + std::vector _loggers {}; + EventPrinter *_printer = nullptr; + + Game *init() + { + Map *m = _r->makeMap(); + Game *g = new Game {m}; + _r->setup(g, m); + return g; + } + + void setBasePrefixes(EventPrinter *p) + { + int n = _g->basesCount(); + for (int i = 0; i < n; ++i) { + std::ostringstream oss {}; + oss << "Base " << (i+1); + p->setPrefix(_g->baseByIdx(i), oss.str()); + } + } + +public: + void + addLogger(EventPrinter *l) + { + _loggers.push_back(l); + if (_g) { + _g->logSink()->subscribe(l); + setBasePrefixes(l); + } + } + + void + setPrinter(EventPrinter *pr) + { + if (_printer) { + if (_g) { + _g->unsubscribe(_printer); + } + delete _printer; + } + + _printer = pr; + if (_g && _printer) { + _g->subscribe(_printer); + setBasePrefixes(_printer); + } + } + + virtual void reset() override + { + resetFrom(init()); + } + + void resetFrom(Game *g) + { + _g_reset = g; + } + + void run() + { + for (;;) { + if (_g_reset) { + delete _g; + _g = _g_reset; + _g_reset = nullptr; + _g->setResetHandler(this); + + if (_printer) { + _g->subscribe(_printer); + setBasePrefixes(_printer); + } + for (auto *l: _loggers) { + _g->logSink()->subscribe(l); + setBasePrefixes(l); + } + } + if (_r->gameEnded(_g)) { + break; + } + _g->spin(); + } + } + + int winner() + { + return _r->winner(_g); + } + + virtual ~GameDriver() override + { + delete _g; + delete _g_reset; + delete _printer; + for (auto *l: _loggers) { + delete l; + } + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/game_rules.hpp b/8303/Parfentev_Leonid/lab7/game_rules.hpp new file mode 100644 index 000000000..6a43579aa --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/game_rules.hpp @@ -0,0 +1,145 @@ +#ifndef _H_GAME_RULES_HPP +#define _H_GAME_RULES_HPP + +#include +#include + +#include "map.hpp" +#include "game.hpp" +#include "iostream_player.hpp" +#include "game_driver.hpp" +#include "landscape_types.hpp" + + +class BaseWithSpawnCountdown: public Base { + int _cd_max; + int _cd = 0; + +public: + BaseWithSpawnCountdown(int cd_max=0) + :_cd_max{cd_max} {} + + virtual bool + canCreateUnit(const std::string &key) const override + { + return _cd == 0 + && Base::canCreateUnit(key); + } + + virtual int + createUnit(const std::string &key, Mediator *m) override + { + int id = Base::createUnit(key, m); + if (id < 0) { + return id; + } + + _cd = _cd_max; + return id; + } + + virtual void + store(std::ostream &os) const override + { + os << "base_w_countdown " << maxUnitsCount() + << " " << destroyed() << " " << _cd_max << " " + << _cd << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) override + { + if (!Base::restore(is, tab)) { + return false; + } + + is >> _cd_max >> _cd; + return !is.fail(); + } + + virtual void + spin() override + { + if (_cd > 0) { + --_cd; + } + } +}; + +class DefaultRules: public GameRules { +protected: + static void + addBaseAndPlayer(Game *g, Map *m, + std::string name, int x, int y) + { + Base *b = new BaseWithSpawnCountdown {5}; + m->addBase(b, {x, y}); + int id = g->addBase(b); + + auto *p = new IostreamPlayer {std::move(name)}; + p->setOstream(std::cout); + p->setIstream(std::cin); + g->setPlayer(id, p); + } + +public: + virtual Map *makeMap() override + { + return new Map {10, 10}; + } + + virtual void setup(Game *g, Map *m) override + { + addBaseAndPlayer(g, m, "Player 1", 1, 1); + addBaseAndPlayer(g, m, "Player 2", 8, 8); + } + + virtual bool gameEnded(Game *g) override + { + return g->playersCount() < 2; + } + + virtual int winner(Game *g) override + { + int n = g->basesCount(); + int only = -1; + + for (int i = 0; i < n; ++i) { + if (!g->baseByIdx(i)->destroyed()) { + if (only < 0) { + only = i; + } else { + return -1; + } + } + } + + return only; + } +}; + +class FancyRules: public DefaultRules { + virtual Map *makeMap() override + { + Map *m = new Map {15, 15}; + + for (int y = 0; y < 5; ++y) { + for (int x = 0; x < 5; ++x) { + m->setLandscape(new landscapes::Forest {}, + {5+x, 5+y}); + } + } + + return m; + } + + virtual void setup(Game *g, Map *m) override + { + addBaseAndPlayer(g, m, "Player 1", 3, 3); + addBaseAndPlayer(g, m, "Player 2", 3, 11); + addBaseAndPlayer(g, m, "Player 3", 11, 11); + addBaseAndPlayer(g, m, "Player 4", 11, 3); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/iostream_player.cpp b/8303/Parfentev_Leonid/lab7/iostream_player.cpp new file mode 100644 index 000000000..b66882c5e --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/iostream_player.cpp @@ -0,0 +1,122 @@ +#include +#include +#include + +#include "game.hpp" +#include "base.hpp" +#include "iostream_player.hpp" +#include "event.hpp" +#include "logging.hpp" + + +void +IostreamPlayer::addCommand(const std::string &str, + IostreamCommand *cmd) +{ + _cmd_tab[str] = cmd; +} + +IostreamPlayer::IostreamPlayer(std::string name) + :Player{name} +{ + addCommand("move", new iostream_commands::Move {}); + addCommand("attack", new iostream_commands::Attack {}); + addCommand("use", new iostream_commands::Use {}); + addCommand("destroy", new iostream_commands::DestroyBase {}); + addCommand("create", new iostream_commands::Create {}); + addCommand("base", new iostream_commands::FindBase {}); + addCommand("units", new iostream_commands::ListUnits {}); + addCommand("describe", new iostream_commands::DescribeAt {}); + addCommand("map", new iostream_commands::PrintMap {}); + addCommand("skip", new iostream_commands::Skip {}); + addCommand("save", new iostream_commands::Save {}); + addCommand("reset", new iostream_commands::Reset {}); +} + +bool +IostreamPlayer::takeTurn(const Game *g, Mediator *m, Base *b) +{ + for (;;) { + std::string cmd_name; + (*_is) >> cmd_name; + + if (_is->fail()) { + return false; + } + + { + std::ostringstream oss {}; + oss << "User picked action: " << cmd_name; + emit(new events::UserActionEvent {this, oss.str()}); + } + + auto iter = _cmd_tab.find(cmd_name); + if (iter == _cmd_tab.end()) { + std::ostringstream oss {}; + oss << "Unknown command: \"" << cmd_name << "\""; + emit(new events::UserActionEvent {this, oss.str()}); + (*_os) << oss.str() << "\n"; + continue; + } + + if (iter->second->execute(g, this, m, b)) { + break; + } + } + + return true; +} + +int +IostreamPlayer::readInt() +{ + int x; + (*_is) >> x; + return x; +} + +Unit * +IostreamPlayer::readUnitId(Base *b) +{ + int id = readInt(); + Unit *u = b->getUnitById(id); + if (!u) { + (*_os) << "No such unit: " << id << "\n"; + } + + return u; +} + +Vec2 +IostreamPlayer::readVec2() +{ + int x, y; + (*_is) >> x >> y; + return {x, y}; +} + +std::string +IostreamPlayer::readString() +{ + std::string s; + (*_is) >> s; + return s; +} + +void +IostreamPlayer::store(std::ostream &os) const +{ + os << "iostream_player\n" << name() << "\n"; +} + +bool +IostreamPlayer::restore(std::istream &is, + RestorerTable *tab) +{ + Player::restore(is, tab); + + // We can’t serialize/deserialize IO streams + setOstream(std::cout); + setIstream(std::cin); + return true; +} diff --git a/8303/Parfentev_Leonid/lab7/iostream_player.hpp b/8303/Parfentev_Leonid/lab7/iostream_player.hpp new file mode 100644 index 000000000..95e5a8345 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/iostream_player.hpp @@ -0,0 +1,366 @@ +#ifndef _H_STDIO_PLAYER_HPP +#define _H_STDIO_PLAYER_HPP + +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "game.hpp" +#include "object_print.hpp" + +#include "event.hpp" +#include "logging.hpp" + + +class IostreamPlayer; + +class IostreamCommand { +public: + // -> whether to end turn + virtual bool execute(const Game *g, IostreamPlayer *p, + Mediator *m, Base *b) =0; + + virtual ~IostreamCommand() {} +}; + +class IostreamPlayer: public Player { + std::ostream *_os = nullptr; + std::istream *_is = nullptr; + bool _free_os, _free_is; + + std::map _cmd_tab {}; + + void + addCommand(const std::string &str, + IostreamCommand *cmd); + + +public: + IostreamPlayer(std::string name=""); + + void + setOstream(std::ostream *os) { _os = os; _free_is = true; } + void + setOstream(std::ostream &os) { _os = &os; _free_os = false; } + + std::ostream & + ostream() const { return *_os; } + + void + setIstream(std::istream *is) { _is = is; _free_is = true; } + void + setIstream(std::istream &is) { _is = &is; _free_is = false; } + + std::istream & + istream() const { return *_is; } + + virtual bool + takeTurn(const Game *g, Mediator *m, Base *b) override; + + int + readInt(); + + Unit * + readUnitId(Base *b); + + Vec2 + readVec2(); + + std::string + readString(); + + virtual void store(std::ostream &os) const override; + + virtual bool restore(std::istream &is, + RestorerTable *tab) override; +}; + +namespace iostream_commands { + + class Move: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested moving " << u << " to " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->moveUnitTo(u, to)) { + p->ostream() << "Can't move unit " << u + << " to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Attack: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + Vec2 to = p->readVec2(); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u << " attacks " << to; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->attackTo(u, to)) { + p->ostream() << "Unit " << u + << " can't attack to " << to << "\n"; + return false; + } + + return true; + } + }; + + class Use: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u + << " uses an object"; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->useObject(u)) { + p->ostream() << "Unit " << u + << " can't use any object there\n"; + return false; + } + + return true; + } + }; + + class DestroyBase: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + Unit *u = p->readUnitId(b); + if (!u) + return false; + + { + std::ostringstream oss {}; + oss << "User requested that " << u + << " destroys a base"; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!m->destroyBase(u)) { + p->ostream() << "Unit " << u + << " cannot destroy any bases there\n"; + return false; + } + + return true; + } + }; + + class Create: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *b) override + { + std::string s = p->readString(); + + { + std::ostringstream oss {}; + oss << "User requested creation of unit " << s; + p->emit(new events::UserActionEvent {p, oss.str()}); + } + + if (!b->canCreateUnit(s)) { + p->ostream() << "Can't create unit of type " + << s << "\n"; + return false; + } + + int id = b->createUnit(s, m); + if (id < 0) { + p->ostream() << "Failed to create a unit of type " + << s << "\n"; + return false; + } + + p->ostream() << "New unit of type " << s + << ": " << id << "\n"; + return true; + } + }; + + class FindBase: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *b) override + { + p->ostream() << "Base: " << b << "\n"; + return false; + } + }; + + class ListUnits: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *b) override + { + p->ostream() << "Units:"; + for (auto iter = b->unitsBegin(); + iter != b->unitsEnd(); + ++iter) { + p->ostream() << "- " << iter.id() + << ": " << iter.unit() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class DescribeAt: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *) override + { + auto pos = p->readVec2(); + auto info = m->infoAt(pos); + + p->ostream() << "At " << pos << "\n"; + p->ostream() + << "- Landscape: " << info.landscape() << "\n"; + + if (auto *b = info.base()) { + p->ostream() << "- Base: " << b << "\n"; + } + + if (auto *u = info.unit()) { + p->ostream() << "- Unit: " << u << "\n"; + } + + if (auto *n = info.neutralObject()) { + p->ostream() << "- Object: " << n << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class PrintMap: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *m, Base *) override + { + auto from = p->readVec2(); + auto to = p->readVec2(); + + p->ostream() << "From " << from + << " to " << to << ":\n"; + + for (int y = from.y(); y < to.y(); ++y) { + for (int x = from.x(); x < to.x(); ++x) { + if (x != from.x()) { + p->ostream() << " "; + } + displayMapInfo(p->ostream(), m->infoAt({x, y})); + } + p->ostream() << "\n"; + } + + p->ostream() << std::endl; + return false; + } + }; + + class Skip: public IostreamCommand { + public: + virtual bool + execute(const Game *, IostreamPlayer *p, + Mediator *, Base *) override + { + std::ostringstream oss {}; + oss << "User decided to skip the turn"; + p->emit(new events::UserActionEvent {p, oss.str()}); + + return true; + } + }; + + class Save: public IostreamCommand { + public: + virtual bool + execute(const Game *g, IostreamPlayer *p, + Mediator *, Base *) override + { + std::string fn = p->readString(); + std::ofstream of {fn}; + if (!of) { + p->ostream() << "Failed to open file\n"; + return false; + } + + g->store(of); + p->ostream() << "Save complete\n"; + + std::ostringstream oss {}; + oss << "Saved current game to " << fn; + p->emit(new events::UserActionEvent {p, oss.str()}); + + return false; + } + }; + + class Reset: public IostreamCommand { + public: + virtual bool + execute(const Game *g, IostreamPlayer *p, + Mediator *, Base *) override + { + std::ostringstream oss {}; + oss << "Requesting reset"; + p->emit(new events::UserActionEvent {p, oss.str()}); + + g->requestReset(); + return true; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/landscape.hpp b/8303/Parfentev_Leonid/lab7/landscape.hpp new file mode 100644 index 000000000..3df7e506d --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/landscape.hpp @@ -0,0 +1,29 @@ +#ifndef _H_LANDSCAPE_HPP +#define _H_LANDSCAPE_HPP + + +#include "storable.hpp" + +class Unit; + +class Landscape: public Storable { +public: + virtual void onEnter(Unit *u) =0; + virtual void onLeave(Unit *u) =0; + + virtual ~Landscape() {} +}; + +namespace landscapes { + + class Normal: public Landscape { + public: + virtual void onEnter(Unit *) override {} + virtual void onLeave(Unit *) override {} + + TRIVIALLY_STORABLE("l_normal"); + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/landscape_types.hpp b/8303/Parfentev_Leonid/lab7/landscape_types.hpp new file mode 100644 index 000000000..c53d2b99a --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/landscape_types.hpp @@ -0,0 +1,75 @@ +#ifndef _H_LANDSCAPE_TYPES_HPP +#define _H_LANDSCAPE_TYPES_HPP + +#include "landscape.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "common_policies.hpp" +#include "storable.hpp" + + +namespace landscapes { + + // Swamp: max speed is 1; attacking is forbidden + class Swamp: public Landscape { + ModifyingMovePolicy *_p; + AttackPolicy *_prev, *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _p = new ModifyingMovePolicy {u->movePolicy(), 1}; + u->setMovePolicy(_p); + + _prev = u->attackPolicy(); + _cur = new AttackForbidden {}; + u->setAttackPolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *mpc = u->findMoveContainerOf(_p)) { + mpc->setMovePolicy(_p->movePolicy()); + _p->setMovePolicy(nullptr); + delete _p; + _p = nullptr; + } + + // our policy might’ve been wrapped into something + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("l_swamp"); + }; + + class Forest: public Landscape { + DefensePolicy *_prev; + MultiplierDefensePolicy *_cur; + + public: + virtual void onEnter(Unit *u) override + { + _prev = u->defensePolicy(); + _cur = new MultiplierDefensePolicy {_prev, 2.0}; + u->setDefensePolicy(_cur); + } + + virtual void onLeave(Unit *u) override + { + if (auto *dpc = u->findDefenseContainerOf(_cur)) { + dpc->setDefensePolicy(_prev); + _cur->setDefensePolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("l_forest"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/logging.hpp b/8303/Parfentev_Leonid/lab7/logging.hpp new file mode 100644 index 000000000..72b272969 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/logging.hpp @@ -0,0 +1,46 @@ +#ifndef _H_LOGGING_HPP +#define _H_LOGGING_HPP + +#include +#include + +#include "event.hpp" +#include "event_printer.hpp" + + +namespace events { + + class UserActionEvent: public Event { + Player *_p; + std::string _s; + + public: + UserActionEvent(Player *p, std::string s) + :_p{p}, _s{std::move(s)} {} + + const std::string & + message() const { return _s; } + + Player *player() const { return _p;} + }; + +} + +class LoggingEventPrinter: public EventPrinter { +public: + using EventPrinter::EventPrinter; + + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + ostream() << ee->player()->name() + << ": " << ee->message() << "\n"; + return; + } + + EventPrinter::handle(e); + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/main.cpp b/8303/Parfentev_Leonid/lab7/main.cpp new file mode 100644 index 000000000..c9f4240dc --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/main.cpp @@ -0,0 +1,132 @@ +#include + +#include +#include +#include + +#include "demo.hpp" + +#include "event_printer.hpp" +#include "game_driver.hpp" +#include "game_rules.hpp" +#include "exceptions.hpp" + + +void +run_demos(void) +{ + std::cout << "Demo 1\n"; + demo1(); + + std::cout << "\nDemo 2\n"; + demo2(); + + std::cout << "\nDemo 3\n"; + demo3(); + + std::cout << "\nDemo 4\n"; + demo4(); + + std::cout << "\nDemo 5\n"; + demo5(); + + std::cout << "\nDemo 6\n"; + demo6(); + + std::cout << "\nDemo 7\n"; + demo7(); + + std::cout << "\nDemo 8\n"; + demo8(); + + std::cout << "\nDemo 9\n"; + demo9(); +} + +Game * +loadGame(const std::string &load_fn) +{ + std::ifstream f {load_fn}; + if (!f) { + throw NoSavegameFile {load_fn}; + } + auto *tab = RestorerTable::defaultTable(); + Storable *s = tab->restore(f); + delete tab; + + if (auto *lg = dynamic_cast(s)) { + return lg; + } else { + delete s; + throw InvalidSaveFileContents {load_fn, f.tellg()}; + } +} + +int +run_game(int argc, char **argv) +{ + std::vector loggers {}; + bool have_stdout = false; + + const char *load_fn = nullptr; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "-log")) { + char *fn = argv[++i]; + if (!strcmp(fn, "-")) { + loggers.push_back(new LoggingEventPrinter {std::cout}); + have_stdout = true; + } else { + auto *of = new std::ofstream {fn}; + if (!*of) { + std::cerr << "Failed to open file: " << fn << "\n"; + return 1; + } + loggers.push_back(new LoggingEventPrinter {of}); + } + } else if (!strcmp(argv[i], "-load")) { + load_fn = argv[++i]; + } else { + std::cerr << "Unknown option: " << argv[i] << "\n"; + return 1; + } + } + + GameDriver drv {}; + + for (auto *logger: loggers) { + drv.addLogger(logger); + } + + if (!have_stdout) { + drv.setPrinter(new EventPrinter {std::cout}); + } + + if (load_fn) { + try { + drv.resetFrom(loadGame(load_fn)); + } catch (std::exception &e) { + std::cerr << "An error occurred while loading savegame:\n" + << e.what() << "\n"; + return 1; + } + } else { + drv.reset(); + } + + drv.run(); + + return 0; +} + +int +main(int argc, char **argv) +{ + if (argc == 2 + && !strcmp(argv[1], "-demo")) { + run_demos(); + return 0; + } + + return run_game(argc, argv); +} diff --git a/8303/Parfentev_Leonid/lab7/map.cpp b/8303/Parfentev_Leonid/lab7/map.cpp new file mode 100644 index 000000000..66eddff6e --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/map.cpp @@ -0,0 +1,222 @@ +#include + +#include "point.hpp" +#include "unit.hpp" +#include "placeable.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "landscape.hpp" +#include "map.hpp" +#include "storable.hpp" +#include "common_storables.hpp" + + +Map::Map(int w, int h) + :_rm{w, h} +{ + for (auto rmiter = _rm.iterAt({0, 0}); + rmiter.y() < _rm.height(); + rmiter.advance(1)) { + rmiter.cell().setLandscape(new landscapes::Normal {}); + } +} + +MapIter +Map::addUnit(Unit *u, Vec2 pt) +{ + if (u->hasPosition()) + return MapIter::makeNull(); + + if (_units_max >= 0 + && _units_count == _units_max) + return MapIter::makeNull(); + + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.unit()) + return MapIter::makeNull(); + + cell.setUnit(u); + u->setPosition(pt); + + ++_units_count; + + cell.landscape()->onEnter(u); + + return MapIter{rmiter}; +} + +Unit * +Map::removeUnitAt(Vec2 at) +{ + RectMapIter rmiter = _rm.iterAt(at); + Cell &cell = rmiter.cell(); + Unit *u = dynamic_cast(cell.unit()); + + if (u) { + --_units_count; + cell.landscape()->onLeave(u); + if (auto *n = dynamic_cast(cell.object())) { + n->onLeave(u); + } + + cell.setUnit(nullptr); + u->unsetPosition(); + } + + return u; +} + +MapIter +Map::addPlaceable(Placeable *p, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + + if (cell.object()) { + return MapIter::makeNull(); + } + + cell.setObject(p); + p->setPosition(pt); + + return MapIter{rmiter}; +} + +MapIter +Map::addBase(Base *b, Vec2 pt) +{ + return addPlaceable(b, pt); +} + +MapIter +Map::addNeutralObject(NeutralObject *n, Vec2 pt) +{ + return addPlaceable(n, pt); +} + +void +Map::setLandscape(Landscape *l, Vec2 pt) +{ + RectMapIter rmiter = _rm.iterAt(pt); + Cell &cell = rmiter.cell(); + cell.setLandscape(l); +} + +void +Map::store(std::ostream &os) const +{ + os << "map " << width() << " " << height() << "\n"; + os << _units_max << "\n"; + + for (int y = 0; y < height(); ++y) { + for (int x = 0; x < width(); ++x) { + auto info = infoAt({x, y}); + + const auto *l = info.landscape(); + if (!dynamic_cast(l)) { + StorableWithCoords::storeWithCoords({x, y}, l, os); + } else if (const auto *n = info.neutralObject()) { + StorableWithCoords::storeWithCoords({x, y}, n, os); + } + + // bases and units are restored in Game::restore + } + } + + os << "end\n"; +} + +bool +Map::restore(std::istream &is, + RestorerTable *tab) +{ + is >> _units_max; + if (is.fail()) { + return false; + } + + for (;;) { + Storable *s = tab->restore(is); + + if (auto *sc = + dynamic_cast(s)) { + if (auto *l = + dynamic_cast(sc->child())) { + setLandscape(l, sc->coords()); + delete sc; + } else if (auto *n = + dynamic_cast(sc->child())) { + addNeutralObject(n, sc->coords()); + delete sc; + } else { + delete sc->child(); + delete sc; + return false; + } + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} + +MapInfo +Map::infoAt(Vec2 pt) const +{ + return MapInfo{&_rm.at(pt)}; +} + +Unit * +MapIter::unit() const +{ + return _it.cell().unit(); +} + +Base * +MapIter::base() const +{ + return dynamic_cast(_it.cell().object()); +} + +NeutralObject * +MapIter::neutralObject() const +{ + return dynamic_cast(_it.cell().object()); +} + +Landscape * +MapIter::landscape() const +{ + return _it.cell().landscape(); +} + +const Landscape * +MapInfo::landscape() const +{ + return _cell->landscape(); +} + +const Unit * +MapInfo::unit() const +{ + return _cell->unit(); +} + +const Base * +MapInfo::base() const +{ + return dynamic_cast(_cell->object()); +} + +const NeutralObject * +MapInfo::neutralObject() const +{ + return dynamic_cast(_cell->object()); +} diff --git a/8303/Parfentev_Leonid/lab7/map.hpp b/8303/Parfentev_Leonid/lab7/map.hpp new file mode 100644 index 000000000..d8831d7cf --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/map.hpp @@ -0,0 +1,126 @@ +#ifndef _H_MAP_HPP +#define _H_MAP_HPP + +#include + +#include "rectmap.hpp" +#include "storable.hpp" + +// Map interface doesn’t know about cells -- instead, it only cares +// about certain kinds of Placeables + + +class Map; + +class Unit; +class Base; +class NeutralObject; + +class MapIter { + RectMapIter _it; + + friend class Map; + + MapIter(RectMapIter r) + :_it{r} {} + +public: + static MapIter makeNull() + { + return MapIter{RectMapIter::makeNull()}; + } + + bool operator==(const MapIter &o) const { return _it == o._it; } + bool operator!=(const MapIter &o) const { return _it != o._it; } + + int x() const { return _it.x(); } + int y() const { return _it.y(); } + Vec2 point() const { return _it.point(); } + + bool null() const { return _it.null(); } + bool valid() const { return _it.valid(); } + + void shift(Vec2 dxy) { _it.moveTo(point().shifted(dxy)); } + MapIter shifted(Vec2 dxy) const + { + return MapIter{_it.otherAt(point().shifted(dxy))}; + } + + void moveTo(Vec2 xy) { _it.moveTo(xy); } + MapIter otherAt(Vec2 xy) const + { + return MapIter{_it.otherAt(xy)}; + } + + void advance(int d) { _it.advance(d); } + MapIter advanced(int d) const + { + return MapIter{_it.advanced(d)}; + } + + Unit *unit() const; + Base *base() const; + NeutralObject *neutralObject() const; + Landscape *landscape() const; +}; + +class MapInfo { + const Cell *_cell; + +public: + MapInfo(const Cell *c) + :_cell{c} {} + + const Landscape *landscape() const; + const Unit *unit() const; + const Base *base() const; + const NeutralObject *neutralObject() const; +}; + +class Map: public Storable { + RectMap _rm; + int _units_count = 0; + int _units_max = -1; + + MapIter addPlaceable(Placeable *p, Vec2 pt); + +public: + Map(int w, int h); + + int width() const { return _rm.width(); } + int height() const { return _rm.height(); } + MapIter iterAt(Vec2 pt) { return MapIter{_rm.iterAt(pt)}; } + MapIter iterAt(int x, int y) { return iterAt({x, y}); } + + MapIter begin() { return iterAt(0, 0); } + MapIter end() { return iterAt(0, height()); } + + MapInfo infoAt(Vec2 pt) const; + + MapIter addUnit(Unit *u, Vec2 pt); + Unit *removeUnitAt(Vec2 at); + Unit *removeUnitAt(MapIter iter) + { + return removeUnitAt(iter.point()); + } + + MapIter addBase(Base *b, Vec2 pt); + MapIter addNeutralObject(NeutralObject *n, Vec2 pt); + void setLandscape(Landscape *l, Vec2 pt); + + int maxUnitsCount() const { return _units_max; } + bool setMaxUnitsCount(int x) + { + if (_units_count > x) + return false; + _units_max = x; + return true; + } + int unitsCount() const { return _units_count; } + + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/mediator.cpp b/8303/Parfentev_Leonid/lab7/mediator.cpp new file mode 100644 index 000000000..d7e6e9ff1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/mediator.cpp @@ -0,0 +1,127 @@ +#include "point.hpp" +#include "map.hpp" +#include "event.hpp" +#include "event_types.hpp" +#include "neutral_object.hpp" +#include "base.hpp" +#include "mediator.hpp" + + +MapInfo +Mediator::infoAt(Vec2 pt) +{ + return _map->infoAt(pt); +} + +Vec2 +Mediator::mapSize() +{ + return {_map->width(), + _map->height()}; +} + +bool +Mediator::moveUnitTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit() + || !u->canMove(ito)) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitMoved {u, from}); + return true; +} + +bool +Mediator::attackTo(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (!ito.unit() + || !u->canAttackTo(ito)) { + return false; + } + + auto pos = u->actualPosition(ito); + Unit *t = pos.unit(); + + u->emit(new events::UnitAttacked {u, pos.point(), t}); + + if (t) { + t->emit(new events::UnitWasAttacked {u, t}); + + auto damage_pair = u->baseAttack(pos); + auto damage_spec = t->actualDamage(damage_pair.first, + damage_pair.second); + int dmg = damage_spec.evaluate(); + + t->takeDamage(dmg); + } + + return true; +} + +bool +Mediator::useObject(Unit *u) +{ + auto iter = _map->iterAt(u->position()); + + NeutralObject *n = iter.neutralObject(); + + if (!n + || !n->canUse(u)) { + return false; + } + + n->onUse(u, this); + + u->emit(new events::UnitUsedObject {u, n}); + + return true; +} + +bool +Mediator::destroyBase(Unit *u) +{ + auto iter = _map->iterAt(u->position()); + + Base *b = iter.base(); + + if (!b + || !b->becomeDestroyedBy(u)) { + return false; + } + + return true; +} + +bool +Mediator::spawnUnit(Unit *u, Vec2 at) +{ + return !_map->addUnit(u, at).null(); +} + +bool +Mediator::teleportUnit(Unit *u, Vec2 to) +{ + auto ito = _map->iterAt(to); + + if (ito.unit()) { + return false; + } + + Vec2 from = u->position(); + + _map->removeUnitAt(from); + _map->addUnit(u, to); + + u->emit(new events::UnitTeleported {u, from}); + return true; +} diff --git a/8303/Parfentev_Leonid/lab7/mediator.hpp b/8303/Parfentev_Leonid/lab7/mediator.hpp new file mode 100644 index 000000000..31a0d545a --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/mediator.hpp @@ -0,0 +1,32 @@ +#ifndef _H_MEDIATOR_HPP +#define _H_MEDIATOR_HPP + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" + + +class Mediator { + Map *_map; + +public: + Mediator(Map *map) + :_map{map} {} + + MapInfo infoAt(Vec2 pt); + + Vec2 mapSize(); + + bool moveUnitTo(Unit *u, Vec2 to); + + bool attackTo(Unit *u, Vec2 to); + + bool useObject(Unit *u); + bool destroyBase(Unit *u); + + bool spawnUnit(Unit *u, Vec2 at); + bool teleportUnit(Unit *u, Vec2 to); +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab7/melee_units.hpp b/8303/Parfentev_Leonid/lab7/melee_units.hpp new file mode 100644 index 000000000..6409e96cb --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/melee_units.hpp @@ -0,0 +1,111 @@ +#ifndef _H_MELEE_UNITS_HPP +#define _H_MELEE_UNITS_HPP + +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class MeleeAttack: public AttackPolicy { + AttackKind _kind; + int _base_damage; + +public: + explicit MeleeAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0) + :_kind{kind}, _base_damage{base_dmg} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + return to.unit() != nullptr + && to.point().adjacent(u->position()); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter) + { + return std::make_pair( + _kind, + int(_base_damage * u->relativeHealth())); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_melee " << static_cast(_kind) + << " " << _base_damage << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicMeleeUnit: public Unit { +public: + BasicMeleeUnit(int speed, + AttackKind attack_kind, + int base_dmg, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new MeleeAttack {attack_kind, base_dmg}, + def, base_health} {} +}; + +namespace units { + class Swordsman: public BasicMeleeUnit { + public: + Swordsman() :BasicMeleeUnit{ + 2, + AttackKind::sword, 40, + DefenseLevelDeco::good_defense_deco( + AttackKind::spear, + DefenseLevelDeco::vulnerability_deco( + AttackKind::cavalry, + new BasicDefense {})), + 100} {} + + UNIT_STORABLE_NAME("u_swordsman"); + }; + + class Spearsman: public BasicMeleeUnit { + public: + Spearsman() :BasicMeleeUnit{ + 2, + AttackKind::spear, 75, + DefenseLevelDeco::good_defense_deco( + AttackKind::cavalry, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + + UNIT_STORABLE_NAME("u_spearsman"); + }; + + class Cavalry: public BasicMeleeUnit { + public: + Cavalry() :BasicMeleeUnit{ + 3, + AttackKind::cavalry, 50, + DefenseLevelDeco::good_defense_deco( + AttackKind::sword, + DefenseLevelDeco::vulnerability_deco( + AttackKind::spear, + new BasicDefense {})), + 75} {} + + UNIT_STORABLE_NAME("u_cavalry"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/neutral_object.hpp b/8303/Parfentev_Leonid/lab7/neutral_object.hpp new file mode 100644 index 000000000..95e11d9c7 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/neutral_object.hpp @@ -0,0 +1,24 @@ +#ifndef _H_NEUTRAL_OBJECT_HPP +#define _H_NEUTRAL_OBJECT_HPP + +#include "placeable.hpp" +#include "unit.hpp" +#include "map.hpp" +#include "mediator.hpp" +#include "storable.hpp" + + +class NeutralObject: public Placeable, + public Storable { +public: + virtual bool canUse(const Unit *) { return true; } + virtual void onUse(Unit *u, Mediator *m) =0; + + // It’s the object’s job to determine whether it was used by the + // leaving unit. + virtual void onLeave(Unit *) {}; + + virtual ~NeutralObject() {}; +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/neutral_object_types.hpp b/8303/Parfentev_Leonid/lab7/neutral_object_types.hpp new file mode 100644 index 000000000..e6fc373c1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/neutral_object_types.hpp @@ -0,0 +1,255 @@ +#ifndef _H_NEUTRAL_OBJECT_TYPES_HPP +#define _H_NEUTRAL_OBJECT_TYPES_HPP + +#include + +#include "neutral_object.hpp" +#include "mediator.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "ranged_units.hpp" +#include "common_policies.hpp" + +#include "storable.hpp" +#include "common_storables.hpp" + + +class ExtendedShootingRange: public NestedAttack { + double _delta; + +public: + explicit ExtendedShootingRange(AttackPolicy *p=nullptr, + double delta=0) + :NestedAttack{p}, _delta{delta} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = to.point().distance(u->position()); + auto *a = dynamic_cast(attackPolicy()); + return dist >= a->minRange() + && dist <= (a->maxRange() + _delta); + } + + virtual MapIter + actualPosition(const Unit *u, MapIter to) override + { + return attackPolicy()->actualPosition(u, to); + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + return attackPolicy()->baseAttack(u, to); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_extended " << _delta << "\n"; + attackPolicy()->store(os); + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + is >> _delta; + return !is.fail() && NestedAttack::restore(is, tab); + } +}; + + + +namespace objects { + + class HealingWell: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *) override + { + u->heal(25); + } + + TRIVIALLY_STORABLE("n_healingwell"); + }; + + class Tower: public NeutralObject { + AttackPolicy *_prev; + ExtendedShootingRange *_cur = nullptr; + + public: + virtual bool + canUse(const Unit *u) override + { + return dynamic_cast(u); + } + + virtual void + onUse(Unit *u, Mediator *) override + { + _prev = u->attackPolicy(); + _cur = new ExtendedShootingRange {_prev, 5}; + u->setAttackPolicy(_cur); + } + + virtual void + onLeave(Unit *u) override + { + if (_cur == nullptr) { + return; + } + if (auto *apc = u->findAttackContainerOf(_cur)) { + apc->setAttackPolicy(_prev); + _cur->setAttackPolicy(nullptr); + delete _cur; + _cur = nullptr; + } + } + + TRIVIALLY_STORABLE("n_tower"); + }; + + class TunnelsEntrance: public NeutralObject { + public: + virtual void + onUse(Unit *u, Mediator *m) override + { + static const int w = 5; + + Vec2 at = u->position(); + + int max_n = 0; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() == nullptr) { + ++max_n; + } + } + } + + std::uniform_int_distribution<> distr {0, max_n-1}; + int n = distr(global_random); + + Vec2 dest; + for (int j = -w; j <= w; ++j) { + for (int i = -w; i <= w; ++i) { + auto iter = at.shifted({i, j}); + if (m->infoAt(iter).unit() != nullptr) { + continue; + } + if (!--n) { + dest = iter; + break; + } + } + } + + m->teleportUnit(u, dest); + } + + TRIVIALLY_STORABLE("n_tunnelentrance"); + }; + + class WeaponSmiths: public NeutralObject { + public: + class UnitFilter: public Storable { + public: + virtual bool + applicable(const Unit *u) =0; + }; + + template + class SimpleUnitFilter: public UnitFilter{ + public: + virtual bool + applicable(const Unit *u) override + { + return dynamic_cast(u); + } + }; + + class MeleeUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_melee"); + }; + + class RangedUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_ranged"); + }; + + class CatapultUnitFilter: + public SimpleUnitFilter { + TRIVIALLY_STORABLE("uf_catapult"); + }; + + private: + double _mul; + UnitFilter *_filter; + + public: + explicit WeaponSmiths(double mul=0, UnitFilter *filter=nullptr) + :_mul{mul}, _filter{filter} {} + + virtual bool + canUse(const Unit *u) override + { + if (_filter + && !_filter->applicable(u)) { + return false; + } + + for (const AttackPolicyContainer *apc = u; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (dynamic_cast(apc)) { + return false; + } + } + + return true; + } + + virtual void + onUse(Unit *u, Mediator *) override + { + auto *prev = u->attackPolicy(); + auto *new_p = new MultiplierAttackPolicy {prev, _mul}; + u->setAttackPolicy(new_p); + } + + virtual void + store(std::ostream &os) const override + { + os << "n_weaponsmiths " << _mul << "\n"; + if (_filter) { + _filter->store(os); + } else { + os << "end\n"; + } + } + + virtual bool + restore(std::istream &is, RestorerTable *tab) + { + Storable *s = tab->restore(is); + + if (auto *uf = dynamic_cast(s)) { + _filter = uf; + } else if (dynamic_cast(s)) { + _filter = nullptr; + delete s; + } else { + delete s; + return false; + } + + return true; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/object_print.cpp b/8303/Parfentev_Leonid/lab7/object_print.cpp new file mode 100644 index 000000000..da59e1054 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/object_print.cpp @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "base.hpp" +#include "neutral_object.hpp" +#include "neutral_object_types.hpp" +#include "landscape.hpp" +#include "landscape_types.hpp" +#include "object_print.hpp" + + +#define UNIT_ENTRY(T) {std::type_index{typeid(units::T)}, #T} +static const std::map unit_names { + UNIT_ENTRY(Swordsman), + UNIT_ENTRY(Spearsman), + UNIT_ENTRY(Cavalry), + UNIT_ENTRY(Archer), + UNIT_ENTRY(Slinger), + UNIT_ENTRY(Onager), + UNIT_ENTRY(BoltThrower), +}; +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T) {std::type_index{typeid(landscapes::T)}, #T} +static const std::map landscape_names { + LANDSCAPE_ENTRY(Normal), + LANDSCAPE_ENTRY(Swamp), + LANDSCAPE_ENTRY(Forest), +}; +#undef LANDSCAPE_ENTRY + +#define N_OBJECT_ENTRY(T, n) {std::type_index{typeid(objects::T)}, n} +static const std::map objects_names { + N_OBJECT_ENTRY(HealingWell, "Healing Well"), + N_OBJECT_ENTRY(Tower, "Tower"), + N_OBJECT_ENTRY(TunnelsEntrance, "Tunnels Entrance"), + N_OBJECT_ENTRY(WeaponSmiths, "Weapon Smiths"), +}; +#undef N_OBJECT_ENTRY + + +std::ostream & +operator<<(std::ostream &os, Vec2 pt) +{ + return os << "{" << pt.x() << "," << pt.y() << "}"; +} + +std::ostream & +operator<<(std::ostream &os, const Unit *u) +{ + return os << "a " + << unit_names.at(std::type_index{typeid(*u)}) + << " with " << u->health() << " HP at " + << u->position(); +} + +std::ostream & +operator<<(std::ostream &os, const Landscape *l) +{ + return os << landscape_names.at(std::type_index{typeid(*l)}); +} + +std::ostream & +operator<<(std::ostream &os, const Base *b) +{ + os << "a Base with " << b->unitsCount(); + int m = b->maxUnitsCount(); + if (m >= 0) { + os << "/" << m; + } + + return os << " units at " << b->position(); +} + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n) +{ + return os << "a " + << objects_names.at(std::type_index{typeid(*n)}) + << " at " << n->position(); +} + + + +static const std::map class_chars { +#define UNIT_ENTRY(T, x) {std::type_index{typeid(units::T)}, x} + UNIT_ENTRY(Swordsman, 'S'), + UNIT_ENTRY(Spearsman, 'P'), + UNIT_ENTRY(Cavalry, 'C'), + UNIT_ENTRY(Archer, 'A'), + UNIT_ENTRY(Slinger, 's'), + UNIT_ENTRY(Onager, 'O'), + UNIT_ENTRY(BoltThrower, 'B'), +#undef UNIT_ENTRY + +#define LANDSCAPE_ENTRY(T, x) \ + {std::type_index{typeid(landscapes::T)}, x} + LANDSCAPE_ENTRY(Normal, '.'), + LANDSCAPE_ENTRY(Swamp, '='), + LANDSCAPE_ENTRY(Forest, '*'), +#undef LANDSCAPE_ENTRY +}; + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info) +{ + if (const Unit *u = info.unit()) { + os << class_chars.at(std::type_index{typeid(*u)}); + } else if (info.base()) { + os << "+"; + } else if (info.neutralObject()) { + os << '#'; + } else { + auto *l = info.landscape(); + os << class_chars.at(std::type_index{typeid(*l)}); + } + + return os; +} + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1) +{ + for (int y = y0; y < y1; ++y) { + for (int x = x0; x < x1; ++x) { + displayMapInfo(os, map->infoAt({x, y})); + } + os << "\n"; + } + return os; +} + +std::ostream & +operator<<(std::ostream &os, const Map *map) +{ + return displayMap(os, map, 0, 0, map->width(), map->height()); +} diff --git a/8303/Parfentev_Leonid/lab7/object_print.hpp b/8303/Parfentev_Leonid/lab7/object_print.hpp new file mode 100644 index 000000000..8df10d966 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/object_print.hpp @@ -0,0 +1,40 @@ +#ifndef _H_OBJECT_PRINT_HPP +#define _H_OBJECT_PRINT_HPP + +#include +#include +#include + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" +#include "base.hpp" +#include "landscape.hpp" +#include "neutral_object.hpp" + +std::ostream & +operator<<(std::ostream &os, Vec2 pt); + +std::ostream & +operator<<(std::ostream &os, const Unit *u); + +std::ostream & +operator<<(std::ostream &os, const Landscape *l); + +std::ostream & +operator<<(std::ostream &os, const Base *b); + +std::ostream & +operator<<(std::ostream &os, const NeutralObject *n); + +std::ostream & +displayMapInfo(std::ostream &os, const MapInfo &info); + +std::ostream & +displayMap(std::ostream &os, const Map *map, + int x0, int y0, int x1, int y1); + +std::ostream & +operator<<(std::ostream &os, const Map *map); + +#endif diff --git a/8303/Parfentev_Leonid/lab7/object_w_health.hpp b/8303/Parfentev_Leonid/lab7/object_w_health.hpp new file mode 100644 index 000000000..e9c925221 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/object_w_health.hpp @@ -0,0 +1,43 @@ +#ifndef _H_OBJECT_W_HEALTH_HPP +#define _H_OBJECT_W_HEALTH_HPP + +#include "storable.hpp" + + +class ObjectWithHealth: public Storable { + int _health, _base_health; + +public: + ObjectWithHealth(int base_health) + :_health{base_health}, + _base_health{base_health} {} + + int + health() const { return _health; } + int + baseHealth() const { return _base_health; } + double + relativeHealth() const { return _health / (double)_base_health; } + bool + alive() const { return health() > 0; } + + virtual void + heal(int hp) { _health += hp; } + + virtual void + takeDamage(int dmg) { _health -= dmg; } + + virtual void store(std::ostream &os) const override + { + os << health(); + } + + virtual bool restore(std::istream &is, RestorerTable *) override + { + is >> _health; + return !is.fail(); + } +}; + + +#endif diff --git a/8303/Parfentev_Leonid/lab7/pathfinder.cpp b/8303/Parfentev_Leonid/lab7/pathfinder.cpp new file mode 100644 index 000000000..865f34de2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/pathfinder.cpp @@ -0,0 +1,60 @@ +#include +#include + +#include "map.hpp" +#include "point.hpp" +#include "pathfinder.hpp" + + +PathFinder::Pt2 +PathFinder::Pt2::toDirection(int dir) const +{ + // assert(dir >= 0 && dir < 8); + + int d1 = (dir + 1) % 8; + int dx = (d1 % 4 == 3) ? 0 : (d1 < 4) ? 1 : -1, + dy = (dir % 4 == 0) ? 0 : (dir < 4) ? 1 : -1; + + return Pt2{pt.shifted({dx, dy}), depth + 1}; +} + +bool +PathFinder::run() +{ + Vec2 start_pt = _start.point(); + + while (!_frontier.empty()) { + Pt2 current = _frontier.front(); + _frontier.pop(); + + if (start_pt.shifted(current.pt) == _end) + return true; + + if (_max >= 0 + && current.depth >= _max) + continue; + + Vec2WithCmp cur_v2wc {current.pt}; + auto iter = _dirs.find(cur_v2wc); + if (iter == _dirs.end()) + _dirs[cur_v2wc] = -1; + + for (int i = 0; i < 8; ++i) { + Pt2 shifted_delta = current.toDirection(i); + + Vec2WithCmp sh_v2wc {shifted_delta.pt}; + auto iter = _dirs.find(sh_v2wc); + if (iter != _dirs.end()) + continue; + + MapIter shifted = _start.shifted(shifted_delta.pt); + if (!shifted.valid() + || shifted.unit()) + continue; + + _frontier.push(shifted_delta); + } + } + + return false; +} diff --git a/8303/Parfentev_Leonid/lab7/pathfinder.hpp b/8303/Parfentev_Leonid/lab7/pathfinder.hpp new file mode 100644 index 000000000..d4c614aae --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/pathfinder.hpp @@ -0,0 +1,56 @@ +#ifndef _H_PATHFINDER_HPP +#define _H_PATHFINDER_HPP + +#include +#include + +#include "point.hpp" +#include "map.hpp" + + +class PathFinder { + MapIter _start; + Vec2 _end; + int _max; + + struct Vec2WithCmp { + Vec2 v; + + bool operator<(Vec2WithCmp o) const + { + if (v.y() == o.v.y()) + return v.x() < o.v.x(); + return v.y() < o.v.y(); + } + }; + + struct Pt2 { + Vec2 pt; + int depth; + + static Pt2 zero() { return {Vec2{0, 0}, 0}; } + + Pt2(Vec2 pt, int depth=0) + :pt{pt}, depth{depth} {} + + Pt2 toDirection(int dir) const; + + bool pt_equal(Vec2 pt2) + { + return pt == pt2; + } + }; + + std::map _dirs {}; + std::queue _frontier {{Pt2::zero()}}; + +public: + PathFinder(MapIter from, MapIter to, int max_steps) + :_start{from}, + _end{to.point()}, + _max{max_steps} {} + + bool run(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/placeable.hpp b/8303/Parfentev_Leonid/lab7/placeable.hpp new file mode 100644 index 000000000..bd7f0ef74 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/placeable.hpp @@ -0,0 +1,36 @@ +#ifndef _H_PLACEABLE_HPP +#define _H_PLACEABLE_HPP + +#include "point.hpp" + +class Placeable { + bool _placed = false; + Vec2 _pos; + +public: + bool + hasPosition() const { return _placed; } + + const Vec2 & + position() const + { + return _pos; + } + + void + setPosition(const Vec2 &pos) + { + _pos = pos; + _placed = true; + } + + void + unsetPosition() + { + _placed = false; + } + + virtual ~Placeable() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/player.hpp b/8303/Parfentev_Leonid/lab7/player.hpp new file mode 100644 index 000000000..58c15829d --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/player.hpp @@ -0,0 +1,44 @@ +#ifndef _H_PLAYER_HPP +#define _H_PLAYER_HPP + +#include +#include + +#include "mediator.hpp" +#include "base.hpp" +#include "event.hpp" +#include "storable.hpp" + + +class Game; + +class Player: public EventEmitter, + public Storable { + std::string _name; + +public: + explicit Player(std::string name="") + :_name{std::move(name)} {} + + const std::string &name() const { return _name; } + + virtual bool takeTurn(const Game *g, Mediator *m, Base *b) =0; + + virtual void + store(std::ostream &os) const + { + os << name() << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + std::ws(is); + std::getline(is, _name); + return !is.fail(); + } + + virtual ~Player() {} +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/point.cpp b/8303/Parfentev_Leonid/lab7/point.cpp new file mode 100644 index 000000000..bce7b3325 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/point.cpp @@ -0,0 +1,29 @@ +#include + +#include "point.hpp" + +double +Vec2::length() const +{ + return sqrt(_x*_x + _y*_y); +} + +double +Vec2::distance(const Vec2 &pt) const +{ + return delta(pt).length(); +} + +bool +Vec2::unit() const +{ + return (_x || _y) + && abs(_x) <= 1 + && abs(_y) <= 1; +} + +bool +Vec2::adjacent(const Vec2 &pt) const +{ + return delta(pt).unit(); +} diff --git a/8303/Parfentev_Leonid/lab7/point.hpp b/8303/Parfentev_Leonid/lab7/point.hpp new file mode 100644 index 000000000..8eec01d76 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/point.hpp @@ -0,0 +1,40 @@ +#ifndef _H_POINT_HPP +#define _H_POINT_HPP + +class Vec2 { + int _x, _y; + +public: + Vec2() :Vec2{0, 0} {} + Vec2(int x, int y) :_x{x}, _y{y} {} + + int x() const { return _x; } + int y() const { return _y; } + + bool operator==(const Vec2 &pt) const + { + return _x == pt._x && _y == pt._y; + } + bool operator!=(const Vec2 &pt) const + { + return !(*this == pt); + } + + Vec2 delta(const Vec2 &o) const + { + return Vec2{_x - o._x, _y - o._y}; + } + + double length() const; + double distance(const Vec2 &pt) const; + + bool unit() const; + bool adjacent(const Vec2 &pt) const; + + Vec2 shifted(const Vec2 &dxy) const + { + return Vec2{_x + dxy._x, _y + dxy._y}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/ranged_units.hpp b/8303/Parfentev_Leonid/lab7/ranged_units.hpp new file mode 100644 index 000000000..2a41e3ecb --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/ranged_units.hpp @@ -0,0 +1,118 @@ +#ifndef _H_RANGED_UNITS_HPP +#define _H_RANGED_UNITS_HPP + +#include +#include + +#include "point.hpp" +#include "unit.hpp" +#include "common_policies.hpp" + + +class RangedAttack: public AttackPolicy { +protected: + AttackKind _kind; + int _base_damage; + double _min_distance, _max_distance; + double _dist_pow; + + static double + distance(const Unit *u, MapIter to) + { + return to.point().distance(u->position()); + } + +public: + double minRange() const { return _min_distance; } + double maxRange() const { return _max_distance; } + + explicit RangedAttack(AttackKind kind=AttackKind::invalid, + int base_dmg=0, + double min_dist=0, + double max_dist=0, + double dist_pow=0) + :_kind{kind}, + _base_damage{base_dmg}, + _min_distance{min_dist}, + _max_distance{max_dist}, + _dist_pow{dist_pow} {} + + virtual bool + canAttackTo(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return dist >= _min_distance + && dist <= _max_distance; + } + + virtual std::pair + baseAttack(const Unit *u, MapIter to) override + { + double dist = distance(u, to); + return std::make_pair( + _kind, + int(_base_damage + * u->relativeHealth() + / pow(dist, _dist_pow))); + } + + virtual void + store(std::ostream &os) const override + { + os << "ap_ranged " << static_cast(_kind) + << " " << _base_damage << " " << _min_distance + << " " << _max_distance << " " + << _dist_pow << "\n"; + } + + virtual bool + restore(std::istream &is, RestorerTable *) + { + int x; + is >> x >> _base_damage >> _min_distance + >> _max_distance >> _dist_pow; + _kind = static_cast(x); + return !is.fail(); + } +}; + +class BasicRangedUnit: public Unit { +public: + BasicRangedUnit(int speed, + AttackKind attack_kind, + int base_dmg, + double max_dist, + double dist_pow, + DefensePolicy *def, + int base_health) + :Unit{new BasicMovement {speed}, + new RangedAttack {attack_kind, base_dmg, + 1., max_dist, dist_pow}, + def, base_health} {} +}; + +namespace units { + class Archer: public BasicRangedUnit { + public: + Archer() :BasicRangedUnit{ + 2, + AttackKind::arrow, 50, 5., .20, + new BasicDefense {0.9}, + 40} {} + + UNIT_STORABLE_NAME("u_archer"); + }; + + class Slinger: public BasicRangedUnit { + public: + Slinger() :BasicRangedUnit{ + 2, + AttackKind::stone, 60, 3., .30, + new BasicDefense {.09}, + 50} {} + + UNIT_STORABLE_NAME("u_slinger"); + }; +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/rectmap.cpp b/8303/Parfentev_Leonid/lab7/rectmap.cpp new file mode 100644 index 000000000..b82667c40 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/rectmap.cpp @@ -0,0 +1,55 @@ +#include "rectmap.hpp" + +#include "unit.hpp" + +Cell::~Cell() +{ + delete _u; + delete _obj; + delete _l; +} + +bool +RectMapIter::valid() const +{ + return x() >= 0 + && x() < _map->width() + && y() >= 0 + && y() < _map->height(); +} + +Cell & +RectMapIter::cell() const +{ + return _map->at(point()); +} + +void +RectMapIter::moveTo(Vec2 xy) +{ + _pt = xy; +} + +RectMapIter +RectMapIter::otherAt(Vec2 xy) const +{ + RectMapIter other = *this; + other.moveTo(xy); + return other; +} + +void +RectMapIter::advance(int d) +{ + int nx = x() + d, + w = _map->width(); + _pt = Vec2{nx % w, y() + nx / w}; +} + +RectMapIter +RectMapIter::advanced(int d) const +{ + RectMapIter other = *this; + other.advance(d); + return other; +} diff --git a/8303/Parfentev_Leonid/lab7/rectmap.hpp b/8303/Parfentev_Leonid/lab7/rectmap.hpp new file mode 100644 index 000000000..a3119ffb1 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/rectmap.hpp @@ -0,0 +1,99 @@ +#ifndef _H_RECTMAP_HPP +#define _H_RECTMAP_HPP + +#include "point.hpp" +#include "placeable.hpp" +#include "landscape.hpp" + + +class Unit; + +class Cell { + Landscape *_l = nullptr; + Unit *_u = nullptr; + Placeable *_obj = nullptr; + +public: + Cell() {} + + ~Cell(); + + Landscape *landscape() const { return _l; } + void setLandscape(Landscape *l) + { + delete _l; + _l = l; + } + + Unit *unit() const { return _u; } + void setUnit(Unit *u) { _u = u; } + + Placeable *object() const { return _obj; } + void setObject(Placeable *p) { _obj = p; } +}; + +class RectMap; + +class RectMapIter { + RectMap *_map; + Vec2 _pt; + +public: + RectMapIter(RectMap *map, Vec2 pt) + :_map{map}, _pt{pt} {} + RectMapIter(RectMap *map, int x, int y) + :_map{map}, _pt{x, y} {} + + static RectMapIter makeNull() { return {nullptr, {0, 0}}; } + + bool operator==(const RectMapIter &o) const + { + return _map == o._map + && _pt == o._pt; + } + bool operator!=(const RectMapIter &o) const + { + return !(*this == o); + } + + int x() const { return _pt.x(); } + int y() const { return _pt.y(); } + Vec2 point() const { return _pt; } + + Cell &cell() const; + + bool null() const { return _map == nullptr; } + bool valid() const; + + void moveTo(Vec2 xy); + RectMapIter otherAt(Vec2 xy) const; + + void advance(int d); + RectMapIter advanced(int d) const; +}; + +class RectMap { + const int _w, _h; + Cell * const _storage; + +public: + RectMap(int w, int h) + :_w{w}, _h{h}, _storage{new Cell [w * h]} {} + + int width() const { return _w; } + int height() const { return _h; } + + Cell &at(Vec2 pt) { return _storage[pt.x() + pt.y()*_w]; } + const Cell &at(Vec2 pt) const + { + return _storage[pt.x() + pt.y()*_w]; + } + RectMapIter iterAt(Vec2 pt) { return RectMapIter{this, pt}; } + + ~RectMap() + { + delete[] _storage; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/report.pdf b/8303/Parfentev_Leonid/lab7/report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e3919e8f864aefe3b3e1b1cb9c3a1a877cfb5098 GIT binary patch literal 100100 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 literal 0 HcmV?d00001 diff --git a/8303/Parfentev_Leonid/lab7/restorers.hpp b/8303/Parfentev_Leonid/lab7/restorers.hpp new file mode 100644 index 000000000..6fe67c4e2 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/restorers.hpp @@ -0,0 +1,61 @@ +#ifndef _H_RESTORERS_HPP +#define _H_RESTORERS_HPP + +#include "storable.hpp" + +#include "point.hpp" +#include "map.hpp" +#include "unit.hpp" + +#include "base.hpp" +#include "game.hpp" +#include "player.hpp" + + +template +class SimpleRestorer: public Restorer { +public: + virtual Storable * + restore(std::istream &, + RestorerTable *) const override + { + return new T {}; + } +}; + + +namespace restorers { + + class GameRestorer: public Restorer { + public: + virtual Storable * + restore(std::istream &is, + RestorerTable *tab) const override + { + Storable *s = tab->restore(is); + Map *map = dynamic_cast(s); + if (!map) { + delete s; + return nullptr; + } + + return new Game {map}; + } + }; + + class MapRestorer: public Restorer { + public: + virtual Storable * + restore(std::istream &is, + RestorerTable *) const override + { + int w, h; + is >> w >> h; + + return new Map {w, h}; + } + }; + +} + +#endif diff --git a/8303/Parfentev_Leonid/lab7/storable.cpp b/8303/Parfentev_Leonid/lab7/storable.cpp new file mode 100644 index 000000000..2bd50e407 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/storable.cpp @@ -0,0 +1,81 @@ +#include +#include +#include + +#include "storable.hpp" +#include "restorers.hpp" +#include "common_storables.hpp" + +#include "melee_units.hpp" +#include "ranged_units.hpp" +#include "catapult_units.hpp" +#include "landscape_types.hpp" +#include "neutral_object_types.hpp" + +#include "factory_table.hpp" +#include "iostream_player.hpp" + +#include "game_rules.hpp" + + +RestorerTable * +RestorerTable::defaultTable() +{ + return new RestorerTable {{ + +{"end", new SimpleRestorer {}}, +{"coords", new SimpleRestorer {}}, +{"index", new SimpleRestorer {}}, +{"at", new SimpleRestorer {}}, + +{"game", new restorers::GameRestorer {}}, +{"map", new restorers::MapRestorer {}}, + +{"base", new SimpleRestorer {}}, +{"base_w_countdown", new SimpleRestorer {}}, + +{"iostream_player", new SimpleRestorer {}}, + +{"l_normal", new SimpleRestorer {}}, +{"l_swamp", new SimpleRestorer {}}, +{"l_forest", new SimpleRestorer {}}, + +{"u_swordsman", new SimpleRestorer {}}, +{"u_spearsman", new SimpleRestorer {}}, +{"u_cavalry", new SimpleRestorer {}}, + +{"u_archer", new SimpleRestorer {}}, +{"u_slinger", new SimpleRestorer {}}, + +{"u_onager", new SimpleRestorer {}}, +{"u_boltthrower", new SimpleRestorer {}}, + +{"n_healingwell", new SimpleRestorer {}}, +{"n_tower", new SimpleRestorer {}}, +{"n_tunnelentrance", new SimpleRestorer {}}, +{"n_weaponsmiths", new SimpleRestorer {}}, + +{"uf_melee", + new SimpleRestorer {}}, +{"uf_ranged", + new SimpleRestorer {}}, +{"uf_catapult", + new SimpleRestorer {}}, + +{"mp_basic", new SimpleRestorer {}}, +{"mp_modifyiing", new SimpleRestorer {}}, + +{"dp_basic", new SimpleRestorer {}}, +{"dp_level_deco", new SimpleRestorer {}}, +{"dp_multiplier", new SimpleRestorer {}}, + +{"ap_forbidden", new SimpleRestorer {}}, +{"ap_multiplier", new SimpleRestorer {}}, +{"ap_extended", new SimpleRestorer {}}, + +{"ap_melee", new SimpleRestorer {}}, +{"ap_ranged", new SimpleRestorer {}}, +{"ap_catapult", new SimpleRestorer {}}, + + }}; +} diff --git a/8303/Parfentev_Leonid/lab7/storable.hpp b/8303/Parfentev_Leonid/lab7/storable.hpp new file mode 100644 index 000000000..d152b700c --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/storable.hpp @@ -0,0 +1,70 @@ +#ifndef _STORABLE_HPP +#define _STORABLE_HPP + +#include +#include +#include +#include + + +class RestorerTable; + +class Storable { +public: + virtual void store(std::ostream &os) const =0; + virtual bool restore(std::istream &, + RestorerTable *) { return true; }; + + virtual ~Storable() {} +}; + +#define TRIVIALLY_STORABLE(keyword) \ + public: \ + virtual void \ + store(std::ostream &os) const override \ + { \ + os << keyword "\n"; \ + } + + + +class Restorer { +public: + virtual Storable *restore(std::istream &is, + RestorerTable *tab) const =0; +}; + +class RestorerTable { + std::map _tab; + +public: + RestorerTable(std::map m) + :_tab{std::move(m)} {} + + Storable * + restore(std::istream &is) + { + std::string n; + is >> n; + + auto iter = _tab.find(n); + if (iter == _tab.end()) { + return nullptr; + } + + Storable *s = iter->second->restore(is, this); + if (!s) { + return nullptr; + } + + if (!s->restore(is, this)) { + delete s; + return nullptr; + } + return s; + } + + static RestorerTable *defaultTable(); +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/unit.cpp b/8303/Parfentev_Leonid/lab7/unit.cpp new file mode 100644 index 000000000..39098695f --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/unit.cpp @@ -0,0 +1,48 @@ +#include + +#include "storable.hpp" +#include "common_storables.hpp" +#include "unit.hpp" + + +std::default_random_engine global_random {}; + +void +Unit::store(std::ostream &os) const +{ + os << health() << "\n"; + + movePolicy()->store(os); + defensePolicy()->store(os); + attackPolicy()->store(os); + + os << "end\n"; +} + +bool +Unit::restore(std::istream &is, RestorerTable *tab) +{ + if (!ObjectWithHealth::restore(is, tab)) { + return false; + } + + for (;;) { + Storable *s = tab->restore(is); + + if (auto *mp = dynamic_cast(s)) { + setMovePolicy(mp); + } else if (auto *dp = dynamic_cast(s)) { + setDefensePolicy(dp); + } else if (auto *ap = dynamic_cast(s)) { + setAttackPolicy(ap); + } else if (dynamic_cast(s)) { + delete s; + break; + } else { + delete s; + return false; + } + } + + return true; +} diff --git a/8303/Parfentev_Leonid/lab7/unit.hpp b/8303/Parfentev_Leonid/lab7/unit.hpp new file mode 100644 index 000000000..93f745061 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/unit.hpp @@ -0,0 +1,271 @@ +#ifndef _H_UNIT_HPP +#define _H_UNIT_HPP + +#include +#include +#include + +#include "event.hpp" +#include "event_types.hpp" +#include "object_w_health.hpp" +#include "map.hpp" + + +extern std::default_random_engine global_random; + + +class MovePolicy: public Storable { +public: + virtual bool canMove(const Unit *u, MapIter to) =0; + virtual ~MovePolicy() {} +}; + +enum class AttackKind { + invalid, sword, spear, cavalry, arrow, stone, rock, bolt, +}; + +// NOTE: can’t do area damage +class AttackPolicy: public Storable { +public: + virtual bool canAttackTo(const Unit *u, MapIter to) =0; + + virtual MapIter actualPosition(const Unit *, MapIter to) + { + return to; + } + + // returns kind and base damage + virtual std::pair + baseAttack(const Unit *u, MapIter to) =0; + + virtual ~AttackPolicy() {} +}; + +struct DamageSpec { + int base_damage, damage_spread; + + void + scale(double k) + { + base_damage *= k; + damage_spread *= k; + } + + DamageSpec + scaled(double k) const + { + auto ds = *this; + ds.scale(k); + return ds; + } + + int evaluate() const + { + std::uniform_int_distribution<> + dist {-damage_spread, damage_spread}; + + return base_damage + dist(global_random); + } +}; + +class DefensePolicy: public Storable { +protected: + static DamageSpec + make_spec(double base, double spread) + { + return DamageSpec{(int)base, (int)spread}; + } + + static DamageSpec + defense_level(double k, int dmg) + { + return make_spec(round(1.0*dmg/k), + round(0.25*dmg/k)); + } + + static DamageSpec + normal_defense(double dmg) + { + return defense_level(1.0, dmg); + } + +public: + // returns base damage and spread + virtual DamageSpec + actualDamage(const Unit *u, AttackKind kind, int base) =0; + + virtual ~DefensePolicy() {} +}; + +class MovePolicyContainer { + MovePolicy *_mp; + +public: + MovePolicyContainer(MovePolicy *mp) :_mp{mp} {} + + MovePolicy *movePolicy() const { return _mp; } + void setMovePolicy(MovePolicy *mp) + { + _mp = mp; + } + virtual ~MovePolicyContainer() { delete _mp; } + + MovePolicyContainer * + findMoveContainerOf(const MovePolicy *mp) + { + for (MovePolicyContainer *mpc = this; mpc; + mpc = dynamic_cast( + mpc->movePolicy())) { + if (mpc->movePolicy() == mp) { + return mpc; + } + } + return nullptr; + } +}; + +class DefensePolicyContainer { + DefensePolicy *_dp; + +public: + DefensePolicyContainer(DefensePolicy *dp) :_dp{dp} {} + + DefensePolicy *defensePolicy() const { return _dp; } + void setDefensePolicy(DefensePolicy *dp) + { + _dp = dp; + } + virtual ~DefensePolicyContainer() { delete _dp; } + + DefensePolicyContainer * + findDefenseContainerOf(const DefensePolicy *dp) + { + for (DefensePolicyContainer *dpc = this; dpc; + dpc = dynamic_cast( + dpc->defensePolicy())) { + if (dpc->defensePolicy() == dp) { + return dpc; + } + } + return nullptr; + } +}; + +class AttackPolicyContainer { + AttackPolicy *_ap; + +public: + AttackPolicyContainer(AttackPolicy *ap) :_ap{ap} {} + + AttackPolicy *attackPolicy() const { return _ap; } + void setAttackPolicy(AttackPolicy *ap) + { + _ap = ap; + } + virtual ~AttackPolicyContainer() { delete _ap; } + + AttackPolicyContainer * + findAttackContainerOf(const AttackPolicy *ap) + { + for (AttackPolicyContainer *apc = this; apc; + apc = dynamic_cast( + apc->attackPolicy())) { + if (apc->attackPolicy() == ap) { + return apc; + } + } + return nullptr; + } +}; + +class Unit: public Placeable, + public ObjectWithHealth, + public EventEmitter, + public MovePolicyContainer, + public DefensePolicyContainer, + public AttackPolicyContainer { +public: + Unit(MovePolicy *move, + AttackPolicy *attack, + DefensePolicy *defense, + int base_health) + :ObjectWithHealth{base_health}, + MovePolicyContainer{move}, + DefensePolicyContainer{defense}, + AttackPolicyContainer{attack} {} + + virtual void + heal(int hp) + { + emit(new events::UnitGetsHealed {this, hp}); + ObjectWithHealth::heal(hp); + } + + virtual void + takeDamage(int dmg) + { + if (!alive()) { + return; + } + + emit(new events::UnitTakesDamage {this, dmg}); + + ObjectWithHealth::takeDamage(dmg); + + if (!alive()) { + emit(new events::UnitDeath {this}); + } + } + + bool + canMove(MapIter to) const + { + return movePolicy()->canMove(this, to); + } + + bool + canAttackTo(MapIter to) const + { + return attackPolicy()->canAttackTo(this, to); + } + + MapIter + actualPosition(MapIter to) const + { + return attackPolicy()->actualPosition(this, to); + } + + std::pair + baseAttack(MapIter to) const + { + return attackPolicy()->baseAttack(this, to); + } + + DamageSpec + actualDamage(AttackKind kind, int base) const + { + return defensePolicy()->actualDamage(this, kind, base); + } + + // override to add type symbol! + virtual void store(std::ostream &os) const override; + virtual bool restore(std::istream &is, + RestorerTable *tab) override; + + virtual ~Unit() override + { + if (alive()) { + emit(new events::UnitLiveDeleted {this}); + } + } +}; + +#define UNIT_STORABLE_NAME(n) \ + virtual void \ + store(std::ostream &os) const override \ + { \ + os << n "\n"; \ + Unit::store(os); \ + } + +#endif diff --git a/8303/Parfentev_Leonid/lab7/unit_factory.hpp b/8303/Parfentev_Leonid/lab7/unit_factory.hpp new file mode 100644 index 000000000..5f1f475bd --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/unit_factory.hpp @@ -0,0 +1,24 @@ +#ifndef _H_UNIT_FACTORY_HPP +#define _H_UNIT_FACTORY_HPP + +#include "unit.hpp" + + +class UnitFactory { +public: + virtual Unit *create() const =0; + + virtual ~UnitFactory() {} +}; + +template +class SimpleUnitFactory: public UnitFactory { +public: + virtual Unit * + create() const override + { + return new U {}; + } +}; + +#endif diff --git a/8303/Parfentev_Leonid/lab7/zombie_collector.hpp b/8303/Parfentev_Leonid/lab7/zombie_collector.hpp new file mode 100644 index 000000000..9de5f6981 --- /dev/null +++ b/8303/Parfentev_Leonid/lab7/zombie_collector.hpp @@ -0,0 +1,36 @@ +#ifndef _H_ZOMBIE_COLLECTOR_HPP +#define _H_ZOMBIE_COLLECTOR_HPP + +#include + +#include "unit.hpp" +#include "event.hpp" +#include "map.hpp" + + +class ZombieCollector: public EventListener { + std::vector _units {}; + +public: + virtual void + handle(Event *e) override + { + if (auto *ee = dynamic_cast(e)) { + return handle(ee->event()); + } + if (auto *ee = dynamic_cast(e)) { + _units.push_back(ee->unit()); + } + } + + void + collect(Map *m) + { + for (auto *unit: _units) { + delete m->removeUnitAt(unit->position()); + } + _units.clear(); + } +}; + +#endif