C++ - Class for list with 3 elements - c++

I'm attempting to make a data type that is basically an associative array/map, but it would have 3 elements instead of 2. It would be implemented like this:
myTable rouletteBoard;
rouletteBoard.push.back(0, "Green", "Neither");
rouletteBoard.push.back(00, "Green", "Neither");
rouletteBoard.push.back(1, "Red", "Odd");
So really just a map or list with 3 elements, the first one being the unique key.
Yes this is a Roulette game. And I understand how to basically have a class for each number and make a separate instance for each number with the appropriate properties, but I feel that would be rather inefficient, since I could just have a list of each number with it's associated properties.
I've gotten pretty much nowhere on creating the class for this. I keep wondering if there is a better way to do it and trying that, then getting frustrated and quitting.

First let's talk about the data. Note that 0 must be distinguished from 00 so we cannot store them both naively as integers. Second, note that the color and parity (odd/even) can be derived instantly from the number. There is no need to store them as separate data. So we can do this:
struct Pocket {
enum class Color { GREEN, RED, BLACK };
enum class Parity { NONE, ODD, EVEN };
Pocket(int8_t num) : number(num) {}
int8_t number; // -1 for "00" on American style wheel
Parity parity() const {
if (number < 1) return Parity::NONE;
if (number % 2) return Parity::ODD;
return Parity::EVEN;
}
Color color() const {
if (number < 1) return Color::GREEN;
if (number % 2) return Color::RED;
return Color::BLACK;
}
};
Then you can make a simple container:
std::vector<Pocket> wheel;
for (int8_t ii = is_american ? -1 : 0; ii <= 36; ++ii) {
wheel.emplace_back(ii);
}
Finally, you can add code for printing:
std::ostream& operator <<(std::ostream& out, Pocket pocket) {
if (pocket.number == -1) return out << "00";
return out << pocket.number;
}
const char* to_string(Pocket::Color color) {
switch (color) {
case Pocket::Color::GREEN: return "Green";
case Pocket::Color::RED: return "Red";
case Pocket::Color::BLACK: return "Black";
default: return "?";
}
}

If you want an associative array with multiple data, you create a map between a key and a data-structure.
For example here, if you only wanted to store strings, I'd suggest using a map between a key and a vector. Then you can add as many or as few strings as needed to each key, so it's a flexible system.
std::map<int,std::vector<std::string>> rouletteBoard;
Or, have that structure inside your "rouletteBoard" class.
As for the key, if you use literal ints, then you have a problem, as 0 and 00 would be the same int, you either need string keys, or to specify "00" interally with a special value such as -1. You can then create an enum relating to the different fields of the vector, a working prototype could look like:
#include<iostream>
#include<map>
#include<string>
#include<vector>
std::map<int, std::vector<std::string>> rouletteBoard;
enum
{
name,
color,
oddeven,
property_count
};
std::string colors[] = { "Green", "Black", "Red"};
std::string roulette_color(int i)
{
if (i < 1) return colors[0]; // Green
if (i < 11) return colors[1 + (i & 1)]; // Black=Even Red=Odd
if (i < 19) return colors[2 - (i & 1)]; // Black=Odd Red=Even
if (i < 29) return colors[1 + (i & 1)]; // Black=Even Red=Odd
return colors[2 - (i & 1)]; // Black=Even Red=Odd
}
int main()
{
rouletteBoard[-1] = {"00", roulette_color(-1), "Neither"};
rouletteBoard[ 0] = { "0", roulette_color(0), "Neither" };
for(int i = 1; i <=36; ++i)
{
rouletteBoard[i] = { std::to_string(i), roulette_color(i), (i & 1) ? "Odd" : "Even" };
}
for (int i = -1; i <= 36; ++i)
{
std::cout << rouletteBoard[i][name] << ": " << rouletteBoard[i][color] << ", " << rouletteBoard[i][oddeven] << "\n";
}
std::cin.get();
return 0;
}

Related

"The C++ Library doesen't provide a hash for this type." - Using own class in std::unordered_map

I am trying to code Conway's "Game of Life". While getting closer to my goal I got stuck with a compiler error:
C2338: The C++ Library doesen't provide a hash for this type.
At first I used the SFML class sf::Vector2D. When it failed to work for me I wrote a class of my own, hoping I could implement the missing hashCode method.
My question is:
Is it possible to uses my own class with its own hashCode method for std::unordered_map? I need to use a class, that can hold two numbers. (I also tried std::tuple, struct and stuff).
Here is one sheet of my code:
#include "GameMechanics.h"
GameMechanics::GameMechanics(Elements * elements):elements(elements)
{
this->refreshTime = 1000000; //ms
this->clock.restart();
}
GameMechanics::~GameMechanics()
{
}
bool GameMechanics::isRunning()
{
return this->running;
}
void GameMechanics::setRunning(bool running)
{
this->running = running;
}
void GameMechanics::loop()
{
unsigned passedTime = clock.getElapsedTime().asMicroseconds(); //check passed time since the clock got restarted
this->timeHeap += passedTime; //add passed time to the timeheap
this->clock.restart();
//only refresh every "refreshTime" seconds
if (timeHeap >= this->refreshTime) {
std::cout << "Calculated new generation!" << std::endl;
this->timeHeap -= this->refreshTime;
this->calculateNextGeneration();
}
}
void GameMechanics::calculateNextGeneration()
{
std::list<sf::Vector2i> oldGeneration = this->elements->getElements(); // population in the moment
sf::Vector2u elements = this->elements->getElementCount();
std::unordered_map<MyVector2D, int> counter; //here is the problem. Thats the line that makes some trouble
for (std::list<sf::Vector2i>::iterator it = oldGeneration.begin(); it != oldGeneration.end(); it++) {
sf::Vector2i position = *it;
for (int i = -1; i < 2; i++)
{
for (int j = -1; j < 2; j++)
{
if (position.x + i >= 0 && position.x + i <= this->elements->getElementCount().x &&
position.y + j >= 0 && position.y + j <= this->elements->getElementCount().y)
{
if (counter.find(MyVector2D(position.x + i, position.y + j)) != counter.end())
{
counter.at(MyVector2D(position.x + i, position.y + j))++;
}
else //if there is no such element, create a new entry
{
counter.insert({ MyVector2D(position.x + i, position.y + j),1 });
}
}
}
}
}
//create new generation
this->brithNewGeneration(&counter);
}
void GameMechanics::brithNewGeneration(std::unordered_map<MyVector2D,int>* counter)
{
//this methode does work
std::list<sf::Vector2i> newGeneration;
// for (std::unordered_map<MyVector2D, int>::iterator it = counter->begin(); it != counter->end(); it++)
{
//if life vell with < 2 neighbours, it dies
//life cell with 2 or 3 neighbours will continue living
//life cell with >4 cells will die
//dead cell with 3 neighbours will start living
}
}
The custom hashing function required for std::unordered_map (and std::unordered_set) is not a member function of the stored type. You need to specialize the std::hash template:
namespace std {
template<>
struct hash<YourType> {
using argument_type = YourType;
using result_type = std::size_t;
result_type operator()(argument_type const &obj) const {
// Compute and return the hash value for `obj`.
}
};
}
Your case is precisely the reason why it's done this way: you can specialize std::hash for sf::Vector2D if you wish, no need to implement your own class.

C++ std::set<string> Alphanumeric custom comparator

I'm solving a problem with a sorting non-redundant permutation of String Array.
For example, if input string is "8aC", then output should be order like {"Ca8","C8a", "aC8", "a8C", "8Ca", "9aC"}.I chose C++ data structure set because each time I insert the String into std:set, set is automatically sorted and eliminating redundancy. The output is fine.
But I WANT TO SORT SET IN DIFFERENT ALPHANUMERIC ORDER which is different from default alphanumeric sorting order. I want to customize the comparator of set the order priority like: upper case> lower case > digit.
I tried to customize comparator but it was quite frustrating. How can I customize the sorting order of the set? Here's my code.
set<string, StringCompare> setl;
for (i = 0; i < f; i++)
{
setl.insert(p[i]); //p is String Array. it has the information of permutation of String.
}
for (set<string>::iterator iter = setl.begin(); iter != setl.end(); ++iter)
cout << *iter << endl; //printing set items. it works fine.
struct StringCompare
{
bool operator () (const std::string s_left, const std::string s_right)
{
/*I want to use my character comparison function in here, but have no idea about that.
I'm not sure about that this is the right way to customize comparator either.*/
}
};
int compare_char(const char x, const char y)
{
if (char_type(x) == char_type(y))
{
return ( (int) x < (int) y) ? 1 : 0 ;
}
else return (char_type(x) > char_type(y)) ? 1 : 0;
}
int char_type(const char x)
{
int ascii = (int)x;
if (ascii >= 48 && ascii <= 57) // digit
{
return 1;
}
else if (ascii >= 97 && ascii <= 122) // lowercase
{
return 2;
}
else if (ascii >= 48 && ascii <= 57) // uppercase
{
return 3;
}
else
{
return 0;
}
}
You are almost there, but you should compare your string lexicographically.
I roughly added small changes to your code.
int char_type( const char x )
{
if ( isupper( x ) )
{
// upper case has the highest priority
return 0;
}
if ( islower( x ) )
{
return 1;
}
if ( isdigit( x ) )
{
// digit has the lowest priority
return 2;
}
// something else
return 3;
}
bool compare_char( const char x, const char y )
{
if ( char_type( x ) == char_type( y ) )
{
// same type so that we are going to compare characters
return ( x < y );
}
else
{
// different types
return char_type( x ) < char_type( y );
}
}
struct StringCompare
{
bool operator () ( const std::string& s_left, const std::string& s_right )
{
std::string::const_iterator iteLeft = s_left.begin();
std::string::const_iterator iteRight = s_right.begin();
// we are going to compare each character in strings
while ( iteLeft != s_left.end() && iteRight != s_right.end() )
{
if ( compare_char( *iteLeft, *iteRight ) )
{
return true;
}
if ( compare_char( *iteRight, *iteLeft ) )
{
return false;
}
++iteLeft;
++iteRight;
}
// either of strings reached the end.
if ( s_left.length() < s_right.length() )
{
return true;
}
// otherwise.
return false;
}
};
Your comparator is right. I would turn parameters to const ref like this
bool operator () (const std::string &s_left, const std::string &s_right)
and start by this simple implementation:
return s_left < s_right
This will give the default behaviour and give you confidence you are on the right track.
Then start comparing one char at the time with a for loop over the shorter between the length of the two strings. You can get chars out the string simply with the operator[] (e.g. s_left[i])
You're very nearly there with what you have.
In your comparison functor you are given two std::strings. What you need to do is to find the first position where the two strings differ. For that, you can use std::mismatch from the standard library. This returns a std::pair filled with iterators pointing to the first two elements that are different:
auto iterators = std::mismatch(std::begin(s_left), std::end(s_left),
std::begin(s_right), std::end(s_right));
Now, you can dereference the two iterators we've been given to get the characters:
char c_left = *iterators.first;
char c_right = *iterators.second;
You can pass those two characters to your compare_char function and it should all work :-)
Not absoloutely sure about this, but you may be able to use an enumerated class towards your advantage or an array and choose to read from certain indices in which ever order you like.
You can use one enumerated class to define the order you would like to output data in and another that contains the data to be outputed, then you can set a loop that keeps on looping to assign the value to the output in a permuted way!
namespace CustomeType
{
enum Outs { Ca8= 0,C8a, aC8, a8C, 8Ca, 9aC };
enum Order{1 = 0 , 2, 3 , 4 , 5};
void PlayCard(Outs input)
{
if (input == Ca8) // Enumerator is visible without qualification
{
string[] permuted;
permuted[0] = Outs[0];
permuted[1] = Outs[1];
permuted[2] = Outs[2];
permuted[3] = Outs[3];
permuted[4] = Outs[4];
}// else use a different order
else if (input == Ca8) // this might be much better
{
string[] permuted;
for(int i = 0; i<LessThanOutputLength; i++)
{
//use order 1 to assign values from Outs
}
}
}
}
This should work :
bool operator () (const std::string s_left, const std::string s_right)
{
for(int i = 0;i < s_left.size();i++){
if(isupper(s_left[i])){
if(isupper(s_right[i])) return s_left[i] < s_right[i];
else if(islower(s_right[i]) || isdigit(s_right[i]))return true;
}
else if(islower(s_left[i])){
if(islower(s_right[i])) return s_left[i] < s_right[i];
else if(isdigit(s_right[i])) return true;
else if(isupper(s_right[i])) return false;
}
else if(isdigit(s_left[i])){
if(isdigit(s_right[i])) return s_left[i] < s_right[i];
else if(islower(s_right[i]) || isupper(s_right[i])) return false;
}
}
}

How to limit a decrement?

There is a initial game difficulty which is
game_difficulty=5 //Initial
Every 3 times if you get it right, your difficulty goes up to infinity but every 3 times you get it wrong, your difficulty goes down but not below 5. So, in this code for ex:
if(user_words==words) win_count+=1;
else() incorrect_count+=1;
if(win_count%3==0) /*increase diff*/;
if(incorrect_count%3==0) /*decrease difficulty*/;
How should I go about doing this?
Simple answer:
if(incorrect_count%3==0) difficulty = max(difficulty-1, 5);
But personally I would wrap it up in a small class then you can contain all the logic and expand it as you go along, something such as:
class Difficulty
{
public:
Difficulty() {};
void AddWin()
{
m_IncorrectCount = 0; // reset because we got one right?
if (++m_WinCount % 3)
{
m_WinCount = 0;
++m_CurrentDifficulty;
}
}
void AddIncorrect()
{
m_WinCount = 0; // reset because we got one wrong?
if (++m_IncorrectCount >= 3 && m_CurrentDifficulty > 5)
{
m_IncorrectCount = 0;
--m_CurrentDifficulty;
}
}
int GetDifficulty()
{
return m_CurrentDifficulty;
}
private:
int m_CurrentDifficulty = 5;
int m_WinCount = 0;
int m_IncorrectCount = 0;
};
You could just add this as a condition:
if (user words==words) {
win_count += 1;
if (win_count %3 == 0) {
++diff;
}
} else {
incorrect_count += 1;
if (incorrect_count % 3 == 0 && diff > 5) {
--diff
}
}
For example:
if(win_count%3==0) difficulty++;
if(incorrect_count%3==0 && difficulty > 5) difficulty--;
This can be turned into a motivating example for custom data types.
Create a class which wraps the difficulty int as a private member variable, and in the public member functions make sure that the so-called contract is met. You will end up with a value which is always guaranteed to meet your specifications. Here is an example:
class Difficulty
{
public:
// initial values for a new Difficulty object:
Difficulty() :
right_answer_count(0),
wrong_answer_count(0),
value(5)
{}
// called when a right answer should be taken into account:
void GotItRight()
{
++right_answer_count;
if (right_answer_count == 3)
{
right_answer_count = 0;
++value;
}
}
// called when a wrong answer should be taken into account:
void GotItWrong()
{
++wrong_answer_count;
if (wrong_answer_count == 3)
{
wrong_answer_count = 0;
--value;
if (value < 5)
{
value = 5;
}
}
}
// returns the value itself
int Value() const
{
return value;
}
private:
int right_answer_count;
int wrong_answer_count;
int value;
};
And here is how you would use the class:
Difficulty game_difficulty;
// six right answers:
for (int count = 0; count < 6; ++count)
{
game_difficulty.GotItRight();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
// three wrong answers:
for (int count = 0; count < 3; ++count)
{
game_difficulty.GotItWrong();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
// one hundred wrong answers:
for (int count = 0; count < 100; ++count)
{
game_difficulty.GotItWrong();
}
// check wrapped value:
std::cout << game_difficulty.Value() << "\n";
Output:
7
6
5
Once you have a firm grasp on how such types are created and used, you can start to look into operator overloading so that the type can be used more like a real int, i.e. with +, - and so on.
How should I go about doing this?
You have marked this question as C++. IMHO the c++ way is to create a class encapsulating all your issues.
Perhaps something like:
class GameDifficulty
{
public:
GameDifficulty () :
game_difficulty (5), win_count(0), incorrect_count(0)
{}
~GameDifficulty () {}
void update(const T& words)
{
if(user words==words) win_count+=1;
else incorrect_count+=1;
// modify game_difficulty as you desire
if(win_count%3 == 0)
game_difficulty += 1 ; // increase diff no upper limit
if((incorrect_count%3 == 0) && (game_difficulty > 5))
game_difficulty -= 1; //decrease diff;
}
inline int gameDifficulty() { return (game_difficulty); }
// and any other access per needs of your game
private:
int game_difficulty;
int win_count;
int incorrect_count;
}
// note - not compiled or tested
usage would be:
// instantiate
GameDiffculty gameDifficulty;
// ...
// use update()
gameDifficulty.update(word);
// ...
// use access
gameDifficulty.gameDifficulty();
Advantage: encapsulation
This code is in one place, not polluting elsewhere in your code.
You can change these policies in this one place, with no impact to the rest of your code.

Is there a generalization of std::bitset for two-bit values?

Suppose I am a genome scientist trying to store extremely long strings of characters, each of which represents two bits of information (i.e. each element is either G, A, T, or C). Because the strings are incredibly long, I need to be able to store a string of length N in precisely 2N bits (or rather, N/4 bytes).
With that motivation in mind, I am looking for a generalization of std::bitset (or boost::dynamic_bitset<>) that works on two-bit values instead of single-bit values. I want to store N such two-bit values, each of which can be 0, 1, 2, or 3. I need the data packed as closely as possible in memory, so vector<char> will not work (as it wastes a factor of 4 of memory).
What is the best way to achieve my goal? One option is to wrap the existing bitset templates with customized operator[], iterators, etc., but I'd prefer to use an existing library if at all possible.
std::bitset<> is fixed length and you probably do not want that.
I think you should go ahead and wrap std::vector<bool>.
Note that std::vector<bool> is optimised for space, but has the benefit that it is dynamic in size.
Presumably you need to read the genome of arbitrary length on from somewhere.
Have a think about whether you need much of an API to access it; you might only need a couple of methods.
#Jefffrey's answer already covers the relevant code, if for bitset<>.
[ I am not familiar with boost::dynamic_bitset<> and what it might give over vector.]
One further thought is whether it might be convenient for you to work with quads of letters, a quad nicely filling a char in space.
class Genome
{
public:
enum class Letter {A,C,G,T};
Genome(const std::string& source)
{
code_.resize(source.size() * 2);
for (unsigned index = 0; index != source.size(); ++index)
{
char text = source[index];
Letter letter = textToLetter(text);
set(index, letter);
}
}
static Letter textToLetter(char text)
{
// Or search through the array `letterText`.
// Or come up with a neat but unintelligible one liner ...
Letter letter = Letter::A;
switch (text)
{
case 'A':
letter = Letter::A;
break;
case 'C':
letter = Letter::C;
break;
case 'G':
letter = Letter::G;
break;
case 'T':
letter = Letter::T;
break;
default:
// Invalid - handle error.
break;
}
return letter;
}
static char letterToText(Letter l)
{
return letterText[(unsigned)l];
}
// Add bounds checking
Letter get(unsigned index) const
{
unsigned distance = index * 2;
char numeric = code_[distance] + code_[distance + 1] * 2;
return Letter(numeric);
}
// Add bounds checking
void set(unsigned index, Letter value)
{
unsigned distance = index * 2;
bool low = (unsigned)value & 1;
bool high = (bool)((unsigned)value & 2);
code_[distance] = low;
code_[distance + 1] = high;
}
unsigned size()
{
return code_.size() / 2;
}
// Extend by numLetters, initially set to 'A'
void extend(unsigned numLetters)
{
code_.resize(code_.size() + numLetters * 2);
}
private:
static char letterText[4];
std::vector<bool> code_;
};
char Genome::letterText [4] = { 'A', 'C', 'G', 'T' };
int main()
{
Genome g("GATT");
g.extend(3);
g.set(5, Genome::Letter::C);
for (unsigned i = 0; i != g.size(); ++i)
std::cout << Genome::letterToText(g.get(i));
std::cout << std::endl;
return 0;
}
You have two choices.
Given:
enum class nucleobase { a, c, g, t };
You have two choices. You can:
use a single std::bitset and play with indexing
use std::bitset in combination with another container
For the first, you can just define a couple of functions that target the correct number of bits per set/get:
template<std::size_t N>
void set(std::bitset<N>& bits, std::size_t i, nucleobase x) {
switch (x) {
case nucleobase::a: bits.set(i * 2, 0); bits.set(i * 2 + 1, 0); break;
case nucleobase::c: bits.set(i * 2, 0); bits.set(i * 2 + 1, 1); break;
case nucleobase::g: bits.set(i * 2, 1); bits.set(i * 2 + 1, 0); break;
case nucleobase::t: bits.set(i * 2, 1); bits.set(i * 2 + 1, 1); break;
}
}
template<std::size_t N>
nucleobase get(const std::bitset<N>& bits, std::size_t i) {
if (!bits[i * 2])
if (!bits[i * 2 + 1]) return nucleobase::a;
else return nucleobase::c;
else
if (!bits[i * 2 + 1]) return nucleobase::g;
else return nucleobase::t;
}
Live demo
The above is just an example and a terrible one (it's almost 4AM here and I really need to sleep).
For the second you just need to map alleles and bits:
bit_pair bits_for(nucleobase x) {
switch (x) {
case nucleobase::a: return bit_pair("00"); break;
case nucleobase::c: return bit_pair("10"); break;
case nucleobase::g: return bit_pair("01"); break;
case nucleobase::t: return bit_pair("11"); break;
}
}
nucleobase nucleobase_for(bit_pair x) {
switch (x.to_ulong()) {
case 0: return nucleobase::a; break;
case 1: return nucleobase::c; break;
case 2: return nucleobase::g; break;
case 3: return nucleobase::t; break;
default: return nucleobase::a; break; // just for the warning
}
}
Live demo
Of course if you need runtime length you can just use boost::dynamic_bitset and std::vector.
Here's what I use for fixed-length k-mers.
#include <cstdint>
#include <cstdlib>
#include <ostream>
enum class nucleotide { A, C, G, T };
inline std::ostream&
operator<<(std::ostream& pOut, nucleotide pNt)
{
switch (pNt) {
case nucleotide::A: pOut << 'A'; break;
case nucleotide::C: pOut << 'C'; break;
case nucleotide::G: pOut << 'G'; break;
case nucleotide::T: pOut << 'T'; break;
}
return pOut;
}
class kmer_base;
class nucleotide_proxy {
public:
operator nucleotide() const {
return nucleotide((*mWord >> (mPosition * 2)) & 3);
};
nucleotide_proxy& operator=(nucleotide pNt) {
uint64_t word = *mWord;
word &= ~(uint64_t(3) << (mPosition*2));
word |= uint64_t(pNt) << (mPosition*2);
*mWord = word;
return *this;
};
private:
friend class kmer_base;
nucleotide_proxy(uint64_t* pWord, uint8_t pPosition)
: mWord(pWord), mPosition(pPosition)
{
}
uint64_t* mWord;
uint8_t mPosition;
};
class kmer_base {
protected:
nucleotide_proxy access(uint64_t* pWord, size_t pPosition)
{
return nucleotide_proxy(pWord + (pPosition / 32), (pPosition & 31));
}
const nucleotide_proxy access(uint64_t* pWord, size_t pPosition) const
{
return nucleotide_proxy(pWord + (pPosition / 32), (pPosition & 31));
}
};
template<int K>
class kmer : public kmer_base
{
enum { Words = (K + 31) / 32 };
public:
nucleotide_proxy operator[](size_t pOutdex) {
return access(mWords, pOutdex);
}
const nucleotide_proxy operator[](size_t pOutdex) const {
return access(mWords, pOutdex);
}
private:
uint64_t mWords[Words];
};
Extending this to dynamic-length k-mere is left as an exercise; it's pretty easy once you have nucleotide_proxy at your disposal. Implementing the reverse complement operator efficiently is also left as an exercise.

Can I have a single-dimension array of booleans that skip indexes?

I'm pretty new to Arduino programming, but am getting the hang of it. I've got a pretty strong PHP & Javascript background, so that's where I'm coming from.
I've been working on a project with an LCD touchscreen, with grids of buttons that I expect other developers to customize to their needs. There's an array that a developer can freely add items to, and additional pages of buttons are added dynamically. Clicking one simply sends that button's index to a receiver.
Now I want to make some buttons "sticky" so they stick on until turned off. My idea was to set up a simple array of booleans, like so:
boolean stickyTriggers[1] = 1;
boolean stickyTriggers[2] = 0;
boolean stickyTriggers[3] = 0;
boolean stickyTriggers[9] = 1;
What I'd expect is that buttons 1, 2, 3, and 9 are sticky, and buttons 1 and 9 are "on" by default. I figured when I'm rendering the buttons, I can simply say something like:
if (stickyTriggers[i]) {
// highlight button
}
The error I'm getting on compile is:
array must be initialized with a brace-enclosed initializer
I sort of realize I'm re-declaring the variable 3 times here, but I've tried other ways and got other errors.
What is the correct way to hold a simple array of flags? I want to be able to look up a key and get a true or false (or null), without having to hard-code an array length. I'm sure it's all possible, but is there a simple way?
Thanks
Clarification: The key here is that triggers 4-8 are NOT in the stickyTriggers array at all, since they are NOT sticky. I want to be able to skip indexes, or use arbitrary IDs as keys, as the title suggests. Most of the answers miss this.
I think you need somethig like this
#include <iostream>
using namespace std;
int main()
{
bool b[5] = {true, false, true, false, true};
cout << "Hello World" << endl;
for (int i=0; i < 5; i++) {
if (b[i]) {
cout<< "Index " << i << " is true" <<endl;
} else {
cout<< "Index " << i << " is false"<<endl;
}
}
return 0;
}
Try this:
#define ARRAY_SIZE 10
#define BOOL unsigned int
#define TRUE 1
#define FALSE 0
int main()
{
BOOL stickyTriggers[ARRAY_SIZE] = { FALSE };
stickyTriggers[1] = TRUE ;
stickyTriggers[9] = TRUE ;
return 0;
}
Yes, you are re-defining the array several times.
In C++, you may want to use bitset, check this link
http://en.cppreference.com/w/cpp/utility/bitset
You can try vector (which can dynamically change its size) too, like this
#include<iostream>
#include<vector>
using namespace std;
enum trigger_status {ON, OFF, NON_STICKY};
int main(){
vector<trigger_status> sticky_triggers(251, trigger_status::OFF); // you can add element to it dynamically, default all to off
sticky_triggers[0] = trigger_status::ON;
sticky_triggers[9] = trigger_status::ON;
sticky_triggers.push_back(trigger_status::ON); // sticky_triggers[251] = true, now u have 252 elements
}
Maybe I should have left out the C++ tag, to only invite Arduino-specific solutions.
But here's what I found, which solves my problem:
Alexander Brevig's HashMap Library:
http://playground.arduino.cc/Code/HashMap
As discussed here: http://forum.arduino.cc/index.php?PHPSESSID=q7rt05n43aa4enp6hepri50pi1&topic=42114.msg305435#msg305435
#include <HashMap.h>
const byte HASH_SIZE = 5;
HashType<int,boolean> hashRawArray[HASH_SIZE];
HashMap<int,boolean> hashMap = HashMap<int,boolean>(hashRawArray, HASH_SIZE);
void setup() {
Serial.begin(9600);
hashMap[0](1,true);
hashMap[1](2,false);
hashMap[2](3,false);
hashMap[3](4,false);
hashMap[4](9,true);
Serial.println(hashMap.getValueOf(1));
Serial.println(hashMap.getValueOf(2));
Serial.println(hashMap.getValueOf(9));
}
I can add a quick wrapper to add items to the hashMap without having to hard-code the index, but this gives me an easy way to set up an associative array of booleans, using arbitrary integers as the keys.
Thanks for trying everyone, sorry none of the answers worked on Arduino.
You might be able to use this it does not quite fulfil your desire to have no references to non-sticky triggers but it is fast and only uses 64 bytes of memory to map out 256 triggers.
I already had it in single boolean mode so I adapted it to make two maps and renamed it to suit your question..
Basically it is a bitmap.
Compiles and tests run OK for Arduino Duo.
// bitmapped booleans
const int IntBits = 16;
const int NumTriggers = 256;
const int idxSticky = 0;
const int idxDown = 1;
unsigned int TriggerMap[NumTriggers/IntBits][2];
void setup() {
Serial.begin(9600);
clearTriggerMap; // set all to not sticky and not down
// tests
setStickyTrigger(1, true, true);
setStickyTrigger(2, true, false);
setStickyTrigger(3, true, false);
setStickyTrigger(9, true, true);
setStickyTrigger(30, true, true);
setStickyTrigger(128, true, true);
setStickyTrigger(255, true, true);
}
void loop() {
// tests
Test(0);
Test(1);
Test(2);
Test(3);
Test(9);
Test(30);
Test(128);
Test(255);
delay(5000);
}
void Test( int ATrigger) {
// testing
if (IsStickyTrigger(ATrigger)) {
Serial.print( "Trigger ");
Serial.print(ATrigger);
Serial.print(" is sticky");
if (IsStickyTriggerDown(ATrigger)) {
Serial.print(" and it is down");
}
}
Serial.println();
}
void clearTriggerMap() {
for (int i = 0; i < NumTriggers/IntBits; i++) {
for (int j = 0; j < 2; j++){
TriggerMap[i][j] = 0;
}
}
}
void setStickyTrigger(int AIndex, boolean ASticky, boolean IsDown) {
unsigned int o;
unsigned int b = 1;
o = AIndex / IntBits;
b = b << (AIndex % IntBits);
if (ASticky) {
TriggerMap[o][idxSticky] = TriggerMap[o][idxSticky] | b;
} else {
b = ~b;
TriggerMap[o][idxSticky] = TriggerMap[o][idxSticky] & b;
}
if (IsDown) {
TriggerMap[o][idxDown] = TriggerMap[o][idxDown] | b;
} else {
b = ~b;
TriggerMap[o][idxDown] = TriggerMap[o][idxDown] & b;
}
}
boolean IsStickyTrigger(int AIndex) {
unsigned int b = 1;
b = b << (AIndex % IntBits);
return (TriggerMap[AIndex / IntBits][idxSticky] & b) != 0;
}
boolean IsStickyTriggerDown(int AIndex) {
unsigned int b = 1;
b = b << (AIndex % IntBits);
return (TriggerMap[AIndex / IntBits][idxDown] & b) != 0;
}