Coding Feature.

SFML) 정렬 알고리즘 그래픽으로 구현하기. 본문

Game Development/SFML

SFML) 정렬 알고리즘 그래픽으로 구현하기.

codingfeature 2023. 9. 3. 14:12

많은 사람들이 SFML과 같은 그래픽 라이브러리를 사용하면서 하는 것이 바로 알고리즘을 그래픽으로 구현해 가시화, 시뮬레이션 하는 것이다.

대표적으로 정렬 알고리즘을 많이 하는데 나도 직접 한번 구현해보기로 했다.

 

SFML에서 Rectangle 오브젝트를 가지고 긴 막대기로 각 Element의 value 값을 표시했다.

Element의 value 값이 클 수록 막대기는 위로 길쭉하게 나타나는 것이다.

그 다음 C++의 random 라이브러리를 이용해서 무작위로 섞고 정렬 알고리즘으로 다시 재정렬되는 과정을 그래픽화하였다.

 

#include <SFML/Graphics.hpp>
#include <iostream>
#include <random>
#include <chrono>
#include <vector>
#include <string>

// 정렬될 ELEMENT에 대한 정보.
#define ELEMENT_SIZE 100
#define ELEMENT_WIDTH 5
#define ELEMENT_HEIGHT_MULTIPLIER 2
#define BETWEEN_ELEMENT_SIZE 1
int elements[ELEMENT_SIZE] = {0, };

// Window에 대한 정보.
#define BELOW_BLANK_SIZE 300
#define ABOVE_BLACN_SIZE 50
#define WINDOW_SIZE_WIDTH (ELEMENT_WIDTH + BETWEEN_ELEMENT_SIZE) * (ELEMENT_SIZE + 2)
#define WINDOW_SIZE_HEIGHT ELEMENT_SIZE * ELEMENT_HEIGHT_MULTIPLIER + BELOW_BLANK_SIZE + ABOVE_BLACN_SIZE

// Text에 대한 정보.
sf::Font font;
sf::Text random_text;
sf::Text bubble_text;
sf::Text select_text;
#define TEXT_SIZE 24
#define GAP_BETWEEN_TEXT 10

sf::RenderWindow window(sf::VideoMode(WINDOW_SIZE_WIDTH, WINDOW_SIZE_HEIGHT), "Sorting Simulator");

std::vector<sf::RectangleShape> rect_vec;

// 각 Element에 대해서 그래픽으로 그림.
void DrawElements(int key_element) {
	int i;
	for (i = 0; i < ELEMENT_SIZE; i++) {
		sf::RectangleShape rect(sf::Vector2f(ELEMENT_WIDTH, elements[i] * ELEMENT_HEIGHT_MULTIPLIER));
		rect.setOrigin(0, elements[i] * ELEMENT_HEIGHT_MULTIPLIER);
		rect.setPosition(sf::Vector2f(ELEMENT_WIDTH * 2 + i * (ELEMENT_WIDTH + BETWEEN_ELEMENT_SIZE), WINDOW_SIZE_HEIGHT - BELOW_BLANK_SIZE));
		rect.setFillColor(sf::Color::Black);
		if(i == key_element)
			rect.setFillColor(sf::Color::Green);
		window.draw(rect);
	}
}

// Text에 대한 속성을 설정.
void SetText() {
	random_text.setFont(font);
	random_text.setString("RANDOMIZE");
	random_text.setOutlineThickness(5);
	random_text.setOutlineColor(sf::Color::Black);
	random_text.setPosition(50, WINDOW_SIZE_HEIGHT - BELOW_BLANK_SIZE);
	random_text.setCharacterSize(TEXT_SIZE);

	bubble_text.setFont(font);
	bubble_text.setString("BUBBLE SORT");
	bubble_text.setOutlineThickness(5);
	bubble_text.setOutlineColor(sf::Color::Black);
	bubble_text.setPosition(50, WINDOW_SIZE_HEIGHT - BELOW_BLANK_SIZE + (TEXT_SIZE + GAP_BETWEEN_TEXT));
	bubble_text.setCharacterSize(TEXT_SIZE);

	select_text.setFont(font);
	select_text.setString("SELECTION SORT");
	select_text.setOutlineThickness(5);
	select_text.setOutlineColor(sf::Color::Black);
	select_text.setPosition(50, WINDOW_SIZE_HEIGHT - BELOW_BLANK_SIZE + (TEXT_SIZE + GAP_BETWEEN_TEXT) * 2);
	select_text.setCharacterSize(TEXT_SIZE);
};

void DrawText() {
	window.draw(random_text);
	window.draw(bubble_text);
	window.draw(select_text);
}

void BubbleSort() {
	int i, j, temp;
	sf::Event event;

	for (i = 0; i < ELEMENT_SIZE - 1; i++) {
		for (j = 0; j < ELEMENT_SIZE - i - 1; j++) {
			while (window.pollEvent(event)) {
				if (event.type == sf::Event::Closed) {
					window.close();
					return;
				}
			}

			window.clear(sf::Color::White);
			DrawElements(j);
			DrawText();
			window.display();
			if (elements[j] > elements[j + 1]) {
				temp = elements[j + 1];
				elements[j + 1] = elements[j];
				elements[j] = temp;
			}
		}
	}
}

void SelectionSort() {
	int i, j, least, temp;
	sf::Event event;

	for (i = 0; i < ELEMENT_SIZE - 1; i++) {
		
		least = i;

		for (j = i + 1; j < ELEMENT_SIZE; j++) {

			while (window.pollEvent(event)) {
				if (event.type == sf::Event::Closed) {
					window.close();
					return;
				}
			}

			window.clear(sf::Color::White);
			DrawElements(j);
			DrawText();
			window.display();

			if (elements[j] < elements[least])
				least = j;
		}

		if (i != least) {
			temp = elements[least];
			elements[least] = elements[i];
			elements[i] = temp;
		}
	}
}

int main() {
	int i;
	unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
	bool is_sorting_done = false;
	std::default_random_engine engine(seed);

	font.loadFromFile("Arial.ttf");
	SetText();

	for (i = 0; i < ELEMENT_SIZE; i++) {
		elements[i] = i + 1;
	}

	std::shuffle(elements, elements + ELEMENT_SIZE, engine);

	while (window.isOpen()) {
		sf::Event event;
		while (window.pollEvent(event)) {
			if (event.type == sf::Event::Closed)
				window.close();

		}
		if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
			int mouse_x = sf::Mouse::getPosition(window).x;
			int mouse_y = sf::Mouse::getPosition(window).y;

			// randomize 버튼 클릭 시.
			if (random_text.getGlobalBounds().contains(mouse_x, mouse_y)) {
				std::cout << mouse_x << mouse_y;
				std::shuffle(elements, elements + ELEMENT_SIZE, engine);
				is_sorting_done = false;
			}
			// 버블 정렬 버튼 클릭 시.
			if (bubble_text.getGlobalBounds().contains(mouse_x, mouse_y) && is_sorting_done == false) {
				BubbleSort();
				is_sorting_done = true;
			}
			// 선택 정렬 버튼 클릭 시.
			if (select_text.getGlobalBounds().contains(mouse_x, mouse_y) && is_sorting_done == false) {
				SelectionSort();
				is_sorting_done = true;
			}
		}

		window.clear(sf::Color::White);
		DrawElements(-1);
		DrawText();
		window.display();
	}

	return 0;
}

Text의 속성을 처리하는 방식이 지저분한데, 아직 C++의 객체 지향에 익숙하지 않아서 나중에 좀 더 익숙해지면 다시 수정해야겠다.

 

결과.

보완할 점.

- 더 많은 정렬 알고리즘의 구현. (예를 들어, 퀵 정렬, 카운팅 정렬, 힙 정렬, 머지 소트 등)

- 경과 시간 표시로 성능을 수치화하여 비교해볼 수 있는 기능.