You aren't going to like what you see

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

You aren't going to like what you see

Post by albinopapa » August 24th, 2020, 5:03 pm

Code: Select all

#include <array>
#include <iostream>
#include <functional>
#include <random>
#include <string>
#include <string_view>
#include <tuple>

constexpr auto dictionary = std::array{
	std::string_view{"fun"},
	std::string_view{"fork"},
	std::string_view{"forge"},
	std::string_view{"forgot"},
	std::string_view{"feather"},
	std::string_view{"fearsome"},
	std::string_view{"forgotten"}
};

auto is_valid_guess = std::function{ []( char guess ) {
	return std::tolower( guess ) >= 'a' && std::tolower( guess ) <= 'z';
} };

auto get_user_guess = std::function{ []() {
	char letter = {};
	do {
		std::cout << "Guess a letter: ";
		std::cin >> letter;
	} while( !is_valid_guess( letter ) );

	return letter;
} };

auto pick_word = std::function{ []( std::mt19937& rng ) {
	auto dist = std::uniform_int<std::size_t>{0, dictionary.size() - 1};
	return dictionary[ dist( rng ) ];
} };

auto unhide_guess = std::function{ [](std::string hidden, std::string_view word, std::string_view::const_iterator guess_it ) {
	auto dist = std::distance( word.begin(), guess_it );
	hidden[ dist ] = *guess_it;
	return hidden;
} };

auto penalize = std::function{ [](std::string score, std::string::const_iterator score_it ) {
	switch( std::distance( score.cbegin(), score_it ) ) {
		case 0: score[ 0 ] = 'H'; break;
		case 1: score[ 1 ] = 'O'; break;
		case 2: score[ 2 ] = 'R'; break;
		case 3: score[ 3 ] = 'S'; break;
		case 4: score[ 4 ] = 'E'; break;
	}

	return score;
} };

using GameData = std::tuple<
	// rng
	std::mt19937,
	// word to guess
	std::string_view,
	// hidden word
	std::string,
	// score
	std::string,
	// game_over
	bool,
	// winner
	bool
>;

using GameMethods = std::tuple<
	// update function
	std::function<void( GameData& )>,
	// display function
	std::function<void( GameData const& )>,
	// is_finished function
	std::function<bool( GameData const& )>
>;

using Game = std::tuple<std::function<void( GameMethods&, GameData& )>, GameMethods, GameData>;

// Indices to make things easier
// method indices
constexpr auto update_index		= std::size_t{ 0 };
constexpr auto display_index	= std::size_t{ 1 };
constexpr auto finished_index	= std::size_t{ 2 };

// data indices
constexpr auto rng_index		= std::size_t{ 0 };
constexpr auto word_index		= std::size_t{ 1 };
constexpr auto hidden_index		= std::size_t{ 2 };
constexpr auto score_index		= std::size_t{ 3 };
constexpr auto gover_index		= std::size_t{ 4 };
constexpr auto winner_index		= std::size_t{ 5 };

// game indices
constexpr auto construct_index	= std::size_t{ 0 };
constexpr auto methods_index	= std::size_t{ 1 };
constexpr auto data_index		= std::size_t{ 2 };

int main( int argc, char* argv[] ) {
	auto game = Game{ std::function{
			[]( GameMethods& methods, GameData& data ) {
		// init update method
		std::get<update_index>( methods ) = []( GameData& data ) {
			auto letter			= get_user_guess();
			const auto word		= std::get<word_index>( data );

			if( auto guess_it = std::find( word.begin(), word.end(), letter ); guess_it != word.end() ) {
				auto& hidden	= std::get<hidden_index>( data );
				hidden			= unhide_guess( std::move( hidden ), word, guess_it );

				if( auto it = std::find( hidden.begin(), hidden.end(), '_' ); it == hidden.end() ) {
					std::get<winner_index>( data )	= true;
					std::get<gover_index>( data )	= true;
				}
			}
			else {
				auto& score		= std::get<score_index>( data );
				if( auto score_it = std::find( score.begin(), score.end(), '_' ); score_it != score.end() ) {
					score		= penalize( std::move( score ), score_it );
				}
				else {
					std::get<winner_index>( data )	= false;
					std::get<gover_index>( data )	= true;
				}
			}
		};

		// init display method
		std::get<display_index>( methods )	= []( GameData const& data ) {
			if( std::get<gover_index>( data ) ) {
				if( std::get<winner_index>( data ) ) {
					std::cout << "Congratulations!\n";
				}
				else {
					std::cout << "Sorry, the word was: " << std::get<word_index>( data ) << '\n';
					std::cout << "Better luck next time.\n";
				}
			}
			else {
				std::cout << std::get<hidden_index>( data ) << '\n';
			}
		};

		// init is_finished method
		std::get<finished_index>( methods ) = []( GameData const& data ) {
			return std::get<gover_index>( data );
		};

		// get word
		std::get<word_index>( data )		= pick_word( std::get<rng_index>( data ) );

		// init hidden string
		std::get<hidden_index>( data )		= std::string( std::get<word_index>( data ).size(), '_' );

		// init score string
		std::get<score_index>( data )		= std::string( 5, '_' );

		// init game_over
		std::get<gover_index>( data )		= false;

		// init winner
		std::get<winner_index>( data )		= false;
	} 
	}, GameMethods{}, GameData{} };

	std::get<construct_index>( game )( std::get<methods_index>( game ), std::get<data_index>( game ) );

	auto& game_methods = std::get<methods_index>( game );
	auto& game_data = std::get<data_index>( game );

	while( !std::get<finished_index>( game_methods )( game_data ) ) {
		std::get<update_index>( game_methods )( game_data );
		std::get<display_index>( game_methods )( game_data );
	}

	return 0;
}
Here's the gist if you want to have a bigger view of it
What am I doing with my life?
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: You aren't going to like what you see

Post by albinopapa » August 24th, 2020, 5:07 pm

I make no claim on how "functional" it is. I played through it once and it ran. VS intellisense shit the bed about half way through, so things got difficult.

So, what are you looking at?
A hangman type game using the console. No graphics, no third party library, no OS SDK, no user defined classes or global functions ( I'm not counting lambdas as global functions ). Nothing but standard template library objects.

Don't ask why, don't stare too long...it's weird.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

User avatar
chili
Site Admin
Posts: 3939
Joined: December 31st, 2011, 4:53 pm
Location: Japan
Contact:

Re: You aren't going to like what you see

Post by chili » August 29th, 2020, 2:53 pm

Was this beast spawned from a cursed discussion in Discord? :D
Chili

albinopapa
Posts: 4266
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: You aren't going to like what you see

Post by albinopapa » August 30th, 2020, 10:14 am

You know, I don't remember what spawned it exactly. I do recall Discord being involved though.

The only part I remember about the thought process was wondering how much can be done without using third party libraries, user defined classes or global functions. Basically everything save the main function are objects in that code. I realized that not even using the WinSDK would mean I couldn't have any graphics which really sucked. If I'd allowed myself to use the WinSDK, I probably would have redone the chili framework...which would have been quite the undertaking as the last time I tried porting the chili framework to C it was more complicated than I thought.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply