Compare commits

..

24 Commits

Author SHA1 Message Date
1bae5b1d68 only pick words of 15 and shorter 2024-08-04 20:10:02 +02:00
946dcb70e9 set vcpkg to tag and remove curl 2024-08-04 19:31:17 +02:00
1ec6accc5b use release tag 2024-08-04 19:00:00 +02:00
5ab7b40154 resize 2024-08-04 16:16:31 +02:00
549214014d resize support 2024-08-04 15:59:32 +02:00
472b439bed support close 2024-08-04 15:50:42 +02:00
27ac1468c6 hills 2024-08-04 15:43:06 +02:00
3c4dc154b1 add get hills 2024-08-04 15:33:49 +02:00
5e9082b327 restructure 2024-08-04 15:33:41 +02:00
5e728417aa hilldata 2024-08-04 15:24:26 +02:00
97d2e1913f hills len 2024-08-04 15:19:30 +02:00
67318b1be9 sdl_image 2024-08-04 15:18:30 +02:00
7e508cfc85 rename 2024-08-04 15:12:27 +02:00
c609d5ccb0 hills 2024-08-04 15:11:11 +02:00
d99633e7cf bin to array 2024-08-04 14:58:05 +02:00
98a2879129 move to data 2024-08-04 14:51:10 +02:00
86a805b6e9 improve char showing 2024-08-04 14:49:54 +02:00
2ac8c05e1d handle input 2024-08-04 14:43:52 +02:00
7c62375761 font loading and drawing chars 2024-08-03 11:17:48 +02:00
cf538f2cf5 fix 2024-08-03 11:12:12 +02:00
50fe9d9c6d sdl-main 2024-08-03 11:08:26 +02:00
91328c4aca open font 2024-08-03 10:57:53 +02:00
cf7a9a1f1a font 2024-08-03 10:50:29 +02:00
21aeaab088 correct y 2024-08-03 10:47:56 +02:00
23 changed files with 33623 additions and 34 deletions

View File

@ -9,22 +9,33 @@ endif ()
find_package(SDL2 CONFIG REQUIRED)
find_package(SDL2_ttf CONFIG REQUIRED)
find_package(CURL REQUIRED)
find_package(SDL2_image CONFIG REQUIRED)
file(GLOB KULLE_SOURCES src/data/kulle_*_png.cpp)
file(GLOB KULLE_HEADERS src/data/kulle_*_png.hpp)
add_executable(hang_man src/main.cpp
src/Game.cpp
src/Game.hpp
src/State.hpp
src/words.hpp
src/words.cpp
src/data/words.hpp
src/data/words.cpp
src/utils.hpp
src/GuessCorrector.cpp
src/GuessCorrector.hpp
src/default_font.cpp
src/default_font.hpp
${KULLE_SOURCES}
${KULLE_HEADERS}
src/data/hills.cpp
src/data/hills.hpp
src/utils.cpp
)
target_link_libraries(hang_man PRIVATE
SDL2::SDL2
SDL2::SDL2main
SDL2_ttf::SDL2_ttf
CURL::libcurl
SDL2_image::SDL2_image
)

61
bin-to-array.py Normal file
View File

@ -0,0 +1,61 @@
"""
Use this script to write data files as c arrays. This is used to stay cross-platform for embedding.
"""
import argparse
import os.path
def format_hex_line(data):
hex_data = ', '.join(f'0x{byte:02x}' for byte in data)
return f' {hex_data},\n'
def hexdump_to_cpp_array(file_name: str, output_dir: str, variable_name: str):
path = os.path.join(output_dir, variable_name)
header_path, source_path = f"{path}.hpp", f"{path}.cpp"
# Header
with open(header_path, 'w') as file:
file.write("#pragma once\n\n")
file.write("#include <cstddef>\n\n")
file.write(f"extern const unsigned char {variable_name}[];\n")
file.write(f"extern const size_t {variable_name}_length;\n")
# Source
byte_count = 0
with open(file_name, 'rb') as bin_file, open(source_path, 'w') as source_file:
source_file.write(f"// This is the bytes of {file_name}\n")
source_file.write(f'#include "{variable_name}.hpp"\n\n')
source_file.write(f"const unsigned char {variable_name}[] = {{\n")
while True:
chunk = bin_file.read(16)
if not chunk:
break
byte_count += len(chunk)
source_file.write(format_hex_line(chunk))
source_file.write("};\n")
source_file.write(f"const size_t {variable_name}_length = {byte_count};\n")
def main():
parser = argparse.ArgumentParser(description="Hexdump a file to a cpp array.")
parser.add_argument("file", help="Path to the binary file to be dumped")
parser.add_argument("-d", "--dir", help="Output directory", metavar="OUTPUT")
parser.add_argument("-n", "--name", help="Name of the array and header/source files", default="data")
args = parser.parse_args()
if args.dir:
try:
hexdump_to_cpp_array(args.file, args.dir, args.name)
path = os.path.join(args.dir, args.name)
print(f"Hex dump written to {path}.hpp & {path}.cpp")
except FileNotFoundError:
print(f"File not found: {args.file}")
except IOError as e:
print(f"Error: {e}")
else:
print("No output dir specified. Exiting.")
if __name__ == "__main__":
main()

View File

@ -1,15 +1,20 @@
#include <iostream>
#include <algorithm>
#include <random>
#include <sstream>
#include "Game.hpp"
#include "SDL.h"
#include "SDL_ttf.h"
#include "words.hpp"
#include "data/words.hpp"
#include "utils.hpp"
#include "default_font.hpp"
#include "State.hpp"
const int CHAR_SIZE = 30;
const int STEP_SIZE = CHAR_SIZE + CHAR_SIZE / 2;
const int UNDERSCORE_DY = 10;
const SDL_Color TEXT_COLOR = {255, 255, 255};
const int MAX_GUESSES = 8;
void Game::Run() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
@ -40,22 +45,27 @@ void Game::Run() {
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
Game game;
bool quit = false;
SDL_Event event;
while (!quit) {
game.draw(renderer);
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
quit = true;
break;
case SDL_KEYDOWN :
game.handle_key(event.key.keysym.sym);
game.draw(renderer);
{
Game game;
SDL_Event event;
while (true) {
game.draw(renderer);
while (SDL_WaitEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
goto quit;
case SDL_KEYDOWN:
game.handle_key(event.key.keysym.sym);
game.draw(renderer);
break;
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_CLOSE)
goto quit;
break;
}
}
}
quit:;
}
SDL_DestroyRenderer(renderer);
@ -64,7 +74,27 @@ void Game::Run() {
SDL_Quit();
}
Game::Game() {
Game::Game() :
m_wrong_guesses(0),
m_game_state(State::PLAY),
m_hills(get_resized(get_hills(), 400, 400)) {
const char *defaultFontPath = getDefaultFontPath();
if (defaultFontPath == nullptr) {
std::stringstream ss;
ss << "Font path is not set for this platform (null)";
auto s = ss.str();
std::cerr << s << std::endl;
throw std::runtime_error(s);
}
font = TTF_OpenFont(defaultFontPath, CHAR_SIZE);
if (font == nullptr) {
std::stringstream ss;
ss << "Failed to load font: " << TTF_GetError();
auto s = ss.str();
std::cerr << s << std::endl;
throw std::runtime_error(s);
}
std::random_device random_device{};
std::mt19937 rng(random_device());
@ -74,14 +104,28 @@ Game::Game() {
for (int i = 0; i < words_len; i++)
all_words.push_back(words[i]);
std::shuffle(all_words.begin(), all_words.end(), rng);
word = all_words.back();
all_words.pop_back();
do {
word = all_words.back();
all_words.pop_back();
} while (strlen(word) > 15);
guess_corrector = std::make_unique<GuessCorrector>(word);
std::cout << "Word: " << word << std::endl;
}
void Game::handle_key(SDL_Keycode event) {
if (!isalpha(event))
return;
bool is_valid = guess_corrector->has_char(event);
if (is_valid) {
guess_corrector->add(event);
if (guess_corrector->is_filled_out())
m_game_state = State::WIN;
} else {
m_wrong_guesses += 1;
if (m_wrong_guesses >= MAX_GUESSES) {
m_game_state = State::GAME_OVER;
}
}
}
void Game::draw(SDL_Renderer *renderer) {
@ -91,18 +135,54 @@ void Game::draw(SDL_Renderer *renderer) {
draw_guesses(renderer);
}
void Game::draw_hang_man(SDL_Renderer *renderer) {
}
void Game::draw_guesses(SDL_Renderer *renderer) {
size_t len = strlen(word);
int total_width = (len - 1) * STEP_SIZE + CHAR_SIZE;
int start_x = (SCREEN_SIZE.x - total_width) / 2;
int char_y = (SCREEN_SIZE.y / 2) - CHAR_SIZE / 2;
int char_y = (SCREEN_SIZE.y / 4) * 3 - CHAR_SIZE / 2;
for (int i = 0; i < len; i++) {
int here = start_x + i * STEP_SIZE;
const SDL_Rect rect = {here, char_y + UNDERSCORE_DY, CHAR_SIZE, 5};
SDL_SetRenderDrawColor(renderer, 255, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(renderer, &rect);
std::optional<char> current_char = guess_corrector->guessed().lock()[i];
if (current_char) {
const char text_to_write[] = {*current_char, '\0'};
SDL_Surface *surface = TTF_RenderText_Blended(font, text_to_write, TEXT_COLOR);
if (surface == nullptr) {
std::cerr << "Failed to create surface: " << TTF_GetError() << std::endl;
continue;
}
SDL_Texture *txt = SDL_CreateTextureFromSurface(renderer, surface);
if (txt == nullptr) {
std::cerr << "Failed to create texture: " << SDL_GetError() << std::endl;
SDL_FreeSurface(surface);
continue;
}
int text_width = surface->w;
int text_height = surface->h;
SDL_Rect text_rect = {here + (CHAR_SIZE - text_width) / 2, char_y - text_height, text_width, text_height};
SDL_RenderCopy(renderer, txt, nullptr, &text_rect);
SDL_DestroyTexture(txt);
SDL_FreeSurface(surface);
}
}
SDL_RenderPresent(renderer);
}
Game::~Game() {
for (SDL_Surface *surface: m_hills)
SDL_FreeSurface(surface);
}

View File

@ -1,11 +1,13 @@
#pragma once
#include <SDL_rect.h>
#include <SDL_events.h>
#include <vector>
#include <optional>
#include <SDL_render.h>
#include "SDL_rect.h"
#include "SDL_events.h"
#include "SDL_render.h"
#include "SDL_ttf.h"
#include "GuessCorrector.hpp"
#include "State.hpp"
const SDL_Point SCREEN_SIZE{800, 800};
@ -14,11 +16,16 @@ private:
std::vector<const char *> all_words;
std::unique_ptr<GuessCorrector> guess_corrector;
const char *word;
_TTF_Font *font;
int m_wrong_guesses;
State m_game_state;
std::vector<SDL_Surface *> m_hills;
public:
static void Run();
Game();
~Game();
void handle_key(SDL_Keycode event);
@ -26,5 +33,7 @@ public:
private:
void draw_guesses(SDL_Renderer *renderer);
void draw_hang_man(SDL_Renderer *renderer);
};

13
src/data/hills.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "hills.hpp"
const HillData hills[] = {
{kulle_0_png, kulle_0_png_length},
{kulle_1_png, kulle_1_png_length},
{kulle_2_png, kulle_2_png_length},
{kulle_3_png, kulle_3_png_length},
{kulle_4_png, kulle_4_png_length},
{kulle_5_png, kulle_5_png_length},
{kulle_6_png, kulle_6_png_length},
{kulle_7_png, kulle_7_png_length},
};
extern const size_t hills_length = 8;

37
src/data/hills.hpp Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <cstddef>
extern const unsigned char kulle_0_png[];
extern const size_t kulle_0_png_length;
extern const unsigned char kulle_1_png[];
extern const size_t kulle_1_png_length;
extern const unsigned char kulle_2_png[];
extern const size_t kulle_2_png_length;
extern const unsigned char kulle_3_png[];
extern const size_t kulle_3_png_length;
extern const unsigned char kulle_4_png[];
extern const size_t kulle_4_png_length;
extern const unsigned char kulle_5_png[];
extern const size_t kulle_5_png_length;
extern const unsigned char kulle_6_png[];
extern const size_t kulle_6_png_length;
extern const unsigned char kulle_7_png[];
extern const size_t kulle_7_png_length;
struct HillData {
unsigned const char *data;
const size_t length;
};
extern const HillData hills[];
extern const size_t hills_length;

4024
src/data/kulle_0_png.cpp Normal file

File diff suppressed because it is too large Load Diff

3976
src/data/kulle_1_png.cpp Normal file

File diff suppressed because it is too large Load Diff

4123
src/data/kulle_2_png.cpp Normal file

File diff suppressed because it is too large Load Diff

4165
src/data/kulle_3_png.cpp Normal file

File diff suppressed because it is too large Load Diff

4192
src/data/kulle_4_png.cpp Normal file

File diff suppressed because it is too large Load Diff

4219
src/data/kulle_5_png.cpp Normal file

File diff suppressed because it is too large Load Diff

4260
src/data/kulle_6_png.cpp Normal file

File diff suppressed because it is too large Load Diff

4298
src/data/kulle_7_png.cpp Normal file

File diff suppressed because it is too large Load Diff

31
src/default_font.cpp Normal file
View File

@ -0,0 +1,31 @@
#include "default_font.hpp"
#ifdef __linux__
#include <filesystem>
constexpr const char *getLinuxFilePath() {
const char *fonts[] = {"/usr/share/fonts/truetype/DejaVuSans-Bold.ttf", // openSUSE
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", // Debian
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf", // Arch
"/usr/share/fonts/dejavu-sans-fonts/DejaVuSans-Bold.ttf", // Fedora
};
for (const char *font: fonts)
if (std::filesystem::exists(font))
return font;
return nullptr;
}
#endif
const char *getDefaultFontPath() {
#if defined(_WIN32) || defined(_WIN64)
return R"(C:\Windows\Fonts\Arial.ttf)";
#elif defined(__linux__)
return getLinuxFilePath();
#elif defined(__APPLE__) || defined(__MACH__)
return "/System/Library/Fonts/Supplemental/Arial.ttf";
#else
return nullptr;
#endif
}

3
src/default_font.hpp Normal file
View File

@ -0,0 +1,3 @@
#pragma once
const char *getDefaultFontPath();

View File

@ -1,6 +1,9 @@
#include <iostream>
#define SDL_MAIN_HANDLED
#include <SDL.h>
#include "Game.hpp"
int main() {
int SDL_main(int argc, char *argv[]) {
Game::Run();
return 0;
}

78
src/utils.cpp Normal file
View File

@ -0,0 +1,78 @@
#include <stdexcept>
#include "utils.hpp"
#include "SDL_image.h"
#include "data/hills.hpp"
std::vector<SDL_Surface *> get_hills() {
std::vector<SDL_Surface *> surfaces;
for (int i = 0; i < hills_length; i++) {
HillData png = hills[i];
SDL_RWops *rw = SDL_RWFromMem(const_cast<unsigned char *>(png.data), png.length);
if (!rw)
throw std::runtime_error("Failed to create RWops from memory");
SDL_Surface *surface = IMG_Load_RW(rw, 1);
if (!surface) {
SDL_RWclose(rw);
throw std::runtime_error("Failed to load image from memory: " + std::string(IMG_GetError()));
}
surfaces.push_back(surface);
}
return surfaces;
}
SDL_Surface *resize_surface(SDL_Surface *t_surface, int t_width, int t_height) {
if (!t_surface)
throw std::runtime_error("Original surface is null.");
if (t_surface->w == t_width && t_surface->h == t_height)
return t_surface;
SDL_Surface *resizedSurface = SDL_CreateRGBSurface(
0,
t_width,
t_height,
t_surface->format->BitsPerPixel,
t_surface->format->Rmask,
t_surface->format->Gmask,
t_surface->format->Bmask,
t_surface->format->Amask
);
if (!resizedSurface)
throw std::runtime_error("Failed to create resized surface: " + std::string(SDL_GetError()));
SDL_Rect src_rect, dest_rect;
for (int y = 0; y < t_height; ++y) {
for (int x = 0; x < t_width; ++x) {
src_rect.x = x * t_surface->w / t_width;
src_rect.y = y * t_surface->h / t_height;
src_rect.w = 1;
src_rect.h = 1;
dest_rect.x = x;
dest_rect.y = y;
dest_rect.w = 1;
dest_rect.h = 1;
SDL_BlitSurface(t_surface, &src_rect, resizedSurface, &dest_rect);
}
}
return resizedSurface;
}
std::vector<SDL_Surface *> get_resized(const std::vector<SDL_Surface *> &t_originals, int t_width, int t_height) {
std::vector<SDL_Surface *> ret;
ret.reserve(t_originals.size());
for (SDL_Surface *original: t_originals) {
SDL_Surface *resized = resize_surface(original, t_width, t_height);
SDL_FreeSurface(original);
ret.push_back(resized);
}
return ret;
}

View File

@ -1,7 +1,7 @@
#pragma once
#include <cstddef>
#include <vector>
#include "SDL_surface.h"
template<typename T>
constexpr size_t array_len(T *array[]) {
@ -10,3 +10,9 @@ constexpr size_t array_len(T *array[]) {
i++;
return i;
}
std::vector<SDL_Surface *> get_resized(const std::vector<SDL_Surface *>& t_original, int t_width, int t_height);
std::vector<SDL_Surface *> get_hills();
SDL_Surface *resize_surface(SDL_Surface *t_surface, int t_width, int t_height);

2
vcpkg

Submodule vcpkg updated: 5c7d3a872d...1de2026f28

View File

@ -2,7 +2,7 @@
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
"name": "hang-man",
"version": "1.0.0",
"builtin-baseline": "5c7d3a872dd861817fc812647176d5076085a7eb",
"builtin-baseline": "1de2026f28ead93ff1773e6e680387643e914ea1",
"dependencies": [
{
"name": "sdl2",
@ -12,6 +12,6 @@
"version>=": "2.0.20"
},
"sdl2-ttf",
"curl"
"sdl2-image"
]
}