I need an optimal code for parallel mergesort using intel thread building block - tbb

I need an optimal code for parallel mergesort using intel thread building block in c++

First off, let me say that in my experience tbb::parallel_sort() is quite efficient and is a bit faster than the code I'm about to post (at least for input on the order of thousands of elements for which I've tested).
Having said that, I think the following code is exactly what you are looking for. Variables should be self explanatory and documentation in the code should explain the rest -
This will be needed for parallelization :
#include<tbb/parallel_invoke.h>
If you choose to use Concurrency::parallel_invoke(), which may work faster, then include this :
#include<ppl.h>
I recommend these settings -
#define MIN_ELEMENTS_FOR_RECURSION (50)
#define MIN_ELEMENTS_FOR_PARALLEL_PROCESSING (100)
Following is the main function to call. Parameters are iterators to start and end of a random access class (e.g., vector, deque, etc.) and a compare function -
template <typename T_it, typename T_it_dereferenced>
void parallelMergeSort( T_it first, T_it last, bool (*firstLessThanSecond)(const T_it_dereferenced& a, const T_it_dereferenced& b) )
{
// create copy of container for extra space
std::vector<T_it_dereferenced> copy(first, last);
parallelMergeSortRecursive( first, last, copy.begin(), copy.end(), firstLessThanSecond );
}
This is called recursively from parallelMergeSort() in order to sort each half -
template <typename T_it, typename T_it_dereferenced>
void parallelMergeSortRecursive( T_it source_first, T_it source_last, T_it copy_first, T_it copy_last,
bool (*firstLessThanSecond)(const T_it_dereferenced& a, const T_it_dereferenced& b), int recursion_depth = 0 )
{
// divide the array in two, and sort the two halves
long num_elements = source_last - source_first;
if ( num_elements > MIN_ELEMENTS_FOR_RECURSION )
{
T_it source_middle = source_first + num_elements / 2;
T_it copy_middle = copy_first + num_elements / 2;
if ( num_elements > MIN_ELEMENTS_FOR_PARALLEL_PROCESSING )
{
// Concurrency::parallel_invoke() may work faster
tbb::parallel_invoke(
[=] { parallelMergeSortRecursive( source_first, source_middle, copy_first, copy_middle, firstLessThanSecond, recursion_depth + 1 ); },
[=] { parallelMergeSortRecursive( source_middle, source_last, copy_middle, copy_last, firstLessThanSecond, recursion_depth + 1 ); }
);
}
else // sort serially rather than in parallel
{
parallelMergeSortRecursive( source_first, source_middle, copy_first, copy_middle, firstLessThanSecond, recursion_depth + 1 );
parallelMergeSortRecursive( source_middle, source_last, copy_middle, copy_last, firstLessThanSecond, recursion_depth + 1 );
}
// merge the two sorted halves
// we switch source <--> target with each level of recursion.
// at even recursion depths (including zero which is the root level) we assume the source is sorted and merge into the target
if ( recursion_depth % 2 == 0 )
{
merge( source_first, copy_first, copy_middle, copy_last, firstLessThanSecond );
}
else
{
merge( copy_first, source_first, source_middle, source_last, firstLessThanSecond );
}
}
else // very few elements remain to be sorted, stop the recursion and sort in place
{
if ( recursion_depth % 2 == 0 )
{
std::stable_sort(source_first, source_last, firstLessThanSecond);
}
else
{
std::stable_sort(copy_first, copy_last, firstLessThanSecond);
}
}
}
This is called from the recursive function in order to merge two halves -
template <typename T_it, typename T_it_dereferenced>
void merge( T_it target_first, T_it source_first, T_it source_middle, T_it source_last,
bool (*firstLessThanSecond)(const T_it_dereferenced& a, const T_it_dereferenced& b) )
{
// source is assumed to contain two sorted sequences (from first to middle and from middle to last)
T_it source_it1 = source_first;
T_it source_it2 = source_middle;
T_it target_it = target_first;
for ( /* intentional */ ; source_it1 < source_middle && source_it2 < source_last ; ++target_it )
{
//if ( source_container[i] < source_container[j] )
if ( firstLessThanSecond(*source_it1, *source_it2) )
{
*target_it = *source_it1;
++source_it1;
}
else
{
*target_it = *source_it2;
++source_it2;
}
}
// insert remaining elements in non-completely-traversed-half into original container
// only one of these two whiles will execute since one of the conditions caused the previous while to stop
for ( /* intentional */ ; source_it1 < source_middle ; ++target_it )
{
*target_it = *source_it1;
++source_it1;
}
for ( /* intentional */ ; source_it2 < source_last ; ++target_it )
{
*target_it = *source_it2;
++source_it2;
}
}

TBB already includes a sort method (parallel quicksort), which is -however- implemented quite poorly (runtime is at least linear independent of the number of processors).
My proposal would be you port parallel merge sort from an existing implementation.
For example gnu parallel mode sort (included in any recent gcc with source files) wich uses OpenMP.
Just replace all #pragma omp by some tbb parallel code.

Related

xSemaphoreGive() gets stuck when used by different threads

I am working on STM32F756ZG with FreeRTOS. I have one network thread that is made using osThreadDef() which is a part of the CMSIS-RTOS API. I also have other tasks running that are created using xTaskCreate() which is a part of the freeRTOS API.
I have a Semaphore that is shared by a tempSensor and EEPROM. In the network thread, the I try to get the values for IP address from the EEPROM using I2C protocol. It successfully takes the Semaphore using xSemaphoreTake() but when its time to give up the Semaphore using xSemaphoreGive() it gets lost and when I hit pause it stays in I2C_WaitOnFlagUntilTimeout().As a result it never loads the webpage.
The other tasks run fine and the temp sensor that also uses I2c and sempahore returns the values correctly.
So my question is if this problem is created because of using semaphore between two threads each generated by different OS API. I am really struggling with this and any help would be really appreciated. Thanks a lot!
I am adding a little code snippet here.
/* Init networking thread */
osThreadDef(Start, StartNetworkThread, osPriorityNormal, 0, configMINIMAL_STACK_SIZE * 2);
osThreadCreate (osThread(Start), NULL);
start_threads(3);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*) adc1vals, 1);
HAL_ADC_Start_DMA(&hadc2, (uint32_t*) adc2vals, 1);
xTaskCreate (vADC1, "vADC1", configMINIMAL_STACK_SIZE, NULL, uxPriority + 3, ( TaskHandle_t * ) NULL );
xTaskCreate (vADC2, "vADC2", configMINIMAL_STACK_SIZE, NULL, uxPriority + 3, ( TaskHandle_t * ) NULL );
xTaskCreate (vPIDloop, "vPIDloop", configMINIMAL_STACK_SIZE + 100, NULL, uxPriority + 2, ( TaskHandle_t * ) NULL );
xTaskCreate (vIO, "vIO", configMINIMAL_STACK_SIZE + 512, NULL, uxPriority + 1, ( TaskHandle_t * ) NULL ); //Run IO at least important priority
xTaskCreate (vControl, "vControl", configMINIMAL_STACK_SIZE + 512, NULL, uxPriority + 1, ( TaskHandle_t * ) NULL ); //Run control at least important priority
This is how my Semaphore is initialized:
// Initialize the semaphore that controls eeprom access
xI2C3Semaphore = xSemaphoreCreateMutex();
if( xI2C3Semaphore ==NULL)
{
while(1);
}
Following is the code for when I am reading the EEPROM:
int result = 0;
NvVarsEeprom_t eepromVar;
memset( &eepromVar, 0xff, sizeof(eepromVar) );
if( xI2C3Semaphore != NULL )
{
// Wait forever for semaphore
if( xSemaphoreTake( xI2C3Semaphore, (TickType_t)10 ) == pdTRUE )
{
// count = uxSemaphoreGetCount(xI2C3Semaphore);
// Read from EEPROM
if( nvdata_read((char *)&eepromVar, sizeof(eepromVar), addr) != HAL_OK )
{
//vTaskDelay(5);
if( nvdata_read((char *)&eepromVar, sizeof(eepromVar), addr) != HAL_OK )
{
return ERR_EEPROM;
}
}
//count = uxSemaphoreGetCount(xI2C3Semaphore);
// Give up the semaphore
if(xSemaphoreGive( xI2C3Semaphore ) != pdTRUE)
{
while(1);
}
// count = uxSemaphoreGetCount(xI2C3Semaphore);
}
}
if( result == 0 )
{
eepromVar.valid = NVP_VALID;
}
if( eepromVar.valid == NVP_VALID )
{
strncpy( buf, eepromVar.str, EepromVarSize-1 );
buf[EepromVarSize-1] = '\0';
}
else
{
return ERR_EEPROM;
}
return result;
The next code snippet is when I am reading from the temp sensor:
int tempC = 0;
if( xI2C3Semaphore != NULL )
{
// Wait forever for semaphore
if( xSemaphoreTake( xI2C3Semaphore, (TickType_t)10 ) == pdTRUE )
{
// Read from I2C3
tempC = heatSink_read();
// Give up the semaphore
if(xSemaphoreGive( xI2C3Semaphore ) != pdTRUE)
{
while(1);
}
}
}
return tempC;
When I jump from the bootloader to the application and try to read values from the EEPROM, I can take the Semaphore but I does not give it back using xSemaphoreGive().
First of all, make sure semaphore got initialized properly, like this:
if ((SemId_I2C1_Rx = xSemaphoreCreateBinary()) == NULL) { goto InitFailed; };
Secondly, make sure you are using proper function for giving the semaphore.
If it is given from an Interrupt, you have to use
xSemaphoreGiveFromISR(SemId_I2C1_Rx, NULL);

Why is the following a memory leak? [duplicate]

I've got code that looks like this:
for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
bool isActive = (*i)->update();
//if (!isActive)
// items.remove(*i);
//else
other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);
I'd like remove inactive items immediately after update them, inorder to avoid walking the list again. But if I add the commented-out lines, I get an error when I get to i++: "List iterator not incrementable". I tried some alternates which didn't increment in the for statement, but I couldn't get anything to work.
What's the best way to remove items as you are walking a std::list?
You have to increment the iterator first (with i++) and then remove the previous element (e.g., by using the returned value from i++). You can change the code to a while loop like so:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // alternatively, i = items.erase(i);
}
else
{
other_code_involving(*i);
++i;
}
}
You want to do:
i= items.erase(i);
That will correctly update the iterator to point to the location after the iterator you removed.
You need to do the combination of Kristo's answer and MSN's:
// Note: Using the pre-increment operator is preferred for iterators because
// there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
// along the way you can safely save end once; otherwise get it at the
// top of each loop.
std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end = items.end();
while (iter != end)
{
item * pItem = *iter;
if (pItem->update() == true)
{
other_code_involving(pItem);
++iter;
}
else
{
// BTW, who is deleting pItem, a.k.a. (*iter)?
iter = items.erase(iter);
}
}
Of course, the most efficient and SuperCool® STL savy thing would be something like this:
// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
if (m_needsUpdates == true)
{
m_needsUpdates = other_code_involving(this);
}
return (m_needsUpdates);
}
// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));
I have sumup it, here is the three method with example:
1. using while loop
list<int> lst{4, 1, 2, 3, 5};
auto it = lst.begin();
while (it != lst.end()){
if((*it % 2) == 1){
it = lst.erase(it);// erase and go to next
} else{
++it; // go to next
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
2. using remove_if member funtion in list:
list<int> lst{4, 1, 2, 3, 5};
lst.remove_if([](int a){return a % 2 == 1;});
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
3. using std::remove_if funtion combining with erase member function:
list<int> lst{4, 1, 2, 3, 5};
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
return a % 2 == 1;
}), lst.end());
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
4. using for loop , should note update the iterator:
list<int> lst{4, 1, 2, 3, 5};
for(auto it = lst.begin(); it != lst.end();++it){
if ((*it % 2) == 1){
it = lst.erase(it); erase and go to next(erase will return the next iterator)
--it; // as it will be add again in for, so we go back one step
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
Use std::remove_if algorithm.
Edit:
Work with collections should be like:
prepare collection.
process collection.
Life will be easier if you won't mix this steps.
std::remove_if. or list::remove_if ( if you know that you work with list and not with the TCollection )
std::for_each
The alternative for loop version to Kristo's answer.
You lose some efficiency, you go backwards and then forward again when deleting but in exchange for the extra iterator increment you can have the iterator declared in the loop scope and the code looking a bit cleaner. What to choose depends on priorities of the moment.
The answer was totally out of time, I know...
typedef std::list<item*>::iterator item_iterator;
for(item_iterator i = items.begin(); i != items.end(); ++i)
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i--);
}
else
{
other_code_involving(*i);
}
}
Here's an example using a for loop that iterates the list and increments or revalidates the iterator in the event of an item being removed during traversal of the list.
for(auto i = items.begin(); i != items.end();)
{
if(bool isActive = (*i)->update())
{
other_code_involving(*i);
++i;
}
else
{
i = items.erase(i);
}
}
items.remove_if(CheckItemNotActive);
Removal invalidates only the iterators that point to the elements that are removed.
So in this case after removing *i , i is invalidated and you cannot do increment on it.
What you can do is first save the iterator of element that is to be removed , then increment the iterator and then remove the saved one.
If you think of the std::list like a queue, then you can dequeue and enqueue all the items that you want to keep, but only dequeue (and not enqueue) the item you want to remove. Here's an example where I want to remove 5 from a list containing the numbers 1-10...
std::list<int> myList;
int size = myList.size(); // The size needs to be saved to iterate through the whole thing
for (int i = 0; i < size; ++i)
{
int val = myList.back()
myList.pop_back() // dequeue
if (val != 5)
{
myList.push_front(val) // enqueue if not 5
}
}
myList will now only have numbers 1-4 and 6-10.
Iterating backwards avoids the effect of erasing an element on the remaining elements to be traversed:
typedef list<item*> list_t;
for ( list_t::iterator it = items.end() ; it != items.begin() ; ) {
--it;
bool remove = <determine whether to remove>
if ( remove ) {
items.erase( it );
}
}
PS: see this, e.g., regarding backward iteration.
PS2: I did not thoroughly tested if it handles well erasing elements at the ends.
You can write
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive) {
i = items.erase(i);
} else {
other_code_involving(*i);
i++;
}
}
You can write equivalent code with std::list::remove_if, which is less verbose and more explicit
items.remove_if([] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
});
The std::vector::erase std::remove_if idiom should be used when items is a vector instead of a list to keep compexity at O(n) - or in case you write generic code and items might be a container with no effective way to erase single items (like a vector)
items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
}));
do while loop, it's flexable and fast and easy to read and write.
auto textRegion = m_pdfTextRegions.begin();
while(textRegion != m_pdfTextRegions.end())
{
if ((*textRegion)->glyphs.empty())
{
m_pdfTextRegions.erase(textRegion);
textRegion = m_pdfTextRegions.begin();
}
else
textRegion++;
}
I'd like to share my method. This method also allows the insertion of the element to the back of the list during iteration
#include <iostream>
#include <list>
int main(int argc, char **argv) {
std::list<int> d;
for (int i = 0; i < 12; ++i) {
d.push_back(i);
}
auto it = d.begin();
int nelem = d.size(); // number of current elements
for (int ielem = 0; ielem < nelem; ++ielem) {
auto &i = *it;
if (i % 2 == 0) {
it = d.erase(it);
} else {
if (i % 3 == 0) {
d.push_back(3*i);
}
++it;
}
}
for (auto i : d) {
std::cout << i << ", ";
}
std::cout << std::endl;
// result should be: 1, 3, 5, 7, 9, 11, 9, 27,
return 0;
}
I think you have a bug there, I code this way:
for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
itAudioChannel != audioChannels.end(); )
{
CAudioChannel *audioChannel = *itAudioChannel;
std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
itAudioChannel++;
if (audioChannel->destroyMe)
{
audioChannels.erase(itCurrentAudioChannel);
delete audioChannel;
continue;
}
audioChannel->Mix(outBuffer, numSamples);
}

How to multiply strings in Haxe

I'm trying to multiply some string a by some integer b such that a * b = a + a + a... (b times). I've tried doing it the same way I would in python:
class Test {
static function main() {
var a = "Text";
var b = 4;
trace(a * b); //Assumed Output: TextTextTextText
}
}
But this raises:
Build failure Test.hx:6: characters 14-15 : String should be Int
There doesn't seem to be any information in the Haxe Programming Cookbook or the API Documentation about multiplying strings, so I'm wondering if I've mistyped something or if I should use:
class Test {
static function main() {
var a = "Text";
var b = 4;
var c = "";
for (i in 0...b) {
c = c + a;
}
trace(c); // Outputs "TextTextTextText"
}
}
Not very short, but array comprehension might help in some situations :
class Test {
static function main() {
var a = "Text";
var b = 4;
trace( [for (i in 0...b) a].join("") );
//Output: TextTextTextText
}
}
See on try.haxe.org.
The numeric multiplication operator * requires numeric types, like integer. You have a string. If you want to multiply a string, you have to do it manually by appending a target string within the loop.
The + operator is not the numeric plus in your example, but a way to combine strings.
You can achieve what you want by operator overloading:
abstract MyAbstract(String) {
public inline function new(s:String) {
this = s;
}
#:op(A * B)
public function repeat(rhs:Int):MyAbstract {
var s:StringBuf = new StringBuf();
for (i in 0...rhs)
s.add(this);
return new MyAbstract(s.toString());
}
}
class Main {
static public function main() {
var a = new MyAbstract("foo");
trace(a * 3); // foofoofoo
}
}
To build on tokiop's answer, you could also define a times function, and then use it as a static extension.
using Test.Extensions;
class Test {
static function main() {
trace ("Text".times(4));
}
}
class Extensions {
public static function times (str:String, n:Int) {
return [for (i in 0...n) str].join("");
}
}
try.haxe.org demo here
To build on bsinky answer, you can also define a times function as static extension, but avoid the array:
using Test.Extensions;
class Test {
static function main() {
trace ("Text".times(4));
}
}
class Extensions {
public static function times (str:String, n:Int) {
var v = new StringBuf();
for (i in 0...n) v.add(str);
return v.toString();
}
}
Demo: https://try.haxe.org/#e5937
StringBuf may be optimized for different targets. For example, on JavaScript target it is compiled as if you were just using strings https://api.haxe.org/StringBuf.html
The fastest method (at least on the JavaScript target from https://try.haxe.org/#195A8) seems to be using StringTools._pad.
public static inline function stringProduct ( s : String, n : Int ) {
if ( n < 0 ) {
throw ( 1 );
}
return StringTools.lpad ( "", s, s.length * n );
}
StringTools.lpad and StringTools.rpad can't seem to decide which is more efficient. It looks like rpad might be better for larger strings and lpad might be better for smaller strings, but they switch around a bit with each rerun. haxe.format.JsonPrinter uses lpad for concatenation, but I'm not sure which to recommend.

How to make it as parallel processing using OpenMP flag?

How can we use all processor at a time to run below code using openmp flag?
If I am converting while loop as for loop using for(;!xml.atEnd();) it is showing error:
need to initialisation and increment/decrements
//Need parallel processing for this code.
while (!xml.atEnd()) {
// cerr <<"while loop";
xml.readNext();
if (xml.isStartElement()) {
currentXmlElement = xml.name();
if (xml.name() == "sample") {
QString fname = xml.attributes().value("filename").toString();
QString sname = xml.attributes().value("name").toString();
QString setname = xml.attributes().value("setName").toString();
QString sampleOrder = xml.attributes().value("sampleOrder").toString();
QString isSelected = xml.attributes().value("isSelected").toString();
//_mainwindow->setStatusText(tr("Loading sample: %1").arg(sname));
//_mainwindow->setProgressBar(tr("Loading Sample Number %1").arg(++currentSampleCount),currentSampleCount,currentSampleCount+1);
bool checkLoaded=false;
Q_FOREACH(mzSample* loadedFile, _mainwindow->getSamples()) {
if (QString(loadedFile->fileName.c_str())== fname) checkLoaded=true;
}
if(checkLoaded == true) continue; // skip files that have been loaded already
// #pragma omp critical {
qDebug() << "Checking:" << fname;
QFileInfo sampleFile(fname);
if (!sampleFile.exists()) {
Q_FOREACH(QString path, pathlist) {
fname= path + QDir::separator() + sampleFile.fileName();
qDebug() << "Checking if exists:" << fname;
if (sampleFile.exists()) break;
}
}
if ( !fname.isEmpty() ) {
// mzFileIO* fileLoader = new mzFileIO(this);
// fileLoader->setMainWindow(_mainwindow);
// mzSample* sample = fileLoader->loadSample(fname);
// delete(fileLoader);
mzSample* sample = _mainwindow->fileLoader->loadSample(fname);
if (sample) {
_mainwindow->addSample(sample);
currentSample=sample;
if (!sname.isEmpty() ) sample->sampleName = sname.toStdString();
if (!setname.isEmpty() ) sample->setSetName(setname.toStdString());
if (!sampleOrder.isEmpty()) sample->setSampleOrder(sampleOrder.toInt());
if (!isSelected.isEmpty()) sample->isSelected = isSelected.toInt();
} else {
currentSample=NULL;
}
}
}
//change sample color
if (xml.name() == "color" && currentSample) {
currentSample->color[0] = xml.attributes().value("red").toString().toDouble();
currentSample->color[1] = xml.attributes().value("blue").toString().toDouble();
currentSample->color[2] = xml.attributes().value("green").toString().toDouble();
currentSample->color[3] = xml.attributes().value("alpha").toString().toDouble();
}
//polynomialAlignmentTransformation vector
if (xml.name() == "polynomialAlignmentTransformation" && currentSample) {
vector<double>transform;
Q_FOREACH(QXmlStreamAttribute coef, xml.attributes() ) {
double coefValue =coef.value().toString().toDouble();
transform.push_back(coefValue);
}
qDebug() << "polynomialAlignmentTransformation: "; printF(transform);
currentSample->polynomialAlignmentTransformation = transform;
currentSample->saveOriginalRetentionTimes();
currentSample->applyPolynomialTransform();
}
}
if (xml.isCharacters() && currentXmlElement == "projectDescription") {
projectDescription.append( xml.text() );
}
}
OpenMP doesn't really deal with arbitrary objects. Besides, the OpenMP model won't suit you here. The basic idea for OpenMP is to farm out single iterations of a for loop to different threads. This isn't going to work well for reading XML data, which by its very nature has to be read serially to preserve ordering.
In your case, suppose you could use OpenMP, parallelizing the outer loop. Suppose there are 8 OpenMP threads running. Each is going to execute the xml.readNext(); line. I can almost guarantee that the readNext function isn't thread-safe, which is going to give you undefined results. Even if it were thread-safe, it would have to read one full item (not sure what it's reading since I don't know the type of xml) atomically which would make your code mostly serial anyway.

How to properly implement cheat codes?

what would be the best way to implement kind of cheat codes in general?
I have WinForms application in mind, where a cheat code would unlock an easter egg, but the implementation details are not relevant.
The best approach that comes to my mind is to keep index for each code - let's consider famous DOOM codes - IDDQD and IDKFA, in a fictional C# app.
string[] CheatCodes = { "IDDQD", "IDKFA"};
int[] CheatIndexes = { 0, 0 };
const int CHEAT_COUNT = 2;
void KeyPress(char c)
{
for (int i = 0; i < CHEAT_COUNT; i++) //for each cheat code
{
if (CheatCodes[i][CheatIndexes[i]] == c)
{ //we have hit the next key in sequence
if (++CheatIndexes[i] == CheatCodes[i].Length) //are we in the end?
{
//Do cheat work
MessageBox.Show(CheatCodes[i]);
//reset cheat index so we can enter it next time
CheatIndexes[i] = 0;
}
}
else //mistyped, reset cheat index
CheatIndexes[i] = 0;
}
}
Is this the right way to do it?
Edit: Probably the worst thing I should have done was to include the first cheat codes that came from the top of my head as an example. I really did not want to see Doom's source code or their implementation, but general solution to this problem.
Why not download the DOOM source and see for yourself? =)
http://www.doomworld.com/idgames/?id=14576
I think this one's a bit easier to understand, though your original will probably perform better than this one:
using System.Collections.Generic;
void KeyPress(char c)
{
string[] cheatCodes = { "IDDQD", "IDKFA"};
static Queue<char> buffer; //Contains the longest number of characters needed
buffer.Enqueue(c);
if (buffer.Count() > 5) //Replace 5 with whatever your longest cheat code is
buffer.Dequeue();
bufferString = new System.String(buffer.ToArray());
foreach(string code in cheatCodes) {
if (bufferString.EndsWith(code)) {
//Do cheat work
}
}
}
here is the DOOM cheat implementation from the doom source:
#define SCRAMBLE(a) \
((((a)&1)<<7) + (((a)&2)<<5) + ((a)&4) + (((a)&8)<<1) \
+ (((a)&16)>>1) + ((a)&32) + (((a)&64)>>5) + (((a)&128)>>7))
int cht_CheckCheat ( cheatseq_t* cht, char key )
{
int i;
int rc = 0;
if (firsttime)
{
firsttime = 0;
for (i=0;i<256;i++) cheat_xlate_table[i] = SCRAMBLE(i);
}
if (!cht->p)
cht->p = cht->sequence; // initialize if first time
if (*cht->p == 0)
*(cht->p++) = key;
else if
(cheat_xlate_table[(unsigned char)key] == *cht->p) cht->p++;
else
cht->p = cht->sequence;
if (*cht->p == 1)
cht->p++;
else if (*cht->p == 0xff) // end of sequence character
{
cht->p = cht->sequence;
rc = 1;
}
return rc;
}

Resources