From 9c2a69c727c470ad7410e1865f4155ebc143e22d Mon Sep 17 00:00:00 2001 From: ElektriikAtk <songohansmaka@gmail.com> Date: Wed, 6 Mar 2024 15:38:30 +0100 Subject: [PATCH] Adrian push --- {list => Uppgift-2-Lista}/.gitkeep | 0 {list => Uppgift-2-Lista}/a.out | Bin {list => Uppgift-2-Lista}/catch.hpp | 0 {list => Uppgift-2-Lista}/linked_list.cc | 0 {list => Uppgift-2-Lista}/linked_list.h | 0 {list => Uppgift-2-Lista}/linked_list.h.gch | Bin {list => Uppgift-2-Lista}/linked_list1.cc | 0 {list => Uppgift-2-Lista}/linked_list1.h | 0 {list => Uppgift-2-Lista}/linked_list_test.cc | 0 {list => Uppgift-2-Lista}/test_main.cc | 0 {list => Uppgift-2-Lista}/test_main.o | Bin Uppgift-3-Spel/full_game/Code/constants.h | 18 +++ Uppgift-3-Spel/full_game/Code/entity.cc | 38 +++++ Uppgift-3-Spel/full_game/Code/entity.h | 52 +++++++ Uppgift-3-Spel/full_game/Code/game.cc | 90 ++++++++++++ Uppgift-3-Spel/full_game/Code/game.h | 56 ++++++++ .../full_game/Code/ghost_wrapper.cc | 108 ++++++++++++++ Uppgift-3-Spel/full_game/Code/ghost_wrapper.h | 52 +++++++ Uppgift-3-Spel/full_game/Code/given.h | 6 + Uppgift-3-Spel/full_game/Code/grid.cc | 135 ++++++++++++++++++ Uppgift-3-Spel/full_game/Code/grid.h | 59 ++++++++ Uppgift-3-Spel/full_game/Code/pacman.cc | 92 ++++++++++++ Uppgift-3-Spel/full_game/Code/pacman.h | 52 +++++++ Uppgift-3-Spel/full_game/Makefile | 15 ++ Uppgift-3-Spel/full_game/README.md | 14 ++ Uppgift-3-Spel/full_game/main.cc | 20 +++ Uppgift-3-Spel/full_game/map.txt | 22 +++ Uppgift-3-Spel/ghost.h | 74 ++++++++++ Uppgift-3-Spel/given.cc | 46 ++++++ Uppgift-3-Spel/given.h | 62 ++++++++ Uppgift-3-Spel/main.cc | 112 +++++++++++++++ ghost.h | 76 ---------- 32 files changed, 1123 insertions(+), 76 deletions(-) rename {list => Uppgift-2-Lista}/.gitkeep (100%) rename {list => Uppgift-2-Lista}/a.out (100%) rename {list => Uppgift-2-Lista}/catch.hpp (100%) rename {list => Uppgift-2-Lista}/linked_list.cc (100%) rename {list => Uppgift-2-Lista}/linked_list.h (100%) rename {list => Uppgift-2-Lista}/linked_list.h.gch (100%) rename {list => Uppgift-2-Lista}/linked_list1.cc (100%) rename {list => Uppgift-2-Lista}/linked_list1.h (100%) rename {list => Uppgift-2-Lista}/linked_list_test.cc (100%) rename {list => Uppgift-2-Lista}/test_main.cc (100%) rename {list => Uppgift-2-Lista}/test_main.o (100%) create mode 100644 Uppgift-3-Spel/full_game/Code/constants.h create mode 100644 Uppgift-3-Spel/full_game/Code/entity.cc create mode 100644 Uppgift-3-Spel/full_game/Code/entity.h create mode 100644 Uppgift-3-Spel/full_game/Code/game.cc create mode 100644 Uppgift-3-Spel/full_game/Code/game.h create mode 100644 Uppgift-3-Spel/full_game/Code/ghost_wrapper.cc create mode 100644 Uppgift-3-Spel/full_game/Code/ghost_wrapper.h create mode 100644 Uppgift-3-Spel/full_game/Code/given.h create mode 100644 Uppgift-3-Spel/full_game/Code/grid.cc create mode 100644 Uppgift-3-Spel/full_game/Code/grid.h create mode 100644 Uppgift-3-Spel/full_game/Code/pacman.cc create mode 100644 Uppgift-3-Spel/full_game/Code/pacman.h create mode 100644 Uppgift-3-Spel/full_game/Makefile create mode 100644 Uppgift-3-Spel/full_game/README.md create mode 100644 Uppgift-3-Spel/full_game/main.cc create mode 100644 Uppgift-3-Spel/full_game/map.txt create mode 100644 Uppgift-3-Spel/ghost.h create mode 100644 Uppgift-3-Spel/given.cc create mode 100644 Uppgift-3-Spel/given.h create mode 100644 Uppgift-3-Spel/main.cc delete mode 100644 ghost.h diff --git a/list/.gitkeep b/Uppgift-2-Lista/.gitkeep similarity index 100% rename from list/.gitkeep rename to Uppgift-2-Lista/.gitkeep diff --git a/list/a.out b/Uppgift-2-Lista/a.out similarity index 100% rename from list/a.out rename to Uppgift-2-Lista/a.out diff --git a/list/catch.hpp b/Uppgift-2-Lista/catch.hpp similarity index 100% rename from list/catch.hpp rename to Uppgift-2-Lista/catch.hpp diff --git a/list/linked_list.cc b/Uppgift-2-Lista/linked_list.cc similarity index 100% rename from list/linked_list.cc rename to Uppgift-2-Lista/linked_list.cc diff --git a/list/linked_list.h b/Uppgift-2-Lista/linked_list.h similarity index 100% rename from list/linked_list.h rename to Uppgift-2-Lista/linked_list.h diff --git a/list/linked_list.h.gch b/Uppgift-2-Lista/linked_list.h.gch similarity index 100% rename from list/linked_list.h.gch rename to Uppgift-2-Lista/linked_list.h.gch diff --git a/list/linked_list1.cc b/Uppgift-2-Lista/linked_list1.cc similarity index 100% rename from list/linked_list1.cc rename to Uppgift-2-Lista/linked_list1.cc diff --git a/list/linked_list1.h b/Uppgift-2-Lista/linked_list1.h similarity index 100% rename from list/linked_list1.h rename to Uppgift-2-Lista/linked_list1.h diff --git a/list/linked_list_test.cc b/Uppgift-2-Lista/linked_list_test.cc similarity index 100% rename from list/linked_list_test.cc rename to Uppgift-2-Lista/linked_list_test.cc diff --git a/list/test_main.cc b/Uppgift-2-Lista/test_main.cc similarity index 100% rename from list/test_main.cc rename to Uppgift-2-Lista/test_main.cc diff --git a/list/test_main.o b/Uppgift-2-Lista/test_main.o similarity index 100% rename from list/test_main.o rename to Uppgift-2-Lista/test_main.o diff --git a/Uppgift-3-Spel/full_game/Code/constants.h b/Uppgift-3-Spel/full_game/Code/constants.h new file mode 100644 index 0000000..b977ac6 --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/constants.h @@ -0,0 +1,18 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H + +/* + Konstanter för olika aspekter av spelet. Dessa borde läsas från en fil. + + (inline använd för att slippa lägga definitionen av konstanterna i en cc-fil. Ej en del av + kursen!) + */ +inline int const WIDTH { 19 }; +inline int const HEIGHT { 22 }; +inline int TILE_SIZE { 20 }; + +inline int const SCREEN_WIDTH { WIDTH * (TILE_SIZE + 1) }; +inline int const SCREEN_HEIGHT { HEIGHT * (TILE_SIZE + 1) }; +inline int const FPS { 60 }; + +#endif diff --git a/Uppgift-3-Spel/full_game/Code/entity.cc b/Uppgift-3-Spel/full_game/Code/entity.cc new file mode 100644 index 0000000..65e143c --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/entity.cc @@ -0,0 +1,38 @@ +#include "entity.h" + +#include "constants.h" + +Entity::Entity(sf::Vector2f const& start_position, Grid& grid, int speed, sf::Color const& color) + : current_target { start_position }, + previous_target {}, + current_direction {}, + grid { grid }, + speed { speed } +{ + setPosition(start_position); + setFillColor(color); +} + +void Entity::update() +{ + if (at_target_position()) + { + sf::Vector2f tmp { current_target }; + select_new_target(current_target, current_direction); + previous_target = tmp; + } + move_forward(); + + time_passed += 1.0 / FPS; +} + +bool Entity::at_target_position() const +{ + return abs(getPosition().x - current_target.x) < 0.1 and + abs(getPosition().y - current_target.y) < 0.1; +} + +void Entity::move_forward() +{ + move(static_cast<sf::Vector2f>(current_direction * speed)); +} diff --git a/Uppgift-3-Spel/full_game/Code/entity.h b/Uppgift-3-Spel/full_game/Code/entity.h new file mode 100644 index 0000000..0824a6e --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/entity.h @@ -0,0 +1,52 @@ +#ifndef ENTITY_H +#define ENTITY_H + +#include <SFML/Graphics.hpp> +#include <cmath> + +#include "grid.h" + +/* + Typdeklaration för att representera en position i kartan. Går i diskreta steg där varje steg representerar + en ruta. + */ +using Point = sf::Vector2i; + +/* + Basklass som representerar alla ritbara rörliga objekt i spelet. Har härledda klasser för pacman och spöken. + */ +class Entity : public sf::ConvexShape +{ +public: + Entity(sf::Vector2f const& start_position, Grid& grid, int speed, sf::Color const& color); + virtual ~Entity() = default; + + /* + Uppdaterar objektets position. Varje Entity väljer en position en ruta framåt eller bakåt i x- + eller y-led och flyttar sig sedan med en jämn hastighet dit. När den når den rutan väljer den + en ny ruta och börjar om. Sättet den väljer en ruta sker med select_new_target() och + implementeras av den härledda klassen. + */ + virtual void update(); + +protected: + bool at_target_position() const; + virtual void move_forward(); + + /* + Uppdaterar current_target till en ny position. Ska vara en ruta horisontellt eller vertikalt + bort från nuvarande position. + */ + virtual void select_new_target(sf::Vector2f& current_target, + sf::Vector2i& current_direction) const = 0; + + sf::Vector2f current_target; + sf::Vector2f previous_target; + sf::Vector2i current_direction; + Grid& grid; + int speed; + + float time_passed {}; +}; + +#endif diff --git a/Uppgift-3-Spel/full_game/Code/game.cc b/Uppgift-3-Spel/full_game/Code/game.cc new file mode 100644 index 0000000..f42e556 --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/game.cc @@ -0,0 +1,90 @@ +#include "game.h" + +#include "ghost.h" + +Game::Game() + : grid{"map.txt"}, + pacman {{200, 340}, grid}, + ghosts {}, + window {sf::VideoMode {static_cast<unsigned>(SCREEN_WIDTH), static_cast<unsigned>(SCREEN_HEIGHT)}, "Pacman" } +{ + // Lägger till tre dynamiskt allokerade spöken i en vektor. + // Ett spökes konstruktor tar argumenten (pacman, startposition, scatterposition, + speciella argument). + ghosts.emplace_back(sf::Vector2f{360, 40}, grid, new Blinky{pacman, Point{}, Point{WIDTH, 0}}); + ghosts.emplace_back(sf::Vector2f{40 , 40}, grid, new Pinky {pacman, Point{}, Point{0, 0}}); + ghosts.emplace_back(sf::Vector2f{360, 420}, grid, new Clyde{pacman, Point{}, Point{0, HEIGHT}, 8}); + window.setFramerateLimit(FPS); +} + +void Game::run() +{ + bool running {true}; + while (running) + { + window.clear(); + + sf::Event event; + while (window.pollEvent(event)) + { + if (event.type == sf::Event::Closed) + { + window.close(); + running = false; + } + } + + update(); + draw(); + + if (check_collision()) + { + std::cout << "Du kolliderade med ett spöke..." << std::endl; + running = false; + } + + if (grid.food_count() == 0) + { + std::cout << "Du åt upp all mat! Bra jobbat!!" << std::endl; + running = false; + } + } +} + +void Game::update() +{ + pacman.update(); + + for (Ghost_Wrapper & g : ghosts) + { + g.update(); + } +} + +bool Game::check_collision() const +{ + for (Ghost_Wrapper const& g : ghosts) + { + if (pacman.getGlobalBounds().intersects(g.getGlobalBounds())) + { + return true; + } + } + return false; +} + +void Game::draw() +{ + grid.draw(window); + window.draw(pacman); + for (Ghost_Wrapper const& g : ghosts) + { + window.draw(g); + } + + window.display(); +} + +int Game::get_score() +{ + return pacman.get_score(); +} diff --git a/Uppgift-3-Spel/full_game/Code/game.h b/Uppgift-3-Spel/full_game/Code/game.h new file mode 100644 index 0000000..9f1cf21 --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/game.h @@ -0,0 +1,56 @@ +#ifndef GAME_H +#define GAME_H + +#include "grid.h" +#include "entity.h" +#include "pacman.h" +#include "ghost_wrapper.h" +#include "constants.h" + +/* + Spelklassen har det yttersta ansvaret för spelet. Klassen sköter huvudloopen och deligerar ansvar till andra klasser. + */ +class Game +{ +public: + /* + Skapa en spelinstans och alla objekt i spelvärden. De är just nu hårdkodade till specifika + positioner. Detta borde läsas från kart-filen på ett snyggt sätt. + */ + Game(); + + /* + Huvudloopen i spelet. Det är här allt utgår ifrån. Hanterar användarindata, deligerar till andra + funktioner för att uppdatera spellogik och rendera till fönster. Gör också checkar för om spelet + är slut. + */ + void run(); + + /* + Uppdatera spellogik. tex flytta pacman och alla spöken. + */ + void update(); + + /* + Kolla om pacman har kolliderat med någon av spökena. + */ + bool check_collision() const; + + /* + Rendera alla objekt i spelfönstret. + */ + void draw(); + + /* + Beräkna antal poäng spelaren har samlat på sig just nu. + */ + int get_score(); + +private: + Grid grid; + Pacman pacman; + std::vector<Ghost_Wrapper> ghosts; + sf::RenderWindow window; +}; + +#endif diff --git a/Uppgift-3-Spel/full_game/Code/ghost_wrapper.cc b/Uppgift-3-Spel/full_game/Code/ghost_wrapper.cc new file mode 100644 index 0000000..9ceb1ec --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/ghost_wrapper.cc @@ -0,0 +1,108 @@ +#include "ghost_wrapper.h" + +#include <cmath> +#include <algorithm> + +#include "constants.h" + +Ghost_Wrapper::Ghost_Wrapper(sf::Vector2f const& start_position, Grid& grid, Ghost* ghost) + : Entity {start_position, grid, 1, get_sfml_color(ghost)}, ghost {ghost} +{ + setPointCount(7); + + // Plockar fram ett antal punkter runt en cirkel för att skapa den ikoniska pacman-formen. + float radius { static_cast<float>(TILE_SIZE) / 2 }; + int no_points { 5 }; + for (int i {}; i < no_points; ++i) + { + setPoint(i, sf::Vector2f{ std::cos(3.14f / (no_points - 1) * i) * radius, + -std::sin(3.14f / (no_points - 1) * i) * radius }); + } + setPoint(5, {-radius, radius}); + setPoint(6, { radius, radius}); + +} + +void Ghost_Wrapper::update() +{ + if ((chase_mode && time_passed > 10) or + (!chase_mode && time_passed > 5)) + { + chase_mode = !chase_mode; + time_passed = 0; + + current_direction = -current_direction; + std::swap(current_target, previous_target); + } + + Entity::update(); +} + +// Ghost AI genomgång: https://gameinternals.com/understanding-pac-man-ghost-behavior +void Ghost_Wrapper::select_new_target(sf::Vector2f& current_target, + sf::Vector2i& current_direction) const +{ + sf::Vector2i curr { to_grid_pos(current_target) }; + sf::Vector2i goal {}; + + // Plocka fram den nya 'goal'-punkten. Detta är olika för alla spöken. + ghost->set_position(to_grid_pos(getPosition())); + if (chase_mode) + { + goal = ghost->get_chase_point(); + } + else + { + goal = ghost ->get_scatter_point(); + } + + // Hitta alla punkter precis brevid spöket. Detta är de möjliga nästa destinationerna. + std::vector<sf::Vector2i> potential_points { {curr.x + 1, curr.y}, {curr.x - 1, curr.y}, + {curr.x, curr.y + 1}, {curr.x, curr.y - 1} }; + + // Sortera punkterna så att punkten närmast 'goal'-punkten är först. + std::sort(potential_points.begin(), potential_points.end(), + [&goal](sf::Vector2i const& p1, sf::Vector2i const& p2) + { + double p1_dist { pow(p1.x - goal.x, 2) + pow(p1.y - goal.y, 2) }; + double p2_dist { pow(p2.x - goal.x, 2) + pow(p2.y - goal.y, 2) }; + return p1_dist < p2_dist; + }); + + // Hitta den första punkten som inte är en vägg, eller positionen som spöket precis var på. + sf::Vector2i best_point = *std::find_if(potential_points.begin(), potential_points.end(), + [this](sf::Vector2i const& p) + { + return not grid.wall_at(p) and p != to_grid_pos(previous_target); + }); + + // Uppdatera spökets riktningsvariabel. Kollar om spöket står stilla för att undvika division med noll. + sf::Vector2f new_direction = (to_world_pos(best_point) - current_target); + float length = sqrt( (new_direction.x * new_direction.x) + + (new_direction.y * new_direction.y)); + if (length != 0) + { + current_target = to_world_pos(best_point); + current_direction = static_cast<sf::Vector2i>(new_direction / length); + } + else + { + current_direction = {}; + } +} + +sf::Color Ghost_Wrapper::get_sfml_color(Ghost* ghost) const +{ + std::string color {ghost->get_color()}; + if (color == "red" ) return sf::Color::Red; + if (color == "green" ) return sf::Color::Green; + if (color == "blue" ) return sf::Color::Blue; + if (color == "orange") return sf::Color{255,165,0}; + if (color == "yellow") return sf::Color::Yellow; + if (color == "pink" ) return sf::Color{255,105,180}; + if (color == "purple") return sf::Color{128,0,128}; + if (color == "brown" ) return sf::Color{139,69,19}; + if (color == "gray" ) return sf::Color{128,128,128}; + if (color == "black" ) return sf::Color::Black; + return sf::Color::White; +} diff --git a/Uppgift-3-Spel/full_game/Code/ghost_wrapper.h b/Uppgift-3-Spel/full_game/Code/ghost_wrapper.h new file mode 100644 index 0000000..5fc976d --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/ghost_wrapper.h @@ -0,0 +1,52 @@ +#ifndef GHOST_WRAPPER_H +#define GHOST_WRAPPER_H + +#include <SFML/Graphics.hpp> +#include <memory> + +#include "entity.h" +#include "grid.h" +#include "YOUR_CODE_HERE/ghost.h" + +/* + En klass som äger ett objekt av den studentskapade datatypen "Ghost". Se detta som en klass som + lägger till grafikelement och sökalgoritm till klasserna som studenten skapat. + + */ +class Ghost_Wrapper : public Entity +{ +public: + Ghost_Wrapper(sf::Vector2f const& start_position, Grid& grid, Ghost* ghost); + + /* + Utökar Entitys update med funktionallitet för att byta mellan chase och scatter. + + Anropar sedan Entitys update. Kolla där för detaljer. + */ + void update() override; + +protected: + + /* + Väljer en ny punkt för spöket att gå till. Detta görs när spöket har nått sitt förra mål. + + Notera att detta inte är samma sak som det studenten ska implementera. Detta är ruta för ruta + och använder sig av studentkod, men de är inte samma. + */ + void select_new_target(sf::Vector2f& current_target, + sf::Vector2i& current_direction) const override; + +private: + + /* + Konverterar den färg-strängen som spöket sparar till en faktiskt sfml-färg. + + Följ mönstret för att lägga till fler färger. + */ + sf::Color get_sfml_color(Ghost* ghost) const; + + std::unique_ptr<Ghost> ghost; + bool chase_mode {true}; +}; + +#endif diff --git a/Uppgift-3-Spel/full_game/Code/given.h b/Uppgift-3-Spel/full_game/Code/given.h new file mode 100644 index 0000000..b97738d --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/given.h @@ -0,0 +1,6 @@ +/* + Detta är en liten wrapper för att studentkoden inte ska behöva ändras för den läggs in i spelet. + Utan detta skulle man behöva ändra vilka filer som inkluderas i studentkoden. + */ +#include "constants.h" +#include "pacman.h" diff --git a/Uppgift-3-Spel/full_game/Code/grid.cc b/Uppgift-3-Spel/full_game/Code/grid.cc new file mode 100644 index 0000000..d067166 --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/grid.cc @@ -0,0 +1,135 @@ +#include "grid.h" + +#include <fstream> +#include <deque> +#include <algorithm> + +#include "constants.h" + +std::istream& operator>>(std::istream& is, Row& row) +{ + std::string line {}; + getline(is, line); + std::istringstream iss {line}; + + if (line.size() != WIDTH) + { + is.setstate(std::ios::failbit); + } + + row.clear(); + + char c {}; + while(iss >> c) + { + row.push_back(c); + } + + if ( row.size() != 0 and row.size() != WIDTH ) + { + throw std::runtime_error{"wrong width [" + std::to_string(row.size()) + "] of row. Expected " + std::to_string(WIDTH)}; + } + return is; +} + +sf::Vector2i to_grid_pos(sf::Vector2f const& pos) +{ + return { static_cast<int>((pos.x - TILE_SIZE) / TILE_SIZE), + static_cast<int>((pos.y - TILE_SIZE) / TILE_SIZE) }; +} + +sf::Vector2f to_world_pos(sf::Vector2i const& pos) +{ + return { static_cast<float>(pos.x * TILE_SIZE + TILE_SIZE), + static_cast<float>(pos.y * TILE_SIZE + TILE_SIZE) }; +} + +Grid::Grid(std::string const& map_file) +{ + std::ifstream ifs {map_file}; + + Row row {}; + while (ifs >> row) + { + rows.push_back(row); + } + + if (rows.size() != HEIGHT) + { + throw std::runtime_error{"wrong height [" + std::to_string(rows.size()) + "] of map. Expected " + std::to_string(HEIGHT)}; + } +} + +void Grid::draw(sf::RenderWindow& window) const +{ + // Skapa en rektangel med rätt storlek och färg. Flyttas till olika positioner och ritas ut + // en gång för varje vägg. + sf::RectangleShape box {{TILE_SIZE / 1.5f, TILE_SIZE / 1.5f}}; + box.setFillColor(sf::Color::Blue); + box.setOrigin(box.getSize().x / 2,box.getSize().y / 2); + + // Skapa en cirkel med rätt storlek och färg. Flyttas till olika positioner och ritas ut en gång + // för varje mat. + sf::CircleShape food {2}; + food.setFillColor(sf::Color::White); + food.setOrigin(food.getRadius(), food.getRadius()); + + for (unsigned i {}; i < rows.size(); ++i) + { + for (unsigned j {}; j < rows[i].size(); ++j) + { + switch (rows[i][j]) + { + case 'w': + box.setPosition((j + 1) * TILE_SIZE, (i + 1) * TILE_SIZE); + window.draw(box); + break; + case 'f': + food.setPosition((j + 1) * TILE_SIZE, (i + 1) * TILE_SIZE); + window.draw(food); + break; + default: + break; + } + } + } +} + +bool Grid::wall_at(sf::Vector2i const& grid_pos) const +{ + if (grid_pos.x < 0 or grid_pos.x >= static_cast<int>(rows[0].size()) or + grid_pos.y < 0 or grid_pos.y >= static_cast<int>(rows.size()) ) + { + return true; + } + + return rows[grid_pos.y][grid_pos.x] == 'w'; +} + +bool Grid::eat_food(sf::Vector2i const& grid_pos) +{ + if (rows[grid_pos.y][grid_pos.x] == 'f') + { + rows[grid_pos.y][grid_pos.x] = 'e'; + return true; + } + return false; +} + + +int Grid::food_count() const +{ + int count {}; + + for (unsigned i {}; i < rows.size(); ++i) + { + for (unsigned j {}; j < rows[i].size(); ++j) + { + if (rows[i][j] == 'f') + { + ++count; + } + } + } + return count; +} diff --git a/Uppgift-3-Spel/full_game/Code/grid.h b/Uppgift-3-Spel/full_game/Code/grid.h new file mode 100644 index 0000000..6005b99 --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/grid.h @@ -0,0 +1,59 @@ +#ifndef GRID_H +#define GRID_H + +#include <SFML/Graphics.hpp> +#include <iostream> +#include <sstream> +#include <string> + +/* + Datatyp för att representera en rad i rutnätet som är kartan. + */ +using Row = std::vector<char>; +std::istream& operator>>(std::istream& is, Row& row); + +/* + Hjälpfunktioner för att konvertera mellan två olika coordinatsystem. + + 'grid' är ett coordinatsystem där varje ruta på spelplanen är en coordinat. + 'world' är ett coordinatsystem där varje pixel på skärmen är en coordinat. + */ +sf::Vector2i to_grid_pos(sf::Vector2f const& pos); +sf::Vector2f to_world_pos(sf::Vector2i const& pos); + +/* + Klass som representerar hela kartan. Dvs håller koll på alla positioner för väggar och mat på + kartan. + */ +class Grid +{ +public: + + Grid(std::string const& map_file); + + /* + Rendera alla väggar och all mat. + */ + void draw(sf::RenderWindow& window) const; + + /* + Ger sant om det är en vägg på given position. Notera att positionen måste anges i + 'grid'-systemet. + */ + bool wall_at(sf::Vector2i const& grid_pos) const; + + /* + Ger sant om det var en mat på given position. Plockar då också bort maten. Notera att + positionen måste anges i 'grid'-systemet. + + Om det inte var en mat på positionen returneras 'false' och inget mer händer. + */ + bool eat_food(sf::Vector2i const& grid_pos); + + int food_count() const; + +private: + std::vector<Row> rows {}; +}; + +#endif diff --git a/Uppgift-3-Spel/full_game/Code/pacman.cc b/Uppgift-3-Spel/full_game/Code/pacman.cc new file mode 100644 index 0000000..4134a8e --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/pacman.cc @@ -0,0 +1,92 @@ +#include "pacman.h" + +#include <cmath> + +#include "constants.h" + +Pacman::Pacman(sf::Vector2f const& start_position, Grid& grid) + : Entity{start_position, grid, 2, sf::Color::Yellow}, + next_direction {}, score {} +{ + int no_points { 8 }; + float radius { 10 }; + + setPointCount(no_points); + + for (int i {}; i < no_points; ++i) + { + setPoint(i, sf::Vector2f{ std::cos(6.28f / (no_points) * i) * radius, + std::sin(6.28f / (no_points) * i) * radius }); + } +} + +void Pacman::update() +{ + handle_input(); + + if (next_direction == -current_direction) + { + current_direction = next_direction; + std::swap(current_target, previous_target); + } + + if (grid.eat_food(to_grid_pos(getPosition()))) + { + score += 10; + } + + Entity::update(); +} + +Point Pacman::get_position() const +{ + return { static_cast<int>((getPosition().x - TILE_SIZE) / TILE_SIZE), + static_cast<int>((getPosition().y - TILE_SIZE) / TILE_SIZE) }; +} + +Point Pacman::get_direction() const +{ + return { current_direction.x, current_direction.y }; +} + +void Pacman::select_new_target(sf::Vector2f& current_target, + sf::Vector2i& current_direction) const +{ + // Plockar fram potentiella nästa punkter. Det är antingen en position framåt i spelarens + // riktning, eller stå stilla. + std::vector<sf::Vector2i> candidates { next_direction, current_direction }; + + // Stega igenom listan med potentiella punkter och plocka den första som inte är en vägg. + for (sf::Vector2i const& candidate : candidates) + { + sf::Vector2f new_target { current_target + static_cast<sf::Vector2f>(candidate * static_cast<int>(TILE_SIZE)) }; + if ( not grid.wall_at(to_grid_pos(new_target)) ) + { + current_direction = candidate; + current_target = new_target; + return; + } + } + current_direction = {}; +} + +void Pacman::handle_input() +{ + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) + { + next_direction = {0, -1}; + } + else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) + { + next_direction = {0, 1}; + } + else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) + { + next_direction = {-1, 0}; + } + else if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) + { + next_direction = {1, 0}; + } +} + diff --git a/Uppgift-3-Spel/full_game/Code/pacman.h b/Uppgift-3-Spel/full_game/Code/pacman.h new file mode 100644 index 0000000..325b1b4 --- /dev/null +++ b/Uppgift-3-Spel/full_game/Code/pacman.h @@ -0,0 +1,52 @@ +#ifndef PACMAN_H +#define PACMAN_H + +#include <SFML/Graphics.hpp> + +#include "entity.h" +#include "grid.h" + +/* + Pacmanklassen representerar spelarefiguren i spelet. Den innehåller funktionallitet för att flytta och rendera spelarefiguren. + + Är en specialicering av basklassen Entity. + */ +class Pacman : public Entity +{ +public: + Pacman(sf::Vector2f const& start_position, Grid& grid); + + /* + Uppdatera pacmans position och riktning. + */ + void update() override; + + /* + Hämta ut olika egenskaper från pacman. + */ + unsigned get_score() const { return score; }; + Point get_position() const; + Point get_direction() const; + +protected: + + /* + Använder användainmatning för att beräkna vilken nästa ruta spelare ska röra sig till är. + */ + void select_new_target(sf::Vector2f& current_target, + sf::Vector2i& current_direction) const override; + +private: + + /* + Kollar vilka knappar som är nedtryckta och uppdaterar variabeln next_direction baserat på det. + + Ändra i denna om du vill ha andra knappar som styr spelaren. + */ + void handle_input(); + + sf::Vector2i next_direction; + unsigned score; +}; + +#endif diff --git a/Uppgift-3-Spel/full_game/Makefile b/Uppgift-3-Spel/full_game/Makefile new file mode 100644 index 0000000..dad0d52 --- /dev/null +++ b/Uppgift-3-Spel/full_game/Makefile @@ -0,0 +1,15 @@ +FLAGS = -std=c++17 -Wall -Wextra -Wpedantic -Weffc++ -g -I. -ICode -IYOUR_CODE_HERE +LIB = -lsfml-graphics -lsfml-system -lsfml-window + +pacman: main.cc game.o entity.o pacman.o ghost_wrapper.o grid.o student_code.o + g++ $(FLAGS) -o pacman main.cc game.o entity.o pacman.o ghost_wrapper.o grid.o student_code.o $(LIB) + +student_code.o: $(shell find YOUR_CODE_HERE -type f) + g++ -o student_code.o $(FLAGS) -c YOUR_CODE_HERE/*.cc + +%.o: Code/%.cc Code/%.h + g++ $(FLAGS) -o $@ -c $< + +.PHONY: clean +clean: + rm -f *.o ./pacman diff --git a/Uppgift-3-Spel/full_game/README.md b/Uppgift-3-Spel/full_game/README.md new file mode 100644 index 0000000..480b754 --- /dev/null +++ b/Uppgift-3-Spel/full_game/README.md @@ -0,0 +1,14 @@ +Steg för att spela pacman med dina egna spöken. + +1. Lägg en (1) headerfil med kod för alla spöken i mappen YOUR_CODE_HERE. Den ska heta ghost.h. +2. Lägg en (1) implemenationsfil med kod för alla spöken i mappen YOUR_CODE_HERE. +3. Flytta *inte* med huvudprogrammet eller de givna filerna till mappen. +4. Stå i mappen med filen som heter Makefile och skriv 'make' i terminalen. +5. Kör programmet med './pacman'. +6. Fly från dina spöken. + +Om du har problem med att kompilera koden, se till att alla klasser och funktioner heter som angivet +i labbinstruktionen. Det går också att gå in i spelkoden och kolla vad som väntas. Det är primärt +filerna Code/ghost_wrapper.h och Code/game.h som är relevanta. + +Prata med en assistent om ni inte får det att kompilera. diff --git a/Uppgift-3-Spel/full_game/main.cc b/Uppgift-3-Spel/full_game/main.cc new file mode 100644 index 0000000..06a9467 --- /dev/null +++ b/Uppgift-3-Spel/full_game/main.cc @@ -0,0 +1,20 @@ + +#include <SFML/Graphics.hpp> +#include <iostream> + +#include "game.h" + +using namespace std; + +int main() +{ + cout << "Välkommen till Pacman\nTryck 'Enter' för att starta"; + cin.get(); + + Game game {}; + game.run(); + + cout << "Du fick " << game.get_score() << " poäng" << std::endl; + cout << "Tryck 'Enter' för att avsluta" << std::endl; + cin.get(); +} diff --git a/Uppgift-3-Spel/full_game/map.txt b/Uppgift-3-Spel/full_game/map.txt new file mode 100644 index 0000000..f757795 --- /dev/null +++ b/Uppgift-3-Spel/full_game/map.txt @@ -0,0 +1,22 @@ +wwwwwwwwwwwwwwwwwww +wffffffffwffffffffw +wfwwfwwwfwfwwwfwwfw +wfwwfwwwfwfwwwfwwfw +wfffffffffffffffffw +wfwwfwfwwwwwfwfwwfw +wffffwfffwfffwffffw +wwwwfwwwewewwwfwwww +wwwwfweeeeeeewfwwww +wwwwfwewwwwwewfwwww +eeeefeewwwwweefeeee +wwwwfwewwwwwewfwwww +wwwwfweeeeeeewfwwww +wwwwfwewwwwwewfwwww +wffffffffwffffffffw +wfwwfwwwfwfwwwfwwfw +wffwfffffffffwfwffw +wwfwfwfwwwwwfwfwfww +wffffwfffwffffffffw +wfwwwwwwfwfwwwwwwfw +wfffffffffffffffffw +wwwwwwwwwwwwwwwwwww diff --git a/Uppgift-3-Spel/ghost.h b/Uppgift-3-Spel/ghost.h new file mode 100644 index 0000000..74e3750 --- /dev/null +++ b/Uppgift-3-Spel/ghost.h @@ -0,0 +1,74 @@ +#ifndef GHOST_H +#define GHOST_H + +#include "given.h" +#include "string" +using namespace std; + +class Ghost +{ +public: + Ghost(Point const& start_position, std::string const& color); + virtual ~Ghost() = default; + virtual Point get_scatter_point(const Point& pacmanPosition) const = 0; + virtual Point get_chase_point(const Point& pacmanPosition, const Point& pacmanDirection) = 0; + virtual void set_position(const Point& new_position); + virtual Point get_position(); + virtual std::string get_color() const = 0; + // virtual Point get_target_position() const = 0; + + virtual void set_blinky_position(const Point& new_position) + { + blinkyPosition.x = new_position.x; + blinkyPosition.y = new_position.y; + } + virtual void set_pinky_position(const Point& new_position) + { + pinkyPosition.x = new_position.x; + pinkyPosition.y = new_position.y; + } +protected: + Point position; + std::string colorName; + Point blinkyPosition; + Point pinkyPosition; +}; + +class Clyde +{ +public: + Clyde(std::string const &color, Point const &start_pos); + void set_pos(const Point &new_pos); + Point get_scatter(const Point &pos_pacman); + Point get_chase(const Point &pos_pacman, const Point &dir_pacman); + Point get_pos(); + std::string get_color()const; +}; + +class Pinky +{ +public: + Pinky(std::string const &color, Point const &start_pos); + void set_pos(const Point &new_pos); + Point get_scatter(const Point &pos_pacman); + Point get_chase(const Point &pos_pacman, const Point &dir_pacman); + Point get_pos(); + std::string get_color()const; +}; + + + +class Blinky +{ +public: + bool angry(); + bool anger_activated(bool state); + Blinky(std::string const &color, Point const &start_pos); + void set_pos(const Point &new_pos); + Point get_scatter(const Point &pos_pacman); + Point get_chase(const Point &pos_pacman, const Point &dir_pacman); + Point get_pos(); + std::string get_color()const; +}; + +#endif \ No newline at end of file diff --git a/Uppgift-3-Spel/given.cc b/Uppgift-3-Spel/given.cc new file mode 100644 index 0000000..154bfe8 --- /dev/null +++ b/Uppgift-3-Spel/given.cc @@ -0,0 +1,46 @@ +#include "given.h" + +int WIDTH = 19; +int HEIGHT = 22; + +bool operator==(Point const& lhs, Point const& rhs) +{ + return lhs.x == rhs.x && lhs.y == rhs.y; +} + +std::istream& operator>>(std::istream& is, Point& rhs) +{ + return is >> rhs.x >> rhs.y; +} + +Pacman::Pacman() + : pos {}, dir {1,0} +{} + +Point Pacman::get_position() const +{ + return pos; +} + +void Pacman::set_position(Point const& p) +{ + if (p.x > WIDTH or p.x < 0 or p.y > HEIGHT or p.y < 0) + { + throw std::runtime_error("position outside valid range"); + } + pos = p; +} + +Point Pacman::get_direction() const +{ + return dir; +} + +void Pacman::set_direction(Point const& p) +{ + if (p.x > 1 or p.x < -1 or p.y > 1 or p.y < -1 or abs(p.x + p.y) != 1) + { + throw std::runtime_error("direction outside valid range"); + } + dir = p; +} diff --git a/Uppgift-3-Spel/given.h b/Uppgift-3-Spel/given.h new file mode 100644 index 0000000..2999a6d --- /dev/null +++ b/Uppgift-3-Spel/given.h @@ -0,0 +1,62 @@ +// TODO: skydd f�r dubbel inkludering + +/* + I denna fil finns ett utval av kod fr�n det dina kollegor skrivit till ert pacman-projekt. Det �r + precis det som du beh�ver f�r att skriva din del av koden. + + Tips: Du f�r ut�ka denna fil med saker som du beh�ver f�r att ditt program ska kompilera. Det �r + �ven okej att ut�ka Point med fler operatorer om det skulle beh�vas. + */ + + +#include <stdexcept> +#include <iostream> + + +/* + Globala variabler f�r storlek p� spelplanen. + + Din kollega som skrev detta �r medveten om att globala variabler �r d�ligt och borde undvikas, men + har inte haft tid att �tg�rda det. I den slutgiltiga versionen av koden borde dessa s�klart l�sas + fr�n en fil med konfigurationsdata. + */ +extern int WIDTH; +extern int HEIGHT; + + +/* + Ett aggregat som anv�nd f�r att representera punkter och riktninar p� spelplanen. + Aggregatet har ocks� hj�lpsamma operatorer som kan underl�tta din implementation. + + */ +struct Point +{ + int x; + int y; +}; +bool operator==(Point const& lhs, Point const& rhs); +std::istream& operator>>(std::istream& is, Point& rhs); + + +/* + En klass f�r att representera spelarfiguren. Detta �r en nedskalad version j�mf�rt med det som + kollegan skrev till spelet. Notera att din kod inte beh�ver �ndras om man skulle g� fr�n denna + implementaion till den faktiska implementationen. + */ +class Pacman +{ +public: + + Pacman(); + + Point get_position() const; + void set_position(Point const& p); + + Point get_direction() const; + void set_direction(Point const& p); + +private: + + Point pos {}; + Point dir {}; +}; diff --git a/Uppgift-3-Spel/main.cc b/Uppgift-3-Spel/main.cc new file mode 100644 index 0000000..6f829e5 --- /dev/null +++ b/Uppgift-3-Spel/main.cc @@ -0,0 +1,112 @@ +#include "ghost.h" +#include <string> +#include <iostream> +#include <iomanip> +#include <sstream> + +using namespace std; + +/* + Ledning och Tips: + + - Modifiera stukturen till en header-fil och en implementationsfil + - Ut�ka 'run()' och 'draw_map()' med �vrig funktionalitet. + - L�gg alla sp�ken i en l�mplig beh�llare som en datamedlem. + - Bryt ut stora kodblock till egna funktioner. + - Anv�nd hj�lpfunktioner f�r att undvika duplicering av kod. + - T�nk p� att varje funktion inte borde vara l�ngre �n 25 rader. + */ + +class Ghost_Tester +{ + +public: + + Ghost_Tester() + : pacman {} + { + } + + void run() + { + while(true) + { + draw_map(); + cout << "> "; + + string line {}; + getline(cin, line); + istringstream iss {line}; + + string command {}; + iss >> command; + + if (command == "pos") + { + Point new_pos {}; + iss >> new_pos.x >> new_pos.y; + pacman.set_position(new_pos); + } + else if (command == "dir") + { + } + else if (command == "quit") + { + break; + } + } + } + +private: + + /* + En hj�lpfunktion som avg�r vilka tv� tecken som ska ritas ut f�r en given position p� + spelplanen. + */ + string to_draw(Point const& curr_pos) + { + string to_draw{" "}; + + if (pacman.get_position() == curr_pos) + { + to_draw[1] = '@'; + } + + return to_draw; + } + + /* + En hj�lpfunktion f�r att rita ut spelplanen f�r testprogrammet. + + Itererar �ver varje rad och column i kartan. Index f�r raderna �r flippade f�r att placera + y = 0 l�ngst ned. + + Varje punkt i kartan ritas som tv� tecken eftersom ett tecken i terminalen �r ca dubbelt s� + h�gt som det �r brett. + */ + void draw_map() + { + cout << "+" << setfill('-') << setw(WIDTH * 2) << "-" << "+\n"; + + for (int y {HEIGHT - 1}; y >= 0; --y) + { + cout << "|"; + for (int x {}; x < WIDTH; ++x) + { + cout << to_draw( Point{x,y} ); + } + cout << "|\n"; + } + + cout << "+" << setfill('-') << setw(WIDTH * 2) << "-" << "+" << endl; + } + + Pacman pacman; +}; + +int main() +{ + Ghost_Tester gt {}; + gt.run(); + return 0; +} diff --git a/ghost.h b/ghost.h deleted file mode 100644 index 7256769..0000000 --- a/ghost.h +++ /dev/null @@ -1,76 +0,0 @@ - -#infdef ghost.h -#define ghost.h - -#include "given.h" -#include "string" -using namespace std; -class Ghost -{ -public: -Clyde(std::string const &color, Point const &start_pos); -virtual void set_pos(const Point &new_pos); -virtual Point get_scatter(const Point &pos_pacman) = 0; -virtual Point get_chase(const Point &pos_pacman, const Point &dir_pacman) = 0; -virtual Point get_pos(); -virtual std::string get_color()const = 0; -virtual ~Ghost() = default; - - - -private: -std::string color_index -Point position; - -}; - - - -class Clyde -{ - public: -Clyde(std::string const &color, Point const &start_pos); -void set_pos(const Point &new_pos); -Point get_scatter(const Point &pos_pacman); -Point get_chase(const Point &pos_pacman, const Point &dir_pacman); -Point get_pos(); -std::string get_color()const; - - - -}; - - - -class Pinky -{ - public: -Pinky(std::string const &color, Point const &start_pos); -void set_pos(const Point &new_pos); -Point get_scatter(const Point &pos_pacman); -Point get_chase(const Point &pos_pacman, const Point &dir_pacman); -Point get_pos(); -std::string get_color()const; - - - -}; - - - -class Blinky -{ - public: -Bool angry(); -anger_activated(bool state); -Blinky(std::string const &color, Point const &start_pos); -void set_pos(const Point &new_pos); -Point get_scatter(const Point &pos_pacman); -Point get_chase(const Point &pos_pacman, const Point &dir_pacman); -Point get_pos(); -std::string get_color()const; - - - -}; -#endif \ No newline at end of file -- GitLab