Adds a couple of helper functions for battles to run turns, prettify outputs.

This commit is contained in:
Deukhoofd 2021-08-24 21:19:44 +02:00
parent c821e6f53b
commit 1e36f8f5e3
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
7 changed files with 1031 additions and 14 deletions

911
extern/termcolor.hpp vendored Normal file
View File

@ -0,0 +1,911 @@
//!
//! termcolor
//! ~~~~~~~~~
//!
//! termcolor is a header-only c++ library for printing colored messages
//! to the terminal. Written just for fun with a help of the Force.
//!
//! :copyright: (c) 2013 by Ihor Kalnytskyi
//! :license: BSD, see LICENSE for details
//!
#ifndef TERMCOLOR_HPP_
#define TERMCOLOR_HPP_
#include <iostream>
#include <cstdio>
// Detect target's platform and set some macros in order to wrap platform
// specific code this library depends on.
#if defined(_WIN32) || defined(_WIN64)
# define TERMCOLOR_TARGET_WINDOWS
#elif defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
# define TERMCOLOR_TARGET_POSIX
#endif
// If implementation has not been explicitly set, try to choose one based on
// target platform.
#if !defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES) && !defined(TERMCOLOR_USE_WINDOWS_API) && !defined(TERMCOLOR_USE_NOOP)
# if defined(TERMCOLOR_TARGET_POSIX)
# define TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES
# define TERMCOLOR_AUTODETECTED_IMPLEMENTATION
# elif defined(TERMCOLOR_TARGET_WINDOWS)
# define TERMCOLOR_USE_WINDOWS_API
# define TERMCOLOR_AUTODETECTED_IMPLEMENTATION
# endif
#endif
// These headers provide isatty()/fileno() functions, which are used for
// testing whether a standard stream refers to the terminal.
#if defined(TERMCOLOR_TARGET_POSIX)
# include <unistd.h>
#elif defined(TERMCOLOR_TARGET_WINDOWS)
# include <io.h>
# include <windows.h>
#endif
namespace termcolor
{
// Forward declaration of the `_internal` namespace.
// All comments are below.
namespace _internal
{
inline int colorize_index();
inline FILE* get_standard_stream(const std::ostream& stream);
inline bool is_colorized(std::ostream& stream);
inline bool is_atty(const std::ostream& stream);
#if defined(TERMCOLOR_TARGET_WINDOWS)
inline void win_change_attributes(std::ostream& stream, int foreground, int background=-1);
#endif
}
inline
std::ostream& colorize(std::ostream& stream)
{
stream.iword(_internal::colorize_index()) = 1L;
return stream;
}
inline
std::ostream& nocolorize(std::ostream& stream)
{
stream.iword(_internal::colorize_index()) = 0L;
return stream;
}
inline
std::ostream& reset(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[00m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1, -1);
#endif
}
return stream;
}
inline
std::ostream& bold(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[1m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
inline
std::ostream& dark(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[2m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
inline
std::ostream& italic(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[3m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
inline
std::ostream& underline(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[4m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1, COMMON_LVB_UNDERSCORE);
#endif
}
return stream;
}
inline
std::ostream& blink(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[5m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
inline
std::ostream& reverse(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[7m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
inline
std::ostream& concealed(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[8m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
inline
std::ostream& crossed(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[9m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
template <uint8_t code> inline
std::ostream& color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
char command[12];
std::snprintf(command, sizeof(command), "\033[38;5;%dm", code);
stream << command;
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
template <uint8_t code> inline
std::ostream& on_color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
char command[12];
std::snprintf(command, sizeof(command), "\033[48;5;%dm", code);
stream << command;
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
template <uint8_t r, uint8_t g, uint8_t b> inline
std::ostream& color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
char command[20];
std::snprintf(command, sizeof(command), "\033[38;2;%d;%d;%dm", r, g, b);
stream << command;
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
template <uint8_t r, uint8_t g, uint8_t b> inline
std::ostream& on_color(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
char command[20];
std::snprintf(command, sizeof(command), "\033[48;2;%d;%d;%dm", r, g, b);
stream << command;
#elif defined(TERMCOLOR_USE_WINDOWS_API)
#endif
}
return stream;
}
inline
std::ostream& grey(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[30m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
0 // grey (black)
);
#endif
}
return stream;
}
inline
std::ostream& red(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[31m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& green(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[32m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_GREEN
);
#endif
}
return stream;
}
inline
std::ostream& yellow(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[33m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_GREEN | FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& blue(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[34m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE
);
#endif
}
return stream;
}
inline
std::ostream& magenta(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[35m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& cyan(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[36m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_GREEN
);
#endif
}
return stream;
}
inline
std::ostream& white(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[37m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& bright_grey(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[90m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
0 | FOREGROUND_INTENSITY // grey (black)
);
#endif
}
return stream;
}
inline
std::ostream& bright_red(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[91m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_RED | FOREGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& bright_green(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[92m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_GREEN | FOREGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& bright_yellow(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[93m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& bright_blue(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[94m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& bright_magenta(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[95m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& bright_cyan(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[96m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& bright_white(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[97m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream,
FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& on_grey(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[40m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
0 // grey (black)
);
#endif
}
return stream;
}
inline
std::ostream& on_red(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[41m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_green(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[42m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN
);
#endif
}
return stream;
}
inline
std::ostream& on_yellow(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[43m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_blue(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[44m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_BLUE
);
#endif
}
return stream;
}
inline
std::ostream& on_magenta(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[45m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_BLUE | BACKGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_cyan(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[46m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_BLUE
);
#endif
}
return stream;
}
inline
std::ostream& on_white(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[47m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_grey(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[100m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
0 | BACKGROUND_INTENSITY // grey (black)
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_red(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[101m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_RED | BACKGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_green(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[102m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_yellow(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[103m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_blue(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[104m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_BLUE | BACKGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_magenta(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[105m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_cyan(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[106m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY
);
#endif
}
return stream;
}
inline
std::ostream& on_bright_white(std::ostream& stream)
{
if (_internal::is_colorized(stream))
{
#if defined(TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES)
stream << "\033[107m";
#elif defined(TERMCOLOR_USE_WINDOWS_API)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED | BACKGROUND_INTENSITY
);
#endif
}
return stream;
}
//! Since C++ hasn't a way to hide something in the header from
//! the outer access, I have to introduce this namespace which
//! is used for internal purpose and should't be access from
//! the user code.
namespace _internal
{
// An index to be used to access a private storage of I/O streams. See
// colorize / nocolorize I/O manipulators for details. Due to the fact
// that static variables ain't shared between translation units, inline
// function with local static variable is used to do the trick and share
// the variable value between translation units.
inline int colorize_index()
{
static int colorize_index = std::ios_base::xalloc();
return colorize_index;
}
//! Since C++ hasn't a true way to extract stream handler
//! from the a given `std::ostream` object, I have to write
//! this kind of hack.
inline
FILE* get_standard_stream(const std::ostream& stream)
{
if (&stream == &std::cout)
return stdout;
else if ((&stream == &std::cerr) || (&stream == &std::clog))
return stderr;
return nullptr;
}
// Say whether a given stream should be colorized or not. It's always
// true for ATTY streams and may be true for streams marked with
// colorize flag.
inline
bool is_colorized(std::ostream& stream)
{
return is_atty(stream) || static_cast<bool>(stream.iword(colorize_index()));
}
//! Test whether a given `std::ostream` object refers to
//! a terminal.
inline
bool is_atty(const std::ostream& stream)
{
FILE* std_stream = get_standard_stream(stream);
// Unfortunately, fileno() ends with segmentation fault
// if invalid file descriptor is passed. So we need to
// handle this case gracefully and assume it's not a tty
// if standard stream is not detected, and 0 is returned.
if (!std_stream)
return false;
#if defined(TERMCOLOR_TARGET_POSIX)
return ::isatty(fileno(std_stream));
#elif defined(TERMCOLOR_TARGET_WINDOWS)
return ::_isatty(_fileno(std_stream));
#else
return false;
#endif
}
#if defined(TERMCOLOR_TARGET_WINDOWS)
//! Change Windows Terminal colors attribute. If some
//! parameter is `-1` then attribute won't changed.
inline void win_change_attributes(std::ostream& stream, int foreground, int background)
{
// yeah, i know.. it's ugly, it's windows.
static WORD defaultAttributes = 0;
// Windows doesn't have ANSI escape sequences and so we use special
// API to change Terminal output color. That means we can't
// manipulate colors by means of "std::stringstream" and hence
// should do nothing in this case.
if (!_internal::is_atty(stream))
return;
// get terminal handle
HANDLE hTerminal = INVALID_HANDLE_VALUE;
if (&stream == &std::cout)
hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
else if (&stream == &std::cerr)
hTerminal = GetStdHandle(STD_ERROR_HANDLE);
// save default terminal attributes if it unsaved
if (!defaultAttributes)
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
defaultAttributes = info.wAttributes;
}
// restore all default settings
if (foreground == -1 && background == -1)
{
SetConsoleTextAttribute(hTerminal, defaultAttributes);
return;
}
// get current settings
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
if (foreground != -1)
{
info.wAttributes &= ~(info.wAttributes & 0x0F);
info.wAttributes |= static_cast<WORD>(foreground);
}
if (background != -1)
{
info.wAttributes &= ~(info.wAttributes & 0xF0);
info.wAttributes |= static_cast<WORD>(background);
}
SetConsoleTextAttribute(hTerminal, info.wAttributes);
}
#endif // TERMCOLOR_TARGET_WINDOWS
} // namespace _internal
} // namespace termcolor
#undef TERMCOLOR_TARGET_POSIX
#undef TERMCOLOR_TARGET_WINDOWS
#if defined(TERMCOLOR_AUTODETECTED_IMPLEMENTATION)
# undef TERMCOLOR_USE_ANSI_ESCAPE_SEQUENCES
# undef TERMCOLOR_USE_WINDOWS_API
#endif
#endif // TERMCOLOR_HPP_

@ -1 +1 @@
Subproject commit 9b27ad72f18d4924d0851a523b64fbff0c779620 Subproject commit 9dafa51e9d23bc2a41a10e50dcb4a1bd69a7e3d7

View File

@ -1,6 +1,8 @@
#ifndef POKEMONSCRIPTTESTER_BATTLEBUILDER_HPP #ifndef POKEMONSCRIPTTESTER_BATTLEBUILDER_HPP
#define POKEMONSCRIPTTESTER_BATTLEBUILDER_HPP #define POKEMONSCRIPTTESTER_BATTLEBUILDER_HPP
#include <CreatureLib/Battling/TurnChoices/AttackTurnChoice.hpp>
#include <CreatureLib/Battling/TurnChoices/PassTurnChoice.hpp>
#include <PkmnLib/Battling/Battle/Battle.hpp> #include <PkmnLib/Battling/Battle/Battle.hpp>
#include <PkmnLib/Battling/Pokemon/CreatePokemon.hpp> #include <PkmnLib/Battling/Pokemon/CreatePokemon.hpp>
#include <angelscript.h> #include <angelscript.h>
@ -13,11 +15,23 @@ class BattleBuilder {
TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData()); TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData());
auto lib = Globals::Library.GetValue(); auto lib = Globals::Library.GetValue();
auto mon1 = PkmnLib::Battling::CreatePokemon(lib, species1, level).Build(); auto mon1 = PkmnLib::Battling::CreatePokemon(lib, species1, level)
.WithEffortValues(0, 0, 0, 0, 0, 0)
.WithIndividualValues(31, 31, 31, 31, 31, 31)
.WithNature("hardy"_cnc)
.WithGender(CreatureLib::Library::Gender::Male)
.IsAllowedExperienceGain(false)
.Build();
auto p1 = new CreatureLib::Battling::CreatureParty(1); auto p1 = new CreatureLib::Battling::CreatureParty(1);
p1->SwapInto(0, mon1); p1->SwapInto(0, mon1);
auto mon2 = PkmnLib::Battling::CreatePokemon(lib, species2, level).Build(); auto mon2 = PkmnLib::Battling::CreatePokemon(lib, species2, level)
.WithEffortValues(0, 0, 0, 0, 0, 0)
.WithIndividualValues(31, 31, 31, 31, 31, 31)
.WithNature("hardy"_cnc)
.WithGender(CreatureLib::Library::Gender::Male)
.IsAllowedExperienceGain(false)
.Build();
auto p2 = new CreatureLib::Battling::CreatureParty(1); auto p2 = new CreatureLib::Battling::CreatureParty(1);
p1->SwapInto(0, mon2); p1->SwapInto(0, mon2);
@ -40,12 +54,56 @@ class BattleBuilder {
return battle; return battle;
} }
static bool UseMove(PkmnLib::Battling::Pokemon* user, const ArbUt::StringView& moveName, u8 sideTarget, u8 target) {
auto battle = user->GetBattle();
if (!battle.HasValue()) {
return false;
}
auto move = Globals::Library.GetValue()->GetMoveLibrary()->TryGet(moveName);
if (!move.has_value()) {
return false;
}
auto* ctx = asGetActiveContext();
TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData());
auto learnedMove =
new PkmnLib::Battling::LearnedMove(move.value(), CreatureLib::Battling::AttackLearnMethod::Unknown);
env->AddGarbage(learnedMove);
auto choice = new CreatureLib::Battling::AttackTurnChoice(
user, learnedMove, CreatureLib::Battling::CreatureIndex(sideTarget, target));
auto b = battle.GetValue()->TrySetChoice(choice);
if (!b) {
delete choice;
}
return b;
}
static bool PassTurn(PkmnLib::Battling::Pokemon* user) {
auto battle = user->GetBattle();
if (!battle.HasValue()) {
return false;
}
auto choice = new CreatureLib::Battling::PassTurnChoice(user);
auto b = battle.GetValue()->TrySetChoice(choice);
if (!b) {
delete choice;
}
return b;
}
public: public:
static void Register(AngelScriptResolver* scriptResolver) { static void Register(AngelScriptResolver* scriptResolver) {
auto engine = scriptResolver->GetBuilder().GetEngine(); auto engine = scriptResolver->GetBuilder().GetEngine();
Ensure(engine->RegisterGlobalFunction("Battle@ CreateSimpleBattle(uint seed, const constString&in species1, " Ensure(engine->RegisterGlobalFunction("Battle@ CreateSimpleBattle(uint seed, const constString&in species1, "
"const constString&in species2, uint8 level)", "const constString&in species2, uint8 level)",
asFUNCTION(CreateSimpleBattle), asCALL_CDECL) >= 0); asFUNCTION(CreateSimpleBattle), asCALL_CDECL) >= 0);
Ensure(engine->RegisterObjectMethod("Pokemon",
"bool UseMove(const constString&in move, uint8 side, uint8 index)",
asFUNCTION(UseMove), asCALL_CDECL_OBJFIRST) >= 0);
Ensure(engine->RegisterObjectMethod("Pokemon", "bool PassTurn()", asFUNCTION(PassTurn),
asCALL_CDECL_OBJFIRST) >= 0);
} }
}; };

View File

@ -12,10 +12,7 @@ private:
size_t Line; size_t Line;
}; };
static RequirementData GetRequirementData() { static RequirementData GetRequirementData(asIScriptContext* ctx) {
auto* ctx = asGetActiveContext();
TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData());
env->AssertionCount += 1;
auto filename = ctx->GetFunction()->GetScriptSectionName(); auto filename = ctx->GetFunction()->GetScriptSectionName();
auto line = ctx->GetLineNumber(); auto line = ctx->GetLineNumber();
return RequirementData{ return RequirementData{
@ -25,8 +22,12 @@ private:
} }
static bool Require(bool value) { static bool Require(bool value) {
auto* ctx = asGetActiveContext();
TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData());
env->TotalRequirements += 1;
if (!value) { if (!value) {
auto data = GetRequirementData(); env->FailedRequirements += 1;
auto data = GetRequirementData(ctx);
throw RequirementFailed(data.FileName, data.Line); throw RequirementFailed(data.FileName, data.Line);
} }
return true; return true;
@ -41,8 +42,12 @@ private:
} }
static bool RequireEqualsI32(i32 expected, i32 actual) { static bool RequireEqualsI32(i32 expected, i32 actual) {
auto* ctx = asGetActiveContext();
TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData());
env->TotalRequirements += 1;
if (expected != actual) { if (expected != actual) {
auto data = GetRequirementData(); env->FailedRequirements += 1;
auto data = GetRequirementData(ctx);
GetEqualityMessage(expected, actual); GetEqualityMessage(expected, actual);
throw RequirementFailed(data.FileName, data.Line, eqMsg); throw RequirementFailed(data.FileName, data.Line, eqMsg);
} }
@ -50,8 +55,11 @@ private:
} }
static bool RequireEqualsString(const std::string& expected, const std::string& actual) { static bool RequireEqualsString(const std::string& expected, const std::string& actual) {
auto* ctx = asGetActiveContext();
TestEnvironment* env = static_cast<TestEnvironment*>(ctx->GetUserData());
if (expected != actual) { if (expected != actual) {
auto data = GetRequirementData(); env->FailedRequirements += 1;
auto data = GetRequirementData(ctx);
std::string eqMsg; std::string eqMsg;
{ {
std::ostringstream ss; std::ostringstream ss;

View File

@ -30,6 +30,9 @@ public:
inline TestResult GetResult() const noexcept { return _result; } inline TestResult GetResult() const noexcept { return _result; }
inline const std::string& GetErrorMessage() const noexcept { return _errorMessage; } inline const std::string& GetErrorMessage() const noexcept { return _errorMessage; }
inline size_t GetTotalRequirements() const noexcept { return _env->TotalRequirements; }
inline size_t GetFailedRequirements() const noexcept { return _env->FailedRequirements; }
private: private:
std::string _name; std::string _name;
asIScriptFunction* _function; asIScriptFunction* _function;

View File

@ -2,7 +2,8 @@
#define POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP #define POKEMONSCRIPTTESTER_TESTENVIRONMENT_HPP
struct TestEnvironment { struct TestEnvironment {
size_t AssertionCount = 0; size_t TotalRequirements = 0;
size_t FailedRequirements = 0;
template <typename T> void AddGarbage(T* data) { _garbage.Append(GarbageObject(data)); } template <typename T> void AddGarbage(T* data) { _garbage.Append(GarbageObject(data)); }

View File

@ -2,6 +2,7 @@
#define POKEMONSCRIPTTESTER_TESTRUNNER_HPP #define POKEMONSCRIPTTESTER_TESTRUNNER_HPP
#include <PkmnLib/ScriptResolving/AngelScript/AngelScriptMetadata.hpp> #include <PkmnLib/ScriptResolving/AngelScript/AngelScriptMetadata.hpp>
#include "../../extern/termcolor.hpp"
#include "Test.hpp" #include "Test.hpp"
class TestRunner { class TestRunner {
@ -34,27 +35,62 @@ public:
size_t notRunTests = 0; size_t notRunTests = 0;
size_t successfulTests = 0; size_t successfulTests = 0;
size_t failedTests = 0; size_t failedTests = 0;
size_t totalRequirements = 0;
size_t failedRequirements = 0;
for (auto& test : _tests) { for (auto& test : _tests) {
auto result = test.second->GetResult(); auto result = test.second->GetResult();
switch (result) { switch (result) {
case TestResult::NotRan: notRunTests += 1; break; case TestResult::NotRan: notRunTests += 1; break;
case TestResult::Success: successfulTests += 1; break; case TestResult::Success: successfulTests += 1; break;
case TestResult::Failed: { case TestResult::Failed: {
std::cout << "Test '" << test.first << "' failed with message: " << std::endl; std::cout << termcolor::red << "Test '" << termcolor::yellow << test.first << termcolor::red
<< "' failed with message: " << termcolor::reset << std::endl;
std::cout << test.second->GetErrorMessage() << std::endl << std::endl; std::cout << test.second->GetErrorMessage() << std::endl << std::endl;
failedTests += 1; failedTests += 1;
break; break;
} }
} }
totalRequirements += test.second->GetTotalRequirements();
failedRequirements += test.second->GetFailedRequirements();
} }
std::cout << "Ran " << successfulTests + failedTests << " tests. Passed tests: " << successfulTests auto totalTests = successfulTests + failedTests;
<< ". Failed tests: " << failedTests << "." << std::endl; auto digitWidth = std::log10(std::max(totalTests, totalRequirements)) + 2;
std::cout << termcolor::blue << "=====================================================================";
for (int i = 0; i < digitWidth; ++i) {
std::cout << "===";
}
std::cout << termcolor::reset << std::endl;
ReportResults("Test cases", digitWidth, totalTests, successfulTests, failedTests, notRunTests);
ReportResults("Requirements", digitWidth, totalRequirements, totalRequirements - failedRequirements,
failedRequirements, {});
if (failedTests > 0) { if (failedTests > 0) {
return 1; return 1;
} }
return 0; return 0;
} }
static void ReportResults(const std::string& label, double digitWidth, size_t total, size_t passed, size_t failed,
std::optional<size_t> skipped) {
std::cout << std::left // Left align
<< std::setw(16) << label + ":" << std::setw(digitWidth) << total << std::setw(3) << "|";
if (passed > 0) {
std::cout << termcolor::green;
}
std::cout << std::left << std::setw(14) << "Passed:" << std::setw(digitWidth) << passed << termcolor::reset
<< std::setw(3) << "|";
if (failed > 0) {
std::cout << termcolor::red;
}
std::cout << std::setw(14) << "Failed:" << std::setw(digitWidth) << failed << termcolor::reset;
if (skipped.has_value()) {
std::cout << std::setw(3) << "|" << std::setw(14) << "Skipped:" << std::setw(digitWidth) << skipped.value()
<< std::endl;
}
}
}; };
#endif // POKEMONSCRIPTTESTER_TESTRUNNER_HPP #endif // POKEMONSCRIPTTESTER_TESTRUNNER_HPP