Advertisement
Guest User

Untitled

a guest
Feb 25th, 2018
166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 18.73 KB | None | 0 0
  1. #include <ctime>
  2. #include <cmath>
  3. #include <exception>
  4. #include <string>
  5. #include <list>
  6. #include <queue>
  7. #include <chrono>
  8. #include <memory>
  9. #include <algorithm>
  10. #include <iostream>
  11. #include <iomanip>
  12. #include <windows.h>
  13. using namespace std;
  14. #include <SFML/Graphics.hpp>
  15.  
  16. namespace MyColors {
  17.   sf::Color const gray         = sf::Color(128, 128, 128);
  18.   sf::Color const royal_blue   = sf::Color(65 , 105, 225);
  19.   sf::Color const tomato       = sf::Color(255, 99 , 71 );
  20.   sf::Color const orange       = sf::Color(255, 165,  0 );
  21.   sf::Color const gold         = sf::Color(255, 215,  0 );
  22.   sf::Color const lime_green   = sf::Color(50 , 205, 50 );
  23.   sf::Color const dark_green   = sf::Color(0  , 100,  0 );
  24. }
  25.  
  26. // #define private public
  27. // #define protected public
  28. // #define printPartPos cout \
  29. << "(X: " << setw(4) \
  30. << (*part)->filler->rectangle.getPosition().x << ", "\
  31. << "Y: " << setw(4) \
  32. << (*part)->filler->rectangle.getPosition().y << ");" << endl
  33.  
  34. struct Coord {
  35.     // Описывает координату квадрата в матрице квадратов.
  36.     // От левого верхнего угла: x - по ширине, y - по высоте.
  37.     // Координаты целые знаковые - это позволяет использовать
  38.     // их как компоненты вектора направления чего-либо (змейки).
  39.     int x = -1, y = -1;
  40.     double vectorLenght() const {
  41.         return sqrt(x*x + y*y);
  42.     }
  43.    
  44.     bool  operator== (double value) const {
  45.         return abs(vectorLenght() - value) < 1e-5;
  46.     }
  47.     bool  operator== (Coord const& rhs) {
  48.         return this->x == rhs.x && this->y == rhs.y;
  49.     }
  50.     bool  operator!= (int value) const {
  51.         return !(*this == value);
  52.     }
  53.     Coord operator+  (Coord const& rhs) const {
  54.         return {this->x + rhs.x, this->y + rhs.y};
  55.     }
  56.     Coord operator-  (Coord const& rhs) const {
  57.         return *this + -rhs;
  58.     }
  59.     Coord operator-  () const {
  60.         return {-this->x, -this->y};
  61.     }
  62. };
  63. class Cell: public sf::Drawable {
  64.   public:
  65.     class Filler: public sf::Drawable {
  66.       public:
  67.         enum class Type {
  68.             Barrier  ,
  69.             Eat      ,
  70.             Poison   ,
  71.             Destroyer
  72.         };
  73.      
  74.       public:
  75.         Filler(sf::Shape* shape): shape(shape) { }
  76.        
  77.         void move(float x, float y) {
  78.             shape->move(x, y);
  79.         }
  80.         virtual Type getType() const = 0;
  81.        
  82.       private:
  83.         unique_ptr<sf::Shape> shape;
  84.         void draw(sf::RenderTarget& target, sf::RenderStates states) const override  {
  85.             target.draw(*shape, states);
  86.         }
  87.     };
  88.    
  89.   public:
  90.     Cell() = default;
  91.     Cell(Cell&& cell)
  92.     : coord(cell.coord)
  93.     , filler(move(cell.filler))
  94.     , isUsable(cell.isUsable)
  95.     , isPrintable(cell.isPrintable)
  96.     {}
  97.    
  98.     Coord coord = {-1, -1};
  99.     unique_ptr<Filler> filler = nullptr;
  100.     bool isUsable = true;
  101.     bool isPrintable = false;
  102.    
  103.   private:
  104.     void draw(sf::RenderTarget& target, sf::RenderStates states) const override  {
  105.         target.draw(*filler, states);
  106.     }
  107. };
  108.  
  109. struct RectangleSettings {
  110.     RectangleSettings(float cell_width, float cell_height)
  111.     : default_rectangle({cell_width, cell_height})
  112.     {
  113.         default_rectangle.setFillColor(MyColors::royal_blue);
  114.     }
  115.     sf::RectangleShape default_rectangle;
  116.    
  117.     struct Settings {
  118.         Settings(Coord pos_on_field, sf::Color color, float scale = 1)
  119.         : pos_on_field(pos_on_field)
  120.         , color(color)
  121.         , scale(scale)
  122.         { }
  123.    
  124.         sf::RectangleShape configure(sf::RectangleShape rect) {
  125.             setPos(rect);
  126.             rect.setFillColor(color);
  127.             setScale(rect);
  128.            
  129.             return rect;
  130.         }
  131.  
  132.       private:
  133.         void setPos(sf::RectangleShape& rect) {
  134.             float width  = rect.getSize().x;
  135.             float height = rect.getSize().y;
  136.             rect.setPosition(width * pos_on_field.x, height * pos_on_field.y);
  137.         }
  138.         void setScale(sf::RectangleShape& rect) {
  139.             if(abs(scale - 1) < 1e-5)
  140.                 return;
  141.            
  142.             float inverse = 1 - scale;
  143.             rect.move(rect.getSize().x * inverse/2, rect.getSize().y * inverse/2);
  144.             rect.setScale({scale, scale});
  145.         }
  146.        
  147.       private:
  148.         Coord pos_on_field;
  149.         sf::Color color;
  150.         float scale;
  151.     };
  152.    
  153.     sf::RectangleShape rectangleGenerator(Settings settings) const {
  154.         return settings.configure(default_rectangle);
  155.     }
  156. };
  157. class Barrier  : public Cell::Filler {
  158.   public:
  159.     Type getType() const override { return Type::Barrier; }
  160.     Barrier(RectangleSettings const& settings, Cell& cell)
  161.     : Filler(new sf::RectangleShape(settings.rectangleGenerator(
  162.             { cell.coord, MyColors::gray }
  163.     )))
  164.     {
  165.         cell.isUsable = false;
  166.         cell.isPrintable = true;
  167.     } // gray
  168. };
  169. class SnakePart: public Cell::Filler {
  170.   public:
  171.     Type getType() const override { return Type::Barrier; }
  172.     SnakePart(Cell& cell, sf::RectangleShape const& rect)
  173.     : Filler(new sf::RectangleShape(rect))
  174.     {
  175.         cell.isUsable = false;
  176.         cell.isPrintable = true;
  177.     }
  178. };
  179. class Bonus    : public Cell::Filler {
  180.   public:
  181.     Bonus(Cell& cell, sf::RectangleShape const& rect)
  182.     : Filler(new sf::RectangleShape(rect))
  183.     {
  184.         cell.isUsable = true;
  185.         cell.isPrintable = true;
  186.     }
  187. };
  188. class SnakeHead: public SnakePart {
  189.   public:
  190.     SnakeHead(RectangleSettings const& settings, Cell& cell)
  191.     : SnakePart(cell, settings.rectangleGenerator({cell.coord, MyColors::dark_green}))
  192.     { }
  193. };
  194. class SnakeBody: public SnakePart {
  195.   public:
  196.     SnakeBody(RectangleSettings const& settings, Cell& cell)
  197.     : SnakePart(cell, settings.rectangleGenerator({cell.coord, MyColors::lime_green}))
  198.     { } // lime_green
  199. };
  200. class Eat      : public Bonus {
  201.   public:
  202.     Type getType() const override { return Type::Eat; }
  203.     Eat(RectangleSettings const& settings, Cell& cell)
  204.     : Bonus(cell, settings.rectangleGenerator({cell.coord, MyColors::orange}))
  205.     { } // orange
  206. };
  207. class Poison   : public Bonus {
  208.   public:
  209.     Type getType() const override { return Type::Poison; }
  210.     Poison(RectangleSettings const& settings, Cell& cell)
  211.     : Bonus(cell, settings.rectangleGenerator({cell.coord, MyColors::tomato}))
  212.     { } // tomato
  213. };
  214. class Destroyer: public Bonus {
  215.   public:
  216.     Type getType() const override { return Type::Destroyer; }
  217.     Destroyer(RectangleSettings const& settings, Cell& cell)
  218.     : Bonus(cell, settings.rectangleGenerator({cell.coord, MyColors::gold}))
  219.     {  } // gold
  220. };
  221.  
  222. class CellsPool {
  223.   public:
  224.     struct NotFoundFreeCell: public exception {
  225.         NotFoundFreeCell(Cell const& cell, Coord direction = {0, 0})
  226.         : cell(cell)
  227.         , direction(direction)
  228.         { }
  229.        
  230.         char const* what() const noexcept override {
  231.             return "Free cell not found";
  232.         }
  233.        
  234.         Cell const& cell;
  235.         Coord direction;
  236.     };
  237.    
  238.   public:
  239.     CellsPool(size_t count_cells_x, size_t count_cells_y,
  240.               sf::RenderWindow& window, RectangleSettings const& settings)
  241.     : settings(settings)
  242.     , count_cells_x(count_cells_x)
  243.     , count_cells_y(count_cells_y)
  244.     , window(window)
  245.      {
  246.          cells.resize(count_cells_y);
  247.          for(auto& row: cells)
  248.              row = move(vector<Cell>(count_cells_x));
  249.          
  250.          for(size_t y = 0; y != count_cells_y; ++y)
  251.             for(size_t x = 0; x != count_cells_x; ++x) {
  252.                 cells[y][x].coord = {int(x), int(y)};
  253.                 available_cells.push_back(&cells[y][x]);
  254.             }
  255.     }
  256.    
  257.     float cell_width () { return settings.default_rectangle.getSize().x; }
  258.     float cell_height() { return settings.default_rectangle.getSize().y; }
  259.    
  260.     Cell* extractCell(Coord coord) {
  261.         int y = coord.y, x = coord.x;
  262.         auto isInRange = [] (int z, int max) {
  263.             return 0 <= z && z < max;
  264.         };
  265.    
  266.         bool xInRange = isInRange(x, count_cells_x);
  267.         bool yInRange = isInRange(y, count_cells_y);
  268.    
  269.         // Нормировка координат требуемой клетки.
  270.         Cell* required_cell = nullptr;
  271.         if(xInRange && yInRange) {
  272.             required_cell = &cells[y][x];
  273.         } else {
  274.             if(yInRange) {
  275.                 if(x < 0)
  276.                     required_cell = &cells[y][count_cells_x - 1];
  277.                 else // if(x >= int(count_cells_x))
  278.                     required_cell = &cells[y][0];
  279.             }
  280.            
  281.             else if(xInRange) {
  282.                 if(y < 0)
  283.                     required_cell = &cells[count_cells_y - 1][x];
  284.                 else // if(y >= int(count_cells_y))
  285.                     required_cell = &cells[0][x];
  286.             }
  287.            
  288.             else
  289.                 throw logic_error("Both components outed of bounds");
  290.         }
  291.        
  292.         return required_cell;
  293.     }
  294.     Cell* kickFromAvailable(list<Cell*>::iterator runner, Cell::Filler* filler, bool isPrintable = true) {
  295.         // Выбросить клетку из свободных и
  296.         // обновить её содержание новым заполнителем.
  297.         Cell* cell = *runner;
  298.         available_cells.erase(runner);
  299.         cell->filler.reset(filler);
  300.         cell->isPrintable = isPrintable;
  301.         return cell;
  302.     }
  303.     list<Cell*>::iterator findInAvailable(Cell* cell) {
  304.         auto runner = available_cells.begin();
  305.         while(runner != available_cells.end() && *runner != cell)
  306.             ++runner;
  307.        
  308.         if(runner == available_cells.end())
  309.             throw NotFoundFreeCell(*cell);
  310.         else
  311.             return runner;
  312.     }
  313.    
  314.     template <class Filler>
  315.     Cell* getRandCell() {
  316.         size_t i = 0;
  317.         size_t rand_cell = rand()%available_cells.size();
  318.         auto runner = available_cells.begin();
  319.         while(i++ != rand_cell)
  320.             ++runner;
  321.    
  322.         return kickFromAvailable(runner, new Filler(settings, **runner));
  323.     }
  324.     template <class Filler>
  325.     Cell* getNearCell(Cell* cell) {
  326.         size_t y = size_t(cell->coord.y), x = size_t(cell->coord.x);
  327.         Cell* up    = y   != 0             ? &cells[y-1][x  ] : nullptr;
  328.         Cell* down  = y+1 != count_cells_y ? &cells[y+1][x  ] : nullptr;
  329.         Cell* right = x+1 != count_cells_x ? &cells[y  ][x+1] : nullptr;
  330.         Cell* left  = x   != 0             ? &cells[y  ][x-1] : nullptr;
  331.         vector<Cell*> neighbors = {up, down, right, left};
  332.        
  333.         while(!neighbors.empty()) {
  334.             size_t rand_neighbor = rand() % neighbors.size();
  335.             Cell* neighbor = neighbors[rand_neighbor];
  336.            
  337.             if(neighbor && neighbor->isUsable) {
  338.                 Filler* filler = new Filler(settings, *neighbor);
  339.                 return kickFromAvailable(findInAvailable(neighbor), filler);
  340.             }
  341.             else
  342.                 neighbors.erase(neighbors.begin() + rand_neighbor);
  343.         }
  344.        
  345.         throw NotFoundFreeCell(*cell);
  346.     }
  347.     template <class Filler>
  348.     Cell* getNearCell(Cell* cell, Coord move_vector, bool isPrintable = true) {
  349.         Cell* required_cell = extractCell(cell->coord + move_vector);
  350.         Filler* filler = new Filler(settings, *required_cell);
  351.         return kickFromAvailable(findInAvailable(required_cell), filler, isPrintable);
  352.     }
  353.     void  releaseCell(Cell* cell) {
  354.         cell->filler = nullptr;
  355.         cell->isPrintable = false;
  356.         available_cells.push_front(cell);
  357.     }
  358.    
  359.     void display() {
  360.         window.clear(settings.default_rectangle.getFillColor());
  361.        
  362.         for(auto& row: cells)
  363.             for(auto& cell: row)
  364.                 if(cell.isPrintable)
  365.                     window.draw(cell);
  366.        
  367.         window.display();
  368.     }
  369.    
  370.   private:
  371.     RectangleSettings const& settings;
  372.     size_t count_cells_x;
  373.     size_t count_cells_y;
  374.     sf::RenderWindow& window;
  375.     vector<vector<Cell>> cells;
  376.     list<Cell*> available_cells;
  377. };
  378.  
  379. class Snake {
  380.   public:
  381.     enum class Direction { Up, Down, Left, Right };
  382.    
  383.   public:
  384.     Snake(CellsPool& cells_pool, size_t max_start_parts = 4,
  385.           chrono::duration<int64_t, nano> move_time = 80ms)
  386.     : move_time(move_time)
  387.     , cells_pool(cells_pool)
  388.     {
  389.         body.push_back(cells_pool.getRandCell<SnakeHead>());
  390.    
  391.         int guaranteed_parts = 2;
  392.         int additional_parts = rand()%(max_start_parts - guaranteed_parts);
  393.         int start_parts = additional_parts + guaranteed_parts;
  394.         try {
  395.             while(start_parts--)
  396.                 body.push_back(cells_pool.getNearCell<SnakeBody>(body.back()));
  397.         } catch(CellsPool::NotFoundFreeCell const& e) {
  398.             // Заканчиваем построение змеи - нет клеток для продолжения.
  399.         }
  400.        
  401.         direction = (*body.begin())->coord - (*++body.begin())->coord;
  402.     }
  403.    
  404.     void move() {
  405.         if(chrono::steady_clock::now() - last_move < move_time)
  406.            return;
  407.         last_move = chrono::steady_clock::now();
  408.         tryChangeDirection();
  409.    
  410.         auto new_head = cells_pool.getNearCell<SnakeHead>(body.front(), direction, false);
  411.         // slide(new_head->coord); // Создать видимость плавного перехода
  412.        
  413.         cells_pool.releaseCell(body.front());
  414.         body.push_front(cells_pool.getNearCell<SnakeBody>(body.front(), {0, 0}));
  415.         body.pop_front();
  416.        
  417.         body.push_front(new_head);
  418.         body.front()->isPrintable = true;
  419.        
  420.         cells_pool.releaseCell(body.back());
  421.         body.pop_back();
  422.     }
  423.     void changeDirection(Direction direction) {
  424.         switch(direction) {
  425.             case Direction::Up   : moves.push({ 0, -1}); break;
  426.             case Direction::Down : moves.push({ 0,  1}); break;
  427.             case Direction::Left : moves.push({-1,  0}); break;
  428.             case Direction::Right: moves.push({ 1,  0}); break;
  429.         }
  430.     }
  431.    
  432.   private:
  433.     void tryChangeDirection() {
  434.         while(!moves.empty() && moves.front() + direction == 0 && moves.front() == direction)
  435.             moves.pop();
  436.    
  437.         if(!moves.empty()) {
  438.             direction = moves.front();
  439.             moves.pop();
  440.         }
  441.     }
  442.     void slide(Coord destination) {
  443.         float move_step = 4;
  444.         size_t width  = size_t(cells_pool.cell_width()  * (1/move_step));
  445.         size_t height = size_t(cells_pool.cell_height() * (1/move_step));
  446.         size_t distance = (width + height) / 2;
  447.         chrono::duration<int64_t, nano> time_step = move_time / distance;
  448.         float adjusted_distance = distance / move_step;
  449.        
  450.         for(float i = move_step; i <= adjusted_distance; i += move_step) {
  451.             chrono::time_point<chrono::steady_clock, chrono::nanoseconds>
  452.             delay_start = chrono::steady_clock::now();
  453.    
  454.             if(body.front()->coord - destination != 1)
  455.                 continue;
  456.            
  457.             cout << "Start: " << endl;
  458.             auto part = body.begin();
  459.             Coord part_direction = destination - (*part)->coord;
  460.             while(true) {
  461.                 // printPartPos;
  462.                 (*part)->filler->move(part_direction.x * move_step, part_direction.y * move_step);
  463.                 // printPartPos << endl;
  464.                 auto next_part = part++;
  465.                 if(part != body.end())
  466.                     part_direction = (*next_part)->coord - (*part)->coord;
  467.                 else
  468.                     break;
  469.             }
  470.             cout << "Stop." << endl << endl;
  471.            
  472.             while(chrono::steady_clock::now() - delay_start < time_step)
  473.                 ;
  474.             cells_pool.display();
  475.         }
  476.     }
  477.    
  478.   private:
  479.     list<Cell*> body;
  480.     Coord direction = {0, 0};
  481.     chrono::duration<int64_t, nano> move_time;
  482.     queue<Coord> moves;
  483.     chrono::time_point<chrono::steady_clock, chrono::nanoseconds>
  484.     last_move = chrono::steady_clock::now();
  485.    
  486.     CellsPool& cells_pool;
  487. };
  488.  
  489. class Game {
  490.   public:
  491.     Game(unsigned long width, unsigned long height,
  492.           size_t count_cells_x, size_t count_cells_y,
  493.           string FieldName)
  494.     : settings(width / count_cells_x, height / count_cells_y)
  495.     , window(sf::VideoMode(width, height), FieldName, sf::Style::Default)
  496.     , cells_pool(count_cells_x, count_cells_y, window, settings)
  497.     , snake(cells_pool)
  498.     {
  499.         size_t cell_width  = static_cast<size_t>(settings.default_rectangle.getSize().x);
  500.         size_t cell_height = static_cast<size_t>(settings.default_rectangle.getSize().y);
  501.         size_t normalized_size_x = cell_width  * count_cells_x;
  502.         size_t normalized_size_y = cell_height * count_cells_y;
  503.         window.create({normalized_size_x, normalized_size_y}, FieldName, sf::Style::Default);
  504.         return;
  505.     }
  506.    
  507.     void mainLoop() {
  508.         try {
  509.             while(window.isOpen()) {
  510.                 cells_pool.display();
  511.                 handle_events();
  512.                 snake.move();
  513.             }
  514.         } catch(CellsPool::NotFoundFreeCell const& e) {
  515.    
  516.         }
  517.     }
  518.     void handle_events() {
  519.         sf::Event event;
  520.         while(window.pollEvent(event)) {
  521.             switch(event.type) {
  522.                 case sf::Event::Closed:
  523.                     window.close();
  524.                     return;
  525.                    
  526.                 case sf::Event::KeyPressed:
  527.                     switch(event.key.code) {
  528.                         case sf::Keyboard::Up    :
  529.                             snake.changeDirection(Snake::Direction::Up);
  530.                             return;
  531.                         case sf::Keyboard::Down  :
  532.                             snake.changeDirection(Snake::Direction::Down);
  533.                             return;
  534.                         case sf::Keyboard::Left  :
  535.                             snake.changeDirection(Snake::Direction::Left);
  536.                             return;
  537.                         case sf::Keyboard::Right :
  538.                             snake.changeDirection(Snake::Direction::Right);
  539.                             return;
  540.                         default:
  541.                             return;
  542.                     }
  543.                 default: ;
  544.             }
  545.         }
  546.     }
  547.    
  548.   private:
  549.     RectangleSettings settings;
  550.     sf::RenderWindow window;
  551.     CellsPool cells_pool;
  552.     Snake snake;
  553. };
  554.  
  555. int main() {
  556.     ShowWindow(GetConsoleWindow(), SW_HIDE);
  557.     srand(static_cast<unsigned int>(time(0)));
  558.    
  559.    
  560.     Game(500, 500, 25, 25, "Snake").mainLoop();
  561.     return 0;
  562. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement