Click here to Skip to main content
15,881,882 members
Articles / Desktop Programming / WTL

The Psychology of a WTL Project: Lowercase Cause (a typing program)

Rate me:
Please Sign up or sign in to vote.
4.99/5 (32 votes)
20 Apr 200524 min read 63.5K   2.7K   25  
A psychological journey into a project crafted from start to finish.
#include "StdAfx.h"
#include ".\logic.h"
#include "resource.h"

// ---------------------------------------------------------[game_session]---
//

void game_session::init(HWND hwnd)
{
	_hwnd = hwnd;
}

void game_session::reset()
{
	_lives = 0;

	_total_characters = 0;
	_correct_characters = 0;
	_closed_windows = 0;

	_start_time = NULL_TIME;
	_end_time = NULL_TIME;
	_timer = 0;
}

void game_session::increment_valid_characters()
{
	_total_characters++;
	_correct_characters++;
}

void game_session::increment_invalid_characters()
{
	_total_characters++;
}

void game_session::increment_closed_windows()
{
	_closed_windows++;
}

void game_session::set_timer(int timer)
{
	_timer = timer;
	_lives = TIMED_START_LIVES;
}

void game_session::start()
{
	_start_time = CTime::GetCurrentTime();
	if(_timer)
		SetTimer(_hwnd, GAME_SESSION_TIMER, 500, 0);
}

void game_session::end()
{
	_end_time = CTime::GetCurrentTime();
	if(_timer)
		KillTimer(_hwnd, GAME_SESSION_TIMER);
}

CTimeSpan game_session::get_running_time() const
{
	if(_end_time == NULL_TIME)
		return CTime::GetCurrentTime() - _start_time;
	else
		return _end_time - _start_time;
}

CTimeSpan game_session::get_time_left() const
{
	LONGLONG seconds_left = _timer - get_running_time().GetTotalSeconds();
	if(seconds_left < 0)
		seconds_left = 0;
	return CTimeSpan(seconds_left);
}

LRESULT game_session::OnTimer(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	CTimeSpan time_running = get_running_time();
	if(time_running.GetTotalSeconds() >= _timer)
		end();

	return 0;
}

bool game_session::has_started() const
{
	return _start_time != NULL_TIME;
}

bool game_session::is_over() const
{
	return _end_time != NULL_TIME || _lives <= 0;
}

int game_session::_get_wpm(int characters) const
{
	CTimeSpan time_running = get_running_time();

	double characters_per_second = static_cast<double>(characters) /
		static_cast<double>(time_running.GetTotalSeconds());
	double words_per_minute = (characters_per_second * 60.0) / 5.0;

	return static_cast<int>(words_per_minute);
}

int game_session::get_wpm() const
{
	return _get_wpm(get_total_characters());	
}

int game_session::get_awpm() const
{
	return _get_wpm(get_correct_characters());
}

int game_session::get_accuracy() const
{
	return static_cast<int>(static_cast<double>(get_correct_characters()) /
		static_cast<double>(get_total_characters()) * 100.0);
}

int game_session::get_points() const
{
	int bad_characters = _total_characters - _correct_characters;
	int points;
	points = _correct_characters * POINTS_FOR_GOOD_CHARACTERS;
	points += bad_characters * POINTS_FOR_BAD_CHARACTERS;
	points += _closed_windows * POINTS_FOR_CLOSED_WINDOWS;
	return points;
}

int game_session::get_starting_windows() const
{
	return is_timed() ? TIMED_START_WINDOWS : _difficulty + 1;
}

int game_session::get_starting_lives() const
{
	return STARTING_LIVES;
}

int game_session::get_target_wpm(int difficulty /* = -1 */) const
{
	if(difficulty == -1)
		difficulty = _difficulty;
	return (difficulty + 1) * WPM_INCREASE_FACTOR;
}

int game_session::get_window_life(const std::string& s) const
{
	// we calculate how many windows we have and the average length
	// using this we can calculate the estimated time a person typing
	// at the current wpm would need to close them
	int life = get_seconds_to_type_length(s.length());
	life += get_starting_windows() * 
				get_seconds_to_type_length(get_average_phrase_length());
	return life;
}

int game_session::get_seconds_to_type_length(int length) const
{
	// we calculate the target WPM and use that as an indicator of how
	// long it would take to type out the text at that speed
	int wpm = get_target_wpm();
	int cpm = 5 * wpm;
	double cps = static_cast<double>(cpm) / 60.0;
	int life = static_cast<int>(static_cast<double>(length) / cps) + 1;
	return life;
}

game_type game_session::get_game_type() const
{
	return is_timed() ? TIMED : SURVIVAL;
}

//
// --------------------------------------------------------[/game_session]---
// ----------------------------------------------------------[phrase_list]---
//

string_list_t get_phrase_files()
{
	string_list_t string_list;

	HANDLE hf;
	WIN32_FIND_DATA fd;
	hf = FindFirstFile(_T("*"), &fd);
	if(hf == INVALID_HANDLE_VALUE)
		return string_list;

	do
	{
		std::string current_filename = fd.cFileName;

		// make sure there is a .ini at the end
		if(current_filename.find(_T(".ini"), current_filename.size() - 4) !=
			std::string::npos)
		{
			string_list.push_back(
				current_filename.substr(0, current_filename.size() - 4));
		}

	}

	while(FindNextFile(hf, &fd));

	return string_list;
}

void phrase_list::load_phrase_list(const std::string& path)
{
	std::ifstream f(path.c_str());
	if(f)
	{
		_file_name = path;

		// read the contents of a file into a str
		std::string contents;
		std::ostringstream ss;
		ss << f.rdbuf();
		ss.str().swap(contents);

		// empty our list, start fresh
		_list.clear();

		// parse each string based on newlines and add it to _list
		size_t start_index, end_index;
		start_index = end_index = 0;
		while((end_index = contents.find(_T('\n'), start_index)) 
			!= std::string::npos)
		{
			size_t length = end_index - start_index;
			std::string next_line = contents.substr(start_index, length);
 			_process_line(next_line);
			start_index = end_index + 1;
		}

		if(contents.size())
		{			
			std::string next_line = contents.substr(start_index);
			_process_line(next_line);
		}
	}
}

void phrase_list::_process_line(const std::string& line)
{
	// replace "\n" with literal newlines and trim any whitespace at the end
	std::string processed = hf::replace(line, _T("\\n"), _T("\r\n"));
	processed = processed.substr(0, 
		processed.find_last_not_of("\r\n\t ") + 1);

	// if theres anything left, add it
	if(processed.size())
		_list.push_back(processed);
}

std::string phrase_list::get_random_phrase() const
{
	int random_index = rand() % _list.size();
	return _list[random_index];
}

int phrase_list::get_average_length() const
{
	long average = 0;
	phrase_list_t::const_iterator i;
	for(i = _list.begin(); i != _list.end(); i++)
		average += i->size();
	
	return average / _list.size();
}

//
// ---------------------------------------------------------[/phrase_list]---
// -----------------------------------------------------------[high_score]---
//

high_score::high_score(const game_session& gs, 
     				   const std::string& phrase_list)
{
	std::string phrase_name = phrase_list;

	// chop off the ending .ini if necessary
	if(phrase_name.find(_T(".ini"), phrase_name.size() - 4) 
		!= std::string::npos)
		phrase_name = phrase_name.substr(0, phrase_name.size() - 4);

	_phrase_list = phrase_name;
	_difficulty = gs.get_difficulty();
	_points = gs.get_points();

	// time has two purposes in high_score. it can be the time limit in a 
	// timed game, or how long you survived in survival
	if(gs.get_game_type() == SURVIVAL)
		_time = static_cast<int>(gs.get_running_time().GetTotalSeconds());
	else
		_time = gs.get_timer();

	_type = gs.get_game_type();
	_awpm = gs.get_awpm();
	_wpm = gs.get_wpm();
}

std::string high_score::get_string() const
{
	std::ostringstream oss;
	oss << _T(" ") << get_name() << _T(" - ") << get_points();
	return oss.str();
}

std::string high_score::get_type_string() const
{
	std::ostringstream oss;
	oss << _T(" ") << hf::load_string(STR_GAME_TYPE) << _T(": ") 
		<< (get_type() == SURVIVAL ? 
				hf::load_string(STR_GAME_TYPE_SURVIVAL) :
				hf::load_string(STR_GAME_TYPE_TIMED))
		<< _T(", ");

	if(get_type() == SURVIVAL)
	{	 
		oss << hf::load_string(STR_DIFFICULTY) 
			<< _T(": ") << hf::int_to_str(get_difficulty()) << _T(", ");
	}
	else
	{
		oss << hf::load_string(STR_TIME_LIMIT)
			<< _T(": ") << hf::get_seconds_string(get_time()) 
			<< _T(", ");
	}

	oss << hf::load_string(STR_PHRASE_LIST_FILENAME) << _T(": ")
		<< get_phrase_list();

	return oss.str();
}

//
// ----------------------------------------------------------[/high_score]---
// ---------------------------------------------------[high_score_manager]---
//

high_score_manager::high_score_list_t high_score_manager::_list;

high_score high_score_manager::get_current_high_score(const high_score& hs)
{
	if(_get_current_high_score(hs) != _list.end())
		return *_get_current_high_score(hs);
	return high_score();
}

high_score_manager::high_score_list_t::iterator 
high_score_manager::_get_current_high_score(const high_score& hs)
{
	high_score_list_t::iterator i;
	for(i = _list.begin(); i != _list.end(); i++)
	{
		high_score& current_hs = *i;
		if(current_hs.get_type() == hs.get_type() &&
			current_hs.get_difficulty() == hs.get_difficulty() &&
			current_hs.get_phrase_list() == hs.get_phrase_list())
		{
			if(current_hs.get_type() == TIMED)
			{
				if(current_hs.get_time() == hs.get_time())
					break;
			}
			else
				break;
		}
	}

	return i;
}

bool high_score_manager::is_high_score(const high_score& hs)
{
	high_score current_hs = get_current_high_score(hs);
	if(current_hs.is_null() && current_hs.get_points())
		return true;
	return current_hs.get_points() < hs.get_points();
}

void high_score_manager::add(high_score hs)
{
	ATLASSERT(is_high_score(hs));

	high_score_list_t::iterator i = _get_current_high_score(hs);
	if(i != _list.end())
		_list.erase(i);

	_list.push_back(hs);
}

void high_score_manager::save()
{
	std::ofstream f(HIGH_SCORE_FILENAME.c_str());

	high_score_list_t::iterator i;
	for(i = _list.begin(); i != _list.end(); i++)
	{
		if(i != _list.begin())
			f << std::endl;

		high_score& hs = *i;
		f << hs.get_name() << DELIMITER 
		  << hs.get_phrase_list() << DELIMITER
		  << hs.get_type() << DELIMITER
		  << hs.get_difficulty() << DELIMITER
		  << hs.get_points() << DELIMITER
		  << hs.get_time() << DELIMITER
		  << hs.get_wpm() << DELIMITER
		  << hs.get_awpm();
	}

	f.close();
}

void high_score_manager::load()
{
	std::ifstream f(HIGH_SCORE_FILENAME.c_str());
	std::ostringstream ss;
	ss << f.rdbuf();
	std::string buffer = ss.str();

	size_t index = 0;
	while(index != std::string::npos)
	{
		high_score hs;
		for(int n = 0; n < 8 && index != std::string::npos; n++)
		{
			long l = 0;
			std::string s;

			if(n < 2)
			{
				index = _get_next_token_as_string(s, buffer, index);
			}
			else
			{
				index = _get_next_token_as_long(l, buffer, index, 
					n == 7 ? _T('\n') : DELIMITER);
			}

			switch(n)
			{
			case 0:
				hs.set_name(s);
				break;
			case 1:
				hs.set_phrase_list(s);
				break;
			case 2:
				hs.set_type(static_cast<game_type>(l));
				break;
			case 3:
				hs.set_difficulty(l);
				break;
			case 4:
				hs.set_points(l);
				break;
			case 5:
				hs.set_time(l);
				break;
			case 6:
				hs.set_wpm(l);
				break;
			case 7:
				hs.set_awpm(l);
				break;
			}
		}

		if(!hs.is_null())
			high_score_manager::add(hs);
	}
}

size_t high_score_manager::_get_next_token_as_string(
	std::string& s, const std::string& buffer, size_t index, TCHAR delimiter)
{
	size_t next_index = buffer.find(delimiter, index + 1);
	if(next_index == std::string::npos)
		s = buffer.substr(index);
	else
		s = buffer.substr(index, next_index - index);
	return next_index == std::string::npos ? next_index : next_index + 1;
}

size_t high_score_manager::_get_next_token_as_long(
	long& l, const std::string& buffer, size_t index, TCHAR delimiter)
{
	std::string str;
	size_t next_index = 
		_get_next_token_as_string(str, buffer, index, delimiter);
	l = _ttol(str.c_str());
	return next_index;
}

//
// --------------------------------------------------[/high_score_manager]---

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions