diff --git a/VisualC/SDL.sln b/VisualC/SDL.sln index 29b57a0ee1..039ff338e3 100644 --- a/VisualC/SDL.sln +++ b/VisualC/SDL.sln @@ -113,6 +113,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "03-load-wav", "examples\aud EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "02-woodeneye-008", "examples\game\02-woodeneye-008\02-woodeneye-008.vcxproj", "{A3F601E0-B54C-4DD8-8A97-FDEF7624EE60}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "03-infinite-monkeys", "examples\game\03-infinite-monkeys\03-infinite-monkeys.vcxproj", "{75AEE75A-C016-4497-960B-D767B822237D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -505,6 +507,14 @@ Global {A3F601E0-B54C-4DD8-8A97-FDEF7624EE60}.Release|Win32.Build.0 = Release|Win32 {A3F601E0-B54C-4DD8-8A97-FDEF7624EE60}.Release|x64.ActiveCfg = Release|x64 {A3F601E0-B54C-4DD8-8A97-FDEF7624EE60}.Release|x64.Build.0 = Release|x64 + {75AEE75A-C016-4497-960B-D767B822237D}.Debug|Win32.ActiveCfg = Debug|Win32 + {75AEE75A-C016-4497-960B-D767B822237D}.Debug|Win32.Build.0 = Debug|Win32 + {75AEE75A-C016-4497-960B-D767B822237D}.Debug|x64.ActiveCfg = Debug|x64 + {75AEE75A-C016-4497-960B-D767B822237D}.Debug|x64.Build.0 = Debug|x64 + {75AEE75A-C016-4497-960B-D767B822237D}.Release|Win32.ActiveCfg = Release|Win32 + {75AEE75A-C016-4497-960B-D767B822237D}.Release|Win32.Build.0 = Release|Win32 + {75AEE75A-C016-4497-960B-D767B822237D}.Release|x64.ActiveCfg = Release|x64 + {75AEE75A-C016-4497-960B-D767B822237D}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -561,6 +571,7 @@ Global {CC0714AA-8A81-4E29-BEC5-2E4FBC50E7FE} = {F91DDAF0-B74F-4516-A1A9-42ED8DFCBF6A} {608C6C67-7766-471F-BBFF-8B00086039AF} = {1B61A1B7-92DE-4C37-9151-D2928D6449AB} {A3F601E0-B54C-4DD8-8A97-FDEF7624EE60} = {D1BF59F6-22DC-493B-BDEB-451A50DA793D} + {75AEE75A-C016-4497-960B-D767B822237D} = {D1BF59F6-22DC-493B-BDEB-451A50DA793D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C320C9F2-1A8F-41D7-B02B-6338F872BCAD} diff --git a/VisualC/examples/game/03-infinite-monkeys/03-infinite-monkeys.vcxproj b/VisualC/examples/game/03-infinite-monkeys/03-infinite-monkeys.vcxproj new file mode 100644 index 0000000000..9535db263b --- /dev/null +++ b/VisualC/examples/game/03-infinite-monkeys/03-infinite-monkeys.vcxproj @@ -0,0 +1,13 @@ + + + + {75AEE75A-C016-4497-960B-D767B822237D} + + + + + + + + + \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index dd971c10e1..37ab6bfc40 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -142,6 +142,7 @@ add_sdl_example_executable(camera-read-and-draw SOURCES camera/01-read-and-draw/ add_sdl_example_executable(pen-drawing-lines SOURCES pen/01-drawing-lines/drawing-lines.c) add_sdl_example_executable(game-snake SOURCES game/01-snake/snake.c) add_sdl_example_executable(game-woodeneye-008 SOURCES game/02-woodeneye-008/woodeneye-008.c) +add_sdl_example_executable(game-infinite-monkeys SOURCES game/03-infinite-monkeys/infinite-monkeys.c) # When you add an example, remember to add the Visual Studio project as well: # - Add a new example in examples/ diff --git a/examples/game/03-infinite-monkeys/README.txt b/examples/game/03-infinite-monkeys/README.txt new file mode 100644 index 0000000000..3a80b690c0 --- /dev/null +++ b/examples/game/03-infinite-monkeys/README.txt @@ -0,0 +1,7 @@ + +How many monkeys does it take to write the complete works of Shakespeare? + + Now you can find out! + +Cheer on your favorite monkey as they bash keyboards on their way through classic literature. + diff --git a/examples/game/03-infinite-monkeys/infinite-monkeys.c b/examples/game/03-infinite-monkeys/infinite-monkeys.c new file mode 100644 index 0000000000..99baf27145 --- /dev/null +++ b/examples/game/03-infinite-monkeys/infinite-monkeys.c @@ -0,0 +1,377 @@ +/* + * This code is public domain. Feel free to use it for any purpose! + */ + +#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ +#include +#include + +/* We will use this renderer to draw into this window every frame. */ +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static char *text; +static const char *end; +static const char *progress; +static SDL_Time start_time; +static SDL_Time end_time; +typedef struct { + Uint32 *text; + int length; +} Line; +int row = 0; +int rows = 0; +int cols = 0; +static Line **lines; +static Line monkey_chars; +static int monkeys = 100; + +/* The highest and lowest scancodes a monkey can hit */ +#define MIN_MONKEY_SCANCODE SDL_SCANCODE_A +#define MAX_MONKEY_SCANCODE SDL_SCANCODE_SLASH + +static const char *default_text = +"Jabberwocky, by Lewis Carroll\n" +"\n" +"'Twas brillig, and the slithy toves\n" +" Did gyre and gimble in the wabe:\n" +"All mimsy were the borogoves,\n" +" And the mome raths outgrabe.\n" +"\n" +"\"Beware the Jabberwock, my son!\n" +" The jaws that bite, the claws that catch!\n" +"Beware the Jubjub bird, and shun\n" +" The frumious Bandersnatch!\"\n" +"\n" +"He took his vorpal sword in hand;\n" +" Long time the manxome foe he sought-\n" +"So rested he by the Tumtum tree\n" +" And stood awhile in thought.\n" +"\n" +"And, as in uffish thought he stood,\n" +" The Jabberwock, with eyes of flame,\n" +"Came whiffling through the tulgey wood,\n" +" And burbled as it came!\n" +"\n" +"One, two! One, two! And through and through\n" +" The vorpal blade went snicker-snack!\n" +"He left it dead, and with its head\n" +" He went galumphing back.\n" +"\n" +"\"And hast thou slain the Jabberwock?\n" +" Come to my arms, my beamish boy!\n" +"O frabjous day! Callooh! Callay!\"\n" +" He chortled in his joy.\n" +"\n" +"'Twas brillig, and the slithy toves\n" +" Did gyre and gimble in the wabe:\n" +"All mimsy were the borogoves,\n" +" And the mome raths outgrabe.\n"; + + +static void FreeLines(void) +{ + int i; + + if (rows > 0 && cols > 0) { + for (i = 0; i < rows; ++i) { + SDL_free(lines[i]->text); + SDL_free(lines[i]); + } + SDL_free(lines); + lines = NULL; + } + SDL_free(monkey_chars.text); + monkey_chars.text = NULL; +} + +static void OnWindowSizeChanged(void) +{ + int w, h; + + if (!SDL_GetCurrentRenderOutputSize(renderer, &w, &h)) { + return; + } + + FreeLines(); + + row = 0; + rows = (h / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) - 4; + cols = (w / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE); + if (rows > 0 && cols > 0) { + int i; + + lines = (Line **)SDL_malloc(rows * sizeof(Line *)); + if (lines) { + for (i = 0; i < rows; ++i) { + lines[i] = (Line *)SDL_malloc(sizeof(Line)); + if (!lines[i]) { + FreeLines(); + break; + } + lines[i]->text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32)); + if (!lines[i]->text) { + FreeLines(); + break; + } + lines[i]->length = 0; + } + } + + monkey_chars.text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32)); + if (monkey_chars.text) { + for (i = 0; i < cols; ++i) { + monkey_chars.text[i] = ' '; + } + monkey_chars.length = cols; + } + } +} + +/* This function runs once at startup. */ +SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) +{ + int arg = 1; + + SDL_SetAppMetadata("Infinite Monkeys", "1.0", "com.example.infinite-monkeys"); + + if (!SDL_Init(SDL_INIT_VIDEO)) { + SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + + if (!SDL_CreateWindowAndRenderer("examples/game/03-infinite-monkeys", 640, 480, 0, &window, &renderer)) { + SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); + return SDL_APP_FAILURE; + } + SDL_SetRenderVSync(renderer, 1); + + if (argv[arg] && SDL_strcmp(argv[arg], "--monkeys") == 0) { + ++arg; + if (argv[arg]) { + monkeys = SDL_atoi(argv[arg]); + ++arg; + } else { + SDL_Log("Usage: %s [--monkeys N] [file.txt]", argv[0]); + return SDL_APP_FAILURE; + } + } + + if (argv[arg]) { + const char *file = argv[arg]; + size_t size; + text = (char *)SDL_LoadFile(file, &size); + if (!text) { + SDL_Log("Couldn't open %s: %s", file, SDL_GetError()); + return SDL_APP_FAILURE; + } + end = text + size; + } else { + text = SDL_strdup(default_text); + end = text + SDL_strlen(text); + } + progress = text; + + SDL_GetCurrentTime(&start_time); + + OnWindowSizeChanged(); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ +SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) +{ + switch (event->type) { + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + OnWindowSizeChanged(); + break; + case SDL_EVENT_QUIT: + return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ + } + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +static void DisplayLine(float x, float y, Line *line) +{ + /* Allocate maximum space potentially needed for this line */ + char *utf8 = (char *)SDL_malloc(line->length * 4 + 1); + if (utf8) { + char *spot = utf8; + int i; + + for (i = 0; i < line->length; ++i) { + spot = SDL_UCS4ToUTF8(line->text[i], spot); + } + *spot = '\0'; + + SDL_RenderDebugText(renderer, x, y, utf8); + SDL_free(utf8); + } +} + +static bool CanMonkeyType(Uint32 ch) +{ + SDL_Keymod modstate; + SDL_Scancode scancode = SDL_GetScancodeFromKey(ch, &modstate); + if (scancode < MIN_MONKEY_SCANCODE || scancode > MAX_MONKEY_SCANCODE) { + return false; + } + /* Monkeys can hit the shift key, but nothing else */ + if ((modstate & ~SDL_KMOD_SHIFT) != 0) { + return false; + } + return true; +} + +static void AdvanceRow(void) +{ + Line *line; + + ++row; + line = lines[row % rows]; + line->length = 0; +} + +static void AddMonkeyChar(int monkey, Uint32 ch) +{ + if (monkey >= 0 && monkey_chars.text) { + monkey_chars.text[(monkey % cols)] = ch; + } + + if (lines) { + if (ch == '\n') { + AdvanceRow(); + } else { + Line *line = lines[row % rows]; + line->text[line->length++] = ch; + if (line->length == cols) { + AdvanceRow(); + } + } + } + + SDL_StepUTF8(&progress, NULL); +} + +static Uint32 GetNextChar(void) +{ + Uint32 ch = 0; + while (progress < end) { + const char *spot = progress; + ch = SDL_StepUTF8(&spot, NULL); + if (CanMonkeyType(ch)) { + break; + } else { + /* This is a freebie, monkeys can't type this */ + AddMonkeyChar(-1, ch); + } + } + return ch; +} + +static Uint32 MonkeyPlay(void) +{ + int count = (MAX_MONKEY_SCANCODE - MIN_MONKEY_SCANCODE + 1); + SDL_Scancode scancode = (SDL_Scancode)(MIN_MONKEY_SCANCODE + SDL_rand(count)); + SDL_Keymod modstate = (SDL_rand(2) ? SDL_KMOD_SHIFT : 0); + + return SDL_GetKeyFromScancode(scancode, modstate, false); +} + +/* This function runs once per frame, and is the heart of the program. */ +SDL_AppResult SDL_AppIterate(void *appstate) +{ + int i, monkey; + Uint32 next_char = 0, ch; + float x, y; + char *caption = NULL; + SDL_Time now, elapsed; + int hours, minutes, seconds; + SDL_FRect rect; + + for (monkey = 0; monkey < monkeys; ++monkey) { + if (next_char == 0) { + next_char = GetNextChar(); + if (!next_char) { + /* All done! */ + break; + } + } + + ch = MonkeyPlay(); + if (ch == next_char) { + AddMonkeyChar(monkey, ch); + next_char = 0; + } + } + + /* Clear the screen */ + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + + /* Show the text already decoded */ + SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); + x = 0.0f; + y = 0.0f; + if (lines) { + int row_offset = row - rows + 1; + if (row_offset < 0) { + row_offset = 0; + } + for (i = 0; i < rows; ++i) { + Line *line = lines[(row_offset + i) % rows]; + DisplayLine(x, y, line); + y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; + } + + /* Show the caption */ + y = (float)((rows + 1) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE); + if (progress == end) { + if (!end_time) { + SDL_GetCurrentTime(&end_time); + } + now = end_time; + } else { + SDL_GetCurrentTime(&now); + } + elapsed = (now - start_time); + elapsed /= SDL_NS_PER_SECOND; + seconds = (int)(elapsed % 60); + elapsed /= 60; + minutes = (int)(elapsed % 60); + elapsed /= 60; + hours = (int)elapsed; + SDL_asprintf(&caption, "Monkeys: %d - %dH:%dM:%dS", monkeys, hours, minutes, seconds); + if (caption) { + SDL_RenderDebugText(renderer, x, y, caption); + SDL_free(caption); + } + y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; + + /* Show the characters currently typed */ + DisplayLine(x, y, &monkey_chars); + y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; + } + + /* Show the current progress */ + SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE); + rect.x = x; + rect.y = y; + rect.w = ((float)(progress - text) / (end - text)) * (cols * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE); + rect.h = (float)SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; + SDL_RenderFillRect(renderer, &rect); + + SDL_RenderPresent(renderer); + + return SDL_APP_CONTINUE; /* carry on with the program! */ +} + +/* This function runs once at shutdown. */ +void SDL_AppQuit(void *appstate, SDL_AppResult result) +{ + /* SDL will clean up the window/renderer for us. */ + + FreeLines(); + SDL_free(text); +} +