Skip to content

Instantly share code, notes, and snippets.

@yuyoyuppe
Created December 16, 2014 14:55
Show Gist options
  • Save yuyoyuppe/3edd605caea025085f52 to your computer and use it in GitHub Desktop.
Save yuyoyuppe/3edd605caea025085f52 to your computer and use it in GitHub Desktop.
clone of http://moh97.us/flow/. dev time ~1.5h
#include <SFML/Graphics.hpp>
#include <chrono>
#include <iostream>
#include <vector>
///////////////////////////////////////////////////////////////////////////////////////////
// КОНСТАНТЫ, СТРУКТУРЫ И ПРОЧИЕ НЯШНОСТИ
float screen_width = 500;
float screen_height = 500;
int field_size = 5;
struct Link
{
int colorid;
sf::Vector2i position;
};
struct Path
{
int colorid;
std::vector<sf::Vector2i> links;
bool completed;
};
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// ИГРОВАЯ ЛОГИКА
std::vector<Link> fixed_points = {{0, {0, 0}}, {1, {1, 2}}, {1, {0, 1}}, {0, {2, 2}}}; // стратовые координаты
//std::vector<Path> paths; // пути
std::vector<Path> paths = {{0, {{0, 0}, {1, 0}, {2, 0}, {2, 1}}}}; // для удобства
typedef std::pair<bool, Link> OptionalLink;
OptionalLink pen_cell; // последняя позиция пера. первый параметр bool будет false, если не рисуем путь
sf::Vector2i cell_from_xy(sf::Vector2i xy)
{
return{xy.x / screen_width * field_size, xy.y / screen_height * field_size};
}
OptionalLink none_link()
{
return std::make_pair(false, Link{-1, sf::Vector2i(-1, -1)});
}
OptionalLink search_in_fixed(sf::Vector2i cell)
{
auto res = std::find_if(fixed_points.begin(), fixed_points.end(), [&](Link l) {return cell == l.position;});
if(fixed_points.end() != res)
return std::make_pair(true, *res);
else
return none_link();
}
OptionalLink search_in_paths(sf::Vector2i cell)
{
for(auto & p : paths)
{
auto res = std::find_if(p.links.begin(), p.links.end(), [&](sf::Vector2i l) {return cell == l; });
if(p.links.end() != res)
return std::make_pair(true, Link{p.colorid, *res});
}
return none_link();
}
void remove_path_by_colorid(int color_id)
{
paths.erase(std::remove_if(paths.begin(), paths.end(), [&](Path & p) {return p.colorid == color_id;}), paths.end());
}
Path * path_by_link(Link ol)
{
for(auto & p : paths)
{
auto res = std::find_if(p.links.begin(), p.links.end(), [&](sf::Vector2i l) {return ol.position == l;});
if(p.links.end() != res)
return &p;
}
return nullptr;
}
void handle_pressed_down(sf::Vector2i cell)
{
pen_cell = search_in_fixed(cell);
if(!pen_cell.first)
pen_cell = search_in_paths(cell);
std::cout << "pressed down at " << cell.x << " " << cell.y << (pen_cell.first? " and probably could start a path!" : "") << std::endl;
}
void handle_released(sf::Vector2i cell)
{
std::cout << "released in " << cell.x << " " << cell.y << std::endl;
pen_cell.first = false;
}
bool validate(sf::Vector2i from, sf::Vector2i to)
{
sf::Vector2f diff{float((from - to).x), float((from - to).y)};
float length = std::sqrt(diff.x * diff.x + diff.y * diff.y);
return (length - 1.0f) < 0.0001f;
}
void handle_move_to_another_pressed(sf::Vector2i cell)
{
if(!pen_cell.first)
{
std::cout << "cannot draw a path from the invalid position!" << std::endl;
return;
}
Path * current_path = nullptr;
auto pen_search = search_in_fixed(pen_cell.second.position);
if(pen_search.first) // если нам нужно начать новый путь, т.к. мы рисуем из фиксированной точки
{
remove_path_by_colorid(pen_search.second.colorid); // предварительно удаляем путь заданного цвета, если он был
paths.push_back(Path{pen_cell.second.colorid, {pen_cell.second.position}}); // создаём его
current_path = &paths.back(); // и направляем на него указатель - пусть он охренеет
}
else
current_path = path_by_link(pen_cell.second); // иначе находим существующий путь
auto movedto_search = search_in_fixed(cell); // теперь займёмся точкой, в которую мы переместились
bool path_completed = false;
if(movedto_search.first) // если мы попали в фиксированную точку
{
if((movedto_search.second.colorid == pen_cell.second.colorid) && !search_in_paths(cell).first) // если цвета совпадают, а координаты - нет, то заканчиваем путь.
{
std::cout << "you've completed the path!" << std::endl;
path_completed = true; // добавляем к условию победы
}
else // иначе ничего не делаем
{
std::cout << "cannot do anything from here!" << std::endl;
return;
}
}
else // если мы не попали в фикс. точку, то нужно проверить, попали ли мы на какой-нибудь путь
{
movedto_search = search_in_paths(cell);
if(movedto_search.first) // если мы попали в существующий путь
{
if(movedto_search.second.colorid == pen_cell.second.colorid) // если мы попали в свой путь
{
std::cout << "moved while pressed down to " << cell.x << " " << cell.y << " to the same path" << std::endl;
auto res = std::find_if(current_path->links.begin(), current_path->links.end(),
[&](sf::Vector2i l) {return l == movedto_search.second.position; }); // ищем в пути позицию текущей координаты
current_path->links.erase(res, current_path->links.end()); // удаляем всё, что было после неё, т.к. теперь мы путь рисуем от неё
}
else
{
std::cout << "moved while pressed down to " << cell.x << " " << cell.y << " on the different path" << std::endl;
auto tmp = Link{current_path->colorid, current_path->links.front()}; // запоминаем первое звено текущего пути, т.к. придётся получать на него указатель заново(C++ за 300)
remove_path_by_colorid(movedto_search.second.colorid); // удаляем другой путь, т.к. мы рисуем на его месте
current_path = path_by_link(tmp); // восстанавливаем указатель, ведь он умер после удаления пути
}
}
else // попали в пустоту
{
std::cout << "moved while pressed down to " << cell.x << " " << cell.y << " to the v01d" << std::endl;
auto res = std::find_if(current_path->links.begin(), current_path->links.end(),
[](sf::Vector2i l) {return l == pen_cell.second.position; }); // ищем в пути позицию предыдущей координаты, т.е. pen_cell
current_path->links.erase(++res, current_path->links.end()); // удаляем всё, что было после pen_cell, т.к. теперь мы путь рисуем от неё, а оставшийся путь нам больше не нужен
}
}
if(validate(pen_cell.second.position, cell)) // проверяем, что новая ячейка находится на расстоянии 1 от предыдущей, т.е. 1 шаг в горизонтали или вертикали онли
{ // предотвращает случайное перемещение по диагонали и проброс через фиксированную точку
current_path->links.push_back(cell); // добавляем в путь текущую координату
pen_cell = OptionalLink{true, {current_path->colorid, cell}};
}
else // не будем ничего добавлять, если человек пытается протянуть как-то безумно:)
{
std::cout << "Whoa! calm down, stop trying to polish the screen!" << std::endl;
pen_cell = none_link(); // прерываем рисование. заебал
}
current_path->completed = path_completed;
}
void handle_controls(sf::RenderWindow & w, float delta)
{
static bool ButtonPressed = false; // запоминаем состоянии мышки!
static sf::Vector2i current_cell = {0, 0};
sf::Event event;
while(w.pollEvent(event))
{
if(event.type == sf::Event::Closed)
w.close();
if(event.type == sf::Event::MouseButtonPressed) // обрабатывааем событие нажатия кнопки мышки
{
current_cell = cell_from_xy({event.mouseButton.x, event.mouseButton.y});
handle_pressed_down(current_cell);
ButtonPressed = true;
}
if(event.type == sf::Event::MouseMoved && ButtonPressed) // обрабатывааем событие перемещения мышки с нажатой кнопкой
{
auto possibly_new_cell = cell_from_xy({event.mouseMove.x, event.mouseMove.y}); // нам интересен только факт перемещения в другую ячейку
if(possibly_new_cell != current_cell)
{
current_cell = possibly_new_cell;
handle_move_to_another_pressed(current_cell);
}
}
if(event.type == sf::Event::MouseButtonReleased) // обрабатываем событие отпускания кнопки мышки
{
current_cell = cell_from_xy({event.mouseButton.x, event.mouseButton.y});
handle_released(current_cell);
ButtonPressed = false;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// ГРАФОН
std::vector<sf::Color> color_values; // случайные цвета по айди, ня
void draw_line(sf::RenderWindow & w, sf::Vector2f start, sf::Vector2f end, sf::Color c)
{
sf::Vertex line[] =
{
sf::Vertex(start),
sf::Vertex(end)
};
line[0].color = c;
line[1].color = c;
w.draw(line, 2, sf::Lines);
}
void draw_circle(sf::RenderWindow & w, sf::Vector2i cell, sf::Color c)
{
sf::CircleShape s(screen_width / field_size / 5);
s.setFillColor(c);
s.setPosition(cell.x * screen_width / field_size + screen_width / field_size / 2 - screen_width / field_size / 5,
cell.y * screen_height / field_size + screen_height / field_size / 2 - screen_width / field_size / 5);
w.draw(s);
}
sf::Color color_from_id(int id)
{
if(id >= color_values.size())
color_values.push_back(sf::Color(rand() % 255, rand() % 255, rand() % 255));
return color_values[id];
}
void draw_elements(sf::RenderWindow & w)
{
// рисуем решетку
for(int i = 1; i < field_size; i++)
{
draw_line(w, {screen_width / field_size * i, 0.0f}, {screen_width / field_size * i, screen_height}, sf::Color::Black);
draw_line(w, {0.0f, screen_height / field_size * i}, {screen_width, screen_height / field_size * i}, sf::Color::Black);
}
// рисуем начальные звенья
for(auto & p : fixed_points)
draw_circle(w, p.position, color_from_id(p.colorid));
// рисуем пути
for(auto & p : paths)
for(int i = 1; i < p.links.size(); i++)
draw_line(w,
{p.links[i - 1].x * screen_width / field_size + screen_width / field_size / 2, p.links[i - 1].y * screen_height / field_size + screen_height / field_size / 2},
{p.links[i].x * screen_width / field_size + screen_width / field_size / 2, p.links[i].y * screen_height / field_size + screen_height / field_size / 2},
color_from_id(p.colorid));
}
///////////////////////////////////////////////////////////////////////////////////////////
void game()
{
srand(time(NULL));
sf::RenderWindow window(sf::VideoMode(screen_width, screen_height), "Ebola");
window.setFramerateLimit(250);
float delta_time = 0;
while(window.isOpen())
{
auto frame_start = std::chrono::high_resolution_clock::now();
window.clear(sf::Color(153, 217, 234));
handle_controls(window, delta_time);
draw_elements(window);
window.display();
delta_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - frame_start).count();
if(paths.size() == fixed_points.size() / 2) // проверяем условие победы
{
if(std::all_of(paths.begin(), paths.end(), [](Path p) {return p.completed; }))
{
std::cout << "you won^_^" << std::endl;
break;
}
}
}
}
void settings()
{
char choice = '1';
while(true)
{
std::cout << "1. Resolution\n2.Field size\n3.Exit\nSelect an option: ";
std::cin >> choice;
switch(choice)
{
case '1':
std::cin >> screen_height;
screen_width = screen_height;
break;
case '2':
std::cin >> field_size;
break;
default:
case '3':
return;
break;
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
int main()
{
char choice = '1';
while(true)
{
std::cout << "1. New game\n2.Settings\n3.Exit\nSelect an option: ";
std::cin >> choice;
switch(choice)
{
case '1':
game();
break;
case '2':
settings();
break;
default:
case '3':
return 0;
break;
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment