I'm not sure exactly how to phrase what I'm trying to ask; in C++, using the stdio.h header instead of iostream, how would I make it so that if the escape key is pressed at any point, the program is terminated? Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually? Below is my code (the sleep() function is just for a visual loading/calculating effect):
#include <stdio.h>
#include <math.h>
#include <windows.h>
void repeat();
void quadratic()
{
double a, b, c;
double ans[2];
printf("-Arrange your equation in the form aX^2+bX+c \n-Enter the value of a: ");
scanf("%lf", &a);
printf("-Enter the value of b: ");
scanf("%lf", &b);
printf("-Enter the value of c: ");
scanf("%lf", &c);
double radical=((b*b)-(4*a*c));
double root=sqrt(radical);
double negB=(-1)*b;
double denominator=2*a;
if(denominator==0)
{
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf("\nError: Denominator must be non-zero.\n \n \n");
}
else if(radical==0)
{
ans[0]=negB/denominator;
printf("Both roots are equal: both values are X=%lf\n \n \n", ans[0]);
}
else if(radical<0)
{
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
double r,i;
radical*=-1;
r=negB/(2*a);
i=sqrt(radical)/(2*a);
printf("\nBoth roots are imaginary numbers.\n");
printf("Non-real answer(s): X=%lf+%lfi X=%lf-%lfi\n \n \n",r,i,r,i);
}
else
{
ans[0]=(negB+root)/denominator;
ans[1]=(negB-root)/denominator;
printf("Calculating");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf(".");
Sleep(100);
printf("\nX=%lf, X=%lf\n \n", ans[0], ans[1]);
}
repeat();
}
void repeat()
{
quadratic();
}
int main(void)
{
quadratic();
return 0;
}
The terminal used in stdio is likely to be line buffered
(cooked). If it is, checking for the escape key through scanf will
not work.
SEE THESE URLS:
Capture characters from standard input without waiting for enter to be pressed
scanf not reading input
CURSES or NCURSES will detect the escape key (ASCII character 27), depending
on the terminal type.
This code can be used in WINDOWS to check for the ESCAPE
key.
#include <conio.h>
#include <ctype.h>
int ch;
_cputs( "Type 'Y' when finished typing keys: " );
do
{
ch = _getch();
ch = toupper( ch );
if (ch != 27) {
_cputs( "CHARACTER: " );
_putch( ch );
_putch( '\r' ); // Carriage return
_putch( '\n' ); // Line feed
}
} while( ch != 27 );
Is there something I could add once at the top of the program, or would I have to add it to every loop/conditional individually?
I think you can add something once, and use it to catch key stroke events easily throughout your program.
Following is a code snippet showing a function I have used to handle key stroke events in a console application. It uses GetAsyncKeyState(). Included is a section that shows how to capture a CTRL key, and how you can do something once you see it. (the snippet shown shows how I capture a <ctrl><shift><h> key sequence to display a help menu for using this particular routine.
Note: In the description, delay_x(float delay) is simply a custom, non-blocking sleep, or delay function that includes a call to the following snippet. It is called from within the main program loop while(1){...} . Exiting the program is provided in one of the keystroke combinations: <CTRL><SHIFT><K>
Code snippet:
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
//
// SetAppState() is called continuously from within delay_x()
// to capture keystroke combinations as they occur asynchronously
// with this application, Keystroke combinations are listed below
//
// Note: GetAsyncKeyState() can maintian information regarding the
// state of a key instantaineously by use the MSB,
// and recently by using the LSB.
//
// For this application
// only instantaineous information will be kept, minimizing
// conflicts with other keyboard shortcut definitions
// defined by other applications that may be running
// simultaineously.
//
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
void SetAppState(void)
{
short state=0;
short state1=0;
state = GetAsyncKeyState(VK_CONTROL);
if (0x80000000 & state) //check instantaineous state of key
{
state = GetAsyncKeyState(VK_SHIFT);
if (0x80000000 & state) //check instantaineous state of key
{
state = GetAsyncKeyState('h');
state1 = GetAsyncKeyState('H');
if ((0x80000000 & state) ||
(0x80000000 & state1))
{ sprintf(gTempBuf, "Usage - keystrokes to access and control the PaAutoStartSlot application:\n\n"
"<CTRL><SHIFT> H (H)elp - \n"
"<CTRL><SHIFT> V o(V)erride - \n"
"<CTRL><SHIFT> S (S)tatus - \n"
"<CTRL><SHIFT> K (K)ill - \n"
"<CTRL><SHIFT> N (N)o - \n"
"<CTRL><SHIFT> I (I)Inside - \n"
"<CTRL><SHIFT> O (O)Outside- \n"
"\nSee log file at this location for runtime errors: \n\n%s", LOGFILE);
MessagePopup("Usage Menu",gTempBuf);
}
///// ... more code ...
End of snippet
Edit - Answer to questions in comments how to call GetAsyncKeyState()
There are many ways you could call GetAsyncKeyState() at the same time other stuff is going on. Threads are a good way. You can also do it all in line using a while()/switch(){} combination. Here is a very simple example of how you could do this (in pseudo code)
int gRunning = 1;
int state = 1;
int main(void)
{
//create variables, initialize stuff
while(gRunning)//this is your main program loop
{
delay_x(1.0);//check for keystrokes
switch(state) {
case 1:
//do some stuff here
//and experiment with values passed to delay_x(n)
delay_x(10000);//check for keystrokes
state++;
break;
case 2:
//do some different stuff here
delay_x(10000);//check for keystrokes
state++;
break;
... Add as many cases as you need for your program.
case n://last case, set execution flow to top
//do some more different stuff here
delay_x(10000);//check for keystrokes
state = 1;//loop back to top
break;
}
}
return 0;
}
void delay_x (float delay)
{
static clock_t time1;
static clock_t time2; clock();
time1 = clock();
time2 = clock();
while ((time2 - time1) < delay)
{
time2 = clock();
SetAppState(); //see partial definition in my original answer above.
}
}
Note: Using this method, you can have as many, or as few, cases as you need, the important thing is to keep a steady flow of calls to GetAsyncKeyState(). This does that via the call to delay_x().
Note2: Here is the segment (added to above definition) that will cause your program to exit:
state = GetAsyncKeyState('k');
state1 = GetAsyncKeyState('K');
if ((0x80000000 & state) ||
(0x80000000 & state1))
{
printf("Kill Program");
gRunning = FALSE;
}
Related
What I want to do is sampling the keyboard input at a certain rate (e.g. 10-20 Hz). I use a while loop that read from stdin (I use read because I want to read asynchronously, e.i. I don't want to press enter every time) and then I have a pause before a new cycle starts to keep the sampling frequency stable.
The user press left/right arrow to give a command (to a robot). If nothing is pressed, the output is 0.
The problem is that, during the pause, the stdin buffer is written (I suppose), and so the read will return an old input. The final result is that the output is delayed. So if I press left the output immediately change to 1, but when I release it takes some seconds to return to 0. I want to remove this delay.
My aim is to sample just the more recent key pressed, in order to synchronize user input and output command without delays. Is there a way? Thank you in advance.
This is the method I'm using:
void key_reader::keyLoop()
{
char c;
bool dirty = false;
int read_flag;
// get the console in raw mode
tcgetattr(kfd, &cooked);
memcpy(&raw, &cooked, sizeof(struct termios));
raw.c_lflag &= ~(ICANON | ECHO);
// Setting a new line, then end of file
raw.c_cc[VEOL] = 1;
raw.c_cc[VEOF] = 2;
tcsetattr(kfd, TCSANOW, &raw);
//FD_ZERO(&set); /* clear the set */
//FD_SET(kfd, &set); /* add our file descriptor to the set */
//timeout.tv_sec = 0;
//timeout.tv_usec = 10000;
if (fcntl(kfd, F_SETFL, O_NONBLOCK) == -1)
{
perror("fcntl:"); // an error accured
exit(-1);
}
puts("Reading from keyboard");
puts("---------------------------");
puts("Use arrow keys to move the turtle.");
ros::Rate r(10);
while (ros::ok())
{
read_flag = read(kfd, &c, 1);
switch (read_flag)
{
case -1:
// case -1: is empty and errono
// set EAGAIN
if (errno == EAGAIN)
{
//no input yet
direction = 0;
break;
}
else
{
perror("read:");
exit(2);
}
// case 0 means all bytes are read and EOF(end of conv.)
case 0:
//no input yet
direction = 0;
break;
default:
ROS_DEBUG("value: 0x%02X\n", c);
switch (c)
{
case KEYCODE_L:
ROS_DEBUG("LEFT");
direction = 1;
dirty = true;
break;
case KEYCODE_R:
ROS_DEBUG("RIGHT");
direction = -1;
dirty = true;
break;
}
}
continuos_input::input_command cmd;
cmd.type = "Keyboard";
cmd.command = direction;
cmd.stamp = ros::Time::now();
key_pub.publish(cmd);
r.sleep();
}
}
I feel that the issue is with your subscriber rather than publisher. I can see that you have used rate to limit the publishing rate to 10Hz. Do confirm the publishing rate using Topic Monitor in rqt. Also setting a lower queue size for the publisher might help. Can't give a more definitive answer without referring to your subscriber node.
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
if(Serial.available())
{
kontrol=Serial.read(); // it reads from python voice recognition
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right")
{pos += 30;
kontrol = '0';
kontrolstr = "";
}
else if(kontrolstr== "left")
{pos -= 30;
kontrol= '0';
kontrolstr = "";
}
myservo.write(pos);
delay(100);
}
It works with voice_command.py (which I wrote) on linux terminal. When code is like this, right after uploading this code to arduino, it works well until voice recognition understand a different word from "right" or "left". When voice command send to arduino another string different from "right" or "left", program still works without any error but after this point it starts not to respond "right" or "left" command anymore. To solve this I did this change. I put an 'else':
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
if(Serial.available())
{
kontrol=Serial.read();
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right")
{pos += 30;
kontrol = '0';
kontrolstr = "";
}
else if(kontrolstr== "left")
{pos -= 30;
kontrol= '0';
kontrolstr = "";
}
else { // I write this to make it work..
kontrol = '0';
kontrolstr = "";
}
myservo.write(pos);
delay(100);
}
However, now it is not responding "right" and "left" command too. How can I solve this problem?
The Problem
The problem you are having is that your Serial.available() block is only reading one byte from the Serial buffer in each iteration of the loop. As a consequence, when your servo sends the word "right", the Serial buffer is "right". The first iteration through loop() gives "r" as the value for kontrolstr.
Without the else block, on the second loop, kontrolstr is set to ri, then rig, then righ, etc, and is only reset when left or right are found. This is also what causes the problem of left and right not being reached if another word has been recognized - kontrolstr would have been set to , e.g. "horse", this is not recognized, so then when it sends "right", you get "horseright", etc.
With the else block, on the first loop, kontrolstr is "r", so it hits the else block, and resets the string. On the second loop, kontrolstr is "i", it hits the else block and resets the string, etc, never reaching the relevant control block.
Possible solutions
The start of the solution is to read the entire Serial buffer before processing it, so replace the block starting with if(Serial.available() to:
while(Serial.available())
{
kontrol = Serial.read();
kontrolstr.concat(kontrol);
}
This will read the entire buffer in the first loop, so as long as all the data has been sent between iterations of the loop, your problem will be solved. However, it takes a non-zero amount of time to send data over a Serial port, so it's possible that your loop() iteration triggers in the middle of a send, in which case the Serial buffer might be something like "rig", which won't match "right" or "left", will be reset, then in the next loop you'll get "ht", and again it will be reset - the trigger will be missed.
If possible, I think the best solution would be to have your servo send the control words with a delimiter between them, e.g. \n. If your servo sends "right\nanother word\nleft\n", then you can wait for entire words to come in before processing them. You can do this by changing your loop() to:
void loop()
{
kontrolstr = ""; // Reset on each iteration of the loop
while(Serial.available())
{
kontrol = Serial.read();
// If we reach the delimiter, stop reading from the Serial buffer
if (control == '\n') {
break;
}
kontrolstr.concat(kontrol);
}
if(kontrolstr== "right") {
pos += 30;
} else if(kontrolstr== "left") {
pos -= 30;
}
myservo.write(pos);
delay(100);
}
Of course, this assumes that you're OK with allowing extra words to accumulate in the Serial buffer (seems fine since the buffer wasn't filling up even when you were reading only 1 character every 100ms). However, if it does happen that the Serial buffer is overflowing, then you can create a second string bufferstring and always append whatever is in the Serial buffer to that string, then on each iteration of the loop, pull out the oldest command, giving:
#include <Servo.h>
Servo myservo; // create servo object to control a servo
int pos = 90;
String kontrolstr = "";
String bufferstring = "";
char kontrol;
void setup()
{
Serial.begin(9600);
myservo.attach(9);// attaches the servo on pin 9 to the servo object
}
void loop()
{
// Read whatever's in the Serial port into the buffer string
while(Serial.available())
{
kontrol = Serial.read();
// If we reach the delimiter, stop reading from the Serial buffer
bufferstring.concat(kontrol);
}
// Split the string by the delimiter
int delimiter_loc = bufferstring.indexOf('\n');
if (delimiter_loc != -1) {
// Get the first delimiter_loc characters (doesn't include the delimiter)
kontrolstr = bufferstring.substring(0, delimiter_loc);
// Remove all the characters up to and including the delimiter_loc
bufferstring.remove(0, delimiter_loc + 1);
}
if(kontrolstr== "right") {
pos += 30;
} else if(kontrolstr== "left") {
pos -= 30;
}
// Reset on each iteration of the loop
kontrolstr = "";
myservo.write(pos);
delay(100);
}
This question already has answers here:
What is an undefined reference/unresolved external symbol error and how do I fix it?
(39 answers)
Closed 7 years ago.
When trying to compile my code in Code::Blocks I receive a bunch of Undefined reference errors. The code is below.
Header
#ifndef BLACKJACK_GAME
#define BLACKJACK_GAME
// A game of blackjack has a deck and two hands
#include "Deck.h"
#include "BlackjackHand.h"
// Different ways the game can end. The first states take precedence over
// over the latter states so check for the early ones first (like blackjack)!
// Note that a "natural 21" is when you are delt two cards totaling 21.
enum eGameState {
GAME_BLACKJACK_PUSH, // Both player and dealer have a natural 21
GAME_DEALER_BLACKJACK, // Dealer has a natural 21
GAME_PLAYER_BLACKJACK, // Player has a natural 21
GAME_DEALER_BUST, // The dealer has more than 21
GAME_PLAYER_BUST, // The player has more than 21
GAME_DEALER_WIN, // The dealer's score is higher than players
GAME_PLAYER_WIN, // The player's score is higher than dealers
GAME_PUSH // The scores are tied
};
class BlackjackGame
{
public:
BlackjackGame();
// Clear the players hands and shuffle the deck
void newGame();
// Play a single round of blackjack showing the output using
// the 'PDcurses' library (for color and card suits).
void playGameCurses();
// Display the deck on the screen (for debugging purposes)
void printDeckCurses();
// Print both players hands to the screen using 'PDCurses'
// If pShowDealerScore is true then the dealer's score is printed
// Otherwise, the dealer's score is shown as '??'
void printHandsCurses(bool pShowDealerScore = false) const;
// Determine the state of the game. Note that if you have not yet
// played a round since you constructed this object or called newGame()
// Then the state returned is not accurate.
eGameState getGameState() const;
private:
// A deck of cards for shufflying and dealing hands
Deck mDeck;
// The two hands for the player and the dealer
BlackjackHand mPlayer, mDealer;
// A helper function for prompting the player (internal use only)
char promptPlayerCurses();
// A helper function to quit the game (internal use only)
void quitGameCurses();
};
#endif
CPP
#include "BlackjackGame.h"
// The library used for our text based user interface
#include <curses.h>
// Normal text color for PDCurses (defined in main)
#define NORM_TEXT 1
// Default constructor (does nothing)
BlackjackGame::BlackjackGame() {}
/* newGame() - Clear the two hands so we are ready for a new game */
void BlackjackGame::newGame()
{
mDealer.clear();
mPlayer.clear();
}
/* playGameCurses() - Play a single round of poker with one human player and one
* dealer following standard Vegas rules. Uses PDCurses for input and output to
* the console.
*
* You must implement this method but you do not need to worry about curses. Call
* 'promptPlayerCurses() to show the hands and prompt the human player to hit, stand
* or quit. This method will return the key they pressed. You can also use
* quitGameCurses() to exit properly if the user chose to 'quit'.
*/
void BlackjackGame::playGameCurses()
{
// TODO: Shuffle the deck and deal the cards (make sure dealer's first card is hidden).
mDeck.shuffle();
mPlayer.takeCard(mDeck.dealCard());
mDealer.takeCard(mDeck.dealCard());
mDealer.hideCard();
mPlayer.takeCard(mDeck.dealCard());
mDealer.takeCard(mDeck.dealCard());
// TODO: Check for a 'natural 21' (blackjack) before playing
if ( mDealer.hasBlackjack() || mPlayer.hasBlackjack())
quitGameCurses();
// TODO: Allower human player to hit, stand and quit as needed (repeat until player is done)
int flag = 0;
while(flag!=1)
{
char input = promptPlayerCurses(); // This line is an example only, a placeholder
switch(input)
{
case 'h': mPlayer.takeCard(mDeck.dealCard());
break;
case 's': flag =1;
break;
case 'q': quitGameCurses();
break;
default: break;
}
}
// TODO: Play the 'dealer' hand according to vegas rules
mDealer.showCards();
while(vegasDealerWouldHit())
{
mDealer.takeCard(mDeck.dealCard());
}
}
/* promptPlayerCurses() - Show the hands and prompt the human player to hit, stand or quit.
* output: returns the single character entered by the player at the prompt.
* - 'h' means hit, 's' means stand
* - 'q' means you should immediatly quit (call 'quitGameCurses()')
*/
char BlackjackGame::promptPlayerCurses()
{
// Show the hands
printHandsCurses();
// Hit or stand?
attron(COLOR_PAIR(NORM_TEXT));
mvprintw(3, 0, "Hit, stand or Quit ('h', 's', 'q'): ");
refresh();
// Read and return a single character
return getch();
}
/* quitGameCurses() - End curses output and exit the program immediately */
void BlackjackGame::quitGameCurses()
{
// End curses output, then pause and exit
endwin();
system("pause");
exit(0);
}
/* printDeckCurses() - A handy function that displays the content of the game deck
* using curses.
*
* This can be handy for debugging your deck and making sure it is getting properly
* shuffled. It is presently used for the fancy opening screen.
*/
void BlackjackGame::printDeckCurses()
{
// Start at the upper left corner of the screen
move(0, 0);
// For all 52 cards
for(int i=1; i<=52; i++)
{
// Get the next card and print it
PrintableCard lCard = mDeck.dealCard();
lCard.printCurses();
// If we've output 13 cards then move down a row
if(i%13 == 0)
{
move(2*(i/13), 0);
}
else
{
// Switch back to normal text color and output ' ' characters
attron(COLOR_PAIR(NORM_TEXT));
if(lCard.getFaceValue() == VALUE_TEN) printw(" ");
else printw(" ");
}
}
}
/* printHandsCurses() - A function to display the current scores and hands for this game
* using curses.
*
* This function is used in promptPlayerCurses() to show the hands before asking them if
* they want to hit or stand. Note that it alsways clears the window before drawing.
*/
void BlackjackGame::printHandsCurses(bool pShowDealerScore) const
{
// Clear window
erase();
// Show dealer and player hands
attron(COLOR_PAIR(NORM_TEXT));
mvprintw(0, 0, "Player: %d\n", mPlayer.score());
move(1, 0); mPlayer.printCurses();
attron(COLOR_PAIR(NORM_TEXT));
if(pShowDealerScore)
{
mvprintw(0, 50, "Dealer: %d\n", mDealer.score());
}
else
{
mvprintw(0, 50, "Dealer: ??\n");
}
move(1, 50); mDealer.printCurses();
refresh();
}
/* getGameStat() - Examine the hands of both players and return the current state of
* the game using the eGameState enum.
*
* You must examine the state of the game by comparing the two hands (their scores and their
* number of cards) and return the appropriate constant from the eGameState enum. Assume
* that the game is over (i.e. the player and dealer have both either gone bust or decided
* to stand).
*/
eGameState BlackjackGame::getGameState() const
{
if(mDealer.hasBlackjack() && mPlayer.hasBlackjack())
{
return GAME_BLACKJACK_PUSH;
}
else if(mDealer.hasBlackjack())
{
return GAME_DEALER_BLACKJACK;
}
else if(mPlayer.hasBlackJack())
{
return GAME_PLAYER_BLACKJACK;
}
else if(mPlayer.score() > 21)
{
return GAME_PLAYER_BUST;
}
else if(mDealer.score() > 21)
{
return GAME_DEALER_BUST;
}
else if(mDealer.score() > mPlayer.score())
{
return GAME_DEALER_WIN;
}
else if(mDealer.score() < mPlayer.score())
{
return GAME_PLAYER_WIN;
}
else {
return GAME_PUSH;
}
}
}
Main
#include <cstdlib>
#include <iostream> // Standard input and output
#include <string>
using namespace std;
#include <curses.h>
#include "BlackjackGame.h"
// Three different types of text colors used with PDCurses
#define NORM_TEXT 1
#define WIN_TEXT 2
#define LOSE_TEXT 3
int main()
{
// Setup 'PDcurses'
initscr();
start_color();
// Define our colors text colors (foreground, background)
init_pair(NORM_TEXT, COLOR_WHITE, COLOR_BLACK);
init_pair(WIN_TEXT, COLOR_YELLOW, COLOR_BLACK);
init_pair(LOSE_TEXT, COLOR_RED, COLOR_BLACK);
// Define our card colors (these are declared in PrintableCard.h)
init_pair(BLACK_CARD, COLOR_BLACK, COLOR_WHITE);
init_pair(RED_CARD, COLOR_RED, COLOR_WHITE);
// Input from the user and a game object
char ch = '\0';
BlackjackGame myGame;
// Output a 'fancy' welcome screen
myGame.printDeckCurses();
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(1, 13, "Welcome to Blackjack!");
mvprintw(5, 11, "Press any key to play ...");
refresh();
// Wait for input (the 'press any key to begin' thing)
ch = getch();
// Back to normal text to start the game
attron(COLOR_PAIR(NORM_TEXT));
do // Loop to play a new game until the user exits
{
// Restart and play a game (using curses)
myGame.newGame();
// Play a round of blackjack (most of the magic happens here!)
myGame.playGameCurses();
// Print the final status of the game
myGame.printHandsCurses(true);
// Print a game results message (use BOLD and the appropriate text color)
attron(A_BOLD);
switch(myGame.getGameState())
{
case GAME_BLACKJACK_PUSH:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "BLACKJACK TIE!!");
break;
case GAME_DEALER_BLACKJACK:
attron(COLOR_PAIR(LOSE_TEXT));
mvprintw(10, 25, "Dealer Blackjack. You lose.");
break;
case GAME_PLAYER_BLACKJACK:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "BLACKJACK! You win!");
break;
case GAME_DEALER_BUST:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "Dealer Bust. You Win!");
break;
case GAME_PLAYER_BUST:
attron(COLOR_PAIR(LOSE_TEXT));
mvprintw(10, 25, "BUST. You lose.");
break;
case GAME_DEALER_WIN:
attron(COLOR_PAIR(LOSE_TEXT));
mvprintw(10, 25, "You lose.");
break;
case GAME_PLAYER_WIN:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "You Win!");
break;
case GAME_PUSH:
attron(COLOR_PAIR(WIN_TEXT));
mvprintw(10, 25, "It's a tie!");
break;
}
// Turn off bold and return to normal text color
attroff(A_BOLD);
attron(COLOR_PAIR(NORM_TEXT));
// Prompt user to play again
mvprintw(20, 0, "Play again (y/n): ");
refresh();
ch = getch();
} while(ch != 'n');
// Close out 'PDCurses' and pause before exiting
endwin();
system("pause");
return 0;
}
In this program I am trying to create the game BlackJack, most of the game is in other files, but I do not receive errors in the other files. The errors come from the code inside of the Main. The errors are
undefined reference to `BlackjackGame::blackjackGame()
undefined reference to `BlackjackGame::printDeckCurses()
undefined reference to `BlackjackGame::newGame()
undefined reference to `BlackjackGame::playGameCurses()
undefined reference to `BlackjackGame::printedHandsCurses()
undefined reference to `BlackjackGame::getGameState()
With all of these errors coming from the main and these references in the header, that must be my problem, linking the two. Is that correct?
This problem is telling you that you are not linking in the BlackjackGame.o object file when you are building your executable or library.
You must fix your Makefile to link in this file in order to pass the linking stage.
Edit:
Seeing as how you are using Codeblocks for compilation, check these links out(as this error message means different things in different compilers):
undefined reference to function code blocks
Code::Blocks 10.05 Undefined reference to function
It looks like you need to add BlackJackGame.cpp to your project, based on the answer in link 2
Go to Project/Add files to add BlackJackGame source files to your Project, then re-build and that should work
I have a multi-threaded Windows console app whose control thread runs a user input loop like this:
char c;
do {
cin >> c;
// Alter activity based on c
} while(c != 'q')
// Tell other threads to close, .join(), and do cleanup
However at a certain time I want the program itself to be able to gracefully quit. The most obvious way to do so would be to put a "q\n" onto the stdin stream. Is there a reasonable way to do that?
Or a good alternative for a callback to force exit the main control loop (on the primary thread) so that the program falls through to the subsequent cleanup methods?
(The closest I have found so far is this which requires spawning a child process and seems like kludgy overkill at best.)
You can use another flag as loop break condition for all of your threads. The problem with interthread-communication can be solved by synchronization objects which came with C++11 like atomics or mutex.
I ended up using the suggestion from #Captain Obvlious, but even that was tricky (thanks Windows!): As soon as a control signal is handled it causes all sorts of problems with the unchecked char input loop. However, with the following additions any thread can gracefully end the program by calling GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0);
bool exiting = false;
bool cleanedUp = false;
void Cleanup() {
if(!cleanedUp) cleanedUp = true;
else return;
// Tell other threads to close, .join(), and do cleanup
}
// https://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx
bool ConsoleControl(DWORD dwCtrlType) {
exiting = true; // This will cause the stdin loop to break
Cleanup();
return false;
}
int main() {
SetConsoleCtrlHandler((PHANDLER_ROUTINE) ConsoleControl, true);
.
.
.
// Main user control loop
char c;
do {
cin >> c;
if(exiting) break;
if(c < 0) continue; // This must be a control character
// Alter activity based on c
} while(c != 'q')
Cleanup();
return 0;
}
This is an old question but I stumbled upon it and ended up solving the original problem.
I used the WriteConsoleInput function to simulate a user entering keystrokes to the console. This code sample is based off of This question.
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwTmp;
INPUT_RECORD ir[2];
ir[0].EventType = KEY_EVENT;
ir[0].Event.KeyEvent.bKeyDown = TRUE;
ir[0].Event.KeyEvent.dwControlKeyState = 0;
ir[0].Event.KeyEvent.uChar.UnicodeChar = 'q';
ir[0].Event.KeyEvent.wRepeatCount = 1;
ir[0].Event.KeyEvent.wVirtualKeyCode = 'Q';
ir[0].Event.KeyEvent.wVirtualScanCode = MapVirtualKey('Q', MAPVK_VK_TO_VSC);
ir[1].EventType = KEY_EVENT;
ir[1].Event.KeyEvent.bKeyDown = TRUE;
ir[1].Event.KeyEvent.dwControlKeyState = 0;
ir[1].Event.KeyEvent.uChar.UnicodeChar = VK_RETURN;
ir[1].Event.KeyEvent.wRepeatCount = 1;
ir[1].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
ir[1].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
WriteConsoleInput(hStdin, ir, 2, &dwTmp);
On my system MAPVK_VK_TO_VSC wasn't defined so I used 0 instead.
You should add error checking where appropriate.
I hope this helps.
As a first project I plan on making a teensyduino ambient light with different light modes, which are checked in a big switch statement - now I want to switch from one mode to another by pressing a button.
googling lead me to using interrupts, but there is one point that is not clear - if I press the button during an expensive function, which takes long time and has many variables in use, what happens if i call the main loop from the interrupt, does the remaining state of remain in the ram and leads to a stackoverflow if I do switch too many times or is it cleared.
Here some code:
const int speed = 30 //milliseconds
const int modes = 11; //maximum number of modes
const int red = 15;
const int green = 14;
const int blue = 12;
volatile int mode = 0;
void setup() {
pinMode(red , OUTPUT);
pinMode(green , OUTPUT);
pinMode(blue , OUTPUT);
randomSeed(analogRead(0));
Serial.begin(9600);
attachInterrupt(0,incMode,CHANGE); // 0 -> digital pin 2
}
void loop() {
switch(mode){
case 0:{
Serial.println("powerdown");
setAll(0);
delay(1000);
break;
}
\\...
case modes:{
\\ expensive long function
}
}
}
void blinkAll(int times){
for(int i=1;i <= times;i++){
setAll(255);
delay(speed*17);
setAll(0);
delay(speed*17);
}
}
void setAll(int bright){
analogWrite(red , bright);
analogWrite(green , bright);
analogWrite(blue , bright);
}
void incMode(){
delay(speed);
blinkAll(2); //to indicate mode has changed
mode = (mode+1) % (modes+1); //switch starts with 0 so use "% modes+1"!
Serial.println("mode increased");
//--> loop();
//--> would resume the main loop but lead to a stackoverflow i presume
}
How would I break out of the running function without delay and stack pollution.
I know I could just set the mode and wait until the function has ended, but if I have a mode that takes minutes to end I want to be able to switch from it immediately.
PS.: Though I am using a teensyduino, I will use the arduino tag, and as I don't know what language the arduinio uses the tags c/c++. Please change this if it is not appropriate.
You would eventually overflow the stack if you were to reenter main from the interrupt handler multiple times recursively. Additionally, since you'll still be in the interrupt handler as far as the hardware is concerned, you'll have all kinds of weirdness - in particular, interrupts are blocked when you're already in an interrupt, which means delay() won't work and millis() won't count up, and various other things will be broken as well unless you figure out some way to manually re-enable interrupts.
A better way to solve this would be to make your 'expensive long function' instead be a state machine driven by a cheap, short function that is called very frequently. Your interrupt handler can then simply set a flag that is checked on entry into this function, at which point the current mode (ie, current state machine) is changed.
This approach also makes it easier to define new lighting modes. For example, you could define something like this:
struct phase {
unsigned char r, g, b, delay;
};
unsigned long t_nextPhase;
volatile struct phase *forceMode = NULL;
struct phase *mode = blinkAll;
int nextPhase = 0;
struct phase blinkAll[] = {
{ 255, 255, 255, 17 },
{ 0, 0, 0, 17 },
{ 0, 0, 0, 255 } // loop sentinel
};
void lighting_kernel() {
noInterrupts(); // ensure we don't race with interrupts
if (forceMode) {
mode = forceMode;
forceMode = NULL;
t_nextPhase = millis();
nextPhase = 0;
}
interrupts();
if (t_nextPhase > millis()) {
return;
}
struct phase *cur_phase;
do {
cur_phase = mode[nextPhase++];
if (cur_phase->delay == 255) {
nextPhase = 0;
}
} while (cur_phase->delay == 255);
analogWrite(red , cur_phase->r);
analogWrite(green , cur_phase->g);
analogWrite(blue , cur_phase->b);
t_nextPhase = millis() + cur_phase->delay;
}
Now to define a new lighting mode you just need a new array of colors and times, rather than writing new code. Adding things like color ramps and other such effects is left as an exercise to the reader.